﻿ 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.

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

Also see Blending Modes of Photoshop & Co..

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

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

## SVG standard

```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 (
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 (
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.9.5-3 Q16 x86 2016-07-22 http://www.imagemagick.org
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP
Delegates (built-in): bzlib cairo flif freetype jng jp2 jpeg lcms lqr openexr pangocairo 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.