image_jpg.c 13 KB

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