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
241	img = fopen(path, "r");
242	if( !img )
243		return(-1);
244
245	fread(&buf, 2, 1, img);
246	if( (buf[0] != 0xFF) || (buf[1] != 0xD8) )
247	{
248		fclose(img);
249		return(-1);
250	}
251	memset(&buf, 0, sizeof(buf));
252
253	while( !feof(img) )
254	{
255		while( buf[0] != 0xFF && !feof(img) )
256			fread(&buf, 1, 1, img);
257
258		while( buf[0] == 0xFF && !feof(img) )
259			fread(&buf, 1, 1, img);
260
261		if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) )
262		{
263			fread(&buf, 7, 1, img);
264			*width = 0;
265			*height = 0;
266			memcpy(&h, buf+3, 2);
267			*height = SWAP16(h);
268			memcpy(&w, buf+5, 2);
269			*width = SWAP16(w);
270			ret = 0;
271			break;
272		}
273		else
274		{
275			offset = 0;
276			fread(&buf, 2, 1, img);
277			memcpy(&offset, buf, 2);
278			offset = SWAP16(offset) - 2;
279			fseek(img, offset, SEEK_CUR);
280		}
281	}
282	fclose(img);
283	return ret;
284}
285
286int
287image_get_jpeg_date_xmp(const char * path, char ** date)
288{
289	FILE *img;
290	unsigned char buf[8];
291	char *data = NULL;
292	u_int16_t offset;
293	struct NameValueParserData xml;
294	char * exif;
295	int ret = 1;
296
297	img = fopen(path, "r");
298	if( !img )
299		return(-1);
300
301	fread(&buf, 2, 1, img);
302	if( (buf[0] != 0xFF) || (buf[1] != 0xD8) )
303	{
304		fclose(img);
305		return(-1);
306	}
307	memset(&buf, 0, sizeof(buf));
308
309	while( !feof(img) )
310	{
311		while( buf[0] != 0xFF && !feof(img) )
312			fread(&buf, 1, 1, img);
313
314		while( buf[0] == 0xFF && !feof(img) )
315			fread(&buf, 1, 1, img);
316
317		if( feof(img) )
318			break;
319
320		if( buf[0] == 0xE1 ) // APP1 marker
321		{
322			offset = 0;
323			fread(&buf, 2, 1, img);
324			memcpy(&offset, buf, 2);
325			offset = SWAP16(offset) - 2;
326
327			if( offset < 30 )
328			{
329				fseek(img, offset, SEEK_CUR);
330				continue;
331			}
332
333			data = realloc(data, 30);
334			fread(data, 29, 1, img);
335			offset -= 29;
336			if( strcmp(data, "http://ns.adobe.com/xap/1.0/") != 0 )
337			{
338				fseek(img, offset, SEEK_CUR);
339				continue;
340			}
341
342			data = realloc(data, offset+1);
343			fread(data, offset, 1, img);
344
345			ParseNameValue(data, offset, &xml);
346			exif = GetValueFromNameValueList(&xml, "DateTimeOriginal");
347			if( !exif )
348				break;
349			*date = realloc(*date, strlen(exif)+1);
350			strcpy(*date, exif);
351			ClearNameValueList(&xml);
352
353			ret = 0;
354			break;
355		}
356		else
357		{
358			offset = 0;
359			fread(&buf, 2, 1, img);
360			memcpy(&offset, buf, 2);
361			offset = SWAP16(offset) - 2;
362			fseek(img, offset, SEEK_CUR);
363		}
364	}
365	fclose(img);
366	if( data )
367		free(data);
368	return ret;
369}
370
371image *
372image_new(int32_t width, int32_t height)
373{
374	image *vimage;
375
376	if((vimage = (image *)malloc(sizeof(image))) == NULL)
377	{
378		DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
379		return NULL;
380	}
381	vimage->width = width; vimage->height = height;
382
383	DPRINTF(E_WARN, L_METADATA, "\n=================\n" );
384
385
386	printf("width:%d, height:%d, size of pix: %d\n", width, height, sizeof(pix));
387
388	printf("try to alloc %d bytes\n", (width * height * sizeof(pix)));
389
390
391	if((vimage->buf = (pix *)malloc(width * height * sizeof(pix))) == NULL)
392	{
393		DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
394		DPRINTF(E_WARN, L_METADATA, "=================\n" );
395		free(vimage);
396		return NULL;
397	}
398	else
399		DPRINTF(E_WARN, L_METADATA, "malloc ok\n\n" );
400
401	DPRINTF(E_WARN, L_METADATA, "=================\n" );
402	return(vimage);
403}
404
405image *
406image_new_from_jpeg(const char * path, int is_file, const char * buf, int size)
407{
408	image *vimage;
409	FILE  *file = NULL;
410	struct jpeg_decompress_struct cinfo;
411	unsigned char *line[16], *ptr;
412	int x, y, i, w, h, ofs;
413	int maxbuf;
414	struct jpeg_error_mgr pub;
415
416
417	cinfo.err = jpeg_std_error(&pub);
418	pub.error_exit = libjpeg_error_handler;
419	jpeg_create_decompress(&cinfo);
420	if( is_file )
421	{
422		if( (file = fopen(path, "r")) == NULL )
423		{
424			return NULL;
425		}
426		jpeg_stdio_src(&cinfo, file);
427	}
428	else
429	{
430		jpeg_memory_src(&cinfo, (const unsigned char *)buf, size);
431	}
432	if( setjmp(setjmp_buffer) )
433	{
434		jpeg_destroy_decompress(&cinfo);
435		if( is_file && file )
436			fclose(file);
437		return NULL;
438	}
439	jpeg_read_header(&cinfo, TRUE);
440	cinfo.do_fancy_upsampling = FALSE;
441	cinfo.do_block_smoothing = FALSE;
442	jpeg_start_decompress(&cinfo);
443	w = cinfo.output_width;
444	h = cinfo.output_height;
445	vimage = image_new(w, h);
446	if(!vimage)
447	{
448		jpeg_destroy_decompress(&cinfo);
449		if( is_file )
450			fclose(file);
451		return NULL;
452	}
453
454	if( setjmp(setjmp_buffer) )
455	{
456		jpeg_destroy_decompress(&cinfo);
457		if( is_file && file )
458			fclose(file);
459		if( vimage )
460		{
461			if( vimage->buf )
462				free(vimage->buf);
463			free(vimage);
464		}
465		return NULL;
466	}
467
468	if(cinfo.rec_outbuf_height > 16)
469	{
470		DPRINTF(E_WARN, L_METADATA, "ERROR image_from_jpeg : (image_from_jpeg.c) JPEG uses line buffers > 16. Cannot load.\n");
471		image_free(vimage);
472		if( is_file )
473			fclose(file);
474		return NULL;
475	}
476	maxbuf = vimage->width * vimage->height;
477	if(cinfo.output_components == 3)
478	{
479		ofs = 0;
480		if((ptr = (unsigned char *)malloc(w * 3 * cinfo.rec_outbuf_height)) == NULL)
481		{
482			DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
483			return NULL;
484		}
485
486		for(y = 0; y < h; y += cinfo.rec_outbuf_height)
487		{
488			for(i = 0; i < cinfo.rec_outbuf_height; i++)
489			{
490				line[i] = ptr + (w * 3 * i);
491			}
492			jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
493			for(x = 0; x < w * cinfo.rec_outbuf_height; x++)
494			{
495				if( ofs < maxbuf )
496				{
497					vimage->buf[ofs] = COL(ptr[x + x + x], ptr[x + x + x + 1], ptr[x + x + x + 2]);
498					ofs++;
499				}
500			}
501		}
502		free(ptr);
503	}
504	else if(cinfo.output_components == 1)
505	{
506		ofs = 0;
507		for(i = 0; i < cinfo.rec_outbuf_height; i++)
508		{
509			if((line[i] = (unsigned char *)malloc(w)) == NULL)
510			{
511				int t = 0;
512
513				for(t = 0; t < i; t++) free(line[t]);
514				jpeg_destroy_decompress(&cinfo);
515				image_free(vimage);
516				if( is_file )
517					fclose(file);
518				return NULL;
519			}
520		}
521		for(y = 0; y < h; y += cinfo.rec_outbuf_height)
522		{
523			jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
524			for(i = 0; i < cinfo.rec_outbuf_height; i++)
525			{
526				for(x = 0; x < w; x++)
527				{
528					vimage->buf[ofs++] = COL(line[i][x], line[i][x], line[i][x]);
529				}
530			}
531		}
532		for(i = 0; i < cinfo.rec_outbuf_height; i++)
533		{
534			 free(line[i]);
535		}
536	}
537	jpeg_finish_decompress(&cinfo);
538	jpeg_destroy_decompress(&cinfo);
539	if( is_file )
540		fclose(file);
541
542	return vimage;
543}
544
545void
546image_upsize(image * pdest, image * psrc, int32_t width, int32_t height)
547{
548	int32_t vx, vy;
549#if !defined __i386__ && !defined __x86_64__
550	int32_t rx, ry;
551	pix vcol;
552
553	if((pdest == NULL) || (psrc == NULL))
554		return;
555
556	for(vy = 0; vy < height; vy++)
557	{
558		for(vx = 0; vx < width; vx++)
559		{
560			rx = ((vx * psrc->width) / width);
561			ry = ((vy * psrc->height) / height);
562			vcol = get_pix(psrc, rx, ry);
563#else
564	pix   vcol,vcol1,vcol2,vcol3,vcol4;
565	float rx,ry;
566	float width_scale, height_scale;
567	float x_dist, y_dist;
568
569	width_scale  = (float)psrc->width  / (float)width;
570	height_scale = (float)psrc->height / (float)height;
571
572	for(vy = 0;vy < height; vy++)
573	{
574		for(vx = 0;vx < width; vx++)
575		{
576			rx = vx * width_scale;
577			ry = vy * height_scale;
578			vcol1 = get_pix(psrc, (int32_t)rx, (int32_t)ry);
579			vcol2 = get_pix(psrc, ((int32_t)rx)+1, (int32_t)ry);
580			vcol3 = get_pix(psrc, (int32_t)rx, ((int32_t)ry)+1);
581			vcol4 = get_pix(psrc, ((int32_t)rx)+1, ((int32_t)ry)+1);
582
583			x_dist = rx - ((float)((int32_t)rx));
584			y_dist = ry - ((float)((int32_t)ry));
585			vcol = COL_FULL( (u_int8_t)((COL_RED(vcol1)*(1.0-x_dist)
586			                  + COL_RED(vcol2)*(x_dist))*(1.0-y_dist)
587			                  + (COL_RED(vcol3)*(1.0-x_dist)
588			                  + COL_RED(vcol4)*(x_dist))*(y_dist)),
589			                 (u_int8_t)((COL_GREEN(vcol1)*(1.0-x_dist)
590			                  + COL_GREEN(vcol2)*(x_dist))*(1.0-y_dist)
591			                  + (COL_GREEN(vcol3)*(1.0-x_dist)
592			                  + COL_GREEN(vcol4)*(x_dist))*(y_dist)),
593			                 (u_int8_t)((COL_BLUE(vcol1)*(1.0-x_dist)
594			                  + COL_BLUE(vcol2)*(x_dist))*(1.0-y_dist)
595			                  + (COL_BLUE(vcol3)*(1.0-x_dist)
596			                  + COL_BLUE(vcol4)*(x_dist))*(y_dist)),
597			                 (u_int8_t)((COL_ALPHA(vcol1)*(1.0-x_dist)
598			                  + COL_ALPHA(vcol2)*(x_dist))*(1.0-y_dist)
599			                  + (COL_ALPHA(vcol3)*(1.0-x_dist)
600			                  + COL_ALPHA(vcol4)*(x_dist))*(y_dist))
601			               );
602#endif
603			put_pix_alpha_replace(pdest, vx, vy, vcol);
604		}
605	}
606}
607
608void
609image_downsize(image * pdest, image * psrc, int32_t width, int32_t height)
610{
611	int32_t vx, vy;
612	pix vcol;
613	int32_t i, j;
614#if !defined __i386__ && !defined __x86_64__
615	int32_t rx, ry, rx_next, ry_next;
616	int red, green, blue, alpha;
617	int factor;
618
619	if((pdest == NULL) || (psrc == NULL))
620		return;
621
622	for(vy = 0; vy < height; vy++)
623	{
624		for(vx = 0; vx < width; vx++)
625		{
626
627			rx = ((vx * psrc->width) / width);
628			ry = ((vy * psrc->height) / height);
629
630			red = green = blue = alpha = 0;
631
632			rx_next = rx + (psrc->width / width);
633			ry_next = ry + (psrc->width / width);
634			factor = 0;
635
636			for( j = rx; j < rx_next; j++)
637			{
638				for( i = ry; i < ry_next; i++)
639				{
640					factor += 1;
641					vcol = get_pix(psrc, j, i);
642
643					red   += COL_RED(vcol);
644					green += COL_GREEN(vcol);
645					blue  += COL_BLUE(vcol);
646					alpha += COL_ALPHA(vcol);
647				}
648			}
649
650			red   /= factor;
651			green /= factor;
652			blue  /= factor;
653			alpha /= factor;
654
655			/* on sature les valeurs */
656			red   = (red   > 255) ? 255 : ((red   < 0) ? 0 : red  );
657			green = (green > 255) ? 255 : ((green < 0) ? 0 : green);
658			blue  = (blue  > 255) ? 255 : ((blue  < 0) ? 0 : blue );
659			alpha = (alpha > 255) ? 255 : ((alpha < 0) ? 0 : alpha);
660#else
661	float rx,ry;
662	float width_scale, height_scale;
663	float red, green, blue, alpha;
664	int32_t half_square_width, half_square_height;
665	float round_width, round_height;
666
667	if( (pdest == NULL) || (psrc == NULL) )
668		return;
669
670	width_scale  = (float)psrc->width  / (float)width;
671	height_scale = (float)psrc->height / (float)height;
672
673	half_square_width  = (int32_t)(width_scale  / 2.0);
674	half_square_height = (int32_t)(height_scale / 2.0);
675	round_width  = (width_scale  / 2.0) - (float)half_square_width;
676	round_height = (height_scale / 2.0) - (float)half_square_height;
677	if(round_width  > 0.0)
678		half_square_width++;
679	else
680		round_width = 1.0;
681	if(round_height > 0.0)
682		half_square_height++;
683	else
684		round_height = 1.0;
685
686	for(vy = 0;vy < height; vy++)
687	{
688		for(vx = 0;vx < width; vx++)
689		{
690			rx = vx * width_scale;
691			ry = vy * height_scale;
692			vcol = get_pix(psrc, (int32_t)rx, (int32_t)ry);
693
694			red = green = blue = alpha = 0.0;
695
696			for(j=0;j<half_square_height<<1;j++)
697			{
698				for(i=0;i<half_square_width<<1;i++)
699				{
700					vcol = get_pix(psrc, ((int32_t)rx)-half_square_width+i,
701					                     ((int32_t)ry)-half_square_height+j);
702
703					if(((j == 0) || (j == (half_square_height<<1)-1)) &&
704					   ((i == 0) || (i == (half_square_width<<1)-1)))
705					{
706						red   += round_width*round_height*(float)COL_RED  (vcol);
707						green += round_width*round_height*(float)COL_GREEN(vcol);
708						blue  += round_width*round_height*(float)COL_BLUE (vcol);
709						alpha += round_width*round_height*(float)COL_ALPHA(vcol);
710					}
711					else if((j == 0) || (j == (half_square_height<<1)-1))
712					{
713						red   += round_height*(float)COL_RED  (vcol);
714						green += round_height*(float)COL_GREEN(vcol);
715						blue  += round_height*(float)COL_BLUE (vcol);
716						alpha += round_height*(float)COL_ALPHA(vcol);
717					}
718					else if((i == 0) || (i == (half_square_width<<1)-1))
719					{
720						red   += round_width*(float)COL_RED  (vcol);
721						green += round_width*(float)COL_GREEN(vcol);
722						blue  += round_width*(float)COL_BLUE (vcol);
723						alpha += round_width*(float)COL_ALPHA(vcol);
724					}
725					else
726					{
727						red   += (float)COL_RED  (vcol);
728						green += (float)COL_GREEN(vcol);
729						blue  += (float)COL_BLUE (vcol);
730						alpha += (float)COL_ALPHA(vcol);
731					}
732				}
733			}
734
735			red   /= width_scale*height_scale;
736			green /= width_scale*height_scale;
737			blue  /= width_scale*height_scale;
738			alpha /= width_scale*height_scale;
739
740			/* on sature les valeurs */
741			red   = (red   > 255.0)? 255.0 : ((red   < 0.0)? 0.0:red  );
742			green = (green > 255.0)? 255.0 : ((green < 0.0)? 0.0:green);
743			blue  = (blue  > 255.0)? 255.0 : ((blue  < 0.0)? 0.0:blue );
744			alpha = (alpha > 255.0)? 255.0 : ((alpha < 0.0)? 0.0:alpha);
745#endif
746			put_pix_alpha_replace(pdest, vx, vy,
747					      COL_FULL((u_int8_t)red, (u_int8_t)green, (u_int8_t)blue, (u_int8_t)alpha));
748		}
749	}
750}
751
752image *
753image_resize(image * src_image, int32_t width, int32_t height)
754{
755	image * dst_image;
756
757	dst_image = image_new(width, height);
758	if( !dst_image )
759		return NULL;
760	if( (src_image->width < width) || (src_image->height < height) )
761		image_upsize(dst_image, src_image, width, height);
762	else
763		image_downsize(dst_image, src_image, width, height);
764
765	return dst_image;
766}
767
768
769unsigned char *
770image_save_to_jpeg_buf(image * pimage, int * size)
771{
772	struct jpeg_compress_struct cinfo;
773	struct jpeg_error_mgr jerr;
774	JSAMPROW row_pointer[1];
775	int row_stride;
776	char *data;
777	int i, x;
778	struct my_dst_mgr dst;
779
780	cinfo.err = jpeg_std_error(&jerr);
781	jpeg_create_compress(&cinfo);
782	jpeg_memory_dest(&cinfo, &dst);
783	cinfo.image_width = pimage->width;
784	cinfo.image_height = pimage->height;
785	cinfo.input_components = 3;
786	cinfo.in_color_space = JCS_RGB;
787	jpeg_set_defaults(&cinfo);
788	jpeg_set_quality(&cinfo, JPEG_QUALITY, TRUE);
789	jpeg_start_compress(&cinfo, TRUE);
790	row_stride = cinfo.image_width * 3;
791	if((data = malloc(row_stride)) == NULL)
792	{
793		DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
794		return NULL;
795	}
796	i = 0;
797	while(cinfo.next_scanline < cinfo.image_height)
798	{
799		for(x = 0; x < pimage->width; x++)
800		{
801			data[x + x + x]   = COL_RED(pimage->buf[i]);
802			data[x + x + x + 1] = COL_GREEN(pimage->buf[i]);
803			data[x + x + x + 2] = COL_BLUE(pimage->buf[i]);
804			i++;
805		}
806		row_pointer[0] = (unsigned char *)data;
807		jpeg_write_scanlines(&cinfo, row_pointer, 1);
808	}
809	jpeg_finish_compress(&cinfo);
810	*size = dst.used;
811	free(data);
812	jpeg_destroy_compress(&cinfo);
813
814	return dst.buf;
815}
816
817int
818image_save_to_jpeg_file(image * pimage, const char * path)
819{
820	int nwritten, size = 0;
821	unsigned char * buf;
822	FILE * dst_file;
823
824	buf = image_save_to_jpeg_buf(pimage, &size);
825	if( !buf )
826		return -1;
827 	dst_file = fopen(path, "w");
828	if( !dst_file )
829	{
830		free(buf);
831		return -1;
832	}
833	nwritten = fwrite(buf, 1, size, dst_file);
834	fclose(dst_file);
835	free(buf);
836
837	return (nwritten==size ? 0 : 1);
838}
839