1/* MiniDLNA media server
2 * Copyright (C) 2009  Justin Maggard
3 *
4 * This file is part of MiniDLNA.
5 *
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.
9 *
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.
14 *
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/>.
17 */
18
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.
23 *
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
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <setjmp.h>
34#include <jpeglib.h>
35#include <endian.h>
36
37#include "upnpreplyparse.h"
38#include "image_utils.h"
39#include "log.h"
40
41#if __BYTE_ORDER == __LITTLE_ENDIAN
42# define SWAP16(w) ( (((w) >> 8) & 0x00ff) | (((w) << 8) & 0xff00) )
43#else
44# define SWAP16(w) (w)
45#endif
46
47#define JPEG_QUALITY  96
48
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
56
57
58struct my_dst_mgr {
59	struct jpeg_destination_mgr jdst;
60	JOCTET *buf;
61	JOCTET *off;
62	size_t sz;
63	size_t used;
64};
65
66/* Destination manager to store data in a buffer */
67static void
68my_dst_mgr_init(j_compress_ptr cinfo)
69{
70	struct my_dst_mgr *dst = (void *)cinfo->dest;
71
72	dst->used = 0;
73	dst->sz = cinfo->image_width
74		  * cinfo->image_height
75		  * cinfo->input_components;
76	dst->buf = malloc(dst->sz * sizeof *dst->buf);
77	dst->off = dst->buf;
78	dst->jdst.next_output_byte = dst->off;
79	dst->jdst.free_in_buffer = dst->sz;
80
81	return;
82
83}
84
85static boolean
86my_dst_mgr_empty(j_compress_ptr cinfo)
87{
88	struct my_dst_mgr *dst = (void *)cinfo->dest;
89
90	dst->sz *= 2;
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;
96
97	return TRUE;
98
99}
100
101static void
102my_dst_mgr_term(j_compress_ptr cinfo)
103{
104	struct my_dst_mgr *dst = (void *)cinfo->dest;
105
106	dst->used += dst->sz - dst->jdst.free_in_buffer;
107	dst->off = dst->buf + dst->used;
108
109	return;
110
111}
112
113static void
114jpeg_memory_dest(j_compress_ptr cinfo, struct my_dst_mgr *dst)
115{
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;
120
121	return;
122
123}
124
125/* Source manager to read data from a buffer */
126struct
127my_src_mgr
128{
129	struct jpeg_source_mgr pub;
130	JOCTET eoi_buffer[2];
131};
132
133static void
134init_source(j_decompress_ptr cinfo)
135{
136	return;
137}
138
139static int
140fill_input_buffer(j_decompress_ptr cinfo)
141{
142	struct my_src_mgr *src = (void *)cinfo->src;
143
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;
149
150	return TRUE;
151}
152
153static void
154skip_input_data(j_decompress_ptr cinfo, long num_bytes)
155{
156	struct my_src_mgr *src = (void *)cinfo->src;
157	if (num_bytes > 0)
158	{
159		while (num_bytes > (long)src->pub.bytes_in_buffer)
160		{
161			num_bytes -= (long)src->pub.bytes_in_buffer;
162			fill_input_buffer(cinfo);
163		}
164	}
165	src->pub.next_input_byte += num_bytes;
166	src->pub.bytes_in_buffer -= num_bytes;
167}
168
169static void
170term_source(j_decompress_ptr cinfo)
171{
172	return;
173}
174
175void
176jpeg_memory_src(j_decompress_ptr cinfo, const unsigned char * buffer, size_t bufsize)
177{
178	struct my_src_mgr *src;
179
180	if (! cinfo->src)
181	{
182		cinfo->src = (*cinfo->mem->alloc_small)((void *)cinfo, JPOOL_PERMANENT, sizeof(struct my_src_mgr));;
183	}
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;
192}
193
194jmp_buf setjmp_buffer;
195/* Don't exit on error like libjpeg likes to do */
196static void
197libjpeg_error_handler(j_common_ptr cinfo)
198{
199	cinfo->err->output_message(cinfo);
200	longjmp(setjmp_buffer, 1);
201	return;
202}
203
204void
205image_free(image_s *pimage)
206{
207	free(pimage->buf);
208	free(pimage);
209}
210
211pix
212get_pix(image_s *pimage, int32_t x, int32_t y)
213{
214	if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height))
215	{
216		return(pimage->buf[(y * pimage->width) + x]);
217	}
218	else
219	{
220		pix vpix = BLACK;
221		return(vpix);
222	}
223}
224
225void
226put_pix_alpha_replace(image_s *pimage, int32_t x, int32_t y, pix col)
227{
228	if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height))
229		pimage->buf[(y * pimage->width) + x] = col;
230}
231
232int
233image_get_jpeg_resolution(const char * path, int * width, int * height)
234{
235	FILE *img;
236	unsigned char buf[8];
237	uint16_t offset, h, w;
238	int ret = 1;
239	size_t nread;
240	long size;
241
242
243	img = fopen(path, "r");
244	if( !img )
245		return -1;
246
247	fseek(img, 0, SEEK_END);
248	size = ftell(img);
249	rewind(img);
250
251	nread = fread(&buf, 2, 1, img);
252	if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) )
253	{
254		fclose(img);
255		return -1;
256	}
257	memset(&buf, 0, sizeof(buf));
258
259	while( ftell(img) < size )
260	{
261		while( nread > 0 && buf[0] != 0xFF && !feof(img) )
262			nread = fread(&buf, 1, 1, img);
263
264		while( nread > 0 && buf[0] == 0xFF && !feof(img) )
265			nread = fread(&buf, 1, 1, img);
266
267		if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) )
268		{
269			nread = fread(&buf, 7, 1, img);
270			*width = 0;
271			*height = 0;
272			if( nread < 1 )
273				break;
274			memcpy(&h, buf+3, 2);
275			*height = SWAP16(h);
276			memcpy(&w, buf+5, 2);
277			*width = SWAP16(w);
278			ret = 0;
279			break;
280		}
281		else
282		{
283			offset = 0;
284			nread = fread(&buf, 2, 1, img);
285			if( nread < 1 )
286				break;
287			memcpy(&offset, buf, 2);
288			offset = SWAP16(offset) - 2;
289			if( fseek(img, offset, SEEK_CUR) == -1 )
290				break;
291		}
292	}
293	fclose(img);
294	return ret;
295}
296
297int
298image_get_jpeg_date_xmp(const char * path, char ** date)
299{
300	FILE *img;
301	unsigned char buf[8];
302	char *data = NULL, *newdata;
303	uint16_t offset;
304	struct NameValueParserData xml;
305	char * exif;
306	int ret = 1;
307	size_t nread;
308
309	img = fopen(path, "r");
310	if( !img )
311		return(-1);
312
313	nread = fread(&buf, 2, 1, img);
314	if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) )
315	{
316		fclose(img);
317		return(-1);
318	}
319	memset(&buf, 0, sizeof(buf));
320
321	while( !feof(img) )
322	{
323		while( nread > 0 && buf[0] != 0xFF && !feof(img) )
324			nread = fread(&buf, 1, 1, img);
325
326		while( nread > 0 && buf[0] == 0xFF && !feof(img) )
327			nread = fread(&buf, 1, 1, img);
328
329		if( feof(img) )
330			break;
331
332		if( buf[0] == 0xE1 ) // APP1 marker
333		{
334			offset = 0;
335			nread = fread(&buf, 2, 1, img);
336			if( nread < 1 )
337				break;
338			memcpy(&offset, buf, 2);
339			offset = SWAP16(offset) - 2;
340
341			if( offset < 30 )
342			{
343				fseek(img, offset, SEEK_CUR);
344				continue;
345			}
346
347			newdata = realloc(data, 30);
348			if( !newdata )
349				break;
350			data = newdata;
351
352			nread = fread(data, 29, 1, img);
353			if( nread < 1 )
354				break;
355			offset -= 29;
356			if( strcmp(data, "http://ns.adobe.com/xap/1.0/") != 0 )
357			{
358				fseek(img, offset, SEEK_CUR);
359				continue;
360			}
361
362			newdata = realloc(data, offset+1);
363			if( !newdata )
364				break;
365			data = newdata;
366			nread = fread(data, offset, 1, img);
367			if( nread < 1 )
368				break;
369
370			ParseNameValue(data, offset, &xml);
371			exif = GetValueFromNameValueList(&xml, "DateTimeOriginal");
372			if( !exif )
373			{
374				ClearNameValueList(&xml);
375				break;
376			}
377			*date = realloc(*date, strlen(exif)+1);
378			strcpy(*date, exif);
379			ClearNameValueList(&xml);
380
381			ret = 0;
382			break;
383		}
384		else
385		{
386			offset = 0;
387			nread = fread(&buf, 2, 1, img);
388			if( nread < 1 )
389				break;
390			memcpy(&offset, buf, 2);
391			offset = SWAP16(offset) - 2;
392			fseek(img, offset, SEEK_CUR);
393		}
394	}
395	fclose(img);
396	if( data )
397		free(data);
398	return ret;
399}
400
401image_s *
402image_new(int32_t width, int32_t height)
403{
404	image_s *vimage;
405
406	if((vimage = (image_s *)malloc(sizeof(image_s))) == NULL)
407	{
408		DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
409		return NULL;
410	}
411	vimage->width = width; vimage->height = height;
412
413	if((vimage->buf = (pix *)malloc(width * height * sizeof(pix))) == NULL)
414	{
415		DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
416		free(vimage);
417		return NULL;
418	}
419	return(vimage);
420}
421
422image_s *
423image_new_from_jpeg(const char * path, int is_file, const char * buf, int size, int scale, int rotate)
424{
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;
432
433	cinfo.err = jpeg_std_error(&pub);
434	pub.error_exit = libjpeg_error_handler;
435	jpeg_create_decompress(&cinfo);
436	if( is_file )
437	{
438		if( (file = fopen(path, "r")) == NULL )
439		{
440			return NULL;
441		}
442		jpeg_stdio_src(&cinfo, file);
443	}
444	else
445	{
446		jpeg_memory_src(&cinfo, (const unsigned char *)buf, size);
447	}
448	if( setjmp(setjmp_buffer) )
449	{
450		jpeg_destroy_decompress(&cinfo);
451		if( is_file && file )
452			fclose(file);
453		return NULL;
454	}
455	jpeg_read_header(&cinfo, TRUE);
456
457	/* added by Michael Jiang, for CTT 1.56 */
458	cinfo.scale_num   = 1;
459	/* ended by Michael Jiang, for CTT 1.56 */
460
461	cinfo.scale_denom = scale;
462	cinfo.do_fancy_upsampling = FALSE;
463	cinfo.do_block_smoothing = FALSE;
464	jpeg_start_decompress(&cinfo);
465	w = cinfo.output_width;
466	h = cinfo.output_height;
467	vimage = (rotate & (ROTATE_90|ROTATE_270)) ? image_new(h, w) : image_new(w, h);
468	if(!vimage)
469	{
470		jpeg_destroy_decompress(&cinfo);
471		if( is_file )
472			fclose(file);
473		return NULL;
474	}
475
476	if( setjmp(setjmp_buffer) )
477	{
478		jpeg_destroy_decompress(&cinfo);
479		if( is_file && file )
480			fclose(file);
481		if( vimage )
482		{
483			if( vimage->buf )
484				free(vimage->buf);
485			free(vimage);
486		}
487		return NULL;
488	}
489
490	if(cinfo.rec_outbuf_height > 16)
491	{
492		DPRINTF(E_WARN, L_METADATA, "ERROR image_from_jpeg : (image_from_jpeg.c) JPEG uses line buffers > 16. Cannot load.\n");
493		image_free(vimage);
494		if( is_file )
495			fclose(file);
496		return NULL;
497	}
498	maxbuf = vimage->width * vimage->height;
499	if(cinfo.output_components == 3)
500	{
501		int rx, ry;
502		ofs = 0;
503		if((ptr = malloc(w * 3 * cinfo.rec_outbuf_height + 16)) == NULL)
504		{
505			DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
506			image_free(vimage);
507			if( is_file )
508				fclose(file);
509			return NULL;
510		}
511
512		for(y = 0; y < h; y += cinfo.rec_outbuf_height)
513		{
514			ry = (rotate & (ROTATE_90|ROTATE_180)) ? (y - h + 1) * -1 : y;
515			for(i = 0; i < cinfo.rec_outbuf_height; i++)
516			{
517				line[i] = ptr + (w * 3 * i);
518			}
519			jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
520			for(x = 0; x < w * cinfo.rec_outbuf_height; x++)
521			{
522				rx = (rotate & (ROTATE_180|ROTATE_270)) ? (x - w + 1) * -1 : x;
523				ofs = (rotate & (ROTATE_90|ROTATE_270)) ? ry + (rx * h) : rx + (ry * w);
524				if( ofs < maxbuf )
525					vimage->buf[ofs] = COL(ptr[x + x + x], ptr[x + x + x + 1], ptr[x + x + x + 2]);
526			}
527		}
528		free(ptr);
529	}
530	else if(cinfo.output_components == 1)
531	{
532		ofs = 0;
533		for(i = 0; i < cinfo.rec_outbuf_height; i++)
534		{
535			if((line[i] = malloc(w)) == NULL)
536			{
537				int t = 0;
538
539				for(t = 0; t < i; t++) free(line[t]);
540				jpeg_destroy_decompress(&cinfo);
541				image_free(vimage);
542				if( is_file )
543					fclose(file);
544				return NULL;
545			}
546		}
547		for(y = 0; y < h; y += cinfo.rec_outbuf_height)
548		{
549			jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
550			for(i = 0; i < cinfo.rec_outbuf_height; i++)
551			{
552				for(x = 0; x < w; x++)
553				{
554					vimage->buf[ofs++] = COL(line[i][x], line[i][x], line[i][x]);
555				}
556			}
557		}
558		for(i = 0; i < cinfo.rec_outbuf_height; i++)
559		{
560			 free(line[i]);
561		}
562	}
563	jpeg_finish_decompress(&cinfo);
564	jpeg_destroy_decompress(&cinfo);
565	if( is_file )
566		fclose(file);
567
568	return vimage;
569}
570
571void
572image_upsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
573{
574	int32_t vx, vy;
575#if !defined __i386__ && !defined __x86_64__
576	int32_t rx, ry;
577	pix vcol;
578
579	if((pdest == NULL) || (psrc == NULL))
580		return;
581
582	for(vy = 0; vy < height; vy++)
583	{
584		for(vx = 0; vx < width; vx++)
585		{
586			rx = ((vx * psrc->width) / width);
587			ry = ((vy * psrc->height) / height);
588			vcol = get_pix(psrc, rx, ry);
589#else
590	pix   vcol,vcol1,vcol2,vcol3,vcol4;
591	float rx,ry;
592	float width_scale, height_scale;
593	float x_dist, y_dist;
594
595	width_scale  = (float)psrc->width  / (float)width;
596	height_scale = (float)psrc->height / (float)height;
597
598	for(vy = 0;vy < height; vy++)
599	{
600		for(vx = 0;vx < width; vx++)
601		{
602			rx = vx * width_scale;
603			ry = vy * height_scale;
604			vcol1 = get_pix(psrc, (int32_t)rx, (int32_t)ry);
605			vcol2 = get_pix(psrc, ((int32_t)rx)+1, (int32_t)ry);
606			vcol3 = get_pix(psrc, (int32_t)rx, ((int32_t)ry)+1);
607			vcol4 = get_pix(psrc, ((int32_t)rx)+1, ((int32_t)ry)+1);
608
609			x_dist = rx - ((float)((int32_t)rx));
610			y_dist = ry - ((float)((int32_t)ry));
611			vcol = COL_FULL( (uint8_t)((COL_RED(vcol1)*(1.0-x_dist)
612			                  + COL_RED(vcol2)*(x_dist))*(1.0-y_dist)
613			                  + (COL_RED(vcol3)*(1.0-x_dist)
614			                  + COL_RED(vcol4)*(x_dist))*(y_dist)),
615			                 (uint8_t)((COL_GREEN(vcol1)*(1.0-x_dist)
616			                  + COL_GREEN(vcol2)*(x_dist))*(1.0-y_dist)
617			                  + (COL_GREEN(vcol3)*(1.0-x_dist)
618			                  + COL_GREEN(vcol4)*(x_dist))*(y_dist)),
619			                 (uint8_t)((COL_BLUE(vcol1)*(1.0-x_dist)
620			                  + COL_BLUE(vcol2)*(x_dist))*(1.0-y_dist)
621			                  + (COL_BLUE(vcol3)*(1.0-x_dist)
622			                  + COL_BLUE(vcol4)*(x_dist))*(y_dist)),
623			                 (uint8_t)((COL_ALPHA(vcol1)*(1.0-x_dist)
624			                  + COL_ALPHA(vcol2)*(x_dist))*(1.0-y_dist)
625			                  + (COL_ALPHA(vcol3)*(1.0-x_dist)
626			                  + COL_ALPHA(vcol4)*(x_dist))*(y_dist))
627			               );
628#endif
629			put_pix_alpha_replace(pdest, vx, vy, vcol);
630		}
631	}
632}
633
634void
635image_downsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
636{
637	int32_t vx, vy;
638	pix vcol;
639	int32_t i, j;
640#if !defined __i386__ && !defined __x86_64__
641	int32_t rx, ry, rx_next, ry_next;
642	int red, green, blue, alpha;
643	int factor;
644
645	if((pdest == NULL) || (psrc == NULL))
646		return;
647
648	for(vy = 0; vy < height; vy++)
649	{
650		for(vx = 0; vx < width; vx++)
651		{
652
653			rx = ((vx * psrc->width) / width);
654			ry = ((vy * psrc->height) / height);
655
656			red = green = blue = alpha = 0;
657
658			rx_next = rx + (psrc->width / width);
659			ry_next = ry + (psrc->width / width);
660			factor = 0;
661
662			for( j = rx; j < rx_next; j++)
663			{
664				for( i = ry; i < ry_next; i++)
665				{
666					factor += 1;
667					vcol = get_pix(psrc, j, i);
668
669					red   += COL_RED(vcol);
670					green += COL_GREEN(vcol);
671					blue  += COL_BLUE(vcol);
672					alpha += COL_ALPHA(vcol);
673				}
674			}
675
676			red   /= factor;
677			green /= factor;
678			blue  /= factor;
679			alpha /= factor;
680
681			/* on sature les valeurs */
682			red   = (red   > 255) ? 255 : ((red   < 0) ? 0 : red  );
683			green = (green > 255) ? 255 : ((green < 0) ? 0 : green);
684			blue  = (blue  > 255) ? 255 : ((blue  < 0) ? 0 : blue );
685			alpha = (alpha > 255) ? 255 : ((alpha < 0) ? 0 : alpha);
686#else
687	float rx,ry;
688	float width_scale, height_scale;
689	float red, green, blue, alpha;
690	int32_t half_square_width, half_square_height;
691	float round_width, round_height;
692
693	if( (pdest == NULL) || (psrc == NULL) )
694		return;
695
696	width_scale  = (float)psrc->width  / (float)width;
697	height_scale = (float)psrc->height / (float)height;
698
699	half_square_width  = (int32_t)(width_scale  / 2.0);
700	half_square_height = (int32_t)(height_scale / 2.0);
701	round_width  = (width_scale  / 2.0) - (float)half_square_width;
702	round_height = (height_scale / 2.0) - (float)half_square_height;
703	if(round_width  > 0.0)
704		half_square_width++;
705	else
706		round_width = 1.0;
707	if(round_height > 0.0)
708		half_square_height++;
709	else
710		round_height = 1.0;
711
712	for(vy = 0;vy < height; vy++)
713	{
714		for(vx = 0;vx < width; vx++)
715		{
716			rx = vx * width_scale;
717			ry = vy * height_scale;
718			vcol = get_pix(psrc, (int32_t)rx, (int32_t)ry);
719
720			red = green = blue = alpha = 0.0;
721
722			for(j=0;j<half_square_height<<1;j++)
723			{
724				for(i=0;i<half_square_width<<1;i++)
725				{
726					vcol = get_pix(psrc, ((int32_t)rx)-half_square_width+i,
727					                     ((int32_t)ry)-half_square_height+j);
728
729					if(((j == 0) || (j == (half_square_height<<1)-1)) &&
730					   ((i == 0) || (i == (half_square_width<<1)-1)))
731					{
732						red   += round_width*round_height*(float)COL_RED  (vcol);
733						green += round_width*round_height*(float)COL_GREEN(vcol);
734						blue  += round_width*round_height*(float)COL_BLUE (vcol);
735						alpha += round_width*round_height*(float)COL_ALPHA(vcol);
736					}
737					else if((j == 0) || (j == (half_square_height<<1)-1))
738					{
739						red   += round_height*(float)COL_RED  (vcol);
740						green += round_height*(float)COL_GREEN(vcol);
741						blue  += round_height*(float)COL_BLUE (vcol);
742						alpha += round_height*(float)COL_ALPHA(vcol);
743					}
744					else if((i == 0) || (i == (half_square_width<<1)-1))
745					{
746						red   += round_width*(float)COL_RED  (vcol);
747						green += round_width*(float)COL_GREEN(vcol);
748						blue  += round_width*(float)COL_BLUE (vcol);
749						alpha += round_width*(float)COL_ALPHA(vcol);
750					}
751					else
752					{
753						red   += (float)COL_RED  (vcol);
754						green += (float)COL_GREEN(vcol);
755						blue  += (float)COL_BLUE (vcol);
756						alpha += (float)COL_ALPHA(vcol);
757					}
758				}
759			}
760
761			red   /= width_scale*height_scale;
762			green /= width_scale*height_scale;
763			blue  /= width_scale*height_scale;
764			alpha /= width_scale*height_scale;
765
766			/* on sature les valeurs */
767			red   = (red   > 255.0)? 255.0 : ((red   < 0.0)? 0.0:red  );
768			green = (green > 255.0)? 255.0 : ((green < 0.0)? 0.0:green);
769			blue  = (blue  > 255.0)? 255.0 : ((blue  < 0.0)? 0.0:blue );
770			alpha = (alpha > 255.0)? 255.0 : ((alpha < 0.0)? 0.0:alpha);
771#endif
772			put_pix_alpha_replace(pdest, vx, vy,
773					      COL_FULL((uint8_t)red, (uint8_t)green, (uint8_t)blue, (uint8_t)alpha));
774		}
775	}
776}
777
778image_s *
779image_resize(image_s * src_image, int32_t width, int32_t height)
780{
781	image_s * dst_image;
782
783	dst_image = image_new(width, height);
784	if( !dst_image )
785		return NULL;
786	if( (src_image->width < width) || (src_image->height < height) )
787		image_upsize(dst_image, src_image, width, height);
788	else
789		image_downsize(dst_image, src_image, width, height);
790
791	return dst_image;
792}
793
794
795unsigned char *
796image_save_to_jpeg_buf(image_s * pimage, int * size)
797{
798	struct jpeg_compress_struct cinfo;
799	struct jpeg_error_mgr jerr;
800	JSAMPROW row_pointer[1];
801	int row_stride;
802	char *data;
803	int i, x;
804	struct my_dst_mgr dst;
805
806	cinfo.err = jpeg_std_error(&jerr);
807	jpeg_create_compress(&cinfo);
808	jpeg_memory_dest(&cinfo, &dst);
809	cinfo.image_width = pimage->width;
810	cinfo.image_height = pimage->height;
811	cinfo.input_components = 3;
812	cinfo.in_color_space = JCS_RGB;
813	jpeg_set_defaults(&cinfo);
814	jpeg_set_quality(&cinfo, JPEG_QUALITY, TRUE);
815	jpeg_start_compress(&cinfo, TRUE);
816	row_stride = cinfo.image_width * 3;
817	if((data = malloc(row_stride)) == NULL)
818	{
819		DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
820		return NULL;
821	}
822	i = 0;
823	while(cinfo.next_scanline < cinfo.image_height)
824	{
825		for(x = 0; x < pimage->width; x++)
826		{
827			data[x * 3]     = COL_RED(pimage->buf[i]);
828			data[x * 3 + 1] = COL_GREEN(pimage->buf[i]);
829			data[x * 3 + 2] = COL_BLUE(pimage->buf[i]);
830			i++;
831		}
832		row_pointer[0] = (unsigned char *)data;
833		jpeg_write_scanlines(&cinfo, row_pointer, 1);
834	}
835	jpeg_finish_compress(&cinfo);
836	*size = dst.used;
837	free(data);
838	jpeg_destroy_compress(&cinfo);
839
840	return dst.buf;
841}
842
843int
844image_save_to_jpeg_file(image_s * pimage, const char * path)
845{
846	int nwritten, size = 0;
847	unsigned char * buf;
848	FILE * dst_file;
849
850	buf = image_save_to_jpeg_buf(pimage, &size);
851	if( !buf )
852		return -1;
853 	dst_file = fopen(path, "w");
854	if( !dst_file )
855	{
856		free(buf);
857		return -1;
858	}
859	nwritten = fwrite(buf, 1, size, dst_file);
860	fclose(dst_file);
861	free(buf);
862
863	return (nwritten==size ? 0 : 1);
864}
865