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 "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
41
42#include "upnpreplyparse.h"
43#include "image_utils.h"
44#include "log.h"
45
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
51
52#define JPEG_QUALITY  96
53
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
61
62
63struct my_dst_mgr {
64	struct jpeg_destination_mgr jdst;
65	JOCTET *buf;
66	JOCTET *off;
67	size_t sz;
68	size_t used;
69};
70
71/* Destination manager to store data in a buffer */
72static void
73my_dst_mgr_init(j_compress_ptr cinfo)
74{
75	struct my_dst_mgr *dst = (void *)cinfo->dest;
76
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;
85
86	return;
87}
88
89static boolean
90my_dst_mgr_empty(j_compress_ptr cinfo)
91{
92	struct my_dst_mgr *dst = (void *)cinfo->dest;
93
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;
100
101	return TRUE;
102}
103
104static void
105my_dst_mgr_term(j_compress_ptr cinfo)
106{
107	struct my_dst_mgr *dst = (void *)cinfo->dest;
108
109	dst->used += dst->sz - dst->jdst.free_in_buffer;
110	dst->off = dst->buf + dst->used;
111
112	return;
113}
114
115static void
116jpeg_memory_dest(j_compress_ptr cinfo, struct my_dst_mgr *dst)
117{
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;
122
123	return;
124}
125
126/* Source manager to read data from a buffer */
127struct
128my_src_mgr
129{
130	struct jpeg_source_mgr pub;
131	JOCTET eoi_buffer[2];
132};
133
134static void
135init_source(j_decompress_ptr cinfo)
136{
137	return;
138}
139
140static int
141fill_input_buffer(j_decompress_ptr cinfo)
142{
143	struct my_src_mgr *src = (void *)cinfo->src;
144
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;
150
151	return TRUE;
152}
153
154static void
155skip_input_data(j_decompress_ptr cinfo, long num_bytes)
156{
157	struct my_src_mgr *src = (void *)cinfo->src;
158	if (num_bytes > 0)
159	{
160		while (num_bytes > (long)src->pub.bytes_in_buffer)
161		{
162			num_bytes -= (long)src->pub.bytes_in_buffer;
163			fill_input_buffer(cinfo);
164		}
165	}
166	src->pub.next_input_byte += num_bytes;
167	src->pub.bytes_in_buffer -= num_bytes;
168}
169
170static void
171term_source(j_decompress_ptr cinfo)
172{
173	return;
174}
175
176void
177jpeg_memory_src(j_decompress_ptr cinfo, const unsigned char * buffer, size_t bufsize)
178{
179	struct my_src_mgr *src;
180
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;
191}
192
193jmp_buf setjmp_buffer;
194/* Don't exit on error like libjpeg likes to do */
195static void
196libjpeg_error_handler(j_common_ptr cinfo)
197{
198	cinfo->err->output_message(cinfo);
199	longjmp(setjmp_buffer, 1);
200	return;
201}
202
203void
204image_free(image_s *pimage)
205{
206	free(pimage->buf);
207	free(pimage);
208}
209
210pix
211get_pix(image_s *pimage, int32_t x, int32_t y)
212{
213	if (x < 0)
214		x = 0;
215	else if (x >= pimage->width)
216		x = pimage->width - 1;
217
218	if (y < 0)
219		y = 0;
220	else if (y >= pimage->height)
221		y = pimage->height - 1;
222
223	return(pimage->buf[(y * pimage->width) + x]);
224}
225
226void
227put_pix_alpha_replace(image_s *pimage, int32_t x, int32_t y, pix col)
228{
229	if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height))
230		pimage->buf[(y * pimage->width) + x] = col;
231}
232
233int
234image_get_jpeg_resolution(const char * path, int * width, int * height)
235{
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;
242
243
244	img = fopen(path, "r");
245	if( !img )
246		return -1;
247
248	fseek(img, 0, SEEK_END);
249	size = ftell(img);
250	rewind(img);
251
252	nread = fread(&buf, 2, 1, img);
253	if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) )
254	{
255		fclose(img);
256		return -1;
257	}
258	memset(&buf, 0, sizeof(buf));
259
260	while( ftell(img) < size )
261	{
262		while( nread > 0 && buf[0] != 0xFF && !feof(img) )
263			nread = fread(&buf, 1, 1, img);
264
265		while( nread > 0 && buf[0] == 0xFF && !feof(img) )
266			nread = fread(&buf, 1, 1, img);
267
268		if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) )
269		{
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;
281		}
282		else
283		{
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;
292		}
293	}
294	fclose(img);
295	return ret;
296}
297
298int
299image_get_jpeg_date_xmp(const char * path, char ** date)
300{
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;
309
310	img = fopen(path, "r");
311	if( !img )
312		return(-1);
313
314	nread = fread(&buf, 2, 1, img);
315	if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) )
316	{
317		fclose(img);
318		return(-1);
319	}
320	memset(&buf, 0, sizeof(buf));
321
322	while( !feof(img) )
323	{
324		while( nread > 0 && buf[0] != 0xFF && !feof(img) )
325			nread = fread(&buf, 1, 1, img);
326
327		while( nread > 0 && buf[0] == 0xFF && !feof(img) )
328			nread = fread(&buf, 1, 1, img);
329
330		if( feof(img) )
331			break;
332
333		if( buf[0] == 0xE1 ) // APP1 marker
334		{
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;
341
342			if( offset < 30 )
343			{
344				fseek(img, offset, SEEK_CUR);
345				continue;
346			}
347
348			newdata = realloc(data, 30);
349			if( !newdata )
350				break;
351			data = newdata;
352
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 )
358			{
359				fseek(img, offset, SEEK_CUR);
360				continue;
361			}
362
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;
370
371			ParseNameValue(data, offset, &xml, 0);
372			exif = GetValueFromNameValueList(&xml, "DateTimeOriginal");
373			if( !exif )
374			{
375				ClearNameValueList(&xml);
376				break;
377			}
378			*date = realloc(*date, strlen(exif)+1);
379			strcpy(*date, exif);
380			ClearNameValueList(&xml);
381
382			ret = 0;
383			break;
384		}
385		else
386		{
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);
394		}
395	}
396	fclose(img);
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 uint8_t *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, 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	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)
465	{
466		jpeg_destroy_decompress(&cinfo);
467		if( is_file )
468			fclose(file);
469		return NULL;
470	}
471
472	if( setjmp(setjmp_buffer) )
473	{
474		jpeg_destroy_decompress(&cinfo);
475		if( is_file && file )
476			fclose(file);
477		if( vimage )
478		{
479			free(vimage->buf);
480			free(vimage);
481		}
482		return NULL;
483	}
484
485	if(cinfo.rec_outbuf_height > 16)
486	{
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;
493	}
494	maxbuf = vimage->width * vimage->height;
495	if(cinfo.output_components == 3)
496	{
497		int rx, ry;
498		ofs = 0;
499		if((ptr = malloc(w * 3 * cinfo.rec_outbuf_height + 16)) == NULL)
500		{
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;
507		}
508
509		for(y = 0; y < h; y += cinfo.rec_outbuf_height)
510		{
511			ry = (rotate & (ROTATE_90|ROTATE_180)) ? (y - h + 1) * -1 : y;
512			for(i = 0; i < cinfo.rec_outbuf_height; i++)
513			{
514				line[i] = ptr + (w * 3 * i);
515			}
516			jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
517			for(x = 0; x < w * cinfo.rec_outbuf_height; x++)
518			{
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]);
523			}
524		}
525		free(ptr);
526	}
527	else if(cinfo.output_components == 1)
528	{
529		int rx, ry;
530		ofs = 0;
531		for(i = 0; i < cinfo.rec_outbuf_height; i++)
532		{
533			if((line[i] = malloc(w)) == NULL)
534			{
535				int t = 0;
536
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;
543			}
544		}
545		for(y = 0; y < h; y += cinfo.rec_outbuf_height)
546		{
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++)
550			{
551				for(x = 0; x < w; x++)
552				{
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]);
560				}
561			}
562		}
563		for(i = 0; i < cinfo.rec_outbuf_height; i++)
564		{
565			 free(line[i]);
566		}
567	}
568	jpeg_finish_decompress(&cinfo);
569	jpeg_destroy_decompress(&cinfo);
570	if( is_file )
571		fclose(file);
572
573	return vimage;
574}
575
576void
577image_upsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
578{
579	int32_t vx, vy;
580#if !defined __i386__ && !defined __x86_64__
581	int32_t rx, ry;
582	pix vcol;
583
584	if((pdest == NULL) || (psrc == NULL))
585		return;
586
587	for(vy = 0; vy < height; vy++)
588	{
589		for(vx = 0; vx < width; vx++)
590		{
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;
599
600	width_scale  = (float)psrc->width  / (float)width;
601	height_scale = (float)psrc->height / (float)height;
602
603	for(vy = 0;vy < height; vy++)
604	{
605		for(vx = 0;vx < width; vx++)
606		{
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);
613
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))
632			               );
633#endif
634			put_pix_alpha_replace(pdest, vx, vy, vcol);
635		}
636	}
637}
638
639void
640image_downsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
641{
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;
649
650	if((pdest == NULL) || (psrc == NULL))
651		return;
652
653	for(vy = 0; vy < height; vy++)
654	{
655		for(vx = 0; vx < width; vx++)
656		{
657
658			rx = ((vx * psrc->width) / width);
659			ry = ((vy * psrc->height) / height);
660
661			red = green = blue = alpha = 0;
662
663			rx_next = rx + (psrc->width / width);
664			ry_next = ry + (psrc->width / width);
665			factor = 0;
666
667			for( j = rx; j < rx_next; j++)
668			{
669				for( i = ry; i < ry_next; i++)
670				{
671					factor += 1;
672					vcol = get_pix(psrc, j, i);
673
674					red   += COL_RED(vcol);
675					green += COL_GREEN(vcol);
676					blue  += COL_BLUE(vcol);
677					alpha += COL_ALPHA(vcol);
678				}
679			}
680
681			red   /= factor;
682			green /= factor;
683			blue  /= factor;
684			alpha /= factor;
685
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;
697
698	if( (pdest == NULL) || (psrc == NULL) )
699		return;
700
701	width_scale  = (float)psrc->width  / (float)width;
702	height_scale = (float)psrc->height / (float)height;
703
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;
716
717	for(vy = 0;vy < height; vy++)
718	{
719		for(vx = 0;vx < width; vx++)
720		{
721			rx = vx * width_scale;
722			ry = vy * height_scale;
723			vcol = get_pix(psrc, (int32_t)rx, (int32_t)ry);
724
725			red = green = blue = alpha = 0.0;
726
727			for(j=0;j<half_square_height<<1;j++)
728			{
729				for(i=0;i<half_square_width<<1;i++)
730				{
731					vcol = get_pix(psrc, ((int32_t)rx)-half_square_width+i,
732					                     ((int32_t)ry)-half_square_height+j);
733
734					if(((j == 0) || (j == (half_square_height<<1)-1)) &&
735					   ((i == 0) || (i == (half_square_width<<1)-1)))
736					{
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);
741					}
742					else if((j == 0) || (j == (half_square_height<<1)-1))
743					{
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);
748					}
749					else if((i == 0) || (i == (half_square_width<<1)-1))
750					{
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);
755					}
756					else
757					{
758						red   += (float)COL_RED  (vcol);
759						green += (float)COL_GREEN(vcol);
760						blue  += (float)COL_BLUE (vcol);
761						alpha += (float)COL_ALPHA(vcol);
762					}
763				}
764			}
765
766			red   /= width_scale*height_scale;
767			green /= width_scale*height_scale;
768			blue  /= width_scale*height_scale;
769			alpha /= width_scale*height_scale;
770
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));
779		}
780	}
781}
782
783image_s *
784image_resize(image_s * src_image, int32_t width, int32_t height)
785{
786	image_s * dst_image;
787
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);
795
796	return dst_image;
797}
798
799
800unsigned char *
801image_save_to_jpeg_buf(image_s * pimage, int * size)
802{
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;
810
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)
823	{
824		DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
825		free(dst.buf);
826		jpeg_destroy_compress(&cinfo);
827		return NULL;
828	}
829	i = 0;
830	while(cinfo.next_scanline < cinfo.image_height)
831	{
832		for(x = 0; x < pimage->width; x++)
833		{
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++;
838		}
839		row_pointer[0] = (unsigned char *)data;
840		jpeg_write_scanlines(&cinfo, row_pointer, 1);
841	}
842	jpeg_finish_compress(&cinfo);
843	*size = dst.used;
844	free(data);
845	jpeg_destroy_compress(&cinfo);
846
847	return dst.buf;
848}
849
850char *
851image_save_to_jpeg_file(image_s * pimage, char * path)
852{
853	int nwritten, size = 0;
854	unsigned char * buf;
855	FILE * dst_file;
856
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 )
862	{
863		free(buf);
864		return NULL;
865	}
866	nwritten = fwrite(buf, 1, size, dst_file);
867	fclose(dst_file);
868	free(buf);
869
870	return (nwritten == size) ? path : NULL;
871}
872