snibgo's ImageMagick pages

Compose methods

Here are some confused and confusing notes about compose methods.

convert dest src -compose OP -composite result

Result size etc will be of destination.

"-gravity" and "-geometry" position src with respect to dest.

If a mask is given, mask's alpha is ignored; treated as greyscale. Limits area modified (black in mask = no modification, ie result = dest. Mask is aligned with destination.

DP. = Duff-Porter

M. = Mathematical. With default channel (with sync), colours obey the operation but alpha is screened. With no sync, alpha channel obey the operation.

A. = Associative (ie if dest and src can be swapped, result is the same).

aOver. = alpha calculation as for Over.

Atop tca_Atop.png tca_Atop_a.pngDP. aDst. As SrcAtop
Blend tca_Blend.png tca_Blend_a.pngaPlus. Interpolation. Use with "-define compose:args={src_percent}[,{dest_percent}]". dest_percent defaults to 100-src_percent.
Blur tca_Blur.png tca_Blur_a.pngaDst. Use with -define compose:args='{Xscale}[x{Yscale}[+{angle}]]'.
Bumpmap tca_Bumpmap.png tca_Bumpmap_a.pngM. aBumpMap. result = dest * grey(src)
ChangeMask tca_ChangeMask.png tca_ChangeMask_a.pngaChangeMask. result.alpha = (src==dest) ? 0 : 1, where "==" is defined by fuzz.
Clear tca_Clear.png tca_Clear_a.pngDP. aClear. result := transparent.
ColorBurn tca_ColorBurn.png tca_ColorBurn_a.pngaOver. Blackout, but white stays white. result = 1 - ( (1-dest) / src).
ColorDodge tca_ColorDodge.png tca_ColorDodge_a.pngaOver. Whiteout, but black stays black. result = dest / (1-src)
Colorize tca_Colorize.png tca_Colorize_a.pngaHue. Hue followed by Saturate.
Copy tca_Copy.png tca_Copy_a.pngaCopy. result := dest. Background outside src is unchanged.
CopyBlack tca_CopyBlack.png tca_CopyBlack_a.pngaDst. result.black = src.black, if they exist.
CopyBlue tca_CopyBlue.png tca_CopyBlue_a.pngaDst.
CopyCyan tca_CopyCyan.png tca_CopyCyan_a.pngaDst. Synonym for CopyRed.
CopyGreen tca_CopyGreen.png tca_CopyGreen_a.pngaDst.
CopyMagenta tca_CopyMagenta.png tca_CopyMagenta_a.pngaDst. Synonym for CopyGreen.
CopyOpacity tca_CopyOpacity.png tca_CopyOpacity_a.pngaSrc. result.alpha = src_has_alpha ? src.alpha : intensity(src)
CopyRed tca_CopyRed.png tca_CopyRed_a.pngaDst.
CopyYellow tca_CopyYellow.png tca_CopyYellow_a.pngaDst. Synonym for CopyBlue.
Darken tca_Darken.png tca_Darken_a.pngM. aOver. src < dest ? src : dest; individual channels
DarkenIntensity tca_DarkenIntensity.png tca_DarkenIntensity_a.pngM. aDarkenIntensity. src < dest ? src : dest; test is intensity; copies all channels
Divide tca_Divide.png tca_Divide_a.pngM. aOver. As DivideDst.
DivideDst tca_DivideDst.png tca_DivideDst_a.pngM. aOver. result = src/dest
DivideSrc tca_DivideSrc.png tca_DivideSrc_a.pngM. aOver. result = dest/src
Difference tca_Difference.png tca_Difference_a.pngM. aOver. result = abs (src - dest)
Displace tca_Displace.png tca_Displace_a.pngaDisplace.
Dissolve tca_Dissolve.png tca_Dissolve_a.pngaOver. As Over, but modifies alpha, from "-define compose:args={src_percent}[,{dst_percent}]". dest_percent defaults to 100. If 100 < src_percent <= 200, actually sets dest_percent = 200-src_percent; src_percent = 100.
Distort tca_Distort.png tca_Distort_a.pngaDistort.
Dst tca_Dst.png tca_Dst_a.pngDP. aDst. No-op. Result := dest.
DstAtop tca_DstAtop.png tca_DstAtop_a.pngDP. aSrc.
DstIn tca_DstIn.png tca_DstIn_a.pngDP. aIn. result.alpha = dest.alpha * src.alpha
DstOut tca_DstOut.png tca_DstOut_a.pngDP. aDstOut. result.alpha = dest.alpha * (1-src.alpha)
DstOver tca_DstOver.png tca_DstOver_a.pngDP. aOver.
Exclusion tca_Exclusion.png tca_Exclusion_a.pngM. aOver. result = src + dest - 2*src*dest
HardLight tca_HardLight.png tca_HardLight_a.pngaOver. src <= 0.5 ? 2*src*dest : 1 - 2*(1-src)*(1-dest). Like overlay, but src/dest swapped.
Hue tca_Hue.png tca_Hue_a.pngaHue. result.hue = src.hue.
In tca_In.png tca_In_a.pngDP. aIn. As SrcIn
Lighten tca_Lighten.png tca_Lighten_a.pngM. aOver. src > dest ? src : dest; individual channels
LightenIntensity tca_LightenIntensity.png tca_LightenIntensity_a.pngM. aLightenIntensity. src > dest ? src : dest; test is intensity; copies all channels
LinearBurn tca_LinearBurn.png tca_LinearBurn_a.pngaOver. Blackout. result = src + dest - 1.
LinearDodge tca_LinearDodge.png tca_LinearDodge_a.pngaOver. Whiteout. result = src + dest, but alpha blending is as for Over.
LinearLight tca_LinearLight.png tca_LinearLight_a.pngaOver. Strong shading, creating whiteout/blackout. result = 2*src + dest - 1.
Luminize tca_Luminize.png tca_Luminize_a.pngaHue. result.luminance = src.luminance.
Mathematics tca_Mathematics.png tca_Mathematics_a.pngaOver. result = A*src*dest + B*src + C*dest + D. Use with "-define compose:args='A,B,C,D'".
Minus tca_Minus.png tca_Minus_a.pngM. aOver. As MinusDst.
MinusDst tca_MinusDst.png tca_MinusDst_a.pngM. aOver. result = src - dest
MinusSrc tca_MinusSrc.png tca_MinusSrc_a.pngM. aOver. result = dest - src
Modulate tca_Modulate.png tca_Modulate_a.pngaDst. Use with "-define compose:args={brightness}[,{saturation}]". result.lightness = src.lightness * brightness. saturation (default 100) generally desaturates.
ModulusAdd tca_ModulusAdd.png tca_ModulusAdd_a.pngM. aOver. result = dest + src; wrapped.
ModulusSubtract tca_ModulusSubtract.png tca_ModulusSubtract_a.pngM. aOver. result = src - dest; wrapped
Multiply tca_Multiply.png tca_Multiply_a.pngM. aOver. result = dest * src. Darkens.
None tca_None.png tca_None_a.pngaDst.
Out tca_Out.png tca_Out_a.pngaOut. As SrcOut
Overlay tca_Overlay.png tca_Overlay_a.pngaOver. dest <= 0.5 ? 2*src*dest : 1 - 2*(1-src)*(1-dest). Thus dark dest darkens image; light dest lightens it. Dest black/white stays black/white; otherwise colour and tone influenced by src. Removes (?) transparency.
Over tca_Over.png tca_Over_a.pngDP. aOver. As SrcOver
PegtopLight tca_PegtopLight.png tca_PegtopLight_a.pngaOver. Similar to SoftLight but simpler. result = 2*src*dest + src^2*(1 - 2*dest)
PinLight tca_PinLight.png tca_PinLight_a.pngaOver. Weird.
Plus tca_Plus.png tca_Plus_a.pngM. aPlus. result = dest + src; clipped. Alpha is added.
Replace tca_Replace.png tca_Replace_a.pngaCopy.
Saturate tca_Saturate.png tca_Saturate_a.pngaHue. result.saturation = src.saturation.
Screen tca_Screen.png tca_Screen_a.pngM. aOver. result = 1 - (1-src)*(1-dest). Lightens.
SoftLight tca_SoftLight.png tca_SoftLight_a.pngaOver. Similar to HardLight, but softer. Complex formula.
Src tca_Src.png tca_Src_a.pngDP. aSrc. result := dest. Background outside src becomes transparent black.
SrcAtop tca_SrcAtop.png tca_SrcAtop_a.pngDP. aDst. Like Over but dest alpha is unchanged.
SrcIn tca_SrcIn.png tca_SrcIn_a.pngDP. aIn.
SrcOut tca_SrcOut.png tca_SrcOut_a.pngaOut.
SrcOver tca_SrcOver.png tca_SrcOver_a.pngaOver. DP.
VividLight tca_VividLight.png tca_VividLight_a.pngaOver. Refinement of LinearLight, avoiding shading at extremes. result = src <= 0.5 ? 1-(1-dest)/(2*src) : dest/(2*(1-src)).
Xor tca_Xor.png tca_Xor_a.pngDP. aXor. Overlays, then result.alpha = dest.alpha XOR src.alpha

"compose:outside-overlay=false" prevents Duff-Porter composition clearing the destination image outside the overlaid area.

See also SVG Compositing Specification.

See testCompose.bat.

Calculation of alpha, when there is no mask (third image)

"Outside" means the part of dest that isn't overlapped by src. "Inside" means the image where dest and src overlap.

aClear: result.a = 0

aDst: result.a = dest.a

aIn: Outside: result.a = 0. Inside: result.a = dest.a * src.a

aOver: Outside: result.a = dest.a. Inside: result.a = 1 - ((1 - dest.a) * (1 - src.a)) = 1 - (1 - src.a - dest.a + src.a * dest.a) = scr.a + dest.a - src.a * dest.a

aOut: Outside: result.a = 0. Inside: result.a = src.a * (1 - dest.a)

aSrc: Outside: result.a = 0. Inside: result.a = src.a

aPlus: Outside: result.a = dest.a. Inside: result.a = dest.a + src.a

aXor: Outside: result.a = dest.a. Inside: result.a = dest.a XOR src.a

aBumpMap aChangeMask aCopy aDarkenIntensity aDisplace aDistort aDstOut aHue aLightenIntensity

SVG standard

From SVG Compositing Specification:

 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:

Dca Premultiplied (by the corresponding alpha value) destination color component
Sca Premultiplied source color component
Da Destination opacity component
Sa Source opacity component
Da Destination opacity component

Operation f(src,dest) X Y Z Dca' Da'
clear 0 0 0 0 0 0
src Sc 1 1 0 Sca Sa
dst Dc 1 0 1 Dca Da
src-over Sc 1 1 1 Sca + Dca*(1-Sa) Sa + Da - Sa*Da
dst-over Dc 1 1 1 Dca + Sca*(1-Da) Sa + Da - Sa*Da
src-in Sc 1 0 0 Sca * Da Sa * Da
dst-in Dc 1 0 0 Dca * Sa Sa * Da
src-out 0 0 1 0 Sca * (1-Da) Sa * (1-Da)
dst-out 0 0 0 1 Dca * (1-Sa) Da * (1-Sa)
src-atop Sc 1 0 1 Sca*Da + Dca*(1 - Sa) Da
dst-atop Dc 1 1 0 Dca*Sa + Sca*(1 - Da) Sa
xor 0 0 1 1 Sca*(1 - Da) + Dca*(1 - Sa) Sa + Da - 2*Sa*Da
plus Sc+Dc 1 1 1 Sca+Dca Sa+Da (??)
multiply Sc*Dc 1 1 1 Sca*Dca + Sca*(1 - Da) + Dca*(1 - Sa) Sa + Da - Sa*Da
screen Sc + Dc - (Sc*Dc) 1 1 1 Sca + Dca - Sca*Dca Sa + Da - Sa*Da
overlay if 2 * Dc <= 1
    f(Sc,Dc) = 2 * Sc * Dc
otherwise
    f(Sc,Dc) = 1 - 2 * (1 - Dc) * (1 - Sc)
1 1 1 2*Sca*Dca + Sca * (1 - Da) + Dca * (1 - Sa)
or
Sca * (1 + Da) + Dca * (1 + Sa) - 2*Dca*Sca - Da*Sa
Sa + Da - Sa*Da
darken min(Sc,Dc) 1 1 1 min(Sca*Da, Dca*Sa) + Sca * (1 - Da) + Dca * (1 - Sa) Sa + Da - Sa*Da
lighten max(Sc,Dc) 1 1 1 max(Sca*Da, Dca*Sa) + Sca * (1 - Da) + Dca * (1 - Sa) Sa + Da - Sa*Da
color-dodge if Sc == 1
    f(Sc,Dc) = 1
otherwise
    f(Sc,Dc) = min(1, Dc/(1 - Sc))
1 1 1 if Sca == Sa and Dca == 0
    Dca' = Sca * (1-Da)
otherwise if Sca == Sa
    Dca' = Sa * Da + Sca * (1-Da) + Dca * (1-Sa)
otherwise if Sca < Sa
    Dca' = Sa * Da * min(1, Dca/Da * Sa/(Sa-Sca)) + Sca * (1-Da) + Dca * (1-Sa)
Sa + Da - Sa*Da
color-burn if Sc == 0
    f(Sc,Dc) = 0
otherwise
    f(Sc,Dc) = 1 - min(1, (1-Dc)/Sc)
1 1 1 if Sca == 0 and Dca == Da
    Dca' = Sa * Da + Dca * (1-Sa)
otherwise if Sca == 0
    Dca' = Dca * (1-Sa)
otherwise if Sca > 0
    Dca' = Sa * Da * (1 - min(1, (1 - Dca/Da) * Sa/Sca)) + Sca * (1-Da) + Dca * (1-Sa)
Sa + Da - Sa*Da
hard-light if 2 * Sc <= 1
    f(Sc,Dc) = 2 * Sc * Dc
otherwise
    f(Sc,Dc) = 1 - 2 * (1-Dc) * (1-Sc)
1 1 1 if 2 * Sca <= Sa
    Dca' = 2*Sca*Dca + Sca * (1-Da) + Dca * (1-Sa)
otherwise
    Dca' = Sa*Da - 2 * (Da-Dca) * (Sa-Sca) + Sca * (1-Da) + Dca * (1-Sa)
     = Sca * (1+Da) + Dca * (1+Sa) - Sa*Da - 2*Sca*Dca
Sa + Da - Sa*Da
soft-light if 2 * Sc <= 1
    f(Sc,Dc) = Dc - (1 - 2*Sc) * Dc * (1-Dc)
otherwise if 2 * Sc > 1 and 4 * Dc <= 1
    f(Sc,Dc) = Dc + (2*Sc - 1) * (4*Dc * (4*Dc + 1) * (Dc-1) + 7 * Dc)
otherwise if 2 * Sc > 1 and 4 * Dc > 1
    f(Sc,Dc) = Dc + (2*Sc - 1) * ((Dc)^0.5 - Dc)
1 1 1 if 2 * Sca <= Sa
    Dca' = Dca * (Sa + (2*Sca - Sa) * (1-m)) + Sca * (1-Da) + Dca * (1-Sa)
otherwise if 2 * Sca > Sa and 4 * Dca <= Da
    Dca' = Da * (2*Sca - Sa) * (16 * m^3 - 12 * m^2 - 3*m) + Sca - Sca * Da + Dca
otherwise if 2 * Sca > Sa and 4 * Dca > Da
    Dca' = Da * (2*Sca - Sa) * (m^0.5 - m) + Sca - Sca * Da + Dca
Where: m = Dca/Da
Sa + Da - Sa*Da
difference abs(Dc-Sc) 1 1 1 Sca + Dca - 2 * min(Sca * Da, Dca * Sa) Sa + Da - Sa*Da
exclusion Sc + Dc - 2 * Sc * Dc 1 1 1 (Sca * Da + Dca * Sa - 2*Sca*Dca) + Sca * (1-Da) + Dca * (1-Sa) Sa + Da - Sa*Da

Scripts

testCompose.bat:

if "%IM%"=="" call imagepath

setlocal enabledelayedexpansion
set I=%IM%

set SIZE=-size 80x80
set SIZE2=-size 100x100

%I%convert %SIZE% xc:red gradient: -compose CopyOpacity -composite tc_red.png

%I%convert %SIZE2% xc:green gradient: -rotate 90 -compose CopyOpacity -composite tc_green.png


rem %I%convert %SIZE% xc:red tc_red.png

rem %I%convert %SIZE2% xc:green -rotate 90 tc_green.png



set COMPLIST=%TEMP%\compList.txt
%I%convert -list compose >%COMPLIST%

echo Divide>>%COMPLIST%
echo Minus>>%COMPLIST%
cSort /i%COMPLIST% /o%COMPLIST%

rem goto skipImages
type %COMPLIST%

for /F %%L in (%COMPLIST%) do (
  %I%convert tc_green.png tc_red.png -gravity Center -compose %%L -composite ^
  -write tca_%%L.png ^
  -alpha Extract ^
  tca_%%L_a.png
)
:skipImages

goto skipCompare
del test_comp.lis

goto skipListComp
for /F %%L in (%COMPLIST%) do (
  %I%compare -metric RMSE ^
  tca_%%L_a.png ^
  tca_Over_a.png ^
  NULL:

for /F "tokens=1,2 usebackq delims=() " %%R IN (`%I%compare -metric RMSE tca_%%L_a.png tca_Copy_a.png NULL: 2^>^&1`) do (
echo answer: %%R %%S
set COMP_INT=%%R
set COMP_FLT=%%S
echo %%R,%%L>>test_comp.lis
)
)

cSort /itest_comp.lis /otest_comp.lis /k0,1


:skipListComp
for /F %%L in (%COMPLIST%) do (
  %I%compare -metric RMSE ^
  tca_%%L_a.png ^
  tca_Over_a.png ^
  NULL:

for /F "tokens=1,2 usebackq delims=() " %%R IN (`%I%compare -metric RMSE tca_%%L_a.png tca_Out_a.png NULL: 2^>^&1`) do (
echo answer: %%R %%S
set COMP_INT=%%R
set COMP_FLT=%%S
echo %%R,%%L>>test_comp.lis
)
)

cSort /itest_comp.lis /otest_comp.lis /k0,1

:skipCompare

All images on this page were created by the commands shown, using:

%IM%identify -version
Version: ImageMagick 6.8.9-0 Q16 x64 2014-04-06 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2014 ImageMagick Studio LLC
Features: DPC OpenMP
Delegates: bzlib cairo freetype jbig jng jp2 jpeg lcms lqr pangocairo png ps rsvg tiff webp xml zlib

Source file for this web page is composngocairo png ps rsvg tiff webp xml zlib

Source file for this web page is compose.h1. To re-create this web page, execute "procH1 compose".


This page, including the images, is my copyright. Anyone is permitted to use or adapt any of the code, scripts or images for any purpose, including commercial use.

Anyone is permitted to re-publish this page, but only for non-commercial use.

Anyone is permitted to link to this page, including for commercial use.


Page version v1.0 19-Apr-2014.

Page created 14-Oct-2015 05:47:09.

Copyright © 2015 Alan Gibson.