image_jpg.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. #include <rtthread.h>
  2. #include <rtgui/rtgui.h>
  3. #ifdef RTGUI_IMAGE_JPEG
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include "jpeglib.h"
  7. #include <rtgui/rtgui_system.h>
  8. #include <rtgui/filerw.h>
  9. #include <rtgui/image_jpeg.h>
  10. #ifdef RTGUI_USING_DFS_FILERW
  11. #include <dfs_posix.h>
  12. #endif
  13. static rt_bool_t rtgui_image_jpeg_check(struct rtgui_filerw* file);
  14. static rt_bool_t rtgui_image_jpeg_load(struct rtgui_image* image, struct rtgui_filerw* file, rt_bool_t load);
  15. static void rtgui_image_jpeg_unload(struct rtgui_image* image);
  16. static void rtgui_image_jpeg_blit(struct rtgui_image* image, struct rtgui_dc* dc, struct rtgui_rect* rect);
  17. struct rtgui_jpeg_error_mgr
  18. {
  19. struct jpeg_error_mgr pub; /* "public" fields */
  20. };
  21. struct rtgui_image_jpeg
  22. {
  23. rt_bool_t is_loaded;
  24. struct rtgui_filerw* filerw;
  25. /* jpeg structure */
  26. struct jpeg_decompress_struct cinfo;
  27. struct rtgui_jpeg_error_mgr errmgr;
  28. rt_uint8_t *pixels;
  29. rt_uint8_t *line_pixels;
  30. };
  31. struct rtgui_image_engine rtgui_image_jpeg_engine =
  32. {
  33. "jpeg",
  34. {RT_NULL},
  35. rtgui_image_jpeg_check,
  36. rtgui_image_jpeg_load,
  37. rtgui_image_jpeg_unload,
  38. rtgui_image_jpeg_blit
  39. };
  40. struct rtgui_image_engine rtgui_image_jpg_engine =
  41. {
  42. "jpg",
  43. {RT_NULL},
  44. rtgui_image_jpeg_check,
  45. rtgui_image_jpeg_load,
  46. rtgui_image_jpeg_unload,
  47. rtgui_image_jpeg_blit
  48. };
  49. #define INPUT_BUFFER_SIZE 4096
  50. typedef struct {
  51. struct jpeg_source_mgr pub;
  52. struct rtgui_filerw* ctx;
  53. rt_uint8_t buffer[INPUT_BUFFER_SIZE];
  54. } rtgui_jpeg_source_mgr;
  55. /*
  56. * Initialize source --- called by jpeg_read_header
  57. * before any data is actually read.
  58. */
  59. static void init_source (j_decompress_ptr cinfo)
  60. {
  61. /* We don't actually need to do anything */
  62. return;
  63. }
  64. /*
  65. * Fill the input buffer --- called whenever buffer is emptied.
  66. */
  67. static boolean fill_input_buffer (j_decompress_ptr cinfo)
  68. {
  69. rtgui_jpeg_source_mgr * src = (rtgui_jpeg_source_mgr *) cinfo->src;
  70. int nbytes;
  71. nbytes = rtgui_filerw_read(src->ctx, src->buffer, 1, INPUT_BUFFER_SIZE);
  72. if (nbytes <= 0)
  73. {
  74. /* Insert a fake EOI marker */
  75. src->buffer[0] = (rt_uint8_t) 0xFF;
  76. src->buffer[1] = (rt_uint8_t) JPEG_EOI;
  77. nbytes = 2;
  78. }
  79. src->pub.next_input_byte = src->buffer;
  80. src->pub.bytes_in_buffer = nbytes;
  81. return TRUE;
  82. }
  83. /*
  84. * Skip data --- used to skip over a potentially large amount of
  85. * uninteresting data (such as an APPn marker).
  86. *
  87. * Writers of suspendable-input applications must note that skip_input_data
  88. * is not granted the right to give a suspension return. If the skip extends
  89. * beyond the data currently in the buffer, the buffer can be marked empty so
  90. * that the next read will cause a fill_input_buffer call that can suspend.
  91. * Arranging for additional bytes to be discarded before reloading the input
  92. * buffer is the application writer's problem.
  93. */
  94. static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
  95. {
  96. rtgui_jpeg_source_mgr * src = (rtgui_jpeg_source_mgr *) cinfo->src;
  97. /* Just a dumb implementation for now. Could use fseek() except
  98. * it doesn't work on pipes. Not clear that being smart is worth
  99. * any trouble anyway --- large skips are infrequent.
  100. */
  101. if (num_bytes > 0)
  102. {
  103. while (num_bytes > (long) src->pub.bytes_in_buffer)
  104. {
  105. num_bytes -= (long) src->pub.bytes_in_buffer;
  106. (void) src->pub.fill_input_buffer(cinfo);
  107. /* note we assume that fill_input_buffer will never
  108. * return FALSE, so suspension need not be handled.
  109. */
  110. }
  111. src->pub.next_input_byte += (size_t) num_bytes;
  112. src->pub.bytes_in_buffer -= (size_t) num_bytes;
  113. }
  114. }
  115. /*
  116. * Terminate source --- called by jpeg_finish_decompress
  117. * after all data has been read.
  118. */
  119. static void term_source (j_decompress_ptr cinfo)
  120. {
  121. /* We don't actually need to do anything */
  122. return;
  123. }
  124. /*
  125. * Prepare for input from a stdio stream.
  126. * The caller must have already opened the stream, and is responsible
  127. * for closing it after finishing decompression.
  128. */
  129. static void rtgui_jpeg_filerw_src_init(j_decompress_ptr cinfo, struct rtgui_filerw *ctx)
  130. {
  131. rtgui_jpeg_source_mgr *src;
  132. /* The source object and input buffer are made permanent so that a series
  133. * of JPEG images can be read from the same file by calling jpeg_stdio_src
  134. * only before the first one. (If we discarded the buffer at the end of
  135. * one image, we'd likely lose the start of the next one.)
  136. * This makes it unsafe to use this manager and a different source
  137. * manager serially with the same JPEG object. Caveat programmer.
  138. */
  139. if (cinfo->src == NULL) { /* first time for this JPEG object? */
  140. cinfo->src = (struct jpeg_source_mgr *)
  141. (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
  142. sizeof(rtgui_jpeg_source_mgr));
  143. src = (rtgui_jpeg_source_mgr *) cinfo->src;
  144. }
  145. src = (rtgui_jpeg_source_mgr *) cinfo->src;
  146. src->pub.init_source = init_source;
  147. src->pub.fill_input_buffer = fill_input_buffer;
  148. src->pub.skip_input_data = skip_input_data;
  149. src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
  150. src->pub.term_source = term_source;
  151. src->ctx = ctx;
  152. src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
  153. src->pub.next_input_byte = NULL; /* until buffer loaded */
  154. }
  155. /* get line data of a jpeg image */
  156. static rt_uint8_t *rtgui_image_get_line(struct rtgui_image* image, int h)
  157. {
  158. struct rtgui_image_jpeg* jpeg;
  159. rt_uint8_t *result_ptr;
  160. JSAMPARRAY buffer; /* Output row buffer */
  161. int row_stride;
  162. RT_ASSERT(image != RT_NULL);
  163. jpeg = (struct rtgui_image_jpeg*) image->data;
  164. RT_ASSERT(jpeg != RT_NULL);
  165. if (h < 0 || h > image->h) return RT_NULL;
  166. /* if the image is loaded, */
  167. if (jpeg->is_loaded == RT_TRUE)
  168. {
  169. result_ptr = jpeg->pixels + (image->w * sizeof(rtgui_color_t)) * h;
  170. return result_ptr;
  171. }
  172. if (jpeg->line_pixels == RT_NULL)
  173. jpeg->line_pixels = rtgui_malloc(image->w * sizeof(rtgui_color_t));
  174. row_stride = jpeg->cinfo.output_width * jpeg->cinfo.output_components;
  175. buffer = (*jpeg->cinfo.mem->alloc_sarray)
  176. ((j_common_ptr) &jpeg->cinfo, JPOOL_IMAGE, row_stride, 1);
  177. /* decompress line data */
  178. jpeg->cinfo.output_scanline = h;
  179. jpeg_read_scanlines(&jpeg->cinfo, buffer, (JDIMENSION) 1);
  180. /* copy pixels memory */
  181. {
  182. int index;
  183. rtgui_color_t *ptr;
  184. ptr = (rtgui_color_t*)jpeg->line_pixels;
  185. for (index = 0; index < image->w; index ++)
  186. ptr[index] = RTGUI_ARGB(0, buffer[0][index*3], buffer[0][index*3+1], buffer[0][index*3+2]);
  187. }
  188. return jpeg->line_pixels;
  189. }
  190. static rt_bool_t rtgui_image_jpeg_loadall(struct rtgui_image* image)
  191. {
  192. struct rtgui_image_jpeg* jpeg;
  193. rt_uint8_t* line_ptr;
  194. JSAMPARRAY buffer; /* Output row buffer */
  195. int row_stride;
  196. jpeg = (struct rtgui_image_jpeg*) image->data;
  197. RT_ASSERT(jpeg != RT_NULL);
  198. /* already load */
  199. if (jpeg->pixels != RT_NULL) return RT_TRUE;
  200. /* allocate all pixels */
  201. jpeg->pixels = rtgui_malloc(image->h * image->w * sizeof(rtgui_color_t));
  202. if (jpeg->pixels == RT_NULL) return RT_FALSE;
  203. /* reset scan line to zero */
  204. jpeg->cinfo.output_scanline = 0;
  205. line_ptr = jpeg->pixels;
  206. row_stride = jpeg->cinfo.output_width * jpeg->cinfo.output_components;
  207. buffer = (*jpeg->cinfo.mem->alloc_sarray)
  208. ((j_common_ptr) &jpeg->cinfo, JPOOL_IMAGE, row_stride, 1);
  209. /* decompress all pixels */
  210. while (jpeg->cinfo.output_scanline < jpeg->cinfo.output_height)
  211. {
  212. /* jpeg_read_scanlines expects an array of pointers to scanlines.
  213. * Here the array is only one element long, but you could ask for
  214. * more than one scanline at a time if that's more convenient.
  215. */
  216. (void) jpeg_read_scanlines(&jpeg->cinfo, buffer, 1);
  217. /* copy pixels memory */
  218. {
  219. int index;
  220. rtgui_color_t *ptr;
  221. ptr = (rtgui_color_t*)line_ptr;
  222. for (index = 0; index < image->w; index ++)
  223. ptr[index] = RTGUI_ARGB(0, buffer[0][index*3], buffer[0][index*3+1], buffer[0][index*3+2]);
  224. }
  225. /* move to next line */
  226. line_ptr += image->w * sizeof(rtgui_color_t);
  227. }
  228. /* decompress done */
  229. rtgui_filerw_close(jpeg->filerw);
  230. jpeg_finish_decompress(&jpeg->cinfo);
  231. jpeg->is_loaded = RT_TRUE;
  232. return RT_TRUE;
  233. }
  234. void rtgui_image_jpeg_init()
  235. {
  236. /* register jpeg on image system */
  237. rtgui_image_register_engine(&rtgui_image_jpeg_engine);
  238. /* register jpg on image system */
  239. rtgui_image_register_engine(&rtgui_image_jpg_engine);
  240. }
  241. static void my_error_exit(j_common_ptr cinfo)
  242. {
  243. }
  244. static void output_no_message(j_common_ptr cinfo)
  245. {
  246. /* do nothing */
  247. }
  248. static rt_bool_t rtgui_image_jpeg_load(struct rtgui_image* image, struct rtgui_filerw* file, rt_bool_t load)
  249. {
  250. struct rtgui_image_jpeg* jpeg;
  251. jpeg = (struct rtgui_image_jpeg*) rtgui_malloc(sizeof(struct rtgui_image_jpeg));
  252. if (jpeg == RT_NULL) return RT_FALSE;
  253. jpeg->filerw = file;
  254. /* read file header */
  255. /* Create a decompression structure and load the JPEG header */
  256. jpeg->cinfo.err = jpeg_std_error(&jpeg->errmgr.pub);
  257. jpeg->errmgr.pub.error_exit = my_error_exit;
  258. jpeg->errmgr.pub.output_message = output_no_message;
  259. jpeg_create_decompress(&jpeg->cinfo);
  260. rtgui_jpeg_filerw_src_init(&jpeg->cinfo, jpeg->filerw);
  261. (void)jpeg_read_header(&jpeg->cinfo, TRUE);
  262. image->w = jpeg->cinfo.image_width;
  263. image->h = jpeg->cinfo.image_height;
  264. /* set image private data and engine */
  265. image->data = jpeg;
  266. image->engine = &rtgui_image_jpeg_engine;
  267. /* start decompression */
  268. (void) jpeg_start_decompress(&jpeg->cinfo);
  269. jpeg->cinfo.out_color_space = JCS_RGB;
  270. jpeg->cinfo.quantize_colors = FALSE;
  271. /* use fast jpeg */
  272. jpeg->cinfo.scale_num = 1;
  273. jpeg->cinfo.scale_denom = 1;
  274. jpeg->cinfo.dct_method = JDCT_FASTEST;
  275. jpeg->cinfo.do_fancy_upsampling = FALSE;
  276. jpeg->pixels = RT_NULL;
  277. jpeg->is_loaded = RT_FALSE;
  278. /* allocate line pixels */
  279. jpeg->line_pixels = rtgui_malloc(image->w * sizeof(rtgui_color_t));
  280. if (jpeg->line_pixels == RT_NULL)
  281. {
  282. /* no memory */
  283. jpeg_finish_decompress(&jpeg->cinfo);
  284. rt_free(jpeg);
  285. return RT_FALSE;
  286. }
  287. if (load == RT_TRUE) rtgui_image_jpeg_loadall(image);
  288. /* create jpeg image successful */
  289. return RT_TRUE;
  290. }
  291. static void rtgui_image_jpeg_unload(struct rtgui_image* image)
  292. {
  293. if (image != RT_NULL)
  294. {
  295. struct rtgui_image_jpeg* jpeg;
  296. jpeg = (struct rtgui_image_jpeg*) image->data;
  297. RT_ASSERT(jpeg != RT_NULL);
  298. if (jpeg->is_loaded == RT_TRUE)
  299. rtgui_free(jpeg->pixels);
  300. if (jpeg->line_pixels != RT_NULL) rtgui_free(jpeg->line_pixels);
  301. if (jpeg->is_loaded != RT_TRUE)
  302. {
  303. rtgui_filerw_close(jpeg->filerw);
  304. jpeg_finish_decompress(&jpeg->cinfo);
  305. }
  306. rt_free(jpeg);
  307. }
  308. }
  309. static void rtgui_image_jpeg_blit(struct rtgui_image* image, struct rtgui_dc* dc, struct rtgui_rect* rect)
  310. {
  311. rt_uint16_t x, y;
  312. rtgui_color_t* ptr;
  313. struct rtgui_image_jpeg* jpeg;
  314. RT_ASSERT(image != RT_NULL && dc != RT_NULL && rect != RT_NULL);
  315. jpeg = (struct rtgui_image_jpeg*) image->data;
  316. RT_ASSERT(jpeg != RT_NULL);
  317. if (jpeg->pixels != RT_NULL)
  318. {
  319. ptr = (rtgui_color_t*) jpeg->pixels;
  320. /* draw each point within dc */
  321. for (y = 0; y < image->h; y ++)
  322. {
  323. for (x = 0; x < image->w; x++)
  324. {
  325. /* not alpha */
  326. if ((*ptr >> 24) != 255)
  327. {
  328. rtgui_dc_draw_color_point(dc, x + rect->x1, y + rect->y1, *ptr);
  329. }
  330. /* move to next color buffer */
  331. ptr ++;
  332. }
  333. }
  334. }
  335. else
  336. {
  337. /* seek to the begin of file */
  338. rtgui_filerw_seek(jpeg->filerw, 0, SEEK_SET);
  339. /* decompress line and line */
  340. for (y = 0; y < image->h; y ++)
  341. {
  342. ptr = (rtgui_color_t*)rtgui_image_get_line(image, y);
  343. for (x = 0; x < image->w; x++)
  344. {
  345. /* not alpha */
  346. if ((*ptr >> 24) != 255)
  347. {
  348. rtgui_dc_draw_color_point(dc, x + rect->x1, y + rect->y1, *ptr);
  349. }
  350. /* move to next color buffer */
  351. ptr ++;
  352. }
  353. }
  354. }
  355. }
  356. static rt_bool_t rtgui_image_jpeg_check(struct rtgui_filerw* file)
  357. {
  358. int start;
  359. rt_bool_t is_JPG;
  360. int in_scan;
  361. rt_uint8_t magic[4];
  362. if (file == RT_NULL) return RT_FALSE; /* open file failed */
  363. start = rtgui_filerw_tell(file);
  364. is_JPG = RT_FALSE;
  365. in_scan = 0;
  366. /* seek to the begining of file */
  367. rtgui_filerw_seek(file, 0, SEEK_SET);
  368. if ( rtgui_filerw_read(file, magic, 2, 1) ) {
  369. if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) {
  370. is_JPG = RT_TRUE;
  371. while (is_JPG == RT_TRUE) {
  372. if(rtgui_filerw_read(file, magic, 1, 2) != 2) {
  373. is_JPG = RT_FALSE;
  374. } else if( (magic[0] != 0xFF) && (in_scan == 0) ) {
  375. is_JPG = RT_FALSE;
  376. } else if( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) {
  377. /* Extra padding in JPEG (legal) */
  378. /* or this is data and we are scanning */
  379. rtgui_filerw_seek(file, -1, SEEK_CUR);
  380. } else if(magic[1] == 0xD9) {
  381. /* Got to end of good JPEG */
  382. break;
  383. } else if( (in_scan == 1) && (magic[1] == 0x00) ) {
  384. /* This is an encoded 0xFF within the data */
  385. } else if( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) {
  386. /* These have nothing else */
  387. } else if(rtgui_filerw_read(file, magic+2, 1, 2) != 2) {
  388. is_JPG = RT_FALSE;
  389. } else {
  390. /* Yes, it's big-endian */
  391. rt_uint32_t start;
  392. rt_uint32_t size;
  393. rt_uint32_t end;
  394. start = rtgui_filerw_tell(file);
  395. size = (magic[2] << 8) + magic[3];
  396. end = rtgui_filerw_seek(file, size-2, SEEK_CUR);
  397. if ( end != start + size - 2 ) is_JPG = RT_FALSE;
  398. if ( magic[1] == 0xDA ) {
  399. /* Now comes the actual JPEG meat */
  400. /* It is a JPEG. */
  401. break;
  402. }
  403. }
  404. }
  405. }
  406. }
  407. rtgui_filerw_seek(file, start, SEEK_SET);
  408. return is_JPG;
  409. }
  410. #endif