mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-08 04:27:12 +03:00
migrating to esp-idf V4.0 gcc 8.2 and CMake
This commit is contained in:
@@ -1,141 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
* 2020 Philippe G.
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "gds.h"
|
||||
#include "gds_private.h"
|
||||
|
||||
static struct GDS_Device Display;
|
||||
|
||||
static char TAG[] = "gds";
|
||||
|
||||
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[] ) {
|
||||
for (int i = 0; DetectFunc[i]; i++) {
|
||||
if (DetectFunc[i](Driver, &Display)) {
|
||||
ESP_LOGD(TAG, "Detected driver %p", &Display);
|
||||
return &Display;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void GDS_ClearExt(struct GDS_Device* Device, bool full, ...) {
|
||||
bool commit = true;
|
||||
|
||||
if (full) {
|
||||
GDS_Clear( Device, GDS_COLOR_BLACK );
|
||||
} else {
|
||||
va_list args;
|
||||
va_start(args, full);
|
||||
commit = va_arg(args, int);
|
||||
int x1 = va_arg(args, int), y1 = va_arg(args, int), x2 = va_arg(args, int), y2 = va_arg(args, int);
|
||||
if (x2 < 0) x2 = Device->Width - 1;
|
||||
if (y2 < 0) y2 = Device->Height - 1;
|
||||
GDS_ClearWindow( Device, x1, y1, x2, y2, GDS_COLOR_BLACK );
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
Device->Dirty = true;
|
||||
if (commit) GDS_Update(Device);
|
||||
}
|
||||
|
||||
void GDS_Clear( struct GDS_Device* Device, int Color ) {
|
||||
if (Device->Depth == 1) Color = Color == GDS_COLOR_BLACK ? 0 : 0xff;
|
||||
else if (Device->Depth == 4) Color = Color | (Color << 4);
|
||||
memset( Device->Framebuffer, Color, Device->FramebufferSize );
|
||||
Device->Dirty = true;
|
||||
}
|
||||
|
||||
void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
|
||||
// driver can provide own optimized clear window
|
||||
if (Device->ClearWindow) {
|
||||
Device->ClearWindow( Device, x1, y1, x2, y2, Color );
|
||||
} else if (Device->Depth == 1) {
|
||||
// single shot if we erase all screen
|
||||
if (x2 - x1 == Device->Width - 1 && y2 - y1 == Device->Height - 1) {
|
||||
memset( Device->Framebuffer, Color == GDS_COLOR_BLACK ? 0 : 0xff, Device->FramebufferSize );
|
||||
} else {
|
||||
uint8_t _Color = Color == GDS_COLOR_BLACK ? 0: 0xff;
|
||||
uint8_t Width = Device->Width >> 3;
|
||||
uint8_t *optr = Device->Framebuffer;
|
||||
// try to do byte processing as much as possible
|
||||
for (int r = y1; r <= y2;) {
|
||||
int c = x1;
|
||||
// for a row that is not on a boundary, no optimization possible
|
||||
while (r & 0x07 && r <= y2) {
|
||||
for (c = x1; c <= x2; c++) GDS_DrawPixelFast( Device, c, r, Color );
|
||||
r++;
|
||||
}
|
||||
// go fast if we have more than 8 lines to write
|
||||
if (r + 8 <= y2) {
|
||||
memset(optr + Width * r + x1, _Color, x2 - x1 + 1);
|
||||
r += 8;
|
||||
} else while (r <= y2) {
|
||||
for (c = x1; c <= x2; c++) GDS_DrawPixelFast( Device, c, r, Color );
|
||||
r++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} if (Device->Depth == 4) {
|
||||
if (x2 - x1 == Device->Width - 1 && y2 - y1 == Device->Height - 1) {
|
||||
// we assume color is 0..15
|
||||
memset( Device->Framebuffer, Color | (Color << 4), Device->FramebufferSize );
|
||||
} else {
|
||||
uint8_t _Color = Color | (Color << 4);
|
||||
uint8_t Width = Device->Width;
|
||||
uint8_t *optr = Device->Framebuffer;
|
||||
// try to do byte processing as much as possible
|
||||
for (int r = y1; r <= y2; r++) {
|
||||
int c = x1;
|
||||
if (c & 0x01) GDS_DrawPixelFast( Device, c++, r, Color);
|
||||
int chunk = (x2 - c + 1) >> 1;
|
||||
memset(optr + ((r * Width + c) >> 1), _Color, chunk);
|
||||
if (c + chunk <= x2) GDS_DrawPixelFast( Device, x2, r, Color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
GDS_DrawPixelFast( Device, x, y, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure diplay will do update
|
||||
Device->Dirty = true;
|
||||
}
|
||||
|
||||
void GDS_Update( struct GDS_Device* Device ) {
|
||||
if (Device->Dirty) Device->Update( Device );
|
||||
Device->Dirty = false;
|
||||
}
|
||||
|
||||
bool GDS_Reset( struct GDS_Device* Device ) {
|
||||
if ( Device->RSTPin >= 0 ) {
|
||||
gpio_set_level( Device->RSTPin, 0 );
|
||||
vTaskDelay( pdMS_TO_TICKS( 100 ) );
|
||||
gpio_set_level( Device->RSTPin, 1 );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { Device->SetContrast( Device, Contrast); }
|
||||
void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { Device->SetHFlip( Device, On ); }
|
||||
void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { Device->SetVFlip( Device, On ); }
|
||||
int GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
|
||||
int GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }
|
||||
void GDS_DisplayOn( struct GDS_Device* Device ) { Device->DisplayOn( Device ); }
|
||||
void GDS_DisplayOff( struct GDS_Device* Device ) { Device->DisplayOff( Device ); }
|
||||
@@ -1,43 +0,0 @@
|
||||
#ifndef _GDS_H_
|
||||
#define _GDS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* NOTE for drivers:
|
||||
The build-in DrawPixel(Fast), DrawCBR and ClearWindow are optimized for 1 bit
|
||||
and 4 bits screen depth. For any other type of screen, DrawCBR and ClearWindow
|
||||
default to use DrawPixel, which is very sub-optimal. For such other depth, you
|
||||
must supply the DrawPixelFast. The built-in 1 bit depth function are only for
|
||||
screen with vertical framing (1 byte = 8 lines). For example SSD1326 in
|
||||
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,
|
||||
GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15,
|
||||
};
|
||||
|
||||
#define GDS_COLOR_BLACK GDS_COLOR_L0
|
||||
#define GDS_COLOR_WHITE GDS_COLOR_L15
|
||||
#define GDS_COLOR_XOR 2
|
||||
|
||||
struct GDS_Device;
|
||||
struct GDS_FontDef;
|
||||
|
||||
typedef struct GDS_Device* GDS_DetectFunc(char *Driver, struct GDS_Device *Device);
|
||||
|
||||
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[] );
|
||||
|
||||
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast );
|
||||
void GDS_DisplayOn( struct GDS_Device* Device );
|
||||
void GDS_DisplayOff( struct GDS_Device* Device );
|
||||
void GDS_Update( struct GDS_Device* Device );
|
||||
void GDS_SetHFlip( struct GDS_Device* Device, bool On );
|
||||
void GDS_SetVFlip( struct GDS_Device* Device, bool On );
|
||||
int GDS_GetWidth( struct GDS_Device* Device );
|
||||
int GDS_GetHeight( struct GDS_Device* Device );
|
||||
void GDS_ClearExt( struct GDS_Device* Device, bool full, ...);
|
||||
void GDS_Clear( struct GDS_Device* Device, int Color );
|
||||
void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef _GDS_DEFAULT_IF_H_
|
||||
#define _GDS_DEFAULT_IF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct GDS_Device;
|
||||
|
||||
bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int speed );
|
||||
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin );
|
||||
|
||||
bool GDS_SPIInit( int SPI, int DC );
|
||||
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,271 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
* 2020 Philippe G.
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <esp_attr.h>
|
||||
|
||||
#include "gds_private.h"
|
||||
#include "gds.h"
|
||||
#include "gds_draw.h"
|
||||
|
||||
static const unsigned char BitReverseTable256[] =
|
||||
{
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
|
||||
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
|
||||
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
|
||||
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
|
||||
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
|
||||
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
|
||||
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
|
||||
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
|
||||
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
|
||||
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
|
||||
};
|
||||
|
||||
__attribute__( ( always_inline ) ) static inline void SwapInt( int* a, int* b ) {
|
||||
int Temp = *b;
|
||||
|
||||
*b = *a;
|
||||
*a = Temp;
|
||||
}
|
||||
|
||||
// un-comment if need to be instanciated for external callers
|
||||
extern inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color );
|
||||
extern inline void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int X, int Y, int Color );
|
||||
|
||||
void GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Color ) {
|
||||
int XEnd = x + Width;
|
||||
|
||||
Device->Dirty = true;
|
||||
|
||||
if (x < 0) x = 0;
|
||||
if (XEnd >= Device->Width) XEnd = Device->Width - 1;
|
||||
|
||||
if (y < 0) y = 0;
|
||||
else if (y >= Device->Height) x = Device->Height - 1;
|
||||
|
||||
for ( ; x < XEnd; x++ ) GDS_DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
|
||||
void GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color ) {
|
||||
int YEnd = y + Height;
|
||||
|
||||
Device->Dirty = true;
|
||||
|
||||
if (x < 0) x = 0;
|
||||
if (x >= Device->Width) x = Device->Width - 1;
|
||||
|
||||
if (y < 0) y = 0;
|
||||
else if (YEnd >= Device->Height) YEnd = Device->Height - 1;
|
||||
|
||||
for ( ; y < YEnd; y++ ) GDS_DrawPixel( Device, x, y, Color );
|
||||
}
|
||||
|
||||
static inline void DrawWideLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
|
||||
int dx = ( x1 - x0 );
|
||||
int dy = ( y1 - y0 );
|
||||
int Error = 0;
|
||||
int Incr = 1;
|
||||
int x = x0;
|
||||
int y = y0;
|
||||
|
||||
if ( dy < 0 ) {
|
||||
Incr = -1;
|
||||
dy = -dy;
|
||||
}
|
||||
|
||||
Error = ( dy * 2 ) - dx;
|
||||
|
||||
for ( ; x < x1; x++ ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
GDS_DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
|
||||
if ( Error > 0 ) {
|
||||
Error-= ( dx * 2 );
|
||||
y+= Incr;
|
||||
}
|
||||
|
||||
Error+= ( dy * 2 );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void DrawTallLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
|
||||
int dx = ( x1 - x0 );
|
||||
int dy = ( y1 - y0 );
|
||||
int Error = 0;
|
||||
int Incr = 1;
|
||||
int x = x0;
|
||||
int y = y0;
|
||||
|
||||
if ( dx < 0 ) {
|
||||
Incr = -1;
|
||||
dx = -dx;
|
||||
}
|
||||
|
||||
Error = ( dx * 2 ) - dy;
|
||||
|
||||
for ( ; y < y1; y++ ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
GDS_DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
|
||||
if ( Error > 0 ) {
|
||||
Error-= ( dy * 2 );
|
||||
x+= Incr;
|
||||
}
|
||||
|
||||
Error+= ( dx * 2 );
|
||||
}
|
||||
}
|
||||
|
||||
void GDS_DrawLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
|
||||
if ( x0 == x1 ) {
|
||||
GDS_DrawVLine( Device, x0, y0, ( y1 - y0 ), Color );
|
||||
} else if ( y0 == y1 ) {
|
||||
GDS_DrawHLine( Device, x0, y0, ( x1 - x0 ), Color );
|
||||
} else {
|
||||
Device->Dirty = true;
|
||||
if ( abs( x1 - x0 ) > abs( y1 - y0 ) ) {
|
||||
/* Wide ( run > rise ) */
|
||||
if ( x0 > x1 ) {
|
||||
SwapInt( &x0, &x1 );
|
||||
SwapInt( &y0, &y1 );
|
||||
}
|
||||
|
||||
DrawWideLine( Device, x0, y0, x1, y1, Color );
|
||||
} else {
|
||||
/* Tall ( rise > run ) */
|
||||
if ( y0 > y1 ) {
|
||||
SwapInt( &y0, &y1 );
|
||||
SwapInt( &x0, &x1 );
|
||||
}
|
||||
|
||||
DrawTallLine( Device, x0, y0, x1, y1, Color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GDS_DrawBox( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color, bool Fill ) {
|
||||
int Width = ( x2 - x1 );
|
||||
int Height = ( y2 - y1 );
|
||||
|
||||
Device->Dirty = true;
|
||||
|
||||
if ( Fill == false ) {
|
||||
/* Top side */
|
||||
GDS_DrawHLine( Device, x1, y1, Width, Color );
|
||||
|
||||
/* Bottom side */
|
||||
GDS_DrawHLine( Device, x1, y1 + Height, Width, Color );
|
||||
|
||||
/* Left side */
|
||||
GDS_DrawVLine( Device, x1, y1, Height, Color );
|
||||
|
||||
/* Right side */
|
||||
GDS_DrawVLine( Device, x1 + Width, y1, Height, Color );
|
||||
} else {
|
||||
/* Fill the box by drawing horizontal lines */
|
||||
for ( ; y1 <= y2; y1++ ) {
|
||||
GDS_DrawHLine( Device, x1, y1, Width, Color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************************
|
||||
* Process graphic display data from column-oriented data (MSbit first)
|
||||
*/
|
||||
void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) {
|
||||
if (!Height) Height = Device->Height;
|
||||
if (!Width) Width = Device->Width;
|
||||
Height >>= 3;
|
||||
|
||||
if (Device->DrawBitmapCBR) {
|
||||
Device->DrawBitmapCBR( Device, Data, Width, Height, Color );
|
||||
} else if (Device->Depth == 1) {
|
||||
// need to do row/col swap and bit-reverse
|
||||
for (int r = 0; r < Height; r++) {
|
||||
uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r;
|
||||
for (int c = Width; --c >= 0;) {
|
||||
*optr++ = BitReverseTable256[*iptr];
|
||||
iptr += Height;
|
||||
}
|
||||
}
|
||||
} else if (Device->Depth == 4) {
|
||||
uint8_t *optr = Device->Framebuffer;
|
||||
int LineLen = Device->Width >> 1;
|
||||
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
|
||||
uint8_t Byte = BitReverseTable256[*Data++];
|
||||
// we need to linearize code to let compiler better optimize
|
||||
if (c & 0x01) {
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
} else {
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
}
|
||||
// end of a column, move to next one
|
||||
if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); }
|
||||
}
|
||||
} else {
|
||||
// don't know bitdepth, use brute-force solution
|
||||
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
|
||||
uint8_t Byte = *Data++;
|
||||
GDS_DrawPixelFast( Device, c, (r << 3) + 7, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
GDS_DrawPixelFast( Device, c, (r << 3) + 6, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
GDS_DrawPixelFast( Device, c, (r << 3) + 5, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
GDS_DrawPixelFast( Device, c, (r << 3) + 4, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
GDS_DrawPixelFast( Device, c, (r << 3) + 3, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
GDS_DrawPixelFast( Device, c, (r << 3) + 2, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
GDS_DrawPixelFast( Device, c, (r << 3) + 1, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
GDS_DrawPixelFast( Device, c, (r << 3) + 0, (Byte & 0x01) * Color );
|
||||
if (++r == Height) { c++; r = 0; }
|
||||
}
|
||||
/* for better understanding, here is the mundane version
|
||||
for (int x = 0; x < Width; x++) {
|
||||
for (int y = 0; y < Height; y++) {
|
||||
uint8_t Byte = *Data++;
|
||||
GDS_DrawPixel4Fast( Device, x, y * 8 + 0, ((Byte >> 7) & 0x01) * Color );
|
||||
GDS_DrawPixel4Fast( Device, x, y * 8 + 1, ((Byte >> 6) & 0x01) * Color );
|
||||
GDS_DrawPixel4Fast( Device, x, y * 8 + 2, ((Byte >> 5) & 0x01) * Color );
|
||||
GDS_DrawPixel4Fast( Device, x, y * 8 + 3, ((Byte >> 4) & 0x01) * Color );
|
||||
GDS_DrawPixel4Fast( Device, x, y * 8 + 4, ((Byte >> 3) & 0x01) * Color );
|
||||
GDS_DrawPixel4Fast( Device, x, y * 8 + 5, ((Byte >> 2) & 0x01) * Color );
|
||||
GDS_DrawPixel4Fast( Device, x, y * 8 + 6, ((Byte >> 1) & 0x01) * Color );
|
||||
GDS_DrawPixel4Fast( Device, x, y * 8 + 7, ((Byte >> 0) & 0x01) * Color );
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Device->Dirty = true;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef _GDS_DRAW_H_
|
||||
#define _GDS_DRAW_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef _GDS_PRIVATE_H_
|
||||
void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color );
|
||||
void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int X, int Y, int Color );
|
||||
#endif
|
||||
void GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Color );
|
||||
void GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color );
|
||||
void GDS_DrawLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color );
|
||||
void GDS_DrawBox( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color, bool Fill );
|
||||
|
||||
// draw a bitmap with source 1-bit depth organized in column and col0 = bit7 of byte 0
|
||||
void GDS_DrawBitmapCBR( struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
#ifndef _GDS_ERR_H_
|
||||
#define _GDS_ERR_H_
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#define GDS_DoAbort( )
|
||||
|
||||
#if ! defined NullCheck
|
||||
#define NullCheck( ptr, retexpr ) { \
|
||||
if ( ptr == NULL ) { \
|
||||
ESP_LOGE( __FUNCTION__, "%s == NULL", #ptr ); \
|
||||
GDS_DoAbort( ); \
|
||||
retexpr; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ! defined ESP_ERROR_CHECK_NONFATAL
|
||||
#define ESP_ERROR_CHECK_NONFATAL( expr, retexpr ) { \
|
||||
esp_err_t __err_rc = ( expr ); \
|
||||
if ( __err_rc != ESP_OK ) { \
|
||||
ESP_LOGE( __FUNCTION__, "%s != ESP_OK, result: %d", #expr, __err_rc ); \
|
||||
GDS_DoAbort( ); \
|
||||
retexpr; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ! defined CheckBounds
|
||||
#define CheckBounds( expr, retexpr ) { \
|
||||
if ( expr ) { \
|
||||
ESP_LOGE( __FUNCTION__, "Line %d: %s", __LINE__, #expr ); \
|
||||
GDS_DoAbort( ); \
|
||||
retexpr; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,270 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include "gds_private.h"
|
||||
#include "gds.h"
|
||||
#include "gds_font.h"
|
||||
#include "gds_draw.h"
|
||||
#include "gds_err.h"
|
||||
|
||||
static int RoundUpFontHeight( const struct GDS_FontDef* Font ) {
|
||||
int Height = Font->Height;
|
||||
|
||||
if ( ( Height % 8 ) != 0 ) {
|
||||
return ( ( Height + 7 ) / 8 ) * 8;
|
||||
}
|
||||
|
||||
return Height;
|
||||
}
|
||||
|
||||
static const uint8_t* GetCharPtr( const struct GDS_FontDef* Font, char Character ) {
|
||||
return &Font->FontData[ ( Character - Font->StartChar ) * ( ( Font->Width * ( RoundUpFontHeight( Font ) / 8 ) ) + 1 ) ];
|
||||
}
|
||||
|
||||
void GDS_FontDrawChar( struct GDS_Device* Device, char Character, int x, int y, int Color ) {
|
||||
const uint8_t* GlyphData = NULL;
|
||||
int GlyphColumnLen = 0;
|
||||
int CharStartX = 0;
|
||||
int CharStartY = 0;
|
||||
int CharWidth = 0;
|
||||
int CharHeight = 0;
|
||||
int CharEndX = 0;
|
||||
int CharEndY = 0;
|
||||
int OffsetX = 0;
|
||||
int OffsetY = 0;
|
||||
int YByte = 0;
|
||||
int YBit = 0;
|
||||
int i = 0;
|
||||
|
||||
NullCheck( ( GlyphData = GetCharPtr( Device->Font, Character ) ), return );
|
||||
|
||||
if ( Character >= Device->Font->StartChar && Character <= Device->Font->EndChar ) {
|
||||
/* The first byte in the glyph data is the width of the character in pixels, skip over */
|
||||
GlyphData++;
|
||||
GlyphColumnLen = RoundUpFontHeight( Device->Font ) / 8;
|
||||
|
||||
CharWidth = GDS_FontGetCharWidth( Device, Character );
|
||||
CharHeight = GDS_FontGetHeight( Device );
|
||||
|
||||
CharStartX = x;
|
||||
CharStartY = y;
|
||||
|
||||
CharEndX = CharStartX + CharWidth;
|
||||
CharEndY = CharStartY + CharHeight;
|
||||
|
||||
/* If the character is partially offscreen offset the end by
|
||||
* distance between (coord) and 0.
|
||||
*/
|
||||
OffsetX = ( CharStartX < 0 ) ? abs( CharStartX ) : 0;
|
||||
OffsetY = ( CharStartY < 0 ) ? abs( CharStartY ) : 0;
|
||||
|
||||
/* This skips into the proper column within the glyph data */
|
||||
GlyphData+= ( OffsetX * GlyphColumnLen );
|
||||
|
||||
CharStartX+= OffsetX;
|
||||
CharStartY+= OffsetY;
|
||||
|
||||
/* Do not attempt to draw if this character is entirely offscreen */
|
||||
if ( CharEndX < 0 || CharStartX >= Device->Width || CharEndY < 0 || CharStartY >= Device->Height ) {
|
||||
ClipDebug( x, y );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do not attempt to draw past the end of the screen */
|
||||
CharEndX = ( CharEndX >= Device->Width ) ? Device->Width - 1 : CharEndX;
|
||||
CharEndY = ( CharEndY >= Device->Height ) ? Device->Height - 1 : CharEndY;
|
||||
Device->Dirty = true;
|
||||
|
||||
for ( x = CharStartX; x < CharEndX; x++ ) {
|
||||
for ( y = CharStartY, i = 0; y < CharEndY && i < CharHeight; y++, i++ ) {
|
||||
YByte = ( i + OffsetY ) / 8;
|
||||
YBit = ( i + OffsetY ) & 0x07;
|
||||
|
||||
if ( GlyphData[ YByte ] & BIT( YBit ) ) {
|
||||
GDS_DrawPixel( Device, x, y, Color );
|
||||
}
|
||||
}
|
||||
|
||||
GlyphData+= GlyphColumnLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font ) {
|
||||
Display->FontForceProportional = false;
|
||||
Display->FontForceMonospace = false;
|
||||
Display->Font = Font;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GDS_FontForceProportional( struct GDS_Device* Display, bool Force ) {
|
||||
Display->FontForceProportional = Force;
|
||||
}
|
||||
|
||||
void GDS_FontForceMonospace( struct GDS_Device* Display, bool Force ) {
|
||||
Display->FontForceMonospace = Force;
|
||||
}
|
||||
|
||||
int GDS_FontGetWidth( struct GDS_Device* Display ) {
|
||||
return Display->Font->Width;
|
||||
}
|
||||
|
||||
int GDS_FontGetHeight( struct GDS_Device* Display ) {
|
||||
return Display->Font->Height;
|
||||
}
|
||||
|
||||
int GDS_FontGetCharWidth( struct GDS_Device* Display, char Character ) {
|
||||
const uint8_t* CharPtr = NULL;
|
||||
int Width = 0;
|
||||
|
||||
if ( Character >= Display->Font->StartChar && Character <= Display->Font->EndChar ) {
|
||||
CharPtr = GetCharPtr( Display->Font, Character );
|
||||
|
||||
Width = ( Display->Font->Monospace == true ) ? Display->Font->Width : *CharPtr;
|
||||
|
||||
if ( Display->FontForceMonospace == true ) {
|
||||
Width = Display->Font->Width;
|
||||
}
|
||||
|
||||
if ( Display->FontForceProportional == true ) {
|
||||
Width = *CharPtr;
|
||||
}
|
||||
}
|
||||
|
||||
return Width;
|
||||
}
|
||||
|
||||
int GDS_FontGetMaxCharsPerRow( struct GDS_Device* Display ) {
|
||||
return Display->Width / Display->Font->Width;
|
||||
}
|
||||
|
||||
int GDS_FontGetMaxCharsPerColumn( struct GDS_Device* Display ) {
|
||||
return Display->Height / Display->Font->Height;
|
||||
}
|
||||
|
||||
int GDS_FontGetCharHeight( struct GDS_Device* Display ) {
|
||||
return Display->Font->Height;
|
||||
}
|
||||
|
||||
int GDS_FontMeasureString( struct GDS_Device* Display, const char* Text ) {
|
||||
int Width = 0;
|
||||
int Len = 0;
|
||||
|
||||
NullCheck( Text, return 0 );
|
||||
|
||||
for ( Len = strlen( Text ); Len >= 0; Len--, Text++ ) {
|
||||
if ( *Text >= Display->Font->StartChar && *Text <= Display->Font->EndChar ) {
|
||||
Width+= GDS_FontGetCharWidth( Display, *Text );
|
||||
}
|
||||
}
|
||||
|
||||
return Width;
|
||||
}
|
||||
|
||||
void GDS_FontDrawString( struct GDS_Device* Display, int x, int y, const char* Text, int Color ) {
|
||||
int Len = 0;
|
||||
int i = 0;
|
||||
|
||||
NullCheck( Text, return );
|
||||
|
||||
for ( Len = strlen( Text ), i = 0; i < Len; i++ ) {
|
||||
GDS_FontDrawChar( Display, *Text, x, y, Color );
|
||||
|
||||
x+= GDS_FontGetCharWidth( Display, *Text );
|
||||
Text++;
|
||||
}
|
||||
}
|
||||
|
||||
void GDS_FontDrawAnchoredString( struct GDS_Device* Display, TextAnchor Anchor, const char* Text, int Color ) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
NullCheck( Text, return );
|
||||
|
||||
GDS_FontGetAnchoredStringCoords( Display, &x, &y, Anchor, Text );
|
||||
GDS_FontDrawString( Display, x, y, Text, Color );
|
||||
}
|
||||
|
||||
void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text ) {
|
||||
int StringWidth = 0;
|
||||
int StringHeight = 0;
|
||||
|
||||
NullCheck( OutX, return );
|
||||
NullCheck( OutY, return );
|
||||
NullCheck( Text, return );
|
||||
|
||||
StringWidth = GDS_FontMeasureString( Display, Text );
|
||||
StringHeight = GDS_FontGetCharHeight( Display );
|
||||
|
||||
switch ( Anchor ) {
|
||||
case TextAnchor_East: {
|
||||
*OutY = ( Display->Height / 2 ) - ( StringHeight / 2 );
|
||||
*OutX = ( Display->Width - StringWidth );
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_West: {
|
||||
*OutY = ( Display->Height / 2 ) - ( StringHeight / 2 );
|
||||
*OutX = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_North: {
|
||||
*OutX = ( Display->Width / 2 ) - ( StringWidth / 2 );
|
||||
*OutY = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_South: {
|
||||
*OutX = ( Display->Width / 2 ) - ( StringWidth / 2 );
|
||||
*OutY = ( Display->Height - StringHeight );
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_NorthEast: {
|
||||
*OutX = ( Display->Width - StringWidth );
|
||||
*OutY = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_NorthWest: {
|
||||
*OutY = 0;
|
||||
*OutX = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_SouthEast: {
|
||||
*OutY = ( Display->Height - StringHeight );
|
||||
*OutX = ( Display->Width - StringWidth );
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_SouthWest: {
|
||||
*OutY = ( Display->Height - StringHeight );
|
||||
*OutX = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_Center: {
|
||||
*OutY = ( Display->Height / 2 ) - ( StringHeight / 2 );
|
||||
*OutX = ( Display->Width / 2 ) - ( StringWidth / 2 );
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
*OutX = 128;
|
||||
*OutY = 64;
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
#ifndef _GDS_FONT_H_
|
||||
#define _GDS_FONT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct GDS_Device;
|
||||
|
||||
/*
|
||||
* X-GLCD Font format:
|
||||
*
|
||||
* First byte of glyph is it's width in pixels.
|
||||
* Each data byte represents 8 pixels going down from top to bottom.
|
||||
*
|
||||
* Example glyph layout for a 16x16 font
|
||||
* 'a': [Glyph width][Pixel column 0][Pixel column 1] where the number of pixel columns is the font height divided by 8
|
||||
* 'b': [Glyph width][Pixel column 0][Pixel column 1]...
|
||||
* 'c': And so on...
|
||||
*/
|
||||
|
||||
struct GDS_FontDef {
|
||||
const uint8_t* FontData;
|
||||
|
||||
int Width;
|
||||
int Height;
|
||||
|
||||
int StartChar;
|
||||
int EndChar;
|
||||
|
||||
bool Monospace;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TextAnchor_East = 0,
|
||||
TextAnchor_West,
|
||||
TextAnchor_North,
|
||||
TextAnchor_South,
|
||||
TextAnchor_NorthEast,
|
||||
TextAnchor_NorthWest,
|
||||
TextAnchor_SouthEast,
|
||||
TextAnchor_SouthWest,
|
||||
TextAnchor_Center
|
||||
} TextAnchor;
|
||||
|
||||
bool GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font );
|
||||
|
||||
void GDS_FontForceProportional( struct GDS_Device* Display, bool Force );
|
||||
void GDS_FontForceMonospace( struct GDS_Device* Display, bool Force );
|
||||
|
||||
int GDS_FontGetWidth( struct GDS_Device* Display );
|
||||
int GDS_FontGetHeight( struct GDS_Device* Display );
|
||||
|
||||
int GDS_FontGetMaxCharsPerRow( struct GDS_Device* Display );
|
||||
int GDS_FontGetMaxCharsPerColumn( struct GDS_Device* Display );
|
||||
|
||||
int GDS_FontGetCharWidth( struct GDS_Device* Display, char Character );
|
||||
int GDS_FontGetCharHeight( struct GDS_Device* Display );
|
||||
int GDS_FontMeasureString( struct GDS_Device* Display, const char* Text );\
|
||||
|
||||
void GDS_FontDrawChar( struct GDS_Device* Display, char Character, int x, int y, int Color );
|
||||
void GDS_FontDrawString( struct GDS_Device* Display, int x, int y, const char* Text, int Color );
|
||||
void GDS_FontDrawAnchoredString( struct GDS_Device* Display, TextAnchor Anchor, const char* Text, int Color );
|
||||
void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text );
|
||||
|
||||
extern const struct GDS_FontDef Font_droid_sans_fallback_11x13;
|
||||
extern const struct GDS_FontDef Font_droid_sans_fallback_15x17;
|
||||
extern const struct GDS_FontDef Font_droid_sans_fallback_24x28;
|
||||
|
||||
extern const struct GDS_FontDef Font_droid_sans_mono_7x13;
|
||||
extern const struct GDS_FontDef Font_droid_sans_mono_13x24;
|
||||
extern const struct GDS_FontDef Font_droid_sans_mono_16x31;
|
||||
|
||||
extern const struct GDS_FontDef Font_liberation_mono_9x15;
|
||||
extern const struct GDS_FontDef Font_liberation_mono_13x21;
|
||||
extern const struct GDS_FontDef Font_liberation_mono_17x30;
|
||||
|
||||
extern const struct GDS_FontDef Font_Tarable7Seg_16x32;
|
||||
extern const struct GDS_FontDef Font_Tarable7Seg_32x64;
|
||||
|
||||
extern const struct GDS_FontDef Font_line_1;
|
||||
extern const struct GDS_FontDef Font_line_2;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,228 +0,0 @@
|
||||
#include <string.h>
|
||||
#include "math.h"
|
||||
#include "esp32/rom/tjpgd.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "gds.h"
|
||||
#include "gds_private.h"
|
||||
#include "gds_image.h"
|
||||
|
||||
const char TAG[] = "ImageDec";
|
||||
|
||||
#define SCRATCH_SIZE 3100
|
||||
|
||||
//Data that is passed from the decoder function to the infunc/outfunc functions.
|
||||
typedef struct {
|
||||
const unsigned char *InData; // Pointer to jpeg data
|
||||
int InPos; // Current position in jpeg data
|
||||
int Width, Height;
|
||||
union {
|
||||
uint16_t *OutData; // Decompress
|
||||
struct { // DirectDraw
|
||||
struct GDS_Device * Device;
|
||||
int XOfs, YOfs;
|
||||
int Depth;
|
||||
};
|
||||
};
|
||||
} JpegCtx;
|
||||
|
||||
static unsigned InHandler(JDEC *Decoder, uint8_t *Buf, unsigned Len) {
|
||||
JpegCtx *Context = (JpegCtx*) Decoder->device;
|
||||
if (Buf) memcpy(Buf, Context->InData + Context->InPos, Len);
|
||||
Context->InPos += Len;
|
||||
return Len;
|
||||
}
|
||||
|
||||
static unsigned OutHandler(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
|
||||
JpegCtx *Context = (JpegCtx*) Decoder->device;
|
||||
uint8_t *Pixels = (uint8_t*) Bitmap;
|
||||
|
||||
for (int y = Frame->top; y <= Frame->bottom; y++) {
|
||||
if (y < Context->YOfs) continue;
|
||||
for (int x = Frame->left; x <= Frame->right; x++) {
|
||||
if (x < Context->XOfs) continue;
|
||||
// Convert the 888 to RGB565
|
||||
uint16_t Value = (*Pixels++ & ~0x07) << 8;
|
||||
Value |= (*Pixels++ & ~0x03) << 3;
|
||||
Value |= *Pixels++ >> 3;
|
||||
Context->OutData[Context->Width * y + x] = Value;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
|
||||
JpegCtx *Context = (JpegCtx*) Decoder->device;
|
||||
uint8_t *Pixels = (uint8_t*) Bitmap;
|
||||
int Shift = 8 - Context->Depth;
|
||||
|
||||
for (int y = Frame->top; y <= Frame->bottom; y++) {
|
||||
for (int x = Frame->left; x <= Frame->right; x++) {
|
||||
// Convert the 888 to RGB565
|
||||
int Value = ((Pixels[0]*11 + Pixels[1]*59 + Pixels[2]*30) / 100) >> Shift;
|
||||
Pixels += 3;
|
||||
// used DrawPixel and not "fast" version as X,Y may be beyond screen
|
||||
GDS_DrawPixel( Context->Device, Context->XOfs + x, Context->YOfs + y, Value);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//Decode the embedded image into pixel lines that can be used with the rest of the logic.
|
||||
static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, bool SizeOnly) {
|
||||
JDEC Decoder;
|
||||
JpegCtx Context;
|
||||
char *Scratch = calloc(SCRATCH_SIZE, 1);
|
||||
|
||||
if (!Scratch) {
|
||||
ESP_LOGE(TAG, "Cannot allocate workspace");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Context.OutData = NULL;
|
||||
Context.InData = Source;
|
||||
Context.InPos = 0;
|
||||
|
||||
//Prepare and decode the jpeg.
|
||||
int Res = jd_prepare(&Decoder, InHandler, Scratch, SCRATCH_SIZE, (void*) &Context);
|
||||
if (Width) *Width = Decoder.width;
|
||||
if (Height) *Height = Decoder.height;
|
||||
Decoder.scale = Scale;
|
||||
|
||||
if (Res == JDR_OK && !SizeOnly) {
|
||||
// find the scaling factor
|
||||
Context.OutData = malloc(Decoder.width * Decoder.height * sizeof(uint16_t));
|
||||
uint8_t N = 0, ScaleInt = ceil(1.0 / Scale);
|
||||
ScaleInt--; ScaleInt |= ScaleInt >> 1; ScaleInt |= ScaleInt >> 2; ScaleInt++;
|
||||
while (ScaleInt >>= 1) N++;
|
||||
|
||||
// ready to decode
|
||||
if (Context.OutData) {
|
||||
Context.Width = Decoder.width / (1 << N);
|
||||
Context.Height = Decoder.height / (1 << N);
|
||||
if (Width) *Width = Context.Width;
|
||||
if (Height) *Height = Context.Height;
|
||||
Res = jd_decomp(&Decoder, OutHandler, N > 3 ? 3 : N);
|
||||
if (Res != JDR_OK) {
|
||||
ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Can't allocate bitmap %dx%d", Decoder.width, Decoder.height);
|
||||
}
|
||||
} else if (!SizeOnly) {
|
||||
ESP_LOGE(TAG, "Image decoder: jd_prepare failed (%d)", Res);
|
||||
}
|
||||
|
||||
// free scratch area
|
||||
if (Scratch) free(Scratch);
|
||||
return Context.OutData;
|
||||
}
|
||||
|
||||
uint16_t* GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale) {
|
||||
return DecodeJPEG(Source, Width, Height, Scale, false);
|
||||
}
|
||||
|
||||
void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height) {
|
||||
DecodeJPEG(Source, Width, Height, 1, true);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Simply draw a RGB565 image
|
||||
* monoschrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
|
||||
* grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
|
||||
*/
|
||||
void GDS_DrawRGB16( struct GDS_Device* Device, uint16_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
|
||||
if (Device->DrawRGB16) {
|
||||
Device->DrawRGB16( Device, x, y, Width, Height, RGB_Mode, Image );
|
||||
} else {
|
||||
int Scale = Device->Depth < 5 ? 5 - Device->Depth : 0;
|
||||
switch(RGB_Mode) {
|
||||
case GDS_RGB565:
|
||||
for (int c = 0; c < Width; c++) {
|
||||
for (int r = 0; r < Height; r++) {
|
||||
int pixel = Image[Width*r + c];
|
||||
pixel = ((pixel & 0x1f) * 11 + ((((pixel >> 5) & 0x3f) * 59) >> 1) + (pixel >> 11) * 30) / 100;
|
||||
GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GDS_RGB555:
|
||||
for (int c = 0; c < Width; c++) {
|
||||
for (int r = 0; r < Height; r++) {
|
||||
int pixel = Image[Width*r + c];
|
||||
pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
|
||||
GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GDS_RGB444:
|
||||
for (int c = 0; c < Width; c++) {
|
||||
for (int r = 0; r < Height; r++) {
|
||||
int pixel = Image[Width*r + c];
|
||||
pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
|
||||
GDS_DrawPixel( Device, c + x, r + y, pixel >> (Scale - 1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Decode the embedded image into pixel lines that can be used with the rest of the logic.
|
||||
bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit) {
|
||||
JDEC Decoder;
|
||||
JpegCtx Context;
|
||||
bool Ret = false;
|
||||
char *Scratch = calloc(SCRATCH_SIZE, 1);
|
||||
|
||||
if (!Scratch) {
|
||||
ESP_LOGE(TAG, "Cannot allocate workspace");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Populate fields of the JpegCtx struct.
|
||||
Context.InData = Source;
|
||||
Context.InPos = 0;
|
||||
Context.XOfs = x;
|
||||
Context.YOfs = y;
|
||||
Context.Device = Device;
|
||||
Context.Depth = Device->Depth;
|
||||
|
||||
//Prepare and decode the jpeg.
|
||||
int Res = jd_prepare(&Decoder, InHandler, Scratch, SCRATCH_SIZE, (void*) &Context);
|
||||
Context.Width = Decoder.width;
|
||||
Context.Height = Decoder.height;
|
||||
|
||||
if (Res == JDR_OK) {
|
||||
uint8_t N = 0;
|
||||
|
||||
// do we need to fit the image
|
||||
if (Fit & GDS_IMAGE_FIT) {
|
||||
float XRatio = (Device->Width - x) / (float) Decoder.width, YRatio = (Device->Height - y) / (float) Decoder.height;
|
||||
uint8_t Ratio = XRatio < YRatio ? ceil(1/XRatio) : ceil(1/YRatio);
|
||||
Ratio--; Ratio |= Ratio >> 1; Ratio |= Ratio >> 2; Ratio++;
|
||||
while (Ratio >>= 1) N++;
|
||||
Context.Width /= 1 << N;
|
||||
Context.Height /= 1 << N;
|
||||
}
|
||||
|
||||
// then place it
|
||||
if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = x + ((Device->Width - x) - Context.Width) / 2;
|
||||
if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = y + ((Device->Height - y) - Context.Height) / 2;
|
||||
|
||||
// do decompress & draw
|
||||
Res = jd_decomp(&Decoder, OutHandlerDirect, N > 3 ? 3 : N);
|
||||
if (Res == JDR_OK) {
|
||||
Ret = true;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Image decoder: jd_prepare failed (%d)", Res);
|
||||
}
|
||||
|
||||
// free scratch area
|
||||
if (Scratch) free(Scratch);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
// no progressive JPEG handling
|
||||
|
||||
struct GDS_Device;
|
||||
|
||||
enum { GDS_RGB565, GDS_RGB555, GDS_RGB444 };
|
||||
|
||||
#define GDS_IMAGE_TOP 0x00
|
||||
#define GDS_IMAGE_CENTER_X 0x01
|
||||
#define GDS_IMAGE_CENTER_Y 0x02
|
||||
#define GDS_IMAGE_CENTER (GDS_IMAGE_CENTER_X | GDS_IMAGE_CENTER_Y)
|
||||
#define GDS_IMAGE_FIT 0x10
|
||||
|
||||
// Width and Height can be NULL if you already know them (actual scaling is closest ^2)
|
||||
uint16_t* GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale);
|
||||
void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height);
|
||||
void GDS_DrawRGB16( struct GDS_Device* Device, uint16_t *Image, int x, int y, int Width, int Height, int RGB_Mode );
|
||||
// set DisplayWidth and DisplayHeight to non-zero if you want autoscale to closest factor ^2 from 0..3
|
||||
bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit);
|
||||
@@ -1,178 +0,0 @@
|
||||
#ifndef _GDS_PRIVATE_H_
|
||||
#define _GDS_PRIVATE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
#include "gds.h"
|
||||
#include "gds_err.h"
|
||||
|
||||
#define GDS_CLIPDEBUG_NONE 0
|
||||
#define GDS_CLIPDEBUG_WARNING 1
|
||||
#define GDS_CLIPDEBUG_ERROR 2
|
||||
|
||||
#if CONFIG_GDS_CLIPDEBUG == GDS_CLIPDEBUG_NONE
|
||||
/*
|
||||
* Clip silently with no console output.
|
||||
*/
|
||||
#define ClipDebug( x, y )
|
||||
#elif CONFIG_GDS_CLIPDEBUG == GDS_CLIPDEBUG_WARNING
|
||||
/*
|
||||
* Log clipping to the console as a warning.
|
||||
*/
|
||||
#define ClipDebug( x, y ) { \
|
||||
ESP_LOGW( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED", __LINE__, x, y ); \
|
||||
}
|
||||
#elif CONFIG_GDS_CLIPDEBUG == GDS_CLIPDEBUG_ERROR
|
||||
/*
|
||||
* Log clipping as an error to the console.
|
||||
* Also invokes an abort with stack trace.
|
||||
*/
|
||||
#define ClipDebug( x, y ) { \
|
||||
ESP_LOGE( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED, ABORT", __LINE__, x, y ); \
|
||||
abort( ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define GDS_ALWAYS_INLINE __attribute__( ( always_inline ) )
|
||||
|
||||
#define MAX_LINES 8
|
||||
|
||||
#if ! defined BIT
|
||||
#define BIT( n ) ( 1 << ( n ) )
|
||||
#endif
|
||||
|
||||
struct GDS_Device;
|
||||
struct GDS_FontDef;
|
||||
|
||||
/*
|
||||
* These can optionally return a succeed/fail but are as of yet unused in the driver.
|
||||
*/
|
||||
typedef bool ( *WriteCommandProc ) ( struct GDS_Device* Device, uint8_t Command );
|
||||
typedef bool ( *WriteDataProc ) ( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
|
||||
|
||||
struct spi_device_t;
|
||||
typedef struct spi_device_t* spi_device_handle_t;
|
||||
|
||||
#define IF_SPI 0
|
||||
#define IF_I2C 1
|
||||
|
||||
struct GDS_Device {
|
||||
uint8_t IF;
|
||||
union {
|
||||
// I2C Specific
|
||||
struct {
|
||||
uint8_t Address;
|
||||
};
|
||||
// SPI specific
|
||||
struct {
|
||||
spi_device_handle_t SPIHandle;
|
||||
int8_t RSTPin;
|
||||
int8_t CSPin;
|
||||
};
|
||||
};
|
||||
|
||||
// cooked text mode
|
||||
struct {
|
||||
int16_t Y, Space;
|
||||
const struct GDS_FontDef* Font;
|
||||
} Lines[MAX_LINES];
|
||||
|
||||
uint16_t Width;
|
||||
uint16_t Height;
|
||||
uint8_t Depth;
|
||||
|
||||
uint8_t* Framebuffer, *Shadowbuffer;
|
||||
uint16_t FramebufferSize;
|
||||
bool Dirty;
|
||||
|
||||
// default fonts when using direct draw
|
||||
const struct GDS_FontDef* Font;
|
||||
bool FontForceProportional;
|
||||
bool FontForceMonospace;
|
||||
|
||||
// various driver-specific method
|
||||
// must always provide
|
||||
bool (*Init)( struct GDS_Device* Device);
|
||||
void (*SetContrast)( struct GDS_Device* Device, uint8_t Contrast );
|
||||
void (*DisplayOn)( struct GDS_Device* Device );
|
||||
void (*DisplayOff)( struct GDS_Device* Device );
|
||||
void (*SetHFlip)( struct GDS_Device* Device, bool On );
|
||||
void (*SetVFlip)( struct GDS_Device* Device, bool On );
|
||||
void (*Update)( struct GDS_Device* Device );
|
||||
// must provide for depth other than 1 (vertical) and 4 (may provide for optimization)
|
||||
void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color );
|
||||
void (*DrawBitmapCBR)(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color );
|
||||
// may provide for optimization
|
||||
void (*DrawRGB16)( struct GDS_Device* Device, int x, int y, int Width, int Height, int RGB_Mode, uint16_t *Image );
|
||||
void (*ClearWindow)( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );
|
||||
|
||||
// interface-specific methods
|
||||
WriteCommandProc WriteCommand;
|
||||
WriteDataProc WriteData;
|
||||
|
||||
// 16 bytes for whatever the driver wants (should be aligned as it's 32 bits)
|
||||
uint32_t Private[4];
|
||||
};
|
||||
|
||||
bool GDS_Reset( struct GDS_Device* Device );
|
||||
|
||||
inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) {
|
||||
bool Result = (
|
||||
( x >= 0 ) &&
|
||||
( x < Device->Width ) &&
|
||||
( y >= 0 ) &&
|
||||
( y < Device->Height )
|
||||
) ? true : false;
|
||||
|
||||
#if CONFIG_GDS_CLIPDEBUG > 0
|
||||
if ( Result == false ) {
|
||||
ClipDebug( x, y );
|
||||
}
|
||||
#endif
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint32_t YBit = ( Y & 0x07 );
|
||||
uint8_t* FBOffset = NULL;
|
||||
|
||||
/*
|
||||
* We only need to modify the Y coordinate since the pitch
|
||||
* of the screen is the same as the width.
|
||||
* Dividing Y by 8 gives us which row the pixel is in but not
|
||||
* the bit position.
|
||||
*/
|
||||
Y>>= 3;
|
||||
|
||||
FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
|
||||
|
||||
if ( Color == GDS_COLOR_XOR ) {
|
||||
*FBOffset ^= BIT( YBit );
|
||||
} else {
|
||||
*FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
|
||||
}
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint8_t* FBOffset;
|
||||
|
||||
FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
|
||||
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | (Color << 4) : ((*FBOffset & 0xf0) | Color);
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
if (Device->DrawPixelFast) Device->DrawPixelFast( Device, X, Y, Color );
|
||||
else if (Device->Depth == 4) GDS_DrawPixel4Fast( Device, X, Y, Color);
|
||||
else if (Device->Depth == 1) GDS_DrawPixel1Fast( Device, X, Y, Color);
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int x, int y, int Color ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
GDS_DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* (c) 2004,2006 Richard Titmuss for SlimProtoLib
|
||||
* (c) Philippe G. 2019, philippe_44@outlook.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "gds_private.h"
|
||||
#include "gds.h"
|
||||
#include "gds_draw.h"
|
||||
#include "gds_text.h"
|
||||
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
static char TAG[] = "gds";
|
||||
|
||||
/****************************************************************************************
|
||||
* Set fonts for each line in text mode
|
||||
*/
|
||||
static const struct GDS_FontDef *GuessFont( struct GDS_Device *Device, int FontType) {
|
||||
switch(FontType) {
|
||||
case GDS_FONT_LINE_1:
|
||||
return &Font_line_1;
|
||||
case GDS_FONT_LINE_2:
|
||||
return &Font_line_2;
|
||||
case GDS_FONT_SMALL:
|
||||
return &Font_droid_sans_fallback_11x13;
|
||||
case GDS_FONT_MEDIUM:
|
||||
default:
|
||||
return &Font_droid_sans_fallback_15x17;
|
||||
case GDS_FONT_LARGE:
|
||||
return &Font_droid_sans_fallback_24x28;
|
||||
break;
|
||||
case GDS_FONT_SEGMENT:
|
||||
if (Device->Height == 32) return &Font_Tarable7Seg_16x32;
|
||||
else return &Font_Tarable7Seg_32x64;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************************
|
||||
* Set fonts for each line in text mode
|
||||
*/
|
||||
bool GDS_TextSetFontAuto(struct GDS_Device* Device, int N, int FontType, int Space) {
|
||||
const struct GDS_FontDef *Font = GuessFont( Device, FontType );
|
||||
return GDS_TextSetFont( Device, N, Font, Space );
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Set fonts for each line in text mode
|
||||
*/
|
||||
bool GDS_TextSetFont(struct GDS_Device* Device, int N, const struct GDS_FontDef *Font, int Space) {
|
||||
if (--N >= MAX_LINES) return false;
|
||||
|
||||
Device->Lines[N].Font = Font;
|
||||
|
||||
// re-calculate lines absolute position
|
||||
Device->Lines[N].Space = Space;
|
||||
Device->Lines[0].Y = Device->Lines[0].Space;
|
||||
for (int i = 1; i <= N; i++) Device->Lines[i].Y = Device->Lines[i-1].Y + Device->Lines[i-1].Font->Height + Device->Lines[i].Space;
|
||||
|
||||
ESP_LOGI(TAG, "Adding line %u at %d (height:%u)", N + 1, Device->Lines[N].Y, Device->Lines[N].Font->Height);
|
||||
|
||||
if (Device->Lines[N].Y + Device->Lines[N].Font->Height > Device->Height) {
|
||||
ESP_LOGW(TAG, "line does not fit display");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Text) {
|
||||
int Width, X = Pos;
|
||||
|
||||
// counting 1..n
|
||||
N--;
|
||||
|
||||
GDS_SetFont( Device, Device->Lines[N].Font );
|
||||
if (Attr & GDS_TEXT_MONOSPACE) GDS_FontForceMonospace( Device, true );
|
||||
|
||||
Width = GDS_FontMeasureString( Device, Text );
|
||||
|
||||
// adjusting position, erase only EoL for rigth-justified
|
||||
if (Pos == GDS_TEXT_RIGHT) X = Device->Width - Width - 1;
|
||||
else if (Pos == GDS_TEXT_CENTER) X = (Device->Width - Width) / 2;
|
||||
|
||||
// erase if requested
|
||||
if (Attr & GDS_TEXT_CLEAR) {
|
||||
int Y_min = max(0, Device->Lines[N].Y), Y_max = max(0, Device->Lines[N].Y + Device->Lines[N].Font->Height);
|
||||
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->Width; c++)
|
||||
for (int y = Y_min; y < Y_max; y++)
|
||||
GDS_DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
|
||||
}
|
||||
|
||||
GDS_FontDrawString( Device, X, Device->Lines[N].Y, Text, GDS_COLOR_WHITE );
|
||||
|
||||
ESP_LOGD(TAG, "displaying %s line %u (x:%d, attr:%u)", Text, N+1, X, Attr);
|
||||
|
||||
// update whole display if requested
|
||||
Device->Dirty = true;
|
||||
if (Attr & GDS_TEXT_UPDATE) GDS_Update( Device );
|
||||
|
||||
return Width + X < Device->Width;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Try to align string for better scrolling visual. there is probably much better to do
|
||||
*/
|
||||
int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max) {
|
||||
char Space[] = " ";
|
||||
int Len = strlen(String), Extra = 0, Boundary;
|
||||
|
||||
N--;
|
||||
|
||||
// we might already fit
|
||||
GDS_SetFont( Device, Device->Lines[N].Font );
|
||||
if (GDS_FontMeasureString( Device, String ) <= Device->Width) return 0;
|
||||
|
||||
// add some space for better visual
|
||||
strncat(String, Space, Max-Len);
|
||||
String[Max] = '\0';
|
||||
Len = strlen(String);
|
||||
|
||||
// mark the end of the extended string
|
||||
Boundary = GDS_FontMeasureString( Device, String );
|
||||
|
||||
// add a full display width
|
||||
while (Len < Max && GDS_FontMeasureString( Device, String ) - Boundary < Device->Width) {
|
||||
String[Len++] = String[Extra++];
|
||||
String[Len] = '\0';
|
||||
}
|
||||
|
||||
return Boundary;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
void GDS_TextPos(struct GDS_Device* Device, int FontType, int Where, int Attr, char *Text, ...) {
|
||||
va_list args;
|
||||
|
||||
TextAnchor Anchor = TextAnchor_Center;
|
||||
|
||||
if (Attr & GDS_TEXT_CLEAR) GDS_Clear( Device, GDS_COLOR_BLACK );
|
||||
|
||||
if (!Text) return;
|
||||
|
||||
va_start(args, Text);
|
||||
|
||||
switch(Where) {
|
||||
case GDS_TEXT_TOP_LEFT:
|
||||
default:
|
||||
Anchor = TextAnchor_NorthWest;
|
||||
break;
|
||||
case GDS_TEXT_MIDDLE_LEFT:
|
||||
Anchor = TextAnchor_West;
|
||||
break;
|
||||
case GDS_TEXT_BOTTOM_LEFT:
|
||||
Anchor = TextAnchor_SouthWest;
|
||||
break;
|
||||
case GDS_TEXT_CENTERED:
|
||||
Anchor = TextAnchor_Center;
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Displaying %s at %u with attribute %u", Text, Anchor, Attr);
|
||||
|
||||
GDS_SetFont( Device, GuessFont( Device, FontType ) );
|
||||
GDS_FontDrawAnchoredString( Device, Anchor, Text, GDS_COLOR_WHITE );
|
||||
|
||||
Device->Dirty = true;
|
||||
if (Attr & GDS_TEXT_UPDATE) GDS_Update( Device );
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* (c) Philippe G. 2019, philippe_44@outlook.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gds_font.h"
|
||||
|
||||
#define GDS_TEXT_CLEAR 0x01
|
||||
#define GDS_TEXT_CLEAR_EOL 0x02
|
||||
#define GDS_TEXT_UPDATE 0x04
|
||||
#define GDS_TEXT_MONOSPACE 0x08
|
||||
|
||||
// these ones are for 'Pos' parameter of TextLine
|
||||
#define GDS_TEXT_LEFT 0
|
||||
#define GDS_TEXT_RIGHT 0xff00
|
||||
#define GDS_TEXT_CENTER 0xff01
|
||||
|
||||
// these ones are for the 'Where' parameter of TextPos
|
||||
enum { GDS_TEXT_TOP_LEFT, GDS_TEXT_MIDDLE_LEFT, GDS_TEXT_BOTTOM_LEFT, GDS_TEXT_CENTERED };
|
||||
|
||||
enum { GDS_FONT_DEFAULT, GDS_FONT_LINE_1, GDS_FONT_LINE_2, GDS_FONT_SEGMENT,
|
||||
GDS_FONT_TINY, GDS_FONT_SMALL, GDS_FONT_MEDIUM, GDS_FONT_LARGE, GDS_FONT_FONT_HUGE };
|
||||
|
||||
struct GDS_Device;
|
||||
|
||||
bool GDS_TextSetFontAuto(struct GDS_Device* Device, int N, int FontType, int Space);
|
||||
bool GDS_TextSetFont(struct GDS_Device* Device, int N, const struct GDS_FontDef *Font, int Space);
|
||||
bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Text);
|
||||
int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max);
|
||||
void GDS_TextPos(struct GDS_Device* Device, int FontType, int Where, int Attr, char *Text, ...);
|
||||
@@ -1,127 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/gpio.h>
|
||||
#include "gds.h"
|
||||
#include "gds_err.h"
|
||||
#include "gds_private.h"
|
||||
#include "gds_default_if.h"
|
||||
|
||||
static int I2CPortNumber;
|
||||
static int I2CWait;
|
||||
|
||||
static const int GDS_I2C_COMMAND_MODE = 0x80;
|
||||
static const int GDS_I2C_DATA_MODE = 0x40;
|
||||
|
||||
static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength );
|
||||
static bool I2CDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command );
|
||||
static bool I2CDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
|
||||
|
||||
/*
|
||||
* Initializes the i2c master with the parameters specified
|
||||
* in the component configuration in sdkconfig.h.
|
||||
*
|
||||
* Returns true on successful init of the i2c bus.
|
||||
*/
|
||||
bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int Speed ) {
|
||||
I2CPortNumber = PortNumber;
|
||||
|
||||
I2CWait = pdMS_TO_TICKS( Speed ? Speed / 4000 : 100 );
|
||||
|
||||
if (SDA != -1 && SCL != -1) {
|
||||
i2c_config_t Config = { 0 };
|
||||
|
||||
Config.mode = I2C_MODE_MASTER;
|
||||
Config.sda_io_num = SDA;
|
||||
Config.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
Config.scl_io_num = SCL;
|
||||
Config.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
Config.master.clk_speed = Speed ? Speed : 400000;
|
||||
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_param_config( I2CPortNumber, &Config ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_driver_install( I2CPortNumber, Config.mode, 0, 0, 0 ), return false );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attaches a display to the I2C bus using default communication functions.
|
||||
*
|
||||
* Params:
|
||||
* Device: Pointer to your GDS_Device object
|
||||
* Width: Width of display
|
||||
* Height: Height of display
|
||||
* I2CAddress: Address of your display
|
||||
* RSTPin: Optional GPIO pin to use for hardware reset, if none pass -1 for this parameter.
|
||||
*
|
||||
* Returns true on successful init of display.
|
||||
*/
|
||||
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin ) {
|
||||
NullCheck( Device, return false );
|
||||
|
||||
Device->WriteCommand = I2CDefaultWriteCommand;
|
||||
Device->WriteData = I2CDefaultWriteData;
|
||||
Device->Address = I2CAddress;
|
||||
Device->RSTPin = RSTPin;
|
||||
Device->IF = IF_I2C;
|
||||
Device->Width = Width;
|
||||
Device->Height = Height;
|
||||
|
||||
if ( RSTPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( RSTPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( RSTPin, 1 ), return false );
|
||||
GDS_Reset( Device );
|
||||
}
|
||||
|
||||
return Device->Init( Device );
|
||||
}
|
||||
|
||||
static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength ) {
|
||||
i2c_cmd_handle_t* CommandHandle = NULL;
|
||||
static uint8_t ModeByte = 0;
|
||||
|
||||
NullCheck( Data, return false );
|
||||
|
||||
if ( ( CommandHandle = i2c_cmd_link_create( ) ) != NULL ) {
|
||||
ModeByte = ( IsCommand == true ) ? GDS_I2C_COMMAND_MODE: GDS_I2C_DATA_MODE;
|
||||
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_master_start( CommandHandle ), goto error );
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_master_write_byte( CommandHandle, ( Address << 1 ) | I2C_MASTER_WRITE, true ), goto error );
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_master_write_byte( CommandHandle, ModeByte, true ), goto error );
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_master_write( CommandHandle, ( uint8_t* ) Data, DataLength, true ), goto error );
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_master_stop( CommandHandle ), goto error );
|
||||
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_master_cmd_begin( I2CPortNumber, CommandHandle, I2CWait ), goto error );
|
||||
i2c_cmd_link_delete( CommandHandle );
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
if (CommandHandle) i2c_cmd_link_delete( CommandHandle );
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool I2CDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command ) {
|
||||
uint8_t CommandByte = ( uint8_t ) Command;
|
||||
|
||||
NullCheck( Device, return false );
|
||||
return I2CDefaultWriteBytes( Device->Address, true, ( const uint8_t* ) &CommandByte, 1 );
|
||||
}
|
||||
|
||||
static bool I2CDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength ) {
|
||||
NullCheck( Device, return false );
|
||||
NullCheck( Data, return false );
|
||||
|
||||
return I2CDefaultWriteBytes( Device->Address, false, Data, DataLength );
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <freertos/task.h>
|
||||
#include "gds.h"
|
||||
#include "gds_err.h"
|
||||
#include "gds_private.h"
|
||||
#include "gds_default_if.h"
|
||||
|
||||
static const int GDS_SPI_Command_Mode = 0;
|
||||
static const int GDS_SPI_Data_Mode = 1;
|
||||
|
||||
static spi_host_device_t SPIHost;
|
||||
static int DCPin;
|
||||
|
||||
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength );
|
||||
static bool SPIDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command );
|
||||
static bool SPIDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
|
||||
|
||||
bool GDS_SPIInit( int SPI, int DC ) {
|
||||
SPIHost = SPI;
|
||||
DCPin = DC;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed ) {
|
||||
spi_device_interface_config_t SPIDeviceConfig;
|
||||
spi_device_handle_t SPIDevice;
|
||||
|
||||
NullCheck( Device, return false );
|
||||
|
||||
if (CSPin >= 0) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( CSPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( CSPin, 0 ), return false );
|
||||
}
|
||||
|
||||
memset( &SPIDeviceConfig, 0, sizeof( spi_device_interface_config_t ) );
|
||||
|
||||
SPIDeviceConfig.clock_speed_hz = Speed > 0 ? Speed : SPI_MASTER_FREQ_8M;
|
||||
SPIDeviceConfig.spics_io_num = CSPin;
|
||||
SPIDeviceConfig.queue_size = 1;
|
||||
|
||||
ESP_ERROR_CHECK_NONFATAL( spi_bus_add_device( SPIHost, &SPIDeviceConfig, &SPIDevice ), return false );
|
||||
|
||||
Device->WriteCommand = SPIDefaultWriteCommand;
|
||||
Device->WriteData = SPIDefaultWriteData;
|
||||
Device->SPIHandle = SPIDevice;
|
||||
Device->RSTPin = RSTPin;
|
||||
Device->CSPin = CSPin;
|
||||
Device->IF = IF_SPI;
|
||||
Device->Width = Width;
|
||||
Device->Height = Height;
|
||||
|
||||
if ( RSTPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( RSTPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( RSTPin, 0 ), return false );
|
||||
GDS_Reset( Device );
|
||||
}
|
||||
|
||||
return Device->Init( Device );
|
||||
}
|
||||
|
||||
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength ) {
|
||||
spi_transaction_t SPITransaction = { 0 };
|
||||
|
||||
NullCheck( SPIHandle, return false );
|
||||
NullCheck( Data, return false );
|
||||
|
||||
if ( DataLength > 0 ) {
|
||||
gpio_set_level( DCPin, WriteMode );
|
||||
|
||||
SPITransaction.length = DataLength * 8;
|
||||
SPITransaction.tx_buffer = Data;
|
||||
|
||||
// only do polling as we don't have contention on SPI (otherwise DMA for transfers > 16 bytes)
|
||||
ESP_ERROR_CHECK_NONFATAL( spi_device_polling_transmit(SPIHandle, &SPITransaction), return false );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SPIDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command ) {
|
||||
static uint8_t CommandByte = 0;
|
||||
|
||||
NullCheck( Device, return false );
|
||||
NullCheck( Device->SPIHandle, return false );
|
||||
|
||||
CommandByte = Command;
|
||||
|
||||
return SPIDefaultWriteBytes( Device->SPIHandle, GDS_SPI_Command_Mode, &CommandByte, 1 );
|
||||
}
|
||||
|
||||
static bool SPIDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength ) {
|
||||
NullCheck( Device, return false );
|
||||
NullCheck( Device->SPIHandle, return false );
|
||||
|
||||
return SPIDefaultWriteBytes( Device->SPIHandle, GDS_SPI_Data_Mode, Data, DataLength );
|
||||
}
|
||||
Reference in New Issue
Block a user