gds_image.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * (c) Philippe G. 2019, philippe_44@outlook.com
  3. *
  4. * This software is released under the MIT License.
  5. * https://opensource.org/licenses/MIT
  6. *
  7. */
  8. #include <string.h>
  9. #include "math.h"
  10. #include "esp32/rom/tjpgd.h"
  11. #include "esp_log.h"
  12. #include "gds.h"
  13. #include "gds_private.h"
  14. #include "gds_image.h"
  15. const char TAG[] = "ImageDec";
  16. #define SCRATCH_SIZE 3100
  17. //Data that is passed from the decoder function to the infunc/outfunc functions.
  18. typedef struct {
  19. const unsigned char *InData; // Pointer to jpeg data
  20. int InPos; // Current position in jpeg data
  21. int Width, Height;
  22. union {
  23. uint16_t *OutData; // Decompress
  24. struct { // DirectDraw
  25. struct GDS_Device * Device;
  26. int XOfs, YOfs;
  27. int XMin, YMin;
  28. int Depth;
  29. };
  30. };
  31. } JpegCtx;
  32. static unsigned InHandler(JDEC *Decoder, uint8_t *Buf, unsigned Len) {
  33. JpegCtx *Context = (JpegCtx*) Decoder->device;
  34. if (Buf) memcpy(Buf, Context->InData + Context->InPos, Len);
  35. Context->InPos += Len;
  36. return Len;
  37. }
  38. static unsigned OutHandler(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
  39. JpegCtx *Context = (JpegCtx*) Decoder->device;
  40. uint8_t *Pixels = (uint8_t*) Bitmap;
  41. for (int y = Frame->top; y <= Frame->bottom; y++) {
  42. for (int x = Frame->left; x <= Frame->right; x++) {
  43. // Convert the 888 to RGB565
  44. uint16_t Value = (*Pixels++ & ~0x07) << 8;
  45. Value |= (*Pixels++ & ~0x03) << 3;
  46. Value |= *Pixels++ >> 3;
  47. Context->OutData[Context->Width * y + x] = Value;
  48. }
  49. }
  50. return 1;
  51. }
  52. static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
  53. JpegCtx *Context = (JpegCtx*) Decoder->device;
  54. uint8_t *Pixels = (uint8_t*) Bitmap;
  55. int Shift = 8 - Context->Depth;
  56. for (int y = Frame->top; y <= Frame->bottom; y++) {
  57. if (y < Context->YMin) continue;
  58. for (int x = Frame->left; x <= Frame->right; x++) {
  59. if (x < Context->XMin) continue;
  60. // Convert the 888 to RGB565
  61. int Value = ((Pixels[0]*11 + Pixels[1]*59 + Pixels[2]*30) / 100) >> Shift;
  62. Pixels += 3;
  63. // used DrawPixel and not "fast" version as X,Y may be beyond screen
  64. GDS_DrawPixel( Context->Device, x + Context->XOfs, y + Context->YOfs, Value);
  65. }
  66. }
  67. return 1;
  68. }
  69. //Decode the embedded image into pixel lines that can be used with the rest of the logic.
  70. static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, bool SizeOnly) {
  71. JDEC Decoder;
  72. JpegCtx Context;
  73. char *Scratch = calloc(SCRATCH_SIZE, 1);
  74. if (!Scratch) {
  75. ESP_LOGE(TAG, "Cannot allocate workspace");
  76. return NULL;
  77. }
  78. Context.OutData = NULL;
  79. Context.InData = Source;
  80. Context.InPos = 0;
  81. //Prepare and decode the jpeg.
  82. int Res = jd_prepare(&Decoder, InHandler, Scratch, SCRATCH_SIZE, (void*) &Context);
  83. if (Width) *Width = Decoder.width;
  84. if (Height) *Height = Decoder.height;
  85. Decoder.scale = Scale;
  86. if (Res == JDR_OK && !SizeOnly) {
  87. Context.OutData = malloc(Decoder.width * Decoder.height * sizeof(uint16_t));
  88. // find the scaling factor
  89. uint8_t N = 0, ScaleInt = ceil(1.0 / Scale);
  90. ScaleInt--; ScaleInt |= ScaleInt >> 1; ScaleInt |= ScaleInt >> 2; ScaleInt++;
  91. while (ScaleInt >>= 1) N++;
  92. if (N > 3) {
  93. ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height);
  94. N = 3;
  95. }
  96. // ready to decode
  97. if (Context.OutData) {
  98. Context.Width = Decoder.width / (1 << N);
  99. Context.Height = Decoder.height / (1 << N);
  100. if (Width) *Width = Context.Width;
  101. if (Height) *Height = Context.Height;
  102. Res = jd_decomp(&Decoder, OutHandler, N);
  103. if (Res != JDR_OK) {
  104. ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
  105. }
  106. } else {
  107. ESP_LOGE(TAG, "Can't allocate bitmap %dx%d", Decoder.width, Decoder.height);
  108. }
  109. } else if (!SizeOnly) {
  110. ESP_LOGE(TAG, "Image decoder: jd_prepare failed (%d)", Res);
  111. }
  112. // free scratch area
  113. if (Scratch) free(Scratch);
  114. return Context.OutData;
  115. }
  116. uint16_t* GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale) {
  117. return DecodeJPEG(Source, Width, Height, Scale, false);
  118. }
  119. void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height) {
  120. DecodeJPEG(Source, Width, Height, 1, true);
  121. }
  122. /****************************************************************************************
  123. * Simply draw a RGB 16bits image
  124. * monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
  125. * grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
  126. */
  127. void GDS_DrawRGB16( struct GDS_Device* Device, uint16_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
  128. if (Device->DrawRGB16) {
  129. Device->DrawRGB16( Device, Image, x, y, Width, Height, RGB_Mode );
  130. } else {
  131. switch(RGB_Mode) {
  132. case GDS_RGB565:
  133. // 6 bits pixels to be placed. Use a linearized structure for a bit of optimization
  134. if (Device->Depth < 6) {
  135. int Scale = 6 - Device->Depth;
  136. for (int r = 0; r < Height; r++) {
  137. for (int c = 0; c < Width; c++) {
  138. int pixel = *Image++;
  139. pixel = ((((pixel & 0x1f) * 11) << 1) + ((pixel >> 5) & 0x3f) * 59 + (((pixel >> 11) * 30) << 1) + 1) / 100;
  140. GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
  141. }
  142. }
  143. } else {
  144. int Scale = Device->Depth - 6;
  145. for (int r = 0; r < Height; r++) {
  146. for (int c = 0; c < Width; c++) {
  147. int pixel = *Image++;
  148. pixel = ((((pixel & 0x1f) * 11) << 1) + ((pixel >> 5) & 0x3f) * 59 + (((pixel >> 11) * 30) << 1) + 1) / 100;
  149. GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
  150. }
  151. }
  152. }
  153. break;
  154. case GDS_RGB555:
  155. // 5 bits pixels to be placed Use a linearized structure for a bit of optimization
  156. if (Device->Depth < 5) {
  157. int Scale = 5 - Device->Depth;
  158. for (int r = 0; r < Height; r++) {
  159. for (int c = 0; c < Width; c++) {
  160. int pixel = *Image++;
  161. pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
  162. GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
  163. }
  164. }
  165. } else {
  166. int Scale = Device->Depth - 5;
  167. for (int r = 0; r < Height; r++) {
  168. for (int c = 0; c < Width; c++) {
  169. int pixel = *Image++;
  170. pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
  171. GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
  172. }
  173. }
  174. }
  175. break;
  176. case GDS_RGB444:
  177. // 4 bits pixels to be placed
  178. if (Device->Depth < 4) {
  179. int Scale = 4 - Device->Depth;
  180. for (int r = 0; r < Height; r++) {
  181. for (int c = 0; c < Width; c++) {
  182. int pixel = *Image++;
  183. pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
  184. GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
  185. }
  186. }
  187. } else {
  188. int Scale = Device->Depth - 4;
  189. for (int r = 0; r < Height; r++) {
  190. for (int c = 0; c < Width; c++) {
  191. int pixel = *Image++;
  192. pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
  193. GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
  194. }
  195. }
  196. }
  197. break;
  198. }
  199. }
  200. Device->Dirty = true;
  201. }
  202. /****************************************************************************************
  203. * Simply draw a RGB 8 bits image (R:3,G:3,B:2) or plain grayscale
  204. * monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
  205. * grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
  206. */
  207. void GDS_DrawRGB8( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
  208. if (Device->DrawRGB8) {
  209. Device->DrawRGB8( Device, Image, x, y, Width, Height, RGB_Mode );
  210. } else if (RGB_Mode == GDS_GRAYSCALE) {
  211. // 8 bits pixels
  212. int Scale = 8 - Device->Depth;
  213. for (int r = 0; r < Height; r++) {
  214. for (int c = 0; c < Width; c++) {
  215. GDS_DrawPixel( Device, c + x, r + y, *Image++ >> Scale);
  216. }
  217. }
  218. } else if (Device->Depth < 3) {
  219. // 3 bits pixels to be placed
  220. int Scale = 3 - Device->Depth;
  221. for (int r = 0; r < Height; r++) {
  222. for (int c = 0; c < Width; c++) {
  223. int pixel = *Image++;
  224. pixel = ((((pixel & 0x3) * 11) << 1) + ((pixel >> 2) & 0x7) * 59 + (pixel >> 5) * 30 + 1) / 100;
  225. GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
  226. }
  227. }
  228. } else {
  229. // 3 bits pixels to be placed
  230. int Scale = Device->Depth - 3;
  231. for (int r = 0; r < Height; r++) {
  232. for (int c = 0; c < Width; c++) {
  233. int pixel = *Image++;
  234. pixel = ((((pixel & 0x3) * 11) << 1) + ((pixel >> 2) & 0x7) * 59 + (pixel >> 5) * 30 + 1) / 100;
  235. GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
  236. }
  237. }
  238. }
  239. Device->Dirty = true;
  240. }
  241. //Decode the embedded image into pixel lines that can be used with the rest of the logic.
  242. bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit) {
  243. JDEC Decoder;
  244. JpegCtx Context;
  245. bool Ret = false;
  246. char *Scratch = calloc(SCRATCH_SIZE, 1);
  247. if (!Scratch) {
  248. ESP_LOGE(TAG, "Cannot allocate workspace");
  249. return NULL;
  250. }
  251. // Populate fields of the JpegCtx struct.
  252. Context.InData = Source;
  253. Context.InPos = 0;
  254. Context.XOfs = x;
  255. Context.YOfs = y;
  256. Context.Device = Device;
  257. Context.Depth = Device->Depth;
  258. //Prepare and decode the jpeg.
  259. int Res = jd_prepare(&Decoder, InHandler, Scratch, SCRATCH_SIZE, (void*) &Context);
  260. Context.Width = Decoder.width;
  261. Context.Height = Decoder.height;
  262. if (Res == JDR_OK) {
  263. uint8_t N = 0;
  264. // do we need to fit the image
  265. if (Fit & GDS_IMAGE_FIT) {
  266. float XRatio = (Device->Width - x) / (float) Decoder.width, YRatio = (Device->Height - y) / (float) Decoder.height;
  267. uint8_t Ratio = XRatio < YRatio ? ceil(1/XRatio) : ceil(1/YRatio);
  268. Ratio--; Ratio |= Ratio >> 1; Ratio |= Ratio >> 2; Ratio++;
  269. while (Ratio >>= 1) N++;
  270. if (N > 3) {
  271. ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height);
  272. N = 3;
  273. }
  274. Context.Width /= 1 << N;
  275. Context.Height /= 1 << N;
  276. }
  277. // then place it
  278. if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = (Device->Width + x - Context.Width) / 2;
  279. else if (Fit & GDS_IMAGE_RIGHT) Context.XOfs = Device->Width - Context.Width;
  280. if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = (Device->Height + y - Context.Height) / 2;
  281. else if (Fit & GDS_IMAGE_BOTTOM) Context.YOfs = Device->Height - Context.Height;
  282. Context.XMin = x - Context.XOfs;
  283. Context.YMin = y - Context.YOfs;
  284. // do decompress & draw
  285. Res = jd_decomp(&Decoder, OutHandlerDirect, N);
  286. if (Res == JDR_OK) {
  287. Device->Dirty = true;
  288. Ret = true;
  289. } else {
  290. ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
  291. }
  292. } else {
  293. ESP_LOGE(TAG, "Image decoder: jd_prepare failed (%d)", Res);
  294. }
  295. // free scratch area
  296. if (Scratch) free(Scratch);
  297. return Ret;
  298. }