migrating to esp-idf V4.0 gcc 8.2 and CMake

This commit is contained in:
Sebastien
2020-03-06 16:43:56 -05:00
parent f998ea2a52
commit 562bec14fe
77 changed files with 2002 additions and 2802 deletions

View File

@@ -0,0 +1,141 @@
/**
* 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 ); }

View File

@@ -0,0 +1,43 @@
#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

View File

@@ -0,0 +1,20 @@
#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

View File

@@ -0,0 +1,271 @@
/**
* 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;
}

View File

@@ -0,0 +1,28 @@
#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

View File

@@ -0,0 +1,39 @@
#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

View File

@@ -0,0 +1,270 @@
/**
* 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;
}
};
}

View File

@@ -0,0 +1,91 @@
#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

View File

@@ -0,0 +1,228 @@
#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;
}

View File

@@ -0,0 +1,23 @@
#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);

View File

@@ -0,0 +1,178 @@
#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 );
static 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;
}
static 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 );
}
}
static 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);
}
static 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);
}
static 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

View File

@@ -0,0 +1,196 @@
/*
* (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);
}

View File

@@ -0,0 +1,45 @@
/*
* (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, ...);

View File

@@ -0,0 +1,127 @@
/**
* 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 );
}

View File

@@ -0,0 +1,109 @@
/**
* 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 );
}