1 /*
2  * Copyright (c) 2001 Sasha Vasko <sasha@aftercode.net>
3  * Copyright (c) 2001 Eric Kowalski <eric@beancrock.net>
4  * Copyright (c) 2001 Ethan Fisher <allanon@crystaltokyo.com>
5  *
6  * This module is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  */
21 
22 #undef LOCAL_DEBUG
23 #ifdef _WIN32
24 #include "win32/config.h"
25 #else
26 #include "config.h"
27 #endif
28 
29 #include <ctype.h>
30 #include <math.h>
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 #ifdef HAVE_STDLIB_H
35 #include <stdlib.h>
36 #endif
37 #ifdef HAVE_STDARG_H
38 #include <stdarg.h>
39 #endif
40 #include <string.h>
41 #if TIME_WITH_SYS_TIME
42 # include <sys/time.h>
43 # include <time.h>
44 #else
45 # if HAVE_SYS_TIME_H
46 #  include <sys/time.h>
47 # else
48 #  include <time.h>
49 # endif
50 #endif
51 #ifndef _WIN32
52 #include <sys/times.h>
53 #endif
54 
55 #ifdef _WIN32
56 # include "win32/afterbase.h"
57 #else
58 # include "afterbase.h"
59 #endif
60 #include "afterimage.h"
61 #include "imencdec.h"
62 
63 static char* cdata_str = XML_CDATA_STR;
64 
65 /****h* libAfterImage/asimagexml
66  * NAME
67  * ascompose is a tool to compose image(s) and display/save it based on
68  * supplied XML input file.
69  *
70  * DESCRIPTION
71  * ascompose reads supplied XML data, and manipulates image accordingly.
72  * It could transform images from files of any supported file format,
73  * draw gradients, render antialiased texturized text, perform
74  * superimposition of arbitrary number of images, and save images into
75  * files of any of supported output file formats.
76  *
77  * At any point, the result of any operation could be assigned a name,
78  * and later on referenced under this name.
79  *
80  * At any point during the script processing, result of any operation
81  * could be saved into a file of any supported file types.
82  *
83  * Internal image format is 32bit ARGB with 8bit per channel.
84  *
85  * Last image referenced, will be displayed in X window, unless -n option
86  * is specified. If -r option is specified, then this image will be
87  * displayed in root window of X display, effectively setting a background
88  * for a desktop. If -o option is specified, this image will also be
89  * saved into the file or requested type.
90  *
91  * TAGS
92  *
93  * Here is the list and description of possible XML tags to use in the
94  * script :
95  * 	img       - load image from the file.
96  * 	recall    - recall previously loaded/generated image by its name.
97  * 	text      - render text string into new image.
98  * 	save      - save an image into the file.
99  * 	bevel     - draw solid bevel frame around the image.
100  * 	gradient  - render multipoint gradient.
101  * 	mirror    - create mirror copy of an image.
102  * 	blur      - perform gaussian blur on an image.
103  * 	rotate    - rotate/flip image in 90 degree increments.
104  * 	scale     - scale an image to arbitrary size.
105  * 	slice     - enlarge image to arbitrary size leaving corners unchanged.
106  * 	crop      - crop an image to arbitrary size.
107  * 	tile      - tile an image to arbitrary size.
108  * 	hsv       - adjust Hue, Saturation and Value of an image.
109  * 	pad       - pad image with solid color from either or all sides.
110  * 	solid     - generate new image of requested size, filled with solid
111  *              color.
112  * 	composite - superimpose arbitrary number of images using one of 15
113  *              available methods.
114  *  if        - conditional processing based on value of the variables
115  *  set       - sets value of the variable
116  *  printf    - formated printing of the value of the variable
117  *
118  * Each tag generates new image as the result of the transformation -
119  * existing images are never modified and could be reused as many times
120  * as needed. See below for description of each tag.
121  *
122  * Whenever numerical values are involved, the basic math ops (add,
123  * subtract, multiply, divide), unary minus, and parentheses are
124  * supported.
125  *
126  * Operator precedence is NOT supported.  Percentages are allowed, and
127  * apply to either width or height of the appropriate image (usually
128  * the refid image).
129  *
130  * Also, variables of the form $image.width and $image.height are
131  * supported.  $image.width is the width of the image with refid "image",
132  * and $image.height is the height of the same image.  The special
133  * $xroot.width and $xroot.height values are defined by the the X root
134  * window, if there is one.  This allows images to be scaled to the
135  * desktop size: <scale width="$xroot.width" height="$xroot.height">.
136  *
137  * Each tag is only allowed to return ONE image.
138  *
139 *
140  *****/
141 
142 static ASImageManager *_as_xml_image_manager = NULL ;
143 static ASFontManager *_as_xml_font_manager = NULL ;
144 
set_xml_image_manager(ASImageManager * imman)145 void set_xml_image_manager( ASImageManager *imman )
146 {
147 	_as_xml_image_manager = imman ;
148 }
set_xml_font_manager(ASFontManager * fontman)149 void set_xml_font_manager( ASFontManager *fontman )
150 {
151 	_as_xml_font_manager = fontman ;
152 }
153 
154 
155 
156 
create_generic_imageman(const char * path)157 ASImageManager *create_generic_imageman(const char *path)
158 {
159 	ASImageManager *my_imman = NULL ;
160 	char *path2 = copy_replace_envvar( getenv( ASIMAGE_PATH_ENVVAR ) );
161 	show_progress("image path is \"%s\".", path2?path2:"(null)" );
162 	if( path != NULL )
163 		my_imman = create_image_manager( NULL, SCREEN_GAMMA, path, path2, NULL );
164 	else
165 		my_imman = create_image_manager( NULL, SCREEN_GAMMA, path2, NULL );
166 	LOCAL_DEBUG_OUT( "created image manager %p with search path \"%s\"", my_imman, my_imman->search_path[0] );
167 	if( path2 )
168 		free( path2 );
169 	return my_imman;
170 }
171 
create_generic_fontman(Display * dpy,const char * path)172 ASFontManager *create_generic_fontman(Display *dpy, const char *path)
173 {
174 	ASFontManager  *my_fontman ;
175 	char *path2 = copy_replace_envvar( getenv( ASFONT_PATH_ENVVAR ) );
176 	if( path != NULL )
177 	{
178 		if( path2 != NULL )
179 		{
180 			int path_len = strlen(path);
181 			char *full_path = safemalloc( path_len+1+strlen(path2)+1);
182 			strcpy( full_path, path );
183 			full_path[path_len] = ':';
184 			strcpy( &(full_path[path_len+1]), path2 );
185 			free( path2 );
186 			path2 = full_path ;
187 		}else
188 			path2 = (char*)path ;
189 	}
190 	my_fontman = create_font_manager( dpy, path2, NULL );
191 	if( path2 && path2 != path )
192 		free( path2 );
193 
194 	return my_fontman;
195 }
196 
197 ASImage *
compose_asimage_xml_from_doc(ASVisual * asv,ASImageManager * imman,ASFontManager * fontman,xml_elem_t * doc,ASFlagType flags,int verbose,Window display_win,const char * path,int target_width,int target_height)198 compose_asimage_xml_from_doc(ASVisual *asv, ASImageManager *imman, ASFontManager *fontman, xml_elem_t* doc, ASFlagType flags, int verbose, Window display_win, const char *path, int target_width, int target_height)
199 {
200 	/* Build the image(s) from the xml document structure. */
201 	ASImage* im = NULL;
202 	ASImageManager *my_imman = imman, *old_as_xml_imman = _as_xml_image_manager ;
203 	ASFontManager  *my_fontman = fontman, *old_as_xml_fontman = _as_xml_font_manager ;
204 	int my_imman_curr_dir_path_idx = MAX_SEARCH_PATHS ;
205 
206 	if (doc)
207 	{
208 		int old_target_width = -1;
209 		int old_target_height = -1;
210 		xml_elem_t* ptr;
211 		Bool local_dir_included = False ;
212 
213 	    asxml_var_init();
214 #if (HAVE_AFTERBASE_FLAG==1)
215 		if (verbose > 1)
216 		{
217 			xml_print(doc);
218 			fprintf(stderr, "\n");
219 		}
220 #endif
221 
222 		if( my_imman == NULL )
223 		{
224 			if( _as_xml_image_manager == NULL )
225 			{
226 				local_dir_included	  = True ;
227 				_as_xml_image_manager = create_generic_imageman( path );/* we'll want to reuse it in case of recursion */
228 			}
229 			my_imman = _as_xml_image_manager ;
230 		}
231 
232 		if( !local_dir_included )
233 		{
234 			register int i = 0;
235 			char **paths = my_imman->search_path ;
236 			while( i < MAX_SEARCH_PATHS && paths[i] != NULL ) ++i;
237 			if( i < MAX_SEARCH_PATHS )
238 			{
239 				paths[i] = mystrdup(path) ;
240 				paths[i+1] = NULL ;
241 				my_imman_curr_dir_path_idx = i ;
242 			}
243 		}
244 
245 		if( my_fontman == NULL )
246 		{
247 			if( _as_xml_font_manager == NULL )
248 				_as_xml_font_manager = create_generic_fontman( asv->dpy, path );
249 			my_fontman = _as_xml_font_manager ;
250 		}
251 
252 		/* save old target size to be restored at the end */
253 		old_target_width = asxml_var_get(ASXMLVAR_TargetWidth);
254 		old_target_height = asxml_var_get(ASXMLVAR_TargetHeight);
255 		/* set current target size */
256 		asxml_var_insert(ASXMLVAR_TargetWidth, target_width);
257 		asxml_var_insert(ASXMLVAR_TargetHeight, target_height);
258 
259 		for (ptr = doc->child ; ptr ; ptr = ptr->next) {
260 			ASImage* tmpim = build_image_from_xml(asv, my_imman, my_fontman, ptr, NULL, flags, verbose, display_win);
261 			if (tmpim && im) safe_asimage_destroy(im);
262 			if (tmpim) im = tmpim;
263 		}
264 		if (im && (target_width > 0 || target_height > 0) )
265 		  {
266 			int scale_width = (target_width>0)?target_width:im->width;
267 			int scale_height = (target_height>0)?target_height:im->height;
268 			if (im->width != scale_width || im->height != scale_height)
269 			  {
270 			  	ASImage *tmp = scale_asimage( asv, im, scale_width, scale_height, ASA_ASImage, 100, ASIMAGE_QUALITY_DEFAULT );
271 				if (tmp != NULL)
272 				  {
273 				  	safe_asimage_destroy(im);
274 					im = tmp;
275 				  }
276 			  }
277 		  }
278 		/* restore old target size to be restored at the end */
279 		asxml_var_insert(ASXMLVAR_TargetWidth, old_target_width);
280 		asxml_var_insert(ASXMLVAR_TargetHeight, old_target_height);
281 
282 LOCAL_DEBUG_OUT( "result im = %p, im->imman	= %p, my_imman = %p, im->magic = %8.8lX", im, im?im->imageman:NULL, my_imman, im?im->magic:0 );
283 
284 		if( my_imman_curr_dir_path_idx < MAX_SEARCH_PATHS && my_imman->search_path[my_imman_curr_dir_path_idx])
285 		{
286 			free(my_imman->search_path[my_imman_curr_dir_path_idx]);
287 			my_imman->search_path[my_imman_curr_dir_path_idx] = NULL ;
288 		}
289 
290 		if( my_imman != imman && my_imman != old_as_xml_imman )
291 		{/* detach created image from imman to be destroyed : */
292 			if( im && im->imageman == my_imman )
293 				forget_asimage( im );
294 			destroy_image_manager(my_imman, False);
295 		}
296 
297 		if( my_fontman != fontman && my_fontman != old_as_xml_fontman  )
298 			destroy_font_manager(my_fontman, False);
299 		/* must restore managers to its original state */
300 		_as_xml_image_manager = old_as_xml_imman   ;
301 		_as_xml_font_manager =  old_as_xml_fontman ;
302 
303 	}
304 	LOCAL_DEBUG_OUT( "returning im = %p, im->imman	= %p, im->magic = %8.8lX", im, im?im->imageman:NULL, im?im->magic:0 );
305 	return im;
306 }
307 
308 ASImage *
compose_asimage_xml_at_size(ASVisual * asv,ASImageManager * imman,ASFontManager * fontman,char * doc_str,ASFlagType flags,int verbose,Window display_win,const char * path,int target_width,int target_height)309 compose_asimage_xml_at_size(ASVisual *asv, ASImageManager *imman, ASFontManager *fontman, char *doc_str, ASFlagType flags, int verbose, Window display_win, const char *path, int target_width, int target_height)
310 {
311 	xml_elem_t* doc = xml_parse_doc(doc_str, NULL);
312 	ASImage *im = compose_asimage_xml_from_doc(asv, imman, fontman, doc, flags, verbose, display_win, path, target_width, target_height);
313 	if (doc)
314 		xml_elem_delete(NULL, doc);
315 	return im;
316 }
317 
318 inline ASImage *
compose_asimage_xml(ASVisual * asv,ASImageManager * imman,ASFontManager * fontman,char * doc_str,ASFlagType flags,int verbose,Window display_win,const char * path)319 compose_asimage_xml(ASVisual *asv, ASImageManager *imman, ASFontManager *fontman, char *doc_str, ASFlagType flags, int verbose, Window display_win, const char *path)
320 {
321 	xml_elem_t* doc = xml_parse_doc(doc_str, NULL);
322 	ASImage *im = compose_asimage_xml_from_doc(asv, imman, fontman, doc, flags, verbose, display_win, path, -1, -1);
323 	if (doc)
324 		xml_elem_delete(NULL, doc);
325 	return im;
326 }
327 
328 
save_asimage_to_file(const char * file2bsaved,ASImage * im,const char * strtype,const char * compress,const char * opacity,int delay,int replace)329 Bool save_asimage_to_file(const char *file2bsaved, ASImage *im,
330 	           const char *strtype,
331 			   const char *compress,
332 			   const char *opacity,
333 			   int delay, int replace)
334 {
335 	ASImageExportParams params ;
336 
337 	memset( &params, 0x00, sizeof(params) );
338 	params.gif.flags = EXPORT_ALPHA ;
339 	if (strtype == NULL || !mystrcasecmp(strtype, "jpeg") || !mystrcasecmp(strtype, "jpg"))  {
340 		params.type = ASIT_Jpeg;
341 		params.jpeg.quality = (compress==NULL)?-1:100-atoi(compress);
342 		if( params.jpeg.quality > 100 )
343 			params.jpeg.quality = 100;
344 	} else if (!mystrcasecmp(strtype, "bitmap") || !mystrcasecmp(strtype, "bmp")) {
345 		params.type = ASIT_Bmp;
346 	} else if (!mystrcasecmp(strtype, "png")) {
347 		params.type = ASIT_Png;
348 		params.png.compression = (compress==NULL)?-1:atoi(compress);
349 		if( params.png.compression > 99 )
350 			params.png.compression = 99;
351 	} else if (!mystrcasecmp(strtype, "xcf")) {
352 		params.type = ASIT_Xcf;
353 	} else if (!mystrcasecmp(strtype, "ppm")) {
354 		params.type = ASIT_Ppm;
355 	} else if (!mystrcasecmp(strtype, "pnm")) {
356 		params.type = ASIT_Pnm;
357 	} else if (!mystrcasecmp(strtype, "ico")) {
358 		params.type = ASIT_Ico;
359 	} else if (!mystrcasecmp(strtype, "cur")) {
360 		params.type = ASIT_Cur;
361 	} else if (!mystrcasecmp(strtype, "gif")) {
362 		params.type = ASIT_Gif;
363 		params.gif.flags |= EXPORT_APPEND ;
364 		params.gif.opaque_threshold = (opacity==NULL)?127:atoi(opacity) ;
365 		params.gif.dither = (compress==NULL)?3:atoi(compress)/17;
366 		if( params.gif.dither > 6 )
367 			params.gif.dither = 6;
368 		params.gif.animate_delay = delay ;
369 	} else if (!mystrcasecmp(strtype, "xpm")) {
370 		params.type = ASIT_Xpm;
371 		params.xpm.opaque_threshold = (opacity==NULL)?127:atoi(opacity) ;
372 		params.xpm.dither = (compress==NULL)?3:atoi(compress)/17;
373 		if( params.xpm.dither > 6 )
374 			params.xpm.dither = 6;
375 	} else if (!mystrcasecmp(strtype, "xbm")) {
376 		params.type = ASIT_Xbm;
377 	} else if (!mystrcasecmp(strtype, "tiff")) {
378 		params.type = ASIT_Tiff;
379 		params.tiff.compression_type = TIFF_COMPRESSION_NONE ;
380 		if( compress )
381 		{
382 			if( mystrcasecmp( compress, "deflate" ) == 0 )
383 				params.tiff.compression_type = TIFF_COMPRESSION_DEFLATE ;
384 			else if( mystrcasecmp( compress, "jpeg" ) == 0 )
385 				params.tiff.compression_type = TIFF_COMPRESSION_JPEG ;
386 			else if( mystrcasecmp( compress, "ojpeg" ) == 0 )
387 				params.tiff.compression_type = TIFF_COMPRESSION_OJPEG ;
388 			else if( mystrcasecmp( compress, "packbits" ) == 0 )
389 				params.tiff.compression_type = TIFF_COMPRESSION_PACKBITS ;
390 		}
391 	} else {
392 		show_error("File type not found.");
393 		return(0);
394 	}
395 
396 	if( replace && file2bsaved )
397 		unlink( file2bsaved );
398 
399 	return ASImage2file(im, NULL, file2bsaved, params.type, &params);
400 
401 }
402 
show_asimage(ASVisual * asv,ASImage * im,Window w,long delay)403 void show_asimage(ASVisual *asv, ASImage* im, Window w, long delay)
404 {
405 #ifndef X_DISPLAY_MISSING
406 	if ( im && w && asv)
407 	{
408 		Pixmap p = asimage2pixmap(asv, w, im, NULL, False);
409 		struct timeval value;
410 
411 		XSetWindowBackgroundPixmap( asv->dpy, w, p );
412 		XClearWindow( asv->dpy, w );
413 		XFlush( asv->dpy );
414 		XFreePixmap( asv->dpy, p );
415 		p = None ;
416 		value.tv_usec = delay % 10000;
417 		value.tv_sec = delay / 10000;
418 		PORTABLE_SELECT (1, 0, 0, 0, &value);
419 	}
420 #endif /* X_DISPLAY_MISSING */
421 }
422 
423 typedef struct ASImageXMLState
424 {
425 	ASFlagType 		flags ;
426  	ASVisual 		*asv;
427 	ASImageManager 	*imman ;
428 	ASFontManager 	*fontman ;
429 
430 	int verbose ;
431 	Window display_win ;
432 
433 }ASImageXMLState;
434 
435 
commit_xml_image_built(ASImageXMLState * state,char * id,ASImage * result)436 ASImage *commit_xml_image_built( ASImageXMLState *state, char *id, ASImage *result )
437 {
438 	if (state && id && result)
439 	{
440     	char* buf = NEW_ARRAY(char, strlen(id) + 1 + 6 + 1);
441 		if( state->verbose > 1 )
442 			show_progress("Storing image id [%s] with image manager %p .", id, state->imman);
443     	sprintf(buf, "%s.width", id);
444         asxml_var_insert(buf, result->width);
445         sprintf(buf, "%s.height", id);
446         asxml_var_insert(buf, result->height);
447         free(buf);
448 		if( result->imageman != NULL )
449 		{
450 			ASImage *tmp = clone_asimage(result, SCL_DO_ALL );
451 			safe_asimage_destroy(result );
452 			result = tmp ;
453 		}
454 		if( result )
455 		{
456 			if( !store_asimage( state->imman, result, id ) )
457 			{
458 				show_warning("Failed to store image id [%s].", id);
459 				//safe_asimage_destroy(result );
460 				//result = fetch_asimage( state->imman, id );
461 				/*show_warning("Old image with the name fetched as %p.", result);*/
462 			}else
463 			{
464 				/* normally generated image will be destroyed right away, so we need to
465 			 	* increase ref count, in order to preserve it for future uses : */
466 				dup_asimage( result );
467 			}
468 		}
469 	}
470 	return result;
471 }
472 
473 static void
translate_tag_size(const char * width_str,const char * height_str,ASImage * imtmp,ASImage * refimg,int * width_ret,int * height_ret)474 translate_tag_size(	const char *width_str, const char *height_str, ASImage *imtmp, ASImage *refimg, int *width_ret, int *height_ret )
475 {
476 	int width_ref = 0;
477 	int height_ref = 0;
478 	int width = 0, height = 0 ;
479 	LOCAL_DEBUG_OUT("width_str = \"%s\", height_str = \"%s\", imtmp = %p, refimg = %p", width_str?width_str:"(null)", height_str?height_str:"(null)", imtmp, refimg );
480 
481 	if( imtmp )
482 	{
483 		width_ref = width = imtmp->width ;
484 		height_ref = height = imtmp->height ;
485 	}
486 	if (refimg)
487 	{
488 		width_ref = refimg->width;
489 		height_ref = refimg->height;
490 	}
491 	if( width_str )
492 	{
493 		if( width_str[0] == '$' || isdigit( (int)width_str[0] ) )
494 			width = (int)parse_math(width_str, NULL, width);
495 	}
496 	if( height_str )
497 	{
498 		if( height_str[0] == '$' || isdigit( (int)height_str[0] ) )
499 			height = (int)parse_math(height_str, NULL, height);
500 	}
501 	if( width_str && height_ref > 0 && mystrcasecmp(width_str,"proportional") == 0 )
502 		width = (width_ref * height) / height_ref ;
503 	else if( height_str && width_ref > 0 && mystrcasecmp(height_str,"proportional") == 0 )
504 		height = (height_ref * width) / width_ref ;
505 	if( width_ret )
506 		*width_ret = (width==0)?(imtmp?imtmp->width:(refimg?refimg->width:0)):width;
507 	if( height_ret )
508 		*height_ret = (height==0)?(imtmp?imtmp->height:(refimg?refimg->height:0)):height;
509 
510 	LOCAL_DEBUG_OUT("width = %d, height = %d", *width_ret, *height_ret );
511 
512 }
513 
514 /****** libAfterImage/asimagexml/text
515  * NAME
516  * text - render text string into new image, using specific font, size
517  *        and texture.
518  * SYNOPSIS
519  * <text id="new_id" font="font" point="size" fgcolor="color"
520  *       bgcolor="color" fgimage="image_id" bgimage="image_id"
521  *       spacing="points" type="3dtype">My Text Here</text>
522  * ATTRIBUTES
523  * id       Optional.  Image will be given this name for future reference.
524  * font     Optional.  Default is "fixed".  Font to use for text.
525  * point    Optional.  Default is 12.  Size of text in points.
526  * fgcolor  Optional.  No default.  The text will be drawn in this color.
527  * bgcolor  Optional.  No default.  The area behind the text will be drawn
528  *          in this color.
529  * fgimage  Optional.  No default.  The text will be textured by this image.
530  * bgimage  Optional.  No default.  The area behind the text will be filled
531  *          with this image.
532  * spacing  Optional.  Default 0.  Extra pixels to place between each glyph.
533  * type     Optional.  Default 0.  Valid values are from 0 to 7 and each
534  * 			represeend different 3d type.
535  * NOTES
536  * <text> without bgcolor, fgcolor, fgimage, or bgimage will NOT
537  * produce visible output by itself.  See EXAMPLES below.
538  ******/
539 static ASImage *
handle_asxml_tag_text(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm)540 handle_asxml_tag_text( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm )
541 {
542 	ASImage *result = NULL ;
543 	xml_elem_t* ptr ;
544 	const char* text = NULL;
545 	const char* font_name = "fixed";
546 	const char* fgimage_str = NULL;
547 	const char* bgimage_str = NULL;
548 	const char* fgcolor_str = NULL;
549 	const char* bgcolor_str = NULL;
550 	ARGB32 fgcolor = ARGB32_White, bgcolor = ARGB32_Black;
551 	int point = 12, spacing = 0, type = AST_Plain;
552 	unsigned int width = 0;
553 	LOCAL_DEBUG_OUT("doc = %p, parm = %p", doc, parm );
554 	for (ptr = parm ; ptr ; ptr = ptr->next) {
555 		if (!strcmp(ptr->tag, "font")) font_name = ptr->parm;
556 		else if (!strcmp(ptr->tag, "point")) point = strtol(ptr->parm, NULL, 0);
557 		else if (!strcmp(ptr->tag, "spacing")) spacing = strtol(ptr->parm, NULL, 0);
558 		else if (!strcmp(ptr->tag, "fgimage")) fgimage_str = ptr->parm;
559 		else if (!strcmp(ptr->tag, "bgimage")) bgimage_str = ptr->parm;
560 		else if (!strcmp(ptr->tag, "fgcolor")) fgcolor_str = ptr->parm;
561 	   	else if (!strcmp(ptr->tag, "bgcolor")) bgcolor_str = ptr->parm;
562 		else if (!strcmp(ptr->tag, "type")) type = strtol(ptr->parm, NULL, 0);
563 		else if (!strcmp(ptr->tag, "width")) width = strtol(ptr->parm, NULL, 0);
564 	}
565 	for (ptr = doc->child ; ptr && text == NULL ; ptr = ptr->next)
566 		if (!strcmp(ptr->tag, cdata_str)) text = ptr->parm;
567 
568 	if (text && point > 0)
569 	{
570 		struct ASFont *font = NULL;
571 		if( state->verbose > 1 )
572 			show_progress("Rendering text [%s] with font [%s] size [%d].", text, font_name, point);
573 		if (state->fontman) font = get_asfont(state->fontman, font_name, 0, point, ASF_GuessWho);
574 		if (font != NULL)
575 		{
576 		  ASTextAttributes attr = {ASTA_VERSION_INTERNAL, 0, 0, ASCT_Char, 8, 0, NULL, 0, ARGB32_White, width};
577 			attr.type = type ;
578 			if( IsUTF8Locale() )
579 				attr.char_type = ASCT_UTF8 ;
580 			set_asfont_glyph_spacing(font, spacing, 0);
581 			if( fgcolor_str )
582 				parse_argb_color(fgcolor_str, &(attr.fore_color) );
583 
584 			result = draw_fancy_text( text, font, &attr, 0, 0/*autodetect length*/ );
585 			if (result && fgcolor_str) {
586 #if 0
587 				result->back_color = attr.fore_color ;
588 #else
589 				ASImage* fgimage = create_asimage(result->width, result->height, ASIMAGE_QUALITY_TOP);
590 				parse_argb_color(fgcolor_str, &fgcolor);
591 				fill_asimage(state->asv, fgimage, 0, 0, result->width, result->height, fgcolor);
592 				move_asimage_channel(fgimage, IC_ALPHA, result, IC_ALPHA);
593 				safe_asimage_destroy(result);
594 				result = fgimage ;
595 #endif
596 			}
597 			if (result && fgimage_str) {
598 				ASImage* fgimage = NULL;
599 				fgimage = get_asimage(state->imman, fgimage_str, 0xFFFFFFFF, 100 );
600 				if( state->verbose > 1 )
601 					show_progress("Using image [%s](%p) as foreground. Text size is %dx%d", fgimage_str, fgimage, result->width, result->height);
602 				if (fgimage) {
603 					ASImage *tmp = tile_asimage(state->asv, fgimage, 0, 0, result->width, result->height, 0, ASA_ASImage, 100, ASIMAGE_QUALITY_TOP);
604 					if( tmp )
605 					{
606 					   	release_asimage( fgimage );
607 						fgimage = tmp ;
608 					}
609 					move_asimage_channel(fgimage, IC_ALPHA, result, IC_ALPHA);
610 					safe_asimage_destroy(result);
611 					result = fgimage;
612 				}
613 			}
614 			if (result && (bgcolor_str || bgimage_str)) {
615 				ASImageLayer layers[2];
616 				init_image_layers(&(layers[0]), 2);
617 				if (bgimage_str) layers[0].im = fetch_asimage(state->imman, bgimage_str);
618 				if (bgcolor_str)
619 					if( parse_argb_color(bgcolor_str, &bgcolor) != bgcolor_str )
620 					{
621 						if( layers[0].im != NULL )
622 							layers[0].im->back_color = bgcolor ;
623 						else
624 							layers[0].solid_color = bgcolor ;
625 					}
626 				result->back_color = fgcolor ;
627 				layers[0].dst_x = 0;
628 				layers[0].dst_y = 0;
629 				layers[0].clip_width = result->width;
630 				layers[0].clip_height = result->height;
631 				layers[0].bevel = NULL;
632 				layers[1].im = result;
633 				layers[1].dst_x = 0;
634 				layers[1].dst_y = 0;
635 				layers[1].clip_width = result->width;
636 				layers[1].clip_height = result->height;
637 				result = merge_layers(state->asv, layers, 2, result->width, result->height, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
638 				safe_asimage_destroy( layers[0].im );
639 			}
640 		}
641 	}
642 
643 	return result;
644 }
645 /****** libAfterImage/asimagexml/composite
646  * NAME
647  * composite - superimpose arbitrary number of images on top of each
648  * other.
649  * SYNOPSIS
650  * <composite id="new_id" op="op_desc"
651  *            keep-transparency="0|1" merge="0|1">
652  * ATTRIBUTES
653  * id       Optional. Image will be given this name for future reference.
654  * op       Optional. Default is "alphablend". The compositing operation.
655  *          Valid values are the standard AS blending ops: add, alphablend,
656  *          allanon, colorize, darken, diff, dissipate, hue, lighten,
657  *          overlay, saturate, screen, sub, tint, value.
658  * merge    Optional. Default is "expand". Valid values are "clip" and
659  *          "expand". Determines whether final image will be expanded to
660  *          the maximum size of the layers, or clipped to the bottom
661  *          layer.
662  * keep-transparency
663  *          Optional. Default is "0". Valid values are "0" and "1". If
664  *          set to "1", the transparency of the bottom layer will be
665  *          kept for the final image.
666  * NOTES
667  * All images surrounded by this tag will be composited with the given op.
668  *
669  * ATTRIBUTES
670  *  All tags surrounded by this tag may have some of the common attributes
671  *  in addition to their normal ones.  Under no circumstances is there a
672  *  conflict with the normal child attributes:
673  *
674  * crefid   Optional. An image ID defined with the "id" parameter for
675  *          any previously created image. If set, percentages in "x"
676  *          and "y" will be derived from the width and height of the
677  *          crefid image.
678  * x        Optional. Default is 0. Pixel coordinate of left edge.
679  * y        Optional. Default is 0. Pixel coordinate of top edge.
680  * align    Optional. Alternative to x - allowed values are right, center
681  *          and left.
682  * valign   Optional. Alternative to y - allowed values are top, middle
683  *          and bottom.
684  * clip_x   Optional. Default is 0. X Offset on infinite surface tiled
685  *          with this image, from which to cut portion of an image to be
686  *          used in composition.
687  * clip_y   Optional. Default is 0. Y Offset on infinite surface tiled
688  *          with this image, from which to cut portion of an image to be
689  *          used in composition.
690  * clip_width
691  *          Optional. Default is image width. Tile image to this width
692  *          prior to superimposition.
693  * clip_height
694  *          Optional. Default is image height. Tile image to this height
695  *          prior to superimposition.
696  * tile     Optional. Default is 0. If set will cause image to be tiled
697  *          across entire composition, unless overridden by clip_width or
698  *          clip_height.
699  * tint     Optional. Additionally tint an image to specified color.
700  *          Tinting can both lighten and darken an image. Tinting color
701  *          0 or #7f7f7f7f yields no tinting. Tinting can be performed
702  *          on any channel, including alpha channel.
703  * SEE ALSO
704  * libAfterImage
705  ******/
706 static ASImage *
handle_asxml_tag_composite(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm)707 handle_asxml_tag_composite( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm )
708 {
709 	ASImage *result = NULL ;
710 	xml_elem_t* ptr ;
711 	const char* pop = "alphablend";
712 	int keep_trans = 0;
713 	int merge = 0;
714 	int num = 0;
715 	int width = 0, height = 0;
716 	ASImageLayer *layers;
717 #define  ASXML_ALIGN_LEFT 	(0x01<<0)
718 #define  ASXML_ALIGN_RIGHT 	(0x01<<1)
719 #define  ASXML_ALIGN_TOP    (0x01<<2)
720 #define  ASXML_ALIGN_BOTTOM (0x01<<3)
721 	int *align ;
722 	int i ;
723 	merge_scanlines_func op_func = NULL ;
724 
725 	LOCAL_DEBUG_OUT("doc = %p, parm = %p", doc, parm );
726 	for (ptr = parm ; ptr ; ptr = ptr->next) {
727 		if (!strcmp(ptr->tag, "op")) { pop = ptr->parm; op_func = blend_scanlines_name2func(pop); }
728 		else if (!strcmp(ptr->tag, "keep-transparency")) keep_trans = strtol(ptr->parm, NULL, 0);
729 		else if (!strcmp(ptr->tag, "merge") && !mystrcasecmp(ptr->parm, "clip")) merge = 1;
730 	}
731 	/* Find out how many subimages we have. */
732 	for (ptr = doc->child ; ptr ; ptr = ptr->next)
733 		if (strcmp(ptr->tag, cdata_str)) num++;
734 
735 	if( num == 0 )
736 	{
737 		show_warning( "composite tag with no subimages to compose from specified!");
738 		return NULL;
739 	}
740 
741 
742 	if( op_func == NULL )
743 	{
744 		LOCAL_DEBUG_OUT( "defaulting to alpha-blending%s","");
745 		op_func = alphablend_scanlines ;
746 	}
747 	/* Build the layers first. */
748 	layers = create_image_layers( num );
749 	align = safecalloc( num, sizeof(int));
750 
751 	for (num = 0, ptr = doc->child ; ptr ; ptr = ptr->next)
752 	{
753 		int x = 0, y = 0;
754 		int clip_x = 0, clip_y = 0;
755 		int clip_width = 0, clip_height = 0;
756 		ARGB32 tint = 0;
757 		Bool tile = False ;
758 		xml_elem_t* sparm = NULL;
759 		if (!strcmp(ptr->tag, cdata_str)) continue;
760 		if( (layers[num].im = build_image_from_xml(state->asv, state->imman, state->fontman, ptr, &sparm, state->flags, state->verbose, state->display_win)) != NULL )
761 		{
762 			clip_width = layers[num].im->width;
763 			clip_height = layers[num].im->height;
764 		}
765 		if (sparm)
766 		{
767 			xml_elem_t* tmp;
768 			const char* x_str = NULL;
769 			const char* y_str = NULL;
770 			const char* clip_x_str = NULL;
771 			const char* clip_y_str = NULL;
772 			const char* clip_width_str = NULL;
773 			const char* clip_height_str = NULL;
774 			const char* refid = NULL;
775 			for (tmp = sparm ; tmp ; tmp = tmp->next) {
776 				if (!strcmp(tmp->tag, "crefid")) refid = tmp->parm;
777 				else if (!strcmp(tmp->tag, "x")) x_str = tmp->parm;
778 				else if (!strcmp(tmp->tag, "y")) y_str = tmp->parm;
779 				else if (!strcmp(tmp->tag, "clip_x")) clip_x_str = tmp->parm;
780 				else if (!strcmp(tmp->tag, "clip_y")) clip_y_str = tmp->parm;
781 				else if (!strcmp(tmp->tag, "clip_width")) clip_width_str = tmp->parm;
782 				else if (!strcmp(tmp->tag, "clip_height")) clip_height_str = tmp->parm;
783 				else if (!strcmp(tmp->tag, "tint")) parse_argb_color(tmp->parm, &tint);
784 				else if (!strcmp(tmp->tag, "tile")) tile = True;
785 				else if (!strcmp(tmp->tag, "align"))
786 				{
787 					if (!strcmp(tmp->parm, "left"))set_flags( align[num], ASXML_ALIGN_LEFT);
788 					else if (!strcmp(tmp->parm, "right"))set_flags( align[num], ASXML_ALIGN_RIGHT);
789 					else if (!strcmp(tmp->parm, "center"))set_flags( align[num], ASXML_ALIGN_LEFT|ASXML_ALIGN_RIGHT);
790 				}else if (!strcmp(tmp->tag, "valign"))
791 				{
792 					if (!strcmp(tmp->parm, "top"))set_flags( align[num], ASXML_ALIGN_TOP) ;
793 					else if (!strcmp(tmp->parm, "bottom"))set_flags( align[num], ASXML_ALIGN_BOTTOM);
794 					else if (!strcmp(tmp->parm, "middle"))set_flags( align[num], ASXML_ALIGN_TOP|ASXML_ALIGN_BOTTOM);
795 				}
796 			}
797 			if (refid) {
798 				ASImage* refimg = fetch_asimage(state->imman, refid);
799 				if (refimg) {
800 					x = refimg->width;
801 					y = refimg->height;
802 				}
803 				safe_asimage_destroy(refimg );
804 			}
805 			x = x_str ? (int)parse_math(x_str, NULL, x) : 0;
806 			y = y_str ? (int)parse_math(y_str, NULL, y) : 0;
807 			clip_x = clip_x_str ? (int)parse_math(clip_x_str, NULL, x) : 0;
808 			clip_y = clip_y_str ? (int)parse_math(clip_y_str, NULL, y) : 0;
809 			if( clip_width_str )
810 				clip_width = (int)parse_math(clip_width_str, NULL, clip_width);
811 			else if( tile )
812 				clip_width = 0 ;
813 			if( clip_height_str )
814 				clip_height = (int)parse_math(clip_height_str, NULL, clip_height);
815 			else if( tile )
816 				clip_height = 0 ;
817 		}
818 		if (layers[num].im) {
819 			layers[num].dst_x = x;
820 			layers[num].dst_y = y;
821 			layers[num].clip_x = clip_x;
822 			layers[num].clip_y = clip_y;
823 			layers[num].clip_width = clip_width ;
824 			layers[num].clip_height = clip_height ;
825 			layers[num].tint = tint;
826 			layers[num].bevel = 0;
827 			layers[num].merge_scanlines = op_func;
828 			if( clip_width + x > 0 )
829 			{
830 				if( width < clip_width + x )
831 					width = clip_width + x;
832 			}else
833 				if (width < (int)(layers[num].im->width)) width = layers[num].im->width;
834 			if( clip_height + y > 0 )
835 			{
836 				if( height < clip_height + y )
837 					height = clip_height + y ;
838 			}else
839 				if (height < (int)(layers[num].im->height)) height = layers[num].im->height;
840 			num++;
841 		}
842 		if (sparm) xml_elem_delete(NULL, sparm);
843 	}
844 
845 	if (num && merge && layers[0].im ) {
846 		width = layers[0].im->width;
847 		height = layers[0].im->height;
848 	}
849 
850 
851 	for (i = 0 ; i < num ; i++)
852 	{
853 		if( get_flags(align[i], ASXML_ALIGN_LEFT|ASXML_ALIGN_RIGHT ) )
854 		{
855 			int im_width = ( layers[i].clip_width == 0 )? layers[i].im->width : layers[i].clip_width ;
856 			int x = 0 ;
857 			if( get_flags( align[i], ASXML_ALIGN_RIGHT ) )
858 				x = width - im_width ;
859 			if( get_flags( align[i], ASXML_ALIGN_LEFT ) )
860 				x /= 2;
861 			layers[i].dst_x = x;
862 		}
863 		if( get_flags(align[i], ASXML_ALIGN_TOP|ASXML_ALIGN_BOTTOM ) )
864 		{
865 			int im_height = ( layers[i].clip_height == 0 )? layers[i].im->height : layers[i].clip_height;
866 			int y = 0 ;
867 			if( get_flags( align[i], ASXML_ALIGN_BOTTOM ) )
868 				y = height - im_height ;
869 			if( get_flags( align[i], ASXML_ALIGN_TOP ) )
870 				y /= 2;
871 			layers[i].dst_y = y;
872 		}
873 		if( layers[i].clip_width == 0 )
874 			layers[i].clip_width = width - layers[i].dst_x;
875 		if( layers[i].clip_height == 0 )
876 			layers[i].clip_height = height - layers[i].dst_y;
877 	}
878 
879 	if (state->verbose > 1) {
880 		show_progress("Compositing [%d] image(s) with op [%s].  Final geometry [%dx%d].", num, pop, width, height);
881 		if (keep_trans) show_progress("  Keeping transparency.");
882 
883 		for (i = 0 ; i < num ; i++) {
884 			show_progress("  Image [%d] geometry [%dx%d+%d+%d]", i, layers[i].clip_width, layers[i].clip_height, layers[i].dst_x, layers[i].dst_y);
885 			if (layers[i].tint) show_progress(" tint (#%08x)", (unsigned int)layers[i].tint);
886 		}
887 	}
888 
889 	result = merge_layers( state->asv, layers, num, width, height,
890 							ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
891 	if (keep_trans && result && layers[0].im)
892 		copy_asimage_channel(result, IC_ALPHA, layers[0].im, IC_ALPHA);
893 
894 	while (--num >= 0 )
895 		safe_asimage_destroy( layers[num].im );
896 
897 	free(align);
898 	free(layers);
899 
900 	return result;
901 }
902 
903 /****** libAfterImage/asimagexml/img
904  * NAME
905  * img - load image from the file.
906  * SYNOPSIS
907  * <img id="new_img_id" src="filename"/>
908  * ATTRIBUTES
909  * id     Optional.  Image will be given this name for future reference.
910  * src    Required.  The filename (NOT URL) of the image file to load.
911  * NOTES
912  * The special image src "xroot:" will import the background image
913  * of the root X window, if any.  No attempt will be made to offset this
914  * image to fit the location of the resulting window, if one is displayed.
915  ******/
916 static ASImage *
handle_asxml_tag_img(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,int dst_width,int dst_height)917 handle_asxml_tag_img( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, int dst_width, int dst_height)
918 {
919 	ASImage *result = NULL ;
920 	const char* src = NULL;
921 	xml_elem_t* ptr ;
922 	LOCAL_DEBUG_OUT("doc = %p, parm = %p", doc, parm );
923 	for (ptr = parm ; ptr ; ptr = ptr->next) {
924 		if (!strcmp(ptr->tag, "src")) src = ptr->parm;
925 	}
926 	if (src && !strcmp(src, "xroot:")) {
927 		unsigned int width, height;
928 		Pixmap rp = GetRootPixmap(None);
929 		if( state->verbose > 1 )
930 			show_progress("Getting root pixmap.");
931 		if (rp) {
932 			get_dpy_drawable_size( state->asv->dpy, rp, &width, &height);
933 			result = pixmap2asimage(state->asv, rp, 0, 0, width, height, 0xFFFFFFFF, False, 100);
934 			if( dst_width == 0 ) dst_width = width ;
935 			if( dst_height == 0 ) dst_height = height ;
936 			if( dst_width != (int)width || dst_height != (int)height )
937 			{
938 				ASImage *tmp = scale_asimage( NULL, result, dst_width, dst_height, ASA_ASImage, 100, ASIMAGE_QUALITY_DEFAULT );
939 				if( tmp )
940 				{
941 					safe_asimage_destroy( result );
942 					result = tmp ;
943 				}
944 			}
945 		}
946 	} else if (src) {
947 		if( state->verbose > 1 )
948 			show_progress("Loading image [%s] using imman (%p) with search path \"%s\" (dst_size = %dx%d).", src, state->imman, state->imman?state->imman->search_path[0]:"", dst_width, dst_height);
949 		if( dst_width != 0 || dst_height != 0 )
950 			result = get_thumbnail_asimage( state->imman, src, dst_width, dst_height, (dst_width==0||dst_height==0)?AS_THUMBNAIL_PROPORTIONAL:0 );
951 		else
952 			result = get_asimage( state->imman, src, 0xFFFFFFFF, 100 );
953 	}
954 	return result;
955 }
956 
957 /****** libAfterImage/asimagexml/recall
958  * NAME
959  * recall - recall previously generated and named image by its id.
960  * SYNOPSIS
961  * <recall id="new_id" srcid="image_id" default_src="filename"/>
962  * ATTRIBUTES
963  * id       Optional.  Image will be given this name for future reference.
964  * srcid    Required.  An image ID defined with the "id" parameter for
965  *          any previously created image.
966  ******/
967 static ASImage *
handle_asxml_tag_recall(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm)968 handle_asxml_tag_recall( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm)
969 {
970 	ASImage *result = NULL ;
971 	xml_elem_t* ptr = parm ;
972 	LOCAL_DEBUG_OUT("doc = %p, parm = %p", doc, parm );
973 	while ( ptr && !result )
974 	{
975 		if (!strcmp(ptr->tag, "srcid"))
976 		{
977 			if( state->verbose > 1 )
978 				show_progress("Recalling image id [%s] from imman %p.", ptr->parm, state->imman);
979 			result = fetch_asimage(state->imman, ptr->parm );
980 			if (!result)
981 				show_warning("Image recall failed for id [%s].", ptr->parm);
982 		}
983 		ptr = ptr->next ;
984 	}
985 	if( result == NULL )
986 	{
987 		for( ptr = parm ; ptr && !result ; ptr = ptr->next )
988 			if (!strcmp(ptr->tag, "default_src"))
989 			{
990 				if( state->verbose > 1 )
991 					show_progress("loading default image [%s] from imman %p.", ptr->parm, state->imman);
992 				result = get_asimage( state->imman, ptr->parm, 0xFFFFFFFF, 100 );
993 			}
994 	}
995 	return result;
996 }
997 
998 /****** libAfterImage/asimagexml/release
999  * NAME
1000  * release - release (destroy if possible) previously generated and named image by its id.
1001  * SYNOPSIS
1002  * <release srcid="image_id"/>
1003  * ATTRIBUTES
1004  * srcid    Required.  An image ID defined with the "id" parameter for
1005  *          any previously created image.
1006  ******/
1007 static ASImage *
handle_asxml_tag_release(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm)1008 handle_asxml_tag_release( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm)
1009 {
1010 	xml_elem_t* ptr ;
1011 	LOCAL_DEBUG_OUT("doc = %p, parm = %p", doc, parm );
1012 	for (ptr = parm ; ptr ; ptr = ptr->next)
1013 		if (!strcmp(ptr->tag, "srcid"))
1014 		{
1015 			if( state->verbose > 1 )
1016 				show_progress("Releasing image id [%s] from imman %p.", ptr->parm, state->imman);
1017 			release_asimage_by_name(state->imman, (char*)ptr->parm );
1018 			break;
1019 		}
1020 	return NULL;
1021 }
1022 
1023 /****** libAfterImage/asimagexml/color
1024  * NAME
1025  * color - defines symbolic name for a color and set of variables.
1026  * SYNOPSIS
1027  * <color name="sym_name" domain="var_domain" argb="colorvalue"/>
1028  * ATTRIBUTES
1029  * name   Symbolic name for the color value, to be used to refer to that color.
1030  * argb   8 characters hex definition of the color or other symbolic color name.
1031  * domain string to be used to prepend names of defined variables.
1032  * NOTES
1033  * In addition to defining symbolic name for the color this tag will define
1034  * 7 other variables : 	domain.sym_name.red, domain.sym_name.green,
1035  * 					   	domain.sym_name.blue, domain.sym_name.alpha,
1036  * 					  	domain.sym_name.hue, domain.sym_name.saturation,
1037  *                     	domain.sym_name.value
1038  ******/
1039 static ASImage *
handle_asxml_tag_color(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm)1040 handle_asxml_tag_color( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm)
1041 {
1042 	xml_elem_t* ptr ;
1043 	char* name = NULL;
1044 	const char* argb_text = NULL;
1045 	const char* var_domain = NULL;
1046 	LOCAL_DEBUG_OUT("doc = %p, parm = %p", doc, parm );
1047 	for (ptr = parm ; ptr ; ptr = ptr->next) {
1048 		if (!strcmp(ptr->tag, "name")) name = ptr->parm;
1049 		else if (!strcmp(ptr->tag, "argb")) argb_text = ptr->parm;
1050 		else if (!strcmp(ptr->tag, "domain")) var_domain = ptr->parm;
1051 	}
1052 	if (name && argb_text)
1053 	{
1054 		ARGB32 argb = ARGB32_Black;
1055 		if( parse_argb_color( argb_text, &argb ) != argb_text )
1056 		{
1057 			char *tmp;
1058 			CARD32 hue16, sat16, val16 ;
1059 			int vd_len = var_domain?strlen(var_domain):0 ;
1060 
1061 			tmp = safemalloc( vd_len + 1+ strlen(name )+32+1 ) ;
1062 
1063 			if( var_domain && var_domain[0] != '\0' )
1064 			{
1065 				if( var_domain[vd_len-1] != '.' )
1066 				{
1067 					sprintf( tmp, "%s.", var_domain );
1068 					++vd_len ;
1069 				}else
1070 					strcpy( tmp, var_domain );
1071 			}
1072 
1073 
1074 #ifdef HAVE_AFTERBASE
1075 	   		if( state->verbose > 1 )
1076 				show_progress("defining synonim [%s] for color value #%8.8X.", name, argb);
1077 	   		register_custom_color( name, argb );
1078 #endif
1079 			sprintf( tmp+vd_len, "%s.alpha", name );
1080 			asxml_var_insert( tmp, ARGB32_ALPHA8(argb) );
1081 			sprintf( tmp+vd_len, "%s.red", name );
1082 			asxml_var_insert( tmp, ARGB32_RED8(argb) );
1083 			sprintf( tmp+vd_len, "%s.green", name );
1084 			asxml_var_insert( tmp, ARGB32_GREEN8(argb) );
1085 			sprintf( tmp+vd_len, "%s.blue", name );
1086 			asxml_var_insert( tmp, ARGB32_BLUE8(argb) );
1087 
1088 			hue16 = rgb2hsv( ARGB32_RED16(argb), ARGB32_GREEN16(argb), ARGB32_BLUE16(argb), &sat16, &val16 );
1089 
1090 			sprintf( tmp+vd_len, "%s.hue", name );
1091 			asxml_var_insert( tmp, hue162degrees( hue16 ) );
1092 			sprintf( tmp+vd_len, "%s.saturation", name );
1093 			asxml_var_insert( tmp, val162percent( sat16 ) );
1094 			sprintf( tmp+vd_len, "%s.value", name );
1095 			asxml_var_insert( tmp, val162percent( val16 ) );
1096 			free( tmp );
1097 		}
1098 	}
1099 	return NULL;
1100 }
1101 /****** libAfterImage/asimagexml/printf
1102  * NAME
1103  * printf - prints variable value to standard output.
1104  * SYNOPSIS
1105  * <printf format="format_string" var="variable_name" val="expression"/>
1106  * ATTRIBUTES
1107  * format_string  Standard C format string with exactly 1 placeholder.
1108  * var            Name of the variable, which value will be printed.
1109  * val            math expression to be printed.
1110  * NOTES
1111  ******/
1112 static ASImage *
handle_asxml_tag_printf(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm)1113 handle_asxml_tag_printf( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm)
1114 {
1115 	xml_elem_t* ptr ;
1116 	const char* format = NULL;
1117 	const char* var = NULL;
1118 	int val = 0 ;
1119 	Bool use_val = False ;
1120 	int arg_count = 0, i;
1121 	LOCAL_DEBUG_OUT("doc = %p, parm = %p", doc, parm );
1122 	for (ptr = parm ; ptr ; ptr = ptr->next)
1123 	{
1124 		if (!strcmp(ptr->tag, "format")) format = ptr->parm;
1125 		else if (!strcmp(ptr->tag, "var")) { var = ptr->parm; use_val = False; }
1126 		else if (!strcmp(ptr->tag, "val")) { val = (int)parse_math(ptr->parm, NULL, 0); use_val = True; }
1127 	}
1128 
1129 	if( format != NULL )
1130 	{
1131 		char *interpreted_format = interpret_ctrl_codes( mystrdup(format) );
1132 
1133 		for( i = 0 ; format[i] != '\0' ; ++i )
1134 			if( format[i] == '%' )
1135 			{
1136 				if( format[i+1] != '%' )
1137 			 		++arg_count ;
1138 				else
1139 					++i ;
1140 			}
1141 
1142 		if( use_val && arg_count == 1)
1143 			printf( interpreted_format, val );
1144 		else if( var != NULL && arg_count == 1 )
1145 			printf( interpreted_format, asxml_var_get(var) );
1146 		else if( arg_count == 0 )
1147 			fputs( interpreted_format, stdout );
1148 		free( interpreted_format );
1149 	}
1150 
1151 	return NULL;
1152 }
1153 /****** libAfterImage/asimagexml/set
1154  * NAME
1155  * set - declares variable, assigning it a numeric value of expression.
1156  * SYNOPSIS
1157  * <set var="variable_name" domain="var_domain" val="expression"/>
1158  * ATTRIBUTES
1159  * var            Name of the variable, which value will be set.
1160  * val            math expression to be evaluated.
1161  * domain         (optional) variable's domain to be prepended to its name
1162  *                using format var_domain.variable_name
1163  ******/
1164 static ASImage *
handle_asxml_tag_set(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm)1165 handle_asxml_tag_set( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm)
1166 {
1167 	xml_elem_t* ptr ;
1168 	const char* var_domain = NULL ;
1169 	const char* var = NULL;
1170 	int val = 0 ;
1171 	LOCAL_DEBUG_OUT("doc = %p, parm = %p", doc, parm );
1172 	for (ptr = parm ; ptr ; ptr = ptr->next)
1173 	{
1174 		if (!strcmp(ptr->tag, "var")) 			var = ptr->parm;
1175 		else if (!strcmp(ptr->tag, "domain")) 	var_domain = ptr->parm;
1176 		else if (!strcmp(ptr->tag, "val"))  	val = (int)parse_math(ptr->parm, NULL, 0);
1177 	}
1178 
1179 	if( var != NULL )
1180 	{
1181 		char *tmp = (char*)var ;
1182 		if( var_domain && var_domain[0] != '\0' )
1183 		{
1184 			int vd_len = strlen(var_domain);
1185 			tmp = safemalloc( vd_len + 1 + strlen(var) + 1 );
1186 			sprintf( tmp, ( var_domain[vd_len-1] != '.' )?"%s.%s":"%s%s", var_domain, var );
1187 		}
1188 		asxml_var_insert( tmp, val );
1189 		if( tmp != var )
1190 			free( tmp );
1191 	}
1192 
1193 	return NULL;
1194 }
1195 /****** libAfterImage/asimagexml/if
1196  * NAME
1197  * if - evaluates logical expression and if result evaluates to not true(or false
1198  * if <unless> tag is used ), handles tags within.
1199  * SYNOPSIS
1200  * <if val1="expression" [op="gt|lt|ge|le|eq|ne" val2="expression"]/>
1201  * 	[<then>...</then><else>...</else>]
1202  * </if>
1203  * <unless val1="expression" [op="gt|lt|ge|le|eq|ne" val2="expression"]/>
1204  * ATTRIBUTES
1205  * val1            math expression to be evaluated.
1206  * val2            math expression to be evaluated.
1207  * op         	 (optional) comparison op to be applied to values
1208  * EXAMPLE
1209  * <if val1="$ascs.Base.value" val2="50" op="gt"><then>...</then><else>...</else></if>
1210  ******/
1211 static ASImage *
handle_asxml_tag_if(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm)1212 handle_asxml_tag_if( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm)
1213 {
1214 	xml_elem_t* ptr ;
1215 	int val1 = 0, val2 = 0 ;
1216 	const char *op = NULL ;
1217 	int res = 0 ;
1218 	ASImage *im = NULL, *imtmp = NULL  ;
1219 	LOCAL_DEBUG_OUT("doc = %p, parm = %p", doc, parm );
1220 
1221 	for (ptr = parm ; ptr ; ptr = ptr->next)
1222 	{
1223 		if (!strcmp(ptr->tag, "op")) 			op = ptr->parm;
1224 		else if (!strcmp(ptr->tag, "val1"))  	val1 = (int)parse_math(ptr->parm, NULL, 0);
1225 		else if (!strcmp(ptr->tag, "val2"))  	val2 = (int)parse_math(ptr->parm, NULL, 0);
1226 	}
1227 
1228 	if( op != NULL )
1229 	{
1230 		if     ( strcmp(op, "gt") == 0 ) res = (val1 > val2);
1231 		else if( strcmp(op, "lt") == 0 ) res = (val1 < val2);
1232 		else if( strcmp(op, "ge") == 0 ) res = (val1 >= val2);
1233 		else if( strcmp(op, "le") == 0 ) res = (val1 <= val2);
1234 		else if( strcmp(op, "eq") == 0 ) res = (val1 == val2);
1235 		else if( strcmp(op, "ne") == 0 ) res = (val1 != val2);
1236 	}
1237 
1238 	if( doc->tag[0] == 'u' ) /* <unless> */
1239 		res = !res ;
1240 
1241 	ptr = NULL ;
1242 	for (ptr = doc->child ; ptr ; ptr = ptr->next)
1243 	{
1244 		if( strcmp(ptr->tag, res?"then":"else" ) )
1245 		{
1246 			ptr = ptr->child ;
1247 			break;
1248 		}
1249 		if( res && ptr->next == NULL )
1250 			ptr = doc->child ;
1251 	}
1252 
1253 	while( ptr )
1254 	{
1255 		imtmp = build_image_from_xml(state->asv, state->imman, state->fontman, ptr, NULL, state->flags, state->verbose, state->display_win);
1256 		if( im && imtmp ) safe_asimage_destroy( im );
1257 		if( imtmp ) im = imtmp ;
1258 		 ptr = ptr->next ;
1259 	}
1260 	return im ;
1261 }
1262 
1263 /****** libAfterImage/asimagexml/gradient
1264  * NAME
1265  * gradient - render multipoint gradient.
1266  * SYNOPSIS
1267  * <gradient id="new_id" angle="degrees"
1268  *           refid="refid" width="pixels" height="pixels"
1269  *           colors ="color1 color2 color3 [...]"
1270  *           offsets="fraction1 fraction2 fraction3 [...]"/>
1271  * ATTRIBUTES
1272  * id       Optional.  Image will be given this name for future reference.
1273  * refid    Optional.  An image ID defined with the "id" parameter for
1274  *          any previously created image.  If set, percentages in "width"
1275  *          and "height" will be derived from the width and height of the
1276  *          refid image.
1277  * width    Optional.  The result will have this width.
1278  * height   Optional.  The result will have this height.
1279  * colors   Required.  Whitespace-separated list of colors.  At least two
1280  *          colors are required.  Each color in this list will be visited
1281  *          in turn, at the intervals given by the offsets attribute.
1282  * offsets  Optional.  Whitespace-separated list of floating point values
1283  *          ranging from 0.0 to 1.0.  The colors from the colors attribute
1284  *          are given these offsets, and the final gradient is rendered
1285  *          from the combination of the two.  If both colors and offsets
1286  *          are given but the number of colors and offsets do not match,
1287  *          the minimum of the two will be used, and the other will be
1288  *          truncated to match.  If offsets are not given, a smooth
1289  *          stepping from 0.0 to 1.0 will be used.
1290  * angle    Optional.  Given in degrees.  Default is 0.  This is the
1291  *          direction of the gradient.  Currently the only supported
1292  *          values are 0, 45, 90, 135, 180, 225, 270, 315.  0 means left
1293  *          to right, 90 means top to bottom, etc.
1294  *****/
1295 static ASImage *
handle_asxml_tag_gradient(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,int width,int height)1296 handle_asxml_tag_gradient( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, int width, int height)
1297 {
1298 	ASImage *result = NULL ;
1299 	xml_elem_t* ptr ;
1300 	double angle = 0;
1301 	char* color_str = NULL;
1302 	char* offset_str = NULL;
1303 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, width = %d, height = %d", doc, parm, width, height );
1304 	for (ptr = parm ; ptr ; ptr = ptr->next) {
1305 		if (!strcmp(ptr->tag, "angle")) angle = strtod(ptr->parm, NULL);
1306 		else if (!strcmp(ptr->tag, "colors")) color_str = ptr->parm;
1307 		else if (!strcmp(ptr->tag, "offsets")) offset_str = ptr->parm;
1308 	}
1309 	if ( color_str)
1310 	{
1311 		ASGradient gradient;
1312 		int reverse = 0, npoints1 = 0, npoints2 = 0;
1313 		char* p;
1314 		angle = fmod(angle, 2 * PI);
1315 		if (angle > 2 * PI * 15 / 16 || angle < 2 * PI * 1 / 16) {
1316 			gradient.type = GRADIENT_Left2Right;
1317 		} else if (angle < 2 * PI * 3 / 16) {
1318 			gradient.type = GRADIENT_TopLeft2BottomRight;
1319 		} else if (angle < 2 * PI * 5 / 16) {
1320 			gradient.type = GRADIENT_Top2Bottom;
1321 		} else if (angle < 2 * PI * 7 / 16) {
1322 			gradient.type = GRADIENT_BottomLeft2TopRight; reverse = 1;
1323 		} else if (angle < 2 * PI * 9 / 16) {
1324 			gradient.type = GRADIENT_Left2Right; reverse = 1;
1325 		} else if (angle < 2 * PI * 11 / 16) {
1326 			gradient.type = GRADIENT_TopLeft2BottomRight; reverse = 1;
1327 		} else if (angle < 2 * PI * 13 / 16) {
1328 			gradient.type = GRADIENT_Top2Bottom; reverse = 1;
1329 		} else {
1330 			gradient.type = GRADIENT_BottomLeft2TopRight;
1331 		}
1332 		for (p = color_str ; isspace((int)*p) ; p++);
1333 		for (npoints1 = 0 ; *p ; npoints1++) {
1334 			if (*p) for ( ; *p && !isspace((int)*p) ; p++);
1335 			for ( ; isspace((int)*p) ; p++);
1336 		}
1337 		if (offset_str) {
1338 			for (p = offset_str ; isspace((int)*p) ; p++);
1339 			for (npoints2 = 0 ; *p ; npoints2++) {
1340 				if (*p) for ( ; *p && !isspace((int)*p) ; p++);
1341 				for ( ; isspace((int)*p) ; p++);
1342 			}
1343 		}
1344 		gradient.npoints = max( npoints1, npoints2 );
1345 		if (npoints1 > 1) {
1346 			int i;
1347 			gradient.color = safecalloc(gradient.npoints, sizeof(ARGB32));
1348 			gradient.offset = NEW_ARRAY(double, gradient.npoints);
1349 			for (p = color_str ; isspace((int)*p) ; p++);
1350 			for (npoints1 = 0 ; *p ; ) {
1351 				char* pb = p, ch;
1352 				if (*p) for ( ; *p && !isspace((int)*p) ; p++);
1353 				for ( ; isspace((int)*p) ; p++);
1354 				ch = *p; *p = '\0';
1355 				if (parse_argb_color(pb, gradient.color + npoints1) != pb)
1356 				{
1357 					npoints1++;
1358 				}else
1359 					show_warning( "failed to parse color [%s] - defaulting to black", pb );
1360 				*p = ch;
1361 			}
1362 			if (offset_str) {
1363 				for (p = offset_str ; isspace((int)*p) ; p++);
1364 				for (npoints2 = 0 ; *p ; ) {
1365 					char* pb = p, ch;
1366 					if (*p) for ( ; *p && !isspace((int)*p) ; p++);
1367 					ch = *p; *p = '\0';
1368 					gradient.offset[npoints2] = strtod(pb, &pb);
1369 					if (pb == p) npoints2++;
1370 					*p = ch;
1371 					for ( ; isspace((int)*p) ; p++);
1372 				}
1373 			} else {
1374 				for (npoints2 = 0 ; npoints2 < gradient.npoints ; npoints2++)
1375 					gradient.offset[npoints2] = (double)npoints2 / (gradient.npoints - 1);
1376 			}
1377 			if (reverse) {
1378 				for (i = 0 ; i < gradient.npoints / 2 ; i++) {
1379 					int i2 = gradient.npoints - 1 - i;
1380 					ARGB32 c = gradient.color[i];
1381 					double o = gradient.offset[i];
1382 					gradient.color[i] = gradient.color[i2];
1383 					gradient.color[i2] = c;
1384 					gradient.offset[i] = gradient.offset[i2];
1385 					gradient.offset[i2] = o;
1386 				}
1387 				for (i = 0 ; i < gradient.npoints ; i++) {
1388 					gradient.offset[i] = 1.0 - gradient.offset[i];
1389 				}
1390 			}
1391 			if (state->verbose > 1) {
1392 				show_progress("Generating [%dx%d] gradient with angle [%f] and npoints [%d/%d].", width, height, angle, npoints1, npoints2);
1393 				for (i = 0 ; i < gradient.npoints ; i++) {
1394 					show_progress("  Point [%d] has color [#%08x] and offset [%f].", i, (unsigned int)gradient.color[i], gradient.offset[i]);
1395 				}
1396 			}
1397 			result = make_gradient(state->asv, &gradient, width, height, SCL_DO_ALL, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
1398 			if( gradient.color )
1399 				free( gradient.color );
1400 			if( gradient.offset )
1401 				free( gradient.offset );
1402 		}
1403 	}
1404 	return result;
1405 }
1406 
1407 /****** libAfterImage/asimagexml/solid
1408  * NAME
1409  * solid - generate image of specified size and fill it with solid color.
1410  * SYNOPSIS
1411  * <solid id="new_id" color="color" opacity="opacity"
1412  * 	width="pixels" height="pixels"
1413  *	refid="refid" width="pixels" height="pixels"/>
1414  *
1415  * ATTRIBUTES
1416  * id       Optional. Image will be given this name for future reference.
1417  * width    Optional.  The result will have this width.
1418  * height   Optional.  The result will have this height.
1419  * refid    Optional.  An image ID defined with the "id" parameter for
1420  *          any previously created image.  If set, percentages in "width"
1421  *          and "height" will be derived from the width and height of the
1422  *          refid image.
1423  * color    Optional.  Default is "#ffffffff".  An image will be created
1424  *          and filled with this color.
1425  * width    Required.  The image will have this width.
1426  * height   Required.  The image will have this height.
1427  * opacity  Optional. Default is 100. Values from 0 to 100 represent the
1428  *          opacity of resulting image with 100 being completely opaque.
1429  * 		    Effectively overrides alpha component of the color setting.
1430  ******/
1431 static ASImage *
handle_asxml_tag_solid(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,int width,int height)1432 handle_asxml_tag_solid( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, int width, int height)
1433 {
1434 	ASImage *result = NULL ;
1435 	xml_elem_t* ptr;
1436 	Bool opacity_set = False ;
1437 	int opacity = 100 ;
1438 	ARGB32 color = ARGB32_White;
1439 	CARD32 a, r, g, b ;
1440 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, width = %d, height = %d", doc, parm, width, height );
1441 	for (ptr = parm ; ptr ; ptr = ptr->next) {
1442 		if (!strcmp(ptr->tag, "color")) parse_argb_color(ptr->parm, &color);
1443 		else if (!strcmp(ptr->tag, "opacity")) { opacity = atol(ptr->parm); opacity_set = True ; }
1444 	}
1445 	if( state->verbose > 1 )
1446 		show_progress("Creating solid color [#%08x] image [%dx%d].", (unsigned int)color, width, height);
1447 	result = create_asimage(width, height, ASIMAGE_QUALITY_TOP);
1448 	if( opacity < 0 ) opacity = 0 ;
1449 	else if( opacity > 100 )  opacity = 100 ;
1450 	a = opacity_set? (0x000000FF * (CARD32)opacity)/100: ARGB32_ALPHA8(color);
1451 	r = ARGB32_RED8(color);
1452 	g = ARGB32_GREEN8(color);
1453 	b = ARGB32_BLUE8(color);
1454 	color = MAKE_ARGB32(a,r,g,b);
1455 	if (result)
1456 		fill_asimage(state->asv, result, 0, 0, width, height, color);
1457 
1458 	return result;
1459 }
1460 
1461 
1462 
1463 /****** libAfterImage/asimagexml/save
1464  * NAME
1465  * save - write generated/loaded image into the file of one of the
1466  *        supported types
1467  * SYNOPSIS
1468  * <save id="new_id" dst="filename" format="format" compress="value"
1469  *       opacity="value" replace="0|1" delay="mlsecs">
1470  * ATTRIBUTES
1471  * id       Optional.  Image will be given this name for future reference.
1472  * dst      Optional.  Name of file image will be saved to. If omitted
1473  *          image will be dumped into stdout - usefull for CGI apps.
1474  * format   Optional.  Ouput format of saved image.  Defaults to the
1475  *          extension of the "dst" parameter.  Valid values are the
1476  *          standard AS image file formats: xpm, jpg, png, gif, tiff.
1477  * compress Optional.  Compression level if supported by output file
1478  *          format. Valid values are in range of 0 - 100 and any of
1479  *          "deflate", "jpeg", "ojpeg", "packbits" for TIFF files.
1480  *          Note that JPEG and GIF will produce images with deteriorated
1481  *          quality when compress is greater then 0. For JPEG default is
1482  *          25, for PNG default is 6 and for GIF it is 0.
1483  * opacity  Optional. Level below which pixel is considered to be
1484  *          transparent, while saving image as XPM or GIF. Valid values
1485  *          are in range 0-255. Default is 127.
1486  * replace  Optional. Causes ascompose to delete file if the file with the
1487  *          same name already exists. Valid values are 0 and 1. Default
1488  *          is 1 - files are deleted before being saved. Disable this to
1489  *          get multimage animated gifs.
1490  * delay    Optional. Delay to be stored in GIF image. This could be used
1491  *          to create animated gifs. Note that you have to set replace="0"
1492  *          and then write several images into the GIF file with the same
1493  *          name.
1494  * NOTES
1495  * This tag applies to the first image contained within the tag.  Any
1496  * further images will be discarded.
1497  *******/
1498 static ASImage *
handle_asxml_tag_save(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp)1499 handle_asxml_tag_save( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp)
1500 {
1501 	ASImage *result = NULL ;
1502 	xml_elem_t* ptr ;
1503 	const char* dst = NULL;
1504 	const char* ext = NULL;
1505 	const char* compress = NULL ;
1506 	const char* opacity = NULL ;
1507 	int delay = 0 ;
1508 	int replace = 1;
1509 	/*<save id="" dst="" format="" compression="" delay="" replace="" opacity=""> */
1510 	int autoext = 0;
1511 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p", doc, parm, imtmp );
1512 	for (ptr = parm ; ptr ; ptr = ptr->next) {
1513 		if (!strcmp(ptr->tag, "dst")) dst = ptr->parm;
1514 		else if (!strcmp(ptr->tag, "format")) ext = ptr->parm;
1515 		else if (!strncmp(ptr->tag, "compress", 8)) compress = ptr->parm;
1516 		else if (!strcmp(ptr->tag, "opacity")) opacity = ptr->parm;
1517 		else if (!strcmp(ptr->tag, "delay"))   delay = atoi(ptr->parm);
1518 		else if (!strcmp(ptr->tag, "replace")) replace = atoi(ptr->parm);
1519 	}
1520 	if (dst && !ext) {
1521 		ext = strrchr(dst, '.');
1522 		if (ext) ext++;
1523 		autoext = 1;
1524 	}
1525 
1526 	result = imtmp;
1527 
1528 	if ( autoext && ext )
1529 		show_warning("No format given.  File extension [%s] used as format.", ext);
1530 	if( state->verbose > 1 )
1531 		show_progress("reSaving image to file [%s].", dst?dst:"stdout");
1532 	if (result && get_flags( state->flags, ASIM_XML_ENABLE_SAVE) )
1533 	{
1534 		if( state->verbose > 1 )
1535 			show_progress("Saving image to file [%s].", dst?dst:"stdout");
1536 		if( !save_asimage_to_file(dst, result, ext, compress, opacity, delay, replace))
1537 			show_error("Unable to save image into file [%s].", dst?dst:"stdout");
1538 	}
1539 
1540 	return result;
1541 }
1542 
1543 /****** libAfterImage/asimagexml/background
1544  * NAME
1545  * background - set image's background color.
1546  * SYNOPSIS
1547  *  <background id="new_id" color="color">
1548  * ATTRIBUTES
1549  * id       Optional. Image will be given this name for future reference.
1550  * color    Required. Color to be used for background - fills all the
1551  *          spaces in image with missing pixels.
1552  * NOTES
1553  * This tag applies to the first image contained within the tag.  Any
1554  * further images will be discarded.
1555  ******/
1556 static ASImage *
handle_asxml_tag_background(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp)1557 handle_asxml_tag_background( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp)
1558 {
1559 	ASImage *result = NULL ;
1560 	xml_elem_t* ptr ;
1561 	ARGB32 argb = ARGB32_Black;
1562 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p", doc, parm, imtmp );
1563 	for (ptr = parm ; ptr ; ptr = ptr->next) {
1564 		if (!strcmp(ptr->tag, "color")) parse_argb_color( ptr->parm, &argb );
1565 	}
1566 	if (imtmp) {
1567 		result = clone_asimage( imtmp, SCL_DO_ALL );
1568 		result->back_color = argb ;
1569 	}
1570 	if( state->verbose > 1 )
1571 		show_progress( "Setting back_color for image %p to 0x%8.8X", result, argb );
1572 	return result;
1573 }
1574 
1575 /****** libAfterImage/asimagexml/blur
1576  * NAME
1577  * blur - perform a gaussian blurr on an image.
1578  * SYNOPSIS
1579  * <blur id="new_id" horz="radius" vert="radius" channels="argb">
1580  * ATTRIBUTES
1581  * id       Optional. Image will be given this name for future reference.
1582  * horz     Optional. Horizontal radius of the blur in pixels.
1583  * vert     Optional. Vertical radius of the blur in pixels.
1584  * channels Optional. Applys blur only on listed color channels:
1585  *                       a - alpha,
1586  *                       r - red,
1587  *                       g - green,
1588  *                       b - blue
1589  * NOTES
1590  * This tag applies to the first image contained within the tag.  Any
1591  * further images will be discarded.
1592  ******/
1593 static ASImage *
handle_asxml_tag_blur(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp)1594 handle_asxml_tag_blur( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp)
1595 {
1596 	ASImage *result = NULL ;
1597 	xml_elem_t* ptr ;
1598 	int horz = 0, vert = 0;
1599     int filter = SCL_DO_ALL;
1600 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p", doc, parm, imtmp );
1601 	for (ptr = parm ; ptr ; ptr = ptr->next)
1602 	{
1603 		if (!strcmp(ptr->tag, "horz")) horz = atoi(ptr->parm);
1604         else if (!strcmp(ptr->tag, "vert")) vert = atoi(ptr->parm);
1605         else if (!strcmp(ptr->tag, "channels"))
1606         {
1607             int i = 0 ;
1608             char *str = &(ptr->parm[0]) ;
1609             filter = 0 ;
1610             while( str[i] != '\0' )
1611             {
1612                 if( str[i] == 'a' )
1613                     filter |= SCL_DO_ALPHA ;
1614                 else if( str[i] == 'r' )
1615                     filter |= SCL_DO_RED ;
1616                 else if( str[i] == 'g' )
1617                     filter |= SCL_DO_GREEN ;
1618                 else if( str[i] == 'b' )
1619                     filter |= SCL_DO_BLUE ;
1620                 ++i ;
1621             }
1622         }
1623 	}
1624     result = blur_asimage_gauss(state->asv, imtmp, horz, vert, filter, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
1625 	if( state->verbose > 1 )
1626 		show_progress("Blurrer image with radii %d, %d.", horz, vert);
1627 	return result;
1628 }
1629 
1630 
1631 
1632 /****** libAfterImage/asimagexml/bevel
1633  * NAME
1634  * bevel - draws solid bevel frame around the image.
1635  * SYNOPSIS
1636  * <bevel id="new_id" colors="color1 color2"
1637  * 		  width="pixels" height="pixels" refid="refid"
1638  *        border="left top right bottom" solid=0|1 outline=0|1>
1639  * ATTRIBUTES
1640  * id       Optional.  Image will be given this name for future reference.
1641  * colors   Optional.  Whitespace-separated list of colors.  Exactly two
1642  *          colors are required.  Default is "#ffdddddd #ff555555".  The
1643  *          first color is the color of the upper and left edges, and the
1644  *          second is the color of the lower and right edges.
1645  * borders  Optional.  Whitespace-separated list of integer values.
1646  *          Default is "10 10 10 10".  The values represent the offsets
1647  *          toward the center of the image of each border: left, top,
1648  *          right, bottom.
1649  * solid    Optional - default is 1. If set to 0 will draw bevel gradually
1650  *          fading into the image.
1651  * outline  Optional - default is 0. If set to 1 will draw bevel around the
1652  * 			image vs. inside the image.
1653  * width    Optional. The result will have this width.
1654  * height   Optional. The result will have this height.
1655  * refid    Optional.  An image ID defined with the "id" parameter for
1656  *          any previously created image.  If set, percentages in "width"
1657  *          and "height" will be derived from the width and height of the
1658  *          refid image.
1659  * NOTES
1660  * This tag applies to the first image contained within the tag.  Any
1661  * further images will be discarded.
1662  ******/
1663 static ASImage *
handle_asxml_tag_bevel(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)1664 handle_asxml_tag_bevel( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
1665 {
1666 	ASImage *result = NULL ;
1667 	xml_elem_t* ptr ;
1668 	char* color_str = NULL;
1669 	char* border_str = NULL;
1670 	int solid = 1, outline = 0 ;
1671 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
1672 	for (ptr = parm ; ptr ; ptr = ptr->next) {
1673 		if (!strcmp(ptr->tag, "colors")) color_str = ptr->parm;
1674 		else if (!strcmp(ptr->tag, "border")) border_str = ptr->parm;
1675 		else if (!strcmp(ptr->tag, "solid")) solid = atoi(ptr->parm);
1676 		else if (!strcmp(ptr->tag, "outline")) outline = atoi(ptr->parm);
1677 	}
1678 	if (imtmp)
1679 	{
1680 		ASImageBevel bevel;
1681 		ASImageLayer layer;
1682 		memset( &bevel, 0x00, sizeof(ASImageBevel) );
1683 		if( solid )
1684 			bevel.type = BEVEL_SOLID_INLINE;
1685 		bevel.hi_color = 0xffdddddd;
1686 		bevel.lo_color = 0xff555555;
1687 		if( outline )
1688 			bevel.top_outline = bevel.left_outline = bevel.right_outline = bevel.bottom_outline = 10;
1689 		else
1690 			bevel.top_inline = bevel.left_inline = bevel.right_inline = bevel.bottom_inline = 10;
1691 		if (color_str) {
1692 			char* p = color_str;
1693 			while (isspace((int)*p)) p++;
1694 			p = (char*)parse_argb_color(p, &bevel.hi_color);
1695 			while (isspace((int)*p)) p++;
1696 			parse_argb_color(p, &bevel.lo_color);
1697 		}
1698 		if (border_str) {
1699 			char* p = (char*)border_str;
1700 			if( outline )
1701 			{
1702 				bevel.left_outline = (unsigned short)parse_math(p, &p, width);
1703 				bevel.top_outline = (unsigned short)parse_math(p, &p, height);
1704 				bevel.right_outline = (unsigned short)parse_math(p, &p, width);
1705 				bevel.bottom_outline = (unsigned short)parse_math(p, &p, height);
1706 			}else
1707 			{
1708 				bevel.left_inline = (unsigned short)parse_math(p, &p, width);
1709 				bevel.top_inline = (unsigned short)parse_math(p, &p, height);
1710 				bevel.right_inline = (unsigned short)parse_math(p, &p, width);
1711 				bevel.bottom_inline = (unsigned short)parse_math(p, &p, height);
1712 			}
1713 		}
1714 		bevel.hihi_color = bevel.hi_color;
1715 		bevel.hilo_color = bevel.hi_color;
1716 		bevel.lolo_color = bevel.lo_color;
1717 		if( state->verbose > 1 )
1718 			show_progress("Generating bevel with offsets [%d %d %d %d] and colors [#%08x #%08x].", bevel.left_inline, bevel.top_inline, bevel.right_inline, bevel.bottom_inline, (unsigned int)bevel.hi_color, (unsigned int)bevel.lo_color);
1719 		init_image_layers( &layer, 1 );
1720 		layer.im = imtmp;
1721 		if( width <= bevel.left_outline+bevel.right_outline )
1722 			layer.clip_width = 1;
1723 		else
1724 			layer.clip_width = width-(bevel.left_outline+bevel.right_outline);
1725 		if( height <= bevel.top_outline+bevel.bottom_outline )
1726 			layer.clip_height = 1;
1727 		else
1728 			layer.clip_height = height-(bevel.top_outline+bevel.bottom_outline);
1729 		layer.bevel = &bevel;
1730 		result = merge_layers(state->asv, &layer, 1,
1731 							  width, height, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
1732 	}
1733 	return result;
1734 }
1735 
1736 /****** libAfterImage/asimagexml/mirror
1737  * NAME
1738  * mirror - create new image as mirror copy of an old one.
1739  * SYNOPSIS
1740  *  <mirror id="new_id" dir="direction"
1741  * 			width="pixels" height="pixels" refid="refid">
1742  * ATTRIBUTES
1743  * id       Optional. Image will be given this name for future reference.
1744  * dir      Required. Possible values are "vertical" and "horizontal".
1745  *          The image will be flipped over the x-axis if dir is vertical,
1746  *          and flipped over the y-axis if dir is horizontal.
1747  * width    Optional.  The result will have this width.
1748  * height   Optional.  The result will have this height.
1749  * refid    Optional.  An image ID defined with the "id" parameter for
1750  *          any previously created image.  If set, percentages in "width"
1751  *          and "height" will be derived from the width and height of the
1752  *          refid image.
1753  * NOTES
1754  * This tag applies to the first image contained within the tag.  Any
1755  * further images will be discarded.
1756  ******/
1757 static ASImage *
handle_asxml_tag_mirror(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)1758 handle_asxml_tag_mirror( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
1759 {
1760 	ASImage *result = NULL ;
1761 	xml_elem_t* ptr ;
1762 	int dir = 0;
1763 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
1764 	for (ptr = parm ; ptr ; ptr = ptr->next) {
1765 		if (!strcmp(ptr->tag, "dir")) dir = !mystrcasecmp(ptr->parm, "vertical");
1766 	}
1767 	result = mirror_asimage(state->asv, imtmp, 0, 0, width, height, dir,
1768 							ASA_ASImage,
1769 							0, ASIMAGE_QUALITY_DEFAULT);
1770 	if( state->verbose > 1 )
1771 		show_progress("Mirroring image [%sally].", dir ? "horizont" : "vertic");
1772 	return result;
1773 }
1774 
1775 /****** libAfterImage/asimagexml/rotate
1776  * NAME
1777  * rotate - rotate an image in 90 degree increments (flip).
1778  * SYNOPSIS
1779  *  <rotate id="new_id" angle="degrees"
1780  * 			width="pixels" height="pixels" refid="refid">
1781   * ATTRIBUTES
1782  * id       Optional. Image will be given this name for future reference.
1783  * angle    Required.  Given in degrees.  Possible values are currently
1784  *          "90", "180", and "270".  Rotates the image through the given
1785  *          angle.
1786  * width    Optional.  The result will have this width.
1787  * height   Optional.  The result will have this height.
1788  * refid    Optional.  An image ID defined with the "id" parameter for
1789  *          any previously created image.  If set, percentages in "width"
1790  *          and "height" will be derived from the width and height of the
1791  *          refid image.
1792  * NOTES
1793  * This tag applies to the first image contained within the tag.  Any
1794  * further images will be discarded.
1795  ******/
1796 static ASImage *
handle_asxml_tag_rotate(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)1797 handle_asxml_tag_rotate( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
1798 {
1799 	ASImage *result = NULL ;
1800 	xml_elem_t* ptr ;
1801 	double angle = 0;
1802 	int dir = 0;
1803 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
1804 	for (ptr = parm ; ptr ; ptr = ptr->next)
1805 		if (!strcmp(ptr->tag, "angle")) angle = strtod(ptr->parm, NULL);
1806 
1807 	angle = fmod(angle, 2 * PI);
1808 	if (angle > 2 * PI * 7 / 8 || angle < 2 * PI * 1 / 8)
1809 		dir = 0;
1810 	else if (angle < 2 * PI * 3 / 8)
1811 		dir = FLIP_VERTICAL;
1812 	else if (angle < 2 * PI * 5 / 8)
1813 		dir = FLIP_UPSIDEDOWN;
1814 	else
1815 		dir = FLIP_VERTICAL | FLIP_UPSIDEDOWN;
1816 	if (dir)
1817 	{
1818 		if( get_flags(dir, FLIP_VERTICAL))
1819 		{
1820 			int tmp = width ;
1821 			width = height ;
1822 			height = tmp ;
1823 		}
1824 		result = flip_asimage(state->asv, imtmp, 0, 0, width, height, dir, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
1825 		if( state->verbose > 1 )
1826 			show_progress("Rotating image [%f degrees].", angle);
1827 	} else
1828 		result = imtmp;
1829 
1830 	return result;
1831 }
1832 
1833 /****** libAfterImage/asimagexml/scale
1834  * NAME
1835  * scale - scale image to arbitrary size
1836  * SYNOPSIS
1837  * <scale id="new_id" refid="other_imag" src_x="pixels"  src_y="pixels"
1838  *        src_width="pixels" src_height="pixels"
1839  *        width="pixels" height="pixels">
1840  * ATTRIBUTES
1841  * id       Optional. Image will be given this name for future reference.
1842  * refid    Optional.  An image ID defined with the "id" parameter for
1843  *          any previously created image.  If set, percentages in "width"
1844  *          and "height" will be derived from the width and height of the
1845  *          refid image.
1846  * width    Required.  The image will be scaled to this width.
1847  * height   Required.  The image will be scaled to this height.
1848  * src_x   Optional. Default is 0. X Offset on infinite surface tiled
1849  *          with this image, from which to cut portion of an image to be
1850  *          used in scaling.
1851  * src_y   Optional. Default is 0. Y Offset on infinite surface tiled
1852  *          with this image, from which to cut portion of an image to be
1853  *          used in scaling.
1854  * src_width
1855  *          Optional. Default is image width. Tile image to this width
1856  *          prior to scaling.
1857  * src_height
1858  *          Optional. Default is image height. Tile image to this height
1859  *          prior to scaling.
1860   * NOTES
1861  * This tag applies to the first image contained within the tag.  Any
1862  * further images will be discarded.
1863  * If you want to keep image proportions while scaling - use "proportional"
1864  * instead of specific size for particular dimention.
1865  ******/
1866 static ASImage *
handle_asxml_tag_scale(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)1867 handle_asxml_tag_scale( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
1868 {
1869 	ASImage *result = NULL ;
1870 	xml_elem_t* ptr;
1871 	int src_x = 0, src_y = 0 ;
1872 	int src_width = 0, src_height = 0 ;
1873 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
1874 	for (ptr = parm ; ptr ; ptr = ptr->next)
1875 	{
1876 		if (!strcmp(ptr->tag, "src_x")) 		src_x = (int)parse_math(ptr->parm, NULL, width);
1877 		else if (!strcmp(ptr->tag, "src_y")) 	src_y = (int)parse_math(ptr->parm, NULL, width);
1878 		else if (!strcmp(ptr->tag, "src_width")) 	src_width = (int)parse_math(ptr->parm, NULL, width);
1879 		else if (!strcmp(ptr->tag, "src_height")) 	src_height = (int)parse_math(ptr->parm, NULL, width);
1880 	}
1881 	if( state->verbose > 1 )
1882 		show_progress("Scaling image to [%dx%d].", width, height);
1883 	result = scale_asimage2( state->asv, imtmp,
1884 							src_x, src_y, src_width, src_height,
1885 							width, height,
1886 							ASA_ASImage, 100, ASIMAGE_QUALITY_DEFAULT);
1887 	return result;
1888 }
1889 /****** libAfterImage/asimagexml/slice
1890  * NAME
1891  * slice - slice image to arbitrary size leaving corners unchanged
1892  * SYNOPSIS
1893  * <slice id="new_id" ref_id="other_imag" width="pixels" height="pixels"
1894  *        x_start="slice_x_start" x_end="slice_x_end"
1895  * 		  y_start="slice_y_start" y_end="slice_y_end"
1896  * 		  scale="0|1">
1897  * ATTRIBUTES
1898  * id       Optional. Image will be given this name for future reference.
1899  * refid    Optional.  An image ID defined with the "id" parameter for
1900  *          any previously created image.  If set, percentages in "width"
1901  *          and "height" will be derived from the width and height of the
1902  *          refid image.
1903  * width    Required.  The image will be scaled to this width.
1904  * height   Required.  The image will be scaled to this height.
1905  * x_start  Optional. Position at which vertical image slicing begins.
1906  * 			Corresponds to the right side of the left corners.
1907  * x_end    Optional. Position at which vertical image slicing end.
1908  * 			Corresponds to the left side of the right corners.
1909  * y_start  Optional. Position at which horisontal image slicing begins.
1910  *          Corresponds to the bottom side of the top corners.
1911  * y_end    Optional. Position at which horisontal image slicing end.
1912  * 			Corresponds to the top side of the bottom corners.
1913  * scale    Optional. If set to 1 will cause middle portion of the
1914  * 			image to be scaled instead of tiled.
1915  * NOTES
1916  * This tag applies to the first image contained within the tag.  Any
1917  * further images will be discarded.
1918  * Contents of the image between x_start and x_end will be tiled
1919  * horizontally. Contents of the image between y_start and y_end will be
1920  * tiled vertically. This is usefull to get background images to fit the
1921  * size of the text or a widget, while preserving its borders undistorted,
1922  * which is the usuall result of simple scaling.
1923  * If you want to keep image proportions while resizing-use "proportional"
1924  * instead of specific size for particular dimention.
1925  ******/
1926 static ASImage *
handle_asxml_tag_slice(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)1927 handle_asxml_tag_slice( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
1928 {
1929 	ASImage *result = NULL ;
1930 	xml_elem_t* ptr;
1931 	int x_start = 0, x_end = 0 ;
1932 	int y_start = 0, y_end = 0 ;
1933 	Bool scale = False ;
1934 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
1935 	for (ptr = parm ; ptr ; ptr = ptr->next)
1936 	{
1937 		if (!strcmp(ptr->tag, "x_start")) 		x_start = (int)parse_math(ptr->parm, NULL, width);
1938 		else if (!strcmp(ptr->tag, "x_end")) 	x_end = (int)parse_math(ptr->parm, NULL, width);
1939 		else if (!strcmp(ptr->tag, "y_start")) 	y_start = (int)parse_math(ptr->parm, NULL, height);
1940 		else if (!strcmp(ptr->tag, "y_end")) 	y_end = (int)parse_math(ptr->parm, NULL, height);
1941 		else if (!strcmp(ptr->tag, "scale")) 	scale = (ptr->parm[0] == '1');
1942 	}
1943 
1944 	if( state->verbose > 1 )
1945 		show_progress("Slicing image to [%dx%d].", width, height);
1946 	result = slice_asimage2( state->asv, imtmp, x_start, x_end, y_start, y_end, width, height,
1947 							 scale, ASA_ASImage, 100, ASIMAGE_QUALITY_DEFAULT);
1948 	return result;
1949 }
1950 
1951 /****** libAfterImage/asimagexml/pixelize
1952  * NAME
1953  * pixelize - pixelize image using arbitrary pixel size
1954  * SYNOPSIS
1955  * <pixelize id="new_id" ref_id="other_imag" width="pixels" height="pixels"
1956  *        clip_x="clip_x" clip_y="clip_y"
1957  *        pixel_width="pixel_width" pixel_height="pixel_height">
1958  * ATTRIBUTES
1959  * id       Optional. Image will be given this name for future reference.
1960  * refid    Optional.  An image ID defined with the "id" parameter for
1961  *          any previously created image.  If set, percentages in "width"
1962  *          and "height" will be derived from the width and height of the
1963  *          refid image.
1964  * width    Required.  The image will be scaled to this width.
1965  * height   Required.  The image will be scaled to this height.
1966  * clip_x   Optional. Offset into original image.
1967  * clip_y   Optional. Offset into original image.
1968  * pixel_width Required. Horizontal pixelization step;
1969  * pixel_height Required. Vertical pixelization step;
1970  * NOTES
1971  * This tag applies to the first image contained within the tag.  Any
1972  * further images will be discarded.
1973  * If you want to keep image proportions while resizing-use "proportional"
1974  * instead of specific size for particular dimention.
1975  ******/
1976 static ASImage *
handle_asxml_tag_pixelize(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)1977 handle_asxml_tag_pixelize( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
1978 {
1979 	ASImage *result = NULL ;
1980 	xml_elem_t* ptr;
1981 	int clip_x = 0, clip_y = 0 ;
1982 	int pixel_width = 1, pixel_height = 1 ;
1983 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
1984 	for (ptr = parm ; ptr ; ptr = ptr->next)
1985 	{
1986 		if (!strcmp(ptr->tag, "clip_x")) 		clip_x = (int)parse_math(ptr->parm, NULL, width);
1987 		else if (!strcmp(ptr->tag, "clip_y")) 	clip_y = (int)parse_math(ptr->parm, NULL, height);
1988 		else if (!strcmp(ptr->tag, "pixel_width")) 		pixel_width = (int)parse_math(ptr->parm, NULL, width);
1989 		else if (!strcmp(ptr->tag, "pixel_height")) 	pixel_height = (int)parse_math(ptr->parm, NULL, height);
1990 	}
1991 
1992 	if( state->verbose > 1 )
1993 		show_progress("Pixelizing image to [%dx%d] using pixel size %dx%d.",
1994 						width, height, pixel_width, pixel_height);
1995 	result = pixelize_asimage (state->asv, imtmp, clip_x, clip_y, width, height,
1996 							   pixel_width, pixel_height,
1997 							   ASA_ASImage, 100, ASIMAGE_QUALITY_DEFAULT);
1998 	return result;
1999 }
2000 
2001 /****** libAfterImage/asimagexml/color2alpha
2002  * NAME
2003  * color2alpha - set alpha channel based on color closeness to specified color
2004  * SYNOPSIS
2005  * <color2alpha id="new_id" ref_id="other_imag" width="pixels" height="pixels"
2006  *        clip_x="clip_x" clip_y="clip_y"
2007  *        color="color">
2008  * ATTRIBUTES
2009  * id       Optional. Image will be given this name for future reference.
2010  * refid    Optional.  An image ID defined with the "id" parameter for
2011  *          any previously created image.  If set, percentages in "width"
2012  *          and "height" will be derived from the width and height of the
2013  *          refid image.
2014  * width    Required.  The image will be scaled to this width.
2015  * height   Required.  The image will be scaled to this height.
2016  * clip_x   Optional. Offset into original image.
2017  * clip_y   Optional. Offset into original image.
2018  * color    Required. Color to match against.
2019  * NOTES
2020  * This tag applies to the first image contained within the tag.  Any
2021  * further images will be discarded.
2022  * If you want to keep image proportions while resizing-use "proportional"
2023  * instead of specific size for particular dimention.
2024  ******/
2025 static ASImage *
handle_asxml_tag_color2alpha(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)2026 handle_asxml_tag_color2alpha( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
2027 {
2028 	ASImage *result = NULL ;
2029 	xml_elem_t* ptr;
2030 	int clip_x = 0, clip_y = 0 ;
2031 	ARGB32 color;
2032 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
2033 	for (ptr = parm ; ptr ; ptr = ptr->next)
2034 	{
2035 		if (!strcmp(ptr->tag, "clip_x")) 		clip_x = (int)parse_math(ptr->parm, NULL, width);
2036 		else if (!strcmp(ptr->tag, "clip_y")) 	clip_y = (int)parse_math(ptr->parm, NULL, height);
2037 		else if (!strcmp(ptr->tag, "color")) 	parse_argb_color(ptr->parm, &color);
2038 	}
2039 
2040 	if( state->verbose > 1 )
2041 		show_progress("color2alpha image to [%dx%d] using color #%8.8X.", width, height, color);
2042 	result = color2alpha_asimage (state->asv, imtmp, clip_x, clip_y, width, height,
2043 							   		color,
2044 							   		ASA_ASImage, 100, ASIMAGE_QUALITY_DEFAULT);
2045 	return result;
2046 }
2047 
2048 /****** libAfterImage/asimagexml/crop
2049  * NAME
2050  * crop - crop image to arbitrary area within it.
2051  * SYNOPSIS
2052  *  <crop id="new_id" refid="other_image" srcx="pixels" srcy="pixels"
2053  *        width="pixels" height="pixels" tint="color">
2054  * ATTRIBUTES
2055  * id       Optional. Image will be given this name for future reference.
2056  * refid    Optional. An image ID defined with the "id" parameter for
2057  *          any previously created image.  If set, percentages in "width"
2058  *          and "height" will be derived from the width and height of the
2059  *          refid image.
2060  * srcx     Optional. Default is "0". Skip this many pixels from the left.
2061  * srcy     Optional. Default is "0". Skip this many pixels from the top.
2062  * width    Optional. Default is "100%".  Keep this many pixels wide.
2063  * height   Optional. Default is "100%".  Keep this many pixels tall.
2064  * tint     Optional. Additionally tint an image to specified color.
2065  *          Tinting can both lighten and darken an image. Tinting color
2066  *          0 or #7f7f7f7f yeilds no tinting. Tinting can be performed on
2067  *          any channel, including alpha channel.
2068  * NOTES
2069  * This tag applies to the first image contained within the tag.  Any
2070  * further images will be discarded.
2071  ******/
2072 static ASImage *
handle_asxml_tag_crop(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)2073 handle_asxml_tag_crop( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
2074 {
2075 	ASImage *result = NULL ;
2076 	xml_elem_t* ptr;
2077 	ARGB32 tint = 0 ;
2078 	int srcx = 0, srcy = 0;
2079 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
2080 	for (ptr = parm ; ptr ; ptr = ptr->next) {
2081 		if (!strcmp(ptr->tag, "srcx")) srcx = (int)parse_math(ptr->parm, NULL, width);
2082 		else if (!strcmp(ptr->tag, "srcy")) srcy = (int)parse_math(ptr->parm, NULL, height);
2083 		else if (!strcmp(ptr->tag, "tint")) parse_argb_color(ptr->parm, &tint);
2084 	}
2085 	if( state->verbose > 1 )
2086 		show_progress("Cropping image to [%dx%d].", width, height);
2087 	result = tile_asimage(state->asv, imtmp, srcx, srcy, width, height, tint, ASA_ASImage, 100, ASIMAGE_QUALITY_TOP);
2088 	return result;
2089 }
2090 
2091 /****** libAfterImage/asimagexml/tile
2092  * NAME
2093  * tile - tile an image to specified area.
2094  * SYNOPSIS
2095  *  <tile id="new_id" refid="other_image" width="pixels" height="pixels"
2096  *        x_origin="pixels" y_origin="pixels" tint="color" complement=0|1>
2097  * ATTRIBUTES
2098  * id       Optional. Image will be given this name for future reference.
2099  * refid    Optional. An image ID defined with the "id" parameter for
2100  *          any previously created image.  If set, percentages in "width"
2101  *          and "height" will be derived from the width and height of the
2102  *          refid image.
2103  * width    Optional. Default is "100%". The image will be tiled to this
2104  *          width.
2105  * height   Optional. Default is "100%". The image will be tiled to this
2106  *          height.
2107  * x_origin Optional. Horizontal position on infinite surface, covered
2108  *          with tiles of the image, from which to cut out resulting
2109  *          image.
2110  * y_origin Optional. Vertical position on infinite surface, covered
2111  *          with tiles of the image, from which to cut out resulting
2112  *          image.
2113  * tint     Optional. Additionally tint an image to specified color.
2114  *          Tinting can both lighten and darken an image. Tinting color
2115  *          0 or #7f7f7f7f yields no tinting. Tinting can be performed
2116  *          on any channel, including alpha channel.
2117  * complement Optional. Will use color that is the complement to tint color
2118  *          for the tinting, if set to 1. Default is 0.
2119  *
2120  * NOTES
2121  * This tag applies to the first image contained within the tag.  Any
2122  * further images will be discarded.
2123  ******/
2124 static ASImage *
handle_asxml_tag_tile(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)2125 handle_asxml_tag_tile( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
2126 {
2127 	ASImage *result = NULL ;
2128 	xml_elem_t* ptr;
2129 	int xorig = 0, yorig = 0;
2130 	ARGB32 tint = 0 ;
2131 	char *complement_str = NULL ;
2132 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
2133 	for (ptr = parm ; ptr ; ptr = ptr->next) {
2134 		if (!strcmp(ptr->tag, "x_origin")) xorig = (int)parse_math(ptr->parm, NULL, width);
2135 		else if (!strcmp(ptr->tag, "y_origin")) yorig = (int)parse_math(ptr->parm, NULL, height);
2136 		else if (!strcmp(ptr->tag, "tint")) parse_argb_color(ptr->parm, &tint);
2137 		else if (!strcmp(ptr->tag, "complement")) complement_str = ptr->parm;
2138 	}
2139 	if( complement_str )
2140 	{
2141 		register char *ptr = complement_str ;
2142 		CARD32 a = ARGB32_ALPHA8(tint),
2143 				r = ARGB32_RED8(tint),
2144 				g = ARGB32_GREEN8(tint),
2145 				b = ARGB32_BLUE8(tint) ;
2146 		while( *ptr )
2147 		{
2148 			if( *ptr == 'a' ) 		a = ~a ;
2149 			else if( *ptr == 'r' ) 	r = ~r ;
2150 			else if( *ptr == 'g' ) 	g = ~g ;
2151 			else if( *ptr == 'b' ) 	b = ~b ;
2152 			++ptr ;
2153 		}
2154 
2155 		tint = MAKE_ARGB32(a, r, g, b );
2156 	}
2157 	if( state->verbose > 1 )
2158 		show_progress("Tiling image to [%dx%d].", width, height);
2159 	result = tile_asimage(state->asv, imtmp, xorig, yorig, width, height, tint, ASA_ASImage, 100, ASIMAGE_QUALITY_TOP);
2160 	return result;
2161 }
2162 
2163 /****** libAfterImage/asimagexml/hsv
2164  * NAME
2165  * hsv - adjust Hue, Saturation and/or Value of an image and optionally
2166  * tile an image to arbitrary area.
2167  * SYNOPSIS
2168  * <hsv id="new_id" refid="other_image"
2169  *      x_origin="pixels" y_origin="pixels" width="pixels" height="pixels"
2170  *      affected_hue="degrees|color" affected_radius="degrees"
2171  *      hue_offset="degrees" saturation_offset="value"
2172  *      value_offset="value">
2173  * ATTRIBUTES
2174  * id       Optional. Image will be given this name for future reference.
2175  * refid    Optional. An image ID defined with the "id" parameter for
2176  *          any previously created image.  If set, percentages in "width"
2177  *          and "height" will be derived from the width and height of the
2178  *          refid image.
2179  * width    Optional. Default is "100%". The image will be tiled to this
2180  *          width.
2181  * height   Optional. Default is "100%". The image will be tiled to this
2182  *          height.
2183  * x_origin Optional. Horizontal position on infinite surface, covered
2184  *          with tiles of the image, from which to cut out resulting
2185  *          image.
2186  * y_origin Optional. Vertical position on infinite surface, covered
2187  *          with tiles of the image, from which to cut out resulting
2188  *          image.
2189  * affected_hue    Optional. Limits effects to the renage of hues around
2190  *          this hue. If numeric value is specified - it is treated as
2191  *          degrees on 360 degree circle, with :
2192  *              red = 0,
2193  *              yellow = 60,
2194  *              green = 120,
2195  *              cyan = 180,
2196  *              blue = 240,
2197  *              magenta = 300.
2198  *          If colorname or value preceded with # is specified here - it
2199  *          will be treated as RGB color and converted into hue
2200  *          automagically.
2201  * affected_radius
2202  *          Optional. Value in degrees to be used in order to
2203  *          calculate the range of affected hues. Range is determined by
2204  *          substracting and adding this value from/to affected_hue.
2205  * hue_offset
2206  *          Optional. Value by which to adjust the hue.
2207  * saturation_offset
2208  *          Optional. Value by which to adjust the saturation.
2209  * value_offset
2210  *          Optional. Value by which to adjust the value.
2211  * NOTES
2212  * One of the Offsets must be not 0, in order for operation to be
2213  * performed.
2214  *
2215  * This tag applies to the first image contained within the tag.  Any
2216  * further images will be discarded.
2217  ******/
2218 static ASImage *
handle_asxml_tag_hsv(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)2219 handle_asxml_tag_hsv( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
2220 {
2221 	ASImage *result = NULL ;
2222 	xml_elem_t* ptr;
2223 	int affected_hue = 0, affected_radius = 360 ;
2224 	int hue_offset = 0, saturation_offset = 0, value_offset = 0 ;
2225 	int xorig = 0, yorig = 0;
2226 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
2227 	for (ptr = parm ; ptr ; ptr = ptr->next)
2228 	{
2229 		if (!strcmp(ptr->tag, "x_origin")) xorig = (int)parse_math(ptr->parm, NULL, width);
2230 		else if (!strcmp(ptr->tag, "y_origin")) yorig = (int)parse_math(ptr->parm, NULL, height);
2231 		else if (!strcmp(ptr->tag, "affected_hue"))
2232 		{
2233 			if( isdigit( (int)ptr->parm[0] ) )
2234 				affected_hue = (int)parse_math(ptr->parm, NULL, 360);
2235 			else
2236 			{
2237 				ARGB32 color = 0;
2238 				if( parse_argb_color( ptr->parm, &color ) != ptr->parm )
2239 				{
2240 					affected_hue = rgb2hue( ARGB32_RED16(color),
2241 											ARGB32_GREEN16(color),
2242 											ARGB32_BLUE16(color));
2243   					affected_hue = hue162degrees( affected_hue );
2244 				}
2245 			}
2246 		}
2247 		else if (!strcmp(ptr->tag, "affected_radius")) 	affected_radius = (int)parse_math(ptr->parm, NULL, 360);
2248 		else if (!strcmp(ptr->tag, "hue_offset")) 		hue_offset = (int)parse_math(ptr->parm, NULL, 360);
2249 		else if (!strcmp(ptr->tag, "saturation_offset"))saturation_offset = (int)parse_math(ptr->parm, NULL, 100);
2250 		else if (!strcmp(ptr->tag, "value_offset")) 	value_offset = (int)parse_math(ptr->parm, NULL, 100);
2251 	}
2252 	if( hue_offset == -1 && saturation_offset == -1 )
2253 	{
2254 		hue_offset = 0 ;
2255 		saturation_offset = -99 ;
2256 	}
2257 	if (hue_offset!=0 || saturation_offset != 0 || value_offset != 0 )
2258 	{
2259 		result = adjust_asimage_hsv(state->asv, imtmp, xorig, yorig, width, height,
2260 				                    affected_hue, affected_radius,
2261 									hue_offset, saturation_offset, value_offset,
2262 				                    ASA_ASImage, 100, ASIMAGE_QUALITY_TOP);
2263 	}
2264 	if( state->verbose > 1 )
2265 		show_progress("adjusted HSV of the image by [%d,%d,%d] affected hues are %+d-%+d.result = %p", hue_offset, saturation_offset, value_offset, affected_hue-affected_radius, affected_hue+affected_radius, result);
2266 	return result;
2267 }
2268 
2269 /****** libAfterImage/asimagexml/pad
2270  * NAME
2271  * pad - pad an image with solid color rectangles.
2272  * SYNOPSIS
2273  * <pad id="new_id" left="pixels" top="pixels"
2274  *      right="pixels" bottom="pixels" color="color"
2275  * 		refid="refid" width="pixels" height="pixels">
2276  * ATTRIBUTES
2277  * id       Optional. Image will be given this name for future reference.
2278  * width    Optional.  The result will have this width.
2279  * height   Optional.  The result will have this height.
2280  * refid    Optional.  An image ID defined with the "id" parameter for
2281  *          any previously created image.  If set, percentages in "width"
2282  *          and "height" will be derived from the width and height of the
2283  *          refid image.
2284  * left     Optional. Size to add to the left of the image.
2285  * top      Optional. Size to add to the top of the image.
2286  * right    Optional. Size to add to the right of the image.
2287  * bottom   Optional. Size to add to the bottom of the image.
2288  * color    Optional. Color value to fill added areas with. It could be
2289  *          transparent of course. Default is #FF000000 - totally black.
2290  * NOTES
2291  * This tag applies to the first image contained within the tag.  Any
2292  * further images will be discarded.
2293  ******/
2294 static ASImage *
handle_asxml_tag_pad(ASImageXMLState * state,xml_elem_t * doc,xml_elem_t * parm,ASImage * imtmp,int width,int height)2295 handle_asxml_tag_pad( ASImageXMLState *state, xml_elem_t* doc, xml_elem_t* parm, ASImage *imtmp, int width, int height)
2296 {
2297 	ASImage *result = NULL ;
2298 	xml_elem_t* ptr;
2299 	ARGB32 color  = ARGB32_Black;
2300 	int left = 0, top = 0, right = 0, bottom = 0;
2301 	LOCAL_DEBUG_OUT("doc = %p, parm = %p, imtmp = %p, width = %d, height = %d", doc, parm, imtmp, width, height );
2302 	for (ptr = parm ; ptr ; ptr = ptr->next) {
2303 		if (!strcmp(ptr->tag, "left"))   left = (int)parse_math(ptr->parm, NULL, width);
2304 		else if (!strcmp(ptr->tag, "top"))    top = (int)parse_math(ptr->parm, NULL, height);
2305 		else if (!strcmp(ptr->tag, "right"))  right = (int)parse_math(ptr->parm, NULL, width);
2306 		else if (!strcmp(ptr->tag, "bottom")) bottom = (int)parse_math(ptr->parm, NULL, height);
2307 		else if (!strcmp(ptr->tag, "color"))  parse_argb_color(ptr->parm, &color);
2308 	}
2309 	if( state->verbose > 1 )
2310 		show_progress("Padding image to [%dx%d%+d%+d].", width+left+right, height+top+bottom, left, top);
2311 	if (left > 0 || top > 0 || right > 0 || bottom > 0 )
2312 		result = pad_asimage(state->asv, imtmp, left, top, width+left+right, height+top+bottom,
2313 					            color, ASA_ASImage, 100, ASIMAGE_QUALITY_DEFAULT);
2314 	return result;
2315 }
2316 
2317 #define REPLACE_STRING(str,val) do {if(str)free(str);(str) = (val);}while(0)
2318 
2319 /* Each tag is only allowed to return ONE image. */
2320 ASImage*
build_image_from_xml(ASVisual * asv,ASImageManager * imman,ASFontManager * fontman,xml_elem_t * doc,xml_elem_t ** rparm,ASFlagType flags,int verbose,Window display_win)2321 build_image_from_xml( ASVisual *asv, ASImageManager *imman, ASFontManager *fontman, xml_elem_t* doc, xml_elem_t** rparm, ASFlagType flags, int verbose, Window display_win)
2322 {
2323 	xml_elem_t* ptr;
2324 	char* id = NULL;
2325 	ASImage* result = NULL;
2326 	ASImageXMLState state ;
2327 
2328 	if( IsCDATA(doc) )  return NULL ;
2329 
2330 	memset( &state, 0x00, sizeof(state));
2331 	state.flags = flags ;
2332 	state.asv = asv ;
2333 	state.imman = imman ;
2334 	state.fontman = fontman ;
2335 	state.verbose = verbose ;
2336 	state.display_win = display_win ;
2337 
2338 	if( doc )
2339 	{
2340 		xml_elem_t* parm = xml_parse_parm(doc->parm, NULL);
2341 		xml_elem_t* ptr ;
2342 		char* refid = NULL;
2343 		char* width_str = NULL;
2344 		char* height_str = NULL;
2345 		ASImage *refimg = NULL ;
2346 		int width = 0, height = 0 ;
2347 		LOCAL_DEBUG_OUT("parm = %p", parm);
2348 
2349 		for (ptr = parm ; ptr ; ptr = ptr->next)
2350 		{
2351 			if (ptr->tag[0] == 'i' && ptr->tag[1] == 'd' && ptr->tag[2] == '\0')
2352 				REPLACE_STRING(id,mystrdup(ptr->parm));
2353 			else if (strcmp(ptr->tag, "refid") == 0 ) 	refid = ptr->parm ;
2354 			else if (strcmp(ptr->tag, "width") == 0 ) 	width_str = ptr->parm ;
2355 			else if (strcmp(ptr->tag, "height") == 0 ) 	height_str = ptr->parm ;
2356 		}
2357 
2358 		if( id )
2359 			if( (result = fetch_asimage( imman, id)) != NULL )
2360 			{
2361 				free( id );
2362 				xml_elem_delete(NULL, parm);
2363 				return result ;
2364 			}
2365 
2366 		if( refid )
2367 			refimg = fetch_asimage( imman, refid);
2368 
2369 		if (!strcmp(doc->tag, "composite"))
2370 			result = handle_asxml_tag_composite( &state, doc, parm );
2371 		else if (!strcmp(doc->tag, "text"))
2372 			result = handle_asxml_tag_text( &state, doc, parm );
2373 		else if (!strcmp(doc->tag, "img"))
2374 		{
2375 			translate_tag_size(	width_str, height_str, NULL, refimg, &width, &height );
2376 			result = handle_asxml_tag_img( &state, doc, parm, width, height );
2377 		}else if (!strcmp(doc->tag, "recall"))
2378 			result = handle_asxml_tag_recall( &state, doc, parm );
2379 		else if (!strcmp(doc->tag, "release"))
2380 			result = handle_asxml_tag_release( &state, doc, parm );
2381 		else if (!strcmp(doc->tag, "color"))
2382 			result = handle_asxml_tag_color( &state, doc, parm );
2383 		else if (!strcmp(doc->tag, "printf"))
2384 			result = handle_asxml_tag_printf( &state, doc, parm );
2385 		else if (!strcmp(doc->tag, "set"))
2386 			result = handle_asxml_tag_set( &state, doc, parm );
2387 		else if (!strcmp(doc->tag, "if") || !strcmp(doc->tag, "unless") )
2388 			result = handle_asxml_tag_if( &state, doc, parm );
2389 		else if ( !strcmp(doc->tag, "gradient") )
2390 		{
2391 			translate_tag_size(	width_str, height_str, NULL, refimg, &width, &height );
2392 			if( width > 0 && height > 0 )
2393 				result = handle_asxml_tag_gradient( &state, doc, parm, width, height );
2394 		}else if (!strcmp(doc->tag, "solid"))
2395 		{
2396 			translate_tag_size(	width_str, height_str, NULL, refimg, &width, &height );
2397 			if( width > 0 && height > 0 )
2398 				result = handle_asxml_tag_solid( &state, doc, parm, width, height);
2399 		}else
2400 		{
2401 			ASImage *imtmp = NULL ;
2402 
2403 			for (ptr = doc->child ; ptr && !imtmp ; ptr = ptr->next)
2404 				imtmp = build_image_from_xml(asv, imman, fontman, ptr, NULL, flags, verbose, display_win);
2405 
2406 			if( imtmp )
2407 			{
2408 				if (imtmp && !strcmp(doc->tag, "save"))
2409 					result = handle_asxml_tag_save( &state, doc, parm, imtmp );
2410 				else if (imtmp && !strcmp(doc->tag, "background"))
2411 					result = handle_asxml_tag_background( &state, doc, parm, imtmp );
2412 				else if (imtmp && !strcmp(doc->tag, "blur"))
2413 					result = handle_asxml_tag_blur( &state, doc, parm, imtmp );
2414 				else
2415 				{
2416 					translate_tag_size(	width_str, height_str, imtmp, refimg, &width, &height );
2417 
2418 					if ( width > 0 && height > 0 )
2419 					{
2420 #define HANDLE_SIZED_TAG(ttag) \
2421 		else if( !strcmp(doc->tag, #ttag) )	result = handle_asxml_tag_##ttag( &state, doc, parm, imtmp, width, height )
2422 						if (0){}
2423 						HANDLE_SIZED_TAG(bevel);
2424 						HANDLE_SIZED_TAG(mirror);
2425 						HANDLE_SIZED_TAG(rotate);
2426 						HANDLE_SIZED_TAG(scale);
2427 						HANDLE_SIZED_TAG(slice);
2428 						HANDLE_SIZED_TAG(crop);
2429 						HANDLE_SIZED_TAG(tile);
2430 						HANDLE_SIZED_TAG(hsv);
2431 						HANDLE_SIZED_TAG(pad);
2432 						HANDLE_SIZED_TAG(pixelize);
2433 						HANDLE_SIZED_TAG(color2alpha);
2434 #undef HANDLE_SIZED_TAG
2435 					}
2436 				}
2437 
2438 				if( result != imtmp )
2439 					safe_asimage_destroy(imtmp);
2440 			}
2441 		}
2442 
2443 		if( refimg )
2444 			release_asimage( refimg );
2445 
2446 		if (rparm) *rparm = parm;
2447 		else xml_elem_delete(NULL, parm);
2448 	}
2449 	LOCAL_DEBUG_OUT("result = %p, id = \"%s\"", result, id?id:"(null)" );
2450 
2451 
2452 
2453 	/* No match so far... see if one of our children can do any better.*/
2454 	if (!result  && doc )
2455 	{
2456 		xml_elem_t* tparm = NULL;
2457 		for (ptr = doc->child ; ptr && !result ; ptr = ptr->next)
2458 		{
2459 			xml_elem_t* sparm = NULL;
2460 			result = build_image_from_xml(asv, imman, fontman, ptr, &sparm, flags, verbose, display_win);
2461 			if (result)
2462 			{
2463 				if (tparm) xml_elem_delete(NULL, tparm);
2464 				tparm = sparm;
2465 			}else
2466 				if (sparm) xml_elem_delete(NULL, sparm);
2467 
2468 		}
2469 		if (rparm)
2470 		{
2471 			if( *rparm ) xml_elem_delete(NULL, *rparm); *rparm = tparm;
2472 		}else
2473 			xml_elem_delete(NULL, tparm);
2474 	}
2475 
2476 	LOCAL_DEBUG_OUT("result = %p", result );
2477 	result = commit_xml_image_built( &state, id, result );
2478 	if( id )
2479 		free( id );
2480 	LOCAL_DEBUG_OUT("result = %p", result );
2481 	if( result )
2482 	{
2483 		LOCAL_DEBUG_OUT("result's size = %dx%d", result->width, result->height );
2484 	}
2485 	return result;
2486 }
2487 
2488 
2489 
2490 
2491