|
MagickCore
6.7.5
|
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % DDDD RRRR AAA W W % 00007 % D D R R A A W W % 00008 % D D RRRR AAAAA W W W % 00009 % D D R RN A A WW WW % 00010 % DDDD R R A A W W % 00011 % % 00012 % % 00013 % MagickCore Image Drawing Methods % 00014 % % 00015 % % 00016 % Software Design % 00017 % John Cristy % 00018 % July 1998 % 00019 % % 00020 % % 00021 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 00022 % dedicated to making software imaging solutions freely available. % 00023 % % 00024 % You may not use this file except in compliance with the License. You may % 00025 % obtain a copy of the License at % 00026 % % 00027 % http://www.imagemagick.org/script/license.php % 00028 % % 00029 % Unless required by applicable law or agreed to in writing, software % 00030 % distributed under the License is distributed on an "AS IS" BASIS, % 00031 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00032 % See the License for the specific language governing permissions and % 00033 % limitations under the License. % 00034 % % 00035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00036 % 00037 % Bill Radcliffe of Corbis (www.corbis.com) contributed the polygon 00038 % rendering code based on Paul Heckbert's "Concave Polygon Scan Conversion", 00039 % Graphics Gems, 1990. Leonard Rosenthal and David Harr of Appligent 00040 % (www.appligent.com) contributed the dash pattern, linecap stroking 00041 % algorithm, and minor rendering improvements. 00042 % 00043 */ 00044 00045 /* 00046 Include declarations. 00047 */ 00048 #include "MagickCore/studio.h" 00049 #include "MagickCore/annotate.h" 00050 #include "MagickCore/artifact.h" 00051 #include "MagickCore/blob.h" 00052 #include "MagickCore/cache.h" 00053 #include "MagickCore/cache-view.h" 00054 #include "MagickCore/color.h" 00055 #include "MagickCore/composite.h" 00056 #include "MagickCore/composite-private.h" 00057 #include "MagickCore/constitute.h" 00058 #include "MagickCore/draw.h" 00059 #include "MagickCore/draw-private.h" 00060 #include "MagickCore/enhance.h" 00061 #include "MagickCore/exception.h" 00062 #include "MagickCore/exception-private.h" 00063 #include "MagickCore/gem.h" 00064 #include "MagickCore/geometry.h" 00065 #include "MagickCore/image-private.h" 00066 #include "MagickCore/list.h" 00067 #include "MagickCore/log.h" 00068 #include "MagickCore/monitor.h" 00069 #include "MagickCore/monitor-private.h" 00070 #include "MagickCore/option.h" 00071 #include "MagickCore/paint.h" 00072 #include "MagickCore/pixel-accessor.h" 00073 #include "MagickCore/property.h" 00074 #include "MagickCore/resample.h" 00075 #include "MagickCore/resample-private.h" 00076 #include "MagickCore/string_.h" 00077 #include "MagickCore/string-private.h" 00078 #include "MagickCore/thread-private.h" 00079 #include "MagickCore/token.h" 00080 #include "MagickCore/transform.h" 00081 #include "MagickCore/utility.h" 00082 00083 /* 00084 Define declarations. 00085 */ 00086 #define BezierQuantum 200 00087 00088 /* 00089 Typedef declarations. 00090 */ 00091 typedef struct _EdgeInfo 00092 { 00093 SegmentInfo 00094 bounds; 00095 00096 MagickRealType 00097 scanline; 00098 00099 PointInfo 00100 *points; 00101 00102 size_t 00103 number_points; 00104 00105 ssize_t 00106 direction; 00107 00108 MagickBooleanType 00109 ghostline; 00110 00111 size_t 00112 highwater; 00113 } EdgeInfo; 00114 00115 typedef struct _ElementInfo 00116 { 00117 MagickRealType 00118 cx, 00119 cy, 00120 major, 00121 minor, 00122 angle; 00123 } ElementInfo; 00124 00125 typedef struct _PolygonInfo 00126 { 00127 EdgeInfo 00128 *edges; 00129 00130 size_t 00131 number_edges; 00132 } PolygonInfo; 00133 00134 typedef enum 00135 { 00136 MoveToCode, 00137 OpenCode, 00138 GhostlineCode, 00139 LineToCode, 00140 EndCode 00141 } PathInfoCode; 00142 00143 typedef struct _PathInfo 00144 { 00145 PointInfo 00146 point; 00147 00148 PathInfoCode 00149 code; 00150 } PathInfo; 00151 00152 /* 00153 Forward declarations. 00154 */ 00155 static MagickBooleanType 00156 DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *, 00157 ExceptionInfo *); 00158 00159 static PrimitiveInfo 00160 *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *); 00161 00162 static size_t 00163 TracePath(PrimitiveInfo *,const char *); 00164 00165 static void 00166 TraceArc(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo), 00167 TraceArcPath(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo, 00168 const MagickRealType,const MagickBooleanType,const MagickBooleanType), 00169 TraceBezier(PrimitiveInfo *,const size_t), 00170 TraceCircle(PrimitiveInfo *,const PointInfo,const PointInfo), 00171 TraceEllipse(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo), 00172 TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo), 00173 TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo), 00174 TraceRoundRectangle(PrimitiveInfo *,const PointInfo,const PointInfo, 00175 PointInfo), 00176 TraceSquareLinecap(PrimitiveInfo *,const size_t,const MagickRealType); 00177 00178 /* 00179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00180 % % 00181 % % 00182 % % 00183 % A c q u i r e D r a w I n f o % 00184 % % 00185 % % 00186 % % 00187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00188 % 00189 % AcquireDrawInfo() returns a DrawInfo structure properly initialized. 00190 % 00191 % The format of the AcquireDrawInfo method is: 00192 % 00193 % DrawInfo *AcquireDrawInfo(void) 00194 % 00195 */ 00196 MagickExport DrawInfo *AcquireDrawInfo(void) 00197 { 00198 DrawInfo 00199 *draw_info; 00200 00201 draw_info=(DrawInfo *) AcquireMagickMemory(sizeof(*draw_info)); 00202 if (draw_info == (DrawInfo *) NULL) 00203 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 00204 GetDrawInfo((ImageInfo *) NULL,draw_info); 00205 return(draw_info); 00206 } 00207 00208 /* 00209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00210 % % 00211 % % 00212 % % 00213 % C l o n e D r a w I n f o % 00214 % % 00215 % % 00216 % % 00217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00218 % 00219 % CloneDrawInfo() makes a copy of the given draw_info structure. If NULL 00220 % is specified, a new draw_info structure is created initialized to 00221 % default values, according to the given image_info. 00222 % 00223 % The format of the CloneDrawInfo method is: 00224 % 00225 % DrawInfo *CloneDrawInfo(const ImageInfo *image_info, 00226 % const DrawInfo *draw_info) 00227 % 00228 % A description of each parameter follows: 00229 % 00230 % o image_info: the image info. 00231 % 00232 % o draw_info: the draw info. 00233 % 00234 */ 00235 MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info, 00236 const DrawInfo *draw_info) 00237 { 00238 DrawInfo 00239 *clone_info; 00240 00241 ExceptionInfo 00242 *exception; 00243 00244 clone_info=(DrawInfo *) AcquireMagickMemory(sizeof(*clone_info)); 00245 if (clone_info == (DrawInfo *) NULL) 00246 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 00247 GetDrawInfo(image_info,clone_info); 00248 if (draw_info == (DrawInfo *) NULL) 00249 return(clone_info); 00250 exception=AcquireExceptionInfo(); 00251 (void) CloneString(&clone_info->primitive,draw_info->primitive); 00252 (void) CloneString(&clone_info->geometry,draw_info->geometry); 00253 clone_info->viewbox=draw_info->viewbox; 00254 clone_info->affine=draw_info->affine; 00255 clone_info->gravity=draw_info->gravity; 00256 clone_info->fill=draw_info->fill; 00257 clone_info->stroke=draw_info->stroke; 00258 clone_info->stroke_width=draw_info->stroke_width; 00259 if (draw_info->fill_pattern != (Image *) NULL) 00260 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue, 00261 exception); 00262 if (draw_info->stroke_pattern != (Image *) NULL) 00263 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0, 00264 MagickTrue,exception); 00265 clone_info->stroke_antialias=draw_info->stroke_antialias; 00266 clone_info->text_antialias=draw_info->text_antialias; 00267 clone_info->fill_rule=draw_info->fill_rule; 00268 clone_info->linecap=draw_info->linecap; 00269 clone_info->linejoin=draw_info->linejoin; 00270 clone_info->miterlimit=draw_info->miterlimit; 00271 clone_info->dash_offset=draw_info->dash_offset; 00272 clone_info->decorate=draw_info->decorate; 00273 clone_info->compose=draw_info->compose; 00274 (void) CloneString(&clone_info->text,draw_info->text); 00275 (void) CloneString(&clone_info->font,draw_info->font); 00276 (void) CloneString(&clone_info->metrics,draw_info->metrics); 00277 (void) CloneString(&clone_info->family,draw_info->family); 00278 clone_info->style=draw_info->style; 00279 clone_info->stretch=draw_info->stretch; 00280 clone_info->weight=draw_info->weight; 00281 (void) CloneString(&clone_info->encoding,draw_info->encoding); 00282 clone_info->pointsize=draw_info->pointsize; 00283 clone_info->kerning=draw_info->kerning; 00284 clone_info->interline_spacing=draw_info->interline_spacing; 00285 clone_info->interword_spacing=draw_info->interword_spacing; 00286 clone_info->direction=draw_info->direction; 00287 (void) CloneString(&clone_info->density,draw_info->density); 00288 clone_info->align=draw_info->align; 00289 clone_info->undercolor=draw_info->undercolor; 00290 clone_info->border_color=draw_info->border_color; 00291 (void) CloneString(&clone_info->server_name,draw_info->server_name); 00292 if (draw_info->dash_pattern != (double *) NULL) 00293 { 00294 register ssize_t 00295 x; 00296 00297 for (x=0; draw_info->dash_pattern[x] != 0.0; x++) ; 00298 clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) x+1UL, 00299 sizeof(*clone_info->dash_pattern)); 00300 if (clone_info->dash_pattern == (double *) NULL) 00301 ThrowFatalException(ResourceLimitFatalError, 00302 "UnableToAllocateDashPattern"); 00303 (void) CopyMagickMemory(clone_info->dash_pattern,draw_info->dash_pattern, 00304 (size_t) (x+1)*sizeof(*clone_info->dash_pattern)); 00305 } 00306 clone_info->gradient=draw_info->gradient; 00307 if (draw_info->gradient.stops != (StopInfo *) NULL) 00308 { 00309 size_t 00310 number_stops; 00311 00312 number_stops=clone_info->gradient.number_stops; 00313 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t) 00314 number_stops,sizeof(*clone_info->gradient.stops)); 00315 if (clone_info->gradient.stops == (StopInfo *) NULL) 00316 ThrowFatalException(ResourceLimitFatalError, 00317 "UnableToAllocateDashPattern"); 00318 (void) CopyMagickMemory(clone_info->gradient.stops, 00319 draw_info->gradient.stops,(size_t) number_stops* 00320 sizeof(*clone_info->gradient.stops)); 00321 } 00322 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask); 00323 clone_info->bounds=draw_info->bounds; 00324 clone_info->clip_units=draw_info->clip_units; 00325 clone_info->render=draw_info->render; 00326 clone_info->alpha=draw_info->alpha; 00327 clone_info->element_reference=draw_info->element_reference; 00328 clone_info->debug=IsEventLogging(); 00329 exception=DestroyExceptionInfo(exception); 00330 return(clone_info); 00331 } 00332 00333 /* 00334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00335 % % 00336 % % 00337 % % 00338 + C o n v e r t P a t h T o P o l y g o n % 00339 % % 00340 % % 00341 % % 00342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00343 % 00344 % ConvertPathToPolygon() converts a path to the more efficient sorted 00345 % rendering form. 00346 % 00347 % The format of the ConvertPathToPolygon method is: 00348 % 00349 % PolygonInfo *ConvertPathToPolygon(const DrawInfo *draw_info, 00350 % const PathInfo *path_info) 00351 % 00352 % A description of each parameter follows: 00353 % 00354 % o Method ConvertPathToPolygon returns the path in a more efficient sorted 00355 % rendering form of type PolygonInfo. 00356 % 00357 % o draw_info: Specifies a pointer to an DrawInfo structure. 00358 % 00359 % o path_info: Specifies a pointer to an PathInfo structure. 00360 % 00361 % 00362 */ 00363 00364 #if defined(__cplusplus) || defined(c_plusplus) 00365 extern "C" { 00366 #endif 00367 00368 static int CompareEdges(const void *x,const void *y) 00369 { 00370 register const EdgeInfo 00371 *p, 00372 *q; 00373 00374 /* 00375 Compare two edges. 00376 */ 00377 p=(const EdgeInfo *) x; 00378 q=(const EdgeInfo *) y; 00379 if ((p->points[0].y-MagickEpsilon) > q->points[0].y) 00380 return(1); 00381 if ((p->points[0].y+MagickEpsilon) < q->points[0].y) 00382 return(-1); 00383 if ((p->points[0].x-MagickEpsilon) > q->points[0].x) 00384 return(1); 00385 if ((p->points[0].x+MagickEpsilon) < q->points[0].x) 00386 return(-1); 00387 if (((p->points[1].x-p->points[0].x)*(q->points[1].y-q->points[0].y)- 00388 (p->points[1].y-p->points[0].y)*(q->points[1].x-q->points[0].x)) > 0.0) 00389 return(1); 00390 return(-1); 00391 } 00392 00393 #if defined(__cplusplus) || defined(c_plusplus) 00394 } 00395 #endif 00396 00397 static void LogPolygonInfo(const PolygonInfo *polygon_info) 00398 { 00399 register EdgeInfo 00400 *p; 00401 00402 register ssize_t 00403 i, 00404 j; 00405 00406 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge"); 00407 p=polygon_info->edges; 00408 for (i=0; i < (ssize_t) polygon_info->number_edges; i++) 00409 { 00410 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:", 00411 (double) i); 00412 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s", 00413 p->direction != MagickFalse ? "down" : "up"); 00414 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s", 00415 p->ghostline != MagickFalse ? "transparent" : "opaque"); 00416 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 00417 " bounds: %g %g - %g %g",p->bounds.x1,p->bounds.y1, 00418 p->bounds.x2,p->bounds.y2); 00419 for (j=0; j < (ssize_t) p->number_points; j++) 00420 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g %g", 00421 p->points[j].x,p->points[j].y); 00422 p++; 00423 } 00424 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge"); 00425 } 00426 00427 static void ReversePoints(PointInfo *points,const size_t number_points) 00428 { 00429 PointInfo 00430 point; 00431 00432 register ssize_t 00433 i; 00434 00435 for (i=0; i < (ssize_t) (number_points >> 1); i++) 00436 { 00437 point=points[i]; 00438 points[i]=points[number_points-(i+1)]; 00439 points[number_points-(i+1)]=point; 00440 } 00441 } 00442 00443 static PolygonInfo *ConvertPathToPolygon( 00444 const DrawInfo *magick_unused(draw_info),const PathInfo *path_info) 00445 { 00446 long 00447 direction, 00448 next_direction; 00449 00450 PointInfo 00451 point, 00452 *points; 00453 00454 PolygonInfo 00455 *polygon_info; 00456 00457 SegmentInfo 00458 bounds; 00459 00460 register ssize_t 00461 i, 00462 n; 00463 00464 MagickBooleanType 00465 ghostline; 00466 00467 size_t 00468 edge, 00469 number_edges, 00470 number_points; 00471 00472 /* 00473 Convert a path to the more efficient sorted rendering form. 00474 */ 00475 polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info)); 00476 if (polygon_info == (PolygonInfo *) NULL) 00477 return((PolygonInfo *) NULL); 00478 number_edges=16; 00479 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory((size_t) number_edges, 00480 sizeof(*polygon_info->edges)); 00481 if (polygon_info->edges == (EdgeInfo *) NULL) 00482 return((PolygonInfo *) NULL); 00483 direction=0; 00484 edge=0; 00485 ghostline=MagickFalse; 00486 n=0; 00487 number_points=0; 00488 points=(PointInfo *) NULL; 00489 (void) ResetMagickMemory(&point,0,sizeof(point)); 00490 (void) ResetMagickMemory(&bounds,0,sizeof(bounds)); 00491 for (i=0; path_info[i].code != EndCode; i++) 00492 { 00493 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) || 00494 (path_info[i].code == GhostlineCode)) 00495 { 00496 /* 00497 Move to. 00498 */ 00499 if ((points != (PointInfo *) NULL) && (n >= 2)) 00500 { 00501 if (edge == number_edges) 00502 { 00503 number_edges<<=1; 00504 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory( 00505 polygon_info->edges,(size_t) number_edges, 00506 sizeof(*polygon_info->edges)); 00507 if (polygon_info->edges == (EdgeInfo *) NULL) 00508 return((PolygonInfo *) NULL); 00509 } 00510 polygon_info->edges[edge].number_points=(size_t) n; 00511 polygon_info->edges[edge].scanline=(-1.0); 00512 polygon_info->edges[edge].highwater=0; 00513 polygon_info->edges[edge].ghostline=ghostline; 00514 polygon_info->edges[edge].direction=(ssize_t) (direction > 0); 00515 if (direction < 0) 00516 ReversePoints(points,(size_t) n); 00517 polygon_info->edges[edge].points=points; 00518 polygon_info->edges[edge].bounds=bounds; 00519 polygon_info->edges[edge].bounds.y1=points[0].y; 00520 polygon_info->edges[edge].bounds.y2=points[n-1].y; 00521 points=(PointInfo *) NULL; 00522 ghostline=MagickFalse; 00523 edge++; 00524 } 00525 if (points == (PointInfo *) NULL) 00526 { 00527 number_points=16; 00528 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points, 00529 sizeof(*points)); 00530 if (points == (PointInfo *) NULL) 00531 return((PolygonInfo *) NULL); 00532 } 00533 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse; 00534 point=path_info[i].point; 00535 points[0]=point; 00536 bounds.x1=point.x; 00537 bounds.x2=point.x; 00538 direction=0; 00539 n=1; 00540 continue; 00541 } 00542 /* 00543 Line to. 00544 */ 00545 next_direction=((path_info[i].point.y > point.y) || 00546 ((path_info[i].point.y == point.y) && 00547 (path_info[i].point.x > point.x))) ? 1 : -1; 00548 if ((direction != 0) && (direction != next_direction)) 00549 { 00550 /* 00551 New edge. 00552 */ 00553 point=points[n-1]; 00554 if (edge == number_edges) 00555 { 00556 number_edges<<=1; 00557 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory( 00558 polygon_info->edges,(size_t) number_edges, 00559 sizeof(*polygon_info->edges)); 00560 if (polygon_info->edges == (EdgeInfo *) NULL) 00561 return((PolygonInfo *) NULL); 00562 } 00563 polygon_info->edges[edge].number_points=(size_t) n; 00564 polygon_info->edges[edge].scanline=(-1.0); 00565 polygon_info->edges[edge].highwater=0; 00566 polygon_info->edges[edge].ghostline=ghostline; 00567 polygon_info->edges[edge].direction=(ssize_t) (direction > 0); 00568 if (direction < 0) 00569 ReversePoints(points,(size_t) n); 00570 polygon_info->edges[edge].points=points; 00571 polygon_info->edges[edge].bounds=bounds; 00572 polygon_info->edges[edge].bounds.y1=points[0].y; 00573 polygon_info->edges[edge].bounds.y2=points[n-1].y; 00574 number_points=16; 00575 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points, 00576 sizeof(*points)); 00577 if (points == (PointInfo *) NULL) 00578 return((PolygonInfo *) NULL); 00579 n=1; 00580 ghostline=MagickFalse; 00581 points[0]=point; 00582 bounds.x1=point.x; 00583 bounds.x2=point.x; 00584 edge++; 00585 } 00586 direction=next_direction; 00587 if (points == (PointInfo *) NULL) 00588 continue; 00589 if (n == (ssize_t) number_points) 00590 { 00591 number_points<<=1; 00592 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points, 00593 sizeof(*points)); 00594 if (points == (PointInfo *) NULL) 00595 return((PolygonInfo *) NULL); 00596 } 00597 point=path_info[i].point; 00598 points[n]=point; 00599 if (point.x < bounds.x1) 00600 bounds.x1=point.x; 00601 if (point.x > bounds.x2) 00602 bounds.x2=point.x; 00603 n++; 00604 } 00605 if (points != (PointInfo *) NULL) 00606 { 00607 if (n < 2) 00608 points=(PointInfo *) RelinquishMagickMemory(points); 00609 else 00610 { 00611 if (edge == number_edges) 00612 { 00613 number_edges<<=1; 00614 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory( 00615 polygon_info->edges,(size_t) number_edges, 00616 sizeof(*polygon_info->edges)); 00617 if (polygon_info->edges == (EdgeInfo *) NULL) 00618 return((PolygonInfo *) NULL); 00619 } 00620 polygon_info->edges[edge].number_points=(size_t) n; 00621 polygon_info->edges[edge].scanline=(-1.0); 00622 polygon_info->edges[edge].highwater=0; 00623 polygon_info->edges[edge].ghostline=ghostline; 00624 polygon_info->edges[edge].direction=(ssize_t) (direction > 0); 00625 if (direction < 0) 00626 ReversePoints(points,(size_t) n); 00627 polygon_info->edges[edge].points=points; 00628 polygon_info->edges[edge].bounds=bounds; 00629 polygon_info->edges[edge].bounds.y1=points[0].y; 00630 polygon_info->edges[edge].bounds.y2=points[n-1].y; 00631 ghostline=MagickFalse; 00632 edge++; 00633 } 00634 } 00635 polygon_info->number_edges=edge; 00636 qsort(polygon_info->edges,(size_t) polygon_info->number_edges, 00637 sizeof(*polygon_info->edges),CompareEdges); 00638 if (IsEventLogging() != MagickFalse) 00639 LogPolygonInfo(polygon_info); 00640 return(polygon_info); 00641 } 00642 00643 /* 00644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00645 % % 00646 % % 00647 % % 00648 + C o n v e r t P r i m i t i v e T o P a t h % 00649 % % 00650 % % 00651 % % 00652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00653 % 00654 % ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector 00655 % path structure. 00656 % 00657 % The format of the ConvertPrimitiveToPath method is: 00658 % 00659 % PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info, 00660 % const PrimitiveInfo *primitive_info) 00661 % 00662 % A description of each parameter follows: 00663 % 00664 % o Method ConvertPrimitiveToPath returns a vector path structure of type 00665 % PathInfo. 00666 % 00667 % o draw_info: a structure of type DrawInfo. 00668 % 00669 % o primitive_info: Specifies a pointer to an PrimitiveInfo structure. 00670 % 00671 % 00672 */ 00673 00674 static void LogPathInfo(const PathInfo *path_info) 00675 { 00676 register const PathInfo 00677 *p; 00678 00679 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path"); 00680 for (p=path_info; p->code != EndCode; p++) 00681 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 00682 " %g %g %s",p->point.x,p->point.y,p->code == GhostlineCode ? 00683 "moveto ghostline" : p->code == OpenCode ? "moveto open" : 00684 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" : 00685 "?"); 00686 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path"); 00687 } 00688 00689 static PathInfo *ConvertPrimitiveToPath( 00690 const DrawInfo *magick_unused(draw_info),const PrimitiveInfo *primitive_info) 00691 { 00692 PathInfo 00693 *path_info; 00694 00695 PathInfoCode 00696 code; 00697 00698 PointInfo 00699 p, 00700 q; 00701 00702 register ssize_t 00703 i, 00704 n; 00705 00706 ssize_t 00707 coordinates, 00708 start; 00709 00710 /* 00711 Converts a PrimitiveInfo structure into a vector path structure. 00712 */ 00713 switch (primitive_info->primitive) 00714 { 00715 case PointPrimitive: 00716 case ColorPrimitive: 00717 case MattePrimitive: 00718 case TextPrimitive: 00719 case ImagePrimitive: 00720 return((PathInfo *) NULL); 00721 default: 00722 break; 00723 } 00724 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ; 00725 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (2UL*i+3UL), 00726 sizeof(*path_info)); 00727 if (path_info == (PathInfo *) NULL) 00728 return((PathInfo *) NULL); 00729 coordinates=0; 00730 n=0; 00731 p.x=(-1.0); 00732 p.y=(-1.0); 00733 q.x=(-1.0); 00734 q.y=(-1.0); 00735 start=0; 00736 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) 00737 { 00738 code=LineToCode; 00739 if (coordinates <= 0) 00740 { 00741 coordinates=(ssize_t) primitive_info[i].coordinates; 00742 p=primitive_info[i].point; 00743 start=n; 00744 code=MoveToCode; 00745 } 00746 coordinates--; 00747 /* 00748 Eliminate duplicate points. 00749 */ 00750 if ((i == 0) || (fabs(q.x-primitive_info[i].point.x) > MagickEpsilon) || 00751 (fabs(q.y-primitive_info[i].point.y) > MagickEpsilon)) 00752 { 00753 path_info[n].code=code; 00754 path_info[n].point=primitive_info[i].point; 00755 q=primitive_info[i].point; 00756 n++; 00757 } 00758 if (coordinates > 0) 00759 continue; 00760 if ((fabs(p.x-primitive_info[i].point.x) <= MagickEpsilon) && 00761 (fabs(p.y-primitive_info[i].point.y) <= MagickEpsilon)) 00762 continue; 00763 /* 00764 Mark the p point as open if it does not match the q. 00765 */ 00766 path_info[start].code=OpenCode; 00767 path_info[n].code=GhostlineCode; 00768 path_info[n].point=primitive_info[i].point; 00769 n++; 00770 path_info[n].code=LineToCode; 00771 path_info[n].point=p; 00772 n++; 00773 } 00774 path_info[n].code=EndCode; 00775 path_info[n].point.x=0.0; 00776 path_info[n].point.y=0.0; 00777 if (IsEventLogging() != MagickFalse) 00778 LogPathInfo(path_info); 00779 return(path_info); 00780 } 00781 00782 /* 00783 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00784 % % 00785 % % 00786 % % 00787 % D e s t r o y D r a w I n f o % 00788 % % 00789 % % 00790 % % 00791 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00792 % 00793 % DestroyDrawInfo() deallocates memory associated with an DrawInfo 00794 % structure. 00795 % 00796 % The format of the DestroyDrawInfo method is: 00797 % 00798 % DrawInfo *DestroyDrawInfo(DrawInfo *draw_info) 00799 % 00800 % A description of each parameter follows: 00801 % 00802 % o draw_info: the draw info. 00803 % 00804 */ 00805 MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info) 00806 { 00807 if (draw_info->debug != MagickFalse) 00808 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 00809 assert(draw_info != (DrawInfo *) NULL); 00810 assert(draw_info->signature == MagickSignature); 00811 if (draw_info->primitive != (char *) NULL) 00812 draw_info->primitive=DestroyString(draw_info->primitive); 00813 if (draw_info->text != (char *) NULL) 00814 draw_info->text=DestroyString(draw_info->text); 00815 if (draw_info->geometry != (char *) NULL) 00816 draw_info->geometry=DestroyString(draw_info->geometry); 00817 if (draw_info->fill_pattern != (Image *) NULL) 00818 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern); 00819 if (draw_info->stroke_pattern != (Image *) NULL) 00820 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern); 00821 if (draw_info->font != (char *) NULL) 00822 draw_info->font=DestroyString(draw_info->font); 00823 if (draw_info->metrics != (char *) NULL) 00824 draw_info->metrics=DestroyString(draw_info->metrics); 00825 if (draw_info->family != (char *) NULL) 00826 draw_info->family=DestroyString(draw_info->family); 00827 if (draw_info->encoding != (char *) NULL) 00828 draw_info->encoding=DestroyString(draw_info->encoding); 00829 if (draw_info->density != (char *) NULL) 00830 draw_info->density=DestroyString(draw_info->density); 00831 if (draw_info->server_name != (char *) NULL) 00832 draw_info->server_name=(char *) 00833 RelinquishMagickMemory(draw_info->server_name); 00834 if (draw_info->dash_pattern != (double *) NULL) 00835 draw_info->dash_pattern=(double *) RelinquishMagickMemory( 00836 draw_info->dash_pattern); 00837 if (draw_info->gradient.stops != (StopInfo *) NULL) 00838 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory( 00839 draw_info->gradient.stops); 00840 if (draw_info->clip_mask != (char *) NULL) 00841 draw_info->clip_mask=DestroyString(draw_info->clip_mask); 00842 draw_info->signature=(~MagickSignature); 00843 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info); 00844 return(draw_info); 00845 } 00846 00847 /* 00848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00849 % % 00850 % % 00851 % % 00852 + D e s t r o y E d g e % 00853 % % 00854 % % 00855 % % 00856 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00857 % 00858 % DestroyEdge() destroys the specified polygon edge. 00859 % 00860 % The format of the DestroyEdge method is: 00861 % 00862 % ssize_t DestroyEdge(PolygonInfo *polygon_info,const int edge) 00863 % 00864 % A description of each parameter follows: 00865 % 00866 % o polygon_info: Specifies a pointer to an PolygonInfo structure. 00867 % 00868 % o edge: the polygon edge number to destroy. 00869 % 00870 */ 00871 static size_t DestroyEdge(PolygonInfo *polygon_info, 00872 const size_t edge) 00873 { 00874 assert(edge < polygon_info->number_edges); 00875 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory( 00876 polygon_info->edges[edge].points); 00877 polygon_info->number_edges--; 00878 if (edge < polygon_info->number_edges) 00879 (void) CopyMagickMemory(polygon_info->edges+edge,polygon_info->edges+edge+1, 00880 (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges)); 00881 return(polygon_info->number_edges); 00882 } 00883 00884 /* 00885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00886 % % 00887 % % 00888 % % 00889 + D e s t r o y P o l y g o n I n f o % 00890 % % 00891 % % 00892 % % 00893 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00894 % 00895 % DestroyPolygonInfo() destroys the PolygonInfo data structure. 00896 % 00897 % The format of the DestroyPolygonInfo method is: 00898 % 00899 % PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info) 00900 % 00901 % A description of each parameter follows: 00902 % 00903 % o polygon_info: Specifies a pointer to an PolygonInfo structure. 00904 % 00905 */ 00906 static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info) 00907 { 00908 register ssize_t 00909 i; 00910 00911 for (i=0; i < (ssize_t) polygon_info->number_edges; i++) 00912 polygon_info->edges[i].points=(PointInfo *) 00913 RelinquishMagickMemory(polygon_info->edges[i].points); 00914 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges); 00915 return((PolygonInfo *) RelinquishMagickMemory(polygon_info)); 00916 } 00917 00918 /* 00919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00920 % % 00921 % % 00922 % % 00923 % D r a w A f f i n e I m a g e % 00924 % % 00925 % % 00926 % % 00927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00928 % 00929 % DrawAffineImage() composites the source over the destination image as 00930 % dictated by the affine transform. 00931 % 00932 % The format of the DrawAffineImage method is: 00933 % 00934 % MagickBooleanType DrawAffineImage(Image *image,const Image *source, 00935 % const AffineMatrix *affine,ExceptionInfo *exception) 00936 % 00937 % A description of each parameter follows: 00938 % 00939 % o image: the image. 00940 % 00941 % o source: the source image. 00942 % 00943 % o affine: the affine transform. 00944 % 00945 % o exception: return any errors or warnings in this structure. 00946 % 00947 */ 00948 00949 static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine, 00950 const double y,const SegmentInfo *edge) 00951 { 00952 double 00953 intercept, 00954 z; 00955 00956 register double 00957 x; 00958 00959 SegmentInfo 00960 inverse_edge; 00961 00962 /* 00963 Determine left and right edges. 00964 */ 00965 inverse_edge.x1=edge->x1; 00966 inverse_edge.y1=edge->y1; 00967 inverse_edge.x2=edge->x2; 00968 inverse_edge.y2=edge->y2; 00969 z=affine->ry*y+affine->tx; 00970 if (affine->sx > MagickEpsilon) 00971 { 00972 intercept=(-z/affine->sx); 00973 x=intercept; 00974 if (x > inverse_edge.x1) 00975 inverse_edge.x1=x; 00976 intercept=(-z+(double) image->columns)/affine->sx; 00977 x=intercept; 00978 if (x < inverse_edge.x2) 00979 inverse_edge.x2=x; 00980 } 00981 else 00982 if (affine->sx < -MagickEpsilon) 00983 { 00984 intercept=(-z+(double) image->columns)/affine->sx; 00985 x=intercept; 00986 if (x > inverse_edge.x1) 00987 inverse_edge.x1=x; 00988 intercept=(-z/affine->sx); 00989 x=intercept; 00990 if (x < inverse_edge.x2) 00991 inverse_edge.x2=x; 00992 } 00993 else 00994 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns)) 00995 { 00996 inverse_edge.x2=edge->x1; 00997 return(inverse_edge); 00998 } 00999 /* 01000 Determine top and bottom edges. 01001 */ 01002 z=affine->sy*y+affine->ty; 01003 if (affine->rx > MagickEpsilon) 01004 { 01005 intercept=(-z/affine->rx); 01006 x=intercept; 01007 if (x > inverse_edge.x1) 01008 inverse_edge.x1=x; 01009 intercept=(-z+(double) image->rows)/affine->rx; 01010 x=intercept; 01011 if (x < inverse_edge.x2) 01012 inverse_edge.x2=x; 01013 } 01014 else 01015 if (affine->rx < -MagickEpsilon) 01016 { 01017 intercept=(-z+(double) image->rows)/affine->rx; 01018 x=intercept; 01019 if (x > inverse_edge.x1) 01020 inverse_edge.x1=x; 01021 intercept=(-z/affine->rx); 01022 x=intercept; 01023 if (x < inverse_edge.x2) 01024 inverse_edge.x2=x; 01025 } 01026 else 01027 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows)) 01028 { 01029 inverse_edge.x2=edge->x2; 01030 return(inverse_edge); 01031 } 01032 return(inverse_edge); 01033 } 01034 01035 static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine) 01036 { 01037 AffineMatrix 01038 inverse_affine; 01039 01040 double 01041 determinant; 01042 01043 determinant=1.0/(affine->sx*affine->sy-affine->rx*affine->ry); 01044 inverse_affine.sx=determinant*affine->sy; 01045 inverse_affine.rx=determinant*(-affine->rx); 01046 inverse_affine.ry=determinant*(-affine->ry); 01047 inverse_affine.sy=determinant*affine->sx; 01048 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty* 01049 inverse_affine.ry; 01050 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty* 01051 inverse_affine.sy; 01052 return(inverse_affine); 01053 } 01054 01055 static inline ssize_t MagickAbsoluteValue(const ssize_t x) 01056 { 01057 if (x < 0) 01058 return(-x); 01059 return(x); 01060 } 01061 01062 static inline double MagickMax(const double x,const double y) 01063 { 01064 if (x > y) 01065 return(x); 01066 return(y); 01067 } 01068 01069 static inline double MagickMin(const double x,const double y) 01070 { 01071 if (x < y) 01072 return(x); 01073 return(y); 01074 } 01075 01076 MagickExport MagickBooleanType DrawAffineImage(Image *image, 01077 const Image *source,const AffineMatrix *affine,ExceptionInfo *exception) 01078 { 01079 AffineMatrix 01080 inverse_affine; 01081 01082 CacheView 01083 *image_view, 01084 *source_view; 01085 01086 MagickBooleanType 01087 status; 01088 01089 PixelInfo 01090 zero; 01091 01092 PointInfo 01093 extent[4], 01094 min, 01095 max, 01096 point; 01097 01098 register ssize_t 01099 i; 01100 01101 SegmentInfo 01102 edge; 01103 01104 ssize_t 01105 start, 01106 stop, 01107 y; 01108 01109 /* 01110 Determine bounding box. 01111 */ 01112 assert(image != (Image *) NULL); 01113 assert(image->signature == MagickSignature); 01114 if (image->debug != MagickFalse) 01115 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01116 assert(source != (const Image *) NULL); 01117 assert(source->signature == MagickSignature); 01118 assert(affine != (AffineMatrix *) NULL); 01119 extent[0].x=0.0; 01120 extent[0].y=0.0; 01121 extent[1].x=(double) source->columns-1.0; 01122 extent[1].y=0.0; 01123 extent[2].x=(double) source->columns-1.0; 01124 extent[2].y=(double) source->rows-1.0; 01125 extent[3].x=0.0; 01126 extent[3].y=(double) source->rows-1.0; 01127 for (i=0; i < 4; i++) 01128 { 01129 point=extent[i]; 01130 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx; 01131 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty; 01132 } 01133 min=extent[0]; 01134 max=extent[0]; 01135 for (i=1; i < 4; i++) 01136 { 01137 if (min.x > extent[i].x) 01138 min.x=extent[i].x; 01139 if (min.y > extent[i].y) 01140 min.y=extent[i].y; 01141 if (max.x < extent[i].x) 01142 max.x=extent[i].x; 01143 if (max.y < extent[i].y) 01144 max.y=extent[i].y; 01145 } 01146 /* 01147 Affine transform image. 01148 */ 01149 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 01150 return(MagickFalse); 01151 status=MagickTrue; 01152 edge.x1=MagickMax(min.x,0.0); 01153 edge.y1=MagickMax(min.y,0.0); 01154 edge.x2=MagickMin(max.x,(double) image->columns-1.0); 01155 edge.y2=MagickMin(max.y,(double) image->rows-1.0); 01156 inverse_affine=InverseAffineMatrix(affine); 01157 GetPixelInfo(image,&zero); 01158 start=(ssize_t) ceil(edge.y1-0.5); 01159 stop=(ssize_t) ceil(edge.y2-0.5); 01160 image_view=AcquireCacheView(image); 01161 source_view=AcquireCacheView(source); 01162 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01163 #pragma omp parallel for schedule(static) shared(status) 01164 #endif 01165 for (y=start; y <= stop; y++) 01166 { 01167 PixelInfo 01168 composite, 01169 pixel; 01170 01171 PointInfo 01172 point; 01173 01174 register ssize_t 01175 x; 01176 01177 register Quantum 01178 *restrict q; 01179 01180 SegmentInfo 01181 inverse_edge; 01182 01183 ssize_t 01184 x_offset; 01185 01186 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge); 01187 if (inverse_edge.x2 < inverse_edge.x1) 01188 continue; 01189 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) ceil(inverse_edge.x1- 01190 0.5),y,(size_t) ((ssize_t) floor(inverse_edge.x2+0.5)-(ssize_t) floor( 01191 inverse_edge.x1+0.5)+1),1,exception); 01192 if (q == (Quantum *) NULL) 01193 continue; 01194 pixel=zero; 01195 composite=zero; 01196 x_offset=0; 01197 for (x=(ssize_t) ceil(inverse_edge.x1-0.5); x <= (ssize_t) floor(inverse_edge.x2+0.5); x++) 01198 { 01199 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+ 01200 inverse_affine.tx; 01201 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+ 01202 inverse_affine.ty; 01203 (void) InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel, 01204 point.x,point.y,&pixel,exception); 01205 GetPixelInfoPixel(image,q,&composite); 01206 CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha, 01207 &composite); 01208 SetPixelInfoPixel(image,&composite,q); 01209 x_offset++; 01210 q+=GetPixelChannels(image); 01211 } 01212 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 01213 status=MagickFalse; 01214 } 01215 source_view=DestroyCacheView(source_view); 01216 image_view=DestroyCacheView(image_view); 01217 return(status); 01218 } 01219 01220 /* 01221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01222 % % 01223 % % 01224 % % 01225 + D r a w B o u n d i n g R e c t a n g l e s % 01226 % % 01227 % % 01228 % % 01229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01230 % 01231 % DrawBoundingRectangles() draws the bounding rectangles on the image. This 01232 % is only useful for developers debugging the rendering algorithm. 01233 % 01234 % The format of the DrawBoundingRectangles method is: 01235 % 01236 % void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info, 01237 % PolygonInfo *polygon_info,ExceptionInfo *exception) 01238 % 01239 % A description of each parameter follows: 01240 % 01241 % o image: the image. 01242 % 01243 % o draw_info: the draw info. 01244 % 01245 % o polygon_info: Specifies a pointer to a PolygonInfo structure. 01246 % 01247 % o exception: return any errors or warnings in this structure. 01248 % 01249 */ 01250 static void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info, 01251 const PolygonInfo *polygon_info,ExceptionInfo *exception) 01252 { 01253 DrawInfo 01254 *clone_info; 01255 01256 MagickRealType 01257 mid; 01258 01259 PointInfo 01260 end, 01261 resolution, 01262 start; 01263 01264 PrimitiveInfo 01265 primitive_info[6]; 01266 01267 register ssize_t 01268 i; 01269 01270 SegmentInfo 01271 bounds; 01272 01273 ssize_t 01274 coordinates; 01275 01276 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 01277 (void) QueryColorCompliance("#0000",AllCompliance,&clone_info->fill, 01278 exception); 01279 resolution.x=DefaultResolution; 01280 resolution.y=DefaultResolution; 01281 if (clone_info->density != (char *) NULL) 01282 { 01283 GeometryInfo 01284 geometry_info; 01285 01286 MagickStatusType 01287 flags; 01288 01289 flags=ParseGeometry(clone_info->density,&geometry_info); 01290 resolution.x=geometry_info.rho; 01291 resolution.y=geometry_info.sigma; 01292 if ((flags & SigmaValue) == MagickFalse) 01293 resolution.y=resolution.x; 01294 } 01295 mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)* 01296 clone_info->stroke_width/2.0; 01297 bounds.x1=0.0; 01298 bounds.y1=0.0; 01299 bounds.x2=0.0; 01300 bounds.y2=0.0; 01301 if (polygon_info != (PolygonInfo *) NULL) 01302 { 01303 bounds=polygon_info->edges[0].bounds; 01304 for (i=1; i < (ssize_t) polygon_info->number_edges; i++) 01305 { 01306 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1) 01307 bounds.x1=polygon_info->edges[i].bounds.x1; 01308 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1) 01309 bounds.y1=polygon_info->edges[i].bounds.y1; 01310 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2) 01311 bounds.x2=polygon_info->edges[i].bounds.x2; 01312 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2) 01313 bounds.y2=polygon_info->edges[i].bounds.y2; 01314 } 01315 bounds.x1-=mid; 01316 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double) 01317 image->columns ? (double) image->columns-1 : bounds.x1; 01318 bounds.y1-=mid; 01319 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double) 01320 image->rows ? (double) image->rows-1 : bounds.y1; 01321 bounds.x2+=mid; 01322 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double) 01323 image->columns ? (double) image->columns-1 : bounds.x2; 01324 bounds.y2+=mid; 01325 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double) 01326 image->rows ? (double) image->rows-1 : bounds.y2; 01327 for (i=0; i < (ssize_t) polygon_info->number_edges; i++) 01328 { 01329 if (polygon_info->edges[i].direction != 0) 01330 (void) QueryColorCompliance("red",AllCompliance,&clone_info->stroke, 01331 exception); 01332 else 01333 (void) QueryColorCompliance("green",AllCompliance,&clone_info->stroke, 01334 exception); 01335 start.x=(double) (polygon_info->edges[i].bounds.x1-mid); 01336 start.y=(double) (polygon_info->edges[i].bounds.y1-mid); 01337 end.x=(double) (polygon_info->edges[i].bounds.x2+mid); 01338 end.y=(double) (polygon_info->edges[i].bounds.y2+mid); 01339 primitive_info[0].primitive=RectanglePrimitive; 01340 TraceRectangle(primitive_info,start,end); 01341 primitive_info[0].method=ReplaceMethod; 01342 coordinates=(ssize_t) primitive_info[0].coordinates; 01343 primitive_info[coordinates].primitive=UndefinedPrimitive; 01344 (void) DrawPrimitive(image,clone_info,primitive_info,exception); 01345 } 01346 } 01347 (void) QueryColorCompliance("blue",AllCompliance,&clone_info->stroke, 01348 exception); 01349 start.x=(double) (bounds.x1-mid); 01350 start.y=(double) (bounds.y1-mid); 01351 end.x=(double) (bounds.x2+mid); 01352 end.y=(double) (bounds.y2+mid); 01353 primitive_info[0].primitive=RectanglePrimitive; 01354 TraceRectangle(primitive_info,start,end); 01355 primitive_info[0].method=ReplaceMethod; 01356 coordinates=(ssize_t) primitive_info[0].coordinates; 01357 primitive_info[coordinates].primitive=UndefinedPrimitive; 01358 (void) DrawPrimitive(image,clone_info,primitive_info,exception); 01359 clone_info=DestroyDrawInfo(clone_info); 01360 } 01361 01362 /* 01363 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01364 % % 01365 % % 01366 % % 01367 % D r a w C l i p P a t h % 01368 % % 01369 % % 01370 % % 01371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01372 % 01373 % DrawClipPath() draws the clip path on the image mask. 01374 % 01375 % The format of the DrawClipPath method is: 01376 % 01377 % MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info, 01378 % const char *name,ExceptionInfo *exception) 01379 % 01380 % A description of each parameter follows: 01381 % 01382 % o image: the image. 01383 % 01384 % o draw_info: the draw info. 01385 % 01386 % o name: the name of the clip path. 01387 % 01388 % o exception: return any errors or warnings in this structure. 01389 % 01390 */ 01391 MagickExport MagickBooleanType DrawClipPath(Image *image, 01392 const DrawInfo *draw_info,const char *name,ExceptionInfo *exception) 01393 { 01394 char 01395 filename[MaxTextExtent]; 01396 01397 Image 01398 *clip_mask; 01399 01400 const char 01401 *value; 01402 01403 DrawInfo 01404 *clone_info; 01405 01406 MagickStatusType 01407 status; 01408 01409 assert(image != (Image *) NULL); 01410 assert(image->signature == MagickSignature); 01411 if (image->debug != MagickFalse) 01412 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01413 assert(draw_info != (const DrawInfo *) NULL); 01414 (void) FormatLocaleString(filename,MaxTextExtent,"%s",name); 01415 value=GetImageArtifact(image,filename); 01416 if (value == (const char *) NULL) 01417 return(MagickFalse); 01418 clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,exception); 01419 if (clip_mask == (Image *) NULL) 01420 return(MagickFalse); 01421 (void) QueryColorCompliance("#0000",AllCompliance, 01422 &clip_mask->background_color,exception); 01423 clip_mask->background_color.alpha=(Quantum) TransparentAlpha; 01424 (void) SetImageBackgroundColor(clip_mask,exception); 01425 if (image->debug != MagickFalse) 01426 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s", 01427 draw_info->clip_mask); 01428 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 01429 (void) CloneString(&clone_info->primitive,value); 01430 (void) QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill, 01431 exception); 01432 clone_info->clip_mask=(char *) NULL; 01433 status=NegateImage(clip_mask,MagickFalse,exception); 01434 (void) SetImageMask(image,clip_mask,exception); 01435 clip_mask=DestroyImage(clip_mask); 01436 clone_info=DestroyDrawInfo(clone_info); 01437 status=DrawImage(image,clone_info,exception); 01438 if (image->debug != MagickFalse) 01439 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path"); 01440 return(status != 0 ? MagickTrue : MagickFalse); 01441 } 01442 01443 /* 01444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01445 % % 01446 % % 01447 % % 01448 + D r a w D a s h P o l y g o n % 01449 % % 01450 % % 01451 % % 01452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01453 % 01454 % DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the 01455 % image while respecting the dash offset and dash pattern attributes. 01456 % 01457 % The format of the DrawDashPolygon method is: 01458 % 01459 % MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info, 01460 % const PrimitiveInfo *primitive_info,Image *image, 01461 % ExceptionInfo *exception) 01462 % 01463 % A description of each parameter follows: 01464 % 01465 % o draw_info: the draw info. 01466 % 01467 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure. 01468 % 01469 % o image: the image. 01470 % 01471 % o exception: return any errors or warnings in this structure. 01472 % 01473 */ 01474 static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info, 01475 const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception) 01476 { 01477 DrawInfo 01478 *clone_info; 01479 01480 MagickRealType 01481 length, 01482 maximum_length, 01483 offset, 01484 scale, 01485 total_length; 01486 01487 MagickStatusType 01488 status; 01489 01490 PrimitiveInfo 01491 *dash_polygon; 01492 01493 register ssize_t 01494 i; 01495 01496 register MagickRealType 01497 dx, 01498 dy; 01499 01500 size_t 01501 number_vertices; 01502 01503 ssize_t 01504 j, 01505 n; 01506 01507 assert(draw_info != (const DrawInfo *) NULL); 01508 if (image->debug != MagickFalse) 01509 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash"); 01510 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 01511 clone_info->miterlimit=0; 01512 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ; 01513 number_vertices=(size_t) i; 01514 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t) 01515 (2UL*number_vertices+1UL),sizeof(*dash_polygon)); 01516 if (dash_polygon == (PrimitiveInfo *) NULL) 01517 return(MagickFalse); 01518 dash_polygon[0]=primitive_info[0]; 01519 scale=ExpandAffine(&draw_info->affine); 01520 length=scale*(draw_info->dash_pattern[0]-0.5); 01521 offset=draw_info->dash_offset != 0.0 ? scale*draw_info->dash_offset : 0.0; 01522 j=1; 01523 for (n=0; offset > 0.0; j=0) 01524 { 01525 if (draw_info->dash_pattern[n] <= 0.0) 01526 break; 01527 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5)); 01528 if (offset > length) 01529 { 01530 offset-=length; 01531 n++; 01532 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5)); 01533 continue; 01534 } 01535 if (offset < length) 01536 { 01537 length-=offset; 01538 offset=0.0; 01539 break; 01540 } 01541 offset=0.0; 01542 n++; 01543 } 01544 status=MagickTrue; 01545 maximum_length=0.0; 01546 total_length=0.0; 01547 for (i=1; i < (ssize_t) number_vertices; i++) 01548 { 01549 dx=primitive_info[i].point.x-primitive_info[i-1].point.x; 01550 dy=primitive_info[i].point.y-primitive_info[i-1].point.y; 01551 maximum_length=hypot((double) dx,dy); 01552 if (length == 0.0) 01553 { 01554 n++; 01555 if (draw_info->dash_pattern[n] == 0.0) 01556 n=0; 01557 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5)); 01558 } 01559 for (total_length=0.0; (total_length+length) < maximum_length; ) 01560 { 01561 total_length+=length; 01562 if ((n & 0x01) != 0) 01563 { 01564 dash_polygon[0]=primitive_info[0]; 01565 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx* 01566 total_length/maximum_length); 01567 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy* 01568 total_length/maximum_length); 01569 j=1; 01570 } 01571 else 01572 { 01573 if ((j+1) > (ssize_t) (2*number_vertices)) 01574 break; 01575 dash_polygon[j]=primitive_info[i-1]; 01576 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx* 01577 total_length/maximum_length); 01578 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy* 01579 total_length/maximum_length); 01580 dash_polygon[j].coordinates=1; 01581 j++; 01582 dash_polygon[0].coordinates=(size_t) j; 01583 dash_polygon[j].primitive=UndefinedPrimitive; 01584 status|=DrawStrokePolygon(image,clone_info,dash_polygon,exception); 01585 } 01586 n++; 01587 if (draw_info->dash_pattern[n] == 0.0) 01588 n=0; 01589 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5)); 01590 } 01591 length-=(maximum_length-total_length); 01592 if ((n & 0x01) != 0) 01593 continue; 01594 dash_polygon[j]=primitive_info[i]; 01595 dash_polygon[j].coordinates=1; 01596 j++; 01597 } 01598 if ((total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1)) 01599 { 01600 dash_polygon[j]=primitive_info[i-1]; 01601 dash_polygon[j].point.x+=MagickEpsilon; 01602 dash_polygon[j].point.y+=MagickEpsilon; 01603 dash_polygon[j].coordinates=1; 01604 j++; 01605 dash_polygon[0].coordinates=(size_t) j; 01606 dash_polygon[j].primitive=UndefinedPrimitive; 01607 status|=DrawStrokePolygon(image,clone_info,dash_polygon,exception); 01608 } 01609 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon); 01610 clone_info=DestroyDrawInfo(clone_info); 01611 if (image->debug != MagickFalse) 01612 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash"); 01613 return(status != 0 ? MagickTrue : MagickFalse); 01614 } 01615 01616 /* 01617 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01618 % % 01619 % % 01620 % % 01621 % D r a w I m a g e % 01622 % % 01623 % % 01624 % % 01625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01626 % 01627 % DrawImage() draws a graphic primitive on your image. The primitive 01628 % may be represented as a string or filename. Precede the filename with an 01629 % "at" sign (@) and the contents of the file are drawn on the image. You 01630 % can affect how text is drawn by setting one or more members of the draw 01631 % info structure. 01632 % 01633 % The format of the DrawImage method is: 01634 % 01635 % MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info, 01636 % ExceptionInfo *exception) 01637 % 01638 % A description of each parameter follows: 01639 % 01640 % o image: the image. 01641 % 01642 % o draw_info: the draw info. 01643 % 01644 % o exception: return any errors or warnings in this structure. 01645 % 01646 */ 01647 01648 static inline MagickBooleanType IsPoint(const char *point) 01649 { 01650 char 01651 *p; 01652 01653 double 01654 value; 01655 01656 value=StringToDouble(point,&p); 01657 return((value == 0.0) && (p == point) ? MagickFalse : MagickTrue); 01658 } 01659 01660 static inline void TracePoint(PrimitiveInfo *primitive_info, 01661 const PointInfo point) 01662 { 01663 primitive_info->coordinates=1; 01664 primitive_info->point=point; 01665 } 01666 01667 MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info, 01668 ExceptionInfo *exception) 01669 { 01670 #define RenderImageTag "Render/Image" 01671 01672 AffineMatrix 01673 affine, 01674 current; 01675 01676 char 01677 key[2*MaxTextExtent], 01678 keyword[MaxTextExtent], 01679 geometry[MaxTextExtent], 01680 name[MaxTextExtent], 01681 pattern[MaxTextExtent], 01682 *primitive, 01683 *token; 01684 01685 const char 01686 *q; 01687 01688 DrawInfo 01689 **graphic_context; 01690 01691 MagickBooleanType 01692 proceed, 01693 status; 01694 01695 MagickRealType 01696 angle, 01697 factor, 01698 primitive_extent; 01699 01700 PointInfo 01701 point; 01702 01703 PixelInfo 01704 start_color; 01705 01706 PrimitiveInfo 01707 *primitive_info; 01708 01709 PrimitiveType 01710 primitive_type; 01711 01712 register const char 01713 *p; 01714 01715 register ssize_t 01716 i, 01717 x; 01718 01719 SegmentInfo 01720 bounds; 01721 01722 size_t 01723 length, 01724 number_points; 01725 01726 ssize_t 01727 j, 01728 k, 01729 n; 01730 01731 /* 01732 Ensure the annotation info is valid. 01733 */ 01734 assert(image != (Image *) NULL); 01735 assert(image->signature == MagickSignature); 01736 if (image->debug != MagickFalse) 01737 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01738 assert(draw_info != (DrawInfo *) NULL); 01739 assert(draw_info->signature == MagickSignature); 01740 if (image->debug != MagickFalse) 01741 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 01742 if ((draw_info->primitive == (char *) NULL) || 01743 (*draw_info->primitive == '\0')) 01744 return(MagickFalse); 01745 if (image->debug != MagickFalse) 01746 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image"); 01747 if (*draw_info->primitive != '@') 01748 primitive=AcquireString(draw_info->primitive); 01749 else 01750 primitive=FileToString(draw_info->primitive+1,~0,exception); 01751 if (primitive == (char *) NULL) 01752 return(MagickFalse); 01753 primitive_extent=(MagickRealType) strlen(primitive); 01754 (void) SetImageArtifact(image,"MVG",primitive); 01755 n=0; 01756 /* 01757 Allocate primitive info memory. 01758 */ 01759 graphic_context=(DrawInfo **) AcquireMagickMemory( 01760 sizeof(*graphic_context)); 01761 if (graphic_context == (DrawInfo **) NULL) 01762 { 01763 primitive=DestroyString(primitive); 01764 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 01765 image->filename); 01766 } 01767 number_points=2047; 01768 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points, 01769 sizeof(*primitive_info)); 01770 if (primitive_info == (PrimitiveInfo *) NULL) 01771 { 01772 primitive=DestroyString(primitive); 01773 for ( ; n >= 0; n--) 01774 graphic_context[n]=DestroyDrawInfo(graphic_context[n]); 01775 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context); 01776 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 01777 image->filename); 01778 } 01779 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info); 01780 graphic_context[n]->viewbox=image->page; 01781 if ((image->page.width == 0) || (image->page.height == 0)) 01782 { 01783 graphic_context[n]->viewbox.width=image->columns; 01784 graphic_context[n]->viewbox.height=image->rows; 01785 } 01786 token=AcquireString(primitive); 01787 (void) QueryColorCompliance("#000000",AllCompliance,&start_color, 01788 exception); 01789 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 01790 return(MagickFalse); 01791 status=MagickTrue; 01792 for (q=primitive; *q != '\0'; ) 01793 { 01794 /* 01795 Interpret graphic primitive. 01796 */ 01797 GetMagickToken(q,&q,keyword); 01798 if (*keyword == '\0') 01799 break; 01800 if (*keyword == '#') 01801 { 01802 /* 01803 Comment. 01804 */ 01805 while ((*q != '\n') && (*q != '\0')) 01806 q++; 01807 continue; 01808 } 01809 p=q-strlen(keyword)-1; 01810 primitive_type=UndefinedPrimitive; 01811 current=graphic_context[n]->affine; 01812 GetAffineMatrix(&affine); 01813 switch (*keyword) 01814 { 01815 case ';': 01816 break; 01817 case 'a': 01818 case 'A': 01819 { 01820 if (LocaleCompare("affine",keyword) == 0) 01821 { 01822 GetMagickToken(q,&q,token); 01823 affine.sx=StringToDouble(token,(char **) NULL); 01824 GetMagickToken(q,&q,token); 01825 if (*token == ',') 01826 GetMagickToken(q,&q,token); 01827 affine.rx=StringToDouble(token,(char **) NULL); 01828 GetMagickToken(q,&q,token); 01829 if (*token == ',') 01830 GetMagickToken(q,&q,token); 01831 affine.ry=StringToDouble(token,(char **) NULL); 01832 GetMagickToken(q,&q,token); 01833 if (*token == ',') 01834 GetMagickToken(q,&q,token); 01835 affine.sy=StringToDouble(token,(char **) NULL); 01836 GetMagickToken(q,&q,token); 01837 if (*token == ',') 01838 GetMagickToken(q,&q,token); 01839 affine.tx=StringToDouble(token,(char **) NULL); 01840 GetMagickToken(q,&q,token); 01841 if (*token == ',') 01842 GetMagickToken(q,&q,token); 01843 affine.ty=StringToDouble(token,(char **) NULL); 01844 break; 01845 } 01846 if (LocaleCompare("arc",keyword) == 0) 01847 { 01848 primitive_type=ArcPrimitive; 01849 break; 01850 } 01851 status=MagickFalse; 01852 break; 01853 } 01854 case 'b': 01855 case 'B': 01856 { 01857 if (LocaleCompare("bezier",keyword) == 0) 01858 { 01859 primitive_type=BezierPrimitive; 01860 break; 01861 } 01862 if (LocaleCompare("border-color",keyword) == 0) 01863 { 01864 GetMagickToken(q,&q,token); 01865 (void) QueryColorCompliance(token,AllCompliance, 01866 &graphic_context[n]->border_color,exception); 01867 break; 01868 } 01869 status=MagickFalse; 01870 break; 01871 } 01872 case 'c': 01873 case 'C': 01874 { 01875 if (LocaleCompare("clip-path",keyword) == 0) 01876 { 01877 /* 01878 Create clip mask. 01879 */ 01880 GetMagickToken(q,&q,token); 01881 (void) CloneString(&graphic_context[n]->clip_mask,token); 01882 (void) DrawClipPath(image,graphic_context[n], 01883 graphic_context[n]->clip_mask,exception); 01884 break; 01885 } 01886 if (LocaleCompare("clip-rule",keyword) == 0) 01887 { 01888 ssize_t 01889 fill_rule; 01890 01891 GetMagickToken(q,&q,token); 01892 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse, 01893 token); 01894 if (fill_rule == -1) 01895 status=MagickFalse; 01896 else 01897 graphic_context[n]->fill_rule=(FillRule) fill_rule; 01898 break; 01899 } 01900 if (LocaleCompare("clip-units",keyword) == 0) 01901 { 01902 ssize_t 01903 clip_units; 01904 01905 GetMagickToken(q,&q,token); 01906 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse, 01907 token); 01908 if (clip_units == -1) 01909 { 01910 status=MagickFalse; 01911 break; 01912 } 01913 graphic_context[n]->clip_units=(ClipPathUnits) clip_units; 01914 if (clip_units == ObjectBoundingBox) 01915 { 01916 GetAffineMatrix(¤t); 01917 affine.sx=draw_info->bounds.x2; 01918 affine.sy=draw_info->bounds.y2; 01919 affine.tx=draw_info->bounds.x1; 01920 affine.ty=draw_info->bounds.y1; 01921 break; 01922 } 01923 break; 01924 } 01925 if (LocaleCompare("circle",keyword) == 0) 01926 { 01927 primitive_type=CirclePrimitive; 01928 break; 01929 } 01930 if (LocaleCompare("color",keyword) == 0) 01931 { 01932 primitive_type=ColorPrimitive; 01933 break; 01934 } 01935 status=MagickFalse; 01936 break; 01937 } 01938 case 'd': 01939 case 'D': 01940 { 01941 if (LocaleCompare("decorate",keyword) == 0) 01942 { 01943 ssize_t 01944 decorate; 01945 01946 GetMagickToken(q,&q,token); 01947 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse, 01948 token); 01949 if (decorate == -1) 01950 status=MagickFalse; 01951 else 01952 graphic_context[n]->decorate=(DecorationType) decorate; 01953 break; 01954 } 01955 status=MagickFalse; 01956 break; 01957 } 01958 case 'e': 01959 case 'E': 01960 { 01961 if (LocaleCompare("ellipse",keyword) == 0) 01962 { 01963 primitive_type=EllipsePrimitive; 01964 break; 01965 } 01966 if (LocaleCompare("encoding",keyword) == 0) 01967 { 01968 GetMagickToken(q,&q,token); 01969 (void) CloneString(&graphic_context[n]->encoding,token); 01970 break; 01971 } 01972 status=MagickFalse; 01973 break; 01974 } 01975 case 'f': 01976 case 'F': 01977 { 01978 if (LocaleCompare("fill",keyword) == 0) 01979 { 01980 GetMagickToken(q,&q,token); 01981 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token); 01982 if (GetImageArtifact(image,pattern) != (const char *) NULL) 01983 (void) DrawPatternPath(image,draw_info,token, 01984 &graphic_context[n]->fill_pattern,exception); 01985 else 01986 { 01987 status=QueryColorCompliance(token,AllCompliance, 01988 &graphic_context[n]->fill,exception); 01989 if (status == MagickFalse) 01990 { 01991 ImageInfo 01992 *pattern_info; 01993 01994 pattern_info=AcquireImageInfo(); 01995 (void) CopyMagickString(pattern_info->filename,token, 01996 MaxTextExtent); 01997 graphic_context[n]->fill_pattern=ReadImage(pattern_info, 01998 exception); 01999 CatchException(exception); 02000 pattern_info=DestroyImageInfo(pattern_info); 02001 } 02002 } 02003 break; 02004 } 02005 if (LocaleCompare("fill-opacity",keyword) == 0) 02006 { 02007 GetMagickToken(q,&q,token); 02008 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0; 02009 graphic_context[n]->fill.alpha=(MagickRealType) QuantumRange* 02010 factor*StringToDouble(token,(char **) NULL); 02011 break; 02012 } 02013 if (LocaleCompare("fill-rule",keyword) == 0) 02014 { 02015 ssize_t 02016 fill_rule; 02017 02018 GetMagickToken(q,&q,token); 02019 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse, 02020 token); 02021 if (fill_rule == -1) 02022 status=MagickFalse; 02023 else 02024 graphic_context[n]->fill_rule=(FillRule) fill_rule; 02025 break; 02026 } 02027 if (LocaleCompare("font",keyword) == 0) 02028 { 02029 GetMagickToken(q,&q,token); 02030 (void) CloneString(&graphic_context[n]->font,token); 02031 if (LocaleCompare("none",token) == 0) 02032 graphic_context[n]->font=(char *) 02033 RelinquishMagickMemory(graphic_context[n]->font); 02034 break; 02035 } 02036 if (LocaleCompare("font-family",keyword) == 0) 02037 { 02038 GetMagickToken(q,&q,token); 02039 (void) CloneString(&graphic_context[n]->family,token); 02040 break; 02041 } 02042 if (LocaleCompare("font-size",keyword) == 0) 02043 { 02044 GetMagickToken(q,&q,token); 02045 graphic_context[n]->pointsize=StringToDouble(token,(char **) NULL); 02046 break; 02047 } 02048 if (LocaleCompare("font-stretch",keyword) == 0) 02049 { 02050 ssize_t 02051 stretch; 02052 02053 GetMagickToken(q,&q,token); 02054 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token); 02055 if (stretch == -1) 02056 status=MagickFalse; 02057 else 02058 graphic_context[n]->stretch=(StretchType) stretch; 02059 break; 02060 } 02061 if (LocaleCompare("font-style",keyword) == 0) 02062 { 02063 ssize_t 02064 style; 02065 02066 GetMagickToken(q,&q,token); 02067 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token); 02068 if (style == -1) 02069 status=MagickFalse; 02070 else 02071 graphic_context[n]->style=(StyleType) style; 02072 break; 02073 } 02074 if (LocaleCompare("font-weight",keyword) == 0) 02075 { 02076 GetMagickToken(q,&q,token); 02077 graphic_context[n]->weight=StringToUnsignedLong(token); 02078 if (LocaleCompare(token,"all") == 0) 02079 graphic_context[n]->weight=0; 02080 if (LocaleCompare(token,"bold") == 0) 02081 graphic_context[n]->weight=700; 02082 if (LocaleCompare(token,"bolder") == 0) 02083 if (graphic_context[n]->weight <= 800) 02084 graphic_context[n]->weight+=100; 02085 if (LocaleCompare(token,"lighter") == 0) 02086 if (graphic_context[n]->weight >= 100) 02087 graphic_context[n]->weight-=100; 02088 if (LocaleCompare(token,"normal") == 0) 02089 graphic_context[n]->weight=400; 02090 break; 02091 } 02092 status=MagickFalse; 02093 break; 02094 } 02095 case 'g': 02096 case 'G': 02097 { 02098 if (LocaleCompare("gradient-units",keyword) == 0) 02099 { 02100 GetMagickToken(q,&q,token); 02101 break; 02102 } 02103 if (LocaleCompare("gravity",keyword) == 0) 02104 { 02105 ssize_t 02106 gravity; 02107 02108 GetMagickToken(q,&q,token); 02109 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token); 02110 if (gravity == -1) 02111 status=MagickFalse; 02112 else 02113 graphic_context[n]->gravity=(GravityType) gravity; 02114 break; 02115 } 02116 status=MagickFalse; 02117 break; 02118 } 02119 case 'i': 02120 case 'I': 02121 { 02122 if (LocaleCompare("image",keyword) == 0) 02123 { 02124 ssize_t 02125 compose; 02126 02127 primitive_type=ImagePrimitive; 02128 GetMagickToken(q,&q,token); 02129 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token); 02130 if (compose == -1) 02131 status=MagickFalse; 02132 else 02133 graphic_context[n]->compose=(CompositeOperator) compose; 02134 break; 02135 } 02136 if (LocaleCompare("interline-spacing",keyword) == 0) 02137 { 02138 GetMagickToken(q,&q,token); 02139 graphic_context[n]->interline_spacing=StringToDouble(token, 02140 (char **) NULL); 02141 break; 02142 } 02143 if (LocaleCompare("interword-spacing",keyword) == 0) 02144 { 02145 GetMagickToken(q,&q,token); 02146 graphic_context[n]->interword_spacing=StringToDouble(token, 02147 (char **) NULL); 02148 break; 02149 } 02150 status=MagickFalse; 02151 break; 02152 } 02153 case 'k': 02154 case 'K': 02155 { 02156 if (LocaleCompare("kerning",keyword) == 0) 02157 { 02158 GetMagickToken(q,&q,token); 02159 graphic_context[n]->kerning=StringToDouble(token,(char **) NULL); 02160 break; 02161 } 02162 status=MagickFalse; 02163 break; 02164 } 02165 case 'l': 02166 case 'L': 02167 { 02168 if (LocaleCompare("line",keyword) == 0) 02169 primitive_type=LinePrimitive; 02170 else 02171 status=MagickFalse; 02172 break; 02173 } 02174 case 'm': 02175 case 'M': 02176 { 02177 if (LocaleCompare("matte",keyword) == 0) 02178 primitive_type=MattePrimitive; 02179 else 02180 status=MagickFalse; 02181 break; 02182 } 02183 case 'o': 02184 case 'O': 02185 { 02186 if (LocaleCompare("offset",keyword) == 0) 02187 { 02188 GetMagickToken(q,&q,token); 02189 break; 02190 } 02191 if (LocaleCompare("opacity",keyword) == 0) 02192 { 02193 GetMagickToken(q,&q,token); 02194 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0; 02195 graphic_context[n]->alpha=ClampToQuantum((MagickRealType) 02196 QuantumRange*(1.0-((1.0-QuantumScale*graphic_context[n]->alpha)* 02197 factor*StringToDouble(token,(char **) NULL)))); 02198 graphic_context[n]->fill.alpha=(double) graphic_context[n]->alpha; 02199 graphic_context[n]->stroke.alpha=(double) graphic_context[n]->alpha; 02200 break; 02201 } 02202 status=MagickFalse; 02203 break; 02204 } 02205 case 'p': 02206 case 'P': 02207 { 02208 if (LocaleCompare("path",keyword) == 0) 02209 { 02210 primitive_type=PathPrimitive; 02211 break; 02212 } 02213 if (LocaleCompare("point",keyword) == 0) 02214 { 02215 primitive_type=PointPrimitive; 02216 break; 02217 } 02218 if (LocaleCompare("polyline",keyword) == 0) 02219 { 02220 primitive_type=PolylinePrimitive; 02221 break; 02222 } 02223 if (LocaleCompare("polygon",keyword) == 0) 02224 { 02225 primitive_type=PolygonPrimitive; 02226 break; 02227 } 02228 if (LocaleCompare("pop",keyword) == 0) 02229 { 02230 GetMagickToken(q,&q,token); 02231 if (LocaleCompare("clip-path",token) == 0) 02232 break; 02233 if (LocaleCompare("defs",token) == 0) 02234 break; 02235 if (LocaleCompare("gradient",token) == 0) 02236 break; 02237 if (LocaleCompare("graphic-context",token) == 0) 02238 { 02239 if (n <= 0) 02240 { 02241 (void) ThrowMagickException(exception,GetMagickModule(), 02242 DrawError,"UnbalancedGraphicContextPushPop","`%s'",token); 02243 n=0; 02244 break; 02245 } 02246 if (graphic_context[n]->clip_mask != (char *) NULL) 02247 if (LocaleCompare(graphic_context[n]->clip_mask, 02248 graphic_context[n-1]->clip_mask) != 0) 02249 SetImageMask(image,(Image *) NULL,exception); 02250 graphic_context[n]=DestroyDrawInfo(graphic_context[n]); 02251 n--; 02252 break; 02253 } 02254 if (LocaleCompare("pattern",token) == 0) 02255 break; 02256 status=MagickFalse; 02257 break; 02258 } 02259 if (LocaleCompare("push",keyword) == 0) 02260 { 02261 GetMagickToken(q,&q,token); 02262 if (LocaleCompare("clip-path",token) == 0) 02263 { 02264 char 02265 name[MaxTextExtent]; 02266 02267 GetMagickToken(q,&q,token); 02268 (void) FormatLocaleString(name,MaxTextExtent,"%s",token); 02269 for (p=q; *q != '\0'; ) 02270 { 02271 GetMagickToken(q,&q,token); 02272 if (LocaleCompare(token,"pop") != 0) 02273 continue; 02274 GetMagickToken(q,(const char **) NULL,token); 02275 if (LocaleCompare(token,"clip-path") != 0) 02276 continue; 02277 break; 02278 } 02279 (void) CopyMagickString(token,p,(size_t) (q-p-4+1)); 02280 (void) SetImageArtifact(image,name,token); 02281 GetMagickToken(q,&q,token); 02282 break; 02283 } 02284 if (LocaleCompare("gradient",token) == 0) 02285 { 02286 char 02287 key[2*MaxTextExtent], 02288 name[MaxTextExtent], 02289 type[MaxTextExtent]; 02290 02291 SegmentInfo 02292 segment; 02293 02294 GetMagickToken(q,&q,token); 02295 (void) CopyMagickString(name,token,MaxTextExtent); 02296 GetMagickToken(q,&q,token); 02297 (void) CopyMagickString(type,token,MaxTextExtent); 02298 GetMagickToken(q,&q,token); 02299 segment.x1=StringToDouble(token,(char **) NULL); 02300 GetMagickToken(q,&q,token); 02301 if (*token == ',') 02302 GetMagickToken(q,&q,token); 02303 segment.y1=StringToDouble(token,(char **) NULL); 02304 GetMagickToken(q,&q,token); 02305 if (*token == ',') 02306 GetMagickToken(q,&q,token); 02307 segment.x2=StringToDouble(token,(char **) NULL); 02308 GetMagickToken(q,&q,token); 02309 if (*token == ',') 02310 GetMagickToken(q,&q,token); 02311 segment.y2=StringToDouble(token,(char **) NULL); 02312 if (LocaleCompare(type,"radial") == 0) 02313 { 02314 GetMagickToken(q,&q,token); 02315 if (*token == ',') 02316 GetMagickToken(q,&q,token); 02317 } 02318 for (p=q; *q != '\0'; ) 02319 { 02320 GetMagickToken(q,&q,token); 02321 if (LocaleCompare(token,"pop") != 0) 02322 continue; 02323 GetMagickToken(q,(const char **) NULL,token); 02324 if (LocaleCompare(token,"gradient") != 0) 02325 continue; 02326 break; 02327 } 02328 (void) CopyMagickString(token,p,(size_t) (q-p-4+1)); 02329 bounds.x1=graphic_context[n]->affine.sx*segment.x1+ 02330 graphic_context[n]->affine.ry*segment.y1+ 02331 graphic_context[n]->affine.tx; 02332 bounds.y1=graphic_context[n]->affine.rx*segment.x1+ 02333 graphic_context[n]->affine.sy*segment.y1+ 02334 graphic_context[n]->affine.ty; 02335 bounds.x2=graphic_context[n]->affine.sx*segment.x2+ 02336 graphic_context[n]->affine.ry*segment.y2+ 02337 graphic_context[n]->affine.tx; 02338 bounds.y2=graphic_context[n]->affine.rx*segment.x2+ 02339 graphic_context[n]->affine.sy*segment.y2+ 02340 graphic_context[n]->affine.ty; 02341 (void) FormatLocaleString(key,MaxTextExtent,"%s",name); 02342 (void) SetImageArtifact(image,key,token); 02343 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name); 02344 (void) FormatLocaleString(geometry,MaxTextExtent, 02345 "%gx%g%+.15g%+.15g", 02346 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0), 02347 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0), 02348 bounds.x1,bounds.y1); 02349 (void) SetImageArtifact(image,key,geometry); 02350 GetMagickToken(q,&q,token); 02351 break; 02352 } 02353 if (LocaleCompare("pattern",token) == 0) 02354 { 02355 RectangleInfo 02356 bounds; 02357 02358 GetMagickToken(q,&q,token); 02359 (void) CopyMagickString(name,token,MaxTextExtent); 02360 GetMagickToken(q,&q,token); 02361 bounds.x=(ssize_t) ceil(StringToDouble(token,(char **) NULL)- 02362 0.5); 02363 GetMagickToken(q,&q,token); 02364 if (*token == ',') 02365 GetMagickToken(q,&q,token); 02366 bounds.y=(ssize_t) ceil(StringToDouble(token,(char **) NULL)- 02367 0.5); 02368 GetMagickToken(q,&q,token); 02369 if (*token == ',') 02370 GetMagickToken(q,&q,token); 02371 bounds.width=(size_t) floor(StringToDouble(token, 02372 (char **) NULL)+0.5); 02373 GetMagickToken(q,&q,token); 02374 if (*token == ',') 02375 GetMagickToken(q,&q,token); 02376 bounds.height=(size_t) floor(StringToDouble(token, 02377 (char **) NULL)+0.5); 02378 for (p=q; *q != '\0'; ) 02379 { 02380 GetMagickToken(q,&q,token); 02381 if (LocaleCompare(token,"pop") != 0) 02382 continue; 02383 GetMagickToken(q,(const char **) NULL,token); 02384 if (LocaleCompare(token,"pattern") != 0) 02385 continue; 02386 break; 02387 } 02388 (void) CopyMagickString(token,p,(size_t) (q-p-4+1)); 02389 (void) FormatLocaleString(key,MaxTextExtent,"%s",name); 02390 (void) SetImageArtifact(image,key,token); 02391 (void) FormatLocaleString(key,MaxTextExtent,"%s-geometry",name); 02392 (void) FormatLocaleString(geometry,MaxTextExtent, 02393 "%.20gx%.20g%+.20g%+.20g",(double) bounds.width,(double) 02394 bounds.height,(double) bounds.x,(double) bounds.y); 02395 (void) SetImageArtifact(image,key,geometry); 02396 GetMagickToken(q,&q,token); 02397 break; 02398 } 02399 if (LocaleCompare("graphic-context",token) == 0) 02400 { 02401 n++; 02402 graphic_context=(DrawInfo **) ResizeQuantumMemory( 02403 graphic_context,(size_t) (n+1),sizeof(*graphic_context)); 02404 if (graphic_context == (DrawInfo **) NULL) 02405 { 02406 (void) ThrowMagickException(exception,GetMagickModule(), 02407 ResourceLimitError,"MemoryAllocationFailed","`%s'", 02408 image->filename); 02409 break; 02410 } 02411 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL, 02412 graphic_context[n-1]); 02413 break; 02414 } 02415 if (LocaleCompare("defs",token) == 0) 02416 break; 02417 status=MagickFalse; 02418 break; 02419 } 02420 status=MagickFalse; 02421 break; 02422 } 02423 case 'r': 02424 case 'R': 02425 { 02426 if (LocaleCompare("rectangle",keyword) == 0) 02427 { 02428 primitive_type=RectanglePrimitive; 02429 break; 02430 } 02431 if (LocaleCompare("rotate",keyword) == 0) 02432 { 02433 GetMagickToken(q,&q,token); 02434 angle=StringToDouble(token,(char **) NULL); 02435 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0))); 02436 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0))); 02437 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0)))); 02438 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0))); 02439 break; 02440 } 02441 if (LocaleCompare("roundRectangle",keyword) == 0) 02442 { 02443 primitive_type=RoundRectanglePrimitive; 02444 break; 02445 } 02446 status=MagickFalse; 02447 break; 02448 } 02449 case 's': 02450 case 'S': 02451 { 02452 if (LocaleCompare("scale",keyword) == 0) 02453 { 02454 GetMagickToken(q,&q,token); 02455 affine.sx=StringToDouble(token,(char **) NULL); 02456 GetMagickToken(q,&q,token); 02457 if (*token == ',') 02458 GetMagickToken(q,&q,token); 02459 affine.sy=StringToDouble(token,(char **) NULL); 02460 break; 02461 } 02462 if (LocaleCompare("skewX",keyword) == 0) 02463 { 02464 GetMagickToken(q,&q,token); 02465 angle=StringToDouble(token,(char **) NULL); 02466 affine.ry=sin(DegreesToRadians(angle)); 02467 break; 02468 } 02469 if (LocaleCompare("skewY",keyword) == 0) 02470 { 02471 GetMagickToken(q,&q,token); 02472 angle=StringToDouble(token,(char **) NULL); 02473 affine.rx=(-tan(DegreesToRadians(angle)/2.0)); 02474 break; 02475 } 02476 if (LocaleCompare("stop-color",keyword) == 0) 02477 { 02478 PixelInfo 02479 stop_color; 02480 02481 GetMagickToken(q,&q,token); 02482 (void) QueryColorCompliance(token,AllCompliance,&stop_color, 02483 exception); 02484 (void) GradientImage(image,LinearGradient,ReflectSpread, 02485 &start_color,&stop_color,exception); 02486 start_color=stop_color; 02487 GetMagickToken(q,&q,token); 02488 break; 02489 } 02490 if (LocaleCompare("stroke",keyword) == 0) 02491 { 02492 GetMagickToken(q,&q,token); 02493 (void) FormatLocaleString(pattern,MaxTextExtent,"%s",token); 02494 if (GetImageArtifact(image,pattern) != (const char *) NULL) 02495 (void) DrawPatternPath(image,draw_info,token, 02496 &graphic_context[n]->stroke_pattern,exception); 02497 else 02498 { 02499 status=QueryColorCompliance(token,AllCompliance, 02500 &graphic_context[n]->stroke,exception); 02501 if (status == MagickFalse) 02502 { 02503 ImageInfo 02504 *pattern_info; 02505 02506 pattern_info=AcquireImageInfo(); 02507 (void) CopyMagickString(pattern_info->filename,token, 02508 MaxTextExtent); 02509 graphic_context[n]->stroke_pattern=ReadImage(pattern_info, 02510 exception); 02511 CatchException(exception); 02512 pattern_info=DestroyImageInfo(pattern_info); 02513 } 02514 } 02515 break; 02516 } 02517 if (LocaleCompare("stroke-antialias",keyword) == 0) 02518 { 02519 GetMagickToken(q,&q,token); 02520 graphic_context[n]->stroke_antialias= 02521 StringToLong(token) != 0 ? MagickTrue : MagickFalse; 02522 break; 02523 } 02524 if (LocaleCompare("stroke-dasharray",keyword) == 0) 02525 { 02526 if (graphic_context[n]->dash_pattern != (double *) NULL) 02527 graphic_context[n]->dash_pattern=(double *) 02528 RelinquishMagickMemory(graphic_context[n]->dash_pattern); 02529 if (IsPoint(q) != MagickFalse) 02530 { 02531 const char 02532 *p; 02533 02534 p=q; 02535 GetMagickToken(p,&p,token); 02536 if (*token == ',') 02537 GetMagickToken(p,&p,token); 02538 for (x=0; IsPoint(token) != MagickFalse; x++) 02539 { 02540 GetMagickToken(p,&p,token); 02541 if (*token == ',') 02542 GetMagickToken(p,&p,token); 02543 } 02544 graphic_context[n]->dash_pattern=(double *) 02545 AcquireQuantumMemory((size_t) (2UL*x+1UL), 02546 sizeof(*graphic_context[n]->dash_pattern)); 02547 if (graphic_context[n]->dash_pattern == (double *) NULL) 02548 { 02549 (void) ThrowMagickException(exception,GetMagickModule(), 02550 ResourceLimitError,"MemoryAllocationFailed","`%s'", 02551 image->filename); 02552 break; 02553 } 02554 for (j=0; j < x; j++) 02555 { 02556 GetMagickToken(q,&q,token); 02557 if (*token == ',') 02558 GetMagickToken(q,&q,token); 02559 graphic_context[n]->dash_pattern[j]=StringToDouble(token, 02560 (char **) NULL); 02561 } 02562 if ((x & 0x01) != 0) 02563 for ( ; j < (2*x); j++) 02564 graphic_context[n]->dash_pattern[j]= 02565 graphic_context[n]->dash_pattern[j-x]; 02566 graphic_context[n]->dash_pattern[j]=0.0; 02567 break; 02568 } 02569 GetMagickToken(q,&q,token); 02570 break; 02571 } 02572 if (LocaleCompare("stroke-dashoffset",keyword) == 0) 02573 { 02574 GetMagickToken(q,&q,token); 02575 graphic_context[n]->dash_offset=StringToDouble(token, 02576 (char **) NULL); 02577 break; 02578 } 02579 if (LocaleCompare("stroke-linecap",keyword) == 0) 02580 { 02581 ssize_t 02582 linecap; 02583 02584 GetMagickToken(q,&q,token); 02585 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token); 02586 if (linecap == -1) 02587 status=MagickFalse; 02588 else 02589 graphic_context[n]->linecap=(LineCap) linecap; 02590 break; 02591 } 02592 if (LocaleCompare("stroke-linejoin",keyword) == 0) 02593 { 02594 ssize_t 02595 linejoin; 02596 02597 GetMagickToken(q,&q,token); 02598 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse, 02599 token); 02600 if (linejoin == -1) 02601 status=MagickFalse; 02602 else 02603 graphic_context[n]->linejoin=(LineJoin) linejoin; 02604 break; 02605 } 02606 if (LocaleCompare("stroke-miterlimit",keyword) == 0) 02607 { 02608 GetMagickToken(q,&q,token); 02609 graphic_context[n]->miterlimit=StringToUnsignedLong(token); 02610 break; 02611 } 02612 if (LocaleCompare("stroke-opacity",keyword) == 0) 02613 { 02614 GetMagickToken(q,&q,token); 02615 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0; 02616 graphic_context[n]->stroke.alpha=(MagickRealType) QuantumRange* 02617 factor*StringToDouble(token,(char **) NULL); 02618 break; 02619 } 02620 if (LocaleCompare("stroke-width",keyword) == 0) 02621 { 02622 GetMagickToken(q,&q,token); 02623 graphic_context[n]->stroke_width=StringToDouble(token, 02624 (char **) NULL); 02625 break; 02626 } 02627 status=MagickFalse; 02628 break; 02629 } 02630 case 't': 02631 case 'T': 02632 { 02633 if (LocaleCompare("text",keyword) == 0) 02634 { 02635 primitive_type=TextPrimitive; 02636 break; 02637 } 02638 if (LocaleCompare("text-align",keyword) == 0) 02639 { 02640 ssize_t 02641 align; 02642 02643 GetMagickToken(q,&q,token); 02644 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token); 02645 if (align == -1) 02646 status=MagickFalse; 02647 else 02648 graphic_context[n]->align=(AlignType) align; 02649 break; 02650 } 02651 if (LocaleCompare("text-anchor",keyword) == 0) 02652 { 02653 ssize_t 02654 align; 02655 02656 GetMagickToken(q,&q,token); 02657 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token); 02658 if (align == -1) 02659 status=MagickFalse; 02660 else 02661 graphic_context[n]->align=(AlignType) align; 02662 break; 02663 } 02664 if (LocaleCompare("text-antialias",keyword) == 0) 02665 { 02666 GetMagickToken(q,&q,token); 02667 graphic_context[n]->text_antialias= 02668 StringToLong(token) != 0 ? MagickTrue : MagickFalse; 02669 break; 02670 } 02671 if (LocaleCompare("text-undercolor",keyword) == 0) 02672 { 02673 GetMagickToken(q,&q,token); 02674 (void) QueryColorCompliance(token,AllCompliance, 02675 &graphic_context[n]->undercolor,exception); 02676 break; 02677 } 02678 if (LocaleCompare("translate",keyword) == 0) 02679 { 02680 GetMagickToken(q,&q,token); 02681 affine.tx=StringToDouble(token,(char **) NULL); 02682 GetMagickToken(q,&q,token); 02683 if (*token == ',') 02684 GetMagickToken(q,&q,token); 02685 affine.ty=StringToDouble(token,(char **) NULL); 02686 break; 02687 } 02688 status=MagickFalse; 02689 break; 02690 } 02691 case 'v': 02692 case 'V': 02693 { 02694 if (LocaleCompare("viewbox",keyword) == 0) 02695 { 02696 GetMagickToken(q,&q,token); 02697 graphic_context[n]->viewbox.x=(ssize_t) ceil(StringToDouble(token, 02698 (char **) NULL)-0.5); 02699 GetMagickToken(q,&q,token); 02700 if (*token == ',') 02701 GetMagickToken(q,&q,token); 02702 graphic_context[n]->viewbox.y=(ssize_t) ceil(StringToDouble(token, 02703 (char **) NULL)-0.5); 02704 GetMagickToken(q,&q,token); 02705 if (*token == ',') 02706 GetMagickToken(q,&q,token); 02707 graphic_context[n]->viewbox.width=(size_t) floor(StringToDouble( 02708 token,(char **) NULL)+0.5); 02709 GetMagickToken(q,&q,token); 02710 if (*token == ',') 02711 GetMagickToken(q,&q,token); 02712 graphic_context[n]->viewbox.height=(size_t) floor(StringToDouble( 02713 token,(char **) NULL)+0.5); 02714 break; 02715 } 02716 status=MagickFalse; 02717 break; 02718 } 02719 default: 02720 { 02721 status=MagickFalse; 02722 break; 02723 } 02724 } 02725 if (status == MagickFalse) 02726 break; 02727 if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) || 02728 (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0)) 02729 { 02730 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx; 02731 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx; 02732 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy; 02733 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy; 02734 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+ 02735 current.tx; 02736 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+ 02737 current.ty; 02738 } 02739 if (primitive_type == UndefinedPrimitive) 02740 { 02741 if (image->debug != MagickFalse) 02742 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s", 02743 (int) (q-p),p); 02744 continue; 02745 } 02746 /* 02747 Parse the primitive attributes. 02748 */ 02749 i=0; 02750 j=0; 02751 primitive_info[0].point.x=0.0; 02752 primitive_info[0].point.y=0.0; 02753 for (x=0; *q != '\0'; x++) 02754 { 02755 /* 02756 Define points. 02757 */ 02758 if (IsPoint(q) == MagickFalse) 02759 break; 02760 GetMagickToken(q,&q,token); 02761 point.x=StringToDouble(token,(char **) NULL); 02762 GetMagickToken(q,&q,token); 02763 if (*token == ',') 02764 GetMagickToken(q,&q,token); 02765 point.y=StringToDouble(token,(char **) NULL); 02766 GetMagickToken(q,(const char **) NULL,token); 02767 if (*token == ',') 02768 GetMagickToken(q,&q,token); 02769 primitive_info[i].primitive=primitive_type; 02770 primitive_info[i].point=point; 02771 primitive_info[i].coordinates=0; 02772 primitive_info[i].method=FloodfillMethod; 02773 i++; 02774 if (i < (ssize_t) number_points) 02775 continue; 02776 number_points<<=1; 02777 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info, 02778 (size_t) number_points,sizeof(*primitive_info)); 02779 if (primitive_info == (PrimitiveInfo *) NULL) 02780 { 02781 (void) ThrowMagickException(exception,GetMagickModule(), 02782 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 02783 break; 02784 } 02785 } 02786 primitive_info[j].primitive=primitive_type; 02787 primitive_info[j].coordinates=(size_t) x; 02788 primitive_info[j].method=FloodfillMethod; 02789 primitive_info[j].text=(char *) NULL; 02790 /* 02791 Circumscribe primitive within a circle. 02792 */ 02793 bounds.x1=primitive_info[j].point.x; 02794 bounds.y1=primitive_info[j].point.y; 02795 bounds.x2=primitive_info[j].point.x; 02796 bounds.y2=primitive_info[j].point.y; 02797 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++) 02798 { 02799 point=primitive_info[j+k].point; 02800 if (point.x < bounds.x1) 02801 bounds.x1=point.x; 02802 if (point.y < bounds.y1) 02803 bounds.y1=point.y; 02804 if (point.x > bounds.x2) 02805 bounds.x2=point.x; 02806 if (point.y > bounds.y2) 02807 bounds.y2=point.y; 02808 } 02809 /* 02810 Speculate how many points our primitive might consume. 02811 */ 02812 length=primitive_info[j].coordinates; 02813 switch (primitive_type) 02814 { 02815 case RectanglePrimitive: 02816 { 02817 length*=5; 02818 break; 02819 } 02820 case RoundRectanglePrimitive: 02821 { 02822 length*=5+8*BezierQuantum; 02823 break; 02824 } 02825 case BezierPrimitive: 02826 { 02827 if (primitive_info[j].coordinates > 107) 02828 (void) ThrowMagickException(exception,GetMagickModule(),DrawError, 02829 "TooManyBezierCoordinates","`%s'",token); 02830 length=BezierQuantum*primitive_info[j].coordinates; 02831 break; 02832 } 02833 case PathPrimitive: 02834 { 02835 char 02836 *s, 02837 *t; 02838 02839 GetMagickToken(q,&q,token); 02840 length=1; 02841 t=token; 02842 for (s=token; *s != '\0'; s=t) 02843 { 02844 double 02845 value; 02846 02847 value=StringToDouble(s,&t); 02848 (void) value; 02849 if (s == t) 02850 { 02851 t++; 02852 continue; 02853 } 02854 length++; 02855 } 02856 length=length*BezierQuantum/2; 02857 break; 02858 } 02859 case CirclePrimitive: 02860 case ArcPrimitive: 02861 case EllipsePrimitive: 02862 { 02863 MagickRealType 02864 alpha, 02865 beta, 02866 radius; 02867 02868 alpha=bounds.x2-bounds.x1; 02869 beta=bounds.y2-bounds.y1; 02870 radius=hypot((double) alpha,(double) beta); 02871 length=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360; 02872 break; 02873 } 02874 default: 02875 break; 02876 } 02877 if ((size_t) (i+length) >= number_points) 02878 { 02879 /* 02880 Resize based on speculative points required by primitive. 02881 */ 02882 number_points+=length+1; 02883 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info, 02884 (size_t) number_points,sizeof(*primitive_info)); 02885 if (primitive_info == (PrimitiveInfo *) NULL) 02886 { 02887 (void) ThrowMagickException(exception,GetMagickModule(), 02888 ResourceLimitError,"MemoryAllocationFailed","`%s'", 02889 image->filename); 02890 break; 02891 } 02892 } 02893 switch (primitive_type) 02894 { 02895 case PointPrimitive: 02896 default: 02897 { 02898 if (primitive_info[j].coordinates != 1) 02899 { 02900 status=MagickFalse; 02901 break; 02902 } 02903 TracePoint(primitive_info+j,primitive_info[j].point); 02904 i=(ssize_t) (j+primitive_info[j].coordinates); 02905 break; 02906 } 02907 case LinePrimitive: 02908 { 02909 if (primitive_info[j].coordinates != 2) 02910 { 02911 status=MagickFalse; 02912 break; 02913 } 02914 TraceLine(primitive_info+j,primitive_info[j].point, 02915 primitive_info[j+1].point); 02916 i=(ssize_t) (j+primitive_info[j].coordinates); 02917 break; 02918 } 02919 case RectanglePrimitive: 02920 { 02921 if (primitive_info[j].coordinates != 2) 02922 { 02923 status=MagickFalse; 02924 break; 02925 } 02926 TraceRectangle(primitive_info+j,primitive_info[j].point, 02927 primitive_info[j+1].point); 02928 i=(ssize_t) (j+primitive_info[j].coordinates); 02929 break; 02930 } 02931 case RoundRectanglePrimitive: 02932 { 02933 if (primitive_info[j].coordinates != 3) 02934 { 02935 status=MagickFalse; 02936 break; 02937 } 02938 TraceRoundRectangle(primitive_info+j,primitive_info[j].point, 02939 primitive_info[j+1].point,primitive_info[j+2].point); 02940 i=(ssize_t) (j+primitive_info[j].coordinates); 02941 break; 02942 } 02943 case ArcPrimitive: 02944 { 02945 if (primitive_info[j].coordinates != 3) 02946 { 02947 primitive_type=UndefinedPrimitive; 02948 break; 02949 } 02950 TraceArc(primitive_info+j,primitive_info[j].point, 02951 primitive_info[j+1].point,primitive_info[j+2].point); 02952 i=(ssize_t) (j+primitive_info[j].coordinates); 02953 break; 02954 } 02955 case EllipsePrimitive: 02956 { 02957 if (primitive_info[j].coordinates != 3) 02958 { 02959 status=MagickFalse; 02960 break; 02961 } 02962 TraceEllipse(primitive_info+j,primitive_info[j].point, 02963 primitive_info[j+1].point,primitive_info[j+2].point); 02964 i=(ssize_t) (j+primitive_info[j].coordinates); 02965 break; 02966 } 02967 case CirclePrimitive: 02968 { 02969 if (primitive_info[j].coordinates != 2) 02970 { 02971 status=MagickFalse; 02972 break; 02973 } 02974 TraceCircle(primitive_info+j,primitive_info[j].point, 02975 primitive_info[j+1].point); 02976 i=(ssize_t) (j+primitive_info[j].coordinates); 02977 break; 02978 } 02979 case PolylinePrimitive: 02980 break; 02981 case PolygonPrimitive: 02982 { 02983 primitive_info[i]=primitive_info[j]; 02984 primitive_info[i].coordinates=0; 02985 primitive_info[j].coordinates++; 02986 i++; 02987 break; 02988 } 02989 case BezierPrimitive: 02990 { 02991 if (primitive_info[j].coordinates < 3) 02992 { 02993 status=MagickFalse; 02994 break; 02995 } 02996 TraceBezier(primitive_info+j,primitive_info[j].coordinates); 02997 i=(ssize_t) (j+primitive_info[j].coordinates); 02998 break; 02999 } 03000 case PathPrimitive: 03001 { 03002 i=(ssize_t) (j+TracePath(primitive_info+j,token)); 03003 break; 03004 } 03005 case ColorPrimitive: 03006 case MattePrimitive: 03007 { 03008 ssize_t 03009 method; 03010 03011 if (primitive_info[j].coordinates != 1) 03012 { 03013 status=MagickFalse; 03014 break; 03015 } 03016 GetMagickToken(q,&q,token); 03017 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token); 03018 if (method == -1) 03019 status=MagickFalse; 03020 else 03021 primitive_info[j].method=(PaintMethod) method; 03022 break; 03023 } 03024 case TextPrimitive: 03025 { 03026 if (primitive_info[j].coordinates != 1) 03027 { 03028 status=MagickFalse; 03029 break; 03030 } 03031 if (*token != ',') 03032 GetMagickToken(q,&q,token); 03033 primitive_info[j].text=AcquireString(token); 03034 break; 03035 } 03036 case ImagePrimitive: 03037 { 03038 if (primitive_info[j].coordinates != 2) 03039 { 03040 status=MagickFalse; 03041 break; 03042 } 03043 GetMagickToken(q,&q,token); 03044 primitive_info[j].text=AcquireString(token); 03045 break; 03046 } 03047 } 03048 if (primitive_info == (PrimitiveInfo *) NULL) 03049 break; 03050 if (image->debug != MagickFalse) 03051 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p); 03052 if (status == MagickFalse) 03053 break; 03054 primitive_info[i].primitive=UndefinedPrimitive; 03055 if (i == 0) 03056 continue; 03057 /* 03058 Transform points. 03059 */ 03060 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) 03061 { 03062 point=primitive_info[i].point; 03063 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+ 03064 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx; 03065 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+ 03066 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty; 03067 point=primitive_info[i].point; 03068 if (point.x < graphic_context[n]->bounds.x1) 03069 graphic_context[n]->bounds.x1=point.x; 03070 if (point.y < graphic_context[n]->bounds.y1) 03071 graphic_context[n]->bounds.y1=point.y; 03072 if (point.x > graphic_context[n]->bounds.x2) 03073 graphic_context[n]->bounds.x2=point.x; 03074 if (point.y > graphic_context[n]->bounds.y2) 03075 graphic_context[n]->bounds.y2=point.y; 03076 if (primitive_info[i].primitive == ImagePrimitive) 03077 break; 03078 if (i >= (ssize_t) number_points) 03079 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 03080 } 03081 if (graphic_context[n]->render != MagickFalse) 03082 { 03083 if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) && 03084 (LocaleCompare(graphic_context[n]->clip_mask, 03085 graphic_context[n-1]->clip_mask) != 0)) 03086 (void) DrawClipPath(image,graphic_context[n], 03087 graphic_context[n]->clip_mask,exception); 03088 (void) DrawPrimitive(image,graphic_context[n],primitive_info,exception); 03089 } 03090 if (primitive_info->text != (char *) NULL) 03091 primitive_info->text=(char *) RelinquishMagickMemory( 03092 primitive_info->text); 03093 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType) 03094 primitive_extent); 03095 if (proceed == MagickFalse) 03096 break; 03097 } 03098 if (image->debug != MagickFalse) 03099 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image"); 03100 /* 03101 Relinquish resources. 03102 */ 03103 token=DestroyString(token); 03104 if (primitive_info != (PrimitiveInfo *) NULL) 03105 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info); 03106 primitive=DestroyString(primitive); 03107 for ( ; n >= 0; n--) 03108 graphic_context[n]=DestroyDrawInfo(graphic_context[n]); 03109 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context); 03110 if (status == MagickFalse) 03111 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition", 03112 keyword); 03113 return(status); 03114 } 03115 03116 /* 03117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03118 % % 03119 % % 03120 % % 03121 % D r a w G r a d i e n t I m a g e % 03122 % % 03123 % % 03124 % % 03125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03126 % 03127 % DrawGradientImage() draws a linear gradient on the image. 03128 % 03129 % The format of the DrawGradientImage method is: 03130 % 03131 % MagickBooleanType DrawGradientImage(Image *image, 03132 % const DrawInfo *draw_info,ExceptionInfo *exception) 03133 % 03134 % A description of each parameter follows: 03135 % 03136 % o image: the image. 03137 % 03138 % o draw_info: the draw info. 03139 % 03140 % o exception: return any errors or warnings in this structure. 03141 % 03142 */ 03143 03144 static inline MagickRealType GetStopColorOffset(const GradientInfo *gradient, 03145 const ssize_t x,const ssize_t y) 03146 { 03147 switch (gradient->type) 03148 { 03149 case UndefinedGradient: 03150 case LinearGradient: 03151 { 03152 MagickRealType 03153 gamma, 03154 length, 03155 offset, 03156 scale; 03157 03158 PointInfo 03159 p, 03160 q; 03161 03162 const SegmentInfo 03163 *gradient_vector; 03164 03165 gradient_vector=(&gradient->gradient_vector); 03166 p.x=gradient_vector->x2-gradient_vector->x1; 03167 p.y=gradient_vector->y2-gradient_vector->y1; 03168 q.x=(double) x-gradient_vector->x1; 03169 q.y=(double) y-gradient_vector->y1; 03170 length=sqrt(q.x*q.x+q.y*q.y); 03171 gamma=sqrt(p.x*p.x+p.y*p.y)*length; 03172 gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma); 03173 scale=p.x*q.x+p.y*q.y; 03174 offset=gamma*scale*length; 03175 return(offset); 03176 } 03177 case RadialGradient: 03178 { 03179 MagickRealType 03180 length, 03181 offset; 03182 03183 PointInfo 03184 v; 03185 03186 v.x=(double) x-gradient->center.x; 03187 v.y=(double) y-gradient->center.y; 03188 length=sqrt(v.x*v.x+v.y*v.y); 03189 if (gradient->spread == RepeatSpread) 03190 return(length); 03191 offset=length/gradient->radius; 03192 return(offset); 03193 } 03194 } 03195 return(0.0); 03196 } 03197 03198 MagickExport MagickBooleanType DrawGradientImage(Image *image, 03199 const DrawInfo *draw_info,ExceptionInfo *exception) 03200 { 03201 CacheView 03202 *image_view; 03203 03204 const GradientInfo 03205 *gradient; 03206 03207 const SegmentInfo 03208 *gradient_vector; 03209 03210 MagickBooleanType 03211 status; 03212 03213 PixelInfo 03214 zero; 03215 03216 MagickRealType 03217 length; 03218 03219 PointInfo 03220 point; 03221 03222 RectangleInfo 03223 bounding_box; 03224 03225 ssize_t 03226 y; 03227 03228 /* 03229 Draw linear or radial gradient on image. 03230 */ 03231 assert(image != (Image *) NULL); 03232 assert(image->signature == MagickSignature); 03233 if (image->debug != MagickFalse) 03234 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 03235 assert(draw_info != (const DrawInfo *) NULL); 03236 gradient=(&draw_info->gradient); 03237 gradient_vector=(&gradient->gradient_vector); 03238 point.x=gradient_vector->x2-gradient_vector->x1; 03239 point.y=gradient_vector->y2-gradient_vector->y1; 03240 length=sqrt(point.x*point.x+point.y*point.y); 03241 bounding_box=gradient->bounding_box; 03242 status=MagickTrue; 03243 GetPixelInfo(image,&zero); 03244 image_view=AcquireCacheView(image); 03245 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03246 #pragma omp parallel for schedule(static) shared(status) 03247 #endif 03248 for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++) 03249 { 03250 PixelInfo 03251 composite, 03252 pixel; 03253 03254 MagickRealType 03255 alpha, 03256 offset; 03257 03258 register Quantum 03259 *restrict q; 03260 03261 register ssize_t 03262 i, 03263 x; 03264 03265 ssize_t 03266 j; 03267 03268 if (status == MagickFalse) 03269 continue; 03270 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 03271 if (q == (Quantum *) NULL) 03272 { 03273 status=MagickFalse; 03274 continue; 03275 } 03276 pixel=zero; 03277 composite=zero; 03278 offset=GetStopColorOffset(gradient,0,y); 03279 if (gradient->type != RadialGradient) 03280 offset/=length; 03281 for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++) 03282 { 03283 GetPixelInfoPixel(image,q,&pixel); 03284 switch (gradient->spread) 03285 { 03286 case UndefinedSpread: 03287 case PadSpread: 03288 { 03289 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) || 03290 (y != (ssize_t) ceil(gradient_vector->y1-0.5))) 03291 { 03292 offset=GetStopColorOffset(gradient,x,y); 03293 if (gradient->type != RadialGradient) 03294 offset/=length; 03295 } 03296 for (i=0; i < (ssize_t) gradient->number_stops; i++) 03297 if (offset < gradient->stops[i].offset) 03298 break; 03299 if ((offset < 0.0) || (i == 0)) 03300 composite=gradient->stops[0].color; 03301 else 03302 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops)) 03303 composite=gradient->stops[gradient->number_stops-1].color; 03304 else 03305 { 03306 j=i; 03307 i--; 03308 alpha=(offset-gradient->stops[i].offset)/ 03309 (gradient->stops[j].offset-gradient->stops[i].offset); 03310 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha, 03311 &gradient->stops[j].color,alpha,&composite); 03312 } 03313 break; 03314 } 03315 case ReflectSpread: 03316 { 03317 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) || 03318 (y != (ssize_t) ceil(gradient_vector->y1-0.5))) 03319 { 03320 offset=GetStopColorOffset(gradient,x,y); 03321 if (gradient->type != RadialGradient) 03322 offset/=length; 03323 } 03324 if (offset < 0.0) 03325 offset=(-offset); 03326 if ((ssize_t) fmod(offset,2.0) == 0) 03327 offset=fmod(offset,1.0); 03328 else 03329 offset=1.0-fmod(offset,1.0); 03330 for (i=0; i < (ssize_t) gradient->number_stops; i++) 03331 if (offset < gradient->stops[i].offset) 03332 break; 03333 if (i == 0) 03334 composite=gradient->stops[0].color; 03335 else 03336 if (i == (ssize_t) gradient->number_stops) 03337 composite=gradient->stops[gradient->number_stops-1].color; 03338 else 03339 { 03340 j=i; 03341 i--; 03342 alpha=(offset-gradient->stops[i].offset)/ 03343 (gradient->stops[j].offset-gradient->stops[i].offset); 03344 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha, 03345 &gradient->stops[j].color,alpha,&composite); 03346 } 03347 break; 03348 } 03349 case RepeatSpread: 03350 { 03351 MagickBooleanType 03352 antialias; 03353 03354 MagickRealType 03355 repeat; 03356 03357 antialias=MagickFalse; 03358 repeat=0.0; 03359 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) || 03360 (y != (ssize_t) ceil(gradient_vector->y1-0.5))) 03361 { 03362 offset=GetStopColorOffset(gradient,x,y); 03363 if (gradient->type == LinearGradient) 03364 { 03365 repeat=fmod(offset,length); 03366 if (repeat < 0.0) 03367 repeat=length-fmod(-repeat,length); 03368 else 03369 repeat=fmod(offset,length); 03370 antialias=(repeat < length) && ((repeat+1.0) > length) ? 03371 MagickTrue : MagickFalse; 03372 offset=repeat/length; 03373 } 03374 else 03375 { 03376 repeat=fmod(offset,gradient->radius); 03377 if (repeat < 0.0) 03378 repeat=gradient->radius-fmod(-repeat,gradient->radius); 03379 else 03380 repeat=fmod(offset,gradient->radius); 03381 antialias=repeat+1.0 > gradient->radius ? MagickTrue : 03382 MagickFalse; 03383 offset=repeat/gradient->radius; 03384 } 03385 } 03386 for (i=0; i < (ssize_t) gradient->number_stops; i++) 03387 if (offset < gradient->stops[i].offset) 03388 break; 03389 if (i == 0) 03390 composite=gradient->stops[0].color; 03391 else 03392 if (i == (ssize_t) gradient->number_stops) 03393 composite=gradient->stops[gradient->number_stops-1].color; 03394 else 03395 { 03396 j=i; 03397 i--; 03398 alpha=(offset-gradient->stops[i].offset)/ 03399 (gradient->stops[j].offset-gradient->stops[i].offset); 03400 if (antialias != MagickFalse) 03401 { 03402 if (gradient->type == LinearGradient) 03403 alpha=length-repeat; 03404 else 03405 alpha=gradient->radius-repeat; 03406 i=0; 03407 j=(ssize_t) gradient->number_stops-1L; 03408 } 03409 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha, 03410 &gradient->stops[j].color,alpha,&composite); 03411 } 03412 break; 03413 } 03414 } 03415 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha, 03416 &pixel); 03417 SetPixelInfoPixel(image,&pixel,q); 03418 q+=GetPixelChannels(image); 03419 } 03420 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 03421 status=MagickFalse; 03422 } 03423 image_view=DestroyCacheView(image_view); 03424 return(status); 03425 } 03426 03427 /* 03428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03429 % % 03430 % % 03431 % % 03432 % D r a w P a t t e r n P a t h % 03433 % % 03434 % % 03435 % % 03436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03437 % 03438 % DrawPatternPath() draws a pattern. 03439 % 03440 % The format of the DrawPatternPath method is: 03441 % 03442 % MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info, 03443 % const char *name,Image **pattern,ExceptionInfo *exception) 03444 % 03445 % A description of each parameter follows: 03446 % 03447 % o image: the image. 03448 % 03449 % o draw_info: the draw info. 03450 % 03451 % o name: the pattern name. 03452 % 03453 % o image: the image. 03454 % 03455 % o exception: return any errors or warnings in this structure. 03456 % 03457 */ 03458 MagickExport MagickBooleanType DrawPatternPath(Image *image, 03459 const DrawInfo *draw_info,const char *name,Image **pattern, 03460 ExceptionInfo *exception) 03461 { 03462 char 03463 property[MaxTextExtent]; 03464 03465 const char 03466 *geometry, 03467 *path; 03468 03469 DrawInfo 03470 *clone_info; 03471 03472 ImageInfo 03473 *image_info; 03474 03475 MagickBooleanType 03476 status; 03477 03478 assert(image != (Image *) NULL); 03479 assert(image->signature == MagickSignature); 03480 if (image->debug != MagickFalse) 03481 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 03482 assert(draw_info != (const DrawInfo *) NULL); 03483 assert(name != (const char *) NULL); 03484 (void) FormatLocaleString(property,MaxTextExtent,"%s",name); 03485 path=GetImageArtifact(image,property); 03486 if (path == (const char *) NULL) 03487 return(MagickFalse); 03488 (void) FormatLocaleString(property,MaxTextExtent,"%s-geometry",name); 03489 geometry=GetImageArtifact(image,property); 03490 if (geometry == (const char *) NULL) 03491 return(MagickFalse); 03492 if ((*pattern) != (Image *) NULL) 03493 *pattern=DestroyImage(*pattern); 03494 image_info=AcquireImageInfo(); 03495 image_info->size=AcquireString(geometry); 03496 *pattern=AcquireImage(image_info,exception); 03497 image_info=DestroyImageInfo(image_info); 03498 (void) QueryColorCompliance("#00000000",AllCompliance, 03499 &(*pattern)->background_color,exception); 03500 (void) SetImageBackgroundColor(*pattern,exception); 03501 if (image->debug != MagickFalse) 03502 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 03503 "begin pattern-path %s %s",name,geometry); 03504 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 03505 clone_info->fill_pattern=NewImageList(); 03506 clone_info->stroke_pattern=NewImageList(); 03507 (void) CloneString(&clone_info->primitive,path); 03508 status=DrawImage(*pattern,clone_info,exception); 03509 clone_info=DestroyDrawInfo(clone_info); 03510 if (image->debug != MagickFalse) 03511 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path"); 03512 return(status); 03513 } 03514 03515 /* 03516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03517 % % 03518 % % 03519 % % 03520 + D r a w P o l y g o n P r i m i t i v e % 03521 % % 03522 % % 03523 % % 03524 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03525 % 03526 % DrawPolygonPrimitive() draws a polygon on the image. 03527 % 03528 % The format of the DrawPolygonPrimitive method is: 03529 % 03530 % MagickBooleanType DrawPolygonPrimitive(Image *image, 03531 % const DrawInfo *draw_info,const PrimitiveInfo *primitive_info, 03532 % ExceptionInfo *exception) 03533 % 03534 % A description of each parameter follows: 03535 % 03536 % o image: the image. 03537 % 03538 % o draw_info: the draw info. 03539 % 03540 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure. 03541 % 03542 % o exception: return any errors or warnings in this structure. 03543 % 03544 */ 03545 03546 static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info) 03547 { 03548 register ssize_t 03549 i; 03550 03551 assert(polygon_info != (PolygonInfo **) NULL); 03552 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++) 03553 if (polygon_info[i] != (PolygonInfo *) NULL) 03554 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]); 03555 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info); 03556 return(polygon_info); 03557 } 03558 03559 static PolygonInfo **AcquirePolygonThreadSet(const DrawInfo *draw_info, 03560 const PrimitiveInfo *primitive_info) 03561 { 03562 PathInfo 03563 *restrict path_info; 03564 03565 PolygonInfo 03566 **polygon_info; 03567 03568 register ssize_t 03569 i; 03570 03571 size_t 03572 number_threads; 03573 03574 number_threads=GetOpenMPMaximumThreads(); 03575 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads, 03576 sizeof(*polygon_info)); 03577 if (polygon_info == (PolygonInfo **) NULL) 03578 return((PolygonInfo **) NULL); 03579 (void) ResetMagickMemory(polygon_info,0,GetOpenMPMaximumThreads()* 03580 sizeof(*polygon_info)); 03581 path_info=ConvertPrimitiveToPath(draw_info,primitive_info); 03582 if (path_info == (PathInfo *) NULL) 03583 return(DestroyPolygonThreadSet(polygon_info)); 03584 for (i=0; i < (ssize_t) number_threads; i++) 03585 { 03586 polygon_info[i]=ConvertPathToPolygon(draw_info,path_info); 03587 if (polygon_info[i] == (PolygonInfo *) NULL) 03588 return(DestroyPolygonThreadSet(polygon_info)); 03589 } 03590 path_info=(PathInfo *) RelinquishMagickMemory(path_info); 03591 return(polygon_info); 03592 } 03593 03594 static MagickRealType GetFillAlpha(PolygonInfo *polygon_info, 03595 const MagickRealType mid,const MagickBooleanType fill, 03596 const FillRule fill_rule,const double x,const double y, 03597 MagickRealType *stroke_alpha) 03598 { 03599 MagickRealType 03600 alpha, 03601 beta, 03602 distance, 03603 subpath_alpha; 03604 03605 PointInfo 03606 delta; 03607 03608 register EdgeInfo 03609 *p; 03610 03611 register const PointInfo 03612 *q; 03613 03614 register ssize_t 03615 i; 03616 03617 ssize_t 03618 j, 03619 winding_number; 03620 03621 /* 03622 Compute fill & stroke opacity for this (x,y) point. 03623 */ 03624 *stroke_alpha=0.0; 03625 subpath_alpha=0.0; 03626 p=polygon_info->edges; 03627 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++) 03628 { 03629 if (y <= (p->bounds.y1-mid-0.5)) 03630 break; 03631 if (y > (p->bounds.y2+mid+0.5)) 03632 { 03633 (void) DestroyEdge(polygon_info,(size_t) j); 03634 continue; 03635 } 03636 if ((x <= (p->bounds.x1-mid-0.5)) || (x > (p->bounds.x2+mid+0.5))) 03637 continue; 03638 i=(ssize_t) MagickMax((double) p->highwater,1.0); 03639 for ( ; i < (ssize_t) p->number_points; i++) 03640 { 03641 if (y <= (p->points[i-1].y-mid-0.5)) 03642 break; 03643 if (y > (p->points[i].y+mid+0.5)) 03644 continue; 03645 if (p->scanline != y) 03646 { 03647 p->scanline=y; 03648 p->highwater=(size_t) i; 03649 } 03650 /* 03651 Compute distance between a point and an edge. 03652 */ 03653 q=p->points+i-1; 03654 delta.x=(q+1)->x-q->x; 03655 delta.y=(q+1)->y-q->y; 03656 beta=delta.x*(x-q->x)+delta.y*(y-q->y); 03657 if (beta < 0.0) 03658 { 03659 delta.x=x-q->x; 03660 delta.y=y-q->y; 03661 distance=delta.x*delta.x+delta.y*delta.y; 03662 } 03663 else 03664 { 03665 alpha=delta.x*delta.x+delta.y*delta.y; 03666 if (beta > alpha) 03667 { 03668 delta.x=x-(q+1)->x; 03669 delta.y=y-(q+1)->y; 03670 distance=delta.x*delta.x+delta.y*delta.y; 03671 } 03672 else 03673 { 03674 alpha=1.0/alpha; 03675 beta=delta.x*(y-q->y)-delta.y*(x-q->x); 03676 distance=alpha*beta*beta; 03677 } 03678 } 03679 /* 03680 Compute stroke & subpath opacity. 03681 */ 03682 beta=0.0; 03683 if (p->ghostline == MagickFalse) 03684 { 03685 alpha=mid+0.5; 03686 if ((*stroke_alpha < 1.0) && 03687 (distance <= ((alpha+0.25)*(alpha+0.25)))) 03688 { 03689 alpha=mid-0.5; 03690 if (distance <= ((alpha+0.25)*(alpha+0.25))) 03691 *stroke_alpha=1.0; 03692 else 03693 { 03694 beta=1.0; 03695 if (distance != 1.0) 03696 beta=sqrt((double) distance); 03697 alpha=beta-mid-0.5; 03698 if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25))) 03699 *stroke_alpha=(alpha-0.25)*(alpha-0.25); 03700 } 03701 } 03702 } 03703 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0)) 03704 continue; 03705 if (distance <= 0.0) 03706 { 03707 subpath_alpha=1.0; 03708 continue; 03709 } 03710 if (distance > 1.0) 03711 continue; 03712 if (beta == 0.0) 03713 { 03714 beta=1.0; 03715 if (distance != 1.0) 03716 beta=sqrt(distance); 03717 } 03718 alpha=beta-1.0; 03719 if (subpath_alpha < (alpha*alpha)) 03720 subpath_alpha=alpha*alpha; 03721 } 03722 } 03723 /* 03724 Compute fill opacity. 03725 */ 03726 if (fill == MagickFalse) 03727 return(0.0); 03728 if (subpath_alpha >= 1.0) 03729 return(1.0); 03730 /* 03731 Determine winding number. 03732 */ 03733 winding_number=0; 03734 p=polygon_info->edges; 03735 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++) 03736 { 03737 if (y <= p->bounds.y1) 03738 break; 03739 if ((y > p->bounds.y2) || (x <= p->bounds.x1)) 03740 continue; 03741 if (x > p->bounds.x2) 03742 { 03743 winding_number+=p->direction ? 1 : -1; 03744 continue; 03745 } 03746 i=(ssize_t) MagickMax((double) p->highwater,1.0); 03747 for ( ; i < (ssize_t) p->number_points; i++) 03748 if (y <= p->points[i].y) 03749 break; 03750 q=p->points+i-1; 03751 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x))) 03752 winding_number+=p->direction ? 1 : -1; 03753 } 03754 if (fill_rule != NonZeroRule) 03755 { 03756 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0) 03757 return(1.0); 03758 } 03759 else 03760 if (MagickAbsoluteValue(winding_number) != 0) 03761 return(1.0); 03762 return(subpath_alpha); 03763 } 03764 03765 static MagickBooleanType DrawPolygonPrimitive(Image *image, 03766 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info, 03767 ExceptionInfo *exception) 03768 { 03769 CacheView 03770 *image_view; 03771 03772 MagickBooleanType 03773 fill, 03774 status; 03775 03776 MagickRealType 03777 mid; 03778 03779 PolygonInfo 03780 **restrict polygon_info; 03781 03782 register EdgeInfo 03783 *p; 03784 03785 register ssize_t 03786 i; 03787 03788 SegmentInfo 03789 bounds; 03790 03791 ssize_t 03792 start, 03793 stop, 03794 y; 03795 03796 /* 03797 Compute bounding box. 03798 */ 03799 assert(image != (Image *) NULL); 03800 assert(image->signature == MagickSignature); 03801 if (image->debug != MagickFalse) 03802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 03803 assert(draw_info != (DrawInfo *) NULL); 03804 assert(draw_info->signature == MagickSignature); 03805 assert(primitive_info != (PrimitiveInfo *) NULL); 03806 if (primitive_info->coordinates == 0) 03807 return(MagickTrue); 03808 polygon_info=AcquirePolygonThreadSet(draw_info,primitive_info); 03809 if (polygon_info == (PolygonInfo **) NULL) 03810 return(MagickFalse); 03811 if (0) 03812 DrawBoundingRectangles(image,draw_info,polygon_info[0],exception); 03813 if (image->debug != MagickFalse) 03814 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon"); 03815 fill=(primitive_info->method == FillToBorderMethod) || 03816 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse; 03817 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0; 03818 bounds=polygon_info[0]->edges[0].bounds; 03819 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++) 03820 { 03821 p=polygon_info[0]->edges+i; 03822 if (p->bounds.x1 < bounds.x1) 03823 bounds.x1=p->bounds.x1; 03824 if (p->bounds.y1 < bounds.y1) 03825 bounds.y1=p->bounds.y1; 03826 if (p->bounds.x2 > bounds.x2) 03827 bounds.x2=p->bounds.x2; 03828 if (p->bounds.y2 > bounds.y2) 03829 bounds.y2=p->bounds.