function outpict=imblend(FG,BG,opacity,blendmode,varargin)
% IMBLEND(FG, BG, OPACITY, BLENDMODE,{AMOUNT},{COMPMODE},{CAMOUNT},{OPTIONS})
% Blend images or imagesets as one would blend layers in GIMP or Photoshop.
%
% FG, BG are image arrays of same H,V dimension
% Mismatches of dimensions 1:2 are not supported. Use IMSTACKER, IMRESIZE,
% IMCROP, or PADARRAY to enforce desired colocation of layer content.
% Mismatches of dimension 3 are handled by array expansion.
% 1 or 3 channel images are assumed to be monochrome or RGB, respectively
% 2 or 4 channel images are assumed to have an added alpha channel
% blending a RGB image and a monochrome image results in an RGB image
% blending a I/RGB image with a IA/RGBA image results in an image with alpha
% Mismatches of dimension 4 are handled by array expansion.
% both can be single images or 4-D imagesets of equal length
% can also blend a single image with a 4-D imageset
% OPACITY is a scalar from 0 to 1
% defines mixing of blended result and original BG
% BLENDMODE is a string assignment (see list & notes)
% this parameter is insensitive to case and spacing
% see included contour plots for insight into relationships and scaling behaviors
% AMOUNT is a numeric parameter (optional, default 1)
% used to internally scale the influence of blend calculations
% modes which accept this argument are marked with effective range
% COMPMODE optionally specifies the compositing or alpha blending used for images. (see list)
% default behavior replicates legacy GIMP behavior
% CAMOUNT is a thresholding parameter used by some compositing modes (optional, default 1)
%
% OPTIONS may include the following keys:
% The user may optionally specify the standard used for luma or YPbPr calculations.
% This only affects modes which perform Y or YPbPr conversion within the scope of IMBLEND.
% 'rec601' (default) or 'rec709' are valid keys.
% For niche application, a polar color model can be selected with the 'hsy' or 'ypbpr' keys.
% Both modes enforce gamut extents via chroma normalization (hsy) or truncation (ypbpr).
% Specifying 'quiet' will suppress non-terminal warnings
% Specifying 'verbose' will dump extra information relevant for some modes
%
% ============================= BLEND MODES =============================
% Opacity & Composition
% normal (compositing only)
%
% Light & Contrast
% overlay (standard method) amount:[0 to +inf)
% hard light (transpose of overlay) amount:[0 to +inf)
% soft light (legacy GIMP & GEGL)
% soft light ps (Photoshop)
% soft light svg (SVG 1.2)
% soft light eb (EffectBank/illusions.hu)
% soft linear light amount:[0 1]
% linear light (transpose of softlinearlight) amount:[0 1]
% vivid light amount:[0 to +inf)
% flat light (softdodge & softburn) amount:[0 to +inf)
% pin light (relational highlight & shadow) amount:[0 1]
% super light (adjust from linear to pin light) amount:[1 to +inf)
% hard mix (similar to posterization) amount:[0 2]
% scale add (add bg to fg deviation from mean) amount:(-inf to +inf)
% scale mult (scale bg by mean-normalized fg) amount:[0 to +inf)
% contrast (adjust bg contrast by mean-normalized fg) amount:[0 to +inf)
% curves (adjust bg contrast directly) amount:(-inf to +inf)
%
% Dodge & Burn
% color dodge (similar to GIMP dodge) amount:[0 1]
% color burn (similar to GIMP burn) amount:[0 1]
% linear dodge amount:[0 1]
% linear burn amount:[0 1]
% soft dodge amount:[0 to +inf)
% soft burn (transpose of soft dodge) amount:[0 to +inf)
% easy dodge (improved detail retention) amount:[0 to +inf)
% easy burn amount:[0 to +inf)
%
% Niche Complements from EffectBank
% light
% shadow (complement of 'light')
% bright (like a softer easydodge)
% dark (complement of 'bright')
% lighteneb (like a strong softdodge)
% darkeneb (complement of 'lighteneb')
%
% Quadratics & Complements
% glow (similar to dodge) amount:(-inf to +inf)
% heat (similar to burn) amount:(-inf to +inf)
% reflect (glow transpose) amount:(-inf to +inf)
% freeze (heat transpose) amount:(-inf to +inf)
% helow (similar to softlight) amount:(-inf to +inf)
% gleat (similar to vividlight) amount:(-inf to +inf)
% frect (helow transpose) amount:(-inf to +inf)
% reeze (gleat transpose) amount:(-inf to +inf)
%
% Relational
% lighten RGB (lighten only (RGB)) amount:[1 100]
% darken RGB (darken only (RGB)) amount:[1 100]
% lighten Y (lighten only (test luma only)) amount:[1 100]
% darken Y (darken only (test luma only)) amount:[1 100]
% saturate (only increase saturation) amount:[0 to +inf)
% desaturate (only decrease saturation) amount:[0 to +inf)
% near {layer} (apply only similar colors) amount:[0 1]
% far {layer} (transpose of 'near') amount:[0 1]
% replace color (replace specified regions of BG) amount:[0 1]
% exclude color (transpose of 'replacecolor') amount:[0 1]
%
% Mathematic
% multiply
% screen
% divide
% addition
% subtraction
% bleach (inverse of addition)
% stain (transpose of bleach)
% difference
% equivalence (inverse of difference)
% negation amount:[0 1]
% extremity (inverse of negation) amount:[0 1]
% exclusion (continuized XOR)
% interpolate (cosine interpolation)
% hard int (quantized cosine interpolation) amount:[1 to +inf)
% average (arithmetic mean, linear interpolation)
% geometric (geometric mean)
% harmonic (harmonic mean)
% pnorm (p-norm for p=amount) amount:[0 to +inf)
% sqrtdiff
% invsqrtdiff
% arctan
% gammalight (apply gamma correction map) amount:[0 to +inf)
% gammadark (inverse gamma correction) amount:[0 to +inf)
% grain extract
% grain merge
%
% Mesh Effects
% mesh (apply arbitrary transfer function) amount:[0 1]
% hard mesh (apply arbitrary transfer function) amount:[0 1]
% bomb (random transfer function) amount:[1 to +inf)
% bomb locked (channel-locked bomb) amount:[1 to +inf)
% hard bomb (think bomb + hardmix) amount:[1 to +inf)
%
% Component
% hue (H in CIELCHab)
% saturation (C in CIELCHab)
% color (HS in HSL, preserve Y)
% color lchab (CH in CIELCHab)
% color lchsr (CH in SRLAB2 LCH)
% color hsl (HS in HSL)
% color hsyp (HS in HSYp)
% value (max(R,G,B))
% luma (Rec601 or {Rec709})
% lightness (mean(min(R,G,B),max(R,G,B))
% intensity (mean(R,G,B))
% transfer inchan>outchan (directly transfer any channel to another)
% permute inchan>H (rotate hue) amount:(-inf to +inf)
% permute inchan>HS (rotate hue and blend chroma) amount:(-inf to +inf)
%
% NOTES:
% The 'lighten Y', 'darken Y', and some other component modes expect RGB input
% and will force expansion if fed single-channel images.
%
% SYNONYMOUS MODES:
% 'equivalence' is referred to as 'phoenix' in several sources.
% 'average' is referred to as 'allanon' in Krita and EffectBank
% 'harmonic' is referred to as 'parallel' in Krita
% 'linearburn' is referred to as 'inverse subtract' in Krita
% 'sqrtdiff' is referred to as 'additive subtractive' in Krita
% 'softlight' is referred to as 'pegtop light' by ImageMagick
% Either name may be used.
%
% OVERLAY & HARDLIGHT MODES:
% For AMOUNT=1, these behave as per standard formulae. Otherwise, an alternative is used.
% These are custom mesh modes which approximate iterated application of the named blend
% in a fashion which is continuously scalable so as to simulate fractional iterates.
% Consider the following examples using 'overlay'
% For AMOUNT=0.7, results approximate a softlight blend
% For AMOUNT=1, results are identical to standard methods
% For AMOUNT=2, results approximate IMBLEND(FG,IMBLEND(FG,BG,1,'overlay'),1,'overlay')
%
% SOFT LIGHT:
% 'softlight' is equivalent to ImageMagick, GIMP, and GEGL code. Also known as 'pegtop light'.
% 'softlightps' is equivalent to all formulae found attributed to Photoshop (afaik).
% 'softlightsvg' follows SVG 1.2 spec, and is nearly identical to 'softlightps'.
% 'softlighteb' uses an improved method posed for EffectBank by illusions.hu
% See also: https://mail.gnome.org/archives/gimp-developer-list/2012-July/msg00201.html
%
% PIN LIGHT:
% This mode combines lighten-only and darken-only thresholding to allow incorporation of FG
% extrema into the BG image. As AMOUNT is decreased, the thresholding becomes more exclusive.
%
% SUPER LIGHT:
% Piecewise union of functions whose level curves are superelliptic. Allows transition between
% behaviors of other blend modes. Useful in place of 'pin light' if a soft threshold is desired.
% For AMOUNT=1, behavior matches 'linear light'
% For AMOUNT=2, behavior is similar to 'hard light'
% For AMOUNT>>2, behavior approaches 'pin light'
%
% FLAT LIGHT:
% This is a piecewise combination of 'softdodge' & 'softburn'. The effect is flat and somewhat
% opaque, tending to superimpose FG extrema. The result is a middle ground between the behaviors
% and utility of 'vividlight' and 'pinlight'.
%
% MEAN-CENTERED CONTRAST MODES:
% The modes 'scale add', 'scale mult', and 'contrast' are intended for uses similar to those of GIMP's
% 'grain merge' mode. In normal operation, the FG content is treated as a mean-centered gain map for
% the blend effect in question. As an alternative to mean-centering, the center color may be specified
% via AMOUNT in the form of [k cc], where k is the scaling factor and cc is the center color, which may
% be either 1 or 3 elements; i.e. [1 0.5] is equivalent to [1 0.5 0.5 0.5]. In this fashion, 'scale add'
% and 'scale mult' effect adjustable additive and multiplicative gain mapping. 'contrast' acts as a
% levels tool, shifting the BG input white point or black point depending on FG value. This mode is
% similar to a subtle application of 'vivid light', with its breakpoints controlled by the center color.
% When cc=0 or 1, 'contrast' becomes equivalent to 'color dodge' or 'color burn'.
%
% CURVES:
% This mode allows direct manipulation of BG contrast in a fashion similar to that of a curves tool.
% This mode can take up to three parameters via AMOUNT. A full specification is of the form [k os gp],
% where k is a scaling factor, os is an offset (default 0), and gp is the input grey value (default 0.5).
% The amount of curvature is modulated following C=(k*FG + os), and the midpoint of the curve is shifted
% by gp. Setting k=0 allows scalar manipulation of contrast without the necessity for a solid FG fill.
% When C>1, BG contrast is increased
% When 01, BG content remains dominant; for AMOUNT<1, FG content dominates.
% See contour plots for insight. These modes have no neutral FG color.
%
% EASY DODGE & BURN:
% These are modified power functions allowing scalable dodge/burn functionality without destroying
% highlight and shadow details. As traditional methods are constant-valued for 50% of their
% domain, they tend to exhibit a thresholding behavior. While 'easydodge' and Gruschel's 'softdodge'
% can both darken and lighten, 'easydodge' is more asymmetric and has only subtle darkening effect.
% Unlike 'color' or 'linear' dodge & burn, the neutral FG color for the 'easy' modes is not black/white,
% but 1/6 and 5/6 (for AMOUNT=1). Results from 'easydodge' tend to be soft and less-oversaturated,
% as if a compromise between 'colordodge' and 'screen'. These are good all-around tools for dodge/burn
% tasks where extra contrast stretching is desired.
%
% QUADRATIC MODES:
% 'glow' and 'heat' are higher-ordered variants of 'colordodge' and 'colorburn'. Compared to the
% latter, they have subdued effect for default AMOUNT, though they are largely constant-valued
% and exhibit the same thresholding behavior. While both color and linear dodge/burn are unidirectional,
% these modes can both lighten and darken an image. The complementary modes 'gleat' and 'helow' are
% symmetric modes derived from the prior. 'gleat' behaves similar to 'vividlight', whereas 'helow'
% is similar to 'overlay' but with the midtone contrast response inverted (see contour plots).
% 'helow' and its transpose do not exhibit thresholding.
%
% LIGHTEN & DARKEN:
% The RGB modes are simple max/min relationals, much like GIMP's 'lighten only' and 'darken only'.
% The hard edge of these RGB modes can be tempered by specifying a range of smooth transition.
% For AMOUNT=1, the operation is simple relational. Otherwise, AMOUNT specifies the width of the
% transition region about the unit square diagonal (in percent). When AMOUNT=100, no unaltered FG
% or BG content remains; a value of 10% or so helps reduce the appearance of transition edges.
%
% 'lighteny' and 'darkeny' are similar to Photoshop's 'lighter color' and 'darker color' modes.
% Here, the FG/BG pixel luma is evaluated and the pixels replaced as a whole instead of evaluating
% each channel. This results in a binary masking behavior when AMOUNT=1. Otherwise, the transition
% between FG and BG is a linearized opacity blend. This is a tentative measure to reduce perceived
% brightness inversions or banding which otherwise occur in the blended region.
%
% DISTANCE MODES:
% The modes 'near' and 'far' locate regions in which FG and BG colors are within or beyond a weighted
% euclidean distance. Both modes accept an optional argument {layer} which may be 'fg' or 'bg'.
% 'near fg' will return only the FG content in the match region
% 'near bg' will return only the BG content in the match region
% 'near' merges matching FG content into BG
% For RGB inputs, distance calculation is performed in YPbPr, with extra weighting on luma.
% For distance specified by AMOUNT>=1, all colors are considered 'near'.
%
% REPLACE & EXCLUDE COLOR:
% These incorporate simple masking behavior which allows the user to handle composition with
% solid-color image matting. For example, if AMOUNT=[0 0 0], 'replacecolor' copies FG data
% to all black BG regions; 'excludecolor' copies all non-black FG content to the BG.
% If AMOUNT is a scalar, it will be expanded as necessary. Masking has a tolerance of 1%
%
% SATURATE & DESATURATE:
% Unlike the component mode 'saturation', these are thresholding modes operating on chroma
% in LCHab, much like 'lightenrgb' or 'lighteny' operates on rgb channels and luma. In these
% modes, AMOUNT modulates foreground chroma.
%
% KRITA & EFFECTBANK/ILLUSIONS.HU MODES:
% 'light' and 'shadow' are strong dodge/burn-like modes
% 'bright' and 'dark' are soft, partially inverting dodge/burn-like modes
% 'lighteneb' and 'darkeneb' are similar to a strong, transposed softdodge/burn
% 'gammalight' and 'gammadark' allow gamma adjustment with AMOUNTxFG as a gamma map.
% 'bleach' and 'stain' are the inverse of 'lineardodge' (addition), and 'linearburn'
% 'sqrtdiff', aka 'additivesubtract' is the difference of square roots
% 'harmonic', aka 'parallel' is the harmonic mean; roughly equivalent to 'geometric'
% 'arctan' is similar to 'softdodge' with an inverted FG
%
% MESH MODES:
% These modes accept AMOUNT in the form of a 2x2 or larger matrix whose elements represent
% output intensity for input intensities from 0 to 1 (consider BG as horizontal axis, etc)
% e.g [0 0.5; 0.5 1] is equivalent to 'average'; [0 0; 1 1] is the same as 'normal'
% Compare to the included contour plots; amount(1,1) is at the origin of BG and FG axes.
% Values are assumed to be evenly spaced and are subject to interpolation (bilinear for 'mesh'
% and nearest-neighbor for 'hardmesh'). If AMOUNT is not set explicitly to a valid matrix,
% a warning will be dumped and a default used.
%
% RANDOMIZED MODES:
% The 'bomb' mode applies a random piecewise-linear blend function of size AMOUNTxAMOUNT
% The 'bomblocked' mode is the same as 'bomb', but without channel-independence
% The 'hardbomb' mode applies a random piecewise-constant blend function of size AMOUNTxAMOUNT
% Using the 'verbose' key with these modes will display the transformation matrix as a command string.
% These can then be used with the mesh modes to reproduce a particular random blend.
%
% COLOR MODES:
% 'color' is a variant of the HSL method with an attempt to enforce luma preservation (fast)
% 'color hsyp' attempts to provide best uniformity, at the cost of maximum chroma range.
% 'color hsl' matches the legacy 'color' blend mode in GIMP
% Based only on experiment, LCHab method best approximates Photoshop behavior.
%
% The 'hue' & 'saturation' modes are derived from LCHab instead of HSL as in GIMP.
% If H or S modes are desired in HuSL, HSY, HSI or HSV, use 'transfer' instead.
%
% TRANSFER MODES:
% mode accepts channel strings based on RGBA, HuSLuv, HSY, HSYp, HSI, HSL, HSV, or CIELCHab models
% 'y', 'r', 'g', 'b', 'a'
% 'h_husl', 's_husl', 'l_husl'
% 'h_hsy', 's_hsy', 'y_hsy'
% 'h_hsyp', 's_hsyp', 'y_hsyp'
% 'h_hsi', 's_hsi', 'i_hsi'
% 'h_hsl', 's_hsl', 'l_hsl'
% 'h_hsv', 's_hsv', 'v_hsv'
% 'l_lch', 'c_lch', 'h_lch'
% non-rgb symmetric channel transfers (e.g. V>V or Y>Y) are easier applied otherwise
% (e.g. 'value' or 'luma' blend modes)
%
% PERMUTATION MODES:
% modes can accept input channel strings 'h', 's', 'y', 'dh', 'ds', 'dy'
% permutations actually occur on H and S in the HuSLuv model
% color permutations (inchan>HS) combine hue rotation and chroma blending
% chroma blending is maximized when abs(amount)==1
%
% ============================= COMPOSITION MODES =============================
% Porter-Duff
% src over camount:[0 to +inf)
% src atop camount:[0 to +inf)
% src in camount:[0 to +inf)
% src out camount:[0 to +inf)
% dst over camount:[0 to +inf)
% dst atop camount:[0 to +inf)
% dst in camount:[0 to +inf)
% dst out camount:[0 to +inf)
% xor camount:[0 to +inf)
%
% Other
% gimp (default)
% translucent
% dissolve {type} (alpha dithering) camount:[0 1]
% lindissolve {type} (preserve linear alpha) camount:[0 1]
%
% NOTES:
% Some of these modes (e.g. 'dst in', 'xor') don't really make much sense to use with
% any blend mode other than 'normal'. You're not restricted from doing it, though.
%
% 'gimp' specifies the legacy approach used by GIMP prior to GEGL (default)
% This is similar to SRC-OVER composition for 'normal' and a modified SRC-ATOP
% composition for other blends
%
% The SVG 1.2 spec and GEGL follow a Porter-Duff SRC-OVER composition for all blends
%
% PORTER-DUFF MODES:
% If using these modes where hard-edged masking behavior is desired, specifying a nonunity CAMOUNT
% will invoke a thresholding operation on the FG alpha channel.
% CAMOUNT>1 sets all alpha <1 to 0
% CAMOUNT between (0,1) thresholds alpha at the specified value
% CAMOUNT=0 will set all nonzero alpha to 1
% Unlike other modes, using these on I/RGB inputs may force IA/RGBA output depending on the mode
% and the value of specified OPACITY. These are generally not useful cases.
%
% TRANSLUCENT MODE:
% This mode is based on an article by Søren Sandmann Pedersen, and uses a transmission-reflection
% model to emulate the effect of a translucent material. Calculations are performed in linear RGB.
% http://ssp.impulsetrain.com/translucency.html
%
% DISSOLVE MODES:
% These modes are essentially SRC-OVER composition after FG alpha is converted to a dithered
% binary mask using one of the following methods:
% 'dissolve' applies a white noise thresholding dither (GIMP behavior)
% 'dissolve ord' applies a 64-level ordered dither
% 'dissolve zf' applies a Zhou-Fang variable-coefficient E-D dither (best)
% When using these modes, final mixdown opacity is linearly scaled with OPACITY as usual.
% The masking density is controlled via CAMOUNT. The combination of dithering and linear opacity
% makes the creation of texture or grain overlays very simple.
%
% The 'lindissolve' modes offer the same methods, but the dithering is performed only on a uniform
% mask. This leaves linear FG alpha intact, allowing for a different range of control. i.e.:
% In 'dissolve', opacity is scalar (OPACITY) and density is a map (alpha*CAMOUNT)
% In 'lindissolve', opacity is a map (alpha*OPACITY) and density is scalar (CAMOUNT)
% When no FG alpha channel is present, 'dissolve' and 'lindissolve' are identical.
%
% =====================================================================
% EXAMPLES:
% Do a simple multiply blend as would GIMP:
% R=imblend(FG,BG,1,'multiply');
%
% Specify SRC-OVER composition and use CAMOUNT for alpha thresholding:
% R=imblend(FG,BG,1,'multiply','srcover',0.5);
%
% CLASS SUPPORT:
% Accepts 'double','single','uint8','uint16','int16', and 'logical'
% Return type is inherited from BG
%
% See also: REPLACEPIXELS, IMCOMPOSE
% REFERENCES:
% https://www.ffmpeg.org/doxygen/2.4/vf__blend_8c_source.html
% http://dunnbypaul.net/blends/
% http://www.pegtop.net/delphi/articles/blendmodes/
% http://www.venture-ware.com/kevin/coding/lets-learn-math-photoshop-blend-modes/
% http://www.deepskycolors.com/archive/2010/04/21/formulas-for-Photoshop-blending-modes.html
% http://www.kineticsystem.org/?q=node/13
% http://www.simplefilter.de/en/basics/mixmods.html
% http://en.wikipedia.org/wiki/Blend_modes
% http://en.wikipedia.org/wiki/YUV
% https://en.wikipedia.org/wiki/Alpha_compositing
% http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf
% https://dev.w3.org/SVG/modules/compositing/master/
% https://www.w3.org/TR/SVG11/filters.html#feBlendElement
% http://ssp.impulsetrain.com/porterduff.html
% http://ssp.impulsetrain.com/translucency.html
% GIMP 2.4 & 2.8.10 source
% Krita 3.1.4 source
% http://illusions.hu/effectwiki/doku.php?id=list_of_blendings (DEAD LINK)
% https://mail.gnome.org/archives/gimp-developer-list/2012-July/msg00201.html
% https://yahvuu.files.wordpress.com/2009/09/table-contrast-2100b.png
% https://yahvuu.wordpress.com/2009/09/27/blendmodes1/
compkeys={'gimp','translucent','srcover','srcatop','srcin','srcout','dstover','dstatop','dstin', ...
'dstout','xor','dissolve','dissolvezf','dissolveord','lindissolve','lindissolvezf','lindissolveord'};
amount=1;
camount=1;
compositionmode='gimp';
rec='rec601';
quiet=0;
verbose=0;
colormodel='rgb';
wclass='double'; % this isn't documented
compkeyset=0;
for k=1:1:length(varargin);
if isnumeric(varargin{k})
if compkeyset==1;
camount=varargin{k};
else
amount=varargin{k};
end
elseif ischar(varargin{k})
key=lower(varargin{k});
key=key(key~=' ');
switch key
case compkeys
compositionmode=key;
compkeyset=1;
case {'rec601','rec709'}
rec=key;
case 'quiet'
quiet=1;
case 'verbose'
verbose=1;
case 'ypbpr'
colormodel='ypbpr';
case 'hsy'
colormodel='hsy';
case 'double'
wclass='double';
case 'single'
wclass='single';
otherwise
if ~quiet % only suppressed if quiet key comes first!
fprintf('IMBLEND: Ignoring unknown key ''%s''\n',key)
end
end
end
end
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% check and modify datatypes %%%%%%%%%%%%%%%%%%%%%%%%%
% output type is inherited from BG
try
FG=imcast(FG,wclass);
catch b
error('IMBLEND: unsupported class for FG')
end
try
[BG inclassBG]=imcast(BG,wclass);
catch b
error('IMBLEND: unsupported class for BG')
end
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% check and modify dimensions %%%%%%%%%%%%%%%%%%%%%%%%%
% check if height & width match
sFG=size(FG);
sBG=size(BG);
if any(sFG(1:2)~=sBG(1:2))
error('IMBLEND: images of mismatched dimension')
end
% check frame count and expand as necessary
if length(sFG)~=4 && length(sBG)~=4 % two single images
images=1;
else
if length(sFG)~=4 % single FG, multiple BG
FG=repmat(FG,[1 1 1 sBG(4)]);
elseif length(sBG)~=4 % multiple FG, single BG
BG=repmat(BG,[1 1 1 sFG(4)]); sBG=size(BG);
elseif sFG(4)~=sBG(4) % two unequal imagesets
error('IMBLEND: imagesets of unequal length')
end
images=sBG(4);
end
% expand along dimension 3 where necessary
% some blend modes expect RGB input; force expansion to avoid error bombing users
% yes, ~isempty(find(strcmp())) is about 50x faster than ismember() in R2009b!
modestring=lower(blendmode(blendmode~=' '));
if ~isempty(find(strcmp(modestring,{'value','lightness','intensity','hue','saturation','luma','lighteny', ...
'darkeny','color','colorlchab','colorlchsr','colorhsl','colorhsyp','saturate','desaturate'}),1)) ...
|| ~isempty(strmatch('transfer',modestring)) ...
|| ~isempty(strmatch('permute',modestring)) || ~strcmp(colormodel,'rgb')
mustRGB=true;
else
mustRGB=false;
end
s3FG=size(FG,3); % image size
s3BG=size(BG,3);
ccFG=s3FG; % number of color channels
ccBG=s3BG;
FGhasalpha=0; % alpha flag
BGhasalpha=0;
% determine number of color channels, alpha presence, and split alpha from color
if any(ccFG==[2 4])
FGhasalpha=1;
ccFG=2*floor((ccFG+1)/2)-1;
FGA=FG(:,:,ccFG+1,:);
FG=FG(:,:,1:ccFG,:);
end
if any(ccBG==[2 4])
BGhasalpha=1;
ccBG=2*floor((ccBG+1)/2)-1;
BGA=BG(:,:,ccBG+1,:);
BG=BG(:,:,1:ccBG,:);
end
% at this point, both FG and BG can only be 1 or 3 channel images with or without seperate alpha
if ccFGccBG
BG=repmat(BG,[1 1 3 1]);
elseif mustRGB && all([ccFG==1 ccBG==1])
FG=repmat(FG,[1 1 3 1]);
BG=repmat(BG,[1 1 3 1]);
end
% add a solid alpha channel where missing
if FGhasalpha==0 && BGhasalpha==1
sFG=size(FG);
FGA=ones([sFG(1:2) 1 size(FG,4)]);
elseif FGhasalpha==1 && BGhasalpha==0
sBG=size(BG);
BGA=ones([sBG(1:2) 1 size(BG,4)]);
end
% perform blend operations per frame %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% these composition modes are not dependent on the results of any blend
% i.e. the [both] term is either 0 or BG, so just shunt straight to composition
noblendcompmodes={'srcout','dstover','dstatop','dstin','dstout','xor'};
if strcmp(modestring,'normal') || ~isempty(find(strcmp(compositionmode,noblendcompmodes),1))
outpict=FG;
else
if isempty(find(strcmp(modestring,{'mesh','hardmesh','replacecolor','excludecolor','curves','scaleadd', ...
'scalemult','contrast'}),1)) && numel(amount)~=1
if ~quiet
fprintf('IMBLEND: AMOUNT parameter must be scalar for ''%s'' mode. Defaulting to 1\n',modestring)
end
amount=1;
end
if images~=1
outpict=zeros(size(BG));
end
for f=1:images
if images==1
I=BG;
M=FG;
else
I=BG(:,:,:,f);
M=FG(:,:,:,f);
end
switch colormodel
case 'ypbpr'
A=gettfm('ypbpr');
My=sum(bsxfun(@times,M,A(1,:,:)),3);
Mpb=sum(bsxfun(@times,M,A(2,:,:)),3);
Mpr=sum(bsxfun(@times,M,A(3,:,:)),3);
Iy=sum(bsxfun(@times,I,A(1,:,:)),3);
Ipb=sum(bsxfun(@times,I,A(2,:,:)),3);
Ipr=sum(bsxfun(@times,I,A(3,:,:)),3);
% convert to lch
M(:,:,1)=My;
M(:,:,2)=sqrt(Mpr.^2+Mpb.^2);
M(:,:,3)=mod(atan2(Mpr,Mpb),2*pi)/(2*pi);
I(:,:,1)=Iy;
I(:,:,2)=sqrt(Ipr.^2+Ipb.^2);
I(:,:,3)=mod(atan2(Ipr,Ipb),2*pi)/(2*pi);
case 'hsy'
M=rgb2hsy(M,'normal');
I=rgb2hsy(I,'normal');
M(:,:,1)=M(:,:,1)/360;
I(:,:,1)=I(:,:,1)/360;
end
switch modestring
case 'overlay'
% for amount==1, this is a standard 'overlay' mode
% otherwise, it's a brute-force attempt to approximate iterative an 'overlay'
% it's probably still faster than the horrible giant polynomial alternative
% results for amt=1 equal standard 'overlay'
% results for amt=2 approximate twice-recursed 'overlay', and so on ...
amount=max(amount,0);
if amount==1;
hi=I>0.5;
R=(1-2*(1-I).*(1-M)).*hi + (2*M.*I).*~hi;
else
if amount<1
mesh=(1-amount)*fetchLUT(0) + amount*fetchLUT(1);
elseif amount==1
mesh=fetchLUT(1);
elseif amount>1 && amount<2
mesh=(2-amount)*fetchLUT(1) + (amount-1)*fetchLUT(2);
elseif amount==2
mesh=fetchLUT(2);
elseif amount==3
mesh=fetchLUT(3);
elseif amount==4
mesh=fetchLUT(4);
elseif amount>2 && amount<3
mesh=(3-amount)*fetchLUT(2) + (amount-2)*fetchLUT(3);
elseif amount>3
mesh=(4-amount)*fetchLUT(3) + (amount-3)*fetchLUT(4);
end
[bg fg]=meshgrid(0:1/(size(mesh,2)-1):1,0:1/(size(mesh,1)-1):1);
R=zeros(size(I));
for c=1:size(I,3)
R(:,:,c)=interp2(bg,fg,mesh,I(:,:,c),M(:,:,c),'bilinear');
end
end
case 'hardlight'
% this is the transpose of 'overlay' and follows the same concept
amount=max(amount,0);
if amount==1;
hi=M>0.5;
R=(1-2*(1-M).*(1-I)).*hi + (2*I.*M).*~hi;
else
if amount<1
mesh=(1-amount)*fetchLUT(0) + amount*fetchLUT(1);
elseif amount>1 && amount<2
mesh=(2-amount)*fetchLUT(1) + (amount-1)*fetchLUT(2);
elseif amount==2
mesh=fetchLUT(2);
elseif amount==3
mesh=fetchLUT(3);
elseif amount==4
mesh=fetchLUT(4);
elseif amount>2 && amount<3
mesh=(3-amount)*fetchLUT(2) + (amount-2)*fetchLUT(3);
elseif amount>3
mesh=(4-amount)*fetchLUT(3) + (amount-3)*fetchLUT(4);
end
[bg fg]=meshgrid(0:1/(size(mesh,2)-1):1,0:1/(size(mesh,1)-1):1);
R=zeros(size(I));
for c=1:size(I,3)
R(:,:,c)=interp2(bg,fg,mesh,M(:,:,c),I(:,:,c),'bilinear');
end
end
case {'softlight','pegtoplight'}
% algebraically identical to GIMP for both legacy and GEGL methods
% this is the same as ImageMagick's 'pegtop light' variant
% same as legacy GIMP 'overlay' due to bug
R=I.^2 + 2*M.*I.*(1-I);
case 'softlightsvg'
% https://dev.w3.org/SVG/modules/compositing/master/
m1=M<=0.50;
m2=I<=0.25;
m3=~m1 & m2;
m4=~m1 & ~m2;
R=(I - (1-2*M).*I.*(1-I)).*m1 ...
+ (I + (2*M-1).*(4*I.*(4*I + 1).*(I-1) + 7*I)).*m3 ...
+ (I + (2*M-1).*(I.^0.5 - I)).*m4;
case {'softlighthu','softlighteb'}
I=max(I,0);
% i'm not sure which version was originally given at illusions.hu. the domain was parked
% these test equivalent to within 3LSB (uint8)
% https://en.wikipedia.org/wiki/Blend_modes#Soft_Light
% this is probably the most correct version; gradient angle is independent of FG
% R=I.^(2.^(1-2*M));
% https://yahvuu.files.wordpress.com/2009/09/table-contrast-2100b.png
% this version is ~2x faster; gradient angle is only weakly dependent on FG
% still has continuity advantage over PS/SVG methods; still has better symmetry than pegtop method
R=I.^(M.^2 - 2.5*M + 2);
case 'softlightps' % krita's version of ps softlight; equiv to formulae for ps afaict
I=max(I,0);
hi=M>0.5;
R=(I+(2*M-1).*(sqrt(I)-I)).*hi ...
+ (I-(1-2*M).*I.*(1-I)).*~hi;
case 'softlinearlight'
amount=min(max(amount,0),1);
R=I*(amount+1)+M*amount-amount;
case 'linearlight'
amount=min(max(amount,0),1);
R=M*(amount+1)+I*amount-amount;
case 'flatlight'
% this is a thing made from parametric softdodge & inverse softburn
amount=max(amount,0);
hi=M>=I;
pm1=((M+I*amount)<1);
pm2=((M*amount+I)<1);
pmu1=pm1 & hi;
pmd1=~pm1 & hi;
pmu2=pm2 & ~hi;
pmd2=~pm2 & ~hi;
R=(0.5*amount*I./(eps+1-M)).*pmu1 ...
+ (1-0.5*(1-M)./(eps+I*amount)).*pmd1 ...
+ (0.5*amount*M./(eps+1-I)).*pmu2 ...
+ (1-0.5*(1-I)./(eps+M*amount)).*pmd2;
case 'vividlight'
% this parametric method is actually faster than standard method
amount=max(amount,0);
R=zeros(size(I));
for c=1:1:size(M,3);
lo=-min(M(:,:,c)-0.5,0)*amount*2;
hi=1-max(M(:,:,c)-0.5,0)*amount*2;
R(:,:,c)=(I(:,:,c)-lo)./max(hi-lo,0);
end
case 'pinlight' % highlights are lighten-only, shadows are darken-only
amount=min(max(amount,0),1);
roiw=amount/2;
hi=M>0.5;
R=(max(I,(1/roiw)*(M-(1-roiw)))).*hi ...
+ (min(I,(1/roiw)*M)).*~hi;
case 'superlight' % use piecewise-pnorm to create a superelliptic contrast mode
amount=max(1,amount);
if amount==1;
R=2*M+I-1;
else
lo=M<0.5;
R=(1-((1-I).^amount + (1-2*M).^amount).^(1/amount)).*lo ...
+ ((I.^amount + (2*M-1).^amount).^(1/amount)).*~lo;
end
% my own variation; slope varies with amount
case 'hardmix'
amount=max(min(amount,2),0);
if amount>=1
R=M*(2-amount)+I*amount;
else
R=M*amount+I*(2-amount);
end
m=R>amount;
R(m)=1;
R(~m)=0;
case 'hardint'
R=round(0.5*amount*(2-cos(M*pi)-cos(I*pi)))/(2*amount);
% DODGES/BURNS
case'colordodge'
amount=max(min(amount,1),0);
R=I./(1-M*amount);
case 'colorburn'
amount=max(min(amount,1),0);
R=1-(1-I)./(M*amount+(1-amount));
% neutral is 1-(5/6)*amount
case 'easydodge'
amount=1/(eps+max(0,amount));
I=max(I,0);
R=I.^((1-M)*1.2*amount);
% neutral is (5/6)*amount
case 'easyburn'
amount=1/(eps+max(0,amount));
I=min(I,1);
R=1-(1-I).^(M*1.2*amount);
case 'lineardodge' % addition
amount=max(min(amount,1),0);
R=M*amount+I;
case {'linearburn','inversesubtract'}
amount=max(min(amount,1),0);
R=M*amount+I-1*amount;
case 'softdodge'
amount=max(0,amount);
pm=(M+I*amount)<1;
R=(0.5*amount*I./(eps+1-M)).*pm ...
+ (1-0.5*(1-M)./(I*amount+eps)).*~pm;
case 'softburn'
amount=max(0,amount);
pm=(M/amount+I)<1;
R=(0.5*M./((eps+1-I)*amount)).*pm ...
+ (1-0.5*amount*(1-I)./(M+eps)).*~pm;
% SIMPLE MATH OPS
case 'lightenrgb'
% use a fillet to effect a smooth transition from FG to BG
% it might seem like an opacity blend would make more sense
% but it causes dark banding, where this method causes minor lightening
if amount==1;
R=max(I,M);
else
amount=amount*0.01;
amount=min(max(0.01,amount),1);
mlo=M<(I-amount);
mhi=M>(I+amount);
mid=~mhi & ~mlo;
OS=min(I,M)-0.5*(amount*1.414*sin(pi/4 - asin(abs(I-M)/(amount*1.414))));
R=I.*mlo ...
+ M.*mhi ...
+ (sqrt((M-OS).^2 + (I-OS).^2)+OS).*mid;
end
case 'darkenrgb'
if amount==1;
R=min(I,M);
else
amount=amount*0.01;
amount=min(max(0.01,amount),1);
mlo=M<(I-amount);
mhi=M>(I+amount);
mid=~mhi & ~mlo;
OS=min(1-I,1-M)-0.5*(amount*1.414*sin(pi/4 - asin(abs(M-I)/(amount*1.414))));
R=1-((1-M).*mlo ...
+ (1-I).*mhi ...
+ (sqrt((1-M-OS).^2 + (1-I-OS).^2)+OS).*mid);
end
case 'lighteny'
% soft mode does a faux-linear opacity blend
% as a compromise to avoid inversions or dark banding
factors=gettfm('luma');
My=sum(bsxfun(@times,M,factors),3);
Iy=sum(bsxfun(@times,I,factors),3);
if amount==1;
mask=My>Iy;
R=bsxfun(@times,M,mask) + bsxfun(@times,I,1-mask);
else
I=max(I,0);
M=max(M,0);
amount=amount*0.01;
amount=min(max(0.01,amount),1);
mask=((1-Iy)+My-(1-amount))/(2*amount);
mask=max(min(mask,1),0);
p=2.4;
R=(bsxfun(@times,M.^(p),mask) + bsxfun(@times,I.^(p),1-mask)).^(1/p);
end
case 'darkeny'
factors=gettfm('luma');
My=sum(bsxfun(@times,M,factors),3);
Iy=sum(bsxfun(@times,I,factors),3);
if amount==1;
mask=My>Iy;
R=bsxfun(@times,M,1-mask) + bsxfun(@times,I,mask);
else
I=max(I,0);
M=max(M,0);
amount=amount*0.01;
amount=min(max(0.01,amount),1);
mask=((1-Iy)+My-(1-amount))/(2*amount);
mask=max(min(mask,1),0);
p=2.4;
R=(bsxfun(@times,M.^(1/p),1-mask) + bsxfun(@times,I.^(1/p),mask)).^(p);
end
% distance modes should probably also change alpha when in fg/bg mode
% luma is weighted more to keep appearance good
case {'near','nearbg','nearfg'}
if size(M,3)==3
A=gettfm('ypbpr');
My=sum(bsxfun(@times,M,A(1,:,:)),3);
Mpb=sum(bsxfun(@times,M,A(2,:,:)),3);
Mpr=sum(bsxfun(@times,M,A(3,:,:)),3);
Iy=sum(bsxfun(@times,I,A(1,:,:)),3);
Ipb=sum(bsxfun(@times,I,A(2,:,:)),3);
Ipr=sum(bsxfun(@times,I,A(3,:,:)),3);
D=sqrt(25*(My-Iy).^2 + (Mpb-Ipb).^2 + (Mpr-Ipr).^2)<=(amount*5*1.27);
else
D=abs(M-I)<=amount;
end
if strcmp(modestring,'nearfg')
R=zeros(size(M));
R=bsxfun(@times,R,1-D) + bsxfun(@times,M,D);
elseif strcmp(modestring,'nearbg')
R=zeros(size(M));
R=bsxfun(@times,R,1-D) + bsxfun(@times,I,D);
else
R=bsxfun(@times,I,1-D) + bsxfun(@times,M,D);
end
case 'far'
if size(M,3)==3
A=gettfm('ypbpr');
My=sum(bsxfun(@times,M,A(1,:,:)),3);
Mpb=sum(bsxfun(@times,M,A(2,:,:)),3);
Mpr=sum(bsxfun(@times,M,A(3,:,:)),3);
Iy=sum(bsxfun(@times,I,A(1,:,:)),3);
Ipb=sum(bsxfun(@times,I,A(2,:,:)),3);
Ipr=sum(bsxfun(@times,I,A(3,:,:)),3);
D=sqrt(25*(My-Iy).^2 + (Mpb-Ipb).^2 + (Mpr-Ipr).^2)>(amount*5*1.27);
else
D=abs(M-I)>amount;
end
if strcmp(modestring,'farfg')
R=zeros(size(M));
R=bsxfun(@times,R,1-D) + bsxfun(@times,M,D);
elseif strcmp(modestring,'farbg')
R=zeros(size(M));
R=bsxfun(@times,R,1-D) + bsxfun(@times,I,D);
else
R=bsxfun(@times,I,1-D) + bsxfun(@times,M,D);
end
% replace BG == color areas with FG
case 'replacecolor'
if numel(amount)==1
amount=[1 1 1]*max(min(amount,1),0);
end
mhi=all(bsxfun(@le,I,reshape(amount+0.01,[1 1 3])),3);
mlo=all(bsxfun(@ge,I,reshape(amount-0.01,[1 1 3])),3);
m=mhi & mlo;
R=bsxfun(@times,I,1-m) + bsxfun(@times,M,m);
% apply only FG content == color
% same as replacecolor transposed
case 'excludecolor'
if numel(amount)==1
amount=[1 1 1]*max(min(amount,1),0);
end
mhi=all(bsxfun(@le,M,reshape(amount+0.01,[1 1 3])),3);
mlo=all(bsxfun(@ge,M,reshape(amount-0.01,[1 1 3])),3);
m=~(mhi & mlo);
R=bsxfun(@times,I,1-m) + bsxfun(@times,M,m);
case 'multiply'
R=M.*I;
case 'screen'
R=1-((1-M).*(1-I));
case {'division','divide'}
R=I./(M+eps);
case {'addition','add'} % same as lineardodge
R=M+I;
case {'subtraction','subtract'}
R=I-M;
case 'difference'
R=abs(M-I);
case {'equivalence','phoenix'}
R=1 - abs(I-M);
case 'exclusion'
R=M+I-2*M.*I;
case 'negation'
R=1-abs(amount-M-I);
case 'extremity' % inverse of negation
R=abs(amount-M-I);
case 'grainextract'
R=I-M+0.5;
case 'grainmerge'
R=I+M-0.5;
case 'interpolate'
R=0.25-cos(M*pi)/4 + 0.25-cos(I*pi)/4;
case {'average','allanon'}
R=(M+I)/2;
case 'pnorm' % default is sum, generally p-norm for p=(amount)
amount=max(0,amount);
if amount~=1
I=max(I,0);
M=max(M,0);
end
R=(M.^(amount) + I.^(amount)).^(1/(amount));
case 'geometric' % geometric mean
R=sqrt(max(M,0).*max(I,0));
case 'mesh' % apply a user-supplied transfer function
amount=max(0,min(1,amount));
meshh=size(amount,1);
meshw=size(amount,2);
if meshh==1 || meshw==1
if ~quiet
disp('IMBLEND: AMOUNT parameter must be at least 2x2 for mesh mode. Default is eye(4)');
end
amount=eye(4);
meshh=size(amount,1);
meshw=size(amount,2);
end
% amount=flipud(amount); % flip this if you can't stand the array orientation convention
[bg fg]=meshgrid(0:1/(meshw-1):1,0:1/(meshh-1):1);
R=zeros(size(I));
if size(I,3)==3 && size(amount,3)==3
for c=1:size(I,3)
R(:,:,c)=interp2(bg,fg,amount(:,:,c),I(:,:,c),M(:,:,c),'bilinear');
end
else
for c=1:size(I,3)
R(:,:,c)=interp2(bg,fg,amount(:,:,1),I(:,:,c),M(:,:,c),'bilinear');
end
end
case 'hardmesh' % apply a user-supplied transfer function
amount=max(0,min(1,amount));
meshh=size(amount,1);
meshw=size(amount,2);
if meshh==1 || meshw==1
if ~quiet
disp('IMBLEND: AMOUNT parameter must be at least 2x2 for mesh mode. Default is eye(4)');
end
amount=eye(4);
meshh=size(amount,1);
meshw=size(amount,2);
end
% amount=flipud(amount); % flip this if you can't stand the array orientation convention
[bg fg]=meshgrid(0:1/(meshw-1):1,0:1/(meshh-1):1);
R=zeros(size(I));
if size(I,3)==3 && size(amount,3)==3
for c=1:size(I,3)
R(:,:,c)=interp2(bg,fg,amount(:,:,c),I(:,:,c),M(:,:,c),'nearest');
end
else
for c=1:size(I,3)
R(:,:,c)=interp2(bg,fg,amount(:,:,1),I(:,:,c),M(:,:,c),'nearest');
end
end
case 'bomb' % apply a random transfer function (independent channels)
amount=max(1,round(amount));
c=0:1/amount:1;
[bg fg]=meshgrid(c,c);
tf=imadjustFB(rand([size(bg) size(I,3)]));
R=zeros(size(I));
for c=1:size(I,3)
R(:,:,c)=interp2(bg,fg,tf(:,:,c),I(:,:,c),M(:,:,c),'bilinear');
end
if verbose
tfstring='cat(3,';
for c=1:size(tf,3)
tfstring=[tfstring mat2str(tf(:,:,c),5)];
if c~=3; tfstring=[tfstring ',']; end
end
tfstring=[tfstring ');'];
disp(['TF for ''bomb'' op: ' tfstring])
end
case 'bomblocked' % apply a random transfer function (locked channels)
amount=max(1,round(amount));
c=0:1/amount:1;
[bg fg]=meshgrid(c,c);
R=zeros(size(I));
tf=imadjustFB(rand(size(bg)));
for c=1:size(I,3)
R(:,:,c)=interp2(bg,fg,tf,I(:,:,c),M(:,:,c),'bilinear');
end
if verbose
disp(['TF for ''bomblocked'' op: ' mat2str(tf,5)])
end
case 'hardbomb'
amount=max(1,round(amount));
c=0:1/amount:1;
[bg fg]=meshgrid(c,c);
tf=imadjustFB(rand([size(bg) size(I,3)]));
R=zeros(size(I));
for c=1:size(I,3)
R(:,:,c)=interp2(bg,fg,tf(:,:,c),I(:,:,c),M(:,:,c),'nearest');
end
if verbose
tfstring='cat(3,';
for c=1:size(tf,3)
tfstring=[tfstring mat2str(tf(:,:,c),5)];
if c~=3; tfstring=[tfstring ',']; end
end
tfstring=[tfstring ');'];
disp(['TF for ''hardbomb'' op: ' tfstring])
end
case 'hue' % bounded LCHab operation
Mlch=rgb2lch(M,'lab');
Rlch=rgb2lch(I,'lab');
Rlch(:,:,3)=Mlch(:,:,3);
R=lch2rgb(Rlch,'lab','truncatelch');
case 'saturation' % bounded LCHab operation
Mlch=rgb2lch(M,'lab');
Rlch=rgb2lch(I,'lab');
Rlch(:,:,2)=Mlch(:,:,2);
R=lch2rgb(Rlch,'lab','truncatelch');
% these are thresholding methods
case 'saturate'
amount=max(amount,0);
Mlch=rgb2lch(M,'lab');
Rlch=rgb2lch(I,'lab');
Rlch(:,:,2)=max(Rlch(:,:,2),Mlch(:,:,2)*amount);
R=lch2rgb(Rlch,'lab','truncatelch');
case 'desaturate'
amount=max(amount,0);
Mlch=rgb2lch(M,'lab');
Rlch=rgb2lch(I,'lab');
Rlch(:,:,2)=min(Rlch(:,:,2),Mlch(:,:,2)*amount);
R=lch2rgb(Rlch,'lab','truncatelch');
% COLOR BLEND MODES
% COLOR_HSL matches legacy GIMP mode
% COLOR_HSY uses a chroma-normalized variant of YPbPr
case 'color' % swap H & S in HSL; preserve initial Y
A=gettfm('ypbpr');
Ai=gettfm('ypbpr_inv');
Y=sum(bsxfun(@times,I,A(1,:,:)),3);
Mhsl=rgb2hsl(M);
Rhsl=rgb2hsl(I);
Rhsl(:,:,1:2)=Mhsl(:,:,1:2);
R=hsl2rgb(Rhsl);
Rpb=sum(bsxfun(@times,R,A(2,:,:)),3);
Rpr=sum(bsxfun(@times,R,A(3,:,:)),3);
Rypp=cat(3,Y,Rpb,Rpr);
R(:,:,1)=sum(bsxfun(@times,Rypp,Ai(1,:,:)),3);
R(:,:,2)=sum(bsxfun(@times,Rypp,Ai(2,:,:)),3);
R(:,:,3)=sum(bsxfun(@times,Rypp,Ai(3,:,:)),3);
case 'colorhsyp' % swap H & S in HSYp
Mhsy=rgb2hsy(M,'pastel');
Rhsy=rgb2hsy(I,'pastel');
Rhsy(:,:,1:2)=Mhsy(:,:,1:2);
R=hsy2rgb(Rhsy,'pastel');
case 'colorlchab' % bounded LCHab operation
Mlch=rgb2lch(M,'lab');
Rlch=rgb2lch(I,'lab');
Rlch(:,:,2:3)=Mlch(:,:,2:3);
R=lch2rgb(Rlch,'lab','truncatelch');
case 'colorlchsr' % bounded SRLAB2 operation
Mlch=rgb2lch(M,'srlab');
Rlch=rgb2lch(I,'srlab');
Rlch(:,:,2:3)=Mlch(:,:,2:3);
R=lch2rgb(Rlch,'srlab','truncatelch');
case 'colorhsl' % swap H & S in HSL
Mhsl=rgb2hsl(M);
Rhsl=rgb2hsl(I);
Rhsl(:,:,1:2)=Mhsl(:,:,1:2);
R=hsl2rgb(Rhsl);
% V=max([R G B])
% L=mean(max([R G B]),min([R G B]))
% I=mean([R G B])
% Y=[0.299 0.587 0.114]*[R G B]' or whatever R709 is
case 'value'
Mhsv=rgb2hsv(M);
Rhsv=rgb2hsv(I);
Rhsv(:,:,3)=Mhsv(:,:,3);
R=hsv2rgb(Rhsv);
case {'luma', 'luma1', 'luma2'} % swaps fg bg luma
A=gettfm('ypbpr');
Ai=gettfm('ypbpr_inv');
My=sum(bsxfun(@times,M,A(1,:,:)),3);
Ipb=sum(bsxfun(@times,I,A(2,:,:)),3);
Ipr=sum(bsxfun(@times,I,A(3,:,:)),3);
Rypp=cat(3,My,Ipb,Ipr);
R=zeros(size(M));
R(:,:,1)=sum(bsxfun(@times,Rypp,Ai(1,:,:)),3);
R(:,:,2)=sum(bsxfun(@times,Rypp,Ai(2,:,:)),3);
R(:,:,3)=sum(bsxfun(@times,Rypp,Ai(3,:,:)),3);
case 'lightness' % swaps fg bg lightness
Mhsl=rgb2hsl(M);
Rhsl=rgb2hsl(I);
Rhsl(:,:,3)=Mhsl(:,:,3);
R=hsl2rgb(Rhsl);
case 'intensity' % swaps fg bg intensity
Mhsi=rgb2hsi(M);
Rhsi=rgb2hsi(I);
Rhsi(:,:,3)=Mhsi(:,:,3);
R=hsi2rgb(Rhsi);
% SCALE ADD treats FG as an additive gain map with a null point at its mean
case 'scaleadd'
% RGB independent limits
%Mstretch=imadjustFB(M,stretchlimFB(M));
% RGB average limits
Mstretch=imadjustFB(M,mean(stretchlimFB(M,0.001),2)',[0; 1],1);
sf=amount(1);
if numel(amount)>1
centercolor=amount(2:end);
if size(M,3)>numel(centercolor)
centercolor=repmat(centercolor(1),[1 size(M,3)]);
end
else
centercolor=mean(mean(Mstretch,1),2);
end
R=zeros(size(I));
for c=1:1:size(M,3);
R(:,:,c)=I(:,:,c)+(Mstretch(:,:,c)-centercolor(c))*sf;
end
% SCALE MULT treats FG as a gain map with a null point at its mean
case 'scalemult'
% RGB independent limits
%Mstretch=imadjustFB(M,stretchlimFB(M));
% RGB average limits
Mstretch=imadjustFB(M,mean(stretchlimFB(M,0.001),2)',[0; 1],1);
amount=max(amount,0);
sf=amount(1);
if numel(amount)>1
centercolor=amount(2:end);
if size(M,3)>numel(centercolor)
centercolor=repmat(centercolor(1),[1 size(M,3)]);
end
else
centercolor=mean(mean(Mstretch,1),2);
end
R=zeros(size(I));
for c=1:1:size(M,3);
R(:,:,c)=I(:,:,c).*(Mstretch(:,:,c)./(centercolor(c)+eps))*sf;
end
% CONTRAST uses a stretched copy of FG to map [IN_LO and IN_HI] for stretching BG contrast
% treats FG as a gain map with a null point at its mean
case 'contrast'
% RGB independent limits
%Mstretch=imadjustFB(M,stretchlimFB(M));
% RGB average limits
Mstretch=imadjustFB(M,mean(stretchlimFB(M,0.001),2)',[0; 1],1);
amount=max(amount,0);
sf=amount(1);
if numel(amount)>1
centercolor=amount(2:end);
if size(M,3)>numel(centercolor)
centercolor=repmat(centercolor(1),[1 size(M,3)]);
end
else
centercolor=mean(mean(Mstretch,1),2);
end
R=zeros(size(I));
for c=1:1:size(M,3);
lo=-min(Mstretch(:,:,c)-centercolor(c),0)*sf;
hi=1-max(Mstretch(:,:,c)-centercolor(c),0)*sf;
R(:,:,c)=(I(:,:,c)-lo)./max(hi-lo,0);
end
% this implements direct contrast mapping
case 'curves'
I=min(max(I,0),1);
ko=amount(1);
switch numel(amount)
case 1
os=0; c=0.5;
case 2
os=amount(2); c=0.5;
otherwise
os=amount(2); c=max(min(amount(3),1),0);
end
k=ko*M+os;
mk=abs(ko+os)<1;
mc=c<0.5;
if ~xor(mk,mc)
pp=k; kk=k*c/(1-c);
else
kk=k; pp=(1-c)*k/c;
end
hi=I>c;
R=(1-0.5*((1-I)*(1/(1-c))).^pp).*hi ...
+ (0.5*((1/c)*I).^kk).*~hi;
% quadratic modes
% i guess they aren't really quadratic when you change 'amount'
% quadratic modes are semi-complementary when parameterized
% reflect(amount)=1-heat(-(amount+1))
% freeze(amount)=1-glow(-(amount+1))
% frect(amount)=1-gleat(-(amount+1))
% reeze(amount)=1-helow(-(amount+1))
case 'reflect'
I=I*0.995;
if amount==1 % faster for trivial case
R=min(1,(M.^2./(1-I+eps)));
else
M=max(M,0);
I=min(I,1);
R=min(1,(M.^(amount+1)./(1-I+eps).^(amount)));
end
case 'glow'
M=M*0.995;
if amount==1 % faster for trivial case
R=min(1,(I.^2./(1-M+eps)));
else
M=min(M,1);
I=max(I,0);
R=min(1,(I.^(amount+1)./(1-M+eps).^amount));
end
case 'freeze'
I=0.005+I*0.995;
if amount==1 % faster for trivial case
R=1-min(1,((1-M).*(1-M)./I+eps));
else
I=max(I,0);
R=1-min(1,((1-M).^(amount+1)./(I+eps).^amount));
end
case 'heat'
M=0.005+M*0.995;
if amount==1 % faster for trivial case
R=1-min(1,((1-I).*(1-I)./M+eps));
else
M=max(M,0);
R=1-min(1,((1-I).^(amount+1)./(M+eps).^amount));
end
% complementary quadratic modes
case 'frect' % same as 'helow' with layers swapped
hi=M>=1-I;
I=min(1,I+0.0001);
if amount==1
R=(1-min(1,((1-M).*(1-M)./I))).*hi ...
+ (min(1,(M.^2./(1-I)))).*~hi;
else
M=max(M,0);
R=(1-min(1,((1-M).^(amount+1)./I.^amount))).*hi ...
+ (min(1,(M.^(amount+1)./(1-I).^(amount)))).*~hi;
end
case 'reeze' % same as 'gleat' with layers swapped
hi=M>=1-I;
I=0.005+I*0.99;
if amount==1
R=(min(1,(M.^2./(1-I)))).*hi ...
+ (1-min(1,((1-M).*(1-M)./I))).*~hi;
else
I=max(min(I,1),0);
M=max(min(M,1),0);
R=(min(1,(M.^(amount+1)./(1-I).^(amount)))).*hi ...
+ (1-min(1,((1-M).^(amount+1)./I.^amount))).*~hi;
end
case 'gleat' % compare to 'vividlight'
hi=M>1-I;
M=0.005+M*0.99;
if amount==1
R=(min(1,(I.*I./(1-M)))).*hi ...
+ (1-min(1,((1-I).*(1-I)./M))).*~hi;
else
I=max(min(I,1),0);
M=max(min(M,1),0);
R=(min(1,(I.^(amount+1)./(1-M).^amount))).*hi ...
+ (1-min(1,((1-I).^(amount+1)./M.^amount))).*~hi;
end
case 'helow' % compare to 'overlay' and 'softlight' for amt=0.4-0.6
hi=M>1-I;
M=min(1,M+0.0001);
if amount==1
R=(1-min(1,((1-I).*(1-I)./M))).*hi ...
+ (min(1,(I.*I./(1-M)))).*~hi;
else
I=max(I,0);
R=(1-min(1,((1-I).^(amount+1)./M.^amount))).*hi ...
+ (min(1,(I.^(amount+1)./(1-M).^amount))).*~hi;
end
% MATH OPS FROM KRITA
% allanon = average
% equivalence = phoenix
% inversesubtract = linearburn
% greater = alpha thresholding
case 'gammalight'
amount=max(eps,amount);
R=max(I,0).^(amount*M);
case 'gammadark'
amount=max(eps,amount);
R=max(I,0).^(1./(amount*M));
% 'parallel' is actually the harmonic mean instead. good job, krita.
case {'harmonic','parallel'} % practically equivalent to geometric
R=2./(1./I+1./M);
case {'sqrtdiff','additivesubtractive'}
R=abs(sqrt(I)-sqrt(M));
case 'invsqrtdiff'
R=1-abs(sqrt(1-I)-sqrt(1-M));
case 'arctan'
R=2*atan(I./M)./pi;
% Unverified modes attributed to EffectBank/illusions.hu
case 'light'
M=max(M,0);
R=I.*(1-M)+sqrt(M);
case 'shadow'
M=min(M,1);
R=(1-((1-I).*M+sqrt(1-M)));
case 'bright'
lo=M<0.5;
R=(1-(1-M).*M-(1-I).*(1-M)).*lo ...
+ (M-(1-I).*(1-M)+(1-M).^2).*~lo;
case 'dark'
lo=M<0.5;
R=(M.*(1-M)+M.*I).*lo ...
+ (M.*I+M-M.^2).*~lo;
case 'lighteneb'
R=1-log2(1+(1-I)./(8*M));
case 'darkeneb'
R=log2(1+I./(8*(1-M)));
case 'bleach' % these are inverted linear dodge/burn
R=(1-I)+(1-M)-1;
case 'stain'
R=2-I-M;
otherwise
% PARAMETRIC COMPONENT MODES
if numel(modestring)>=11 && strcmp(modestring(1:8),'transfer')
% CHANNEL TRANSFER
com=modestring(9:end);
com=com(com~='_');
[inchan outchan]=strtok(com,'>');
outchan=outchan(outchan~='>');
R=I;
switch inchan
case 'r'
pass=M(:,:,1);
case 'g'
pass=M(:,:,2);
case 'b'
pass=M(:,:,3);
case 'a'
if any([FGhasalpha BGhasalpha]==1)
pass=FGA(:,:,:,f);
else
error('IMBLEND: inputs have no alpha to transfer')
end
case 'hhsl'
Mhsl=rgb2hsl(M);
pass=Mhsl(:,:,1)/360;
case 'shsl'
Mhsl=rgb2hsl(M);
pass=Mhsl(:,:,2);
case 'lhsl'
Mhsl=rgb2hsl(M);
pass=Mhsl(:,:,3);
case 'hhsi'
Mhsi=rgb2hsi(M);
pass=Mhsi(:,:,1)/360;
case 'shsi'
Mhsi=rgb2hsi(M);
pass=Mhsi(:,:,2);
case {'ihsi','i'}
Mhsi=rgb2hsi(M);
pass=Mhsi(:,:,3);
case 'hhsv'
Mhsv=rgb2hsv(M);
pass=Mhsv(:,:,1);
case 'shsv'
Mhsv=rgb2hsv(M);
pass=Mhsv(:,:,2);
case {'vhsv','v'}
Mhsv=rgb2hsv(M);
pass=Mhsv(:,:,3);
case {'llch','l'}
Mlch=rgb2lch(M,'lab');
pass=Mlch(:,:,1)/100;
case {'clch','c'}
Mlch=rgb2lch(M,'lab');
pass=Mlch(:,:,2)/134.2;
case 'hlch'
Mlch=rgb2lch(M,'lab');
pass=Mlch(:,:,3)/360;
case 'hhusl'
Mhusl=rgb2husl(M);
pass=Mhusl(:,:,1)/360;
case 'shusl'
Mhusl=rgb2husl(M);
pass=Mhusl(:,:,2)/100;
case 'lhusl'
Mhusl=rgb2husl(M);
pass=Mhusl(:,:,3)/100;
case {'y','yhsy','yhsyp'}
factors=gettfm('luma');
pass=sum(bsxfun(@times,M,factors),3);
case {'hhsy','hhsyp'}
Mhsy=rgb2hsy(M);
pass=Mhsy(:,:,1)/360;
case 'shsy'
Mhsy=rgb2hsy(M);
pass=Mhsy(:,:,2);
case 'shsyp'
Mhsy=rgb2hsy(M,'pastel');
pass=Mhsy(:,:,2);
otherwise
error('IMBLEND: unknown INCHAN parameter ''%s'' for TRANSFER mode',inchan);
end
switch outchan
case 'r'
R(:,:,1)=pass;
case 'g'
R(:,:,2)=pass;
case 'b'
R(:,:,3)=pass;
case 'a'
FGA(:,:,:,f)=pass;
case 'hhsl'
Rhsl=rgb2hsl(R);
Rhsl(:,:,1)=pass*360;
R=hsl2rgb(Rhsl);
case 'shsl'
Rhsl=rgb2hsl(R);
Rhsl(:,:,2)=pass;
R=hsl2rgb(Rhsl);
case 'lhsl'
Rhsl=rgb2hsl(R);
Rhsl(:,:,3)=pass;
R=hsl2rgb(Rhsl);
case 'hhsi'
Rhsi=rgb2hsi(R);
Rhsi(:,:,1)=pass*360;
R=hsi2rgb(Rhsi);
case 'shsi'
Rhsi=rgb2hsi(R);
Rhsi(:,:,2)=pass;
R=hsi2rgb(Rhsi);
case {'ihsi','i'}
Rhsi=rgb2hsi(R);
Rhsi(:,:,3)=pass;
R=hsi2rgb(Rhsi);
case 'hhsv'
Rhsv=rgb2hsv(R);
Rhsv(:,:,1)=pass;
R=hsv2rgb(Rhsv);
case 'shsv'
Rhsv=rgb2hsv(R);
Rhsv(:,:,2)=pass;
R=hsv2rgb(Rhsv);
case {'vhsv','v'}
Rhsv=rgb2hsv(R);
Rhsv(:,:,3)=pass;
R=hsv2rgb(Rhsv);
case {'llch','l'}
Rlch=rgb2lch(R,'lab');
Rlch(:,:,1)=pass*100;
R=lch2rgb(Rlch,'lab','truncatelch');
case {'clch','c'}
Rlch=rgb2lch(R,'lab');
Rlch(:,:,2)=pass*134.2;
R=lch2rgb(Rlch,'lab','truncatelch');
case 'hlch'
Rlch=rgb2lch(R,'lab');
Rlch(:,:,3)=pass*360;
R=lch2rgb(Rlch,'lab','truncatelch');
case 'hhusl'
Rhusl=rgb2husl(R);
Rhusl(:,:,1)=pass*360;
R=husl2rgb(Rhusl);
case 'shusl'
Rhusl=rgb2husl(R);
Rhusl(:,:,2)=pass*100;
R=husl2rgb(Rhusl);
case 'lhusl'
Rhusl=rgb2husl(R);
Rhusl(:,:,3)=pass*100;
R=husl2rgb(Rhusl);
case {'y','yhsy','yhsyp'}
Rhsy=rgb2hsy(R);
Rhsy(:,:,3)=pass;
R=hsy2rgb(Rhsy);
case {'hhsy','hhsyp'}
Rhsy=rgb2hsy(R);
Rhsy(:,:,1)=pass*360;
R=hsy2rgb(Rhsy);
case 'shsy'
Rhsy=rgb2hsy(R);
Rhsy(:,:,2)=pass;
R=hsy2rgb(Rhsy);
case 'shsyp'
Rhsy=rgb2hsy(R,'pastel');
Rhsy(:,:,2)=pass;
R=hsy2rgb(Rhsy,'pastel');
otherwise
error('IMBLEND: unknown OUTCHAN parameter ''%s'' for TRANSFER mode',outchan);
end
elseif numel(modestring)>=10 && strcmp(modestring(1:7),'permute')
% HUE/COLOR PERMUTATION
com=modestring(8:end);
[inchan outchan]=strtok(com,'>');
outchan=outchan(outchan~='>');
Rhusl=rgb2husl(I);
Rhusl(:,:,1)=Rhusl(:,:,1)/360;
Rhusl(:,:,2)=Rhusl(:,:,2)/100;
Rhusl(:,:,3)=Rhusl(:,:,3)/100;
switch inchan
case 'h'
Mhusl=rgb2husl(M);
pass=Mhusl(:,:,1)/360;
case 'dh'
Mhusl=rgb2husl(M);
pass=Rhusl(:,:,1)-Mhusl(:,:,1)/360;
case 's'
Mhusl=rgb2husl(M);
pass=Mhusl(:,:,2)/100;
case 'ds'
Mhusl=rgb2husl(M);
pass=Rhusl(:,:,2)-Mhusl(:,:,2)/100;
case 'y'
factors=gettfm('luma');
pass=sum(bsxfun(@times,M,factors),3);
case 'dy'
factors=gettfm('luma');
Ym=sum(bsxfun(@times,M,factors),3);
Yi=sum(bsxfun(@times,I,factors),3);
pass=Yi-Ym;
otherwise
error('IMBLEND: unknown INCHAN parameter ''%s'' for PERMUTE mode',inchan);
end
switch outchan
case 'h'
Rhusl(:,:,1)=mod(Rhusl(:,:,1)+pass*amount,1)*360;
Rhusl(:,:,2)=Rhusl(:,:,2)*100;
Rhusl(:,:,3)=Rhusl(:,:,3)*100;
R=husl2rgb(Rhusl);
case 'hs'
if any(inchan=='y')
Mhusl=rgb2husl(M);
Mhusl(:,:,1)=Mhusl(:,:,1)/360;
Mhusl(:,:,2)=Mhusl(:,:,2)/100;
end
amt=max(min(abs(amount),1),0); % needed since S-blending has limited range
Rhusl(:,:,1)=mod(Rhusl(:,:,1)+pass*amount,1)*360;
Rhusl(:,:,2)=amt*Mhusl(:,:,2)+(1-amt)*Rhusl(:,:,2);
Rhusl(:,:,2)=Rhusl(:,:,2)*100;
Rhusl(:,:,3)=Rhusl(:,:,3)*100;
R=husl2rgb(Rhusl);
otherwise
error('IMBLEND: unknown OUTCHAN parameter ''%s'' for PERMUTE mode',outchan);
end
else
error('IMBLEND: unknown blend mode ''%s''',modestring);
end
end
switch colormodel
case 'ypbpr'
Ai=gettfm('ypbpr_inv');
Y=R(:,:,1);
C=R(:,:,2);
H=R(:,:,3)*2*pi; % rescale H
% clamp at max chroma
Cnorm=maxchroma('ypp','luma',Y,'hue',H);
C=min(C,Cnorm);
Rw(:,:,1)=Y;
Rw(:,:,2)=C.*cos(H); % B
Rw(:,:,3)=C.*sin(H); % R
R(:,:,1)=sum(bsxfun(@times,Rw,Ai(1,:,:)),3);
R(:,:,2)=sum(bsxfun(@times,Rw,Ai(2,:,:)),3);
R(:,:,3)=sum(bsxfun(@times,Rw,Ai(3,:,:)),3);
case 'hsy'
R(:,:,1)=R(:,:,1)*360;
R=hsy2rgb(R,'normal');
end
if images==1
outpict=R;
else
outpict(:,:,:,f)=R;
end
end
end
outpict=max(min(outpict,1),0);
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% handle alpha compositing %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if ~strcmp(compositionmode,'gimp')
if all([FGhasalpha BGhasalpha]==1)
if ~isempty(find(strcmp(compositionmode,{'srcover','srcatop','srcin','srcout','dstover', ...
'dstatop','dstin','dstout','xor'}),1))
% stretch alpha if desired
if camount>1
FGA=FGA==1;
elseif camount==0
FGA=FGA~=0;
elseif camount<1
FGA=FGA>=camount;
end
end
else
% there's not much point in using these modes this way, but just in case ...
% some composite-only modes will generate nonopaque output when both inputs are opaque
if (~isempty(find(strcmp(compositionmode,{'srcin','dstatop','dstin'}),1)) && opacity~=1) ...
|| (~isempty(find(strcmp(compositionmode,{'dstout','xor'}),1)) && opacity~=0) ...
|| strcmp(compositionmode,'srcout')
% expand and force alpha mode
if ~quiet
disp('IMBLEND: using this mode and opacity with I/RGB inputs will produce IA/RGBA output')
end
FGhasalpha=1;
sFG=size(FG);
FGA=ones([sFG(1:2) 1 size(FG,4)]);
BGA=FGA;
end
end
end
if any([FGhasalpha BGhasalpha]==1)
switch compositionmode
case 'gimp'
% this is configured to match legacy (ver<=2.8) GIMP behavior
% don't ask me to justify the propriety of these methods
% this was based on paint-funcs.c and gimp-composite-generic.c
% and tweaked to match observed output
if strcmp(blendmode,'normal')
FGA=FGA*opacity;
outA=BGA+(1-BGA).*FGA; % FGA when BGA=0; 0 when both=0
ratio=FGA./(outA+eps);
outpict=bsxfun(@times,outpict,ratio) + bsxfun(@times,BG,(1-ratio));
% when outA=0, gimp sets outRGB=0
% otherwise, this retains BG
outpict(outA==0)=0;
outpict=cat(3,outpict,outA);
else
FGA=min(FGA,BGA); % << why this?
FGA=FGA*opacity;
outA=BGA+(1-BGA).*FGA;
ratio=FGA./(outA+eps);
outpict=bsxfun(@times,outpict,ratio) + bsxfun(@times,BG,(1-ratio));
% when BGA=0, gimp sets outRGB=0
% otherwise this retains BG
outpict(BGA==0)=0;
outpict=cat(3,outpict,BGA);
end
% Porter-Duff compositing
% SVG 1.2 blend modes are essentially SRC-OVER, with the Ab term (i.e. Sa.*Da)
% buried algebraically in the blend math. e.g.
% MULTIPLY: Dca' = Sca × Dca + Sca × (1 - Da) + Dca × (1 - Sa)
% = (Sc.*Dc).*Ab + Sc.*As + Dc.*Ad
% if we're not using premultiplied alpha then we can just do that here for now
% some modes can't be optimized, but this simplifies GIMP mode compatibility
case 'srcover'
if strcmp(blendmode,'normal')
FGA=FGA*opacity;
As=FGA;
Ad=BGA.*(1-FGA);
outpict=bsxfun(@times,As,FG) ...
+ bsxfun(@times,Ad,BG);
outA=As+Ad;
else
FGA=FGA*opacity;
As=FGA.*(1-BGA);
Ad=BGA.*(1-FGA);
Ab=FGA.*BGA;
outpict=bsxfun(@times,As,FG) ...
+ bsxfun(@times,Ad,BG) ...
+ bsxfun(@times,Ab,outpict);
outA=As+Ad+Ab;
end
case 'srcatop'
FGA=FGA*opacity;
Ad=BGA.*(1-FGA);
Ab=FGA.*BGA;
outpict=bsxfun(@times,Ad,BG) ...
+ bsxfun(@times,Ab,outpict);
outA=BGA;
case 'srcin'
FGA=FGA*opacity;
Ab=FGA.*BGA;
outpict=bsxfun(@times,Ab,outpict);
outA=Ab;
case 'srcout'
FGA=FGA*opacity;
As=FGA.*(1-BGA);
outpict=bsxfun(@times,As,FG);
outA=As;
case 'dstover'
FGA=FGA*opacity;
As=FGA.*(1-BGA);
Ad=BGA;
outpict=bsxfun(@times,As,FG) ...
+ bsxfun(@times,Ad,BG);
outA=As+Ad;
case 'dstatop'
FGA=FGA*opacity;
As=FGA.*(1-BGA);
Ab=FGA.*BGA;
outpict=bsxfun(@times,As,FG) ...
+ bsxfun(@times,Ab,BG);
outA=FGA;
case 'dstin'
FGA=FGA*opacity;
Ab=FGA.*BGA;
outpict=bsxfun(@times,Ab,BG);
outA=Ab;
case 'dstout'
FGA=FGA*opacity;
Ad=BGA.*(1-FGA);
outpict=bsxfun(@times,Ad,BG);
outA=Ad;
case 'xor'
FGA=FGA*opacity;
As=FGA.*(1-BGA);
Ad=BGA.*(1-FGA);
outpict=bsxfun(@times,As,FG) ...
+ bsxfun(@times,Ad,BG);
outA=As+Ad;
case 'translucent'
% http://ssp.impulsetrain.com/translucency.html
FGA=FGA*opacity;
outpict=invgammac(outpict);
BG=invgammac(BG);
FGp=bsxfun(@times,FGA,outpict);
BGp=bsxfun(@times,BGA,BG);
outpict=FGp + bsxfun(@times,(1-FGA).^2,BGp) ./ (1-(FGp.*BGp)+eps);
outpict=gammac(outpict);
outA=FGA + ((1-FGA).^2.*BGA)./(1-FGA.*BGA+eps);
case {'dissolve','dissolvezf','dissolveord','lindissolve','lindissolvezf','lindissolveord'}
switch compositionmode
case 'dissolve'
FGA=(rand(size(FGA))+eps<=FGA*camount)*opacity;
case 'dissolvezf'
for f=1:images
FGA(:,:,:,f)=zfdither(FGA(:,:,:,f)*camount)*opacity;
end
case 'dissolveord'
for f=1:images
FGA(:,:,:,f)=orddither(FGA(:,:,:,f)*camount)*opacity;
end
case 'lindissolve'
FGA=(rand(size(FGA))+eps<=camount).*FGA*opacity;
case 'lindissolvezf'
for f=1:images
FGA(:,:,:,f)=zfdither(ones(sFG(1:2))*camount).*FGA(:,:,:,f)*opacity;
end
case 'lindissolveord'
for f=1:images
FGA(:,:,:,f)=orddither(ones(sFG(1:2))*camount).*FGA(:,:,:,f)*opacity;
end
end
As=FGA.*(1-BGA);
Ad=BGA.*(1-FGA);
Ab=FGA.*BGA;
outpict=bsxfun(@times,As,FG) ...
+ bsxfun(@times,Ad,BG) ...
+ bsxfun(@times,Ab,outpict);
outA=As+Ad+Ab;
otherwise
% this shouldn't ever execute since keys are matched
error('IMBLEND: unknown composition mode ''%s''',compositionmode);
end
if ~strcmp(compositionmode,'gimp')
outpict=bsxfun(@rdivide,outpict,outA+eps);
outpict=cat(3,outpict,outA);
end
else
switch compositionmode
% if no alpha is present, do regular opacity mixdown
% when Sa,Da==1, both GIMP and SRC-OVER methods collapse to this
case {'gimp','srcover','srcatop'}
if opacity~=1 % don't waste time if opaque
outpict=opacity*outpict + BG*(1-opacity);
end
% VALID FOR 'srcin' ONLY WHEN OPACITY==1
case 'srcin'
% outpict=outpict; NOP
% VALID FOR 'dstout' and 'xor' ONLY WHEN OPACITY==0
% VALID FOR 'dstin' and 'dstatop' ONLY WHEN OPACITY==1
case {'dstout','dstin','dstatop','xor','dstover'}
outpict=BG;
case 'translucent'
if opacity~=1
outpict=invgammac(outpict);
BG=invgammac(BG);
FGp=opacity*outpict;
outpict=FGp + (1-opacity)^2*BG ./ (1-(FGp.*BG)+eps);
outpict=gammac(outpict);
end
case {'dissolve','lindissolve'}
if opacity~=1 || camount~=1
m=(rand(size(outpict(:,:,1)))+eps<=camount)*opacity;
outpict=bsxfun(@times,BG,1-m) + bsxfun(@times,outpict,m);
end
case {'dissolvezf','lindissolvezf'}
if opacity~=1 || camount~=1
m=zfdither(ones(size(outpict(:,:,1)))*camount)*opacity;
outpict=bsxfun(@times,BG,1-m) + bsxfun(@times,outpict,m);
end
case {'dissolveord','lindissolveord'}
if opacity~=1 || camount~=1
m=orddither(ones(size(outpict(:,:,1)))*camount)*opacity;
outpict=bsxfun(@times,BG,1-m) + bsxfun(@times,outpict,m);
end
otherwise
% this shouldn't ever execute since keys are matched
error('IMBLEND: unknown composition mode ''%s''',compositionmode);
end
end
% handle output typecast %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
outpict=imcast(outpict,inclassBG);
% returns a transformation matrix oriented along dim3 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function mat=gettfm(transformation)
switch rec
case 'rec601'
switch transformation
case 'luma'
mat=[0.299 0.587 0.114];
case 'ypbpr'
mat=[0.299,0.587,0.114;-0.1687367,-0.331264,0.5;0.5,-0.418688,-0.081312];
case 'ypbpr_inv'
mat=[1,0,1.402; 1,-0.3441,-0.7141; 1,1.772,0];
end
case 'rec709'
switch transformation
case 'luma'
mat=[0.213 0.715 0.072];
case 'ypbpr'
mat=[0.213 0.715 0.072; -0.115 -0.385 0.500; 0.500 -0.454 -0.046];
case 'ypbpr_inv'
mat=[1 0 1.575; 1 -0.187 -0.468; 1 1.856 0];
end
end
mat=permute(mat,[1 3 2]);
end
end
% return a LUT for custom mesh modes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function thisLUT=fetchLUT(whichlut)
switch whichlut
case 1 % overlay 1x
thisLUT=[0,0,0,0,0,0,0,0,0,0,0.0526315789473686,0.157894736842105,0.263157894736842,0.368421052631579,0.473684210526316,0.578947368421053,0.684210526315789,0.789473684210526,0.894736842105263,1;0,0.00554016620498615,0.0110803324099723,0.0166204986149584,0.0221606648199446,0.0277008310249307,0.0332409972299169,0.0387811634349030,0.0443213296398892,0.0498614958448753,0.102493074792244,0.202216066481994,0.301939058171745,0.401662049861496,0.501385041551247,0.601108033240997,0.700831024930748,0.800554016620499,0.900277008310249,1;0,0.0110803324099723,0.0221606648199446,0.0332409972299169,0.0443213296398892,0.0554016620498615,0.0664819944598338,0.0775623268698061,0.0886426592797784,0.0997229916897507,0.152354570637119,0.246537396121884,0.340720221606648,0.434903047091413,0.529085872576177,0.623268698060942,0.717451523545706,0.811634349030471,0.905817174515236,1;0,0.0166204986149584,0.0332409972299169,0.0498614958448753,0.0664819944598338,0.0831024930747922,0.0997229916897507,0.116343490304709,0.132963988919668,0.149584487534626,0.202216066481995,0.290858725761773,0.379501385041551,0.468144044321330,0.556786703601108,0.645429362880887,0.734072022160665,0.822714681440443,0.911357340720222,1;0,0.0221606648199446,0.0443213296398892,0.0664819944598338,0.0886426592797784,0.110803324099723,0.132963988919668,0.155124653739612,0.177285318559557,0.199445983379501,0.252077562326870,0.335180055401662,0.418282548476454,0.501385041551247,0.584487534626039,0.667590027700831,0.750692520775623,0.833795013850416,0.916897506925208,1;0,0.0277008310249307,0.0554016620498615,0.0831024930747922,0.110803324099723,0.138504155124654,0.166204986149584,0.193905817174515,0.221606648199446,0.249307479224377,0.301939058171745,0.379501385041551,0.457063711911357,0.534626038781163,0.612188365650970,0.689750692520776,0.767313019390582,0.844875346260388,0.922437673130194,1;0,0.0332409972299169,0.0664819944598338,0.0997229916897507,0.132963988919668,0.166204986149584,0.199445983379501,0.232686980609418,0.265927977839335,0.299168975069252,0.351800554016621,0.423822714681441,0.495844875346260,0.567867036011080,0.639889196675900,0.711911357340720,0.783933518005540,0.855955678670360,0.927977839335180,1;0,0.0387811634349030,0.0775623268698061,0.116343490304709,0.155124653739612,0.193905817174515,0.232686980609418,0.271468144044321,0.310249307479224,0.349030470914127,0.401662049861496,0.468144044321330,0.534626038781163,0.601108033240997,0.667590027700831,0.734072022160665,0.800554016620499,0.867036011080333,0.933518005540166,1;0,0.0443213296398892,0.0886426592797784,0.132963988919668,0.177285318559557,0.221606648199446,0.265927977839335,0.310249307479224,0.354570637119114,0.398891966759003,0.451523545706371,0.512465373961219,0.573407202216066,0.634349030470914,0.695290858725762,0.756232686980609,0.817174515235457,0.878116343490305,0.939058171745152,1;0,0.0498614958448753,0.0997229916897507,0.149584487534626,0.199445983379501,0.249307479224377,0.299168975069252,0.349030470914127,0.398891966759003,0.448753462603878,0.501385041551247,0.556786703601108,0.612188365650970,0.667590027700831,0.722991689750693,0.778393351800554,0.833795013850415,0.889196675900277,0.944598337950139,1;0,0.0554016620498615,0.110803324099723,0.166204986149585,0.221606648199446,0.277008310249308,0.332409972299169,0.387811634349031,0.443213296398892,0.498614958448754,0.551246537396122,0.601108033240997,0.650969529085873,0.700831024930748,0.750692520775623,0.800554016620499,0.850415512465374,0.900277008310249,0.950138504155125,1;0,0.0609418282548476,0.121883656509695,0.182825484764543,0.243767313019391,0.304709141274238,0.365650969529086,0.426592797783934,0.487534626038781,0.548476454293629,0.601108033240997,0.645429362880887,0.689750692520776,0.734072022160665,0.778393351800554,0.822714681440443,0.867036011080332,0.911357340720222,0.955678670360111,1;0,0.0664819944598338,0.132963988919668,0.199445983379501,0.265927977839335,0.332409972299169,0.398891966759003,0.465373961218837,0.531855955678670,0.598337950138504,0.650969529085873,0.689750692520776,0.728531855955679,0.767313019390582,0.806094182825485,0.844875346260388,0.883656509695291,0.922437673130194,0.961218836565097,1;0,0.0720221606648199,0.144044321329640,0.216066481994460,0.288088642659280,0.360110803324100,0.432132963988920,0.504155124653740,0.576177285318560,0.648199445983380,0.700831024930748,0.734072022160665,0.767313019390582,0.800554016620499,0.833795013850416,0.867036011080333,0.900277008310249,0.933518005540166,0.966759002770083,1;0,0.0775623268698061,0.155124653739612,0.232686980609418,0.310249307479224,0.387811634349031,0.465373961218837,0.542936288088643,0.620498614958449,0.698060941828255,0.750692520775623,0.778393351800554,0.806094182825485,0.833795013850416,0.861495844875346,0.889196675900277,0.916897506925208,0.944598337950139,0.972299168975069,1;0,0.0831024930747922,0.166204986149584,0.249307479224377,0.332409972299169,0.415512465373961,0.498614958448753,0.581717451523546,0.664819944598338,0.747922437673130,0.800554016620499,0.822714681440443,0.844875346260388,0.867036011080333,0.889196675900277,0.911357340720222,0.933518005540166,0.955678670360111,0.977839335180055,1;0,0.0886426592797784,0.177285318559557,0.265927977839335,0.354570637119114,0.443213296398892,0.531855955678670,0.620498614958449,0.709141274238227,0.797783933518006,0.850415512465374,0.867036011080332,0.883656509695291,0.900277008310249,0.916897506925208,0.933518005540166,0.950138504155125,0.966759002770083,0.983379501385042,1;0,0.0941828254847645,0.188365650969529,0.282548476454294,0.376731301939058,0.470914127423823,0.565096952908587,0.659279778393352,0.753462603878116,0.847645429362881,0.900277008310249,0.911357340720222,0.922437673130194,0.933518005540166,0.944598337950139,0.955678670360111,0.966759002770083,0.977839335180055,0.988919667590028,1;0,0.0997229916897507,0.199445983379501,0.299168975069252,0.398891966759003,0.498614958448754,0.598337950138504,0.698060941828255,0.797783933518006,0.897506925207756,0.950138504155125,0.955678670360111,0.961218836565097,0.966759002770083,0.972299168975069,0.977839335180055,0.983379501385042,0.988919667590028,0.994459833795014,1;0,0.105263157894737,0.210526315789474,0.315789473684211,0.421052631578947,0.526315789473684,0.631578947368421,0.736842105263158,0.842105263157895,0.947368421052632,1,1,1,1,1,1,1,1,1,1;];
case 2 % overlay 2x
thisLUT=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.157894736842105,0.368421052631579,0.578947368421053,0.789473684210527,1;0,0.000583175389998542,0.00116635077999708,0.00174952616999563,0.00233270155999417,0.00291587694999271,0.00349905233999125,0.00408222772998979,0.00466540311998834,0.00524857850998688,0.0107887447149730,0.0212859017349468,0.0317830587549205,0.0422802157748943,0.0552558682023619,0.244204694561890,0.433153520921417,0.622102347280945,0.811051173640473,1;0,0.00233270155999417,0.00466540311998834,0.00699810467998250,0.00933080623997667,0.0116635077999708,0.0139962093599650,0.0163289109199592,0.0186616124799533,0.0209943140399475,0.0320746464499198,0.0519026097098703,0.0717305729698207,0.0915585362297711,0.157311561452107,0.325849249161685,0.494386936871264,0.662924624580843,0.831462312290422,1;0,0.00524857850998688,0.0104971570199738,0.0157457355299606,0.0209943140399475,0.0262428925499344,0.0314914710599213,0.0367400495699081,0.0419886280798950,0.0472372065898819,0.0638577052048404,0.0918501239247704,0.119842542644700,0.147834961364630,0.253535500801866,0.402828400641493,0.552121300481120,0.701414200320747,0.850707100160373,1;0,0.00933080623997667,0.0186616124799533,0.0279924187199300,0.0373232249599067,0.0466540311998834,0.0559848374398600,0.0653156436798367,0.0746464499198134,0.0839772561597900,0.106137920979735,0.141128444379647,0.176118967779560,0.212713223501968,0.343927686251640,0.475142149001312,0.606356611750984,0.737571074500656,0.868785537250328,1;0,0.0145793847499635,0.0291587694999271,0.0437381542498907,0.0583175389998542,0.0728969237498177,0.0874763084997813,0.102055693249745,0.116635077999708,0.131214462749672,0.158915293774603,0.199737571074501,0.240559848374399,0.314185741361715,0.428488117801429,0.542790494241143,0.657092870680857,0.771395247120571,0.885697623560286,1;0,0.0209943140399475,0.0419886280798950,0.0629829421198425,0.0839772561597900,0.104971570199738,0.125965884239685,0.146960198279633,0.167954512319580,0.188948826359528,0.222189823589445,0.267677504009331,0.313165184429217,0.408660154541478,0.507216795451232,0.605773436360986,0.704330077270739,0.802886718180493,0.901443359090246,1;0,0.0285755941099286,0.0571511882198571,0.0857267823297857,0.114302376439714,0.142877970549643,0.171453564659571,0.200029158769500,0.228604752879428,0.257180346989357,0.295961510424260,0.344948243184138,0.412159206881470,0.496136463041260,0.580113719201050,0.664090975360840,0.748068231520630,0.832045487680420,0.916022743840210,1;0,0.0373232249599067,0.0746464499198134,0.111969674879720,0.149292899839627,0.186616124799533,0.223939349759440,0.261262574719347,0.298585799679253,0.335909024639160,0.380230354279049,0.435486222481411,0.506050444671235,0.576614666861058,0.647178889050882,0.717743111240706,0.788307333430529,0.858871555620353,0.929435777810177,1;0,0.0472372065898819,0.0944744131797638,0.141711619769646,0.188948826359528,0.236186032949410,0.283423239539291,0.330660446129173,0.377897652719055,0.425134859308937,0.475142149001312,0.533459688001166,0.591777227001020,0.650094766000875,0.708412305000729,0.766729844000583,0.825047383000437,0.883364922000292,0.941682461000146,1;0,0.0583175389998542,0.116635077999708,0.174952616999563,0.233270155999417,0.291587694999271,0.349905233999125,0.408222772998980,0.466540311998834,0.524857850998688,0.574865140691063,0.622102347280945,0.669339553870827,0.716576760460709,0.763813967050591,0.811051173640472,0.858288380230354,0.905525586820236,0.952762793410118,1;0,0.0705642221898236,0.141128444379647,0.211692666569471,0.282256888759294,0.352821110949118,0.423385333138942,0.493949555328765,0.564513777518589,0.619769645720951,0.664090975360840,0.701414200320747,0.738737425280653,0.776060650240560,0.813383875200467,0.850707100160373,0.888030325120280,0.925353550080187,0.962676775040093,1;0,0.0839772561597900,0.167954512319580,0.251931768479370,0.335909024639160,0.419886280798950,0.503863536958740,0.587840793118530,0.655051756815862,0.704038489575740,0.742819653010643,0.771395247120572,0.799970841230500,0.828546435340429,0.857122029450357,0.885697623560286,0.914273217670214,0.942848811780143,0.971424405890072,1;0,0.0985566409097536,0.197113281819507,0.295669922729261,0.394226563639014,0.492783204548768,0.591339845458522,0.686834815570783,0.732322495990669,0.777810176410556,0.811051173640472,0.832045487680420,0.853039801720367,0.874034115760315,0.895028429800262,0.916022743840210,0.937017057880157,0.958011371920105,0.979005685960053,1;0,0.114302376439714,0.228604752879429,0.342907129319143,0.457209505758857,0.571511882198571,0.685814258638286,0.759440151625602,0.800262428925499,0.841084706225397,0.868785537250328,0.883364922000292,0.897944306750255,0.912523691500219,0.927103076250182,0.941682461000146,0.956261845750109,0.970841230500073,0.985420615250036,1;0,0.131214462749672,0.262428925499344,0.393643388249016,0.524857850998688,0.656072313748360,0.787286776498032,0.823881032220440,0.858871555620353,0.893862079020265,0.916022743840210,0.925353550080187,0.934684356320163,0.944015162560140,0.953345968800117,0.962676775040093,0.972007581280070,0.981338387520047,0.990669193760023,1;0,0.149292899839627,0.298585799679253,0.447878699518880,0.597171599358507,0.746464499198134,0.852165038635370,0.880157457355300,0.908149876075230,0.936142294795160,0.952762793410118,0.958011371920105,0.963259950430092,0.968508528940079,0.973757107450066,0.979005685960052,0.984254264470039,0.989502842980026,0.994751421490013,1;0,0.168537687709579,0.337075375419157,0.505613063128736,0.674150750838315,0.842688438547893,0.908441463770229,0.928269427030179,0.948097390290130,0.967925353550080,0.979005685960053,0.981338387520047,0.983671089080041,0.986003790640035,0.988336492200029,0.990669193760023,0.993001895320018,0.995334596880012,0.997667298440006,1;0,0.188948826359528,0.377897652719055,0.566846479078583,0.755795305438111,0.944744131797638,0.957719784225106,0.968216941245080,0.978714098265053,0.989211255285027,0.994751421490013,0.995334596880012,0.995917772270010,0.996500947660009,0.997084123050007,0.997667298440006,0.998250473830004,0.998833649220003,0.999416824610002,1;0,0.210526315789474,0.421052631578947,0.631578947368421,0.842105263157895,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;];
case 0 % overlay 0x (BG only)
thisLUT=repmat(0:1/19:1,[20 1]);
case 3
thisLUT=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.157894736842105,0.578947368421053,1;0,6.13868831577413e-05,0.000122773766315483,0.000184160649473224,0.000245547532630965,0.000306934415788706,0.000368321298946448,0.000429708182104189,0.000491095065261930,0.000552481948419671,0.00113565733841821,0.00224062123525756,0.00334558513209690,0.00445054902893624,0.00581640717919599,0.0257057573223042,0.0455951074654123,0.283983394848106,0.641991697424053,1;0,0.000491095065261930,0.000982190130523860,0.00147328519578579,0.00196438026104772,0.00245547532630965,0.00294657039157158,0.00343766545683351,0.00392876052209544,0.00441985558735737,0.00675255714735154,0.0109268652020779,0.0151011732568044,0.0192754813115308,0.0331182234636014,0.0685998419287759,0.104081460393950,0.396812486092034,0.698406243046018,1;0,0.00165744584525901,0.00331489169051803,0.00497233753577704,0.00662978338103606,0.00828722922629507,0.00994467507155408,0.0116021209168131,0.0132595667620721,0.0149170126073311,0.0201655911173180,0.0290053022920328,0.0378450134667475,0.0466847246414622,0.0800638423584841,0.127208968623629,0.245677979757675,0.497118653171784,0.748559326585892,1;0,0.00392876052209544,0.00785752104419088,0.0117862815662863,0.0157150420883818,0.0196438026104772,0.0235725631325726,0.0275013236546681,0.0314300841767635,0.0353588446988590,0.0446896509388357,0.0594225028966935,0.0741553548545514,0.0895634625271446,0.144811657369112,0.200059852211079,0.378457808027869,0.585638538685246,0.792819269342624,1;0,0.00767336039471766,0.0153467207894353,0.0230200811841530,0.0306934415788706,0.0383668019735883,0.0460401623683059,0.0537135227630236,0.0613868831577413,0.0690602435524589,0.0836396283024225,0.105125037407632,0.126610446512841,0.165360916506166,0.225520062000752,0.326217570460632,0.494663177845474,0.663108785230316,0.831554392615158,1;0,0.0132595667620721,0.0265191335241442,0.0397787002862163,0.0530382670482884,0.0662978338103606,0.0795574005724327,0.0928169673345048,0.106076534096577,0.119336100858649,0.140330414898597,0.169059476216419,0.197788537534242,0.258101150236723,0.325665088512212,0.460532070809770,0.595399053107327,0.730266035404885,0.865133017702442,1;0,0.0210557009231053,0.0421114018462105,0.0631671027693158,0.0842228036924210,0.105278504615526,0.126334205538632,0.147389906461737,0.168445607384842,0.189501308307947,0.218076902417876,0.254172389714628,0.303696257702136,0.365574235925139,0.469617329517116,0.575693863613693,0.681770397710269,0.787846931806846,0.893923465903423,1;0,0.0314300841767635,0.0628601683535271,0.0942902525302906,0.125720336707054,0.157150420883818,0.188580505060581,0.220010589237345,0.251440673414108,0.282870757590872,0.320193982550778,0.366725239984346,0.428058409619325,0.509764351102278,0.591470292585232,0.673176234068185,0.754882175551139,0.836588117034093,0.918294058517047,1;0,0.0447510378219934,0.0895020756439868,0.134253113465980,0.179004151287974,0.223755189109967,0.268506226931960,0.313257264753954,0.358008302575947,0.402759340397940,0.450134667474927,0.508904934738070,0.570291817895811,0.631678701053552,0.693065584211294,0.754452467369035,0.815839350526776,0.877226233684517,0.938613116842259,1;0,0.0613868831577413,0.122773766315483,0.184160649473224,0.245547532630965,0.306934415788707,0.368321298946448,0.429708182104189,0.491095065261930,0.549865332525073,0.597240659602060,0.641991697424053,0.686742735246046,0.731493773068040,0.776244810890033,0.820995848712027,0.865746886534020,0.910497924356013,0.955248962178007,1;0,0.0817059414829536,0.163411882965907,0.245117824448861,0.326823765931815,0.408529707414768,0.490235648897722,0.571941590380675,0.633274760015654,0.679806017449222,0.717129242409128,0.748559326585892,0.779989410762655,0.811419494939419,0.842849579116182,0.874279663292946,0.905709747469709,0.937139831646473,0.968569915823237,1;0,0.106076534096577,0.212153068193154,0.318229602289731,0.424306136386308,0.530382670482885,0.634425764074861,0.696303742297864,0.745827610285372,0.781923097582124,0.810498691692053,0.831554392615158,0.852610093538263,0.873665794461368,0.894721495384474,0.915777196307579,0.936832897230684,0.957888598153790,0.978944299076895,1;0,0.134866982297558,0.269733964595115,0.404600946892673,0.539467929190230,0.674334911487788,0.741898849763277,0.802211462465758,0.830940523783581,0.859669585101404,0.880663899141351,0.893923465903423,0.907183032665495,0.920442599427567,0.933702166189639,0.946961732951712,0.960221299713784,0.973480866475856,0.986740433237928,1;0,0.168445607384842,0.336891214769684,0.505336822154526,0.673782429539368,0.774479937999248,0.834639083493835,0.873389553487159,0.894874962592368,0.916360371697578,0.930939756447541,0.938613116842259,0.946286477236977,0.953959837631694,0.961633198026412,0.969306558421129,0.976979918815847,0.984653279210565,0.992326639605282,1;0,0.207180730657377,0.414361461314754,0.621542191972130,0.799940147788921,0.855188342630888,0.910436537472856,0.925844645145449,0.940577497103306,0.955310349061164,0.964641155301141,0.968569915823237,0.972498676345332,0.976427436867427,0.980356197389523,0.984284957911618,0.988213718433714,0.992142478955809,0.996071239477905,1;0,0.251440673414108,0.502881346828216,0.754322020242325,0.872791031376371,0.919936157641516,0.953315275358538,0.962154986533253,0.970994697707967,0.979834408882682,0.985082987392669,0.986740433237928,0.988397879083187,0.990055324928446,0.991712770773705,0.993370216618964,0.995027662464223,0.996685108309482,0.998342554154741,1;0,0.301593756953983,0.603187513907966,0.895918539606050,0.931400158071224,0.966881776536399,0.980724518688469,0.984898826743196,0.989073134797922,0.993247442852648,0.995580144412643,0.996071239477905,0.996562334543167,0.997053429608429,0.997544524673690,0.998035619738952,0.998526714804214,0.999017809869476,0.999508904934738,1;0,0.358008302575947,0.716016605151894,0.954404892534588,0.974294242677696,0.994183592820804,0.995549450971064,0.996654414867903,0.997759378764742,0.998864342661582,0.999447518051580,0.999508904934738,0.999570291817896,0.999631678701054,0.999693065584211,0.999754452467369,0.999815839350527,0.999877226233685,0.999938613116842,1;0,0.421052631578947,0.842105263157895,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;];
case 4 % overlay 4x
thisLUT=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.157894736842106,1;0,6.46177717449908e-06,1.29235543489982e-05,1.93853315234972e-05,2.58471086979963e-05,3.23088858724954e-05,3.87706630469945e-05,4.52324402214936e-05,5.16942173959926e-05,5.81559945704917e-05,0.000119542877728233,0.000235854866869216,0.000352166856010200,0.000468478845151183,0.000612253387283789,0.00270586919182149,0.00479948499635919,0.0298929889313796,0.321668479329785,1;0,0.000103388434791985,0.000206776869583971,0.000310165304375956,0.000413553739167941,0.000516942173959926,0.000620330608751912,0.000723719043543897,0.000827107478335882,0.000930495913127867,0.00142159097838980,0.00230039267412167,0.00317919436985355,0.00405799606558542,0.00697225757128451,0.0144420719850054,0.0219118863987264,0.0835394707562177,0.460305908608663,1;0,0.000523403951134425,0.00104680790226885,0.00157021185340328,0.00209361580453770,0.00261701975567213,0.00314042370680655,0.00366382765794098,0.00418723160907540,0.00471063556020983,0.00636808140546885,0.00915956914485245,0.0119510568842360,0.0147425446236197,0.0252833186395213,0.0401712532495672,0.0775825199234763,0.156984837843721,0.576520971092029,1;0,0.00165421495667176,0.00330842991334353,0.00496264487001529,0.00661685982668706,0.00827107478335882,0.00992528974003059,0.0115795046967024,0.0132337196533741,0.0148879346100459,0.0188166951321413,0.0250200012196604,0.0312233073071795,0.0377109315903766,0.0609733294185733,0.0842357272467700,0.159350656011734,0.345745061081968,0.672872530540985,1;0,0.00403861073406193,0.00807722146812385,0.0121158322021858,0.0161544429362477,0.0201930536703096,0.0242316644043715,0.0282702751384335,0.0323088858724954,0.0363474966065573,0.0440208570012750,0.0553289670566484,0.0666370771120217,0.0870320613190345,0.118694769474080,0.171693458137175,0.260349040971302,0.503528736128886,0.751764368064444,1;0,0.00837446321815081,0.0167489264363016,0.0251233896544524,0.0334978528726032,0.0418723160907540,0.0502467793089048,0.0586212425270557,0.0669957057452065,0.0753701689633573,0.0886297357254294,0.106774406031423,0.124919076337416,0.163011252781088,0.205683213797187,0.290862360511434,0.446335546357395,0.630890364238264,0.815445182119132,1;0,0.0155147269959723,0.0310294539919446,0.0465441809879169,0.0620589079838892,0.0775736349798615,0.0930883619758337,0.108603088971806,0.124117815967778,0.139632542963751,0.160688243886856,0.187284918737094,0.223776189885784,0.269370489629050,0.346033821749454,0.464034354038349,0.598025765528761,0.732017177019174,0.866008588509587,1;0,0.0264674393067482,0.0529348786134965,0.0794023179202447,0.105869757226993,0.132337196533741,0.158804635840489,0.185272075147238,0.211739514453986,0.238206953760734,0.269637037937498,0.308821254723660,0.360470239679431,0.432358722328953,0.526965601940795,0.621572481552636,0.716179361164477,0.810786240776318,0.905393120388159,1;0,0.0423957200418885,0.0847914400837769,0.127187160125665,0.169582880167554,0.211978600209442,0.254374320251331,0.296770040293219,0.339165760335108,0.381561480376996,0.426443369186773,0.483057826040073,0.547675597785064,0.612293369530055,0.676911141275046,0.741528913020037,0.806146684765027,0.870764456510018,0.935382228255009,1;0,0.0646177717449909,0.129235543489982,0.193853315234973,0.258471086979963,0.323088858724954,0.387706630469945,0.452324402214936,0.516942173959927,0.573556630813227,0.618438519623004,0.660834239664892,0.703229959706781,0.745625679748670,0.788021399790558,0.830417119832446,0.872812839874335,0.915208559916223,0.957604279958112,1;0,0.0946068796118411,0.189213759223682,0.283820638835523,0.378427518447364,0.473034398059205,0.567641277671046,0.639529760320569,0.691178745276340,0.730362962062502,0.761793046239266,0.788260485546014,0.814727924852762,0.841195364159511,0.867662803466259,0.894130242773007,0.920597682079755,0.947065121386504,0.973532560693252,1;0,0.133991411490413,0.267982822980826,0.401974234471239,0.535965645961652,0.653966178250546,0.730629510370950,0.776223810114216,0.812715081262906,0.839311756113144,0.860367457036249,0.875882184032222,0.891396911028194,0.906911638024166,0.922426365020139,0.937941092016111,0.953455819012083,0.968970546008055,0.984485273004028,1;0,0.184554817880868,0.369109635761737,0.553664453642605,0.709137639488567,0.794316786202813,0.836988747218912,0.875080923662584,0.893225593968577,0.911370264274571,0.924629831036643,0.933004294254794,0.941378757472944,0.949753220691095,0.958127683909246,0.966502147127397,0.974876610345548,0.983251073563698,0.991625536781849,1;0,0.248235631935557,0.496471263871114,0.739650959028698,0.828306541862826,0.881305230525920,0.912967938680966,0.933362922887978,0.944671032943352,0.955979142998725,0.963652503393443,0.967691114127505,0.971729724861567,0.975768335595629,0.979806946329691,0.983845557063752,0.987884167797814,0.991922778531876,0.995961389265938,1;0,0.327127469459016,0.654254938918032,0.840649343988265,0.915764272753230,0.939026670581427,0.962289068409623,0.968776692692820,0.974979998780340,0.981183304867859,0.985112065389954,0.986766280346626,0.988420495303298,0.990074710259969,0.991728925216641,0.993383140173313,0.995037355129985,0.996691570086656,0.998345785043328,1;0,0.423479028907972,0.843015162156279,0.922417480076524,0.959828746750433,0.974716681360479,0.985257455376380,0.988048943115764,0.990840430855148,0.993631918594531,0.995289364439790,0.995812768390925,0.996336172342059,0.996859576293193,0.997382980244328,0.997906384195462,0.998429788146597,0.998953192097731,0.999476596048866,1;0,0.539694091391338,0.916460529243782,0.978088113601274,0.985557928014995,0.993027742428716,0.995942003934415,0.996820805630146,0.997699607325878,0.998578409021610,0.999069504086872,0.999172892521664,0.999276280956456,0.999379669391248,0.999483057826040,0.999586446260832,0.999689834695624,0.999793223130416,0.999896611565208,1;0,0.678331520670216,0.970107011068621,0.995200515003641,0.997294130808179,0.999387746612716,0.999531521154849,0.999647833143990,0.999764145133131,0.999880457122272,0.999941844005430,0.999948305782604,0.999954767559779,0.999961229336953,0.999967691114128,0.999974152891302,0.999980614668477,0.999987076445651,0.999993538222826,1;0,0.842105263157895,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;];
end
end
% gamma correction functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function out=invgammac(channel)
out=zeros(size(channel));
mk=(channel<=0.0404482362771076);
out(mk)=channel(mk)/12.92;
out(~mk)=real(((channel(~mk)+0.055)/1.055).^2.4);
end
function out=gammac(channel)
out=zeros(size(channel));
mk=(channel<=0.0031306684425005883);
out(mk)=12.92*channel(mk);
out(~mk)=real(1.055*channel(~mk).^0.416666666666666667-0.055);
end
% bark bark bark