Wrap up version 1.3.3.
[minidlna.git] / image_utils.c
blobfc85968b6b181cbbcce2469400feec9ed61daf27
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 "config.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <setjmp.h>
35 #include <jpeglib.h>
36 #ifdef HAVE_MACHINE_ENDIAN_H
37 #include <machine/endian.h>
38 #else
39 #include <endian.h>
40 #endif
42 #include "upnpreplyparse.h"
43 #include "image_utils.h"
44 #include "log.h"
46 #if __BYTE_ORDER == __LITTLE_ENDIAN
47 # define SWAP16(w) ( (((w) >> 8) & 0x00ff) | (((w) << 8) & 0xff00) )
48 #else
49 # define SWAP16(w) (w)
50 #endif
52 #define JPEG_QUALITY 96
54 #define COL(red, green, blue) (((red) << 24) | ((green) << 16) | ((blue) << 8) | 0xFF)
55 #define COL_FULL(red, green, blue, alpha) (((red) << 24) | ((green) << 16) | ((blue) << 8) | (alpha))
56 #define COL_RED(col) (col >> 24)
57 #define COL_GREEN(col) ((col >> 16) & 0xFF)
58 #define COL_BLUE(col) ((col >> 8) & 0xFF)
59 #define COL_ALPHA(col) (col & 0xFF)
60 #define BLACK 0x000000FF
63 struct my_dst_mgr {
64 struct jpeg_destination_mgr jdst;
65 JOCTET *buf;
66 JOCTET *off;
67 size_t sz;
68 size_t used;
71 /* Destination manager to store data in a buffer */
72 static void
73 my_dst_mgr_init(j_compress_ptr cinfo)
75 struct my_dst_mgr *dst = (void *)cinfo->dest;
77 dst->used = 0;
78 dst->sz = cinfo->image_width
79 * cinfo->image_height
80 * cinfo->input_components;
81 dst->buf = malloc(dst->sz * sizeof *dst->buf);
82 dst->off = dst->buf;
83 dst->jdst.next_output_byte = dst->off;
84 dst->jdst.free_in_buffer = dst->sz;
86 return;
89 static boolean
90 my_dst_mgr_empty(j_compress_ptr cinfo)
92 struct my_dst_mgr *dst = (void *)cinfo->dest;
94 dst->sz *= 2;
95 dst->used = dst->off - dst->buf;
96 dst->buf = realloc(dst->buf, dst->sz * sizeof *dst->buf);
97 dst->off = dst->buf + dst->used;
98 dst->jdst.next_output_byte = dst->off;
99 dst->jdst.free_in_buffer = dst->sz - dst->used;
101 return TRUE;
104 static void
105 my_dst_mgr_term(j_compress_ptr cinfo)
107 struct my_dst_mgr *dst = (void *)cinfo->dest;
109 dst->used += dst->sz - dst->jdst.free_in_buffer;
110 dst->off = dst->buf + dst->used;
112 return;
115 static void
116 jpeg_memory_dest(j_compress_ptr cinfo, struct my_dst_mgr *dst)
118 dst->jdst.init_destination = my_dst_mgr_init;
119 dst->jdst.empty_output_buffer = my_dst_mgr_empty;
120 dst->jdst.term_destination = my_dst_mgr_term;
121 cinfo->dest = (void *)dst;
123 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 boolean
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)
182 cinfo->src = (*cinfo->mem->alloc_small)((void *)cinfo, JPOOL_PERMANENT, sizeof(struct my_src_mgr));;
183 src = (void *)cinfo->src;
184 src->pub.init_source = init_source;
185 src->pub.fill_input_buffer = fill_input_buffer;
186 src->pub.skip_input_data = skip_input_data;
187 src->pub.resync_to_restart = jpeg_resync_to_restart;
188 src->pub.term_source = term_source;
189 src->pub.next_input_byte = buffer;
190 src->pub.bytes_in_buffer = bufsize;
193 static jmp_buf setjmp_buffer;
194 /* Don't exit on error like libjpeg likes to do */
195 static void
196 libjpeg_error_handler(j_common_ptr cinfo)
198 cinfo->err->output_message(cinfo);
199 longjmp(setjmp_buffer, 1);
200 return;
203 void
204 image_free(image_s *pimage)
206 free(pimage->buf);
207 free(pimage);
211 get_pix(image_s *pimage, int32_t x, int32_t y)
213 if (x < 0)
214 x = 0;
215 else if (x >= pimage->width)
216 x = pimage->width - 1;
218 if (y < 0)
219 y = 0;
220 else if (y >= pimage->height)
221 y = pimage->height - 1;
223 return(pimage->buf[(y * pimage->width) + x]);
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 uint16_t offset, h, w;
239 int ret = 1;
240 size_t nread;
241 long size;
244 img = fopen(path, "r");
245 if( !img )
246 return -1;
248 fseek(img, 0, SEEK_END);
249 size = ftell(img);
250 rewind(img);
252 nread = fread(&buf, 2, 1, img);
253 if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) )
255 fclose(img);
256 return -1;
258 memset(&buf, 0, sizeof(buf));
260 while( ftell(img) < size )
262 while( nread > 0 && buf[0] != 0xFF && !feof(img) )
263 nread = fread(&buf, 1, 1, img);
265 while( nread > 0 && buf[0] == 0xFF && !feof(img) )
266 nread = fread(&buf, 1, 1, img);
268 if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) )
270 nread = fread(&buf, 7, 1, img);
271 *width = 0;
272 *height = 0;
273 if( nread < 1 )
274 break;
275 memcpy(&h, buf+3, 2);
276 *height = SWAP16(h);
277 memcpy(&w, buf+5, 2);
278 *width = SWAP16(w);
279 ret = 0;
280 break;
282 else
284 offset = 0;
285 nread = fread(&buf, 2, 1, img);
286 if( nread < 1 )
287 break;
288 memcpy(&offset, buf, 2);
289 offset = SWAP16(offset) - 2;
290 if( fseek(img, offset, SEEK_CUR) == -1 )
291 break;
294 fclose(img);
295 return ret;
299 image_get_jpeg_date_xmp(const char * path, char ** date)
301 FILE *img;
302 unsigned char buf[8];
303 char *data = NULL, *newdata;
304 uint16_t offset;
305 struct NameValueParserData xml;
306 char * exif;
307 int ret = 1;
308 size_t nread;
310 img = fopen(path, "r");
311 if( !img )
312 return(-1);
314 nread = fread(&buf, 2, 1, img);
315 if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) )
317 fclose(img);
318 return(-1);
320 memset(&buf, 0, sizeof(buf));
322 while( !feof(img) )
324 while( nread > 0 && buf[0] != 0xFF && !feof(img) )
325 nread = fread(&buf, 1, 1, img);
327 while( nread > 0 && buf[0] == 0xFF && !feof(img) )
328 nread = fread(&buf, 1, 1, img);
330 if( feof(img) )
331 break;
333 if( buf[0] == 0xE1 ) // APP1 marker
335 offset = 0;
336 nread = fread(&buf, 2, 1, img);
337 if( nread < 1 )
338 break;
339 memcpy(&offset, buf, 2);
340 offset = SWAP16(offset) - 2;
342 if( offset < 30 )
344 fseek(img, offset, SEEK_CUR);
345 continue;
348 newdata = realloc(data, 30);
349 if( !newdata )
350 break;
351 data = newdata;
353 nread = fread(data, 29, 1, img);
354 if( nread < 1 )
355 break;
356 offset -= 29;
357 if( strcmp(data, "http://ns.adobe.com/xap/1.0/") != 0 )
359 fseek(img, offset, SEEK_CUR);
360 continue;
363 newdata = realloc(data, offset+1);
364 if( !newdata )
365 break;
366 data = newdata;
367 nread = fread(data, offset, 1, img);
368 if( nread < 1 )
369 break;
371 ParseNameValue(data, offset, &xml, 0);
372 exif = GetValueFromNameValueList(&xml, "DateTimeOriginal");
373 if( !exif )
375 ClearNameValueList(&xml);
376 break;
378 *date = realloc(*date, strlen(exif)+1);
379 strcpy(*date, exif);
380 ClearNameValueList(&xml);
382 ret = 0;
383 break;
385 else
387 offset = 0;
388 nread = fread(&buf, 2, 1, img);
389 if( nread < 1 )
390 break;
391 memcpy(&offset, buf, 2);
392 offset = SWAP16(offset) - 2;
393 fseek(img, offset, SEEK_CUR);
396 fclose(img);
397 free(data);
398 return ret;
401 image_s *
402 image_new(int32_t width, int32_t height)
404 image_s *vimage;
406 if((vimage = (image_s *)malloc(sizeof(image_s))) == NULL)
408 DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
409 return NULL;
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");
416 free(vimage);
417 return NULL;
419 return(vimage);
422 image_s *
423 image_new_from_jpeg(const char *path, int is_file, const uint8_t *buf, int size, int scale, int rotate)
425 image_s *vimage;
426 FILE *file = NULL;
427 struct jpeg_decompress_struct cinfo;
428 unsigned char *line[16], *ptr;
429 int x, y, i, w, h, ofs;
430 int maxbuf;
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);
436 if( is_file )
438 if( (file = fopen(path, "r")) == NULL )
440 return NULL;
442 jpeg_stdio_src(&cinfo, file);
444 else
446 jpeg_memory_src(&cinfo, buf, size);
448 if( setjmp(setjmp_buffer) )
450 jpeg_destroy_decompress(&cinfo);
451 if( is_file && file )
452 fclose(file);
453 return NULL;
455 jpeg_read_header(&cinfo, TRUE);
456 cinfo.scale_denom = scale;
457 cinfo.do_fancy_upsampling = FALSE;
458 cinfo.do_block_smoothing = FALSE;
459 cinfo.dct_method = JDCT_IFAST;
460 jpeg_start_decompress(&cinfo);
461 w = cinfo.output_width;
462 h = cinfo.output_height;
463 vimage = (rotate & (ROTATE_90|ROTATE_270)) ? image_new(h, w) : image_new(w, h);
464 if(!vimage)
466 jpeg_destroy_decompress(&cinfo);
467 if( is_file )
468 fclose(file);
469 return NULL;
472 if( setjmp(setjmp_buffer) )
474 jpeg_destroy_decompress(&cinfo);
475 if( is_file && file )
476 fclose(file);
477 if( vimage )
479 free(vimage->buf);
480 free(vimage);
482 return NULL;
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");
488 jpeg_destroy_decompress(&cinfo);
489 image_free(vimage);
490 if( is_file )
491 fclose(file);
492 return NULL;
494 maxbuf = vimage->width * vimage->height;
495 if(cinfo.output_components == 3)
497 int rx, ry;
498 ofs = 0;
499 if((ptr = malloc(w * 3 * cinfo.rec_outbuf_height + 16)) == NULL)
501 DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
502 jpeg_destroy_decompress(&cinfo);
503 image_free(vimage);
504 if( is_file )
505 fclose(file);
506 return NULL;
509 for(y = 0; y < h; y += cinfo.rec_outbuf_height)
511 ry = (rotate & (ROTATE_90|ROTATE_180)) ? (y - h + 1) * -1 : y;
512 for(i = 0; i < cinfo.rec_outbuf_height; i++)
514 line[i] = ptr + (w * 3 * i);
516 jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
517 for(x = 0; x < w * cinfo.rec_outbuf_height; x++)
519 rx = (rotate & (ROTATE_180|ROTATE_270)) ? (x - w + 1) * -1 : x;
520 ofs = (rotate & (ROTATE_90|ROTATE_270)) ? ry + (rx * h) : rx + (ry * w);
521 if( ofs < maxbuf )
522 vimage->buf[ofs] = COL(ptr[x + x + x], ptr[x + x + x + 1], ptr[x + x + x + 2]);
525 free(ptr);
527 else if(cinfo.output_components == 1)
529 int rx, ry;
530 ofs = 0;
531 for(i = 0; i < cinfo.rec_outbuf_height; i++)
533 if((line[i] = malloc(w)) == NULL)
535 int t = 0;
537 for(t = 0; t < i; t++) free(line[t]);
538 jpeg_destroy_decompress(&cinfo);
539 image_free(vimage);
540 if( is_file )
541 fclose(file);
542 return NULL;
545 for(y = 0; y < h; y += cinfo.rec_outbuf_height)
547 ry = (rotate & (ROTATE_90|ROTATE_180)) ? (y - h + 1) * -1 : y;
548 jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
549 for(i = 0; i < cinfo.rec_outbuf_height; i++)
551 for(x = 0; x < w; x++)
553 rx = (rotate & (ROTATE_180|ROTATE_270)) ?
554 (x - w + 1) * -1 : x;
555 ofs = (rotate & (ROTATE_90|ROTATE_270)) ?
556 ry + (rx * h) : rx + (ry * w);
557 if( ofs < maxbuf )
558 vimage->buf[ofs] =
559 COL(line[i][x], line[i][x], line[i][x]);
563 for(i = 0; i < cinfo.rec_outbuf_height; i++)
565 free(line[i]);
568 jpeg_finish_decompress(&cinfo);
569 jpeg_destroy_decompress(&cinfo);
570 if( is_file )
571 fclose(file);
573 return vimage;
576 void
577 image_upsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
579 int32_t vx, vy;
580 #if !defined __i386__ && !defined __x86_64__
581 int32_t rx, ry;
582 pix vcol;
584 if((pdest == NULL) || (psrc == NULL))
585 return;
587 for(vy = 0; vy < height; vy++)
589 for(vx = 0; vx < width; vx++)
591 rx = ((vx * psrc->width) / width);
592 ry = ((vy * psrc->height) / height);
593 vcol = get_pix(psrc, rx, ry);
594 #else
595 pix vcol,vcol1,vcol2,vcol3,vcol4;
596 float rx,ry;
597 float width_scale, height_scale;
598 float x_dist, y_dist;
600 width_scale = (float)psrc->width / (float)width;
601 height_scale = (float)psrc->height / (float)height;
603 for(vy = 0;vy < height; vy++)
605 for(vx = 0;vx < width; vx++)
607 rx = vx * width_scale;
608 ry = vy * height_scale;
609 vcol1 = get_pix(psrc, (int32_t)rx, (int32_t)ry);
610 vcol2 = get_pix(psrc, ((int32_t)rx)+1, (int32_t)ry);
611 vcol3 = get_pix(psrc, (int32_t)rx, ((int32_t)ry)+1);
612 vcol4 = get_pix(psrc, ((int32_t)rx)+1, ((int32_t)ry)+1);
614 x_dist = rx - ((float)((int32_t)rx));
615 y_dist = ry - ((float)((int32_t)ry));
616 vcol = COL_FULL( (uint8_t)((COL_RED(vcol1)*(1.0-x_dist)
617 + COL_RED(vcol2)*(x_dist))*(1.0-y_dist)
618 + (COL_RED(vcol3)*(1.0-x_dist)
619 + COL_RED(vcol4)*(x_dist))*(y_dist)),
620 (uint8_t)((COL_GREEN(vcol1)*(1.0-x_dist)
621 + COL_GREEN(vcol2)*(x_dist))*(1.0-y_dist)
622 + (COL_GREEN(vcol3)*(1.0-x_dist)
623 + COL_GREEN(vcol4)*(x_dist))*(y_dist)),
624 (uint8_t)((COL_BLUE(vcol1)*(1.0-x_dist)
625 + COL_BLUE(vcol2)*(x_dist))*(1.0-y_dist)
626 + (COL_BLUE(vcol3)*(1.0-x_dist)
627 + COL_BLUE(vcol4)*(x_dist))*(y_dist)),
628 (uint8_t)((COL_ALPHA(vcol1)*(1.0-x_dist)
629 + COL_ALPHA(vcol2)*(x_dist))*(1.0-y_dist)
630 + (COL_ALPHA(vcol3)*(1.0-x_dist)
631 + COL_ALPHA(vcol4)*(x_dist))*(y_dist))
633 #endif
634 put_pix_alpha_replace(pdest, vx, vy, vcol);
639 void
640 image_downsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
642 int32_t vx, vy;
643 pix vcol;
644 int32_t i, j;
645 #if !defined __i386__ && !defined __x86_64__
646 int32_t rx, ry, rx_next, ry_next;
647 int red, green, blue, alpha;
648 int factor;
650 if((pdest == NULL) || (psrc == NULL))
651 return;
653 for(vy = 0; vy < height; vy++)
655 for(vx = 0; vx < width; vx++)
658 rx = ((vx * psrc->width) / width);
659 ry = ((vy * psrc->height) / height);
661 red = green = blue = alpha = 0;
663 rx_next = rx + (psrc->width / width);
664 ry_next = ry + (psrc->width / width);
665 factor = 0;
667 for( j = rx; j < rx_next; j++)
669 for( i = ry; i < ry_next; i++)
671 factor += 1;
672 vcol = get_pix(psrc, j, i);
674 red += COL_RED(vcol);
675 green += COL_GREEN(vcol);
676 blue += COL_BLUE(vcol);
677 alpha += COL_ALPHA(vcol);
681 red /= factor;
682 green /= factor;
683 blue /= factor;
684 alpha /= factor;
686 /* on sature les valeurs */
687 red = (red > 255) ? 255 : ((red < 0) ? 0 : red );
688 green = (green > 255) ? 255 : ((green < 0) ? 0 : green);
689 blue = (blue > 255) ? 255 : ((blue < 0) ? 0 : blue );
690 alpha = (alpha > 255) ? 255 : ((alpha < 0) ? 0 : alpha);
691 #else
692 float rx,ry;
693 float width_scale, height_scale;
694 float red, green, blue, alpha;
695 int32_t half_square_width, half_square_height;
696 float round_width, round_height;
698 if( (pdest == NULL) || (psrc == NULL) )
699 return;
701 width_scale = (float)psrc->width / (float)width;
702 height_scale = (float)psrc->height / (float)height;
704 half_square_width = (int32_t)(width_scale / 2.0);
705 half_square_height = (int32_t)(height_scale / 2.0);
706 round_width = (width_scale / 2.0) - (float)half_square_width;
707 round_height = (height_scale / 2.0) - (float)half_square_height;
708 if(round_width > 0.0)
709 half_square_width++;
710 else
711 round_width = 1.0;
712 if(round_height > 0.0)
713 half_square_height++;
714 else
715 round_height = 1.0;
717 for(vy = 0;vy < height; vy++)
719 for(vx = 0;vx < width; vx++)
721 rx = vx * width_scale;
722 ry = vy * height_scale;
723 vcol = get_pix(psrc, (int32_t)rx, (int32_t)ry);
725 red = green = blue = alpha = 0.0;
727 for(j=0;j<half_square_height<<1;j++)
729 for(i=0;i<half_square_width<<1;i++)
731 vcol = get_pix(psrc, ((int32_t)rx)-half_square_width+i,
732 ((int32_t)ry)-half_square_height+j);
734 if(((j == 0) || (j == (half_square_height<<1)-1)) &&
735 ((i == 0) || (i == (half_square_width<<1)-1)))
737 red += round_width*round_height*(float)COL_RED (vcol);
738 green += round_width*round_height*(float)COL_GREEN(vcol);
739 blue += round_width*round_height*(float)COL_BLUE (vcol);
740 alpha += round_width*round_height*(float)COL_ALPHA(vcol);
742 else if((j == 0) || (j == (half_square_height<<1)-1))
744 red += round_height*(float)COL_RED (vcol);
745 green += round_height*(float)COL_GREEN(vcol);
746 blue += round_height*(float)COL_BLUE (vcol);
747 alpha += round_height*(float)COL_ALPHA(vcol);
749 else if((i == 0) || (i == (half_square_width<<1)-1))
751 red += round_width*(float)COL_RED (vcol);
752 green += round_width*(float)COL_GREEN(vcol);
753 blue += round_width*(float)COL_BLUE (vcol);
754 alpha += round_width*(float)COL_ALPHA(vcol);
756 else
758 red += (float)COL_RED (vcol);
759 green += (float)COL_GREEN(vcol);
760 blue += (float)COL_BLUE (vcol);
761 alpha += (float)COL_ALPHA(vcol);
766 red /= width_scale*height_scale;
767 green /= width_scale*height_scale;
768 blue /= width_scale*height_scale;
769 alpha /= width_scale*height_scale;
771 /* on sature les valeurs */
772 red = (red > 255.0)? 255.0 : ((red < 0.0)? 0.0:red );
773 green = (green > 255.0)? 255.0 : ((green < 0.0)? 0.0:green);
774 blue = (blue > 255.0)? 255.0 : ((blue < 0.0)? 0.0:blue );
775 alpha = (alpha > 255.0)? 255.0 : ((alpha < 0.0)? 0.0:alpha);
776 #endif
777 put_pix_alpha_replace(pdest, vx, vy,
778 COL_FULL((uint8_t)red, (uint8_t)green, (uint8_t)blue, (uint8_t)alpha));
783 image_s *
784 image_resize(image_s * src_image, int32_t width, int32_t height)
786 image_s * dst_image;
788 dst_image = image_new(width, height);
789 if( !dst_image )
790 return NULL;
791 if( (src_image->width < width) || (src_image->height < height) )
792 image_upsize(dst_image, src_image, width, height);
793 else
794 image_downsize(dst_image, src_image, width, height);
796 return dst_image;
800 unsigned char *
801 image_save_to_jpeg_buf(image_s * pimage, int * size)
803 struct jpeg_compress_struct cinfo;
804 struct jpeg_error_mgr jerr;
805 JSAMPROW row_pointer[1];
806 int row_stride;
807 char *data;
808 int i, x;
809 struct my_dst_mgr dst;
811 cinfo.err = jpeg_std_error(&jerr);
812 jpeg_create_compress(&cinfo);
813 jpeg_memory_dest(&cinfo, &dst);
814 cinfo.image_width = pimage->width;
815 cinfo.image_height = pimage->height;
816 cinfo.input_components = 3;
817 cinfo.in_color_space = JCS_RGB;
818 jpeg_set_defaults(&cinfo);
819 jpeg_set_quality(&cinfo, JPEG_QUALITY, TRUE);
820 jpeg_start_compress(&cinfo, TRUE);
821 row_stride = cinfo.image_width * 3;
822 if((data = malloc(row_stride)) == NULL)
824 DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
825 free(dst.buf);
826 jpeg_destroy_compress(&cinfo);
827 return NULL;
829 i = 0;
830 while(cinfo.next_scanline < cinfo.image_height)
832 for(x = 0; x < pimage->width; x++)
834 data[x * 3] = COL_RED(pimage->buf[i]);
835 data[x * 3 + 1] = COL_GREEN(pimage->buf[i]);
836 data[x * 3 + 2] = COL_BLUE(pimage->buf[i]);
837 i++;
839 row_pointer[0] = (unsigned char *)data;
840 jpeg_write_scanlines(&cinfo, row_pointer, 1);
842 jpeg_finish_compress(&cinfo);
843 *size = dst.used;
844 free(data);
845 jpeg_destroy_compress(&cinfo);
847 return dst.buf;
850 char *
851 image_save_to_jpeg_file(image_s * pimage, char * path)
853 int nwritten, size = 0;
854 unsigned char * buf;
855 FILE * dst_file;
857 buf = image_save_to_jpeg_buf(pimage, &size);
858 if( !buf )
859 return NULL;
860 dst_file = fopen(path, "w");
861 if( !dst_file )
863 free(buf);
864 return NULL;
866 nwritten = fwrite(buf, 1, size, dst_file);
867 fclose(dst_file);
868 free(buf);
870 return (nwritten == size) ? path : NULL;