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( ¶ms, 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, ¶ms);
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