1/*  MiniDLNA media server
2 *  Copyright (C) 2009  Justin Maggard
3 *
4 *  This program is free software; you can redistribute it and/or modify
5 *  it under the terms of the GNU General Public License as published by
6 *  the Free Software Foundation; either version 2 of the License, or
7 *  (at your option) any later version.
8 *
9 *  This program is distributed in the hope that it will be useful,
10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 *  GNU General Public License for more details.
13 *
14 *  You should have received a copy of the GNU General Public License
15 *  along with this program; if not, write to the Free Software
16 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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 <sys/types.h>
34#include <setjmp.h>
35#include <jpeglib.h>
36#include <endian.h>
37
38#include "upnpreplyparse.h"
39#include "image_utils.h"
40#include "log.h"
41
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
47
48#define JPEG_QUALITY  96
49
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
57
58
59struct my_dst_mgr {
60	struct jpeg_destination_mgr jdst;
61	JOCTET *buf;
62	JOCTET *off;
63	size_t sz;
64	size_t used;
65};
66
67/* Destination manager to store data in a buffer */
68static void
69my_dst_mgr_init(j_compress_ptr cinfo)
70{
71	struct my_dst_mgr *dst = (void *)cinfo->dest;
72
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;
81
82	return;
83
84}
85
86static boolean
87my_dst_mgr_empty(j_compress_ptr cinfo)
88{
89	struct my_dst_mgr *dst = (void *)cinfo->dest;
90
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;
97
98	return TRUE;
99
100}
101
102static void
103my_dst_mgr_term(j_compress_ptr cinfo)
104{
105	struct my_dst_mgr *dst = (void *)cinfo->dest;
106
107	dst->used += dst->sz - dst->jdst.free_in_buffer;
108	dst->off = dst->buf + dst->used;
109
110	return;
111
112}
113
114static void
115jpeg_memory_dest(j_compress_ptr cinfo, struct my_dst_mgr *dst)
116{
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;
121
122	return;
123
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	{
183		cinfo->src = (*cinfo->mem->alloc_small)((void *)cinfo, JPOOL_PERMANENT, sizeof(struct my_src_mgr));;
184	}
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;
193}
194
195jmp_buf setjmp_buffer;
196/* Don't exit on error like libjpeg likes to do */
197static void
198libjpeg_error_handler(j_common_ptr cinfo)
199{
200	cinfo->err->output_message(cinfo);
201	longjmp(setjmp_buffer, 1);
202	return;
203}
204
205void
206image_free(image *pimage)
207{
208	free(pimage->buf);
209	free(pimage);
210}
211
212pix
213get_pix(image *pimage, int32_t x, int32_t y)
214{
215	if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height))
216	{
217		return(pimage->buf[(y * pimage->width) + x]);
218	}
219	else
220	{
221		pix vpix = BLACK;
222		return(vpix);
223	}
224}
225
226void
227put_pix_alpha_replace(image *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	u_int16_t offset, h, w;
239	int ret = 1;
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	fread(&buf, 2, 1, img);
252	if( (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( buf[0] != 0xFF && !feof(img) )
262			fread(&buf, 1, 1, img);
263
264		while( buf[0] == 0xFF && !feof(img) )
265			fread(&buf, 1, 1, img);
266
267		if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) )
268		{
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;
278		}
279		else
280		{
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;
287		}
288	}
289	fclose(img);
290	return ret;
291}
292
293int
294image_get_jpeg_date_xmp(const char * path, char ** date)
295{
296	FILE *img;
297	unsigned char buf[8];
298	char *data = NULL;
299	u_int16_t offset;
300	struct NameValueParserData xml;
301	char * exif;
302	int ret = 1;
303
304	img = fopen(path, "r");
305	if( !img )
306		return(-1);
307
308	fread(&buf, 2, 1, img);
309	if( (buf[0] != 0xFF) || (buf[1] != 0xD8) )
310	{
311		fclose(img);
312		return(-1);
313	}
314	memset(&buf, 0, sizeof(buf));
315
316	while( !feof(img) )
317	{
318		while( buf[0] != 0xFF && !feof(img) )
319			fread(&buf, 1, 1, img);
320
321		while( buf[0] == 0xFF && !feof(img) )
322			fread(&buf, 1, 1, img);
323
324		if( feof(img) )
325			break;
326
327		if( buf[0] == 0xE1 ) // APP1 marker
328		{
329			offset = 0;
330			fread(&buf, 2, 1, img);
331			memcpy(&offset, buf, 2);
332			offset = SWAP16(offset) - 2;
333
334			if( offset < 30 )
335			{
336				fseek(img, offset, SEEK_CUR);
337				continue;
338			}
339
340			data = realloc(data, 30);
341			fread(data, 29, 1, img);
342			offset -= 29;
343			if( strcmp(data, "http://ns.adobe.com/xap/1.0/") != 0 )
344			{
345				fseek(img, offset, SEEK_CUR);
346				continue;
347			}
348
349			data = realloc(data, offset+1);
350			fread(data, offset, 1, img);
351
352			ParseNameValue(data, offset, &xml);
353			exif = GetValueFromNameValueList(&xml, "DateTimeOriginal");
354			if( !exif )
355			{
356				ClearNameValueList(&xml);
357				break;
358			}
359			*date = realloc(*date, strlen(exif)+1);
360			strcpy(*date, exif);
361			ClearNameValueList(&xml);
362
363			ret = 0;
364			break;
365		}
366		else
367		{
368			offset = 0;
369			fread(&buf, 2, 1, img);
370			memcpy(&offset, buf, 2);
371			offset = SWAP16(offset) - 2;
372			fseek(img, offset, SEEK_CUR);
373		}
374	}
375	fclose(img);
376	if( data )
377		free(data);
378	return ret;
379}
380
381image *
382image_new(int32_t width, int32_t height)
383{
384	image *vimage;
385
386	if((vimage = (image *)malloc(sizeof(image))) == NULL)
387	{
388		DPRINTF(E_WARN, L_METADATA, "0.malloc failed\n");
389		return NULL;
390	}
391	vimage->width = width; vimage->height = height;
392
393    //printf("width:%d, height:%d, size of pix: %d\n", width, height, sizeof(pix));
394
395    //printf("try to alloc %d bytes\n", (width * height * sizeof(pix)));
396
397	if((vimage->buf = (pix *)malloc(width * height * sizeof(pix))) == NULL)
398	{
399		DPRINTF(E_WARN, L_METADATA, "1.malloc failed\n");
400		free(vimage);
401		return NULL;
402	}
403	return(vimage);
404}
405
406image *
407image_new_from_jpeg(const char * path, int is_file, const char * buf, int size, int scale)
408{
409	image *vimage;
410	FILE  *file = NULL;
411	struct jpeg_decompress_struct cinfo;
412	unsigned char *line[16], *ptr;
413	int x, y, i, w, h, ofs;
414	int maxbuf;
415	struct jpeg_error_mgr pub;
416
417
418	cinfo.err = jpeg_std_error(&pub);
419	pub.error_exit = libjpeg_error_handler;
420	jpeg_create_decompress(&cinfo);
421	if( is_file )
422	{
423		if( (file = fopen(path, "r")) == NULL )
424		{
425			return NULL;
426		}
427		jpeg_stdio_src(&cinfo, file);
428	}
429	else
430	{
431		jpeg_memory_src(&cinfo, (const unsigned char *)buf, size);
432	}
433	if( setjmp(setjmp_buffer) )
434	{
435		jpeg_destroy_decompress(&cinfo);
436		if( is_file && file )
437			fclose(file);
438		return NULL;
439	}
440	jpeg_read_header(&cinfo, TRUE);
441	cinfo.scale_denom = scale;
442	/* added by Michael Jiang, for CTT 1.56 */
443	cinfo.scale_num   = 1;
444	/* ended by Michael Jiang, for CTT 1.56 */
445
446	DPRINTF(E_WARN, L_METADATA, "%s: scale:%d\n", __FUNCTION__, scale);
447
448
449	cinfo.do_fancy_upsampling = FALSE;
450	cinfo.do_block_smoothing = FALSE;
451	jpeg_start_decompress(&cinfo);
452	w = cinfo.output_width;
453	h = cinfo.output_height;
454	vimage = image_new(w, h);
455	if(!vimage)
456	{
457		jpeg_destroy_decompress(&cinfo);
458		if( is_file )
459			fclose(file);
460		return NULL;
461	}
462
463	if( setjmp(setjmp_buffer) )
464	{
465		jpeg_destroy_decompress(&cinfo);
466		if( is_file && file )
467			fclose(file);
468		if( vimage )
469		{
470			if( vimage->buf )
471				free(vimage->buf);
472			free(vimage);
473		}
474		return NULL;
475	}
476
477	if(cinfo.rec_outbuf_height > 16)
478	{
479		DPRINTF(E_WARN, L_METADATA, "ERROR image_from_jpeg : (image_from_jpeg.c) JPEG uses line buffers > 16. Cannot load.\n");
480		image_free(vimage);
481		if( is_file )
482			fclose(file);
483		return NULL;
484	}
485	maxbuf = vimage->width * vimage->height;
486	if(cinfo.output_components == 3)
487	{
488		ofs = 0;
489		if((ptr = (unsigned char *)malloc(w * 3 * cinfo.rec_outbuf_height)) == NULL)
490		{
491			DPRINTF(E_WARN, L_METADATA, "3.malloc failed\n");
492			return NULL;
493		}
494
495		for(y = 0; y < h; y += cinfo.rec_outbuf_height)
496		{
497			for(i = 0; i < cinfo.rec_outbuf_height; i++)
498			{
499				line[i] = ptr + (w * 3 * i);
500			}
501			jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
502			for(x = 0; x < w * cinfo.rec_outbuf_height; x++)
503			{
504				if( ofs < maxbuf )
505				{
506					vimage->buf[ofs] = COL(ptr[x + x + x], ptr[x + x + x + 1], ptr[x + x + x + 2]);
507					ofs++;
508				}
509			}
510		}
511		free(ptr);
512	}
513	else if(cinfo.output_components == 1)
514	{
515		ofs = 0;
516		for(i = 0; i < cinfo.rec_outbuf_height; i++)
517		{
518			if((line[i] = (unsigned char *)malloc(w)) == NULL)
519			{
520				int t = 0;
521
522				for(t = 0; t < i; t++) free(line[t]);
523				jpeg_destroy_decompress(&cinfo);
524				image_free(vimage);
525				if( is_file )
526					fclose(file);
527				return NULL;
528			}
529		}
530		for(y = 0; y < h; y += cinfo.rec_outbuf_height)
531		{
532			jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
533			for(i = 0; i < cinfo.rec_outbuf_height; i++)
534			{
535				for(x = 0; x < w; x++)
536				{
537					vimage->buf[ofs++] = COL(line[i][x], line[i][x], line[i][x]);
538				}
539			}
540		}
541		for(i = 0; i < cinfo.rec_outbuf_height; i++)
542		{
543			 free(line[i]);
544		}
545	}
546	jpeg_finish_decompress(&cinfo);
547	jpeg_destroy_decompress(&cinfo);
548	if( is_file )
549		fclose(file);
550
551	return vimage;
552}
553
554void
555image_upsize(image * pdest, image * psrc, int32_t width, int32_t height)
556{
557	int32_t vx, vy;
558#if !defined __i386__ && !defined __x86_64__
559	int32_t rx, ry;
560	pix vcol;
561
562	if((pdest == NULL) || (psrc == NULL))
563		return;
564
565	for(vy = 0; vy < height; vy++)
566	{
567		for(vx = 0; vx < width; vx++)
568		{
569			rx = ((vx * psrc->width) / width);
570			ry = ((vy * psrc->height) / height);
571			vcol = get_pix(psrc, rx, ry);
572#else
573	pix   vcol,vcol1,vcol2,vcol3,vcol4;
574	float rx,ry;
575	float width_scale, height_scale;
576	float x_dist, y_dist;
577
578	width_scale  = (float)psrc->width  / (float)width;
579	height_scale = (float)psrc->height / (float)height;
580
581	for(vy = 0;vy < height; vy++)
582	{
583		for(vx = 0;vx < width; vx++)
584		{
585			rx = vx * width_scale;
586			ry = vy * height_scale;
587			vcol1 = get_pix(psrc, (int32_t)rx, (int32_t)ry);
588			vcol2 = get_pix(psrc, ((int32_t)rx)+1, (int32_t)ry);
589			vcol3 = get_pix(psrc, (int32_t)rx, ((int32_t)ry)+1);
590			vcol4 = get_pix(psrc, ((int32_t)rx)+1, ((int32_t)ry)+1);
591
592			x_dist = rx - ((float)((int32_t)rx));
593			y_dist = ry - ((float)((int32_t)ry));
594			vcol = COL_FULL( (u_int8_t)((COL_RED(vcol1)*(1.0-x_dist)
595			                  + COL_RED(vcol2)*(x_dist))*(1.0-y_dist)
596			                  + (COL_RED(vcol3)*(1.0-x_dist)
597			                  + COL_RED(vcol4)*(x_dist))*(y_dist)),
598			                 (u_int8_t)((COL_GREEN(vcol1)*(1.0-x_dist)
599			                  + COL_GREEN(vcol2)*(x_dist))*(1.0-y_dist)
600			                  + (COL_GREEN(vcol3)*(1.0-x_dist)
601			                  + COL_GREEN(vcol4)*(x_dist))*(y_dist)),
602			                 (u_int8_t)((COL_BLUE(vcol1)*(1.0-x_dist)
603			                  + COL_BLUE(vcol2)*(x_dist))*(1.0-y_dist)
604			                  + (COL_BLUE(vcol3)*(1.0-x_dist)
605			                  + COL_BLUE(vcol4)*(x_dist))*(y_dist)),
606			                 (u_int8_t)((COL_ALPHA(vcol1)*(1.0-x_dist)
607			                  + COL_ALPHA(vcol2)*(x_dist))*(1.0-y_dist)
608			                  + (COL_ALPHA(vcol3)*(1.0-x_dist)
609			                  + COL_ALPHA(vcol4)*(x_dist))*(y_dist))
610			               );
611#endif
612			put_pix_alpha_replace(pdest, vx, vy, vcol);
613		}
614	}
615}
616
617void
618image_downsize(image * pdest, image * psrc, int32_t width, int32_t height)
619{
620	int32_t vx, vy;
621	pix vcol;
622	int32_t i, j;
623#if !defined __i386__ && !defined __x86_64__
624	int32_t rx, ry, rx_next, ry_next;
625	int red, green, blue, alpha;
626	int factor;
627
628	if((pdest == NULL) || (psrc == NULL))
629		return;
630
631	for(vy = 0; vy < height; vy++)
632	{
633		for(vx = 0; vx < width; vx++)
634		{
635
636			rx = ((vx * psrc->width) / width);
637			ry = ((vy * psrc->height) / height);
638
639			red = green = blue = alpha = 0;
640
641			rx_next = rx + (psrc->width / width);
642			ry_next = ry + (psrc->width / width);
643			factor = 0;
644
645			for( j = rx; j < rx_next; j++)
646			{
647				for( i = ry; i < ry_next; i++)
648				{
649					factor += 1;
650					vcol = get_pix(psrc, j, i);
651
652					red   += COL_RED(vcol);
653					green += COL_GREEN(vcol);
654					blue  += COL_BLUE(vcol);
655					alpha += COL_ALPHA(vcol);
656				}
657			}
658
659			red   /= factor;
660			green /= factor;
661			blue  /= factor;
662			alpha /= factor;
663
664			/* on sature les valeurs */
665			red   = (red   > 255) ? 255 : ((red   < 0) ? 0 : red  );
666			green = (green > 255) ? 255 : ((green < 0) ? 0 : green);
667			blue  = (blue  > 255) ? 255 : ((blue  < 0) ? 0 : blue );
668			alpha = (alpha > 255) ? 255 : ((alpha < 0) ? 0 : alpha);
669#else
670	float rx,ry;
671	float width_scale, height_scale;
672	float red, green, blue, alpha;
673	int32_t half_square_width, half_square_height;
674	float round_width, round_height;
675
676	if( (pdest == NULL) || (psrc == NULL) )
677		return;
678
679	width_scale  = (float)psrc->width  / (float)width;
680	height_scale = (float)psrc->height / (float)height;
681
682	half_square_width  = (int32_t)(width_scale  / 2.0);
683	half_square_height = (int32_t)(height_scale / 2.0);
684	round_width  = (width_scale  / 2.0) - (float)half_square_width;
685	round_height = (height_scale / 2.0) - (float)half_square_height;
686	if(round_width  > 0.0)
687		half_square_width++;
688	else
689		round_width = 1.0;
690	if(round_height > 0.0)
691		half_square_height++;
692	else
693		round_height = 1.0;
694
695	for(vy = 0;vy < height; vy++)
696	{
697		for(vx = 0;vx < width; vx++)
698		{
699			rx = vx * width_scale;
700			ry = vy * height_scale;
701			vcol = get_pix(psrc, (int32_t)rx, (int32_t)ry);
702
703			red = green = blue = alpha = 0.0;
704
705			for(j=0;j<half_square_height<<1;j++)
706			{
707				for(i=0;i<half_square_width<<1;i++)
708				{
709					vcol = get_pix(psrc, ((int32_t)rx)-half_square_width+i,
710					                     ((int32_t)ry)-half_square_height+j);
711
712					if(((j == 0) || (j == (half_square_height<<1)-1)) &&
713					   ((i == 0) || (i == (half_square_width<<1)-1)))
714					{
715						red   += round_width*round_height*(float)COL_RED  (vcol);
716						green += round_width*round_height*(float)COL_GREEN(vcol);
717						blue  += round_width*round_height*(float)COL_BLUE (vcol);
718						alpha += round_width*round_height*(float)COL_ALPHA(vcol);
719					}
720					else if((j == 0) || (j == (half_square_height<<1)-1))
721					{
722						red   += round_height*(float)COL_RED  (vcol);
723						green += round_height*(float)COL_GREEN(vcol);
724						blue  += round_height*(float)COL_BLUE (vcol);
725						alpha += round_height*(float)COL_ALPHA(vcol);
726					}
727					else if((i == 0) || (i == (half_square_width<<1)-1))
728					{
729						red   += round_width*(float)COL_RED  (vcol);
730						green += round_width*(float)COL_GREEN(vcol);
731						blue  += round_width*(float)COL_BLUE (vcol);
732						alpha += round_width*(float)COL_ALPHA(vcol);
733					}
734					else
735					{
736						red   += (float)COL_RED  (vcol);
737						green += (float)COL_GREEN(vcol);
738						blue  += (float)COL_BLUE (vcol);
739						alpha += (float)COL_ALPHA(vcol);
740					}
741				}
742			}
743
744			red   /= width_scale*height_scale;
745			green /= width_scale*height_scale;
746			blue  /= width_scale*height_scale;
747			alpha /= width_scale*height_scale;
748
749			/* on sature les valeurs */
750			red   = (red   > 255.0)? 255.0 : ((red   < 0.0)? 0.0:red  );
751			green = (green > 255.0)? 255.0 : ((green < 0.0)? 0.0:green);
752			blue  = (blue  > 255.0)? 255.0 : ((blue  < 0.0)? 0.0:blue );
753			alpha = (alpha > 255.0)? 255.0 : ((alpha < 0.0)? 0.0:alpha);
754#endif
755			put_pix_alpha_replace(pdest, vx, vy,
756					      COL_FULL((u_int8_t)red, (u_int8_t)green, (u_int8_t)blue, (u_int8_t)alpha));
757		}
758	}
759}
760
761image *
762image_resize(image * src_image, int32_t width, int32_t height)
763{
764	image * dst_image;
765
766	dst_image = image_new(width, height);
767	if( !dst_image )
768		return NULL;
769	if( (src_image->width < width) || (src_image->height < height) )
770		image_upsize(dst_image, src_image, width, height);
771	else
772		image_downsize(dst_image, src_image, width, height);
773
774	return dst_image;
775}
776
777
778unsigned char *
779image_save_to_jpeg_buf(image * pimage, int * size)
780{
781	struct jpeg_compress_struct cinfo;
782	struct jpeg_error_mgr jerr;
783	JSAMPROW row_pointer[1];
784	int row_stride;
785	char *data;
786	int i, x;
787	struct my_dst_mgr dst;
788
789	cinfo.err = jpeg_std_error(&jerr);
790	jpeg_create_compress(&cinfo);
791	jpeg_memory_dest(&cinfo, &dst);
792	cinfo.image_width = pimage->width;
793	cinfo.image_height = pimage->height;
794	cinfo.input_components = 3;
795	cinfo.in_color_space = JCS_RGB;
796	jpeg_set_defaults(&cinfo);
797	jpeg_set_quality(&cinfo, JPEG_QUALITY, TRUE);
798	jpeg_start_compress(&cinfo, TRUE);
799	row_stride = cinfo.image_width * 3;
800	if((data = malloc(row_stride)) == NULL)
801	{
802		DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
803		return NULL;
804	}
805	i = 0;
806	while(cinfo.next_scanline < cinfo.image_height)
807	{
808		for(x = 0; x < pimage->width; x++)
809		{
810			data[x + x + x]   = COL_RED(pimage->buf[i]);
811			data[x + x + x + 1] = COL_GREEN(pimage->buf[i]);
812			data[x + x + x + 2] = COL_BLUE(pimage->buf[i]);
813			i++;
814		}
815		row_pointer[0] = (unsigned char *)data;
816		jpeg_write_scanlines(&cinfo, row_pointer, 1);
817	}
818	jpeg_finish_compress(&cinfo);
819	*size = dst.used;
820	free(data);
821	jpeg_destroy_compress(&cinfo);
822
823	return dst.buf;
824}
825
826int
827image_save_to_jpeg_file(image * pimage, const char * path)
828{
829	int nwritten, size = 0;
830	unsigned char * buf;
831	FILE * dst_file;
832
833	buf = image_save_to_jpeg_buf(pimage, &size);
834	if( !buf )
835		return -1;
836 	dst_file = fopen(path, "w");
837	if( !dst_file )
838	{
839		free(buf);
840		return -1;
841	}
842	nwritten = fwrite(buf, 1, size, dst_file);
843	fclose(dst_file);
844	free(buf);
845
846	return (nwritten==size ? 0 : 1);
847}
848