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
37 #include "upnpreplyparse.h"
38 #include "image_utils.h"
41 #if __BYTE_ORDER == __LITTLE_ENDIAN
42 # define SWAP16(w) ( (((w) >> 8) & 0x00ff) | (((w) << 8) & 0xff00) )
44 # define SWAP16(w) (w)
47 #define JPEG_QUALITY 96
49 #define COL(red, green, blue) (((red) << 24) | ((green) << 16) | ((blue) << 8) | 0xFF)
50 #define COL_FULL(red, green, blue, alpha) (((red) << 24) | ((green) << 16) | ((blue) << 8) | (alpha))
51 #define COL_RED(col) (col >> 24)
52 #define COL_GREEN(col) ((col >> 16) & 0xFF)
53 #define COL_BLUE(col) ((col >> 8) & 0xFF)
54 #define COL_ALPHA(col) (col & 0xFF)
55 #define BLACK 0x000000FF
59 struct jpeg_destination_mgr jdst
;
66 /* Destination manager to store data in a buffer */
68 my_dst_mgr_init(j_compress_ptr cinfo
)
70 struct my_dst_mgr
*dst
= (void *)cinfo
->dest
;
73 dst
->sz
= cinfo
->image_width
75 * cinfo
->input_components
;
76 dst
->buf
= malloc(dst
->sz
* sizeof *dst
->buf
);
78 dst
->jdst
.next_output_byte
= dst
->off
;
79 dst
->jdst
.free_in_buffer
= dst
->sz
;
86 my_dst_mgr_empty(j_compress_ptr cinfo
)
88 struct my_dst_mgr
*dst
= (void *)cinfo
->dest
;
91 dst
->used
= dst
->off
- dst
->buf
;
92 dst
->buf
= realloc(dst
->buf
, dst
->sz
* sizeof *dst
->buf
);
93 dst
->off
= dst
->buf
+ dst
->used
;
94 dst
->jdst
.next_output_byte
= dst
->off
;
95 dst
->jdst
.free_in_buffer
= dst
->sz
- dst
->used
;
102 my_dst_mgr_term(j_compress_ptr cinfo
)
104 struct my_dst_mgr
*dst
= (void *)cinfo
->dest
;
106 dst
->used
+= dst
->sz
- dst
->jdst
.free_in_buffer
;
107 dst
->off
= dst
->buf
+ dst
->used
;
114 jpeg_memory_dest(j_compress_ptr cinfo
, struct my_dst_mgr
*dst
)
116 dst
->jdst
.init_destination
= my_dst_mgr_init
;
117 dst
->jdst
.empty_output_buffer
= my_dst_mgr_empty
;
118 dst
->jdst
.term_destination
= my_dst_mgr_term
;
119 cinfo
->dest
= (void *)dst
;
125 /* Source manager to read data from a buffer */
129 struct jpeg_source_mgr pub
;
130 JOCTET eoi_buffer
[2];
134 init_source(j_decompress_ptr cinfo
)
140 fill_input_buffer(j_decompress_ptr cinfo
)
142 struct my_src_mgr
*src
= (void *)cinfo
->src
;
144 /* Create a fake EOI marker */
145 src
->eoi_buffer
[0] = (JOCTET
) 0xFF;
146 src
->eoi_buffer
[1] = (JOCTET
) JPEG_EOI
;
147 src
->pub
.next_input_byte
= src
->eoi_buffer
;
148 src
->pub
.bytes_in_buffer
= 2;
154 skip_input_data(j_decompress_ptr cinfo
, long num_bytes
)
156 struct my_src_mgr
*src
= (void *)cinfo
->src
;
159 while (num_bytes
> (long)src
->pub
.bytes_in_buffer
)
161 num_bytes
-= (long)src
->pub
.bytes_in_buffer
;
162 fill_input_buffer(cinfo
);
165 src
->pub
.next_input_byte
+= num_bytes
;
166 src
->pub
.bytes_in_buffer
-= num_bytes
;
170 term_source(j_decompress_ptr cinfo
)
176 jpeg_memory_src(j_decompress_ptr cinfo
, const unsigned char * buffer
, size_t bufsize
)
178 struct my_src_mgr
*src
;
182 cinfo
->src
= (*cinfo
->mem
->alloc_small
)((void *)cinfo
, JPOOL_PERMANENT
, sizeof(struct my_src_mgr
));;
184 src
= (void *)cinfo
->src
;
185 src
->pub
.init_source
= init_source
;
186 src
->pub
.fill_input_buffer
= fill_input_buffer
;
187 src
->pub
.skip_input_data
= skip_input_data
;
188 src
->pub
.resync_to_restart
= jpeg_resync_to_restart
;
189 src
->pub
.term_source
= term_source
;
190 src
->pub
.next_input_byte
= buffer
;
191 src
->pub
.bytes_in_buffer
= bufsize
;
194 jmp_buf setjmp_buffer
;
195 /* Don't exit on error like libjpeg likes to do */
197 libjpeg_error_handler(j_common_ptr cinfo
)
199 cinfo
->err
->output_message(cinfo
);
200 longjmp(setjmp_buffer
, 1);
205 image_free(image_s
*pimage
)
212 get_pix(image_s
*pimage
, int32_t x
, int32_t y
)
214 if((x
>= 0) && (y
>= 0) && (x
< pimage
->width
) && (y
< pimage
->height
))
216 return(pimage
->buf
[(y
* pimage
->width
) + x
]);
226 put_pix_alpha_replace(image_s
*pimage
, int32_t x
, int32_t y
, pix col
)
228 if((x
>= 0) && (y
>= 0) && (x
< pimage
->width
) && (y
< pimage
->height
))
229 pimage
->buf
[(y
* pimage
->width
) + x
] = col
;
233 image_get_jpeg_resolution(const char * path
, int * width
, int * height
)
236 unsigned char buf
[8];
237 uint16_t offset
, h
, w
;
243 img
= fopen(path
, "r");
247 fseek(img
, 0, SEEK_END
);
251 nread
= fread(&buf
, 2, 1, img
);
252 if( (nread
< 1) || (buf
[0] != 0xFF) || (buf
[1] != 0xD8) )
257 memset(&buf
, 0, sizeof(buf
));
259 while( ftell(img
) < size
)
261 while( nread
> 0 && buf
[0] != 0xFF && !feof(img
) )
262 nread
= fread(&buf
, 1, 1, img
);
264 while( nread
> 0 && buf
[0] == 0xFF && !feof(img
) )
265 nread
= fread(&buf
, 1, 1, img
);
267 if( (buf
[0] >= 0xc0) && (buf
[0] <= 0xc3) )
269 nread
= fread(&buf
, 7, 1, img
);
274 memcpy(&h
, buf
+3, 2);
276 memcpy(&w
, buf
+5, 2);
284 nread
= fread(&buf
, 2, 1, img
);
287 memcpy(&offset
, buf
, 2);
288 offset
= SWAP16(offset
) - 2;
289 if( fseek(img
, offset
, SEEK_CUR
) == -1 )
298 image_get_jpeg_date_xmp(const char * path
, char ** date
)
301 unsigned char buf
[8];
302 char *data
= NULL
, *newdata
;
304 struct NameValueParserData xml
;
309 img
= fopen(path
, "r");
313 nread
= fread(&buf
, 2, 1, img
);
314 if( (nread
< 1) || (buf
[0] != 0xFF) || (buf
[1] != 0xD8) )
319 memset(&buf
, 0, sizeof(buf
));
323 while( nread
> 0 && buf
[0] != 0xFF && !feof(img
) )
324 nread
= fread(&buf
, 1, 1, img
);
326 while( nread
> 0 && buf
[0] == 0xFF && !feof(img
) )
327 nread
= fread(&buf
, 1, 1, img
);
332 if( buf
[0] == 0xE1 ) // APP1 marker
335 nread
= fread(&buf
, 2, 1, img
);
338 memcpy(&offset
, buf
, 2);
339 offset
= SWAP16(offset
) - 2;
343 fseek(img
, offset
, SEEK_CUR
);
347 newdata
= realloc(data
, 30);
352 nread
= fread(data
, 29, 1, img
);
356 if( strcmp(data
, "http://ns.adobe.com/xap/1.0/") != 0 )
358 fseek(img
, offset
, SEEK_CUR
);
362 newdata
= realloc(data
, offset
+1);
366 nread
= fread(data
, offset
, 1, img
);
370 ParseNameValue(data
, offset
, &xml
);
371 exif
= GetValueFromNameValueList(&xml
, "DateTimeOriginal");
374 ClearNameValueList(&xml
);
377 *date
= realloc(*date
, strlen(exif
)+1);
379 ClearNameValueList(&xml
);
387 nread
= fread(&buf
, 2, 1, img
);
390 memcpy(&offset
, buf
, 2);
391 offset
= SWAP16(offset
) - 2;
392 fseek(img
, offset
, SEEK_CUR
);
402 image_new(int32_t width
, int32_t height
)
406 if((vimage
= (image_s
*)malloc(sizeof(image_s
))) == NULL
)
408 DPRINTF(E_WARN
, L_METADATA
, "malloc failed\n");
411 vimage
->width
= width
; vimage
->height
= height
;
413 if((vimage
->buf
= (pix
*)malloc(width
* height
* sizeof(pix
))) == NULL
)
415 DPRINTF(E_WARN
, L_METADATA
, "malloc failed\n");
423 image_new_from_jpeg(const char * path
, int is_file
, const char * buf
, int size
, int scale
, int rotate
)
427 struct jpeg_decompress_struct cinfo
;
428 unsigned char *line
[16], *ptr
;
429 int x
, y
, i
, w
, h
, ofs
;
431 struct jpeg_error_mgr pub
;
433 cinfo
.err
= jpeg_std_error(&pub
);
434 pub
.error_exit
= libjpeg_error_handler
;
435 jpeg_create_decompress(&cinfo
);
438 if( (file
= fopen(path
, "r")) == NULL
)
442 jpeg_stdio_src(&cinfo
, file
);
446 jpeg_memory_src(&cinfo
, (const unsigned char *)buf
, size
);
448 if( setjmp(setjmp_buffer
) )
450 jpeg_destroy_decompress(&cinfo
);
451 if( is_file
&& file
)
455 jpeg_read_header(&cinfo
, TRUE
);
456 cinfo
.scale_denom
= scale
;
457 cinfo
.do_fancy_upsampling
= FALSE
;
458 cinfo
.do_block_smoothing
= FALSE
;
459 jpeg_start_decompress(&cinfo
);
460 w
= cinfo
.output_width
;
461 h
= cinfo
.output_height
;
462 vimage
= (rotate
& (ROTATE_90
|ROTATE_270
)) ? image_new(h
, w
) : image_new(w
, h
);
465 jpeg_destroy_decompress(&cinfo
);
471 if( setjmp(setjmp_buffer
) )
473 jpeg_destroy_decompress(&cinfo
);
474 if( is_file
&& file
)
485 if(cinfo
.rec_outbuf_height
> 16)
487 DPRINTF(E_WARN
, L_METADATA
, "ERROR image_from_jpeg : (image_from_jpeg.c) JPEG uses line buffers > 16. Cannot load.\n");
493 maxbuf
= vimage
->width
* vimage
->height
;
494 if(cinfo
.output_components
== 3)
498 if((ptr
= malloc(w
* 3 * cinfo
.rec_outbuf_height
+ 16)) == NULL
)
500 DPRINTF(E_WARN
, L_METADATA
, "malloc failed\n");
507 for(y
= 0; y
< h
; y
+= cinfo
.rec_outbuf_height
)
509 ry
= (rotate
& (ROTATE_90
|ROTATE_180
)) ? (y
- h
+ 1) * -1 : y
;
510 for(i
= 0; i
< cinfo
.rec_outbuf_height
; i
++)
512 line
[i
] = ptr
+ (w
* 3 * i
);
514 jpeg_read_scanlines(&cinfo
, line
, cinfo
.rec_outbuf_height
);
515 for(x
= 0; x
< w
* cinfo
.rec_outbuf_height
; x
++)
517 rx
= (rotate
& (ROTATE_180
|ROTATE_270
)) ? (x
- w
+ 1) * -1 : x
;
518 ofs
= (rotate
& (ROTATE_90
|ROTATE_270
)) ? ry
+ (rx
* h
) : rx
+ (ry
* w
);
520 vimage
->buf
[ofs
] = COL(ptr
[x
+ x
+ x
], ptr
[x
+ x
+ x
+ 1], ptr
[x
+ x
+ x
+ 2]);
525 else if(cinfo
.output_components
== 1)
528 for(i
= 0; i
< cinfo
.rec_outbuf_height
; i
++)
530 if((line
[i
] = malloc(w
)) == NULL
)
534 for(t
= 0; t
< i
; t
++) free(line
[t
]);
535 jpeg_destroy_decompress(&cinfo
);
542 for(y
= 0; y
< h
; y
+= cinfo
.rec_outbuf_height
)
544 jpeg_read_scanlines(&cinfo
, line
, cinfo
.rec_outbuf_height
);
545 for(i
= 0; i
< cinfo
.rec_outbuf_height
; i
++)
547 for(x
= 0; x
< w
; x
++)
549 vimage
->buf
[ofs
++] = COL(line
[i
][x
], line
[i
][x
], line
[i
][x
]);
553 for(i
= 0; i
< cinfo
.rec_outbuf_height
; i
++)
558 jpeg_finish_decompress(&cinfo
);
559 jpeg_destroy_decompress(&cinfo
);
567 image_upsize(image_s
* pdest
, image_s
* psrc
, int32_t width
, int32_t height
)
570 #if !defined __i386__ && !defined __x86_64__
574 if((pdest
== NULL
) || (psrc
== NULL
))
577 for(vy
= 0; vy
< height
; vy
++)
579 for(vx
= 0; vx
< width
; vx
++)
581 rx
= ((vx
* psrc
->width
) / width
);
582 ry
= ((vy
* psrc
->height
) / height
);
583 vcol
= get_pix(psrc
, rx
, ry
);
585 pix vcol
,vcol1
,vcol2
,vcol3
,vcol4
;
587 float width_scale
, height_scale
;
588 float x_dist
, y_dist
;
590 width_scale
= (float)psrc
->width
/ (float)width
;
591 height_scale
= (float)psrc
->height
/ (float)height
;
593 for(vy
= 0;vy
< height
; vy
++)
595 for(vx
= 0;vx
< width
; vx
++)
597 rx
= vx
* width_scale
;
598 ry
= vy
* height_scale
;
599 vcol1
= get_pix(psrc
, (int32_t)rx
, (int32_t)ry
);
600 vcol2
= get_pix(psrc
, ((int32_t)rx
)+1, (int32_t)ry
);
601 vcol3
= get_pix(psrc
, (int32_t)rx
, ((int32_t)ry
)+1);
602 vcol4
= get_pix(psrc
, ((int32_t)rx
)+1, ((int32_t)ry
)+1);
604 x_dist
= rx
- ((float)((int32_t)rx
));
605 y_dist
= ry
- ((float)((int32_t)ry
));
606 vcol
= COL_FULL( (uint8_t)((COL_RED(vcol1
)*(1.0-x_dist
)
607 + COL_RED(vcol2
)*(x_dist
))*(1.0-y_dist
)
608 + (COL_RED(vcol3
)*(1.0-x_dist
)
609 + COL_RED(vcol4
)*(x_dist
))*(y_dist
)),
610 (uint8_t)((COL_GREEN(vcol1
)*(1.0-x_dist
)
611 + COL_GREEN(vcol2
)*(x_dist
))*(1.0-y_dist
)
612 + (COL_GREEN(vcol3
)*(1.0-x_dist
)
613 + COL_GREEN(vcol4
)*(x_dist
))*(y_dist
)),
614 (uint8_t)((COL_BLUE(vcol1
)*(1.0-x_dist
)
615 + COL_BLUE(vcol2
)*(x_dist
))*(1.0-y_dist
)
616 + (COL_BLUE(vcol3
)*(1.0-x_dist
)
617 + COL_BLUE(vcol4
)*(x_dist
))*(y_dist
)),
618 (uint8_t)((COL_ALPHA(vcol1
)*(1.0-x_dist
)
619 + COL_ALPHA(vcol2
)*(x_dist
))*(1.0-y_dist
)
620 + (COL_ALPHA(vcol3
)*(1.0-x_dist
)
621 + COL_ALPHA(vcol4
)*(x_dist
))*(y_dist
))
624 put_pix_alpha_replace(pdest
, vx
, vy
, vcol
);
630 image_downsize(image_s
* pdest
, image_s
* psrc
, int32_t width
, int32_t height
)
635 #if !defined __i386__ && !defined __x86_64__
636 int32_t rx
, ry
, rx_next
, ry_next
;
637 int red
, green
, blue
, alpha
;
640 if((pdest
== NULL
) || (psrc
== NULL
))
643 for(vy
= 0; vy
< height
; vy
++)
645 for(vx
= 0; vx
< width
; vx
++)
648 rx
= ((vx
* psrc
->width
) / width
);
649 ry
= ((vy
* psrc
->height
) / height
);
651 red
= green
= blue
= alpha
= 0;
653 rx_next
= rx
+ (psrc
->width
/ width
);
654 ry_next
= ry
+ (psrc
->width
/ width
);
657 for( j
= rx
; j
< rx_next
; j
++)
659 for( i
= ry
; i
< ry_next
; i
++)
662 vcol
= get_pix(psrc
, j
, i
);
664 red
+= COL_RED(vcol
);
665 green
+= COL_GREEN(vcol
);
666 blue
+= COL_BLUE(vcol
);
667 alpha
+= COL_ALPHA(vcol
);
676 /* on sature les valeurs */
677 red
= (red
> 255) ? 255 : ((red
< 0) ? 0 : red
);
678 green
= (green
> 255) ? 255 : ((green
< 0) ? 0 : green
);
679 blue
= (blue
> 255) ? 255 : ((blue
< 0) ? 0 : blue
);
680 alpha
= (alpha
> 255) ? 255 : ((alpha
< 0) ? 0 : alpha
);
683 float width_scale
, height_scale
;
684 float red
, green
, blue
, alpha
;
685 int32_t half_square_width
, half_square_height
;
686 float round_width
, round_height
;
688 if( (pdest
== NULL
) || (psrc
== NULL
) )
691 width_scale
= (float)psrc
->width
/ (float)width
;
692 height_scale
= (float)psrc
->height
/ (float)height
;
694 half_square_width
= (int32_t)(width_scale
/ 2.0);
695 half_square_height
= (int32_t)(height_scale
/ 2.0);
696 round_width
= (width_scale
/ 2.0) - (float)half_square_width
;
697 round_height
= (height_scale
/ 2.0) - (float)half_square_height
;
698 if(round_width
> 0.0)
702 if(round_height
> 0.0)
703 half_square_height
++;
707 for(vy
= 0;vy
< height
; vy
++)
709 for(vx
= 0;vx
< width
; vx
++)
711 rx
= vx
* width_scale
;
712 ry
= vy
* height_scale
;
713 vcol
= get_pix(psrc
, (int32_t)rx
, (int32_t)ry
);
715 red
= green
= blue
= alpha
= 0.0;
717 for(j
=0;j
<half_square_height
<<1;j
++)
719 for(i
=0;i
<half_square_width
<<1;i
++)
721 vcol
= get_pix(psrc
, ((int32_t)rx
)-half_square_width
+i
,
722 ((int32_t)ry
)-half_square_height
+j
);
724 if(((j
== 0) || (j
== (half_square_height
<<1)-1)) &&
725 ((i
== 0) || (i
== (half_square_width
<<1)-1)))
727 red
+= round_width
*round_height
*(float)COL_RED (vcol
);
728 green
+= round_width
*round_height
*(float)COL_GREEN(vcol
);
729 blue
+= round_width
*round_height
*(float)COL_BLUE (vcol
);
730 alpha
+= round_width
*round_height
*(float)COL_ALPHA(vcol
);
732 else if((j
== 0) || (j
== (half_square_height
<<1)-1))
734 red
+= round_height
*(float)COL_RED (vcol
);
735 green
+= round_height
*(float)COL_GREEN(vcol
);
736 blue
+= round_height
*(float)COL_BLUE (vcol
);
737 alpha
+= round_height
*(float)COL_ALPHA(vcol
);
739 else if((i
== 0) || (i
== (half_square_width
<<1)-1))
741 red
+= round_width
*(float)COL_RED (vcol
);
742 green
+= round_width
*(float)COL_GREEN(vcol
);
743 blue
+= round_width
*(float)COL_BLUE (vcol
);
744 alpha
+= round_width
*(float)COL_ALPHA(vcol
);
748 red
+= (float)COL_RED (vcol
);
749 green
+= (float)COL_GREEN(vcol
);
750 blue
+= (float)COL_BLUE (vcol
);
751 alpha
+= (float)COL_ALPHA(vcol
);
756 red
/= width_scale
*height_scale
;
757 green
/= width_scale
*height_scale
;
758 blue
/= width_scale
*height_scale
;
759 alpha
/= width_scale
*height_scale
;
761 /* on sature les valeurs */
762 red
= (red
> 255.0)? 255.0 : ((red
< 0.0)? 0.0:red
);
763 green
= (green
> 255.0)? 255.0 : ((green
< 0.0)? 0.0:green
);
764 blue
= (blue
> 255.0)? 255.0 : ((blue
< 0.0)? 0.0:blue
);
765 alpha
= (alpha
> 255.0)? 255.0 : ((alpha
< 0.0)? 0.0:alpha
);
767 put_pix_alpha_replace(pdest
, vx
, vy
,
768 COL_FULL((uint8_t)red
, (uint8_t)green
, (uint8_t)blue
, (uint8_t)alpha
));
774 image_resize(image_s
* src_image
, int32_t width
, int32_t height
)
778 dst_image
= image_new(width
, height
);
781 if( (src_image
->width
< width
) || (src_image
->height
< height
) )
782 image_upsize(dst_image
, src_image
, width
, height
);
784 image_downsize(dst_image
, src_image
, width
, height
);
791 image_save_to_jpeg_buf(image_s
* pimage
, int * size
)
793 struct jpeg_compress_struct cinfo
;
794 struct jpeg_error_mgr jerr
;
795 JSAMPROW row_pointer
[1];
799 struct my_dst_mgr dst
;
801 cinfo
.err
= jpeg_std_error(&jerr
);
802 jpeg_create_compress(&cinfo
);
803 jpeg_memory_dest(&cinfo
, &dst
);
804 cinfo
.image_width
= pimage
->width
;
805 cinfo
.image_height
= pimage
->height
;
806 cinfo
.input_components
= 3;
807 cinfo
.in_color_space
= JCS_RGB
;
808 jpeg_set_defaults(&cinfo
);
809 jpeg_set_quality(&cinfo
, JPEG_QUALITY
, TRUE
);
810 jpeg_start_compress(&cinfo
, TRUE
);
811 row_stride
= cinfo
.image_width
* 3;
812 if((data
= malloc(row_stride
)) == NULL
)
814 DPRINTF(E_WARN
, L_METADATA
, "malloc failed\n");
818 while(cinfo
.next_scanline
< cinfo
.image_height
)
820 for(x
= 0; x
< pimage
->width
; x
++)
822 data
[x
* 3] = COL_RED(pimage
->buf
[i
]);
823 data
[x
* 3 + 1] = COL_GREEN(pimage
->buf
[i
]);
824 data
[x
* 3 + 2] = COL_BLUE(pimage
->buf
[i
]);
827 row_pointer
[0] = (unsigned char *)data
;
828 jpeg_write_scanlines(&cinfo
, row_pointer
, 1);
830 jpeg_finish_compress(&cinfo
);
833 jpeg_destroy_compress(&cinfo
);
839 image_save_to_jpeg_file(image_s
* pimage
, const char * path
)
841 int nwritten
, size
= 0;
845 buf
= image_save_to_jpeg_buf(pimage
, &size
);
848 dst_file
= fopen(path
, "w");
854 nwritten
= fwrite(buf
, 1, size
, dst_file
);
858 return (nwritten
==size
? 0 : 1);