Another request/question. Would it be possible to have different x and y scaling on scalable blits, or if thats too slow or complicated, perhaps just scale in only 1 dimension?
im2.getCrop(B2).copyfrom(im1.getCrop(B1))
im2(B2).copyfrom(im1(B1))
I've started porting my desktop graphics library over to Teensy 4.1 with the help of this wonderful library. I'm trying to draw with transparency. Do you have a simple of example of setting up the buffers?
@vindar
Any chance on getting a ILI9488 driver that's compatible with gtx?
You might look at what I hacked up for ILI9341_t3n and ST7789_t3 code over in the thread:So I did manage to get gtx working with the ILI9488 driver, but it's incredibly slow. No double buffering, no differential buffers really makes things crawl.
void update_display(bool force_whole_display) {
if (!force_whole_display) {
// look for bounding range of changes.
uint16_t first_changed_x = tft.width();
uint16_t last_changed_x = 0;
uint16_t first_changed_y = tft.height();
uint16_t last_changed_y = 0;
for (uint16_t y = 0; y < tft.height(); y++) {
uint16_t* ptftfb_y = &ib[y * tft.width()];
uint16_t* pimage_newfb_y = &fb[y * tft.width()];
uint16_t x;
for (x = 0; x < tft.width(); x++) {
// find first if any change in line.
if (ptftfb_y[x] != pimage_newfb_y[x]) {
if (first_changed_y == tft.height()) first_changed_y = y;
last_changed_y = y;
if (x < first_changed_x) first_changed_x = x;
break;
}
}
// find last change in line
if (x < tft.width()) {
for (x = tft.width() - 1; x > last_changed_x; x--) {
if (ptftfb_y[x] != pimage_newfb_y[x]) {
last_changed_x = x; //
break;
}
}
}
}
// now have the bounds
tft.setClipRect(first_changed_x, first_changed_y, (last_changed_x - first_changed_x) + 1, (last_changed_y - first_changed_y) + 1);
static uint8_t debug_count = 20;
if (debug_count) {
Serial.printf("(%u, %u), (%u, %u)\n", first_changed_x, last_changed_x, first_changed_y, last_changed_y );
debug_count--;
}
} else {
tft.setClipRect();
}
memcpy(ib, fb, sizeof(fb));
tft.updateScreen(); // simply update.
}
#if defined(USE_ILI9488)
em = 0;
float scale = 320.0/240.0;
while ((t = em) < 10000) {
im.fillScreen(RGB565_Black);
drawClock(0, scale);
drawSmallHand(100 + 360 * sin(t / 1500.0f), scale);
drawLongHand(500 * cos(t / 5000.0f), scale);
update_display(false);
yield(); // to keep the board responsive
}
#endif
Quick question, if anyone is listening...
Wondering about drawing transparency. Is this only supported in RGB32? i.e. not supported in RGB565?
I was thinking about trying to draw the clock face background outside of the actual clock as transparent. But looking so far at the code
and the clock examples, where the hands were in RGB32.
Obviously could probably convert the clock face to this format, but was wondering if there was support to do this with RGB565, where potentially one
could set what color is the transparent color in that mode...
Just wondering and goofing off
/**
* alpha-blend `fg_col` over this one with a given opacity in the range 0.0f (fully transparent)
* to 1.0f (fully opaque).
*
* @param fg_col The foreground color.
* @param alpha The opacity/alpha multiplier in [0.0f,1.0f].
**/
inline void blend(RGB565 fg_col, float alpha)
{
blend256(fg_col, (uint32_t)(alpha * 256));
}
/**
* alpha-blend `fg_col` over this one with a given opacity in the integer range 0 (fully
* transparent) to 256 (fully opaque).
*
* @param fg_col The foreground color.
* @param alpha The opacity/alpha multiplier in [0,256].
**/
inline void blend256(const RGB565 & fg_col, uint32_t alpha)
{
const uint32_t a = (alpha >> 3); // map to 0 - 32.
const uint32_t bg = (val | (val << 16)) & 0b00000111111000001111100000011111;
const uint32_t fg = (fg_col.val | (fg_col.val << 16)) & 0b00000111111000001111100000011111;
const uint32_t result = ((((fg - bg) * a) >> 5) + bg) & 0b00000111111000001111100000011111;
val = (uint16_t)((result >> 16) | result); // contract result
}
#define COLOR_VAL_TEST 2
#if COLOR_VAL_TEST == 0
void convert_to_rgb32() {
for (int y = 0; y < 234; y++) {
// for each row find each of the values
// Lets find the first none 0 value in this row
int x_first;
const RGB565 *row_data = &antique_clock_data[y * 234];
for (x_first = 0; x_first < 234; x_first++) {
if (row_data[x_first].val != 0) break; // found some data.
if ((x_first & 0xf) == 0) Serial.println();
Serial.print("C(0,0,0,0),");
}
if (x_first != 234) {
// So row is not completely empty. So lets find the last non-zero value
int x_last;
for (x_last = 233; x_last >= x_first; x_last--) {
if (row_data[x_last].val != 0) break; // found some data.
}
// now lets convert each of the actual data elements to RGB32
for (; x_first <= x_last; x_first++) {
if ((x_first & 0xf) == 0) Serial.println();
RGB565 c = row_data[x_first];
Serial.printf("C(%d,%d,%d,255),",
(uint8_t)((((uint8_t)c.R) << 3) | (((uint8_t)c.R) >> 2)),
(uint8_t)((((uint8_t)c.G) << 2) | (((uint8_t)c.G) >> 4)),
(uint8_t)((((uint8_t)c.B) << 3) | (((uint8_t)c.B) >> 2)));
}
for (; x_first < 234; x_first++) {
if ((x_first & 0xf) == 0) Serial.println();
Serial.print("C(0,0,0,0),");
}
}
Serial.println(); // show breaks for rows
}
}
#else
// this version will look at the colors and stop if any one over threahold.
void convert_to_rgb32() {
for (int y = 0; y < 234; y++) {
// for each row find each of the values
// Lets find the first none 0 value in this row
int x_first;
const RGB565 *row_data = &antique_clock_data[y * 234];
for (x_first = 0; x_first < 234; x_first++) {
RGB565 c = row_data[x_first];
if ((c.R > COLOR_VAL_TEST) || (c.G > COLOR_VAL_TEST) || (c.B > COLOR_VAL_TEST)) break;
if ((x_first & 0xf) == 0) Serial.println();
Serial.printf("C(%d,%d,%d,0),",
0,0,0);
/* (uint8_t)((((uint8_t)c.R) << 3) | (((uint8_t)c.R) >> 2)),
(uint8_t)((((uint8_t)c.G) << 2) | (((uint8_t)c.G) >> 4)),
(uint8_t)((((uint8_t)c.B) << 3) | (((uint8_t)c.B) >> 2))); */
}
if (x_first != 234) {
// So row is not completely empty. So lets find the last non-zero value
int x_last;
for (x_last = 233; x_last >= x_first; x_last--) {
RGB565 c = row_data[x_last];
if ((c.R > COLOR_VAL_TEST) || (c.G > COLOR_VAL_TEST) || (c.B > COLOR_VAL_TEST)) break;
}
// now lets convert each of the actual data elements to RGB32
for (; x_first <= x_last; x_first++) {
if ((x_first & 0xf) == 0) Serial.println();
RGB565 c = row_data[x_first];
Serial.printf("C(%d,%d,%d,255),",
(uint8_t)((((uint8_t)c.R) << 3) | (((uint8_t)c.R) >> 2)),
(uint8_t)((((uint8_t)c.G) << 2) | (((uint8_t)c.G) >> 4)),
(uint8_t)((((uint8_t)c.B) << 3) | (((uint8_t)c.B) >> 2)));
}
for (; x_first < 234; x_first++) {
if ((x_first & 0xf) == 0) Serial.println();
RGB565 c = row_data[x_first];
Serial.printf("C(%d,%d,%d,0),",
0,0,0);
/* (uint8_t)((((uint8_t)c.R) << 3) | (((uint8_t)c.R) >> 2)),
(uint8_t)((((uint8_t)c.G) << 2) | (((uint8_t)c.G) >> 4)),
(uint8_t)((((uint8_t)c.B) << 3) | (((uint8_t)c.B) >> 2))); */
}
}
Serial.println(); // show breaks for rows
}
}
#endif
const RGB32 background_color = RGB565_Black;
switch (loop_count) {
case 1: background_color = RGB32_Green; break;
case 2: background_color = RGB32_Orange; break;
case 3: background_color = RGB32_Silver; break;
}
C:\Users\kurte\Documents\Arduino\Teensy Tests\CrazyClock_ili9341_t3x\CrazyClock_ili9341_t3x.ino: In function 'void loop()':
C:\Users\kurte\Documents\Arduino\Teensy Tests\CrazyClock_ili9341_t3x\CrazyClock_ili9341_t3x.ino:314:32: warning: passing 'const tgx::RGB32' as 'this' argument discards qualifiers [-fpermissive]
314 | case 1: background_color = RGB32_Green; break;
| ^~~~~~~~~~~
In file included from c:\Users\kurte\Documents\Arduino\libraries\tgx\src/tgx.h:38,
from C:\Users\kurte\Documents\Arduino\Teensy Tests\CrazyClock_ili9341_t3x\CrazyClock_ili9341_t3x.ino:41:
c:\Users\kurte\Documents\Arduino\libraries\tgx\src/Color.h:1354:16: note: in call to 'constexpr tgx::RGB32& tgx::RGB32::operator=(const tgx::RGB32&)'
1354 | RGB32& operator=(const RGB32&) = default;
| ^~~~~~~~
C:\Users\kurte\Documents\Arduino\Teensy Tests\CrazyClock_ili9341_t3x\CrazyClock_ili9341_t3x.ino:315:32: warning: passing 'const tgx::RGB32' as 'this' argument discards qualifiers [-fpermissive]
315 | case 2: background_color = RGB32_Orange; break;
| ^~~~~~~~~~~~
In file included from c:\Users\kurte\Documents\Arduino\libraries\tgx\src/tgx.h:38,
from C:\Users\kurte\Documents\Arduino\Teensy Tests\CrazyClock_ili9341_t3x\CrazyClock_ili9341_t3x.ino:41:
c:\Users\kurte\Documents\Arduino\libraries\tgx\src/Color.h:1354:16: note: in call to 'constexpr tgx::RGB32& tgx::RGB32::operator=(const tgx::RGB32&)'
1354 | RGB32& operator=(const RGB32&) = default;
| ^~~~~~~~
C:\Users\kurte\Documents\Arduino\Teensy Tests\CrazyClock_ili9341_t3x\CrazyClock_ili9341_t3x.ino:316:32: warning: passing 'const tgx::RGB32' as 'this' argument discards qualifiers [-fpermissive]
316 | case 3: background_color = RGB32_Silver; break;
| ^~~~~~~~~~~~
In file included from c:\Users\kurte\Documents\Arduino\libraries\tgx\src/tgx.h:38,
from C:\Users\kurte\Documents\Arduino\Teensy Tests\CrazyClock_ili9341_t3x\CrazyClock_ili9341_t3x.ino:41:
c:\Users\kurte\Documents\Arduino\libraries\tgx\src/Color.h:1354:16: note: in call to 'constexpr tgx::RGB32& tgx::RGB32::operator=(const tgx::RGB32&)'
1354 | RGB32& operator=(const RGB32&) = default;
| ^~~~~~~~
Thank you for this CrazyClock face. I've cleaned up the face with Photoshop here: View attachment 30921.
Snapshot in use here: View attachment 30922.