% MagickCore Image Layering Methods %
% %
% Software Design %
-% John Cristy %
+% Cristy %
% Anthony Thyssen %
% January 2006 %
% %
% %
-% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
#include "MagickCore/studio.h"
#include "MagickCore/artifact.h"
#include "MagickCore/cache.h"
+#include "MagickCore/channel.h"
#include "MagickCore/color.h"
#include "MagickCore/color-private.h"
#include "MagickCore/composite.h"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ClearBounds() Clear the area specified by the bounds in an image to
-% transparency. This typically used to handle Background Disposal
-% for the previous frame in an animation sequence.
+% transparency. This typically used to handle Background Disposal for the
+% previous frame in an animation sequence.
%
-% WARNING: no bounds checks are performed, except for the null or
-% missed image, for images that don't change. in all other cases
-% bound must fall within the image.
+% Warning: no bounds checks are performed, except for the null or missed
+% image, for images that don't change. in all other cases bound must fall
+% within the image.
%
% The format is:
%
-% void ClearBounds(Image *image,RectangleInfo *bounds
+% void ClearBounds(Image *image,RectangleInfo *bounds,
% ExceptionInfo *exception)
%
% A description of each parameter follows:
if (bounds->x < 0)
return;
- if (image->matte == MagickFalse)
+ if (image->alpha_trait == UndefinedPixelTrait)
(void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
for (y=0; y < (ssize_t) bounds->height; y++)
{
% to check if a proposed disposal method will work successfully to generate
% the second frame image from the first disposed form of the previous frame.
%
+% Warning: no bounds checks are performed, except for the null or missed
+% image, for images that don't change. in all other cases bound must fall
+% within the image.
+%
% The format is:
%
% MagickBooleanType IsBoundsCleared(const Image *image1,
%
% o exception: return any errors or warnings in this structure.
%
-% WARNING: no bounds checks are performed, except for the null or
-% missed image, for images that don't change. in all other cases
-% bound must fall within the image.
-%
*/
static MagickBooleanType IsBoundsCleared(const Image *image1,
const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
{
- register ssize_t
- x;
-
register const Quantum
*p,
*q;
+ register ssize_t
+ x;
+
ssize_t
y;
return(MagickFalse);
for (y=0; y < (ssize_t) bounds->height; y++)
{
- p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,
- exception);
- q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,
- exception);
+ p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,exception);
+ q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,exception);
if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
break;
for (x=0; x < (ssize_t) bounds->width; x++)
{
- if ((GetPixelAlpha(image1,p) <= (Quantum) (QuantumRange/2)) &&
- (GetPixelAlpha(image1,q) > (Quantum) (QuantumRange/2)))
+ if ((GetPixelAlpha(image1,p) >= (Quantum) (QuantumRange/2)) &&
+ (GetPixelAlpha(image2,q) < (Quantum) (QuantumRange/2)))
break;
p+=GetPixelChannels(image1);
- q++;
+ q+=GetPixelChannels(image2);
}
if (x < (ssize_t) bounds->width)
break;
Coalesce the image sequence.
*/
assert(image != (Image *) NULL);
- assert(image->signature == MagickSignature);
+ assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
next=GetFirstImageInList(image);
bounds=next->page;
if (bounds.width == 0)
exception);
if (coalesce_image == (Image *) NULL)
return((Image *) NULL);
+ coalesce_image->background_color.alpha=(Quantum) TransparentAlpha;
(void) SetImageBackgroundColor(coalesce_image,exception);
- coalesce_image->matte=mext->matte;
+ coalesce_image->alpha_trait=next->alpha_trait;
coalesce_image->page=bounds;
coalesce_image->dispose=NoneDispose;
/*
coalesce_image->next->previous=coalesce_image;
previous=coalesce_image;
coalesce_image=GetNextImageInList(coalesce_image);
- (void) CompositeImage(coalesce_image,next,next->matte != MagickFalse ?
- OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
- exception);
+ (void) CompositeImage(coalesce_image,next,
+ next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,
+ MagickTrue,next->page.x,next->page.y,exception);
(void) CloneImageProfiles(coalesce_image,next);
(void) CloneImageProperties(coalesce_image,next);
(void) CloneImageArtifacts(coalesce_image,next);
/*
If a pixel goes opaque to transparent, use background dispose.
*/
- if (IsBoundsCleared(previous,coalesce_image,&bounds,exception))
+ if (IsBoundsCleared(previous,coalesce_image,&bounds,exception) != MagickFalse)
coalesce_image->dispose=BackgroundDispose;
else
coalesce_image->dispose=NoneDispose;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DisposeImages() returns the coalesced frames of a GIF animation as it would
-% appear after the GIF dispose method of that frame has been applied. That
-% is it returned the appearance of each frame before the next is overlaid.
+% appear after the GIF dispose method of that frame has been applied. That is
+% it returned the appearance of each frame before the next is overlaid.
%
% The format of the DisposeImages method is:
%
*dispose_image,
*dispose_images;
+ RectangleInfo
+ bounds;
+
register Image
*image,
*next;
- RectangleInfo
- bounds;
-
/*
Run the image through the animation sequence
*/
assert(images != (Image *) NULL);
- assert(images->signature == MagickSignature);
+ assert(images->signature == MagickCoreSignature);
if (images->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
image=GetFirstImageInList(images);
dispose_image=CloneImage(image,image->page.width,image->page.height,
MagickTrue,exception);
dispose_image=DestroyImage(dispose_image);
return((Image *) NULL);
}
- (void) CompositeImage(current_image,next,next->matte != MagickFalse ?
- OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
- exception);
+ (void) CompositeImage(current_image,next,
+ next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,
+ MagickTrue,next->page.x,next->page.y,exception);
/*
Handle Background dispose: image is displayed for the delay period.
*/
{
dispose_image=DestroyImage(dispose_image);
dispose_image=current_image;
- current_image=(Image *)NULL;
+ current_image=(Image *) NULL;
}
/*
Save the dispose image just calculated for return.
%
% The format of the ComparePixels method is:
%
-% MagickBooleanType *ComparePixels(const ImageLayerMethod method,
+% MagickBooleanType *ComparePixels(const LayerMethod method,
% const PixelInfo *p,const PixelInfo *q)
%
% A description of each parameter follows:
%
*/
-static MagickBooleanType ComparePixels(const ImageLayerMethod method,
+static MagickBooleanType ComparePixels(const LayerMethod method,
const PixelInfo *p,const PixelInfo *q)
{
- MagickRealType
+ double
o1,
o2;
if (method == CompareAnyLayer)
return((MagickBooleanType)(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
- o1 = (p->matte != MagickFalse) ? p->alpha : OpaqueAlpha;
- o2 = (q->matte != MagickFalse) ? q->alpha : OpaqueAlpha;
-
+ o1 = (p->alpha_trait != UndefinedPixelTrait) ? p->alpha : OpaqueAlpha;
+ o2 = (q->alpha_trait != UndefinedPixelTrait) ? q->alpha : OpaqueAlpha;
/*
- Pixel goes from opaque to transprency
+ Pixel goes from opaque to transprency.
*/
if (method == CompareClearLayer)
- return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
- (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
-
+ return((MagickBooleanType) ( (o1 <= ((double) QuantumRange/2.0)) &&
+ (o2 > ((double) QuantumRange/2.0)) ) );
/*
- overlay would change first pixel by second
+ Overlay would change first pixel by second.
*/
if (method == CompareOverlayLayer)
{
- if (o2 > ((MagickRealType) QuantumRange/2.0))
+ if (o2 > ((double) QuantumRange/2.0))
return MagickFalse;
return((MagickBooleanType) (IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
}
%
% The format of the CompareImagesBounds method is:
%
-% RectangleInfo *CompareImagesBounds(const ImageLayerMethod method,
+% RectangleInfo *CompareImagesBounds(const LayerMethod method,
% const Image *image1, const Image *image2, ExceptionInfo *exception)
%
% A description of each parameter follows:
%
*/
-static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2,
- const ImageLayerMethod method,ExceptionInfo *exception)
+static RectangleInfo CompareImagesBounds(const Image *image1,
+ const Image *image2,const LayerMethod method,ExceptionInfo *exception)
{
RectangleInfo
bounds;
if (ComparePixels(method,&pixel1,&pixel2))
break;
p+=GetPixelChannels(image1);
- q++;
+ q+=GetPixelChannels(image2);
}
if (y < (ssize_t) image1->rows)
break;
if (ComparePixels(method,&pixel1,&pixel2))
break;
p+=GetPixelChannels(image1);
- q++;
+ q+=GetPixelChannels(image2);
}
if (y < (ssize_t) image1->rows)
break;
if (ComparePixels(method,&pixel1,&pixel2))
break;
p+=GetPixelChannels(image1);
- q++;
+ q+=GetPixelChannels(image2);
}
if (x < (ssize_t) image1->columns)
break;
if (ComparePixels(method,&pixel1,&pixel2))
break;
p+=GetPixelChannels(image1);
- q++;
+ q+=GetPixelChannels(image2);
}
if (x < (ssize_t) image1->columns)
break;
%
% CompareImagesLayers() compares each image with the next in a sequence and
% returns the minimum bounding region of all the pixel differences (of the
-% ImageLayerMethod specified) it discovers.
+% LayerMethod specified) it discovers.
%
% Images do NOT have to be the same size, though it is best that all the
% images are 'coalesced' (images are all the same size, on a flattened
% The format of the CompareImagesLayers method is:
%
% Image *CompareImagesLayers(const Image *images,
-% const ImageLayerMethod method,ExceptionInfo *exception)
+% const LayerMethod method,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
*/
MagickExport Image *CompareImagesLayers(const Image *image,
- const ImageLayerMethod method, ExceptionInfo *exception)
+ const LayerMethod method, ExceptionInfo *exception)
{
Image
*image_a,
i;
assert(image != (const Image *) NULL);
- assert(image->signature == MagickSignature);
+ assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
assert((method == CompareAnyLayer) ||
(method == CompareClearLayer) ||
(method == CompareOverlayLayer));
(void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
next->page.y,exception);
bounds[i]=CompareImagesBounds(image_b,image_a,method,exception);
-
image_b=DestroyImage(image_b);
i++;
}
next=GetNextImageInList(next);
for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
{
+ if ((bounds[i].x == -1) && (bounds[i].y == -1) &&
+ (bounds[i].width == 1) && (bounds[i].height == 1))
+ {
+ /*
+ An empty frame is returned from CompareImageBounds(), which means the
+ current frame is identical to the previous frame.
+ */
+ i++;
+ continue;
+ }
image_a=CloneImage(next,0,0,MagickTrue,exception);
if (image_a == (Image *) NULL)
break;
% The format of the OptimizeLayerFrames method is:
%
% Image *OptimizeLayerFrames(const Image *image,
-% const ImageLayerMethod method, ExceptionInfo *exception)
+% const LayerMethod method, ExceptionInfo *exception)
%
% A description of each parameter follows:
%
#define DEBUG_OPT_FRAME 0
static Image *OptimizeLayerFrames(const Image *image,
- const ImageLayerMethod method, ExceptionInfo *exception)
+ const LayerMethod method, ExceptionInfo *exception)
{
ExceptionInfo
*sans_exception;
i;
assert(image != (const Image *) NULL);
- assert(image->signature == MagickSignature);
+ assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
assert(method == OptimizeLayer ||
method == OptimizeImageLayer ||
method == OptimizePlusLayer);
-
/*
- Are we allowed to add/remove frames from animation
+ Are we allowed to add/remove frames from animation?
*/
add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
/*
- Ensure all the images are the same size
+ Ensure all the images are the same size.
*/
curr=GetFirstImageInList(image);
for (; curr != (Image *) NULL; curr=GetNextImageInList(curr))
Compute the bounding box of changes for each pair of images.
*/
i=1;
- bgnd_image=(Image *)NULL;
- dup_image=(Image *)NULL;
+ bgnd_image=(Image *) NULL;
+ dup_image=(Image *) NULL;
dup_bounds.width=0;
dup_bounds.height=0;
dup_bounds.x=0;
if ( disposals[i-1] != PreviousDispose )
prev_image=DestroyImage(prev_image);
if ( disposals[i-1] == BackgroundDispose )
- prev_image=bgnd_image, bgnd_image=(Image *)NULL;
+ prev_image=bgnd_image, bgnd_image=(Image *) NULL;
if (bgnd_image != (Image *) NULL)
bgnd_image=DestroyImage(bgnd_image);
if ( disposals[i-1] == NoneDispose )
Run the image through the animation sequence
*/
assert(image != (Image *) NULL);
- assert(image->signature == MagickSignature);
+ assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
next=GetFirstImageInList(image);
dispose_image=CloneImage(next,next->page.width,next->page.height,
MagickTrue,exception);
dispose_image=DestroyImage(dispose_image);
return;
}
- (void) CompositeImage(current_image,next,next->matte != MagickFalse ?
+ (void) CompositeImage(current_image,next,next->alpha_trait != UndefinedPixelTrait ?
OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
exception);
/*
bounds;
assert((*images) != (const Image *) NULL);
- assert((*images)->signature == MagickSignature);
+ assert((*images)->signature == MagickCoreSignature);
if ((*images)->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
curr=GetFirstImageInList(*images);
for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next)
*i;
assert((*images) != (const Image *) NULL);
- assert((*images)->signature == MagickSignature);
+ assert((*images)->signature == MagickCoreSignature);
if ((*images)->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
i=GetFirstImageInList(*images);
for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
if ( i->delay != 0L ) break;
if ( i == (Image *) NULL ) {
(void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
- "ZeroTimeAnimation","'%s'",GetFirstImageInList(*images)->filename);
+ "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
return;
}
i=GetFirstImageInList(*images);
const ssize_t y_offset,ExceptionInfo *exception)
{
assert(destination != (Image *) NULL);
- assert(destination->signature == MagickSignature);
+ assert(destination->signature == MagickCoreSignature);
assert(source != (Image *) NULL);
- assert(source->signature == MagickSignature);
+ assert(source->signature == MagickCoreSignature);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
if (source->debug != MagickFalse || destination->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
source->filename, destination->filename);
% MergeImageLayers() composes all the image layers from the current given
% image onward to produce a single image of the merged layers.
%
-% The inital canvas's size depends on the given ImageLayerMethod, and is
+% The inital canvas's size depends on the given LayerMethod, and is
% initialized using the first images background color. The images
% are then compositied onto that image in sequence using the given
% composition that has been assigned to each individual image.
% The format of the MergeImageLayers is:
%
% Image *MergeImageLayers(const Image *image,
-% const ImageLayerMethod method, ExceptionInfo *exception)
+% const LayerMethod method, ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o exception: return any errors or warnings in this structure.
%
*/
-MagickExport Image *MergeImageLayers(Image *image,const ImageLayerMethod method,
+MagickExport Image *MergeImageLayers(Image *image,const LayerMethod method,
ExceptionInfo *exception)
{
#define MergeLayersTag "Merge/Layers"
scene;
assert(image != (Image *) NULL);
- assert(image->signature == MagickSignature);
+ assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
/*
Determine canvas image size, and its virtual canvas size and offset
*/
page.y=next->page.y;
}
if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
- width=(size_t) next->page.x+(ssize_t)next->columns-page.x;
+ width=(size_t) next->page.x+(ssize_t) next->columns-page.x;
if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
height=(size_t) next->page.y+(ssize_t) next->rows-page.y;
}