|  | @@ -24,10 +24,11 @@ typedef struct {
 | 
	
		
			
				|  |  |      const unsigned char *InData;	// Pointer to jpeg data
 | 
	
		
			
				|  |  |      int InPos;						// Current position in jpeg data
 | 
	
		
			
				|  |  |  	int Width, Height;	
 | 
	
		
			
				|  |  | +	uint8_t Mode;
 | 
	
		
			
				|  |  |  	union {
 | 
	
		
			
				|  |  | -		uint16_t *OutData;				// Decompress
 | 
	
		
			
				|  |  | +		void *OutData;
 | 
	
		
			
				|  |  |  		struct {						// DirectDraw
 | 
	
		
			
				|  |  | -			struct GDS_Device * Device;
 | 
	
		
			
				|  |  | +			struct GDS_Device *Device;
 | 
	
		
			
				|  |  |  			int XOfs, YOfs;
 | 
	
		
			
				|  |  |  			int XMin, YMin;
 | 
	
		
			
				|  |  |  			int Depth;
 | 
	
	
		
			
				|  | @@ -35,6 +36,40 @@ typedef struct {
 | 
	
		
			
				|  |  |  	};	
 | 
	
		
			
				|  |  |  } JpegCtx;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/****************************************************************************************
 | 
	
		
			
				|  |  | + * RGB conversion (24 bits 888: RRRRRRRRGGGGGGGGBBBBBBBB and 16 bits 565: RRRRRGGGGGGBBBBB = B31..B0)
 | 
	
		
			
				|  |  | + * so in other words for an array of 888 bytes: [0]=B, [1]=G, [2]=R, ...
 | 
	
		
			
				|  |  | + * monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
 | 
	
		
			
				|  |  | + * grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +inline int Scaler332(uint8_t *Pixels) {
 | 
	
		
			
				|  |  | +	return (Pixels[2] & ~0x1f) | ((Pixels[1] & ~0x1f) >> 3) | (Pixels[0] >> 6);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int Scaler444(uint8_t *Pixels) {
 | 
	
		
			
				|  |  | +	return ((Pixels[2] & ~0x0f) << 4) | (Pixels[1] & ~0x0f) | (Pixels[0] >> 4);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int Scaler555(uint8_t *Pixels) {
 | 
	
		
			
				|  |  | +	return ((Pixels[2] & ~0x07) << 7) | ((Pixels[1] & ~0x07) << 2) | (Pixels[0] >> 3);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int Scaler565(uint8_t *Pixels) {
 | 
	
		
			
				|  |  | +	return ((Pixels[2] & ~0x07) << 8) | ((Pixels[1] & ~0x03) << 3) | (Pixels[0] >> 3);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int Scaler666(uint8_t *Pixels) {
 | 
	
		
			
				|  |  | +	return ((Pixels[2] & ~0x03) << 10) | ((Pixels[1] & ~0x03) << 4) | (Pixels[0] >> 2);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int Scaler888(uint8_t *Pixels) {
 | 
	
		
			
				|  |  | +	return (Pixels[2] << 16) | (Pixels[1] << 8) | Pixels[0];
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int ScalerGray(uint8_t *Pixels) {
 | 
	
		
			
				|  |  | +	return (Pixels[2] * 14 + Pixels[1] * 76 + Pixels[0] * 38) >> 7;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static unsigned InHandler(JDEC *Decoder, uint8_t *Buf, unsigned Len) {
 | 
	
		
			
				|  |  |      JpegCtx *Context = (JpegCtx*) Decoder->device;
 | 
	
		
			
				|  |  |      if (Buf) memcpy(Buf, Context->InData +  Context->InPos, Len);
 | 
	
	
		
			
				|  | @@ -42,43 +77,94 @@ static unsigned InHandler(JDEC *Decoder, uint8_t *Buf, unsigned Len) {
 | 
	
		
			
				|  |  |      return Len;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#define OUTHANDLER(F)										\
 | 
	
		
			
				|  |  | +	for (int y = Frame->top; y <= Frame->bottom; y++) {		\
 | 
	
		
			
				|  |  | +		for (int x = Frame->left; x <= Frame->right; x++) {	\
 | 
	
		
			
				|  |  | +			OutData[Context->Width * y + x] = F(Pixels);	\
 | 
	
		
			
				|  |  | +			Pixels += 3;									\
 | 
	
		
			
				|  |  | +		}													\
 | 
	
		
			
				|  |  | +	}	
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +#define OUTHANDLER24(F)													\
 | 
	
		
			
				|  |  | +	for (int y = Frame->top; y <= Frame->bottom; y++) {					\
 | 
	
		
			
				|  |  | +		uint8_t *p = OutData + (Context->Width * y + Frame->left) * 3;	\
 | 
	
		
			
				|  |  | +		for (int c = Frame->right - Frame->left; c-- >= 0;) {			\
 | 
	
		
			
				|  |  | +			uint32_t v = F(Pixels);										\
 | 
	
		
			
				|  |  | +			*p++ = v; *p++ = v >> 8; *p++ = v >> 16;					\
 | 
	
		
			
				|  |  | +			Pixels += 3;												\
 | 
	
		
			
				|  |  | +		}																\
 | 
	
		
			
				|  |  | +	}		
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  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++) {
 | 
	
		
			
				|  |  | -        for (int x = Frame->left; x <= Frame->right; x++) {
 | 
	
		
			
				|  |  | -            // 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;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// decoded image is RGB888
 | 
	
		
			
				|  |  | +	if (Context->Mode == GDS_RGB888) {
 | 
	
		
			
				|  |  | +		uint8_t *OutData = (uint8_t*) Context->OutData;		
 | 
	
		
			
				|  |  | +		OUTHANDLER24(Scaler888);
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB666) {
 | 
	
		
			
				|  |  | +		uint8_t *OutData = (uint8_t*) Context->OutData;		
 | 
	
		
			
				|  |  | +		OUTHANDLER24(Scaler666);		
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB565) {
 | 
	
		
			
				|  |  | +		uint16_t *OutData = (uint16_t*) Context->OutData;
 | 
	
		
			
				|  |  | +		OUTHANDLER(Scaler565);		
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB555) {
 | 
	
		
			
				|  |  | +		uint16_t *OutData = (uint16_t*) Context->OutData;
 | 
	
		
			
				|  |  | +		OUTHANDLER(Scaler555);				
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB444) {
 | 
	
		
			
				|  |  | +		uint16_t *OutData = (uint16_t*) Context->OutData;
 | 
	
		
			
				|  |  | +		OUTHANDLER(Scaler444);						
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB332) {
 | 
	
		
			
				|  |  | +		uint8_t *OutData = (uint8_t*) Context->OutData;
 | 
	
		
			
				|  |  | +		OUTHANDLER(Scaler332);						
 | 
	
		
			
				|  |  | +	} else if (Context->Mode <= GDS_GRAYSCALE) { 	 
 | 
	
		
			
				|  |  | +		uint8_t *OutData = (uint8_t*) Context->OutData;		
 | 
	
		
			
				|  |  | +		OUTHANDLER(ScalerGray);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  |      return 1;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// Convert the RGB888 to destination color plane, use DrawPixel and not "fast" 
 | 
	
		
			
				|  |  | +// version as X,Y may be beyond screen				
 | 
	
		
			
				|  |  | +#define OUTHANDLERDIRECT(F,S)																		\
 | 
	
		
			
				|  |  | +	for (int y = Frame->top; y <= Frame->bottom; y++) {												\
 | 
	
		
			
				|  |  | +		if (y < Context->YMin) continue;															\
 | 
	
		
			
				|  |  | +		for (int x = Frame->left; x <= Frame->right; x++) {											\
 | 
	
		
			
				|  |  | +			if (x < Context->XMin) continue;														\
 | 
	
		
			
				|  |  | +			GDS_DrawPixel( Context->Device, x + Context->XOfs, y + Context->YOfs, F(Pixels) >> S);	\
 | 
	
		
			
				|  |  | +			Pixels += 3;																			\
 | 
	
		
			
				|  |  | +		}																							\
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  |  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++) {
 | 
	
		
			
				|  |  | -		if (y < Context->YMin) continue;
 | 
	
		
			
				|  |  | -        for (int x = Frame->left; x <= Frame->right; x++) {
 | 
	
		
			
				|  |  | -			if (x < Context->XMin) continue;
 | 
	
		
			
				|  |  | -            // 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, x + Context->XOfs, y + Context->YOfs, Value);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +	// decoded image is RGB888, shift only make sense for grayscale
 | 
	
		
			
				|  |  | +	if (Context->Mode == GDS_RGB888) {
 | 
	
		
			
				|  |  | +		OUTHANDLERDIRECT(Scaler888, 0);
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB666) {
 | 
	
		
			
				|  |  | +		OUTHANDLERDIRECT(Scaler666, 0);		
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB565) {
 | 
	
		
			
				|  |  | +		OUTHANDLERDIRECT(Scaler565, 0);		
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB555) {
 | 
	
		
			
				|  |  | +		OUTHANDLERDIRECT(Scaler555, 0);				
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB444) {
 | 
	
		
			
				|  |  | +		OUTHANDLERDIRECT(Scaler444, 0);						
 | 
	
		
			
				|  |  | +	} else if (Context->Mode == GDS_RGB332) {
 | 
	
		
			
				|  |  | +		OUTHANDLERDIRECT(Scaler332, 0);						
 | 
	
		
			
				|  |  | +	} else if (Context->Mode <= GDS_GRAYSCALE) { 	 
 | 
	
		
			
				|  |  | +		OUTHANDLERDIRECT(ScalerGray, Shift);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  |      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) {
 | 
	
		
			
				|  |  | +static void* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, bool SizeOnly, int RGB_Mode) {
 | 
	
		
			
				|  |  |      JDEC Decoder;
 | 
	
		
			
				|  |  |      JpegCtx Context;
 | 
	
		
			
				|  |  |  	char *Scratch = calloc(SCRATCH_SIZE, 1);
 | 
	
	
		
			
				|  | @@ -99,7 +185,9 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
 | 
	
		
			
				|  |  |  	Decoder.scale = Scale;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (Res == JDR_OK && !SizeOnly) {
 | 
	
		
			
				|  |  | -		Context.OutData = malloc(Decoder.width * Decoder.height * sizeof(uint16_t));
 | 
	
		
			
				|  |  | +		if (RGB_Mode <= GDS_RGB332) Context.OutData = malloc(Decoder.width * Decoder.height);
 | 
	
		
			
				|  |  | +		else if (RGB_Mode < GDS_RGB666) Context.OutData = malloc(Decoder.width * Decoder.height * 2);
 | 
	
		
			
				|  |  | +		else if (RGB_Mode <= GDS_RGB888) Context.OutData = malloc(Decoder.width * Decoder.height * 3);
 | 
	
		
			
				|  |  |  		
 | 
	
		
			
				|  |  |  		// find the scaling factor
 | 
	
		
			
				|  |  |  		uint8_t N = 0, ScaleInt =  ceil(1.0 / Scale);
 | 
	
	
		
			
				|  | @@ -114,6 +202,7 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
 | 
	
		
			
				|  |  |  		if (Context.OutData) {
 | 
	
		
			
				|  |  |  			Context.Width = Decoder.width / (1 << N);
 | 
	
		
			
				|  |  |  			Context.Height = Decoder.height / (1 << N);
 | 
	
		
			
				|  |  | +			Context.Mode = RGB_Mode;
 | 
	
		
			
				|  |  |  			if (Width) *Width = Context.Width;
 | 
	
		
			
				|  |  |  			if (Height) *Height = Context.Height;
 | 
	
		
			
				|  |  |  			Res = jd_decomp(&Decoder, OutHandler, N);
 | 
	
	
		
			
				|  | @@ -121,150 +210,165 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
 | 
	
		
			
				|  |  |  				ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
 | 
	
		
			
				|  |  |  			}	
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  | -			ESP_LOGE(TAG, "Can't allocate bitmap %dx%d", Decoder.width, Decoder.height);			
 | 
	
		
			
				|  |  | +			ESP_LOGE(TAG, "Can't allocate bitmap %dx%d or invalid mode %d", Decoder.width, Decoder.height, RGB_Mode);			
 | 
	
		
			
				|  |  |  		}	
 | 
	
		
			
				|  |  |  	} 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_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, int RGB_Mode) {
 | 
	
		
			
				|  |  | +	return DecodeJPEG(Source, Width, Height, Scale, false, RGB_Mode);
 | 
	
		
			
				|  |  |  }	
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height) {
 | 
	
		
			
				|  |  | -	DecodeJPEG(Source, Width, Height, 1, true);
 | 
	
		
			
				|  |  | +	DecodeJPEG(Source, Width, Height, 1, true, -1);
 | 
	
		
			
				|  |  |  }	
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /****************************************************************************************
 | 
	
		
			
				|  |  | - * Simply draw a RGB 16bits image
 | 
	
		
			
				|  |  | + * RGB conversion (24 bits: RRRRRRRRGGGGGGGGBBBBBBBB and 16 bits 565: RRRRRGGGGGGBBBBB = B31..B0)
 | 
	
		
			
				|  |  | + * so in other words for an array of 888 bytes: [0]=B, [1]=G, [2]=R, ...
 | 
	
		
			
				|  |  |   * monochrome (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, Image, x, y, Width, Height, RGB_Mode );
 | 
	
		
			
				|  |  | -	} else {
 | 
	
		
			
				|  |  | -		switch(RGB_Mode) {
 | 
	
		
			
				|  |  | -		case GDS_RGB565:
 | 
	
		
			
				|  |  | -			// 6 bits pixels to be placed. Use a linearized structure for a bit of optimization
 | 
	
		
			
				|  |  | -			if (Device->Depth < 6) {
 | 
	
		
			
				|  |  | -				int Scale = 6 - Device->Depth;
 | 
	
		
			
				|  |  | -				for (int r = 0; r < Height; r++) {
 | 
	
		
			
				|  |  | -					for (int c = 0; c < Width; c++) {
 | 
	
		
			
				|  |  | -						int pixel = *Image++;
 | 
	
		
			
				|  |  | -						pixel = ((((pixel & 0x1f) * 11) << 1) + ((pixel >> 5) & 0x3f) * 59 + (((pixel >> 11) * 30) << 1) + 1) / 100;
 | 
	
		
			
				|  |  | -						GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
 | 
	
		
			
				|  |  | -					}	
 | 
	
		
			
				|  |  | -				}	
 | 
	
		
			
				|  |  | -			} else {
 | 
	
		
			
				|  |  | -				int Scale = Device->Depth - 6;
 | 
	
		
			
				|  |  | -				for (int r = 0; r < Height; r++) {
 | 
	
		
			
				|  |  | -					for (int c = 0; c < Width; c++) {
 | 
	
		
			
				|  |  | -						int pixel = *Image++;
 | 
	
		
			
				|  |  | -						pixel = ((((pixel & 0x1f) * 11) << 1) + ((pixel >> 5) & 0x3f) * 59 + (((pixel >> 11) * 30) << 1) + 1) / 100;
 | 
	
		
			
				|  |  | -						GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
 | 
	
		
			
				|  |  | -					}	
 | 
	
		
			
				|  |  | -				}	
 | 
	
		
			
				|  |  | -			}	
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		case GDS_RGB555:
 | 
	
		
			
				|  |  | -			// 5 bits pixels to be placed Use a linearized structure for a bit of optimization
 | 
	
		
			
				|  |  | -			if (Device->Depth < 5) {
 | 
	
		
			
				|  |  | -				int Scale = 5 - Device->Depth;
 | 
	
		
			
				|  |  | -				for (int r = 0; r < Height; r++) {
 | 
	
		
			
				|  |  | -					for (int c = 0; c < Width; c++) {
 | 
	
		
			
				|  |  | -						int pixel = *Image++;
 | 
	
		
			
				|  |  | -						pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
 | 
	
		
			
				|  |  | -						GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
 | 
	
		
			
				|  |  | -					}	
 | 
	
		
			
				|  |  | -				}	
 | 
	
		
			
				|  |  | -			} else {
 | 
	
		
			
				|  |  | -				int Scale = Device->Depth - 5;
 | 
	
		
			
				|  |  | -				for (int r = 0; r < Height; r++) {
 | 
	
		
			
				|  |  | -					for (int c = 0; c < Width; c++) {
 | 
	
		
			
				|  |  | -						int pixel = *Image++;
 | 
	
		
			
				|  |  | -						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:
 | 
	
		
			
				|  |  | -			// 4 bits pixels to be placed 
 | 
	
		
			
				|  |  | -			if (Device->Depth < 4) {
 | 
	
		
			
				|  |  | -				int Scale = 4 - Device->Depth;
 | 
	
		
			
				|  |  | -				for (int r = 0; r < Height; r++) {
 | 
	
		
			
				|  |  | -					for (int c = 0; c < Width; c++) {
 | 
	
		
			
				|  |  | -						int pixel = *Image++;
 | 
	
		
			
				|  |  | -						pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
 | 
	
		
			
				|  |  | -						GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
 | 
	
		
			
				|  |  | -					}	
 | 
	
		
			
				|  |  | -				}	
 | 
	
		
			
				|  |  | -			} else {
 | 
	
		
			
				|  |  | -				int Scale = Device->Depth - 4;
 | 
	
		
			
				|  |  | -				for (int r = 0; r < Height; r++) {
 | 
	
		
			
				|  |  | -					for (int c = 0; c < Width; c++) {
 | 
	
		
			
				|  |  | -						int pixel = *Image++;
 | 
	
		
			
				|  |  | -						pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
 | 
	
		
			
				|  |  | -						GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
 | 
	
		
			
				|  |  | -					}	
 | 
	
		
			
				|  |  | -				}	
 | 
	
		
			
				|  |  | -			}	
 | 
	
		
			
				|  |  | -			break;				
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	}	
 | 
	
		
			
				|  |  | -	
 | 
	
		
			
				|  |  | -	Device->Dirty = true;	
 | 
	
		
			
				|  |  | + 
 | 
	
		
			
				|  |  | +inline int ToGray888(uint8_t **Pixel) {
 | 
	
		
			
				|  |  | +	uint32_t v = *(*Pixel)++; v |= *(*Pixel)++ << 8; v |= *(*Pixel)++ << 16; 
 | 
	
		
			
				|  |  | +	return (((v & 0xff) * 14) + ((v >> 8) & 0xff) * 76 + ((v >> 16) * 38) + 1) >> 7;
 | 
	
		
			
				|  |  | +} 
 | 
	
		
			
				|  |  | + 
 | 
	
		
			
				|  |  | +inline int ToGray666(uint8_t **Pixel) {
 | 
	
		
			
				|  |  | +	uint32_t v = *(*Pixel)++; v |= *(*Pixel)++ << 8; v |= *(*Pixel)++ << 16; 
 | 
	
		
			
				|  |  | +	return (((v & 0x3f) * 14) + ((v >> 6) & 0x3f) * 76 + ((v >> 12) * 38) + 1) >> 7;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +inline int ToGray565(uint16_t **Pixel) {
 | 
	
		
			
				|  |  | +	uint16_t v = *(*Pixel)++; 
 | 
	
		
			
				|  |  | +	return ((((v & 0x1f) * 14) << 1) + ((v >> 5) & 0x3f) * 76 + (((v >> 11) * 38) << 1) + 1) >> 7;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int ToGray555(uint16_t **Pixel) {
 | 
	
		
			
				|  |  | +	uint16_t v = *(*Pixel)++; 
 | 
	
		
			
				|  |  | +	return ((v & 0x1f) * 14 + ((v >> 5) & 0x1f) * 76 + (v >> 10) * 38) >> 7;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int ToGray444(uint16_t **Pixel) {
 | 
	
		
			
				|  |  | +	uint16_t v = *(*Pixel)++; 
 | 
	
		
			
				|  |  | +	return ((v & 0x0f) * 14 + ((v >> 4) & 0x0f) * 76 + (v >> 8) * 38) >> 7;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int ToGray332(uint8_t **Pixel) {
 | 
	
		
			
				|  |  | +	uint8_t v = *(*Pixel)++; 
 | 
	
		
			
				|  |  | +	return ((((v & 0x3) * 14) << 1) + ((v >> 2) & 0x7) * 76 + (v >> 5) * 38 + 1) >> 7;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +inline int ToSelf(uint8_t **Pixel) {
 | 
	
		
			
				|  |  | +	return *(*Pixel)++; 
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +#define DRAW_GRAYRGB(S,F)													\
 | 
	
		
			
				|  |  | +	if (Scale > 0) {														\
 | 
	
		
			
				|  |  | +		for (int r = 0; r < Height; r++) {									\
 | 
	
		
			
				|  |  | +			for (int c = 0; c < Width; c++) {								\
 | 
	
		
			
				|  |  | +				GDS_DrawPixel( Device, c + x, r + y, F(S) >> Scale);		\
 | 
	
		
			
				|  |  | +			}																\
 | 
	
		
			
				|  |  | +		}																	\
 | 
	
		
			
				|  |  | +	} else {																\
 | 
	
		
			
				|  |  | +		for (int r = 0; r < Height; r++) {									\
 | 
	
		
			
				|  |  | +			for (int c = 0; c < Width; c++) {								\
 | 
	
		
			
				|  |  | +				GDS_DrawPixel( Device, c + x, r + y, F(S) << -Scale);	\
 | 
	
		
			
				|  |  | +			}																\
 | 
	
		
			
				|  |  | +		}																	\
 | 
	
		
			
				|  |  | +	}									
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +#define DRAW_RGB(T)										\
 | 
	
		
			
				|  |  | +	T *S = (T*) Image;									\
 | 
	
		
			
				|  |  | +	for (int r = 0; r < Height; r++) {					\
 | 
	
		
			
				|  |  | +		for (int c = 0; c < Width; c++) {				\
 | 
	
		
			
				|  |  | +			GDS_DrawPixel(Device, c + x, r + y, *S++);	\
 | 
	
		
			
				|  |  | +		}												\
 | 
	
		
			
				|  |  | +	}																	
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +#define DRAW_RGB24													\
 | 
	
		
			
				|  |  | +	uint8_t *S = (uint8_t*) Image;									\
 | 
	
		
			
				|  |  | +	for (int r = 0; r < Height; r++) {								\
 | 
	
		
			
				|  |  | +		for (int c = 0; c < Width; c++) {							\
 | 
	
		
			
				|  |  | +			uint32_t v = *S++; v |= *S++ << 8; v |= *S++ << 16;		\
 | 
	
		
			
				|  |  | +			GDS_DrawPixel(Device, c + x, r + y, v);					\
 | 
	
		
			
				|  |  | +		}															\
 | 
	
		
			
				|  |  | +	}	
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /****************************************************************************************
 | 
	
		
			
				|  |  | - * Simply draw a RGB 8 bits  image (R:3,G:3,B:2) or plain grayscale
 | 
	
		
			
				|  |  | - * monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
 | 
	
		
			
				|  |  | - * grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
 | 
	
		
			
				|  |  | + *  Decode the embedded image into pixel lines that can be used with the rest of the logic.
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | -void GDS_DrawRGB8( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
 | 
	
		
			
				|  |  | -	if (Device->DrawRGB8) {
 | 
	
		
			
				|  |  | -		Device->DrawRGB8( Device, Image, x, y, Width, Height, RGB_Mode );
 | 
	
		
			
				|  |  | -	} else if (RGB_Mode == GDS_GRAYSCALE) {
 | 
	
		
			
				|  |  | -		// 8 bits pixels
 | 
	
		
			
				|  |  | -		int Scale = 8 - Device->Depth;
 | 
	
		
			
				|  |  | -		for (int r = 0; r < Height; r++) {
 | 
	
		
			
				|  |  | -			for (int c = 0; c < Width; c++) {
 | 
	
		
			
				|  |  | -				GDS_DrawPixel( Device, c + x, r + y, *Image++ >> Scale);
 | 
	
		
			
				|  |  | -			}	
 | 
	
		
			
				|  |  | -		}	
 | 
	
		
			
				|  |  | -	} else if (Device->Depth < 3) {
 | 
	
		
			
				|  |  | -		// 3 bits pixels to be placed 
 | 
	
		
			
				|  |  | -		int Scale = 3 - Device->Depth;
 | 
	
		
			
				|  |  | -		for (int r = 0; r < Height; r++) {
 | 
	
		
			
				|  |  | -			for (int c = 0; c < Width; c++) {
 | 
	
		
			
				|  |  | -				int pixel = *Image++;
 | 
	
		
			
				|  |  | -				pixel = ((((pixel & 0x3) * 11) << 1) + ((pixel >> 2) & 0x7) * 59 + (pixel >> 5) * 30 + 1) / 100;
 | 
	
		
			
				|  |  | -				GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
 | 
	
		
			
				|  |  | -			}	
 | 
	
		
			
				|  |  | +void GDS_DrawRGB( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// don't do anything if driver supplies a draw function
 | 
	
		
			
				|  |  | +	if (Device->DrawRGB) {
 | 
	
		
			
				|  |  | +		Device->DrawRGB( Device, Image, x, y, Width, Height, RGB_Mode );
 | 
	
		
			
				|  |  | +		Device->Dirty = true;	
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	// RGB type displays
 | 
	
		
			
				|  |  | +	if (Device->Mode > GDS_GRAYSCALE) {
 | 
	
		
			
				|  |  | +		// image must match the display mode!
 | 
	
		
			
				|  |  | +		if (Device->Mode != RGB_Mode) {
 | 
	
		
			
				|  |  | +			ESP_LOGE(TAG, "non-matching display & image mode %u %u", Device->Mode, RGB_Mode);
 | 
	
		
			
				|  |  | +			return;
 | 
	
		
			
				|  |  |  		}	
 | 
	
		
			
				|  |  | -	} else {	
 | 
	
		
			
				|  |  | -		// 3 bits pixels to be placed 
 | 
	
		
			
				|  |  | -		int Scale = Device->Depth  - 3;
 | 
	
		
			
				|  |  | -		for (int r = 0; r < Height; r++) {
 | 
	
		
			
				|  |  | -			for (int c = 0; c < Width; c++) {
 | 
	
		
			
				|  |  | -				int pixel = *Image++;
 | 
	
		
			
				|  |  | -				pixel = ((((pixel & 0x3) * 11) << 1) + ((pixel >> 2) & 0x7) * 59 + (pixel >> 5) * 30 + 1) / 100;
 | 
	
		
			
				|  |  | -				GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
 | 
	
		
			
				|  |  | -			}	
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +		if (RGB_Mode == GDS_RGB332) {
 | 
	
		
			
				|  |  | +			DRAW_RGB(uint8_t);
 | 
	
		
			
				|  |  | +		} else if (RGB_Mode < GDS_RGB666) {
 | 
	
		
			
				|  |  | +			DRAW_RGB(uint16_t);
 | 
	
		
			
				|  |  | +		} else {
 | 
	
		
			
				|  |  | +			DRAW_RGB24;
 | 
	
		
			
				|  |  |  		}	
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +		Device->Dirty = true;
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	
 | 
	
		
			
				|  |  | -	Device->Dirty = true;		
 | 
	
		
			
				|  |  | -}	
 | 
	
		
			
				|  |  | +	// set the right scaler when displaying grayscale
 | 
	
		
			
				|  |  | +	if (RGB_Mode <= GDS_GRAYSCALE) {
 | 
	
		
			
				|  |  | +		int Scale = 8 - Device->Depth;
 | 
	
		
			
				|  |  | +		DRAW_GRAYRGB(&Image,ToSelf);
 | 
	
		
			
				|  |  | +	} else if (RGB_Mode == GDS_RGB332) {
 | 
	
		
			
				|  |  | +		int Scale = 3 - Device->Depth;		
 | 
	
		
			
				|  |  | +		DRAW_GRAYRGB(&Image,ToGray332);
 | 
	
		
			
				|  |  | +	} else if (RGB_Mode < GDS_RGB666)	{
 | 
	
		
			
				|  |  | +		if (RGB_Mode == GDS_RGB565) {
 | 
	
		
			
				|  |  | +			int Scale = 6 - Device->Depth;
 | 
	
		
			
				|  |  | +			DRAW_GRAYRGB((uint16_t**)&Image,ToGray565);
 | 
	
		
			
				|  |  | +		} else if (RGB_Mode == GDS_RGB555) {
 | 
	
		
			
				|  |  | +			int Scale = 5 - Device->Depth;
 | 
	
		
			
				|  |  | +			DRAW_GRAYRGB((uint16_t**)&Image,ToGray555);
 | 
	
		
			
				|  |  | +		} else if (RGB_Mode == GDS_RGB444) {
 | 
	
		
			
				|  |  | +			int Scale = 4 - Device->Depth; 
 | 
	
		
			
				|  |  | +			DRAW_GRAYRGB((uint16_t**)&Image,ToGray444)
 | 
	
		
			
				|  |  | +		}	
 | 
	
		
			
				|  |  | +	} else {
 | 
	
		
			
				|  |  | +		if (RGB_Mode == GDS_RGB666) {
 | 
	
		
			
				|  |  | +			int Scale = 6 - Device->Depth;
 | 
	
		
			
				|  |  | +			DRAW_GRAYRGB(&Image,ToGray666);
 | 
	
		
			
				|  |  | +		} else if (RGB_Mode == GDS_RGB888) {
 | 
	
		
			
				|  |  | +			int Scale = 8 - Device->Depth;
 | 
	
		
			
				|  |  | +			DRAW_GRAYRGB(&Image,ToGray888);
 | 
	
		
			
				|  |  | +		}	
 | 
	
		
			
				|  |  | +	} 
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	Device->Dirty = true;	
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -//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) {
 | 
	
		
			
				|  |  | +/****************************************************************************************
 | 
	
		
			
				|  |  | + *  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;
 | 
	
	
		
			
				|  | @@ -313,6 +417,7 @@ bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		Context.XMin = x - Context.XOfs;
 | 
	
		
			
				|  |  |  		Context.YMin = y - Context.YOfs;
 | 
	
		
			
				|  |  | +		Context.Mode = Device->Mode;
 | 
	
		
			
				|  |  |  					
 | 
	
		
			
				|  |  |  		// do decompress & draw
 | 
	
		
			
				|  |  |  		Res = jd_decomp(&Decoder, OutHandlerDirect, N);
 |