mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-08 04:27:12 +03:00
scroller correction + JPEG improvements
This commit is contained in:
@@ -21,6 +21,10 @@
|
|||||||
|
|
||||||
static char TAG[] = "SH1106";
|
static char TAG[] = "SH1106";
|
||||||
|
|
||||||
|
struct SH1106_Private {
|
||||||
|
uint8_t *Shadowbuffer;
|
||||||
|
};
|
||||||
|
|
||||||
// Functions are not declared to minimize # of lines
|
// Functions are not declared to minimize # of lines
|
||||||
|
|
||||||
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
||||||
@@ -36,9 +40,10 @@ static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t En
|
|||||||
|
|
||||||
static void Update( struct GDS_Device* Device ) {
|
static void Update( struct GDS_Device* Device ) {
|
||||||
#ifdef SHADOW_BUFFER
|
#ifdef SHADOW_BUFFER
|
||||||
|
struct SH1106_Private *Private = (struct SH1106_Private*) Device->Private;
|
||||||
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
||||||
int width = Device->Width, rows = Device->Height / 8;
|
int width = Device->Width, rows = Device->Height / 8;
|
||||||
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
|
uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer;
|
||||||
|
|
||||||
// by row, find first and last columns that have been updated
|
// by row, find first and last columns that have been updated
|
||||||
for (int r = 0; r < rows; r++) {
|
for (int r = 0; r < rows; r++) {
|
||||||
@@ -55,7 +60,7 @@ static void Update( struct GDS_Device* Device ) {
|
|||||||
if (first--) {
|
if (first--) {
|
||||||
SetColumnAddress( Device, first, last );
|
SetColumnAddress( Device, first, last );
|
||||||
SetPageAddress( Device, r, r);
|
SetPageAddress( Device, r, r);
|
||||||
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
|
Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -83,15 +88,16 @@ static bool Init( struct GDS_Device* Device ) {
|
|||||||
|
|
||||||
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
|
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
|
||||||
#ifdef SHADOW_BUFFER
|
#ifdef SHADOW_BUFFER
|
||||||
|
struct SH1106_Private *Private = (struct SH1106_Private*) Device->Private;
|
||||||
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
|
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
|
||||||
NullCheck( Device->Framebuffer, return false );
|
NullCheck( Device->Framebuffer, return false );
|
||||||
#ifdef USE_IRAM
|
#ifdef USE_IRAM
|
||||||
if (Device->IF == IF_SPI) Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
if (Device->IF == IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
Device->Shadowbuffer = malloc( Device->FramebufferSize );
|
Private->Shadowbuffer = malloc( Device->FramebufferSize );
|
||||||
NullCheck( Device->Shadowbuffer, return false );
|
NullCheck( Private->Shadowbuffer, return false );
|
||||||
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
||||||
#else // not SHADOW_BUFFER
|
#else // not SHADOW_BUFFER
|
||||||
#ifdef USE_IRAM
|
#ifdef USE_IRAM
|
||||||
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
|
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
|
|
||||||
static char TAG[] = "SSD1306";
|
static char TAG[] = "SSD1306";
|
||||||
|
|
||||||
|
struct SSD1306_Private {
|
||||||
|
uint8_t *Shadowbuffer;
|
||||||
|
};
|
||||||
|
|
||||||
// Functions are not deckared to minimize # of lines
|
// Functions are not deckared to minimize # of lines
|
||||||
|
|
||||||
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
||||||
@@ -36,9 +40,10 @@ static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t En
|
|||||||
|
|
||||||
static void Update( struct GDS_Device* Device ) {
|
static void Update( struct GDS_Device* Device ) {
|
||||||
#ifdef SHADOW_BUFFER
|
#ifdef SHADOW_BUFFER
|
||||||
|
struct SSD1306_Private *Private = (struct SSD1306_Private*) Device->Private;
|
||||||
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
||||||
int width = Device->Width, rows = Device->Height / 8;
|
int width = Device->Width, rows = Device->Height / 8;
|
||||||
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
|
uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer;
|
||||||
int CurrentRow = -1, FirstCol = -1, LastCol = -1;
|
int CurrentRow = -1, FirstCol = -1, LastCol = -1;
|
||||||
|
|
||||||
// by row, find first and last columns that have been updated
|
// by row, find first and last columns that have been updated
|
||||||
@@ -54,7 +59,6 @@ static void Update( struct GDS_Device* Device ) {
|
|||||||
|
|
||||||
// now update the display by "byte rows"
|
// now update the display by "byte rows"
|
||||||
if (first--) {
|
if (first--) {
|
||||||
|
|
||||||
// only set column when useful, saves a fair bit of CPU
|
// only set column when useful, saves a fair bit of CPU
|
||||||
if (first > FirstCol && first <= FirstCol + 4 && last < LastCol && last >= LastCol - 4) {
|
if (first > FirstCol && first <= FirstCol + 4 && last < LastCol && last >= LastCol - 4) {
|
||||||
first = FirstCol;
|
first = FirstCol;
|
||||||
@@ -70,7 +74,7 @@ static void Update( struct GDS_Device* Device ) {
|
|||||||
CurrentRow = r + 1;
|
CurrentRow = r + 1;
|
||||||
|
|
||||||
// actual write
|
// actual write
|
||||||
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
|
Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -94,17 +98,18 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
|
|||||||
static bool Init( struct GDS_Device* Device ) {
|
static bool Init( struct GDS_Device* Device ) {
|
||||||
Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;
|
Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;
|
||||||
|
|
||||||
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
|
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
|
||||||
#ifdef SHADOW_BUFFER
|
#ifdef SHADOW_BUFFER
|
||||||
|
struct SSD1306_Private *Private = (struct SSD1306_Private*) Device->Private;
|
||||||
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
|
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
|
||||||
NullCheck( Device->Framebuffer, return false );
|
NullCheck( Device->Framebuffer, return false );
|
||||||
#ifdef USE_IRAM
|
#ifdef USE_IRAM
|
||||||
if (Device->IF == IF_SPI) Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
if (Device->IF == IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
Device->Shadowbuffer = malloc( Device->FramebufferSize );
|
Private->Shadowbuffer = malloc( Device->FramebufferSize );
|
||||||
NullCheck( Device->Shadowbuffer, return false );
|
NullCheck( Private->Shadowbuffer, return false );
|
||||||
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
||||||
#else // not SHADOW_BUFFER
|
#else // not SHADOW_BUFFER
|
||||||
#ifdef USE_IRAM
|
#ifdef USE_IRAM
|
||||||
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
|
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ static char TAG[] = "SSD132x";
|
|||||||
enum { SSD1326, SSD1327 };
|
enum { SSD1326, SSD1327 };
|
||||||
|
|
||||||
struct SSD132x_Private {
|
struct SSD132x_Private {
|
||||||
uint8_t *iRAM;
|
uint8_t *iRAM, *Shadowbuffer;
|
||||||
uint8_t ReMap, PageSize;
|
uint8_t ReMap, PageSize;
|
||||||
uint8_t Model;
|
uint8_t Model;
|
||||||
};
|
};
|
||||||
@@ -73,7 +73,7 @@ static void Update4( struct GDS_Device* Device ) {
|
|||||||
SetColumnAddress( Device, 0, Device->Width / 2 - 1);
|
SetColumnAddress( Device, 0, Device->Width / 2 - 1);
|
||||||
|
|
||||||
#ifdef SHADOW_BUFFER
|
#ifdef SHADOW_BUFFER
|
||||||
uint16_t *optr = (uint16_t*) Device->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
|
uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
|
||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
|
|
||||||
for (int r = 0, page = 0; r < Device->Height; r++) {
|
for (int r = 0, page = 0; r < Device->Height; r++) {
|
||||||
@@ -92,10 +92,10 @@ static void Update4( struct GDS_Device* Device ) {
|
|||||||
SetRowAddress( Device, r - page + 1, r );
|
SetRowAddress( Device, r - page + 1, r );
|
||||||
// own use of IRAM has not proven to be much better than letting SPI do its copy
|
// own use of IRAM has not proven to be much better than letting SPI do its copy
|
||||||
if (Private->iRAM) {
|
if (Private->iRAM) {
|
||||||
memcpy(Private->iRAM, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
|
memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
|
||||||
Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 );
|
Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 );
|
||||||
} else {
|
} else {
|
||||||
Device->WriteData( Device, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
|
Device->WriteData( Device, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
|
||||||
}
|
}
|
||||||
dirty = false;
|
dirty = false;
|
||||||
}
|
}
|
||||||
@@ -122,9 +122,10 @@ static void Update4( struct GDS_Device* Device ) {
|
|||||||
*/
|
*/
|
||||||
static void Update1( struct GDS_Device* Device ) {
|
static void Update1( struct GDS_Device* Device ) {
|
||||||
#ifdef SHADOW_BUFFER
|
#ifdef SHADOW_BUFFER
|
||||||
|
struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private;
|
||||||
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
||||||
int width = Device->Width / 8, rows = Device->Height;
|
int width = Device->Width / 8, rows = Device->Height;
|
||||||
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
|
uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer;
|
||||||
|
|
||||||
// by row, find first and last columns that have been updated
|
// by row, find first and last columns that have been updated
|
||||||
for (int r = 0; r < rows; r++) {
|
for (int r = 0; r < rows; r++) {
|
||||||
@@ -141,7 +142,7 @@ static void Update1( struct GDS_Device* Device ) {
|
|||||||
if (first--) {
|
if (first--) {
|
||||||
SetColumnAddress( Device, first, last );
|
SetColumnAddress( Device, first, last );
|
||||||
SetRowAddress( Device, r, r);
|
SetRowAddress( Device, r, r);
|
||||||
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
|
Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -242,15 +243,15 @@ static bool Init( struct GDS_Device* Device ) {
|
|||||||
#ifdef USE_IRAM
|
#ifdef USE_IRAM
|
||||||
if (Device->IF == IF_SPI) {
|
if (Device->IF == IF_SPI) {
|
||||||
if (Device->Depth == 1) {
|
if (Device->Depth == 1) {
|
||||||
Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||||
} else {
|
} else {
|
||||||
Device->Shadowbuffer = malloc( Device->FramebufferSize );
|
Private->Shadowbuffer = malloc( Device->FramebufferSize );
|
||||||
Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
Device->Shadowbuffer = malloc( Device->FramebufferSize );
|
Private->Shadowbuffer = malloc( Device->FramebufferSize );
|
||||||
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
||||||
#else // not SHADOW_BUFFER
|
#else // not SHADOW_BUFFER
|
||||||
#ifdef USE_IRAM
|
#ifdef USE_IRAM
|
||||||
if (Device->IF == IF_SPI) {
|
if (Device->IF == IF_SPI) {
|
||||||
|
|||||||
@@ -13,13 +13,14 @@
|
|||||||
monochrome mode is not such type of screen, SH1106 and SSD1306 are
|
monochrome mode is not such type of screen, SH1106 and SSD1306 are
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7,
|
enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1 = 1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7,
|
||||||
GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15,
|
GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15,
|
||||||
|
GDS_COLOR_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GDS_COLOR_BLACK GDS_COLOR_L0
|
#define GDS_COLOR_BLACK GDS_COLOR_L0
|
||||||
#define GDS_COLOR_WHITE GDS_COLOR_L15
|
#define GDS_COLOR_WHITE GDS_COLOR_L1
|
||||||
#define GDS_COLOR_XOR 2
|
#define GDS_COLOR_XOR (GDS_COLOR_MAX + 1)
|
||||||
|
|
||||||
struct GDS_Device;
|
struct GDS_Device;
|
||||||
struct GDS_FontDef;
|
struct GDS_FontDef;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ typedef struct {
|
|||||||
struct { // DirectDraw
|
struct { // DirectDraw
|
||||||
struct GDS_Device * Device;
|
struct GDS_Device * Device;
|
||||||
int XOfs, YOfs;
|
int XOfs, YOfs;
|
||||||
|
int XMin, YMin;
|
||||||
int Depth;
|
int Depth;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -38,9 +39,7 @@ static unsigned OutHandler(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
|
|||||||
uint8_t *Pixels = (uint8_t*) Bitmap;
|
uint8_t *Pixels = (uint8_t*) Bitmap;
|
||||||
|
|
||||||
for (int y = Frame->top; y <= Frame->bottom; y++) {
|
for (int y = Frame->top; y <= Frame->bottom; y++) {
|
||||||
if (y < Context->YOfs) continue;
|
|
||||||
for (int x = Frame->left; x <= Frame->right; x++) {
|
for (int x = Frame->left; x <= Frame->right; x++) {
|
||||||
if (x < Context->XOfs) continue;
|
|
||||||
// Convert the 888 to RGB565
|
// Convert the 888 to RGB565
|
||||||
uint16_t Value = (*Pixels++ & ~0x07) << 8;
|
uint16_t Value = (*Pixels++ & ~0x07) << 8;
|
||||||
Value |= (*Pixels++ & ~0x03) << 3;
|
Value |= (*Pixels++ & ~0x03) << 3;
|
||||||
@@ -57,12 +56,14 @@ static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
|
|||||||
int Shift = 8 - Context->Depth;
|
int Shift = 8 - Context->Depth;
|
||||||
|
|
||||||
for (int y = Frame->top; y <= Frame->bottom; y++) {
|
for (int y = Frame->top; y <= Frame->bottom; y++) {
|
||||||
|
if (y < Context->YMin) continue;
|
||||||
for (int x = Frame->left; x <= Frame->right; x++) {
|
for (int x = Frame->left; x <= Frame->right; x++) {
|
||||||
|
if (x < Context->XMin) continue;
|
||||||
// Convert the 888 to RGB565
|
// Convert the 888 to RGB565
|
||||||
int Value = ((Pixels[0]*11 + Pixels[1]*59 + Pixels[2]*30) / 100) >> Shift;
|
int Value = ((Pixels[0]*11 + Pixels[1]*59 + Pixels[2]*30) / 100) >> Shift;
|
||||||
Pixels += 3;
|
Pixels += 3;
|
||||||
// used DrawPixel and not "fast" version as X,Y may be beyond screen
|
// used DrawPixel and not "fast" version as X,Y may be beyond screen
|
||||||
GDS_DrawPixel( Context->Device, Context->XOfs + x, Context->YOfs + y, Value);
|
GDS_DrawPixel( Context->Device, x + Context->XOfs, y + Context->YOfs, Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@@ -90,11 +91,16 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
|
|||||||
Decoder.scale = Scale;
|
Decoder.scale = Scale;
|
||||||
|
|
||||||
if (Res == JDR_OK && !SizeOnly) {
|
if (Res == JDR_OK && !SizeOnly) {
|
||||||
// find the scaling factor
|
|
||||||
Context.OutData = malloc(Decoder.width * Decoder.height * sizeof(uint16_t));
|
Context.OutData = malloc(Decoder.width * Decoder.height * sizeof(uint16_t));
|
||||||
|
|
||||||
|
// find the scaling factor
|
||||||
uint8_t N = 0, ScaleInt = ceil(1.0 / Scale);
|
uint8_t N = 0, ScaleInt = ceil(1.0 / Scale);
|
||||||
ScaleInt--; ScaleInt |= ScaleInt >> 1; ScaleInt |= ScaleInt >> 2; ScaleInt++;
|
ScaleInt--; ScaleInt |= ScaleInt >> 1; ScaleInt |= ScaleInt >> 2; ScaleInt++;
|
||||||
while (ScaleInt >>= 1) N++;
|
while (ScaleInt >>= 1) N++;
|
||||||
|
if (N > 3) {
|
||||||
|
ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height);
|
||||||
|
N = 3;
|
||||||
|
}
|
||||||
|
|
||||||
// ready to decode
|
// ready to decode
|
||||||
if (Context.OutData) {
|
if (Context.OutData) {
|
||||||
@@ -102,7 +108,7 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
|
|||||||
Context.Height = Decoder.height / (1 << N);
|
Context.Height = Decoder.height / (1 << N);
|
||||||
if (Width) *Width = Context.Width;
|
if (Width) *Width = Context.Width;
|
||||||
if (Height) *Height = Context.Height;
|
if (Height) *Height = Context.Height;
|
||||||
Res = jd_decomp(&Decoder, OutHandler, N > 3 ? 3 : N);
|
Res = jd_decomp(&Decoder, OutHandler, N);
|
||||||
if (Res != JDR_OK) {
|
if (Res != JDR_OK) {
|
||||||
ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
|
ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
|
||||||
}
|
}
|
||||||
@@ -202,16 +208,23 @@ bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int
|
|||||||
uint8_t Ratio = XRatio < YRatio ? ceil(1/XRatio) : ceil(1/YRatio);
|
uint8_t Ratio = XRatio < YRatio ? ceil(1/XRatio) : ceil(1/YRatio);
|
||||||
Ratio--; Ratio |= Ratio >> 1; Ratio |= Ratio >> 2; Ratio++;
|
Ratio--; Ratio |= Ratio >> 1; Ratio |= Ratio >> 2; Ratio++;
|
||||||
while (Ratio >>= 1) N++;
|
while (Ratio >>= 1) N++;
|
||||||
|
if (N > 3) {
|
||||||
|
ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height);
|
||||||
|
N = 3;
|
||||||
|
}
|
||||||
Context.Width /= 1 << N;
|
Context.Width /= 1 << N;
|
||||||
Context.Height /= 1 << N;
|
Context.Height /= 1 << N;
|
||||||
}
|
}
|
||||||
|
|
||||||
// then place it
|
// then place it
|
||||||
if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = x + ((Device->Width - x) - Context.Width) / 2;
|
if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = (Device->Width + x - Context.Width) / 2;
|
||||||
if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = y + ((Device->Height - y) - Context.Height) / 2;
|
if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = (Device->Height + y - Context.Height) / 2;
|
||||||
|
|
||||||
|
Context.XMin = x - Context.XOfs;
|
||||||
|
Context.YMin = y - Context.YOfs;
|
||||||
|
|
||||||
// do decompress & draw
|
// do decompress & draw
|
||||||
Res = jd_decomp(&Decoder, OutHandlerDirect, N > 3 ? 3 : N);
|
Res = jd_decomp(&Decoder, OutHandlerDirect, N);
|
||||||
if (Res == JDR_OK) {
|
if (Res == JDR_OK) {
|
||||||
Ret = true;
|
Ret = true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ struct GDS_Device {
|
|||||||
uint16_t Height;
|
uint16_t Height;
|
||||||
uint8_t Depth;
|
uint8_t Depth;
|
||||||
|
|
||||||
uint8_t* Framebuffer, *Shadowbuffer;
|
uint8_t* Framebuffer;
|
||||||
uint16_t FramebufferSize;
|
uint16_t FramebufferSize;
|
||||||
bool Dirty;
|
bool Dirty;
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ static struct scroller_s {
|
|||||||
u16_t mode;
|
u16_t mode;
|
||||||
s16_t by;
|
s16_t by;
|
||||||
// scroller management & sharing between grfg and scrolling task
|
// scroller management & sharing between grfg and scrolling task
|
||||||
bool active, first;
|
bool active, first, overflow;
|
||||||
int scrolled;
|
int scrolled;
|
||||||
struct {
|
struct {
|
||||||
u8_t *frame;
|
u8_t *frame;
|
||||||
@@ -234,7 +234,7 @@ bool sb_display_init(void) {
|
|||||||
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
|
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
|
||||||
|
|
||||||
// size scroller (width + current screen)
|
// size scroller (width + current screen)
|
||||||
scroller.scroll.max = (displayer.width * displayer.height / 8) * (10 + 1);
|
scroller.scroll.max = (displayer.width * displayer.height / 8) * (15 + 1);
|
||||||
scroller.scroll.frame = malloc(scroller.scroll.max);
|
scroller.scroll.frame = malloc(scroller.scroll.max);
|
||||||
scroller.back.frame = malloc(displayer.width * displayer.height / 8);
|
scroller.back.frame = malloc(displayer.width * displayer.height / 8);
|
||||||
scroller.frame = malloc(displayer.width * displayer.height / 8);
|
scroller.frame = malloc(displayer.width * displayer.height / 8);
|
||||||
@@ -559,6 +559,7 @@ static void grfs_handler(u8_t *data, int len) {
|
|||||||
scroller.mode = htons(pkt->mode);
|
scroller.mode = htons(pkt->mode);
|
||||||
scroller.scroll.width = htons(pkt->width);
|
scroller.scroll.width = htons(pkt->width);
|
||||||
scroller.first = true;
|
scroller.first = true;
|
||||||
|
scroller.overflow = false;
|
||||||
|
|
||||||
// background excludes space taken by visu (if any)
|
// background excludes space taken by visu (if any)
|
||||||
scroller.back.width = displayer.width - ((visu.mode && visu.row < SB_HEIGHT) ? visu.width : 0);
|
scroller.back.width = displayer.width - ((visu.mode && visu.row < SB_HEIGHT) ? visu.width : 0);
|
||||||
@@ -576,13 +577,14 @@ static void grfs_handler(u8_t *data, int len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy scroll frame data (no semaphore needed)
|
// copy scroll frame data (no semaphore needed)
|
||||||
if (scroller.scroll.size + size < scroller.scroll.max) {
|
if (scroller.scroll.size + size < scroller.scroll.max && !scroller.overflow) {
|
||||||
memcpy(scroller.scroll.frame + offset, data + sizeof(struct grfs_packet), size);
|
memcpy(scroller.scroll.frame + offset, data + sizeof(struct grfs_packet), size);
|
||||||
scroller.scroll.size = offset + size;
|
scroller.scroll.size = offset + size;
|
||||||
LOG_INFO("scroller current size %u", scroller.scroll.size);
|
LOG_INFO("scroller current size %u (w:%u)", scroller.scroll.size, scroller.scroll.width);
|
||||||
} else {
|
} else {
|
||||||
LOG_INFO("scroller too larger %u/%u/%u", scroller.scroll.size + size, scroller.scroll.max, scroller.scroll.width);
|
LOG_INFO("scroller too large %u/%u (w:%u)", scroller.scroll.size + size, scroller.scroll.max, scroller.scroll.width);
|
||||||
scroller.scroll.width = scroller.scroll.size / (displayer.height / 8);
|
scroller.scroll.width = scroller.scroll.size / (displayer.height / 8) - scroller.back.width;
|
||||||
|
scroller.overflow = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,7 +862,7 @@ static void displayer_task(void *args) {
|
|||||||
// by default go for the long sleep, will change below if required
|
// by default go for the long sleep, will change below if required
|
||||||
scroller.wake = LONG_WAKE;
|
scroller.wake = LONG_WAKE;
|
||||||
|
|
||||||
// do we have more to scroll (scroll.width is the last column from which we havea full zone)
|
// do we have more to scroll (scroll.width is the last column from which we have a full zone)
|
||||||
if (scroller.by > 0 ? (scroller.scrolled <= scroller.scroll.width) : (scroller.scrolled >= 0)) {
|
if (scroller.by > 0 ? (scroller.scrolled <= scroller.scroll.width) : (scroller.scrolled >= 0)) {
|
||||||
memcpy(scroller.frame, scroller.back.frame, scroller.back.width * displayer.height / 8);
|
memcpy(scroller.frame, scroller.back.frame, scroller.back.width * displayer.height / 8);
|
||||||
for (int i = 0; i < scroller.width * displayer.height / 8; i++) scroller.frame[i] |= scroller.scroll.frame[scroller.scrolled * displayer.height / 8 + i];
|
for (int i = 0; i < scroller.width * displayer.height / 8; i++) scroller.frame[i] |= scroller.scroll.frame[scroller.scrolled * displayer.height / 8 + i];
|
||||||
|
|||||||
Reference in New Issue
Block a user