image_jpg.c 13 KB

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