43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/attribute.h"
46 #include "MagickCore/blob.h"
47 #include "MagickCore/blob-private.h"
48 #include "MagickCore/cache.h"
49 #include "MagickCore/cache-private.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/client.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colormap-private.h"
57 #include "MagickCore/colorspace.h"
58 #include "MagickCore/colorspace-private.h"
59 #include "MagickCore/composite.h"
60 #include "MagickCore/composite-private.h"
61 #include "MagickCore/constitute.h"
62 #include "MagickCore/draw.h"
63 #include "MagickCore/draw-private.h"
64 #include "MagickCore/effect.h"
65 #include "MagickCore/enhance.h"
66 #include "MagickCore/exception.h"
67 #include "MagickCore/exception-private.h"
68 #include "MagickCore/geometry.h"
69 #include "MagickCore/histogram.h"
70 #include "MagickCore/identify.h"
71 #include "MagickCore/image.h"
72 #include "MagickCore/image-private.h"
73 #include "MagickCore/list.h"
74 #include "MagickCore/log.h"
75 #include "MagickCore/memory_.h"
76 #include "MagickCore/magick.h"
77 #include "MagickCore/monitor.h"
78 #include "MagickCore/monitor-private.h"
79 #include "MagickCore/option.h"
80 #include "MagickCore/paint.h"
81 #include "MagickCore/pixel.h"
82 #include "MagickCore/pixel-accessor.h"
83 #include "MagickCore/property.h"
84 #include "MagickCore/quantize.h"
85 #include "MagickCore/quantum-private.h"
86 #include "MagickCore/random_.h"
87 #include "MagickCore/resource_.h"
88 #include "MagickCore/semaphore.h"
89 #include "MagickCore/segment.h"
90 #include "MagickCore/splay-tree.h"
91 #include "MagickCore/string_.h"
92 #include "MagickCore/string-private.h"
93 #include "MagickCore/thread-private.h"
94 #include "MagickCore/threshold.h"
95 #include "MagickCore/transform.h"
96 #include "MagickCore/utility.h"
136 static double GetEdgeBackgroundCensus(
const Image *image,
137 const CacheView *image_view,
const GravityType gravity,
const size_t width,
138 const size_t height,
const ssize_t x_offset,
const ssize_t y_offset,
171 case NorthWestGravity:
175 p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
178 case NorthEastGravity:
181 p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
185 case SouthEastGravity:
188 p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,
189 (ssize_t) image->rows-1,1,1,exception);
192 case SouthWestGravity:
195 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
200 if (p == (
const Quantum *) NULL)
202 GetPixelInfoPixel(image,p,&background);
203 artifact=GetImageArtifact(image,
"background");
204 if (artifact != (
const char *) NULL)
205 (void) QueryColorCompliance(artifact,AllCompliance,&background,exception);
206 artifact=GetImageArtifact(image,
"trim:background-color");
207 if (artifact != (
const char *) NULL)
208 (void) QueryColorCompliance(artifact,AllCompliance,&background,exception);
209 edge_geometry.width=width;
210 edge_geometry.height=height;
211 edge_geometry.x=x_offset;
212 edge_geometry.y=y_offset;
213 GravityAdjustGeometry(image->columns,image->rows,gravity,&edge_geometry);
214 edge_image=CropImage(image,&edge_geometry,exception);
215 if (edge_image == (
Image *) NULL)
218 edge_view=AcquireVirtualCacheView(edge_image,exception);
219 for (y=0; y < (ssize_t) edge_image->rows; y++)
224 p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
225 if (p == (
const Quantum *) NULL)
227 for (x=0; x < (ssize_t) edge_image->columns; x++)
229 GetPixelInfoPixel(edge_image,p,&pixel);
230 if (IsFuzzyEquivalencePixelInfo(&pixel,&background) == MagickFalse)
232 p+=GetPixelChannels(edge_image);
235 census/=((double) edge_image->columns*edge_image->rows);
236 edge_view=DestroyCacheView(edge_view);
237 edge_image=DestroyImage(edge_image);
241 static inline double GetMinEdgeBackgroundCensus(
const EdgeInfo *edge)
246 census=MagickMin(MagickMin(MagickMin(edge->left,edge->right),edge->top),
277 assert(image != (
Image *) NULL);
278 assert(image->signature == MagickCoreSignature);
279 if (IsEventLogging() != MagickFalse)
280 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
281 SetGeometry(image,&bounds);
282 edge_image=CloneImage(image,0,0,MagickTrue,exception);
283 if (edge_image == (
Image *) NULL)
285 (void) ParseAbsoluteGeometry(
"0x0+0+0",&edge_image->page);
286 (void) memset(&vertex,0,
sizeof(vertex));
287 edge_view=AcquireVirtualCacheView(edge_image,exception);
288 edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,WestGravity,
290 edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,EastGravity,
292 edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,NorthGravity,
294 edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,SouthGravity,
296 percent_background=1.0;
297 artifact=GetImageArtifact(edge_image,
"trim:percent-background");
298 if (artifact != (
const char *) NULL)
299 percent_background=StringToDouble(artifact,(
char **) NULL)/100.0;
300 percent_background=MagickMin(MagickMax(1.0-percent_background,MagickEpsilon),
302 background_census=GetMinEdgeBackgroundCensus(&edge);
303 for ( ; background_census < percent_background;
304 background_census=GetMinEdgeBackgroundCensus(&edge))
306 if ((bounds.width == 0) || (bounds.height == 0))
308 if (fabs(edge.left-background_census) < MagickEpsilon)
315 edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,
316 NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t)
317 vertex.top,exception);
318 edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,
319 NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
320 vertex.top,exception);
321 edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,
322 SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
323 vertex.bottom,exception);
326 if (fabs(edge.right-background_census) < MagickEpsilon)
333 edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,
334 NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t)
335 vertex.top,exception);
336 edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,
337 NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
338 vertex.top,exception);
339 edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,
340 SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
341 vertex.bottom,exception);
344 if (fabs(edge.top-background_census) < MagickEpsilon)
351 edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,
352 NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t)
353 vertex.top,exception);
354 edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,
355 NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t)
356 vertex.top,exception);
357 edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,
358 NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
359 vertex.top,exception);
362 if (fabs(edge.bottom-background_census) < MagickEpsilon)
369 edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,
370 NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t)
371 vertex.top,exception);
372 edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,
373 NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t)
374 vertex.top,exception);
375 edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,
376 SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
377 vertex.bottom,exception);
381 edge_view=DestroyCacheView(edge_view);
382 edge_image=DestroyImage(edge_image);
383 bounds.x=(ssize_t) vertex.left;
384 bounds.y=(ssize_t) vertex.top;
385 if ((bounds.width == 0) || (bounds.height == 0))
386 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
387 "GeometryDoesNotContainImage",
"`%s'",image->filename);
416 assert(image != (
Image *) NULL);
417 assert(image->signature == MagickCoreSignature);
418 if (IsEventLogging() != MagickFalse)
419 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
420 artifact=GetImageArtifact(image,
"trim:percent-background");
421 if (artifact != (
const char *) NULL)
422 return(GetEdgeBoundingBox(image,exception));
423 artifact=GetImageArtifact(image,
"trim:edges");
424 if (artifact == (
const char *) NULL)
426 bounds.width=(size_t) (image->columns == 1 ? 1 : 0);
427 bounds.height=(size_t) (image->rows == 1 ? 1 : 0);
428 bounds.x=(ssize_t) image->columns;
429 bounds.y=(ssize_t) image->rows;
438 bounds.width=(size_t) image->columns;
439 bounds.height=(
size_t) image->rows;
442 edges=AcquireString(artifact);
444 while ((q=StringToken(
",",&r)) != (
char *) NULL)
446 if (LocaleCompare(q,
"north") == 0)
447 bounds.y=(ssize_t) image->rows;
448 if (LocaleCompare(q,
"east") == 0)
450 if (LocaleCompare(q,
"south") == 0)
452 if (LocaleCompare(q,
"west") == 0)
453 bounds.x=(ssize_t) image->columns;
455 edges=DestroyString(edges);
457 GetPixelInfo(image,&target[0]);
458 image_view=AcquireVirtualCacheView(image,exception);
459 p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
460 if (p == (
const Quantum *) NULL)
462 image_view=DestroyCacheView(image_view);
465 GetPixelInfoPixel(image,p,&target[0]);
466 GetPixelInfo(image,&target[1]);
467 p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
469 if (p != (
const Quantum *) NULL)
470 GetPixelInfoPixel(image,p,&target[1]);
471 GetPixelInfo(image,&target[2]);
472 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
474 if (p != (
const Quantum *) NULL)
475 GetPixelInfoPixel(image,p,&target[2]);
476 p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,(ssize_t)
477 image->rows-1,1,1,exception);
478 if (p != (
const Quantum *) NULL)
479 GetPixelInfoPixel(image,p,&target[3]);
481 GetPixelInfo(image,&zero);
482 #if defined(MAGICKCORE_OPENMP_SUPPORT)
483 #pragma omp parallel for schedule(static) shared(status) \
484 magick_number_threads(image,image,image->rows,1)
486 for (y=0; y < (ssize_t) image->rows; y++)
500 if (status == MagickFalse)
502 #if defined(MAGICKCORE_OPENMP_SUPPORT)
503 # pragma omp critical (MagickCore_GetImageBoundingBox)
506 q=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
507 if (q == (
const Quantum *) NULL)
513 for (x=0; x < (ssize_t) image->columns; x++)
515 GetPixelInfoPixel(image,q,&pixel);
516 if ((x < bounding_box.x) &&
517 (IsFuzzyEquivalencePixelInfo(&pixel,&target[0]) == MagickFalse))
519 if ((x > (ssize_t) bounding_box.width) &&
520 (IsFuzzyEquivalencePixelInfo(&pixel,&target[1]) == MagickFalse))
521 bounding_box.width=(size_t) x;
522 if ((y < bounding_box.y) &&
523 (IsFuzzyEquivalencePixelInfo(&pixel,&target[0]) == MagickFalse))
525 if ((y > (ssize_t) bounding_box.height) &&
526 (IsFuzzyEquivalencePixelInfo(&pixel,&target[2]) == MagickFalse))
527 bounding_box.height=(size_t) y;
528 if ((x < (ssize_t) bounding_box.width) &&
529 (y > (ssize_t) bounding_box.height) &&
530 (IsFuzzyEquivalencePixelInfo(&pixel,&target[3]) == MagickFalse))
532 bounding_box.width=(size_t) x;
533 bounding_box.height=(size_t) y;
535 q+=GetPixelChannels(image);
537 #if defined(MAGICKCORE_OPENMP_SUPPORT)
538 # pragma omp critical (MagickCore_GetImageBoundingBox)
541 if (bounding_box.x < bounds.x)
542 bounds.x=bounding_box.x;
543 if (bounding_box.y < bounds.y)
544 bounds.y=bounding_box.y;
545 if (bounding_box.width > bounds.width)
546 bounds.width=bounding_box.width;
547 if (bounding_box.height > bounds.height)
548 bounds.height=bounding_box.height;
551 image_view=DestroyCacheView(image_view);
552 if ((bounds.width == 0) || (bounds.height == 0))
553 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
554 "GeometryDoesNotContainImage",
"`%s'",image->filename);
557 bounds.width-=(bounds.x-1);
558 bounds.height-=(bounds.y-1);
596 return((b->x-a->x)*(c->y-a->y)-(b->y-a->y)*(c->x-a->x));
619 memset(&edge_background,0,
sizeof(edge_background));
620 artifact=GetImageArtifact(image,
"convex-hull:background-color");
621 if (artifact == (
const char *) NULL)
622 artifact=GetImageArtifact(image,
"background");
623 #if defined(MAGICKCORE_OPENMP_SUPPORT)
624 #pragma omp parallel for schedule(static)
626 for (i=0; i < 4; i++)
650 (void) memset(&edge_geometry,0,
sizeof(edge_geometry));
656 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
659 edge_geometry.width=1;
660 edge_geometry.height=0;
665 p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
668 edge_geometry.width=1;
669 edge_geometry.height=0;
674 p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
675 gravity=NorthGravity;
676 edge_geometry.width=0;
677 edge_geometry.height=1;
682 p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,
683 (ssize_t) image->rows-1,1,1,exception);
684 gravity=SouthGravity;
685 edge_geometry.width=0;
686 edge_geometry.height=1;
690 GetPixelInfoPixel(image,p,background+i);
691 if (artifact != (
const char *) NULL)
692 (void) QueryColorCompliance(artifact,AllCompliance,background+i,
694 GravityAdjustGeometry(image->columns,image->rows,gravity,&edge_geometry);
695 edge_image=CropImage(image,&edge_geometry,exception);
696 if (edge_image == (
Image *) NULL)
698 edge_view=AcquireVirtualCacheView(edge_image,exception);
699 for (y=0; y < (ssize_t) edge_image->rows; y++)
704 p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,
706 if (p == (
const Quantum *) NULL)
708 for (x=0; x < (ssize_t) edge_image->columns; x++)
710 GetPixelInfoPixel(edge_image,p,&pixel);
711 if (IsFuzzyEquivalencePixelInfo(&pixel,background+i) == MagickFalse)
713 p+=GetPixelChannels(edge_image);
716 edge_view=DestroyCacheView(edge_view);
717 edge_image=DestroyImage(edge_image);
720 for (i=0; i < 4; i++)
721 if (census[i] > edge_census)
723 edge_background=background[i];
724 edge_census=census[i];
726 return(edge_background);
729 void TraceConvexHull(
PointInfo *vertices,
size_t number_vertices,
730 PointInfo ***monotone_chain,
size_t *chain_length)
745 chain=(*monotone_chain);
747 for (i=0; i < (ssize_t) number_vertices; i++)
750 (LexicographicalOrder(chain[n-2],chain[n-1],&vertices[i]) <= 0.0))
752 chain[n++]=(&vertices[i]);
755 for (i=(ssize_t) number_vertices-2; i >= 0; i--)
757 while ((n >= demark) &&
758 (LexicographicalOrder(chain[n-2],chain[n-1],&vertices[i]) <= 0.0))
760 chain[n++]=(&vertices[i]);
795 assert(image != (
Image *) NULL);
796 assert(image->signature == MagickCoreSignature);
797 if (IsEventLogging() != MagickFalse)
798 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
800 vertices_info=AcquireVirtualMemory(image->columns,image->rows*
802 monotone_info=AcquireVirtualMemory(2*image->columns,2*
803 image->rows*
sizeof(*monotone_chain));
808 monotone_info=(
MemoryInfo *) RelinquishVirtualMemory(monotone_info);
810 vertices_info=RelinquishVirtualMemory(vertices_info);
813 vertices=(
PointInfo *) GetVirtualMemoryBlob(vertices_info);
814 monotone_chain=(
PointInfo **) GetVirtualMemoryBlob(monotone_info);
815 image_view=AcquireVirtualCacheView(image,exception);
816 background=GetEdgeBackgroundColor(image,image_view,exception);
819 for (y=0; y < (ssize_t) image->rows; y++)
827 if (status == MagickFalse)
829 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
830 if (p == (
const Quantum *) NULL)
835 for (x=0; x < (ssize_t) image->columns; x++)
840 GetPixelInfoPixel(image,p,&pixel);
841 if (IsFuzzyEquivalencePixelInfo(&pixel,&background) == MagickFalse)
843 vertices[n].x=(double) x;
844 vertices[n].y=(double) y;
847 p+=GetPixelChannels(image);
850 image_view=DestroyCacheView(image_view);
854 TraceConvexHull(vertices,n,&monotone_chain,number_vertices);
855 convex_hull=(
PointInfo *) AcquireQuantumMemory(*number_vertices,
856 sizeof(*convex_hull));
858 for (n=0; n < *number_vertices; n++)
859 convex_hull[n]=(*monotone_chain[n]);
860 monotone_info=RelinquishVirtualMemory(monotone_info);
861 vertices_info=RelinquishVirtualMemory(vertices_info);
911 assert(image != (
Image *) NULL);
912 assert(image->signature == MagickCoreSignature);
913 if (IsEventLogging() != MagickFalse)
914 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
915 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
916 current_depth=(
size_t *) AcquireQuantumMemory(number_threads,
917 sizeof(*current_depth));
918 if (current_depth == (
size_t *) NULL)
919 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
921 for (i=0; i < (ssize_t) number_threads; i++)
923 if ((image->storage_class == PseudoClass) &&
924 (image->alpha_trait == UndefinedPixelTrait))
926 for (i=0; i < (ssize_t) image->colors; i++)
929 id = GetOpenMPThreadId();
931 while (current_depth[
id] < MAGICKCORE_QUANTUM_DEPTH)
940 range=GetQuantumRange(current_depth[
id]);
941 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
942 if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].red),range) == MagickFalse)
944 if ((atDepth != MagickFalse) &&
945 (GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
946 if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].green),range) == MagickFalse)
948 if ((atDepth != MagickFalse) &&
949 (GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
950 if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].blue),range) == MagickFalse)
952 if ((atDepth != MagickFalse))
957 depth=current_depth[0];
958 for (i=1; i < (ssize_t) number_threads; i++)
959 if (depth < current_depth[i])
960 depth=current_depth[i];
961 current_depth=(
size_t *) RelinquishMagickMemory(current_depth);
964 image_view=AcquireVirtualCacheView(image,exception);
965 #if !defined(MAGICKCORE_HDRI_SUPPORT)
966 DisableMSCWarning(4127)
967 if ((1UL*QuantumRange) <= MaxMap)
976 depth_map=(
size_t *) AcquireQuantumMemory(MaxMap+1,
sizeof(*depth_map));
977 if (depth_map == (
size_t *) NULL)
978 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
979 for (i=0; i <= (ssize_t) MaxMap; i++)
981 for (depth=1; depth < (size_t) MAGICKCORE_QUANTUM_DEPTH; depth++)
989 range=GetQuantumRange(depth);
991 if (pixel == ScaleAnyToQuantum(ScaleQuantumToAny(pixel,range),range))
996 #if defined(MAGICKCORE_OPENMP_SUPPORT)
997 #pragma omp parallel for schedule(static) shared(status) \
998 magick_number_threads(image,image,image->rows,1)
1000 for (y=0; y < (ssize_t) image->rows; y++)
1003 id = GetOpenMPThreadId();
1011 if (status == MagickFalse)
1013 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1014 if (p == (
const Quantum *) NULL)
1016 for (x=0; x < (ssize_t) image->columns; x++)
1021 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1023 PixelChannel channel = GetPixelChannelChannel(image,j);
1024 PixelTrait traits = GetPixelChannelTraits(image,channel);
1025 if ((traits & UpdatePixelTrait) == 0)
1027 if (depth_map[ScaleQuantumToMap(p[j])] > current_depth[
id])
1028 current_depth[id]=depth_map[ScaleQuantumToMap(p[j])];
1030 p+=GetPixelChannels(image);
1032 if (current_depth[
id] == MAGICKCORE_QUANTUM_DEPTH)
1035 image_view=DestroyCacheView(image_view);
1036 depth=current_depth[0];
1037 for (i=1; i < (ssize_t) number_threads; i++)
1038 if (depth < current_depth[i])
1039 depth=current_depth[i];
1040 depth_map=(
size_t *) RelinquishMagickMemory(depth_map);
1041 current_depth=(
size_t *) RelinquishMagickMemory(current_depth);
1048 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1049 #pragma omp parallel for schedule(static) shared(status) \
1050 magick_number_threads(image,image,image->rows,1)
1052 for (y=0; y < (ssize_t) image->rows; y++)
1055 id = GetOpenMPThreadId();
1063 if (status == MagickFalse)
1065 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1066 if (p == (
const Quantum *) NULL)
1068 for (x=0; x < (ssize_t) image->columns; x++)
1073 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1081 channel=GetPixelChannelChannel(image,j);
1082 traits=GetPixelChannelTraits(image,channel);
1083 if ((traits & UpdatePixelTrait) == 0)
1085 while (current_depth[
id] < MAGICKCORE_QUANTUM_DEPTH)
1090 range=GetQuantumRange(current_depth[
id]);
1091 if (p[j] == ScaleAnyToQuantum(ScaleQuantumToAny(p[j],range),range))
1093 current_depth[id]++;
1096 p+=GetPixelChannels(image);
1098 if (current_depth[
id] == MAGICKCORE_QUANTUM_DEPTH)
1101 image_view=DestroyCacheView(image_view);
1102 depth=current_depth[0];
1103 for (i=1; i < (ssize_t) number_threads; i++)
1104 if (depth < current_depth[i])
1105 depth=current_depth[i];
1106 current_depth=(
size_t *) RelinquishMagickMemory(current_depth);
1161 return(RadiansToDegrees(atan2(q->y-p->y,q->x-p->x)));
1169 distance=hypot(p->x-q->x,p->y-q->y);
1170 return(distance*distance);
1181 distance=getDistance(p,q);
1182 if (distance < MagickEpsilon)
1184 return((q->x-p->x)*(v->x-p->x)+(v->y-p->y)*(q->y-p->y))/sqrt(distance);
1195 distance=getDistance(p,q);
1196 if (distance < MagickEpsilon)
1198 return((q->x-p->x)*(v->y-p->y)-(v->x-p->x)*(q->y-p->y))/sqrt(distance);
1223 number_hull_vertices;
1228 assert(image != (
Image *) NULL);
1229 assert(image->signature == MagickCoreSignature);
1230 if (IsEventLogging() != MagickFalse)
1231 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1233 vertices=GetImageConvexHull(image,&number_hull_vertices,exception);
1237 bounding_box=(
PointInfo *) AcquireQuantumMemory(*number_vertices,
1238 sizeof(*bounding_box));
1241 vertices=(
PointInfo *) RelinquishMagickMemory(vertices);
1244 caliper_info.area=2.0*image->columns*image->rows;
1245 caliper_info.width=(double) image->columns+image->rows;
1246 caliper_info.height=0.0;
1247 caliper_info.projection=0.0;
1248 caliper_info.p=(-1);
1249 caliper_info.q=(-1);
1250 caliper_info.v=(-1);
1251 for (i=0; i < (ssize_t) number_hull_vertices; i++)
1255 max_projection = 0.0,
1256 min_diameter = -1.0,
1257 min_projection = 0.0;
1268 for (j=0; j < (ssize_t) number_hull_vertices; j++)
1270 diameter=fabs(getFeretDiameter(&vertices[i],
1271 &vertices[(i+1) % number_hull_vertices],&vertices[j]));
1272 if (min_diameter < diameter)
1274 min_diameter=diameter;
1276 q=(i+1) % number_hull_vertices;
1280 for (k=0; k < (ssize_t) number_hull_vertices; k++)
1288 projection=getProjection(&vertices[p],&vertices[q],&vertices[k]);
1289 min_projection=MagickMin(min_projection,projection);
1290 max_projection=MagickMax(max_projection,projection);
1292 area=min_diameter*(max_projection-min_projection);
1293 if (caliper_info.area > area)
1295 caliper_info.area=area;
1296 caliper_info.width=min_diameter;
1297 caliper_info.height=max_projection-min_projection;
1298 caliper_info.projection=max_projection;
1307 diameter=getFeretDiameter(&vertices[caliper_info.p],
1308 &vertices[caliper_info.q],&vertices[caliper_info.v]);
1309 angle=atan2(vertices[caliper_info.q].y-vertices[caliper_info.p].y,
1310 vertices[caliper_info.q].x-vertices[caliper_info.p].x);
1311 bounding_box[0].x=vertices[caliper_info.p].x+cos(angle)*
1312 caliper_info.projection;
1313 bounding_box[0].y=vertices[caliper_info.p].y+sin(angle)*
1314 caliper_info.projection;
1315 bounding_box[1].x=floor(bounding_box[0].x+cos(angle+MagickPI/2.0)*diameter+
1317 bounding_box[1].y=floor(bounding_box[0].y+sin(angle+MagickPI/2.0)*diameter+
1319 bounding_box[2].x=floor(bounding_box[1].x+cos(angle)*(-caliper_info.height)+
1321 bounding_box[2].y=floor(bounding_box[1].y+sin(angle)*(-caliper_info.height)+
1323 bounding_box[3].x=floor(bounding_box[2].x+cos(angle+MagickPI/2.0)*(-diameter)+
1325 bounding_box[3].y=floor(bounding_box[2].y+sin(angle+MagickPI/2.0)*(-diameter)+
1330 (void) FormatImageProperty(image,
"minimum-bounding-box:area",
"%.*g",
1331 GetMagickPrecision(),caliper_info.area);
1332 (void) FormatImageProperty(image,
"minimum-bounding-box:width",
"%.*g",
1333 GetMagickPrecision(),caliper_info.width);
1334 (void) FormatImageProperty(image,
"minimum-bounding-box:height",
"%.*g",
1335 GetMagickPrecision(),caliper_info.height);
1336 (void) FormatImageProperty(image,
"minimum-bounding-box:_p",
"%.*g,%.*g",
1337 GetMagickPrecision(),vertices[caliper_info.p].x,
1338 GetMagickPrecision(),vertices[caliper_info.p].y);
1339 (void) FormatImageProperty(image,
"minimum-bounding-box:_q",
"%.*g,%.*g",
1340 GetMagickPrecision(),vertices[caliper_info.q].x,
1341 GetMagickPrecision(),vertices[caliper_info.q].y);
1342 (void) FormatImageProperty(image,
"minimum-bounding-box:_v",
"%.*g,%.*g",
1343 GetMagickPrecision(),vertices[caliper_info.v].x,
1344 GetMagickPrecision(),vertices[caliper_info.v].y);
1348 distance=hypot(bounding_box[0].x,bounding_box[0].y);
1349 angle=getAngle(&bounding_box[0],&bounding_box[1]);
1350 for (i=1; i < 4; i++)
1352 double d = hypot(bounding_box[i].x,bounding_box[i].y);
1356 angle=getAngle(&bounding_box[i],&bounding_box[(i+1) % 4]);
1359 artifact=GetImageArtifact(image,
"minimum-bounding-box:orientation");
1360 if (artifact != (
const char *) NULL)
1374 point=bounding_box[0];
1375 for (i=1; i < 4; i++)
1377 if (bounding_box[i].x < point.x)
1378 point.x=bounding_box[i].x;
1379 if (bounding_box[i].y < point.y)
1380 point.y=bounding_box[i].y;
1382 for (i=0; i < 4; i++)
1384 bounding_box[i].x-=point.x;
1385 bounding_box[i].y-=point.y;
1387 for (i=0; i < 4; i++)
1394 delta.x=bounding_box[(i+1) % 4].x-bounding_box[i].x;
1395 delta.y=bounding_box[(i+1) % 4].y-bounding_box[i].y;
1396 slope=delta.y*PerceptibleReciprocal(delta.x);
1397 intercept=bounding_box[(i+1) % 4].y-slope*bounding_box[i].x;
1398 d=fabs((slope*bounding_box[i].x-bounding_box[i].y+intercept)*
1399 PerceptibleReciprocal(sqrt(slope*slope+1.0)));
1400 if ((i == 0) || (d < distance))
1406 angle=RadiansToDegrees(atan(point.y*PerceptibleReciprocal(point.x)));
1407 length=hypot(point.x,point.y);
1408 p_length=fabs((
double) MagickMax(caliper_info.width,caliper_info.height)-
1410 q_length=fabs(length-(
double) MagickMin(caliper_info.width,
1411 caliper_info.height));
1412 if (LocaleCompare(artifact,
"landscape") == 0)
1414 if (p_length > q_length)
1415 angle+=(angle < 0.0) ? 90.0 : -90.0;
1418 if (LocaleCompare(artifact,
"portrait") == 0)
1420 if (p_length < q_length)
1421 angle+=(angle >= 0.0) ? 90.0 : -90.0;
1424 (void) FormatImageProperty(image,
"minimum-bounding-box:angle",
"%.*g",
1425 GetMagickPrecision(),angle);
1426 (void) FormatImageProperty(image,
"minimum-bounding-box:unrotate",
"%.*g",
1427 GetMagickPrecision(),-angle);
1428 vertices=(
PointInfo *) RelinquishMagickMemory(vertices);
1429 return(bounding_box);
1459 MagickExport
size_t GetImageQuantumDepth(
const Image *image,
1460 const MagickBooleanType constrain)
1477 if (constrain != MagickFalse)
1478 depth=(size_t) MagickMin((
double) depth,(double) MAGICKCORE_QUANTUM_DEPTH);
1508 MagickExport ImageType GetImageType(
const Image *image)
1510 assert(image != (
Image *) NULL);
1511 assert(image->signature == MagickCoreSignature);
1512 if (image->colorspace == CMYKColorspace)
1514 if (image->alpha_trait == UndefinedPixelTrait)
1515 return(ColorSeparationType);
1516 return(ColorSeparationAlphaType);
1518 if (IsImageMonochrome(image) != MagickFalse)
1519 return(BilevelType);
1520 if (IsImageGray(image) != MagickFalse)
1522 if (image->alpha_trait != UndefinedPixelTrait)
1523 return(GrayscaleAlphaType);
1524 return(GrayscaleType);
1526 if (IsPaletteImage(image) != MagickFalse)
1528 if (image->alpha_trait != UndefinedPixelTrait)
1529 return(PaletteAlphaType);
1530 return(PaletteType);
1532 if (image->alpha_trait != UndefinedPixelTrait)
1533 return(TrueColorAlphaType);
1534 return(TrueColorType);
1563 MagickExport ImageType IdentifyImageGray(
const Image *image,
1581 assert(image != (
Image *) NULL);
1582 assert(image->signature == MagickCoreSignature);
1583 if (IsEventLogging() != MagickFalse)
1584 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1585 if (IsImageGray(image) != MagickFalse)
1586 return(image->type);
1587 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1588 return(UndefinedType);
1590 image_view=AcquireVirtualCacheView(image,exception);
1591 for (y=0; y < (ssize_t) image->rows; y++)
1593 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1594 if (p == (
const Quantum *) NULL)
1596 for (x=0; x < (ssize_t) image->columns; x++)
1598 if (IsPixelGray(image,p) == MagickFalse)
1603 if ((type == BilevelType) &&
1604 (IsPixelMonochrome(image,p) == MagickFalse))
1606 p+=GetPixelChannels(image);
1608 if (type == UndefinedType)
1611 image_view=DestroyCacheView(image_view);
1612 if ((type == GrayscaleType) && (image->alpha_trait != UndefinedPixelTrait))
1613 type=GrayscaleAlphaType;
1644 MagickExport MagickBooleanType IdentifyImageMonochrome(
const Image *image,
1662 assert(image != (
Image *) NULL);
1663 assert(image->signature == MagickCoreSignature);
1664 if (IsEventLogging() != MagickFalse)
1665 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1666 if (image->type == BilevelType)
1668 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1669 return(MagickFalse);
1671 image_view=AcquireVirtualCacheView(image,exception);
1672 for (y=0; y < (ssize_t) image->rows; y++)
1674 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1675 if (p == (
const Quantum *) NULL)
1677 for (x=0; x < (ssize_t) image->columns; x++)
1679 if (IsPixelMonochrome(image,p) == MagickFalse)
1681 bilevel=MagickFalse;
1684 p+=GetPixelChannels(image);
1686 if (bilevel == MagickFalse)
1689 image_view=DestroyCacheView(image_view);
1725 MagickExport ImageType IdentifyImageType(
const Image *image,
1731 assert(image != (
Image *) NULL);
1732 assert(image->signature == MagickCoreSignature);
1733 if (IsEventLogging() != MagickFalse)
1734 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1735 if (image->colorspace == CMYKColorspace)
1737 if (image->alpha_trait == UndefinedPixelTrait)
1738 return(ColorSeparationType);
1739 return(ColorSeparationAlphaType);
1741 type=IdentifyImageGray(image,exception);
1742 if (IsGrayImageType(type))
1744 if (IdentifyPaletteImage(image,exception) != MagickFalse)
1746 if (image->alpha_trait != UndefinedPixelTrait)
1747 return(PaletteAlphaType);
1748 return(PaletteType);
1750 if (image->alpha_trait != UndefinedPixelTrait)
1751 return(TrueColorAlphaType);
1752 return(TrueColorType);
1778 MagickExport MagickBooleanType IsImageGray(
const Image *image)
1780 assert(image != (
Image *) NULL);
1781 assert(image->signature == MagickCoreSignature);
1782 if (IsGrayImageType(image->type))
1784 return(MagickFalse);
1809 MagickExport MagickBooleanType IsImageMonochrome(
const Image *image)
1811 assert(image != (
Image *) NULL);
1812 assert(image->signature == MagickCoreSignature);
1813 if (image->type == BilevelType)
1815 return(MagickFalse);
1846 MagickExport MagickBooleanType IsImageOpaque(
const Image *image,
1864 assert(image != (
Image *) NULL);
1865 assert(image->signature == MagickCoreSignature);
1866 if (IsEventLogging() != MagickFalse)
1867 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1868 if (image->alpha_trait == UndefinedPixelTrait)
1870 image_view=AcquireVirtualCacheView(image,exception);
1871 for (y=0; y < (ssize_t) image->rows; y++)
1873 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1874 if (p == (
const Quantum *) NULL)
1876 for (x=0; x < (ssize_t) image->columns; x++)
1878 if (GetPixelAlpha(image,p) != OpaqueAlpha)
1880 p+=GetPixelChannels(image);
1882 if (x < (ssize_t) image->columns)
1885 image_view=DestroyCacheView(image_view);
1886 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
1918 MagickExport MagickBooleanType SetImageDepth(
Image *image,
1933 assert(image != (
Image *) NULL);
1934 if (IsEventLogging() != MagickFalse)
1935 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
1936 assert(image->signature == MagickCoreSignature);
1937 if (depth >= MAGICKCORE_QUANTUM_DEPTH)
1942 range=GetQuantumRange(depth);
1943 if (image->storage_class == PseudoClass)
1948 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1949 #pragma omp parallel for schedule(static) shared(status) \
1950 magick_number_threads(image,image,image->colors,1)
1952 for (i=0; i < (ssize_t) image->colors; i++)
1954 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1955 image->colormap[i].red=(
double) ScaleAnyToQuantum(ScaleQuantumToAny(
1956 ClampPixel(image->colormap[i].red),range),range);
1957 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1958 image->colormap[i].green=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1959 ClampPixel(image->colormap[i].green),range),range);
1960 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1961 image->colormap[i].blue=(
double) ScaleAnyToQuantum(ScaleQuantumToAny(
1962 ClampPixel(image->colormap[i].blue),range),range);
1963 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1964 image->colormap[i].alpha=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1965 ClampPixel(image->colormap[i].alpha),range),range);
1969 image_view=AcquireAuthenticCacheView(image,exception);
1970 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1971 DisableMSCWarning(4127)
1972 if ((1UL*QuantumRange) <= MaxMap)
1984 depth_map=(Quantum *) AcquireQuantumMemory(MaxMap+1,
sizeof(*depth_map));
1985 if (depth_map == (Quantum *) NULL)
1986 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
1987 for (i=0; i <= (ssize_t) MaxMap; i++)
1988 depth_map[i]=ScaleAnyToQuantum(ScaleQuantumToAny((Quantum) i,range),
1990 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1991 #pragma omp parallel for schedule(static) shared(status) \
1992 magick_number_threads(image,image,image->rows,1)
1994 for (y=0; y < (ssize_t) image->rows; y++)
2002 if (status == MagickFalse)
2004 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2006 if (q == (Quantum *) NULL)
2011 for (x=0; x < (ssize_t) image->columns; x++)
2016 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2024 channel=GetPixelChannelChannel(image,j);
2025 traits=GetPixelChannelTraits(image,channel);
2026 if ((traits & UpdatePixelTrait) == 0)
2028 q[j]=depth_map[ScaleQuantumToMap(q[j])];
2030 q+=GetPixelChannels(image);
2032 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2038 image_view=DestroyCacheView(image_view);
2039 depth_map=(Quantum *) RelinquishMagickMemory(depth_map);
2040 if (status != MagickFalse)
2048 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2049 #pragma omp parallel for schedule(static) shared(status) \
2050 magick_number_threads(image,image,image->rows,1)
2052 for (y=0; y < (ssize_t) image->rows; y++)
2060 if (status == MagickFalse)
2062 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2063 if (q == (Quantum *) NULL)
2068 for (x=0; x < (ssize_t) image->columns; x++)
2073 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2081 channel=GetPixelChannelChannel(image,i);
2082 traits=GetPixelChannelTraits(image,channel);
2083 if ((traits & UpdatePixelTrait) == 0)
2085 q[i]=ScaleAnyToQuantum(ScaleQuantumToAny(ClampPixel((MagickRealType)
2086 q[i]),range),range);
2088 q+=GetPixelChannels(image);
2090 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2096 image_view=DestroyCacheView(image_view);
2097 if (status != MagickFalse)
2134 MagickExport MagickBooleanType SetImageType(
Image *image,
const ImageType type,
2149 assert(image != (
Image *) NULL);
2150 if (IsEventLogging() != MagickFalse)
2151 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
2152 assert(image->signature == MagickCoreSignature);
2154 image_info=AcquireImageInfo();
2155 image_info->dither=image->dither;
2156 artifact=GetImageArtifact(image,
"dither");
2157 if (artifact != (
const char *) NULL)
2158 (void) SetImageOption(image_info,
"dither",artifact);
2163 status=TransformImageColorspace(image,GRAYColorspace,exception);
2164 (void) NormalizeImage(image,exception);
2165 (void) BilevelImage(image,(
double) QuantumRange/2.0,exception);
2166 quantize_info=AcquireQuantizeInfo(image_info);
2167 quantize_info->number_colors=2;
2168 quantize_info->colorspace=GRAYColorspace;
2169 status=QuantizeImage(quantize_info,image,exception);
2170 quantize_info=DestroyQuantizeInfo(quantize_info);
2171 image->alpha_trait=UndefinedPixelTrait;
2176 status=TransformImageColorspace(image,GRAYColorspace,exception);
2177 image->alpha_trait=UndefinedPixelTrait;
2180 case GrayscaleAlphaType:
2182 status=TransformImageColorspace(image,GRAYColorspace,exception);
2183 if (image->alpha_trait == UndefinedPixelTrait)
2184 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2189 status=TransformImageColorspace(image,sRGBColorspace,exception);
2190 if ((image->storage_class == DirectClass) || (image->colors > 256))
2192 quantize_info=AcquireQuantizeInfo(image_info);
2193 quantize_info->number_colors=256;
2194 status=QuantizeImage(quantize_info,image,exception);
2195 quantize_info=DestroyQuantizeInfo(quantize_info);
2197 image->alpha_trait=UndefinedPixelTrait;
2200 case PaletteBilevelAlphaType:
2205 status=TransformImageColorspace(image,sRGBColorspace,exception);
2206 if (image->alpha_trait == UndefinedPixelTrait)
2207 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2208 channel_mask=SetImageChannelMask(image,AlphaChannel);
2209 (void) BilevelImage(image,(
double) QuantumRange/2.0,exception);
2210 (void) SetImageChannelMask(image,channel_mask);
2211 quantize_info=AcquireQuantizeInfo(image_info);
2212 status=QuantizeImage(quantize_info,image,exception);
2213 quantize_info=DestroyQuantizeInfo(quantize_info);
2216 case PaletteAlphaType:
2218 status=TransformImageColorspace(image,sRGBColorspace,exception);
2219 if (image->alpha_trait == UndefinedPixelTrait)
2220 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2221 quantize_info=AcquireQuantizeInfo(image_info);
2222 quantize_info->colorspace=TransparentColorspace;
2223 status=QuantizeImage(quantize_info,image,exception);
2224 quantize_info=DestroyQuantizeInfo(quantize_info);
2229 status=TransformImageColorspace(image,sRGBColorspace,exception);
2230 if (image->storage_class != DirectClass)
2231 status=SetImageStorageClass(image,DirectClass,exception);
2232 image->alpha_trait=UndefinedPixelTrait;
2235 case TrueColorAlphaType:
2237 status=TransformImageColorspace(image,sRGBColorspace,exception);
2238 if (image->storage_class != DirectClass)
2239 status=SetImageStorageClass(image,DirectClass,exception);
2240 if (image->alpha_trait == UndefinedPixelTrait)
2241 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2244 case ColorSeparationType:
2246 status=TransformImageColorspace(image,CMYKColorspace,exception);
2247 if (image->storage_class != DirectClass)
2248 status=SetImageStorageClass(image,DirectClass,exception);
2249 image->alpha_trait=UndefinedPixelTrait;
2252 case ColorSeparationAlphaType:
2254 status=TransformImageColorspace(image,CMYKColorspace,exception);
2255 if (image->storage_class != DirectClass)
2256 status=SetImageStorageClass(image,DirectClass,exception);
2257 if (image->alpha_trait == UndefinedPixelTrait)
2258 status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2265 image_info=DestroyImageInfo(image_info);
2266 if (status == MagickFalse)