gds_image.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #include <string.h>
  2. #include "math.h"
  3. #include "esp32/rom/tjpgd.h"
  4. #include "esp_log.h"
  5. #include "gds.h"
  6. #include "gds_private.h"
  7. #include "gds_image.h"
  8. const char TAG[] = "ImageDec";
  9. #define SCRATCH_SIZE 3100
  10. //Data that is passed from the decoder function to the infunc/outfunc functions.
  11. typedef struct {
  12. const unsigned char *InData; // Pointer to jpeg data
  13. int InPos; // Current position in jpeg data
  14. int Width, Height;
  15. union {
  16. uint16_t *OutData; // Decompress
  17. struct { // DirectDraw
  18. struct GDS_Device * Device;
  19. int XOfs, YOfs;
  20. int XMin, YMin;
  21. int Depth;
  22. };
  23. };
  24. } JpegCtx;
  25. static unsigned InHandler(JDEC *Decoder, uint8_t *Buf, unsigned Len) {
  26. JpegCtx *Context = (JpegCtx*) Decoder->device;
  27. if (Buf) memcpy(Buf, Context->InData + Context->InPos, Len);
  28. Context->InPos += Len;
  29. return Len;
  30. }
  31. static unsigned OutHandler(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
  32. JpegCtx *Context = (JpegCtx*) Decoder->device;
  33. uint8_t *Pixels = (uint8_t*) Bitmap;
  34. for (int y = Frame->top; y <= Frame->bottom; y++) {
  35. for (int x = Frame->left; x <= Frame->right; x++) {
  36. // Convert the 888 to RGB565
  37. uint16_t Value = (*Pixels++ & ~0x07) << 8;
  38. Value |= (*Pixels++ & ~0x03) << 3;
  39. Value |= *Pixels++ >> 3;
  40. Context->OutData[Context->Width * y + x] = Value;
  41. }
  42. }
  43. return 1;
  44. }
  45. static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
  46. JpegCtx *Context = (JpegCtx*) Decoder->device;
  47. uint8_t *Pixels = (uint8_t*) Bitmap;
  48. int Shift = 8 - Context->Depth;
  49. for (int y = Frame->top; y <= Frame->bottom; y++) {
  50. if (y < Context->YMin) continue;
  51. for (int x = Frame->left; x <= Frame->right; x++) {
  52. if (x < Context->XMin) continue;
  53. // Convert the 888 to RGB565
  54. int Value = ((Pixels[0]*11 + Pixels[1]*59 + Pixels[2]*30) / 100) >> Shift;
  55. Pixels += 3;
  56. // used DrawPixel and not "fast" version as X,Y may be beyond screen
  57. GDS_DrawPixel( Context->Device, x + Context->XOfs, y + Context->YOfs, Value);
  58. }
  59. }
  60. return 1;
  61. }
  62. //Decode the embedded image into pixel lines that can be used with the rest of the logic.
  63. static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, bool SizeOnly) {
  64. JDEC Decoder;
  65. JpegCtx Context;
  66. char *Scratch = calloc(SCRATCH_SIZE, 1);
  67. if (!Scratch) {
  68. ESP_LOGE(TAG, "Cannot allocate workspace");
  69. return NULL;
  70. }
  71. Context.OutData = NULL;
  72. Context.InData = Source;
  73. Context.InPos = 0;
  74. //Prepare and decode the jpeg.
  75. int Res = jd_prepare(&Decoder, InHandler, Scratch, SCRATCH_SIZE, (void*) &Context);
  76. if (Width) *Width = Decoder.width;
  77. if (Height) *Height = Decoder.height;
  78. Decoder.scale = Scale;
  79. if (Res == JDR_OK && !SizeOnly) {
  80. Context.OutData = malloc(Decoder.width * Decoder.height * sizeof(uint16_t));
  81. // find the scaling factor
  82. uint8_t N = 0, ScaleInt = ceil(1.0 / Scale);
  83. ScaleInt--; ScaleInt |= ScaleInt >> 1; ScaleInt |= ScaleInt >> 2; ScaleInt++;
  84. while (ScaleInt >>= 1) N++;
  85. if (N > 3) {
  86. ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height);
  87. N = 3;
  88. }
  89. // ready to decode
  90. if (Context.OutData) {
  91. Context.Width = Decoder.width / (1 << N);
  92. Context.Height = Decoder.height / (1 << N);
  93. if (Width) *Width = Context.Width;
  94. if (Height) *Height = Context.Height;
  95. Res = jd_decomp(&Decoder, OutHandler, N);
  96. if (Res != JDR_OK) {
  97. ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
  98. }
  99. } else {
  100. ESP_LOGE(TAG, "Can't allocate bitmap %dx%d", Decoder.width, Decoder.height);
  101. }
  102. } else if (!SizeOnly) {
  103. ESP_LOGE(TAG, "Image decoder: jd_prepare failed (%d)", Res);
  104. }
  105. // free scratch area
  106. if (Scratch) free(Scratch);
  107. return Context.OutData;
  108. }
  109. uint16_t* GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale) {
  110. return DecodeJPEG(Source, Width, Height, Scale, false);
  111. }
  112. void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height) {
  113. DecodeJPEG(Source, Width, Height, 1, true);
  114. }
  115. /****************************************************************************************
  116. * Simply draw a RGB565 image
  117. * monoschrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
  118. * grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
  119. */
  120. void GDS_DrawRGB16( struct GDS_Device* Device, uint16_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
  121. if (Device->DrawRGB16) {
  122. Device->DrawRGB16( Device, x, y, Width, Height, RGB_Mode, Image );
  123. } else {
  124. int Scale = Device->Depth < 5 ? 5 - Device->Depth : 0;
  125. switch(RGB_Mode) {
  126. case GDS_RGB565:
  127. for (int c = 0; c < Width; c++) {
  128. for (int r = 0; r < Height; r++) {
  129. int pixel = Image[Width*r + c];
  130. pixel = ((pixel & 0x1f) * 11 + ((((pixel >> 5) & 0x3f) * 59) >> 1) + (pixel >> 11) * 30) / 100;
  131. GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
  132. }
  133. }
  134. break;
  135. case GDS_RGB555:
  136. for (int c = 0; c < Width; c++) {
  137. for (int r = 0; r < Height; r++) {
  138. int pixel = Image[Width*r + c];
  139. pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
  140. GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
  141. }
  142. }
  143. break;
  144. case GDS_RGB444:
  145. for (int c = 0; c < Width; c++) {
  146. for (int r = 0; r < Height; r++) {
  147. int pixel = Image[Width*r + c];
  148. pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
  149. GDS_DrawPixel( Device, c + x, r + y, pixel >> (Scale - 1));
  150. }
  151. }
  152. break;
  153. }
  154. }
  155. }
  156. //Decode the embedded image into pixel lines that can be used with the rest of the logic.
  157. bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit) {
  158. JDEC Decoder;
  159. JpegCtx Context;
  160. bool Ret = false;
  161. char *Scratch = calloc(SCRATCH_SIZE, 1);
  162. if (!Scratch) {
  163. ESP_LOGE(TAG, "Cannot allocate workspace");
  164. return NULL;
  165. }
  166. // Populate fields of the JpegCtx struct.
  167. Context.InData = Source;
  168. Context.InPos = 0;
  169. Context.XOfs = x;
  170. Context.YOfs = y;
  171. Context.Device = Device;
  172. Context.Depth = Device->Depth;
  173. //Prepare and decode the jpeg.
  174. int Res = jd_prepare(&Decoder, InHandler, Scratch, SCRATCH_SIZE, (void*) &Context);
  175. Context.Width = Decoder.width;
  176. Context.Height = Decoder.height;
  177. if (Res == JDR_OK) {
  178. uint8_t N = 0;
  179. // do we need to fit the image
  180. if (Fit & GDS_IMAGE_FIT) {
  181. float XRatio = (Device->Width - x) / (float) Decoder.width, YRatio = (Device->Height - y) / (float) Decoder.height;
  182. uint8_t Ratio = XRatio < YRatio ? ceil(1/XRatio) : ceil(1/YRatio);
  183. Ratio--; Ratio |= Ratio >> 1; Ratio |= Ratio >> 2; Ratio++;
  184. while (Ratio >>= 1) N++;
  185. if (N > 3) {
  186. ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height);
  187. N = 3;
  188. }
  189. Context.Width /= 1 << N;
  190. Context.Height /= 1 << N;
  191. }
  192. // then place it
  193. if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = (Device->Width + x - Context.Width) / 2;
  194. if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = (Device->Height + y - Context.Height) / 2;
  195. Context.XMin = x - Context.XOfs;
  196. Context.YMin = y - Context.YOfs;
  197. // do decompress & draw
  198. Res = jd_decomp(&Decoder, OutHandlerDirect, N);
  199. if (Res == JDR_OK) {
  200. Ret = true;
  201. } else {
  202. ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
  203. }
  204. } else {
  205. ESP_LOGE(TAG, "Image decoder: jd_prepare failed (%d)", Res);
  206. }
  207. // free scratch area
  208. if (Scratch) free(Scratch);
  209. return Ret;
  210. }