]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/composite.c
(no commit message)
[imagemagick] / MagickCore / composite.c
index 0804cd9b41c8a37e7ad9048218cdbf4cbd1986a1..f0e46aad6cc8a24f2d0adec70a77b8186620bcdf 100644 (file)
 %
 */
 
-static void CompositeHSB(const Quantum red,const Quantum green,
-  const Quantum blue,double *hue,double *saturation,double *brightness)
+/*
+   Composition based on the SVG specification:
+
+   A Composition is defined by...
+      Color Function :  f(Sc,Dc)  where Sc and Dc are the normizalized colors
+      Blending areas :  X = 1     for area of overlap, ie: f(Sc,Dc)
+                        Y = 1     for source preserved
+                        Z = 1     for destination preserved
+
+   Conversion to transparency (then optimized)
+      Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
+      Da'  = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
+
+   Where...
+      Sca = Sc*Sa     normalized Source color divided by Source alpha
+      Dca = Dc*Da     normalized Dest color divided by Dest alpha
+      Dc' = Dca'/Da'  the desired color value for this channel.
+
+   Da' in in the follow formula as 'gamma'  The resulting alpla value.
+
+   Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
+   the following optimizations...
+      gamma = Sa+Da-Sa*Da;
+      gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
+      opacity = QuantiumScale*alpha*beta;  // over blend, optimized 1-Gamma
+
+   The above SVG definitions also definate that Mathematical Composition
+   methods should use a 'Over' blending mode for Alpha Channel.
+   It however was not applied for composition modes of 'Plus', 'Minus',
+   the modulus versions of 'Add' and 'Subtract'.
+
+   Mathematical operator changes to be applied from IM v6.7...
+
+    1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
+       'ModulusAdd' and 'ModulusSubtract' for clarity.
+
+    2) All mathematical compositions work as per the SVG specification
+       with regard to blending.  This now includes 'ModulusAdd' and
+       'ModulusSubtract'.
+
+    3) When the special channel flag 'sync' (syncronize channel updates)
+       is turned off (enabled by default) then mathematical compositions are
+       only performed on the channels specified, and are applied
+       independantally of each other.  In other words the mathematics is
+       performed as 'pure' mathematical operations, rather than as image
+       operations.
+*/
+static void CompositeHSB(const double red,const double green,
+  const double blue,double *hue,double *saturation,double *brightness)
 {
   double
-    delta;
-
-  Quantum
+    delta,
     max,
     min;
 
@@ -254,6 +299,7 @@ static inline double MagickMin(const double x,const double y)
     return(x);
   return(y);
 }
+
 static inline double MagickMax(const double x,const double y)
 {
   if (x > y)
@@ -285,8 +331,8 @@ static MagickBooleanType CompositeOverImage(Image *image,
   */
   status=MagickTrue;
   progress=0;
-  image_view=AcquireCacheView(image);
-  composite_view=AcquireCacheView(composite_image);
+  composite_view=AcquireVirtualCacheView(composite_image,exception);
+  image_view=AcquireAuthenticCacheView(image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(static,4) shared(progress,status)
 #endif
@@ -523,53 +569,6 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
   ssize_t
     y;
 
-  /*
-     Composition based on the SVG specification:
-
-     A Composition is defined by...
-        Color Function :  f(Sc,Dc)  where Sc and Dc are the normizalized colors
-        Blending areas :  X = 1     for area of overlap, ie: f(Sc,Dc)
-                          Y = 1     for source preserved
-                          Z = 1     for destination preserved
-
-     Conversion to transparency (then optimized)
-        Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
-        Da'  = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
-
-     Where...
-        Sca = Sc*Sa     normalized Source color divided by Source alpha
-        Dca = Dc*Da     normalized Dest color divided by Dest alpha
-        Dc' = Dca'/Da'  the desired color value for this channel.
-
-     Da' in in the follow formula as 'gamma'  The resulting alpla value.
-
-     Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
-     the following optimizations...
-        gamma = Sa+Da-Sa*Da;
-        gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
-        opacity = QuantiumScale*alpha*beta;  // over blend, optimized 1-Gamma
-
-     The above SVG definitions also definate that Mathematical Composition
-     methods should use a 'Over' blending mode for Alpha Channel.
-     It however was not applied for composition modes of 'Plus', 'Minus',
-     the modulus versions of 'Add' and 'Subtract'.
-
-     Mathematical operator changes to be applied from IM v6.7...
-
-      1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
-         'ModulusAdd' and 'ModulusSubtract' for clarity.
-
-      2) All mathematical compositions work as per the SVG specification
-         with regard to blending.  This now includes 'ModulusAdd' and
-         'ModulusSubtract'.
-
-      3) When the special channel flag 'sync' (syncronize channel updates)
-         is turned off (enabled by default) then mathematical compositions are
-         only performed on the channels specified, and are applied
-         independantally of each other.  In other words the mathematics is
-         performed as 'pure' mathematical operations, rather than as image
-         operations.
-  */
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
   if (image->debug != MagickFalse)
@@ -605,8 +604,8 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
       if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
         break;
       status=MagickTrue;
-      image_view=AcquireCacheView(image);
-      composite_view=AcquireCacheView(composite_image);
+      composite_view=AcquireVirtualCacheView(composite_image,exception);
+      image_view=AcquireAuthenticCacheView(image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
       #pragma omp parallel for schedule(static,4) shared(status)
 #endif
@@ -696,7 +695,10 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
         channel to exist, to add transparency.
       */
       if (image->matte == MagickFalse)
-        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
+        {
+          (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
+          image->matte=MagickFalse;
+        }
       break;
     }
     case BlurCompositeOp:
@@ -724,6 +726,8 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
         blur;
 
       /*
+        Blur Image by resampling.
+
         Blur Image dictated by an overlay gradient map: X = red_channel;
           Y = green_channel; compose:args =  x_scale[,y_scale[,angle]].
       */
@@ -732,28 +736,35 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
       if (destination_image == (Image *) NULL)
         return(MagickFalse);
       /*
-        Determine the horizontal and vertical maximim blur.
+        Gather the maximum blur sigma values from user.
       */
       SetGeometryInfo(&geometry_info);
       flags=NoValue;
       value=GetImageArtifact(composite_image,"compose:args");
       if (value != (char *) NULL)
         flags=ParseGeometry(value,&geometry_info);
-      if ((flags & WidthValue) == 0 )
-        {
+      if ((flags & WidthValue) == 0 ) {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionWarning,"InvalidSetting","'%s' '%s'",
+               "compose:args",value);
           destination_image=DestroyImage(destination_image);
           return(MagickFalse);
         }
-      width=geometry_info.rho;
-      height=geometry_info.sigma;
-      blur.x1=geometry_info.rho;
+      /*
+        Users input sigma now needs to be converted to the EWA ellipse size.
+        The filter defaults to a sigma of 0.5 so to make this match the
+        users input the ellipse size needs to be doubled.
+      */
+      width=height=geometry_info.rho*2.0;
+      if ((flags & HeightValue) != 0 )
+        height=geometry_info.sigma*2.0;
+
+      /* default the unrotated ellipse width and height axis vectors */
+      blur.x1=width;
       blur.x2=0.0;
       blur.y1=0.0;
-      blur.y2=geometry_info.sigma;
-      angle_start=0.0;
-      angle_range=0.0;
-      if ((flags & HeightValue) == 0)
-        blur.y2=blur.x1;
+      blur.y2=height;
+      /* rotate vectors if a rotation angle is given */
       if ((flags & XValue) != 0 )
         {
           MagickRealType
@@ -765,21 +776,32 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
           blur.y1=(-height*sin(angle));
           blur.y2=height*cos(angle);
         }
+      /* Otherwise lets set a angle range and calculate in the loop */
+      angle_start=0.0;
+      angle_range=0.0;
       if ((flags & YValue) != 0 )
         {
           angle_start=DegreesToRadians(geometry_info.xi);
           angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
         }
       /*
-        Blur Image by resampling.
-       FUTURE: this is currently broken, especially for small sigma blurs
-        This needs to be fixed to use a non-user filter setup that provides
-        far more control than currently available.
+        Set up a gaussian cylindrical filter for EWA Bluring.
+
+        As the minimum ellipse radius of support*1.0 the EWA algorithm
+        can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
+        This means that even 'No Blur' will be still a little blurry!
+
+        The solution (as well as the problem of preventing any user
+        expert filter settings, is to set our own user settings, then
+        restore them afterwards.
       */
       resample_filter=AcquireResampleFilter(image,exception);
-      SetResampleFilter(resample_filter,CubicFilter); /* was blur*2 */
-      destination_view=AcquireCacheView(destination_image);
-      composite_view=AcquireCacheView(composite_image);
+      SetResampleFilter(resample_filter,GaussianFilter);
+
+      /* do the variable blurring of each pixel in image */
+      GetPixelInfo(image,&pixel);
+      composite_view=AcquireVirtualCacheView(composite_image,exception);
+      destination_view=AcquireAuthenticCacheView(destination_image,exception);
       for (y=0; y < (ssize_t) composite_image->rows; y++)
       {
         MagickBooleanType
@@ -821,13 +843,20 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
               blur.y1=(-height*sin(angle));
               blur.y2=height*cos(angle);
             }
-          ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
-            GetPixelRed(composite_image,p),blur.y1*QuantumScale*
-            GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
-            GetPixelRed(composite_image,p),blur.y2*QuantumScale*
-            GetPixelGreen(composite_image,p));
+#if 0
+          if ( x == 10 && y == 60 ) {
+            fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
+                blur.x1, blur.x2, blur.y1, blur.y2);
+            fprintf(stderr, "scaled by=%lf,%lf\n",
+                QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
+#endif
+          ScaleResampleFilter(resample_filter,
+            blur.x1*QuantumScale*GetPixelRed(composite_image,p),
+            blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
+            blur.x2*QuantumScale*GetPixelRed(composite_image,p),
+            blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
           (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
-            (double) y_offset+y,&pixel);
+            (double) y_offset+y,&pixel,exception);
           SetPixelInfoPixel(destination_image,&pixel,q);
           p+=GetPixelChannels(composite_image);
           q+=GetPixelChannels(destination_image);
@@ -951,9 +980,9 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
         displacement/distortion map.  -- Like a lens...
       */
       GetPixelInfo(image,&pixel);
-      image_view=AcquireCacheView(image);
-      destination_view=AcquireCacheView(destination_image);
-      composite_view=AcquireCacheView(composite_image);
+      image_view=AcquireVirtualCacheView(image,exception);
+      composite_view=AcquireVirtualCacheView(composite_image,exception);
+      destination_view=AcquireAuthenticCacheView(destination_image,exception);
       for (y=0; y < (ssize_t) composite_image->rows; y++)
       {
         MagickBooleanType
@@ -1041,6 +1070,14 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
             destination_dissolve=geometry_info.sigma/100.0;
           if ((destination_dissolve-MagickEpsilon) < 0.0)
             destination_dissolve=0.0;
+       /* posible speed up?  -- from IMv6 update
+          clip_to_self=MagickFalse;
+          if ((destination_dissolve+MagickEpsilon) > 1.0 )
+            {
+              destination_dissolve=1.0;
+              clip_to_self=MagickTrue;
+            }
+        */
         }
       break;
     }
@@ -1126,8 +1163,8 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
   status=MagickTrue;
   progress=0;
   midpoint=((MagickRealType) QuantumRange+1.0)/2;
-  image_view=AcquireCacheView(image);
-  composite_view=AcquireCacheView(composite_image);
+  composite_view=AcquireVirtualCacheView(composite_image,exception);
+  image_view=AcquireAuthenticCacheView(image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(static,4) shared(progress,status)
 #endif
@@ -1144,6 +1181,10 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
       red,
       saturation;
 
+    PixelInfo
+      destination_pixel,
+      source_pixel;
+
     register const Quantum
       *restrict p;
 
@@ -1189,6 +1230,8 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
     hue=0.0;
     saturation=0.0;
     brightness=0.0;
+    GetPixelInfo(image,&destination_pixel);
+    GetPixelInfo(composite_image,&source_pixel);
     for (x=0; x < (ssize_t) image->columns; x++)
     {
       MagickRealType
@@ -1408,6 +1451,21 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
           q+=GetPixelChannels(image);
           continue;
         }
+      switch (compose)
+      {
+        case ColorizeCompositeOp:
+        case HueCompositeOp:
+        case LuminizeCompositeOp:
+        case ModulateCompositeOp:
+        case SaturateCompositeOp:
+        {
+          GetPixelInfoPixel(composite_image,p,&source_pixel);
+          GetPixelInfoPixel(image,q,&destination_pixel);
+          break;
+        }
+        default:
+          break;
+      }
       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
         double
@@ -1682,10 +1740,9 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
                 pixel=Sc;
                 break;
               }
-            CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
-              GetPixelBlue(image,q),&sans,&sans,&brightness);
-            CompositeHSB(GetPixelRed(composite_image,p),
-              GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
+            CompositeHSB(destination_pixel.red,destination_pixel.green,
+              destination_pixel.blue,&sans,&sans,&brightness);
+            CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
               &hue,&saturation,&sans);
             HSBComposite(hue,saturation,brightness,&red,&green,&blue);
             switch (channel)
@@ -1849,10 +1906,9 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
                 pixel=Sc;
                 break;
               }
-            CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
-              GetPixelBlue(image,q),&hue,&saturation,&brightness);
-            CompositeHSB(GetPixelRed(composite_image,p),
-              GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
+            CompositeHSB(destination_pixel.red,destination_pixel.green,
+              destination_pixel.blue,&hue,&saturation,&brightness);
+            CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
               &hue,&sans,&sans);
             HSBComposite(hue,saturation,brightness,&red,&green,&blue);
             switch (channel)
@@ -1930,10 +1986,9 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
                 pixel=Sc;
                 break;
               }
-            CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
-              GetPixelBlue(image,q),&hue,&saturation,&brightness);
-            CompositeHSB(GetPixelRed(composite_image,p),
-              GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
+            CompositeHSB(destination_pixel.red,destination_pixel.green,
+              destination_pixel.blue,&hue,&saturation,&brightness);
+            CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
               &sans,&sans,&brightness);
             HSBComposite(hue,saturation,brightness,&red,&green,&blue);
             switch (channel)
@@ -2002,8 +2057,8 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
                 pixel=Dc;
                 break;
               }
-            CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
-              GetPixelBlue(image,q),&hue,&saturation,&brightness);
+            CompositeHSB(destination_pixel.red,destination_pixel.green,
+              destination_pixel.blue,&hue,&saturation,&brightness);
             brightness+=(0.01*percent_brightness*offset)/midpoint;
             saturation*=0.01*percent_saturation;
             HSBComposite(hue,saturation,brightness,&red,&green,&blue);
@@ -2118,10 +2173,9 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
                 pixel=Sc;
                 break;
               }
-            CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
-              GetPixelBlue(image,q),&hue,&saturation,&brightness);
-            CompositeHSB(GetPixelRed(composite_image,p),
-              GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
+            CompositeHSB(destination_pixel.red,destination_pixel.green,
+              destination_pixel.blue,&hue,&saturation,&brightness);
+            CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
               &sans,&saturation,&sans);
             HSBComposite(hue,saturation,brightness,&red,&green,&blue);
             switch (channel)
@@ -2356,8 +2410,8 @@ MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
     Tile texture onto the image background (optimized).
   */
   status=MagickTrue;
-  image_view=AcquireCacheView(image);
-  texture_view=AcquireCacheView(texture_image);
+  texture_view=AcquireVirtualCacheView(texture_image,exception);
+  image_view=AcquireAuthenticCacheView(image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(static) shared(status)
 #endif