MiniDLNA update: 1.0.19.1 to 1.0.20
[tomato.git] / release / src / router / minidlna / image_utils.c
blob79b46251803167a8aee5088df5877269a64faef4
1 /* MiniDLNA media server
2 * Copyright (C) 2009 Justin Maggard
4 * This file is part of MiniDLNA.
6 * MiniDLNA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * MiniDLNA is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
19 /* These functions are mostly based on code from other projects.
20 * There are function to effiently resize a JPEG image, and some utility functions.
21 * They are here to allow loading and saving JPEG data directly to or from memory with libjpeg.
22 * The standard functions only allow you to read from or write to a file.
24 * The reading code comes from the JpgAlleg library, at http://wiki.allegro.cc/index.php?title=Libjpeg
25 * The writing code was posted on a Google group from openjpeg, at http://groups.google.com/group/openjpeg/browse_thread/thread/331e6cf60f70797f
26 * The resize functions come from the resize_image project, at http://www.golac.fr/Image-Resizer
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <setjmp.h>
35 #include <jpeglib.h>
36 #include <endian.h>
38 #include "upnpreplyparse.h"
39 #include "image_utils.h"
40 #include "log.h"
42 #if __BYTE_ORDER == __LITTLE_ENDIAN
43 # define SWAP16(w) ( (((w) >> 8) & 0x00ff) | (((w) << 8) & 0xff00) )
44 #else
45 # define SWAP16(w) (w)
46 #endif
48 #define JPEG_QUALITY 96
50 #define COL(red, green, blue) (((red) << 24) | ((green) << 16) | ((blue) << 8) | 0xFF)
51 #define COL_FULL(red, green, blue, alpha) (((red) << 24) | ((green) << 16) | ((blue) << 8) | (alpha))
52 #define COL_RED(col) (col >> 24)
53 #define COL_GREEN(col) ((col >> 16) & 0xFF)
54 #define COL_BLUE(col) ((col >> 8) & 0xFF)
55 #define COL_ALPHA(col) (col & 0xFF)
56 #define BLACK 0x000000FF
59 struct my_dst_mgr {
60 struct jpeg_destination_mgr jdst;
61 JOCTET *buf;
62 JOCTET *off;
63 size_t sz;
64 size_t used;
67 /* Destination manager to store data in a buffer */
68 static void
69 my_dst_mgr_init(j_compress_ptr cinfo)
71 struct my_dst_mgr *dst = (void *)cinfo->dest;
73 dst->used = 0;
74 dst->sz = cinfo->image_width
75 * cinfo->image_height
76 * cinfo->input_components;
77 dst->buf = malloc(dst->sz * sizeof *dst->buf);
78 dst->off = dst->buf;
79 dst->jdst.next_output_byte = dst->off;
80 dst->jdst.free_in_buffer = dst->sz;
82 return;
86 static boolean
87 my_dst_mgr_empty(j_compress_ptr cinfo)
89 struct my_dst_mgr *dst = (void *)cinfo->dest;
91 dst->sz *= 2;
92 dst->used = dst->off - dst->buf;
93 dst->buf = realloc(dst->buf, dst->sz * sizeof *dst->buf);
94 dst->off = dst->buf + dst->used;
95 dst->jdst.next_output_byte = dst->off;
96 dst->jdst.free_in_buffer = dst->sz - dst->used;
98 return TRUE;
102 static void
103 my_dst_mgr_term(j_compress_ptr cinfo)
105 struct my_dst_mgr *dst = (void *)cinfo->dest;
107 dst->used += dst->sz - dst->jdst.free_in_buffer;
108 dst->off = dst->buf + dst->used;
110 return;
114 static void
115 jpeg_memory_dest(j_compress_ptr cinfo, struct my_dst_mgr *dst)
117 dst->jdst.init_destination = my_dst_mgr_init;
118 dst->jdst.empty_output_buffer = my_dst_mgr_empty;
119 dst->jdst.term_destination = my_dst_mgr_term;
120 cinfo->dest = (void *)dst;
122 return;
126 /* Source manager to read data from a buffer */
127 struct
128 my_src_mgr
130 struct jpeg_source_mgr pub;
131 JOCTET eoi_buffer[2];
134 static void
135 init_source(j_decompress_ptr cinfo)
137 return;
140 static int
141 fill_input_buffer(j_decompress_ptr cinfo)
143 struct my_src_mgr *src = (void *)cinfo->src;
145 /* Create a fake EOI marker */
146 src->eoi_buffer[0] = (JOCTET) 0xFF;
147 src->eoi_buffer[1] = (JOCTET) JPEG_EOI;
148 src->pub.next_input_byte = src->eoi_buffer;
149 src->pub.bytes_in_buffer = 2;
151 return TRUE;
154 static void
155 skip_input_data(j_decompress_ptr cinfo, long num_bytes)
157 struct my_src_mgr *src = (void *)cinfo->src;
158 if (num_bytes > 0)
160 while (num_bytes > (long)src->pub.bytes_in_buffer)
162 num_bytes -= (long)src->pub.bytes_in_buffer;
163 fill_input_buffer(cinfo);
166 src->pub.next_input_byte += num_bytes;
167 src->pub.bytes_in_buffer -= num_bytes;
170 static void
171 term_source(j_decompress_ptr cinfo)
173 return;
176 void
177 jpeg_memory_src(j_decompress_ptr cinfo, const unsigned char * buffer, size_t bufsize)
179 struct my_src_mgr *src;
181 if (! cinfo->src)
183 cinfo->src = (*cinfo->mem->alloc_small)((void *)cinfo, JPOOL_PERMANENT, sizeof(struct my_src_mgr));;
185 src = (void *)cinfo->src;
186 src->pub.init_source = init_source;
187 src->pub.fill_input_buffer = fill_input_buffer;
188 src->pub.skip_input_data = skip_input_data;
189 src->pub.resync_to_restart = jpeg_resync_to_restart;
190 src->pub.term_source = term_source;
191 src->pub.next_input_byte = buffer;
192 src->pub.bytes_in_buffer = bufsize;
195 jmp_buf setjmp_buffer;
196 /* Don't exit on error like libjpeg likes to do */
197 static void
198 libjpeg_error_handler(j_common_ptr cinfo)
200 cinfo->err->output_message(cinfo);
201 longjmp(setjmp_buffer, 1);
202 return;
205 void
206 image_free(image_s *pimage)
208 free(pimage->buf);
209 free(pimage);
213 get_pix(image_s *pimage, int32_t x, int32_t y)
215 if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height))
217 return(pimage->buf[(y * pimage->width) + x]);
219 else
221 pix vpix = BLACK;
222 return(vpix);
226 void
227 put_pix_alpha_replace(image_s *pimage, int32_t x, int32_t y, pix col)
229 if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height))
230 pimage->buf[(y * pimage->width) + x] = col;
234 image_get_jpeg_resolution(const char * path, int * width, int * height)
236 FILE *img;
237 unsigned char buf[8];
238 u_int16_t offset, h, w;
239 int ret = 1;
240 long size;
243 img = fopen(path, "r");
244 if( !img )
245 return(-1);
247 fseek(img, 0, SEEK_END);
248 size = ftell(img);
249 rewind(img);
251 fread(&buf, 2, 1, img);
252 if( (buf[0] != 0xFF) || (buf[1] != 0xD8) )
254 fclose(img);
255 return(-1);
257 memset(&buf, 0, sizeof(buf));
259 while( ftell(img) < size )
261 while( buf[0] != 0xFF && !feof(img) )
262 fread(&buf, 1, 1, img);
264 while( buf[0] == 0xFF && !feof(img) )
265 fread(&buf, 1, 1, img);
267 if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) )
269 fread(&buf, 7, 1, img);
270 *width = 0;
271 *height = 0;
272 memcpy(&h, buf+3, 2);
273 *height = SWAP16(h);
274 memcpy(&w, buf+5, 2);
275 *width = SWAP16(w);
276 ret = 0;
277 break;
279 else
281 offset = 0;
282 fread(&buf, 2, 1, img);
283 memcpy(&offset, buf, 2);
284 offset = SWAP16(offset) - 2;
285 if( fseek(img, offset, SEEK_CUR) == -1 )
286 break;
289 fclose(img);
290 return ret;
294 image_get_jpeg_date_xmp(const char * path, char ** date)
296 FILE *img;
297 unsigned char buf[8];
298 char *data = NULL, *newdata;
299 u_int16_t offset;
300 struct NameValueParserData xml;
301 char * exif;
302 int ret = 1;
304 img = fopen(path, "r");
305 if( !img )
306 return(-1);
308 fread(&buf, 2, 1, img);
309 if( (buf[0] != 0xFF) || (buf[1] != 0xD8) )
311 fclose(img);
312 return(-1);
314 memset(&buf, 0, sizeof(buf));
316 while( !feof(img) )
318 while( buf[0] != 0xFF && !feof(img) )
319 fread(&buf, 1, 1, img);
321 while( buf[0] == 0xFF && !feof(img) )
322 fread(&buf, 1, 1, img);
324 if( feof(img) )
325 break;
327 if( buf[0] == 0xE1 ) // APP1 marker
329 offset = 0;
330 fread(&buf, 2, 1, img);
331 memcpy(&offset, buf, 2);
332 offset = SWAP16(offset) - 2;
334 if( offset < 30 )
336 fseek(img, offset, SEEK_CUR);
337 continue;
340 newdata = realloc(data, 30);
341 if( !newdata )
342 break;
343 data = newdata;
345 fread(data, 29, 1, img);
346 offset -= 29;
347 if( strcmp(data, "http://ns.adobe.com/xap/1.0/") != 0 )
349 fseek(img, offset, SEEK_CUR);
350 continue;
353 newdata = realloc(data, offset+1);
354 if( !newdata )
355 break;
356 data = newdata;
357 fread(data, offset, 1, img);
359 ParseNameValue(data, offset, &xml);
360 exif = GetValueFromNameValueList(&xml, "DateTimeOriginal");
361 if( !exif )
363 ClearNameValueList(&xml);
364 break;
366 *date = realloc(*date, strlen(exif)+1);
367 strcpy(*date, exif);
368 ClearNameValueList(&xml);
370 ret = 0;
371 break;
373 else
375 offset = 0;
376 fread(&buf, 2, 1, img);
377 memcpy(&offset, buf, 2);
378 offset = SWAP16(offset) - 2;
379 fseek(img, offset, SEEK_CUR);
382 fclose(img);
383 if( data )
384 free(data);
385 return ret;
388 image_s *
389 image_new(int32_t width, int32_t height)
391 image_s *vimage;
393 if((vimage = (image_s *)malloc(sizeof(image_s))) == NULL)
395 DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
396 return NULL;
398 vimage->width = width; vimage->height = height;
400 if((vimage->buf = (pix *)malloc(width * height * sizeof(pix))) == NULL)
402 DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
403 free(vimage);
404 return NULL;
406 return(vimage);
409 image_s *
410 image_new_from_jpeg(const char * path, int is_file, const char * buf, int size, int scale)
412 image_s *vimage;
413 FILE *file = NULL;
414 struct jpeg_decompress_struct cinfo;
415 unsigned char *line[16], *ptr;
416 int x, y, i, w, h, ofs;
417 int maxbuf;
418 struct jpeg_error_mgr pub;
421 cinfo.err = jpeg_std_error(&pub);
422 pub.error_exit = libjpeg_error_handler;
423 jpeg_create_decompress(&cinfo);
424 if( is_file )
426 if( (file = fopen(path, "r")) == NULL )
428 return NULL;
430 jpeg_stdio_src(&cinfo, file);
432 else
434 jpeg_memory_src(&cinfo, (const unsigned char *)buf, size);
436 if( setjmp(setjmp_buffer) )
438 jpeg_destroy_decompress(&cinfo);
439 if( is_file && file )
440 fclose(file);
441 return NULL;
443 jpeg_read_header(&cinfo, TRUE);
444 cinfo.scale_denom = scale;
445 cinfo.do_fancy_upsampling = FALSE;
446 cinfo.do_block_smoothing = FALSE;
447 jpeg_start_decompress(&cinfo);
448 w = cinfo.output_width;
449 h = cinfo.output_height;
450 vimage = image_new(w, h);
451 if(!vimage)
453 jpeg_destroy_decompress(&cinfo);
454 if( is_file )
455 fclose(file);
456 return NULL;
459 if( setjmp(setjmp_buffer) )
461 jpeg_destroy_decompress(&cinfo);
462 if( is_file && file )
463 fclose(file);
464 if( vimage )
466 if( vimage->buf )
467 free(vimage->buf);
468 free(vimage);
470 return NULL;
473 if(cinfo.rec_outbuf_height > 16)
475 DPRINTF(E_WARN, L_METADATA, "ERROR image_from_jpeg : (image_from_jpeg.c) JPEG uses line buffers > 16. Cannot load.\n");
476 image_free(vimage);
477 if( is_file )
478 fclose(file);
479 return NULL;
481 maxbuf = vimage->width * vimage->height;
482 if(cinfo.output_components == 3)
484 ofs = 0;
485 if((ptr = (unsigned char *)malloc(w * 3 * cinfo.rec_outbuf_height + 8)) == NULL)
487 DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
488 image_free(vimage);
489 if( is_file )
490 fclose(file);
491 return NULL;
494 for(y = 0; y < h; y += cinfo.rec_outbuf_height)
496 for(i = 0; i < cinfo.rec_outbuf_height; i++)
498 line[i] = ptr + (w * 3 * i);
500 jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
501 for(x = 0; x < w * cinfo.rec_outbuf_height; x++)
503 if( ofs < maxbuf )
505 vimage->buf[ofs] = COL(ptr[x + x + x], ptr[x + x + x + 1], ptr[x + x + x + 2]);
506 ofs++;
510 free(ptr);
512 else if(cinfo.output_components == 1)
514 ofs = 0;
515 for(i = 0; i < cinfo.rec_outbuf_height; i++)
517 if((line[i] = (unsigned char *)malloc(w)) == NULL)
519 int t = 0;
521 for(t = 0; t < i; t++) free(line[t]);
522 jpeg_destroy_decompress(&cinfo);
523 image_free(vimage);
524 if( is_file )
525 fclose(file);
526 return NULL;
529 for(y = 0; y < h; y += cinfo.rec_outbuf_height)
531 jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
532 for(i = 0; i < cinfo.rec_outbuf_height; i++)
534 for(x = 0; x < w; x++)
536 vimage->buf[ofs++] = COL(line[i][x], line[i][x], line[i][x]);
540 for(i = 0; i < cinfo.rec_outbuf_height; i++)
542 free(line[i]);
545 jpeg_finish_decompress(&cinfo);
546 jpeg_destroy_decompress(&cinfo);
547 if( is_file )
548 fclose(file);
550 return vimage;
553 void
554 image_upsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
556 int32_t vx, vy;
557 #if !defined __i386__ && !defined __x86_64__
558 int32_t rx, ry;
559 pix vcol;
561 if((pdest == NULL) || (psrc == NULL))
562 return;
564 for(vy = 0; vy < height; vy++)
566 for(vx = 0; vx < width; vx++)
568 rx = ((vx * psrc->width) / width);
569 ry = ((vy * psrc->height) / height);
570 vcol = get_pix(psrc, rx, ry);
571 #else
572 pix vcol,vcol1,vcol2,vcol3,vcol4;
573 float rx,ry;
574 float width_scale, height_scale;
575 float x_dist, y_dist;
577 width_scale = (float)psrc->width / (float)width;
578 height_scale = (float)psrc->height / (float)height;
580 for(vy = 0;vy < height; vy++)
582 for(vx = 0;vx < width; vx++)
584 rx = vx * width_scale;
585 ry = vy * height_scale;
586 vcol1 = get_pix(psrc, (int32_t)rx, (int32_t)ry);
587 vcol2 = get_pix(psrc, ((int32_t)rx)+1, (int32_t)ry);
588 vcol3 = get_pix(psrc, (int32_t)rx, ((int32_t)ry)+1);
589 vcol4 = get_pix(psrc, ((int32_t)rx)+1, ((int32_t)ry)+1);
591 x_dist = rx - ((float)((int32_t)rx));
592 y_dist = ry - ((float)((int32_t)ry));
593 vcol = COL_FULL( (u_int8_t)((COL_RED(vcol1)*(1.0-x_dist)
594 + COL_RED(vcol2)*(x_dist))*(1.0-y_dist)
595 + (COL_RED(vcol3)*(1.0-x_dist)
596 + COL_RED(vcol4)*(x_dist))*(y_dist)),
597 (u_int8_t)((COL_GREEN(vcol1)*(1.0-x_dist)
598 + COL_GREEN(vcol2)*(x_dist))*(1.0-y_dist)
599 + (COL_GREEN(vcol3)*(1.0-x_dist)
600 + COL_GREEN(vcol4)*(x_dist))*(y_dist)),
601 (u_int8_t)((COL_BLUE(vcol1)*(1.0-x_dist)
602 + COL_BLUE(vcol2)*(x_dist))*(1.0-y_dist)
603 + (COL_BLUE(vcol3)*(1.0-x_dist)
604 + COL_BLUE(vcol4)*(x_dist))*(y_dist)),
605 (u_int8_t)((COL_ALPHA(vcol1)*(1.0-x_dist)
606 + COL_ALPHA(vcol2)*(x_dist))*(1.0-y_dist)
607 + (COL_ALPHA(vcol3)*(1.0-x_dist)
608 + COL_ALPHA(vcol4)*(x_dist))*(y_dist))
610 #endif
611 put_pix_alpha_replace(pdest, vx, vy, vcol);
616 void
617 image_downsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
619 int32_t vx, vy;
620 pix vcol;
621 int32_t i, j;
622 #if !defined __i386__ && !defined __x86_64__
623 int32_t rx, ry, rx_next, ry_next;
624 int red, green, blue, alpha;
625 int factor;
627 if((pdest == NULL) || (psrc == NULL))
628 return;
630 for(vy = 0; vy < height; vy++)
632 for(vx = 0; vx < width; vx++)
635 rx = ((vx * psrc->width) / width);
636 ry = ((vy * psrc->height) / height);
638 red = green = blue = alpha = 0;
640 rx_next = rx + (psrc->width / width);
641 ry_next = ry + (psrc->width / width);
642 factor = 0;
644 for( j = rx; j < rx_next; j++)
646 for( i = ry; i < ry_next; i++)
648 factor += 1;
649 vcol = get_pix(psrc, j, i);
651 red += COL_RED(vcol);
652 green += COL_GREEN(vcol);
653 blue += COL_BLUE(vcol);
654 alpha += COL_ALPHA(vcol);
658 red /= factor;
659 green /= factor;
660 blue /= factor;
661 alpha /= factor;
663 /* on sature les valeurs */
664 red = (red > 255) ? 255 : ((red < 0) ? 0 : red );
665 green = (green > 255) ? 255 : ((green < 0) ? 0 : green);
666 blue = (blue > 255) ? 255 : ((blue < 0) ? 0 : blue );
667 alpha = (alpha > 255) ? 255 : ((alpha < 0) ? 0 : alpha);
668 #else
669 float rx,ry;
670 float width_scale, height_scale;
671 float red, green, blue, alpha;
672 int32_t half_square_width, half_square_height;
673 float round_width, round_height;
675 if( (pdest == NULL) || (psrc == NULL) )
676 return;
678 width_scale = (float)psrc->width / (float)width;
679 height_scale = (float)psrc->height / (float)height;
681 half_square_width = (int32_t)(width_scale / 2.0);
682 half_square_height = (int32_t)(height_scale / 2.0);
683 round_width = (width_scale / 2.0) - (float)half_square_width;
684 round_height = (height_scale / 2.0) - (float)half_square_height;
685 if(round_width > 0.0)
686 half_square_width++;
687 else
688 round_width = 1.0;
689 if(round_height > 0.0)
690 half_square_height++;
691 else
692 round_height = 1.0;
694 for(vy = 0;vy < height; vy++)
696 for(vx = 0;vx < width; vx++)
698 rx = vx * width_scale;
699 ry = vy * height_scale;
700 vcol = get_pix(psrc, (int32_t)rx, (int32_t)ry);
702 red = green = blue = alpha = 0.0;
704 for(j=0;j<half_square_height<<1;j++)
706 for(i=0;i<half_square_width<<1;i++)
708 vcol = get_pix(psrc, ((int32_t)rx)-half_square_width+i,
709 ((int32_t)ry)-half_square_height+j);
711 if(((j == 0) || (j == (half_square_height<<1)-1)) &&
712 ((i == 0) || (i == (half_square_width<<1)-1)))
714 red += round_width*round_height*(float)COL_RED (vcol);
715 green += round_width*round_height*(float)COL_GREEN(vcol);
716 blue += round_width*round_height*(float)COL_BLUE (vcol);
717 alpha += round_width*round_height*(float)COL_ALPHA(vcol);
719 else if((j == 0) || (j == (half_square_height<<1)-1))
721 red += round_height*(float)COL_RED (vcol);
722 green += round_height*(float)COL_GREEN(vcol);
723 blue += round_height*(float)COL_BLUE (vcol);
724 alpha += round_height*(float)COL_ALPHA(vcol);
726 else if((i == 0) || (i == (half_square_width<<1)-1))
728 red += round_width*(float)COL_RED (vcol);
729 green += round_width*(float)COL_GREEN(vcol);
730 blue += round_width*(float)COL_BLUE (vcol);
731 alpha += round_width*(float)COL_ALPHA(vcol);
733 else
735 red += (float)COL_RED (vcol);
736 green += (float)COL_GREEN(vcol);
737 blue += (float)COL_BLUE (vcol);
738 alpha += (float)COL_ALPHA(vcol);
743 red /= width_scale*height_scale;
744 green /= width_scale*height_scale;
745 blue /= width_scale*height_scale;
746 alpha /= width_scale*height_scale;
748 /* on sature les valeurs */
749 red = (red > 255.0)? 255.0 : ((red < 0.0)? 0.0:red );
750 green = (green > 255.0)? 255.0 : ((green < 0.0)? 0.0:green);
751 blue = (blue > 255.0)? 255.0 : ((blue < 0.0)? 0.0:blue );
752 alpha = (alpha > 255.0)? 255.0 : ((alpha < 0.0)? 0.0:alpha);
753 #endif
754 put_pix_alpha_replace(pdest, vx, vy,
755 COL_FULL((u_int8_t)red, (u_int8_t)green, (u_int8_t)blue, (u_int8_t)alpha));
760 image_s *
761 image_resize(image_s * src_image, int32_t width, int32_t height)
763 image_s * dst_image;
765 dst_image = image_new(width, height);
766 if( !dst_image )
767 return NULL;
768 if( (src_image->width < width) || (src_image->height < height) )
769 image_upsize(dst_image, src_image, width, height);
770 else
771 image_downsize(dst_image, src_image, width, height);
773 return dst_image;
777 unsigned char *
778 image_save_to_jpeg_buf(image_s * pimage, int * size)
780 struct jpeg_compress_struct cinfo;
781 struct jpeg_error_mgr jerr;
782 JSAMPROW row_pointer[1];
783 int row_stride;
784 char *data;
785 int i, x;
786 struct my_dst_mgr dst;
788 cinfo.err = jpeg_std_error(&jerr);
789 jpeg_create_compress(&cinfo);
790 jpeg_memory_dest(&cinfo, &dst);
791 cinfo.image_width = pimage->width;
792 cinfo.image_height = pimage->height;
793 cinfo.input_components = 3;
794 cinfo.in_color_space = JCS_RGB;
795 jpeg_set_defaults(&cinfo);
796 jpeg_set_quality(&cinfo, JPEG_QUALITY, TRUE);
797 jpeg_start_compress(&cinfo, TRUE);
798 row_stride = cinfo.image_width * 3;
799 if((data = malloc(row_stride)) == NULL)
801 DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
802 return NULL;
804 i = 0;
805 while(cinfo.next_scanline < cinfo.image_height)
807 for(x = 0; x < pimage->width; x++)
809 data[x + x + x] = COL_RED(pimage->buf[i]);
810 data[x + x + x + 1] = COL_GREEN(pimage->buf[i]);
811 data[x + x + x + 2] = COL_BLUE(pimage->buf[i]);
812 i++;
814 row_pointer[0] = (unsigned char *)data;
815 jpeg_write_scanlines(&cinfo, row_pointer, 1);
817 jpeg_finish_compress(&cinfo);
818 *size = dst.used;
819 free(data);
820 jpeg_destroy_compress(&cinfo);
822 return dst.buf;
826 image_save_to_jpeg_file(image_s * pimage, const char * path)
828 int nwritten, size = 0;
829 unsigned char * buf;
830 FILE * dst_file;
832 buf = image_save_to_jpeg_buf(pimage, &size);
833 if( !buf )
834 return -1;
835 dst_file = fopen(path, "w");
836 if( !dst_file )
838 free(buf);
839 return -1;
841 nwritten = fwrite(buf, 1, size, dst_file);
842 fclose(dst_file);
843 free(buf);
845 return (nwritten==size ? 0 : 1);