From 0252f0e81a5b5b2c7d02452b5bf77c499522abfe Mon Sep 17 00:00:00 2001 From: Rodney Date: Jan 15 2023 01:33:40 +0000 Subject: Merge pull request #4700 from shun-iwasawa/g/floating_point_rendering Floating point & Linear color space rendering --- diff --git a/stuff/config/current.txt b/stuff/config/current.txt index fc53318..12f20f4 100644 --- a/stuff/config/current.txt +++ b/stuff/config/current.txt @@ -822,7 +822,7 @@ "STD_particlesFx.pick_color_for_every_frame" "Pick Control Image's Color for Every Frame" "STD_particlesFx.perspective_distribution" "Perspective Distribution" "STD_particlesFx.motion_blur" "Motion Blur" - "STD_particlesFx.motion_blur_gamma" "Gamma" + "STD_particlesFx.motion_blur_gamma_adjust" "Gamma Adjust" @@ -844,134 +844,178 @@ "STD_inoAddFx.opacity" "Opacity" "STD_inoAddFx.clipping_mask" "Clipping Mask" "STD_inoAddFx.linear" "Linear Color Space" + "STD_inoAddFx.colorSpaceMode" "Color Space" "STD_inoAddFx.gamma" "Gamma" + "STD_inoAddFx.gammaAdjust" "Gamma Adjust" "STD_inoAddFx.premultiplied" "Source is Premultiplied" "STD_inoColorBurnFx" "Color Burn Ino" "STD_inoColorBurnFx.opacity" "Opacity" "STD_inoColorBurnFx.clipping_mask" "Clipping Mask" "STD_inoColorBurnFx.linear" "Linear Color Space" + "STD_inoColorBurnFx.colorSpaceMode" "Color Space" "STD_inoColorBurnFx.gamma" "Gamma" + "STD_inoColorBurnFx.gammaAdjust" "Gamma Adjust" "STD_inoColorBurnFx.premultiplied" "Source is Premultiplied" "STD_inoColorDodgeFx" "Color Dodge Ino" "STD_inoColorDodgeFx.opacity" "Opacity" "STD_inoColorDodgeFx.clipping_mask" "Clipping Mask" "STD_inoColorDodgeFx.linear" "Linear Color Space" + "STD_inoColorDodgeFx.colorSpaceMode" "Color Space" "STD_inoColorDodgeFx.gamma" "Gamma" + "STD_inoColorDodgeFx.gammaAdjust" "Gamma Adjust" "STD_inoColorDodgeFx.premultiplied" "Source is Premultiplied" "STD_inoCrossDissolveFx" "Cross Dissolve Ino" "STD_inoCrossDissolveFx.opacity" "Opacity" "STD_inoCrossDissolveFx.clipping_mask" "Clipping Mask" "STD_inoCrossDissolveFx.linear" "Linear Color Space" + "STD_inoCrossDissolveFx.colorSpaceMode" "Color Space" "STD_inoCrossDissolveFx.gamma" "Gamma" + "STD_inoCrossDissolveFx.gammaAdjust" "Gamma Adjust" "STD_inoCrossDissolveFx.premultiplied" "Source is Premultiplied" "STD_inoDarkenFx" "Darken Ino" "STD_inoDarkenFx.opacity" "Opacity" "STD_inoDarkenFx.clipping_mask" "Clipping Mask" "STD_inoDarkenFx.linear" "Linear Color Space" + "STD_inoDarkenFx.colorSpaceMode" "Color Space" "STD_inoDarkenFx.gamma" "Gamma" + "STD_inoDarkenFx.gammaAdjust" "Gamma Adjust" "STD_inoDarkenFx.premultiplied" "Source is Premultiplied" "STD_inoDarkerColorFx" "Darker Color Ino" "STD_inoDarkerColorFx.opacity" "Opacity" "STD_inoDarkerColorFx.clipping_mask" "Clipping Mask" "STD_inoDarkerColorFx.linear" "Linear Color Space" + "STD_inoDarkerColorFx.colorSpaceMode" "Color Space" "STD_inoDarkerColorFx.gamma" "Gamma" + "STD_inoDarkerColorFx.gammaAdjust" "Gamma Adjust" "STD_inoDarkerColorFx.premultiplied" "Source is Premultiplied" "STD_inoDivideFx" "Divide Ino" "STD_inoDivideFx.opacity" "Opacity" "STD_inoDivideFx.clipping_mask" "Clipping Mask" "STD_inoDivideFx.linear" "Linear Color Space" + "STD_inoDivideFx.colorSpaceMode" "Color Space" "STD_inoDivideFx.gamma" "Gamma" + "STD_inoDivideFx.gammaAdjust" "Gamma Adjust" "STD_inoDivideFx.premultiplied" "Source is Premultiplied" "STD_inoHardLightFx" "Hard Light Ino" "STD_inoHardLightFx.opacity" "Opacity" "STD_inoHardLightFx.clipping_mask" "Clipping Mask" "STD_inoHardLightFx.linear" "Linear Color Space" + "STD_inoHardLightFx.colorSpaceMode" "Color Space" "STD_inoHardLightFx.gamma" "Gamma" + "STD_inoHardLightFx.gammaAdjust" "Gamma Adjust" "STD_inoHardLightFx.premultiplied" "Source is Premultiplied" "STD_inoHardMixFx" "Hard Mix Ino" "STD_inoHardMixFx.opacity" "Opacity" "STD_inoHardMixFx.clipping_mask" "Clipping Mask" "STD_inoHardMixFx.linear" "Linear Color Space" + "STD_inoHardMixFx.colorSpaceMode" "Color Space" "STD_inoHardMixFx.gamma" "Gamma" + "STD_inoHardMixFx.gammaAdjust" "Gamma Adjust" "STD_inoHardMixFx.premultiplied" "Source is Premultiplied" "STD_inoLightenFx" "Lighten Ino" "STD_inoLightenFx.opacity" "Opacity" "STD_inoLightenFx.clipping_mask" "Clipping Mask" "STD_inoLightenFx.linear" "Linear Color Space" + "STD_inoLightenFx.colorSpaceMode" "Color Space" "STD_inoLightenFx.gamma" "Gamma" + "STD_inoLightenFx.gammaAdjust" "Gamma Adjust" "STD_inoLightenFx.premultiplied" "Source is Premultiplied" "STD_inoLighterColorFx" "Lighter Color Ino" "STD_inoLighterColorFx.opacity" "Opacity" "STD_inoLighterColorFx.clipping_mask" "Clipping Mask" "STD_inoLighterColorFx.linear" "Linear Color Space" + "STD_inoLighterColorFx.colorSpaceMode" "Color Space" "STD_inoLighterColorFx.gamma" "Gamma" + "STD_inoLighterColorFx.gammaAdjust" "Gamma Adjust" "STD_inoLighterColorFx.premultiplied" "Source is Premultiplied" "STD_inoLinearBurnFx" "Linear Burn Ino" "STD_inoLinearBurnFx.opacity" "Opacity" "STD_inoLinearBurnFx.clipping_mask" "Clipping Mask" "STD_inoLinearBurnFx.linear" "Linear Color Space" + "STD_inoLinearBurnFx.colorSpaceMode" "Color Space" "STD_inoLinearBurnFx.gamma" "Gamma" + "STD_inoLinearBurnFx.gammaAdjust" "Gamma Adjust" "STD_inoLinearBurnFx.premultiplied" "Source is Premultiplied" "STD_inoLinearDodgeFx" "Linear Dodge Ino" "STD_inoLinearDodgeFx.opacity" "Opacity" "STD_inoLinearDodgeFx.clipping_mask" "Clipping Mask" "STD_inoLinearDodgeFx.linear" "Linear Color Space" + "STD_inoLinearDodgeFx.colorSpaceMode" "Color Space" "STD_inoLinearDodgeFx.gamma" "Gamma" + "STD_inoLinearDodgeFx.gammaAdjust" "Gamma Adjust" "STD_inoLinearDodgeFx.premultiplied" "Source is Premultiplied" "STD_inoLinearLightFx" "Linear Light Ino" "STD_inoLinearLightFx.opacity" "Opacity" "STD_inoLinearLightFx.clipping_mask" "Clipping Mask" "STD_inoLinearLightFx.linear" "Linear Color Space" + "STD_inoLinearLightFx.colorSpaceMode" "Color Space" "STD_inoLinearLightFx.gamma" "Gamma" + "STD_inoLinearLightFx.gammaAdjust" "Gamma Adjust" "STD_inoLinearLightFx.premultiplied" "Source is Premultiplied" "STD_inoMultiplyFx" "Multiply Ino" "STD_inoMultiplyFx.opacity" "Opacity" "STD_inoMultiplyFx.clipping_mask" "Clipping Mask" "STD_inoMultiplyFx.linear" "Linear Color Space" + "STD_inoMultiplyFx.colorSpaceMode" "Color Space" "STD_inoMultiplyFx.gamma" "Gamma" + "STD_inoMultiplyFx.gammaAdjust" "Gamma Adjust" "STD_inoMultiplyFx.premultiplied" "Source is Premultiplied" "STD_inoOverFx" "Over Ino" "STD_inoOverFx.opacity" "Opacity" "STD_inoOverFx.clipping_mask" "Clipping Mask" "STD_inoOverFx.linear" "Linear Color Space" + "STD_inoOverFx.colorSpaceMode" "Color Space" "STD_inoOverFx.gamma" "Gamma" + "STD_inoOverFx.gammaAdjust" "Gamma Adjust" "STD_inoOverFx.premultiplied" "Source is Premultiplied" "STD_inoOverlayFx" "Overlay Ino" "STD_inoOverlayFx.opacity" "Opacity" "STD_inoOverlayFx.clipping_mask" "Clipping Mask" "STD_inoOverlayFx.linear" "Linear Color Space" + "STD_inoOverlayFx.colorSpaceMode" "Color Space" "STD_inoOverlayFx.gamma" "Gamma" + "STD_inoOverlayFx.gammaAdjust" "Gamma Adjust" "STD_inoOverlayFx.premultiplied" "Source is Premultiplied" "STD_inoPinLightFx" "Pin Light Ino" "STD_inoPinLightFx.opacity" "Opacity" "STD_inoPinLightFx.clipping_mask" "Clipping Mask" "STD_inoPinLightFx.linear" "Linear Color Space" + "STD_inoPinLightFx.colorSpaceMode" "Color Space" "STD_inoPinLightFx.gamma" "Gamma" + "STD_inoPinLightFx.gammaAdjust" "Gamma Adjust" "STD_inoPinLightFx.premultiplied" "Source is Premultiplied" "STD_inoScreenFx" "Screen Ino" "STD_inoScreenFx.opacity" "Opacity" "STD_inoScreenFx.clipping_mask" "Clipping Mask" "STD_inoScreenFx.linear" "Linear Color Space" + "STD_inoScreenFx.colorSpaceMode" "Color Space" "STD_inoScreenFx.gamma" "Gamma" + "STD_inoScreenFx.gammaAdjust" "Gamma Adjust" "STD_inoScreenFx.premultiplied" "Source is Premultiplied" "STD_inoSoftLightFx" "Soft Light Ino" "STD_inoSoftLightFx.opacity" "Opacity" "STD_inoSoftLightFx.clipping_mask" "Clipping Mask" "STD_inoSoftLightFx.linear" "Linear Color Space" + "STD_inoSoftLightFx.colorSpaceMode" "Color Space" "STD_inoSoftLightFx.gamma" "Gamma" + "STD_inoSoftLightFx.gammaAdjust" "Gamma Adjust" "STD_inoSoftLightFx.premultiplied" "Source is Premultiplied" "STD_inoSubtractFx" "Subtract Ino" "STD_inoSubtractFx.opacity" "Opacity" "STD_inoSubtractFx.clipping_mask" "Clipping Mask" "STD_inoSubtractFx.alpha_rendering" "Alpha Rendering" "STD_inoSubtractFx.linear" "Linear Color Space" + "STD_inoSubtractFx.colorSpaceMode" "Color Space" "STD_inoSubtractFx.gamma" "Gamma" + "STD_inoSubtractFx.gammaAdjust" "Gamma Adjust" "STD_inoSubtractFx.premultiplied" "Source is Premultiplied" "STD_inoVividLightFx" "Vivid Light Ino" "STD_inoVividLightFx.opacity" "Opacity" "STD_inoVividLightFx.clipping_mask" "Clipping Mask" "STD_inoVividLightFx.linear" "Linear Color Space" + "STD_inoVividLightFx.colorSpaceMode" "Color Space" "STD_inoVividLightFx.gamma" "Gamma" + "STD_inoVividLightFx.gammaAdjust" "Gamma Adjust" "STD_inoVividLightFx.premultiplied" "Source is Premultiplied" "STD_inoBlurFx" "Blur Ino" "STD_inoBlurFx.radius" "Radius" @@ -1192,6 +1236,8 @@ "STD_iwa_AdjustExposureFx" "Adjust Exposure Iwa" "STD_iwa_AdjustExposureFx.hardness" "Hardness" + "STD_iwa_AdjustExposureFx.gamma" "Gamma" + "STD_iwa_AdjustExposureFx.gammaAdjust" "Gamma Adjust" "STD_iwa_AdjustExposureFx.scale" "Scale" "STD_iwa_AdjustExposureFx.offset" "Offset" @@ -1209,6 +1255,8 @@ "STD_iwa_MotionBlurCompFx" "Motion Blur Iwa" "STD_iwa_MotionBlurCompFx.hardness" "Hardness" + "STD_iwa_MotionBlurCompFx.gamma" "Gamma" + "STD_iwa_MotionBlurCompFx.gammaAdjust" "Gamma Adjust" "STD_iwa_MotionBlurCompFx.shutterStart" "Shutter Start" "STD_iwa_MotionBlurCompFx.shutterEnd" "Shutter End" "STD_iwa_MotionBlurCompFx.traceResolution" "Trace Resolution" @@ -1300,6 +1348,9 @@ "STD_iwa_BokehFx.on_focus_distance" "On-Focus Distance" "STD_iwa_BokehFx.bokeh_amount" "Bokeh Amount" "STD_iwa_BokehFx.hardness" "Hardness" + "STD_iwa_BokehFx.gamma" "Gamma" + "STD_iwa_BokehFx.gammaAdjust" "Gamma Adjust" + "STD_iwa_BokehFx.linearizeMode" "Linearize Mode" "STD_iwa_BokehFx.distance1" "Source1 Distance" "STD_iwa_BokehFx.bokeh_adjustment1" "Source1 Bokeh Adjustment" "STD_iwa_BokehFx.distance2" "Source2 Distance" @@ -1315,6 +1366,9 @@ "STD_iwa_BokehRefFx.on_focus_distance" "On-Focus Distance" "STD_iwa_BokehRefFx.bokeh_amount" "Bokeh Amount" "STD_iwa_BokehRefFx.hardness" "Hardness" + "STD_iwa_BokehRefFx.gamma" "Gamma" + "STD_iwa_BokehRefFx.gammaAdjust" "Gamma Adjust" + "STD_iwa_BokehRefFx.linearizeMode" "Linearize Mode" "STD_iwa_BokehRefFx.distance_precision" "Distance Precision" "STD_iwa_BokehRefFx.fill_gap" "Fill Gap" "STD_iwa_BokehRefFx.fill_gap_with_median_filter" "Use Median Filter" @@ -1323,11 +1377,16 @@ "STD_iwa_BokehAdvancedFx.on_focus_distance" "On-Focus Distance" "STD_iwa_BokehAdvancedFx.bokeh_amount" "Bokeh Amount" "STD_iwa_BokehAdvancedFx.masterHardness" "Master Hardness" - "STD_iwa_BokehAdvancedFx.hardnessPerSource" "Hardness per Source" + "STD_iwa_BokehAdvancedFx.masterGamma" "Master Gamma" + "STD_iwa_BokehAdvancedFx.masterGammaAdjust" "Master Gamma Adjust" + "STD_iwa_BokehAdvancedFx.linearizeMode" "Linearize Mode" + "STD_iwa_BokehAdvancedFx.hardnessPerSource" "Gamma/Hardness per Source" "STD_iwa_BokehAdvancedFx.distance1" "Source1 Distance" "STD_iwa_BokehAdvancedFx.bokeh_adjustment1" "Source1 Bokeh Adjustment" "STD_iwa_BokehAdvancedFx.hardness1" "Source1 Hardness" + "STD_iwa_BokehAdvancedFx.gamma1" "Source1 Gamma" + "STD_iwa_BokehAdvancedFx.gammaAdjust1" "Source1 Gamma Adjust" "STD_iwa_BokehAdvancedFx.depth_ref1" "Depth Image" "STD_iwa_BokehAdvancedFx.depthRange1" "Source1 Depth Range" "STD_iwa_BokehAdvancedFx.fillGap1" "Fill Gap" @@ -1336,6 +1395,8 @@ "STD_iwa_BokehAdvancedFx.distance2" "Source2 Distance" "STD_iwa_BokehAdvancedFx.bokeh_adjustment2" "Source2 Bokeh Adjustment" "STD_iwa_BokehAdvancedFx.hardness2" "Source2 Hardness" + "STD_iwa_BokehAdvancedFx.gamma2" "Source2 Gamma" + "STD_iwa_BokehAdvancedFx.gammaAdjust2" "Source2 Gamma Adjust" "STD_iwa_BokehAdvancedFx.depth_ref2" "Depth Image" "STD_iwa_BokehAdvancedFx.depthRange2" "Source2 Depth Range" "STD_iwa_BokehAdvancedFx.fillGap2" "Fill Gap" @@ -1344,6 +1405,8 @@ "STD_iwa_BokehAdvancedFx.distance3" "Source3 Distance" "STD_iwa_BokehAdvancedFx.bokeh_adjustment3" "Source3 Bokeh Adjustment" "STD_iwa_BokehAdvancedFx.hardness3" "Source3 Hardness" + "STD_iwa_BokehAdvancedFx.gamma3" "Source3 Gamma" + "STD_iwa_BokehAdvancedFx.gammaAdjust3" "Source3 Gamma Adjust" "STD_iwa_BokehAdvancedFx.depth_ref3" "Depth Image" "STD_iwa_BokehAdvancedFx.depthRange3" "Source3 Depth Range" "STD_iwa_BokehAdvancedFx.fillGap3" "Fill Gap" @@ -1352,6 +1415,8 @@ "STD_iwa_BokehAdvancedFx.distance4" "Source4 Distance" "STD_iwa_BokehAdvancedFx.bokeh_adjustment4" "Source4 Bokeh Adjustment" "STD_iwa_BokehAdvancedFx.hardness4" "Source4 Hardness" + "STD_iwa_BokehAdvancedFx.gamma4" "Source4 Gamma" + "STD_iwa_BokehAdvancedFx.gammaAdjust4" "Source4 Gamma Adjust" "STD_iwa_BokehAdvancedFx.depth_ref4" "Depth Image" "STD_iwa_BokehAdvancedFx.depthRange4" "Source4 Depth Range" "STD_iwa_BokehAdvancedFx.fillGap4" "Fill Gap" @@ -1360,6 +1425,8 @@ "STD_iwa_BokehAdvancedFx.distance5" "Source5 Distance" "STD_iwa_BokehAdvancedFx.bokeh_adjustment5" "Source5 Bokeh Adjustment" "STD_iwa_BokehAdvancedFx.hardness5" "Source5 Hardness" + "STD_iwa_BokehAdvancedFx.gamma5" "Source5 Gamma" + "STD_iwa_BokehAdvancedFx.gammaAdjust5" "Source5 Gamma Adjust" "STD_iwa_BokehAdvancedFx.depth_ref5" "Depth Image" "STD_iwa_BokehAdvancedFx.depthRange5" "Source5 Depth Range" "STD_iwa_BokehAdvancedFx.fillGap5" "Fill Gap" @@ -1497,6 +1564,7 @@ "STD_iwa_BloomFx" "Bloom Iwa" "STD_iwa_BloomFx.gamma" "Gamma" + "STD_iwa_BloomFx.gammaAdjust" "Gamma Adjust" "STD_iwa_BloomFx.auto_gain" "Auto Gain" "STD_iwa_BloomFx.gain_adjust" "Gain Adjustment" "STD_iwa_BloomFx.gain" "Gain" diff --git a/stuff/profiles/layouts/fxs/STD_inoAddFx.xml b/stuff/profiles/layouts/fxs/STD_inoAddFx.xml index dcb99ab..0921417 100644 --- a/stuff/profiles/layouts/fxs/STD_inoAddFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoAddFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoColorBurnFx.xml b/stuff/profiles/layouts/fxs/STD_inoColorBurnFx.xml index 6073f3c..66b2dff 100644 --- a/stuff/profiles/layouts/fxs/STD_inoColorBurnFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoColorBurnFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoColorDodgeFx.xml b/stuff/profiles/layouts/fxs/STD_inoColorDodgeFx.xml index 3d4e87d..8e36f06 100644 --- a/stuff/profiles/layouts/fxs/STD_inoColorDodgeFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoColorDodgeFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoCrossDissolveFx.xml b/stuff/profiles/layouts/fxs/STD_inoCrossDissolveFx.xml index 171b739..d65ab81 100644 --- a/stuff/profiles/layouts/fxs/STD_inoCrossDissolveFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoCrossDissolveFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoDarkenFx.xml b/stuff/profiles/layouts/fxs/STD_inoDarkenFx.xml index 8041df3..77d29ec 100644 --- a/stuff/profiles/layouts/fxs/STD_inoDarkenFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoDarkenFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoDarkerColorFx.xml b/stuff/profiles/layouts/fxs/STD_inoDarkerColorFx.xml index 358c5a0..21dee73 100644 --- a/stuff/profiles/layouts/fxs/STD_inoDarkerColorFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoDarkerColorFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoDivideFx.xml b/stuff/profiles/layouts/fxs/STD_inoDivideFx.xml index 42eaca0..211d97a 100644 --- a/stuff/profiles/layouts/fxs/STD_inoDivideFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoDivideFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoHardLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoHardLightFx.xml index 8b822ef..21bb3e6 100644 --- a/stuff/profiles/layouts/fxs/STD_inoHardLightFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoHardLightFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoHardMixFx.xml b/stuff/profiles/layouts/fxs/STD_inoHardMixFx.xml index c69b392..f157ee3 100644 --- a/stuff/profiles/layouts/fxs/STD_inoHardMixFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoHardMixFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoLightenFx.xml b/stuff/profiles/layouts/fxs/STD_inoLightenFx.xml index 6c6a760..0836204 100644 --- a/stuff/profiles/layouts/fxs/STD_inoLightenFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoLightenFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoLighterColorFx.xml b/stuff/profiles/layouts/fxs/STD_inoLighterColorFx.xml index a143e51..5d33ebd 100644 --- a/stuff/profiles/layouts/fxs/STD_inoLighterColorFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoLighterColorFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoLinearBurnFx.xml b/stuff/profiles/layouts/fxs/STD_inoLinearBurnFx.xml index ed64746..47c2d8a 100644 --- a/stuff/profiles/layouts/fxs/STD_inoLinearBurnFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoLinearBurnFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoLinearDodgeFx.xml b/stuff/profiles/layouts/fxs/STD_inoLinearDodgeFx.xml index 70f34a5..00f4989 100644 --- a/stuff/profiles/layouts/fxs/STD_inoLinearDodgeFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoLinearDodgeFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoLinearLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoLinearLightFx.xml index 303bd46..8e31467 100644 --- a/stuff/profiles/layouts/fxs/STD_inoLinearLightFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoLinearLightFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoMultiplyFx.xml b/stuff/profiles/layouts/fxs/STD_inoMultiplyFx.xml index 5064564..1be3408 100644 --- a/stuff/profiles/layouts/fxs/STD_inoMultiplyFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoMultiplyFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoOverFx.xml b/stuff/profiles/layouts/fxs/STD_inoOverFx.xml index 5d3f173..5d5b2d5 100644 --- a/stuff/profiles/layouts/fxs/STD_inoOverFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoOverFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoOverlayFx.xml b/stuff/profiles/layouts/fxs/STD_inoOverlayFx.xml index d87c028..244216e 100644 --- a/stuff/profiles/layouts/fxs/STD_inoOverlayFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoOverlayFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoPinLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoPinLightFx.xml index d8332df..62de5e2 100644 --- a/stuff/profiles/layouts/fxs/STD_inoPinLightFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoPinLightFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoScreenFx.xml b/stuff/profiles/layouts/fxs/STD_inoScreenFx.xml index 5e301a8..733b218 100644 --- a/stuff/profiles/layouts/fxs/STD_inoScreenFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoScreenFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoSoftLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoSoftLightFx.xml index 416be10..ff6f660 100644 --- a/stuff/profiles/layouts/fxs/STD_inoSoftLightFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoSoftLightFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoSubtractFx.xml b/stuff/profiles/layouts/fxs/STD_inoSubtractFx.xml index 4922cf0..3ce1166 100644 --- a/stuff/profiles/layouts/fxs/STD_inoSubtractFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoSubtractFx.xml @@ -4,9 +4,10 @@ clipping_mask alpha_rendering - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_inoVividLightFx.xml b/stuff/profiles/layouts/fxs/STD_inoVividLightFx.xml index 12eb439..24c0799 100644 --- a/stuff/profiles/layouts/fxs/STD_inoVividLightFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoVividLightFx.xml @@ -3,9 +3,10 @@ opacity clipping_mask - linear - + colorSpaceMode + gamma + gammaAdjust premultiplied diff --git a/stuff/profiles/layouts/fxs/STD_iwa_AdjustExposureFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_AdjustExposureFx.xml index 71170ef..a3056f7 100644 --- a/stuff/profiles/layouts/fxs/STD_iwa_AdjustExposureFx.xml +++ b/stuff/profiles/layouts/fxs/STD_iwa_AdjustExposureFx.xml @@ -1,6 +1,8 @@ hardness + gamma + gammaAdjust scale offset diff --git a/stuff/profiles/layouts/fxs/STD_iwa_BloomFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_BloomFx.xml index f6c57bf..e34a6a3 100644 --- a/stuff/profiles/layouts/fxs/STD_iwa_BloomFx.xml +++ b/stuff/profiles/layouts/fxs/STD_iwa_BloomFx.xml @@ -1,6 +1,7 @@ gamma + gammaAdjust auto_gain gain_adjust gain diff --git a/stuff/profiles/layouts/fxs/STD_iwa_BokehAdvancedFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_BokehAdvancedFx.xml index b12f23e..e355c1c 100644 --- a/stuff/profiles/layouts/fxs/STD_iwa_BokehAdvancedFx.xml +++ b/stuff/profiles/layouts/fxs/STD_iwa_BokehAdvancedFx.xml @@ -1,16 +1,29 @@ - + on_focus_distance bokeh_amount - masterHardness + linearizeMode + + masterHardness + + + masterGamma + masterGammaAdjust + hardnessPerSource distance1 bokeh_adjustment1 - hardness1 + + hardness1 + + + gamma1 + gammaAdjust1 + depth_ref1 fillGap1 @@ -22,7 +35,13 @@ distance2 bokeh_adjustment2 - hardness2 + + hardness2 + + + gamma2 + gammaAdjust2 + depth_ref2 fillGap2 @@ -34,7 +53,13 @@ distance3 bokeh_adjustment3 - hardness3 + + hardness3 + + + gamma3 + gammaAdjust3 + depth_ref3 fillGap3 @@ -46,7 +71,13 @@ distance4 bokeh_adjustment4 - hardness4 + + hardness4 + + + gamma4 + gammaAdjust4 + depth_ref4 fillGap4 @@ -58,7 +89,13 @@ distance5 bokeh_adjustment5 - hardness5 + + hardness5 + + + gamma5 + gammaAdjust5 + depth_ref5 fillGap5 @@ -74,6 +111,16 @@ hardness3 hardness4 hardness5 + gamma1 + gamma2 + gamma3 + gamma4 + gamma5 + gammaAdjust1 + gammaAdjust2 + gammaAdjust3 + gammaAdjust4 + gammaAdjust5 fillGap1 diff --git a/stuff/profiles/layouts/fxs/STD_iwa_BokehFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_BokehFx.xml index e5c7a8c..c9716df 100644 --- a/stuff/profiles/layouts/fxs/STD_iwa_BokehFx.xml +++ b/stuff/profiles/layouts/fxs/STD_iwa_BokehFx.xml @@ -3,8 +3,15 @@ on_focus_distance bokeh_amount + linearizeMode + hardness - + + + gamma + gammaAdjust + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_BokehRefFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_BokehRefFx.xml index 6df0b34..2e30573 100644 --- a/stuff/profiles/layouts/fxs/STD_iwa_BokehRefFx.xml +++ b/stuff/profiles/layouts/fxs/STD_iwa_BokehRefFx.xml @@ -3,7 +3,14 @@ on_focus_distance bokeh_amount - hardness + linearizeMode + + hardness + + + gamma + gammaAdjust + distance_precision fill_gap fill_gap_with_median_filter diff --git a/stuff/profiles/layouts/fxs/STD_iwa_MotionBlurCompFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_MotionBlurCompFx.xml index 419bb00..190b975 100644 --- a/stuff/profiles/layouts/fxs/STD_iwa_MotionBlurCompFx.xml +++ b/stuff/profiles/layouts/fxs/STD_iwa_MotionBlurCompFx.xml @@ -1,10 +1,10 @@ + motionObjectType - motionObjectIndex - + shutterStart startValue startCurve @@ -13,6 +13,8 @@ endCurve traceResolution hardness + gamma + gammaAdjust zanzoMode premultiType diff --git a/stuff/profiles/layouts/fxs/STD_particlesFx.xml b/stuff/profiles/layouts/fxs/STD_particlesFx.xml index 88b295f..1ae1fe0 100644 --- a/stuff/profiles/layouts/fxs/STD_particlesFx.xml +++ b/stuff/profiles/layouts/fxs/STD_particlesFx.xml @@ -122,7 +122,7 @@ motion_blur - motion_blur_gamma + motion_blur_gamma_adjust scale_step diff --git a/toonz/sources/common/tcolor/tpixel.cpp b/toonz/sources/common/tcolor/tpixel.cpp index 00a64fc..ffd82ac 100644 --- a/toonz/sources/common/tcolor/tpixel.cpp +++ b/toonz/sources/common/tcolor/tpixel.cpp @@ -41,6 +41,17 @@ const TPixelD TPixelD::White(1, 1, 1); const TPixelD TPixelD::Black(0, 0, 0); const TPixelD TPixelD::Transparent(0, 0, 0, 0); //--------------------------------------------------- +const float TPixelF::maxChannelValue = 1.f; +const TPixelF TPixelF::Red(1.f, 0.f, 0.f); +const TPixelF TPixelF::Green(0.f, 1.f, 0.f); +const TPixelF TPixelF::Blue(0.f, 0.f, 1.f); +const TPixelF TPixelF::Yellow(1.f, 1.f, 0.f); +const TPixelF TPixelF::Cyan(0.f, 1.f, 1.f); +const TPixelF TPixelF::Magenta(1.f, 0.f, 1.f); +const TPixelF TPixelF::White(1.f, 1.f, 1.f); +const TPixelF TPixelF::Black(0.f, 0.f, 0.f); +const TPixelF TPixelF::Transparent(0.f, 0.f, 0.f, 0.f); +//--------------------------------------------------- const TPixelGR8 TPixelGR8::White(maxChannelValue); const TPixelGR8 TPixelGR8::Black(0); @@ -113,6 +124,7 @@ TPixelGR8 DVAPI TPixelGR8::from(const TPixel32 &pix) { } //----------------------------------------------------------------------------- + TPixelGR16 DVAPI TPixelGR16::from(const TPixel64 &pix) { return TPixelGR16((((UINT)(pix.r) * 19594 + (UINT)(pix.g) * 38472 + (UINT)(pix.b) * 7470 + (UINT)(1 << 15)) >> @@ -120,3 +132,9 @@ TPixelGR16 DVAPI TPixelGR16::from(const TPixel64 &pix) { } //----------------------------------------------------------------------------- + +TPixelGRF DVAPI TPixelGRF::from(const TPixelF &pix) { + return TPixelGRF(pix.r * 0.299f + pix.g * 0.587f + pix.b * 0.114f); +} + +//----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tcolor/tpixelutils.cpp b/toonz/sources/common/tcolor/tpixelutils.cpp index e04ea8e..25e8955 100644 --- a/toonz/sources/common/tcolor/tpixelutils.cpp +++ b/toonz/sources/common/tcolor/tpixelutils.cpp @@ -31,8 +31,8 @@ void hsv2rgb(TPixel32 &dstRgb, int srcHsv[3], int maxHsv) { if (hue > 360) hue -= 360; if (hue < 0) hue += 360; - if (sat < 0) sat = 0; - if (sat > 1) sat = 1; + if (sat < 0) sat = 0; + if (sat > 1) sat = 1; if (value < 0) value = 0; if (value > 1) value = 1; if (sat == 0) { @@ -86,8 +86,8 @@ void HSV2RGB(double hue, double sat, double value, double *red, double *green, // hue=0; if (hue > 360) hue -= 360; if (hue < 0) hue += 360; - if (sat < 0) sat = 0; - if (sat > 1) sat = 1; + if (sat < 0) sat = 0; + if (sat > 1) sat = 1; if (value < 0) value = 0; if (value > 1) value = 1; if (sat == 0) { @@ -164,7 +164,7 @@ void RGB2HSV(double r, double g, double b, double *h, double *s, double *v) { *h = 2 + (b - r) / delta; else if (b == max) *h = 4 + (r - g) / delta; - *h = *h * 60; + *h = *h * 60; if (*h < 0) *h += 360; } } @@ -198,7 +198,7 @@ void rgb2hsv(int dstHsv[3], const TPixel32 &srcRgb, int maxHsv) { h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g) / delta; - h = h * 60; + h = h * 60; if (h < 0) h += 360; } @@ -227,7 +227,7 @@ inline double HLSValue(double n1, double n2, double h) { else return n1; } -} +} // namespace void HLS2RGB(double h, double l, double s, double *r, double *g, double *b) { if (s == 0) { *r = *g = *b = l; @@ -240,7 +240,7 @@ void HLS2RGB(double h, double l, double s, double *r, double *g, double *b) { m2 = l * (1 + s); else m2 = l + s + l * s; - m1 = 2 * l - m2; + m1 = 2 * l - m2; *r = HLSValue(m1, m2, h + 120); *g = HLSValue(m1, m2, h); @@ -304,6 +304,15 @@ TPixel32 toPixel32(const TPixelGR8 &src) { //----------------------------------------------------------------------------- +TPixel32 toPixel32(const TPixelF &src) { + const double factor = 255.0f; + return TPixel32( + byteCrop(tround(src.r * factor)), byteCrop(tround(src.g * factor)), + byteCrop(tround(src.b * factor)), byteCrop(tround(src.m * factor))); +} + +//----------------------------------------------------------------------------- + TPixel64 toPixel64(const TPixel32 &src) { return TPixelRGBM64(ushortFromByte(src.r), ushortFromByte(src.g), ushortFromByte(src.b), ushortFromByte(src.m)); @@ -327,6 +336,15 @@ TPixel64 toPixel64(const TPixelGR8 &src) { //----------------------------------------------------------------------------- +TPixel64 toPixel64(const TPixelF &src) { + const float factor = 65535.0f; + return TPixel64( + wordCrop(tround(src.r * factor)), wordCrop(tround(src.g * factor)), + wordCrop(tround(src.b * factor)), wordCrop(tround(src.m * factor))); +} + +//----------------------------------------------------------------------------- + TPixelD toPixelD(const TPixel32 &src) { const double factor = 1.0 / 255.0; return TPixelD(factor * src.r, factor * src.g, factor * src.b, @@ -349,7 +367,84 @@ TPixelD toPixelD(const TPixelGR8 &src) { } //----------------------------------------------------------------------------- + +TPixelD toPixelD(const TPixelF &src) { + return TPixelD(src.r, src.g, src.b, src.m); +} + +//----------------------------------------------------------------------------- + +TPixelF toPixelF(const TPixel32 &src) { + const float factor = 1.f / 255.f; + return TPixelF(factor * src.r, factor * src.g, factor * src.b, + factor * src.m); +} + +//----------------------------------------------------------------------------- + +TPixelF toPixelF(const TPixelD &src) { + return TPixelF(src.r, src.g, src.b, src.m); +} + +//----------------------------------------------------------------------------- + +TPixelF toPixelF(const TPixel64 &src) { + const float factor = 1.f / 65535.f; + return TPixelF(factor * src.r, factor * src.g, factor * src.b, + factor * src.m); +} + +//----------------------------------------------------------------------------- + +TPixelF toPixelF(const TPixelGR8 &src) { + const float v = (float)src.value / 255.f; + return TPixelF(v, v, v); +} + +//----------------------------------------------------------------------------- +namespace { +template +Q toLin(Q val, double gamma) { + return (Q)((T::maxChannelValue)*std::pow( + (double)val / (double)(T::maxChannelValue), gamma) + + 0.5); +} +template <> +float toLin(float val, double gamma) { + return (val < 0.f) ? val : std::pow(val, (float)gamma); +} +template <> +double toLin(double val, double gamma) { + return std::pow(val, gamma); +} +} // namespace + //----------------------------------------------------------------------------- + +TPixel32 toLinear(const TPixel32 &pix, const double gamma) { + return TPixel32(toLin(pix.r, gamma), + toLin(pix.g, gamma), + toLin(pix.b, gamma), pix.m); +} +TPixel64 toLinear(const TPixel64 &pix, const double gamma) { + return TPixel64(toLin(pix.r, gamma), + toLin(pix.g, gamma), + toLin(pix.b, gamma), pix.m); +} +TPixelD toLinear(const TPixelD &pix, const double gamma) { + return TPixelD(toLin(pix.r, gamma), + toLin(pix.g, gamma), + toLin(pix.b, gamma), pix.m); +} +TPixelF toLinear(const TPixelF &pix, const double gamma) { + return TPixelF(toLin(pix.r, gamma), + toLin(pix.g, gamma), + toLin(pix.b, gamma), pix.m); +} +TPixelGR8 toLinear(const TPixelGR8 &pix, const double gamma) { + return TPixelGR8(toLin(pix.value, gamma)); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tfx/binaryFx.cpp b/toonz/sources/common/tfx/binaryFx.cpp index 3efc624..7c3b368 100644 --- a/toonz/sources/common/tfx/binaryFx.cpp +++ b/toonz/sources/common/tfx/binaryFx.cpp @@ -24,7 +24,7 @@ void makeRectCoherent(TRectD &rect, const TPointD &pos) { rect.y1 = tceil(rect.y1); rect += pos; } -} +} // namespace //****************************************************************************************** // TImageCombinationFx declaration @@ -82,6 +82,11 @@ public: std::string &portName) override; int getPreferredInputPort() override { return 1; } + + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override { + return settingsIsLinear; + } }; //****************************************************************************************** @@ -92,6 +97,7 @@ TImageCombinationFx::TImageCombinationFx() : m_group("Source", 2) { addInputPort("Source1", new TRasterFxPort, 0); addInputPort("Source2", new TRasterFxPort, 0); setName(L"ImageCombinationFx"); + enableComputeInFloat(true); } //--------------------------------------------------------------------------- @@ -407,7 +413,7 @@ public: }; //================================================================== - +/* class LinearBurnFx final : public TImageCombinationFx { FX_DECLARATION(LinearBurnFx) @@ -435,7 +441,7 @@ public: TRop::overlay(up, down, down); } }; - +*/ //================================================================== class BlendFx final : public TImageCombinationFx { @@ -476,6 +482,7 @@ public: addInputPort("Source", m_source); addInputPort("Matte", m_matte); setName(L"InFx"); + enableComputeInFloat(true); } ~InFx() {} @@ -540,6 +547,7 @@ public: addInputPort("Source", m_source); addInputPort("Matte", m_matte); setName(L"OutFx"); + enableComputeInFloat(true); } ~OutFx() {} @@ -607,6 +615,7 @@ public: AtopFx() { addInputPort("Up", m_up); addInputPort("Down", m_dn); + enableComputeInFloat(true); } bool canHandle(const TRenderSettings &info, double frame) override { @@ -691,8 +700,8 @@ FX_IDENTIFIER(AtopFx, "atopFx") // FX_IDENTIFIER(XorFx, "xorFx") FX_IDENTIFIER(MinFx, "minFx") FX_IDENTIFIER(MaxFx, "maxFx") -FX_IDENTIFIER(LinearBurnFx, "linearBurnFx") -FX_IDENTIFIER(OverlayFx, "overlayFx") +// FX_IDENTIFIER(LinearBurnFx, "linearBurnFx") +// FX_IDENTIFIER(OverlayFx, "overlayFx") FX_IDENTIFIER(BlendFx, "blendFx") FX_IDENTIFIER(ColorDodgeFx, "colorDodgeFx") FX_IDENTIFIER(ColorBurnFx, "colorBurnFx") diff --git a/toonz/sources/common/tfx/tcacheresource.cpp b/toonz/sources/common/tfx/tcacheresource.cpp index 5bff082..3f31369 100644 --- a/toonz/sources/common/tfx/tcacheresource.cpp +++ b/toonz/sources/common/tfx/tcacheresource.cpp @@ -16,7 +16,7 @@ #include "trop.h" // File I/O includes -//#include "tstream.h" +// #include "tstream.h" // Qt classes #include @@ -134,6 +134,8 @@ inline int getRasterType(const TRasterP &ras) { return TCacheResource::RGBM32; else if ((TRaster64P)ras) return TCacheResource::RGBM64; + else if ((TRasterFP)ras) + return TCacheResource::RGBMFloat; else if ((TRasterCM32P)ras) return TCacheResource::CM32; @@ -210,6 +212,8 @@ inline void loadCompressed(const TFilePath &fp, TRasterP &ras, ras = TRaster32P(latticeStep, latticeStep); else if (rasType == TCacheResource::RGBM64) ras = TRaster64P(latticeStep, latticeStep); + else if (rasType == TCacheResource::RGBMFloat) + ras = TRasterFP(latticeStep, latticeStep); else assert(false); @@ -227,7 +231,7 @@ inline void loadCompressed(const TFilePath &fp, TRasterP &ras, ras->unlock(); } -} +} // namespace //**************************************************************************************************** // TCacheResourceP implementation @@ -336,6 +340,8 @@ TRasterP TCacheResource::buildCompatibleRaster(const TDimension &size) { result = TRaster32P(size); else if (m_tileType == RGBM64) result = TRaster64P(size); + else if (m_tileType == RGBMFloat) + result = TRasterFP(size); else if (m_tileType == CM32) result = TRasterCM32P(size); @@ -393,6 +399,9 @@ inline TRasterP TCacheResource::createCellRaster(int rasterType, } else if (rasterType == TCacheResource::RGBM64) { result = TRaster64P(latticeStep, latticeStep); img = TRasterImageP(result); + } else if (rasterType == TCacheResource::RGBMFloat) { + result = TRasterFP(latticeStep, latticeStep); + img = TRasterImageP(result); } else if (rasterType == TCacheResource::CM32) { result = TRasterCM32P(latticeStep, latticeStep); img = TToonzImageP(result, result->getBounds()); @@ -791,9 +800,10 @@ int TCacheResource::size() const { // NOTE: It's better to store the size incrementally. This complies // with the possibility of specifying a bbox to fit the stored cells to... - return m_tileType == NONE ? 0 - : m_tileType == RGBM64 ? (m_cellsCount << 11) - : (m_cellsCount << 10); + return m_tileType == NONE ? 0 + : m_tileType == RGBM64 ? (m_cellsCount << 11) + : m_tileType == RGBMFloat ? (m_cellsCount << 12) + : (m_cellsCount << 10); } //**************************************************************************************************** diff --git a/toonz/sources/common/tfx/tmacrofx.cpp b/toonz/sources/common/tfx/tmacrofx.cpp index 4d4c2f7..b2b3240 100644 --- a/toonz/sources/common/tfx/tmacrofx.cpp +++ b/toonz/sources/common/tfx/tmacrofx.cpp @@ -200,7 +200,7 @@ bool TMacroFx::isaLeaf(TFx *fx) const { //-------------------------------------------------- -TMacroFx::TMacroFx() : m_isEditing(false) {} +TMacroFx::TMacroFx() : m_isEditing(false) { enableComputeInFloat(true); } //-------------------------------------------------- diff --git a/toonz/sources/common/tfx/trenderer.cpp b/toonz/sources/common/tfx/trenderer.cpp index d4f50ae..89f65e4 100644 --- a/toonz/sources/common/tfx/trenderer.cpp +++ b/toonz/sources/common/tfx/trenderer.cpp @@ -128,6 +128,8 @@ public: raster = TRaster32P(size); else if (bpp == 64) raster = TRaster64P(size); + else if (bpp == 128) + raster = TRasterFP(size); else assert(false); @@ -1044,6 +1046,8 @@ void RenderTask::buildTile(TTile &tile) { tile.m_pos = m_framePos; tile.setRaster( m_rendererImp->m_rasterPool.getRaster(m_frameSize, m_info.m_bpp)); + // set the linear flag + tile.getRaster()->setLinear(m_info.m_linearColorSpace); } //--------------------------------------------------------- diff --git a/toonz/sources/common/tfx/unaryFx.cpp b/toonz/sources/common/tfx/unaryFx.cpp index 86260d3..ad4deab 100644 --- a/toonz/sources/common/tfx/unaryFx.cpp +++ b/toonz/sources/common/tfx/unaryFx.cpp @@ -8,11 +8,14 @@ #include "tfxparam.h" #include "tparamset.h" -//#define ALLOW_SHEAR +// #define ALLOW_SHEAR //============================================================================== -TGeometryFx::TGeometryFx() { setName(L"Geometry"); } +TGeometryFx::TGeometryFx() { + setName(L"Geometry"); + enableComputeInFloat(true); +} //--------------------------------------------------------------- @@ -28,7 +31,8 @@ void TGeometryFx::doCompute(TTile &tile, double frame, return; } - if (!TRaster32P(tile.getRaster()) && !TRaster64P(tile.getRaster())) + if (!TRaster32P(tile.getRaster()) && !TRaster64P(tile.getRaster()) && + !TRasterFP(tile.getRaster())) throw TException("AffineFx unsupported pixel type"); TAffine aff1 = getPlacement(frame); @@ -111,6 +115,13 @@ void TGeometryFx::transform(double frame, int port, const TRectD &rectOnOutput, infoOnInput.m_affine = infoOnInput.m_affine * aff; } +//-------------------------------------------------- + +bool TGeometryFx::toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const { + return tileIsLinear; +} + //================================================== NaAffineFx::NaAffineFx(bool isDpiAffine) @@ -212,6 +223,7 @@ public: // bindParam(this, "blue_channel" , m_blueChan); // bindParam(this, "alpha_channel", m_alphaChan); setName(L"InvertFx"); + enableComputeInFloat(true); }; ~InvertFx(){}; diff --git a/toonz/sources/common/tfx/zeraryFx.cpp b/toonz/sources/common/tfx/zeraryFx.cpp index 3864aaa..e6534ea 100644 --- a/toonz/sources/common/tfx/zeraryFx.cpp +++ b/toonz/sources/common/tfx/zeraryFx.cpp @@ -25,6 +25,7 @@ public: bindParam(this, "color", m_color); m_color->setDefaultValue(TPixel32::Green); setName(L"ColorCardFx"); + enableComputeInFloat(true); } bool canHandle(const TRenderSettings &info, double frame) override { @@ -37,18 +38,36 @@ public: return true; } - void doCompute(TTile &tile, double frame, const TRenderSettings &) override { - TRaster32P raster32 = tile.getRaster(); - if (raster32) - raster32->fill(m_color->getPremultipliedValue(frame)); - else { - TRaster64P ras64 = tile.getRaster(); - if (ras64) + void doCompute(TTile &tile, double frame, + const TRenderSettings &ri) override { + TRaster32P ras32 = tile.getRaster(); + TRaster64P ras64 = tile.getRaster(); + TRasterFP rasF = tile.getRaster(); + // currently the tile should always be nonlinear + assert(!tile.getRaster()->isLinear()); + if (!tile.getRaster()->isLinear()) { + if (ras32) + ras32->fill(m_color->getPremultipliedValue(frame)); + else if (ras64) ras64->fill(toPixel64(m_color->getPremultipliedValue(frame))); + else if (rasF) + rasF->fill(toPixelF(m_color->getPremultipliedValue(frame))); + else + throw TException("ColorCardFx unsupported pixel type"); + } else { // linear color space + if (ras32) + ras32->fill(toLinear(m_color->getPremultipliedValue(frame), + ri.m_colorSpaceGamma)); + else if (ras64) + ras64->fill(toLinear(toPixel64(m_color->getPremultipliedValue(frame)), + ri.m_colorSpaceGamma)); + else if (rasF) + rasF->fill(toLinear(toPixelF(m_color->getPremultipliedValue(frame)), + ri.m_colorSpaceGamma)); else throw TException("ColorCardFx unsupported pixel type"); } - }; + } }; //================================================================== @@ -71,6 +90,7 @@ public: m_size->setValueRange(1, 1000); m_size->setDefaultValue(50); setName(L"CheckBoardFx"); + enableComputeInFloat(true); } bool canHandle(const TRenderSettings &info, double frame) override { @@ -84,8 +104,13 @@ public: void doCompute(TTile &tile, double frame, const TRenderSettings &info) override { - const TPixel32 &c1 = m_color1->getValue(frame); - const TPixel32 &c2 = m_color2->getValue(frame); + bool isLinear = tile.getRaster()->isLinear(); + // currently the tile should always be nonlinear + assert(!isLinear); + const TPixel32 &c1 = + m_color1->getValue(frame, isLinear, info.m_colorSpaceGamma); + const TPixel32 &c2 = + m_color2->getValue(frame, isLinear, info.m_colorSpaceGamma); double size = m_size->getValue(frame); diff --git a/toonz/sources/common/timage_io/timage_io.cpp b/toonz/sources/common/timage_io/timage_io.cpp index c98f8e1..7fecbbd 100644 --- a/toonz/sources/common/timage_io/timage_io.cpp +++ b/toonz/sources/common/timage_io/timage_io.cpp @@ -54,8 +54,10 @@ TImageReader::TImageReader(const TFilePath &path) , m_readGreytones(true) , m_file(NULL) , m_is64BitEnabled(false) + , m_isFloatEnabled(false) , m_shrink(1) - , m_region(TRect()) {} + , m_region(TRect()) + , m_colorSpaceGamma(2.2) {} //----------------------------------------------------------- @@ -180,6 +182,12 @@ struct pixel_traits { }; template <> +struct pixel_traits { + typedef TPixelF rgbm_pixel_type; + typedef float buffer_type; +}; + +template <> struct pixel_traits { typedef TPixel32 rgbm_pixel_type; typedef char buffer_type; @@ -352,6 +360,9 @@ TImageP TImageReader::load0() { y1 -= (y1 - y0) % m_shrink; } + if (m_path.getType() == "exr") + m_reader->setColorSpaceGamma(m_colorSpaceGamma); + assert(x0 <= x1 && y0 <= y1); TDimension imageDimension = @@ -422,7 +433,16 @@ TImageP TImageReader::load0() { TRasterP _ras; // currently m_bitsPerSample == 32 is only possible when loading // full-float / uint EXR images - if (info.m_bitsPerSample == 16 || info.m_bitsPerSample == 32) { + // EDIT: half float EXR images also set m_bitsPerSample to 32 + // to obtain floating point images + // �摜���̏��info + if (info.m_bitsPerSample == 32 && m_isFloatEnabled) { + // ��������m�����j�A�ɕϊ����ēǂݍ��� + TRasterFP ras(imageDimension); + readRaster(ras, m_reader, x0, y0, x1, y1, info.m_lx, info.m_ly, + m_shrink); + _ras = ras; + } else if (info.m_bitsPerSample == 16 || info.m_bitsPerSample == 32) { if (m_is64BitEnabled || m_path.getType() != "tif") { // Standard 64-bit case. @@ -550,6 +570,11 @@ static void convertForWriting(TRasterP &ras, const TRasterP &rin, int bpp) { ras = TRaster64P(rin->getSize()); TRop::convert(ras, rin); break; + case 96: + case 128: + ras = TRasterFP(rin->getSize()); + TRop::convert(ras, rin); + break; default: assert(false); } @@ -576,6 +601,7 @@ void TImageWriter::save(const TImageP &img) { TRasterGR8P rasGr = ri->getRaster(); TRaster32P ras32 = ri->getRaster(); TRaster64P ras64 = ri->getRaster(); + TRasterFP rasF = ri->getRaster(); TEnumProperty *p = m_properties @@ -589,19 +615,20 @@ void TImageWriter::save(const TImageP &img) { int bpp = p ? std::stoi(p->getValue()) : 32; - // bpp 1 8 16 24 32 40 48 56 64 - int spp[] = { - 1, 1, 1, 4, 4, - 0, 4, 0, 4}; // 0s are for pixel sizes which are normally unsupported - int bps[] = { - 1, 8, 16, 8, 8, - 0, 16, 0, 16}; // by image formats, let alone by Toonz raster ones. + // bpp 1 8 16 24 32 40 48 56 64 96 128 + int spp[] = {1, 1, 1, 4, 4, 0, + 4, 0, 4, 4, 4}; // 0s are for pixel sizes which are normally + // unsupported + int bps[] = {1, 8, 16, 8, 8, 0, + 16, 0, 16, 32, 32}; // by image formats, let alone by Toonz + // raster ones. // The 24 and 48 cases get automatically promoted to 32 and 64. - int bypp = bpp / 8; - assert(bypp < boost::size(spp) && spp[bypp] && bps[bypp]); + int bypp = bpp / 8; + int bypp_id = (bpp <= 64) ? bypp : (bpp == 96) ? 9 : 10; + assert(bypp_id < boost::size(spp) && spp[bypp_id] && bps[bypp_id]); - info.m_samplePerPixel = spp[bypp]; - info.m_bitsPerSample = bps[bypp]; + info.m_samplePerPixel = spp[bypp_id]; + info.m_bitsPerSample = bps[bypp_id]; if (rasGr) { if (bypp < 2) // Seems 16 bit greymaps are not handled... why? @@ -618,6 +645,11 @@ void TImageWriter::save(const TImageP &img) { ras = ras64; else convertForWriting(ras, ras64, bpp); + } else if (rasF) { + if (bpp == 96 || bpp == 128) + ras = rasF; + else + convertForWriting(ras, rasF, bpp); } else { fclose(file); throw TImageException(m_path, "unsupported raster type"); @@ -634,7 +666,7 @@ void TImageWriter::save(const TImageP &img) { writer->open(file, info); // add background colors for non alpha-enabled image types - if ((ras32 || ras64) && !writer->writeAlphaSupported() && + if ((ras32 || ras64 || rasF) && !writer->writeAlphaSupported() && TImageWriter::getBackgroundColor() != TPixel::Black) { if (bpp == 32 || bpp == 24) TRop::addBackground(ras, TImageWriter::getBackgroundColor()); @@ -643,6 +675,11 @@ void TImageWriter::save(const TImageP &img) { bgRas->fill(toPixel64(TImageWriter::getBackgroundColor())); TRop::over(bgRas, ras); ras = bgRas; + } else if (bpp == 96 || bpp == 128) { + TRasterFP bgRas(ras->getSize()); + bgRas->fill(toPixelF(TImageWriter::getBackgroundColor())); + TRop::over(bgRas, ras); + ras = bgRas; } // for other bpp values, do nothing for now } @@ -652,6 +689,9 @@ void TImageWriter::save(const TImageP &img) { if (bpp == 1 || bpp == 8 || bpp == 24 || bpp == 32 || bpp == 16) for (int i = 0; i < ras->getLy(); i++) writer->writeLine((char *)ras->getRawData(0, i)); + else if (bpp == 96 || bpp == 128) + for (int i = 0; i < ras->getLy(); i++) + writer->writeLine((float *)ras->getRawData(0, i)); else for (int i = 0; i < ras->getLy(); i++) writer->writeLine((short *)ras->getRawData(0, i)); @@ -659,6 +699,9 @@ void TImageWriter::save(const TImageP &img) { if (bpp == 1 || bpp == 8 || bpp == 24 || bpp == 32 || bpp == 16) for (int i = ras->getLy() - 1; i >= 0; i--) writer->writeLine((char *)ras->getRawData(0, i)); + else if (bpp == 96 || bpp == 128) + for (int i = ras->getLy() - 1; i >= 0; i--) + writer->writeLine((float *)ras->getRawData(0, i)); else for (int i = ras->getLy() - 1; i >= 0; i--) writer->writeLine((short *)ras->getRawData(0, i)); @@ -688,7 +731,6 @@ void TImageWriter::save(const TImageP &img) { } //----------------------------------------------------------- - TImageWriterP::TImageWriterP(const TFilePath &path) { m_pointer = new TImageWriter(path); m_pointer->addRef(); diff --git a/toonz/sources/common/tparam/tpixelparam.cpp b/toonz/sources/common/tparam/tpixelparam.cpp index 73a5ca2..95e3c0c 100644 --- a/toonz/sources/common/tparam/tpixelparam.cpp +++ b/toonz/sources/common/tparam/tpixelparam.cpp @@ -1,6 +1,6 @@ -//#include "tpixelparam.h" +// #include "tpixelparam.h" #include "tparamset.h" #include "tdoubleparam.h" #include "texception.h" @@ -112,6 +112,14 @@ TPixel32 TPixelParam::getValue(double frame) const { //--------------------------------------------------------- +TPixel32 TPixelParam::getValue(double frame, bool linear, + double colorSpaceGamma) const { + if (!linear) return getValue(frame); + return toPixel32(toLinear(getValueD(frame), colorSpaceGamma)); +} + +//--------------------------------------------------------- + TPixel64 TPixelParam::getValue64(double frame) const { return toPixel64(getValueD(frame)); } @@ -200,7 +208,7 @@ TDoubleParamP &TPixelParam::getMatte() { return m_data->m_m; } //--------------------------------------------------------- void TPixelParam::enableMatte(bool on) { - m_data->m_isMatteEnabled = on; + m_data->m_isMatteEnabled = on; if (on == false) m_data->m_m = new TDoubleParam(255.0); } //--------------------------------------------------------- diff --git a/toonz/sources/common/tparam/tspectrumparam.cpp b/toonz/sources/common/tparam/tspectrumparam.cpp index f0d502d..ee98870 100644 --- a/toonz/sources/common/tparam/tspectrumparam.cpp +++ b/toonz/sources/common/tparam/tspectrumparam.cpp @@ -181,6 +181,22 @@ TSpectrum64 TSpectrumParam::getValue64(double frame) const { } return TSpectrum64(keys.size(), &keys[0]); } + +//--------------------------------------------------------- + +TSpectrumF TSpectrumParam::getValueF(double frame) const { + assert(m_imp); + std::vector keys; + int keyCount = m_imp->getKeyCount(); + for (int i = 0; i < keyCount; i++) { + ColorKeyParam paramKey = m_imp->getKey(i); + TSpectrumF::ColorKey key(paramKey.first->getValue(frame), + toPixelF(paramKey.second->getValue(frame))); + keys.push_back(key); + } + return TSpectrumF(keys.size(), &keys[0]); +} + //--------------------------------------------------------- void TSpectrumParam::setValue(double frame, const TSpectrum &spectrum, diff --git a/toonz/sources/common/tproperty.cpp b/toonz/sources/common/tproperty.cpp index c05eae4..ea5e4f5 100644 --- a/toonz/sources/common/tproperty.cpp +++ b/toonz/sources/common/tproperty.cpp @@ -3,7 +3,7 @@ #include "tproperty.h" #include "tstream.h" #include "texception.h" -//#include "tconvert.h" +// #include "tconvert.h" void TProperty::addListener(Listener *listener) { if (std::find(m_listeners.begin(), m_listeners.end(), listener) == @@ -230,8 +230,7 @@ void TPropertyGroup::loadData(TIStream &is) { double min = std::stod(is.getTagAttribute("min")); double max = std::stod(is.getTagAttribute("max")); add(new TDoubleProperty(name, min, max, std::stod(svalue))); - } - if (type == "pair") { + } else if (type == "pair") { double min = std::stod(is.getTagAttribute("min")); double max = std::stod(is.getTagAttribute("max")); TDoublePairProperty::Value v(0, 0); @@ -302,7 +301,7 @@ void TEnumProperty::assignUIName(TProperty *refP) { if (!enumRefP) return; Items refItems = enumRefP->getItems(); for (int i = 0; i < m_range.size(); i++) { - int refIndex = enumRefP->indexOf(m_range[i]); + int refIndex = enumRefP->indexOf(m_range[i]); if (0 <= refIndex) m_items[i].UIName = refItems[refIndex].UIName; } } diff --git a/toonz/sources/common/traster/traster.cpp b/toonz/sources/common/traster/traster.cpp index 718f432..1f2700b 100644 --- a/toonz/sources/common/traster/traster.cpp +++ b/toonz/sources/common/traster/traster.cpp @@ -9,7 +9,7 @@ #include "tbigmemorymanager.h" #include "traster.h" #include "trastercm.h" -//#include "tspecialstyleid.h" +// #include "tspecialstyleid.h" #include "tpixel.h" #include "tpixelgr.h" #include "timagecache.h" @@ -28,6 +28,7 @@ TRaster::TRaster(int lx, int ly, int pixelSize) , m_bufferOwner(true) , m_buffer(0) , m_lockCount(0) + , m_isLinear(false) #ifdef _DEBUG , m_cashed(false) #endif @@ -83,15 +84,18 @@ TRaster::TRaster(int lx, int ly, int pixelSize, int wrap, UCHAR *buffer, , m_buffer(buffer) , m_bufferOwner(bufferOwner) , m_lockCount(0) + , m_isLinear(false) #ifdef _DEBUG , m_cashed(false) #endif + , m_parent(nullptr) { if (parent) { assert(bufferOwner == false); while (parent->m_parent) parent = parent->m_parent; parent->addRef(); + setLinear(parent->isLinear()); } #ifdef _DEBUG else if (bufferOwner) @@ -288,6 +292,7 @@ void TRaster::copy(const TRasterP &src0, const TPoint &offset) { srcRow += srcWrapSize; } } + setLinear(src0->isLinear()); dst->unlock(); src0->unlock(); } diff --git a/toonz/sources/common/trop/quickput.cpp b/toonz/sources/common/trop/quickput.cpp index df01d45..a7f98f9 100644 --- a/toonz/sources/common/trop/quickput.cpp +++ b/toonz/sources/common/trop/quickput.cpp @@ -1165,6 +1165,456 @@ void doQuickPutNoFilter(const TRaster64P &dn, const TRaster64P &up, dn->unlock(); up->unlock(); } + +//============================================================================= + +void doQuickPutNoFilter(const TRaster64P &dn, const TRasterFP &up, + const TAffine &aff, bool doPremultiply, + bool firstColumn) { + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) return; + + // contatore bit di shift + const int PADN = 16; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(std::max(up->getLx(), up->getLy()) < + (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = + TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = std::max(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = std::min(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = std::max(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = std::min(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate del + // pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) return; + + // TINT32 predecessore di up->getLx() + int lxPred = up->getLx() * (1 << PADN) - 1; + + // TINT32 predecessore di up->getLy() + int lyPred = up->getLy() * (1 << PADN) - 1; + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixel64 *dnRow = dn->pixels(yMin); + TPixelF *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), k = 0, ..., (xMax - xMin) + + // (2) equazione k-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente + // intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL + // < up->getLx()*(1<getLy()*(1<getLx()*(1< + // 0 <= xL0 + k*deltaXL + // <= lxPred + // + // 0 <= yL0 + k*deltaYL + // < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL + // <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up+(bordo destro/basso) + if ((xL0 < 0) || (lxPred < xL0)) continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lxPred < xL0) continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (xL0 < 0) continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up+(bordo destro/basso) + if ((yL0 < 0) || (lyPred < yL0)) continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lyPred < yL0) continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (yL0 < 0) continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = std::max({kMinX, kMinY, (int)0}); + int kMax = std::min({kMaxX, kMaxY, xMax - xMin}); + + TPixel64 *dnPix = dnRow + xMin + kMin; + TPixel64 *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && (0 <= yI) && + (yI <= up->getLy() - 1)); + + TPixelF upPix = *(upBasePix + (yI * upWrap + xI)); + + if (firstColumn) upPix.m = 1.0; + if (upPix.m <= 0.0) continue; + + TPixel64 upPix64 = toPixel64(upPix); + if (upPix.m >= 1.f) + *dnPix = upPix64; + else if (doPremultiply) + *dnPix = quickOverPixPremult(*dnPix, upPix64); + else + *dnPix = quickOverPix(*dnPix, upPix64); + } + } + dn->unlock(); + up->unlock(); +} + +//============================================================================= + +void doQuickPutNoFilter(const TRasterFP &dn, const TRasterFP &up, + const TAffine &aff, bool doPremultiply, + bool firstColumn) { + // se aff := TAffine(sx, 0, tx, 0, sy, ty) e' degenere la controimmagine + // di up e' un segmento (o un punto) + if ((aff.a11 * aff.a22 - aff.a12 * aff.a21) == 0) return; + + // contatore bit di shift + const int PADN = 16; + + // max dimensioni di up gestibili (limite imposto dal numero di bit + // disponibili per la parte intera di xL, yL) + assert(std::max(up->getLx(), up->getLy()) < + (1 << (8 * sizeof(int) - PADN - 1))); + + TRectD boundingBoxD = + TRectD(convert(dn->getBounds())) * + (aff * TRectD(-0.5, -0.5, up->getLx() - 0.5, up->getLy() - 0.5)); + + // clipping + if (boundingBoxD.x0 >= boundingBoxD.x1 || boundingBoxD.y0 >= boundingBoxD.y1) + return; + + // clipping y su dn + int yMin = std::max(tfloor(boundingBoxD.y0), 0); + + // clipping y su dn + int yMax = std::min(tceil(boundingBoxD.y1), dn->getLy() - 1); + + // clipping x su dn + int xMin = std::max(tfloor(boundingBoxD.x0), 0); + + // clipping x su dn + int xMax = std::min(tceil(boundingBoxD.x1), dn->getLx() - 1); + + // inversa di aff + TAffine invAff = inv(aff); + + // nel disegnare la y-esima scanline di dn, il passaggio al pixel + // successivo comporta l'incremento (deltaXD, deltaYD) delle coordinate del + // pixel corrispondente di up + double deltaXD = invAff.a11; + double deltaYD = invAff.a21; + + // deltaXD "TLonghizzato" (round) + int deltaXL = tround(deltaXD * (1 << PADN)); + + // deltaYD "TLonghizzato" (round) + int deltaYL = tround(deltaYD * (1 << PADN)); + + // se aff "TLonghizzata" (round) e' degenere la controimmagine di up e' un + // segmento (o un punto) + if ((deltaXL == 0) && (deltaYL == 0)) return; + + // TINT32 predecessore di up->getLx() + int lxPred = up->getLx() * (1 << PADN) - 1; + + // TINT32 predecessore di up->getLy() + int lyPred = up->getLy() * (1 << PADN) - 1; + + int dnWrap = dn->getWrap(); + int upWrap = up->getWrap(); + dn->lock(); + up->lock(); + + TPixelF *dnRow = dn->pixels(yMin); + TPixelF *upBasePix = up->pixels(); + + // scorre le scanline di boundingBoxD + for (int y = yMin; y <= yMax; y++, dnRow += dnWrap) { + // (1) equazione k-parametrica della y-esima scanline di boundingBoxD: + // (xMin, y) + k*(1, 0), k = 0, ..., (xMax - xMin) + + // (2) equazione k-parametrica dell'immagine mediante invAff di (1): + // invAff*(xMin, y) + k*(deltaXD, deltaYD), + // k = kMin, ..., kMax con 0 <= kMin <= kMax <= (xMax - xMin) + + // calcola kMin, kMax per la scanline corrente + // intersecando la (2) con i lati di up + + // il segmento [a, b] di up e' la controimmagine mediante aff della + // porzione di scanline [ (xMin, y), (xMax, y) ] di dn + + // TPointD b = invAff*TPointD(xMax, y); + TPointD a = invAff * TPointD(xMin, y); + + // (xL0, yL0) sono le coordinate di a (inizializzate per il round) + // in versione "TLonghizzata" + // 0 <= xL0 + k*deltaXL + // < up->getLx()*(1<getLy()*(1<getLx()*(1< + // 0 <= xL0 + k*deltaXL + // <= lxPred + // + // 0 <= yL0 + k*deltaYL + // < up->getLy()*(1< + // 0 <= yL0 + k*deltaYL + // <= lyPred + + // calcola kMinX, kMaxX + if (deltaXL == 0) { + // [a, b] verticale esterno ad up+(bordo destro/basso) + if ((xL0 < 0) || (lxPred < xL0)) continue; + // altrimenti usa solo + // kMinY, kMaxY ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaXL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lxPred < xL0) continue; + + kMaxX = (lxPred - xL0) / deltaXL; // floor + if (xL0 < 0) { + kMinX = ((-xL0) + deltaXL - 1) / deltaXL; // ceil + } + } else // (deltaXL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (xL0 < 0) continue; + + kMaxX = xL0 / (-deltaXL); // floor + if (lxPred < xL0) { + kMinX = (xL0 - lxPred - deltaXL - 1) / (-deltaXL); // ceil + } + } + + // calcola kMinY, kMaxY + if (deltaYL == 0) { + // [a, b] orizzontale esterno ad up+(bordo destro/basso) + if ((yL0 < 0) || (lyPred < yL0)) continue; + // altrimenti usa solo + // kMinX, kMaxX ((deltaXL != 0) || (deltaYL != 0)) + } else if (deltaYL > 0) { + // [a, b] esterno ad up+(bordo destro/basso) + if (lyPred < yL0) continue; + + kMaxY = (lyPred - yL0) / deltaYL; // floor + if (yL0 < 0) { + kMinY = ((-yL0) + deltaYL - 1) / deltaYL; // ceil + } + } else // (deltaYL < 0) + { + // [a, b] esterno ad up+(bordo destro/basso) + if (yL0 < 0) continue; + + kMaxY = yL0 / (-deltaYL); // floor + if (lyPred < yL0) { + kMinY = (yL0 - lyPred - deltaYL - 1) / (-deltaYL); // ceil + } + } + + // calcola kMin, kMax effettuando anche il clippind su dn + int kMin = std::max({kMinX, kMinY, (int)0}); + int kMax = std::min({kMaxX, kMaxY, xMax - xMin}); + + TPixelF *dnPix = dnRow + xMin + kMin; + TPixelF *dnEndPix = dnRow + xMin + kMax + 1; + + // (xL, yL) sono le coordinate (inizializzate per il round) + // in versione "TLonghizzata" del pixel corrente di up + int xL = xL0 + (kMin - 1) * deltaXL; // inizializza xL + int yL = yL0 + (kMin - 1) * deltaYL; // inizializza yL + + // scorre i pixel sulla y-esima scanline di boundingBoxD + for (; dnPix < dnEndPix; ++dnPix) { + xL += deltaXL; + yL += deltaYL; + + // il punto di up TPointD(xL/(1<> PADN; // round + int yI = yL >> PADN; // round + + assert((0 <= xI) && (xI <= up->getLx() - 1) && (0 <= yI) && + (yI <= up->getLy() - 1)); + + TPixelF upPix = *(upBasePix + (yI * upWrap + xI)); + + if (firstColumn) upPix.m = 1.0; + if (upPix.m <= 0.0) continue; + + if (upPix.m >= 1.f) + *dnPix = upPix; + else if (doPremultiply) + *dnPix = quickOverPixPremult(*dnPix, upPix); + else + *dnPix = quickOverPix(*dnPix, upPix); + } + } + dn->unlock(); + up->unlock(); +} + //============================================================================= void doQuickPutNoFilter(const TRaster64P &dn, const TRaster32P &up, @@ -4718,6 +5168,8 @@ void quickPut(const TRasterP &dn, const TRasterP &up, const TAffine &aff, TRasterGR8P up8 = up; TRaster64P dn64 = dn; TRaster64P up64 = up; + TRasterFP upF = up; + TRasterFP dnF = dn; if (up8 && dn32) { assert(filterType == TRop::ClosestPixel); @@ -4747,6 +5199,10 @@ void quickPut(const TRasterP &dn, const TRasterP &up, const TAffine &aff, doQuickPutNoFilter(dn64, up64, aff, doPremultiply, firstColumn); else if (dn64 && up32) doQuickPutNoFilter(dn64, up32, aff, doPremultiply, firstColumn); + else if (dn64 && upF) + doQuickPutNoFilter(dn64, upF, aff, doPremultiply, firstColumn); + else if (dnF && upF) + doQuickPutNoFilter(dnF, upF, aff, doPremultiply, firstColumn); else throw TRopException("raster type mismatch"); } diff --git a/toonz/sources/common/trop/tblur.cpp b/toonz/sources/common/trop/tblur.cpp index d6d5f57..c022d81 100644 --- a/toonz/sources/common/trop/tblur.cpp +++ b/toonz/sources/common/trop/tblur.cpp @@ -13,7 +13,6 @@ #include #endif - namespace { #ifdef _WIN32 @@ -172,10 +171,10 @@ inline void blur_code(PIXEL_SRC *row1, PIXEL_DST *row2, int length, float coeff, sigma2.b += pix2->b; sigma2.m += pix2->m; - sigma3.r += i * (pix1->r + pix2->r); - sigma3.g += i * (pix1->g + pix2->g); - sigma3.b += i * (pix1->b + pix2->b); - sigma3.m += i * (pix1->m + pix2->m); + sigma3.r += (T)i * (pix1->r + pix2->r); + sigma3.g += (T)i * (pix1->g + pix2->g); + sigma3.b += (T)i * (pix1->b + pix2->b); + sigma3.m += (T)i * (pix1->m + pix2->m); pix1++; pix2--; @@ -522,6 +521,31 @@ void store_colRgb(T *buffer, int wrap, int r_ly, T *col, int ly, int x, int dy, assert(false); } +template <> +void store_colRgb(TPixelF *buffer, int wrap, int r_ly, TPixelF *col, + int ly, int x, int dy, int backlit, double blur) { + int i; + buffer += x; + if (backlit) { + double ampl = 1.0 + blur / 15.0; + float crop_val = 204.f / 255.f; + for (i = ((dy >= 0) ? 0 : -dy); i < std::min(ly, r_ly - dy); i++) { + float val = col[i].r * ampl; + buffer->r = (val > crop_val) ? crop_val : val; + val = col[i].g * ampl; + buffer->g = (val > crop_val) ? crop_val : val; + val = col[i].b * ampl; + buffer->b = (val > crop_val) ? crop_val : val; + val = col[i].m * ampl; + buffer->m = (val > crop_val) ? crop_val : val; + buffer += wrap; + } + } else + for (i = ((dy >= 0) ? 0 : -dy); i < std::min(ly, r_ly - dy); i++) { + *buffer = col[i]; + buffer += wrap; + } +} //------------------------------------------------------------------- template void store_colGray(T *buffer, int wrap, int r_ly, T *col, int ly, int x, int dy, @@ -576,7 +600,19 @@ void do_filtering_chan(BlurPixel

*row1, T *row2, int length, float coeff, BLUR_CODE((P)0.5, Q) } } +template <> +void do_filtering_chan(BlurPixel *row1, + TPixelF *row2, int length, + float coeff, float coeffq, + int brad, float diff, + bool useSSE) { + int i; + double rsum, gsum, bsum, msum; + BlurPixel sigma1, sigma2, sigma3, desigma; + BlurPixel *pix1, *pix2, *pix3, *pix4; + BLUR_CODE(0.0, float) +} //------------------------------------------------------------------- template @@ -741,14 +777,14 @@ void load_rowGray(TRasterPT &rin, T *row, int lx, int y, int brad, int bx1, template void do_filtering_floatRgb(T *row1, BlurPixel

*row2, int length, float coeff, float coeffq, int brad, float diff, bool useSSE) { -/* - int i; - float rsum, gsum, bsum, msum; - CASM_FPIXEL sigma1, sigma2, sigma3, desigma; - TPixel32 *pix1, *pix2, *pix3, *pix4; + /* + int i; + float rsum, gsum, bsum, msum; + CASM_FPIXEL sigma1, sigma2, sigma3, desigma; + TPixel32 *pix1, *pix2, *pix3, *pix4; - BLUR_CODE(0, unsigned char) -*/ + BLUR_CODE(0, unsigned char) + */ #ifdef USE_SSE2 if (useSSE) @@ -771,7 +807,7 @@ void doBlurRgb(TRasterPT &dstRas, TRasterPT &srcRas, double blur, int dx, // int border = brad*2; // per sicurezza - coeff = (float)(blur / + coeff = (float)(blur / (brad - brad * brad + blur * (2 * brad - 1))); /*sum of the weights of triangolar filter. */ @@ -805,7 +841,7 @@ void doBlurRgb(TRasterPT &dstRas, TRasterPT &srcRas, double blur, int dx, r1->lock(); fbuffer = (BlurPixel

*)r1->getRawData(); // new CASM_FPIXEL [llx *ly]; row1 = new T[llx + 2 * brad]; - col1 = new BlurPixel

[ lly + 2 * brad ]; + col1 = new BlurPixel

[lly + 2 * brad]; col2 = new T[lly]; } @@ -879,8 +915,8 @@ void doBlurGray(TRasterPT &dstRas, TRasterPT &srcRas, double blur, int dx, float coeff, coeffq, diff; int bx1 = 0, by1 = 0, bx2 = 0, by2 = 0; - brad = (int)ceil(blur); /* number of pixels involved in the filtering */ - coeff = (float)(blur / + brad = (int)ceil(blur); /* number of pixels involved in the filtering */ + coeff = (float)(blur / (brad - brad * brad + blur * (2 * brad - 1))); /*sum of the weights of triangolar filter. */ @@ -958,30 +994,39 @@ void TRop::blur(const TRasterP &dstRas, const TRasterP &srcRas, double blur, int dx, int dy, bool useSSE) { TRaster32P dstRas32 = dstRas; TRaster32P srcRas32 = srcRas; - - if (dstRas32 && srcRas32) + if (dstRas32 && srcRas32) { doBlurRgb(dstRas32, srcRas32, blur, dx, dy, useSSE); - else { - TRaster64P dstRas64 = dstRas; - TRaster64P srcRas64 = srcRas; - if (dstRas64 && srcRas64) - doBlurRgb(dstRas64, srcRas64, blur, dx, dy, - useSSE); - else { - TRasterGR8P dstRasGR8 = dstRas; - TRasterGR8P srcRasGR8 = srcRas; - - if (dstRasGR8 && srcRasGR8) - doBlurGray(dstRasGR8, srcRasGR8, blur, dx, dy); - else { - TRasterGR16P dstRasGR16 = dstRas; - TRasterGR16P srcRasGR16 = srcRas; - - if (dstRasGR16 && srcRasGR16) - doBlurGray(dstRasGR16, srcRasGR16, blur, dx, dy); - else - throw TException("TRop::blur unsupported pixel type"); - } - } + return; + } + + TRaster64P dstRas64 = dstRas; + TRaster64P srcRas64 = srcRas; + if (dstRas64 && srcRas64) { + doBlurRgb(dstRas64, srcRas64, blur, dx, dy, + useSSE); + return; + } + + TRasterFP dstRasF = dstRas; + TRasterFP srcRasF = srcRas; + if (dstRasF && srcRasF) { + doBlurRgb(dstRasF, srcRasF, blur, dx, dy, useSSE); + return; } + + TRasterGR8P dstRasGR8 = dstRas; + TRasterGR8P srcRasGR8 = srcRas; + if (dstRasGR8 && srcRasGR8) { + doBlurGray(dstRasGR8, srcRasGR8, blur, dx, dy); + return; + } + + TRasterGR16P dstRasGR16 = dstRas; + TRasterGR16P srcRasGR16 = srcRas; + if (dstRasGR16 && srcRasGR16) { + doBlurGray(dstRasGR16, srcRasGR16, blur, dx, dy); + return; + } + + throw TException("TRop::blur unsupported pixel type"); } diff --git a/toonz/sources/common/trop/tcheckboard.cpp b/toonz/sources/common/trop/tcheckboard.cpp index 71afc4a..a9be5f7 100644 --- a/toonz/sources/common/trop/tcheckboard.cpp +++ b/toonz/sources/common/trop/tcheckboard.cpp @@ -82,14 +82,15 @@ void TRop::checkBoard(TRasterP rout, const TPixel32 &pix1, const TPixel32 &pix2, // assert(offset.x<=dim.lx && offset.y<=dim.ly); TRaster32P rout32 = rout; + TRaster64P rout64 = rout; + TRasterFP routF = rout; if (rout32) do_checkBoard(rout32, pix1, pix2, dim, offset); - else { - TRaster64P rout64 = rout; - if (rout64) - do_checkBoard(rout64, toPixel64(pix1), toPixel64(pix2), dim, - offset); - else - throw TRopException("unsupported pixel type"); - } + else if (rout64) + do_checkBoard(rout64, toPixel64(pix1), toPixel64(pix2), dim, + offset); + else if (routF) + do_checkBoard(routF, toPixelF(pix1), toPixelF(pix2), dim, offset); + else + throw TRopException("unsupported pixel type"); } diff --git a/toonz/sources/common/trop/tconvert.cpp b/toonz/sources/common/trop/tconvert.cpp index 413ae34..72a877a 100644 --- a/toonz/sources/common/trop/tconvert.cpp +++ b/toonz/sources/common/trop/tconvert.cpp @@ -252,9 +252,9 @@ static void do_convert(const TRasterYUV422P &dst, const TRaster32P &src) { /* limit the chroma */ if (u1 < -112) u1 = -112; - if (u1 > 111) u1 = 111; + if (u1 > 111) u1 = 111; if (v1 < -112) v1 = -112; - if (v1 > 111) v1 = 111; + if (v1 > 111) v1 = 111; /* limit the lum */ if (y1 > 0x00dbffff) y1 = 0x00dbffff; @@ -291,17 +291,17 @@ static void do_convert(const TRaster32P &dst, const TRasterYUV422P &src) { y2 -= 16; in++; - r = 76310 * y1 + 104635 * v; + r = 76310 * y1 + 104635 * v; if (r > 0xFFFFFF) r = 0xFFFFFF; - if (r <= 0xFFFF) r = 0; + if (r <= 0xFFFF) r = 0; - g = 76310 * y1 + -25690 * u + -53294 * v; + g = 76310 * y1 + -25690 * u + -53294 * v; if (g > 0xFFFFFF) g = 0xFFFFFF; - if (g <= 0xFFFF) g = 0; + if (g <= 0xFFFF) g = 0; - b = 76310 * y1 + 132278 * u; + b = 76310 * y1 + 132278 * u; if (b > 0xFFFFFF) b = 0xFFFFFF; - if (b <= 0xFFFF) b = 0; + if (b <= 0xFFFF) b = 0; buf->r = (UCHAR)(r >> 16); buf->g = (UCHAR)(g >> 16); @@ -309,17 +309,17 @@ static void do_convert(const TRaster32P &dst, const TRasterYUV422P &src) { buf->m = (UCHAR)255; buf++; - r = 76310 * y2 + 104635 * v; + r = 76310 * y2 + 104635 * v; if (r > 0xFFFFFF) r = 0xFFFFFF; - if (r <= 0xFFFF) r = 0; + if (r <= 0xFFFF) r = 0; - g = 76310 * y2 + -25690 * u + -53294 * v; + g = 76310 * y2 + -25690 * u + -53294 * v; if (g > 0xFFFFFF) g = 0xFFFFFF; - if (g <= 0xFFFF) g = 0; + if (g <= 0xFFFF) g = 0; - b = 76310 * y2 + 132278 * u; + b = 76310 * y2 + 132278 * u; if (b > 0xFFFFFF) b = 0xFFFFFF; - if (b <= 0xFFFF) b = 0; + if (b <= 0xFFFF) b = 0; buf->r = (UCHAR)(r >> 16); buf->g = (UCHAR)(g >> 16); @@ -330,6 +330,94 @@ static void do_convert(const TRaster32P &dst, const TRasterYUV422P &src) { } //****************************************************************** +// Conversion from/to double raster +//****************************************************************** + +static void do_convert(const TRasterFP &dst, const TRaster32P &src) { + assert(dst->getSize() == src->getSize()); + int lx = src->getLx(); + for (int y = 0; y < src->getLy(); y++) { + TPixelF *outPix = dst->pixels(y); + TPixel32 *inPix = src->pixels(y); + TPixel32 *inEndPix = inPix + lx; + for (; inPix < inEndPix; ++outPix, ++inPix) { + outPix->r = (float)inPix->r / (float)TPixel32::maxChannelValue; + outPix->g = (float)inPix->g / (float)TPixel32::maxChannelValue; + outPix->b = (float)inPix->b / (float)TPixel32::maxChannelValue; + outPix->m = (float)inPix->m / (float)TPixel32::maxChannelValue; + } + } +} + +//----------------------------------------------------------------------------- + +static void do_convert(const TRasterFP &dst, const TRaster64P &src) { + assert(dst->getSize() == src->getSize()); + int lx = src->getLx(); + for (int y = 0; y < src->getLy(); y++) { + TPixelF *outPix = dst->pixels(y); + TPixel64 *inPix = src->pixels(y); + TPixel64 *inEndPix = inPix + lx; + for (; inPix < inEndPix; ++outPix, ++inPix) { + outPix->r = (float)inPix->r / (float)TPixel64::maxChannelValue; + outPix->g = (float)inPix->g / (float)TPixel64::maxChannelValue; + outPix->b = (float)inPix->b / (float)TPixel64::maxChannelValue; + outPix->m = (float)inPix->m / (float)TPixel64::maxChannelValue; + } + } +} + +//----------------------------------------------------------------------------- + +static void do_convert(const TRaster32P &dst, const TRasterFP &src) { + auto clamp01 = [](float val) { + return (val < 0.f) ? 0.f : (val > 1.f) ? 1.f : val; + }; + assert(dst->getSize() == src->getSize()); + int lx = src->getLx(); + for (int y = 0; y < src->getLy(); y++) { + TPixel32 *outPix = dst->pixels(y); + TPixelF *inPix = src->pixels(y); + TPixelF *inEndPix = inPix + lx; + for (; inPix < inEndPix; ++outPix, ++inPix) { + outPix->r = (TPixel32::Channel)( + clamp01(inPix->r) * (float)TPixel32::maxChannelValue + 0.5f); + outPix->g = (TPixel32::Channel)( + clamp01(inPix->g) * (float)TPixel32::maxChannelValue + 0.5f); + outPix->b = (TPixel32::Channel)( + clamp01(inPix->b) * (float)TPixel32::maxChannelValue + 0.5f); + outPix->m = (TPixel32::Channel)( + clamp01(inPix->m) * (float)TPixel32::maxChannelValue + 0.5f); + } + } +} + +//----------------------------------------------------------------------------- + +static void do_convert(const TRaster64P &dst, const TRasterFP &src) { + auto clamp01 = [](float val) { + return (val < 0.f) ? 0.f : (val > 1.f) ? 1.f : val; + }; + assert(dst->getSize() == src->getSize()); + int lx = src->getLx(); + for (int y = 0; y < src->getLy(); y++) { + TPixel64 *outPix = dst->pixels(y); + TPixelF *inPix = src->pixels(y); + TPixelF *inEndPix = inPix + lx; + for (; inPix < inEndPix; ++outPix, ++inPix) { + outPix->r = (TPixel64::Channel)( + clamp01(inPix->r) * (float)TPixel64::maxChannelValue + 0.5f); + outPix->g = (TPixel64::Channel)( + clamp01(inPix->g) * (float)TPixel64::maxChannelValue + 0.5f); + outPix->b = (TPixel64::Channel)( + clamp01(inPix->b) * (float)TPixel64::maxChannelValue + 0.5f); + outPix->m = (TPixel64::Channel)( + clamp01(inPix->m) * (float)TPixel64::maxChannelValue + 0.5f); + } + } +} + +//****************************************************************** // Main conversion function //****************************************************************** @@ -337,17 +425,19 @@ void TRop::convert(TRasterP dst, const TRasterP &src) { if (dst->getSize() != src->getSize()) throw TRopException("convert: size mismatch"); - TRaster32P dst32 = dst; - TRasterGR8P dst8 = dst; - TRasterGR16P dst16 = dst; - TRaster64P dst64 = dst; - TRasterCM32P dstCm = dst; + TRaster32P dst32 = dst; + TRasterGR8P dst8 = dst; + TRasterGR16P dst16 = dst; + TRaster64P dst64 = dst; + TRasterCM32P dstCm = dst; + TRasterYUV422P dstYUV = dst; + TRasterFP dstF = dst; TRaster32P src32 = src; TRasterGR8P src8 = src; TRaster64P src64 = src; TRasterYUV422P srcYUV = src; - TRasterYUV422P dstYUV = dst; + TRasterFP srcF = src; src->lock(); dst->lock(); @@ -372,6 +462,15 @@ void TRop::convert(TRasterP dst, const TRasterP &src) { do_convert(dstCm, src32); // else if (dstCm && src8) do_convert(dstCm, src8); // + // conversion from/to double + else if (dstF && src32) + do_convert(dstF, src32); + else if (dstF && src64) + do_convert(dstF, src64); + else if (dst32 && srcF) + do_convert(dst32, srcF); + else if (dst64 && srcF) + do_convert(dst64, srcF); else { dst->unlock(); src->unlock(); @@ -379,6 +478,7 @@ void TRop::convert(TRasterP dst, const TRasterP &src) { throw TRopException("unsupported pixel type"); } + dst->setLinear(src->isLinear()); dst->unlock(); src->unlock(); } diff --git a/toonz/sources/common/trop/tinvert.cpp b/toonz/sources/common/trop/tinvert.cpp index 0208a8b..e473b62 100644 --- a/toonz/sources/common/trop/tinvert.cpp +++ b/toonz/sources/common/trop/tinvert.cpp @@ -42,9 +42,9 @@ inline void do_invert(TRasterPT ras, bool invRed, bool invGreen, pixIn = rowIn; endPix = pixIn + lx; while (pixIn < endPix) { - if (invRed) pixIn->r = pixIn->m - pixIn->r; + if (invRed) pixIn->r = pixIn->m - pixIn->r; if (invGreen) pixIn->g = pixIn->m - pixIn->g; - if (invBlue) pixIn->b = pixIn->m - pixIn->b; + if (invBlue) pixIn->b = pixIn->m - pixIn->b; if (invMatte) pixIn->m = ~pixIn->m; ++pixIn; } @@ -74,8 +74,35 @@ inline void do_invert(TRasterPT ras) { rowIn += wrap; } } + +//------------------------------------------------------------------------------ + +template <> +inline void do_invert(TRasterFP ras, bool invRed, bool invGreen, + bool invBlue, bool invMatte) { + int wrap = ras->getWrap(); + int lx = ras->getLx(); + TPixelF *rowIn = ras->pixels(); + TPixelF *lastPix = rowIn + wrap * ras->getLy(); + TPixelF *pixIn = 0; + TPixelF *endPix = 0; + + while (pixIn < lastPix) { + pixIn = rowIn; + endPix = pixIn + lx; + while (pixIn < endPix) { + if (invRed) pixIn->r = pixIn->m - pixIn->r; + if (invGreen) pixIn->g = pixIn->m - pixIn->g; + if (invBlue) pixIn->b = pixIn->m - pixIn->b; + if (invMatte) pixIn->m = 1.f - pixIn->m; + ++pixIn; + } + rowIn += wrap; + } } +} // namespace + //------------------------------------------------------------------------------ void TRop::invert(TRasterP ras, bool invRed, bool invGreen, bool invBlue, @@ -84,29 +111,33 @@ void TRop::invert(TRasterP ras, bool invRed, bool invGreen, bool invBlue, bool flag = invRed && invGreen && invBlue && !invMatte; TRaster32P ras32 = ras; + TRaster64P ras64 = ras; + TRasterFP rasF = ras; + TRasterGR8P ras8 = ras; ras->lock(); - if (ras32) + + if (ras32) { if (flag) do_invert(ras32); else do_invert(ras, invRed, invGreen, invBlue, invMatte); + } else if (ras64) { + if (flag) + do_invert(ras64); + else + do_invert(ras64, invRed, invGreen, invBlue, invMatte); + } else if (rasF) { + if (flag) + do_invert(rasF); + else + do_invert(rasF, invRed, invGreen, invBlue, invMatte); + } else if (ras8) + do_invert(ras8); else { - TRaster64P ras64 = ras; - if (ras64) - if (flag) - do_invert(ras64); - else - do_invert(ras64, invRed, invGreen, invBlue, invMatte); - else { - TRasterGR8P ras8 = ras; - if (ras8) - do_invert(ras8); - else { - ras->unlock(); - throw TRopException("unsupported pixel type"); - } - } + ras->unlock(); + throw TRopException("unsupported pixel type"); } + ras->unlock(); } diff --git a/toonz/sources/common/trop/toperators.cpp b/toonz/sources/common/trop/toperators.cpp index a1bb38b..55ffe33 100644 --- a/toonz/sources/common/trop/toperators.cpp +++ b/toonz/sources/common/trop/toperators.cpp @@ -92,6 +92,18 @@ inline double luminance(TPixel64 *pix) { //----------------------------------------------------------------------------- +#define FOR_EACH_PIXEL_F_BEGIN_LOOP \ + assert(upF &&downF &&outF); \ + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelF, upF, TPixelF, downF, TPixelF, outF) + +//----------------------------------------------------------------------------- + +#define FOR_EACH_PIXEL_F_END_LOOP \ + assert(upF &&downF &&outF); \ + FOR_EACH_PIXEL_END_LOOP(upF, downF, outF) + +//----------------------------------------------------------------------------- + #define FOR_EACH_PIXEL_8_BEGIN_LOOP \ assert(up8 &&down8 &&out8); \ FOR_EACH_PIXEL_BEGIN_LOOP(TPixelGR8, up8, TPixelGR8, down8, TPixelGR8, out8) @@ -128,43 +140,63 @@ void TRop::add(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout, } FOR_EACH_PIXEL_32_END_LOOP - } else { - TRaster64P up64 = rup; - TRaster64P down64 = rdown; - TRaster64P out64 = rout; + return; + } - if (up64 && down64 && out64) { - FOR_EACH_PIXEL_64_BEGIN_LOOP + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; - TINT32 r, g, b, m; - r = downPix->r + tround(upPix->r * v); - g = downPix->g + tround(upPix->g * v); - b = downPix->b + tround(upPix->b * v); - m = downPix->m + tround(upPix->m * v); + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP - outPix->r = (USHORT)tcrop(r, 0, 0xffff); - outPix->g = (USHORT)tcrop(g, 0, 0xffff); - outPix->b = (USHORT)tcrop(b, 0, 0xffff); - outPix->m = (USHORT)tcrop(m, 0, 0xffff); + TINT32 r, g, b, m; + r = downPix->r + tround(upPix->r * v); + g = downPix->g + tround(upPix->g * v); + b = downPix->b + tround(upPix->b * v); + m = downPix->m + tround(upPix->m * v); - FOR_EACH_PIXEL_64_END_LOOP - } else { - TRasterGR8P up8 = rup; - TRasterGR8P down8 = rdown; - TRasterGR8P out8 = rout; + outPix->r = (USHORT)tcrop(r, 0, 0xffff); + outPix->g = (USHORT)tcrop(g, 0, 0xffff); + outPix->b = (USHORT)tcrop(b, 0, 0xffff); + outPix->m = (USHORT)tcrop(m, 0, 0xffff); - if (up8 && down8 && out8) { - FOR_EACH_PIXEL_8_BEGIN_LOOP + FOR_EACH_PIXEL_64_END_LOOP + return; + } - USHORT value = troundp(upPix->value * v) + downPix->value; + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; - outPix->value = (UCHAR)tcrop(value, 0, 255); + if (upF && downF && outF) { + FOR_EACH_PIXEL_F_BEGIN_LOOP - FOR_EACH_PIXEL_8_END_LOOP - } else - throw TRopException("TRop::add invalid raster combination"); - } + outPix->r = downPix->r + upPix->r * v; + outPix->g = downPix->g + upPix->g * v; + outPix->b = downPix->b + upPix->b * v; + outPix->m = tcrop(downPix->m + upPix->m * v, 0.f, 1.f); + + FOR_EACH_PIXEL_F_END_LOOP + return; + } + + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + USHORT value = troundp(upPix->value * v) + downPix->value; + + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + return; } + + throw TRopException("TRop::add invalid raster combination"); } //----------------------------------------------------------------------------- @@ -190,43 +222,62 @@ void TRop::add(const TRasterP &rup, const TRasterP &rdown, outPix->m = (UCHAR)tcrop(m, 0, 255); FOR_EACH_PIXEL_32_END_LOOP - } else { - TRaster64P up64 = rup; - TRaster64P down64 = rdown; - TRaster64P out64 = rout; + return; + } + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; - if (up64 && down64 && out64) { - FOR_EACH_PIXEL_64_BEGIN_LOOP + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP - TINT32 r, g, b, m; - r = downPix->r + upPix->r; - g = downPix->g + upPix->g; - b = downPix->b + upPix->b; - m = downPix->m + upPix->m; + TINT32 r, g, b, m; + r = downPix->r + upPix->r; + g = downPix->g + upPix->g; + b = downPix->b + upPix->b; + m = downPix->m + upPix->m; - outPix->r = (USHORT)tcrop(r, 0, 0xffff); - outPix->g = (USHORT)tcrop(g, 0, 0xffff); - outPix->b = (USHORT)tcrop(b, 0, 0xffff); - outPix->m = (USHORT)tcrop(m, 0, 0xffff); + outPix->r = (USHORT)tcrop(r, 0, 0xffff); + outPix->g = (USHORT)tcrop(g, 0, 0xffff); + outPix->b = (USHORT)tcrop(b, 0, 0xffff); + outPix->m = (USHORT)tcrop(m, 0, 0xffff); - FOR_EACH_PIXEL_64_END_LOOP - } else { - TRasterGR8P up8 = rup; - TRasterGR8P down8 = rdown; - TRasterGR8P out8 = rout; + FOR_EACH_PIXEL_64_END_LOOP + return; + } - if (up8 && down8 && out8) { - FOR_EACH_PIXEL_8_BEGIN_LOOP + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; - USHORT value = upPix->value + downPix->value; + if (upF && downF && outF) { + FOR_EACH_PIXEL_F_BEGIN_LOOP - outPix->value = (UCHAR)tcrop(value, 0, 255); + outPix->r = downPix->r + upPix->r; + outPix->g = downPix->g + upPix->g; + outPix->b = downPix->b + upPix->b; + outPix->m = tcrop(downPix->m + upPix->m, 0.f, 1.f); - FOR_EACH_PIXEL_8_END_LOOP - } else - throw TRopException("TRop::add invalid raster combination"); - } + FOR_EACH_PIXEL_F_END_LOOP + return; + } + + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + USHORT value = upPix->value + downPix->value; + + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + return; } + + throw TRopException("TRop::add invalid raster combination"); } //----------------------------------------------------------------------------- @@ -277,21 +328,36 @@ void TRop::colordodge(const TRasterP &rup, const TRasterP &rdown, FOR_EACH_PIXEL_64_END_LOOP } else { - TRasterGR8P up8 = rup; - TRasterGR8P down8 = rdown; - TRasterGR8P out8 = rout; + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; - if (up8 && down8 && out8) { - FOR_EACH_PIXEL_8_BEGIN_LOOP - USHORT value; - if (downPix->value) - value = (USHORT)((downPix->value << 8) / (255.0 - upPix->value)); + if (upF && downF && outF) { + FOR_EACH_PIXEL_F_BEGIN_LOOP - outPix->value = (UCHAR)tcrop(value, 0, 255); + outPix->r = downPix->r / (1.f - upPix->r); + outPix->g = downPix->g / (1.f - upPix->g); + outPix->b = downPix->b / (1.f - upPix->b); + outPix->m = tcrop(downPix->m + upPix->m, 0.f, 1.f); - FOR_EACH_PIXEL_8_END_LOOP - } else - throw TRopException("TRop::color dodge invalid raster combination"); + FOR_EACH_PIXEL_F_END_LOOP + } else { + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + USHORT value; + if (downPix->value) + value = (USHORT)((downPix->value << 8) / (255.0 - upPix->value)); + + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + } else + throw TRopException("TRop::color dodge invalid raster combination"); + } } } } @@ -392,8 +458,50 @@ void TRop::colorburn(const TRasterP &rup, const TRasterP &rdown, outPix = downPix; } FOR_EACH_PIXEL_64_END_LOOP - } else - throw TRopException("TRop::color burn invalid raster combination"); + } else { + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; + + if (upF && downF && outF) { + FOR_EACH_PIXEL_F_BEGIN_LOOP + float r, g, b; + if (upPix->m > 0.f) { + if (downPix->r <= 0.f || downPix->r >= 1.f) + r = downPix->r; + else if (upPix->r) + r = 1.f - (1.f - downPix->r) / upPix->r; + else + r = 0.f; + if (downPix->g <= 0.f || downPix->g >= 1.f) + g = downPix->g; + else if (upPix->g) + g = 1.f - (1.f - downPix->g) / upPix->g; + else + g = 0.f; + if (downPix->b <= 0.f || downPix->b >= 1.f) + b = downPix->b; + else if (upPix->b) + b = 1.f - (1.f - downPix->b) / upPix->b; + else + b = 0.f; + + if (upPix->m < 1.f) { + overPix(*outPix, *downPix, + TPixelF(r, g, b, upPix->m)); + } else { + outPix->r = r; + outPix->g = g; + outPix->b = b; + outPix->m = downPix->m; + } + } else { + outPix = downPix; + } + FOR_EACH_PIXEL_F_END_LOOP + } else + throw TRopException("TRop::color burn invalid raster combination"); + } } } @@ -428,54 +536,86 @@ void TRop::screen(const TRasterP &rup, const TRasterP &rdown, outPix->m = upPix->m; } FOR_EACH_PIXEL_32_END_LOOP - } else { - TRaster64P up64 = rup; - TRaster64P down64 = rdown; - TRaster64P out64 = rout; + return; + } - if (up64 && down64 && out64) { - FOR_EACH_PIXEL_64_BEGIN_LOOP + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; - double r, g, b; - r = 65536 - (65536 - upPix->r) * ((65536 - downPix->r) / 65536.0); - g = 65536 - (65536 - upPix->g) * ((65536 - downPix->g) / 65536.0); - b = 65536 - (65536 - upPix->b) * ((65536 - downPix->b) / 65536.0); - - if (upPix->m != 65535) { - double m; - m = 65536 - (65536 - upPix->m) * ((65536 - downPix->m) / 65536.0); - TPixel64 tmpPix; - tmpPix.r = (USHORT)tcrop(r, 0, 65535); - tmpPix.g = (USHORT)tcrop(g, 0, 65535); - tmpPix.b = (USHORT)tcrop(b, 0, 65535); - tmpPix.m = (USHORT)tcrop(m, 0, 65535); - overPix(*outPix, *downPix, tmpPix); - } else { - outPix->r = (USHORT)tcrop(r, 0, 65535); - outPix->g = (USHORT)tcrop(g, 0, 65535); - outPix->b = (USHORT)tcrop(b, 0, 65535); - outPix->m = upPix->m; - } + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP - FOR_EACH_PIXEL_64_END_LOOP + double r, g, b; + r = 65536 - (65536 - upPix->r) * ((65536 - downPix->r) / 65536.0); + g = 65536 - (65536 - upPix->g) * ((65536 - downPix->g) / 65536.0); + b = 65536 - (65536 - upPix->b) * ((65536 - downPix->b) / 65536.0); + + if (upPix->m != 65535) { + double m; + m = 65536 - (65536 - upPix->m) * ((65536 - downPix->m) / 65536.0); + TPixel64 tmpPix; + tmpPix.r = (USHORT)tcrop(r, 0, 65535); + tmpPix.g = (USHORT)tcrop(g, 0, 65535); + tmpPix.b = (USHORT)tcrop(b, 0, 65535); + tmpPix.m = (USHORT)tcrop(m, 0, 65535); + overPix(*outPix, *downPix, tmpPix); } else { - TRasterGR8P up8 = rup; - TRasterGR8P down8 = rdown; - TRasterGR8P out8 = rout; + outPix->r = (USHORT)tcrop(r, 0, 65535); + outPix->g = (USHORT)tcrop(g, 0, 65535); + outPix->b = (USHORT)tcrop(b, 0, 65535); + outPix->m = upPix->m; + } - if (up8 && down8 && out8) { - FOR_EACH_PIXEL_8_BEGIN_LOOP - USHORT value; - if (downPix->value) - value = (USHORT)((downPix->value << 8) / (255.0 - upPix->value)); + FOR_EACH_PIXEL_64_END_LOOP + return; + } - outPix->value = (UCHAR)tcrop(value, 0, 255); + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; - FOR_EACH_PIXEL_8_END_LOOP - } else - throw TRopException("TRop::color dodge invalid raster combination"); + if (upF && downF && outF) { + FOR_EACH_PIXEL_F_BEGIN_LOOP + + float r, g, b; + r = 1.f - (1.f - upPix->r) * (1.f - downPix->r); + g = 1.f - (1.f - upPix->g) * (1.f - downPix->g); + b = 1.f - (1.f - upPix->b) * (1.f - downPix->b); + + if (upPix->m <= 1.f) { + float m; + m = 1.f - (1.f - upPix->m) * (1.f - downPix->m); + TPixelF tmpPix(r, g, b, tcrop(m, 0.f, 1.f)); + overPix(*outPix, *downPix, tmpPix); + } else { + outPix->r = r; + outPix->g = g; + outPix->b = b; + outPix->m = upPix->m; } + + FOR_EACH_PIXEL_F_END_LOOP + return; + } + + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + USHORT value; + if (downPix->value) + value = (USHORT)((downPix->value << 8) / (255.0 - upPix->value)); + + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + return; } + + throw TRopException("TRop::color dodge invalid raster combination"); } //----------------------------------------------------------------------------- @@ -500,40 +640,61 @@ void TRop::sub(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout, outPix->m = (UCHAR)tcrop(m, 0, 255); FOR_EACH_PIXEL_32_END_LOOP - } else { - TRaster64P up64 = rup; - TRaster64P down64 = rdown; - TRaster64P out64 = rout; - - if (up64 && down64 && out64) { - FOR_EACH_PIXEL_64_BEGIN_LOOP - - TINT32 r = downPix->r - upPix->r; - TINT32 g = downPix->g - upPix->g; - TINT32 b = downPix->b - upPix->b; - TINT32 m = downPix->m - upPix->m; - outPix->r = (USHORT)tcrop(r, 0, 0xffff); - outPix->g = (USHORT)tcrop(g, 0, 0xffff); - outPix->b = (USHORT)tcrop(b, 0, 0xffff); - outPix->m = (USHORT)tcrop(m, 0, 0xffff); - - FOR_EACH_PIXEL_64_END_LOOP - } else { - TRasterGR8P up8 = rup; - TRasterGR8P down8 = rdown; - TRasterGR8P out8 = rout; + return; + } - if (up8 && down8 && out8) { - FOR_EACH_PIXEL_8_BEGIN_LOOP + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; - SHORT value = upPix->value - downPix->value; - outPix->value = (UCHAR)tcrop(value, 0, 255); + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP - FOR_EACH_PIXEL_8_END_LOOP - } else - throw TRopException("TRop::sub invalid raster combination"); - } + TINT32 r = downPix->r - upPix->r; + TINT32 g = downPix->g - upPix->g; + TINT32 b = downPix->b - upPix->b; + TINT32 m = downPix->m - upPix->m; + outPix->r = (USHORT)tcrop(r, 0, 0xffff); + outPix->g = (USHORT)tcrop(g, 0, 0xffff); + outPix->b = (USHORT)tcrop(b, 0, 0xffff); + outPix->m = (USHORT)tcrop(m, 0, 0xffff); + + FOR_EACH_PIXEL_64_END_LOOP + return; + } + + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; + + if (upF && downF && outF) { + FOR_EACH_PIXEL_F_BEGIN_LOOP + + outPix->r = downPix->r - upPix->r; + outPix->g = downPix->g - upPix->g; + outPix->b = downPix->b - upPix->b; + outPix->m = tcrop(downPix->m - upPix->m, 0.f, 1.f); + + FOR_EACH_PIXEL_F_END_LOOP + return; + } + + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + SHORT value = upPix->value - downPix->value; + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + return; } + + throw TRopException("TRop::sub invalid raster combination"); + } else { if (up32 && down32 && out32) { FOR_EACH_PIXEL_32_BEGIN_LOOP @@ -549,40 +710,60 @@ void TRop::sub(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout, outPix->m = (UCHAR)tcrop(m, 0, 255); FOR_EACH_PIXEL_32_END_LOOP - } else { - TRaster64P up64 = rup; - TRaster64P down64 = rdown; - TRaster64P out64 = rout; - - if (up64 && down64 && out64) { - FOR_EACH_PIXEL_64_BEGIN_LOOP - - TINT32 r = downPix->r - upPix->r; - TINT32 g = downPix->g - upPix->g; - TINT32 b = downPix->b - upPix->b; - TINT32 m = downPix->m; // - upPix->m; - outPix->r = (USHORT)tcrop(r, 0, 0xffff); - outPix->g = (USHORT)tcrop(g, 0, 0xffff); - outPix->b = (USHORT)tcrop(b, 0, 0xffff); - outPix->m = (USHORT)tcrop(m, 0, 0xffff); - - FOR_EACH_PIXEL_64_END_LOOP - } else { - TRasterGR8P up8 = rup; - TRasterGR8P down8 = rdown; - TRasterGR8P out8 = rout; + return; + } - if (up8 && down8 && out8) { - FOR_EACH_PIXEL_8_BEGIN_LOOP + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; - SHORT value = upPix->value - downPix->value; - outPix->value = (UCHAR)tcrop(value, 0, 255); + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP - FOR_EACH_PIXEL_8_END_LOOP - } else - throw TRopException("TRop::sub invalid raster combination"); - } + TINT32 r = downPix->r - upPix->r; + TINT32 g = downPix->g - upPix->g; + TINT32 b = downPix->b - upPix->b; + TINT32 m = downPix->m; // - upPix->m; + outPix->r = (USHORT)tcrop(r, 0, 0xffff); + outPix->g = (USHORT)tcrop(g, 0, 0xffff); + outPix->b = (USHORT)tcrop(b, 0, 0xffff); + outPix->m = (USHORT)tcrop(m, 0, 0xffff); + + FOR_EACH_PIXEL_64_END_LOOP + return; + } + + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; + + if (upF && downF && outF) { + FOR_EACH_PIXEL_F_BEGIN_LOOP + + outPix->r = downPix->r - upPix->r; + outPix->g = downPix->g - upPix->g; + outPix->b = downPix->b - upPix->b; + outPix->m = tcrop(downPix->m, 0.f, 1.f); + + FOR_EACH_PIXEL_F_END_LOOP + return; } + + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + SHORT value = upPix->value - downPix->value; + outPix->value = (UCHAR)tcrop(value, 0, 255); + + FOR_EACH_PIXEL_8_END_LOOP + return; + } + + throw TRopException("TRop::sub invalid raster combination"); } } @@ -776,6 +957,76 @@ D_m) return; } + // 32-bit floating point images case + TRasterFP upF = rup, downF = rdown, outF = rout; + + if (upF && downF && outF) { + float vf = (float)v / (float)(TPixel32::maxChannelValue); + + if (matte) { + float outMf; + + FOR_EACH_PIXEL_F_BEGIN_LOOP + + outMf = downPix->m * upPix->m; + + outPix->r = tcrop((upPix->r / upPix->m + vf) * (downPix->r / downPix->m), + 0.f, outMf); + outPix->g = tcrop((upPix->g / upPix->m + vf) * (downPix->g / downPix->m), + 0.f, outMf); + outPix->b = tcrop((upPix->b / upPix->m + vf) * (downPix->b / downPix->m), + 0.f, outMf); + outPix->m = outMf; + + FOR_EACH_PIXEL_F_END_LOOP + } else { + float umdmf_norm, outMf; + float mSumf, uf, df, ufdf, normalizer; + + FOR_EACH_PIXEL_F_BEGIN_LOOP + + mSumf = upPix->m + downPix->m; + if (mSumf > 0.f) { + outMf = upPix->m + (1.f - upPix->m) * downPix->m; + + normalizer = outMf / mSumf; + umdmf_norm = upPix->m * downPix->m; + ; + + uf = upPix->r + vf * umdmf_norm; + df = downPix->r; + ufdf = uf * df; + outPix->r = tcrop( + (uf * (1.f - downPix->m) + df * (1.f - upPix->m) + ufdf + ufdf) * + normalizer, + 0.f, outMf); + + uf = upPix->g + vf * umdmf_norm; + df = downPix->g; + ufdf = uf * df; + outPix->g = tcrop( + (uf * (1.f - downPix->m) + df * (1.f - upPix->m) + ufdf + ufdf) * + normalizer, + 0.f, outMf); + + uf = upPix->b + vf * umdmf_norm; + df = downPix->b; + ufdf = uf * df; + outPix->b = tcrop( + (uf * (1.f - downPix->m) + df * (1.f - upPix->m) + ufdf + ufdf) * + normalizer, + 0.f, outMf); + + outPix->m = outMf; + } else + *outPix = TPixelF::Transparent; + + FOR_EACH_PIXEL_F_END_LOOP + } + + return; + } + // According to the specifics, throw an exception. I think it's not // appropriate, though. throw TRopException("TRop::mult invalid raster combination"); @@ -791,6 +1042,9 @@ void TRop::ropin(const TRasterP &source, const TRasterP &matte, TRaster64P source64 = source; TRaster64P matte64 = matte; TRaster64P out64 = rout; + TRasterFP sourceF = source; + TRasterFP matteF = matte; + TRasterFP outF = rout; if (source32 && matte32 && out32) { FOR_EACH_PIXEL_BEGIN_LOOP(TPixelRGBM32, source32, TPixelRGBM32, matte32, @@ -868,6 +1122,21 @@ outPix_packed_i = _mm_packus_epi16(outPix_packed_i, zeros); } FOR_EACH_PIXEL_END_LOOP(source64, matte64, out64) + } else if (sourceF && matteF && outF) { + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelF, sourceF, TPixelF, matteF, TPixelF, outF) + + if (downPix->m <= 0.f) + outPix->r = outPix->g = outPix->b = outPix->m = 0.f; + else if (downPix->m >= 1.f) + *outPix = *upPix; + else { + outPix->r = upPix->r * downPix->m; + outPix->g = upPix->g * downPix->m; + outPix->b = upPix->b * downPix->m; + outPix->m = upPix->m * downPix->m; + } + + FOR_EACH_PIXEL_END_LOOP(sourceF, matteF, outF) } else throw TRopException("TRop::in invalid raster combination"); } @@ -882,6 +1151,9 @@ void TRop::ropout(const TRasterP &source, const TRasterP &matte, TRaster64P source64 = source; TRaster64P matte64 = matte; TRaster64P out64 = rout; + TRasterFP sourceF = source; + TRasterFP matteF = matte; + TRasterFP outF = rout; if (source32 && matte32 && out32) { FOR_EACH_PIXEL_BEGIN_LOOP(TPixelRGBM32, source32, TPixelRGBM32, matte32, @@ -920,6 +1192,23 @@ void TRop::ropout(const TRasterP &source, const TRasterP &matte, } FOR_EACH_PIXEL_END_LOOP(source64, matte64, out64) + } else if (sourceF && matteF && outF) { + FOR_EACH_PIXEL_BEGIN_LOOP(TPixelF, sourceF, TPixelF, matteF, TPixelF, outF) + + if (downPix->m >= 1.f) + outPix->r = outPix->g = outPix->b = outPix->m = 0; + else if (downPix->m <= 0.f) + *outPix = *upPix; + else { + float fac = 1.f - downPix->m; + + outPix->r = upPix->r * fac; + outPix->g = upPix->g * fac; + outPix->b = upPix->b * fac; + outPix->m = upPix->m * fac; + } + + FOR_EACH_PIXEL_END_LOOP(sourceF, matteF, outF) } else throw TRopException("TRop::out invalid raster combination"); } @@ -937,6 +1226,9 @@ void TRop::atop(const TRasterP &rup, const TRasterP &rdown, TRaster64P up64 = rup; TRaster64P down64 = rdown; TRaster64P out64 = rout; + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; if (up32 && down32 && out32) { FOR_EACH_PIXEL_32_BEGIN_LOOP @@ -971,6 +1263,19 @@ void TRop::atop(const TRasterP &rup, const TRasterP &rdown, overPix(*outPix, *downPix, tmpPix); FOR_EACH_PIXEL_64_END_LOOP + } else if (upF && downF && outF) { + FOR_EACH_PIXEL_F_BEGIN_LOOP + + TPixelF tmpPix = TPixelF::Transparent; + if (downPix->m >= 0.f) { + tmpPix.r = upPix->r * downPix->m; + tmpPix.g = upPix->g * downPix->m; + tmpPix.b = upPix->b * downPix->m; + tmpPix.m = upPix->m * downPix->m; + } + + overPix(*outPix, *downPix, tmpPix); + FOR_EACH_PIXEL_F_END_LOOP } else throw TRopException("TRop::atop invalid raster combination"); } @@ -1031,6 +1336,9 @@ void TRop::crossDissolve(const TRasterP &rup, const TRasterP &rdown, TRaster64P up64 = rup; TRaster64P down64 = rdown; TRaster64P out64 = rout; + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; if (up32 && down32 && out32) { FOR_EACH_PIXEL_32_BEGIN_LOOP @@ -1052,6 +1360,16 @@ void TRop::crossDissolve(const TRasterP &rup, const TRasterP &rdown, outPix->m = (upPix->m * vv + downPix->m * (65535 - vv)) / 65535; FOR_EACH_PIXEL_64_END_LOOP + } else if (upF && downF && outF) { + float vv = (float)v / 255.0f; + FOR_EACH_PIXEL_F_BEGIN_LOOP + + outPix->r = upPix->r * vv + downPix->r * (1.f - vv); + outPix->g = upPix->g * vv + downPix->g * (1.f - vv); + outPix->b = upPix->b * vv + downPix->b * (1.f - vv); + outPix->m = upPix->m * vv + downPix->m * (1.f - vv); + + FOR_EACH_PIXEL_F_END_LOOP } else throw TRopException("TRop::crossDissolve invalid raster combination"); } @@ -1200,59 +1518,102 @@ void TRop::ropmin(const TRasterP &rup, const TRasterP &rdown, *outPix = *downPix; FOR_EACH_PIXEL_32_END_LOOP } - } else { - TRaster64P up64 = rup; - TRaster64P down64 = rdown; - TRaster64P out64 = rout; + return; + } - if (up64 && down64 && out64) { - if (matte) { - FOR_EACH_PIXEL_64_BEGIN_LOOP + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + if (matte) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + outPix->r = upPix->r < downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g < downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b < downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m < downPix->m ? upPix->m : downPix->m; + + FOR_EACH_PIXEL_64_END_LOOP + } else { + FOR_EACH_PIXEL_32_BEGIN_LOOP + if (upPix->m >= 65535) { outPix->r = upPix->r < downPix->r ? upPix->r : downPix->r; outPix->g = upPix->g < downPix->g ? upPix->g : downPix->g; outPix->b = upPix->b < downPix->b ? upPix->b : downPix->b; outPix->m = upPix->m < downPix->m ? upPix->m : downPix->m; - FOR_EACH_PIXEL_64_END_LOOP - } else { - FOR_EACH_PIXEL_32_BEGIN_LOOP - if (upPix->m >= 65535) { - outPix->r = upPix->r < downPix->r ? upPix->r : downPix->r; - outPix->g = upPix->g < downPix->g ? upPix->g : downPix->g; - outPix->b = upPix->b < downPix->b ? upPix->b : downPix->b; - outPix->m = upPix->m < downPix->m ? upPix->m : downPix->m; - - } else if (upPix->m) { - TPixel32 tmp; - tmp.r = upPix->r < downPix->r ? upPix->r : downPix->r; - tmp.g = upPix->g < downPix->g ? upPix->g : downPix->g; - tmp.b = upPix->b < downPix->b ? upPix->b : downPix->b; - // tmp.m = upPix->m < downPix->m ? upPix->m : downPix->m; - outPix->r = upPix->m * (tmp.r - downPix->r) / 65535.0 + downPix->r; - outPix->g = upPix->m * (tmp.g - downPix->g) / 65535.0 + downPix->g; - outPix->b = upPix->m * (tmp.b - downPix->b) / 65535.0 + downPix->b; - outPix->m = upPix->m * (tmp.m - downPix->m) / 65535.0 + downPix->m; - } else - *outPix = *downPix; - FOR_EACH_PIXEL_32_END_LOOP - } - } else { - TRasterGR8P up8 = rup; - TRasterGR8P down8 = rdown; - TRasterGR8P out8 = rout; + } else if (upPix->m) { + TPixel32 tmp; + tmp.r = upPix->r < downPix->r ? upPix->r : downPix->r; + tmp.g = upPix->g < downPix->g ? upPix->g : downPix->g; + tmp.b = upPix->b < downPix->b ? upPix->b : downPix->b; + // tmp.m = upPix->m < downPix->m ? upPix->m : downPix->m; + outPix->r = upPix->m * (tmp.r - downPix->r) / 65535.0 + downPix->r; + outPix->g = upPix->m * (tmp.g - downPix->g) / 65535.0 + downPix->g; + outPix->b = upPix->m * (tmp.b - downPix->b) / 65535.0 + downPix->b; + outPix->m = upPix->m * (tmp.m - downPix->m) / 65535.0 + downPix->m; + } else + *outPix = *downPix; + FOR_EACH_PIXEL_32_END_LOOP + } + return; + } - if (up8 && down8 && out8) { - FOR_EACH_PIXEL_8_BEGIN_LOOP + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; - outPix->value = - upPix->value < downPix->value ? upPix->value : downPix->value; + if (upF && downF && outF) { + if (matte) { + FOR_EACH_PIXEL_F_BEGIN_LOOP - FOR_EACH_PIXEL_8_END_LOOP + outPix->r = upPix->r < downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g < downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b < downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m < downPix->m ? upPix->m : downPix->m; + + FOR_EACH_PIXEL_F_END_LOOP + } else { + FOR_EACH_PIXEL_F_BEGIN_LOOP + if (upPix->m >= 1.f) { + outPix->r = upPix->r < downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g < downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b < downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m < downPix->m ? upPix->m : downPix->m; + + } else if (upPix->m > 0.f) { + TPixelF tmp; + tmp.r = upPix->r < downPix->r ? upPix->r : downPix->r; + tmp.g = upPix->g < downPix->g ? upPix->g : downPix->g; + tmp.b = upPix->b < downPix->b ? upPix->b : downPix->b; + // tmp.m = upPix->m < downPix->m ? upPix->m : downPix->m; + outPix->r = upPix->m * (tmp.r - downPix->r) + downPix->r; + outPix->g = upPix->m * (tmp.g - downPix->g) + downPix->g; + outPix->b = upPix->m * (tmp.b - downPix->b) + downPix->b; + outPix->m = upPix->m * (tmp.m - downPix->m) + downPix->m; } else - throw TRopException("TRop::min invalid raster combination"); + *outPix = *downPix; + FOR_EACH_PIXEL_F_END_LOOP } + return; + } + + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; + + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + outPix->value = + upPix->value < downPix->value ? upPix->value : downPix->value; + + FOR_EACH_PIXEL_8_END_LOOP + return; } + + throw TRopException("TRop::min invalid raster combination"); } //----------------------------------------------------------------------------- @@ -1272,39 +1633,56 @@ void TRop::ropmax(const TRasterP &rup, const TRasterP &rdown, outPix->m = upPix->m > downPix->m ? upPix->m : downPix->m; FOR_EACH_PIXEL_32_END_LOOP - } else { - TRaster64P up64 = rup; - TRaster64P down64 = rdown; - TRaster64P out64 = rout; + return; + } + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; - if (up64 && down64 && out64) { - FOR_EACH_PIXEL_64_BEGIN_LOOP + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP - outPix->r = upPix->r > downPix->r ? upPix->r : downPix->r; - outPix->g = upPix->g > downPix->g ? upPix->g : downPix->g; - outPix->b = upPix->b > downPix->b ? upPix->b : downPix->b; - outPix->m = upPix->m > downPix->m ? upPix->m : downPix->m; + outPix->r = upPix->r > downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g > downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b > downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m > downPix->m ? upPix->m : downPix->m; - FOR_EACH_PIXEL_64_END_LOOP - } else { - TRasterGR8P up8 = rup; - TRasterGR8P down8 = rdown; - TRasterGR8P out8 = rout; + FOR_EACH_PIXEL_64_END_LOOP + return; + } + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; - if (up8 && down8 && out8) { - FOR_EACH_PIXEL_8_BEGIN_LOOP + if (upF && downF && outF) { + FOR_EACH_PIXEL_F_BEGIN_LOOP + + outPix->r = upPix->r > downPix->r ? upPix->r : downPix->r; + outPix->g = upPix->g > downPix->g ? upPix->g : downPix->g; + outPix->b = upPix->b > downPix->b ? upPix->b : downPix->b; + outPix->m = upPix->m > downPix->m ? upPix->m : downPix->m; - outPix->value = - upPix->value > downPix->value ? upPix->value : downPix->value; + FOR_EACH_PIXEL_F_END_LOOP + return; + } + TRasterGR8P up8 = rup; + TRasterGR8P down8 = rdown; + TRasterGR8P out8 = rout; - FOR_EACH_PIXEL_8_END_LOOP - } else - throw TRopException("TRop::max invalid raster combination"); - } + if (up8 && down8 && out8) { + FOR_EACH_PIXEL_8_BEGIN_LOOP + + outPix->value = + upPix->value > downPix->value ? upPix->value : downPix->value; + + FOR_EACH_PIXEL_8_END_LOOP + return; } + + throw TRopException("TRop::max invalid raster combination"); } //----------------------------------------------------------------------------- - +/* void TRop::linearburn(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout) { TRaster32P up32 = rup; @@ -1497,12 +1875,14 @@ void TRop::overlay(const TRasterP &rup, const TRasterP &rdown, } } } - +*/ //----------------------------------------------------------------------------- void TRop::premultiply(const TRasterP &ras) { ras->lock(); TRaster32P ras32 = ras; + TRaster64P ras64 = ras; + TRasterFP rasF = ras; if (ras32) { TPixel32 *endPix, *upPix = 0, *upRow = ras32->pixels(); TPixel32 *lastPix = @@ -1517,27 +1897,39 @@ void TRop::premultiply(const TRasterP &ras) { } upRow += ras32->getWrap(); } - } else { - TRaster64P ras64 = ras; - if (ras64) { - TPixel64 *endPix, *upPix = 0, *upRow = ras64->pixels(); - TPixel64 *lastPix = - upRow + ras64->getWrap() * (ras64->getLy() - 1) + ras64->getLx(); + } else if (ras64) { + TPixel64 *endPix, *upPix = 0, *upRow = ras64->pixels(); + TPixel64 *lastPix = + upRow + ras64->getWrap() * (ras64->getLy() - 1) + ras64->getLx(); - while (upPix < lastPix) { - upPix = upRow; - endPix = upPix + ras64->getLx(); - while (upPix < endPix) { - premult(*upPix); - ++upPix; - } - upRow += ras64->getWrap(); + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + ras64->getLx(); + while (upPix < endPix) { + premult(*upPix); + ++upPix; } - } else { - ras->unlock(); - throw TException("TRop::premultiply invalid raster type"); + upRow += ras64->getWrap(); } + } else if (rasF) { + TPixelF *endPix, *upPix = nullptr, *upRow = rasF->pixels(); + TPixelF *lastPix = + upRow + rasF->getWrap() * (rasF->getLy() - 1) + rasF->getLx(); + + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + rasF->getLx(); + while (upPix < endPix) { + premult(*upPix); + ++upPix; + } + upRow += rasF->getWrap(); + } + } else { + ras->unlock(); + throw TException("TRop::premultiply invalid raster type"); } + ras->unlock(); } @@ -1546,6 +1938,8 @@ void TRop::premultiply(const TRasterP &ras) { void TRop::depremultiply(const TRasterP &ras) { ras->lock(); TRaster32P ras32 = ras; + TRaster64P ras64 = ras; + TRasterFP rasF = ras; if (ras32) { TPixel32 *endPix, *upPix = 0, *upRow = ras32->pixels(); TPixel32 *lastPix = @@ -1560,26 +1954,37 @@ void TRop::depremultiply(const TRasterP &ras) { } upRow += ras32->getWrap(); } - } else { - TRaster64P ras64 = ras; - if (ras64) { - TPixel64 *endPix, *upPix = 0, *upRow = ras64->pixels(); - TPixel64 *lastPix = - upRow + ras64->getWrap() * (ras64->getLy() - 1) + ras64->getLx(); + } else if (ras64) { + TPixel64 *endPix, *upPix = 0, *upRow = ras64->pixels(); + TPixel64 *lastPix = + upRow + ras64->getWrap() * (ras64->getLy() - 1) + ras64->getLx(); - while (upPix < lastPix) { - upPix = upRow; - endPix = upPix + ras64->getLx(); - while (upPix < endPix) { - if (upPix->m != 0) depremult(*upPix); - ++upPix; - } - upRow += ras64->getWrap(); + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + ras64->getLx(); + while (upPix < endPix) { + if (upPix->m != 0) depremult(*upPix); + ++upPix; } - } else { - ras->unlock(); - throw TException("TRop::depremultiply invalid raster type"); + upRow += ras64->getWrap(); + } + } else if (rasF) { + TPixelF *endPix, *upPix = nullptr, *upRow = rasF->pixels(); + TPixelF *lastPix = + upRow + rasF->getWrap() * (rasF->getLy() - 1) + rasF->getLx(); + + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + rasF->getLx(); + while (upPix < endPix) { + if (upPix->m > 0.f) depremult(*upPix); + ++upPix; + } + upRow += rasF->getWrap(); } + } else { + ras->unlock(); + throw TException("TRop::depremultiply invalid raster type"); } ras->unlock(); } @@ -1651,6 +2056,8 @@ void TRop::expandColor(const TRaster32P &ras32, bool precise) { void TRop::whiteTransp(const TRasterP &ras) { ras->lock(); TRaster32P ras32 = ras; + TRaster64P ras64 = ras; + TRasterFP rasF = ras; if (ras32) { TPixel32 *endPix, *upPix = 0, *upRow = ras32->pixels(); TPixel32 *lastPix = @@ -1665,26 +2072,40 @@ void TRop::whiteTransp(const TRasterP &ras) { } upRow += ras32->getWrap(); } - } else { - TRaster64P ras64 = ras; - if (ras64) { - TPixel64 *endPix, *upPix = 0, *upRow = ras64->pixels(); - TPixel64 *lastPix = - upRow + ras64->getWrap() * (ras64->getLy() - 1) + ras64->getLx(); + } else if (ras64) { + TPixel64 *endPix, *upPix = 0, *upRow = ras64->pixels(); + TPixel64 *lastPix = + upRow + ras64->getWrap() * (ras64->getLy() - 1) + ras64->getLx(); - while (upPix < lastPix) { - upPix = upRow; - endPix = upPix + ras64->getLx(); - while (upPix < endPix) { - if (*upPix == TPixel64::White) *upPix = TPixel64::Transparent; - ++upPix; - } - upRow += ras64->getWrap(); + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + ras64->getLx(); + while (upPix < endPix) { + if (*upPix == TPixel64::White) *upPix = TPixel64::Transparent; + ++upPix; } - } else { - ras->unlock(); - throw TException("TRop::premultiply invalid raster type"); + upRow += ras64->getWrap(); + } + } else if (rasF) { + TPixelF *endPix, *upPix = 0, *upRow = rasF->pixels(); + TPixelF *lastPix = + upRow + rasF->getWrap() * (rasF->getLy() - 1) + rasF->getLx(); + + while (upPix < lastPix) { + upPix = upRow; + endPix = upPix + rasF->getLx(); + while (upPix < endPix) { + if ((*upPix).r >= TPixelF::maxChannelValue && + (*upPix).g >= TPixelF::maxChannelValue && + (*upPix).b >= TPixelF::maxChannelValue) + *upPix = TPixelF::Transparent; + ++upPix; + } + upRow += rasF->getWrap(); } + } else { + ras->unlock(); + throw TException("TRop::premultiply invalid raster type"); } ras->unlock(); } diff --git a/toonz/sources/common/trop/tover.cpp b/toonz/sources/common/trop/tover.cpp index aae900f..1be7f0f 100644 --- a/toonz/sources/common/trop/tover.cpp +++ b/toonz/sources/common/trop/tover.cpp @@ -250,6 +250,28 @@ void do_over(TRasterGR8P rout, const TRaster32P &rup) { } } +//----------------------------------------------------------------------------- + +void do_over(TRasterFP rout, const TRasterFP &rup) { + assert(rout->getSize() == rup->getSize()); + for (int y = 0; y < rout->getLy(); y++) { + TPixelF *out_pix = rout->pixels(y); + TPixelF *const out_end = out_pix + rout->getLx(); + const TPixelF *up_pix = rup->pixels(y); + + for (; out_pix < out_end; ++out_pix, ++up_pix) { + if (up_pix->m >= 1.f) + *out_pix = *up_pix; + else if (up_pix->m > 0.f) { + out_pix->r = up_pix->r + out_pix->r * (1.f - up_pix->m); + out_pix->g = up_pix->g + out_pix->g * (1.f - up_pix->m); + out_pix->b = up_pix->b + out_pix->b * (1.f - up_pix->m); + out_pix->m = up_pix->m + out_pix->m * (1.f - up_pix->m); + } + } + } +} + } // namespace //----------------------------------------------------------------------------- @@ -325,6 +347,7 @@ void TRop::over(const TRasterP &rout, const TRasterP &rup, const TPoint &pos) { TRaster32P rout32 = cRout, rup32 = cRup; TRaster64P rout64 = cRout, rup64 = cRup; + TRasterFP routF = cRout, rupF = cRup; TRasterGR8P rout8 = cRout, rup8 = cRup; @@ -356,6 +379,8 @@ void TRop::over(const TRasterP &rout, const TRasterP &rup, const TPoint &pos) { TRop::copy(rout8, rup8); else if (routCM32 && rupCM32) do_over(routCM32, rupCM32); + else if (routF && rupF) + do_over(routF, rupF); else { rout->unlock(); rup->unlock(); diff --git a/toonz/sources/common/trop/traylit.cpp b/toonz/sources/common/trop/traylit.cpp index a0a09c9..cbde86d 100644 --- a/toonz/sources/common/trop/traylit.cpp +++ b/toonz/sources/common/trop/traylit.cpp @@ -151,7 +151,7 @@ of the ray we're tracing (rayPos.x * ratio * pow((double)(sq(rayPos.x * ratio) + sq(rayPos.y * ratio) + sq_z), - decay)) + + decay)) + 0.5); // * ^-d... 0.5 rounds } } else @@ -187,6 +187,176 @@ of the ray we're tracing } } +// specialization for floating point pixel + +template <> +void performStandardRaylit(TPixelF *bufIn, TPixelF *bufOut, int dxIn, + int dyIn, int dxOut, int dyOut, + const TRect &srcRect, const TRect &dstRect, + const TRop::RaylitParams ¶ms) { + /* NOTATION: Diagram assuming octant 1 + + / | + / | +/ - ray_final_y | octLy +/ 1 | ++---- | +_____ octLx + + +So, octLx and octLy are the octant's lx and ly; ray_final_y is the final height +of the ray we're tracing +*/ + + // Build colors-related variables + float max = TPixelF::maxChannelValue; + /*-- 透明部分の色 --*/ + float transp_val = (params.m_invert) ? max : 0.f, + opaque_val = max - transp_val; + float value, val_r, val_g, val_b, val_m; + double lightness, r_fac, g_fac, b_fac, m_fac; + /*-- 8bit/ 32bit-Float の違いを吸収する係数 --*/ + double factor = max / 255.0; + + // NOTE: These variable initializations are, well, + // heuristic at least. They were probably tested + // to be good, but didn't quite make any REAL sense. + // They could be done MUCH better, but changing them + // would alter the way raylit has been applied until now. + // Should be changed at some point, though... + + double scale = params.m_scale; + double decay = log(params.m_decay / 100.0 + 1.0) + 1.0; + double intensity = 1e8 * log(params.m_intensity / 100.0 + 1.0) / scale; + double smoothness = log(params.m_smoothness * 5.0 / 100.0 + 1.0); + double radius = params.m_radius; + + /*-- 1ステップ進んだ時、次のピクセルで光源が無かったときの光の弱まる割合 --*/ + double neg_delta_p = smoothness * intensity; + /*-- 1ステップ進んだ時、次のピクセルで光源が有ったときの光の強まる割合 --*/ + double quot_delta_p = intensity / max; // + + /*-- + * m_colorはRaylitFxのColor値。r_fac、g_fac、b_facは各チャンネルをPremultiplyした値 + * --*/ + TPixelF colorF = toPixelF(params.m_color); + m_fac = colorF.m; + r_fac = m_fac * colorF.r; + g_fac = m_fac * colorF.g; + b_fac = m_fac * colorF.b; + + // Geometry-related variables + int x, y, ray_final_y; + int octLx = dstRect.x1 - dstRect.x0; + + double rayPosIncrementX = 1.0 / scale; + + double sq_z = sq(params.m_lightOriginSrc.z); // We'll be making square + // distances from p, so square + // it once now + + // Perform raylit + TPixelF *pixIn, *pixOut; + + for (ray_final_y = 0; ray_final_y < octLx; ++ray_final_y) { + // Initialize increment variables + lightness = 0.0; + + double rayPosIncrementY = rayPosIncrementX * (ray_final_y / (double)octLx); + + // Use an integer counter to know when y must increase. Will add ray_final_y + // as long as + // a multiple of octLx-1 is reached, then increase + int yIncrementCounter = 0, yIncrementThreshold = octLx - 1; + + // Trace a single ray of light + TPointD rayPos(rayPosIncrementX, rayPosIncrementY); + + for (x = dstRect.x0, y = dstRect.y0, pixIn = bufIn, pixOut = bufOut; + (x < dstRect.x1) && (y < dstRect.y1); ++x) { + bool insideSrc = (x >= srcRect.x0) && (x < srcRect.x1) && + (y >= srcRect.y0) && (y < srcRect.y1); + if (insideSrc) { + // Add a light component depending on source's matte + if (areAlmostEqual((double)pixIn->m, (double)opaque_val)) + lightness = std::max( + 0.0, lightness - neg_delta_p); // No light source - ray fading + else { + if (areAlmostEqual((double)pixIn->m, (double)transp_val)) + lightness += intensity; // Full light source - ray enforcing + else + lightness = std::max( + 0.0, lightness + // Half light source + (params.m_invert ? pixIn->m : (max - pixIn->m)) * + quot_delta_p); // matte-linear enforcing + } + + if (params.m_includeInput) { + val_r = pixIn->r; + val_g = pixIn->g; + val_b = pixIn->b; + val_m = pixIn->m; + } else + val_r = val_g = val_b = val_m = 0.f; + } else { + if (!params.m_invert) + lightness += intensity; + else + lightness = std::max(0.0, lightness - neg_delta_p); + + val_r = val_g = val_b = val_m = 0.f; + } + + bool insideDst = (x >= 0) && (y >= 0); + if (insideDst) { + // Write the corresponding destination pixel + if (lightness > 0.0) { + if (radius == 0.0) { + value = + factor * lightness / + (rayPos.x * pow((double)(sq(rayPos.x) + sq(rayPos.y) + sq_z), + decay)); // * ^-d... + } else { + double ratio = std::max(0.001, 1.0 - radius / norm(rayPos)); + value = factor * lightness / + (rayPos.x * ratio * + pow((double)(sq(rayPos.x * ratio) + sq(rayPos.y * ratio) + + sq_z), + decay)); // * ^-d... + } + } else + value = 0.f; + + // NOTE: pow() could be slow. If that is the case, it could be cached + // for the whole octant along the longest ray at integer positions, + // and then linearly interpolated between those... Have to profile this + // before resorting to that... + + val_r += value * r_fac; + val_g += value * g_fac; + val_b += value * b_fac; + val_m += value * m_fac; + + pixOut->r = val_r; + pixOut->g = val_g; + pixOut->b = val_b; + pixOut->m = (val_m > max) ? max : val_m; + } + + // Increment variables along the x-axis + pixIn += dxIn, pixOut += dxOut; + + rayPos.x += rayPosIncrementX, rayPos.y += rayPosIncrementY; + + // Increment variables along the y-axis + if ((yIncrementCounter += ray_final_y) >= yIncrementThreshold) { + ++y, pixIn += dyIn, pixOut += dyOut; + yIncrementCounter -= yIncrementThreshold; + } + } + } +} + //-------------------------------------------------------------------------------------------- template @@ -322,6 +492,141 @@ void performColorRaylit(T *bufIn, T *bufOut, int dxIn, int dyIn, int dxOut, } } +// specialization for floating point pixel + +template <> +void performColorRaylit(TPixelF *bufIn, TPixelF *bufOut, int dxIn, + int dyIn, int dxOut, int dyOut, + const TRect &srcRect, const TRect &dstRect, + const TRop::RaylitParams ¶ms) { + // Build colors-related variables + float max = TPixelF::maxChannelValue; + + float val_r, val_g, val_b, val_m; + double lightness_r, lightness_g, lightness_b; + double factor = max / 255.0; + + // NOTE: These variable initializations are, well, + // heuristic at least. They were probably tested + // to be good, but didn't quite make any REAL sense. + // They could be done MUCH better, but changing them + // would alter the way raylit has been applied until now. + // Should be changed at some point, though... + + double scale = params.m_scale; + double decay = log(params.m_decay / 100.0 + 1.0) + 1.0; + double intensity = 1e8 * log(params.m_intensity / 100.0 + 1.0) / scale; + double smoothness = log(params.m_smoothness * 5.0 / 100.0 + 1.0); + double radius = params.m_radius; + + double neg_delta_p = + smoothness * + intensity; // would alter the way raylit has been applied until now. + double quot_delta_p = intensity / max; // + // Should be changed at some point, though... + + // Geometry-related variables + int x, y, ray_final_y; + int octLx = dstRect.x1 - dstRect.x0; + + double rayPosIncrementX = 1.0 / scale; + + double fac, sq_z = sq(params.m_lightOriginSrc.z); // We'll be making square + // distances from p, so + // square it once now + + // Perform raylit + TPixelF *pixIn, *pixOut; + + for (ray_final_y = 0; ray_final_y < octLx; ++ray_final_y) { + // Initialize increment variables + lightness_r = lightness_g = lightness_b = 0.0; + double l, l_max; + + double rayPosIncrementY = rayPosIncrementX * (ray_final_y / (double)octLx); + + // Use an integer counter to know when y must increase. Will add ray_final_y + // as long as + // a multiple of octLx-1 is reached, then increase + int yIncrementCounter = 0, yIncrementThreshold = octLx - 1; + + // Trace a single ray of light + TPointD rayPos(rayPosIncrementX, rayPosIncrementY); + + for (x = dstRect.x0, y = dstRect.y0, pixIn = bufIn, pixOut = bufOut; + (x < dstRect.x1) && (y < dstRect.y1); ++x) { + bool insideSrc = (x >= srcRect.x0) && (x < srcRect.x1) && + (y >= srcRect.y0) && (y < srcRect.y1); + if (insideSrc) { + val_r = pixIn->r; + val_g = pixIn->g; + val_b = pixIn->b; + val_m = pixIn->m; + + lightness_r = std::max(0.0, val_r ? lightness_r + val_r * quot_delta_p + : lightness_r - neg_delta_p); + lightness_g = std::max(0.0, val_g ? lightness_g + val_g * quot_delta_p + : lightness_g - neg_delta_p); + lightness_b = std::max(0.0, val_b ? lightness_b + val_b * quot_delta_p + : lightness_b - neg_delta_p); + + if (!params.m_includeInput) val_r = val_g = val_b = val_m = 0.f; + } else { + lightness_r = std::max(0.0, lightness_r - neg_delta_p); + lightness_g = std::max(0.0, lightness_g - neg_delta_p); + lightness_b = std::max(0.0, lightness_b - neg_delta_p); + + val_r = val_g = val_b = val_m = 0.f; + } + + bool insideDst = (x >= 0) && (y >= 0); + if (insideDst) { + // Write the corresponding destination pixel + if (radius == 0.0) { + fac = factor / + (rayPos.x * + pow((double)(sq(rayPos.x) + sq(rayPos.y) + sq_z), decay)); + } else { + double ratio = std::max(0.001, 1.0 - radius / norm(rayPos)); + fac = + factor / + (rayPos.x * ratio * + pow((double)(sq(rayPos.x * ratio) + sq(rayPos.y * ratio) + sq_z), + decay)); + } + + // NOTE: pow() could be slow. If that is the case, it could be cached + // for the whole octant along the longest ray at integer positions, + // and then linearly interpolated between those... Have to profile this + // before resorting to that... + + val_r += l = fac * lightness_r; + l_max = l; + val_g += l = fac * lightness_g; + l_max = std::max(l, l_max); + val_b += l = fac * lightness_b; + l_max = std::max(l, l_max); + val_m += l_max; + + pixOut->r = val_r; + pixOut->g = val_g; + pixOut->b = val_b; + pixOut->m = (val_m > max) ? max : val_m; + } + + // Increment variables along the x-axis + pixIn += dxIn, pixOut += dxOut; + + rayPos.x += rayPosIncrementX, rayPos.y += rayPosIncrementY; + + // Increment variables along the y-axis + if ((yIncrementCounter += ray_final_y) >= yIncrementThreshold) { + ++y, pixIn += dyIn, pixOut += dyOut; + yIncrementCounter -= yIncrementThreshold; + } + } + } +} //-------------------------------------------------------------------------------------------- /*-- ピザ状に8分割された領域の1つを計算する --*/ template @@ -461,6 +766,8 @@ void TRop::raylit(const TRasterP &dstRas, const TRasterP &srcRas, else if ((TRaster64P)dstRas && (TRaster64P)srcRas) doRaylit(srcRas, dstRas, params, &performStandardRaylit); + else if ((TRasterFP)dstRas && (TRasterFP)srcRas) + doRaylit(srcRas, dstRas, params, &performStandardRaylit); else throw TException("TRop::raylit unsupported pixel type"); } @@ -473,6 +780,8 @@ void TRop::glassRaylit(const TRasterP &dstRas, const TRasterP &srcRas, doRaylit(srcRas, dstRas, params, &performColorRaylit); else if ((TRaster64P)dstRas && (TRaster64P)srcRas) doRaylit(srcRas, dstRas, params, &performColorRaylit); + else if ((TRasterFP)dstRas && (TRasterFP)srcRas) + doRaylit(srcRas, dstRas, params, &performColorRaylit); else throw TException("TRop::raylit unsupported pixel type"); } diff --git a/toonz/sources/common/trop/tresample.cpp b/toonz/sources/common/trop/tresample.cpp index fd92cc7..8219a60 100644 --- a/toonz/sources/common/trop/tresample.cpp +++ b/toonz/sources/common/trop/tresample.cpp @@ -4,12 +4,12 @@ #include "tpixelgr.h" #include "quickputP.h" -//#include "tspecialstyleid.h" +// #include "tspecialstyleid.h" #include "tsystem.h" #include "tcolorstyles.h" #include "tpixelutils.h" -//#include "tstopwatch.h" +// #include "tstopwatch.h" #ifndef TNZCORE_LIGHT #include "tpalette.h" #include "trastercm.h" @@ -159,7 +159,7 @@ inline TINT32 Double2Int(double val) { (d2iaux = D, d2iaux += _double2fixmagic, \ (((TINT32 *)&(d2iaux))[iman_] >> _shiftamt)) -//#define USE_DOUBLE_TO_INT +// #define USE_DOUBLE_TO_INT //=========================================================================== @@ -598,7 +598,7 @@ static inline void get_flt_fun_rad(TRop::ResampleFilterType flt_type, break; } if (flt_fun) *flt_fun = fun; - flt_rad = rad; + flt_rad = rad; } //--------------------------------------------------------------------------- @@ -624,9 +624,9 @@ static FILTER *create_filter(TRop::ResampleFilterType flt_type, double blur, nodedist_u = blur; /* magnification */ else nodedist_u = du_dx * blur; /* minification */ - rad_u = flt_rad * nodedist_u; - rad_x = rad_u * dx_du; - nodefreq_u = 1 / nodedist_u; + rad_u = flt_rad * nodedist_u; + rad_x = rad_u * dx_du; + nodefreq_u = 1 / nodedist_u; /* mu = lu - 1; */ @@ -658,11 +658,11 @@ NOT_MORE_THAN(mu, uhi) if (f->w[uhi]) break; if (ulo < ulomin) ulomin = ulo; if (uhi > uhimax) uhimax = uhi; - n = uhi - ulo + 1; - if (n > nmax) nmax = n; - f->first = ulo; - f->last = uhi; - norm = 1 / sum; + n = uhi - ulo + 1; + if (n > nmax) nmax = n; + f->first = ulo; + f->last = uhi; + norm = 1 / sum; for (u = ulo; u <= uhi; u++) f->w[u] *= (float)norm; } else { f->w_base = 0; @@ -955,7 +955,7 @@ void create_calc(const TRasterPT &rin, int min_pix_ref_u, int max_pix_ref_u, calc_bytewrap = p_calc_bytewrap; calc_bytesize = calc_bytewrap * lv; // lv * ceil(lu/8) if (calc_bytesize > p_calc_allocsize) { - if (p_calc_allocsize) delete[](p_calc); + if (p_calc_allocsize) delete[] (p_calc); // TMALLOC (*p_calc, calc_bytesize) p_calc = new UCHAR[calc_bytesize]; assert(p_calc); @@ -1349,6 +1349,255 @@ void resample_main_rgbm(TRasterPT rout, const TRasterPT &rin, //--------------------------------------------------------------------------- +template <> +void resample_main_rgbm( + TRasterFP rout, const TRasterFP &rin, const TAffine &aff_xy2uv, + const TAffine &aff0_uv2fg, int min_pix_ref_u, int min_pix_ref_v, + int max_pix_ref_u, int max_pix_ref_v, int n_pix, int *pix_ref_u, + int *pix_ref_v, int *pix_ref_f, int *pix_ref_g, short *filter) { + const double max_filter_val = 32767.0; + + const TPixelF *buffer_in; + TPixelF *buffer_out; + TPixelF *pix_out; + int lu, lv, wrap_in, mu, mv; + int lx, ly, wrap_out; + int out_x, out_y; + double out_x_, out_y_; + double out_u_, out_v_; + int ref_u, ref_v; + int pix_u, pix_v; + double ref_out_u_, ref_out_v_; + double ref_out_f_, ref_out_g_; + int ref_out_f, ref_out_g; + int pix_out_f, pix_out_g; + int filter_mu, filter_mv; + UINT inside_limit_u, inside_limit_v; + int inside_nonempty; + int outside_min_u, outside_min_v; + int outside_max_u, outside_max_v; + UCHAR *calc; + int calc_allocsize; + int calc_bytewrap; + UCHAR calc_value; + bool must_calc; + TPixelF pix_value, default_value(0.f, 0.f, 0.f, 0.f); + double weight, sum_weights; + double inv_sum_weights; + float sum_contribs_r, sum_contribs_g, sum_contribs_b, sum_contribs_m; + double out_fval_r, out_fval_g, out_fval_b, out_fval_m; + double out_value_r, out_value_g, out_value_b, out_value_m; + int i; + +#ifdef USE_DOUBLE_TO_INT + double d2iaux; +#endif + + if (!(rout->getLx() > 0 && rout->getLy() > 0)) return; + + if (!(rin->getLx() > 0 && rin->getLy() > 0)) { + rout->clear(); + return; + } + + calc = 0; + calc_allocsize = 0; + + // Create a bit array, each indicating whether a pixel has to be calculated or + // not + create_calc(rin, min_pix_ref_u, max_pix_ref_u, min_pix_ref_v, max_pix_ref_v, + calc, calc_allocsize, calc_bytewrap); + + buffer_in = rin->pixels(); + buffer_out = rout->pixels(); + lu = rin->getLx(); + lx = rout->getLx(); + lv = rin->getLy(); + ly = rout->getLy(); + wrap_in = rin->getWrap(); + wrap_out = rout->getWrap(); + mu = lu - 1; + mv = lv - 1; + + filter_mu = max_pix_ref_u - min_pix_ref_u; + filter_mv = max_pix_ref_v - min_pix_ref_v; + inside_limit_u = lu - filter_mu; + inside_limit_v = lv - filter_mv; + inside_nonempty = (int)inside_limit_u > 0 && (int)inside_limit_v > 0; + outside_min_u = -max_pix_ref_u; + outside_min_v = -max_pix_ref_v; + outside_max_u = mu - min_pix_ref_u; + outside_max_v = mv - min_pix_ref_v; + + // For every pixel of the output image + for (out_y = 0, out_y_ = 0.5; out_y < ly; out_y++, out_y_ += 1.0) { + for (out_x = 0, out_x_ = 0.5; out_x < lx; out_x++, out_x_ += 1.0) { + pix_out = buffer_out + out_y * wrap_out + out_x; + + // Take the pre-image of the pixel through the passed affine + out_u_ = affMV1(aff_xy2uv, out_x_, out_y_); + out_v_ = affMV2(aff_xy2uv, out_x_, out_y_); + + // Convert to integer coordinates + ref_u = intLE(out_u_); + ref_v = intLE(out_v_); + + // NOTE: The following condition is equivalent to: + // (ref_u + min_pix_ref_u >= 0 && ref_v + min_pix_ref_v >= 0 && + // ref_u + max_pix_ref_u < lu && ref_v + max_pix_ref_v < lv) + // - since the presence of (UINT) makes integeres < 0 become >> 0 + if (inside_nonempty && (UINT)(ref_u + min_pix_ref_u) < inside_limit_u && + (UINT)(ref_v + min_pix_ref_v) < inside_limit_v) { + // The filter mask starting around (ref_u, ref_v) is completely + // contained + // in the source raster + + // Get the calculation array mask byte + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + if (calc_value && ((calc_value >> (ref_u & 7)) & + 1)) // If the mask bit for this pixel is on + { + ref_out_u_ = ref_u - out_u_; // Fractionary part of the pre-image + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, + ref_out_v_); // Make the image of it into fg + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); // Convert to integer coordinates + ref_out_g = tround(ref_out_g_); + + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + + // Make the weighted sum of source pixels + for (i = n_pix - 1; i >= 0; --i) { + // Build the weight for this pixel + pix_out_f = pix_ref_f[i] + ref_out_f; // image of the integer part + // + that of the fractionary + // part + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (double)filter[pix_out_f] * (double)filter[pix_out_g] / + (max_filter_val * max_filter_val); + + // Add the weighted pixel contribute + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + pix_value = buffer_in[pix_u + pix_v * wrap_in]; + sum_contribs_r += pix_value.r * weight; + sum_contribs_g += pix_value.g * weight; + sum_contribs_b += pix_value.b * weight; + sum_contribs_m += pix_value.m * weight; + sum_weights += weight; + } + + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + // notLessThan(0.0, out_fval_r); + // notLessThan(0.0, out_fval_g); + // notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = out_fval_r; + out_value_g = out_fval_g; + out_value_b = out_fval_b; + out_value_m = out_fval_m; + // notMoreThan(T::maxChannelValue, out_value_r); + // notMoreThan(T::maxChannelValue, out_value_g); + // notMoreThan(T::maxChannelValue, out_value_b); + notMoreThan(1.f, out_value_m); + pix_out->r = out_value_r; + pix_out->g = out_value_g; + pix_out->b = out_value_b; + pix_out->m = out_value_m; + } else + // The pixel is copied from the corresponding source... + *pix_out = buffer_in[ref_u + ref_v * wrap_in]; + } else if (outside_min_u <= ref_u && ref_u <= outside_max_u && + outside_min_v <= ref_v && ref_v <= outside_max_v) { + if ((UINT)ref_u >= (UINT)lu || (UINT)ref_v >= (UINT)lv) + must_calc = true; + else { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + must_calc = calc_value && ((calc_value >> (ref_u & 7)) & 1); + } + + if (must_calc) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_r = 0; + sum_contribs_g = 0; + sum_contribs_b = 0; + sum_contribs_m = 0; + + for (i = n_pix - 1; i >= 0; --i) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (double)filter[pix_out_f] * (double)filter[pix_out_g] / + (max_filter_val * max_filter_val); + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + if (pix_u < 0 || pix_u > mu || pix_v < 0 || pix_v > mv) { + sum_weights += weight; // 0-padding + continue; + } + + notLessThan(0, pix_u); // Copy-padding + notLessThan(0, pix_v); + notMoreThan(mu, pix_u); + notMoreThan(mv, pix_v); + + pix_value = buffer_in[pix_u + pix_v * wrap_in]; + sum_contribs_r += pix_value.r * weight; + sum_contribs_g += pix_value.g * weight; + sum_contribs_b += pix_value.b * weight; + sum_contribs_m += pix_value.m * weight; + sum_weights += weight; + } + + inv_sum_weights = 1.0 / sum_weights; + out_fval_r = sum_contribs_r * inv_sum_weights; + out_fval_g = sum_contribs_g * inv_sum_weights; + out_fval_b = sum_contribs_b * inv_sum_weights; + out_fval_m = sum_contribs_m * inv_sum_weights; + // notLessThan(0.0, out_fval_r); + // notLessThan(0.0, out_fval_g); + // notLessThan(0.0, out_fval_b); + notLessThan(0.0, out_fval_m); + out_value_r = out_fval_r; + out_value_g = out_fval_g; + out_value_b = out_fval_b; + out_value_m = out_fval_m; + // notMoreThan(T::maxChannelValue, out_value_r); + // notMoreThan(T::maxChannelValue, out_value_g); + // notMoreThan(T::maxChannelValue, out_value_b); + notMoreThan(1.f, out_value_m); + pix_out->r = out_value_r; + pix_out->g = out_value_g; + pix_out->b = out_value_b; + pix_out->m = out_value_m; + } else + *pix_out = buffer_in[ref_u + ref_v * wrap_in]; + } else + *pix_out = default_value; + } + } + + delete[] calc; +} + +//--------------------------------------------------------------------------- + #ifdef USE_SSE2 namespace { @@ -1518,74 +1767,75 @@ void resample_main_rgbm_SSE2(TRasterPT rout, const TRasterPT &rin, } else *pix_out = buffer_in[ref_u + ref_v * wrap_in]; } else - // if( outside_min_u_ <= out_u_ && out_u_ <= outside_max_u_ && - // outside_min_v_ <= out_v_ && out_v_ <= outside_max_v_ ) - if (outside_min_u <= ref_u && ref_u <= outside_max_u && - outside_min_v <= ref_v && ref_v <= outside_max_v) { - if ((UINT)ref_u >= (UINT)lu || (UINT)ref_v >= (UINT)lv) - must_calc = true; - else { - calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; - must_calc = calc_value && ((calc_value >> (ref_u & 7)) & 1); - } - - if (must_calc) { - ref_out_u_ = ref_u - out_u_; - ref_out_v_ = ref_v - out_v_; - ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); - ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); - ref_out_f = tround(ref_out_f_); - ref_out_g = tround(ref_out_g_); - sum_weights = 0; - sum_contribs_packed = _mm_setzero_ps(); + // if( outside_min_u_ <= out_u_ && out_u_ <= outside_max_u_ && + // outside_min_v_ <= out_v_ && out_v_ <= outside_max_v_ ) + if (outside_min_u <= ref_u && ref_u <= outside_max_u && + outside_min_v <= ref_v && ref_v <= outside_max_v) { + if ((UINT)ref_u >= (UINT)lu || (UINT)ref_v >= (UINT)lv) + must_calc = true; + else { + calc_value = calc[(ref_u >> 3) + ref_v * calc_bytewrap]; + must_calc = calc_value && ((calc_value >> (ref_u & 7)) & 1); + } - for (i = n_pix - 1; i >= 0; i--) { - pix_out_f = pix_ref_f[i] + ref_out_f; - pix_out_g = pix_ref_g[i] + ref_out_g; - weight = (float)((filter[pix_out_f] * filter[pix_out_g]) >> 16); - pix_u = pix_ref_u[i] + ref_u; - pix_v = pix_ref_v[i] + ref_v; + if (must_calc) { + ref_out_u_ = ref_u - out_u_; + ref_out_v_ = ref_v - out_v_; + ref_out_f_ = aff0MV1(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_g_ = aff0MV2(aff0_uv2fg, ref_out_u_, ref_out_v_); + ref_out_f = tround(ref_out_f_); + ref_out_g = tround(ref_out_g_); + sum_weights = 0; + sum_contribs_packed = _mm_setzero_ps(); + + for (i = n_pix - 1; i >= 0; i--) { + pix_out_f = pix_ref_f[i] + ref_out_f; + pix_out_g = pix_ref_g[i] + ref_out_g; + weight = (float)((filter[pix_out_f] * filter[pix_out_g]) >> 16); + pix_u = pix_ref_u[i] + ref_u; + pix_v = pix_ref_v[i] + ref_v; + + if (pix_u < 0 || pix_u > mu || pix_v < 0 || pix_v > mv) { + sum_weights += weight; + continue; + } + + notLessThan(0, pix_u); + notLessThan(0, pix_v); + notMoreThan(mu, pix_u); + notMoreThan(mv, pix_v); + + pix_value = buffer_in[pix_u + pix_v * wrap_in]; + pix_value_packed_i = _mm_unpacklo_epi8( + _mm_cvtsi32_si128(*(DWORD *)&pix_value), zeros); + pix_value_packed = _mm_cvtepi32_ps( + _mm_unpacklo_epi16(pix_value_packed_i, zeros)); + + weight_packed = _mm_load1_ps(&weight); + sum_contribs_packed = + _mm_add_ps(sum_contribs_packed, + _mm_mul_ps(pix_value_packed, weight_packed)); - if (pix_u < 0 || pix_u > mu || pix_v < 0 || pix_v > mv) { sum_weights += weight; - continue; } - - notLessThan(0, pix_u); - notLessThan(0, pix_v); - notMoreThan(mu, pix_u); - notMoreThan(mv, pix_v); - - pix_value = buffer_in[pix_u + pix_v * wrap_in]; - pix_value_packed_i = _mm_unpacklo_epi8( - _mm_cvtsi32_si128(*(DWORD *)&pix_value), zeros); - pix_value_packed = - _mm_cvtepi32_ps(_mm_unpacklo_epi16(pix_value_packed_i, zeros)); - - weight_packed = _mm_load1_ps(&weight); - sum_contribs_packed = - _mm_add_ps(sum_contribs_packed, - _mm_mul_ps(pix_value_packed, weight_packed)); - - sum_weights += weight; - } - inv_sum_weights = 1.0f / sum_weights; - - __m128 inv_sum_weights_packed = _mm_load1_ps(&inv_sum_weights); - __m128 out_fval_packed = - _mm_mul_ps(sum_contribs_packed, inv_sum_weights_packed); - out_fval_packed = _mm_max_ps(out_fval_packed, zeros2); - out_fval_packed = _mm_min_ps(out_fval_packed, maxChanneValue_packed); - - __m128i out_value_packed_i = _mm_cvtps_epi32(out_fval_packed); - out_value_packed_i = _mm_packs_epi32(out_value_packed_i, zeros); - out_value_packed_i = _mm_packus_epi16(out_value_packed_i, zeros); - *(DWORD *)(pix_out) = _mm_cvtsi128_si32(out_value_packed_i); - } else - *pix_out = buffer_in[ref_u + ref_v * wrap_in]; - } else { - *pix_out = default_value; - } + inv_sum_weights = 1.0f / sum_weights; + + __m128 inv_sum_weights_packed = _mm_load1_ps(&inv_sum_weights); + __m128 out_fval_packed = + _mm_mul_ps(sum_contribs_packed, inv_sum_weights_packed); + out_fval_packed = _mm_max_ps(out_fval_packed, zeros2); + out_fval_packed = + _mm_min_ps(out_fval_packed, maxChanneValue_packed); + + __m128i out_value_packed_i = _mm_cvtps_epi32(out_fval_packed); + out_value_packed_i = _mm_packs_epi32(out_value_packed_i, zeros); + out_value_packed_i = _mm_packus_epi16(out_value_packed_i, zeros); + *(DWORD *)(pix_out) = _mm_cvtsi128_si32(out_value_packed_i); + } else + *pix_out = buffer_in[ref_u + ref_v * wrap_in]; + } else { + *pix_out = default_value; + } } } if (calc) delete[] calc; @@ -1691,9 +1941,9 @@ static void get_prow_gr8(const TRasterGR8P &rin, double a11, double a12, prow[p] = (float)troundp( fu * gv * (((UINT)(u + 1) < lu && (UINT)v < lv) ? in_gr8[du] : BORDER) + - fu * fv * (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) - ? in_gr8[du + dv] - : BORDER) + + fu * fv * + (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) ? in_gr8[du + dv] + : BORDER) + gu * gv * (((UINT)u < lu && (UINT)v < lv) ? in_gr8[0] : BORDER) + gu * fv * (((UINT)u < lu && (UINT)(v + 1) < lv) ? in_gr8[dv] : BORDER)); @@ -1714,9 +1964,9 @@ static void get_prow_gr8(const TRasterGR8P &rin, double a11, double a12, prow[p] = (float)troundp( fu * gv * (((UINT)(u + 1) < lu && (UINT)v < lv) ? in_gr8[du] : BORDER) + - fu * fv * (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) - ? in_gr8[du + dv] - : BORDER) + + fu * fv * + (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) ? in_gr8[du + dv] + : BORDER) + gu * gv * (((UINT)u < lu && (UINT)v < lv) ? in_gr8[0] : BORDER) + gu * fv * (((UINT)u < lu && (UINT)(v + 1) < lv) ? in_gr8[dv] : BORDER)); @@ -1789,14 +2039,16 @@ static void get_prow_gr8(const TRaster32P &rin, double a11, double a12, gv = 1. - fv; in_32 = bufin_32 + (u * du + v * dv); prow[p] = (float)troundp( - fu * gv * (((UINT)(u + 1) < lu && (UINT)v < lv) ? grey(in_32[du]) + fu * gv * + (((UINT)(u + 1) < lu && (UINT)v < lv) ? grey(in_32[du]) + : BORDER) + + fu * fv * + (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) ? grey(in_32[du + dv]) : BORDER) + - fu * fv * (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) - ? grey(in_32[du + dv]) - : BORDER) + gu * gv * (((UINT)u < lu && (UINT)v < lv) ? grey(in_32[0]) : BORDER) + - gu * fv * (((UINT)u < lu && (UINT)(v + 1) < lv) ? grey(in_32[dv]) - : BORDER)); + gu * fv * + (((UINT)u < lu && (UINT)(v + 1) < lv) ? grey(in_32[dv]) + : BORDER)); } p1 = p; for (p = pmax; p > p1; p--) @@ -1812,14 +2064,16 @@ static void get_prow_gr8(const TRaster32P &rin, double a11, double a12, gv = 1. - fv; in_32 = bufin_32 + (u * du + v * dv); prow[p] = (float)troundp( - fu * gv * (((UINT)(u + 1) < lu && (UINT)v < lv) ? grey(in_32[du]) + fu * gv * + (((UINT)(u + 1) < lu && (UINT)v < lv) ? grey(in_32[du]) + : BORDER) + + fu * fv * + (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) ? grey(in_32[du + dv]) : BORDER) + - fu * fv * (((UINT)(u + 1) < lu && (UINT)(v + 1) < lv) - ? grey(in_32[du + dv]) - : BORDER) + gu * gv * (((UINT)u < lu && (UINT)v < lv) ? grey(in_32[0]) : BORDER) + - gu * fv * (((UINT)u < lu && (UINT)(v + 1) < lv) ? grey(in_32[dv]) - : BORDER)); + gu * fv * + (((UINT)u < lu && (UINT)(v + 1) < lv) ? grey(in_32[dv]) + : BORDER)); } p2 = p; for (p = p1; p <= p2; p++) @@ -1948,7 +2202,7 @@ TCALLOC (colval, lu);*/ flatcols = 1; else flatcols = 0; - flatval = colval[u]; + flatval = colval[u]; if (flatcols >= flatdiamu) { #ifdef VECCHIA_MANIERA x_ = AFF_M_V_1(aff, u - flatradu, v - flatradv); @@ -1967,7 +2221,7 @@ TCALLOC (colval, lu);*/ ylo = std::max(0, (int)ylo_); yhi = std::min(my, (int)yhi_); for (y = ylo; y <= yhi; y++) - for (x = xlo; x <= xhi; x++) + for (x = xlo; x <= xhi; x++) bufout_gr8[x + y * wrapout] = flatval, count++; } xlo_ += aff.a11; @@ -2005,7 +2259,7 @@ TCALLOC (colval, lu);*/ nocheight[x] = 0; } if (topy < ly && colnoc[topy].first <= topq) { - for (x = 0; x < lx; x++) + for (x = 0; x < lx; x++) if (nocheight[x] < nocdiamy) xxx[x] = 1.0; /* 1.0 == calc */ } else { for (x = 0; x < lx; x++) xxx[x] = 1.0; /* 1.0 == calc */ @@ -2020,7 +2274,7 @@ TCALLOC (colval, lu);*/ else { nocwidth++; if (nocwidth >= nocdiamx) - for (p = rownoc[x].first; p <= rownoc[x].last; p++) + for (p = rownoc[x].first; p <= rownoc[x].last; p++) prow[p] = 1.0; /* 1.0 == nocalc */ } get_prow_gr8(rin, invrot.a11, invrot.a12, invrot.a21, invrot.a22, pmin, @@ -2157,7 +2411,7 @@ TCALLOC (colval, lu);*/ flatcols = 1; else flatcols = 0; - flatval = colval[u]; + flatval = colval[u]; if (flatcols >= flatdiamu) { #ifdef VECCHIA_MANIERA x_ = AFF_M_V_1(aff, u - flatradu, v - flatradv); @@ -2176,7 +2430,7 @@ TCALLOC (colval, lu);*/ ylo = std::max(0, (int)ylo_); yhi = std::min(my, (int)yhi_); for (y = ylo; y <= yhi; y++) - for (x = xlo; x <= xhi; x++) + for (x = xlo; x <= xhi; x++) bufout_gr8[x + y * wrapout] = flatval, count++; } xlo_ += aff.a11; @@ -2214,7 +2468,7 @@ TCALLOC (colval, lu);*/ nocheight[x] = 0; } if (topy < ly && colnoc[topy].first <= topq) { - for (x = 0; x < lx; x++) + for (x = 0; x < lx; x++) if (nocheight[x] < nocdiamy) xxx[x] = 1.0; /* 1.0 == calc */ } else { for (x = 0; x < lx; x++) xxx[x] = 1.0; /* 1.0 == calc */ @@ -2229,7 +2483,7 @@ TCALLOC (colval, lu);*/ else { nocwidth++; if (nocwidth >= nocdiamx) - for (p = rownoc[x].first; p <= rownoc[x].last; p++) + for (p = rownoc[x].first; p <= rownoc[x].last; p++) prow[p] = 1.0; /* 1.0 == nocalc */ } get_prow_gr8(rin, invrot.a11, invrot.a12, invrot.a21, invrot.a22, pmin, @@ -2555,7 +2809,7 @@ void rop_resample_rgbm(TRasterPT rout, const TRasterPT &rin, if (min_pix_out_fg < min_filter_fg) { int delta = min_filter_fg - min_pix_out_fg; - for (f = max_filter_fg; f >= min_filter_fg; f--) + for (f = max_filter_fg; f >= min_filter_fg; f--) filter[f + delta] = filter[f]; filter += delta; for (f = min_filter_fg - 1; f >= min_pix_out_fg; f--) filter[f] = 0; @@ -2576,7 +2830,12 @@ void rop_resample_rgbm(TRasterPT rout, const TRasterPT &rin, pix_ref_f.get(), pix_ref_g.get(), filter); else #endif - if (n_pix >= 512 || T::maxChannelValue > 255) + if (std::is_same::value) + resample_main_rgbm( + rout, rin, aff_xy2uv, aff0_uv2fg, min_pix_ref_u, min_pix_ref_v, + max_pix_ref_u, max_pix_ref_v, n_pix, pix_ref_u.get(), pix_ref_v.get(), + pix_ref_f.get(), pix_ref_g.get(), filter); + else if (n_pix >= 512 || T::maxChannelValue > 255) resample_main_rgbm( rout, rin, aff_xy2uv, aff0_uv2fg, min_pix_ref_u, min_pix_ref_v, max_pix_ref_u, max_pix_ref_v, n_pix, pix_ref_u.get(), pix_ref_v.get(), @@ -3014,7 +3273,7 @@ void do_resample(TRasterCM32P rout, const TRasterCM32P &rin, tone_tot = 0.0; some_pencil = false; for (i = 0; i < 4; i++) { - tone = tcm[i] & tone_mask; + tone = tcm[i] & tone_mask; if ((TUINT32)tone != tone_mask) some_pencil = true; tone_tot += tone * w[i]; new_color_blob.val = tcm[i] & color_mask; @@ -3059,8 +3318,8 @@ void do_resample(TRasterCM32P rout, const TRasterCM32P &rin, v * wrapin; // Take the associated input pixel pointer tcm[0] = in_tcm[0]; if (u < lu - 1 && v < lv - 1) { - // Also take their 4 next neighbours (we shall perform a kind of bilinear - // interpolation) + // Also take their 4 next neighbours (we shall perform a kind of + // bilinear interpolation) tcm[1] = in_tcm[1]; tcm[2] = in_tcm[wrapin]; tcm[3] = in_tcm[wrapin + 1]; @@ -3165,7 +3424,7 @@ void do_resample(TRasterCM32P rout, const TRasterCM32P &rin, tone_tot = 0.0; some_pencil = false; for (i = 0; i < 4; i++) { - tone = tcm[i] & tone_mask; + tone = tcm[i] & tone_mask; if ((TUINT32)tone != tone_mask) some_pencil = true; tone_tot += tone * w[i]; new_color_blob.val = tcm[i] & color_mask; @@ -3586,7 +3845,7 @@ void resample_main_cm32_rgbm_bigradius( std::vector paints(colorCount); std::vector inks(colorCount); - for (i = 0; i < palette->getStyleCount(); i++) + for (i = 0; i < palette->getStyleCount(); i++) paints[i] = inks[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); @@ -3775,7 +4034,7 @@ void resample_main_cm32_rgbm_bigradius( if (calc) delete[] calc; } -} +} // namespace /*---------------------------------------------------------------------------*/ @@ -3871,7 +4130,7 @@ void resample_main_cm32_rgbm(TRasterPT rout, const TRasterCM32P &rin, std::vector paints(colorCount); std::vector inks(colorCount); - for (i = 0; i < palette->getStyleCount(); i++) + for (i = 0; i < palette->getStyleCount(); i++) paints[i] = inks[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); @@ -4140,7 +4399,7 @@ resample_main_rgbm_bigradius( rout, rin, std::vector paints(colorCount); std::vector inks(colorCount); - for (i = 0; i < palette->getStyleCount(); i++) + for (i = 0; i < palette->getStyleCount(); i++) paints[i] = inks[i] = ::premultiply(palette->getStyle(i)->getAverageColor()); @@ -4585,7 +4844,7 @@ void rop_resample_rgbm_2(TRasterPT rout, const TRasterCM32P &rin, if (min_pix_out_fg < min_filter_fg) { int delta = min_filter_fg - min_pix_out_fg; - for (f = max_filter_fg; f >= min_filter_fg; f--) + for (f = max_filter_fg; f >= min_filter_fg; f--) filter[f + delta] = filter[f]; filter += delta; for (f = min_filter_fg - 1; f >= min_pix_out_fg; f--) filter[f] = 0; @@ -4663,42 +4922,49 @@ void TRop::resample(const TRasterP &rout, const TRasterP &rin, } TRaster32P rout32 = rout, rin32 = rin; + TRasterCM32P routCM32 = rout, rinCM32 = rin; + TRaster64P rout64 = rout, rin64 = rin; + TRasterGR8P routGR8 = rout, rinGR8 = rin; + TRasterFP routF = rout, rinF = rin; + if (rout32) { if (!rin32) { rin32 = TRaster32P(rin->getLx(), rin->getLy()); TRop::convert(rin32, rin); } do_resample(rout32, rin32, aff, filterType, blur); - } else { -#ifndef TNZCORE_LIGHT - TRasterCM32P routCM32 = rout, rinCM32 = rin; - if (routCM32 && rinCM32) - do_resample(routCM32, rinCM32, aff); - else -#endif - { - TRaster64P rout64 = rout, rin64 = rin; - if (rout64) { - if (!rin64) { - rin64 = TRaster64P(rin->getLx(), rin->getLy()); - TRop::convert(rin64, rin); - } - do_resample(rout64, rin64, aff, filterType, blur); - } else { - TRasterGR8P routGR8 = rout, rinGR8 = rin; - TRaster32P rin32 = rin; - if (routGR8 && rinGR8) - do_resample(routGR8, rinGR8, aff, filterType, blur); - else if (routGR8 && rin32) - do_resample(routGR8, rin32, aff, filterType, blur); - else { - rin->unlock(); - rout->unlock(); - throw TRopException("unsupported pixel type"); - } - } + } else if (routCM32 && rinCM32) + do_resample(routCM32, rinCM32, aff); + else if (rout64) { + if (!rin64) { + rin64 = TRaster64P(rin->getLx(), rin->getLy()); + TRop::convert(rin64, rin); } + do_resample(rout64, rin64, aff, filterType, blur); + } else if (routGR8) { + if (rinGR8) + do_resample(routGR8, rinGR8, aff, filterType, blur); + else if (routGR8 && rin32) + do_resample(routGR8, rin32, aff, filterType, blur); + else { + rin->unlock(); + rout->unlock(); + throw TRopException("unsupported pixel type"); + } + } else if (routF) { + if (!rinF) { + rinF = TRasterFP(rin->getLx(), rin->getLy()); + TRop::convert(rinF, rin); + } + do_resample(routF, rinF, aff, filterType, blur); + } else { + rin->unlock(); + rout->unlock(); + throw TRopException("unsupported pixel type"); } + + rout->setLinear(rin->isLinear()); + rin->unlock(); rout->unlock(); } diff --git a/toonz/sources/common/trop/trgbmscale.cpp b/toonz/sources/common/trop/trgbmscale.cpp index f782d50..50d8d0d 100644 --- a/toonz/sources/common/trop/trgbmscale.cpp +++ b/toonz/sources/common/trop/trgbmscale.cpp @@ -24,7 +24,7 @@ void buildLUT(Chan *lut, double a, double k, int chanLow, int chanHigh) { int i, max = (std::numeric_limits::max)(); a += 0.5; // round rather than trunc - for (i = 0; i <= max; ++i) + for (i = 0; i <= max; ++i) lut[i] = tcrop((int)(a + i * k), chanLow, chanHigh); } @@ -94,18 +94,21 @@ void do_rgbmScale_lut(TRasterPT rout, TRasterPT rin, const double *a, int out0M = std::max(fac * out0[3], 0), out1M = std::min(fac * out1[3], T::maxChannelValue); + double aFac[4]; + for (int i = 0; i < 4; i++) aFac[i] = a[i] * (double)fac; + // Build luts Channel *lut_r = new Channel[chanValuesCount]; - buildLUT(lut_r, a[0], k[0], out0R, out1R); + buildLUT(lut_r, aFac[0], k[0], out0R, out1R); Channel *lut_g = new Channel[chanValuesCount]; - buildLUT(lut_g, a[1], k[1], out0G, out1G); + buildLUT(lut_g, aFac[1], k[1], out0G, out1G); Channel *lut_b = new Channel[chanValuesCount]; - buildLUT(lut_b, a[2], k[2], out0B, out1B); + buildLUT(lut_b, aFac[2], k[2], out0B, out1B); Channel *lut_m = new Channel[chanValuesCount]; - buildLUT(lut_m, a[3], k[3], out0M, out1M); + buildLUT(lut_m, aFac[3], k[3], out0M, out1M); // Retrieve de/premultiplication luts const double *lut_prem = premultiplyTable(); @@ -162,6 +165,9 @@ void do_rgbmScale(TRasterPT rout, TRasterPT rin, const double *a, const double *lut_deprem = depremultiplyTable(); double premFac, depremFac; + double aFac[4]; + for (int i = 0; i < 4; i++) aFac[i] = a[i] * (double)fac; + // Process raster int y, lx = rin->getLx(), ly = rin->getLy(); T *in, *end, *out; @@ -169,16 +175,16 @@ void do_rgbmScale(TRasterPT rout, TRasterPT rin, const double *a, for (y = 0; y < ly; ++y) { in = rin->pixels(y), end = in + lx, out = rout->pixels(y); for (; in < end; ++in, ++out) { - m = tcrop((int)(a[3] + k[3] * in->m), out0M, out1M); + m = tcrop((int)(aFac[3] + k[3] * in->m), out0M, out1M); depremFac = lut_deprem[in->m]; premFac = lut_prem[m]; - out->r = - premFac * tcrop((int)(a[0] + k[0] * in->r * depremFac), out0R, out1R); - out->g = - premFac * tcrop((int)(a[1] + k[1] * in->g * depremFac), out0G, out1G); - out->b = - premFac * tcrop((int)(a[2] + k[2] * in->b * depremFac), out0B, out1B); + out->r = premFac * + tcrop((int)(aFac[0] + k[0] * in->r * depremFac), out0R, out1R); + out->g = premFac * + tcrop((int)(aFac[1] + k[1] * in->g * depremFac), out0G, out1G); + out->b = premFac * + tcrop((int)(aFac[2] + k[2] * in->b * depremFac), out0B, out1B); out->m = m; } } @@ -186,10 +192,103 @@ void do_rgbmScale(TRasterPT rout, TRasterPT rin, const double *a, //----------------------------------------------------------------------------- +template <> +void do_rgbmScale(TRasterFP rout, TRasterFP rin, const double *a, + const double *k, const int *out0, const int *out1) { + assert(rout->getSize() == rin->getSize()); + + float fac = 1.f / 255.f; + + float out0R = std::max(fac * (float)out0[0], 0.f); + float out1R = std::min(fac * (float)out1[0], 1.f); + float out0G = std::max(fac * (float)out0[1], 0.f); + float out1G = std::min(fac * (float)out1[1], 1.f); + float out0B = std::max(fac * (float)out0[2], 0.f); + float out1B = std::min(fac * (float)out1[2], 1.f); + float out0M = std::max(fac * (float)out0[3], 0.f); + float out1M = std::min(fac * (float)out1[3], 1.f); + + // Retrieve de/premultiplication luts + double premFac, depremFac; + + float aFac[4]; + for (int i = 0; i < 4; i++) aFac[i] = a[i] * (float)fac; + + // Process raster + int y, lx = rin->getLx(), ly = rin->getLy(); + TPixelF *in, *end, *out; + float m; + + for (y = 0; y < ly; ++y) { + in = rin->pixels(y), end = in + lx, out = rout->pixels(y); + for (; in < end; ++in, ++out) { + m = tcrop(aFac[3] + (float)k[3] * in->m, out0M, out1M); + + if (in->m <= 0.f) { + out->r = m * tcrop(aFac[0], out0R, out1R); + out->g = m * tcrop(aFac[1], out0G, out1G); + out->b = m * tcrop(aFac[2], out0B, out1B); + out->m = m; + } else { + out->r = m * tcrop(aFac[0] + (float)k[0] * in->r / in->m, out0R, out1R); + out->g = m * tcrop(aFac[1] + (float)k[1] * in->g / in->m, out0G, out1G); + out->b = m * tcrop(aFac[2] + (float)k[2] * in->b / in->m, out0B, out1B); + out->m = m; + } + } + } +} + +//----------------------------------------------------------------------------- + +void do_rgbmScaleFloat(TRasterFP rout, TRasterFP rin, const double *a, + const double *k, const int *out0, const int *out1) { + assert(rout->getSize() == rin->getSize()); + float fac = 1.f / 255.f; + float out0R = std::max(fac * (float)out0[0], 0.f); + float out1R = fac * (float)out1[0]; + float out0G = std::max(fac * (float)out0[1], 0.f); + float out1G = fac * (float)out1[1]; + float out0B = std::max(fac * (float)out0[2], 0.f); + float out1B = fac * (float)out1[2]; + float out0M = std::max(fac * (float)out0[3], 0.f); + float out1M = fac * (float)out1[3]; + + float aFac[4]; + for (int i = 0; i < 4; i++) aFac[i] = a[i] * fac; + + // Process raster + for (int y = 0; y < rin->getLy(); ++y) { + TPixelF *in = rin->pixels(y), *out = rout->pixels(y); + TPixelF *end = in + rin->getLx(); + for (; in < end; ++in, ++out) { + out->m = tcrop(aFac[3] + (float)k[3] * in->m, out0M, out1M); + if (out->m == 0.f) { + out->r = 0.f; + out->g = 0.f; + out->b = 0.f; + } else if (in->m == 0.f) { + out->r = out->m * tcrop(aFac[0], out0R, out1R); + out->g = out->m * tcrop(aFac[1], out0G, out1G); + out->b = out->m * tcrop(aFac[2], out0B, out1B); + } else { + out->r = + out->m * tcrop(aFac[0] + (float)k[0] * in->r / in->m, out0R, out1R); + out->g = + out->m * tcrop(aFac[1] + (float)k[1] * in->g / in->m, out0G, out1G); + out->b = + out->m * tcrop(aFac[2] + (float)k[2] * in->b / in->m, out0B, out1B); + } + } + } +} + +//----------------------------------------------------------------------------- + template void do_rgbmAdjust(TRasterPT rout, TRasterPT rin, ScaleFunc scaleFunc, const int *in0, const int *in1, const int *out0, - const int *out1) { + const int *out1, bool doClamp = true) { assert(rout->getSize() == rin->getSize()); double a[5], k[5]; @@ -207,16 +306,25 @@ void do_rgbmAdjust(TRasterPT rout, TRasterPT rin, ScaleFunc scaleFunc, // Ensure that the output is cropped according to output params int out0i[4], out1i[4]; + if (doClamp) { + out0i[0] = std::max(out0[0], tcrop((int)(a[0] + k[0] * out0[1]), 0, 255)); + out1i[0] = std::min(out1[0], tcrop((int)(a[0] + k[0] * out1[1]), 0, 255)); - out0i[0] = std::max(out0[0], tcrop((int)(a[0] + k[0] * out0[1]), 0, 255)); - out1i[0] = std::min(out1[0], tcrop((int)(a[0] + k[0] * out1[1]), 0, 255)); + out0i[1] = std::max(out0[0], tcrop((int)(a[0] + k[0] * out0[2]), 0, 255)); + out1i[1] = std::min(out1[0], tcrop((int)(a[0] + k[0] * out1[2]), 0, 255)); - out0i[1] = std::max(out0[0], tcrop((int)(a[0] + k[0] * out0[2]), 0, 255)); - out1i[1] = std::min(out1[0], tcrop((int)(a[0] + k[0] * out1[2]), 0, 255)); + out0i[2] = std::max(out0[0], tcrop((int)(a[0] + k[0] * out0[3]), 0, 255)); + out1i[2] = std::min(out1[0], tcrop((int)(a[0] + k[0] * out1[3]), 0, 255)); + } else { + out0i[0] = std::max(out0[0], (int)(a[0] + k[0] * out0[1])); + out1i[0] = std::min(out1[0], (int)(a[0] + k[0] * out1[1])); - out0i[2] = std::max(out0[0], tcrop((int)(a[0] + k[0] * out0[3]), 0, 255)); - out1i[2] = std::min(out1[0], tcrop((int)(a[0] + k[0] * out1[3]), 0, 255)); + out0i[1] = std::max(out0[0], (int)(a[0] + k[0] * out0[2])); + out1i[1] = std::min(out1[0], (int)(a[0] + k[0] * out1[2])); + out0i[2] = std::max(out0[0], (int)(a[0] + k[0] * out0[3])); + out1i[2] = std::min(out1[0], (int)(a[0] + k[0] * out1[3])); + } out0i[3] = out0[4]; out1i[3] = out1[4]; @@ -245,6 +353,8 @@ void TRop::rgbmScale(TRasterP rout, TRasterP rin, const double *k, do_greyScale_lut(rout, rin, a[0], k[0], out0[0], out1[0]); else if ((TRasterGR16P)rout && (TRasterGR16P)rin) do_greyScale_lut(rout, rin, a[0], k[0], out0[0], out1[0]); + else if ((TRasterFP)rout && (TRasterFP)rin) + do_rgbmScale(rout, rin, a, k, out0, out1); else { rout->unlock(); rin->unlock(); @@ -292,7 +402,10 @@ void TRop::rgbmAdjust(TRasterP rout, TRasterP rin, const int *in0, else do_rgbmAdjust(rout, rin, &do_rgbmScale_lut, in0, in1, out0, out1); - } else if ((TRasterGR8P)rout && (TRasterGR8P)rin) + } else if ((TRasterFP)rout && (TRasterFP)rin) + do_rgbmAdjust(rout, rin, &do_rgbmScaleFloat, in0, in1, out0, out1, + false); + else if ((TRasterGR8P)rout && (TRasterGR8P)rin) do_greyAdjust(rout, rin, in0[0], in1[0], out0[0], out1[0]); else if ((TRasterGR16P)rout && (TRasterGR16P)rin) do_greyAdjust(rout, rin, in0[0], in1[0], out0[0], out1[0]); diff --git a/toonz/sources/common/trop/trop.cpp b/toonz/sources/common/trop/trop.cpp index 6d247b7..b27f205 100644 --- a/toonz/sources/common/trop/trop.cpp +++ b/toonz/sources/common/trop/trop.cpp @@ -2,13 +2,14 @@ #include "trop.h" #include "tconvert.h" -//#include "trastercm.h" +// #include "trastercm.h" #ifndef TNZCORE_LIGHT #include "timagecache.h" #include "ttile.h" #include "trasterimage.h" #include "ttoonzimage.h" #endif +#include "tpixelutils.h" TString TRopException::getMessage() const { return ::to_wstring(message); } @@ -92,9 +93,9 @@ TRaster32P TRop::copyAndSwapRBChannels(const TRaster32P &srcRaster) { void TRop::copy(TRasterP dst, const TRasterP &src) { assert(!((TRasterCM32P)src) || (TRasterCM32P)dst); - if (dst->getPixelSize() == src->getPixelSize()) + if (dst->getPixelSize() == src->getPixelSize()) { dst->copy(src); - else { + } else { if (dst->getBounds() != src->getBounds()) { TRect rect = dst->getBounds() * src->getBounds(); if (rect.isEmpty()) return; @@ -117,6 +118,11 @@ public: m_table.push_back( (Q)((outsteps) * (pow(i / inspace, 1.0 / gamma)) + 0.5)); } + Gamma_Lut(int insteps, double gamma) { // compute in 0-1 + float inspace = (float)(insteps); + for (int i = 0; i <= insteps; i++) + m_table.push_back((Q)(pow(i / inspace, 1.f / (float)gamma))); + } }; template @@ -141,6 +147,37 @@ pix->b= pix->b*pix->m/T::maxChannelValue; } } } + +template <> +void doGammaCorrect(TRasterFP raster, double gamma) { + Gamma_Lut lut(TPixel64::maxChannelValue, gamma); // compute in 0.0-1.0 + double step = 1.0 / double(TPixel64::maxChannelValue); + double invGamma = 1.0 / gamma; + auto getLutValue = [&](float val) { + if (val < 0.f) + return val; // keep the negative input unchanged (the same behavior as + // Nuke) + else if (val >= 1.f) + return std::pow(val, (float)invGamma); + float v = val * float(TPixel64::maxChannelValue); + int id = (int)tfloor(v); + float ratio = v - float(id); + return lut.m_table[id] * (1.f - ratio) + lut.m_table[id + 1] * ratio; + }; + + // ? doesn't it need to consider alpha ? + for (int j = 0; j < raster->getLy(); j++) { + TPixelF *pix = raster->pixels(j); + TPixelF *endPix = pix + raster->getLx(); + while (pix < endPix) { + pix->r = getLutValue(pix->r); + pix->g = getLutValue(pix->g); + pix->b = getLutValue(pix->b); + pix++; + } + } +} + template void doGammaCorrectRGBM(TRasterPT raster, double gammar, double gammag, double gammab, double gammam) { @@ -167,7 +204,30 @@ pix->b= pix->b*pix->m/T::maxChannelValue; } } } + +template <> +void doGammaCorrectRGBM(TRasterFP raster, double gammar, + double gammag, double gammab, + double gammam) { + double invGammaR = 1.0 / gammar; + double invGammaG = 1.0 / gammag; + double invGammaB = 1.0 / gammab; + double invGammaM = 1.0 / gammam; + for (int j = 0; j < raster->getLy(); j++) { + TPixelF *pix = raster->pixels(j); + TPixelF *endPix = pix + raster->getLx(); + while (pix < endPix) { + // keep the negative input unchanged (the same behavior as Nuke) + if (pix->r > 0.f) pix->r = (float)std::pow(pix->r, invGammaR); + if (pix->g > 0.f) pix->g = (float)std::pow(pix->g, invGammaG); + if (pix->b > 0.f) pix->b = (float)std::pow(pix->b, invGammaB); + if (pix->m > 0.f) pix->m = (float)std::pow(pix->m, invGammaM); + pix++; + } + } } + +} // namespace //------------------------------------------------------------------- void TRop::gammaCorrect(TRasterP raster, double gamma) { @@ -178,6 +238,8 @@ void TRop::gammaCorrect(TRasterP raster, double gamma) { doGammaCorrect(raster, gamma); else if ((TRaster64P)raster) doGammaCorrect(raster, gamma); + else if ((TRasterFP)raster) + doGammaCorrect(raster, gamma); else { raster->unlock(); throw TRopException("isOpaque: unsupported pixel type"); @@ -200,6 +262,8 @@ void TRop::gammaCorrectRGBM(TRasterP raster, double gammar, double gammag, else if ((TRaster64P)raster) doGammaCorrectRGBM(raster, gammar, gammag, gammab, gammam); + else if ((TRasterFP)raster) + doGammaCorrectRGBM(raster, gammar, gammag, gammab, gammam); else { raster->unlock(); throw TRopException("isOpaque: unsupported pixel type"); @@ -261,6 +325,8 @@ void TRop::setChannel(const TRasterP &rin, TRasterP rout, UCHAR chan, doSetChannel(rin, rout, chan, greytones); else if ((TRaster64P)rin && (TRaster64P)rout) doSetChannel(rin, rout, chan, greytones); + else if ((TRasterFP)rin && (TRasterFP)rout) + doSetChannel(rin, rout, chan, greytones); else { rout->unlock(); throw TRopException("setChannel: unsupported pixel type"); @@ -282,9 +348,9 @@ TRasterP TRop::shrink(TRasterP rin, int shrink) { if ((TRaster32P)rin) rout = TRaster32P(lx, ly); else if ((TRaster64P)rin) - rout = TRaster64P(lx, ly); + rout = TRaster64P(lx, ly); if ((TRasterCM32P)rin) rout = TRasterCM32P(lx, ly); - if ((TRasterGR8P)rin) rout = TRasterGR8P(lx, ly); + if ((TRasterGR8P)rin) rout = TRasterGR8P(lx, ly); int i, j; @@ -370,6 +436,8 @@ void TTile::addInCache(const TRasterP &raster) { TImageCache::instance()->add(m_rasterId, TRasterImageP(rin)); else if ((TRasterGR8P)rin || (TRasterGR16P)rin) TImageCache::instance()->add(m_rasterId, TRasterImageP(rin)); + else if ((TRasterFP)rin) + TImageCache::instance()->add(m_rasterId, TRasterImageP(rin)); else assert(false); } @@ -393,3 +461,291 @@ TTile::~TTile() { } #endif + +//------------------------------------------------------------------- + +namespace { +template +class Linear_Lut { +public: + inline double toLinear(double val, double gamma) { + return std::pow(val, gamma); + // if (val <= 0.04045) + // return val / 12.92; + // else + // return std::pow((val + 0.055) / 1.055, 2.4); + } + + std::vector m_table; + Linear_Lut(int insteps, int outsteps, double gamma) { + double inspace = (double)(insteps); + for (int i = 0; i <= insteps; i++) { + m_table.push_back( + (Q)((outsteps)*toLinear((double)i / inspace, gamma) + 0.5)); + } + } + Linear_Lut(int insteps, double gamma) { // compute in 0-1 + double inspace = (double)(insteps); + for (int i = 0; i <= insteps; i++) + m_table.push_back((Q)(toLinear((double)i / inspace, gamma))); + } +}; + +template +void doLinearRGB(TRasterPT raster, double gamma) { + Linear_Lut lut(T::maxChannelValue, T::maxChannelValue, gamma); + + int j; + for (j = 0; j < raster->getLy(); j++) { + T *pix = raster->pixels(j); + T *endPix = pix + raster->getLx(); + while (pix < endPix) { + if (pix->m > 0) { + pix->r = lut.m_table[pix->r]; + pix->b = lut.m_table[pix->b]; + pix->g = lut.m_table[pix->g]; + } + pix++; + } + } +} + +template <> +void doLinearRGB(TRasterFP raster, double gamma) { + Linear_Lut lut(TPixel64::maxChannelValue, + gamma); // compute in 0.0-1.0 + + double step = 1.0 / double(TPixel64::maxChannelValue); + auto getLutValue = [&](float val) { + if (val < 0.f) + return val; // keep the negative input unchanged (the same behavior as + // Nuke) + else if (val >= 1.f) + return (float)lut.toLinear(val, gamma); + float v = val * float(TPixel64::maxChannelValue); + int id = (int)tfloor(v); + float ratio = v - float(id); + return lut.m_table[id] * (1.f - ratio) + lut.m_table[id + 1] * ratio; + }; + + for (int j = 0; j < raster->getLy(); j++) { + TPixelF *pix = raster->pixels(j); + TPixelF *endPix = pix + raster->getLx(); + while (pix < endPix) { + if (pix->m > 0.f) { + pix->r = getLutValue(pix->r); + pix->g = getLutValue(pix->g); + pix->b = getLutValue(pix->b); + } + pix++; + } + } +} + +template +class sRGB_Lut { +public: + inline double to_sRGB(double lin, double gamma) { + double inv_gamma = 1.0 / gamma; + return std::pow(lin, inv_gamma); + // if (lin <= 0.0031308) + // return 12.92 * lin; + // else + // return 1.055 * std::pow(lin, 1.0 / 2.4) - 0.055; + } + std::vector m_table; + + sRGB_Lut(int insteps, int outsteps, double gamma) { + double inspace = (double)(insteps); + for (int i = 0; i <= insteps; i++) { + m_table.push_back( + (Q)((outsteps)*to_sRGB((double)i / inspace, gamma) + 0.5)); + } + } + sRGB_Lut(int insteps, double gamma) { // compute in 0-1 + double inspace = (double)(insteps); + for (int i = 0; i <= insteps; i++) + m_table.push_back((Q)(to_sRGB((double)i / inspace, gamma))); + } +}; + +template +void do_sRGB(TRasterPT raster, double gamma) { + sRGB_Lut lut(T::maxChannelValue, T::maxChannelValue, gamma); + + int j; + for (j = 0; j < raster->getLy(); j++) { + T *pix = raster->pixels(j); + T *endPix = pix + raster->getLx(); + while (pix < endPix) { + if (pix->m > 0) { + pix->r = lut.m_table[pix->r]; + pix->b = lut.m_table[pix->b]; + pix->g = lut.m_table[pix->g]; + } + pix++; + } + } +} + +template <> +void do_sRGB(TRasterFP raster, double gamma) { + sRGB_Lut lut(TPixel64::maxChannelValue, gamma); // compute in 0.0-1.0 + + double step = 1.0 / double(TPixel64::maxChannelValue); + auto getLutValue = [&](float val) { + if (val < 0.f) + return val; // keep the negative input unchanged (the same behavior as + // Nuke) + else if (val >= 1.f) + return (float)lut.to_sRGB(val, gamma); + float v = val * float(TPixel64::maxChannelValue); + int id = (int)tfloor(v); + float ratio = v - float(id); + return lut.m_table[id] * (1.f - ratio) + lut.m_table[id + 1] * ratio; + }; + + for (int j = 0; j < raster->getLy(); j++) { + TPixelF *pix = raster->pixels(j); + TPixelF *endPix = pix + raster->getLx(); + while (pix < endPix) { + if (pix->m > 0.f) { + pix->r = getLutValue(pix->r); + pix->g = getLutValue(pix->g); + pix->b = getLutValue(pix->b); + } + pix++; + } + } +} + +} // namespace + +void TRop::toLinearRGB(TRasterP raster, double gamma, + bool sourceIsPremultiplied) { + // if raster is already in linear color space, do nothing + if (raster->isLinear()) return; + + raster->lock(); + + if (sourceIsPremultiplied) TRop::depremultiply(raster); + + if ((TRaster32P)raster) + doLinearRGB(raster, gamma); + else if ((TRaster64P)raster) + doLinearRGB(raster, gamma); + else if ((TRasterFP)raster) + doLinearRGB(raster, gamma); + else { + raster->unlock(); + throw TRopException("toLinearRGB: unsupported pixel type"); + } + + raster->setLinear(true); + + if (sourceIsPremultiplied) TRop::premultiply(raster); + + raster->unlock(); +} + +void TRop::tosRGB(TRasterP raster, double gamma, bool sourceIsPremultiplied) { + // if raster is already in sRGB color space, do nothing + if (!raster->isLinear()) return; + + raster->lock(); + + if (sourceIsPremultiplied) TRop::depremultiply(raster); + + if ((TRaster32P)raster) + do_sRGB(raster, gamma); + else if ((TRaster64P)raster) + do_sRGB(raster, gamma); + else if ((TRasterFP)raster) + do_sRGB(raster, gamma); + else { + raster->unlock(); + throw TRopException("tosRGB: unsupported pixel type"); + } + + raster->setLinear(false); + + if (sourceIsPremultiplied) TRop::premultiply(raster); + + raster->unlock(); +} + +namespace { +template +void do_adjustGain(TRasterPT raster, const float gainScale) { + int j; + for (j = 0; j < raster->getLy(); j++) { + T *pix = raster->pixels(j); + T *endPix = pix + raster->getLx(); + while (pix < endPix) { + if (pix->m > 0) { + float val; + val = (float)pix->r * gainScale + 0.5f; + pix->r = (typename T::Channel)((val > (float)T::maxChannelValue) + ? (float)T::maxChannelValue + : val); + val = (float)pix->g * gainScale + 0.5f; + pix->g = (typename T::Channel)((val > (float)T::maxChannelValue) + ? (float)T::maxChannelValue + : val); + val = (float)pix->b * gainScale + 0.5f; + pix->b = (typename T::Channel)((val > (float)T::maxChannelValue) + ? (float)T::maxChannelValue + : val); + } + pix++; + } + } +} + +template <> +void do_adjustGain(TRasterFP raster, const float gainScale) { + for (int j = 0; j < raster->getLy(); j++) { + TPixelF *pix = raster->pixels(j); + TPixelF *endPix = pix + raster->getLx(); + while (pix < endPix) { + if (pix->m > 0.f) { + pix->r *= gainScale; + pix->g *= gainScale; + pix->b *= gainScale; + } + pix++; + } + } +} +} // namespace + +// used in the gain adjustment feature of the flipbook +void TRop::adjustGain(TRasterP raster, int gainStep, double gamma) { + if (gainStep == 0) return; + + std::cout << "adjustGain gamma = " << gamma << std::endl; + float gainScale = std::pow(2., (double)gainStep / 2.); + + raster->lock(); + + TRop::depremultiply(raster); + + TRop::toLinearRGB(raster, gamma, false); + + if ((TRaster32P)raster) + do_adjustGain(raster, gainScale); + else if ((TRaster64P)raster) + do_adjustGain(raster, gainScale); + else if ((TRasterFP)raster) + do_adjustGain(raster, gainScale); + else { + raster->unlock(); + throw TRopException("isOpaque: unsupported pixel type"); + } + + TRop::tosRGB(raster, gamma, false); + + TRop::premultiply(raster); + + raster->unlock(); +} \ No newline at end of file diff --git a/toonz/sources/image/exr/tiio_exr.cpp b/toonz/sources/image/exr/tiio_exr.cpp index af989ad..fb878fc 100644 --- a/toonz/sources/image/exr/tiio_exr.cpp +++ b/toonz/sources/image/exr/tiio_exr.cpp @@ -8,6 +8,7 @@ #include "tpixel.h" #include +#include namespace { inline unsigned char ftouc(float f, float gamma = 2.2f) { @@ -32,6 +33,16 @@ inline float ustof(unsigned short us, float gamma = 2.2f) { return powf(static_cast(us) / 65535.0f, gamma); } +inline float toNonlinear(float f, float gamma = 2.2f) { + if (f < 0.f) return f; + return std::pow(f, 1.f / gamma); +} + +inline float toLinear(float f, float gamma = 2.2f) { + if (f < 0.f) return f; + return std::pow(f, gamma); +} + const QMap ExrCompTypeStr = { {TINYEXR_COMPRESSIONTYPE_NONE, L"None"}, {TINYEXR_COMPRESSIONTYPE_RLE, L"RLE"}, @@ -53,6 +64,8 @@ class ExrReader final : public Tiio::Reader { EXRHeader* m_exr_header; FILE* m_fp; + float m_colorSpaceGamma; + public: ExrReader(); ~ExrReader(); @@ -64,10 +77,19 @@ public: ; void readLine(char* buffer, int x0, int x1, int shrink) override; void readLine(short* buffer, int x0, int x1, int shrink) override; + void readLine(float* buffer, int x0, int x1, int shrink) override; void loadImage(); + void setColorSpaceGamma(const double gamma) override { + assert(gamma > 0); + m_colorSpaceGamma = static_cast(gamma); + } }; -ExrReader::ExrReader() : m_rgbaBuf(nullptr), m_row(0), m_exr_header(nullptr) {} +ExrReader::ExrReader() + : m_rgbaBuf(nullptr) + , m_row(0) + , m_exr_header(nullptr) + , m_colorSpaceGamma(2.2f) {} ExrReader::~ExrReader() { if (m_rgbaBuf) free(m_rgbaBuf); @@ -99,7 +121,8 @@ void ExrReader::open(FILE* file) { bps = 32; break; case TINYEXR_PIXELTYPE_HALF: - bps = 16; + // bps = 16; + bps = 32; // set 32bps in order to return float raster break; } m_info.m_bitsPerSample = bps; @@ -149,10 +172,10 @@ void ExrReader::readLine(char* buffer, int x0, int x1, int shrink) { (x1 < x0) ? (m_info.m_lx - 1) / shrink + 1 : (x1 - x0) / shrink + 1; for (int i = 0; i < width; i++) { - pix->r = ftouc(v[0]); - pix->g = ftouc(v[1]); - pix->b = ftouc(v[2]); - pix->m = ftouc(v[3]); + pix->r = ftouc(v[0], m_colorSpaceGamma); + pix->g = ftouc(v[1], m_colorSpaceGamma); + pix->b = ftouc(v[2], m_colorSpaceGamma); + pix->m = ftouc(v[3], 1.0f); v += shrink * 4; pix += shrink; @@ -181,9 +204,9 @@ void ExrReader::readLine(short* buffer, int x0, int x1, int shrink) { (x1 < x0) ? (m_info.m_lx - 1) / shrink + 1 : (x1 - x0) / shrink + 1; for (int i = 0; i < width; i++) { - pix->r = ftous(v[0]); - pix->g = ftous(v[1]); - pix->b = ftous(v[2]); + pix->r = ftous(v[0], m_colorSpaceGamma); + pix->g = ftous(v[1], m_colorSpaceGamma); + pix->b = ftous(v[2], m_colorSpaceGamma); pix->m = ftous(v[3], 1.0f); v += shrink * 4; @@ -193,15 +216,54 @@ void ExrReader::readLine(short* buffer, int x0, int x1, int shrink) { m_row++; } +void ExrReader::readLine(float* buffer, int x0, int x1, int shrink) { + const int pixelSize = 16; + if (m_row < 0 || m_row >= m_info.m_ly) { + memset(buffer, 0, (x1 - x0 + 1) * pixelSize); + m_row++; + return; + } + + if (!m_rgbaBuf) loadImage(); + + TPixelF* pix = (TPixelF*)buffer; + float* v = m_rgbaBuf + m_row * m_info.m_lx * 4; + + pix += x0; + v += x0 * 4; + + int width = + (x1 < x0) ? (m_info.m_lx - 1) / shrink + 1 : (x1 - x0) / shrink + 1; + // ��������m�����j�A�œǂݍ��ށB���j�A�v�Z�̂Ƃ��͂��ƂŃ��j�A�ɖ߂� + for (int i = 0; i < width; i++) { + pix->r = toNonlinear(v[0], m_colorSpaceGamma); + pix->g = toNonlinear(v[1], m_colorSpaceGamma); + pix->b = toNonlinear(v[2], m_colorSpaceGamma); + pix->m = toNonlinear(v[3], 1.0f); + + v += shrink * 4; + pix += shrink; + } + + m_row++; +} + //============================================================ Tiio::ExrWriterProperties::ExrWriterProperties() : m_compressionType("Compression Type") , m_storageType("Storage Type") - , m_bitsPerPixel("Bits Per Pixel") { - m_bitsPerPixel.addValue(L"48(RGB)"); - m_bitsPerPixel.addValue(L"64(RGBA)"); - m_bitsPerPixel.setValue(L"64(RGBA)"); + , m_bitsPerPixel("Bits Per Pixel") + , m_colorSpaceGamma("Color Space Gamma", 0.1, 10.0, 2.2) { + // internally handles float raster + m_bitsPerPixel.addValue(L"96(RGB)_HF"); + m_bitsPerPixel.addValue(L"128(RGBA)_HF"); + m_bitsPerPixel.addValue(L"96(RGB)_F"); + m_bitsPerPixel.addValue(L"128(RGBA)_F"); + m_bitsPerPixel.setValue(L"128(RGBA)_HF"); + // m_bitsPerPixel.addValue(L"48(RGB)"); + // m_bitsPerPixel.addValue(L"64(RGBA)"); + // m_bitsPerPixel.setValue(L"64(RGBA)"); m_compressionType.addValue( ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_NONE)); @@ -221,12 +283,18 @@ Tiio::ExrWriterProperties::ExrWriterProperties() bind(m_bitsPerPixel); bind(m_compressionType); bind(m_storageType); + bind(m_colorSpaceGamma); } void Tiio::ExrWriterProperties::updateTranslation() { m_bitsPerPixel.setQStringName(tr("Bits Per Pixel")); - m_bitsPerPixel.setItemUIName(L"48(RGB)", tr("48(RGB Half Float)")); - m_bitsPerPixel.setItemUIName(L"64(RGBA)", tr("64(RGBA Half Float)")); + // internally handles float raster + m_bitsPerPixel.setItemUIName(L"96(RGB)_HF", tr("48(RGB Half Float)")); + m_bitsPerPixel.setItemUIName(L"128(RGBA)_HF", tr("64(RGBA Half Float)")); + m_bitsPerPixel.setItemUIName(L"96(RGB)_F", tr("96(RGB Float)")); + m_bitsPerPixel.setItemUIName(L"128(RGBA)_F", tr("128(RGBA Float)")); + // m_bitsPerPixel.setItemUIName(L"48(RGB)", tr("48(RGB Half Float)")); + // m_bitsPerPixel.setItemUIName(L"64(RGBA)", tr("64(RGBA Half Float)")); m_compressionType.setQStringName(tr("Compression Type")); m_compressionType.setItemUIName( @@ -247,6 +315,8 @@ void Tiio::ExrWriterProperties::updateTranslation() { m_storageType.setQStringName(tr("Storage Type")); m_storageType.setItemUIName(EXR_STORAGETYPE_SCANLINE, tr("Scan-line based")); m_storageType.setItemUIName(EXR_STORAGETYPE_TILE, tr("Tile based")); + + m_colorSpaceGamma.setQStringName(tr("Color Space Gamma")); } //============================================================ @@ -266,16 +336,18 @@ public: void open(FILE* file, const TImageInfo& info) override; void writeLine(char* buffer) override; void writeLine(short* buffer) override; + void writeLine(float* buffer) override; void flush() override; Tiio::RowOrder getRowOrder() const override { return Tiio::TOP2BOTTOM; } // m_bpp is set to "Bits Per Pixel" property value in the function open() - bool writeAlphaSupported() const override { return m_bpp == 64; } + bool writeAlphaSupported() const override { return m_bpp == 128; } + bool writeInLinearColorSpace() const override { return true; } }; -ExrWriter::ExrWriter() : m_row(0), m_bpp(64) {} +ExrWriter::ExrWriter() : m_row(0), m_bpp(96) {} ExrWriter::~ExrWriter() { free(m_header.channels); @@ -293,8 +365,8 @@ void ExrWriter::open(FILE* file, const TImageInfo& info) { TEnumProperty* bitsPerPixel = (TEnumProperty*)(m_properties->getProperty("Bits Per Pixel")); - m_bpp = bitsPerPixel ? std::stoi(bitsPerPixel->getValue()) : 64; - assert(m_bpp == 48 || m_bpp == 64); + m_bpp = bitsPerPixel ? std::stoi(bitsPerPixel->getValue()) : 128; + assert(m_bpp == 96 || m_bpp == 128); std::wstring compressionType = ((TEnumProperty*)(m_properties->getProperty("Compression Type"))) @@ -311,7 +383,7 @@ void ExrWriter::open(FILE* file, const TImageInfo& info) { } else m_header.tiled = 0; - m_image.num_channels = (m_bpp == 64) ? 4 : 3; + m_image.num_channels = (m_bpp == 128) ? 4 : 3; for (int c = 0; c < m_image.num_channels; c++) m_imageBuf[c].resize(m_info.m_lx * m_info.m_ly); @@ -323,15 +395,15 @@ void ExrWriter::open(FILE* file, const TImageInfo& info) { m_header.channels = (EXRChannelInfo*)malloc(sizeof(EXRChannelInfo) * m_header.num_channels); // Must be BGR(A) order, since most of EXR viewers expect this channel order. - if (m_bpp == 64) { - strncpy(m_header.channels[0].name, "A", 255); - m_header.channels[0].name[strlen("A")] = '\0'; - strncpy(m_header.channels[1].name, "B", 255); - m_header.channels[1].name[strlen("B")] = '\0'; - strncpy(m_header.channels[2].name, "G", 255); - m_header.channels[2].name[strlen("G")] = '\0'; - strncpy(m_header.channels[3].name, "R", 255); - m_header.channels[3].name[strlen("R")] = '\0'; + if (m_bpp == 128) { + strncpy(m_header.channels[0].name, "B", 255); + m_header.channels[0].name[strlen("B")] = '\0'; + strncpy(m_header.channels[1].name, "G", 255); + m_header.channels[1].name[strlen("G")] = '\0'; + strncpy(m_header.channels[2].name, "R", 255); + m_header.channels[2].name[strlen("R")] = '\0'; + strncpy(m_header.channels[3].name, "A", 255); + m_header.channels[3].name[strlen("A")] = '\0'; } else { strncpy(m_header.channels[0].name, "B", 255); m_header.channels[0].name[strlen("B")] = '\0'; @@ -341,6 +413,11 @@ void ExrWriter::open(FILE* file, const TImageInfo& info) { m_header.channels[2].name[strlen("R")] = '\0'; } + int requested_pixel_type = + (QString::fromStdWString(bitsPerPixel->getValue()).endsWith("_HF")) + ? TINYEXR_PIXELTYPE_HALF + : TINYEXR_PIXELTYPE_FLOAT; + m_header.pixel_types = (int*)malloc(sizeof(int) * m_header.num_channels); m_header.requested_pixel_types = (int*)malloc(sizeof(int) * m_header.num_channels); @@ -348,11 +425,12 @@ void ExrWriter::open(FILE* file, const TImageInfo& info) { m_header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image m_header.requested_pixel_types[i] = - TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in - // .EXR + requested_pixel_type; // pixel type of output image to be stored in + // .EXR } } +// unused void ExrWriter::writeLine(char* buffer) { TPixel32* pix = (TPixel32*)buffer; TPixel32* endPix = pix + m_info.m_lx; @@ -361,16 +439,17 @@ void ExrWriter::writeLine(char* buffer) { float* g_p = &m_imageBuf[1][m_row * m_info.m_lx]; float* b_p = &m_imageBuf[2][m_row * m_info.m_lx]; float* a_p; - if (m_bpp == 64) a_p = &m_imageBuf[3][m_row * m_info.m_lx]; + if (m_bpp == 128) a_p = &m_imageBuf[3][m_row * m_info.m_lx]; while (pix < endPix) { *r_p++ = uctof(pix->r); *g_p++ = uctof(pix->g); *b_p++ = uctof(pix->b); - if (m_bpp == 64) *a_p++ = uctof(pix->m, 1.0f); + if (m_bpp == 128) *a_p++ = uctof(pix->m, 1.0f); pix++; } m_row++; } +// unused void ExrWriter::writeLine(short* buffer) { TPixel64* pix = (TPixel64*)buffer; TPixel64* endPix = pix + m_info.m_lx; @@ -379,24 +458,49 @@ void ExrWriter::writeLine(short* buffer) { float* g_p = &m_imageBuf[1][m_row * m_info.m_lx]; float* b_p = &m_imageBuf[2][m_row * m_info.m_lx]; float* a_p; - if (m_bpp == 64) a_p = &m_imageBuf[3][m_row * m_info.m_lx]; + if (m_bpp == 128) a_p = &m_imageBuf[3][m_row * m_info.m_lx]; while (pix < endPix) { *r_p++ = ustof(pix->r); *g_p++ = ustof(pix->g); *b_p++ = ustof(pix->b); - if (m_bpp == 64) *a_p++ = ustof(pix->m, 1.0f); + if (m_bpp == 128) *a_p++ = ustof(pix->m, 1.0f); + pix++; + } + m_row++; +} + +void ExrWriter::writeLine(float* buffer) { + TPixelF* pix = (TPixelF*)buffer; + TPixelF* endPix = pix + m_info.m_lx; + + float* r_p = &m_imageBuf[0][m_row * m_info.m_lx]; + float* g_p = &m_imageBuf[1][m_row * m_info.m_lx]; + float* b_p = &m_imageBuf[2][m_row * m_info.m_lx]; + float* a_p; + if (m_bpp == 128) a_p = &m_imageBuf[3][m_row * m_info.m_lx]; + while (pix < endPix) { + // raster is already linearized (see MovieRenderer::Imp::postProcessImage() + // in movierenderer.cpp) + *r_p++ = pix->r; + *g_p++ = pix->g; + *b_p++ = pix->b; + if (m_bpp == 128) *a_p++ = pix->m; + //*r_p++ = toLinear(pix->r); + //*g_p++ = toLinear(pix->g); + //*b_p++ = toLinear(pix->b); + // if (m_bpp == 128) *a_p++ = toLinear(pix->m, 1.0f); pix++; } m_row++; } void ExrWriter::flush() { - if (m_bpp == 64) { + if (m_bpp == 128) { float* image_ptr[4]; - image_ptr[0] = &(m_imageBuf[3].at(0)); // B - image_ptr[1] = &(m_imageBuf[2].at(0)); // G - image_ptr[2] = &(m_imageBuf[1].at(0)); // R - image_ptr[3] = &(m_imageBuf[0].at(0)); // A + image_ptr[0] = &(m_imageBuf[2].at(0)); // B + image_ptr[1] = &(m_imageBuf[1].at(0)); // G + image_ptr[2] = &(m_imageBuf[0].at(0)); // R + image_ptr[3] = &(m_imageBuf[3].at(0)); // A m_image.images = (unsigned char**)image_ptr; const char* err; int ret = SaveEXRImageToFileHandle(&m_image, &m_header, m_fp, &err); diff --git a/toonz/sources/image/exr/tiio_exr.h b/toonz/sources/image/exr/tiio_exr.h index f206201..60f1914 100644 --- a/toonz/sources/image/exr/tiio_exr.h +++ b/toonz/sources/image/exr/tiio_exr.h @@ -17,6 +17,7 @@ public: TEnumProperty m_compressionType; TEnumProperty m_storageType; TEnumProperty m_bitsPerPixel; + TDoubleProperty m_colorSpaceGamma; ExrWriterProperties(); diff --git a/toonz/sources/include/tcacheresource.h b/toonz/sources/include/tcacheresource.h index 2844b15..d3f87b6 100644 --- a/toonz/sources/include/tcacheresource.h +++ b/toonz/sources/include/tcacheresource.h @@ -138,7 +138,7 @@ public: QMutex *getMutex() { return &m_mutex; } - enum Type { NONE, RGBM32, RGBM64, CM32 }; + enum Type { NONE, RGBM32, RGBM64, RGBMFloat, CM32 }; int getRasterType() const { return m_tileType; } TRasterP buildCompatibleRaster(const TDimension &size); diff --git a/toonz/sources/include/tcommon.h b/toonz/sources/include/tcommon.h index 86f6f7a..f23bc33 100644 --- a/toonz/sources/include/tcommon.h +++ b/toonz/sources/include/tcommon.h @@ -62,6 +62,7 @@ int nanosleep(struct timespec *, int); #include #include #include +#include // .. and so on namespace TConsts { @@ -120,6 +121,11 @@ inline UCHAR byteFromUshort(USHORT u) { return ((256U * 255U + 1U) * u + (1 << 23)) >> 24; } +/*! from[0. .. 1.] to [0..255] */ +inline UCHAR byteFromFloat(float f) { + return (f >= 1.f) ? 0xFF : (f <= 0.f) ? 0 : (UCHAR)(std::floor(f * 256.f)); +} + /*! ditheredByteFromUshort(u) is like byteFromUshort(). It is used in dithering ... */ diff --git a/toonz/sources/include/tgl.h b/toonz/sources/include/tgl.h index b13c754..09e64fb 100644 --- a/toonz/sources/include/tgl.h +++ b/toonz/sources/include/tgl.h @@ -3,7 +3,7 @@ #ifndef TGL_INCLUDED #define TGL_INCLUDED -//#include "tgeometry.h" +// #include "tgeometry.h" #include "tmachine.h" #ifdef _WIN32 @@ -27,9 +27,9 @@ #include #endif -//#include "tcurves.h" +// #include "tcurves.h" #include "traster.h" -//#include "tfilepath.h" +// #include "tfilepath.h" class TFilePath; class TCubic; @@ -73,6 +73,7 @@ class TCubic; #define TGL_TexFmt10 GL_RGB10_A2 #define TGL_TYPE16 GL_UNSIGNED_SHORT +#define TGL_TYPE32F GL_FLOAT //============================================================================= diff --git a/toonz/sources/include/tiio.h b/toonz/sources/include/tiio.h index 30dd6db..6c14705 100644 --- a/toonz/sources/include/tiio.h +++ b/toonz/sources/include/tiio.h @@ -52,8 +52,10 @@ public: void readLine(char *buffer) { readLine(buffer, 0, m_info.m_lx - 1, 1); } void readLine(short *buffer) { readLine(buffer, 0, m_info.m_lx - 1, 1); } + void readLine(float *buffer) { readLine(buffer, 0, m_info.m_lx - 1, 1); } virtual void readLine(char *buffer, int x0, int x1, int shrink) = 0; virtual void readLine(short *, int, int, int) { assert(false); } + virtual void readLine(float *, int, int, int) { assert(false); } // Returns skipped lines number. // If not implemented returns 0; virtual int skipLines(int lineCount) = 0; @@ -72,6 +74,10 @@ public: assert(false); } + // gamma value to be used for converting linear-based image file to nonlinear + // raster. Curretly only used in EXR images. + virtual void setColorSpaceGamma(const double) {} + private: // not implemented Reader(const Reader &); @@ -101,12 +107,15 @@ public: virtual void writeLine(char *buffer) = 0; virtual void writeLine(short *) { assert(false); } + virtual void writeLine(float *) { assert(false); } virtual void flush() {} virtual RowOrder getRowOrder() const { return BOTTOM2TOP; } virtual bool write64bitSupported() const { return false; } virtual bool writeAlphaSupported() const { return true; } + // only true in EXR format + virtual bool writeInLinearColorSpace() const { return false; } void setProperties(TPropertyGroup *properties); @@ -178,6 +187,6 @@ DVAPI void updateFileWritersPropertiesTranslation(); //------------------------------------------------------------------- -} // namespace +} // namespace Tiio #endif diff --git a/toonz/sources/include/timage_io.h b/toonz/sources/include/timage_io.h index 3a2877c..a5d371d 100644 --- a/toonz/sources/include/timage_io.h +++ b/toonz/sources/include/timage_io.h @@ -3,9 +3,9 @@ #ifndef TIMAGE_IO_INCLUDED #define TIMAGE_IO_INCLUDED -//#include "trasterimage.h" -//#include "texception.h" -//#include "tfilepath.h" +// #include "trasterimage.h" +// #include "texception.h" +// #include "tfilepath.h" #include #include "tfilepath_io.h" @@ -28,7 +28,7 @@ class Reader; class Writer; class VectorReader; class VectorWriter; -} +} // namespace Tiio class TPropertyGroup; class TImageInfo; @@ -93,9 +93,11 @@ protected: bool isOpen() const; bool m_readGreytones; bool m_is64BitEnabled; + bool m_isFloatEnabled; int m_shrink; TRect m_region; static bool m_safeMode; + double m_colorSpaceGamma; public: static void setSafeModeReadingForTzl(bool activated) { @@ -148,6 +150,9 @@ Note: if the region, or part of it, is not contained in the image void enable16BitRead(bool is64bitEnabled) { m_is64BitEnabled = is64bitEnabled; } + void enableFloatRead(bool isFloatEnabled) { + m_isFloatEnabled = isFloatEnabled; + } int getShrink() const { return m_shrink; } /*! @@ -174,6 +179,10 @@ Region dimension doesn't consider shrink void getTzpPaletteColorNames( std::map> &pltColorNames); // colorindex(<256: paint), pagename, colorname + + void setColorSpaceGamma(const double colorSpaceGamma) { + m_colorSpaceGamma = colorSpaceGamma; + } }; //----------------------------------------------------------- diff --git a/toonz/sources/include/tools/stylepicker.h b/toonz/sources/include/tools/stylepicker.h index 03b67ae..5b47128 100644 --- a/toonz/sources/include/tools/stylepicker.h +++ b/toonz/sources/include/tools/stylepicker.h @@ -3,7 +3,7 @@ #ifndef STYLE_PICKER_H #define STYLE_PICKER_H -//#include "timage.h" +// #include "timage.h" #include "tcommon.h" #include "tpalette.h" @@ -71,8 +71,11 @@ public: TPixel32 pickColor(const TPointD &point, double radius, double scale2) const; TPixel64 pickColor16(const TPointD &point, double radius, double scale2) const; + TPixelF pickColor32F(const TPointD &point, double radius, + double scale2) const; TPixel32 pickAverageColor(const TRectD &rect) const; TPixel64 pickAverageColor16(const TRectD &rect) const; + TPixelF pickAverageColor32F(const TRectD &rect) const; // ritorna il colore medio presente nell'area della finestra corrente openGL TPixel32 pickColor(const TRectD &area) const; diff --git a/toonz/sources/include/toonz/imagemanager.h b/toonz/sources/include/toonz/imagemanager.h index dd5aba7..6c9ecc2 100644 --- a/toonz/sources/include/toonz/imagemanager.h +++ b/toonz/sources/include/toonz/imagemanager.h @@ -112,6 +112,10 @@ public: toBeSaved = 0x8, // User will save the image, reverts toBeModified is64bitEnabled = 0x10, // Whether 64-bit rasters are allowed to return + isFloatEnabled = 0x20, // Whether 128-bit float rasters are allowed to + // return (for EXR format) + // isLinearEnabled = 0x40, // Whether linear color space rasters are + // allowed to return (for EXR format) controlFlags = 0xF, // Flags dealing with management control imageFlags = diff --git a/toonz/sources/include/toonz/imagepainter.h b/toonz/sources/include/toonz/imagepainter.h index 684f864..58f4e32 100644 --- a/toonz/sources/include/toonz/imagepainter.h +++ b/toonz/sources/include/toonz/imagepainter.h @@ -67,6 +67,9 @@ public: bool m_drawBlankFrame; bool m_useChecks; //!< whether to consider paint check and ink check bool m_forSceneIcon = false; // whether it is rendered for the scene icons + + int m_gainStep; + public: VisualSettings(); @@ -108,6 +111,6 @@ DVAPI void paintImage(const TImageP &image, const TDimension &imageSize, const VisualSettings &visualSettings, const CompareSettings &compareSettings, const TRect &loadbox); -} +} // namespace ImagePainter #endif // IMAGEPAINTER_H diff --git a/toonz/sources/include/toonz/levelproperties.h b/toonz/sources/include/toonz/levelproperties.h index 7bd3adb..4d1934e 100644 --- a/toonz/sources/include/toonz/levelproperties.h +++ b/toonz/sources/include/toonz/levelproperties.h @@ -32,8 +32,9 @@ class DVAPI LevelOptions { public: - enum DpiPolicy //! Describes the dpi policy used for a level. - { DP_ImageDpi = 0, //!< Level uses the natural dpi embedded in its images. + enum DpiPolicy //! Describes the dpi policy used for a level. + { + DP_ImageDpi = 0, //!< Level uses the natural dpi embedded in its images. DP_CustomDpi = 2 //!< Level uses a custom dpi set by the user. }; @@ -55,6 +56,12 @@ public: //! are not). m_isStopMotionLevel; + double m_colorSpaceGamma; // gamma value to be used for converting + // linear-based image file to nonlinear raster. + // Curretly only used in EXR image levels. + + static const double DefaultColorSpaceGamma; + public: LevelOptions(); //!< Constructs with default values. @@ -229,6 +236,11 @@ ie } bool isStopMotionLevel() const { return m_options.m_isStopMotionLevel; } + // gamma value to be used for converting linear-based image file (EXR) to + // nonlinear raster. + void setColorSpaceGamma(double gamma) { m_options.m_colorSpaceGamma = gamma; } + double colorSpaceGamma() const { return m_options.m_colorSpaceGamma; } + private: TPointD m_imageDpi; diff --git a/toonz/sources/include/toonz/txshsimplelevel.h b/toonz/sources/include/toonz/txshsimplelevel.h index 2ed4b8e..6ac6854 100644 --- a/toonz/sources/include/toonz/txshsimplelevel.h +++ b/toonz/sources/include/toonz/txshsimplelevel.h @@ -70,8 +70,9 @@ public: \sa \p TXshSimpleLevel::getFrameStatus() and \p setFrameStatus() for further details. */ - enum FrameStatusBit //! Describes a level's frame status. - { Normal = 0x0, //!< Frame has no special status. + enum FrameStatusBit //! Describes a level's frame status. + { + Normal = 0x0, //!< Frame has no special status. Scanned = 0x1, //!< A fullcolor frame (only tlv levels). Cleanupped = 0x2, //!< A cleanupped frame (only tlv levels). CleanupPreview = 0x4 //!< A cleanup preview (only fullcolor levels). @@ -101,6 +102,12 @@ public: void set16BitChannelLevel(bool value) { m_16BitChannelLevel = (value && getType() == OVL_XSHLEVEL); } + bool isFloatChannelLevel() const { + return getType() == OVL_XSHLEVEL && m_floatChannelLevel; + } + void setFloatChannelLevel(bool value) { + m_floatChannelLevel = (value && getType() == OVL_XSHLEVEL); + } bool isReadOnly() const { return m_isReadOnly; } void setIsReadOnly(bool value) { m_isReadOnly = value; } @@ -385,7 +392,7 @@ private: std::string m_idBase; std::wstring m_editableRangeUserInfo; - bool m_isSubsequence, m_16BitChannelLevel, m_isReadOnly, + bool m_isSubsequence, m_16BitChannelLevel, m_floatChannelLevel, m_isReadOnly, m_temporaryHookMerged; //!< Used only during hook merge (and hence during //! saving) diff --git a/toonz/sources/include/toonzqt/combohistogram.h b/toonz/sources/include/toonzqt/combohistogram.h index 54f7739..3ae3b7d 100644 --- a/toonz/sources/include/toonzqt/combohistogram.h +++ b/toonz/sources/include/toonzqt/combohistogram.h @@ -32,6 +32,7 @@ class QColor; class RGBLabel; class QLabel; +class QPushButton; #define COMBOHIST_RESOLUTION_W 256 #define COMBOHIST_RESOLUTION_H 100 @@ -76,6 +77,7 @@ class DVAPI ChannelHistoGraph : public QWidget { int m_pickedValue; int m_channelIndex; + float m_range; public: bool *m_showComparePtr; @@ -87,6 +89,7 @@ public: virtual void setValues(int *buf, bool isComp); void showCurrentChannelValue(int val); + void setRange(float range) { m_range = range; } protected: void paintEvent(QPaintEvent *event) override; @@ -115,10 +118,12 @@ protected: class DVAPI ChannelColorBar final : public QWidget { Q_OBJECT QColor m_color; + float m_range; public: ChannelColorBar(QWidget *parent = 0, QColor m_color = QColor()); ~ChannelColorBar() {} + void setRange(float range) { m_range = range; } protected: void paintEvent(QPaintEvent *event) override; @@ -142,6 +147,11 @@ public: void showCurrentChannelValue(int val); + void setRange(float range) { + m_histogramGraph->setRange(range); + m_colorBar->setRange(range); + } + protected slots: void onShowAlphaButtonToggled(bool visible); @@ -170,6 +180,12 @@ class DVAPI ComboHistogram final : public QWidget { QLabel *m_xPosLabel; QLabel *m_yPosLabel; + // graph range control (available only with TRasterF) + QWidget *m_rangeControlContainer; + QPushButton *m_rangeUpBtn, *m_rangeDwnBtn; + QLabel *m_rangeLabel; + int m_rangeStep; // 0 = 1.0, 1 = 2.0, 2 = 4.0, 3 = 8.0... + QComboBox *m_displayModeCombo; bool m_showCompare; @@ -184,8 +200,10 @@ public: void setRaster(const TRasterP &raster, const TPaletteP &palette = 0); void updateInfo(const TPixel32 &pix, const TPointD &imagePos); void updateInfo(const TPixel64 &pix, const TPointD &imagePos); + void updateInfo(const TPixelF &pix, const TPointD &imagePos); void updateAverageColor(const TPixel32 &pix); void updateAverageColor(const TPixel64 &pix); + void updateAverageColor(const TPixelF &pix); void updateCompHistogram(); void setShowCompare(bool on) { @@ -197,6 +215,8 @@ public: if (isVisible() && m_showCompare) updateCompHistogram(); } + void refreshHistogram(); + protected: void computeChannelsValue(int *buf, size_t size, TRasterP ras, TPalette *extPlt = nullptr); @@ -205,6 +225,8 @@ protected: protected slots: void onDisplayModeChanged(); void onShowAlphaButtonToggled(bool); + void onRangeUp(); + void onRangeDown(); }; #endif diff --git a/toonz/sources/include/toonzqt/flipconsole.h b/toonz/sources/include/toonzqt/flipconsole.h index 2f2d4a0..7746360 100644 --- a/toonz/sources/include/toonzqt/flipconsole.h +++ b/toonz/sources/include/toonzqt/flipconsole.h @@ -225,6 +225,9 @@ public: eFlipVertical, eResetView, eBlankFrames, + eDecreaseGain, + eResetGain, + eIncreaseGain, // following values are hard-coded in ImagePainter eBlackBg = 0x40000, eWhiteBg = 0x80000, @@ -316,6 +319,7 @@ public: void setFpsFieldColor(const QColor &color) { m_fpsFieldColor = color; } QColor getFpsFieldColor() const { return m_fpsFieldColor; } + void resetGain(bool forceInit = false); signals: void buttonPressed(FlipConsole::EGadget button); @@ -331,7 +335,7 @@ private: QAction *m_customSep, *m_rateSep, *m_histoSep, *m_bgSep, *m_vcrSep, *m_compareSep, *m_saveSep, *m_colorFilterSep, *m_soundSep, *m_subcamSep, - *m_filledRasterSep, *m_viewerSep; + *m_filledRasterSep, *m_viewerSep, *m_gainSep; QToolBar *m_playToolBar; QActionGroup *m_colorFilterGroup; @@ -347,6 +351,7 @@ private: QFrame *createFpsSlider(); QAction *m_doubleRedAction, *m_doubleGreenAction, *m_doubleBlueAction; DoubleButton *m_doubleRed, *m_doubleGreen, *m_doubleBlue; + std::vector m_gadgetsMask; int m_from, m_to, m_step; int m_currentFrame, m_framesCount; @@ -363,6 +368,9 @@ private: int m_blanksToDraw; bool m_isLinkable; + QToolButton *m_resetGainBtn; + int m_prevGainStep; + QMap m_buttons; QMap m_actions; @@ -395,6 +403,8 @@ private: FlipConsoleOwner *m_consoleOwner; TFrameHandle *m_frameHandle; + void adjustGain(bool increase); + protected slots: void OnSetCurrentFrame(); diff --git a/toonz/sources/include/toonzqt/fxsettings.h b/toonz/sources/include/toonzqt/fxsettings.h index f2ffb39..6564805 100644 --- a/toonz/sources/include/toonzqt/fxsettings.h +++ b/toonz/sources/include/toonzqt/fxsettings.h @@ -39,6 +39,7 @@ class QToolBar; class QStackedWidget; class QVBoxLayout; class QGridLayout; +class QLabel; class QPushButton; class FxKeyframeNavigator; class ParamViewer; @@ -153,6 +154,9 @@ class DVAPI ParamsPageSet final : public QWidget { /*-- ヘルプボタンで開くURL --*/ std::string m_helpUrl; QPushButton *m_helpButton; + // waring mark appears when the current fx does not support + // float / linear render settings + QLabel *m_warningMark; public: #if QT_VERSION >= 0x050500 @@ -179,6 +183,8 @@ public: QSize getPreferredSize() { return m_preferredSize; } + void updateWarnings(const TFxP ¤tFx, bool isFloat); + protected: void createPage(TIStream &is, const TFxP &fx, int index); @@ -224,6 +230,9 @@ public: emit preferredSizeChanged(size); } + // show warning if the current Fx does not support float rendering + void updateWarnings(const TFxP ¤tFx, bool isFloat); + protected: ParamsPageSet *getCurrentPageSet() const; diff --git a/toonz/sources/include/toutputproperties.h b/toonz/sources/include/toutputproperties.h index a12382d..57a6a39 100644 --- a/toonz/sources/include/toutputproperties.h +++ b/toonz/sources/include/toutputproperties.h @@ -98,11 +98,18 @@ private: // such as new raster level, captured images by camera capture feature, etc. TFrameId m_formatTemplateFId; + // if true, channel width, linear color space and color space gamma will be + // shared between output and preview settings. + bool m_syncColorSettings; + // for restoring bpp when setting the color space back to nonlinear + int m_nonlinearBpp; + public: /*! Constructs TOutputProperties with default value. */ TOutputProperties(); + /*! Destroys the TOutputProperties object. */ @@ -227,6 +234,11 @@ machine's CPU). BoardSettings *getBoardSettings() const { return m_boardSettings; } TFrameId &formatTemplateFId() { return m_formatTemplateFId; } + + bool isColorSettingsSynced() { return m_syncColorSettings; } + void syncColorSettings(bool sync) { m_syncColorSettings = sync; } + int getNonlinearBpp() { return m_nonlinearBpp; } + void setNonlinearBpp(int bpp) { m_nonlinearBpp = bpp; } }; //-------------------------------------------- diff --git a/toonz/sources/include/tparamcontainer.h b/toonz/sources/include/tparamcontainer.h index 1cfd3a4..66751ce 100644 --- a/toonz/sources/include/tparamcontainer.h +++ b/toonz/sources/include/tparamcontainer.h @@ -6,7 +6,7 @@ #include #include "tparam.h" -//#include "tfx.h" +// #include "tfx.h" #include "tcommon.h" #undef DVAPI @@ -26,6 +26,7 @@ class TParam; class DVAPI TParamVar { std::string m_name; + // hidden parameter will be hidden from the fx settings or the function editor bool m_isHidden; // Flag for an obsolete parameter used for maintaining backward-compatiblity. // - The obsolete parameter will call a special function diff --git a/toonz/sources/include/tparamset.h b/toonz/sources/include/tparamset.h index 8f39985..88d5723 100644 --- a/toonz/sources/include/tparamset.h +++ b/toonz/sources/include/tparamset.h @@ -199,6 +199,7 @@ public: TPixel32 getDefaultValue() const; TPixelD getValueD(double frame) const; TPixel32 getValue(double frame) const; + TPixel32 getValue(double frame, bool linear, double colorSpaceGamma) const; TPixel64 getValue64(double frame) const; TPixel32 getPremultipliedValue(double frame) const; diff --git a/toonz/sources/include/tpixel.h b/toonz/sources/include/tpixel.h index 0b8fe5b..d8677cd 100644 --- a/toonz/sources/include/tpixel.h +++ b/toonz/sources/include/tpixel.h @@ -258,7 +258,6 @@ typedef TPixelRGBM64 TPixel64; class DVAPI TPixelD { public: typedef double Channel; - Channel r, g, b, m; TPixelD() : r(0), g(0), b(0), m(1){}; @@ -319,6 +318,68 @@ static inline TPixelD from(const TPixelD &pix) {return pix;}; }; //----------------------------------------------------------------------------- +// TPixelF is used in floating-point rendering + +class DVAPI TPixelF { +public: + typedef float Channel; + static const float maxChannelValue; + +#ifdef TNZ_MACHINE_CHANNEL_ORDER_BGRM + Channel b, g, r, m; +#elif defined(TNZ_MACHINE_CHANNEL_ORDER_MRGB) + Channel m, r, g, b; +#elif defined(TNZ_MACHINE_CHANNEL_ORDER_RGBM) + Channel r, g, b, m; +#else + undefined machine order !!!! +#endif + + TPixelF() : r(0.f), g(0.f), b(0.f), m(1.f){}; + TPixelF(const TPixelF &pix) : r(pix.r), g(pix.g), b(pix.b), m(pix.m){}; + TPixelF(float rr, float gg, float bb, float mm = 1.f) + : r(rr), g(gg), b(bb), m(mm){}; + + inline bool operator==(const TPixelF &p) const { + return r == p.r && g == p.g && b == p.b && m == p.m; + }; + inline bool operator<(const TPixelF &p) const { + return r < p.r || + (r == p.r && + (g < p.g || (g == p.g && (b < p.b || (b == p.b && (m < p.m)))))); + }; + + inline bool operator>=(const TPixelF &p) const { return !operator<(p); }; + inline bool operator!=(const TPixelF &p) const { return !operator==(p); }; + inline bool operator>(const TPixelF &p) const { + return !operator<(p) && !operator==(p); + }; + inline bool operator<=(const TPixelF &p) const { return !operator>(p); }; + + inline TPixelF operator*=(const TPixelF &p) { + r *= p.r; + g *= p.g; + b *= p.b; + m *= p.m; + return *this; + } + inline TPixelF operator*(const TPixelF &p) const { + TPixelF ret(*this); + return ret *= p; + } + + static const TPixelF Red; + static const TPixelF Green; + static const TPixelF Blue; + static const TPixelF Yellow; + static const TPixelF Cyan; + static const TPixelF Magenta; + static const TPixelF White; + static const TPixelF Black; + static const TPixelF Transparent; +}; + +//----------------------------------------------------------------------------- class DVAPI TPixelCY { public: diff --git a/toonz/sources/include/tpixelgr.h b/toonz/sources/include/tpixelgr.h index 23066cf..720d219 100644 --- a/toonz/sources/include/tpixelgr.h +++ b/toonz/sources/include/tpixelgr.h @@ -28,6 +28,8 @@ class TPixelGR8; //! Gray Scale 2 byte/pixel class TPixelGR16; +class TPixelF; + //----------------------------------------------------------------------------- /*! grey tones, 8 bits A set of predefined colors are included as well. @@ -110,6 +112,22 @@ public: //----------------------------------------------------------------------------- +class DVAPI TPixelGRF { +public: + typedef float Channel; + + float value; + TPixelGRF(float v = 0.f) : value(v){}; + TPixelGRF(const TPixelGRF &pix) : value(pix.value){}; + inline bool operator==(const TPixelGRF &p) const { return value == p.value; }; + inline bool operator<(const TPixelGRF &p) const { return value < p.value; }; + + inline void setValue(float _value) { value = (float)_value; } + static TPixelGRF from(const TPixelF &pix); +}; + +//----------------------------------------------------------------------------- + class DVAPI TPixelGRD { public: typedef double Channel; diff --git a/toonz/sources/include/tpixelutils.h b/toonz/sources/include/tpixelutils.h index 6ffd0be..eeaca14 100644 --- a/toonz/sources/include/tpixelutils.h +++ b/toonz/sources/include/tpixelutils.h @@ -29,6 +29,12 @@ inline T blend(const T &a, const T &b, double t) { troundp((1 - t) * a.b + t * b.b), troundp((1 - t) * a.m + t * b.m)); } +template <> +inline TPixelF blend(const TPixelF &a, const TPixelF &b, double t) { + return TPixelF((1 - t) * a.r + t * b.r, (1 - t) * a.g + t * b.g, + (1 - t) * a.b + t * b.b, (1 - t) * a.m + t * b.m); +} + //----------------------------------------------------------------------------- /*! this template function computes a linear interpolation between @@ -76,6 +82,20 @@ inline T overPixT(const T &bot, const T &top) { (bot.m == max) ? max : max - (max - bot.m) * (max - top.m) / max); } +template <> +inline TPixelF overPixT(const TPixelF &bot, + const TPixelF &top) { + if (top.m >= 1.f) return top; + + if (top.m <= 0.f) return bot; + + float r = top.r + bot.r * (1.f - top.m); + float g = top.g + bot.g * (1.f - top.m); + float b = top.b + bot.b * (1.f - top.m); + return TPixelF(r, g, b, + (bot.m >= 1.f) ? bot.m : 1.f - (1.f - bot.m) * (1.f - top.m)); +} + //----------------------------------------------------------------------------- template inline T overPixGRT(const T &bot, const S &top) { @@ -109,6 +129,16 @@ inline T quickOverPixT(const T &bot, const T &top) { (bot.m == max) ? max : max - (max - bot.m) * (max - top.m) / max); } +template <> +inline TPixelF quickOverPixT(const TPixelF &bot, + const TPixelF &top) { + float r = top.r + bot.r * (1.f - top.m); + float g = top.g + bot.g * (1.f - top.m); + float b = top.b + bot.b * (1.f - top.m); + return TPixelF(r, g, b, + (bot.m == 1.f) ? 1.f : 1.f - (1.f - bot.m) * (1.f - top.m)); +} + //------------------------------------------------------------------------------------ template @@ -122,6 +152,17 @@ inline T quickOverPixPremultT(const T &bot, const T &top) { (b < max) ? (Q)b : (Q)max, (bot.m == max) ? max : max - (max - bot.m) * (max - top.m) / max); } + +template <> +inline TPixelF quickOverPixPremultT(const TPixelF &bot, + const TPixelF &top) { + float r = top.r * top.m + bot.r * (1.f - top.m); + float g = top.g * top.m + bot.g * (1.f - top.m); + float b = top.b * top.m + bot.b * (1.f - top.m); + return TPixelF(r, g, b, + (bot.m == 1.f) ? 1.f : 1.f - (1.f - bot.m) * (1.f - top.m)); +} + //------------------------------------------------------------------------------------ /*-- Show raster images darken-blended on the viewer --*/ /* references from ino_blend_darken.cpp */ @@ -212,6 +253,12 @@ inline TPixel64 overPix(const TPixel64 &bot, const TPixel64 &top) { //----------------------------------------------------------------------------- +inline TPixelF overPix(const TPixelF &bot, const TPixelF &top) { + return overPixT(bot, top); +} + +//----------------------------------------------------------------------------- + inline TPixel32 quickOverPix(const TPixel32 &bot, const TPixelGR8 &top) { return quickOverPixGRT(bot, top); } @@ -246,6 +293,18 @@ inline TPixel64 quickOverPix(const TPixel64 &bot, const TPixel64 &top) { return quickOverPixT(bot, top); } +//----------------------------------------------------------------------------- + +inline TPixelF quickOverPixPremult(const TPixelF &bot, const TPixelF &top) { + return quickOverPixPremultT(bot, top); +} + +//----------------------------------------------------------------------------- + +inline TPixelF quickOverPix(const TPixelF &bot, const TPixelF &top) { + return quickOverPixT(bot, top); +} + //------------------------------------------------------------------------------------ inline TPixel32 quickOverPixDarkenBlended(const TPixel32 &bot, @@ -273,6 +332,20 @@ inline void overPix(T &outPix, const T &bot, const T &top) { } } +template <> +inline void overPix(TPixelF &outPix, const TPixelF &bot, + const TPixelF &top) { + if (top.m >= 1.f) + outPix = top; + else if (top.m <= 0.f) + outPix = bot; + else { + outPix.r = top.r + bot.r * (1.f - top.m); + outPix.g = top.g + bot.g * (1.f - top.m); + outPix.b = top.b + bot.b * (1.f - top.m); + outPix.m = (bot.m >= 1.f) ? bot.m : 1.f - (1.f - bot.m) * (1.f - top.m); + } +} //----------------------------------------------------------------------------- inline TPixel32 overPixOnWhite(const TPixel32 &top) { @@ -351,6 +424,12 @@ inline void premult(TPixel64 &pix) { pix.b = pix.b * pix.m / 65535.0; } +inline void premult(TPixelF &pix) { + pix.r = pix.r * pix.m; + pix.g = pix.g * pix.m; + pix.b = pix.b * pix.m; +} + inline void depremult(TPixel32 &pix) { float fac = 255.0f / pix.m; pix.r = std::min(pix.r * fac, 255.0f); @@ -365,6 +444,11 @@ inline void depremult(TPixel64 &pix) { pix.b = std::min(pix.b * fac, 65535.0); } +inline void depremult(TPixelF &pix) { + pix.r = pix.r / pix.m; + pix.g = pix.g / pix.m; + pix.b = pix.b / pix.m; +} //----------------------------------------------------------------------------- template @@ -389,6 +473,11 @@ inline TPixel64 premultiply(const TPixel64 &pix) { pix.b * pix.m / 65535.0, pix.m); } +inline TPixelF premultiply(const TPixelF &pix) { + if (pix.m <= 0.f) return TPixelF(0.f, 0.f, 0.f, 0.f); + return TPixelF(pix.r * pix.m, pix.g * pix.m, pix.b * pix.m, pix.m); +} + inline TPixel32 depremultiply(const TPixel32 &pix) { return TPixel32(pix.r * 255.0 / pix.m, pix.g * 255.0 / pix.m, pix.b * 255.0 / pix.m, pix.m); @@ -399,6 +488,11 @@ inline TPixel64 depremultiply(const TPixel64 &pix) { pix.b * 65535.0 / pix.m, pix.m); } +inline TPixelF depremultiply(const TPixelF &pix) { + if (pix.m <= 0.f) return TPixelF(); + return TPixelF(pix.r / pix.m, pix.g / pix.m, pix.b / pix.m, pix.m); +} + //----------------------------------------------------------------------------- //! onversion between RGB and HSV colorspace @@ -443,15 +537,28 @@ DVAPI void rgb2hls(double r, double g, double b, double *h, double *l, DVAPI TPixel32 toPixel32(const TPixel64 &); DVAPI TPixel32 toPixel32(const TPixelD &); DVAPI TPixel32 toPixel32(const TPixelGR8 &); +DVAPI TPixel32 toPixel32(const TPixelF &); DVAPI TPixel64 toPixel64(const TPixel32 &); DVAPI TPixel64 toPixel64(const TPixelD &); DVAPI TPixel64 toPixel64(const TPixelGR8 &); +DVAPI TPixel64 toPixel64(const TPixelF &); DVAPI TPixelD toPixelD(const TPixel32 &); DVAPI TPixelD toPixelD(const TPixel64 &); DVAPI TPixelD toPixelD(const TPixelGR8 &); - +DVAPI TPixelD toPixelD(const TPixelF &); + +DVAPI TPixelF toPixelF(const TPixel32 &); +DVAPI TPixelF toPixelF(const TPixelD &); +DVAPI TPixelF toPixelF(const TPixel64 &); +DVAPI TPixelF toPixelF(const TPixelGR8 &); + +DVAPI TPixel32 toLinear(const TPixel32 &, const double); +DVAPI TPixel64 toLinear(const TPixel64 &, const double); +DVAPI TPixelD toLinear(const TPixelD &, const double); +DVAPI TPixelF toLinear(const TPixelF &, const double); +DVAPI TPixelGR8 toLinear(const TPixelGR8 &, const double); // // nel caso in cui il tipo di destinazione sia il parametro di un template // es. template .... @@ -467,6 +574,7 @@ public: inline static T from(const TPixel64 &pix); inline static T from(const TPixelD &pix); inline static T from(const TPixelGR8 &pix); + inline static T from(const TPixelF &pix); }; template <> @@ -476,6 +584,7 @@ public: inline static TPixel32 from(const TPixel64 &pix) { return toPixel32(pix); } inline static TPixel32 from(const TPixelD &pix) { return toPixel32(pix); } inline static TPixel32 from(const TPixelGR8 &pix) { return toPixel32(pix); } + inline static TPixel32 from(const TPixelF &pix) { return toPixel32(pix); } }; template <> @@ -485,6 +594,7 @@ public: inline static TPixel64 from(const TPixel64 &pix) { return pix; } inline static TPixel64 from(const TPixelD &pix) { return toPixel64(pix); } inline static TPixel64 from(const TPixelGR8 &pix) { return toPixel64(pix); } + inline static TPixel64 from(const TPixelF &pix) { return toPixel64(pix); } }; template <> @@ -494,6 +604,17 @@ public: inline static TPixelD from(const TPixel64 &pix) { return toPixelD(pix); } inline static TPixelD from(const TPixelD &pix) { return pix; } inline static TPixelD from(const TPixelGR8 &pix) { return toPixelD(pix); } + inline static TPixelD from(const TPixelF &pix) { return toPixelD(pix); } +}; + +template <> +class PixelConverter { +public: + inline static TPixelF from(const TPixel32 &pix) { return toPixelF(pix); } + inline static TPixelF from(const TPixel64 &pix) { return toPixelF(pix); } + inline static TPixelF from(const TPixelD &pix) { return toPixelF(pix); } + inline static TPixelF from(const TPixelGR8 &pix) { return toPixelF(pix); } + inline static TPixelF from(const TPixelF &pix) { return pix; } }; //--------------------------------------------------------------------------------------- diff --git a/toonz/sources/include/traster.h b/toonz/sources/include/traster.h index f1d737b..a3bf6a0 100644 --- a/toonz/sources/include/traster.h +++ b/toonz/sources/include/traster.h @@ -88,6 +88,7 @@ protected: // i costruttori sono qui per centralizzare la gestione della memoria // e' comunque impossibile fare new TRaster perche' e' una classe astratta // (clone, extract) + bool m_isLinear; // linear color space // crea il buffer associato (NON fa addRef()) TRaster(int lx, int ly, int pixelSize); @@ -172,11 +173,18 @@ public: TRasterP getParent() { return m_parent; } // creazione di TRaster derivati + bool isLinear() const { return m_isLinear; } + void setLinear(const bool linear) { + if (m_isLinear == linear) return; + m_isLinear = linear; + if (m_parent) m_parent->setLinear(linear); + } + // devono essere virtuali puri perche' il nuovo raster creato deve essere del // tipo giusto - virtual TRasterP clone() const = 0; - virtual TRasterP extract(TRect &rect) = 0; - virtual TRasterP create() const = 0; + virtual TRasterP clone() const = 0; + virtual TRasterP extract(TRect &rect) = 0; + virtual TRasterP create() const = 0; virtual TRasterP create(int lx, int ly) const = 0; // definita in termini di extract(rect); non lo posso fare subito perche' @@ -192,7 +200,7 @@ public: // getBounds() // e i due raster sono allineati in basso a sinistra (src[0,0] -> dst[offset]) /*!Copies the content of the source raster in the current raster. -*/ + */ void copy(const TRasterP &src, const TPoint &offset = TPoint()); void xMirror(); @@ -328,6 +336,8 @@ public: if (isEmpty() || getBounds().overlaps(rect) == false) return TRasterP(); rect = getBounds() * rect; // addRef(); + // return TRasterP(new TRasterT(rect.getLx(), rect.getLy(), m_wrap, + // pixels(rect.y0) + rect.x0, this)); return TRasterP(new TRasterT(rect.getLx(), rect.getLy(), m_wrap, pixels(rect.y0) + rect.x0, this)); }; @@ -429,6 +439,9 @@ template class DVAPI TRasterPT; template class DVAPI TSmartPointerT>; template class DVAPI TRasterPT; +template class DVAPI TSmartPointerT>; +template class DVAPI TRasterPT; + template class DVAPI TSmartPointerT>; template class DVAPI TRasterPT; @@ -445,6 +458,7 @@ template class DVAPI TRasterPT; typedef TRasterPT TRaster32P; typedef TRasterPT TRaster64P; +typedef TRasterPT TRasterFP; typedef TRasterPT TRasterGR8P; typedef TRasterPT TRasterGR16P; typedef TRasterPT TRasterGRDP; diff --git a/toonz/sources/include/trasterfx.h b/toonz/sources/include/trasterfx.h index 8542bb0..ca5b40c 100644 --- a/toonz/sources/include/trasterfx.h +++ b/toonz/sources/include/trasterfx.h @@ -117,6 +117,9 @@ public: //! data //! must be accompanied by a tile of the suitable type. \sa //! TRasterFx::compute(). + + bool m_linearColorSpace; // compute in linear color space (gamma 2.2) + int m_maxTileSize; //!< Maximum size (in MegaBytes) of a tile cachable during //! a render process. //! Used by the predictive cache manager to subdivide an fx calculation into @@ -162,6 +165,8 @@ public: // For now it is used only in the plasticDeformerFx. std::shared_ptr m_offScreenSurface; + double m_colorSpaceGamma; + public: TRenderSettings(); ~TRenderSettings(); @@ -273,6 +278,13 @@ public: void enableCache(bool on); bool isCacheEnabled() const; + void enableComputeInFloat(bool on); + bool canComputeInFloat() const; + virtual bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const { + return false; + } + // resituisce una stringa che identifica univocamente il sottoalbero // avente come radice l'effetto std::string getAlias(double frame, @@ -355,6 +367,9 @@ public: void transform(double frame, int port, const TRectD &rectOnOutput, const TRenderSettings &infoOnOutput, TRectD &rectOnInput, TRenderSettings &infoOnInput) override; + + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override; }; //------------------------------------------------------------------- diff --git a/toonz/sources/include/trop.h b/toonz/sources/include/trop.h index 7b060be..23c0f02 100644 --- a/toonz/sources/include/trop.h +++ b/toonz/sources/include/trop.h @@ -286,10 +286,10 @@ DVAPI void ropmin(const TRasterP &rup, const TRasterP &rdown, DVAPI void ropmax(const TRasterP &rup, const TRasterP &rdown, const TRasterP &rout); -DVAPI void linearburn(const TRasterP &rup, const TRasterP &rdown, - const TRasterP &rout); -DVAPI void overlay(const TRasterP &rup, const TRasterP &rdown, - const TRasterP &rout); +// DVAPI void linearburn(const TRasterP &rup, const TRasterP &rdown, +// const TRasterP &rout); +// DVAPI void overlay(const TRasterP &rup, const TRasterP &rdown, +// const TRasterP &rout); //! Make a premultiply of all raster pixels DVAPI void premultiply(const TRasterP &ras); @@ -412,4 +412,12 @@ DVAPI void lockRaster(_RASTER *raster); //! inactivity periods. DVAPI void unlockRaster(_RASTER *raster); +// conversion between sRGB <--> Linear RGB +DVAPI void toLinearRGB(TRasterP raster, double gamma, + bool sourceIsPremultiplied = true); +DVAPI void tosRGB(TRasterP raster, double gamma, + bool sourceIsPremultiplied = true); + +DVAPI void adjustGain(TRasterP raster, int gainStep, double gamma); + } // namespace TRop diff --git a/toonz/sources/include/tspectrum.h b/toonz/sources/include/tspectrum.h index f48174a..9cfe85e 100644 --- a/toonz/sources/include/tspectrum.h +++ b/toonz/sources/include/tspectrum.h @@ -181,10 +181,12 @@ DVAPI TSpectrumT convert(const TSpectrumT &s); #ifdef _WIN32 template class DVAPI TSpectrumT; template class DVAPI TSpectrumT; +template class DVAPI TSpectrumT; #endif typedef TSpectrumT TSpectrum; typedef TSpectrumT TSpectrum64; +typedef TSpectrumT TSpectrumF; #ifdef _MSC_VER #pragma warning(default : 4251) diff --git a/toonz/sources/include/tspectrumparam.h b/toonz/sources/include/tspectrumparam.h index 8eed363..175faa1 100644 --- a/toonz/sources/include/tspectrumparam.h +++ b/toonz/sources/include/tspectrumparam.h @@ -54,6 +54,7 @@ public: TSpectrum getValue(double frame) const; TSpectrum64 getValue64(double frame) const; + TSpectrumF getValueF(double frame) const; void setValue(double frame, const TSpectrum &value, bool undoing = false); void setDefaultValue(const TSpectrum &value); diff --git a/toonz/sources/stdfx/adjustlevelsfx.cpp b/toonz/sources/stdfx/adjustlevelsfx.cpp index d2df1c5..69221ae 100644 --- a/toonz/sources/stdfx/adjustlevelsfx.cpp +++ b/toonz/sources/stdfx/adjustlevelsfx.cpp @@ -1,7 +1,7 @@ #include "stdfx.h" -//#include "tsystem.h" +// #include "tsystem.h" #include "tfxparam.h" #include "tpixelutils.h" #include "tparamset.h" @@ -64,7 +64,11 @@ public: bindParam(this, "gamma_b", m_gamma_b); bindParam(this, "gamma_m", m_gamma_m); addInputPort("Source", m_input); - + // TODO: Float�ł̌v�Z���”\�ɂȂ�ɂ������āA + // �l�͈̔͂�0-255�Ő����������Ȃ� + // �� + // �X���C�_��0-255�̂܂܁A���̓t�B�[���h�͍D���Ȑ�����������悤�ɂ��邩�H + // (Nuke �� Histogram�G�t�F�N�g�̂悤��) m_in_rgb->getMin()->setValueRange(0, 255); m_in_rgb->getMax()->setValueRange(0, 255); m_in_r->getMin()->setValueRange(0, 255); @@ -90,6 +94,8 @@ public: m_gamma_g->setValueRange(0.0, 200.0); m_gamma_b->setValueRange(0.0, 200.0); m_gamma_m->setValueRange(0.0, 200.0); + + enableComputeInFloat(true); } ~AdjustLevelsFx(){}; diff --git a/toonz/sources/stdfx/blurfx.cpp b/toonz/sources/stdfx/blurfx.cpp index dde4f3e..d2b541f 100644 --- a/toonz/sources/stdfx/blurfx.cpp +++ b/toonz/sources/stdfx/blurfx.cpp @@ -21,6 +21,8 @@ public: addInputPort("Source", m_input); m_value->setValueRange(0, std::numeric_limits::max()); + + enableComputeInFloat(true); } ~BlurFx(){}; diff --git a/toonz/sources/stdfx/bright_contfx.cpp b/toonz/sources/stdfx/bright_contfx.cpp index 3e4c62b..568371f 100644 --- a/toonz/sources/stdfx/bright_contfx.cpp +++ b/toonz/sources/stdfx/bright_contfx.cpp @@ -19,6 +19,8 @@ public: m_bright->setValueRange(-127, 127); m_contrast->setValueRange(-127, 127); addInputPort("Source", m_input); + + enableComputeInFloat(true); } ~Bright_ContFx(){}; @@ -114,6 +116,97 @@ void doBrightnessContrast(TRasterPT ras, double contrast, ras->unlock(); } +void my_compute_lut_float(double contrast, double brightness, + std::vector &lut, float &d0, float &d1) { + int i; + float value, nvalue, power; + + int half_maxChannelValue = tfloor(TPixel64::maxChannelValue / 2.0); + + int lutSize = TPixel64::maxChannelValue + 1; + for (i = 0; i < lutSize; i++) { + value = i / float(TPixel64::maxChannelValue); + /*brightness*/ + if (brightness < 0.0) + value = value * (1.f + brightness); + else + value = value + ((1.f - value) * brightness); + /*contrast*/ + if (contrast < 0.f) { + if (value > 0.5f) + nvalue = 1.f - value; + else + nvalue = value; + if (nvalue < 0.f) nvalue = 0.f; + nvalue = 0.5f * pow(nvalue * 2.f, (double)(1.f + contrast)); + if (value > 0.5f) + value = 1.0f - nvalue; + else + value = nvalue; + } else { + if (value > 0.5f) + nvalue = 1.f - value; + else + nvalue = value; + if (nvalue < 0.f) nvalue = 0.f; + power = + (contrast == 1.0f) ? half_maxChannelValue : 1.f / (1.f - contrast); + nvalue = 0.5f * pow(2.f * nvalue, power); + if (value > 0.5f) + value = 1.f - nvalue; + else + value = nvalue; + } + lut[i] = value; + } + + d0 = (lut[1] - lut[0]) * float(TPixel64::maxChannelValue); + d1 = (lut[TPixel64::maxChannelValue] - lut[TPixel64::maxChannelValue - 1]) * + float(TPixel64::maxChannelValue); +} + +void doBrightnessContrastFloat(TRasterFP ras, double contrast, + double brightness) { + int lx = ras->getLx(); + int ly = ras->getLy(); + + // create lut with 65536 levels + std::vector lut(TPixel64::maxChannelValue + 1); + // values less than 0.0 and more than 1.0 will be linear + float d0, d1; + my_compute_lut_float(contrast, brightness, lut, d0, d1); + + auto getLutValue = [&](float val) { + if (val < 0.f) + return lut[0] + d0 * val; + else if (val >= 1.f) + return lut[TPixel64::maxChannelValue] + d1 * (val - 1.f); + float v = val * float(TPixel64::maxChannelValue); + int id = (int)tfloor(v); + float ratio = v - float(id); + return lut[id] * (1.f - ratio) + lut[id + 1] * ratio; + }; + + int j; + ras->lock(); + for (j = 0; j < ly; j++) { + TPixelF *pix = ras->pixels(j); + TPixelF *endPix = pix + lx; + while (pix < endPix) { + if (pix->m) { + *pix = depremultiply(*pix); + pix->r = getLutValue(pix->r); + pix->g = getLutValue(pix->g); + pix->b = getLutValue(pix->b); + pix->m = pix->m; + *pix = premultiply(*pix); + } + pix++; + } + } + ras->unlock(); +} + void Bright_ContFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { if (!m_input.isConnected()) return; @@ -125,15 +218,16 @@ void Bright_ContFx::doCompute(TTile &tile, double frame, if (contrast > 1) contrast = 1; if (contrast < -1) contrast = -1; TRaster32P raster32 = tile.getRaster(); + TRaster64P raster64 = tile.getRaster(); + TRasterFP rasterF = tile.getRaster(); if (raster32) doBrightnessContrast(raster32, contrast, brightness); - else { - TRaster64P raster64 = tile.getRaster(); - if (raster64) - doBrightnessContrast(raster64, contrast, brightness); - else - throw TException("Brightness&Contrast: unsupported Pixel Type"); - } + else if (raster64) + doBrightnessContrast(raster64, contrast, brightness); + else if (rasterF) + doBrightnessContrastFloat(rasterF, contrast, brightness); + else + throw TException("Brightness&Contrast: unsupported Pixel Type"); } FX_PLUGIN_IDENTIFIER(Bright_ContFx, "brightContFx") diff --git a/toonz/sources/stdfx/channelmixerfx.cpp b/toonz/sources/stdfx/channelmixerfx.cpp index 29a52f6..e9aabf1 100644 --- a/toonz/sources/stdfx/channelmixerfx.cpp +++ b/toonz/sources/stdfx/channelmixerfx.cpp @@ -2,7 +2,7 @@ #include "stdfx.h" #include "tfxparam.h" -//#include "trop.h" +// #include "trop.h" #include #include "tpixelutils.h" #include "globalcontrollablefx.h" @@ -83,6 +83,8 @@ public: m_m_g->setValueRange(0, 1); m_m_b->setValueRange(0, 1); m_m_m->setValueRange(0, 1); + + enableComputeInFloat(true); } ~ChannelMixerFx(){}; @@ -146,6 +148,34 @@ void doChannelMixer(TRasterPT ras, double r_r, double r_g, double r_b, } ras->unlock(); } + +void doChannelMixer_Float(TRasterFP ras, double r_r, double r_g, double r_b, + double r_m, double g_r, double g_g, double g_b, + double g_m, double b_r, double b_g, double b_b, + double b_m, double m_r, double m_g, double m_b, + double m_m) { + int j; + ras->lock(); + for (j = 0; j < ras->getLy(); j++) { + TPixelF *pix = ras->pixels(j); + TPixelF *endPix = pix + ras->getLx(); + while (pix < endPix) { + depremultiply(*pix); + double red = pix->r * r_r + pix->g * g_r + pix->b * b_r + pix->m * m_r; + double green = pix->r * r_g + pix->g * g_g + pix->b * b_g + pix->m * m_g; + double blue = pix->r * r_b + pix->g * g_b + pix->b * b_b + pix->m * m_b; + double matte = pix->r * r_m + pix->g * g_m + pix->b * b_m + pix->m * m_m; + pix->r = (float)red; + pix->g = (float)green; + pix->b = (float)blue; + pix->m = (float)matte; + *pix = premultiply(*pix); + pix++; + } + } + ras->unlock(); +} + //------------------------------------------------------------------------------ void ChannelMixerFx::doCompute(TTile &tile, double frame, @@ -172,19 +202,22 @@ void ChannelMixerFx::doCompute(TTile &tile, double frame, double m_m = m_m_m->getValue(frame); TRaster32P raster32 = tile.getRaster(); + TRaster64P raster64 = tile.getRaster(); + TRasterFP rasterF = tile.getRaster(); if (raster32) doChannelMixer(raster32, r_r, r_g, r_b, r_m, g_r, g_g, g_b, g_m, b_r, b_g, b_b, b_m, m_r, m_g, m_b, m_m); - else { - TRaster64P raster64 = tile.getRaster(); - if (raster64) - doChannelMixer(raster64, r_r, r_g, r_b, r_m, g_r, g_g, - g_b, g_m, b_r, b_g, b_b, b_m, m_r, m_g, - m_b, m_m); - else - throw TException("Brightness&Contrast: unsupported Pixel Type"); - } + else if (raster64) + doChannelMixer(raster64, r_r, r_g, r_b, r_m, g_r, g_g, + g_b, g_m, b_r, b_g, b_b, b_m, m_r, m_g, + m_b, m_m); + else if (rasterF) + doChannelMixer_Float(rasterF, r_r, r_g, r_b, r_m, g_r, g_g, g_b, g_m, b_r, + b_g, b_b, b_m, m_r, m_g, m_b, m_m); + + else + throw TException("Brightness&Contrast: unsupported Pixel Type"); } FX_PLUGIN_IDENTIFIER(ChannelMixerFx, "channelMixerFx") diff --git a/toonz/sources/stdfx/gammafx.cpp b/toonz/sources/stdfx/gammafx.cpp index 4849130..83304d1 100644 --- a/toonz/sources/stdfx/gammafx.cpp +++ b/toonz/sources/stdfx/gammafx.cpp @@ -16,6 +16,8 @@ public: addInputPort("Source", m_input); // m_gamma->setValueRange(0, std::numeric_limits::max()); m_gamma->setValueRange(0.0, 200.0); + + enableComputeInFloat(true); } ~GammaFx(){}; diff --git a/toonz/sources/stdfx/gradients.cpp b/toonz/sources/stdfx/gradients.cpp index 89d516a..00d1e1a 100644 --- a/toonz/sources/stdfx/gradients.cpp +++ b/toonz/sources/stdfx/gradients.cpp @@ -76,6 +76,9 @@ void multiRadial(const TRasterP &ras, TPointD posTrasf, else if ((TRaster64P)ras) doComputeRadialT(ras, posTrasf, colors->getValue64(frame), period, count, cycle, aff, inner, type); + else if ((TRasterFP)ras) + doComputeRadialT(ras, posTrasf, colors->getValueF(frame), period, + count, cycle, aff, inner, type); else throw TException("MultiRadialGradientFx: unsupported Pixel Type"); } @@ -150,6 +153,9 @@ void multiLinear(const TRasterP &ras, TPointD posTrasf, else if ((TRaster64P)ras) doComputeLinearT(ras, posTrasf, colors->getValue64(frame), period, count, amplitude, freq, phase, cycle, aff, type); + else if ((TRasterFP)ras) + doComputeLinearT(ras, posTrasf, colors->getValueF(frame), period, + count, amplitude, freq, phase, cycle, aff, type); else throw TException("MultiLinearGradientFx: unsupported Pixel Type"); } diff --git a/toonz/sources/stdfx/hsvkeyfx.cpp b/toonz/sources/stdfx/hsvkeyfx.cpp index 56533c9..3fec09e 100644 --- a/toonz/sources/stdfx/hsvkeyfx.cpp +++ b/toonz/sources/stdfx/hsvkeyfx.cpp @@ -1,6 +1,6 @@ -//#include "trop.h" +// #include "trop.h" #include "tfxparam.h" #include #include "stdfx.h" @@ -42,6 +42,7 @@ public: m_srange->setValueRange(0.0, 1.0); m_vrange->setValueRange(0.0, 1.0); addInputPort("Source", m_input); + enableComputeInFloat(true); } ~HSVKeyFx(){}; @@ -110,17 +111,17 @@ void HSVKeyFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { double highV = std::min(1.0, v_ref + v_range); TRaster32P raster32 = tile.getRaster(); + TRaster64P raster64 = tile.getRaster(); + TRasterFP rasterF = tile.getRaster(); if (raster32) doHSVKey(raster32, lowH, highH, lowS, highS, lowV, highV, gender); - else { - TRaster64P raster64 = tile.getRaster(); - if (raster64) - doHSVKey(raster64, lowH, highH, lowS, highS, lowV, highV, - gender); - else - throw TException("HSVKey: unsupported Pixel Type"); - } + else if (raster64) + doHSVKey(raster64, lowH, highH, lowS, highS, lowV, highV, gender); + else if (rasterF) + doHSVKey(rasterF, lowH, highH, lowS, highS, lowV, highV, gender); + else + throw TException("HSVKey: unsupported Pixel Type"); } //------------------------------------------------------------------ diff --git a/toonz/sources/stdfx/igs_color_blend.cpp b/toonz/sources/stdfx/igs_color_blend.cpp index 4628600..acde365 100644 --- a/toonz/sources/stdfx/igs_color_blend.cpp +++ b/toonz/sources/stdfx/igs_color_blend.cpp @@ -156,7 +156,9 @@ double lighten_ch_(const double dn, const double dn_a, const double up, : dn_add_up_ch_(dn, dn_a, up, up_opacity); } double screen_(const double dn, const double up) { - return 1.0 - (1.0 - dn) * (1.0 - up); + return (dn <= 1.0 && up <= 1.0) ? 1.0 - (1.0 - dn) * (1.0 - up) + : (up > dn) ? up + : dn; } double color_dodge_(const double dn, const double up) { return (1.0 <= up) ? 1.0 : clamp_min1_ch_(dn / (1.0 - up)); diff --git a/toonz/sources/stdfx/igs_color_rgb_hls.cpp b/toonz/sources/stdfx/igs_color_rgb_hls.cpp index 6880350..a2a005b 100644 --- a/toonz/sources/stdfx/igs_color_rgb_hls.cpp +++ b/toonz/sources/stdfx/igs_color_rgb_hls.cpp @@ -4,8 +4,9 @@ void igs::color::rgb_to_hls(const double red, // 0.0...1.0 const double blu, // 0.0...1.0 double &hue, /* 0.0...360.0 hue(色相) */ double &lig, /* 0.0...1.0 lightness(明度) */ - double &sat /* 0.0...1.0 saturation(彩度) */ - ) { + double &sat, /* 0.0...1.0 saturation(彩度) */ + bool cylindrical // either cylindrical or conical +) { const double maxi = (red < gre) ? ((gre < blu) ? blu : gre) : ((red < blu) ? blu : red); const double mini = @@ -17,11 +18,14 @@ void igs::color::rgb_to_hls(const double red, // 0.0...1.0 sat = 0.0; /* saturation(彩度)はゼロ */ hue = 0.0; /* hue(色相)は意味を持たない */ } else { /* 色のあるとき */ - if (lig <= 0.5) { - sat = (maxi - mini) / (maxi + mini); - } else { - sat = (maxi - mini) / (2.0 - (maxi + mini)); - } + if (cylindrical) { + if (lig <= 0.5) { + sat = (maxi - mini) / (maxi + mini); + } else { + sat = (maxi - mini) / (2.0 - (maxi + mini)); + } + } else + sat = maxi - mini; /* 色相(Hue) */ const double rmid = (maxi - red) / (maxi - mini); @@ -70,23 +74,28 @@ double hls2rgb_calc(const double m1, const double m2, const double hue) { } return m1; /* 240 ... 360 */ } -} -void igs::color::hls_to_rgb( - const double hue, /* 0.0...360.0 hue(色相) */ - const double lig, /* 0.0...1.0 lightness(明度) */ - const double sat, /* 0.0...1.0 saturation(彩度) */ - double &red, /* 0.0...1.0 */ - double &gre, /* 0.0...1.0 */ - double &blu /* 0.0...1.0 */ - ) { +} // namespace +void igs::color::hls_to_rgb(const double hue, /* 0.0...360.0 hue(色相) */ + const double lig, /* 0.0...1.0 lightness(明度) */ + const double sat, /* 0.0...1.0 saturation(彩度) */ + double &red, /* 0.0...1.0 */ + double &gre, /* 0.0...1.0 */ + double &blu, /* 0.0...1.0 */ + bool cylindrical // either cylindrical or conical +) { if (0.0 == sat) { /* 白黒で色がない */ red = gre = blu = lig; } else { /* 色のあるとき */ - const double m2 = - (lig <= 0.5) ? (lig * (1.0 + sat)) : (lig + sat - lig * sat); - const double m1 = 2.0 * lig - m2; - red = hls2rgb_calc(m1, m2, hue + 120.0); - gre = hls2rgb_calc(m1, m2, hue); - blu = hls2rgb_calc(m1, m2, hue - 120.0); + double m2, m1; + if (cylindrical) { + m2 = (lig <= 0.5) ? (lig * (1.0 + sat)) : (lig + sat - lig * sat); + m1 = 2.0 * lig - m2; + } else { + m2 = lig + sat * 0.5; + m1 = lig - sat * 0.5; + } + red = hls2rgb_calc(m1, m2, hue + 120.0); + gre = hls2rgb_calc(m1, m2, hue); + blu = hls2rgb_calc(m1, m2, hue - 120.0); } } diff --git a/toonz/sources/stdfx/igs_color_rgb_hls.h b/toonz/sources/stdfx/igs_color_rgb_hls.h index a251494..9d22ddc 100644 --- a/toonz/sources/stdfx/igs_color_rgb_hls.h +++ b/toonz/sources/stdfx/igs_color_rgb_hls.h @@ -7,10 +7,10 @@ namespace igs { namespace color { // Use add_hls & noise_hlsa void rgb_to_hls(const double red, const double gre, const double blu, - double &hue, double &lig, double &sat); + double &hue, double &lig, double &sat, bool cylindrical = true); void hls_to_rgb(const double hue, const double lig, const double sat, - double &red, double &gre, double &blu); -} -} + double &red, double &gre, double &blu, bool cylindrical = true); +} // namespace color +} // namespace igs #endif /* !igs_color_rgb_hls_h */ diff --git a/toonz/sources/stdfx/igs_color_rgb_hsv.cpp b/toonz/sources/stdfx/igs_color_rgb_hsv.cpp index 8b0bad8..6cd2f01 100644 --- a/toonz/sources/stdfx/igs_color_rgb_hsv.cpp +++ b/toonz/sources/stdfx/igs_color_rgb_hsv.cpp @@ -1,23 +1,26 @@ #include "igs_color_rgb_hsv.h" +#include void igs::color::rgb_to_hsv(const double red, // 0.0...1.0 const double gre, // 0.0...1.0 const double blu, // 0.0...1.0 double &hue, /* 0.0...360.0 hue(色相) */ double &sat, /* 0.0...1.0 saturation(彩度) */ double &val /* 0.0...1.0 value(明度) */ - ) { +) { const double maxi = (red < gre) ? ((gre < blu) ? blu : gre) : ((red < blu) ? blu : red); const double mini = (gre < red) ? ((blu < gre) ? blu : gre) : ((blu < red) ? blu : red); - val = maxi; /* value(明度) */ + bool invertValue = std::abs(mini) > std::abs(maxi); + + val = (invertValue) ? mini : maxi; /* value(明度) */ if (maxi == mini) { /* RGB各色に差がない(白黒の)とき */ sat = 0.0; /* saturation(彩度)はゼロ */ hue = 0.0; /* hue(色相)は意味を持たない */ } else { /* 色のあるとき */ - sat = (maxi - mini) / maxi; + sat = (invertValue) ? (mini - maxi) / mini : (maxi - mini) / maxi; const double maxmin = maxi - mini; @@ -40,20 +43,20 @@ void igs::color::rgb_to_hsv(const double red, // 0.0...1.0 -1 0 1 2 3 4 5 */ hue *= 60.0; /* -60 ... 300 */ + if (invertValue) hue -= 180.0; if (hue < 0.0) { hue += 360.0; } } } -#include /* floor() */ -void igs::color::hsv_to_rgb( - const double hue, /* 0.0...360.0 hue(色相) */ - const double sat, /* 0.0...1.0 saturation(彩度) */ - const double val, /* 0.0...1.0 value(明度) */ - double &red, /* 0.0...1.0 */ - double &gre, /* 0.0...1.0 */ - double &blu /* 0.0...1.0 */ - ) { +#include /* floor() */ +void igs::color::hsv_to_rgb(const double hue, /* 0.0...360.0 hue(色相) */ + const double sat, /* 0.0...1.0 saturation(彩度) */ + const double val, /* 0.0...1.0 value(明度) */ + double &red, /* 0.0...1.0 */ + double &gre, /* 0.0...1.0 */ + double &blu /* 0.0...1.0 */ +) { if (0.0 == sat) { /* 白黒で色がない */ red = gre = blu = val; } else { /* 色のあるとき */ diff --git a/toonz/sources/stdfx/igs_density.cpp b/toonz/sources/stdfx/igs_density.cpp index c5ddd90..7d3bfc0 100644 --- a/toonz/sources/stdfx/igs_density.cpp +++ b/toonz/sources/stdfx/igs_density.cpp @@ -1,131 +1,79 @@ #include "igs_density.h" - +#include namespace { -double accum_by_trans_( - const double src_value /* 元値(R,G,B) */ - , - const double transparent /* src_valueの透明度(0...1) */ - , - const int integer_part /* 濃度値の整数部分(0...) */ - , - const double fractional_part /* 濃度値の少数部分(0...1) */ - ) { - double accumulation = src_value; +float accum_by_trans_(const float src_value /* 元値(R,G,B) */ + , + const float transparent /* src_valueの透明度(0...1) */ + , + const int integer_part /* 濃度値の整数部分(0...) */ + , + const double fractional_part /* 濃度値の少数部分(0...1) */ +) { + float accumulation = src_value; if (1 <= integer_part) { for (int ii = 1; ii < integer_part; ++ii) { accumulation = accumulation * transparent + src_value; } - if (0.0 < fractional_part) { + if (0.f < fractional_part) { accumulation += ((accumulation * transparent + src_value) - accumulation) * fractional_part; } } else { /* 整数部分がゼロ以下のとき */ - if (0.0 < fractional_part) { + if (0.f < fractional_part) { accumulation *= fractional_part; } else { /* 少数部分もゼロ以下のとき */ - accumulation = 0.0; + accumulation = 0.f; } } - return (1.0 < accumulation) ? 1.0 - : ((accumulation < 0.0) ? 0.0 : accumulation); -} + return (1.f < accumulation) ? 1.f + : ((accumulation < 0.f) ? 0.f : accumulation); } +} // namespace #include /* std::numeric_limits */ #include "igs_ifx_common.h" /* igs::image::rgba */ namespace { -template -void change_template_( - IT *image_array, const int height, const int width, const int channels - - , - const RT *ref /* 求める画像と同じ高、幅、ch数 */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const double density) { +void change_(float *image_array, const int height, const int width, + const int channels, const float *ref, const double density) { const int integer_part = (int)density; const double fractional_part = density - (int)density; - const double maxi = std::numeric_limits::max(); using namespace igs::image::rgba; const int pixsize = height * width; - const int r_max = std::numeric_limits::max(); for (int ii = 0; ii < pixsize; ++ii, image_array += channels) { - const double rr1 = (double)(image_array[red]) / maxi; - const double gg1 = (double)(image_array[gre]) / maxi; - const double bb1 = (double)(image_array[blu]) / maxi; - const double aa1 = (double)(image_array[alp]) / maxi; + const float rr1 = image_array[red]; + const float gg1 = image_array[gre]; + const float bb1 = image_array[blu]; + const float aa1 = image_array[alp]; - double rr2 = accum_by_trans_(rr1, 1.0 - aa1, integer_part, fractional_part); - double gg2 = accum_by_trans_(gg1, 1.0 - aa1, integer_part, fractional_part); - double bb2 = accum_by_trans_(bb1, 1.0 - aa1, integer_part, fractional_part); - double aa2 = accum_by_trans_(aa1, 1.0 - aa1, integer_part, fractional_part); + float rr2 = accum_by_trans_(rr1, 1.0 - aa1, integer_part, fractional_part); + float gg2 = accum_by_trans_(gg1, 1.0 - aa1, integer_part, fractional_part); + float bb2 = accum_by_trans_(bb1, 1.0 - aa1, integer_part, fractional_part); + float aa2 = accum_by_trans_(aa1, 1.0 - aa1, integer_part, fractional_part); /* 参照画像あればピクセル単位の画像変化 */ - if (ref != 0) { - const double refv = igs::color::ref_value(ref, channels, r_max, ref_mode); - - ref += channels; /* continue;の前に行うこと */ - - rr2 = (rr2 - rr1) * refv + rr1; - gg2 = (gg2 - gg1) * refv + gg1; - bb2 = (bb2 - bb1) * refv + bb1; - aa2 = (aa2 - aa1) * refv + aa1; + if (ref != nullptr) { + rr2 = (rr2 - rr1) * (*ref) + rr1; + gg2 = (gg2 - gg1) * (*ref) + gg1; + bb2 = (bb2 - bb1) * (*ref) + bb1; + aa2 = (aa2 - aa1) * (*ref) + aa1; + ref++; } - image_array[red] = static_cast(rr2 * (maxi + 0.999999)); - image_array[gre] = static_cast(gg2 * (maxi + 0.999999)); - image_array[blu] = static_cast(bb2 * (maxi + 0.999999)); - image_array[alp] = static_cast(aa2 * (maxi + 0.999999)); + image_array[red] = rr2; + image_array[gre] = gg2; + image_array[blu] = bb2; + image_array[alp] = aa2; } } -} +} // namespace #include /* std::domain_error(-) */ -void igs::density::change( - unsigned char *image_array /* RGBAでなければならない */ - , - const int height, const int width, - const int channels /* 4(=RGBAでなければならない) */ - , - const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、ch数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const double density) { +void igs::density::change(float *image_array, const int height, const int width, + const int channels, /* 4(=RGBAでなければならない) */ + const float *ref, const double density) { if (igs::image::rgba::siz != channels) { throw std::domain_error("Bad channels,Not rgba"); } - if ((std::numeric_limits::digits == bits) && - ((std::numeric_limits::digits == ref_bits) || - (0 == ref_bits))) { - change_template_(image_array, height, width, channels, ref, ref_mode, - density); - } else if ((std::numeric_limits::digits == bits) && - ((std::numeric_limits::digits == ref_bits) || - (0 == ref_bits))) { - change_template_(reinterpret_cast(image_array), height, - width, channels, ref, ref_mode, density); - } else if ((std::numeric_limits::digits == bits) && - (std::numeric_limits::digits == ref_bits)) { - change_template_(reinterpret_cast(image_array), height, - width, channels, - reinterpret_cast(ref), ref_mode, - density); - } else if ((std::numeric_limits::digits == bits) && - (std::numeric_limits::digits == ref_bits)) { - change_template_(image_array, height, width, channels, - reinterpret_cast(ref), ref_mode, - density); - } else { - throw std::domain_error("Bad bits,Not uchar/ushort"); - } + change_(image_array, height, width, channels, ref, density); } diff --git a/toonz/sources/stdfx/igs_density.h b/toonz/sources/stdfx/igs_density.h index 820947b..c206d4c 100644 --- a/toonz/sources/stdfx/igs_density.h +++ b/toonz/sources/stdfx/igs_density.h @@ -10,23 +10,10 @@ namespace igs { namespace density { IGS_DENSITY_EXPORT void change( - unsigned char *image_array /* RGBAでなければならない */ - , - const int height, const int width, - const int channels /* 4(=RGBAでなければならない) */ - , - const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、ch数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const double density = 1.0); -} + float *image_array, const int height, const int width, + const int channels, /* 4(=RGBAでなければならない) */ + const float *ref, const double density = 1.0); } +} // namespace igs #endif /* !igs_density_h */ diff --git a/toonz/sources/stdfx/igs_gaussian_blur.cpp b/toonz/sources/stdfx/igs_gaussian_blur.cpp index 15bede5..f8c6b1b 100644 --- a/toonz/sources/stdfx/igs_gaussian_blur.cpp +++ b/toonz/sources/stdfx/igs_gaussian_blur.cpp @@ -13,6 +13,7 @@ const int diameter_from_radius_(const int radius) { /* テーブルの半径サイズ(=中心位置)からテーブルの大きさを決める */ return radius * 2 + 1; } +#if 0 template void blur_1st_hori_( const double **in_plane_with_margin // &(std::vector).at(0) @@ -78,6 +79,66 @@ accum += in_plane_with_margin[yo][xi] * brush_sequence[xb]; } } } +#endif + +void blur_1st_hori_( + const double** in_plane_with_margin // &(std::vector).at(0) + , + const int height_with_margin, const int width_with_margin, + double* brush_sequence // &(std::vector).at(0) + , + const int int_radius, + double** out_plane_with_margin // &(std::vector).at(0) + + /* 参照画像用情報(no margin) */ + , + const float* ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ + , + const double real_radius, const double sigma) { + const int brush_diameter = diameter_from_radius_(int_radius); + const int width_no_margin = width_with_margin - int_radius * 2; + // const int r_max = std::numeric_limits::max(); + const float* ref_vert = ref; + const float* ref_hori = ref; + double before_real_radius = -1.0; + + /* 縦方向 */ + for (int yo = 0; yo < height_with_margin; ++yo) { + if (ref != nullptr) { + if (int_radius < yo && yo < (height_with_margin - int_radius)) { + ref_vert += width_no_margin; + } + ref_hori = ref_vert; + } + + /* 横方向 */ + for (int xx = 0, xo = int_radius; xx < width_no_margin; ++xx, ++xo) { + if (ref != 0) { + const double read_r = real_radius * (*ref_hori); + ref_hori++; + + if (read_r != before_real_radius) { + gauss_distribution_1d_(brush_sequence, brush_diameter, + igs::gaussian_blur_hv::int_radius(read_r), + read_r, sigma); + before_real_radius = read_r; + } + } + /* ガウス分布で横blur */ + double accum = 0; + + const double* bru_seq = brush_sequence; + int bru_dia = brush_diameter; + const double* inn_pla = &in_plane_with_margin[yo][xx]; + while (0 < bru_dia--) { + accum += (*inn_pla++) * (*bru_seq++); + } + out_plane_with_margin[yo][xo] = accum; + } + } +} + +#if 0 template void blur_2nd_vert_( const double **in_plane_with_margin // &(std::vector).at(0) @@ -145,6 +206,67 @@ accum += in_plane_with_margin[yi][xo] * brush_sequence[yb]; } } } +#endif + +void blur_2nd_vert_( + const double** in_plane_with_margin // &(std::vector).at(0) + , + const int height_with_margin, const int width_with_margin, + double* brush_sequence // &(std::vector).at(0) + , + const int int_radius, + double** out_plane_with_margin // &(std::vector).at(0) + + /* 参照画像用情報(no margin) */ + , + const float* ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ + , + const double real_radius, const double sigma) { + const int brush_diameter = diameter_from_radius_(int_radius); + const int height_no_margin = height_with_margin - int_radius * 2; + const int width_no_margin = width_with_margin - int_radius * 2; + // const int r_max = std::numeric_limits::max(); + const float* ref_vert = ref; + const float* ref_hori = ref; + double before_real_radius = -1.0; + + /* 左右マージン部分はもう処理しなくていい */ + + /* 横方向 */ + for (int xx = 0, xo = int_radius; xx < width_no_margin; ++xx, ++xo) { + if (ref != nullptr) { + ref_hori++; + ref_vert = ref_hori; + } + + /* 縦方向 */ + for (int yy = 0, yo = int_radius; yy < height_no_margin; ++yy, ++yo) { + if (ref != 0) { + const double read_r = real_radius * (*ref_vert); + ref_vert += width_no_margin; + + if (read_r != before_real_radius) { + gauss_distribution_1d_(brush_sequence, brush_diameter, + igs::gaussian_blur_hv::int_radius(read_r), + read_r, sigma); + before_real_radius = read_r; + } + } + /* ガウス分布で横blur */ + double accum = 0; + const double* bru_seq = brush_sequence; + int bru_dia = brush_diameter; + const double* inn_pla = &in_plane_with_margin[yy][xo]; + while (0 < bru_dia--) { + accum += (*inn_pla) * (*bru_seq++); + inn_pla += width_with_margin; + } + out_plane_with_margin[yo][xo] = accum; + } + } +} + +#if 0 template void get_(const T *in, const int height, const int width, const int channels, const int current_ch, double **out // &(std::vector).at(0) @@ -158,6 +280,22 @@ void get_(const T *in, const int height, const int width, const int channels, } } } +#endif + +void get_(const float* in, const int height, const int width, + const int channels, const int current_ch, + double** out // &(std::vector).at(0) +) { + in += current_ch; + for (int yy = 0; yy < height; ++yy) { + for (int xx = 0; xx < width; ++xx) { + out[yy][xx] = static_cast(*in); + in += channels; + } + } +} + +#if 0 template void put_margin_( const double **in_with_margin // &(std::vector).at(0) @@ -175,6 +313,23 @@ void put_margin_( } } } +#endif + +void put_margin_( + const double** in_with_margin, // &(std::vector).at(0) + const int height_with_margin, const int width_with_margin, + const int channels, const int current_ch, const int margin, + float* out_no_margin) { + out_no_margin += current_ch; + for (int yy = margin; yy < (height_with_margin - margin); ++yy) { + for (int xx = margin; xx < (width_with_margin - margin); ++xx) { + *out_no_margin = in_with_margin[yy][xx]; + out_no_margin += channels; + } + } +} + +#if 0 template bool diff_between_channel_(const T *in, const int height, const int width, const int channels, const int ch1, const int ch2) { @@ -191,6 +346,21 @@ bool diff_between_channel_(const T *in, const int height, const int width, } return false; } +#endif +bool diff_between_channel_(const float* in, const int height, const int width, + const int channels, const int ch1, const int ch2) { + for (int yy = 0; yy < height; ++yy) { + for (int xx = 0; xx < width; ++xx) { + if (in[ch1] != in[ch2]) { + return true; + } + in += channels; + } + } + return false; +} + +#if 0 template void convert_hv_(const IT *in_with_margin, IT *out_no_margin, const int height_with_margin, const int width_with_margin, @@ -238,7 +408,44 @@ void convert_hv_(const IT *in_with_margin, IT *out_no_margin, width_with_margin, channels, cc, int_radius, out_no_margin); } } +#endif + +void convert_hv_( + const float* in_with_margin, float* out_no_margin, + const int height_with_margin, const int width_with_margin, + const int channels, + double* filter, // (double *)(&filter_buf.at(0)) + const int int_radius, + double** buffer_inn, // &(std::vector).at(0) + double** buffer_out, // &(std::vector).at(0) + /* 参照画像用情報(no margin) */ + const float* ref, /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ + const double real_radius, const double sigma) { + bool diff_sw = true; /* 1番目の画像は処理する */ + for (int cc = 0; cc < channels; ++cc) { + if (0 < cc) { /* 2番目のチャンネル以後 */ + /* 1つ前のチャンネルと違いを調べる */ + diff_sw = diff_between_channel_(in_with_margin, height_with_margin, + width_with_margin, channels, cc - 1, cc); + } + /* 一つ前と同じ画像なら処理せず使い回して高速化する */ + if (diff_sw) { + get_(in_with_margin, height_with_margin, width_with_margin, channels, cc, + buffer_inn); + blur_1st_hori_((const double**)(buffer_inn), height_with_margin, + width_with_margin, filter, int_radius, buffer_out, ref, + real_radius, sigma); + blur_2nd_vert_((const double**)(buffer_out), height_with_margin, + width_with_margin, filter, int_radius, buffer_inn, ref, + real_radius, sigma); + } + + put_margin_((const double**)(buffer_inn), height_with_margin, + width_with_margin, channels, cc, int_radius, out_no_margin); + } } + +} // namespace const int igs::gaussian_blur_hv::int_radius(const double real_radius) { /* ぼかしの半径から、pixelサイズ半径(=中心位置=margin)を決める */ return static_cast(ceil(real_radius)); @@ -253,31 +460,18 @@ const int igs::gaussian_blur_hv::buffer_bytes(const int height_with_margin, } void igs::gaussian_blur_hv::convert( /* 入出力画像 */ - const void *in_with_margin, void *out_no_margin - - , + const float* in_with_margin, float* out_no_margin, const int height_with_margin, const int width_with_margin, - const int channels, const int bits - + const int channels, /* Pixel毎に効果の強弱 */ - , - const unsigned char *ref /* 求める画像(out)と同じ高、幅、ch数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - + const float* ref, /* 求める画像(out)と同じ高、幅、ch数 */ /* 計算バッファ */ - , - void *buffer, - int buffer_bytes // Must be igs::gaussian_blur_hv::buffer_bytes(-) - + void* buffer, + int buffer_bytes, // Must be igs::gaussian_blur_hv::buffer_bytes(-) /* Action Geometry */ - , - const int int_radius // =margin - , + const int int_radius, // =margin const double real_radius, const double sigma //= 0.25 - ) { +) { /* 引数チェック */ if (real_radius <= 0.0) { return; @@ -285,18 +479,18 @@ void igs::gaussian_blur_hv::convert( if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } /* 変数の設定 */ const int int_diameter = diameter_from_radius_(int_radius); - double *double_buffer = static_cast(buffer); + double* double_buffer = static_cast(buffer); /* メモリバッファの設定 */ std::vector filter_buf(int_diameter); - std::vector in_plane_with_margin_dp(height_with_margin); + std::vector in_plane_with_margin_dp(height_with_margin); for (int yy = 0; yy < height_with_margin; ++yy) { in_plane_with_margin_dp.at(yy) = double_buffer; double_buffer += width_with_margin; @@ -307,7 +501,7 @@ void igs::gaussian_blur_hv::convert( } } - std::vector out_plane_with_margin_dp(height_with_margin); + std::vector out_plane_with_margin_dp(height_with_margin); for (int yy = 0; yy < height_with_margin; ++yy) { out_plane_with_margin_dp.at(yy) = double_buffer; double_buffer += width_with_margin; @@ -322,7 +516,11 @@ void igs::gaussian_blur_hv::convert( gauss_distribution_1d_(&filter_buf.at(0), int_diameter, int_radius, real_radius, sigma); - /* 処理 */ + convert_hv_(in_with_margin, out_no_margin, height_with_margin, + width_with_margin, channels, &filter_buf.at(0), int_radius, + &in_plane_with_margin_dp.at(0), &out_plane_with_margin_dp.at(0), + ref, real_radius, sigma); + /* if ((std::numeric_limits::digits == bits) && ((std::numeric_limits::digits == ref_bits) || (0 == ref_bits))) { @@ -369,4 +567,5 @@ void igs::gaussian_blur_hv::convert( } else { throw std::domain_error("Bad bits,Not uchar/ushort"); } + */ } diff --git a/toonz/sources/stdfx/igs_gaussian_blur.h b/toonz/sources/stdfx/igs_gaussian_blur.h index 0f8fdb7..c77ae77 100644 --- a/toonz/sources/stdfx/igs_gaussian_blur.h +++ b/toonz/sources/stdfx/igs_gaussian_blur.h @@ -15,33 +15,18 @@ IGS_GAUSSIAN_BLUR_HV_EXPORT const int buffer_bytes(const int height_with_margin, const int int_radius); IGS_GAUSSIAN_BLUR_HV_EXPORT void convert( /* 入出力画像 */ - const void *in_with_margin, void *out_no_margin - - , + const float* in_with_margin, float* out_no_margin, const int height_with_margin, const int width_with_margin, - const int channels, const int bits - + const int channels, /* Pixel毎に効果の強弱 */ - , - const unsigned char *ref /* 求める画像(out)と同じ高、幅、ch数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - + const float* ref, /* 求める画像(out)と同じ高、幅、ch数 */ /* 計算バッファ */ - , - void *buffer, int buffer_bytes - // Must be igs::gaussian_blur_hv::buffer_bytes(-) - + void* buffer, + int buffer_bytes, // Must be igs::gaussian_blur_hv::buffer_bytes(-) /* Action Geometry */ - , - const int int_radius // margin - // Must be igs::gaussian_blur_hv::int_radius(real_radius) - - , + const int int_radius, // =margin const double real_radius, const double sigma = 0.25); -} -} +} // namespace gaussian_blur_hv +} // namespace igs #endif /* !igs_gaussian_blur_hv_h */ diff --git a/toonz/sources/stdfx/igs_hls_add.cpp b/toonz/sources/stdfx/igs_hls_add.cpp index 3ef717a..f829661 100644 --- a/toonz/sources/stdfx/igs_hls_add.cpp +++ b/toonz/sources/stdfx/igs_hls_add.cpp @@ -9,9 +9,9 @@ void pixel_rgba_(const double red_in, const double gre_in, const double blu_in, , const double sat_noise, const double alp_noise, double &red_out, double &gre_out, double &blu_out, - double &alp_out) { + double &alp_out, const bool cylindrical = true) { double hue, lig, sat, alp; - igs::color::rgb_to_hls(red_in, gre_in, blu_in, hue, lig, sat); + igs::color::rgb_to_hls(red_in, gre_in, blu_in, hue, lig, sat, cylindrical); alp = alp_in; if (0.0 != hue_noise) { @@ -25,40 +25,37 @@ void pixel_rgba_(const double red_in, const double gre_in, const double blu_in, } if (0.0 != lig_noise) { lig += lig_noise; - lig = (lig < 0.0) ? 0.0 : ((1.0 < lig) ? 1.0 : lig); + // lig = (lig < 0.0) ? 0.0 : ((1.0 < lig) ? 1.0 : lig); } if (0.0 != sat_noise) { sat += sat_noise; - sat = (sat < 0.0) ? 0.0 : ((1.0 < sat) ? 1.0 : sat); + sat = (sat < 0.0) ? 0.0 : sat; + // sat = (sat < 0.0) ? 0.0 : ((1.0 < sat) ? 1.0 : sat); } if (0.0 != alp_noise) { alp += alp_noise; alp = (alp < 0.0) ? 0.0 : ((1.0 < alp) ? 1.0 : alp); } - igs::color::hls_to_rgb(hue, lig, sat, red_out, gre_out, blu_out); + igs::color::hls_to_rgb(hue, lig, sat, red_out, gre_out, blu_out, cylindrical); alp_out = alp; } -} +} // namespace //------------------------------------------------------------ namespace { class noise_ref_ { public: - noise_ref_(const unsigned char *array, const int height, const int width, - const int channels, const int bits, const int xoffset, - const int yoffset, const int zz); - double noise(int xx // 0...width-1... - , - int yy // 0...height-1... - ) const; // return range is 0...1 + noise_ref_(const float *array, const int height, const int width, + const int xoffset, const int yoffset, const int zz); + float noise(int xx // 0...width-1... + , + int yy // 0...height-1... + ) const; // return range is 0...1 private: - const unsigned char *array_; + const float *array_; const int height_; const int width_; - const int channels_; - const int bits_; - const double bmax_; const int xoffset_; const int yoffset_; const int zz_; @@ -69,30 +66,22 @@ private: /* 代入演算子を無効化 */ noise_ref_ &operator=(const noise_ref_ &); }; -noise_ref_::noise_ref_(const unsigned char *array, const int height, - const int width, const int channels, const int bits, +noise_ref_::noise_ref_(const float *array, const int height, const int width, const int xoffset, const int yoffset, const int zz) : array_(array) , height_(height) , width_(width) - , channels_(channels) - , bits_(bits) - , bmax_(static_cast((1 << bits) - 1)) , xoffset_(xoffset) , yoffset_(yoffset) , zz_(zz) { if (0 == array) { throw std::domain_error("noise_ref_ no data"); } - if ((zz < 0) || (channels <= zz)) { + if ((zz < 0) || (4 <= zz)) { throw std::domain_error("noise_ref_ bad zz"); } - if ((std::numeric_limits::digits != this->bits_) && - (std::numeric_limits::digits != this->bits_)) { - throw std::domain_error("noise_ref_ bad bits"); - } } -double noise_ref_::noise(int xx, int yy) const { +float noise_ref_::noise(int xx, int yy) const { xx -= this->xoffset_; yy -= this->yoffset_; while (xx < 0) { @@ -108,59 +97,36 @@ double noise_ref_::noise(int xx, int yy) const { yy -= this->height_; } - if (std::numeric_limits::digits == this->bits_) { - return (*(this->array_ + this->channels_ * this->width_ * yy + - this->channels_ * xx + this->zz_)) / - this->bmax_; - } - return (*(reinterpret_cast(this->array_) + - this->channels_ * this->width_ * yy + this->channels_ * xx + - this->zz_)) / - this->bmax_; -} + return (*(this->array_ + 4 * this->width_ * yy + 4 * xx + this->zz_)); } +} // namespace //------------------------------------------------------------ #include "igs_ifx_common.h" /* igs::image::rgba */ #include "igs_hls_add.h" namespace { -/* raster画像にノイズをのせるtemplate */ -template -void change_template_( - IT *image_array, const int height, const int width, const int channels - - , - const noise_ref_ &noi - - , - const RT *ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const double offset, const double hue_scale, const double lig_scale, - const double sat_scale, const double alp_scale - - , - const bool add_blend_sw) { - const int t_max = std::numeric_limits::max(); - const double div_val = static_cast(t_max); - const double mul_val = static_cast(t_max) + 0.999999; - const int r_max = std::numeric_limits::max(); +/* raster画像にノイズをのせる*/ +void change_(float *image_array, const int height, const int width, + const int channels, const noise_ref_ &noi, + const float *ref, /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ + const double offset, const double hue_scale, + const double lig_scale, const double sat_scale, + const double alp_scale, const bool add_blend_sw, + const bool cylindrical = true) { if (igs::image::rgba::siz == channels) { using namespace igs::image::rgba; for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, image_array += channels) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } /* 加算合成で、Alpha値ゼロならRGB値を計算する必要はない */ - if (add_blend_sw && (0 == image_array[alp])) { + if (add_blend_sw && (0.f == image_array[alp])) { continue; } /* 加算合成でなくAlpha合成の時は、 @@ -170,24 +136,22 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ refv *= (noi.noise(xx, yy) - offset); /* マスクSWがON、なら変化をMask */ - if (add_blend_sw && (image_array[alp] < t_max)) { - refv *= static_cast(image_array[alp]) / div_val; + if (add_blend_sw && (image_array[alp] < 1.f)) { + refv *= image_array[alp]; } /* RGBAにHLSAノイズを加える */ double rr, gg, bb, aa; - pixel_rgba_(static_cast(image_array[red]) / div_val, - static_cast(image_array[gre]) / div_val, - static_cast(image_array[blu]) / div_val, - static_cast(image_array[alp]) / div_val, - refv * hue_scale, refv * lig_scale, refv * sat_scale, - refv * alp_scale, rr, gg, bb, aa); + pixel_rgba_(image_array[red], image_array[gre], image_array[blu], + image_array[alp], refv * hue_scale, refv * lig_scale, + refv * sat_scale, refv * alp_scale, rr, gg, bb, aa, + cylindrical); /* 変化後の値を戻す */ - image_array[red] = static_cast(rr * mul_val); - image_array[gre] = static_cast(gg * mul_val); - image_array[blu] = static_cast(bb * mul_val); - image_array[alp] = static_cast(aa * mul_val); + image_array[red] = (float)rr; + image_array[gre] = (float)gg; + image_array[blu] = (float)bb; + image_array[alp] = (float)aa; } } } else if (igs::image::rgb::siz == channels) { @@ -195,12 +159,12 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, image_array += channels) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } /* HLSそれぞれに対するオフセット済ノイズ値 */ @@ -208,28 +172,26 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ /* RGBにHLSノイズを加える */ double rr, gg, bb, aa; - pixel_rgba_(static_cast(image_array[red]) / div_val, - static_cast(image_array[gre]) / div_val, - static_cast(image_array[blu]) / div_val, 1.0, + pixel_rgba_(image_array[red], image_array[gre], image_array[blu], 1.0, refv * hue_scale, refv * lig_scale, refv * sat_scale, 0.0, - rr, gg, bb, aa); + rr, gg, bb, aa, cylindrical); /* 変化後の値を戻す */ - image_array[red] = static_cast(rr * mul_val); - image_array[gre] = static_cast(gg * mul_val); - image_array[blu] = static_cast(bb * mul_val); + image_array[red] = (float)rr; + image_array[gre] = (float)gg; + image_array[blu] = (float)bb; } } } else if (1 == channels) { /* grayscale */ for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, ++image_array) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } /* Lに対するオフセット済ノイズ値 */ @@ -241,40 +203,25 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ } /* GrayscaleにLノイズを加える */ - double lig = - static_cast(image_array[0]) / div_val + refv * lig_scale; - lig = (lig < 0.0) ? 0.0 : ((1.0 < lig) ? 1.0 : lig); + float lig = image_array[0] + refv * lig_scale; + // lig = (lig < 0.f) ? 0.f : ((1.f < lig) ? 1.f : lig); /* 変化後の値を戻す */ - image_array[0] = static_cast(lig * mul_val); + image_array[0] = lig; } } } } -} +} // namespace void igs::hls_add::change( - unsigned char *image_array, const int height, const int width, - const int channels, const int bits - - , - const unsigned char *noi_image_array, const int noi_height, - const int noi_width, const int noi_channels, const int noi_bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , + float *image_array, const int height, const int width, const int channels, + const float *noi_image_array, + const float *ref, /* 求める画像と同じ高、幅、channels数 */ const int xoffset, const int yoffset, const int from_rgba, const double offset, const double hue_scale, const double lig_scale, - const double sat_scale, const double alp_scale - - , - const bool add_blend_sw) { + const double sat_scale, const double alp_scale, const bool add_blend_sw, + const bool cylindrical) { if ((0.0 == hue_scale) && (0.0 == lig_scale) && (0.0 == sat_scale) && (0.0 == alp_scale)) { return; @@ -282,45 +229,43 @@ void igs::hls_add::change( if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } /* ノイズ参照画像を作成する */ - noise_ref_ noi(noi_image_array + noise_ref_ noi(noi_image_array, height, width, xoffset, yoffset, from_rgba); - , - noi_height, noi_width, noi_channels, noi_bits - - , - xoffset, yoffset, from_rgba); + change_(image_array, height, width, channels, noi, ref, offset, hue_scale, + lig_scale, sat_scale, alp_scale, add_blend_sw, cylindrical); /* rgb(a)画像にhls(a)でドットノイズを加える */ - if ((std::numeric_limits::digits == bits) && - ((std::numeric_limits::digits == ref_bits) || - (0 == ref_bits))) { - change_template_(image_array, height, width, channels, noi, ref, ref_mode, - offset, hue_scale, lig_scale, sat_scale, alp_scale, - add_blend_sw); - } else if ((std::numeric_limits::digits == bits) && - ((std::numeric_limits::digits == ref_bits) || - (0 == ref_bits))) { - change_template_(reinterpret_cast(image_array), height, - width, channels, noi, ref, ref_mode, offset, hue_scale, - lig_scale, sat_scale, alp_scale, add_blend_sw); - } else if ((std::numeric_limits::digits == bits) && - (std::numeric_limits::digits == ref_bits)) { - change_template_( - reinterpret_cast(image_array), height, width, - channels, noi, reinterpret_cast(ref), ref_mode, - offset, hue_scale, lig_scale, sat_scale, alp_scale, add_blend_sw); - } else if ((std::numeric_limits::digits == bits) && - (std::numeric_limits::digits == ref_bits)) { - change_template_(image_array, height, width, channels, noi, - reinterpret_cast(ref), ref_mode, - offset, hue_scale, lig_scale, sat_scale, alp_scale, - add_blend_sw); - } else { - throw std::domain_error("Bad bits,Not uchar/ushort"); - } + /* if ((std::numeric_limits::digits == bits) && + ((std::numeric_limits::digits == ref_bits) || + (0 == ref_bits))) { + change_template_(image_array, height, width, channels, noi, ref, ref_mode, + offset, hue_scale, lig_scale, sat_scale, alp_scale, + add_blend_sw); + } else if ((std::numeric_limits::digits == bits) && + ((std::numeric_limits::digits == ref_bits) || + (0 == ref_bits))) { + change_template_(reinterpret_cast(image_array), height, + width, channels, noi, ref, ref_mode, offset, hue_scale, + lig_scale, sat_scale, alp_scale, add_blend_sw); + } else if ((std::numeric_limits::digits == bits) && + (std::numeric_limits::digits == ref_bits)) { + change_template_( + reinterpret_cast(image_array), height, width, + channels, noi, reinterpret_cast(ref), + ref_mode, offset, hue_scale, lig_scale, sat_scale, alp_scale, add_blend_sw); + } else if ((std::numeric_limits::digits == bits) && + (std::numeric_limits::digits == ref_bits)) { + change_template_(image_array, height, width, channels, noi, + reinterpret_cast(ref), ref_mode, + offset, hue_scale, lig_scale, sat_scale, alp_scale, + add_blend_sw); + } else { + throw std::domain_error("Bad bits,Not uchar/ushort"); + } + */ } diff --git a/toonz/sources/stdfx/igs_hls_add.h b/toonz/sources/stdfx/igs_hls_add.h index 013e2d3..c712ac3 100644 --- a/toonz/sources/stdfx/igs_hls_add.h +++ b/toonz/sources/stdfx/igs_hls_add.h @@ -10,39 +10,18 @@ namespace igs { namespace hls_add { IGS_HLS_ADD_EXPORT void change( - unsigned char *image_array, const int height, const int width, - const int channels, const int bits - - , - const unsigned char *noi_image_array, const int noi_height, - const int noi_width, const int noi_channels, const int noi_bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const int xoffset /* 0 INT_MIN ... INT_MAX */ - , - const int yoffset /* 0 INT_MIN ... INT_MAX */ - , - const int from_rgba /* 0 0(R),1(G),2(B),3(A) */ - , - const double offset /* 0.5 -1.0 ... 1.0 */ - , - const double hue_scale /* 0.0 -1.0 ... 1.0 */ - , - const double lig_scale /* 0.5 -1.0 ... 1.0 */ - , - const double sat_scale /* 0.0 -1.0 ... 1.0 */ - , - const double alp_scale /* 0.0 -1.0 ... 1.0 */ - - , - const bool add_blend_sw + float *image_array, const int height, const int width, const int channels, + const float *noi_image_array, + const float *ref, /* 求める画像と同じ高、幅、channels数 */ + const int xoffset, /* 0 INT_MIN ... INT_MAX */ + const int yoffset, /* 0 INT_MIN ... INT_MAX */ + const int from_rgba, /* 0 0(R),1(G),2(B),3(A) */ + const double offset, /* 0.5 -1.0 ... 1.0 */ + const double hue_scale, /* 0.0 -1.0 ... 1.0 */ + const double lig_scale, /* 0.5 -1.0 ... 1.0 */ + const double sat_scale, /* 0.0 -1.0 ... 1.0 */ + const double alp_scale, /* 0.0 -1.0 ... 1.0 */ + const bool add_blend_sw, /* 効果(変化量)をアルファブレンドするか否かのスイッチ add_blend_sw == true アルファ値によりRGBの変化量を調整する @@ -55,7 +34,8 @@ IGS_HLS_ADD_EXPORT void change( 合成画 = 下絵 * (1 - alpha) + 上絵 * alpha の場合こちらを使う */ - ); -} + const bool cylindrical = true // colorspace shape: cylindrical or bicone +); } +} // namespace igs #endif /* !igs_add_hls_h */ diff --git a/toonz/sources/stdfx/igs_hls_adjust.cpp b/toonz/sources/stdfx/igs_hls_adjust.cpp index 58de223..247da97 100644 --- a/toonz/sources/stdfx/igs_hls_adjust.cpp +++ b/toonz/sources/stdfx/igs_hls_adjust.cpp @@ -2,26 +2,18 @@ namespace { void pixel_rgba_(const double red_in, const double gre_in, const double blu_in, double &red_out, double &gre_out, double &blu_out, - const double hue_pivot // 0.0 ...0...360... - , - const double hue_scale // 1.0 ...1... - , - const double hue_shift // 0.0 ...0...360... - , - const double lig_pivot // 0.0 ...0...1... - , - const double lig_scale // 1.0 ...1... - , - const double lig_shift // 0.0 ...0...1... - , - const double sat_pivot // 0.0 ...0...1... - , - const double sat_scale // 1.0 ...1... - , - const double sat_shift // 0.0 ...0...1... - ) { + const double hue_pivot, // 0.0 ...0...360... + const double hue_scale, // 1.0 ...1... + const double hue_shift, // 0.0 ...0...360... + const double lig_pivot, // 0.0 ...0...1... + const double lig_scale, // 1.0 ...1... + const double lig_shift, // 0.0 ...0...1... + const double sat_pivot, // 0.0 ...0...1... + const double sat_scale, // 1.0 ...1... + const double sat_shift, // 0.0 ...0...1... + const bool cylindrical = true) { double hue, lig, sat; - igs::color::rgb_to_hls(red_in, gre_in, blu_in, hue, lig, sat); + igs::color::rgb_to_hls(red_in, gre_in, blu_in, hue, lig, sat, cylindrical); if ((1.0 != hue_scale) || (0.0 != hue_shift)) { hue -= hue_pivot; while (hue < -180.0) { @@ -45,162 +37,139 @@ void pixel_rgba_(const double red_in, const double gre_in, const double blu_in, lig *= lig_scale; lig += lig_pivot; lig += lig_shift; - lig = (lig < 0.0) ? 0.0 : ((1.0 < lig) ? 1.0 : lig); + // lig = (lig < 0.0) ? 0.0 : ((1.0 < lig) ? 1.0 : lig); } if ((1.0 != sat_scale) || (0.0 != sat_shift)) { sat -= sat_pivot; sat *= sat_scale; sat += sat_pivot; sat += sat_shift; - sat = (sat < 0.0) ? 0.0 : ((1.0 < sat) ? 1.0 : sat); + sat = (sat < 0.0) ? 0.0 : sat; + // sat = (sat < 0.0) ? 0.0 : ((1.0 < sat) ? 1.0 : sat); } - igs::color::hls_to_rgb(hue, lig, sat, red_out, gre_out, blu_out); -} + igs::color::hls_to_rgb(hue, lig, sat, red_out, gre_out, blu_out, cylindrical); } +} // namespace //------------------------------------------------------------ #include /* std::numeric_limits */ #include "igs_ifx_common.h" /* igs::image::rgba */ #include "igs_hls_adjust.h" namespace { -/* raster画像にノイズをのせるtemplate */ -template -void change_template_( - IT *image_array, const int height, const int width, const int channels - - , - const RT *ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const double hue_pivot, const double hue_scale, const double hue_shift, - const double lig_pivot, const double lig_scale, const double lig_shift, - const double sat_pivot, const double sat_scale, const double sat_shift - - , - const bool add_blend_sw) { - const int t_max = std::numeric_limits::max(); - const double div_val = static_cast(t_max); - const double mul_val = static_cast(t_max) + 0.999999; - const int pixsize = height * width; - const int r_max = std::numeric_limits::max(); +/* raster画像にノイズをのせる*/ +void change_(float *image_array, const int height, const int width, + const int channels, + const float *ref, /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ + const double hue_pivot, const double hue_scale, + const double hue_shift, const double lig_pivot, + const double lig_scale, const double lig_shift, + const double sat_pivot, const double sat_scale, + const double sat_shift, const bool add_blend_sw, + const bool cylindrical = true) { + // const int t_max = std::numeric_limits::max(); + // const double div_val = static_cast(t_max); + // const double mul_val = static_cast(t_max) + 0.999999; + const int pixsize = height * width; + // const int r_max = std::numeric_limits::max(); if (igs::image::rgba::siz == channels) { using namespace igs::image::rgba; for (int ii = 0; ii < pixsize; ++ii, image_array += channels) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } /* 加算合成で、Alpha値ゼロならRGB値を計算する必要はない */ - if (add_blend_sw && (0 == image_array[alp])) { + if (add_blend_sw && (0.f == image_array[alp])) { continue; } /* 加算合成でなくAlpha合成の時は、 Alpha値がゼロでもRGB値は存在する(してもよい) */ - /* RGB値を正規化 */ - const IT *const ia = image_array; - const double rr1 = static_cast(ia[red]) / div_val; - const double gg1 = static_cast(ia[gre]) / div_val; - const double bb1 = static_cast(ia[blu]) / div_val; - /* pivot,scale,shiftによってRGB値を変化する */ - double rr2, gg2, bb2; - pixel_rgba_(rr1, gg1, bb1, rr2, gg2, bb2, hue_pivot, hue_scale, hue_shift, - lig_pivot, lig_scale, lig_shift, sat_pivot, sat_scale, - sat_shift); + double rr, gg, bb; + pixel_rgba_(image_array[red], image_array[gre], image_array[blu], rr, gg, + bb, hue_pivot, hue_scale, hue_shift, lig_pivot, lig_scale, + lig_shift, sat_pivot, sat_scale, sat_shift, cylindrical); /* 加算合成で、その値がMaxでなければ変化量に乗算 */ - if (add_blend_sw && (ia[alp] < t_max)) { - refv *= static_cast(ia[alp]) / div_val; + if (add_blend_sw && (image_array[alp] < 1.f)) { + refv *= image_array[alp]; } /* ピクセル単位の変化量があれば、RGBを調整する */ - if ((ref != 0) || (add_blend_sw && (ia[alp] < t_max))) { - rr2 = rr1 + (rr2 - rr1) * refv; - gg2 = gg1 + (gg2 - gg1) * refv; - bb2 = bb1 + (bb2 - bb1) * refv; + if ((ref != nullptr) || (add_blend_sw && (image_array[alp] < 1.f))) { + rr = image_array[red] + (rr - image_array[red]) * refv; + gg = image_array[gre] + (gg - image_array[gre]) * refv; + bb = image_array[blu] + (bb - image_array[blu]) * refv; } /* 結果をRGBに戻す */ - image_array[red] = static_cast(rr2 * mul_val); - image_array[gre] = static_cast(gg2 * mul_val); - image_array[blu] = static_cast(bb2 * mul_val); + image_array[red] = rr; + image_array[gre] = gg; + image_array[blu] = bb; } } else if (igs::image::rgb::siz == channels) { using namespace igs::image::rgb; for (int ii = 0; ii < pixsize; ++ii, image_array += channels) { - double refv = 1.0; - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; + float refv = 1.f; + if (ref != nullptr) { + refv *= (*ref); + ref++; } - const IT *const ia = image_array; - const double rr1 = static_cast(ia[red]) / div_val; - const double gg1 = static_cast(ia[gre]) / div_val; - const double bb1 = static_cast(ia[blu]) / div_val; - double rr2, gg2, bb2; - pixel_rgba_(rr1, gg1, bb1, rr2, gg2, bb2, hue_pivot, hue_scale, hue_shift, - lig_pivot, lig_scale, lig_shift, sat_pivot, sat_scale, - sat_shift); - - if (ref != 0) { - rr2 = rr1 + (rr2 - rr1) * refv; - gg2 = gg1 + (gg2 - gg1) * refv; - bb2 = bb1 + (bb2 - bb1) * refv; + // const IT *const ia = image_array; + // const double rr1 = static_cast(ia[red]) / div_val; + // const double gg1 = static_cast(ia[gre]) / div_val; + // const double bb1 = static_cast(ia[blu]) / div_val; + double rr, gg, bb; + pixel_rgba_(image_array[red], image_array[gre], image_array[blu], rr, gg, + bb, hue_pivot, hue_scale, hue_shift, lig_pivot, lig_scale, + lig_shift, sat_pivot, sat_scale, sat_shift, cylindrical); + + if (ref != nullptr) { + rr = image_array[red] + (rr - image_array[red]) * refv; + gg = image_array[gre] + (gg - image_array[gre]) * refv; + bb = image_array[blu] + (bb - image_array[blu]) * refv; } - image_array[red] = static_cast(rr2 * mul_val); - image_array[gre] = static_cast(gg2 * mul_val); - image_array[blu] = static_cast(bb2 * mul_val); + image_array[red] = rr; + image_array[gre] = gg; + image_array[blu] = bb; } } else if (1 == channels) { /* grayscale */ for (int ii = 0; ii < pixsize; ++ii, image_array += channels) { - double refv = 1.0; - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; + double refv = 1.f; + if (ref != nullptr) { + refv *= (*ref); + ref++; } - double li1 = static_cast(image_array[0]) / div_val, - li2 = (li1 - lig_pivot) * lig_scale + lig_pivot + lig_shift; - li2 = (li2 < 0.0) ? 0.0 : ((1.0 < li2) ? 1.0 : li2); + double li = + (*image_array - lig_pivot) * lig_scale + lig_pivot + lig_shift; + // li = (li < 0.0) ? 0.0 : ((1.0 < li) ? 1.0 : li); - if (ref != 0) { - li2 = li1 + (li2 - li1) * refv; + if (ref != nullptr) { + li = *image_array + (li - *image_array) * refv; } - image_array[0] = static_cast(li2 * mul_val); + *image_array = li; } } } -} +} // namespace #include /* std::domain_error */ void igs::hls_adjust::change( - unsigned char *image_array, const int height, const int width, - const int channels, const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , + float *image_array, const int height, const int width, const int channels, + const float *ref, /* 求める画像と同じ高、幅、channels数 */ const double hue_pivot, const double hue_scale, const double hue_shift, const double lig_pivot, const double lig_scale, const double lig_shift, - const double sat_pivot, const double sat_scale, const double sat_shift - - , - const bool add_blend_sw) { + const double sat_pivot, const double sat_scale, const double sat_shift, + const bool add_blend_sw, const bool cylindrical) { if ((1.0 == hue_scale) && (0.0 == hue_shift) && (1.0 == lig_scale) && (0.0 == lig_shift) && (1.0 == sat_scale) && (0.0 == sat_shift)) { return; @@ -208,11 +177,15 @@ void igs::hls_adjust::change( if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } + change_(image_array, height, width, channels, ref, hue_pivot, hue_scale, + hue_shift, lig_pivot, lig_scale, lig_shift, sat_pivot, sat_scale, + sat_shift, add_blend_sw, cylindrical); /* rgb(a)画像にhls(a)でドットノイズを加える */ + /* if ((std::numeric_limits::digits == bits) && ((std::numeric_limits::digits == ref_bits) || (0 == ref_bits))) { @@ -242,4 +215,5 @@ void igs::hls_adjust::change( } else { throw std::domain_error("Bad bits,Not uchar/ushort"); } + */ } diff --git a/toonz/sources/stdfx/igs_hls_adjust.h b/toonz/sources/stdfx/igs_hls_adjust.h index 99d9c85..bf5593b 100644 --- a/toonz/sources/stdfx/igs_hls_adjust.h +++ b/toonz/sources/stdfx/igs_hls_adjust.h @@ -10,37 +10,18 @@ namespace igs { namespace hls_adjust { IGS_HLS_ADJUST_EXPORT void change( - unsigned char *image_array, const int height, const int width, - const int channels, const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const double hue_pivot /* 0.0 ...0...360... */ - , - const double hue_scale /* 1.0 ...1... */ - , - const double hue_shift /* 0.0 ...0...360... */ - , - const double lig_pivot /* 0.0 ...0...1... */ - , - const double lig_scale /* 1.0 ...1... */ - , - const double lig_shift /* 0.0 ...0...1... */ - , - const double sat_pivot /* 0.0 ...0...1... */ - , - const double sat_scale /* 1.0 ...1... */ - , - const double sat_shift /* 0.0 ...0...1... */ - - , - const bool add_blend_sw + float *image_array, const int height, const int width, const int channels, + const float *ref, /* 求める画像と同じ高、幅、channels数 */ + const double hue_pivot, /* 0.0 ...0...360... */ + const double hue_scale, /* 1.0 ...1... */ + const double hue_shift, /* 0.0 ...0...360... */ + const double lig_pivot, /* 0.0 ...0...1... */ + const double lig_scale, /* 1.0 ...1... */ + const double lig_shift, /* 0.0 ...0...1... */ + const double sat_pivot, /* 0.0 ...0...1... */ + const double sat_scale, /* 1.0 ...1... */ + const double sat_shift, /* 0.0 ...0...1... */ + const bool add_blend_sw, /* 効果(変化量)をアルファブレンドするか否かのスイッチ add_blend_sw == true アルファ値によりRGBの変化量を調整する @@ -53,8 +34,9 @@ IGS_HLS_ADJUST_EXPORT void change( 合成画 = 下絵 * (1 - alpha) + 上絵 * alpha の場合こちらを使う */ - ); -} + const bool cylindrical = true // colorspace shape: cylindrical or bicone +); } +} // namespace igs #endif /* !igs_hls_adjust_h */ diff --git a/toonz/sources/stdfx/igs_hls_noise.cpp b/toonz/sources/stdfx/igs_hls_noise.cpp index 7abdcd3..b2607b9 100644 --- a/toonz/sources/stdfx/igs_hls_noise.cpp +++ b/toonz/sources/stdfx/igs_hls_noise.cpp @@ -368,7 +368,8 @@ void pixel_rgb_(const double red_in, const double gre_in, const double blu_in, const double lig_noise, const double sat_noise, control_term_within_limits_ &lig_term, control_term_within_limits_ &sat_term, double &red_out, - double &gre_out, double &blu_out) { + double &gre_out, double &blu_out, + const bool cylindrical = true) { if (0.0 == alp_in) { red_out = red_in; gre_out = gre_in; @@ -376,7 +377,7 @@ void pixel_rgb_(const double red_in, const double gre_in, const double blu_in, return; } double hue, lig, sat; - igs::color::rgb_to_hls(red_in, gre_in, blu_in, hue, lig, sat); + igs::color::rgb_to_hls(red_in, gre_in, blu_in, hue, lig, sat, cylindrical); if (0.0 != hue_noise) { hue += 360.0 * hue_noise * alp_in; while (hue < 0.0) { @@ -392,11 +393,11 @@ void pixel_rgb_(const double red_in, const double gre_in, const double blu_in, lig_term.exec(lig, lignoise, shift_value); lig += shift_value * alp_in; lig += lignoise * alp_in; - if (lig < 0.0) { - lig = 0.0; - } else if (1.0 < lig) { - lig = 1.0; - } + // if (lig < 0.0) { + // lig = 0.0; + // } else if (1.0 < lig) { + // lig = 1.0; + // } } if (0.0 != sat_term.noise_range()) { double shift_value = 0; @@ -404,15 +405,17 @@ void pixel_rgb_(const double red_in, const double gre_in, const double blu_in, sat_term.exec(sat, satnoise, shift_value); sat += shift_value * alp_in; sat += satnoise * alp_in; - if (sat < 0.0) { - sat = 0.0; - } else if (1.0 < sat) { - sat = 1.0; - } - // if( 0.0 == sat ) hue = -1.0; // hls_to_rgb(-) + sat = (sat < 0.0) ? 0.0 : sat; + // if (sat < 0.0) { + // sat = 0.0; + // } else if (1.0 < sat) { + // sat = 1.0; + // } + // if( 0.0 == sat ) hue = -1.0; // hls_to_rgb(-) } - igs::color::hls_to_rgb(hue, lig, sat, red_out, gre_out, blu_out); + igs::color::hls_to_rgb(hue, lig, sat, red_out, gre_out, blu_out, cylindrical); } + /*------ Alpha値にノイズをのせる ------*/ void pixel_a_(const double alp_in, const double alp_noise, control_term_within_limits_ &alp_term, double &alp_out) { @@ -433,38 +436,26 @@ void pixel_a_(const double alp_in, const double alp_noise, } alp_out = alpin; } -/*------ raster画像にノイズをのせるtemplate ------*/ -template -void change_template_( - IT *image_array, const int width, const int height, const int channels - - , - const RT *ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - noise_reference_ &noise, const double hue_range, - control_term_within_limits_ &lig_term, - control_term_within_limits_ &sat_term, control_term_within_limits_ &alp_term - - , - const bool add_blend_sw) { - const int t_max = std::numeric_limits::max(); - const double div_val = static_cast(t_max); - const double mul_val = static_cast(t_max) + 0.999999; - const int r_max = std::numeric_limits::max(); +/*------ raster画像にノイズをのせる ------*/ +void change_(float *image_array, const int width, const int height, + const int channels, + const float *ref, /* 求める画像(out)と同じ高さ、幅 */ + noise_reference_ &noise, const double hue_range, + control_term_within_limits_ &lig_term, + control_term_within_limits_ &sat_term, + control_term_within_limits_ &alp_term, const bool add_blend_sw, + const bool cylindrical = true) { if (igs::image::rgba::siz == channels) { using namespace igs::image::rgba; for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, image_array += channels) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } /* 加算合成で、Alpha値ゼロならRGB値を計算する必要はない */ if (add_blend_sw && (0 == image_array[alp])) { @@ -474,39 +465,37 @@ void change_template_( Alpha値がゼロでもRGB値は存在する(してもよい) */ /* マスクSWがON、なら変化をMask */ - if (add_blend_sw && (image_array[alp] < t_max)) { - refv *= static_cast(image_array[alp]) / div_val; + if (add_blend_sw && (image_array[alp] < 1.f)) { + refv *= image_array[alp]; } if (((0.0 != hue_range) || (0.0 != lig_term.noise_range()) || (0.0 != sat_term.noise_range())) /* ノイズがhlsのどれか一つはある */ - ) { - double rr1 = static_cast(image_array[red]) / div_val, - gg1 = static_cast(image_array[gre]) / div_val, - bb1 = static_cast(image_array[blu]) / div_val, - aa1 = static_cast(image_array[alp]) / div_val; - double rr2 = 0, gg2 = 0, bb2 = 0; + ) { + float rr1 = image_array[red], gg1 = image_array[gre], + bb1 = image_array[blu], aa1 = image_array[alp]; + double rr2 = 0., gg2 = 0., bb2 = 0.; pixel_rgb_(rr1, gg1, bb1, aa1, noise.hue_value(xx, yy), noise.lig_value(xx, yy), noise.sat_value(xx, yy), lig_term, - sat_term, rr2, gg2, bb2); - if (refv != 1.0) { + sat_term, rr2, gg2, bb2, cylindrical); + if (refv != 1.f) { rr2 = (rr2 - rr1) * refv + rr1; gg2 = (gg2 - gg1) * refv + gg1; bb2 = (bb2 - bb1) * refv + bb1; } - image_array[red] = static_cast(rr2 * mul_val); - image_array[gre] = static_cast(gg2 * mul_val); - image_array[blu] = static_cast(bb2 * mul_val); + image_array[red] = static_cast(rr2); + image_array[gre] = static_cast(gg2); + image_array[blu] = static_cast(bb2); } if (0.0 != alp_term.noise_range()) { - double aa1 = static_cast(image_array[alp]) / div_val; - double aa2 = 0; + double aa1 = static_cast(image_array[alp]); + double aa2 = 0.; pixel_a_(aa1, noise.alp_value(xx, yy), alp_term, aa2); - if (refv != 1.0) { + if (refv != 1.f) { aa2 = (aa2 - aa1) * refv + aa1; } - image_array[alp] = static_cast(aa2 * mul_val); + image_array[alp] = static_cast(aa2); } } } @@ -514,33 +503,32 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ using namespace igs::image::rgb; if (((0.0 != hue_range) || (0.0 != lig_term.noise_range()) || (0.0 != sat_term.noise_range())) /* ノイズがhlsのどれか一つはある */ - ) { + ) { for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, image_array += channels) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } - double rr1 = static_cast(image_array[red]) / div_val, - gg1 = static_cast(image_array[gre]) / div_val, - bb1 = static_cast(image_array[blu]) / div_val; - double rr2 = 0, gg2 = 0, bb2 = 0; + float rr1 = image_array[red], gg1 = image_array[gre], + bb1 = image_array[blu]; + double rr2 = 0., gg2 = 0., bb2 = 0.; pixel_rgb_(rr1, gg1, bb1, 1.0, noise.hue_value(xx, yy), noise.lig_value(xx, yy), noise.sat_value(xx, yy), lig_term, - sat_term, rr2, gg2, bb2); - if (refv != 1.0) { + sat_term, rr2, gg2, bb2, cylindrical); + if (refv != 1.f) { rr2 = (rr2 - rr1) * refv + rr1; gg2 = (gg2 - gg1) * refv + gg1; bb2 = (bb2 - bb1) * refv + bb1; } - image_array[red] = static_cast(rr2 * mul_val); - image_array[gre] = static_cast(gg2 * mul_val); - image_array[blu] = static_cast(bb2 * mul_val); + image_array[red] = static_cast(rr2); + image_array[gre] = static_cast(gg2); + image_array[blu] = static_cast(bb2); } } } @@ -549,15 +537,15 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, ++image_array) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } - double li1 = static_cast(image_array[0]) / div_val; + double li1 = static_cast(image_array[0]); double shift_value = 0; double lig_noise = noise.lig_value(xx, yy); lig_term.exec(li1, lig_noise, shift_value); @@ -565,53 +553,36 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ double li2 = li1; li2 += shift_value; li2 += lig_noise; - li2 = (li2 < 0.0) ? 0.0 : ((1.0 < li2) ? 1.0 : li2); + li2 = (li2 < 0.0) ? 0.0 : li2; - if (refv != 1.0) { + if (refv != 1.f) { li2 = li1 + (li2 - li1) * refv; } - image_array[0] = static_cast(li2 * mul_val); + image_array[0] = static_cast(li2); } } } } } -} + +} // namespace //-------------------------------------------------------------------- #include // std::domain_error #include "igs_hls_noise.h" void igs::hls_noise::change( - unsigned char *image_array - - , - const int height, const int width, const int channels, const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - + float *image_array, const int height, const int width, const int channels, + const float *ref, /* 求める画像と同じ高、幅 */ /* image_arrayに余白が変化してもノイズパターンが変わらない - ようにするためにカメラエリアを指定する */ - , + ようにするためにカメラエリアを指定する */ const int camera_x, const int camera_y, const int camera_w, - const int camera_h - - , - const double hue_range, const double lig_range, const double sat_range, - const double alp_range, const unsigned long random_seed, - const double near_blur - - , + const int camera_h, const double hue_range, const double lig_range, + const double sat_range, const double alp_range, + const unsigned long random_seed, const double near_blur, const double lig_effective, const double lig_center, const int lig_type, const double sat_effective, const double sat_center, const int sat_type, - const double alp_effective, const double alp_center, const int alp_type - - , - const bool add_blend_sw) { + const double alp_effective, const double alp_center, const int alp_type, + const bool add_blend_sw, const bool cylindrical) { if ((0.0 == hue_range) && (0.0 == lig_range) && (0.0 == sat_range) && (0.0 == alp_range)) { return; @@ -619,7 +590,7 @@ void igs::hls_noise::change( if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } @@ -636,35 +607,7 @@ void igs::hls_noise::change( control_term_within_limits_ alp_term(alp_effective, alp_effective, alp_center, alp_type, alp_range); - /* rgb(a)画像にhls(a)でドットノイズを加える */ - if ((std::numeric_limits::digits == bits) && - ((std::numeric_limits::digits == ref_bits) || - (0 == ref_bits))) { - change_template_(image_array, width, height, channels, ref, ref_mode, noise, - hue_range, lig_term, sat_term, alp_term, add_blend_sw); - noise.clear(); /* ノイズ画像メモリ解放 */ - } else if ((std::numeric_limits::digits == bits) && - ((std::numeric_limits::digits == ref_bits) || - (0 == ref_bits))) { - change_template_(reinterpret_cast(image_array), width, - height, channels, ref, ref_mode, noise, hue_range, - lig_term, sat_term, alp_term, add_blend_sw); - noise.clear(); /* ノイズ画像メモリ解放 */ - } else if ((std::numeric_limits::digits == bits) && - (std::numeric_limits::digits == ref_bits)) { - change_template_( - reinterpret_cast(image_array), width, height, - channels, reinterpret_cast(ref), ref_mode, - noise, hue_range, lig_term, sat_term, alp_term, add_blend_sw); - noise.clear(); /* ノイズ画像メモリ解放 */ - } else if ((std::numeric_limits::digits == bits) && - (std::numeric_limits::digits == ref_bits)) { - change_template_(image_array, width, height, channels, - reinterpret_cast(ref), ref_mode, - noise, hue_range, lig_term, sat_term, alp_term, - add_blend_sw); - noise.clear(); /* ノイズ画像メモリ解放 */ - } else { - throw std::domain_error("Bad bits,Not uchar/ushort"); - } + change_(image_array, width, height, channels, ref, noise, hue_range, lig_term, + sat_term, alp_term, add_blend_sw, cylindrical); + noise.clear(); /* ノイズ画像メモリ解放 */ } diff --git a/toonz/sources/stdfx/igs_hls_noise.h b/toonz/sources/stdfx/igs_hls_noise.h index 36e16ab..0f46132 100644 --- a/toonz/sources/stdfx/igs_hls_noise.h +++ b/toonz/sources/stdfx/igs_hls_noise.h @@ -10,61 +10,30 @@ namespace igs { namespace hls_noise { IGS_HLS_NOISE_EXPORT void change( - unsigned char *image_array - - , - const int height, const int width, const int channels, const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - + float *image_array, const int height, const int width, const int channels, + const float *ref, /* 求める画像と同じ高、幅*/ /* image_arrayに余白が変化してもノイズパターンが変わらない ようにするためにカメラエリアを指定する */ - , const int camera_x, const int camera_y, const int camera_w, - const int camera_h - - , - const double hue_range /* =0.025 0 ... 1.0 */ - , - const double lig_range /* =0.035 0 ... 1.0 */ - , - const double sat_range /* =0.0 0 ... 1.0 */ - , - const double alp_range /* =0.0 0 ... 1.0 */ - , - const unsigned long random_seed /* =1 0 ... ULONG_MAX */ - , - const double near_blur /* =0.500 0 ... 0.5 */ - - , - const double lig_effective /* =0.0 0 ... 1.0 */ - , - const double lig_center /* =0.5 0 ... 1.0 */ - , - const int lig_type /* =0 - 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ - , - const double sat_effective /* =0.0 0 ... 1.0 */ - , - const double sat_center /* =0.5 0 ... 1.0 */ - , - const int sat_type /* =0 - 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ - , - const double alp_effective /* =0.0 0 ... 1.0 */ - , - const double alp_center /* =0.5 0 ... 1.0 */ - , - const int alp_type /* =0 - 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ - - , - const bool add_blend_sw + const int camera_h, const double hue_range, /* =0.025 0 ... 1.0 */ + const double lig_range, /* =0.035 0 ... 1.0 */ + const double sat_range, /* =0.0 0 ... 1.0 */ + const double alp_range, /* =0.0 0 ... 1.0 */ + const unsigned long random_seed, /* =1 0 ... ULONG_MAX */ + const double near_blur, /* =0.500 0 ... 0.5 */ + const double lig_effective, /* =0.0 0 ... 1.0 */ + const double lig_center, /* =0.5 0 ... 1.0 */ + const int lig_type, /* =0 + 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ + const double sat_effective, /* =0.0 0 ... 1.0 */ + const double sat_center, /* =0.5 0 ... 1.0 */ + const int sat_type, /* =0 + 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ + const double alp_effective, /* =0.0 0 ... 1.0 */ + const double alp_center, /* =0.5 0 ... 1.0 */ + const int alp_type, /* =0 + 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ + const bool add_blend_sw, /* 効果(変化量)をアルファブレンドするか否かのスイッチ add_blend_sw == true アルファ値によりRGBの変化量を調整する @@ -77,8 +46,9 @@ IGS_HLS_NOISE_EXPORT void change( 合成画 = 下絵 * (1 - alpha) + 上絵 * alpha の場合こちらを使う */ - ); -} + const bool cylindrical = true // colorspace shape: cylindrical or bicone +); } +} // namespace igs #endif /* !igs_hls_noise_h */ diff --git a/toonz/sources/stdfx/igs_hsv_add.cpp b/toonz/sources/stdfx/igs_hsv_add.cpp index 95eba74..efad1f8 100644 --- a/toonz/sources/stdfx/igs_hsv_add.cpp +++ b/toonz/sources/stdfx/igs_hsv_add.cpp @@ -26,17 +26,18 @@ void pixel_rgba_(const double red_in, const double gre_in, const double blu_in, sat += sat_noise; if (sat < 0.0) { sat = 0.0; - } else if (1.0 < sat) { - sat = 1.0; } + // else if (1.0 < sat) { + // sat = 1.0; + // } } if (0.0 != val_noise) { val += val_noise; - if (val < 0.0) { - val = 0.0; - } else if (1.0 < val) { - val = 1.0; - } + // if (val < 0.0) { + // val = 0.0; + // } else if (1.0 < val) { + // val = 1.0; + // } } if (0.0 != alp_noise) { alp += alp_noise; @@ -46,26 +47,22 @@ void pixel_rgba_(const double red_in, const double gre_in, const double blu_in, igs::color::hsv_to_rgb(hue, sat, val, red_out, gre_out, blu_out); alp_out = alp; } -} +} // namespace //------------------------------------------------------------ namespace { class noise_ref_ { public: - noise_ref_(const unsigned char *array, const int height, const int width, - const int channels, const int bits, const int xoffset, - const int yoffset, const int zz); + noise_ref_(const float *array, const int height, const int width, + const int xoffset, const int yoffset, const int zz); double noise(int xx // 0...width-1... , - int yy // 0...height-1... - ) const; // return range is 0...1 + int yy // 0...height-1... + ) const; // return range is 0...1 private: - const unsigned char *array_; + const float *array_; const int height_; const int width_; - const int channels_; - const int bits_; - const double bmax_; const int xoffset_; const int yoffset_; const int zz_; @@ -76,28 +73,20 @@ private: /* 代入演算子を無効化 */ noise_ref_ &operator=(const noise_ref_ &); }; -noise_ref_::noise_ref_(const unsigned char *array, const int height, - const int width, const int channels, const int bits, +noise_ref_::noise_ref_(const float *array, const int height, const int width, const int xoffset, const int yoffset, const int zz) : array_(array) , height_(height) , width_(width) - , channels_(channels) - , bits_(bits) - , bmax_(static_cast((1 << bits) - 1)) , xoffset_(xoffset) , yoffset_(yoffset) , zz_(zz) { if (0 == array) { throw std::domain_error("noise_ref_ no data"); } - if ((zz < 0) || (channels <= zz)) { + if ((zz < 0) || (4 <= zz)) { throw std::domain_error("noise_ref_ bad zz"); } - if ((std::numeric_limits::digits != this->bits_) && - (std::numeric_limits::digits != this->bits_)) { - throw std::domain_error("noise_ref_ bad bits"); - } } double noise_ref_::noise(int xx, int yy) const { xx -= this->xoffset_; @@ -115,59 +104,35 @@ double noise_ref_::noise(int xx, int yy) const { yy -= this->height_; } - if (std::numeric_limits::digits == this->bits_) { - return (*(this->array_ + this->channels_ * this->width_ * yy + - this->channels_ * xx + this->zz_)) / - this->bmax_; - } - return (*(reinterpret_cast(this->array_) + - this->channels_ * this->width_ * yy + this->channels_ * xx + - this->zz_)) / - this->bmax_; -} + return (*(this->array_ + 4 * this->width_ * yy + 4 * xx + this->zz_)); } +} // namespace //------------------------------------------------------------ #include "igs_ifx_common.h" /* igs::image::rgba */ #include "igs_hsv_add.h" namespace { -/* raster画像にノイズをのせるtemplate */ -template -void change_template_( - IT *image_array, const int height, const int width, const int channels - - , - const noise_ref_ &noi - - , - const RT *ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const double offset, const double hue_scale, const double sat_scale, - const double val_scale, const double alp_scale - - , - const bool add_blend_sw) { - const int t_max = std::numeric_limits::max(); - const double div_val = static_cast(t_max); - const double mul_val = static_cast(t_max) + 0.999999; - const int r_max = std::numeric_limits::max(); +/* raster画像にノイズをのせる */ +void change_(float *image_array, const int height, const int width, + const int channels, const noise_ref_ &noi, + const float *ref, /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ + const double offset, const double hue_scale, + const double sat_scale, const double val_scale, + const double alp_scale, const bool add_blend_sw) { if (igs::image::rgba::siz == channels) { using namespace igs::image::rgba; for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, image_array += channels) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } /* 加算合成で、Alpha値ゼロならRGB値を計算する必要はない */ - if (add_blend_sw && (0 == image_array[alp])) { + if (add_blend_sw && (0.f == image_array[alp])) { continue; } /* 加算合成でなくAlpha合成の時は、 @@ -177,24 +142,21 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ refv *= (noi.noise(xx, yy) - offset); /* マスクSWがON、なら変化をMask */ - if (add_blend_sw && (image_array[alp] < t_max)) { - refv *= static_cast(image_array[alp]) / div_val; + if (add_blend_sw && (image_array[alp] < 1.f)) { + refv *= image_array[alp]; } /* RGBAにHSVAノイズを加える */ double rr, gg, bb, aa; - pixel_rgba_(static_cast(image_array[red]) / div_val, - static_cast(image_array[gre]) / div_val, - static_cast(image_array[blu]) / div_val, - static_cast(image_array[alp]) / div_val, - refv * hue_scale, refv * sat_scale, refv * val_scale, - refv * alp_scale, rr, gg, bb, aa); + pixel_rgba_(image_array[red], image_array[gre], image_array[blu], + image_array[alp], refv * hue_scale, refv * sat_scale, + refv * val_scale, refv * alp_scale, rr, gg, bb, aa); /* 変化後の値を戻す */ - image_array[red] = static_cast(rr * mul_val); - image_array[gre] = static_cast(gg * mul_val); - image_array[blu] = static_cast(bb * mul_val); - image_array[alp] = static_cast(aa * mul_val); + image_array[red] = (float)rr; + image_array[gre] = (float)gg; + image_array[blu] = (float)bb; + image_array[alp] = (float)aa; } } } else if (igs::image::rgb::siz == channels) { @@ -202,12 +164,12 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, image_array += channels) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } /* HSVそれぞれに対するオフセット済ノイズ値 */ @@ -215,28 +177,26 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ /* RGBにHSVノイズを加える */ double rr, gg, bb, aa; - pixel_rgba_(static_cast(image_array[red]) / div_val, - static_cast(image_array[gre]) / div_val, - static_cast(image_array[blu]) / div_val, 1.0, + pixel_rgba_(image_array[red], image_array[gre], image_array[blu], 1.0, refv * hue_scale, refv * sat_scale, refv * val_scale, 0.0, rr, gg, bb, aa); /* 変化後の値を戻す */ - image_array[red] = static_cast(rr * mul_val); - image_array[gre] = static_cast(gg * mul_val); - image_array[blu] = static_cast(bb * mul_val); + image_array[red] = (float)rr; + image_array[gre] = (float)gg; + image_array[blu] = (float)bb; } } } else if (1 == channels) { /* grayscale */ for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, ++image_array) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像あればピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } /* Lに対するオフセット済ノイズ値 */ @@ -248,40 +208,24 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ } /* GrayscaleにLノイズを加える */ - double val = - static_cast(image_array[0]) / div_val + refv * val_scale; - val = (val < 0.0) ? 0.0 : ((1.0 < val) ? 1.0 : val); + float val = image_array[0] + refv * val_scale; + // val = (val < 0.0) ? 0.0 : ((1.0 < val) ? 1.0 : val); /* 変化後の値を戻す */ - image_array[0] = static_cast(val * mul_val); + image_array[0] = val; } } } } -} +} // namespace void igs::hsv_add::change( - unsigned char *image_array, const int height, const int width, - const int channels, const int bits - - , - const unsigned char *noi_image_array, const int noi_height, - const int noi_width, const int noi_channels, const int noi_bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , + float *image_array, const int height, const int width, const int channels, + const float *noi_image_array, + const float *ref, /* 求める画像と同じ高、幅、channels数 */ const int xoffset, const int yoffset, const int from_rgba, const double offset, const double hue_scale, const double sat_scale, - const double val_scale, const double alp_scale - - , - const bool add_blend_sw) { + const double val_scale, const double alp_scale, const bool add_blend_sw) { if ((0.0 == hue_scale) && (0.0 == sat_scale) && (0.0 == val_scale) && (0.0 == alp_scale)) { return; @@ -289,20 +233,18 @@ void igs::hsv_add::change( if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } /* ノイズ参照画像を作成する */ - noise_ref_ noi(noi_image_array - - , - noi_height, noi_width, noi_channels, noi_bits + noise_ref_ noi(noi_image_array, height, width, xoffset, yoffset, from_rgba); - , - xoffset, yoffset, from_rgba); + change_(image_array, height, width, channels, noi, ref, offset, hue_scale, + sat_scale, val_scale, alp_scale, add_blend_sw); /* rgb(a)画像にhsv(a)でドットノイズを加える */ + /* if ((std::numeric_limits::digits == bits) && ((std::numeric_limits::digits == ref_bits) || (0 == ref_bits))) { @@ -330,4 +272,5 @@ void igs::hsv_add::change( } else { throw std::domain_error("Bad bits,Not uchar/ushort"); } + */ } diff --git a/toonz/sources/stdfx/igs_hsv_add.h b/toonz/sources/stdfx/igs_hsv_add.h index d665501..687661a 100644 --- a/toonz/sources/stdfx/igs_hsv_add.h +++ b/toonz/sources/stdfx/igs_hsv_add.h @@ -10,38 +10,17 @@ namespace igs { namespace hsv_add { IGS_HSV_ADD_EXPORT void change( - unsigned char *image_array, const int height, const int width, - const int channels, const int bits - - , - const unsigned char *noi_image_array, const int noi_height, - const int noi_width, const int noi_channels, const int noi_bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const int xoffset /* 0 INT_MIN ... INT_MAX */ - , - const int yoffset /* 0 INT_MIN ... INT_MAX */ - , - const int from_rgba /* 0 0(R),1(G),2(B),3(A) */ - , - const double offset /* 0.5 -1.0 ... 1.0 */ - , - const double hue_scale /* 0.0 -1.0 ... 1.0 */ - , - const double sat_scale /* 0.0 -1.0 ... 1.0 */ - , - const double val_scale /* 1.0 -1.0 ... 1.0 */ - , - const double alp_scale /* 0.0 -1.0 ... 1.0 */ - - , + float *image_array, const int height, const int width, const int channels, + const float *noi_image_array, + const float *ref, /* 求める画像と同じ高、幅、channels数 */ + const int xoffset, /* 0 INT_MIN ... INT_MAX */ + const int yoffset, /* 0 INT_MIN ... INT_MAX */ + const int from_rgba, /* 0 0(R),1(G),2(B),3(A) */ + const double offset, /* 0.5 -1.0 ... 1.0 */ + const double hue_scale, /* 0.0 -1.0 ... 1.0 */ + const double sat_scale, /* 0.0 -1.0 ... 1.0 */ + const double val_scale, /* 1.0 -1.0 ... 1.0 */ + const double alp_scale, /* 0.0 -1.0 ... 1.0 */ const bool add_blend_sw /* 効果(変化量)をアルファブレンドするか否かのスイッチ add_blend_sw == true @@ -55,7 +34,7 @@ IGS_HSV_ADD_EXPORT void change( 合成画 = 下絵 * (1 - alpha) + 上絵 * alpha の場合こちらを使う */ - ); -} +); } +} // namespace igs #endif /* !igs_hsv_add_h */ diff --git a/toonz/sources/stdfx/igs_hsv_adjust.cpp b/toonz/sources/stdfx/igs_hsv_adjust.cpp index be67ae8..d938670 100644 --- a/toonz/sources/stdfx/igs_hsv_adjust.cpp +++ b/toonz/sources/stdfx/igs_hsv_adjust.cpp @@ -19,7 +19,7 @@ void pixel_rgba_(const double red_in, const double gre_in, const double blu_in, const double val_scale // 1.0 ...1... , const double val_shift // 0.0 ...0...1... - ) { +) { double hue, sat, val; igs::color::rgb_to_hsv(red_in, gre_in, blu_in, hue, sat, val); if ((1.0 != hue_scale) || (0.0 != hue_shift)) { @@ -45,161 +45,136 @@ void pixel_rgba_(const double red_in, const double gre_in, const double blu_in, sat *= sat_scale; sat += sat_pivot; sat += sat_shift; - sat = (sat < 0.0) ? 0.0 : ((1.0 < sat) ? 1.0 : sat); + sat = (sat < 0.0) ? 0.0 : sat; + // sat = (sat < 0.0) ? 0.0 : ((1.0 < sat) ? 1.0 : sat); } if ((1.0 != val_scale) || (0.0 != val_shift)) { val -= val_pivot; val *= val_scale; val += val_pivot; val += val_shift; - val = (val < 0.0) ? 0.0 : ((1.0 < val) ? 1.0 : val); + // val = (val < 0.0) ? 0.0 : ((1.0 < val) ? 1.0 : val); } igs::color::hsv_to_rgb(hue, sat, val, red_out, gre_out, blu_out); } -} +} // namespace //------------------------------------------------------------ #include /* std::numeric_limits */ #include "igs_ifx_common.h" /* igs::image::rgba */ #include "igs_hsv_adjust.h" namespace { -/* raster画像にノイズをのせるtemplate */ -template -void change_template_( - IT *image_array, const int height, const int width, const int channels - - , - const RT *ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ - , - const int ref_mode // R,G,B,A,luminance - - , - const double hue_pivot, const double hue_scale, const double hue_shift, - const double sat_pivot, const double sat_scale, const double sat_shift, - const double val_pivot, const double val_scale, const double val_shift - - , - const bool add_blend_sw) { - const int t_max = std::numeric_limits::max(); - const double div_val = static_cast(t_max); - const double mul_val = static_cast(t_max) + 0.999999; - const int pixsize = height * width; - const int r_max = std::numeric_limits::max(); +/* raster画像にノイズをのせる */ +void change_(float *image_array, const int height, const int width, + const int channels, + const float *ref, /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ + const double hue_pivot, const double hue_scale, + const double hue_shift, const double sat_pivot, + const double sat_scale, const double sat_shift, + const double val_pivot, const double val_scale, + const double val_shift, const bool add_blend_sw) { + // const int t_max = std::numeric_limits::max(); + // const double div_val = static_cast(t_max); + // const double mul_val = static_cast(t_max) + 0.999999; + const int pixsize = height * width; + // const int r_max = std::numeric_limits::max(); if (igs::image::rgba::siz == channels) { using namespace igs::image::rgba; for (int ii = 0; ii < pixsize; ++ii, image_array += channels) { /* 変化量初期値 */ - double refv = 1.0; + float refv = 1.f; /* 参照画像によるピクセル単位の画像変化量を得る */ - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; /* continue;の前に行うこと */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ } /* 加算合成で、Alpha値ゼロならRGB値を計算する必要はない */ - if (add_blend_sw && (0 == image_array[alp])) { + if (add_blend_sw && (0.f == image_array[alp])) { continue; } /* 加算合成でなくAlpha合成の時は、 Alpha値がゼロでもRGB値は存在する(してもよい) */ - /* RGB値を正規化 */ - const IT *const ia = image_array; - const double rr1 = static_cast(ia[red]) / div_val; - const double gg1 = static_cast(ia[gre]) / div_val; - const double bb1 = static_cast(ia[blu]) / div_val; - /* pivot,scale,shiftによってRGB値を変化する */ - double rr2, gg2, bb2; - pixel_rgba_(rr1, gg1, bb1, rr2, gg2, bb2, hue_pivot, hue_scale, hue_shift, - sat_pivot, sat_scale, sat_shift, val_pivot, val_scale, - val_shift); + double rr, gg, bb; + pixel_rgba_(image_array[red], image_array[gre], image_array[blu], rr, gg, + bb, hue_pivot, hue_scale, hue_shift, sat_pivot, sat_scale, + sat_shift, val_pivot, val_scale, val_shift); /* 加算合成で、その値がMaxでなければ変化量に乗算 */ - if (add_blend_sw && (ia[alp] < t_max)) { - refv *= static_cast(ia[alp]) / div_val; + if (add_blend_sw && (image_array[alp] < 1.f)) { + refv *= image_array[alp]; } /* ピクセル単位の変化量があれば、RGBを調整する */ - if ((ref != 0) || (add_blend_sw && (ia[alp] < t_max))) { - rr2 = rr1 + (rr2 - rr1) * refv; - gg2 = gg1 + (gg2 - gg1) * refv; - bb2 = bb1 + (bb2 - bb1) * refv; + if ((ref != 0) || (add_blend_sw && (image_array[alp] < 1.f))) { + rr = image_array[red] + (rr - image_array[red]) * refv; + gg = image_array[gre] + (gg - image_array[gre]) * refv; + bb = image_array[blu] + (bb - image_array[blu]) * refv; } /* 結果をRGBに戻す */ - image_array[red] = static_cast(rr2 * mul_val); - image_array[gre] = static_cast(gg2 * mul_val); - image_array[blu] = static_cast(bb2 * mul_val); + image_array[red] = rr; + image_array[gre] = gg; + image_array[blu] = bb; } } else if (igs::image::rgb::siz == channels) { using namespace igs::image::rgb; for (int ii = 0; ii < pixsize; ++ii, image_array += channels) { - double refv = 1.0; + float refv = 1.f; if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; + refv *= (*ref); + ref++; } - const IT *const ia = image_array; - const double rr1 = static_cast(ia[red]) / div_val; - const double gg1 = static_cast(ia[gre]) / div_val; - const double bb1 = static_cast(ia[blu]) / div_val; - double rr2, gg2, bb2; - pixel_rgba_(rr1, gg1, bb1, rr2, gg2, bb2, hue_pivot, hue_scale, hue_shift, - sat_pivot, sat_scale, sat_shift, val_pivot, val_scale, - val_shift); - - if (ref != 0) { - rr2 = rr1 + (rr2 - rr1) * refv; - gg2 = gg1 + (gg2 - gg1) * refv; - bb2 = bb1 + (bb2 - bb1) * refv; + // const IT *const ia = image_array; + // const double rr1 = static_cast(ia[red]) / div_val; + // const double gg1 = static_cast(ia[gre]) / div_val; + // const double bb1 = static_cast(ia[blu]) / div_val; + double rr, gg, bb; + pixel_rgba_(image_array[red], image_array[gre], image_array[blu], rr, gg, + bb, hue_pivot, hue_scale, hue_shift, sat_pivot, sat_scale, + sat_shift, val_pivot, val_scale, val_shift); + + if (ref != nullptr) { + rr = image_array[red] + (rr - image_array[red]) * refv; + gg = image_array[gre] + (gg - image_array[gre]) * refv; + bb = image_array[blu] + (bb - image_array[blu]) * refv; } - image_array[red] = static_cast(rr2 * mul_val); - image_array[gre] = static_cast(gg2 * mul_val); - image_array[blu] = static_cast(bb2 * mul_val); + image_array[red] = rr; + image_array[gre] = gg; + image_array[blu] = bb; } } else if (1 == channels) { /* grayscale */ for (int ii = 0; ii < pixsize; ++ii, image_array += channels) { - double refv = 1.0; - if (ref != 0) { - refv *= igs::color::ref_value(ref, channels, r_max, ref_mode); - ref += channels; + float refv = 1.f; + if (ref != nullptr) { + refv *= (*ref); + ref++; } - double li1 = static_cast(image_array[0]) / div_val, - li2 = (li1 - val_pivot) * val_scale + val_pivot + val_shift; - li2 = (li2 < 0.0) ? 0.0 : ((1.0 < li2) ? 1.0 : li2); + double li = + (*image_array - val_pivot) * val_scale + val_pivot + val_shift; - if (ref != 0) { - li2 = li1 + (li2 - li1) * refv; + if (ref != nullptr) { + li = *image_array + (li - *image_array) * refv; } - image_array[0] = static_cast(li2 * mul_val); + *image_array = li; } } } -} +} // namespace #include /* std::domain_error */ void igs::hsv_adjust::change( - unsigned char *image_array, const int height, const int width, - const int channels, const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , + float *image_array, const int height, const int width, const int channels, + const float *ref, /* 求める画像と同じ高、幅、channels数 */ const double hue_pivot, const double hue_scale, const double hue_shift, const double sat_pivot, const double sat_scale, const double sat_shift, - const double val_pivot, const double val_scale, const double val_shift - - , + const double val_pivot, const double val_scale, const double val_shift, const bool add_blend_sw) { if ((1.0 == hue_scale) && (0.0 == hue_shift) && (1.0 == sat_scale) && (0.0 == sat_shift) && (1.0 == val_scale) && (0.0 == val_shift)) { @@ -208,11 +183,15 @@ void igs::hsv_adjust::change( if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } + change_(image_array, height, width, channels, ref, hue_pivot, hue_scale, + hue_shift, sat_pivot, sat_scale, sat_shift, val_pivot, val_scale, + val_shift, add_blend_sw); /* rgb(a)画像にhsv(a)でドットノイズを加える */ + /* if ((std::numeric_limits::digits == bits) && ((std::numeric_limits::digits == ref_bits) || (0 == ref_bits))) { @@ -242,4 +221,5 @@ void igs::hsv_adjust::change( } else { throw std::domain_error("Bad bits,Not uchar/ushort"); } + */ } diff --git a/toonz/sources/stdfx/igs_hsv_adjust.h b/toonz/sources/stdfx/igs_hsv_adjust.h index 234f5df..40444e3 100644 --- a/toonz/sources/stdfx/igs_hsv_adjust.h +++ b/toonz/sources/stdfx/igs_hsv_adjust.h @@ -10,36 +10,17 @@ namespace igs { namespace hsv_adjust { IGS_HSV_ADJUST_EXPORT void change( - unsigned char *image_array, const int height, const int width, - const int channels, const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - - , - const double hue_pivot /* 0.0 ...0...360... */ - , - const double hue_scale /* 1.0 ...1... */ - , - const double hue_shift /* 0.0 ...0...360... */ - , - const double sat_pivot /* 0.0 ...0...1... */ - , - const double sat_scale /* 1.0 ...1... */ - , - const double sat_shift /* 0.0 ...0...1... */ - , - const double val_pivot /* 0.0 ...0...1... */ - , - const double val_scale /* 1.0 ...1... */ - , - const double val_shift /* 0.0 ...0...1... */ - - , + float *image_array, const int height, const int width, const int channels, + const float *ref, /* 求める画像と同じ高、幅、channels数 */ + const double hue_pivot, /* 0.0 ...0...360... */ + const double hue_scale, /* 1.0 ...1... */ + const double hue_shift, /* 0.0 ...0...360... */ + const double sat_pivot, /* 0.0 ...0...1... */ + const double sat_scale, /* 1.0 ...1... */ + const double sat_shift, /* 0.0 ...0...1... */ + const double val_pivot, /* 0.0 ...0...1... */ + const double val_scale, /* 1.0 ...1... */ + const double val_shift, /* 0.0 ...0...1... */ const bool add_blend_sw /* 効果(変化量)をアルファブレンドするか否かのスイッチ add_blend_sw == true @@ -53,8 +34,8 @@ IGS_HSV_ADJUST_EXPORT void change( 合成画 = 下絵 * (1 - alpha) + 上絵 * alpha の場合こちらを使う */ - ); -} +); } +} // namespace igs #endif /* !igs_hsv_adjust_h */ diff --git a/toonz/sources/stdfx/igs_hsv_noise.cpp b/toonz/sources/stdfx/igs_hsv_noise.cpp index 63805bf..c969959 100644 --- a/toonz/sources/stdfx/igs_hsv_noise.cpp +++ b/toonz/sources/stdfx/igs_hsv_noise.cpp @@ -395,10 +395,11 @@ void pixel_rgb_(const double red_in, const double gre_in, const double blu_in, sat += satnoise * alp_in; if (sat < 0.0) { sat = 0.0; - } else if (1.0 < sat) { - sat = 1.0; } - // if( 0.0 == sat ) hue = -1.0; // hsv_to_rgb(-) + // else if (1.0 < sat) { + // sat = 1.0; + // } + // if( 0.0 == sat ) hue = -1.0; // hsv_to_rgb(-) } if (0.0 != val_term.noise_range()) { double shift_value = 0; @@ -406,11 +407,11 @@ void pixel_rgb_(const double red_in, const double gre_in, const double blu_in, val_term.exec(val, valnoise, shift_value); val += shift_value * alp_in; val += valnoise * alp_in; - if (val < 0.0) { - val = 0.0; - } else if (1.0 < val) { - val = 1.0; - } + // if (val < 0.0) { + // val = 0.0; + // } else if (1.0 < val) { + // val = 1.0; + // } } igs::color::hsv_to_rgb(hue, sat, val, red_out, gre_out, blu_out); } @@ -482,7 +483,7 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ if (((0.0 != hue_range) || (0.0 != val_term.noise_range()) || (0.0 != sat_term.noise_range())) /* ノイズがhsvのどれか一つはある */ - ) { + ) { double rr1 = static_cast(image_array[red]) / div_val, gg1 = static_cast(image_array[gre]) / div_val, bb1 = static_cast(image_array[blu]) / div_val, @@ -496,6 +497,10 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ gg2 = (gg2 - gg1) * refv + gg1; bb2 = (bb2 - bb1) * refv + bb1; } + rr2 = (rr2 < 0.) ? 0. : (rr2 > 1.) ? 1. : rr2; + gg2 = (gg2 < 0.) ? 0. : (gg2 > 1.) ? 1. : gg2; + bb2 = (bb2 < 0.) ? 0. : (bb2 > 1.) ? 1. : bb2; + image_array[red] = static_cast(rr2 * mul_val); image_array[gre] = static_cast(gg2 * mul_val); image_array[blu] = static_cast(bb2 * mul_val); @@ -515,7 +520,7 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ using namespace igs::image::rgb; if (((0.0 != hue_range) || (0.0 != sat_term.noise_range()) || (0.0 != val_term.noise_range())) /* ノイズがhsvのどれか一つはある */ - ) { + ) { for (int yy = 0; yy < height; ++yy) { for (int xx = 0; xx < width; ++xx, image_array += channels) { /* 変化量初期値 */ @@ -539,6 +544,10 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ gg2 = (gg2 - gg1) * refv + gg1; bb2 = (bb2 - bb1) * refv + bb1; } + rr2 = (rr2 < 0.) ? 0. : (rr2 > 1.) ? 1. : rr2; + gg2 = (gg2 < 0.) ? 0. : (gg2 > 1.) ? 1. : gg2; + bb2 = (bb2 < 0.) ? 0. : (bb2 > 1.) ? 1. : bb2; + image_array[red] = static_cast(rr2 * mul_val); image_array[gre] = static_cast(gg2 * mul_val); image_array[blu] = static_cast(bb2 * mul_val); @@ -578,41 +587,154 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ } } } + +/*------ raster画像にノイズをのせる ------*/ + +void change_(float *image_array, const int width, const int height, + const int channels, + const float *ref, /* 求める画像(out)と同じ高さ*/ + noise_reference_ &noise, const double hue_range, + control_term_within_limits_ &sat_term, + control_term_within_limits_ &val_term, + control_term_within_limits_ &alp_term, const bool add_blend_sw) { + if (igs::image::rgba::siz == channels) { + using namespace igs::image::rgba; + for (int yy = 0; yy < height; ++yy) { + for (int xx = 0; xx < width; ++xx, image_array += channels) { + /* 変化量初期値 */ + float refv = 1.f; + + /* 参照画像あればピクセル単位の画像変化量を得る */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ + } + /* 加算合成で、Alpha値ゼロならRGB値を計算する必要はない */ + if (add_blend_sw && (0 == image_array[alp])) { + continue; + } + /* 加算合成でなくAlpha合成の時は、 +Alpha値がゼロでもRGB値は存在する(してもよい) */ + + /* マスクSWがON、なら変化をMask */ + if (add_blend_sw && (image_array[alp] < 1.f)) { + refv *= image_array[alp]; + } + + if (((0.0 != hue_range) || (0.0 != val_term.noise_range()) || + (0.0 != + sat_term.noise_range())) /* ノイズがhsvのどれか一つはある */ + ) { + float rr1 = image_array[red], gg1 = image_array[gre], + bb1 = image_array[blu], aa1 = image_array[alp]; + double rr2 = 0., gg2 = 0., bb2 = 0.; + pixel_rgb_(rr1, gg1, bb1, aa1, noise.hue_value(xx, yy), + noise.sat_value(xx, yy), noise.val_value(xx, yy), sat_term, + val_term, rr2, gg2, bb2); + if (refv != 1.f) { + rr2 = (rr2 - rr1) * refv + rr1; + gg2 = (gg2 - gg1) * refv + gg1; + bb2 = (bb2 - bb1) * refv + bb1; + } + image_array[red] = static_cast(rr2); + image_array[gre] = static_cast(gg2); + image_array[blu] = static_cast(bb2); + } + if (0.0 != alp_term.noise_range()) { + double aa1 = static_cast(image_array[alp]); + double aa2 = 0.; + pixel_a_(aa1, noise.alp_value(xx, yy), alp_term, aa2); + if (refv != 1.f) { + aa2 = (aa2 - aa1) * refv + aa1; + } + image_array[alp] = static_cast(aa2); + } + } + } + } else if (igs::image::rgb::siz == channels) { + using namespace igs::image::rgb; + if (((0.0 != hue_range) || (0.0 != sat_term.noise_range()) || + (0.0 != val_term.noise_range())) /* ノイズがhsvのどれか一つはある */ + ) { + for (int yy = 0; yy < height; ++yy) { + for (int xx = 0; xx < width; ++xx, image_array += channels) { + /* 変化量初期値 */ + float refv = 1.f; + + /* 参照画像あればピクセル単位の画像変化量を得る */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ + } + + float rr1 = image_array[red], gg1 = image_array[gre], + bb1 = image_array[blu]; + double rr2 = 0., gg2 = 0., bb2 = 0.; + pixel_rgb_(rr1, gg1, bb1, 1.0, noise.hue_value(xx, yy), + noise.sat_value(xx, yy), noise.val_value(xx, yy), sat_term, + val_term, rr2, gg2, bb2); + if (refv != 1.f) { + rr2 = (rr2 - rr1) * refv + rr1; + gg2 = (gg2 - gg1) * refv + gg1; + bb2 = (bb2 - bb1) * refv + bb1; + } + image_array[red] = static_cast(rr2); + image_array[gre] = static_cast(gg2); + image_array[blu] = static_cast(bb2); + } + } + } + } else if (1 == channels) { /* grayscale */ + if (0.0 != val_term.noise_range()) { + for (int yy = 0; yy < height; ++yy) { + for (int xx = 0; xx < width; ++xx, ++image_array) { + /* 変化量初期値 */ + float refv = 1.f; + + /* 参照画像あればピクセル単位の画像変化量を得る */ + if (ref != 0) { + refv *= (*ref); + ref++; /* continue;の前に行うこと */ + } + + double va1 = static_cast(image_array[0]); + double shift_value = 0; + double val_noise = noise.val_value(xx, yy); + val_term.exec(va1, val_noise, shift_value); + + double va2 = va1; + va2 += shift_value; + va2 += val_noise; + va2 = (va2 < 0.0) ? 0.0 : ((1.0 < va2) ? 1.0 : va2); + + if (refv != 1.f) { + va2 = va1 + (va2 - va1) * refv; + } + + image_array[0] = static_cast(va2); + } + } + } + } } + +} // namespace //-------------------------------------------------------------------- #include // std::domain_error #include "igs_hsv_noise.h" void igs::hsv_noise::change( - unsigned char *image_array - - , - const int height, const int width, const int channels, const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - + float *image_array, const int height, const int width, const int channels, + const float *ref, /* 求める画像と同じ高、幅 */ /* image_arrayに余白が変化してもノイズパターンが変わらない ようにするためにカメラエリアを指定する */ - , const int camera_x, const int camera_y, const int camera_w, - const int camera_h - - , - const double hue_range, const double sat_range, const double val_range, - const double alp_range, const unsigned long random_seed, - const double near_blur - - , + const int camera_h, const double hue_range, const double sat_range, + const double val_range, const double alp_range, + const unsigned long random_seed, const double near_blur, const double sat_effective, const double sat_center, const int sat_type, const double val_effective, const double val_center, const int val_type, - const double alp_effective, const double alp_center, const int alp_type - - , + const double alp_effective, const double alp_center, const int alp_type, const bool add_blend_sw) { if ((0.0 == hue_range) && (0.0 == sat_range) && (0.0 == val_range) && (0.0 == alp_range)) { @@ -621,7 +743,7 @@ void igs::hsv_noise::change( if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } @@ -638,35 +760,40 @@ void igs::hsv_noise::change( control_term_within_limits_ alp_term(alp_effective, alp_effective, alp_center, alp_type, alp_range); - /* rgb(a)画像にhsv(a)でドットノイズを加える */ - if ((std::numeric_limits::digits == bits) && - ((std::numeric_limits::digits == ref_bits) || - (0 == ref_bits))) { - change_template_(image_array, width, height, channels, ref, ref_mode, noise, - hue_range, sat_term, val_term, alp_term, add_blend_sw); - noise.clear(); /* ノイズ画像メモリ解放 */ - } else if ((std::numeric_limits::digits == bits) && - ((std::numeric_limits::digits == ref_bits) || - (0 == ref_bits))) { - change_template_(reinterpret_cast(image_array), width, - height, channels, ref, ref_mode, noise, hue_range, - sat_term, val_term, alp_term, add_blend_sw); - noise.clear(); /* ノイズ画像メモリ解放 */ - } else if ((std::numeric_limits::digits == bits) && - (std::numeric_limits::digits == ref_bits)) { - change_template_( - reinterpret_cast(image_array), width, height, - channels, reinterpret_cast(ref), ref_mode, - noise, hue_range, sat_term, val_term, alp_term, add_blend_sw); - noise.clear(); /* ノイズ画像メモリ解放 */ - } else if ((std::numeric_limits::digits == bits) && - (std::numeric_limits::digits == ref_bits)) { - change_template_(image_array, width, height, channels, - reinterpret_cast(ref), ref_mode, - noise, hue_range, sat_term, val_term, alp_term, - add_blend_sw); - noise.clear(); /* ノイズ画像メモリ解放 */ - } else { - throw std::domain_error("Bad bits,Not uchar/ushort"); - } + change_(image_array, width, height, channels, ref, noise, hue_range, sat_term, + val_term, alp_term, add_blend_sw); + noise.clear(); /* ノイズ画像メモリ解放 */ + + ///* rgb(a)画像にhsv(a)でドットノイズを加える */ + // if ((std::numeric_limits::digits == bits) && + // ((std::numeric_limits::digits == ref_bits) || + // (0 == ref_bits))) { + // change_template_(image_array, width, height, channels, ref, ref_mode, + // noise, + // hue_range, sat_term, val_term, alp_term, add_blend_sw); + // noise.clear(); /* ノイズ画像メモリ解放 */ + // } else if ((std::numeric_limits::digits == bits) && + // ((std::numeric_limits::digits == ref_bits) || + // (0 == ref_bits))) { + // change_template_(reinterpret_cast(image_array), width, + // height, channels, ref, ref_mode, noise, hue_range, + // sat_term, val_term, alp_term, add_blend_sw); + // noise.clear(); /* ノイズ画像メモリ解放 */ + // } else if ((std::numeric_limits::digits == bits) && + // (std::numeric_limits::digits == ref_bits)) { + // change_template_( + // reinterpret_cast(image_array), width, height, + // channels, reinterpret_cast(ref), ref_mode, + // noise, hue_range, sat_term, val_term, alp_term, add_blend_sw); + // noise.clear(); /* ノイズ画像メモリ解放 */ + // } else if ((std::numeric_limits::digits == bits) && + // (std::numeric_limits::digits == ref_bits)) { + // change_template_(image_array, width, height, channels, + // reinterpret_cast(ref), ref_mode, + // noise, hue_range, sat_term, val_term, alp_term, + // add_blend_sw); + // noise.clear(); /* ノイズ画像メモリ解放 */ + // } else { + // throw std::domain_error("Bad bits,Not uchar/ushort"); + // } } diff --git a/toonz/sources/stdfx/igs_hsv_noise.h b/toonz/sources/stdfx/igs_hsv_noise.h index a37e339..0bc760c 100644 --- a/toonz/sources/stdfx/igs_hsv_noise.h +++ b/toonz/sources/stdfx/igs_hsv_noise.h @@ -10,60 +10,29 @@ namespace igs { namespace hsv_noise { IGS_HSV_NOISE_EXPORT void change( - unsigned char *image_array - - , - const int height, const int width, const int channels, const int bits - - , - const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */ - , - const int ref_bits /* refがゼロのときはここもゼロ */ - , - const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ - + float *image_array, const int height, const int width, const int channels, + const float *ref, /* 求める画像と同じ高、幅 */ /* image_arrayに余白が変化してもノイズパターンが変わらない ようにするためにカメラエリアを指定する */ - , const int camera_x, const int camera_y, const int camera_w, - const int camera_h - - , - const double hue_range /* =0.025 0 ... 1.0 */ - , - const double sat_range /* =0.0 0 ... 1.0 */ - , - const double val_range /* =0.035 0 ... 1.0 */ - , - const double alp_range /* =0.0 0 ... 1.0 */ - , - const unsigned long random_seed /* =1 0 ... ULONG_MAX */ - , - const double near_blur /* =0.500 0 ... 0.5 */ - - , - const double sat_effective /* =0.0 0 ... 1.0 */ - , - const double sat_center /* =0.5 0 ... 1.0 */ - , - const int sat_type /* =0 - 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ - , - const double val_effective /* =0.0 0 ... 1.0 */ - , - const double val_center /* =0.5 0 ... 1.0 */ - , - const int val_type /* =0 - 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ - , - const double alp_effective /* =0.0 0 ... 1.0 */ - , - const double alp_center /* =0.5 0 ... 1.0 */ - , - const int alp_type /* =0 - 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ - - , + const int camera_h, const double hue_range, /* =0.025 0 ... 1.0 */ + const double sat_range, /* =0.0 0 ... 1.0 */ + const double val_range, /* =0.035 0 ... 1.0 */ + const double alp_range, /* =0.0 0 ... 1.0 */ + const unsigned long random_seed, /* =1 0 ... ULONG_MAX */ + const double near_blur, /* =0.500 0 ... 0.5 */ + const double sat_effective, /* =0.0 0 ... 1.0 */ + const double sat_center, /* =0.5 0 ... 1.0 */ + const int sat_type, /* =0 + 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ + const double val_effective, /* =0.0 0 ... 1.0 */ + const double val_center, /* =0.5 0 ... 1.0 */ + const int val_type, /* =0 + 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ + const double alp_effective, /* =0.0 0 ... 1.0 */ + const double alp_center, /* =0.5 0 ... 1.0 */ + const int alp_type, /* =0 + 0(shift_whole),1(shift_term),2(decrease_whole),3(decrease_term) */ const bool add_blend_sw /* 効果(変化量)をアルファブレンドするか否かのスイッチ add_blend_sw == true @@ -77,8 +46,8 @@ IGS_HSV_NOISE_EXPORT void change( 合成画 = 下絵 * (1 - alpha) + 上絵 * alpha の場合こちらを使う */ - ); -} +); } +} // namespace igs #endif /* !igs_hsv_noise_h */ diff --git a/toonz/sources/stdfx/igs_level_auto_in_camera.cpp b/toonz/sources/stdfx/igs_level_auto_in_camera.cpp index ca08959..96d7f4d 100644 --- a/toonz/sources/stdfx/igs_level_auto_in_camera.cpp +++ b/toonz/sources/stdfx/igs_level_auto_in_camera.cpp @@ -47,6 +47,42 @@ double level_value_(double value, double mul_max, bool act_sw, double in_min, /* 0〜1.0 --> 0〜mul_maxスケール変換し、整数値化 */ return floor(value * mul_max); } + +//------------------------------------------------------------ + +struct LevelAutoValueF { + double in_min[4]; + double out_min[4]; + double gamma[4]; + double in_max_minus_in_min[4]; + double out_max_minus_out_min[4]; + LevelAutoValueF(const int channels, const float *_in_min, + const float *_in_max, const double *_in_min_shift, + const double *_in_max_shift, const double *_out_min, + const double *_out_max, const double *_gamma) { + for (int c = 0; c < channels; c++) { + in_min[c] = _in_min[c] + _in_min_shift[c]; + out_min[c] = _out_min[c]; + gamma[c] = _gamma[c]; + in_max_minus_in_min[c] = _in_max[c] + _in_max_shift[c] - in_min[c]; + out_max_minus_out_min[c] = _out_max[c] - _out_min[c]; + } + } + + inline float convert(const int c, float value) { + if (in_max_minus_in_min[c] == 0.0) { + value = in_min[c]; + } else // normalize + value = (value - in_min[c]) / in_max_minus_in_min[c]; + // gamma transform + if ((1.0 != gamma[c]) && (0.0 != gamma[c]) && value > 0.0) + value = std::pow(value, 1.0 / gamma[c]); + // normalize to the output range + value = out_min[c] + value * out_max_minus_out_min[c]; + return value; + } +}; + //------------------------------------------------------------ void level_ctable_template_(const unsigned int channels, const bool *act_sw, // user setting @@ -67,7 +103,7 @@ void level_ctable_template_(const unsigned int channels, ならOK 2009-01-27 */ - ) { +) { const double div_val = static_cast(div_num); const double mul_val = div_val + 0.999999; #if defined _WIN32 // vc compile_type @@ -163,33 +199,95 @@ void change_template_(T *image_array, const int height, const int width, table_array.clear(); } + +//------------------------------------------------------------ + +template <> +void change_template_(float *image_array, const int height, + const int width, const int channels, + const bool *act_sw, const double *in_min_shift, + const double *in_max_shift, const double *out_min, + const double *out_max, const double *gamma, + const int camera_x, const int camera_y, + const int camera_w, const int camera_h) { + /* 1.まずcameraエリア内の最大値、最小値を求める */ +#if defined _WIN32 + float in_min[4], in_max[4]; +#else + float in_min[channels], in_max[channels]; +#endif + float *image_crnt = + image_array + camera_y * width * channels + camera_x * channels; + for (int zz = 0; zz < channels; ++zz) { + in_min[zz] = in_max[zz] = image_crnt[zz]; + } + float *image_xx = nullptr; + for (int yy = 0; yy < camera_h; ++yy) { + image_xx = image_crnt; + image_crnt += width * channels; + for (int xx = 0; xx < camera_w; ++xx) { + for (int zz = 0; zz < channels; ++zz) { + if (image_xx[zz] < in_min[zz]) { + in_min[zz] = image_xx[zz]; + } else if (in_max[zz] < image_xx[zz]) { + in_max[zz] = image_xx[zz]; + } + } + image_xx += channels; + } + } + + /* 2.最大値、最小値から変換テーブルを求める */ + // std::vector> table_array; + // + // level_ctable_template_(channels, act_sw, in_min, in_max, in_min_shift, + // in_max_shift, out_min, out_max, gamma, + // std::numeric_limits::max(), table_array); + + LevelAutoValueF converter(channels, in_min, in_max, in_min_shift, + in_max_shift, out_min, out_max, gamma); + + /* 画像全体をlevel変換する */ + image_crnt = image_array; + const int pixsize = height * width; + + if (igs::image::rgba::siz == channels) { + using namespace igs::image::rgba; + for (int ii = 0; ii < pixsize; ++ii, image_crnt += channels) { + image_crnt[red] = converter.convert(0, image_crnt[red]); + image_crnt[gre] = converter.convert(1, image_crnt[gre]); + image_crnt[blu] = converter.convert(2, image_crnt[blu]); + image_crnt[alp] = converter.convert(3, image_crnt[alp]); + } + } else if (igs::image::rgb::siz == channels) { + using namespace igs::image::rgb; + for (int ii = 0; ii < pixsize; ++ii, image_crnt += channels) { + image_crnt[red] = converter.convert(0, image_crnt[red]); + image_crnt[gre] = converter.convert(1, image_crnt[gre]); + image_crnt[blu] = converter.convert(2, image_crnt[blu]); + } + } else if (1 == channels) { /* grayscale */ + for (int ii = 0; ii < pixsize; ++ii, ++image_crnt) { + image_crnt[0] = converter.convert(0, image_crnt[0]); + } + } } +} // namespace void igs::level_auto_in_camera::change( - void *image_array - - , - const int height, const int width, const int channels, const int bits - - , - const bool *act_sw // channels array - , - const double *in_min_shift // channels array - , - const double *in_max_shift // channels array - , - const double *out_min // channels array - , - const double *out_max // channels array - , - const double *gamma // channels array - - , + void *image_array, const int height, const int width, const int channels, + const int bits, + const bool *act_sw, // channels array + const double *in_min_shift, // channels array + const double *in_max_shift, // channels array + const double *out_min, // channels array + const double *out_max, // channels array + const double *gamma, // channels array const int camera_x, const int camera_y, const int camera_w, const int camera_h) { if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } @@ -201,6 +299,10 @@ void igs::level_auto_in_camera::change( change_template_(static_cast(image_array), height, width, channels, act_sw, in_min_shift, in_max_shift, out_min, out_max, gamma, camera_x, camera_y, camera_w, camera_h); + } else if (std::numeric_limits::digits == bits) { + change_template_(static_cast(image_array), height, width, channels, + act_sw, in_min_shift, in_max_shift, out_min, out_max, + gamma, camera_x, camera_y, camera_w, camera_h); } else { throw std::domain_error("Bad bits,Not uchar/ushort"); } diff --git a/toonz/sources/stdfx/igs_level_auto_in_camera.h b/toonz/sources/stdfx/igs_level_auto_in_camera.h index 89409d7..71758ed 100644 --- a/toonz/sources/stdfx/igs_level_auto_in_camera.h +++ b/toonz/sources/stdfx/igs_level_auto_in_camera.h @@ -10,28 +10,17 @@ namespace igs { namespace level_auto_in_camera { IGS_LEVEL_AUTO_IN_CAMERA_EXPORT void change( - void *image_array - - , - const int height, const int width, const int channels, const int bits - - , - const bool *act_sw // true(false/true) - , - const double *in_min_shift // 0(-1...1) - , - const double *in_max_shift // 0(-1...1) - , - const double *out_min // 0(0...1) - , - const double *out_max // 1(0...1) - , - const double *gamma // 1(0.01...100) - - , + void *image_array, const int height, const int width, const int channels, + const int bits, + const bool *act_sw, // true(false/true) + const double *in_min_shift, // 0(-1...1) + const double *in_max_shift, // 0(-1...1) + const double *out_min, // 0(0...1) + const double *out_max, // 1(0...1) + const double *gamma, // 1(0.01...100) const int camera_x, const int camera_y, const int camera_w, const int camera_h); } -} +} // namespace igs #endif /* !igs_level_auto_in_camera_h */ diff --git a/toonz/sources/stdfx/igs_levels.cpp b/toonz/sources/stdfx/igs_levels.cpp index 60a7ec0..731bb1c 100644 --- a/toonz/sources/stdfx/igs_levels.cpp +++ b/toonz/sources/stdfx/igs_levels.cpp @@ -2,9 +2,12 @@ #include // std::domain_error(-) #include // std::numeric_limits #include +#include #include "igs_ifx_common.h" /* igs::image::rgba */ #include "igs_levels.h" +#include "tutil.h" // areAlmostEqual + //------------------------------------------------------------ namespace { void levels_(double &val // 0...1 @@ -24,8 +27,10 @@ void levels_(double &val // 0...1 val = (in_max == in_min) ? in_max : (val - in_min) / (in_max - in_min); /* 2 (出力範囲に)clamp */ - if (clamp_sw) { + if (clamp_sw || !areAlmostEqual(out_max, 1.)) { val = (val < 0.0) ? 0.0 : ((1.0 < val) ? 1.0 : val); + } else { + val = (val < 0.0) ? 0.0 : val; } /* 3 正規化の範囲でgamma変換 */ @@ -33,18 +38,28 @@ void levels_(double &val // 0...1 if ((0.0 < val) && (val < 1.0)) { val = pow(val, 1.0 / gamma); } + // ガンマ補正の線を直線で延長する + else if (val > 1.0) { + val = 1. + (val - 1.) / gamma; + } } /* 4 正規化範囲を出力範囲に */ val = out_min + val * (out_max - out_min); /* 5 clamp */ - val = (val < 0.0) ? 0.0 : ((1.0 < val) ? 1.0 : val); + if (clamp_sw) + val = (val < 0.0) ? 0.0 : ((1.0 < val) ? 1.0 : val); + else + val = (val < 0.0) ? 0.0 : val; } double refchk_(const int src, const int tgt, const double refv) { return (src < tgt) ? (tgt - src + 0.999999) * refv + src : (src - tgt + 0.999999) * (1.0 - refv) + tgt; } +float refchk_(const float src, const float tgt, const double refv) { + return tgt * refv + src * (1. - refv); +} //------------------------------------------------------------ /* std::vector< std::vector > &tables @@ -196,7 +211,152 @@ void change_(IT *image_array, const int height, const int width, /* 1 変換テーブルのメモリ解放 */ tables.clear(); } + +template <> +void change_(float *image_array, const int height, const int width, + const int channels, + const float *ref, /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ + const int ref_mode, // R,G,B,A,luminance + const double r_in_min, const double r_in_max, // 0...1 + const double g_in_min, const double g_in_max, // 0...1 + const double b_in_min, const double b_in_max, // 0...1 + const double a_in_min, const double a_in_max, // 0...1 + const double r_gamma, // 0.1 ... 10.0 + const double g_gamma, // 0.1 ... 10.0 + const double b_gamma, // 0.1 ... 10.0 + const double a_gamma, // 0.1 ... 10.0 + const double r_out_min, const double r_out_max, // 0...1 + const double g_out_min, const double g_out_max, // 0...1 + const double b_out_min, const double b_out_max, // 0...1 + const double a_out_min, const double a_out_max, // 0...1 + const bool clamp_sw, const bool alpha_sw, + const bool add_blend_sw) { + /* 1 最大値、最小値から変換テーブルを求める */ + std::vector> tables; + const unsigned int table_size = + std::numeric_limits::max() + 1; + const double div_val = static_cast(table_size - 1); + // const double mul_val = div_val + 0.999999; + { + using namespace igs::image::rgba; + tables.resize(siz); + tables[red].resize(table_size); + tables[gre].resize(table_size); + tables[blu].resize(table_size); + tables[alp].resize(table_size); + for (unsigned int yy = 0; yy < table_size; ++yy) { + double rr, gg, bb, aa; + rr = gg = bb = aa = yy / div_val; + levels_(rr, r_in_min, r_in_max, r_gamma, r_out_min, r_out_max, + false); // クランプしない + levels_(gg, g_in_min, g_in_max, g_gamma, g_out_min, g_out_max, + false); // クランプしない + levels_(bb, b_in_min, b_in_max, b_gamma, b_out_min, b_out_max, + false); // クランプしない + levels_(aa, a_in_min, a_in_max, a_gamma, a_out_min, a_out_max, clamp_sw); + /* 0〜1.0 --> 0〜mul_maxスケール変換し、整数値化 */ + tables[red][yy] = static_cast(rr); + tables[gre][yy] = static_cast(gg); + tables[blu][yy] = static_cast(bb); + tables[alp][yy] = static_cast(aa); + } + } + + // テーブル値を返すラムダ式。Inが1より大きい場合はmaxの線を直線で延長 + auto getTableVal = [&](int channel, float in) { + if (in <= 0.f) { // 先頭の値を返す + return tables[channel][0]; + } else if (in <= + 1.f) { // テーブル範囲に収まっている場合、前後の値で線形補間 + int index = static_cast(std::floor(in * div_val)); + float ratio = in * div_val - static_cast(index); + if (areAlmostEqual(ratio, 0.)) return tables[channel][index]; + return tables[channel][index] * (1.f - ratio) + + tables[channel][index + 1] * ratio; + } + // inが1より大きい場合 + // 傾き + float dv = + (tables[channel][table_size - 1] - tables[channel][table_size - 2]) * + div_val; + return tables[channel][table_size - 1] + dv * (in - 1.f); + }; + + /* 2 変換テーブルを使ってlevel変換する */ + const int pixsize = height * width; + if (igs::image::rgba::siz == channels) { + using namespace igs::image::rgba; + for (int ii = 0; ii < pixsize; ++ii, image_array += channels) { + /* 変化量初期値 */ + double refv = 1.0; + + /* 参照画像あればピクセル単位の画像変化量を得る */ + if (ref != 0) { + refv *= igs::color::ref_value(ref, channels, 1.0, ref_mode); + // clamp 0. to 1. in case the reference is more than 1 + refv = std::min(1., std::max(0., refv)); + ref += channels; /* continue;の前に行うこと */ + } + + /* AlphaのLevel処理した値をrefvの変化量比で加える */ + if (alpha_sw) { + image_array[alp] = + refchk_(image_array[alp], getTableVal(alp, image_array[alp]), refv); + } + + /* 加算合成で、Alpha値ゼロならRGB値を計算する必要はない */ + if (add_blend_sw && areAlmostEqual(image_array[alp], 0.)) { + continue; + } + /* 加算合成でなくAlpha合成の時は、Alpha値がゼロでも + RGB値は存在する(してもよい)の計算する */ + + /* 加算合成で、その値がMaxでなければ変化量に乗算 */ + if (add_blend_sw && (image_array[alp] < 1.f)) { + refv *= static_cast(image_array[alp]); + } + + /* RGBのLevel処理した値をrefvの変化量比で加える */ + image_array[red] = + refchk_(image_array[red], getTableVal(red, image_array[red]), refv); + image_array[gre] = + refchk_(image_array[gre], getTableVal(gre, image_array[gre]), refv); + image_array[blu] = + refchk_(image_array[blu], getTableVal(blu, image_array[blu]), refv); + } + } else if (igs::image::rgb::siz == channels) { + using namespace igs::image::rgb; + for (int ii = 0; ii < pixsize; ++ii, image_array += channels) { + double refv = 1.0; + if (ref != 0) { + refv *= igs::color::ref_value(ref, channels, 1.0, ref_mode); + ref += channels; + } + /* RGBのLevel処理した値をrefvの変化量比で加える */ + image_array[red] = + refchk_(image_array[red], getTableVal(red, image_array[red]), refv); + image_array[gre] = + refchk_(image_array[gre], getTableVal(gre, image_array[gre]), refv); + image_array[blu] = + refchk_(image_array[blu], getTableVal(blu, image_array[blu]), refv); + } + } else if (1 == channels) { /* grayscale */ + for (int ii = 0; ii < pixsize; ++ii, ++image_array) { + double refv = 1.0; + if (ref != 0) { + refv *= igs::color::ref_value(ref, channels, 1.0, ref_mode); + ref += channels; + } + image_array[0] = + refchk_(image_array[0], getTableVal(0, image_array[0]), refv); + } + } + + /* 1 変換テーブルのメモリ解放 */ + tables.clear(); } + +} // namespace //------------------------------------------------------------ void igs::levels::change( unsigned char *image_array, const int height, const int width, @@ -238,7 +398,7 @@ void igs::levels::change( const bool clamp_sw, const bool alpha_sw, const bool add_blend_sw) { if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } @@ -274,6 +434,14 @@ void igs::levels::change( a_in_max, r_gamma, g_gamma, b_gamma, a_gamma, r_out_min, r_out_max, g_out_min, g_out_max, b_out_min, b_out_max, a_out_min, a_out_max, clamp_sw, alpha_sw, add_blend_sw); + } else if (std::numeric_limits::digits == bits) { + assert(std::numeric_limits::digits == ref_bits || 0 == ref_bits); + change_(reinterpret_cast(image_array), height, width, channels, + reinterpret_cast(ref), ref_mode, r_in_min, r_in_max, + g_in_min, g_in_max, b_in_min, b_in_max, a_in_min, a_in_max, r_gamma, + g_gamma, b_gamma, a_gamma, r_out_min, r_out_max, g_out_min, + g_out_max, b_out_min, b_out_max, a_out_min, a_out_max, clamp_sw, + alpha_sw, add_blend_sw); } else { throw std::domain_error("Bad bits,Not uchar/ushort"); } diff --git a/toonz/sources/stdfx/igs_maxmin.cpp b/toonz/sources/stdfx/igs_maxmin.cpp index 59401ce..dd1ab3f 100644 --- a/toonz/sources/stdfx/igs_maxmin.cpp +++ b/toonz/sources/stdfx/igs_maxmin.cpp @@ -10,7 +10,8 @@ void igs::maxmin::convert( const unsigned char *inn, unsigned char *out , - const int height, const int width, const int channels, const int bits + const int height, const int width, const int channels, + const int bits /* Pixel毎に効果の強弱 */ , @@ -41,10 +42,10 @@ void igs::maxmin::convert( /* Speed up */ , const int number_of_thread /* =1 1...24...INT_MAX */ - ) { +) { if ((igs::image::rgba::siz != channels) && (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */ - ) { + ) { throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); } @@ -104,7 +105,17 @@ void igs::maxmin::convert( alpha_rendering_sw, add_blend_sw, number_of_thread); mthread.run(); mthread.clear(); + } else if ((std::numeric_limits::digits == bits) && + ((std::numeric_limits::digits == ref_bits) || + (0 == ref_bits))) { + igs::maxmin::multithread mthread( + reinterpret_cast(inn), reinterpret_cast(out), + height, width, channels, reinterpret_cast(ref), ref_mode, + radius, smooth_outer_range, polygon_number, roll_degree, min_sw, + alpha_rendering_sw, add_blend_sw, number_of_thread); + mthread.run(); + mthread.clear(); } else { - throw std::domain_error("Bad bits,Not uchar/ushort"); + throw std::domain_error("Bad bits,Not uchar/ushort/float"); } } diff --git a/toonz/sources/stdfx/igs_maxmin_getput.h b/toonz/sources/stdfx/igs_maxmin_getput.h index a515748..dcdad17 100644 --- a/toonz/sources/stdfx/igs_maxmin_getput.h +++ b/toonz/sources/stdfx/igs_maxmin_getput.h @@ -4,7 +4,7 @@ #define igs_maxmin_getput_h #include -#include /* std::numeric_limits<->::max() +#include /* std::numeric_limits<->::max() --> (std::numeric_limits<->::max)() */ #include "igs_ifx_common.h" // igs::color::ref_value(-) @@ -58,7 +58,7 @@ void inn_to_result_( const T *inn, const int height, const int width, const int channels, const int yy, const int zz, const double div_val, std::vector &result /* 元値をいれといて、結果を入れる */ - ) { +) { const T *ss = csl_top_clamped_in_h_(inn, height, width, channels, yy) + zz; for (int xx = 0; xx < width; ++xx) { result.at(xx) = ss[xx * channels] / div_val; @@ -78,8 +78,10 @@ void alpha_ref_mul_ref_(const RT *ref, const int height, const int width, const int r_max = (std::numeric_limits::max)(); const RT *rr = csl_top_clamped_in_h_(ref, height, width, channels, yy); for (int xx = 0; xx < width; ++xx) { - alpha_ref.at(xx) *= + double refv = igs::color::ref_value(&rr[xx * channels], channels, r_max, ref_mode); + // clamp 0 to 1 in case using HDR raster + alpha_ref.at(xx) *= std::min(1., std::max(0., refv)); } } /* Scanlineで、効果を調節するデータ(alpha_ref)に、 @@ -94,7 +96,7 @@ void alpha_ref_mul_alpha_(const T *out, const int height, const int width, alpha_ref.at(xx) *= dd[xx * channels] / div_val; } } -} +} // namespace //------------------------------------------------------------ namespace igs { namespace maxmin { @@ -121,7 +123,7 @@ void get_first( std::vector &alpha_ref /* pixel毎の変化の割合 */ , std::vector &result /* 元値をいれといて、結果を入れる */ - ) { +) { const int t_max = (std::numeric_limits::max)(); const double div_val = static_cast(t_max); @@ -147,6 +149,49 @@ void get_first( alpha_ref_mul_alpha_(out, hh, ww, ch, yy, div_val, alpha_ref); } } + +template <> +void get_first( + const float *inn /* outと同じ高さ、幅、チャンネル数 */ + , + const float *out /* outの処理結果alpha値をinとして使用 */ + , + const int hh, const int ww, const int ch, + const float *ref /* outと同じ高さ、幅、チャンネル数 */ + , + const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ + , + const int yy, const int zz, const int margin, const bool add_blend_sw, + std::vector> &tracks /* sl影響範囲のpixel値 */ + , + std::vector &alpha_ref /* pixel毎の変化の割合 */ + , + std::vector &result /* 元値をいれといて、結果を入れる */ +) { + const double div_val = 1.; + + /* 計算範囲の画像値を計算バッファ(tracks)に入れる */ + int ii = margin * 2; + for (int yp = -margin + yy; yp <= margin + yy; ++yp, --ii) { + const float *sl = csl_top_clamped_in_h_(inn, hh, ww, ch, yp) + zz; + inn_to_track_(sl, ww, ch, div_val, margin, tracks.at(ii)); + paint_margin_(margin, tracks.at(ii)); + } + inn_to_result_(inn, hh, ww, ch, yy, zz, div_val, result); + if (alpha_ref.size() <= 0) { + return; + } /* alphaチャンネルを計算する場合 */ + alpha_ref_init_one_(ww, alpha_ref); + if (ref != 0) { + alpha_ref_mul_ref_(ref, hh, ww, ch, yy, ref_mode, alpha_ref); + } + if (ch < 4) { + return; + } /* alphaチャンネルがない場合はここで終わり */ + if (add_blend_sw) { + alpha_ref_mul_alpha_(out, hh, ww, ch, yy, div_val, alpha_ref); + } +} /*--- 2番以後のスキャンラインのセット -------------------------------*/ template void get_next(const IT *inn /* outと同じ高さ、幅、チャンネル数 */ @@ -165,7 +210,7 @@ void get_next(const IT *inn /* outと同じ高さ、幅、チャンネル数 */ std::vector &alpha_ref /* pixel毎の変化の割合 */ , std::vector &result /* 元値をいれといて、結果を入れる */ - ) { +) { const int t_max = (std::numeric_limits::max)(); const double div_val = static_cast(t_max); @@ -188,6 +233,46 @@ void get_next(const IT *inn /* outと同じ高さ、幅、チャンネル数 */ alpha_ref_mul_alpha_(out, hh, ww, ch, yy, div_val, alpha_ref); } } +template <> +void get_next(const float *inn /* outと同じ高さ、幅、チャンネル数 */ + , + const float *out /* outの処理結果alpha値をinとして使用 */ + , + const int hh, const int ww, const int ch, + const float *ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ + , + const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */ + , + const int yy, const int zz, const int margin, + const bool add_blend_sw, + std::vector> &tracks /* sl影響範囲のpixel値 */ + , + std::vector &alpha_ref /* pixel毎の変化の割合 */ + , + std::vector &result /* 元値をいれといて、結果を入れる */ +) { + const double div_val = 1.; + + const float *sl = csl_top_clamped_in_h_(inn, hh, ww, ch, yy + margin) + zz; + inn_to_track_(sl, ww, ch, div_val, margin, tracks.at(0)); + paint_margin_(margin, tracks.at(0)); + + inn_to_result_(inn, hh, ww, ch, yy, zz, div_val, result); + if (alpha_ref.size() <= 0) { + return; + } /* alphaチャンネルを計算する場合 */ + alpha_ref_init_one_(ww, alpha_ref); + if (ref != 0) { + alpha_ref_mul_ref_(ref, hh, ww, ch, yy, ref_mode, alpha_ref); + } + if (ch < 4) { + return; + } /* alphaチャンネルがない場合はここで終わり */ + if (add_blend_sw) { + alpha_ref_mul_alpha_(out, hh, ww, ch, yy, div_val, alpha_ref); + } +} + template void copy(const T *inn, const int hh, const int ww, const int ch, const int yy, const int zz, T *out) { @@ -197,6 +282,7 @@ void copy(const T *inn, const int hh, const int ww, const int ch, const int yy, dd[ch * xx] = ss[ch * xx]; } } + template void put(const std::vector &result, const int hh, const int ww, const int ch, const int yy, const int zz, T *out) { @@ -209,7 +295,17 @@ void put(const std::vector &result, const int hh, const int ww, } // std::cout << std::endl; } + +template <> +void put(const std::vector &result, const int hh, const int ww, + const int ch, const int yy, const int zz, float *out) { + float *dd = sl_out_clamped_in_h_(out, hh, ww, ch, yy) + zz; + for (int xx = 0; xx < ww; ++xx) { + dd[ch * xx] = static_cast(result.at(xx)); + } } -} -} + +} // namespace getput +} // namespace maxmin +} // namespace igs #endif /* !igs_maxmin_getput_h */ diff --git a/toonz/sources/stdfx/igs_perlin_noise.cpp b/toonz/sources/stdfx/igs_perlin_noise.cpp index 772893a..793317a 100644 --- a/toonz/sources/stdfx/igs_perlin_noise.cpp +++ b/toonz/sources/stdfx/igs_perlin_noise.cpp @@ -99,6 +99,49 @@ void change_(T *image_array, const int height, const int width, image_scanline += channels * wrap; } } +template <> +void change_(float *image_array, const int height, const int width, + const int wrap, // pixel + const int channels, const bool alpha_rendering_sw, + const double a11 // geometry of 2D affine transformation + , + const double a12, const double a13, const double a21, + const double a22, const double a23, const double zz, + const int octaves_start // 0<= + , + const int octaves_end // 0<= + , + const double persistence // Not 0 +) { + const float max_div = 1.f; + const float max_div_2 = 0.5f; + + const double maxi = + perlin_noise_minmax_(octaves_start, octaves_end, persistence); + + using namespace igs::image::rgba; + float *image_crnt; + float *image_scanline = image_array; + for (int yy = 0; yy < height; ++yy) { + image_crnt = image_scanline; + for (int xx = 0; xx < width; ++xx, image_crnt += channels) { + const float val = static_cast( + perlin_noise_3d_(xx * a11 + yy * a12 + a13, xx * a21 + yy * a22 + a23, + zz, octaves_start, octaves_end, persistence) / + maxi * 0.5f + + 0.5f); + for (int zz = 0; zz < channels; ++zz) { + if (!alpha_rendering_sw && (alp == zz)) { + image_crnt[zz] = 1.f; + } else { + image_crnt[zz] = val; + } + } + } + image_scanline += channels * wrap; + } +} + } // namespace // #include "igs_geometry2d.h" void igs::perlin_noise::change( @@ -125,6 +168,10 @@ void igs::perlin_noise::change( change_(reinterpret_cast(image_array), height, width, wrap, channels, alpha_rendering_sw, a11, a12, a13, a21, a22, a23, zz, octaves_start, octaves_end, persistence); + } else if (std::numeric_limits::digits == bits) { + change_(reinterpret_cast(image_array), height, width, wrap, + channels, alpha_rendering_sw, a11, a12, a13, a21, a22, a23, zz, + octaves_start, octaves_end, persistence); } else { throw std::domain_error("Bad bits,Not uchar/ushort"); } diff --git a/toonz/sources/stdfx/igs_warp.h b/toonz/sources/stdfx/igs_warp.h index b0efa23..76eb330 100644 --- a/toonz/sources/stdfx/igs_warp.h +++ b/toonz/sources/stdfx/igs_warp.h @@ -10,34 +10,16 @@ namespace igs { namespace warp { IGS_WARP_EXPORT void hori_change( - unsigned char *image, const int height, const int width, const int channels, - const int bits - - , - const unsigned char *refer // by height,width,channels - , - const int refchannels, const int refcc, const int refbit - - , - const double offset = 0.5 //(double)(1<<(bits-1))/((1< -void hori_change_template_(ST *image, const int height, const int width, - const int channels - - , - const RT *refer // same as height,width,channels - , - const int refchannels, const int refcc - - , - const double offset, const double maxlen, - const bool alpha_rendering_sw, - const bool anti_aliasing_sw) { - const double smax = std::numeric_limits::max(); - const double rmax = std::numeric_limits::max(); - - std::vector> buf_s1(channels), buf_s2(channels); +void hori_change_(float* image, const int height, const int width, + const int channels, + const float* refer, // same as height,width,channels + const int refchannels, const int refcc, const double maxlen, + const bool alpha_rendering_sw, const bool anti_aliasing_sw) { + std::vector> buf_s(channels); for (int zz = 0; zz < channels; ++zz) { - buf_s1.at(zz).resize(width); - buf_s2.at(zz).resize(width); + buf_s.at(zz).resize(width); } std::vector buf_r(width); refer += refcc; /* 参照画像の参照色チャンネル */ for (int yy = 0; yy < height; ++yy, image += channels * width, refer += refchannels * width) { + // buf_sにSourceのスキャンラインのピクセル値をいれる for (int xx = 0; xx < width; ++xx) { for (int zz = 0; zz < channels; ++zz) { - buf_s1.at(zz).at(xx) = image[xx * channels + zz] / smax; + buf_s.at(zz).at(xx) = image[xx * channels + zz]; } } - + // buf_rに参照画像の指定チャンネルの値を入れる for (int xx = 0; xx < width; ++xx) { // reference red of refer[] - double pos = static_cast(refer[xx * refchannels]); - buf_r.at(xx) = ((pos / rmax) - offset) * maxlen; + float pos = refer[xx * refchannels]; + // clamp 0.f to 1.f in case using TPixelF + pos = std::min(1.f, std::max(0.f, pos)); + buf_r.at(xx) = (pos - 0.5f) * maxlen; } if (anti_aliasing_sw) { for (int xx = 0; xx < width; ++xx) { - double pos = buf_r.at(xx); + float pos = buf_r.at(xx); int fl_pos = xx + static_cast(std::floor(pos)); int ce_pos = xx + static_cast(std::ceil(pos)); - double div = pos - floor(pos); - if (fl_pos < 0) { + float div = pos - floor(pos); + + // clamp + if (fl_pos < 0) fl_pos = 0; - } else if (width <= fl_pos) { + else if (width <= fl_pos) fl_pos = width - 1; - } - if (ce_pos < 0) { + + if (ce_pos < 0) ce_pos = 0; - } else if (width <= ce_pos) { + else if (width <= ce_pos) ce_pos = width - 1; - } + for (int zz = 0; zz < channels; ++zz) { if (!alpha_rendering_sw && (igs::image::rgba::alp == zz)) { - buf_s2.at(zz).at(xx) = buf_s1.at(zz).at(xx); + image[xx * channels + zz] = buf_s.at(zz).at(xx); } else { - buf_s2.at(zz).at(xx) = buf_s1.at(zz).at(fl_pos) * (1.0 - div) + - buf_s1.at(zz).at(ce_pos) * div; + image[xx * channels + zz] = buf_s.at(zz).at(fl_pos) * (1.0 - div) + + buf_s.at(zz).at(ce_pos) * div; } } } } else { for (int xx = 0; xx < width; ++xx) { int pos = xx + static_cast(floor(buf_r.at(xx) + 0.5)); - if (pos < 0) { + // clamp + if (pos < 0) pos = 0; - } else if (width <= pos) { + else if (width <= pos) pos = width - 1; - } + for (int zz = 0; zz < channels; ++zz) { if (!alpha_rendering_sw && (igs::image::rgba::alp == zz)) { - buf_s2.at(zz).at(xx) = buf_s1.at(zz).at(xx); + image[xx * channels + zz] = buf_s.at(zz).at(xx); } else { - buf_s2.at(zz).at(xx) = buf_s1.at(zz).at(pos); + image[xx * channels + zz] = buf_s.at(zz).at(pos); } } } } - - for (int xx = 0; xx < width; ++xx) { - for (int zz = 0; zz < channels; ++zz) { - image[xx * channels + zz] = - static_cast(buf_s2.at(zz).at(xx) * (smax + 0.999999)); - } - } } } -} -//-------------------------------------------------------------------- -void igs::warp::hori_change( - unsigned char *image, const int height, const int width, const int channels, - const int bits - , - const unsigned char *refer // by height,width,channels - , - const int refchannels, const int refcc, const int refbit +} // namespace +//-------------------------------------------------------------------- - , - const double offset, const double maxlen, const bool alpha_rendering_sw, - const bool anti_aliasing_sw) { - const int ucharb = std::numeric_limits::digits; - const int ushortb = std::numeric_limits::digits; - if ((ushortb == bits) && (ushortb == refbit)) { - hori_change_template_( - reinterpret_cast(image), height, width, channels, - reinterpret_cast(refer), refchannels, refcc, - offset, maxlen, alpha_rendering_sw, anti_aliasing_sw); - } else if ((ushortb == bits) && (ucharb == refbit)) { - hori_change_template_(reinterpret_cast(image), height, - width, channels, refer, refchannels, refcc, offset, - maxlen, alpha_rendering_sw, anti_aliasing_sw); - } else if ((ucharb == bits) && (ushortb == refbit)) { - hori_change_template_(image, height, width, channels, - reinterpret_cast(refer), - refchannels, refcc, offset, maxlen, - alpha_rendering_sw, anti_aliasing_sw); - } else if ((ucharb == bits) && (ucharb == refbit)) { - hori_change_template_(image, height, width, channels, refer, refchannels, - refcc, offset, maxlen, alpha_rendering_sw, - anti_aliasing_sw); - } -} +void igs::warp::hori_change(float* image, const int height, const int width, + const int channels, + const float* refer, // by height,width,channels + const int refchannels, const int refcc, + const double maxlen, const bool alpha_rendering_sw, + const bool anti_aliasing_sw) { + hori_change_(image, height, width, channels, refer, refchannels, refcc, + maxlen, alpha_rendering_sw, anti_aliasing_sw); +} \ No newline at end of file diff --git a/toonz/sources/stdfx/igs_warp_vert.cpp b/toonz/sources/stdfx/igs_warp_vert.cpp index 05fcc06..9f02f9b 100644 --- a/toonz/sources/stdfx/igs_warp_vert.cpp +++ b/toonz/sources/stdfx/igs_warp_vert.cpp @@ -5,40 +5,29 @@ #include "igs_warp.h" namespace { -template -void vert_change_template_(ST *image, const int height, const int width, - const int channels - , - const RT *refer // same as height,width,channels - , - const int refchannels, const int refcc - - , - const double offset, const double maxlen, - const bool alpha_rendering_sw, - const bool anti_aliasing_sw) { - const double smax = std::numeric_limits::max(); - const double rmax = std::numeric_limits::max(); - - std::vector> buf_s1(channels), buf_s2(channels); +void vert_change_(float* image, const int height, const int width, + const int channels, + const float* refer, // same as height,width,channels + const int refchannels, const int refcc, const double maxlen, + const bool alpha_rendering_sw, const bool anti_aliasing_sw) { + std::vector> buf_s1(channels); for (int zz = 0; zz < channels; ++zz) { buf_s1.at(zz).resize(height); - buf_s2.at(zz).resize(height); } - std::vector buf_r(height); + std::vector buf_r(height); refer += refcc; /* 参照画像の参照色チャンネル */ for (int xx = 0; xx < width; ++xx, image += channels, refer += refchannels) { for (int yy = 0; yy < height; ++yy) { for (int zz = 0; zz < channels; ++zz) { - buf_s1.at(zz).at(yy) = image[yy * width * channels + zz] / smax; + buf_s1.at(zz).at(yy) = image[yy * width * channels + zz]; } } for (int yy = 0; yy < height; ++yy) { // reference red of refer[] - double pos = static_cast(refer[yy * width * refchannels]); - buf_r.at(yy) = ((pos / rmax) - offset) * maxlen; + float pos = refer[yy * width * refchannels]; + buf_r.at(yy) = (pos - 0.5f) * maxlen; } if (anti_aliasing_sw) { @@ -46,85 +35,57 @@ void vert_change_template_(ST *image, const int height, const int width, double pos = buf_r.at(yy); int fl_pos = yy + static_cast(std::floor(pos)); int ce_pos = yy + static_cast(std::ceil(pos)); - double div = pos - floor(pos); - if (fl_pos < 0) { + float div = pos - floor(pos); + // clamp + if (fl_pos < 0) fl_pos = 0; - } else if (height <= fl_pos) { + else if (height <= fl_pos) fl_pos = height - 1; - } - if (ce_pos < 0) { + if (ce_pos < 0) ce_pos = 0; - } else if (height <= ce_pos) { + else if (height <= ce_pos) ce_pos = height - 1; - } + for (int zz = 0; zz < channels; ++zz) { if (!alpha_rendering_sw && (igs::image::rgba::alp == zz)) { - buf_s2.at(zz).at(yy) = buf_s1.at(zz).at(yy); + image[yy * width * channels + zz] = buf_s1.at(zz).at(yy); } else { - buf_s2.at(zz).at(yy) = buf_s1.at(zz).at(fl_pos) * (1.0 - div) + - buf_s1.at(zz).at(ce_pos) * div; + image[yy * width * channels + zz] = + buf_s1.at(zz).at(fl_pos) * (1.0 - div) + + buf_s1.at(zz).at(ce_pos) * div; } } } } else { for (int yy = 0; yy < height; ++yy) { int pos = yy + static_cast(floor(buf_r.at(yy) + 0.5)); - if (pos < 0) { + // clamp + if (pos < 0) pos = 0; - } else if (height <= pos) { + else if (height <= pos) pos = height - 1; - } + for (int zz = 0; zz < channels; ++zz) { if (!alpha_rendering_sw && (igs::image::rgba::alp == zz)) { - buf_s2.at(zz).at(yy) = buf_s1.at(zz).at(yy); + image[yy * width * channels + zz] = buf_s1.at(zz).at(yy); } else { - buf_s2.at(zz).at(yy) = buf_s1.at(zz).at(pos); + image[yy * width * channels + zz] = buf_s1.at(zz).at(pos); } } } } - - for (int yy = 0; yy < height; ++yy) { - for (int zz = 0; zz < channels; ++zz) { - image[yy * width * channels + zz] = - static_cast(buf_s2.at(zz).at(yy) * (smax + 0.999999)); - } - } } } -} -//-------------------------------------------------------------------- -void igs::warp::vert_change( - unsigned char *image, const int height, const int width, const int channels, - const int bits - , - const unsigned char *refer // by height,width,channels - , - const int refchannels, const int refcc, const int refbit +} // namespace +//-------------------------------------------------------------------- - , - const double offset, const double maxlen, const bool alpha_rendering_sw, - const bool anti_aliasing_sw) { - const int ucharb = std::numeric_limits::digits; - const int ushortb = std::numeric_limits::digits; - if ((ushortb == bits) && (ushortb == refbit)) { - vert_change_template_( - reinterpret_cast(image), height, width, channels, - reinterpret_cast(refer), refchannels, refcc, - offset, maxlen, alpha_rendering_sw, anti_aliasing_sw); - } else if ((ushortb == bits) && (ucharb == refbit)) { - vert_change_template_(reinterpret_cast(image), height, - width, channels, refer, refchannels, refcc, offset, - maxlen, alpha_rendering_sw, anti_aliasing_sw); - } else if ((ucharb == bits) && (ushortb == refbit)) { - vert_change_template_(image, height, width, channels, - reinterpret_cast(refer), - refchannels, refcc, offset, maxlen, - alpha_rendering_sw, anti_aliasing_sw); - } else if ((ucharb == bits) && (ucharb == refbit)) { - vert_change_template_(image, height, width, channels, refer, refchannels, - refcc, offset, maxlen, alpha_rendering_sw, - anti_aliasing_sw); - } -} +void igs::warp::vert_change(float* image, const int height, const int width, + const int channels, + const float* refer, // by height,width,channels + const int refchannels, const int refcc, + const double maxlen, const bool alpha_rendering_sw, + const bool anti_aliasing_sw) { + vert_change_(image, height, width, channels, refer, refchannels, refcc, + maxlen, alpha_rendering_sw, anti_aliasing_sw); +} \ No newline at end of file diff --git a/toonz/sources/stdfx/ino_blend_add.cpp b/toonz/sources/stdfx/ino_blend_add.cpp index fd5df25..2ca6aba 100644 --- a/toonz/sources/stdfx/ino_blend_add.cpp +++ b/toonz/sources/stdfx/ino_blend_add.cpp @@ -28,9 +28,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::add(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_add, "inoAddFx"); \ No newline at end of file diff --git a/toonz/sources/stdfx/ino_blend_color_burn.cpp b/toonz/sources/stdfx/ino_blend_color_burn.cpp index 8f1741a..4025685 100644 --- a/toonz/sources/stdfx/ino_blend_color_burn.cpp +++ b/toonz/sources/stdfx/ino_blend_color_burn.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::color_burn(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_color_burn, "inoColorBurnFx"); diff --git a/toonz/sources/stdfx/ino_blend_color_dodge.cpp b/toonz/sources/stdfx/ino_blend_color_dodge.cpp index 7104cbb..c13ba53 100644 --- a/toonz/sources/stdfx/ino_blend_color_dodge.cpp +++ b/toonz/sources/stdfx/ino_blend_color_dodge.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::color_dodge(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_color_dodge, "inoColorDodgeFx"); diff --git a/toonz/sources/stdfx/ino_blend_cross_dissolve.cpp b/toonz/sources/stdfx/ino_blend_cross_dissolve.cpp index 7aa8f13..ce4a363 100644 --- a/toonz/sources/stdfx/ino_blend_cross_dissolve.cpp +++ b/toonz/sources/stdfx/ino_blend_cross_dissolve.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::cross_dissolve(dnr, dng, dnb, dna, upr, upg, upb, upa, - up_opacity, !is_xyz); + up_opacity, do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_cross_dissolve, "inoCrossDissolveFx"); diff --git a/toonz/sources/stdfx/ino_blend_darken.cpp b/toonz/sources/stdfx/ino_blend_darken.cpp index eb7a533..daaafea 100644 --- a/toonz/sources/stdfx/ino_blend_darken.cpp +++ b/toonz/sources/stdfx/ino_blend_darken.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::darken(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_darken, "inoDarkenFx"); diff --git a/toonz/sources/stdfx/ino_blend_darker_color.cpp b/toonz/sources/stdfx/ino_blend_darker_color.cpp index 1b14055..c493222 100644 --- a/toonz/sources/stdfx/ino_blend_darker_color.cpp +++ b/toonz/sources/stdfx/ino_blend_darker_color.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::darker_color(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_darker_color, "inoDarkerColorFx"); diff --git a/toonz/sources/stdfx/ino_blend_divide.cpp b/toonz/sources/stdfx/ino_blend_divide.cpp index d08b27b..4da2578 100644 --- a/toonz/sources/stdfx/ino_blend_divide.cpp +++ b/toonz/sources/stdfx/ino_blend_divide.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::divide(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_divide, "inoDivideFx"); diff --git a/toonz/sources/stdfx/ino_blend_hard_light.cpp b/toonz/sources/stdfx/ino_blend_hard_light.cpp index 68b5d96..e85760e 100644 --- a/toonz/sources/stdfx/ino_blend_hard_light.cpp +++ b/toonz/sources/stdfx/ino_blend_hard_light.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::hard_light(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_hard_light, "inoHardLightFx"); diff --git a/toonz/sources/stdfx/ino_blend_hard_mix.cpp b/toonz/sources/stdfx/ino_blend_hard_mix.cpp index a09ccd3..2834ebe 100644 --- a/toonz/sources/stdfx/ino_blend_hard_mix.cpp +++ b/toonz/sources/stdfx/ino_blend_hard_mix.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::hard_mix(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_hard_mix, "inoHardMixFx"); diff --git a/toonz/sources/stdfx/ino_blend_lighten.cpp b/toonz/sources/stdfx/ino_blend_lighten.cpp index f1ac551..8e0f92b 100644 --- a/toonz/sources/stdfx/ino_blend_lighten.cpp +++ b/toonz/sources/stdfx/ino_blend_lighten.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::lighten(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_lighten, "inoLightenFx"); diff --git a/toonz/sources/stdfx/ino_blend_lighter_color.cpp b/toonz/sources/stdfx/ino_blend_lighter_color.cpp index e0e0fb3..acf2ceb 100644 --- a/toonz/sources/stdfx/ino_blend_lighter_color.cpp +++ b/toonz/sources/stdfx/ino_blend_lighter_color.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::lighter_color(dnr, dng, dnb, dna, upr, upg, upb, upa, - up_opacity, !is_xyz); + up_opacity, do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_lighter_color, "inoLighterColorFx"); diff --git a/toonz/sources/stdfx/ino_blend_linear_burn.cpp b/toonz/sources/stdfx/ino_blend_linear_burn.cpp index 03e036f..cd4f0ad 100644 --- a/toonz/sources/stdfx/ino_blend_linear_burn.cpp +++ b/toonz/sources/stdfx/ino_blend_linear_burn.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::linear_burn(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_linear_burn, "inoLinearBurnFx"); diff --git a/toonz/sources/stdfx/ino_blend_linear_dodge.cpp b/toonz/sources/stdfx/ino_blend_linear_dodge.cpp index 86a8c26..580eb8f 100644 --- a/toonz/sources/stdfx/ino_blend_linear_dodge.cpp +++ b/toonz/sources/stdfx/ino_blend_linear_dodge.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::linear_dodge(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_linear_dodge, "inoLinearDodgeFx"); diff --git a/toonz/sources/stdfx/ino_blend_linear_light.cpp b/toonz/sources/stdfx/ino_blend_linear_light.cpp index 2bbe0b2..26b947f 100644 --- a/toonz/sources/stdfx/ino_blend_linear_light.cpp +++ b/toonz/sources/stdfx/ino_blend_linear_light.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::linear_light(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_linear_light, "inoLinearLightFx"); diff --git a/toonz/sources/stdfx/ino_blend_multiply.cpp b/toonz/sources/stdfx/ino_blend_multiply.cpp index 961f21b..c3d595c 100644 --- a/toonz/sources/stdfx/ino_blend_multiply.cpp +++ b/toonz/sources/stdfx/ino_blend_multiply.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::multiply(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_multiply, "inoMultiplyFx"); diff --git a/toonz/sources/stdfx/ino_blend_over.cpp b/toonz/sources/stdfx/ino_blend_over.cpp index e053a43..5098f63 100644 --- a/toonz/sources/stdfx/ino_blend_over.cpp +++ b/toonz/sources/stdfx/ino_blend_over.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::over(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_over, "inoOverFx"); diff --git a/toonz/sources/stdfx/ino_blend_overlay.cpp b/toonz/sources/stdfx/ino_blend_overlay.cpp index a7b3244..cde4294 100644 --- a/toonz/sources/stdfx/ino_blend_overlay.cpp +++ b/toonz/sources/stdfx/ino_blend_overlay.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::overlay(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_overlay, "inoOverlayFx"); diff --git a/toonz/sources/stdfx/ino_blend_pin_light.cpp b/toonz/sources/stdfx/ino_blend_pin_light.cpp index bbfdb40..db42255 100644 --- a/toonz/sources/stdfx/ino_blend_pin_light.cpp +++ b/toonz/sources/stdfx/ino_blend_pin_light.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::pin_light(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_pin_light, "inoPinLightFx"); diff --git a/toonz/sources/stdfx/ino_blend_screen.cpp b/toonz/sources/stdfx/ino_blend_screen.cpp index d7b82c8..f182a47 100644 --- a/toonz/sources/stdfx/ino_blend_screen.cpp +++ b/toonz/sources/stdfx/ino_blend_screen.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::screen(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_screen, "inoScreenFx"); diff --git a/toonz/sources/stdfx/ino_blend_soft_light.cpp b/toonz/sources/stdfx/ino_blend_soft_light.cpp index 09da6db..1cf7eee 100644 --- a/toonz/sources/stdfx/ino_blend_soft_light.cpp +++ b/toonz/sources/stdfx/ino_blend_soft_light.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::soft_light(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_soft_light, "inoSoftLightFx"); diff --git a/toonz/sources/stdfx/ino_blend_subtract.cpp b/toonz/sources/stdfx/ino_blend_subtract.cpp index 7fe57f3..00c2236 100644 --- a/toonz/sources/stdfx/ino_blend_subtract.cpp +++ b/toonz/sources/stdfx/ino_blend_subtract.cpp @@ -16,9 +16,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::subtract(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - alpha_rendering_sw, !is_xyz); + alpha_rendering_sw, do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_subtract, "inoSubtractFx"); diff --git a/toonz/sources/stdfx/ino_blend_vivid_light.cpp b/toonz/sources/stdfx/ino_blend_vivid_light.cpp index 23cd459..e1fa91e 100644 --- a/toonz/sources/stdfx/ino_blend_vivid_light.cpp +++ b/toonz/sources/stdfx/ino_blend_vivid_light.cpp @@ -15,9 +15,9 @@ public: const double upr, double upg, double upb, double upa, const double up_opacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) override { + const bool do_clamp = true) override { igs::color::vivid_light(dnr, dng, dnb, dna, upr, upg, upb, upa, up_opacity, - !is_xyz); + do_clamp); } }; FX_PLUGIN_IDENTIFIER(ino_blend_vivid_light, "inoVividLightFx"); diff --git a/toonz/sources/stdfx/ino_blur.cpp b/toonz/sources/stdfx/ino_blur.cpp index 0fba236..05d23da 100644 --- a/toonz/sources/stdfx/ino_blur.cpp +++ b/toonz/sources/stdfx/ino_blur.cpp @@ -30,6 +30,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } //------------------------------------------------------------ double get_render_real_radius(const double frame, const TAffine affine) { @@ -102,54 +104,50 @@ void fx_(const TRasterP in_ras // with margin , const TRasterP refer_ras, const int refer_mode, const int int_radius, const double real_radius) { - TRasterGR8P out_buffer(out_ras->getLy(), - out_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + TRasterGR8P ref_gr8; + if ((refer_ras != nullptr) && (0 <= refer_mode)) { + ref_gr8 = TRasterGR8P(in_ras->getLy(), in_ras->getLx() * sizeof(float)); + ref_gr8->lock(); + ino::ras_to_ref_float_arr(refer_ras, + reinterpret_cast(ref_gr8->getRawData()), + refer_mode); + } + const int buffer_bytes = igs::gaussian_blur_hv::buffer_bytes( in_ras->getLy(), in_ras->getLx(), int_radius); TRasterGR8P cvt_buffer(buffer_bytes, 1); - out_buffer->lock(); cvt_buffer->lock(); - igs::gaussian_blur_hv::convert( - in_ras->getRawData() // const void *in_with_margin (BGRA) - , - out_buffer->getRawData() // void *out_no_margin (BGRA) - - , - in_ras->getLy() // const int height_with_margin - , - in_ras->getLx() // const int width_with_margin - , - ino::channels() // const int channels - , - ino::bits(in_ras) // const int bits - , - (((refer_ras != nullptr) && (0 <= refer_mode)) - ? refer_ras->getRawData() - : nullptr) // BGRA // const unsigned char *ref - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? ino::bits(refer_ras) - : 0) // const int ref_bits - , - refer_mode // const int refer_mode + TRasterGR8P in_gr8(in_ras->getLy(), + in_ras->getLx() * ino::channels() * sizeof(float)); + in_gr8->lock(); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); - , - cvt_buffer->getRawData() // void *buffer - , - buffer_bytes // int buffer_bytes - - , - int_radius // const int int_radius - , - real_radius // const double real_radius // , 0.25 - ); - ino::arr_to_ras(out_buffer->getRawData(), ino::channels(), out_ras, 0); - cvt_buffer->unlock(); + TRasterGR8P out_buffer(out_ras->getLy(), + out_ras->getLx() * ino::channels() * sizeof(float)); + out_buffer->lock(); + igs::gaussian_blur_hv::convert( + reinterpret_cast( + in_gr8->getRawData()), // const void *in_with_margin (BGRA) + reinterpret_cast( + out_buffer->getRawData()), // void *out_no_margin (BGRA) + in_ras->getLy(), // const int height_with_margin + in_ras->getLx(), // const int width_with_margin + ino::channels(), // const int channels + (ref_gr8) ? reinterpret_cast(ref_gr8->getRawData()) : nullptr, + cvt_buffer->getRawData(), // void *buffer + buffer_bytes, // int buffer_bytes + int_radius, // const int int_radius + real_radius // const double real_radius // , 0.25 + ); + in_gr8->unlock(); + ino::float_arr_to_ras(out_buffer->getRawData(), ino::channels(), out_ras, 0); out_buffer->unlock(); + cvt_buffer->unlock(); + if (ref_gr8) ref_gr8->unlock(); } -} +} // namespace //------------------------------------------------------------ void ino_blur::doCompute(TTile &tile, double frame, const TRenderSettings &rend_sets) { @@ -159,7 +157,8 @@ void ino_blur::doCompute(TTile &tile, double frame, return; } /*------ サポートしていないPixelタイプはエラーを投げる -----*/ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } /*------ ボケ足長さの実際の長さをえる ----------------------*/ diff --git a/toonz/sources/stdfx/ino_channel_selector.cpp b/toonz/sources/stdfx/ino_channel_selector.cpp index cb2d4e5..ba3825f 100644 --- a/toonz/sources/stdfx/ino_channel_selector.cpp +++ b/toonz/sources/stdfx/ino_channel_selector.cpp @@ -65,6 +65,8 @@ public: this->m_alp_channel->addItem(0, "Red"); this->m_alp_channel->addItem(1, "Green"); this->m_alp_channel->addItem(2, "Blue"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -94,7 +96,7 @@ void fx_template(const TRasterPT in_ras, const int in_sel, IN_PIXEL *sl_in = in_ras->pixels(yy); OUT_PIXEL *sl_out = out_ras->pixels(yy); for (int xx = 0; xx < out_ras->getLx(); ++xx, ++sl_in, ++sl_out) { - int val = 0; + typename IN_PIXEL::Channel val = 0; switch (in_sel) { case 0: val = sl_in->r; @@ -120,7 +122,11 @@ void fx_template(const TRasterPT in_ras, const int in_sel, sl_out->b = val; break; case 3: - sl_out->m = val; + // clamp the alpha channel in case computing TPixelF + sl_out->m = (val < 0) ? 0 + : (val > OUT_PIXEL::maxChannelValue) + ? OUT_PIXEL::maxChannelValue + : val; break; } } @@ -132,14 +138,17 @@ void fx_(const TRasterP in_ras, const int in_sel, TRasterP out_ras, fx_template(in_ras, in_sel, out_ras, out_sel); } else if ((TRaster64P)in_ras && (TRaster64P)out_ras) { fx_template(in_ras, in_sel, out_ras, out_sel); + } else if ((TRasterFP)in_ras && (TRasterFP)out_ras) { + fx_template(in_ras, in_sel, out_ras, out_sel); } } -} +} // namespace //------------------------------------------------------------ void ino_channel_selector::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } diff --git a/toonz/sources/stdfx/ino_common.cpp b/toonz/sources/stdfx/ino_common.cpp index 7136dbf..0ea93e1 100644 --- a/toonz/sources/stdfx/ino_common.cpp +++ b/toonz/sources/stdfx/ino_common.cpp @@ -42,7 +42,7 @@ void ras_to_arr_(const TRasterPT ras, U* arr, const int channels) { } } -// T is TPixel32 or TPixel64 +// T is TPixel32, TPixel64 or TPixelF // normalize to 0.0 - 1.0 template void ras_to_float_arr_(const TRasterPT ras, float* arr, const int channels) { @@ -113,47 +113,71 @@ void float_arr_to_ras_(const float* arr, const int channels, TRasterPT ras, T* ras_sl = ras->pixels(yy); for (int xx = 0; xx < ras->getLx(); ++xx, arrx += channels) { if (red < channels) { - ras_sl[xx].r = (arrx[red] >= 1.f) - ? T::maxChannelValue - : (arrx[red] <= 0.f) - ? (typename T::Channel)0 - : (typename T::Channel)( - std::round(arrx[red] * fac + 0.5f)); + ras_sl[xx].r = + (arrx[red] >= 1.f) ? T::maxChannelValue + : (arrx[red] <= 0.f) + ? (typename T::Channel)0 + : (typename T::Channel)(std::round(arrx[red] * fac + 0.5f)); } if (gre < channels) { - ras_sl[xx].g = (arrx[gre] >= 1.f) - ? T::maxChannelValue - : (arrx[gre] <= 0.f) - ? (typename T::Channel)0 - : (typename T::Channel)( - std::round(arrx[gre] * fac + 0.5f)); + ras_sl[xx].g = + (arrx[gre] >= 1.f) ? T::maxChannelValue + : (arrx[gre] <= 0.f) + ? (typename T::Channel)0 + : (typename T::Channel)(std::round(arrx[gre] * fac + 0.5f)); } if (blu < channels) { - ras_sl[xx].b = (arrx[blu] >= 1.f) - ? T::maxChannelValue - : (arrx[blu] <= 0.f) - ? (typename T::Channel)0 - : (typename T::Channel)( - std::round(arrx[blu] * fac + 0.5f)); + ras_sl[xx].b = + (arrx[blu] >= 1.f) ? T::maxChannelValue + : (arrx[blu] <= 0.f) + ? (typename T::Channel)0 + : (typename T::Channel)(std::round(arrx[blu] * fac + 0.5f)); } if (alp < channels) { - ras_sl[xx].m = (arrx[alp] >= 1.f) - ? T::maxChannelValue - : (arrx[alp] <= 0.f) - ? (typename T::Channel)0 - : (typename T::Channel)( - std::round(arrx[alp] * fac + 0.5f)); + ras_sl[xx].m = + (arrx[alp] >= 1.f) ? T::maxChannelValue + : (arrx[alp] <= 0.f) + ? (typename T::Channel)0 + : (typename T::Channel)(std::round(arrx[alp] * fac + 0.5f)); } } } } +template <> +void float_arr_to_ras_(const float* arr, const int channels, + TRasterFP ras, + const int margin // default is 0 +) { + arr += + (ras->getLx() + margin + margin) * margin * channels + margin * channels; + + using namespace igs::image::rgba; + + for (int yy = 0; yy < ras->getLy(); + ++yy, arr += (ras->getLx() + margin + margin) * channels) { + const float* arrx = arr; + TPixelF* ras_sl = ras->pixels(yy); + for (int xx = 0; xx < ras->getLx(); ++xx, arrx += channels) { + if (red < channels) ras_sl[xx].r = arrx[red]; + if (gre < channels) ras_sl[xx].g = arrx[gre]; + if (blu < channels) ras_sl[xx].b = arrx[blu]; + if (alp < channels) ras_sl[xx].m = arrx[alp]; + } + } +} + template float getFactor() { return 1.f / (float)T::maxChannelValue; } -// T is either TPixel32, TPixel64 +template <> +float getFactor() { + return 1.f; +} + +// T is either TPixel32, TPixel64, or TPixelF template void ras_to_ref_float_arr_(const TRasterPT ras, float* arr, const int refer_mode) { @@ -182,6 +206,8 @@ void ras_to_ref_float_arr_(const TRasterPT ras, float* arr, fac; break; } + // clamp 0.f to 1.f in case computing TPixelF + *arr = std::min(1.f, std::max(0.f, *arr)); } } } @@ -195,6 +221,8 @@ void ino::ras_to_arr(const TRasterP in_ras, const int channels, } else if ((TRaster64P)in_ras) { ras_to_arr_( in_ras, reinterpret_cast(out_arr), channels); + } else if ((TRasterFP)in_ras) { + ras_to_float_arr(in_ras, channels, reinterpret_cast(out_arr)); } } void ino::ras_to_float_arr(const TRasterP in_ras, const int channels, @@ -203,6 +231,8 @@ void ino::ras_to_float_arr(const TRasterP in_ras, const int channels, ras_to_float_arr_(in_ras, out_arr, channels); } else if ((TRaster64P)in_ras) { ras_to_float_arr_(in_ras, out_arr, channels); + } else if ((TRasterFP)in_ras) { + ras_to_float_arr_(in_ras, out_arr, channels); } } void ino::arr_to_ras(const unsigned char* in_arr, const int channels, @@ -213,6 +243,9 @@ void ino::arr_to_ras(const unsigned char* in_arr, const int channels, arr_to_ras_( reinterpret_cast(in_arr), channels, out_ras, margin); + } else if ((TRasterFP)out_ras) { + arr_to_ras_(reinterpret_cast(in_arr), + channels, out_ras, margin); } } void ino::float_arr_to_ras(const unsigned char* in_arr, const int channels, @@ -223,6 +256,9 @@ void ino::float_arr_to_ras(const unsigned char* in_arr, const int channels, } else if ((TRaster64P)out_ras) { float_arr_to_ras_(reinterpret_cast(in_arr), channels, out_ras, margin); + } else if ((TRasterFP)out_ras) { + float_arr_to_ras_(reinterpret_cast(in_arr), channels, + out_ras, margin); } } //-------------------- @@ -247,6 +283,8 @@ void ino::ras_to_ref_float_arr(const TRasterP in_ras, float* out_arr, ras_to_ref_float_arr_(in_ras, out_arr, refer_mode); } else if ((TRaster64P)in_ras) { ras_to_ref_float_arr_(in_ras, out_arr, refer_mode); + } else if ((TRasterFP)in_ras) { + ras_to_ref_float_arr_(in_ras, out_arr, refer_mode); } } @@ -322,12 +360,14 @@ inline void to_bgr(double* bgr, double const* xyz) { template inline T to_linear_color_space(T nonlinear_color, T exposure, T gamma) { // return -std::log(T(1) - std::pow(nonlinear_color, gamma)) / exposure; + if (nonlinear_color <= T(0)) return T(0); return std::pow(nonlinear_color, gamma) / exposure; } // convert power space to sRGB color space template inline T to_nonlinear_color_space(T linear_color, T exposure, T gamma) { // return std::pow(T(1) - std::exp(-exposure * linear_color), T(1) / gamma); + if (linear_color <= T(0)) return T(0); return std::pow(linear_color * exposure, T(1) / gamma); } @@ -345,22 +385,68 @@ TBlendForeBackRasterFx::TBlendForeBackRasterFx(bool clipping_mask, , m_clipping_mask(clipping_mask) , m_linear(false) , m_gamma(2.2) - , m_premultiplied(true) { + , m_gammaAdjust(0.) + , m_premultiplied(true) + , m_colorSpaceMode(new TIntEnumParam(Auto, "Auto")) { addInputPort("Fore", this->m_up); addInputPort("Back", this->m_down); bindParam(this, "opacity", this->m_opacity); bindParam(this, "clipping_mask", this->m_clipping_mask); - bindParam(this, "linear", this->m_linear); + bindParam(this, "linear", this->m_linear, true, true); // obsolete + bindParam(this, "colorSpaceMode", this->m_colorSpaceMode); bindParam(this, "gamma", this->m_gamma); + bindParam(this, "gammaAdjust", this->m_gammaAdjust); bindParam(this, "premultiplied", this->m_premultiplied); this->m_opacity->setValueRange(0, 1.0 * ino::param_range()); this->m_gamma->setValueRange(0.2, 5.0); + this->m_gammaAdjust->setValueRange(-5., 5.); + + m_colorSpaceMode->addItem(Linear, "Linear"); + m_colorSpaceMode->addItem(Nonlinear, "Nonlinear"); + if (has_alpha_option) { m_alpha_rendering = TBoolParamP(true); bindParam(this, "alpha_rendering", this->m_alpha_rendering); } + enableComputeInFloat(true); + + // version 1: Gamma had been diretory specified + // version 2: Gamma is computed by rs.m_colorSpaceGamma + gammaAdjust + setFxVersion(2); +} +//-------------------------------------------- + +void TBlendForeBackRasterFx::onFxVersionSet() { + bool useGamma = getFxVersion() == 1; + if (useGamma) { + // Automatically update version + if (m_gamma->getKeyframeCount() == 0 && + areAlmostEqual(m_gamma->getDefaultValue(), 2.2)) { + useGamma = false; + // call onObsoleteParamLoaded here in case loading the old fx before + // introducing the linear option + onObsoleteParamLoaded("linear"); + setFxVersion(2); + } + } + getParams()->getParamVar("gamma")->setIsHidden(!useGamma); + getParams()->getParamVar("gammaAdjust")->setIsHidden(useGamma); } + +//------------------------------------------------ +// This will be called in TFx::loadData when obsolete "linear" value is +// loaded +void TBlendForeBackRasterFx::onObsoleteParamLoaded( + const std::string& paramName) { + if (paramName != "linear") return; + + if (m_linear->getValue()) + m_colorSpaceMode->setValue(Linear); + else + m_colorSpaceMode->setValue(Nonlinear); +} + //------------------------------------------------------------ bool TBlendForeBackRasterFx::doGetBBox(double frame, TRectD& bBox, @@ -443,7 +529,16 @@ void TBlendForeBackRasterFx::doCompute(TTile& tile, double frame, /* ------ 動作パラメータを得る ---------------------------- */ const double up_opacity = this->m_opacity->getValue(frame) / ino::param_range(); - const double gamma = this->m_gamma->getValue(frame); + double gamma; + if (getFxVersion() == 1) + gamma = this->m_gamma->getValue(frame); + else { + gamma = std::max(1., rs.m_colorSpaceGamma + m_gammaAdjust->getValue(frame)); + } + + bool linear_sw = toBeComputedInLinearColorSpace(rs.m_linearColorSpace, + tile.getRaster()->isLinear()); + /* ------ (app_begin)log記憶 ------------------------------ */ const bool log_sw = ino::log_enable_sw(); @@ -465,7 +560,8 @@ void TBlendForeBackRasterFx::doCompute(TTile& tile, double frame, if (up_ras) { up_ras->lock(); } - doComputeFx(dn_ras, up_ras, TPoint(), up_opacity, gamma); + doComputeFx(dn_ras, up_ras, TPoint(), up_opacity, + gamma / rs.m_colorSpaceGamma, rs.m_colorSpaceGamma, linear_sw); // fx_(dn_ras, up_ras, TPoint(), up_opacity, // this->m_clipping_mask->getValue(), // this->m_linear->getValue(), gamma, this->m_premultiplied->getValue()); @@ -505,11 +601,10 @@ void TBlendForeBackRasterFx::doCompute(TTile& tile, double frame, } //------------------------------------------------------------ -void TBlendForeBackRasterFx::doComputeFx(TRasterP& dn_ras_out, - const TRasterP& up_ras, - const TPoint& pos, - const double up_opacity, - const double gamma) { +void TBlendForeBackRasterFx::doComputeFx( + TRasterP& dn_ras_out, const TRasterP& up_ras, const TPoint& pos, + const double up_opacity, const double gammaDif, + const double colorSpaceGamma, const bool linear_sw) { /* 交差したエリアを処理するようにする、いるのか??? */ TRect outRect(dn_ras_out->getBounds()); TRect upRect(up_ras->getBounds() + pos); @@ -522,22 +617,38 @@ void TBlendForeBackRasterFx::doComputeFx(TRasterP& dn_ras_out, TRaster32P rout32 = cRout, rup32 = cRup; TRaster64P rout64 = cRout, rup64 = cRup; + TRasterFP routF = cRout, rupF = cRup; - bool linear_sw = this->m_linear->getValue(); + bool premultiplied_sw = this->m_premultiplied->getValue(); if (rout32 && rup32) { - if (linear_sw) - linearTmpl(rout32, rup32, up_opacity, gamma); + if (linear_sw) { + if (!premultiplied_sw) + premultiToUnpremulti(rout32, rup32, colorSpaceGamma); + + linearTmpl(rout32, rup32, up_opacity, gammaDif); + } // linearAdd(rout32, rup32, up_opacity, clipping_mask_sw, // gamma, premultiplied_sw); else nonlinearTmpl(rout32, rup32, up_opacity); // tmpl_(rout32, rup32, up_opacity, clipping_mask_sw); } else if (rout64 && rup64) { - if (linear_sw) - linearTmpl(rout64, rup64, up_opacity, gamma); - else + if (linear_sw) { + if (!premultiplied_sw) + premultiToUnpremulti(rout64, rup64, colorSpaceGamma); + + linearTmpl(rout64, rup64, up_opacity, gammaDif); + } else nonlinearTmpl(rout64, rup64, up_opacity); + } else if (routF && rupF) { + if (linear_sw) { + if (!premultiplied_sw) + premultiToUnpremulti(routF, rupF, colorSpaceGamma); + + linearTmpl(routF, rupF, up_opacity, gammaDif); + } else + nonlinearTmpl(routF, rupF, up_opacity); } else { throw TRopException("unsupported pixel type"); } @@ -556,6 +667,7 @@ void TBlendForeBackRasterFx::nonlinearTmpl(TRasterPT dn_ras_out, double maxi = static_cast(T::maxChannelValue); // 255or65535 assert(dn_ras_out->getSize() == up_ras->getSize()); + assert(dn_ras_out->isLinear() == up_ras->isLinear()); for (int yy = 0; yy < dn_ras_out->getLy(); ++yy) { T* out_pix = dn_ras_out->pixels(yy); @@ -572,7 +684,7 @@ void TBlendForeBackRasterFx::nonlinearTmpl(TRasterPT dn_ras_out, double dna = static_cast(out_pix->m) / maxi; brendKernel(dnr, dng, dnb, dna, upr, upg, upb, upa, clipping_mask_sw ? up_opacity * dna : up_opacity, - alpha_rendering_sw); + alpha_rendering_sw, true); out_pix->r = static_cast(dnr * (maxi + 0.999999)); out_pix->g = static_cast(dng * (maxi + 0.999999)); out_pix->b = static_cast(dnb * (maxi + 0.999999)); @@ -582,16 +694,48 @@ void TBlendForeBackRasterFx::nonlinearTmpl(TRasterPT dn_ras_out, } //------------------------------------------------------------ +template <> +void TBlendForeBackRasterFx::nonlinearTmpl( + TRasterFP dn_ras_out, const TRasterFP& up_ras, const double up_opacity) { + bool clipping_mask_sw = this->m_clipping_mask->getValue(); + bool alpha_rendering_sw = (m_alpha_rendering.getPointer()) + ? this->m_alpha_rendering->getValue() + : true; + + assert(dn_ras_out->getSize() == up_ras->getSize()); + assert(dn_ras_out->isLinear() == up_ras->isLinear()); + + for (int yy = 0; yy < dn_ras_out->getLy(); ++yy) { + TPixelF* out_pix = dn_ras_out->pixels(yy); + const TPixelF* const out_end = out_pix + dn_ras_out->getLx(); + const TPixelF* up_pix = up_ras->pixels(yy); + for (; out_pix < out_end; ++out_pix, ++up_pix) { + double dnr = static_cast(out_pix->r); + double dng = static_cast(out_pix->g); + double dnb = static_cast(out_pix->b); + double dna = static_cast(out_pix->m); + brendKernel(dnr, dng, dnb, dna, up_pix->r, up_pix->g, up_pix->b, + up_pix->m, clipping_mask_sw ? up_opacity * dna : up_opacity, + alpha_rendering_sw, false); + out_pix->r = dnr; + out_pix->g = dng; + out_pix->b = dnb; + out_pix->m = dna; + } + } +} + +//------------------------------------------------------------ template void TBlendForeBackRasterFx::linearTmpl(TRasterPT dn_ras_out, const TRasterPT& up_ras, const double up_opacity, - const double gamma) { + const double gammaDif) { bool clipping_mask_sw = this->m_clipping_mask->getValue(); bool alpha_rendering_sw = (m_alpha_rendering.getPointer()) ? this->m_alpha_rendering->getValue() : true; - bool premultiplied_sw = this->m_premultiplied->getValue(); + bool premultiplied_sw = this->m_premultiplied->getValue(); double maxi = static_cast(T::maxChannelValue); // 255or65535 double limit = (maxi + 0.5) / (maxi + 1.0); @@ -618,10 +762,12 @@ void TBlendForeBackRasterFx::linearTmpl(TRasterPT dn_ras_out, if (dna > 0.0) { for (int c = 0; c < 3; c++) { if (premultiplied_sw) - dnBGR[c] = to_linear_color_space(dnBGR[c] / dna, 1.0, gamma) * dna; + dnBGR[c] = + to_linear_color_space(dnBGR[c] / dna, 1.0, gammaDif) * dna; else - dnBGR[c] = to_linear_color_space(dnBGR[c], 1.0, gamma); + dnBGR[c] = to_linear_color_space(dnBGR[c], 1.0, gammaDif); } + to_xyz(dnXYZ, dnBGR); } @@ -630,28 +776,29 @@ void TBlendForeBackRasterFx::linearTmpl(TRasterPT dn_ras_out, upBGR[1] = static_cast(up_pix->g) / maxi; upBGR[2] = static_cast(up_pix->r) / maxi; double upa = static_cast(up_pix->m) / maxi; + for (int c = 0; c < 3; c++) { if (premultiplied_sw) - upBGR[c] = to_linear_color_space(upBGR[c] / upa, 1.0, gamma) * upa; + upBGR[c] = to_linear_color_space(upBGR[c] / upa, 1.0, gammaDif) * upa; else - upBGR[c] = to_linear_color_space(upBGR[c], 1.0, gamma); + upBGR[c] = to_linear_color_space(upBGR[c], 1.0, gammaDif); } double upXYZ[3]; to_xyz(upXYZ, upBGR); brendKernel(dnXYZ[0], dnXYZ[1], dnXYZ[2], dna, upXYZ[0], upXYZ[1], - upXYZ[2], upa, tmp_opacity, alpha_rendering_sw, true); + upXYZ[2], upa, tmp_opacity, alpha_rendering_sw, false); to_bgr(dnBGR, dnXYZ); // premultiply the result double nonlinear_b = - to_nonlinear_color_space(dnBGR[0] / dna, 1.0, gamma) * dna; + to_nonlinear_color_space(dnBGR[0] / dna, 1.0, gammaDif) * dna; double nonlinear_g = - to_nonlinear_color_space(dnBGR[1] / dna, 1.0, gamma) * dna; + to_nonlinear_color_space(dnBGR[1] / dna, 1.0, gammaDif) * dna; double nonlinear_r = - to_nonlinear_color_space(dnBGR[2] / dna, 1.0, gamma) * dna; + to_nonlinear_color_space(dnBGR[2] / dna, 1.0, gammaDif) * dna; out_pix->r = static_cast(clamp(nonlinear_r, 0.0, 1.0) * (maxi + 0.999999)); @@ -666,13 +813,166 @@ void TBlendForeBackRasterFx::linearTmpl(TRasterPT dn_ras_out, //------------------------------------------------------------ +template <> +void TBlendForeBackRasterFx::linearTmpl(TRasterFP dn_ras_out, + const TRasterFP& up_ras, + const double up_opacity, + const double gammaDif) { + bool clipping_mask_sw = this->m_clipping_mask->getValue(); + bool alpha_rendering_sw = (m_alpha_rendering.getPointer()) + ? this->m_alpha_rendering->getValue() + : true; + bool premultiplied_sw = this->m_premultiplied->getValue(); + // double maxi = static_cast(T::maxChannelValue); // 255or65535 + // double limit = (maxi + 0.5) / (maxi + 1.0); + + assert(dn_ras_out->getSize() == up_ras->getSize()); + + for (int yy = 0; yy < dn_ras_out->getLy(); ++yy) { + TPixelF* out_pix = dn_ras_out->pixels(yy); + const TPixelF* const out_end = out_pix + dn_ras_out->getLx(); + const TPixelF* up_pix = up_ras->pixels(yy); + for (; out_pix < out_end; ++out_pix, ++up_pix) { + if (up_pix->m <= 0.f || up_opacity <= 0.f) { + continue; + } + + double dna = static_cast(out_pix->m); + double tmp_opacity = clipping_mask_sw ? up_opacity * dna : up_opacity; + if (tmp_opacity <= 0.) continue; + + double dnBGR[3]; + dnBGR[0] = static_cast(out_pix->b); + dnBGR[1] = static_cast(out_pix->g); + dnBGR[2] = static_cast(out_pix->r); + double dnXYZ[3] = {0.0, 0.0, 0.0}; + if (dna > 0.0) { + for (int c = 0; c < 3; c++) { + if (premultiplied_sw) + dnBGR[c] = + to_linear_color_space(dnBGR[c] / dna, 1.0, gammaDif) * dna; + else + dnBGR[c] = to_linear_color_space(dnBGR[c], 1.0, gammaDif); + } + to_xyz(dnXYZ, dnBGR); + } + + double upBGR[3]; + upBGR[0] = static_cast(up_pix->b); + upBGR[1] = static_cast(up_pix->g); + upBGR[2] = static_cast(up_pix->r); + double upa = static_cast(up_pix->m); + + for (int c = 0; c < 3; c++) { + if (premultiplied_sw) + upBGR[c] = to_linear_color_space(upBGR[c] / upa, 1.0, gammaDif) * upa; + else + upBGR[c] = to_linear_color_space(upBGR[c], 1.0, gammaDif); + } + + double upXYZ[3]; + to_xyz(upXYZ, upBGR); + + brendKernel(dnXYZ[0], dnXYZ[1], dnXYZ[2], dna, upXYZ[0], upXYZ[1], + upXYZ[2], upa, tmp_opacity, alpha_rendering_sw, false); + + to_bgr(dnBGR, dnXYZ); + + // premultiply the result + double nonlinear_b = + to_nonlinear_color_space(dnBGR[0] / dna, 1.0, gammaDif) * dna; + double nonlinear_g = + to_nonlinear_color_space(dnBGR[1] / dna, 1.0, gammaDif) * dna; + double nonlinear_r = + to_nonlinear_color_space(dnBGR[2] / dna, 1.0, gammaDif) * dna; + + out_pix->r = nonlinear_r; + out_pix->g = nonlinear_g; + out_pix->b = nonlinear_b; + out_pix->m = dna; + } + } +} + +//------------------------------------------------------------ +template +void TBlendForeBackRasterFx::premultiToUnpremulti( + TRasterPT dn_ras, const TRasterPT& up_ras, + const double colorSpaceGamma) { + double maxi = static_cast(T::maxChannelValue); // 255or65535 + + assert(dn_ras->getSize() == up_ras->getSize()); + assert(dn_ras->isLinear() == up_ras->isLinear()); + + for (int yy = 0; yy < dn_ras->getLy(); ++yy) { + T* dn_pix = dn_ras->pixels(yy); + const T* const dn_end = dn_pix + dn_ras->getLx(); + T* up_pix = up_ras->pixels(yy); + for (; dn_pix < dn_end; ++dn_pix, ++up_pix) { + double upa = static_cast(up_pix->m) / maxi; + if (upa > 0. && upa < 1.) { + double upr = static_cast(up_pix->r) / maxi; + double upg = static_cast(up_pix->g) / maxi; + double upb = static_cast(up_pix->b) / maxi; + double up_fac = std::pow(upa, colorSpaceGamma - 1.); + up_pix->r = static_cast(upr * up_fac * (maxi + 0.999999)); + up_pix->g = static_cast(upg * up_fac * (maxi + 0.999999)); + up_pix->b = static_cast(upb * up_fac * (maxi + 0.999999)); + } + double dna = static_cast(dn_pix->m) / maxi; + if (dna > 0. && dna < 1.) { + double dnr = static_cast(dn_pix->r) / maxi; + double dng = static_cast(dn_pix->g) / maxi; + double dnb = static_cast(dn_pix->b) / maxi; + double dn_fac = std::pow(dna, colorSpaceGamma - 1.); + dn_pix->r = static_cast(dnr * dn_fac * (maxi + 0.999999)); + dn_pix->g = static_cast(dng * dn_fac * (maxi + 0.999999)); + dn_pix->b = static_cast(dnb * dn_fac * (maxi + 0.999999)); + } + } + } +} + +//------------------------------------------------------------ +template <> +void TBlendForeBackRasterFx::premultiToUnpremulti( + TRasterFP dn_ras, const TRasterFP& up_ras, const double colorSpaceGamma) { + assert(dn_ras->getSize() == up_ras->getSize()); + assert(dn_ras->isLinear() == up_ras->isLinear()); + + for (int yy = 0; yy < dn_ras->getLy(); ++yy) { + TPixelF* dn_pix = dn_ras->pixels(yy); + const TPixelF* const dn_end = dn_pix + dn_ras->getLx(); + TPixelF* up_pix = up_ras->pixels(yy); + for (; dn_pix < dn_end; ++dn_pix, ++up_pix) { + if (up_pix->m > 0.f && up_pix->m < 1.f) { + float up_fac = + std::pow(up_pix->m, static_cast(colorSpaceGamma - 1.)); + up_pix->r *= up_fac; + up_pix->g *= up_fac; + up_pix->b *= up_fac; + } + if (dn_pix->m > 0.f && dn_pix->m < 1.f) { + float dn_fac = + std::pow(dn_pix->m, static_cast(colorSpaceGamma - 1.)); + dn_pix->r *= dn_fac; + dn_pix->g *= dn_fac; + dn_pix->b *= dn_fac; + } + } + } +} + +//------------------------------------------------------------ + void TBlendForeBackRasterFx::computeUpAndDown(TTile& tile, double frame, const TRenderSettings& rs, TRasterP& dn_ras, TRasterP& up_ras, bool upComputesWholeTile) { /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } /* @@ -757,4 +1057,13 @@ fxをreplaceすると、 : tile.getRaster()->extract(dnRect); up_ras = upTile.getRaster(); assert(dn_ras->getSize() == up_ras->getSize()); -} \ No newline at end of file +} + +//------------------------------------------------------------ + +bool TBlendForeBackRasterFx::toBeComputedInLinearColorSpace( + bool settingsIsLinear, bool tileIsLinear) const { + ColorSpaceMode mode = + static_cast(m_colorSpaceMode->getValue()); + return mode == Linear || (mode == Auto && settingsIsLinear); +} diff --git a/toonz/sources/stdfx/ino_common.h b/toonz/sources/stdfx/ino_common.h index 6696460..76d1b5a 100644 --- a/toonz/sources/stdfx/ino_common.h +++ b/toonz/sources/stdfx/ino_common.h @@ -36,7 +36,9 @@ inline double param_range(void) { return 1.0; } // 1 or 100% inline int channels(void) { return 4; } // RGBM is 4 channels inline int bits(const TRasterP ras) { return ((TRaster64P)ras) ? (std::numeric_limits::digits) - : (std::numeric_limits::digits); + : ((TRaster32P)ras) + ? (std::numeric_limits::digits) + : (std::numeric_limits::digits); // TRasterFP } inline int pixel_bits(const TRasterP ras) { return ino::channels() * ino::bits(ras); @@ -47,6 +49,9 @@ inline double pixel_per_mm(void) { return 1.; } } // namespace ino class TBlendForeBackRasterFx : public TRasterFx { +public: + enum ColorSpaceMode { Auto = 0, Linear, Nonlinear }; + protected: TRasterFxPort m_up; TRasterFxPort m_down; @@ -54,7 +59,10 @@ protected: TBoolParamP m_clipping_mask; TBoolParamP m_linear; + TIntEnumParamP m_colorSpaceMode; + TDoubleParamP m_gamma; + TDoubleParamP m_gammaAdjust; // If the pixel is premultiplied, divide color data by the alpha before // converting from the colorspace, and then multiply by the alpha afterwards. @@ -69,7 +77,8 @@ protected: void doComputeFx(TRasterP& dn_ras_out, const TRasterP& up_ras, const TPoint& pos, const double up_opacity, - const double gamma); + const double gammaDif, const double colorSpaceGamma, + const bool linear_sw); template void nonlinearTmpl(TRasterPT dn_ras_out, const TRasterPT& up_ras, @@ -77,14 +86,18 @@ protected: template void linearTmpl(TRasterPT dn_ras_out, const TRasterPT& up_ras, - const double up_opacity, const double gamma); + const double up_opacity, const double gammaDif); + + template + void premultiToUnpremulti(TRasterPT dn_ras, const TRasterPT& up_ras, + const double colorSpaceGamma); // when compute in xyz color space, do not clamp channel values in the kernel virtual void brendKernel(double& dnr, double& dng, double& dnb, double& dna, const double up_, double upg, double upb, double upa, const double upopacity, const bool alpha_rendering_sw = true, - const bool is_xyz = false) = 0; + const bool do_clamp = true) = 0; void computeUpAndDown(TTile& tile, double frame, const TRenderSettings& rs, TRasterP& dn_ras, TRasterP& up_ras, @@ -93,6 +106,9 @@ protected: public: TBlendForeBackRasterFx(bool clipping_mask, bool has_alpha_option = false); + void onFxVersionSet() override; + void onObsoleteParamLoaded(const std::string&) override; + bool canHandle(const TRenderSettings& rs, double frame) override { return true; } @@ -112,7 +128,24 @@ public: /* FX nodeが無効のときの、表示port番号 */ int getPreferredInputPort() override { return 1; } + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override; + std::string getPluginId() const override { return PLUGIN_PREFIX; } }; +template <> +void TBlendForeBackRasterFx::nonlinearTmpl( + TRasterFP dn_ras_out, const TRasterFP& up_ras, const double up_opacity); + +template <> +void TBlendForeBackRasterFx::linearTmpl(TRasterFP dn_ras_out, + const TRasterFP& up_ras, + const double up_opacity, + const double gammaDif); + +template <> +void TBlendForeBackRasterFx::premultiToUnpremulti( + TRasterFP dn_ras, const TRasterFP& up_ras, const double colorSpaceGamma); + #endif /* !ino_common_h */ diff --git a/toonz/sources/stdfx/ino_density.cpp b/toonz/sources/stdfx/ino_density.cpp index 9eb2d32..395493b 100644 --- a/toonz/sources/stdfx/ino_density.cpp +++ b/toonz/sources/stdfx/ino_density.cpp @@ -31,6 +31,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -52,37 +54,38 @@ FX_PLUGIN_IDENTIFIER(ino_density, "inoDensityFx"); #include "igs_density.h" namespace { + void fx_(TRasterP in_ras, TRasterP refer_ras, const int refer_mode, const double density) { + TRasterGR8P ref_gr8; + if ((refer_ras != nullptr) && (0 <= refer_mode)) { + ref_gr8 = TRasterGR8P(in_ras->getLy(), in_ras->getLx() * sizeof(float)); + ref_gr8->lock(); + ino::ras_to_ref_float_arr(refer_ras, + reinterpret_cast(ref_gr8->getRawData()), + refer_mode); + } + TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + in_ras->getLx() * ino::channels() * sizeof(float)); in_gr8->lock(); - ino::ras_to_arr(in_ras, ino::channels(), in_gr8->getRawData()); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); igs::density::change( - in_gr8->getRawData() // BGRA - , - in_ras->getLy(), in_ras->getLx() // Not use in_ras->getWrap() - , - ino::channels(), ino::bits(in_ras) - - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? refer_ras->getRawData() - : nullptr) // BGRA - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? ino::bits(refer_ras) - : 0), - refer_mode - + reinterpret_cast(in_gr8->getRawData()), in_ras->getLy(), + in_ras->getLx() // Not use in_ras->getWrap() , + ino::channels(), + (ref_gr8) ? reinterpret_cast(ref_gr8->getRawData()) : nullptr, density); - ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); + ino::float_arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); in_gr8->unlock(); + + if (ref_gr8) ref_gr8->unlock(); } -} +} // namespace //------------------------------------------------------------ void ino_density::doCompute(TTile &tile, double frame, const TRenderSettings &rend_sets) { @@ -93,7 +96,8 @@ void ino_density::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } @@ -107,7 +111,7 @@ void ino_density::doCompute(TTile &tile, double frame, /*------ 参照画像生成 --------------------------------------*/ TTile refer_tile; bool refer_sw = false; - if (this->m_refer.isConnected()) { + if (this->m_refer.isConnected() && this->m_ref_mode->getValue() >= 0) { refer_sw = true; this->m_refer->allocateAndCompute( refer_tile, tile.m_pos, diff --git a/toonz/sources/stdfx/ino_hls_add.cpp b/toonz/sources/stdfx/ino_hls_add.cpp index 96018f9..21fafe5 100644 --- a/toonz/sources/stdfx/ino_hls_add.cpp +++ b/toonz/sources/stdfx/ino_hls_add.cpp @@ -66,6 +66,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -96,62 +98,44 @@ void fx_(TRasterP in_ras, const TRasterP noise_ras, const TRasterP refer_ras, std::vector refer_vec; ino::ras_to_vec( noise_ras, ino::channels(), refer_vec );***/ + TRasterGR8P ref_gr8; + if ((refer_ras != nullptr) && (0 <= refer_mode)) { + ref_gr8 = TRasterGR8P(in_ras->getLy(), in_ras->getLx() * sizeof(float)); + ref_gr8->lock(); + ino::ras_to_ref_float_arr(refer_ras, + reinterpret_cast(ref_gr8->getRawData()), + refer_mode); + } + TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + in_ras->getLx() * ino::channels() * sizeof(float)); in_gr8->lock(); - ino::ras_to_arr(in_ras, ino::channels(), in_gr8->getRawData()); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); TRasterGR8P noise_gr8(noise_ras->getLy(), - noise_ras->getLx() * ino::channels() * - ((TRaster64P)noise_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + in_ras->getLx() * ino::channels() * sizeof(float)); noise_gr8->lock(); - ino::ras_to_arr(noise_ras, ino::channels(), noise_gr8->getRawData()); + ino::ras_to_float_arr(noise_ras, ino::channels(), + reinterpret_cast(noise_gr8->getRawData())); igs::hls_add::change( - // in_ras->getRawData() // BGRA - //&in_vec.at(0) // RGBA - in_gr8->getRawData() - - , - in_ras->getLy(), in_ras->getLx() // Not use in_ras->getWrap() - , - ino::channels(), - ino::bits(in_ras) - - //,noise_ras->getRawData() // BGRA - //,&refer_vec.at(0) // RGBA - , - noise_gr8->getRawData() - - , - noise_ras->getLy(), noise_ras->getLx(), ino::channels(), - ino::bits(noise_ras) - - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? refer_ras->getRawData() - : nullptr) // BGRA - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? ino::bits(refer_ras) - : 0), - refer_mode - + reinterpret_cast(in_gr8->getRawData()), in_ras->getLy(), + in_ras->getLx() // Not use in_ras->getWrap() , + ino::channels(), reinterpret_cast(noise_gr8->getRawData()), + (ref_gr8) ? reinterpret_cast(ref_gr8->getRawData()) : nullptr, xoffset, yoffset, from_rgba, offset, hue_scale, lig_scale, sat_scale, alp_scale //,true /* add_blend_sw */ , - anti_alias_sw); - - /***ino::vec_to_ras( refer_vec, 0, 0 ); - ino::vec_to_ras( in_vec, ino::channels(), in_ras, 0 );***/ - - ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); + anti_alias_sw, !((TRasterFP)in_ras)); noise_gr8->unlock(); + ino::float_arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); in_gr8->unlock(); + + if (ref_gr8) ref_gr8->unlock(); } } // namespace //------------------------------------------------------------ @@ -166,7 +150,8 @@ void ino_hls_add::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } @@ -193,7 +178,7 @@ void ino_hls_add::doCompute(TTile &tile, double frame, /*------ 参照画像生成 --------------------------------------*/ TTile refer_tile; bool refer_sw = false; - if (this->m_refer.isConnected()) { + if (this->m_refer.isConnected() && this->m_ref_mode->getValue() >= 0) { refer_sw = true; this->m_refer->allocateAndCompute( refer_tile, tile.m_pos, diff --git a/toonz/sources/stdfx/ino_hls_adjust.cpp b/toonz/sources/stdfx/ino_hls_adjust.cpp index a83d23b..ee24a6b 100644 --- a/toonz/sources/stdfx/ino_hls_adjust.cpp +++ b/toonz/sources/stdfx/ino_hls_adjust.cpp @@ -70,6 +70,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -98,44 +100,41 @@ void fx_(TRasterP in_ras, const TRasterP refer_ras, const int refer_mode, /***std::vector in_vec; ino::ras_to_vec( in_ras, ino::channels(), in_vec );***/ + TRasterGR8P ref_gr8; + if ((refer_ras != nullptr) && (0 <= refer_mode)) { + ref_gr8 = TRasterGR8P(in_ras->getLy(), in_ras->getLx() * sizeof(float)); + ref_gr8->lock(); + ino::ras_to_ref_float_arr(refer_ras, + reinterpret_cast(ref_gr8->getRawData()), + refer_mode); + } + TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + in_ras->getLx() * ino::channels() * sizeof(float)); in_gr8->lock(); - ino::ras_to_arr(in_ras, ino::channels(), in_gr8->getRawData()); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); igs::hls_adjust::change( - // in_ras->getRawData() // BGRA - //&in_vec.at(0) // RGBA - in_gr8->getRawData() - - , - in_ras->getLy(), in_ras->getLx() // Not use in_ras->getWrap() - , - ino::channels(), ino::bits(in_ras) - - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? refer_ras->getRawData() - : nullptr) // BGRA - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? ino::bits(refer_ras) - : 0), - refer_mode - + reinterpret_cast(in_gr8->getRawData()), in_ras->getLy(), + in_ras->getLx() // Not use in_ras->getWrap() , + ino::channels(), + (ref_gr8) ? reinterpret_cast(ref_gr8->getRawData()) : nullptr, hue_pivot, hue_scale, hue_shift, lig_pivot, lig_scale, lig_shift, sat_pivot, sat_scale, sat_shift //,true /* add_blend_sw */ , - anti_alias_sw); + anti_alias_sw, !((TRasterFP)in_ras)); /***ino::vec_to_ras( in_vec, ino::channels(), in_ras, 0 );***/ - ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); + ino::float_arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); in_gr8->unlock(); + + if (ref_gr8) ref_gr8->unlock(); } } // namespace //------------------------------------------------------------ @@ -148,7 +147,8 @@ void ino_hls_adjust::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } diff --git a/toonz/sources/stdfx/ino_hls_noise.cpp b/toonz/sources/stdfx/ino_hls_noise.cpp index 2e8aed7..0deac4e 100644 --- a/toonz/sources/stdfx/ino_hls_noise.cpp +++ b/toonz/sources/stdfx/ino_hls_noise.cpp @@ -77,6 +77,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -115,41 +117,35 @@ void fx_(TRasterP in_ras, const TRasterP refer_ras, const int refer_mode, unsigned long random_seed, double near_blur, double effective, double center, int type, const int camera_x, const int camera_y, const int camera_w, const int camera_h, const bool anti_alias_sw) { + TRasterGR8P ref_gr8; + if ((refer_ras != nullptr) && (0 <= refer_mode)) { + ref_gr8 = TRasterGR8P(in_ras->getLy(), in_ras->getLx() * sizeof(float)); + ref_gr8->lock(); + ino::ras_to_ref_float_arr(refer_ras, + reinterpret_cast(ref_gr8->getRawData()), + refer_mode); + } + TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + in_ras->getLx() * ino::channels() * sizeof(float)); in_gr8->lock(); - ino::ras_to_arr(in_ras, ino::channels(), in_gr8->getRawData()); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); - /* igs::hls_noise::change(-)は今後つかわない2011-07-15 */ igs::hls_noise::change( - // in_ras->getRawData() // BGRA - in_gr8->getRawData() - - , - in_ras->getLy(), in_ras->getLx() // Must Not use in_ras->getWrap() - , - ino::channels(), ino::bits(in_ras) + reinterpret_cast(in_gr8->getRawData()), in_ras->getLy(), + in_ras->getLx(), // Must Not use in_ras->getWrap() + ino::channels(), + (ref_gr8) ? reinterpret_cast(ref_gr8->getRawData()) : nullptr, + camera_x, camera_y, camera_w, camera_h, hue_range, lig_range, sat_range, + alp_range, random_seed, near_blur, effective, center, type, effective, + center, type, effective, center, type, anti_alias_sw, + !((TRasterFP)in_ras)); - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? refer_ras->getRawData() - : nullptr) // BGRA - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? ino::bits(refer_ras) - : 0), - refer_mode - - , - camera_x, camera_y, camera_w, camera_h - - , - hue_range, lig_range, sat_range, alp_range, random_seed, near_blur, - effective, center, type, effective, center, type, effective, center, type, - anti_alias_sw); - - ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); + ino::float_arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); in_gr8->unlock(); + + if (ref_gr8) ref_gr8->unlock(); } } // namespace //------------------------------------------------------------ @@ -162,7 +158,8 @@ void ino_hls_noise::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } diff --git a/toonz/sources/stdfx/ino_hsv_add.cpp b/toonz/sources/stdfx/ino_hsv_add.cpp index 319c6bf..4361885 100644 --- a/toonz/sources/stdfx/ino_hsv_add.cpp +++ b/toonz/sources/stdfx/ino_hsv_add.cpp @@ -66,6 +66,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -99,62 +101,44 @@ void fx_(TRasterP in_ras, const TRasterP noise_ras, const TRasterP refer_ras, std::vector refer_vec; ino::ras_to_vec( noise_ras, ino::channels(), refer_vec );***/ + TRasterGR8P ref_gr8; + if ((refer_ras != nullptr) && (0 <= refer_mode)) { + ref_gr8 = TRasterGR8P(in_ras->getLy(), in_ras->getLx() * sizeof(float)); + ref_gr8->lock(); + ino::ras_to_ref_float_arr(refer_ras, + reinterpret_cast(ref_gr8->getRawData()), + refer_mode); + } + TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + in_ras->getLx() * ino::channels() * sizeof(float)); in_gr8->lock(); - ino::ras_to_arr(in_ras, ino::channels(), in_gr8->getRawData()); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); TRasterGR8P noise_gr8(noise_ras->getLy(), - noise_ras->getLx() * ino::channels() * - ((TRaster64P)noise_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + noise_ras->getLx() * ino::channels() * sizeof(float)); noise_gr8->lock(); - ino::ras_to_arr(noise_ras, ino::channels(), noise_gr8->getRawData()); + ino::ras_to_float_arr(noise_ras, ino::channels(), + reinterpret_cast(noise_gr8->getRawData())); igs::hsv_add::change( - // in_ras->getRawData() // BGRA - //&in_vec.at(0) // RGBA - in_gr8->getRawData() - - , - in_ras->getLy(), in_ras->getLx() // Not use in_ras->getWrap() - , - ino::channels(), - ino::bits(in_ras) - - //,noise_ras->getRawData() // BGRA - //,&refer_vec.at(0) // RGBA - , - noise_gr8->getRawData() - - , - noise_ras->getLy(), noise_ras->getLx(), ino::channels(), - ino::bits(noise_ras) - - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? refer_ras->getRawData() - : nullptr) // BGRA - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? ino::bits(refer_ras) - : 0), - refer_mode - + reinterpret_cast(in_gr8->getRawData()), in_ras->getLy(), + in_ras->getLx() // Not use in_ras->getWrap() , + ino::channels(), reinterpret_cast(noise_gr8->getRawData()), + (ref_gr8) ? reinterpret_cast(ref_gr8->getRawData()) : nullptr, xoffset, yoffset, from_rgba, offset, hue_scale, sat_scale, val_scale, - alp_scale - - //,true /* add_blend_sw */ - , - anti_alias_sw); + alp_scale, anti_alias_sw); /***ino::vec_to_ras( refer_vec, 0, 0 ); ino::vec_to_ras( in_vec, ino::channels(), in_ras, 0 );***/ - ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); + ino::float_arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); noise_gr8->unlock(); in_gr8->unlock(); + + if (ref_gr8) ref_gr8->unlock(); } } // namespace //------------------------------------------------------------ @@ -169,7 +153,8 @@ void ino_hsv_add::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } diff --git a/toonz/sources/stdfx/ino_hsv_adjust.cpp b/toonz/sources/stdfx/ino_hsv_adjust.cpp index 811df67..86bba9f 100644 --- a/toonz/sources/stdfx/ino_hsv_adjust.cpp +++ b/toonz/sources/stdfx/ino_hsv_adjust.cpp @@ -70,6 +70,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -98,32 +100,27 @@ void fx_(TRasterP in_ras, const TRasterP refer_ras, const int refer_mode, /***std::vector in_vec; ino::ras_to_vec( in_ras, ino::channels(), in_vec );***/ + TRasterGR8P ref_gr8; + if ((refer_ras != nullptr) && (0 <= refer_mode)) { + ref_gr8 = TRasterGR8P(in_ras->getLy(), in_ras->getLx() * sizeof(float)); + ref_gr8->lock(); + ino::ras_to_ref_float_arr(refer_ras, + reinterpret_cast(ref_gr8->getRawData()), + refer_mode); + } + TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + in_ras->getLx() * ino::channels() * sizeof(float)); in_gr8->lock(); - ino::ras_to_arr(in_ras, ino::channels(), in_gr8->getRawData()); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); igs::hsv_adjust::change( - // in_ras->getRawData() // BGRA - //&in_vec.at(0) // RGBA - in_gr8->getRawData() - - , - in_ras->getLy(), in_ras->getLx() // Not use in_ras->getWrap() - , - ino::channels(), ino::bits(in_ras) - - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? refer_ras->getRawData() - : nullptr) // BGRA - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? ino::bits(refer_ras) - : 0), - refer_mode - + reinterpret_cast(in_gr8->getRawData()), in_ras->getLy(), + in_ras->getLx() // Not use in_ras->getWrap() , + ino::channels(), + (ref_gr8) ? reinterpret_cast(ref_gr8->getRawData()) : nullptr, hue_pivot, hue_scale, hue_shift, sat_pivot, sat_scale, sat_shift, val_pivot, val_scale, val_shift @@ -134,8 +131,10 @@ void fx_(TRasterP in_ras, const TRasterP refer_ras, const int refer_mode, /***ino::vec_to_ras( in_vec, ino::channels(), in_ras, 0 );***/ - ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); + ino::float_arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); in_gr8->unlock(); + + if (ref_gr8) ref_gr8->unlock(); } } // namespace //------------------------------------------------------------ @@ -148,7 +147,8 @@ void ino_hsv_adjust::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } diff --git a/toonz/sources/stdfx/ino_hsv_noise.cpp b/toonz/sources/stdfx/ino_hsv_noise.cpp index 1997d4b..ab9da80 100644 --- a/toonz/sources/stdfx/ino_hsv_noise.cpp +++ b/toonz/sources/stdfx/ino_hsv_noise.cpp @@ -77,6 +77,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -110,48 +112,39 @@ FX_PLUGIN_IDENTIFIER(ino_hsv_noise, "inohsvNoiseFx"); //------------------------------------------------------------ #include "igs_hsv_noise.h" namespace { -void fx_(TRasterP in_ras, const TRasterP refer_ras, const int refer_mode - - , +void fx_(TRasterP in_ras, const TRasterP refer_ras, const int refer_mode, double hue_range, double sat_range, double val_range, double alp_range, unsigned long random_seed, double near_blur, double effective, double center, int type, const int camera_x, const int camera_y, const int camera_w, const int camera_h, const bool anti_alias_sw) { + TRasterGR8P ref_gr8; + if ((refer_ras != nullptr) && (0 <= refer_mode)) { + ref_gr8 = TRasterGR8P(in_ras->getLy(), in_ras->getLx() * sizeof(float)); + ref_gr8->lock(); + ino::ras_to_ref_float_arr(refer_ras, + reinterpret_cast(ref_gr8->getRawData()), + refer_mode); + } + TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + in_ras->getLx() * ino::channels() * sizeof(float)); in_gr8->lock(); - ino::ras_to_arr(in_ras, ino::channels(), in_gr8->getRawData()); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); - /* igs::hsv_noise::change(-)は今後つかわない2011-07-15 */ igs::hsv_noise::change( - // in_ras->getRawData() // BGRA - in_gr8->getRawData() - - , - in_ras->getLy(), in_ras->getLx() // Must Not use in_ras->getWrap() - , - ino::channels(), ino::bits(in_ras) + reinterpret_cast(in_gr8->getRawData()), in_ras->getLy(), + in_ras->getLx(), // Must Not use in_ras->getWrap() + ino::channels(), + (ref_gr8) ? reinterpret_cast(ref_gr8->getRawData()) : nullptr, + camera_x, camera_y, camera_w, camera_h, hue_range, sat_range, val_range, + alp_range, random_seed, near_blur, effective, center, type, effective, + center, type, effective, center, type, anti_alias_sw); - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? refer_ras->getRawData() - : nullptr) // BGRA - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? ino::bits(refer_ras) - : 0), - refer_mode - - , - camera_x, camera_y, camera_w, camera_h - - , - hue_range, sat_range, val_range, alp_range, random_seed, near_blur, - effective, center, type, effective, center, type, effective, center, type, - anti_alias_sw); - - ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); + ino::float_arr_to_ras(in_gr8->getRawData(), ino::channels(), in_ras, 0); in_gr8->unlock(); + + if (ref_gr8) ref_gr8->unlock(); } } // namespace //------------------------------------------------------------ @@ -164,7 +157,8 @@ void ino_hsv_noise::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } diff --git a/toonz/sources/stdfx/ino_level_auto.cpp b/toonz/sources/stdfx/ino_level_auto.cpp index 824cb33..ae89bdc 100644 --- a/toonz/sources/stdfx/ino_level_auto.cpp +++ b/toonz/sources/stdfx/ino_level_auto.cpp @@ -39,6 +39,7 @@ public: 1.0 * ino::param_range()); this->m_gamma->setValueRange(0.1 * ino::param_range(), 10.0 * ino::param_range()); /* gamma値 */ + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -65,8 +66,9 @@ void fx_(TRasterP in_ras, bool *act_sw, double *in_min_shift, const int camera_h) { TRasterGR8P in_gr8(in_ras->getLy(), in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + ((TRaster64P)in_ras ? sizeof(unsigned short) + : (TRaster32P)in_ras ? sizeof(unsigned char) + : sizeof(float))); in_gr8->lock(); ino::ras_to_arr(in_ras, ino::channels(), in_gr8->getRawData()); @@ -98,7 +100,8 @@ void ino_level_auto::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } diff --git a/toonz/sources/stdfx/ino_level_master.cpp b/toonz/sources/stdfx/ino_level_master.cpp index 6f85fc1..f18a44e 100644 --- a/toonz/sources/stdfx/ino_level_master.cpp +++ b/toonz/sources/stdfx/ino_level_master.cpp @@ -55,6 +55,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -83,7 +85,8 @@ void ino_level_master::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } @@ -136,10 +139,12 @@ void ino_level_master::doCompute(TTile &tile, double frame, /* ------ fx処理 ------------------------------------------ */ try { TRasterP in_ras = tile.getRaster(); - TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + TRasterGR8P in_gr8( + in_ras->getLy(), + in_ras->getLx() * ino::channels() * + ((TRaster64P)in_ras ? sizeof(unsigned short) + : (TRaster32P)in_ras ? sizeof(unsigned char) + : sizeof(float))); // TRasterFP case in_ras->lock(); if (refer_tile.getRaster() != nullptr) { diff --git a/toonz/sources/stdfx/ino_level_rgba.cpp b/toonz/sources/stdfx/ino_level_rgba.cpp index ae5a45c..ef6a1f7 100644 --- a/toonz/sources/stdfx/ino_level_rgba.cpp +++ b/toonz/sources/stdfx/ino_level_rgba.cpp @@ -113,6 +113,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -141,7 +143,8 @@ void ino_level_rgba::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } @@ -228,10 +231,12 @@ void ino_level_rgba::doCompute(TTile &tile, double frame, try { TRasterP in_ras = tile.getRaster(); - TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + TRasterGR8P in_gr8( + in_ras->getLy(), + in_ras->getLx() * ino::channels() * + ((TRaster64P)in_ras ? sizeof(unsigned short) + : (TRaster32P)in_ras ? sizeof(unsigned char) + : sizeof(float))); // TRasterFP case in_ras->lock(); if (refer_tile.getRaster() != nullptr) { diff --git a/toonz/sources/stdfx/ino_maxmin.cpp b/toonz/sources/stdfx/ino_maxmin.cpp index e431e0e..9f673e1 100644 --- a/toonz/sources/stdfx/ino_maxmin.cpp +++ b/toonz/sources/stdfx/ino_maxmin.cpp @@ -60,6 +60,8 @@ public: this->m_ref_mode->addItem(-1, "Nothing"); this->m_ref_mode->setDefaultValue(0); this->m_ref_mode->setValue(0); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -108,10 +110,12 @@ void fx_(const TRasterP in_ras // with margin , const int nthread) { - TRasterGR8P out_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + TRasterGR8P out_gr8( + in_ras->getLy(), + in_ras->getLx() * ino::channels() * + ((TRaster64P)in_ras ? sizeof(unsigned short) + : (TRaster32P)in_ras ? sizeof(unsigned char) + : sizeof(float))); // TRasterFP case out_gr8->lock(); igs::maxmin::convert( @@ -142,7 +146,7 @@ void fx_(const TRasterP in_ras // with margin ino::arr_to_ras(out_gr8->getRawData(), ino::channels(), out_ras, margin); out_gr8->unlock(); } -} +} // namespace //------------------------------------------------------------ void ino_maxmin::doCompute(TTile &tile, double frame, const TRenderSettings &rend_sets) { @@ -153,7 +157,8 @@ void ino_maxmin::doCompute(TTile &tile, double frame, } /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } diff --git a/toonz/sources/stdfx/ino_pn_clouds.cpp b/toonz/sources/stdfx/ino_pn_clouds.cpp index b67b447..c627564 100644 --- a/toonz/sources/stdfx/ino_pn_clouds.cpp +++ b/toonz/sources/stdfx/ino_pn_clouds.cpp @@ -46,6 +46,8 @@ public: this->m_size->setValueRange(0.0, 1000.0); this->m_persistance->setValueRange(0.1 * ino::param_range(), 2.0 * ino::param_range()); + + enableComputeInFloat(true); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { @@ -79,7 +81,8 @@ void fx_(TRasterP in_ras, const double zz, const int octaves, void ino_pn_clouds::doCompute(TTile &tile, double frame, const TRenderSettings &rend_sets) { /* ------ サポートしていないPixelタイプはエラーを投げる --- */ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } diff --git a/toonz/sources/stdfx/ino_radial_blur.cpp b/toonz/sources/stdfx/ino_radial_blur.cpp index d04ea0d..8655fc9 100644 --- a/toonz/sources/stdfx/ino_radial_blur.cpp +++ b/toonz/sources/stdfx/ino_radial_blur.cpp @@ -79,6 +79,8 @@ public: this->m_ref_mode->addItem(-1, "Nothing"); this->m_type->addItem(1, "Uniform Length"); this->m_intensity_correlation_with_ellipse->setValueRange(-1.0, 1.0); + + enableComputeInFloat(true); } //------------------------------------------------------------ TPointD get_render_center(const double frame, const TPointD &pos, @@ -230,6 +232,7 @@ void fx_(const TRasterP in_ras, // with margin in_gr8->unlock(); ino::float_arr_to_ras(out_buffer->getRawData(), ino::channels(), out_ras, 0); out_buffer->unlock(); + if (ref_gr8) ref_gr8->unlock(); } } // namespace @@ -242,7 +245,8 @@ void ino_radial_blur::doCompute(TTile &tile, double frame, return; } /*------ サポートしていないPixelタイプはエラーを投げる -----*/ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } /*------ パラメータを得る ----------------------------------*/ diff --git a/toonz/sources/stdfx/ino_spin_blur.cpp b/toonz/sources/stdfx/ino_spin_blur.cpp index 74027b1..69d159e 100644 --- a/toonz/sources/stdfx/ino_spin_blur.cpp +++ b/toonz/sources/stdfx/ino_spin_blur.cpp @@ -64,6 +64,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + + enableComputeInFloat(true); } //------------------------------------------------------------ TPointD get_render_center(const double frame, const TPointD &pos, @@ -197,6 +199,7 @@ void fx_(const TRasterP in_ras, // with margin in_gr8->unlock(); ino::float_arr_to_ras(out_buffer->getRawData(), ino::channels(), out_ras, 0); out_buffer->unlock(); + if (ref_gr8) ref_gr8->unlock(); } } // namespace @@ -209,7 +212,8 @@ void ino_spin_blur::doCompute(TTile &tile, double frame, return; } /*------ サポートしていないPixelタイプはエラーを投げる -----*/ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } /*------ パラメータを得る ----------------------------------*/ diff --git a/toonz/sources/stdfx/ino_warp_hv.cpp b/toonz/sources/stdfx/ino_warp_hv.cpp index 6aa8841..d20ff57 100644 --- a/toonz/sources/stdfx/ino_warp_hv.cpp +++ b/toonz/sources/stdfx/ino_warp_hv.cpp @@ -4,6 +4,7 @@ #include "ino_common.h" #include "igs_warp.h" +#include "trop.h" //------------------------------------------------------------ class ino_warp_hv final : public TStandardRasterFx { FX_PLUGIN_DECLARATION(ino_warp_hv) @@ -51,6 +52,8 @@ public: this->m_v_ref_mode->addItem(1, "Green"); this->m_v_ref_mode->addItem(0, "Blue"); this->m_v_ref_mode->addItem(3, "Alpha"); + + enableComputeInFloat(true); } void get_render_real_hv(const double frame, const TAffine affine, double &h_maxlen, double &v_maxlen) { @@ -114,7 +117,7 @@ template void data_set_template_(const TRasterPT in_ras // with margin , const int margin, TRasterPT out_ras // no margin - ) { +) { for (int yy = 0; yy < out_ras->getLy(); ++yy) { const T *in_ras_sl = in_ras->pixels(yy + margin); T *out_ras_sl = out_ras->pixels(yy); @@ -138,60 +141,87 @@ void fx_(TRasterP in_ras // with margin const double h_maxlen, const double v_maxlen, const int h_ref_mode, const int v_ref_mode, const bool alpha_rendering_sw, const bool anti_aliasing_sw) { - if (0 != hori_ras) { - const int bits = ino::bits(hori_ras); - igs::warp::hori_change(in_ras->getRawData() // BGRA - , - in_ras->getLy(), in_ras->getLx(), ino::channels(), - ino::bits(in_ras) + if (in_ras->getPixelSize() == 4 || in_ras->getPixelSize() == 8) { + TRasterGR8P in_gr8(in_ras->getLy(), + in_ras->getLx() * ino::channels() * sizeof(float)); + in_gr8->lock(); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); - , - hori_ras->getRawData() // BGRA - , - ino::channels() - //, 2 // order is "bgra", then r is 2. - , - h_ref_mode, bits + if (0 != hori_ras) { + TRasterGR8P hori_gr8(hori_ras->getLy(), + hori_ras->getLx() * ino::channels() * sizeof(float)); + hori_gr8->lock(); + ino::ras_to_float_arr(hori_ras, ino::channels(), + reinterpret_cast(hori_gr8->getRawData())); - , - (double)(1 << (bits - 1)) / ((1 << bits) - 1) - // , h_maxlen - , - -h_maxlen /* 移動方向と参照方向は逆 */ - * (double)((1 << bits) - 1) / (1 << (bits - 1)), - alpha_rendering_sw, anti_aliasing_sw); - } - if (0 != vert_ras) { - const int bits = ino::bits(vert_ras); - igs::warp::vert_change(in_ras->getRawData() // BGRA - , - in_ras->getLy(), in_ras->getLx(), ino::channels(), - ino::bits(in_ras) + igs::warp::hori_change( + reinterpret_cast(in_gr8->getRawData()), // BGRA + in_ras->getLy(), in_ras->getLx(), ino::channels(), + reinterpret_cast(hori_gr8->getRawData()), // BGRA + ino::channels(), h_ref_mode, + -h_maxlen /* 移動方向と参照方向は逆 */ + * 2.0, + alpha_rendering_sw, anti_aliasing_sw); + hori_gr8->unlock(); + } + if (0 != vert_ras) { + TRasterGR8P vert_gr8(vert_ras->getLy(), + vert_ras->getLx() * ino::channels() * sizeof(float)); + vert_gr8->lock(); + ino::ras_to_float_arr(vert_ras, ino::channels(), + reinterpret_cast(vert_gr8->getRawData())); - , - vert_ras->getRawData() // BGRA - , - ino::channels() - //, 2 // order is "bgra", then r is 2. - , - v_ref_mode, bits + igs::warp::vert_change( + reinterpret_cast(in_gr8->getRawData()), // BGRA + in_ras->getLy(), in_ras->getLx(), ino::channels(), + reinterpret_cast(vert_gr8->getRawData()), // BGRA + ino::channels(), v_ref_mode, + -v_maxlen /* 移動方向と参照方向は逆 */ + * 2.0, + alpha_rendering_sw, anti_aliasing_sw); + vert_gr8->unlock(); + } - , - (double)(1 << (bits - 1)) / ((1 << bits) - 1) - // , v_maxlen - , - -v_maxlen /* 移動方向と参照方向は逆 */ - * (double)((1 << bits) - 1) / (1 << (bits - 1)), - alpha_rendering_sw, anti_aliasing_sw); + in_gr8->unlock(); + ino::float_arr_to_ras(in_gr8->getRawData(), ino::channels(), out_ras, + margin); + } else if (in_ras->getPixelSize() == 16) { + if (0 != hori_ras) { + igs::warp::hori_change( + reinterpret_cast(in_ras->getRawData()), // BGRA + in_ras->getLy(), in_ras->getLx(), ino::channels(), + reinterpret_cast(hori_ras->getRawData()), // BGRA + ino::channels(), h_ref_mode, + -h_maxlen /* 移動方向と参照方向は逆 */ + * 2.0, + alpha_rendering_sw, anti_aliasing_sw); + } + if (0 != vert_ras) { + igs::warp::vert_change( + reinterpret_cast(in_ras->getRawData()), // BGRA + in_ras->getLy(), in_ras->getLx(), ino::channels(), + reinterpret_cast(vert_ras->getRawData()), // BGRA + ino::channels(), v_ref_mode, + -v_maxlen /* 移動方向と参照方向は逆 */ + * 2.0, + alpha_rendering_sw, anti_aliasing_sw); + } + data_set_template_(in_ras, margin, out_ras); + } else { + throw TRopException("unsupported input pixel type"); } + /* if ((TRaster32P)in_ras) { data_set_template_(in_ras, margin, out_ras); } else if ((TRaster64P)in_ras) { data_set_template_(in_ras, margin, out_ras); - } -} + } else if ((TRasterFP)in_ras) { + data_set_template_(in_ras, margin, out_ras); + }*/ } +} // namespace //------------------------------------------------------------ void ino_warp_hv::doCompute(TTile &tile, double frame, const TRenderSettings &rend_sets) { @@ -202,7 +232,8 @@ void ino_warp_hv::doCompute(TTile &tile, double frame, } /*------ サポートしていないPixelタイプはエラーを投げる -----*/ - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } @@ -237,6 +268,7 @@ void ino_warp_hv::doCompute(TTile &tile, double frame, TTile vert_tile; const bool hori_cn_is = this->m_hori.isConnected(); const bool vert_cn_is = this->m_vert.isConnected(); + if (hori_cn_is) { this->m_hori->allocateAndCompute( hori_tile, bBox.getP00(), diff --git a/toonz/sources/stdfx/iwa_adjustexposurefx.cpp b/toonz/sources/stdfx/iwa_adjustexposurefx.cpp index 92d7d3a..b067caf 100644 --- a/toonz/sources/stdfx/iwa_adjustexposurefx.cpp +++ b/toonz/sources/stdfx/iwa_adjustexposurefx.cpp @@ -25,6 +25,23 @@ void Iwa_AdjustExposureFx::setSourceRaster(const RASTER srcRas, float4 *dstMem, } } +void Iwa_AdjustExposureFx::setSourceRasterF(const TRasterFP srcRas, + float4 *dstMem, TDimensionI dim) { + float4 *chann_p = dstMem; + + for (int j = 0; j < dim.ly; j++) { + TPixelF *pix = srcRas->pixels(j); + for (int i = 0; i < dim.lx; i++) { + (*chann_p).x = pix->r; + (*chann_p).y = pix->g; + (*chann_p).z = pix->b; + (*chann_p).w = pix->m; + + pix++; + chann_p++; + } + } +} /*------------------------------------------------------------ 出力結果をChannel値に変換してタイルに格納 ------------------------------------------------------------*/ @@ -59,18 +76,77 @@ void Iwa_AdjustExposureFx::setOutputRaster(float4 *srcMem, const RASTER dstRas, } } +void Iwa_AdjustExposureFx::setOutputRasterF(float4 *srcMem, + const TRasterFP dstRas, + TDimensionI dim) { + float4 *chan_p = srcMem; + for (int j = 0; j < dim.ly; j++) { + TPixelF *pix = dstRas->pixels(j); + for (int i = 0; i < dim.lx; i++) { + pix->r = (*chan_p).x; + pix->g = (*chan_p).y; + pix->b = (*chan_p).z; + pix->m = (*chan_p).w; + pix++; + chan_p++; + } + } +} + //------------------------------------------------ Iwa_AdjustExposureFx::Iwa_AdjustExposureFx() - : m_hardness(3.3), m_scale(0.0), m_offset(0.0) { + : m_hardness(3.3) + , m_scale(0.0) + , m_offset(0.0) + , m_gamma(2.2) + , m_gammaAdjust(0.) { addInputPort("Source", m_source); bindParam(this, "hardness", m_hardness, false); + bindParam(this, "gamma", m_gamma); + bindParam(this, "gammaAdjust", m_gammaAdjust); bindParam(this, "scale", m_scale, false); bindParam(this, "offset", m_offset, false); m_hardness->setValueRange(0.05, 20.0); + m_gamma->setValueRange(1.0, 10.0); + m_gammaAdjust->setValueRange(-5., 5.); m_scale->setValueRange(-10.0, 10.0); m_offset->setValueRange(-0.5, 0.5); + enableComputeInFloat(true); + + // Version 1: Exposure is computed by using Hardness + // E = std::pow(10.0, (value - 0.5) / hardness) + // Version 2: Exposure is computed by using Gamma, for easier combination with + // the linear color space + // E = std::pow(value, gamma) + // Version 3: Gamma is computed by rs.m_colorSpaceGamma + gammaAdjust + // this must be called after binding the parameters (see onFxVersionSet()) + setFxVersion(3); +} + +//-------------------------------------------- + +void Iwa_AdjustExposureFx::onFxVersionSet() { + if (getFxVersion() == 1) { // use hardness + getParams()->getParamVar("hardness")->setIsHidden(false); + getParams()->getParamVar("gamma")->setIsHidden(true); + getParams()->getParamVar("gammaAdjust")->setIsHidden(true); + return; + } + getParams()->getParamVar("hardness")->setIsHidden(true); + + bool useGamma = getFxVersion() == 2; + if (useGamma) { + // Automatically update version + if (m_gamma->getKeyframeCount() == 0 && + areAlmostEqual(m_gamma->getDefaultValue(), 2.2)) { + useGamma = false; + setFxVersion(3); + } + } + getParams()->getParamVar("gamma")->setIsHidden(!useGamma); + getParams()->getParamVar("gammaAdjust")->setIsHidden(useGamma); } //------------------------------------------------ @@ -83,11 +159,34 @@ void Iwa_AdjustExposureFx::doCompute(TTile &tile, double frame, return; } + double gamma; + if (getFxVersion() == 1) // hardness + gamma = m_hardness->getValue(frame); + else { // gamma + if (getFxVersion() == 2) + gamma = m_gamma->getValue(frame); + else + gamma = std::max( + 1., settings.m_colorSpaceGamma + m_gammaAdjust->getValue(frame)); + if (tile.getRaster()->isLinear()) gamma /= settings.m_colorSpaceGamma; + } + m_source->compute(tile, frame, settings); - float4 *tile_host; TDimensionI dim(tile.getRaster()->getSize()); + if (TRasterFP rasF = tile.getRaster()) { + if (getFxVersion() == 1) + doFloatCompute(rasF, frame, dim, + HardnessBasedConverter(gamma, settings.m_colorSpaceGamma, + tile.getRaster()->isLinear())); + else + doFloatCompute(rasF, frame, dim, GammaBasedConverter(gamma)); + return; + } + + float4 *tile_host; + /*- ホストメモリ確保 -*/ TRasterGR8P tile_host_ras(sizeof(float4) * dim.lx, dim.ly); tile_host_ras->lock(); @@ -95,12 +194,18 @@ void Iwa_AdjustExposureFx::doCompute(TTile &tile, double frame, TRaster32P ras32 = tile.getRaster(); TRaster64P ras64 = tile.getRaster(); + if (ras32) setSourceRaster(ras32, tile_host, dim); else if (ras64) setSourceRaster(ras64, tile_host, dim); - doCompute_CPU(tile, frame, settings, dim, tile_host); + if (getFxVersion() == 1) + doCompute_CPU(frame, dim, tile_host, + HardnessBasedConverter(gamma, settings.m_colorSpaceGamma, + tile.getRaster()->isLinear())); + else + doCompute_CPU(frame, dim, tile_host, GammaBasedConverter(gamma)); /*- 出力結果をチャンネル値に変換 -*/ tile.getRaster()->clear(); @@ -114,14 +219,14 @@ void Iwa_AdjustExposureFx::doCompute(TTile &tile, double frame, //------------------------------------------------ -void Iwa_AdjustExposureFx::doCompute_CPU(TTile &tile, double frame, - const TRenderSettings &settings, - TDimensionI &dim, float4 *tile_host) { - float hardness = (float)m_hardness->getValue(frame); - float scale = (float)m_scale->getValue(frame); - float offset = (float)m_offset->getValue(frame); +void Iwa_AdjustExposureFx::doCompute_CPU(double frame, TDimensionI &dim, + float4 *tile_host, + const ExposureConverter &conv) { + float scale = (float)m_scale->getValue(frame); + float offset = (float)m_offset->getValue(frame); - float exposureOffset = (powf(10.0f, (float)(std::abs(offset) / hardness)) - 1.0f) * + float exposureOffset = (conv.valueToExposure(std::abs(offset) + 0.5) - + conv.valueToExposure(0.5)) * ((offset < 0.0f) ? -1.0f : 1.0f); float4 *pix = tile_host; @@ -129,9 +234,9 @@ void Iwa_AdjustExposureFx::doCompute_CPU(TTile &tile, double frame, int size = dim.lx * dim.ly; for (int i = 0; i < size; i++, pix++) { /*- RGB->Exposure -*/ - pix->x = powf(10, (pix->x - 0.5f) * hardness); - pix->y = powf(10, (pix->y - 0.5f) * hardness); - pix->z = powf(10, (pix->z - 0.5f) * hardness); + pix->x = conv.valueToExposure(pix->x); + pix->y = conv.valueToExposure(pix->y); + pix->z = conv.valueToExposure(pix->z); /*- スケール -*/ pix->x *= powf(10, scale); @@ -144,14 +249,39 @@ void Iwa_AdjustExposureFx::doCompute_CPU(TTile &tile, double frame, pix->z += exposureOffset; /*- Exposure->RGB -*/ - pix->x = log10f(pix->x) / hardness + 0.5f; - pix->y = log10f(pix->y) / hardness + 0.5f; - pix->z = log10f(pix->z) / hardness + 0.5f; - - /*- クランプ -*/ - pix->x = (pix->x > 1.0f) ? 1.0f : ((pix->x < 0.0f) ? 0.0f : pix->x); - pix->y = (pix->y > 1.0f) ? 1.0f : ((pix->y < 0.0f) ? 0.0f : pix->y); - pix->z = (pix->z > 1.0f) ? 1.0f : ((pix->z < 0.0f) ? 0.0f : pix->z); + pix->x = (pix->x < 0.0f) ? 0.0f : conv.exposureToValue(pix->x); + pix->y = (pix->y < 0.0f) ? 0.0f : conv.exposureToValue(pix->y); + pix->z = (pix->z < 0.0f) ? 0.0f : conv.exposureToValue(pix->z); + } +} + +//------------------------------------------------ + +void Iwa_AdjustExposureFx::doFloatCompute(const TRasterFP rasD, double frame, + TDimensionI &dim, + const ExposureConverter &conv) { + double scale = m_scale->getValue(frame); + double offset = m_offset->getValue(frame); + double exposureOffset = (conv.valueToExposure(std::abs(offset) + 0.5) - + conv.valueToExposure(0.5)) * + ((offset < 0.) ? -1. : 1.); + + for (int j = 0; j < dim.ly; j++) { + TPixelF *pix = rasD->pixels(j); + for (int i = 0; i < dim.lx; i++) { + for (int c = 0; c < 3; c++) { + float *val = (c == 0) ? &pix->r : (c == 1) ? &pix->g : &pix->b; + /*- RGB->Exposure -*/ + *val = conv.valueToExposure(*val); + /*- スケール -*/ + *val *= pow(10.0, scale); + /*- オフセット -*/ + *val += exposureOffset; + /*- Exposure->RGB -*/ + *val = (*val < 0.f) ? 0.f : conv.exposureToValue(*val); + } + pix++; + } } } @@ -175,4 +305,12 @@ bool Iwa_AdjustExposureFx::canHandle(const TRenderSettings &info, return true; } +//------------------------------------------------ + +bool Iwa_AdjustExposureFx::toBeComputedInLinearColorSpace( + bool settingsIsLinear, bool tileIsLinear) const { + // 下流がリニア計算するかどうかで判断。これで変換を最小限にできるか…? + return tileIsLinear; +} + FX_PLUGIN_IDENTIFIER(Iwa_AdjustExposureFx, "iwa_AdjustExposureFx") diff --git a/toonz/sources/stdfx/iwa_adjustexposurefx.h b/toonz/sources/stdfx/iwa_adjustexposurefx.h index 61269bf..f128ae1 100644 --- a/toonz/sources/stdfx/iwa_adjustexposurefx.h +++ b/toonz/sources/stdfx/iwa_adjustexposurefx.h @@ -10,6 +10,7 @@ #include "stdfx.h" #include "tfxparam.h" +#include "iwa_bokeh_util.h" // ExposureConverter struct float4 { float x, y, z, w; @@ -20,16 +21,23 @@ class Iwa_AdjustExposureFx final : public TStandardRasterFx { protected: TRasterFxPort m_source; - TDoubleParamP m_hardness; /*- フィルムのガンマ値 -*/ - TDoubleParamP m_scale; /*- 明るさのスケール値 -*/ - TDoubleParamP m_offset; /*- 明るさのオフセット値 -*/ + TDoubleParamP m_hardness; // gamma (version 1) + TDoubleParamP m_gamma; // gamma (version 2) + TDoubleParamP m_gammaAdjust; // Gamma offset from the current color space + // gamma (version 3) + TDoubleParamP m_scale; /*- 明るさのスケール値 -*/ + TDoubleParamP m_offset; /*- 明るさのオフセット値 -*/ /*- タイルの画像を0〜1に正規化してホストメモリに読み込む -*/ template void setSourceRaster(const RASTER srcRas, float4 *dstMem, TDimensionI dim); + void setSourceRasterF(const TRasterFP srcRas, float4 *dstMem, + TDimensionI dim); /*- 出力結果をChannel値に変換して格納 -*/ template void setOutputRaster(float4 *srcMem, const RASTER dstRas, TDimensionI dim); + void setOutputRasterF(float4 *srcMem, const TRasterFP dstRas, + TDimensionI dim); public: Iwa_AdjustExposureFx(); @@ -37,13 +45,21 @@ public: void doCompute(TTile &tile, double frame, const TRenderSettings &settings) override; - void doCompute_CPU(TTile &tile, double frame, const TRenderSettings &settings, - TDimensionI &dim, float4 *tile_host); + void doCompute_CPU(double frame, TDimensionI &dim, float4 *tile_host, + const ExposureConverter &conv); + + void doFloatCompute(const TRasterFP rasF, double frame, TDimensionI &dim, + const ExposureConverter &conv); bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override; bool canHandle(const TRenderSettings &info, double frame) override; + + void onFxVersionSet() final override; + + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override; }; #endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_barreldistortfx.cpp b/toonz/sources/stdfx/iwa_barreldistortfx.cpp index ed80d1e..f829452 100644 --- a/toonz/sources/stdfx/iwa_barreldistortfx.cpp +++ b/toonz/sources/stdfx/iwa_barreldistortfx.cpp @@ -39,9 +39,6 @@ convert the result to channel value and store to the output raster ------------------------------------------------------------*/ template void setOutputRaster(float4 *srcMem, const RASTER dstRas) { - typename PIXEL::Channel halfChan = - (typename PIXEL::Channel)(PIXEL::maxChannelValue / 2); - dstRas->fill(PIXEL::Transparent); float4 *chan_p = srcMem; @@ -69,6 +66,23 @@ void setOutputRaster(float4 *srcMem, const RASTER dstRas) { } } +template <> +void setOutputRaster(float4 *srcMem, + const TRasterFP dstRas) { + dstRas->fill(TPixelF::Transparent); + + float4 *chan_p = srcMem; + for (int j = 0; j < dstRas->getLy(); j++) { + TPixelF *pix = dstRas->pixels(j); + for (int i = 0; i < dstRas->getLx(); i++, chan_p++, pix++) { + pix->r = (*chan_p).x; + pix->g = (*chan_p).y; + pix->b = (*chan_p).z; + pix->m = (*chan_p).w; + } + } +} + float4 getSource_CPU(float4 *source_host, TDimensionI &dim, int pos_x, int pos_y) { if (pos_x < 0 || pos_x >= dim.lx || pos_y < 0 || pos_y >= dim.ly) @@ -97,7 +111,7 @@ float adjustExposure(float source, float distance, float amount, float gamma, return (ret > 1.0f) ? 1.0f : ((ret < 0.0f) ? 0.0f : ret); } -}; +}; // namespace class Iwa_BarrelDistortFx final : public TStandardRasterFx { FX_PLUGIN_DECLARATION(Iwa_BarrelDistortFx) @@ -145,6 +159,8 @@ public: m_vignetteGamma->setValueRange(0.05, 20.0); m_vignetteMidpoint->setValueRange(0.0, 1.0); m_scale->setValueRange(0.1, 2.0); + + enableComputeInFloat(true); } ~Iwa_BarrelDistortFx(){}; @@ -152,7 +168,7 @@ public: bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { if (m_source.isConnected()) { - bool ret = m_source->doGetBBox(frame, bBox, info); + bool ret = m_source->doGetBBox(frame, bBox, info); if (ret) bBox = TConsts::infiniteRectD; return ret; } @@ -224,9 +240,9 @@ void Iwa_BarrelDistortFx::doCompute(TTile &tile, double frame, if (sourceBBox == TConsts::infiniteRectD) { TPointD tileOffset = tile.m_pos + tile.getRaster()->getCenterD(); sourceBBox = TRectD(TPointD(source_ri.m_cameraBox.x0 + tileOffset.x, - source_ri.m_cameraBox.y0 + tileOffset.y), - TDimensionD(source_ri.m_cameraBox.getLx(), - source_ri.m_cameraBox.getLy())); + source_ri.m_cameraBox.y0 + tileOffset.y), + TDimensionD(source_ri.m_cameraBox.getLx(), + source_ri.m_cameraBox.getLy())); } sourceDim.lx = std::ceil(sourceBBox.getLx()); sourceDim.ly = std::ceil(sourceBBox.getLy()); @@ -242,10 +258,13 @@ void Iwa_BarrelDistortFx::doCompute(TTile &tile, double frame, TRaster32P ras32 = (TRaster32P)sourceTile.getRaster(); TRaster64P ras64 = (TRaster64P)sourceTile.getRaster(); + TRasterFP rasF = (TRasterFP)sourceTile.getRaster(); if (ras32) setSourceRaster(ras32, source_host, sourceDim); else if (ras64) setSourceRaster(ras64, source_host, sourceDim); + else if (rasF) + setSourceRaster(rasF, source_host, sourceDim); TRasterGR8P result_host_ras(outDim.lx * sizeof(float4), outDim.ly); @@ -277,10 +296,13 @@ void Iwa_BarrelDistortFx::doCompute(TTile &tile, double frame, // convert the result to channel value and store to the output raster TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); if (outRas32) setOutputRaster(result_host, outRas32); else if (outRas64) setOutputRaster(result_host, outRas64); + else if (outRasF) + setOutputRaster(result_host, outRasF); result_host_ras->unlock(); } @@ -355,11 +377,11 @@ void Iwa_BarrelDistortFx::doCompute_CPU( if (doVignette) { float distance = distortRatio * distortRatio * val; (*result_p).x = adjustExposure((*result_p).x, distance, vignetteAmount, - vignetteGamma, vignetteMidpoint); - (*result_p).y = adjustExposure((*result_p).y, distance, vignetteAmount, - vignetteGamma, vignetteMidpoint); - (*result_p).z = adjustExposure((*result_p).z, distance, vignetteAmount, - vignetteGamma, vignetteMidpoint); + vignetteGamma, vignetteMidpoint); + (*result_p).y = adjustExposure((*result_p).y, distance, vignetteAmount, + vignetteGamma, vignetteMidpoint); + (*result_p).z = adjustExposure((*result_p).z, distance, vignetteAmount, + vignetteGamma, vignetteMidpoint); } } } diff --git a/toonz/sources/stdfx/iwa_bloomfx.cpp b/toonz/sources/stdfx/iwa_bloomfx.cpp index a2018e8..6541d93 100644 --- a/toonz/sources/stdfx/iwa_bloomfx.cpp +++ b/toonz/sources/stdfx/iwa_bloomfx.cpp @@ -9,11 +9,13 @@ namespace { // convert sRGB color space to power space template inline T to_linear_color_space(T nonlinear_color, T exposure, T gamma) { + if (nonlinear_color <= T(0)) return T(0); return std::pow(nonlinear_color, gamma) / exposure; } // convert power space to sRGB color space template inline T to_nonlinear_color_space(T linear_color, T exposure, T gamma) { + if (linear_color <= T(0)) return T(0); return std::pow(linear_color * exposure, T(1) / gamma); } template @@ -53,6 +55,7 @@ void blurByRotate(cv::Mat &mat) { //-------------------------------------------- Iwa_BloomFx::Iwa_BloomFx() : m_gamma(2.2) + , m_gammaAdjust(0.) , m_auto_gain(false) , m_gain_adjust(0.0) , m_gain(2.0) @@ -60,12 +63,9 @@ Iwa_BloomFx::Iwa_BloomFx() , m_size(100.0) , m_alpha_rendering(false) , m_alpha_mode(new TIntEnumParam(NoAlpha, "No Alpha")) { - // Version 1 : gaussian filter applied with standard deviation 0 - // Version 2 : standard deviation = blurRadius * 0.3 - setFxVersion(2); - addInputPort("Source", m_source); bindParam(this, "gamma", m_gamma); + bindParam(this, "gammaAdjust", m_gammaAdjust); bindParam(this, "auto_gain", m_auto_gain); bindParam(this, "gain_adjust", m_gain_adjust); bindParam(this, "gain", m_gain); @@ -79,12 +79,20 @@ Iwa_BloomFx::Iwa_BloomFx() m_alpha_mode->addItem(LightAndSource, "Light and Source"); m_gamma->setValueRange(0.1, 5.0); + m_gammaAdjust->setValueRange(-5., 5.); m_gain_adjust->setValueRange(-1.0, 1.0); m_gain->setValueRange(0.1, 10.0); m_decay->setValueRange(0, 4); m_size->setValueRange(0.1, 1024.0); m_size->setMeasureName("fxLength"); + + enableComputeInFloat(true); + + // Version 1 : gaussian filter applied with standard deviation 0 + // Version 2 : standard deviation = blurRadius * 0.3 + // Version 3: Gamma is computed by rs.m_colorSpaceGamma + gammaAdjust + setFxVersion(3); } //------------------------------------------------ @@ -107,7 +115,7 @@ double Iwa_BloomFx::getSizePixelAmount(const double val, const TAffine affine) { template void Iwa_BloomFx::setSourceTileToMat(const RASTER ras, cv::Mat &imgMat, const double gamma) { - double maxi = static_cast(PIXEL::maxChannelValue); // 255or65535 + double maxi = static_cast(PIXEL::maxChannelValue); // 255or65535or1.0 for (int j = 0; j < ras->getLy(); j++) { const PIXEL *pix = ras->pixels(j); cv::Vec3f *mat_p = imgMat.ptr(j); @@ -118,12 +126,18 @@ void Iwa_BloomFx::setSourceTileToMat(const RASTER ras, cv::Mat &imgMat, continue; } double bgra[3]; - bgra[0] = static_cast(pix->b) / maxi; - bgra[1] = static_cast(pix->g) / maxi; - bgra[2] = static_cast(pix->r) / maxi; - for (int c = 0; c < 3; c++) { - // assuming that the source image is premultiplied - bgra[c] = to_linear_color_space(bgra[c] / pix_a, 1.0, gamma) * pix_a; + if (areAlmostEqual(gamma, 1.0)) { + bgra[0] = static_cast(pix->b) / maxi; + bgra[1] = static_cast(pix->g) / maxi; + bgra[2] = static_cast(pix->r) / maxi; + } else { + bgra[0] = static_cast(pix->b) / maxi; + bgra[1] = static_cast(pix->g) / maxi; + bgra[2] = static_cast(pix->r) / maxi; + for (int c = 0; c < 3; c++) { + // assuming that the source image is premultiplied + bgra[c] = to_linear_color_space(bgra[c] / pix_a, 1.0, gamma) * pix_a; + } } *mat_p = cv::Vec3f(bgra[0], bgra[1], bgra[2]); } @@ -142,12 +156,19 @@ void Iwa_BloomFx::setMatToOutput(const RASTER ras, const RASTER srcRas, PIXEL *srcPix = srcRas->pixels(j + margin) + margin; for (int i = 0; i < ras->getLx(); i++, pix++, srcPix++, mat_p++) { - double nonlinear_b = - to_nonlinear_color_space((double)(*mat_p)[0] * gain, 1.0, gamma); - double nonlinear_g = - to_nonlinear_color_space((double)(*mat_p)[1] * gain, 1.0, gamma); - double nonlinear_r = - to_nonlinear_color_space((double)(*mat_p)[2] * gain, 1.0, gamma); + double nonlinear_b, nonlinear_g, nonlinear_r; + if (areAlmostEqual(gamma, 1.)) { + nonlinear_b = (double)(*mat_p)[0] * gain; + nonlinear_g = (double)(*mat_p)[1] * gain; + nonlinear_r = (double)(*mat_p)[2] * gain; + } else { + nonlinear_b = + to_nonlinear_color_space((double)(*mat_p)[0] * gain, 1.0, gamma); + nonlinear_g = + to_nonlinear_color_space((double)(*mat_p)[1] * gain, 1.0, gamma); + nonlinear_r = + to_nonlinear_color_space((double)(*mat_p)[2] * gain, 1.0, gamma); + } nonlinear_b = clamp(nonlinear_b, 0.0, 1.0); nonlinear_g = clamp(nonlinear_g, 0.0, 1.0); @@ -171,6 +192,43 @@ void Iwa_BloomFx::setMatToOutput(const RASTER ras, const RASTER srcRas, } } +template <> +void Iwa_BloomFx::setMatToOutput( + const TRasterFP ras, const TRasterFP srcRas, cv::Mat &imgMat, + const double gamma, const double gain, const AlphaMode alphaMode, + const int margin) { + for (int j = 0; j < ras->getLy(); j++) { + cv::Vec3f const *mat_p = imgMat.ptr(j); + TPixelF *pix = ras->pixels(j); + TPixelF *srcPix = srcRas->pixels(j + margin) + margin; + + for (int i = 0; i < ras->getLx(); i++, pix++, srcPix++, mat_p++) { + if (areAlmostEqual(gamma, 1.)) { + pix->b = (*mat_p)[0] * (float)gain; + pix->g = (*mat_p)[1] * (float)gain; + pix->r = (*mat_p)[2] * (float)gain; + } else { + pix->b = to_nonlinear_color_space((*mat_p)[0] * (float)gain, 1.f, + (float)gamma); + pix->g = to_nonlinear_color_space((*mat_p)[1] * (float)gain, 1.f, + (float)gamma); + pix->r = to_nonlinear_color_space((*mat_p)[2] * (float)gain, 1.f, + (float)gamma); + } + + if (alphaMode == NoAlpha) + pix->m = 1.f; + else { + float chan_a = std::max(std::max(pix->b, pix->g), pix->r); + if (alphaMode == Light) + pix->m = chan_a; + else // alphaMode == LightAndSource + pix->m = std::max(chan_a, srcPix->m); + } + } + } +} + //------------------------------------------------ double Iwa_BloomFx::computeAutoGain(cv::Mat &imgMat) { @@ -198,7 +256,15 @@ void Iwa_BloomFx::doCompute(TTile &tile, double frame, return; } // obtain parameters - double gamma = m_gamma->getValue(frame); + double gamma; + if (getFxVersion() <= 2) + gamma = m_gamma->getValue(frame); + else + gamma = std::max( + 1., settings.m_colorSpaceGamma + m_gammaAdjust->getValue(frame)); + double adjustGamma = gamma; + if (tile.getRaster()->isLinear()) gamma /= settings.m_colorSpaceGamma; + bool autoGain = m_auto_gain->getValue(); double gainAdjust = (autoGain) ? std::pow(10.0, m_gain_adjust->getValue(frame)) : 1.0; @@ -223,12 +289,16 @@ void Iwa_BloomFx::doCompute(TTile &tile, double frame, cv::Mat imgMat(cv::Size(dimSrc.lx, dimSrc.ly), CV_32FC3); TRaster32P ras32 = tile.getRaster(); TRaster64P ras64 = tile.getRaster(); + TRasterFP rasF = tile.getRaster(); if (ras32) setSourceTileToMat(sourceTile.getRaster(), imgMat, gamma); else if (ras64) setSourceTileToMat(sourceTile.getRaster(), imgMat, gamma); + else if (rasF) + setSourceTileToMat(sourceTile.getRaster(), imgMat, + gamma); // compute size and intensity ratios of resampled layers // resample size is reduced from the specified size, taking into account @@ -307,8 +377,8 @@ void Iwa_BloomFx::doCompute(TTile &tile, double frame, imgMat = imgMat(roi); if (autoGain) { - gain = - to_linear_color_space(gainAdjust, 1.0, gamma) * computeAutoGain(imgMat); + gain = to_linear_color_space(gainAdjust, 1.0, adjustGamma) * + computeAutoGain(imgMat); } // set the result to the tile, converting to rgb channel values @@ -320,6 +390,9 @@ void Iwa_BloomFx::doCompute(TTile &tile, double frame, setMatToOutput(tile.getRaster(), sourceTile.getRaster(), imgMat, gamma, gain, alphaMode, margin); + else if (rasF) + setMatToOutput(tile.getRaster(), sourceTile.getRaster(), + imgMat, gamma, gain, alphaMode, margin); } //------------------------------------------------ @@ -356,6 +429,12 @@ void Iwa_BloomFx::getParamUIs(TParamUIConcept *&concepts, int &length) { // loaded void Iwa_BloomFx::onObsoleteParamLoaded(const std::string ¶mName) { if (paramName != "alpha_rendering") return; + + // this condition is to prevent overwriting the alpha mode parameter + // when both "alpha rendering" and "alpha mode" are saved in the scene + // due to the previous bug + if (m_alpha_mode->getValue() != NoAlpha) return; + if (m_alpha_rendering->getValue()) m_alpha_mode->setValue(LightAndSource); else @@ -363,4 +442,28 @@ void Iwa_BloomFx::onObsoleteParamLoaded(const std::string ¶mName) { } //------------------------------------------------ +void Iwa_BloomFx::onFxVersionSet() { + bool useGamma = getFxVersion() <= 2; + if (getFxVersion() == 2) { + // Automatically update version + if (m_gamma->getKeyframeCount() == 0 && + areAlmostEqual(m_gamma->getDefaultValue(), 2.2)) { + useGamma = false; + setFxVersion(3); + } + } + getParams()->getParamVar("gamma")->setIsHidden(!useGamma); + getParams()->getParamVar("gammaAdjust")->setIsHidden(useGamma); +} + +//------------------------------------------------ + +bool Iwa_BloomFx::toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const { + // made this effect to compute always in nonlinear + return false; + // return tileIsLinear; +} + +//------------------------------------------------ FX_PLUGIN_IDENTIFIER(Iwa_BloomFx, "iwa_BloomFx") \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_bloomfx.h b/toonz/sources/stdfx/iwa_bloomfx.h index 8153898..a7322d8 100644 --- a/toonz/sources/stdfx/iwa_bloomfx.h +++ b/toonz/sources/stdfx/iwa_bloomfx.h @@ -25,6 +25,8 @@ class Iwa_BloomFx : public TStandardRasterFx { protected: TRasterFxPort m_source; TDoubleParamP m_gamma; + TDoubleParamP m_gammaAdjust; // Gamma offset from the current color space + // gamma (Version 3) TBoolParamP m_auto_gain; TDoubleParamP m_gain_adjust; TDoubleParamP m_gain; @@ -55,6 +57,9 @@ public: bool canHandle(const TRenderSettings &info, double frame) override; void getParamUIs(TParamUIConcept *&concepts, int &length) override; void onObsoleteParamLoaded(const std::string ¶mName) override; + void onFxVersionSet() override; + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override; }; #endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_bokeh_advancedfx.cpp b/toonz/sources/stdfx/iwa_bokeh_advancedfx.cpp index f3c4b39..ae0ce58 100644 --- a/toonz/sources/stdfx/iwa_bokeh_advancedfx.cpp +++ b/toonz/sources/stdfx/iwa_bokeh_advancedfx.cpp @@ -134,18 +134,23 @@ Iwa_BokehAdvancedFx::Iwa_BokehAdvancedFx() bindParam(this, "on_focus_distance", m_onFocusDistance, false); bindParam(this, "bokeh_amount", m_bokehAmount, false); bindParam(this, "masterHardness", m_hardness, false); + bindParam(this, "masterGamma", m_gamma, false); + bindParam(this, "masterGammaAdjust", m_gammaAdjust, false); bindParam(this, "hardnessPerSource", m_hardnessPerSource, false); + bindParam(this, "linearizeMode", m_linearizeMode, false); // Bind layer parameters for (int layer = 0; layer < LAYER_NUM; layer++) { m_layerParams[layer].m_distance = TDoubleParamP(0.5); m_layerParams[layer].m_bokehAdjustment = TDoubleParamP(1); - m_layerParams[layer].m_hardness = TDoubleParamP(0.3); - m_layerParams[layer].m_depth_ref = TIntParamP(0); - m_layerParams[layer].m_depthRange = TDoubleParamP(1.0); - m_layerParams[layer].m_fillGap = TBoolParamP(true); - m_layerParams[layer].m_doMedian = TBoolParamP(false); + m_layerParams[layer].m_hardness = TDoubleParamP(0.3); + m_layerParams[layer].m_gamma = TDoubleParamP(2.2); + m_layerParams[layer].m_gammaAdjust = TDoubleParamP(0.); + m_layerParams[layer].m_depth_ref = TIntParamP(0); + m_layerParams[layer].m_depthRange = TDoubleParamP(1.0); + m_layerParams[layer].m_fillGap = TBoolParamP(true); + m_layerParams[layer].m_doMedian = TBoolParamP(false); std::string str = QString("Source%1").arg(layer + 1).toStdString(); addInputPort(str, m_layerParams[layer].m_source); @@ -154,6 +159,10 @@ Iwa_BokehAdvancedFx::Iwa_BokehAdvancedFx() bindParam(this, QString("bokeh_adjustment%1").arg(layer + 1).toStdString(), m_layerParams[layer].m_bokehAdjustment); + bindParam(this, QString("gamma%1").arg(layer + 1).toStdString(), + m_layerParams[layer].m_gamma); + bindParam(this, QString("gammaAdjust%1").arg(layer + 1).toStdString(), + m_layerParams[layer].m_gammaAdjust); bindParam(this, QString("hardness%1").arg(layer + 1).toStdString(), m_layerParams[layer].m_hardness); bindParam(this, QString("depth_ref%1").arg(layer + 1).toStdString(), @@ -169,10 +178,48 @@ Iwa_BokehAdvancedFx::Iwa_BokehAdvancedFx() m_layerParams[layer].m_bokehAdjustment->setValueRange(0.0, 2.0); m_layerParams[layer].m_hardness->setValueRange(0.05, 3.0); + m_layerParams[layer].m_gamma->setValueRange(1.0, 10.0); + m_layerParams[layer].m_gammaAdjust->setValueRange(-5., 5.); m_layerParams[layer].m_depthRange->setValueRange(0.0, 1.0); } addInputPort("Depth1", new TRasterFxPort, 0); + + enableComputeInFloat(true); + + // Version 1: Exposure is computed by using Hardness + // E = std::pow(10.0, (value - 0.5) / hardness) + // Version 2: Exposure can also be computed by using Gamma, for easier + // combination with the linear color space + // E = std::pow(value, gamma) + // this must be called after binding the parameters (see onFxVersionSet()) + // Version 3: Gamma is computed by rs.m_colorSpaceGamma + gammaAdjust + setFxVersion(3); +} + +//-------------------------------------------- + +void Iwa_BokehAdvancedFx::onFxVersionSet() { + bool useGamma = getFxVersion() == 2; + if (getFxVersion() == 1) { + m_linearizeMode->setValue(Hardness); + setFxVersion(3); + } else if (getFxVersion() == 2) { + if (m_linearizeMode->getValue() == Hardness) { + useGamma = false; + setFxVersion(3); + } + } + getParams()->getParamVar("masterGamma")->setIsHidden(!useGamma); + getParams()->getParamVar("masterGammaAdjust")->setIsHidden(useGamma); + for (int layer = 0; layer < LAYER_NUM; layer++) { + getParams() + ->getParamVar(QString("gamma%1").arg(layer + 1).toStdString()) + ->setIsHidden(!useGamma); + getParams() + ->getParamVar(QString("gammaAdjust%1").arg(layer + 1).toStdString()) + ->setIsHidden(useGamma); + } } //-------------------------------------------- @@ -238,12 +285,11 @@ void Iwa_BokehAdvancedFx::doCompute(TTile& tile, double frame, // compute the input tiles QMap sourceTiles; - TRenderSettings infoOnInput(settings); - infoOnInput.m_bpp = 64; for (auto index : sourceIndices) { TTile* layerTile = new TTile(); m_layerParams[index].m_source->allocateAndCompute( - *layerTile, _rectOut.getP00(), dimOut, 0, frame, infoOnInput); + *layerTile, _rectOut.getP00(), dimOut, tile.getRaster(), frame, + settings); sourceTiles[index] = layerTile; } @@ -276,6 +322,7 @@ void Iwa_BokehAdvancedFx::doCompute(TTile& tile, double frame, unsigned char* ctrl_mem = (unsigned char*)ctrlRas->getRawData(); TRaster32P ras32 = (TRaster32P)tmpTile.getRaster(); TRaster64P ras64 = (TRaster64P)tmpTile.getRaster(); + TRasterFP rasF = (TRasterFP)tmpTile.getRaster(); lock.lockForRead(); if (ras32) BokehUtils::setDepthRaster(ras32, ctrl_mem, @@ -283,6 +330,9 @@ void Iwa_BokehAdvancedFx::doCompute(TTile& tile, double frame, else if (ras64) BokehUtils::setDepthRaster(ras64, ctrl_mem, dimOut); + else if (rasF) + BokehUtils::setDepthRaster(rasF, ctrl_mem, + dimOut); lock.unlock(); ctrl_rasters.push_back(ctrlRas); ctrls[portIndex] = ctrl_mem; @@ -290,7 +340,17 @@ void Iwa_BokehAdvancedFx::doCompute(TTile& tile, double frame, } } - double masterHardness = m_hardness->getValue(frame); + double masterGamma; + if (m_linearizeMode->getValue() == Hardness) + masterGamma = m_hardness->getValue(frame); + else { // Gamma + if (getFxVersion() == 2) + masterGamma = m_gamma->getValue(frame); + else + masterGamma = std::max( + 1., settings.m_colorSpaceGamma + m_gammaAdjust->getValue(frame)); + if (tile.getRaster()->isLinear()) masterGamma /= settings.m_colorSpaceGamma; + } QList layerValues; for (auto index : sourceIndices) { @@ -298,10 +358,24 @@ void Iwa_BokehAdvancedFx::doCompute(TTile& tile, double frame, layerValue.sourceTile = sourceTiles[index]; layerValue.premultiply = false; // assuming input images are always premultiplied - layerValue.layerHardness = - (m_hardnessPerSource->getValue()) - ? m_layerParams[index].m_hardness->getValue(frame) - : masterHardness; + + if (m_hardnessPerSource->getValue()) { + if (m_linearizeMode->getValue() == Hardness) + layerValue.layerGamma = + m_layerParams[index].m_hardness->getValue(frame); + else { // Gamma + if (getFxVersion() == 2) + layerValue.layerGamma = m_layerParams[index].m_gamma->getValue(frame); + else + layerValue.layerGamma = std::max( + 1., settings.m_colorSpaceGamma + + m_layerParams[index].m_gammaAdjust->getValue(frame)); + if (tile.getRaster()->isLinear()) + layerValue.layerGamma /= settings.m_colorSpaceGamma; + } + } else + layerValue.layerGamma = masterGamma; + layerValue.depth_ref = m_layerParams[index].m_depth_ref->getValue(); layerValue.irisSize = irisSizes.value(index); layerValue.distance = m_layerParams[index].m_distance->getValue(frame); diff --git a/toonz/sources/stdfx/iwa_bokeh_advancedfx.h b/toonz/sources/stdfx/iwa_bokeh_advancedfx.h index fa29d7d..3cb44e7 100644 --- a/toonz/sources/stdfx/iwa_bokeh_advancedfx.h +++ b/toonz/sources/stdfx/iwa_bokeh_advancedfx.h @@ -13,8 +13,6 @@ #include "tfxparam.h" #include "traster.h" -#include "kiss_fft.h" -#include "tools/kiss_fftnd.h" #include "iwa_bokeh_util.h" #include @@ -37,10 +35,14 @@ protected: TDoubleParamP m_distance; // The layer distance from the camera (0-1) TDoubleParamP m_bokehAdjustment; // Factor for adjusting distance (= focal // distance - layer distance) (0-2.0) - TDoubleParamP m_hardness; // film gamma for each layer - TIntParamP m_depth_ref; // port index of depth reference image - TDoubleParamP m_depthRange; // distance range varies depends on the - // brightness of the reference image (0-1) + TDoubleParamP m_hardness; // film gamma for each layer (Version1) + TDoubleParamP m_gamma; // film gamma for each layer (Version2) + TDoubleParamP m_gammaAdjust; // Gamma offset from the current color space + // gamma (Version 3) + + TIntParamP m_depth_ref; // port index of depth reference image + TDoubleParamP m_depthRange; // distance range varies depends on the + // brightness of the reference image (0-1) TBoolParamP m_fillGap; TBoolParamP m_doMedian; }; @@ -67,6 +69,7 @@ public: const TFxPortDG* dynamicPortGroup(int g) const override { return (g == 0) ? &m_control : 0; } + void onFxVersionSet() final override; }; #endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_bokeh_util.cpp b/toonz/sources/stdfx/iwa_bokeh_util.cpp index f055b8c..5b6d4a5 100644 --- a/toonz/sources/stdfx/iwa_bokeh_util.cpp +++ b/toonz/sources/stdfx/iwa_bokeh_util.cpp @@ -2,6 +2,8 @@ #include "iwa_bokeh_util.h" #include "trop.h" +#include "tparamcontainer.h" + #include #include @@ -62,21 +64,19 @@ void releaseAllRastersAndPlans(QList& rasterList, BokehUtils::MyThread::MyThread(Channel channel, TRasterP layerTileRas, double4* result, double* alpha_bokeh, kiss_fft_cpx* kissfft_comp_iris, - double layerHardness, double masterHardness, - bool doLightenComp) + double layerGamma, double masterGamma) : m_channel(channel) , m_layerTileRas(layerTileRas) , m_result(result) , m_alpha_bokeh(alpha_bokeh) , m_kissfft_comp_iris(kissfft_comp_iris) - , m_layerHardness(layerHardness) - , m_masterHardness(masterHardness) + , m_layerGamma(layerGamma) + , m_masterGamma(masterGamma) , m_finished(false) , m_kissfft_comp_in(0) , m_kissfft_comp_out(0) - , m_isTerminated(false) - , m_doLightenComp(doLightenComp) { - if (m_masterHardness == 0.0) m_masterHardness = m_layerHardness; + , m_isTerminated(false) { + if (m_masterGamma == 0.0) m_masterGamma = m_layerGamma; } bool BokehUtils::MyThread::init() { @@ -168,8 +168,7 @@ void BokehUtils::MyThread::setLayerRaster(const RASTER srcRas, : (double)pix->b; // multiply the exposure by alpha channel value dstMem[j * dim.lx + i].r = - valueToExposure(val / (double)PIXEL::maxChannelValue, - m_layerHardness) * + m_conv->valueToExposure(val / (double)PIXEL::maxChannelValue) * ((double)pix->m / (double)PIXEL::maxChannelValue); } } @@ -193,6 +192,7 @@ void BokehUtils::MyThread::run() { TRaster32P ras32 = (TRaster32P)m_layerTileRas; TRaster64P ras64 = (TRaster64P)m_layerTileRas; + TRasterFP rasF = (TRasterFP)m_layerTileRas; // Prepare data for FFT. // Convert the RGB values to the exposure, then multiply it by the alpha // channel value @@ -202,6 +202,8 @@ void BokehUtils::MyThread::run() { setLayerRaster(ras32, m_kissfft_comp_in, dim); else if (ras64) setLayerRaster(ras64, m_kissfft_comp_in, dim); + else if (rasF) + setLayerRaster(rasF, m_kissfft_comp_in, dim); else { lock.unlock(); return; @@ -221,7 +223,7 @@ void BokehUtils::MyThread::run() { // Filtering. Multiply by the iris FFT data { for (int i = 0; i < lx * ly; i++) { - float re, im; + kiss_fft_scalar re, im; re = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].r - m_kissfft_comp_out[i].i * m_kissfft_comp_iris[i].i; im = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].i + @@ -251,6 +253,7 @@ void BokehUtils::MyThread::run() { double* alp_p = m_alpha_bokeh; double4* res_p = m_result; + for (int i = 0; i < dim.lx * dim.ly; i++, alp_p++, res_p++) { if ((*alp_p) < 0.00001) continue; @@ -259,10 +262,16 @@ void BokehUtils::MyThread::run() { (double)(dim.lx * dim.ly); // convert to layer hardness - if (m_masterHardness != m_layerHardness) - exposure = - std::pow(exposure / (*alp_p), m_layerHardness / m_masterHardness) * - (*alp_p); + if (m_masterGamma != m_layerGamma) { + if (isGammaBased()) + exposure = + std::pow(exposure / (*alp_p), m_masterGamma / m_layerGamma) * + (*alp_p); + else // hardness based + exposure = + std::pow(exposure / (*alp_p), m_layerGamma / m_masterGamma) * + (*alp_p); + } double* res = (m_channel == Red) ? (&((*res_p).x)) : (m_channel == Green) ? (&((*res_p).y)) @@ -343,7 +352,7 @@ void BokehUtils::BokehRefThread::run() { // multiply filter for (int i = 0; i < size; i++) { - float re, im; + kiss_fft_scalar re, im; re = m_fftcpx_channel[i].r * m_fftcpx_iris[i].r - m_fftcpx_channel[i].i * m_fftcpx_iris[i].i; im = m_fftcpx_channel[i].r * m_fftcpx_iris[i].i + @@ -415,6 +424,8 @@ template void BokehUtils::setSourceRaster( const TRaster32P srcRas, double4* dstMem, TDimensionI dim); template void BokehUtils::setSourceRaster( const TRaster64P srcRas, double4* dstMem, TDimensionI dim); +template void BokehUtils::setSourceRaster( + const TRasterFP srcRas, double4* dstMem, TDimensionI dim); template void BokehUtils::setSourceRaster(const RASTER srcRas, double4* dstMem, @@ -439,6 +450,8 @@ template void BokehUtils::setDepthRaster( const TRaster32P srcRas, unsigned char* dstMem, TDimensionI dim); template void BokehUtils::setDepthRaster( const TRaster64P srcRas, unsigned char* dstMem, TDimensionI dim); +template void BokehUtils::setDepthRaster( + const TRasterFP srcRas, unsigned char* dstMem, TDimensionI dim); template void BokehUtils::setDepthRaster(const RASTER srcRas, unsigned char* dstMem, @@ -451,6 +464,9 @@ void BokehUtils::setDepthRaster(const RASTER srcRas, unsigned char* dstMem, double val = ((double)pix->r * 0.3 + (double)pix->g * 0.59 + (double)pix->b * 0.11) / (double)PIXEL::maxChannelValue; + // clamp + val = std::min(1., std::max(0., val)); + // convert to unsigned char (*depth_p) = (unsigned char)(val * (double)UCHAR_MAX + 0.5); } @@ -641,7 +657,7 @@ void BokehUtils::defineSegemntDepth( // convert source image value rgb -> exposure //-------------------------------------------- void BokehUtils::convertRGBToExposure(const double4* source_buff, int size, - double filmGamma) { + const ExposureConverter& conv) { double4* source_p = (double4*)source_buff; for (int i = 0; i < size; i++, source_p++) { // continue if alpha channel is 0 @@ -653,9 +669,9 @@ void BokehUtils::convertRGBToExposure(const double4* source_buff, int size, } // RGB value -> exposure - (*source_p).x = valueToExposure((*source_p).x, filmGamma); - (*source_p).y = valueToExposure((*source_p).y, filmGamma); - (*source_p).z = valueToExposure((*source_p).z, filmGamma); + (*source_p).x = conv.valueToExposure((*source_p).x); + (*source_p).y = conv.valueToExposure((*source_p).y); + (*source_p).z = conv.valueToExposure((*source_p).z); // multiply with alpha channel (*source_p).x *= (*source_p).w; @@ -668,12 +684,12 @@ void BokehUtils::convertRGBToExposure(const double4* source_buff, int size, // convert result image value exposure -> rgb //-------------------------------------------- void BokehUtils::convertExposureToRGB(const double4* result_buff, int size, - double filmGamma) { + const ExposureConverter& conv) { double4* res_p = (double4*)result_buff; for (int i = 0; i < size; i++, res_p++) { - (*res_p).x = clamp01(exposureToValue((*res_p).x, filmGamma)); - (*res_p).y = clamp01(exposureToValue((*res_p).y, filmGamma)); - (*res_p).z = clamp01(exposureToValue((*res_p).z, filmGamma)); + (*res_p).x = conv.exposureToValue((*res_p).x); + (*res_p).y = conv.exposureToValue((*res_p).y); + (*res_p).z = conv.exposureToValue((*res_p).z); } } @@ -1040,7 +1056,7 @@ void BokehUtils::multiplyFilter(kiss_fft_cpx* fftcpx_channel, // dst kiss_fft_cpx* fftcpx_iris, // filter int size) { for (int i = 0; i < size; i++) { - float re, im; + kiss_fft_scalar re, im; re = fftcpx_channel[i].r * fftcpx_iris[i].r - fftcpx_channel[i].i * fftcpx_iris[i].i; im = fftcpx_channel[i].r * fftcpx_iris[i].i + @@ -1127,7 +1143,8 @@ void BokehUtils::interpolateExposureAndConvertToRGB( //-------------------------------------------- //"Over" composite the layer to the output exposure. void BokehUtils::compositLayerAsIs(TTile& layerTile, double4* result, - TDimensionI& dimOut, double filmGamma) { + TDimensionI& dimOut, + const ExposureConverter& conv) { double4* layer_buff; TRasterGR8P layer_buff_ras(dimOut.lx * sizeof(double4), dimOut.ly); layer_buff_ras->lock(); @@ -1135,6 +1152,7 @@ void BokehUtils::compositLayerAsIs(TTile& layerTile, double4* result, TRaster32P ras32 = (TRaster32P)layerTile.getRaster(); TRaster64P ras64 = (TRaster64P)layerTile.getRaster(); + TRasterFP rasF = (TRasterFP)layerTile.getRaster(); lock.lockForRead(); if (ras32) BokehUtils::setSourceRaster(ras32, layer_buff, @@ -1142,6 +1160,8 @@ void BokehUtils::compositLayerAsIs(TTile& layerTile, double4* result, else if (ras64) BokehUtils::setSourceRaster(ras64, layer_buff, dimOut); + else if (rasF) + BokehUtils::setSourceRaster(rasF, layer_buff, dimOut); lock.unlock(); double4* lay_p = layer_buff; @@ -1151,20 +1171,20 @@ void BokehUtils::compositLayerAsIs(TTile& layerTile, double4* result, continue; else if ((*lay_p).w < 1.0) { // composite exposure - (*res_p).x = valueToExposure((*lay_p).x, filmGamma) * (*lay_p).w + + (*res_p).x = conv.valueToExposure((*lay_p).x) * (*lay_p).w + (*res_p).x * (1.0 - (*lay_p).w); - (*res_p).y = valueToExposure((*lay_p).y, filmGamma) * (*lay_p).w + + (*res_p).y = conv.valueToExposure((*lay_p).y) * (*lay_p).w + (*res_p).y * (1.0 - (*lay_p).w); - (*res_p).z = valueToExposure((*lay_p).z, filmGamma) * (*lay_p).w + + (*res_p).z = conv.valueToExposure((*lay_p).z) * (*lay_p).w + (*res_p).z * (1.0 - (*lay_p).w); // over composite alpha (*res_p).w = (*lay_p).w + ((*res_p).w * (1.0 - (*lay_p).w)); (*res_p).w = clamp01((*res_p).w); } else // replace by upper layer { - (*res_p).x = valueToExposure((*lay_p).x, filmGamma); - (*res_p).y = valueToExposure((*lay_p).y, filmGamma); - (*res_p).z = valueToExposure((*lay_p).z, filmGamma); + (*res_p).x = conv.valueToExposure((*lay_p).x); + (*res_p).y = conv.valueToExposure((*lay_p).y); + (*res_p).z = conv.valueToExposure((*lay_p).z); (*res_p).w = 1.0; } } @@ -1200,6 +1220,7 @@ void BokehUtils::calcAlfaChannelBokeh(kiss_fft_cpx* kissfft_comp_iris, TRaster32P ras32 = (TRaster32P)layerTile.getRaster(); TRaster64P ras64 = (TRaster64P)layerTile.getRaster(); + TRasterFP rasF = (TRasterFP)layerTile.getRaster(); if (ras32) { for (int j = 0; j < ly; j++) { TPixel32* pix = ras32->pixels(j); @@ -1216,6 +1237,14 @@ void BokehUtils::calcAlfaChannelBokeh(kiss_fft_cpx* kissfft_comp_iris, pix++; } } + } else if (rasF) { + for (int j = 0; j < ly; j++) { + TPixelF* pix = rasF->pixels(j); + for (int i = 0; i < lx; i++) { + kissfft_comp_in[j * lx + i].r = (double)pix->m; + pix++; + } + } } else return; @@ -1227,7 +1256,7 @@ void BokehUtils::calcAlfaChannelBokeh(kiss_fft_cpx* kissfft_comp_iris, // Filtering. Multiply by the iris FFT data for (int i = 0; i < lx * ly; i++) { - float re, im; + kiss_fft_scalar re, im; re = kissfft_comp_out[i].r * kissfft_comp_iris[i].r - kissfft_comp_out[i].i * kissfft_comp_iris[i].i; im = kissfft_comp_out[i].r * kissfft_comp_iris[i].i + @@ -1305,6 +1334,32 @@ void BokehUtils::setOutputRaster(double4* src, const RASTER dstRas, } } +template <> +void BokehUtils::setOutputRaster(double4* src, + const TRasterFP dstRas, + TDimensionI& dim, + int2 margin) { + double4* src_p = src + (margin.y * dim.lx); + + for (int j = 0; j < dstRas->getLy(); j++) { + TPixelF* outPix = dstRas->pixels(j); + src_p += margin.x; + for (int i = 0; i < dstRas->getLx(); i++, outPix++, src_p++) { + outPix->r = (typename TPixelF::Channel)( + (std::isfinite((*src_p).x) && (*src_p).x > 0.) ? (*src_p).x : 0.); + outPix->g = (typename TPixelF::Channel)( + (std::isfinite((*src_p).y) && (*src_p).y > 0.) ? (*src_p).y : 0.); + outPix->b = (typename TPixelF::Channel)( + (std::isfinite((*src_p).z) && (*src_p).z > 0.) ? (*src_p).z : 0.); + + outPix->m = + (typename TPixelF::Channel)(((*src_p).w > 1.) ? 1. : (*src_p).w); + assert(outPix->m >= 0.0); + } + src_p += margin.x; + } +} + //----------------------------------------------------- // Get the pixel size of bokehAmount ( referenced ino_blur.cpp ) double BokehUtils::getBokehPixelAmount(const double bokehAmount, @@ -1324,7 +1379,12 @@ double BokehUtils::getBokehPixelAmount(const double bokehAmount, //----------------------------------------------------- Iwa_BokehCommonFx::Iwa_BokehCommonFx() - : m_onFocusDistance(0.5), m_bokehAmount(30.0), m_hardness(0.3) { + : m_onFocusDistance(0.5) + , m_bokehAmount(30.0) + , m_hardness(0.3) + , m_gamma(2.2) + , m_gammaAdjust(0.) + , m_linearizeMode(new TIntEnumParam(Gamma, "Gamma")) { addInputPort("Iris", m_iris); // Set the ranges of common parameters @@ -1332,6 +1392,9 @@ Iwa_BokehCommonFx::Iwa_BokehCommonFx() m_bokehAmount->setValueRange(0.0, 300.0); m_bokehAmount->setMeasureName("fxLength"); m_hardness->setValueRange(0.05, 3.0); + m_gamma->setValueRange(1.0, 10.0); + m_gammaAdjust->setValueRange(-5., 5.); + m_linearizeMode->addItem(Hardness, "Hardness"); } //-------------------------------------------- @@ -1373,7 +1436,13 @@ void Iwa_BokehCommonFx::doFx(TTile& tile, double frame, // initialize std::fill_n(result, dimOut.lx * dimOut.ly, zero); - double masterHardness = m_hardness->getValue(frame); + double masterGamma; + if (m_linearizeMode->getValue() == Hardness) + masterGamma = m_hardness->getValue(frame); + else { // gamma + masterGamma = m_gamma->getValue(frame); + if (tile.getRaster()->isLinear()) masterGamma /= settings.m_colorSpaceGamma; + } // cancel check if (settings.m_isCanceled && *settings.m_isCanceled) { @@ -1400,15 +1469,15 @@ void Iwa_BokehCommonFx::doFx(TTile& tile, double frame, if (!layer.premultiply) TRop::depremultiply(layerTile->getRaster()); doBokehRef(result, frame, settings, bokehPixelAmount, margin, dimOut, - irisBBox, irisTile, kissfft_comp_iris, layer, - ctrls[ctrlIndex]); + irisBBox, irisTile, kissfft_comp_iris, layer, ctrls[ctrlIndex], + tile.getRaster()->isLinear()); continue; } //------------------- - double layerHardness = layer.layerHardness; + double layerGamma = layer.layerGamma; // The iris size of the current layer double irisSize = layer.irisSize; @@ -1416,7 +1485,16 @@ void Iwa_BokehCommonFx::doFx(TTile& tile, double frame, if (-1.0 <= irisSize && 1.0 >= irisSize) { TTile* layerTile = layer.sourceTile; if (!layer.premultiply) TRop::depremultiply(layerTile->getRaster()); - BokehUtils::compositLayerAsIs(*layerTile, result, dimOut, masterHardness); + + if (m_linearizeMode->getValue() == Hardness) + BokehUtils::compositLayerAsIs( + *layerTile, result, dimOut, + HardnessBasedConverter(masterGamma, settings.m_colorSpaceGamma, + layerTile->getRaster()->isLinear())); + else + BokehUtils::compositLayerAsIs(*layerTile, result, dimOut, + GammaBasedConverter(masterGamma)); + // Continue to the next layer continue; } @@ -1480,15 +1558,26 @@ void Iwa_BokehCommonFx::doFx(TTile& tile, double frame, return; } - BokehUtils::MyThread threadR( - BokehUtils::MyThread::Red, layerTile->getRaster(), result, alpha_bokeh, - kissfft_comp_iris, layerHardness, masterHardness); - BokehUtils::MyThread threadG( - BokehUtils::MyThread::Green, layerTile->getRaster(), result, - alpha_bokeh, kissfft_comp_iris, layerHardness, masterHardness); - BokehUtils::MyThread threadB( - BokehUtils::MyThread::Blue, layerTile->getRaster(), result, alpha_bokeh, - kissfft_comp_iris, layerHardness, masterHardness); + BokehUtils::MyThread threadR(BokehUtils::MyThread::Red, + layerTile->getRaster(), result, alpha_bokeh, + kissfft_comp_iris, layerGamma, masterGamma); + BokehUtils::MyThread threadG(BokehUtils::MyThread::Green, + layerTile->getRaster(), result, alpha_bokeh, + kissfft_comp_iris, layerGamma, masterGamma); + BokehUtils::MyThread threadB(BokehUtils::MyThread::Blue, + layerTile->getRaster(), result, alpha_bokeh, + kissfft_comp_iris, layerGamma, masterGamma); + + std::shared_ptr conv; + if (m_linearizeMode->getValue() == Hardness) + conv.reset( + new HardnessBasedConverter(layerGamma, settings.m_colorSpaceGamma, + layerTile->getRaster()->isLinear())); + else + conv.reset(new GammaBasedConverter(layerGamma)); + threadR.setConverter(conv); + threadG.setConverter(conv); + threadB.setConverter(conv); // If you set this flag to true, the fx will be forced to compute in single // thread. @@ -1596,13 +1685,20 @@ void Iwa_BokehCommonFx::doFx(TTile& tile, double frame, } // convert result image value exposure -> rgb - BokehUtils::convertExposureToRGB(result, dimOut.lx * dimOut.ly, - masterHardness); + if (m_linearizeMode->getValue() == Hardness) + BokehUtils::convertExposureToRGB( + result, dimOut.lx * dimOut.ly, + HardnessBasedConverter(masterGamma, settings.m_colorSpaceGamma, + tile.getRaster()->isLinear())); + else + BokehUtils::convertExposureToRGB(result, dimOut.lx * dimOut.ly, + GammaBasedConverter(masterGamma)); // clear result raster tile.getRaster()->clear(); TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); int2 outMargin = {(dimOut.lx - tile.getRaster()->getSize().lx) / 2, (dimOut.ly - tile.getRaster()->getSize().ly) / 2}; @@ -1614,18 +1710,19 @@ void Iwa_BokehCommonFx::doFx(TTile& tile, double frame, else if (outRas64) BokehUtils::setOutputRaster(result, outRas64, dimOut, outMargin); + else if (outRasF) + BokehUtils::setOutputRaster(result, outRasF, dimOut, + outMargin); lock.unlock(); releaseAllRastersAndPlans(rasterList, planList); } -void Iwa_BokehCommonFx::doBokehRef(double4* result, double frame, - const TRenderSettings& settings, - double bokehPixelAmount, int margin, - TDimensionI& dimOut, TRectD& irisBBox, - TTile& irisTile, - kiss_fft_cpx* kissfft_comp_iris, - LayerValue layer, unsigned char* ctrl) { +void Iwa_BokehCommonFx::doBokehRef( + double4* result, double frame, const TRenderSettings& settings, + double bokehPixelAmount, int margin, TDimensionI& dimOut, TRectD& irisBBox, + TTile& irisTile, kiss_fft_cpx* kissfft_comp_iris, LayerValue layer, + unsigned char* ctrl, const bool isLinear) { QList rasterList; QList planList; // source image @@ -1634,6 +1731,7 @@ void Iwa_BokehCommonFx::doBokehRef(double4* result, double frame, TRaster32P ras32 = (TRaster32P)layer.sourceTile->getRaster(); TRaster64P ras64 = (TRaster64P)layer.sourceTile->getRaster(); + TRasterFP rasF = (TRasterFP)layer.sourceTile->getRaster(); lock.lockForRead(); if (ras32) BokehUtils::setSourceRaster(ras32, source_buff, @@ -1641,6 +1739,8 @@ void Iwa_BokehCommonFx::doBokehRef(double4* result, double frame, else if (ras64) BokehUtils::setSourceRaster(ras64, source_buff, dimOut); + else if (rasF) + BokehUtils::setSourceRaster(rasF, source_buff, dimOut); lock.unlock(); // create the index map, which indicates which layer each pixel belongs to @@ -1740,13 +1840,26 @@ void Iwa_BokehCommonFx::doBokehRef(double4* result, double frame, memset(result_main_buff, 0, sizeof(double4) * size); memset(result_sub_buff, 0, sizeof(double4) * size); - double masterHardness = (double)m_hardness->getValue(frame); - double layerHardness = layer.layerHardness; + double masterGamma; + if (m_linearizeMode->getValue() == Hardness) + masterGamma = m_hardness->getValue(frame); + else { // gamma + masterGamma = m_gamma->getValue(frame); + if (isLinear) masterGamma /= settings.m_colorSpaceGamma; + } + double layerGamma = layer.layerGamma; // convert source image value rgb -> exposure // note that premultiplied source image is already unpremultiplied before this // function - BokehUtils::convertRGBToExposure(source_buff, size, layerHardness); + if (m_linearizeMode->getValue() == Hardness) + BokehUtils::convertRGBToExposure( + source_buff, size, + HardnessBasedConverter(layerGamma, settings.m_colorSpaceGamma, + layer.sourceTile->getRaster()->isLinear())); + else + BokehUtils::convertRGBToExposure(source_buff, size, + GammaBasedConverter(layerGamma)); double focus = m_onFocusDistance->getValue(frame); double adjust = layer.bokehAdjustment; @@ -1894,13 +2007,16 @@ void Iwa_BokehCommonFx::doBokehRef(double4* result, double frame, return; } - BokehUtils::interpolateExposureAndConvertToRGB( - result_main_buff, // result1 - result_sub_buff, // result2 - mainSub_ratio_buff, // ratio - result, // dst - size, layerHardness / masterHardness); + double adjustFactor = (m_linearizeMode->getValue() == Hardness) + ? layerGamma / masterGamma + : masterGamma / layerGamma; + + BokehUtils::interpolateExposureAndConvertToRGB(result_main_buff, // result1 + result_sub_buff, // result2 + mainSub_ratio_buff, // ratio + result, // dst + size, adjustFactor); // release rasters and plans releaseAllRastersAndPlans(rasterList, planList); -} \ No newline at end of file +} diff --git a/toonz/sources/stdfx/iwa_bokeh_util.h b/toonz/sources/stdfx/iwa_bokeh_util.h index c354ae6..5bb593c 100644 --- a/toonz/sources/stdfx/iwa_bokeh_util.h +++ b/toonz/sources/stdfx/iwa_bokeh_util.h @@ -26,6 +26,56 @@ struct int2 { int x, y; }; +class ExposureConverter { +protected: + double m_gamma; // used as hardness in HardnessBasedConverter + +public: + ExposureConverter(double gamma) : m_gamma(gamma) {} + virtual double valueToExposure(double value) const = 0; + virtual double exposureToValue(double exposure) const = 0; + virtual bool isGammaBased() { return true; } +}; + +class HardnessBasedConverter : public ExposureConverter { + bool m_fromLinear; + double m_colorSpaceGamma; + +public: + HardnessBasedConverter(double gamma, double colorSpaceGamma, + bool fromLinear = false) + : ExposureConverter(gamma) + , m_fromLinear(fromLinear) + , m_colorSpaceGamma(colorSpaceGamma) {} + double valueToExposure(double value) const final override { + // conversion is assumed to be applied to non-linear value + if (m_fromLinear && value > 0.) + value = std::pow(value, 1. / m_colorSpaceGamma); + double logVal = (value - 0.5) / m_gamma; + return std::pow(10.0, logVal); + } + double exposureToValue(double exposure) const final override { + double ret = std::log10(exposure) * m_gamma + 0.5; + if (m_fromLinear && ret > 0.) ret = std::pow(ret, m_colorSpaceGamma); + return ret; + } + bool isGammaBased() final override { return false; } +}; + +class GammaBasedConverter : public ExposureConverter { +public: + GammaBasedConverter(double gamma) : ExposureConverter(gamma) {} + double valueToExposure(double value) const final override { + if (value < 0. || m_gamma == 1.) return value; + return std::pow(value, m_gamma); + } + double exposureToValue(double exposure) const final override { + assert(m_gamma > 0.); + if (exposure < 0. || m_gamma == 1.) return exposure; + return std::pow(exposure, 1. / m_gamma); + } +}; + namespace BokehUtils { //------------------------------------ @@ -45,8 +95,8 @@ private: kiss_fft_cpx* m_kissfft_comp_iris; - double m_layerHardness; - double m_masterHardness; + double m_layerGamma; + double m_masterGamma; TRasterGR8P m_kissfft_comp_in_ras, m_kissfft_comp_out_ras; kiss_fft_cpx *m_kissfft_comp_in, *m_kissfft_comp_out; @@ -54,14 +104,12 @@ private: bool m_isTerminated; - // not used for now - bool m_doLightenComp; + std::shared_ptr m_conv; public: MyThread(Channel channel, TRasterP layerTileRas, double4* result, double* alpha_bokeh, kiss_fft_cpx* kissfft_comp_iris, - double layerHardness, double masterHardness = 0.0, - bool doLightenComp = false); // not used for now + double layerGamma, double masterGamma = 0.0); // Convert the pixels from RGB values to exposures and multiply it by alpha // channel value. @@ -74,12 +122,15 @@ public: bool isFinished() { return m_finished; } - //�������m�� + // �������m�� bool init(); void terminateThread() { m_isTerminated = true; } bool checkTerminationAndCleanupThread(); + + void setConverter(std::shared_ptr conv) { m_conv = conv; } + bool isGammaBased() { return m_conv->isGammaBased(); } }; //------------------------------------ @@ -135,11 +186,11 @@ void defineSegemntDepth( // convert source image value rgb -> exposure void convertRGBToExposure(const double4* source_buff, int size, - double filmGamma); + const ExposureConverter& conv); // convert result image value exposure -> rgb void convertExposureToRGB(const double4* result_buff, int size, - double filmGamma); + const ExposureConverter& conv); // obtain iris size from the depth value double calcIrisSize(const double depth, const double bokehPixelAmount, @@ -195,7 +246,7 @@ void interpolateExposureAndConvertToRGB( //"Over" composite the layer to the output exposure. void compositLayerAsIs(TTile& layerTile, double4* result, TDimensionI& dimOut, - double filmGamma); + const ExposureConverter& conv); // Do FFT the alpha channel. // Forward FFT -> Multiply by the iris data -> Backward FFT @@ -214,13 +265,20 @@ double getBokehPixelAmount(const double bokehAmount, const TAffine affine); //----------------------------------------------------- class Iwa_BokehCommonFx : public TStandardRasterFx { +public: + enum LinearizeMode { Gamma, Hardness }; + protected: TRasterFxPort m_iris; TDoubleParamP m_onFocusDistance; // Focus Distance (0-1) TDoubleParamP m_bokehAmount; // The maximum bokeh size. The size of bokeh at // the layer separated by 1.0 from the focal // position - TDoubleParamP m_hardness; // Film gamma + TDoubleParamP m_hardness; // Film gamma (Version 1) + TDoubleParamP m_gamma; // Film gamma (Version 2) + TDoubleParamP m_gammaAdjust; // Gamma offset from the current color space + // gamma (Version 3) + TIntEnumParamP m_linearizeMode; struct LayerValue { TTile* sourceTile; @@ -228,7 +286,8 @@ protected: // this parameter is now always false (assuming input images are always // premultiplied). the value is left to keep backward compatibility bool premultiply; - double layerHardness; + double layerGamma; // hardness based in fx version 1, gamma based in fx + // version int depth_ref; double irisSize; @@ -250,7 +309,7 @@ protected: const TRenderSettings& settings, double bokehPixelAmount, int margin, TDimensionI& dimOut, TRectD& irisBBox, TTile& irisTile, kiss_fft_cpx* kissfft_comp_iris, - LayerValue layer, unsigned char* ctrl); + LayerValue layer, unsigned char* ctrl, const bool isLinear); public: Iwa_BokehCommonFx(); diff --git a/toonz/sources/stdfx/iwa_bokehfx.cpp b/toonz/sources/stdfx/iwa_bokehfx.cpp index 0a154e7..dda2eda 100644 --- a/toonz/sources/stdfx/iwa_bokehfx.cpp +++ b/toonz/sources/stdfx/iwa_bokehfx.cpp @@ -50,6 +50,9 @@ Iwa_BokehFx::Iwa_BokehFx() { bindParam(this, "on_focus_distance", m_onFocusDistance, false); bindParam(this, "bokeh_amount", m_bokehAmount, false); bindParam(this, "hardness", m_hardness, false); + bindParam(this, "gamma", m_gamma, false); + bindParam(this, "gammaAdjust", m_gammaAdjust, false); + bindParam(this, "linearizeMode", m_linearizeMode, false); // Bind the layer parameters for (int layer = 0; layer < LAYER_NUM; layer++) { @@ -70,8 +73,41 @@ Iwa_BokehFx::Iwa_BokehFx() { m_layerParams[layer].m_distance->setValueRange(0.0, 1.0); m_layerParams[layer].m_bokehAdjustment->setValueRange(0.0, 2.0); } + + enableComputeInFloat(true); + + // Version 1: Exposure is computed by using Hardness + // E = std::pow(10.0, (value - 0.5) / hardness) + // Version 2: Exposure can also be computed by using Gamma, for easier + // combination with the linear color space + // E = std::pow(value, gamma) + // this must be called after binding the parameters (see onFxVersionSet()) + // Version 3: Gamma is computed by rs.m_colorSpaceGamma + gammaAdjust + setFxVersion(3); } +//-------------------------------------------- + +void Iwa_BokehFx::onFxVersionSet() { + bool useGamma = getFxVersion() == 2; + if (getFxVersion() == 1) { + m_linearizeMode->setValue(Hardness); + setFxVersion(3); + } else if (getFxVersion() == 2) { + // Automatically update version + if (m_linearizeMode->getValue() == Hardness || + (m_gamma->getKeyframeCount() == 0 && + areAlmostEqual(m_gamma->getDefaultValue(), 2.2))) { + useGamma = false; + setFxVersion(3); + } + } + getParams()->getParamVar("gamma")->setIsHidden(!useGamma); + getParams()->getParamVar("gammaAdjust")->setIsHidden(useGamma); +} + +//-------------------------------------------- + void Iwa_BokehFx::doCompute(TTile& tile, double frame, const TRenderSettings& settings) { // If the iris is not connected, then do nothing @@ -136,12 +172,11 @@ void Iwa_BokehFx::doCompute(TTile& tile, double frame, //---------------------------- // Compute the input tiles first QMap sourceTiles; - TRenderSettings infoOnInput(settings); - infoOnInput.m_bpp = 64; for (auto index : sourceIndices) { TTile* layerTile = new TTile(); m_layerParams[index].m_source->allocateAndCompute( - *layerTile, _rectOut.getP00(), dimOut, 0, frame, infoOnInput); + *layerTile, _rectOut.getP00(), dimOut, tile.getRaster(), frame, + settings); sourceTiles[index] = layerTile; } @@ -156,19 +191,29 @@ void Iwa_BokehFx::doCompute(TTile& tile, double frame, static_cast(irisBBox.getLy() + 0.5)), tile.getRaster(), frame, settings); - double masterHardness = m_hardness->getValue(frame); + double masterGamma; + if (m_linearizeMode->getValue() == Hardness) + masterGamma = m_hardness->getValue(frame); + else { // Gamma + if (getFxVersion() == 2) + masterGamma = m_gamma->getValue(frame); + else + masterGamma = std::max( + 1., settings.m_colorSpaceGamma + m_gammaAdjust->getValue(frame)); + if (tile.getRaster()->isLinear()) masterGamma /= settings.m_colorSpaceGamma; + } QMap ctrls; QList layerValues; for (auto index : sourceIndices) { LayerValue layerValue; - layerValue.sourceTile = sourceTiles[index]; - layerValue.premultiply = m_layerParams[index].m_premultiply->getValue(); - layerValue.layerHardness = masterHardness; - layerValue.depth_ref = 0; - layerValue.irisSize = irisSizes.value(index); - layerValue.distance = m_layerParams[index].m_distance->getValue(frame); + layerValue.sourceTile = sourceTiles[index]; + layerValue.premultiply = m_layerParams[index].m_premultiply->getValue(); + layerValue.layerGamma = masterGamma; + layerValue.depth_ref = 0; + layerValue.irisSize = irisSizes.value(index); + layerValue.distance = m_layerParams[index].m_distance->getValue(frame); layerValue.bokehAdjustment = m_layerParams[index].m_bokehAdjustment->getValue(frame); layerValues.append(layerValue); diff --git a/toonz/sources/stdfx/iwa_bokehfx.h b/toonz/sources/stdfx/iwa_bokehfx.h index 8196eeb..e37aad2 100644 --- a/toonz/sources/stdfx/iwa_bokehfx.h +++ b/toonz/sources/stdfx/iwa_bokehfx.h @@ -20,7 +20,6 @@ distributed with a 3-clause BSD-style license. #include #include -#include "tools/kiss_fftnd.h" #include "iwa_bokeh_util.h" const int LAYER_NUM = 5; @@ -50,6 +49,8 @@ public: void doCompute(TTile &tile, double frame, const TRenderSettings &settings) override; + + void onFxVersionSet() final override; }; #endif diff --git a/toonz/sources/stdfx/iwa_bokehreffx.cpp b/toonz/sources/stdfx/iwa_bokehreffx.cpp index f45b9de..ee89632 100644 --- a/toonz/sources/stdfx/iwa_bokehreffx.cpp +++ b/toonz/sources/stdfx/iwa_bokehreffx.cpp @@ -82,11 +82,45 @@ Iwa_BokehRefFx::Iwa_BokehRefFx() bindParam(this, "on_focus_distance", m_onFocusDistance, false); bindParam(this, "bokeh_amount", m_bokehAmount, false); bindParam(this, "hardness", m_hardness, false); + bindParam(this, "gamma", m_gamma, false); + bindParam(this, "gammaAdjust", m_gammaAdjust, false); bindParam(this, "distance_precision", m_distancePrecision, false); bindParam(this, "fill_gap", m_fillGap, false); bindParam(this, "fill_gap_with_median_filter", m_doMedian, false); + bindParam(this, "linearizeMode", m_linearizeMode, false); m_distancePrecision->setValueRange(3, 128); + + enableComputeInFloat(true); + + // Version 1: Exposure is computed by using Hardness + // E = std::pow(10.0, (value - 0.5) / hardness) + // Version 2: Exposure can also be computed by using Gamma, for easier + // combination with the linear color space + // E = std::pow(value, gamma) + // this must be called after binding the parameters (see onFxVersionSet()) + // Version 3: Gamma is computed by rs.m_colorSpaceGamma + gammaAdjust + setFxVersion(3); +} + +//-------------------------------------------- + +void Iwa_BokehRefFx::onFxVersionSet() { + bool useGamma = getFxVersion() == 2; + if (getFxVersion() == 1) { + m_linearizeMode->setValue(Hardness); + setFxVersion(3); + } else if (getFxVersion() == 2) { + // Automatically update version + if (m_linearizeMode->getValue() == Hardness || + (m_gamma->getKeyframeCount() == 0 && + areAlmostEqual(m_gamma->getDefaultValue(), 2.2))) { + useGamma = false; + setFxVersion(3); + } + } + getParams()->getParamVar("gamma")->setIsHidden(!useGamma); + getParams()->getParamVar("gammaAdjust")->setIsHidden(useGamma); } //-------------------------------------------- @@ -148,13 +182,12 @@ void Iwa_BokehRefFx::doCompute(TTile& tile, double frame, // rasterList.append(allocateRasterAndLock(&source_buff, dimOut)); LayerValue layerValue; - TRenderSettings infoOnInput(settings); - infoOnInput.m_bpp = 64; + ; // source tile is used only in this focus. // normalized source image data is stored in source_buff. layerValue.sourceTile = new TTile(); m_source->allocateAndCompute(*layerValue.sourceTile, rectOut.getP00(), dimOut, - 0, frame, infoOnInput); + tile.getRaster(), frame, settings); // - - - iris image - - - // Get the original size of Iris image @@ -175,6 +208,18 @@ void Iwa_BokehRefFx::doCompute(TTile& tile, double frame, return; } + double masterGamma; + if (m_linearizeMode->getValue() == Hardness) + masterGamma = m_hardness->getValue(frame); + else { // Gamma + if (getFxVersion() == 2) + masterGamma = m_gamma->getValue(frame); + else + masterGamma = std::max( + 1., settings.m_colorSpaceGamma + m_gammaAdjust->getValue(frame)); + if (tile.getRaster()->isLinear()) masterGamma /= settings.m_colorSpaceGamma; + } + // compute the reference image std::vector ctrl_rasters; // to be stored in uchar QMap @@ -199,6 +244,7 @@ void Iwa_BokehRefFx::doCompute(TTile& tile, double frame, TRasterGR16P rasGR16 = (TRasterGR16P)depthTile.getRaster(); TRaster32P ras32 = (TRaster32P)depthTile.getRaster(); TRaster64P ras64 = (TRaster64P)depthTile.getRaster(); + TRasterFP rasF = (TRasterFP)depthTile.getRaster(); lock.lockForRead(); if (rasGR8) setDepthRasterGray(rasGR8, depth_buff, dimOut); @@ -210,12 +256,14 @@ void Iwa_BokehRefFx::doCompute(TTile& tile, double frame, else if (ras64) BokehUtils::setDepthRaster(ras64, depth_buff, dimOut); + else if (rasF) + BokehUtils::setDepthRaster(rasF, depth_buff, dimOut); lock.unlock(); } ctrls[1] = depth_buff; layerValue.premultiply = false; - layerValue.layerHardness = m_hardness->getValue(frame); + layerValue.layerGamma = masterGamma; layerValue.depth_ref = 1; layerValue.distance = 0.5; layerValue.bokehAdjustment = 1.0; diff --git a/toonz/sources/stdfx/iwa_bokehreffx.h b/toonz/sources/stdfx/iwa_bokehreffx.h index 653a225..604a896 100644 --- a/toonz/sources/stdfx/iwa_bokehreffx.h +++ b/toonz/sources/stdfx/iwa_bokehreffx.h @@ -20,7 +20,6 @@ distributed with a 3-clause BSD-style license. #include #include -#include "tools/kiss_fftnd.h" #include "iwa_bokeh_util.h" //------------------------------------ @@ -56,6 +55,8 @@ public: void doCompute(TTile& tile, double frame, const TRenderSettings& settings) override; + + void onFxVersionSet() final override; }; #endif diff --git a/toonz/sources/stdfx/iwa_corridorgradientfx.cpp b/toonz/sources/stdfx/iwa_corridorgradientfx.cpp index 96e826c..a113498 100644 --- a/toonz/sources/stdfx/iwa_corridorgradientfx.cpp +++ b/toonz/sources/stdfx/iwa_corridorgradientfx.cpp @@ -60,6 +60,8 @@ Iwa_CorridorGradientFx::Iwa_CorridorGradientFx() bindParam(this, "inner_color", m_innerColor); bindParam(this, "outer_color", m_outerColor); + + enableComputeInFloat(true); } //------------------------------------------------------------ @@ -295,7 +297,8 @@ void doCircleT(RASTER ras, TDimensionI dim, TPointD pos[2][4], void Iwa_CorridorGradientFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } @@ -320,6 +323,7 @@ void Iwa_CorridorGradientFx::doCompute(TTile &tile, double frame, tile.getRaster()->clear(); TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); if (m_shape->getValue() == 0) { // Quadrangle if (outRas32) doQuadrangleT( @@ -329,6 +333,10 @@ void Iwa_CorridorGradientFx::doCompute(TTile &tile, double frame, doQuadrangleT( outRas64, dimOut, pos, m_colors->getValue64(frame), (GradientCurveType)m_curveType->getValue()); + else if (outRasF) + doQuadrangleT( + outRasF, dimOut, pos, m_colors->getValueF(frame), + (GradientCurveType)m_curveType->getValue()); } else { // m_shape == 1 : Circle if (outRas32) doCircleT( @@ -338,6 +346,10 @@ void Iwa_CorridorGradientFx::doCompute(TTile &tile, double frame, doCircleT( outRas64, dimOut, pos, m_colors->getValue64(frame), (GradientCurveType)m_curveType->getValue()); + else if (outRasF) + doCircleT(outRasF, dimOut, pos, + m_colors->getValueF(frame), + (GradientCurveType)m_curveType->getValue()); } } diff --git a/toonz/sources/stdfx/iwa_directionalblurfx.cpp b/toonz/sources/stdfx/iwa_directionalblurfx.cpp index 206f4be..e02c1ff 100644 --- a/toonz/sources/stdfx/iwa_directionalblurfx.cpp +++ b/toonz/sources/stdfx/iwa_directionalblurfx.cpp @@ -24,6 +24,8 @@ void Iwa_DirectionalBlurFx::setReferenceRaster(const RASTER srcRas, (*dst_p) = ((float)pix->r * 0.3f + (float)pix->g * 0.59f + (float)pix->b * 0.11f) / (float)PIXEL::maxChannelValue; + // clamp + (*dst_p) = std::min(1.f, std::max(0.f, (*dst_p))); } } } @@ -81,6 +83,23 @@ void Iwa_DirectionalBlurFx::setOutputRaster(float4 *srcMem, const RASTER dstRas, } } +template <> +void Iwa_DirectionalBlurFx::setOutputRaster( + float4 *srcMem, const TRasterFP dstRas, TDimensionI dim, int2 margin) { + int out_j = 0; + for (int j = margin.y; j < dstRas->getLy() + margin.y; j++, out_j++) { + TPixelF *pix = dstRas->pixels(out_j); + float4 *chan_p = srcMem; + chan_p += j * dim.lx + margin.x; + for (int i = 0; i < dstRas->getLx(); i++, pix++, chan_p++) { + pix->r = (*chan_p).x; + pix->g = (*chan_p).y; + pix->b = (*chan_p).z; + pix->m = std::min((*chan_p).w, 1.f); + } + } +} + //------------------------------------ Iwa_DirectionalBlurFx::Iwa_DirectionalBlurFx() @@ -102,6 +121,8 @@ Iwa_DirectionalBlurFx::Iwa_DirectionalBlurFx() m_filterType->addItem(Gaussian, "Gaussian"); m_filterType->addItem(Flat, "Flat"); + + enableComputeInFloat(true); } //------------------------------------ @@ -174,12 +195,16 @@ void Iwa_DirectionalBlurFx::doCompute(TTile &tile, double frame, /*- 参照画像の輝度を0〜1に正規化してホストメモリに読み込む -*/ TRaster32P ras32 = (TRaster32P)reference_tile.getRaster(); TRaster64P ras64 = (TRaster64P)reference_tile.getRaster(); + TRasterFP rasF = (TRasterFP)reference_tile.getRaster(); if (ras32) setReferenceRaster(ras32, reference_host, enlargedDimIn); else if (ras64) setReferenceRaster(ras64, reference_host, enlargedDimIn); + else if (rasF) + setReferenceRaster(rasF, reference_host, + enlargedDimIn); } //------------------------------------------------------- @@ -221,10 +246,13 @@ void Iwa_DirectionalBlurFx::doCompute_CPU( /*- ソース画像を0〜1に正規化してホストメモリに読み込む -*/ TRaster32P ras32 = (TRaster32P)enlarge_tile.getRaster(); TRaster64P ras64 = (TRaster64P)enlarge_tile.getRaster(); + TRasterFP rasF = (TRasterFP)enlarge_tile.getRaster(); if (ras32) setSourceRaster(ras32, in, enlargedDimIn); else if (ras64) setSourceRaster(ras64, in, enlargedDimIn); + else if (rasF) + setSourceRaster(rasF, in, enlargedDimIn); /*- フィルタ作る -*/ makeDirectionalBlurFilter_CPU(filter, blur, bidirectional, marginLeft, @@ -282,7 +310,7 @@ void Iwa_DirectionalBlurFx::doCompute_CPU( if ((*filter_p) == 0.0f) continue; /*- サンプル座標 -*/ int2 samplePos = {tround((float)x - (float)filx * (*ref_p)), - tround((float)y - (float)fily * (*ref_p))}; + tround((float)y - (float)fily * (*ref_p))}; int sampleIndex = samplePos.y * enlargedDimIn.lx + samplePos.x; /*- サンプルピクセルが透明ならcontinue -*/ @@ -350,11 +378,14 @@ void Iwa_DirectionalBlurFx::doCompute_CPU( tile.getRaster()->clear(); TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); int2 margin = {marginRight, marginTop}; if (outRas32) setOutputRaster(out, outRas32, enlargedDimIn, margin); else if (outRas64) setOutputRaster(out, outRas64, enlargedDimIn, margin); + else if (outRasF) + setOutputRaster(out, outRasF, enlargedDimIn, margin); out_ras->unlock(); } @@ -488,8 +519,9 @@ void Iwa_DirectionalBlurFx::makeDirectionalBlurFilter_CPU( case Gaussian: { int index = (int)floor(offset * 100.0f); float ratio = offset * 100.0f - (float)index; - bokeAsiVal = - gaussian[index] * (1.0f - ratio) + gaussian[index + 1] * ratio; + bokeAsiVal = (ratio == 0.f) ? gaussian[index] + : gaussian[index] * (1.0f - ratio) + + gaussian[index + 1] * ratio; } break; case Flat: bokeAsiVal = 1.0f; diff --git a/toonz/sources/stdfx/iwa_flowblurfx.cpp b/toonz/sources/stdfx/iwa_flowblurfx.cpp index 1ab1d2c..d677403 100644 --- a/toonz/sources/stdfx/iwa_flowblurfx.cpp +++ b/toonz/sources/stdfx/iwa_flowblurfx.cpp @@ -49,11 +49,13 @@ inline double clamp01(double val) { // convert sRGB color space to power space template inline T to_linear_color_space(T nonlinear_color, T exposure, T gamma) { + if (nonlinear_color <= T(0)) return T(0); return std::pow(nonlinear_color, gamma) / exposure; } // convert power space to sRGB color space template inline T to_nonlinear_color_space(T linear_color, T exposure, T gamma) { + if (linear_color <= T(0)) return T(0); return std::pow(linear_color * exposure, T(1) / gamma); } diff --git a/toonz/sources/stdfx/iwa_fractalnoisefx.cpp b/toonz/sources/stdfx/iwa_fractalnoisefx.cpp index 5716690..c144b99 100644 --- a/toonz/sources/stdfx/iwa_fractalnoisefx.cpp +++ b/toonz/sources/stdfx/iwa_fractalnoisefx.cpp @@ -9,12 +9,14 @@ namespace { template inline T to_linear_color_space(T nonlinear_color, T exposure, T gamma) { // return -std::log(T(1) - std::pow(nonlinear_color, gamma)) / exposure; + if (nonlinear_color <= T(0)) return T(0); return std::pow(nonlinear_color, gamma) / exposure; } // convert power space to sRGB color space template inline T to_nonlinear_color_space(T linear_color, T exposure, T gamma) { // return std::pow(T(1) - std::exp(-exposure * linear_color), T(1) / gamma); + if (linear_color <= T(0)) return T(0); return std::pow(exposure * linear_color, T(1) / gamma); } @@ -134,6 +136,8 @@ Iwa_FractalNoiseFx::Iwa_FractalNoiseFx() bindParam(this, "zScale", m_zScale); bindParam(this, "alphaRendering", m_alphaRendering); + + enableComputeInFloat(true); } //------------------------------------------------------------------ @@ -457,10 +461,13 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, // convert to RGB channel values TRaster32P ras32 = (TRaster32P)tile.getRaster(); TRaster64P ras64 = (TRaster64P)tile.getRaster(); + TRasterFP rasF = (TRasterFP)tile.getRaster(); if (ras32) outputRaster(ras32, out_buf, param); else if (ras64) outputRaster(ras64, out_buf, param); + else if (rasF) + outputRaster(rasF, out_buf, param); out_buf_ras->unlock(); } @@ -516,11 +523,12 @@ void Iwa_FractalNoiseFx::outputRaster(const RASTER outRas, double *out_buf, const FNParam ¶m) { TDimension dim = outRas->getSize(); double *buf_p = out_buf; + bool doClamp = !(outRas->getPixelSize() == 16); for (int j = 0; j < dim.ly; j++) { PIXEL *pix = outRas->pixels(j); for (int i = 0; i < dim.lx; i++, pix++, buf_p++) { - double val = (param.invert) ? 1.0 - (*buf_p) : (*buf_p); - val = clamp(val, 0.0, 1.0); + double val = (param.invert) ? 1.0 - (*buf_p) : (*buf_p); + if (doClamp) val = clamp(val, 0.0, 1.0); typename PIXEL::Channel chan = static_cast( val * (double)PIXEL::maxChannelValue); pix->r = chan; diff --git a/toonz/sources/stdfx/iwa_glarefx.cpp b/toonz/sources/stdfx/iwa_glarefx.cpp index 6e558da..9a834a6 100644 --- a/toonz/sources/stdfx/iwa_glarefx.cpp +++ b/toonz/sources/stdfx/iwa_glarefx.cpp @@ -41,7 +41,8 @@ inline int getCoord(int i, int j, int lx, int ly) { //-------------------------------------------- Iwa_GlareFx::Iwa_GlareFx() - : m_renderMode(new TIntEnumParam(RendeMode_FilterPreview, "Filter Preview")) + : m_renderMode( + new TIntEnumParam(RenderMode_FilterPreview, "Filter Preview")) , m_irisMode(new TIntEnumParam(Iris_InputImage, "Input Image")) , m_irisScale(0.2) , m_irisGearEdgeCount(10) @@ -59,14 +60,17 @@ Iwa_GlareFx::Iwa_GlareFx() , m_noise_offset(TPointD(0, 0)) { // Version 1 : lights had been constantly summed in all wavelength // Version 2 : intensities are weighted proportional to 1/(rambda^2) - setFxVersion(2); + // Version 3 : * 1/2.2 gamma to intensity when non-linear rendering + // Source image is separated in each RGB channels + // Filter is normalized with sum of green channel values + setFxVersion(3); // Bind the common parameters addInputPort("Source", m_source); addInputPort("Iris", m_iris); bindParam(this, "renderMode", m_renderMode); - m_renderMode->addItem(RendeMode_Render, "Render"); + m_renderMode->addItem(RenderMode_Render, "Render"); m_renderMode->addItem(RenderMode_Iris, "Iris"); bindParam(this, "irisMode", m_irisMode); @@ -114,6 +118,8 @@ Iwa_GlareFx::Iwa_GlareFx() m_aberration->setValueRange(-2.0, 2.0); m_noise_factor->setValueRange(0.0, 1.0); m_noise_size->setValueRange(0.01, 3.0); + + enableComputeInFloat(true); } //-------------------------------------------------------------- @@ -280,7 +286,7 @@ void Iwa_GlareFx::doCompute(TTile& tile, double frame, int renderMode = m_renderMode->getValue(); // If the source is not connected & it is render mode, then do nothing. - if (!m_source.isConnected() && renderMode == RendeMode_Render) { + if (!m_source.isConnected() && renderMode == RenderMode_Render) { tile.getRaster()->clear(); return; } @@ -322,6 +328,8 @@ void Iwa_GlareFx::doCompute(TTile& tile, double frame, dimIris = kiss_fft_next_fast_size(dimIris + 1); double irisResizeFactor = double(dimIris) * 0.5 / size; + bool isLinear = tile.getRaster()->isLinear(); + kiss_fft_cpx* kissfft_comp_iris; // create the iris data for FFT (in the same size as the source tile) TRasterGR8P kissfft_comp_iris_ras(dimIris * sizeof(kiss_fft_cpx), dimIris); @@ -370,13 +378,16 @@ void Iwa_GlareFx::doCompute(TTile& tile, double frame, tile.getRaster()->clear(); TRaster32P ras32 = tile.getRaster(); TRaster64P ras64 = tile.getRaster(); - if (ras32) - ras32->fill(TPixel32::Transparent); - else if (ras64) - ras64->fill(TPixel64::Transparent); + TRasterFP rasF = tile.getRaster(); + // if (ras32) + // ras32->fill(TPixel32::Transparent); + // else if (ras64) + // ras64->fill(TPixel64::Transparent); + // else if (rasF) + // rasF->fill(TPixelF::Transparent); // filter preview mode - if (renderMode == RendeMode_FilterPreview) { + if (renderMode == RenderMode_FilterPreview) { int2 margin = {(dimIris - tile.getRaster()->getSize().lx) / 2, (dimIris - tile.getRaster()->getSize().ly) / 2}; @@ -386,7 +397,14 @@ void Iwa_GlareFx::doCompute(TTile& tile, double frame, else if (ras64) setFilterPreviewToResult(ras64, glare_pattern, dimIris, margin); + else if (rasF) + setFilterPreviewToResult(rasF, glare_pattern, dimIris, + margin); + if (getFxVersion() >= 3 && !isLinear) { + tile.getRaster()->setLinear(true); + TRop::tosRGB(tile.getRaster(), settings.m_colorSpaceGamma); + } return; } @@ -438,25 +456,46 @@ void Iwa_GlareFx::doCompute(TTile& tile, double frame, kiss_fftnd_cfg plan_fwd = kiss_fftnd_alloc(dims, ndims, false, 0, 0); kiss_fftnd_cfg plan_bkwd = kiss_fftnd_alloc(dims, ndims, true, 0, 0); - // store the source image to tmp - { - // obtain the source tile - TTile sourceTile; - m_source->allocateAndCompute(sourceTile, _rectOut.getP00(), dimOut, - tile.getRaster(), frame, settings); + // obtain the source tile + TTile sourceTile; + m_source->allocateAndCompute(sourceTile, _rectOut.getP00(), dimOut, + tile.getRaster(), frame, settings); + + if (getFxVersion() >= 3 && !isLinear) + TRop::toLinearRGB(sourceTile.getRaster(), settings.m_colorSpaceGamma); + // store the source image to tmp + if (getFxVersion() < 3) { if (ras32) setSourceTileToBuffer(sourceTile.getRaster(), kissfft_comp_tmp); else if (ras64) setSourceTileToBuffer(sourceTile.getRaster(), kissfft_comp_tmp); + else if (rasF) + setSourceTileToBuffer(sourceTile.getRaster(), + kissfft_comp_tmp); + // FFT the source + kiss_fftnd(plan_fwd, kissfft_comp_tmp, kissfft_comp_source); } - // FFT the source - kiss_fftnd(plan_fwd, kissfft_comp_tmp, kissfft_comp_source); // compute for each rgb channels for (int ch = 0; ch < 3; ch++) { + // store the source image to tmp for each channel + if (getFxVersion() >= 3) { + if (ras32) + setSourceTileToBuffer(sourceTile.getRaster(), + kissfft_comp_tmp, ch); + else if (ras64) + setSourceTileToBuffer(sourceTile.getRaster(), + kissfft_comp_tmp, ch); + else if (rasF) + setSourceTileToBuffer(sourceTile.getRaster(), + kissfft_comp_tmp, ch); + // FFT the source + kiss_fftnd(plan_fwd, kissfft_comp_tmp, kissfft_comp_source); + } + kissfft_comp_tmp_ras->clear(); // store the glare pattern to tmp setGlarePatternToBuffer(glare_pattern, kissfft_comp_tmp, ch, dimIris, @@ -480,6 +519,14 @@ void Iwa_GlareFx::doCompute(TTile& tile, double frame, else if (ras64) setChannelToResult(ras64, kissfft_comp_tmp, ch, dimOut); + else if (rasF) + setChannelToResult(rasF, kissfft_comp_tmp, ch, + dimOut); + } + + if (getFxVersion() >= 3 && !isLinear) { + tile.getRaster()->setLinear(true); + TRop::tosRGB(tile.getRaster(), settings.m_colorSpaceGamma); } kiss_fft_free(plan_fwd); @@ -517,18 +564,19 @@ void Iwa_GlareFx::powerSpectrum2GlarePattern( }; double factor = - (m_renderMode->getValue() == RendeMode_FilterPreview) ? -5 : -11; + (m_renderMode->getValue() == RenderMode_FilterPreview) ? -5 : -11; double* glarePattern_p; TRasterGR8P glarePattern_ras(dimIris * sizeof(double), dimIris); glarePattern_p = (double*)glarePattern_ras->getRawData(); glarePattern_ras->lock(); double* g_p = glarePattern_p; + double intensityFactor = + (getFxVersion() < 3) ? std::exp(intensity + factor) : 1.0; for (int j = 0; j < dimIris; j++) { for (int i = 0; i < dimIris; i++, g_p++) { kiss_fft_cpx sp_p = spectrum[getCoord(i, j, dimIris, dimIris)]; - (*g_p) = sqrt(sp_p.r * sp_p.r + sp_p.i * sp_p.i) * - std::exp(intensity + factor); + (*g_p) = sqrt(sp_p.r * sp_p.r + sp_p.i * sp_p.i) * intensityFactor; } } @@ -551,7 +599,7 @@ void Iwa_GlareFx::powerSpectrum2GlarePattern( // old version had summed each wavelength constantly. // it was not physically-collect but keep it in order to maintain backward // compatibility. - bool isOldVersion = getFxVersion() < 2; + bool isVersion1 = getFxVersion() < 2; // accumurate xyz values for each optical wavelength for (int ram = 0; ram < 34; ram++) { @@ -559,7 +607,7 @@ void Iwa_GlareFx::powerSpectrum2GlarePattern( // double scale = 0.55 / rambda; double scale = std::pow(0.55 / rambda, aberration); double intensity_scale = - (isOldVersion) ? 1.0 : std::pow(0.55 / rambda, 2.0 * aberration); + (isVersion1) ? 1.0 : std::pow(0.55 / rambda, 2.0 * aberration); scale *= irisResizeFactor; for (int j = 0; j < dimIris; j++) { double j_scaled = (double(j) - irisRadius) * scale + irisRadius; @@ -578,9 +626,9 @@ void Iwa_GlareFx::powerSpectrum2GlarePattern( double gl = lerpGlarePtn(i_scaled, j_scaled, glarePattern_p) * intensity_scale; - g_xyz_p->x += gl * cie_d65[ram] * xyz[ram * 3 + 0]; - g_xyz_p->y += gl * cie_d65[ram] * xyz[ram * 3 + 1]; - g_xyz_p->z += gl * cie_d65[ram] * xyz[ram * 3 + 2]; + g_xyz_p->x += gl * (double)cie_d65[ram] * (double)xyz[ram * 3 + 0]; + g_xyz_p->y += gl * (double)cie_d65[ram] * (double)xyz[ram * 3 + 1]; + g_xyz_p->z += gl * (double)cie_d65[ram] * (double)xyz[ram * 3 + 2]; } } } @@ -589,13 +637,36 @@ void Iwa_GlareFx::powerSpectrum2GlarePattern( // convert to rgb double3* g_xyz_p = glare_xyz; double3* g_out_p = glare; + double max_g = 0.; + double sum_g = 0.; for (int i = 0; i < dimIris * dimIris; i++, g_xyz_p++, g_out_p++) { - (*g_out_p).x = 3.240479f * (*g_xyz_p).x - 1.537150f * (*g_xyz_p).y - - 0.498535f * (*g_xyz_p).z; - (*g_out_p).y = -0.969256f * (*g_xyz_p).x + 1.875992f * (*g_xyz_p).y + - 0.041556f * (*g_xyz_p).z; - (*g_out_p).z = 0.055648f * (*g_xyz_p).x - 0.204043f * (*g_xyz_p).y + - 1.057311f * (*g_xyz_p).z; + (*g_out_p).x = 3.240479 * (*g_xyz_p).x - 1.537150 * (*g_xyz_p).y - + 0.498535 * (*g_xyz_p).z; + (*g_out_p).y = -0.969256 * (*g_xyz_p).x + 1.875992 * (*g_xyz_p).y + + 0.041556 * (*g_xyz_p).z; + (*g_out_p).z = 0.055648 * (*g_xyz_p).x - 0.204043 * (*g_xyz_p).y + + 1.057311 * (*g_xyz_p).z; + + // get the maximum and sum the green channel + if ((*g_out_p).y > max_g) max_g = (*g_out_p).y; + sum_g += (*g_out_p).y; + } + + if (getFxVersion() >= 3) { + double intensityFactor = std::exp(intensity); + // normalize with the brightest green channel + if (m_renderMode->getValue() == RenderMode_FilterPreview) + intensityFactor /= max_g; + // normalize with the sum of the intensity + else + intensityFactor /= sum_g; + + g_out_p = glare; + for (int i = 0; i < dimIris * dimIris; i++, g_out_p++) { + (*g_out_p).x *= intensityFactor; + (*g_out_p).y *= intensityFactor; + (*g_out_p).z *= intensityFactor; + } } glare_xyz_ras->unlock(); @@ -713,7 +784,8 @@ void Iwa_GlareFx::setFilterPreviewToResult(const RASTER ras, double3* glare, if (chan > 1.0) return 1.0; return chan; }; - int j = margin.y; + bool doClamp = (ras->getPixelSize() != 16); + int j = margin.y; for (int out_j = 0; out_j < ras->getLy(); j++, out_j++) { if (j < 0) continue; @@ -727,13 +799,19 @@ void Iwa_GlareFx::setFilterPreviewToResult(const RASTER ras, double3* glare, else if (i >= dimIris) break; double3 gl_p = glare[j * dimIris + i]; - pix->r = (typename PIXEL::Channel)(clamp01(gl_p.x) * - double(PIXEL::maxChannelValue)); - pix->g = (typename PIXEL::Channel)(clamp01(gl_p.y) * - double(PIXEL::maxChannelValue)); - pix->b = (typename PIXEL::Channel)(clamp01(gl_p.z) * - double(PIXEL::maxChannelValue)); - pix->m = PIXEL::maxChannelValue; + if (doClamp) { + pix->r = (typename PIXEL::Channel)(clamp01(gl_p.x) * + double(PIXEL::maxChannelValue)); + pix->g = (typename PIXEL::Channel)(clamp01(gl_p.y) * + double(PIXEL::maxChannelValue)); + pix->b = (typename PIXEL::Channel)(clamp01(gl_p.z) * + double(PIXEL::maxChannelValue)); + } else { // floating point case + pix->r = (typename PIXEL::Channel)gl_p.x; + pix->g = (typename PIXEL::Channel)gl_p.y; + pix->b = (typename PIXEL::Channel)gl_p.z; + } + pix->m = PIXEL::maxChannelValue; } } } @@ -755,6 +833,26 @@ void Iwa_GlareFx::setSourceTileToBuffer(const RASTER ras, kiss_fft_cpx* buf) { } } +// put the source tile's brightness to fft buffer +template +void Iwa_GlareFx::setSourceTileToBuffer(const RASTER ras, kiss_fft_cpx* buf, + int channel) { + kiss_fft_cpx* buf_p = buf; + for (int j = 0; j < ras->getLy(); j++) { + PIXEL* pix = ras->pixels(j); + for (int i = 0; i < ras->getLx(); i++, pix++, buf_p++) { + if (channel == 0) + (*buf_p).r = float(pix->r) / float(PIXEL::maxChannelValue); + else if (channel == 1) + (*buf_p).r = float(pix->g) / float(PIXEL::maxChannelValue); + else { // if (channel == 2) + (*buf_p).r = float(pix->b) / float(PIXEL::maxChannelValue); + } + (*buf_p).i = 0.f; + } + } +} + //------------------------------------------------ void Iwa_GlareFx::setGlarePatternToBuffer(const double3* glare, @@ -767,9 +865,9 @@ void Iwa_GlareFx::setGlarePatternToBuffer(const double3* glare, const double3* glare_p = &glare[(j - margin_y) * dimIris]; kiss_fft_cpx* buf_p = &buf[j * dimOut.lx + margin_x]; for (int i = margin_x; i < margin_x + dimIris; i++, buf_p++, glare_p++) { - (*buf_p).r = (channel == 0) - ? (*glare_p).x - : (channel == 1) ? (*glare_p).y : (*glare_p).z; + (*buf_p).r = (channel == 0) ? (*glare_p).x + : (channel == 1) ? (*glare_p).y + : (*glare_p).z; } } } @@ -801,6 +899,8 @@ void Iwa_GlareFx::setChannelToResult(const RASTER ras, kiss_fft_cpx* buf, int margin_x = (dimOut.lx - ras->getSize().lx) / 2; int margin_y = (dimOut.ly - ras->getSize().ly) / 2; + bool doClamp = (ras->getPixelSize() != 16); + for (int j = 0; j < ras->getLy(); j++) { // kiss_fft_cpx* buf_p = &buf[(j + margin_y)*dimOut.lx + margin_x]; PIXEL* pix = ras->pixels(j); @@ -808,16 +908,27 @@ void Iwa_GlareFx::setChannelToResult(const RASTER ras, kiss_fft_cpx* buf, kiss_fft_cpx fft_val = buf[getCoord(i + margin_x, j + margin_y, dimOut.lx, dimOut.ly)]; double val = fft_val.r / (dimOut.lx * dimOut.ly); - if (channel == 0) - pix->r = (typename PIXEL::Channel)(clamp01(val) * - double(PIXEL::maxChannelValue)); - else if (channel == 1) - pix->g = (typename PIXEL::Channel)(clamp01(val) * - double(PIXEL::maxChannelValue)); - else if (channel == 2) { - pix->b = (typename PIXEL::Channel)(clamp01(val) * - double(PIXEL::maxChannelValue)); - pix->m = PIXEL::maxChannelValue; + if (doClamp) { + if (channel == 0) + pix->r = (typename PIXEL::Channel)(clamp01(val) * + double(PIXEL::maxChannelValue)); + else if (channel == 1) + pix->g = (typename PIXEL::Channel)(clamp01(val) * + double(PIXEL::maxChannelValue)); + else if (channel == 2) { + pix->b = (typename PIXEL::Channel)(clamp01(val) * + double(PIXEL::maxChannelValue)); + pix->m = PIXEL::maxChannelValue; + } + } else { // floating point case + if (channel == 0) + pix->r = (typename PIXEL::Channel)val; + else if (channel == 1) + pix->g = (typename PIXEL::Channel)val; + else if (channel == 2) { + pix->b = (typename PIXEL::Channel)val; + pix->m = PIXEL::maxChannelValue; + } } } } @@ -903,6 +1014,8 @@ void Iwa_GlareFx::convertIris(kiss_fft_cpx* kissfft_comp_iris_before, } } +//------------------------------------------------ + void Iwa_GlareFx::getParamUIs(TParamUIConcept*& concepts, int& length) { concepts = new TParamUIConcept[length = 2]; @@ -915,4 +1028,11 @@ void Iwa_GlareFx::getParamUIs(TParamUIConcept*& concepts, int& length) { concepts[1].m_params.push_back(m_noise_offset); } +//------------------------------------------------ + +bool Iwa_GlareFx::toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const { + return settingsIsLinear; +} + FX_PLUGIN_IDENTIFIER(Iwa_GlareFx, "iwa_GlareFx") \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_glarefx.h b/toonz/sources/stdfx/iwa_glarefx.h index a3c870e..49873a4 100644 --- a/toonz/sources/stdfx/iwa_glarefx.h +++ b/toonz/sources/stdfx/iwa_glarefx.h @@ -57,7 +57,7 @@ protected: TDoubleParamP m_noise_evolution; TPointParamP m_noise_offset; - enum { RendeMode_FilterPreview = 0, RendeMode_Render, RenderMode_Iris }; + enum { RenderMode_FilterPreview = 0, RenderMode_Render, RenderMode_Iris }; enum { Iris_InputImage = 0, @@ -100,6 +100,8 @@ protected: // put the source tile's brightness to fft buffer template void setSourceTileToBuffer(const RASTER ras, kiss_fft_cpx *buf); + template + void setSourceTileToBuffer(const RASTER ras, kiss_fft_cpx *buf, int channel); void setGlarePatternToBuffer(const double3 *glare, kiss_fft_cpx *buf, const int channel, const int dimIris, @@ -124,6 +126,9 @@ public: bool canHandle(const TRenderSettings &info, double frame) override; void getParamUIs(TParamUIConcept *&concepts, int &length) override; + + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override; }; #endif diff --git a/toonz/sources/stdfx/iwa_gradientwarpfx.cpp b/toonz/sources/stdfx/iwa_gradientwarpfx.cpp index ee63dac..2ccc69f 100644 --- a/toonz/sources/stdfx/iwa_gradientwarpfx.cpp +++ b/toonz/sources/stdfx/iwa_gradientwarpfx.cpp @@ -4,6 +4,7 @@ ------------------------------------*/ #include "iwa_gradientwarpfx.h" +#include "trop.h" /*------------------------------------------------------------ ソース画像を0〜1に正規化してホストメモリに読み込む @@ -31,6 +32,8 @@ void Iwa_GradientWarpFx::setSourceRaster(const RASTER srcRas, float4 *dstMem, template void Iwa_GradientWarpFx::setWarperRaster(const RASTER warperRas, float *dstMem, TDimensionI dim) { + auto clamp01 = [](float val) { return std::min(1.f, std::max(0.f, val)); }; + float *chann_p = dstMem; for (int j = 0; j < dim.ly; j++) { PIXEL *pix = warperRas->pixels(j); @@ -39,7 +42,7 @@ void Iwa_GradientWarpFx::setWarperRaster(const RASTER warperRas, float *dstMem, float g = (float)pix->g / (float)PIXEL::maxChannelValue; float b = (float)pix->b / (float)PIXEL::maxChannelValue; - (*chann_p) = 0.298912f * r + 0.586611f * g + 0.114478f * b; + (*chann_p) = clamp01(0.298912f * r + 0.586611f * g + 0.114478f * b); } } } @@ -77,6 +80,22 @@ void Iwa_GradientWarpFx::setOutputRaster(float4 *srcMem, const RASTER dstRas, } } +template <> +void Iwa_GradientWarpFx::setOutputRaster( + float4 *srcMem, const TRasterFP dstRas, TDimensionI dim, int2 margin) { + int out_j = 0; + for (int j = margin.y; j < dstRas->getLy() + margin.y; j++, out_j++) { + TPixelF *pix = dstRas->pixels(out_j); + float4 *chan_p = srcMem; + chan_p += j * dim.lx + margin.x; + for (int i = 0; i < dstRas->getLx(); i++, pix++, chan_p++) { + pix->r = (*chan_p).x; + pix->g = (*chan_p).y; + pix->b = (*chan_p).z; + pix->m = (*chan_p).w; + } + } +} //------------------------------------ Iwa_GradientWarpFx::Iwa_GradientWarpFx() @@ -97,6 +116,7 @@ Iwa_GradientWarpFx::Iwa_GradientWarpFx() m_sampling_size->setMeasureName("fxLength"); m_sampling_size->setValueRange(0.1, 20.0); + enableComputeInFloat(true); // Version 1: sampling distance had been always 1 pixel. // Version 2: sampling distance can be specified with the parameter. // this must be called after binding the parameters (see onFxVersionSet()) @@ -146,7 +166,8 @@ void Iwa_GradientWarpFx::doCompute(TTile &tile, double frame, int margin = static_cast(ceil((std::abs(hLength) < std::abs(vLength)) ? std::abs(vLength) : std::abs(hLength))); - margin = std::max(margin, static_cast(std::ceil(sampling_size)) + 1); + + margin = std::max(margin, static_cast(std::ceil(sampling_size)) + 1); /*- 素材計算範囲を計算 -*/ /*- 出力範囲 -*/ @@ -156,24 +177,30 @@ void Iwa_GradientWarpFx::doCompute(TTile &tile, double frame, TDimensionI enlargedDim((int)enlargedRect.getLx(), (int)enlargedRect.getLy()); /*- ソース画像を正規化して格納 -*/ + TTile sourceTile; + m_source->allocateAndCompute(sourceTile, enlargedRect.getP00(), enlargedDim, + tile.getRaster(), frame, settings); + float4 *source_host; - TRasterGR8P source_host_ras(enlargedDim.lx * sizeof(float4), enlargedDim.ly); - source_host_ras->lock(); - source_host = (float4 *)source_host_ras->getRawData(); - { - /*- - * タイルはこのフォーカス内だけ使用。正規化してsource_hostに取り込んだらもう使わない。 - * -*/ - TTile sourceTile; - m_source->allocateAndCompute(sourceTile, enlargedRect.getP00(), enlargedDim, - tile.getRaster(), frame, settings); - /*- タイルの画像を0〜1に正規化してホストメモリに読み込む -*/ - TRaster32P ras32 = (TRaster32P)sourceTile.getRaster(); - TRaster64P ras64 = (TRaster64P)sourceTile.getRaster(); - if (ras32) - setSourceRaster(ras32, source_host, enlargedDim); - else if (ras64) - setSourceRaster(ras64, source_host, enlargedDim); + TRasterGR8P source_host_ras; + if (tile.getRaster()->getPixelSize() == 4 || + tile.getRaster()->getPixelSize() == 8) { + source_host_ras = + TRasterGR8P(enlargedDim.lx * sizeof(float4), enlargedDim.ly); + source_host_ras->lock(); + source_host = (float4 *)source_host_ras->getRawData(); + { + /*- タイルの画像を0〜1に正規化してホストメモリに読み込む -*/ + TRaster32P ras32 = (TRaster32P)sourceTile.getRaster(); + TRaster64P ras64 = (TRaster64P)sourceTile.getRaster(); + if (ras32) + setSourceRaster(ras32, source_host, enlargedDim); + else if (ras64) + setSourceRaster(ras64, source_host, enlargedDim); + } + } else if (tile.getRaster()->getPixelSize() == 16) { + source_host = + reinterpret_cast(sourceTile.getRaster()->getRawData()); } /*- 参照画像を正規化して格納 -*/ @@ -188,13 +215,19 @@ void Iwa_GradientWarpFx::doCompute(TTile &tile, double frame, TTile warperTile; m_warper->allocateAndCompute(warperTile, enlargedRect.getP00(), enlargedDim, tile.getRaster(), frame, settings); + // nonlinearのはず + assert(!warperTile.getRaster()->isLinear()); + /*- タイルの画像の輝度値を0〜1に正規化してホストメモリに読み込む -*/ TRaster32P ras32 = (TRaster32P)warperTile.getRaster(); TRaster64P ras64 = (TRaster64P)warperTile.getRaster(); + TRasterFP rasF = (TRasterFP)warperTile.getRaster(); if (ras32) setWarperRaster(ras32, warper_host, enlargedDim); else if (ras64) setWarperRaster(ras64, warper_host, enlargedDim); + else if (rasF) + setWarperRaster(rasF, warper_host, enlargedDim); } /*- 変位値をScale倍して増やす -*/ @@ -221,15 +254,19 @@ void Iwa_GradientWarpFx::doCompute(TTile &tile, double frame, tile.getRaster()->clear(); TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); if (outRas32) setOutputRaster(source_host, outRas32, enlargedDim, yohaku); else if (outRas64) setOutputRaster(source_host, outRas64, enlargedDim, yohaku); + else if (outRasF) + setOutputRaster(source_host, outRasF, enlargedDim, + yohaku); /*- ソース画像のメモリ解放 -*/ - source_host_ras->unlock(); + if (source_host_ras) source_host_ras->unlock(); /*- 参照画像のメモリ解放 -*/ warper_host_ras->unlock(); result_host_ras->unlock(); diff --git a/toonz/sources/stdfx/iwa_gradientwarpfx.h b/toonz/sources/stdfx/iwa_gradientwarpfx.h index cd9b756..e4fd585 100644 --- a/toonz/sources/stdfx/iwa_gradientwarpfx.h +++ b/toonz/sources/stdfx/iwa_gradientwarpfx.h @@ -15,7 +15,15 @@ struct float2 { float x, y; }; struct float4 { - float x, y, z, w; +#if defined(TNZ_MACHINE_CHANNEL_ORDER_BGRM) + float z, y, x, w; +#elif defined(TNZ_MACHINE_CHANNEL_ORDER_MBGR) + float w, x, y, x; +#elif defined(TNZ_MACHINE_CHANNEL_ORDER_RGBM) + float x, y, x, w; +#elif defined(TNZ_MACHINE_CHANNEL_ORDER_MRGB) + float w, x, y, z; +#endif }; struct int2 { int x, y; diff --git a/toonz/sources/stdfx/iwa_lineargradientfx.cpp b/toonz/sources/stdfx/iwa_lineargradientfx.cpp index 173aa89..bc334ef 100644 --- a/toonz/sources/stdfx/iwa_lineargradientfx.cpp +++ b/toonz/sources/stdfx/iwa_lineargradientfx.cpp @@ -36,6 +36,8 @@ Iwa_LinearGradientFx::Iwa_LinearGradientFx() bindParam(this, "startColor", m_startColor); bindParam(this, "endColor", m_endColor); + + enableComputeInFloat(true); } //------------------------------------------------------------ @@ -123,7 +125,8 @@ void doLinearGradientT(RASTER ras, TDimensionI dim, TPointD startPos, void Iwa_LinearGradientFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } @@ -144,11 +147,13 @@ void Iwa_LinearGradientFx::doCompute(TTile &tile, double frame, std::vector colors = { TSpectrum::ColorKey(0, m_startColor->getValue(frame)), TSpectrum::ColorKey(1, m_endColor->getValue(frame))}; + TSpectrumParamP m_colors = TSpectrumParamP(colors); tile.getRaster()->clear(); TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); if (outRas32) doLinearGradientT( outRas32, dimOut, startPos, endPos, m_colors->getValue(frame), @@ -159,6 +164,11 @@ void Iwa_LinearGradientFx::doCompute(TTile &tile, double frame, outRas64, dimOut, startPos, endPos, m_colors->getValue64(frame), (GradientCurveType)m_curveType->getValue(), w_amplitude, w_freq, w_phase, aff.inv()); + else if (outRasF) + doLinearGradientT( + outRasF, dimOut, startPos, endPos, m_colors->getValueF(frame), + (GradientCurveType)m_curveType->getValue(), w_amplitude, w_freq, + w_phase, aff.inv()); } //------------------------------------------------------------ diff --git a/toonz/sources/stdfx/iwa_motionblurfx.cpp b/toonz/sources/stdfx/iwa_motionblurfx.cpp index 3b62aa4..983de22 100644 --- a/toonz/sources/stdfx/iwa_motionblurfx.cpp +++ b/toonz/sources/stdfx/iwa_motionblurfx.cpp @@ -35,6 +35,7 @@ bool Iwa_MotionBlurCompFx::setSourceRaster(const RASTER srcRas, float4 *dstMem, /* If there are pixels whose RGB values ​​are larger than the alpha * channel, determine the source is not premutiplied */ + // CAUTION: this condition won't work properly ith HDR image pixels! if (type == AUTO && isPremultiplied && (((*chann_p).x > (*chann_p).w && (*chann_p).x > threshold) || ((*chann_p).y > (*chann_p).w && (*chann_p).y > threshold) || @@ -93,6 +94,25 @@ void Iwa_MotionBlurCompFx::setOutputRaster(float4 *srcMem, const RASTER dstRas, } } +template <> +void Iwa_MotionBlurCompFx::setOutputRaster( + float4 *srcMem, const TRasterFP dstRas, TDimensionI dim, int2 margin) { + int out_j = 0; + for (int j = margin.y; j < dstRas->getLy() + margin.y; j++, out_j++) { + TPixelF *pix = dstRas->pixels(out_j); + float4 *chan_p = srcMem; + chan_p += j * dim.lx + margin.x; + for (int i = 0; i < dstRas->getLx(); i++) { + pix->r = (*chan_p).x; + pix->g = (*chan_p).y; + pix->b = (*chan_p).z; + pix->m = (*chan_p).w; + pix++; + chan_p++; + } + } +} + /*------------------------------------------------------------ Create and normalize filters ------------------------------------------------------------*/ @@ -133,8 +153,8 @@ void Iwa_MotionBlurCompFx::makeMotionBlurFilter_CPU( /* Calculate the inner product of 'p0'->sampling point and 'p0'->'p1' */ float2 vec_p0_sample = {static_cast(pos.x - p0.x), static_cast(pos.y - p0.y)}; - float2 vec_p0_p1 = {static_cast(p1.x - p0.x), - static_cast(p1.y - p0.y)}; + float2 vec_p0_p1 = {static_cast(p1.x - p0.x), + static_cast(p1.y - p0.y)}; float dot = vec_p0_sample.x * vec_p0_p1.x + vec_p0_sample.y * vec_p0_p1.y; /* Calculate the square of distance */ @@ -330,7 +350,7 @@ void Iwa_MotionBlurCompFx::makeZanzoFilter_CPU( float curveValue; float frameOffset = p0.w; /* If the frame is exactly at the frame origin, - * or there is no attenuation value, set curveValue to 1 */ + * or there is no attenuation value, set curveValue to 1 */ if (frameOffset == 0.0f || (frameOffset < 0.0f && startValue == 1.0f) || (frameOffset > 0.0f && endValue == 1.0f)) curveValue = 1.0f; @@ -374,7 +394,7 @@ void Iwa_MotionBlurCompFx::makeZanzoFilter_CPU( ------------------------------------------------------------*/ void Iwa_MotionBlurCompFx::convertRGBtoExposure_CPU( - float4 *in_tile_p, TDimensionI &dim, float hardness, + float4 *in_tile_p, TDimensionI &dim, const ExposureConverter &conv, bool sourceIsPremultiplied) { float4 *cur_tile_p = in_tile_p; for (int i = 0; i < dim.lx * dim.ly; i++, cur_tile_p++) { @@ -398,9 +418,9 @@ void Iwa_MotionBlurCompFx::convertRGBtoExposure_CPU( } /* convert RGB to Exposure */ - cur_tile_p->x = powf(10, (cur_tile_p->x - 0.5f) * hardness); - cur_tile_p->y = powf(10, (cur_tile_p->y - 0.5f) * hardness); - cur_tile_p->z = powf(10, (cur_tile_p->z - 0.5f) * hardness); + cur_tile_p->x = conv.valueToExposure(cur_tile_p->x); + cur_tile_p->y = conv.valueToExposure(cur_tile_p->y); + cur_tile_p->z = conv.valueToExposure(cur_tile_p->z); /* Then multiply with the alpha channel */ cur_tile_p->x *= cur_tile_p->w; @@ -464,9 +484,8 @@ void Iwa_MotionBlurCompFx::applyBlurFilter_CPU( -> premultiply ------------------------------------------------------------*/ -void Iwa_MotionBlurCompFx::convertExposureToRGB_CPU(float4 *out_tile_p, - TDimensionI &dim, - float hardness) { +void Iwa_MotionBlurCompFx::convertExposureToRGB_CPU( + float4 *out_tile_p, TDimensionI &dim, const ExposureConverter &conv) { float4 *cur_tile_p = out_tile_p; for (int i = 0; i < dim.lx * dim.ly; i++, cur_tile_p++) { /* if alpha is 0 return */ @@ -483,25 +502,14 @@ void Iwa_MotionBlurCompFx::convertExposureToRGB_CPU(float4 *out_tile_p, cur_tile_p->z /= cur_tile_p->w; /* Convert Exposure to RGB value */ - cur_tile_p->x = log10f(cur_tile_p->x) / hardness + 0.5f; - cur_tile_p->y = log10f(cur_tile_p->y) / hardness + 0.5f; - cur_tile_p->z = log10f(cur_tile_p->z) / hardness + 0.5f; + cur_tile_p->x = conv.exposureToValue(cur_tile_p->x); + cur_tile_p->y = conv.exposureToValue(cur_tile_p->y); + cur_tile_p->z = conv.exposureToValue(cur_tile_p->z); // multiply cur_tile_p->x *= cur_tile_p->w; cur_tile_p->y *= cur_tile_p->w; cur_tile_p->z *= cur_tile_p->w; - - /* Clamp */ - cur_tile_p->x = (cur_tile_p->x > 1.0f) - ? 1.0f - : ((cur_tile_p->x < 0.0f) ? 0.0f : cur_tile_p->x); - cur_tile_p->y = (cur_tile_p->y > 1.0f) - ? 1.0f - : ((cur_tile_p->y < 0.0f) ? 0.0f : cur_tile_p->y); - cur_tile_p->z = (cur_tile_p->z > 1.0f) - ? 1.0f - : ((cur_tile_p->z < 0.0f) ? 0.0f : cur_tile_p->z); } } @@ -528,7 +536,8 @@ void Iwa_MotionBlurCompFx::composeWithNoMotion( ------------------------------------------------------------*/ void Iwa_MotionBlurCompFx::composeBackgroundExposure_CPU( float4 *out_tile_p, TDimensionI &enlargedDimIn, int marginRight, - int marginTop, TTile &back_tile, TDimensionI &dimOut, float hardness) { + int marginTop, TTile &back_tile, TDimensionI &dimOut, + const ExposureConverter &conv) { /* Memory allocation of host */ TRasterGR8P background_host_ras(sizeof(float4) * dimOut.lx, dimOut.ly); background_host_ras->lock(); @@ -540,12 +549,16 @@ void Iwa_MotionBlurCompFx::composeBackgroundExposure_CPU( */ TRaster32P backRas32 = (TRaster32P)back_tile.getRaster(); TRaster64P backRas64 = (TRaster64P)back_tile.getRaster(); + TRasterFP backRasF = (TRasterFP)back_tile.getRaster(); if (backRas32) bgIsPremultiplied = setSourceRaster( backRas32, background_host, dimOut); else if (backRas64) bgIsPremultiplied = setSourceRaster( backRas64, background_host, dimOut); + else if (backRasF) + bgIsPremultiplied = + setSourceRaster(backRasF, background_host, dimOut); float4 *bg_p = background_host; float4 *out_p; @@ -565,7 +578,7 @@ void Iwa_MotionBlurCompFx::composeBackgroundExposure_CPU( * It is not done for 'digital overlay' (image with alpha mask added by * using Photoshop, as known as 'DigiBook' in Japanese animation industry) * etc. - */ + */ if (bgIsPremultiplied) { // Unpremultiply bgExposure.x /= (*bg_p).w; @@ -573,10 +586,10 @@ void Iwa_MotionBlurCompFx::composeBackgroundExposure_CPU( bgExposure.z /= (*bg_p).w; } - /* Set Exposure to RGB value */ - bgExposure.x = powf(10, (bgExposure.x - 0.5f) * hardness); - bgExposure.y = powf(10, (bgExposure.y - 0.5f) * hardness); - bgExposure.z = powf(10, (bgExposure.z - 0.5f) * hardness); + /* Set RGB value to Exposure*/ + bgExposure.x = conv.valueToExposure(bgExposure.x); + bgExposure.y = conv.valueToExposure(bgExposure.y); + bgExposure.z = conv.valueToExposure(bgExposure.z); // multiply bgExposure.x *= (*bg_p).w; @@ -598,6 +611,8 @@ void Iwa_MotionBlurCompFx::composeBackgroundExposure_CPU( Iwa_MotionBlurCompFx::Iwa_MotionBlurCompFx() : m_hardness(0.3) + , m_gamma(2.2) + , m_gammaAdjust(0.) /* Parameters for blurring left and right */ , m_startValue(1.0) , m_startCurve(1.0) @@ -610,6 +625,8 @@ Iwa_MotionBlurCompFx::Iwa_MotionBlurCompFx() addInputPort("Back", m_background); bindParam(this, "hardness", m_hardness); + bindParam(this, "gamma", m_gamma); + bindParam(this, "gammaAdjust", m_gammaAdjust); bindParam(this, "shutterStart", m_shutterStart); bindParam(this, "shutterEnd", m_shutterEnd); bindParam(this, "traceResolution", m_traceResolution); @@ -627,6 +644,8 @@ Iwa_MotionBlurCompFx::Iwa_MotionBlurCompFx() /* Common parameter range setting */ m_hardness->setValueRange(0.05, 10.0); + m_gamma->setValueRange(1.0, 10.0); + m_gammaAdjust->setValueRange(-5., 5.); m_startValue->setValueRange(0.0, 1.0); m_startCurve->setValueRange(0.1, 10.0); m_endValue->setValueRange(0.0, 1.0); @@ -637,6 +656,40 @@ Iwa_MotionBlurCompFx::Iwa_MotionBlurCompFx() "Source is NOT premultiplied"); getAttributes()->setIsSpeedAware(true); + enableComputeInFloat(true); + + // Version 1: Exposure is computed by using Hardness + // E = std::pow(10.0, (value - 0.5) / hardness) + // Version 2: Exposure is computed by using Gamma, for easier combination with + // the linear color space + // E = std::pow(value, gamma) + // Version 3: Gamma is computed by rs.m_colorSpaceGamma + gammaAdjust + // this must be called after binding the parameters (see onFxVersionSet()) + setFxVersion(3); +} + +//-------------------------------------------- + +void Iwa_MotionBlurCompFx::onFxVersionSet() { + if (getFxVersion() == 1) { // use hardness + getParams()->getParamVar("hardness")->setIsHidden(false); + getParams()->getParamVar("gamma")->setIsHidden(true); + getParams()->getParamVar("gammaAdjust")->setIsHidden(true); + return; + } + getParams()->getParamVar("hardness")->setIsHidden(true); + + bool useGamma = getFxVersion() == 2; + if (useGamma) { + // Automatically update version + if (m_gamma->getKeyframeCount() == 0 && + areAlmostEqual(m_gamma->getDefaultValue(), 2.2)) { + useGamma = false; + setFxVersion(3); + } + } + getParams()->getParamVar("gamma")->setIsHidden(!useGamma); + getParams()->getParamVar("gammaAdjust")->setIsHidden(useGamma); } //------------------------------------------------------------ @@ -656,14 +709,28 @@ void Iwa_MotionBlurCompFx::doCompute(TTile &tile, double frame, /* Get parameters */ QList points = getAttributes()->getMotionPoints(); - double hardness = m_hardness->getValue(frame); - double shutterStart = m_shutterStart->getValue(frame); - double shutterEnd = m_shutterEnd->getValue(frame); - int traceResolution = m_traceResolution->getValue(); - float startValue = (float)m_startValue->getValue(frame); - float startCurve = (float)m_startCurve->getValue(frame); - float endValue = (float)m_endValue->getValue(frame); - float endCurve = (float)m_endCurve->getValue(frame); + double gamma; + // The hardness value had been used inversely with the bokeh fxs. + // Now the convertion functions are shared with the bokeh fxs, + // so we need to change the hardness to reciprocal in order to obtain + // the same result as previous versions. + if (getFxVersion() == 1) + gamma = 1. / m_hardness->getValue(frame); + else { // gamma + if (getFxVersion() == 2) + gamma = m_gamma->getValue(frame); + else + gamma = std::max( + 1., settings.m_colorSpaceGamma + m_gammaAdjust->getValue(frame)); + if (tile.getRaster()->isLinear()) gamma /= settings.m_colorSpaceGamma; + } + double shutterStart = m_shutterStart->getValue(frame); + double shutterEnd = m_shutterEnd->getValue(frame); + int traceResolution = m_traceResolution->getValue(); + float startValue = (float)m_startValue->getValue(frame); + float startCurve = (float)m_startCurve->getValue(frame); + float endValue = (float)m_endValue->getValue(frame); + float endCurve = (float)m_endCurve->getValue(frame); /* Do not process if there are no more than two trajectory data */ if (points.size() < 2) { @@ -697,7 +764,7 @@ void Iwa_MotionBlurCompFx::doCompute(TTile &tile, double frame, int marginBottom = (int)ceil(std::abs(minY)); /* Return the input tile as-is if there is not movement - * (= filter margins are all 0). */ + * (= filter margins are all 0). */ if (marginLeft == 0 && marginRight == 0 && marginTop == 0 && marginBottom == 0) { if (!m_background.isConnected()) m_input->compute(tile, frame, settings); @@ -748,15 +815,15 @@ void Iwa_MotionBlurCompFx::doCompute(TTile &tile, double frame, pointsTable[p].y = (float)points.at(p).y; /* z stores the distance of p -> p + 1 vector */ if (p < pointAmount - 1) { - float2 vec = {(float)(points.at(p + 1).x - points.at(p).x), - (float)(points.at(p + 1).y - points.at(p).y)}; + float2 vec = {(float)(points.at(p + 1).x - points.at(p).x), + (float)(points.at(p + 1).y - points.at(p).y)}; pointsTable[p].z = sqrtf(vec.x * vec.x + vec.y * vec.y); } /* w stores shutter time offset */ pointsTable[p].w = -(float)shutterStart + (float)p * dt; } - doCompute_CPU(tile, frame, settings, pointsTable, pointAmount, hardness, + doCompute_CPU(tile, frame, settings, pointsTable, pointAmount, gamma, shutterStart, shutterEnd, traceResolution, startValue, startCurve, endValue, endCurve, marginLeft, marginRight, marginTop, marginBottom, enlargedDimIn, enlarge_tile, dimOut, @@ -767,7 +834,7 @@ void Iwa_MotionBlurCompFx::doCompute(TTile &tile, double frame, void Iwa_MotionBlurCompFx::doCompute_CPU( TTile &tile, double frame, const TRenderSettings &settings, - float4 *pointsTable, int pointAmount, double hardness, double shutterStart, + float4 *pointsTable, int pointAmount, double gamma, double shutterStart, double shutterEnd, int traceResolution, float startValue, float startCurve, float endValue, float endCurve, int marginLeft, int marginRight, int marginTop, int marginBottom, TDimensionI &enlargedDimIn, @@ -794,6 +861,7 @@ void Iwa_MotionBlurCompFx::doCompute_CPU( /* normalize the source image to 0 - 1 and read it into memory */ TRaster32P ras32 = (TRaster32P)enlarge_tile.getRaster(); TRaster64P ras64 = (TRaster64P)enlarge_tile.getRaster(); + TRasterFP rasF = (TRasterFP)enlarge_tile.getRaster(); if (ras32) sourceIsPremultiplied = setSourceRaster( ras32, in_tile_p, enlargedDimIn, @@ -802,6 +870,10 @@ void Iwa_MotionBlurCompFx::doCompute_CPU( sourceIsPremultiplied = setSourceRaster( ras64, in_tile_p, enlargedDimIn, (PremultiTypes)m_premultiType->getValue()); + else if (rasF) + sourceIsPremultiplied = setSourceRaster( + rasF, in_tile_p, enlargedDimIn, + (PremultiTypes)m_premultiType->getValue()); /* When afterimage mode is off */ if (!m_zanzoMode->getValue()) { @@ -824,8 +896,15 @@ void Iwa_MotionBlurCompFx::doCompute_CPU( * -> convert it to exposure value * -> premultiply again */ - convertRGBtoExposure_CPU(in_tile_p, enlargedDimIn, hardness, - sourceIsPremultiplied); + if (getFxVersion() == 1) + convertRGBtoExposure_CPU( + in_tile_p, enlargedDimIn, + HardnessBasedConverter(gamma, settings.m_colorSpaceGamma, + enlarge_tile.getRaster()->isLinear()), + sourceIsPremultiplied); + else + convertRGBtoExposure_CPU(in_tile_p, enlargedDimIn, + GammaBasedConverter(gamma), sourceIsPremultiplied); /* Filter and blur exposure value */ applyBlurFilter_CPU(in_tile_p, out_tile_p, enlargedDimIn, filter_p, filterDim, @@ -836,19 +915,33 @@ void Iwa_MotionBlurCompFx::doCompute_CPU( /* If there is a background, do Exposure multiplication */ if (m_background.isConnected()) { - composeBackgroundExposure_CPU(out_tile_p, enlargedDimIn, marginRight, - marginTop, back_tile, dimOut, - (float)hardness); + if (getFxVersion() == 1) + composeBackgroundExposure_CPU( + out_tile_p, enlargedDimIn, marginRight, marginTop, back_tile, dimOut, + HardnessBasedConverter(gamma, settings.m_colorSpaceGamma, + tile.getRaster()->isLinear())); + else + composeBackgroundExposure_CPU(out_tile_p, enlargedDimIn, marginRight, + marginTop, back_tile, dimOut, + GammaBasedConverter(gamma)); } /* Unpremultiply the exposure value * -> convert back to RGB value (0 to 1) * -> premultiply */ - convertExposureToRGB_CPU(out_tile_p, enlargedDimIn, hardness); + if (getFxVersion() == 1) + convertExposureToRGB_CPU( + out_tile_p, enlargedDimIn, + HardnessBasedConverter(gamma, settings.m_colorSpaceGamma, + tile.getRaster()->isLinear())); + else + convertExposureToRGB_CPU(out_tile_p, enlargedDimIn, + GammaBasedConverter(gamma)); /* Clear raster */ tile.getRaster()->clear(); TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); int2 margin = {marginRight, marginTop}; if (outRas32) setOutputRaster(out_tile_p, outRas32, enlargedDimIn, @@ -856,6 +949,9 @@ void Iwa_MotionBlurCompFx::doCompute_CPU( else if (outRas64) setOutputRaster(out_tile_p, outRas64, enlargedDimIn, margin); + else if (outRasF) + setOutputRaster(out_tile_p, outRasF, enlargedDimIn, + margin); /* Memory release */ out_tile_ras->unlock(); @@ -951,4 +1047,11 @@ std::string Iwa_MotionBlurCompFx::getAlias(double frame, "]"; } +//------------------------------------------------------------ + +bool Iwa_MotionBlurCompFx::toBeComputedInLinearColorSpace( + bool settingsIsLinear, bool tileIsLinear) const { + return settingsIsLinear; +} + FX_PLUGIN_IDENTIFIER(Iwa_MotionBlurCompFx, "iwa_MotionBlurCompFx") diff --git a/toonz/sources/stdfx/iwa_motionblurfx.h b/toonz/sources/stdfx/iwa_motionblurfx.h index f7a0a31..62715e6 100644 --- a/toonz/sources/stdfx/iwa_motionblurfx.h +++ b/toonz/sources/stdfx/iwa_motionblurfx.h @@ -11,6 +11,7 @@ #include "tfxparam.h" #include "stdfx.h" +#include "iwa_bokeh_util.h" // ExposureConverter #include "motionawarebasefx.h" @@ -25,9 +26,9 @@ struct float3 { struct float4 { float x, y, z, w; }; -struct int2 { - int x, y; -}; +// struct int2 { +// int x, y; +// }; /*- m_premultiTypeの取る値 -*/ enum PremultiTypes { @@ -43,7 +44,10 @@ protected: TRasterFxPort m_input; TRasterFxPort m_background; - TDoubleParamP m_hardness; /*- フィルムのガンマ値 -*/ + TDoubleParamP m_hardness; // gamma (version 1) + TDoubleParamP m_gamma; // gamma (version 2) + TDoubleParamP m_gammaAdjust; // Gamma offset from the current color space + // gamma (version 3) /*-- 左右をぼかすためのパラメータ --*/ TDoubleParamP m_startValue; /*- シャッター開け時のフィルタ値 -*/ @@ -85,7 +89,8 @@ protected: /*- RGB値(0〜1)を露光値に変換 -*/ void convertRGBtoExposure_CPU(float4 *in_tile_p, TDimensionI &dim, - float hardness, bool sourceIsPremultiplied); + const ExposureConverter &conv, + bool sourceIsPremultiplied); /*- 露光値をフィルタリングしてぼかす -*/ void applyBlurFilter_CPU(float4 *in_tile_p, float4 *out_tile_p, @@ -96,7 +101,7 @@ protected: /*- 露光値をdepremultipy→RGB値(0〜1)に戻す→premultiply -*/ void convertExposureToRGB_CPU(float4 *out_tile_p, TDimensionI &dim, - float hardness); + const ExposureConverter &conv); /*- 背景があり、前景が動かない場合、単純にOverする -*/ void composeWithNoMotion(TTile &tile, double frame, @@ -107,7 +112,7 @@ protected: TDimensionI &enlargedDimIn, int marginRight, int marginTop, TTile &back_tile, TDimensionI &dimOut, - float hardness); + const ExposureConverter &conv); public: Iwa_MotionBlurCompFx(); @@ -116,7 +121,7 @@ public: const TRenderSettings &settings) override; void doCompute_CPU(TTile &tile, double frame, const TRenderSettings &settings, - float4 *pointsTable, int pointAmount, double hardness, + float4 *pointsTable, int pointAmount, double gamma, double shutterStart, double shutterEnd, int traceResolution, float startValue, float startCurve, float endValue, float endCurve, int marginLeft, @@ -133,6 +138,10 @@ public: エイリアスは毎フレーム変える -*/ std::string getAlias(double frame, const TRenderSettings &info) const override; + void onFxVersionSet() final override; + + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override; }; #endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_perspectivedistortfx.cpp b/toonz/sources/stdfx/iwa_perspectivedistortfx.cpp index e38f043..0a5f375 100644 --- a/toonz/sources/stdfx/iwa_perspectivedistortfx.cpp +++ b/toonz/sources/stdfx/iwa_perspectivedistortfx.cpp @@ -7,25 +7,28 @@ Iwa_PerspectiveDistortFx #include "iwa_perspectivedistortfx.h" #include "tparamuiconcept.h" -//#include "ino_common.h" +// #include "ino_common.h" +#include "trop.h" namespace { -float4 getSource_CPU(float4 *source_host, TDimensionI &dim, int pos_x, - int pos_y) { +inline float4 getSource_CPU(float4 *source_host, TDimensionI &dim, int pos_x, + int pos_y) { if (pos_x < 0 || pos_x >= dim.lx || pos_y < 0 || pos_y >= dim.ly) return float4{0.0f, 0.0f, 0.0f, 0.0f}; return source_host[pos_y * dim.lx + pos_x]; } -float4 interp_CPU(float4 val1, float4 val2, float ratio) { - return float4{(1.0f - ratio) * val1.x + ratio * val2.x, - (1.0f - ratio) * val1.y + ratio * val2.y, - (1.0f - ratio) * val1.z + ratio * val2.z, - (1.0f - ratio) * val1.w + ratio * val2.w}; -} +inline float4 interp_CPU(float4 val1, float4 val2, float ratio) { + float4 ret; + ret.x = (1.0f - ratio) * val1.x + ratio * val2.x; + ret.y = (1.0f - ratio) * val1.y + ratio * val2.y; + ret.z = (1.0f - ratio) * val1.z + ratio * val2.z; + ret.w = (1.0f - ratio) * val1.w + ratio * val2.w; + return ret; } +} // namespace /*------------------------------------------------------------ 出力結果をChannel値に変換して格納 @@ -35,9 +38,6 @@ template void Iwa_PerspectiveDistortFx::setOutputRaster(float4 *srcMem, const RASTER dstRas, TDimensionI dim, int drawLevel) { - typename PIXEL::Channel halfChan = - (typename PIXEL::Channel)(PIXEL::maxChannelValue / 2); - dstRas->fill(PIXEL::Transparent); float4 *chan_p = srcMem; @@ -67,6 +67,23 @@ void Iwa_PerspectiveDistortFx::setOutputRaster(float4 *srcMem, } } +template <> +void Iwa_PerspectiveDistortFx::setOutputRaster( + float4 *srcMem, const TRasterFP dstRas, TDimensionI dim, int drawLevel) { + dstRas->fill(TPixelF::Transparent); + float4 *chan_p = srcMem; + for (int j = 0; j < drawLevel; j++) { + if (j >= dstRas->getLy()) break; + TPixelF *pix = dstRas->pixels(j); + for (int i = 0; i < dstRas->getLx(); i++, chan_p++, pix++) { + pix->r = (*chan_p).x; + pix->g = (*chan_p).y; + pix->b = (*chan_p).z; + pix->m = (*chan_p).w; + } + } +} + /*------------------------------------------------------------ ソース画像を0〜1に正規化してホストメモリに読み込む ------------------------------------------------------------*/ @@ -106,6 +123,8 @@ Iwa_PerspectiveDistortFx::Iwa_PerspectiveDistortFx() m_anchorPoint->getY()->setMeasureName("fxLength"); m_precision->setValueRange(1.0, 2.0); + + enableComputeInFloat(true); } //------------------------------------ @@ -113,7 +132,7 @@ Iwa_PerspectiveDistortFx::Iwa_PerspectiveDistortFx() bool Iwa_PerspectiveDistortFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) { if (m_source.isConnected()) { - bool ret = m_source->doGetBBox(frame, bBox, info); + bool ret = m_source->doGetBBox(frame, bBox, info); if (ret) bBox = TConsts::infiniteRectD; return ret; } @@ -171,22 +190,28 @@ void Iwa_PerspectiveDistortFx::doCompute(TTile &tile, double frame, /*- ソース画像を正規化して格納 -*/ /*- 素材を拡大させて持ち込む -*/ TDimensionI sourceDim(rectOut.getLx() * (int)tceil(precision), anchorPoint.y); - float4 *source_host; - TRasterGR8P source_host_ras(sourceDim.lx * sizeof(float4), sourceDim.ly); - source_host_ras->lock(); - source_host = (float4 *)source_host_ras->getRawData(); - { - TRenderSettings new_sets(rend_sets); - new_sets.m_affine *= TTranslation(vp.x, vp.y); - new_sets.m_affine *= TScale(precision, 1.0); - new_sets.m_affine *= TTranslation(-vp.x, -vp.y); + float4 *source_host = nullptr; + + TRenderSettings new_sets(rend_sets); + + new_sets.m_affine *= TTranslation(vp.x, vp.y); + new_sets.m_affine *= TScale(precision, 1.0); + new_sets.m_affine *= TTranslation(-vp.x, -vp.y); - double sourcePosX = offs + precision * (tile.m_pos.x - offs); + double sourcePosX = offs + precision * (tile.m_pos.x - offs); - TTile sourceTile; - m_source->allocateAndCompute(sourceTile, TPointD(sourcePosX, tile.m_pos.y), - sourceDim, tile.getRaster(), frame, new_sets); + TTile sourceTile; + m_source->allocateAndCompute(sourceTile, TPointD(sourcePosX, tile.m_pos.y), + sourceDim, tile.getRaster(), frame, new_sets); + + TRasterGR8P source_host_ras; + + if (tile.getRaster()->getPixelSize() == 4 || + tile.getRaster()->getPixelSize() == 8) { + source_host_ras = TRasterGR8P(sourceDim.lx * sizeof(float4), sourceDim.ly); + source_host_ras->lock(); + source_host = (float4 *)source_host_ras->getRawData(); /*- タイルの画像を0〜1に正規化してホストメモリに読み込む -*/ TRaster32P ras32 = (TRaster32P)sourceTile.getRaster(); @@ -195,6 +220,11 @@ void Iwa_PerspectiveDistortFx::doCompute(TTile &tile, double frame, setSourceRaster(ras32, source_host, sourceDim); else if (ras64) setSourceRaster(ras64, source_host, sourceDim); + } else if (tile.getRaster()->getPixelSize() == 16) { + source_host = + reinterpret_cast(sourceTile.getRaster()->getRawData()); + } else { + throw TRopException("unsupported input pixel type"); } TDimensionI resultDim(rectOut.getLx(), anchorPoint.y); @@ -208,17 +238,21 @@ void Iwa_PerspectiveDistortFx::doCompute(TTile &tile, double frame, source_host, result_host, sourceDim, resultDim, precision, offs); - source_host_ras->unlock(); + if (source_host_ras) source_host_ras->unlock(); /*- 出力結果をChannel値に変換して格納 -*/ TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); if (outRas32) setOutputRaster(result_host, outRas32, outDim, resultDim.ly); else if (outRas64) setOutputRaster(result_host, outRas64, outDim, resultDim.ly); + else if (outRasF) + setOutputRaster(result_host, outRasF, outDim, + resultDim.ly); result_host_ras->unlock(); } @@ -266,4 +300,9 @@ void Iwa_PerspectiveDistortFx::getParamUIs(TParamUIConcept *&concepts, //------------------------------------ +bool Iwa_PerspectiveDistortFx::toBeComputedInLinearColorSpace( + bool settingsIsLinear, bool tileIsLinear) const { + return tileIsLinear; +} + FX_PLUGIN_IDENTIFIER(Iwa_PerspectiveDistortFx, "iwa_PerspectiveDistortFx") diff --git a/toonz/sources/stdfx/iwa_perspectivedistortfx.h b/toonz/sources/stdfx/iwa_perspectivedistortfx.h index c479bbc..5f17e46 100644 --- a/toonz/sources/stdfx/iwa_perspectivedistortfx.h +++ b/toonz/sources/stdfx/iwa_perspectivedistortfx.h @@ -14,7 +14,15 @@ #include "tparamset.h" struct float4 { - float x, y, z, w; +#if defined(TNZ_MACHINE_CHANNEL_ORDER_BGRM) + float z, y, x, w; +#elif defined(TNZ_MACHINE_CHANNEL_ORDER_MBGR) + float w, x, y, x; +#elif defined(TNZ_MACHINE_CHANNEL_ORDER_RGBM) + float x, y, x, w; +#elif defined(TNZ_MACHINE_CHANNEL_ORDER_MRGB) + float w, x, y, z; +#endif }; class Iwa_PerspectiveDistortFx final : public TStandardRasterFx { @@ -55,6 +63,9 @@ public: const double offs); void getParamUIs(TParamUIConcept *&concepts, int &length) override; + + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override; }; #endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_pnperspectivefx.cpp b/toonz/sources/stdfx/iwa_pnperspectivefx.cpp index 60f2ced..66085bd 100644 --- a/toonz/sources/stdfx/iwa_pnperspectivefx.cpp +++ b/toonz/sources/stdfx/iwa_pnperspectivefx.cpp @@ -81,6 +81,26 @@ void Iwa_PNPerspectiveFx::setOutputRaster(double4 *srcMem, const RASTER dstRas, } } +template <> +void Iwa_PNPerspectiveFx::setOutputRaster( + double4 *srcMem, const TRasterFP dstRas, TDimensionI dim, int drawLevel, + const bool alp_rend_sw) { + if (alp_rend_sw) + dstRas->fill(TPixelF(0.5f, 0.5f, 0.5f, 0.5f)); + else + dstRas->fill(TPixelF(0.5f, 0.5f, 0.5f)); + double4 *chan_p = srcMem; + for (int j = 0; j < drawLevel; j++) { + TPixelF *pix = dstRas->pixels(j); + for (int i = 0; i < dstRas->getLx(); i++, chan_p++, pix++) { + pix->r = (float)(*chan_p).x; + pix->g = (float)(*chan_p).y; + pix->b = (float)(*chan_p).z; + pix->m = std::min((float)(*chan_p).w, 1.f); + } + } +} + //------------------------------------------------------------ // obtain parameters void Iwa_PNPerspectiveFx::getPNParameters(TTile &tile, double frame, @@ -243,6 +263,8 @@ Iwa_PNPerspectiveFx::Iwa_PNPerspectiveFx() m_waveHeight->setMeasureName("fxLength"); m_waveHeight->setValueRange(1.0, 100.0); m_normalize_margin->setValueRange(0.0, 3.0); + + enableComputeInFloat(true); } //------------------------------------------------------------ @@ -263,7 +285,8 @@ bool Iwa_PNPerspectiveFx::canHandle(const TRenderSettings &info, double frame) { void Iwa_PNPerspectiveFx::doCompute(TTile &tile, double frame, const TRenderSettings &settings) { - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } @@ -288,23 +311,27 @@ void Iwa_PNPerspectiveFx::doCompute(TTile &tile, double frame, out_host_ras->lock(); out_host = (double4 *)out_host_ras->getRawData(); - doCompute_CPU(tile, frame, settings, out_host, dimOut, pnParams); + doCompute_CPU(frame, settings, out_host, dimOut, pnParams); tile.getRaster()->clear(); TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); if (outRas32) setOutputRaster( out_host, outRas32, dimOut, pnParams.drawLevel, pnParams.alp_rend_sw); else if (outRas64) setOutputRaster( out_host, outRas64, dimOut, pnParams.drawLevel, pnParams.alp_rend_sw); + else if (outRasF) + setOutputRaster( + out_host, outRasF, dimOut, pnParams.drawLevel, pnParams.alp_rend_sw); out_host_ras->unlock(); } //------------------------------------------------------------ -void Iwa_PNPerspectiveFx::doCompute_CPU(TTile &tile, double frame, +void Iwa_PNPerspectiveFx::doCompute_CPU(double frame, const TRenderSettings &settings, double4 *out_host, TDimensionI &dimOut, PN_Params &pnParams) { @@ -387,13 +414,13 @@ void Iwa_PNPerspectiveFx::calcPerinNoise_CPU(double4 *out_host, double val = val_sum / (double)count; - // clamp - val = (val < 0.0) ? 0.0 : ((val > 1.0) ? 1.0 : val); - (*out_p).x = val; (*out_p).y = val; (*out_p).z = val; - (*out_p).w = (p.alp_rend_sw) ? val : 1.0; + if (p.alp_rend_sw) // clamp + (*out_p).w = (val < 0.0) ? 0.0 : ((val > 1.0) ? 1.0 : val); + else + (*out_p).w = 1.0; } } } diff --git a/toonz/sources/stdfx/iwa_pnperspectivefx.h b/toonz/sources/stdfx/iwa_pnperspectivefx.h index 7808e36..dcbeace 100644 --- a/toonz/sources/stdfx/iwa_pnperspectivefx.h +++ b/toonz/sources/stdfx/iwa_pnperspectivefx.h @@ -103,7 +103,7 @@ public: void doCompute(TTile &tile, double frame, const TRenderSettings &rend_sets) override; - void doCompute_CPU(TTile &tile, double frame, const TRenderSettings &settings, + void doCompute_CPU(double frame, const TRenderSettings &settings, double4 *out_host, TDimensionI &dimOut, PN_Params &pnParams); diff --git a/toonz/sources/stdfx/iwa_rainbowfx.cpp b/toonz/sources/stdfx/iwa_rainbowfx.cpp index 7087fa4..270ee8d 100644 --- a/toonz/sources/stdfx/iwa_rainbowfx.cpp +++ b/toonz/sources/stdfx/iwa_rainbowfx.cpp @@ -3,6 +3,7 @@ #include "iwa_xyz.h" #include "iwa_rainbow_intensity.h" #include "tparamuiconcept.h" +#include "trop.h" //-------------------------------------------------------------- double Iwa_RainbowFx::getSizePixelAmount(const double val, @@ -23,9 +24,9 @@ double Iwa_RainbowFx::getSizePixelAmount(const double val, //------------------------------------------------------------ -void Iwa_RainbowFx::buildRanbowColorMap(double3* core, double3* wide, - double intensity, double inside, - double secondary) { +void Iwa_RainbowFx::buildRainbowColorMap(double3* core, double3* wide, + double intensity, double inside, + double secondary, bool doClamp) { auto clamp01 = [](double val) { return std::min(1.0, std::max(0.0, val)); }; int mapSize[2] = {301, 91}; @@ -76,15 +77,20 @@ void Iwa_RainbowFx::buildRanbowColorMap(double3* core, double3* wide, cie_d65[ram] * data[ram] * xyz[ram * 3 + c] * inside_ratio; } double tmp_intensity = intensity * 25000.0 * second_ratio; - out_p->r = clamp01((3.240479 * xyz_sum[0] - 1.537150 * xyz_sum[1] - - 0.498535 * xyz_sum[2]) * - tmp_intensity); - out_p->g = clamp01((-0.969256 * xyz_sum[0] + 1.875992 * xyz_sum[1] + - 0.041556 * xyz_sum[2]) * - tmp_intensity); - out_p->b = clamp01((0.055648 * xyz_sum[0] - 0.204043 * xyz_sum[1] + - 1.057311f * xyz_sum[2]) * - tmp_intensity); + out_p->r = (3.240479 * xyz_sum[0] - 1.537150 * xyz_sum[1] - + 0.498535 * xyz_sum[2]) * + tmp_intensity; + out_p->g = (-0.969256 * xyz_sum[0] + 1.875992 * xyz_sum[1] + + 0.041556 * xyz_sum[2]) * + tmp_intensity; + out_p->b = (0.055648 * xyz_sum[0] - 0.204043 * xyz_sum[1] + + 1.057311f * xyz_sum[2]) * + tmp_intensity; + if (doClamp) { + out_p->r = clamp01(out_p->r); + out_p->g = clamp01(out_p->g); + out_p->b = clamp01(out_p->b); + } } } } @@ -113,6 +119,27 @@ void Iwa_RainbowFx::setOutputRaster(const RASTER ras, TDimensionI dim, } } +template <> +void Iwa_RainbowFx::setOutputRaster(const TRasterFP ras, + TDimensionI dim, + double3* outBuf_p) { + bool withAlpha = m_alpha_rendering->getValue(); + double3* out_p = outBuf_p; + for (int j = 0; j < dim.ly; j++) { + TPixelF* pix = ras->pixels(j); + for (int i = 0; i < dim.lx; i++, out_p++, pix++) { + pix->r = (float)(out_p->r); + pix->g = (float)(out_p->g); + pix->b = (float)(out_p->b); + + if (withAlpha) + pix->m = std::max(std::max(pix->r, pix->g), pix->b); + else + pix->m = 1.f; + } + } +} + //------------------------------------------------------------ Iwa_RainbowFx::Iwa_RainbowFx() @@ -123,6 +150,11 @@ Iwa_RainbowFx::Iwa_RainbowFx() , m_inside(1.0) , m_secondary_rainbow(1.0) , m_alpha_rendering(false) { + // Fx Version 1: *2.2 Gamma when linear rendering (it duplicately applies + // gamma and is not correct) Fx Version 2: *1/2.2 Gamma when non-linear + // rendering + setFxVersion(2); + bindParam(this, "center", m_center); bindParam(this, "radius", m_radius); bindParam(this, "intensity", m_intensity); @@ -136,6 +168,8 @@ Iwa_RainbowFx::Iwa_RainbowFx() m_inside->setValueRange(0.0, 1.0); m_secondary_rainbow->setValueRange(0.0, 10.0); m_width_scale->setValueRange(0.1, 50.0); + + enableComputeInFloat(true); } //------------------------------------------------------------ @@ -190,8 +224,11 @@ void Iwa_RainbowFx::doCompute(TTile& tile, double frame, double intensity = m_intensity->getValue(frame); double inside = m_inside->getValue(frame); double secondary = m_secondary_rainbow->getValue(frame); - buildRanbowColorMap(rainbowColorCore_p, rainbowColorWide_p, intensity, inside, - secondary); + + bool doClamp = (tile.getRaster()->getPixelSize() != 16); + + buildRainbowColorMap(rainbowColorCore_p, rainbowColorWide_p, intensity, + inside, secondary, doClamp); // convert center position to render region coordinate TAffine aff = ri.m_affine; @@ -232,10 +269,22 @@ void Iwa_RainbowFx::doCompute(TTile& tile, double frame, tile.getRaster()->clear(); TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); if (outRas32) setOutputRaster(outRas32, dimOut, outBuf_p); else if (outRas64) setOutputRaster(outRas64, dimOut, outBuf_p); + else if (outRasF) + setOutputRaster(outRasF, dimOut, outBuf_p); + + // modify gamma + if (getFxVersion() == 1 && tile.getRaster()->isLinear()) { + tile.getRaster()->setLinear(false); + TRop::toLinearRGB(tile.getRaster(), ri.m_colorSpaceGamma); + } else if (getFxVersion() >= 2 && !tile.getRaster()->isLinear()) { + tile.getRaster()->setLinear(true); + TRop::tosRGB(tile.getRaster(), ri.m_colorSpaceGamma); + } outBuf_ras->unlock(); } @@ -261,6 +310,13 @@ void Iwa_RainbowFx::getParamUIs(TParamUIConcept*& concepts, int& length) { concepts[2].m_params.push_back(m_center); } +//------------------------------------------------------------ + +bool Iwa_RainbowFx::toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const { + return settingsIsLinear; +} + //============================================================================== FX_PLUGIN_IDENTIFIER(Iwa_RainbowFx, "iwa_RainbowFx"); diff --git a/toonz/sources/stdfx/iwa_rainbowfx.h b/toonz/sources/stdfx/iwa_rainbowfx.h index 3547e98..6b52100 100644 --- a/toonz/sources/stdfx/iwa_rainbowfx.h +++ b/toonz/sources/stdfx/iwa_rainbowfx.h @@ -23,8 +23,8 @@ class Iwa_RainbowFx final : public TStandardZeraryFx { TBoolParamP m_alpha_rendering; double getSizePixelAmount(const double val, const TAffine affine); - void buildRanbowColorMap(double3 *core, double3 *wide, double intensity, - double inside, double secondary); + void buildRainbowColorMap(double3 *core, double3 *wide, double intensity, + double inside, double secondary, bool doClamp); inline double3 angleToColor(double angle, double3 *core, double3 *wide); template @@ -40,6 +40,9 @@ public: const TRenderSettings &ri) override; void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override; void getParamUIs(TParamUIConcept *&concepts, int &length) override; + + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override; }; #endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_soapbubblefx.cpp b/toonz/sources/stdfx/iwa_soapbubblefx.cpp index eb189c8..eb84976 100644 --- a/toonz/sources/stdfx/iwa_soapbubblefx.cpp +++ b/toonz/sources/stdfx/iwa_soapbubblefx.cpp @@ -64,6 +64,188 @@ static float* dt(float* f, int n, float a = 1.0f) { //------------------------------------ +template +void Iwa_SoapBubbleFx::convertToRaster(const RASTER ras, float* thickness_map_p, + float* depth_map_p, float* alpha_map_p, + TDimensionI dim, float3* bubbleColor_p) { + int renderMode = m_renderMode->getValue(); + float* depth_p = depth_map_p; + float* thickness_p = thickness_map_p; + float* alpha_p = alpha_map_p; + for (int j = 0; j < dim.ly; j++) { + PIXEL* pix = ras->pixels(j); + for (int i = 0; i < dim.lx; + i++, depth_p++, thickness_p++, alpha_p++, pix++) { + float alpha = (*alpha_p); + if (!m_fit_thickness->getValue()) + alpha *= (float)pix->m / (float)PIXEL::maxChannelValue; + if (alpha == 0.0f) { /* no change for the transparent pixels */ + pix->m = (typename PIXEL::Channel)0; + continue; + } + + // thickness and depth render mode + if (renderMode != RENDER_MODE_BUBBLE) { + float val = alpha * (float)PIXEL::maxChannelValue + 0.5f; + pix->m = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + float mapVal = + (renderMode == RENDER_MODE_THICKNESS) ? (*thickness_p) : (*depth_p); + val = alpha * mapVal * (float)PIXEL::maxChannelValue + 0.5f; + typename PIXEL::Channel chanVal = + (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + pix->r = chanVal; + pix->g = chanVal; + pix->b = chanVal; + continue; + } + + float coordinate[2]; + coordinate[0] = 256.0f * std::min(1.0f, *depth_p); + coordinate[1] = 256.0f * std::min(1.0f, *thickness_p); + + int neighbors[2][2]; + + /* interpolate sampling */ + if (coordinate[0] <= 0.5f) + neighbors[0][0] = 0; + else + neighbors[0][0] = (int)std::floor(coordinate[0] - 0.5f); + if (coordinate[0] >= 255.5f) + neighbors[0][1] = 255; + else + neighbors[0][1] = (int)std::floor(coordinate[0] + 0.5f); + if (coordinate[1] <= 0.5f) + neighbors[1][0] = 0; + else + neighbors[1][0] = (int)std::floor(coordinate[1] - 0.5f); + if (coordinate[1] >= 255.5f) + neighbors[1][1] = 255; + else + neighbors[1][1] = (int)std::floor(coordinate[1] + 0.5f); + + float interp_ratio[2]; + interp_ratio[0] = coordinate[0] - 0.5f - std::floor(coordinate[0] - 0.5f); + interp_ratio[1] = coordinate[1] - 0.5f - std::floor(coordinate[1] - 0.5f); + + float3 nColors[4] = { + bubbleColor_p[neighbors[0][0] * 256 + neighbors[1][0]], + bubbleColor_p[neighbors[0][1] * 256 + neighbors[1][0]], + bubbleColor_p[neighbors[0][0] * 256 + neighbors[1][1]], + bubbleColor_p[neighbors[0][1] * 256 + neighbors[1][1]]}; + + float3 color = + nColors[0] * (1.0f - interp_ratio[0]) * (1.0f - interp_ratio[1]) + + nColors[1] * interp_ratio[0] * (1.0f - interp_ratio[1]) + + nColors[2] * (1.0f - interp_ratio[0]) * interp_ratio[1] + + nColors[3] * interp_ratio[0] * interp_ratio[1]; + + /* clamp */ + float val = alpha * (float)PIXEL::maxChannelValue + 0.5f; + pix->m = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = alpha * color.x * (float)PIXEL::maxChannelValue + 0.5f; + pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = alpha * color.y * (float)PIXEL::maxChannelValue + 0.5f; + pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = alpha * color.z * (float)PIXEL::maxChannelValue + 0.5f; + pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + } + } +} + +// specialization for floating point +template <> +void Iwa_SoapBubbleFx::convertToRaster( + const TRasterFP ras, float* thickness_map_p, float* depth_map_p, + float* alpha_map_p, TDimensionI dim, float3* bubbleColor_p) { + int renderMode = m_renderMode->getValue(); + float* depth_p = depth_map_p; + float* thickness_p = thickness_map_p; + float* alpha_p = alpha_map_p; + for (int j = 0; j < dim.ly; j++) { + TPixelF* pix = ras->pixels(j); + for (int i = 0; i < dim.lx; + i++, depth_p++, thickness_p++, alpha_p++, pix++) { + float alpha = (*alpha_p); + if (!m_fit_thickness->getValue()) alpha *= pix->m; + if (alpha == 0.f) { /* no change for the transparent pixels */ + pix->m = 0.f; + continue; + } + + // thickness and depth render mode + if (renderMode != RENDER_MODE_BUBBLE) { + pix->m = std::min(alpha, 1.f); + float mapVal = + (renderMode == RENDER_MODE_THICKNESS) ? (*thickness_p) : (*depth_p); + float chanVal = alpha * mapVal; + pix->r = chanVal; + pix->g = chanVal; + pix->b = chanVal; + continue; + } + + float coordinate[2]; + coordinate[0] = 256.0f * std::min(1.0f, *depth_p); + coordinate[1] = 256.0f * std::min(1.0f, *thickness_p); + + int neighbors[2][2]; + + /* interpolate sampling */ + if (coordinate[0] <= 0.5f) + neighbors[0][0] = 0; + else + neighbors[0][0] = (int)std::floor(coordinate[0] - 0.5f); + if (coordinate[0] >= 255.5f) + neighbors[0][1] = 255; + else + neighbors[0][1] = (int)std::floor(coordinate[0] + 0.5f); + if (coordinate[1] <= 0.5f) + neighbors[1][0] = 0; + else + neighbors[1][0] = (int)std::floor(coordinate[1] - 0.5f); + if (coordinate[1] >= 255.5f) + neighbors[1][1] = 255; + else + neighbors[1][1] = (int)std::floor(coordinate[1] + 0.5f); + + float interp_ratio[2]; + interp_ratio[0] = coordinate[0] - 0.5f - std::floor(coordinate[0] - 0.5f); + interp_ratio[1] = coordinate[1] - 0.5f - std::floor(coordinate[1] - 0.5f); + + float3 nColors[4] = { + bubbleColor_p[neighbors[0][0] * 256 + neighbors[1][0]], + bubbleColor_p[neighbors[0][1] * 256 + neighbors[1][0]], + bubbleColor_p[neighbors[0][0] * 256 + neighbors[1][1]], + bubbleColor_p[neighbors[0][1] * 256 + neighbors[1][1]]}; + + float3 color = + nColors[0] * (1.0f - interp_ratio[0]) * (1.0f - interp_ratio[1]) + + nColors[1] * interp_ratio[0] * (1.0f - interp_ratio[1]) + + nColors[2] * (1.0f - interp_ratio[0]) * interp_ratio[1] + + nColors[3] * interp_ratio[0] * interp_ratio[1]; + + /* clamp */ + pix->m = std::min(alpha, 1.f); + pix->r = alpha * color.x; + pix->g = alpha * color.y; + pix->b = alpha * color.z; + } + } +} +//------------------------------------ + Iwa_SoapBubbleFx::Iwa_SoapBubbleFx() : Iwa_SpectrumFx() , m_renderMode(new TIntEnumParam(RENDER_MODE_BUBBLE, "Bubble")) @@ -149,7 +331,7 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame, allocatedRasList.append(bubbleColor_ras); float3* bubbleColor_p = (float3*)bubbleColor_ras->getRawData(); if (m_renderMode->getValue() == RENDER_MODE_BUBBLE) - calcBubbleMap(bubbleColor_p, frame, true); + calcBubbleMap(bubbleColor_p, frame, tile.getRaster()->isLinear(), true); if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; @@ -186,6 +368,7 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame, TRaster32P depthRas32 = (TRaster32P)depthRas; TRaster64P depthRas64 = (TRaster64P)depthRas; + TRasterFP depthRasF = (TRasterFP)depthRas; { if (depthRas32) convertToBrightness(depthRas32, depth_map_p, @@ -193,6 +376,9 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame, else if (depthRas64) convertToBrightness(depthRas64, depth_map_p, alpha_map_p, dim); + else if (depthRasF) + convertToBrightness(depthRasF, depth_map_p, + alpha_map_p, dim); } depthRas->unlock(); @@ -227,6 +413,7 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame, TRasterP tileRas = tile.getRaster(); TRaster32P ras32 = (TRaster32P)tileRas; TRaster64P ras64 = (TRaster64P)tileRas; + TRasterFP rasF = (TRasterFP)tileRas; if (m_fit_thickness->getValue()) { // Get the original bbox of thickness image @@ -260,6 +447,9 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame, else if (ras64) convertToBrightness(ras64, thickness_map_p, nullptr, dim); + else if (rasF) + convertToBrightness(rasF, thickness_map_p, nullptr, + dim); } if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; @@ -275,6 +465,9 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame, else if (ras64) convertToRaster(ras64, thickness_map_p, depth_map_p, alpha_map_p, dim, bubbleColor_p); + else if (rasF) + convertToRaster(rasF, thickness_map_p, depth_map_p, + alpha_map_p, dim, bubbleColor_p); for (int i = 0; i < allocatedRasList.size(); i++) allocatedRasList.at(i)->unlock(); @@ -295,6 +488,8 @@ void Iwa_SoapBubbleFx::convertToBrightness(const RASTER srcRas, float* dst, float b = (float)pix->b / (float)PIXEL::maxChannelValue; /* brightness */ *dst_p = 0.298912f * r + 0.586611f * g + 0.114478f * b; + // clamp 0.f to 1.f in case computing HDR + *dst_p = std::min(1.f, std::max(0.f, *dst_p)); if (alpha) { *alpha_p = (float)pix->m / (float)PIXEL::maxChannelValue; alpha_p++; @@ -305,108 +500,6 @@ void Iwa_SoapBubbleFx::convertToBrightness(const RASTER srcRas, float* dst, //------------------------------------ -template -void Iwa_SoapBubbleFx::convertToRaster(const RASTER ras, float* thickness_map_p, - float* depth_map_p, float* alpha_map_p, - TDimensionI dim, float3* bubbleColor_p) { - int renderMode = m_renderMode->getValue(); - float* depth_p = depth_map_p; - float* thickness_p = thickness_map_p; - float* alpha_p = alpha_map_p; - for (int j = 0; j < dim.ly; j++) { - PIXEL* pix = ras->pixels(j); - for (int i = 0; i < dim.lx; - i++, depth_p++, thickness_p++, alpha_p++, pix++) { - float alpha = (*alpha_p); - if (!m_fit_thickness->getValue()) - alpha *= (float)pix->m / (float)PIXEL::maxChannelValue; - if (alpha == 0.0f) { /* no change for the transparent pixels */ - pix->m = (typename PIXEL::Channel)0; - continue; - } - - // thickness and depth render mode - if (renderMode != RENDER_MODE_BUBBLE) { - float val = alpha * (float)PIXEL::maxChannelValue + 0.5f; - pix->m = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - float mapVal = - (renderMode == RENDER_MODE_THICKNESS) ? (*thickness_p) : (*depth_p); - val = alpha * mapVal * (float)PIXEL::maxChannelValue + 0.5f; - typename PIXEL::Channel chanVal = - (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - pix->r = chanVal; - pix->g = chanVal; - pix->b = chanVal; - continue; - } - - float coordinate[2]; - coordinate[0] = 256.0f * std::min(1.0f, *depth_p); - coordinate[1] = 256.0f * std::min(1.0f, *thickness_p); - - int neighbors[2][2]; - - /* interpolate sampling */ - if (coordinate[0] <= 0.5f) - neighbors[0][0] = 0; - else - neighbors[0][0] = (int)std::floor(coordinate[0] - 0.5f); - if (coordinate[0] >= 255.5f) - neighbors[0][1] = 255; - else - neighbors[0][1] = (int)std::floor(coordinate[0] + 0.5f); - if (coordinate[1] <= 0.5f) - neighbors[1][0] = 0; - else - neighbors[1][0] = (int)std::floor(coordinate[1] - 0.5f); - if (coordinate[1] >= 255.5f) - neighbors[1][1] = 255; - else - neighbors[1][1] = (int)std::floor(coordinate[1] + 0.5f); - - float interp_ratio[2]; - interp_ratio[0] = coordinate[0] - 0.5f - std::floor(coordinate[0] - 0.5f); - interp_ratio[1] = coordinate[1] - 0.5f - std::floor(coordinate[1] - 0.5f); - - float3 nColors[4] = { - bubbleColor_p[neighbors[0][0] * 256 + neighbors[1][0]], - bubbleColor_p[neighbors[0][1] * 256 + neighbors[1][0]], - bubbleColor_p[neighbors[0][0] * 256 + neighbors[1][1]], - bubbleColor_p[neighbors[0][1] * 256 + neighbors[1][1]]}; - - float3 color = - nColors[0] * (1.0f - interp_ratio[0]) * (1.0f - interp_ratio[1]) + - nColors[1] * interp_ratio[0] * (1.0f - interp_ratio[1]) + - nColors[2] * (1.0f - interp_ratio[0]) * interp_ratio[1] + - nColors[3] * interp_ratio[0] * interp_ratio[1]; - - /* clamp */ - float val = alpha * (float)PIXEL::maxChannelValue + 0.5f; - pix->m = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - val = alpha * color.x * (float)PIXEL::maxChannelValue + 0.5f; - pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - val = alpha * color.y * (float)PIXEL::maxChannelValue + 0.5f; - pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - val = alpha * color.z * (float)PIXEL::maxChannelValue + 0.5f; - pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - } - } -} - -//------------------------------------ - void Iwa_SoapBubbleFx::processShape(double frame, TTile& shape_tile, float* depth_map_p, float* alpha_map_p, USHORT* regionIds_p, diff --git a/toonz/sources/stdfx/iwa_spectrumfx.cpp b/toonz/sources/stdfx/iwa_spectrumfx.cpp index 74bcb46..2475717 100644 --- a/toonz/sources/stdfx/iwa_spectrumfx.cpp +++ b/toonz/sources/stdfx/iwa_spectrumfx.cpp @@ -16,6 +16,7 @@ const float PI = 3.14159265f; Calculate soap bubble color map ------------------------------------*/ void Iwa_SpectrumFx::calcBubbleMap(float3 *bubbleColor, double frame, + bool isLinear, double colorSpaceGamma, bool computeAngularAxis) { int i, j, k; /* bubbleColor[j][k] = [256][3] */ float d; /* Thickness of the film (μm) */ @@ -36,9 +37,18 @@ void Iwa_SpectrumFx::calcBubbleMap(float3 *bubbleColor, double frame, float refractiveIndex = (float)m_refractiveIndex->getValue(frame); float thickMax = (float)m_thickMax->getValue(frame); float thickMin = (float)m_thickMin->getValue(frame); - float rgbGamma[3] = {(float)m_RGamma->getValue(frame), - (float)m_GGamma->getValue(frame), - (float)m_BGamma->getValue(frame)}; + + // Currently isLinear should be always false + assert(!isLinear); + float gammaAdjust = (getFxVersion() == 1 && isLinear) ? (float)colorSpaceGamma + : (getFxVersion() >= 2 && !isLinear) + ? 1.f / (float)colorSpaceGamma + : 1.f; + + float rgbGamma[3] = {(float)m_RGamma->getValue(frame) * gammaAdjust, + (float)m_GGamma->getValue(frame) * gammaAdjust, + (float)m_BGamma->getValue(frame) * gammaAdjust}; + float lensFactor = (float)m_lensFactor->getValue(frame); float shift = (float)m_spectrumShift->getValue(frame); float fadeWidth = (float)m_loopSpectrumFadeWidth->getValue(frame) / 2.0f; @@ -182,7 +192,8 @@ void Iwa_SpectrumFx::calcBubbleMap(float3 *bubbleColor, double frame, /* gamma adjustment */ tmp_rgb[fadeId][k] = powf((tmp_rgb[fadeId][k] / 255.0f), rgbGamma[k]); - if (tmp_rgb[fadeId][k] >= 1.0f) tmp_rgb[fadeId][k] = 1.0f; + // clamp will be done when storing the result raster + // if (tmp_rgb[fadeId][k] >= 1.0f) tmp_rgb[fadeId][k] = 1.0f; } } bubble_p->x = tmp_rgb[0][0] * tmp_ratio[0] + tmp_rgb[1][0] * tmp_ratio[1]; @@ -208,6 +219,11 @@ Iwa_SpectrumFx::Iwa_SpectrumFx() , m_lightIntensity(1.0) , m_loopSpectrumFadeWidth(0.0) , m_spectrumShift(0.0) { + // Fx Version 1: *2.2 Gamma when linear rendering (it duplicately applies + // gamma and is not correct) Fx Version 2: *1/2.2 Gamma when non-linear + // rendering + setFxVersion(2); + addInputPort("Source", m_input); addInputPort("Light", m_light); bindParam(this, "intensity", m_intensity); @@ -227,14 +243,16 @@ Iwa_SpectrumFx::Iwa_SpectrumFx() m_refractiveIndex->setValueRange(1.0, 3.0); m_thickMax->setValueRange(-1.5, 2.0); m_thickMin->setValueRange(-1.5, 2.0); - m_RGamma->setValueRange(0.001, 1.0); - m_GGamma->setValueRange(0.001, 1.0); - m_BGamma->setValueRange(0.001, 1.0); + m_RGamma->setValueRange(0.001, 5.0); + m_GGamma->setValueRange(0.001, 5.0); + m_BGamma->setValueRange(0.001, 5.0); m_lensFactor->setValueRange(0.01, 10.0); m_lightThres->setValueRange(-5.0, 1.0); m_lightIntensity->setValueRange(0.0, 1.0); m_loopSpectrumFadeWidth->setValueRange(0.0, 1.0); m_spectrumShift->setValueRange(-10.0, 10.0); + + enableComputeInFloat(true); } //------------------------------------ @@ -253,7 +271,8 @@ void Iwa_SpectrumFx::doCompute(TTile &tile, double frame, bubbleColor = (float3 *)bubbleColor_ras->getRawData(); /*- シャボン色マップの生成 -*/ - calcBubbleMap(bubbleColor, frame); + calcBubbleMap(bubbleColor, frame, tile.getRaster()->isLinear(), + settings.m_colorSpaceGamma); /*- いったん素材をTileに収める -*/ m_input->compute(tile, frame, settings); @@ -273,6 +292,7 @@ void Iwa_SpectrumFx::doCompute(TTile &tile, double frame, TRaster32P ras32 = (TRaster32P)tile.getRaster(); TRaster64P ras64 = (TRaster64P)tile.getRaster(); + TRasterFP rasF = (TRasterFP)tile.getRaster(); { if (ras32) { if (lightRas) @@ -290,11 +310,19 @@ void Iwa_SpectrumFx::doCompute(TTile &tile, double frame, (float)m_lightIntensity->getValue(frame)); else convertRaster(ras64, dim, bubbleColor); + } else if (rasF) { + if (lightRas) + convertRasterWithLight( + rasF, dim, bubbleColor, (TRasterFP)lightRas, + (float)m_lightThres->getValue(frame), + (float)m_lightIntensity->getValue(frame)); + else + convertRaster(rasF, dim, bubbleColor); } } - //メモリ解放 - // brightness_ras->unlock(); + // メモリ解放 + // brightness_ras->unlock(); bubbleColor_ras->unlock(); if (lightRas) lightRas->unlock(); } @@ -306,6 +334,9 @@ void Iwa_SpectrumFx::convertRaster(const RASTER ras, TDimensionI dim, float rr, gg, bb, aa; float spec_r, spec_g, spec_b; float brightness; + + bool doClamp = (ras->getPixelSize() != 16); + for (int j = 0; j < dim.ly; j++) { PIXEL *pix = ras->pixels(j); for (int i = 0; i < dim.lx; i++) { @@ -344,20 +375,26 @@ void Iwa_SpectrumFx::convertRaster(const RASTER ras, TDimensionI dim, spec_b *= aa; } /*- 元のピクセルに書き戻す -*/ - float val; - /*- チャンネル範囲にクランプ -*/ - val = spec_r * (float)PIXEL::maxChannelValue + 0.5f; - pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - val = spec_g * (float)PIXEL::maxChannelValue + 0.5f; - pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - val = spec_b * (float)PIXEL::maxChannelValue + 0.5f; - pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); + if (doClamp) { + float val; + /*- チャンネル範囲にクランプ -*/ + val = spec_r * (float)PIXEL::maxChannelValue + 0.5f; + pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = spec_g * (float)PIXEL::maxChannelValue + 0.5f; + pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = spec_b * (float)PIXEL::maxChannelValue + 0.5f; + pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + } else { // floating point case + pix->r = spec_r; + pix->g = spec_g; + pix->b = spec_b; + } pix++; } @@ -374,6 +411,9 @@ void Iwa_SpectrumFx::convertRasterWithLight(const RASTER ras, TDimensionI dim, float rr, gg, bb, aa; float spec_r, spec_g, spec_b; float brightness; + + bool doClamp = (ras->getPixelSize() != 16); + for (int j = 0; j < dim.ly; j++) { PIXEL *light_pix = lightRas->pixels(j); PIXEL *pix = ras->pixels(j); @@ -435,21 +475,26 @@ void Iwa_SpectrumFx::convertRasterWithLight(const RASTER ras, TDimensionI dim, spec_b *= aa; /*- 元のピクセルに書き戻す -*/ - float val; - /*- チャンネル範囲にクランプ -*/ - val = spec_r * (float)PIXEL::maxChannelValue + 0.5f; - pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - val = spec_g * (float)PIXEL::maxChannelValue + 0.5f; - pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - val = spec_b * (float)PIXEL::maxChannelValue + 0.5f; - pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) - ? (float)PIXEL::maxChannelValue - : val); - + if (doClamp) { + float val; + /*- チャンネル範囲にクランプ -*/ + val = spec_r * (float)PIXEL::maxChannelValue + 0.5f; + pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = spec_g * (float)PIXEL::maxChannelValue + 0.5f; + pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = spec_b * (float)PIXEL::maxChannelValue + 0.5f; + pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + } else { // floating point case + pix->r = spec_r; + pix->g = spec_g; + pix->b = spec_b; + } pix->m = light_pix->m; pix++; diff --git a/toonz/sources/stdfx/iwa_spectrumfx.h b/toonz/sources/stdfx/iwa_spectrumfx.h index 6be4562..08754f6 100644 --- a/toonz/sources/stdfx/iwa_spectrumfx.h +++ b/toonz/sources/stdfx/iwa_spectrumfx.h @@ -43,8 +43,8 @@ protected: TDoubleParamP m_lightIntensity; /*- シャボン色マップの生成 -*/ - void calcBubbleMap(float3 *bubbleColor, double frame, - bool computeAngularAxis = false); + void calcBubbleMap(float3 *bubbleColor, double frame, bool isLinear, + double colorSpaceGamma, bool computeAngularAxis = false); template void convertRaster(const RASTER ras, TDimensionI dim, float3 *bubbleColor); diff --git a/toonz/sources/stdfx/iwa_spingradientfx.cpp b/toonz/sources/stdfx/iwa_spingradientfx.cpp index 812be16..07819dd 100644 --- a/toonz/sources/stdfx/iwa_spingradientfx.cpp +++ b/toonz/sources/stdfx/iwa_spingradientfx.cpp @@ -38,6 +38,8 @@ Iwa_SpinGradientFx::Iwa_SpinGradientFx() bindParam(this, "startColor", m_startColor); bindParam(this, "endColor", m_endColor); + + enableComputeInFloat(true); } //------------------------------------------------------------ @@ -109,7 +111,8 @@ void doSpinGradientT(RASTER ras, TDimensionI dim, TPointD centerPos, void Iwa_SpinGradientFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { - if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) { + if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster()) && + !((TRasterFP)tile.getRaster())) { throw TRopException("unsupported input pixel type"); } @@ -142,6 +145,7 @@ void Iwa_SpinGradientFx::doCompute(TTile &tile, double frame, tile.getRaster()->clear(); TRaster32P outRas32 = (TRaster32P)tile.getRaster(); TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + TRasterFP outRasF = (TRasterFP)tile.getRaster(); if (outRas32) doSpinGradientT( outRas32, dimOut, centerPos, startAngle, endAngle, @@ -151,6 +155,10 @@ void Iwa_SpinGradientFx::doCompute(TTile &tile, double frame, outRas64, dimOut, centerPos, startAngle, endAngle, m_colors->getValue64(frame), (GradientCurveType)m_curveType->getValue()); + else if (outRasF) + doSpinGradientT( + outRasF, dimOut, centerPos, startAngle, endAngle, + m_colors->getValueF(frame), (GradientCurveType)m_curveType->getValue()); } //------------------------------------------------------------ diff --git a/toonz/sources/stdfx/iwa_tilefx.cpp b/toonz/sources/stdfx/iwa_tilefx.cpp index 5daa1cc..1babe4c 100644 --- a/toonz/sources/stdfx/iwa_tilefx.cpp +++ b/toonz/sources/stdfx/iwa_tilefx.cpp @@ -48,6 +48,11 @@ public: bool checkIfThisTileShouldBeComptedOrNot(int horizIndex, int vertIndex); bool isInRange(int quantityMode, int index); + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override { + return tileIsLinear; + } + private: void makeTile(const TTile &inputTile, const TTile &tile); }; @@ -97,6 +102,8 @@ Iwa_TileFx::Iwa_TileFx() bindParam(this, "vMargin", m_vmargin); m_vmargin->setMeasureName("fxLength"); + + enableComputeInFloat(true); } //------------------------------------------------------------------------------ diff --git a/toonz/sources/stdfx/kaleido.cpp b/toonz/sources/stdfx/kaleido.cpp index 10646fb..ffbb974 100644 --- a/toonz/sources/stdfx/kaleido.cpp +++ b/toonz/sources/stdfx/kaleido.cpp @@ -84,6 +84,7 @@ public: addInputPort("Source", m_input); m_count->setValueRange(1, 100); + enableComputeInFloat(true); } ~KaleidoFx(){}; @@ -101,6 +102,11 @@ public: return isAlmostIsotropic(info.m_affine); } + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override { + return tileIsLinear; + } + private: void buildSectionRect(TRectD &inRect, double angle); void rotate(TRectD &rect); diff --git a/toonz/sources/stdfx/localtransparencyfx.cpp b/toonz/sources/stdfx/localtransparencyfx.cpp index f978700..7d3f689 100644 --- a/toonz/sources/stdfx/localtransparencyfx.cpp +++ b/toonz/sources/stdfx/localtransparencyfx.cpp @@ -2,9 +2,9 @@ #include "stdfx.h" #include "tfxparam.h" -//#include "trop.h" +// #include "trop.h" #include "tdoubleparam.h" -//#include "tnotanimatableparam.h" +// #include "tnotanimatableparam.h" #include "tpixelgr.h" #include "trasterfx.h" @@ -26,7 +26,7 @@ inline Status operator|(const Status &l, const Status &r) { } inline int operator&(const Status &l) { return int(l); } Status getFxStatus(const TRasterFxPort &port0, const TRasterFxPort &port1) { - Status status = StatusGood; + Status status = StatusGood; if (!port0.isConnected()) status = status | Port0NotConnected; if (!port1.isConnected()) status = status | Port1NotConnected; return status; @@ -87,6 +87,57 @@ void doLocalTransparency(TRasterPT out, TRasterPT src, TRasterPT ref, src->unlock(); } +template <> +void doLocalTransparency(TRasterFP out, + TRasterFP src, + TRasterFP ref, + double transp) { + out->lock(); + ref->lock(); + src->lock(); + + TPixelF *outRow = out->pixels(); + TPixelF *refRow = ref->pixels(); + TPixelF *srcRow = src->pixels(); + int outWrap = out->getWrap(); + int refWrap = ref->getWrap(); + int srcWrap = src->getWrap(); + + TPixelF *outPix = outRow; + TPixelF *srcPix = srcRow; + TPixelF *refPix = refRow; + TPixelF *lastPix = outRow + outWrap * out->getLy(); + + while (outPix < lastPix) { + TPixelF *endPix = outPix + out->getLx(); + while (outPix < endPix) { + // clamp 0.f to 1.f in case computing HDR + float refv = + std::min(1.f, std::max(0.f, (TPixelGRF::from(*refPix).value))); + float local_transp = 1.f - refv * transp; + if (local_transp > 0.f) { + outPix->r = local_transp * srcPix->r; + outPix->g = local_transp * srcPix->g; + outPix->b = local_transp * srcPix->b; + outPix->m = local_transp * srcPix->m; + } else { + outPix->r = outPix->g = outPix->b = outPix->m = 0.f; + } + ++outPix; + ++refPix; + ++srcPix; + } + srcRow += srcWrap; + outRow += outWrap; + refRow += refWrap; + srcPix = srcRow; + outPix = outRow; + refPix = refRow; + } + out->unlock(); + ref->unlock(); + src->unlock(); +} //------------------------------------------------------------------- inline double func(int x, int y, int lx, int ly) { @@ -109,7 +160,7 @@ void drawCheckboard(TRaster32P &raster) { } raster->unlock(); } -}; +}; // namespace //------------------------------------------------------------------- @@ -125,6 +176,7 @@ public: addInputPort("Reference", m_ref); bindParam(this, "value", m_value); m_value->setValueRange(0, 100); + enableComputeInFloat(true); } virtual ~LocalTransparencyFx() {} @@ -150,19 +202,28 @@ public: // TRaster32P ref32 (refTile.getRaster()); TRaster32P out32(tile.getRaster()); TRaster32P src32(srcTile.getRaster()); - if (out32 && src32) + if (out32 && src32) { doLocalTransparency( out32, src32, out32, m_value->getValue(frame) / 100.); - else { - // TRaster64P ref64(refTile.getRaster()); - TRaster64P out64(tile.getRaster()); - TRaster64P src64(srcTile.getRaster()); - if (out64 && src64) - doLocalTransparency( - out64, src64, out64, m_value->getValue(frame) / 100.); - else - throw TException("LocalTransparencyFx: unsupported raster type"); + return; } + // TRaster64P ref64(refTile.getRaster()); + TRaster64P out64(tile.getRaster()); + TRaster64P src64(srcTile.getRaster()); + if (out64 && src64) { + doLocalTransparency( + out64, src64, out64, m_value->getValue(frame) / 100.); + return; + } + TRasterFP outF(tile.getRaster()); + TRasterFP srcF(srcTile.getRaster()); + if (outF && srcF) { + doLocalTransparency( + outF, srcF, outF, m_value->getValue(frame) / 100.); + return; + } + + throw TException("LocalTransparencyFx: unsupported raster type"); } bool checkBeforeCompute(TTile &tile, double frame, diff --git a/toonz/sources/stdfx/motionblurfx.cpp b/toonz/sources/stdfx/motionblurfx.cpp index 613b149..957fdc3 100644 --- a/toonz/sources/stdfx/motionblurfx.cpp +++ b/toonz/sources/stdfx/motionblurfx.cpp @@ -12,7 +12,7 @@ #include "tgeometry.h" #include "tfxattributes.h" #include "tparamuiconcept.h" -//#include "timage_io.h" +// #include "timage_io.h" /*---------------------------------------------------------------------------*/ namespace { @@ -165,6 +165,114 @@ inline void blur_code(T *row1, T *row2, int length, double coeff, double coeffq, } } +template <> +inline void blur_code(TPixelF *row1, TPixelF *row2, int length, + double coeff, double coeffq, int brad, + double diff, double globmatte) { + int i; + double rsum, gsum, bsum, msum; + + TPixelD sigma1, sigma2, sigma3, desigma; + TPixelF *pix1, *pix2, *pix3, *pix4; + // int max = T::maxChannelValue; + + pix1 = row1; + pix2 = row1 - 1; + + sigma1.r = pix1->r; + sigma1.g = pix1->g; + sigma1.b = pix1->b; + sigma1.m = pix1->m; + pix1++; + + sigma2.r = sigma2.g = sigma2.b = sigma2.m = 0.0; + sigma3.r = sigma3.g = sigma3.b = sigma3.m = 0.0; + + for (i = 1; i < brad; i++) { + sigma1.r += pix1->r; + sigma1.g += pix1->g; + sigma1.b += pix1->b; + sigma1.m += pix1->m; + + sigma2.r += pix2->r; + sigma2.g += pix2->g; + sigma2.b += pix2->b; + sigma2.m += pix2->m; + + sigma3.r += i * (pix1->r + pix2->r); + sigma3.g += i * (pix1->g + pix2->g); + sigma3.b += i * (pix1->b + pix2->b); + sigma3.m += i * (pix1->m + pix2->m); + + pix1++; + pix2--; + } + + rsum = (sigma1.r + sigma2.r) * coeff - sigma3.r * coeffq; + gsum = (sigma1.g + sigma2.g) * coeff - sigma3.g * coeffq; + bsum = (sigma1.b + sigma2.b) * coeff - sigma3.b * coeffq; + msum = (sigma1.m + sigma2.m) * coeff - sigma3.m * coeffq; + + row2->r = rsum; + row2->g = gsum; + row2->b = bsum; + row2->m = tcrop((float)msum, 0.f, 1.f); + + if (globmatte != 1.0) { + row2->r = row2->r * globmatte; + row2->g = row2->g * globmatte; + row2->b = row2->b * globmatte; + row2->m = row2->m * globmatte; + } + *row2 = overPix(*row1, *row2); + // overlayPixels(*row1, *row2, *row2, globmatte); + + // row2++; + + sigma2.r += row1[-brad].r; + sigma2.g += row1[-brad].g; + sigma2.b += row1[-brad].b; + sigma2.m += row1[-brad].m; + + pix1 = row1 + brad; + pix2 = row1; + pix3 = row1 - brad; + pix4 = row1 - brad + 1; + + desigma.r = sigma1.r - sigma2.r; + desigma.g = sigma1.g - sigma2.g; + desigma.b = sigma1.b - sigma2.b; + desigma.m = sigma1.m - sigma2.m; + + for (i = 1; i < length; i++) { + desigma.r += pix1->r - 2 * pix2->r + pix3->r; + desigma.g += pix1->g - 2 * pix2->g + pix3->g; + desigma.b += pix1->b - 2 * pix2->b + pix3->b; + desigma.m += pix1->m - 2 * pix2->m + pix3->m; + + rsum += (desigma.r + diff * (pix1->r - pix4->r)) * coeffq; + gsum += (desigma.g + diff * (pix1->g - pix4->g)) * coeffq; + bsum += (desigma.b + diff * (pix1->b - pix4->b)) * coeffq; + msum += (desigma.m + diff * (pix1->m - pix4->m)) * coeffq; + + row2->r = rsum; + row2->g = gsum; + row2->b = bsum; + row2->m = tcrop((float)msum, 0.f, 1.f); + if (globmatte != 1.0) { + row2->r = row2->r * globmatte; + row2->g = row2->g * globmatte; + row2->b = row2->b * globmatte; + row2->m = row2->m * globmatte; + } + *row2 = overPix(*pix2, *row2); + // overlayPixels(*pix2, *row2, *row2, 0.8); + + row2++; + pix1++, pix2++, pix3++, pix4++; + } +} + /*---------------------------------------------------------------------------*/ template @@ -226,6 +334,68 @@ void do_filtering(T *row1, T *row2, int length, double coeff, int brad, } } +template <> +void do_filtering(TPixelF *row1, TPixelF *row2, int length, + double coeff, int brad, double Mblur, + double globmatte) { + int i; + double rsum, gsum, bsum, msum; + TPixelD sigma1, sigma2; + + sigma1.r = sigma1.g = sigma1.b = sigma1.m = 0.0; + sigma2.r = sigma2.g = sigma2.b = sigma2.m = 0.0; + + for (i = 0; i <= brad; i++) { + sigma1.r += row1[-i].r; + sigma1.g += row1[-i].g; + sigma1.b += row1[-i].b; + sigma1.m += row1[-i].m; + + sigma2.r += -i * row1[-i].r; + sigma2.g += -i * row1[-i].g; + sigma2.b += -i * row1[-i].b; + sigma2.m += -i * row1[-i].m; + } + + for (i = 0; i < length; i++) /* for the ith point the previous computing is + used, with the values */ + { + /* stored in the auxiliary variables sigma1 and sigma2. */ + rsum = ((Mblur - i) * sigma1.r + sigma2.r) / coeff; + gsum = ((Mblur - i) * sigma1.g + sigma2.g) / coeff; + bsum = ((Mblur - i) * sigma1.b + sigma2.b) / coeff; + msum = ((Mblur - i) * sigma1.m + sigma2.m) / coeff; + + row2[i].r = rsum; + row2[i].g = gsum; + row2[i].b = bsum; + row2[i].m = std::min(msum, 1.0); + if (globmatte != 1.0) { + row2[i].r = row2[i].r * globmatte; + row2[i].g = row2[i].g * globmatte; + row2[i].b = row2[i].b * globmatte; + row2[i].m = row2[i].m * globmatte; + } + row2[i] = overPix(row1[i], row2[i]); + // overlayPixels(row1[i], row2[i], row2[i], globmatte); + + if (i < length - 1) { + sigma1.r += row1[i + 1].r - row1[i - brad].r; + sigma1.g += row1[i + 1].g - row1[i - brad].g; + sigma1.b += row1[i + 1].b - row1[i - brad].b; + sigma1.m += row1[i + 1].m - row1[i - brad].m; + + sigma2.r += (double)(i + 1) * row2[i + 1].r - + (double)(i - brad) * row1[i - brad].r; + sigma2.g += (double)(i + 1) * row2[i + 1].g - + (double)(i - brad) * row1[i - brad].g; + sigma2.b += (double)(i + 1) * row2[i + 1].b - + (double)(i - brad) * row1[i - brad].b; + sigma2.m += (double)(i + 1) * row2[i + 1].m - + (double)(i - brad) * row1[i - brad].m; + } + } +} /*---------------------------------------------------------------------------*/ template @@ -394,6 +564,8 @@ public: m_intensity->setValueRange(0, (std::numeric_limits::max)()); getAttributes()->setIsSpeedAware(true); + + enableComputeInFloat(true); } TPointD getBlurVector(double frame) const { @@ -562,6 +734,9 @@ void DirectionalBlurBaseFx::doCompute(TTile &tile, double frame, else if ((TRaster64P)rasIn && (TRaster64P)rasOut) directionalBlur(rasOut, rasIn, blur, p, m_bidirectional->getValue()); + else if ((TRasterFP)rasIn && (TRasterFP)rasOut) + directionalBlur(rasOut, rasIn, blur, p, + m_bidirectional->getValue()); } } diff --git a/toonz/sources/stdfx/nothingfx.cpp b/toonz/sources/stdfx/nothingfx.cpp index d82650b..81740eb 100644 --- a/toonz/sources/stdfx/nothingfx.cpp +++ b/toonz/sources/stdfx/nothingfx.cpp @@ -14,6 +14,7 @@ class NothingFx final : public TStandardRasterFx { public: NothingFx() { addInputPort("Source", m_input); + enableComputeInFloat(true); } ~NothingFx(){}; @@ -39,7 +40,12 @@ public: const TRenderSettings &info) override; bool canHandle(const TRenderSettings &info, double frame) override { - return true; + return true; + } + + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override { + return tileIsLinear; } }; @@ -48,8 +54,8 @@ FX_PLUGIN_IDENTIFIER(NothingFx, "nothingFx") //------------------------------------------------------------------- void NothingFx::transform(double frame, int port, const TRectD &rectOnOutput, - const TRenderSettings &infoOnOutput, TRectD &rectOnInput, - TRenderSettings &infoOnInput) { + const TRenderSettings &infoOnOutput, + TRectD &rectOnInput, TRenderSettings &infoOnInput) { infoOnInput = infoOnOutput; rectOnInput = rectOnOutput; return; @@ -58,14 +64,14 @@ void NothingFx::transform(double frame, int port, const TRectD &rectOnOutput, //------------------------------------------------------------------- int NothingFx::getMemoryRequirement(const TRectD &rect, double frame, - const TRenderSettings &info) { - return 0; + const TRenderSettings &info) { + return 0; } //------------------------------------------------------------------- void NothingFx::doCompute(TTile &tile, double frame, - const TRenderSettings &renderSettings) { + const TRenderSettings &renderSettings) { if (!m_input.isConnected()) return; m_input->compute(tile, frame, renderSettings); } diff --git a/toonz/sources/stdfx/particles.h b/toonz/sources/stdfx/particles.h index 8b2ed5d..ba9834c 100644 --- a/toonz/sources/stdfx/particles.h +++ b/toonz/sources/stdfx/particles.h @@ -93,7 +93,7 @@ struct particles_values { bool pick_color_for_every_frame_val; bool perspective_distribution_val; bool motion_blur_val; - double motion_blur_gamma_val; + double motion_blur_gamma_adjust_val; }; //------------------------------------------------------------------------------ diff --git a/toonz/sources/stdfx/particlesengine.cpp b/toonz/sources/stdfx/particlesengine.cpp index 9f6ab2a..bba8d35 100644 --- a/toonz/sources/stdfx/particlesengine.cpp +++ b/toonz/sources/stdfx/particlesengine.cpp @@ -3,11 +3,11 @@ #include "trop.h" #include "tfxparam.h" #include "tofflinegl.h" -//#include "tstroke.h" -//#include "drawutil.h" +// #include "tstroke.h" +// #include "drawutil.h" #include "tstopwatch.h" -//#include "tpalette.h" -//#include "tvectorrenderdata.h" +// #include "tpalette.h" +// #include "tvectorrenderdata.h" #include "tsystem.h" #include "timagecache.h" #include "tconvert.h" @@ -139,8 +139,8 @@ void Particles_Engine::fill_value_struct(struct particles_values &myvalues, myvalues.perspective_distribution_val = m_parent->perspective_distribution_val->getValue(); myvalues.motion_blur_val = m_parent->motion_blur_val->getValue(); - myvalues.motion_blur_gamma_val = - m_parent->motion_blur_gamma_val->getValue(frame); + myvalues.motion_blur_gamma_adjust_val = + m_parent->motion_blur_gamma_adjust_val->getValue(frame); } /*-----------------------------------------------------------------*/ @@ -511,8 +511,9 @@ void Particles_Engine::render_particles( // Perform the roll /*- RenderSettingsを複製して現在のフレームの計算用にする -*/ TRenderSettings riAux(ri); - riAux.m_affine = TAffine(); - riAux.m_bpp = 32; + riAux.m_affine = TAffine(); + riAux.m_bpp = 32; + riAux.m_linearColorSpace = false; // control image using its gradient is computed in 64bpp TRenderSettings riAux64(riAux); riAux64.m_bpp = 64; @@ -772,10 +773,11 @@ void Particles_Engine::do_render( } // Now, these are the particle rendering specifications - bbox = bbox.enlarge(3); - standardRefBBox = bbox; - riNew.m_affine = TScale(partScale); - bbox = riNew.m_affine * bbox; + bbox = bbox.enlarge(3); + standardRefBBox = bbox; + riNew.m_affine = TScale(partScale); + bbox = riNew.m_affine * bbox; + riNew.m_linearColorSpace = false; /*- 縮小済みのParticleのサイズ -*/ partResolution = TDimensionD(tceil(bbox.getLx()), tceil(bbox.getLy())); @@ -864,7 +866,7 @@ void Particles_Engine::do_render( if (values.motion_blur_val) { if (do_render_motion_blur(part, tile, tileRas, rfinalpart, M, bbox, values.trailopacity_val, - values.motion_blur_gamma_val, ri)) + values.motion_blur_gamma_adjust_val, ri)) return; } @@ -890,13 +892,15 @@ void Particles_Engine::do_render( bool Particles_Engine::do_render_motion_blur( Particle *part, TTile *tile, TRasterP tileRas, TRaster32P rfinalpart, TAffine &M, const TRectD &bbox, const DoublePair &trailOpacity, - const double gamma, const TRenderSettings &ri) { + const double gamma_adjust, const TRenderSettings &ri) { QList points; QList lengths; // do not render new-born particles as it has no trace if (part->genlifetime - part->lifetime == 0) return true; + double gamma = gamma_adjust + ri.m_colorSpaceGamma; + TRectD partBBoxD = M * TTranslation(bbox.getP00()) * convert(rfinalpart->getBounds()); partBBoxD = partBBoxD.enlarge(1.0); diff --git a/toonz/sources/stdfx/particlesengine.h b/toonz/sources/stdfx/particlesengine.h index 9ad865e..c5dab55 100644 --- a/toonz/sources/stdfx/particlesengine.h +++ b/toonz/sources/stdfx/particlesengine.h @@ -57,7 +57,8 @@ public: bool do_render_motion_blur(Particle *part, TTile *tile, TRasterP tileRas, TRaster32P rfinalpart, TAffine &M, const TRectD &bbox, const DoublePair &trailOpacity, - const double gamma, const TRenderSettings &ri); + const double gamma_adjust, + const TRenderSettings &ri); bool port_is_used(int i, struct particles_values &values); bool port_is_used_for_value(int i, struct particles_values &values); diff --git a/toonz/sources/stdfx/particlesfx.cpp b/toonz/sources/stdfx/particlesfx.cpp index 39db057..a9b394d 100644 --- a/toonz/sources/stdfx/particlesfx.cpp +++ b/toonz/sources/stdfx/particlesfx.cpp @@ -100,7 +100,7 @@ ParticlesFx::ParticlesFx() , pick_color_for_every_frame_val(false) , perspective_distribution_val(false) , motion_blur_val(false) - , motion_blur_gamma_val(2.2) { + , motion_blur_gamma_adjust_val(0.) { addInputPort("Texture1", new TRasterFxPort, 0); addInputPort("Control1", new TRasterFxPort, 1); @@ -267,8 +267,8 @@ ParticlesFx::ParticlesFx() bindParam(this, "pick_color_for_every_frame", pick_color_for_every_frame_val); bindParam(this, "perspective_distribution", perspective_distribution_val); bindParam(this, "motion_blur", motion_blur_val); - bindParam(this, "motion_blur_gamma", motion_blur_gamma_val); - motion_blur_gamma_val->setValueRange(1.0, 5.0); + bindParam(this, "motion_blur_gamma_adjust", motion_blur_gamma_adjust_val); + motion_blur_gamma_adjust_val->setValueRange(-5., 5.); } //------------------------------------------------------------------ diff --git a/toonz/sources/stdfx/particlesfx.h b/toonz/sources/stdfx/particlesfx.h index eaac440..7d46f43 100644 --- a/toonz/sources/stdfx/particlesfx.h +++ b/toonz/sources/stdfx/particlesfx.h @@ -91,7 +91,7 @@ public: TBoolParamP pick_color_for_every_frame_val; TBoolParamP perspective_distribution_val; TBoolParamP motion_blur_val; - TDoubleParamP motion_blur_gamma_val; + TDoubleParamP motion_blur_gamma_adjust_val; public: enum { UNIT_SMALL_INCH, UNIT_INCH }; diff --git a/toonz/sources/stdfx/perlinnoisefx.cpp b/toonz/sources/stdfx/perlinnoisefx.cpp index f4c38fd..7012f66 100644 --- a/toonz/sources/stdfx/perlinnoisefx.cpp +++ b/toonz/sources/stdfx/perlinnoisefx.cpp @@ -49,6 +49,8 @@ public: m_intensity->setValueRange(0, 300); m_min->setValueRange(0, 1.0); m_max->setValueRange(0, 1.0); + + enableComputeInFloat(true); } ~PerlinNoiseFx(){}; @@ -109,8 +111,8 @@ void doPerlinNoise(const TRasterPT &rasOut, TPointD posAff = aff * pos; double pnoise = Noise.Turbolence(posAff.x + offsetx, posAff.y + offsety, evolution, size, min, max); - int sval = (int)(brad * (pnoise - 0.5)); - int pixshift = sval + rasIn->getWrap() * (sval); + int sval = (int)(brad * (pnoise - 0.5)); + int pixshift = sval + rasIn->getWrap() * (sval); pos.x += 1.0; if (matte) { @@ -141,9 +143,9 @@ void doPerlinNoise(const TRasterPT &rasOut, evolution, size, min, max); double pnoisey = Noise.Marble(posAff.x + offsetx, posAff.y + offsety, evolution + 100, size, min, max); - int svalx = (int)(brad * (pnoisex - 0.5)); - int svaly = (int)(brad * (pnoisey - 0.5)); - int pixshift = svalx + rasIn->getWrap() * (svaly); + int svalx = (int)(brad * (pnoisex - 0.5)); + int svaly = (int)(brad * (pnoisey - 0.5)); + int pixshift = svalx + rasIn->getWrap() * (svaly); pos.x += 1.0; if (matte) { @@ -206,7 +208,7 @@ void PerlinNoiseFx::doCompute(TTile &tile, double frame, double min = m_min->getValue(frame); double max = m_max->getValue(frame); - if (brad < 0) brad = abs(brad); + if (brad < 0) brad = abs(brad); if (size < 0.01) size = 0.01; if (!brad) { @@ -226,23 +228,27 @@ void PerlinNoiseFx::doCompute(TTile &tile, double frame, TPointD pos = tile.m_pos; - TRaster32P rasOut = tile.getRaster(); - TRaster32P rasIn = tileIn.getRaster(); + TRaster32P rasOut32 = tile.getRaster(); + TRaster64P rasOut64 = tile.getRaster(); + TRasterFP rasOutF = tile.getRaster(); - if (rasOut) - doPerlinNoise(rasOut, rasIn, pos, evolution, size, min, + if (rasOut32) { + TRaster32P rasIn32 = tileIn.getRaster(); + doPerlinNoise(rasOut32, rasIn32, pos, evolution, size, min, max, offsetx, offsety, type, brad, matte, scale); - else { - TRaster64P rasOut = tile.getRaster(); - TRaster64P rasIn = tileIn.getRaster(); - if (rasOut) - doPerlinNoise(rasOut, rasIn, pos, evolution, size, min, - max, offsetx, offsety, type, brad, matte, - scale); - else - throw TException("Brightness&Contrast: unsupported Pixel Type"); - } + } else if (rasOut64) { + TRaster64P rasIn64 = tileIn.getRaster(); + doPerlinNoise(rasOut64, rasIn64, pos, evolution, size, + min, max, offsetx, offsety, type, brad, + matte, scale); + } else if (rasOutF) { + TRasterFP rasInF = tileIn.getRaster(); + doPerlinNoise(rasOutF, rasInF, pos, evolution, size, min, + max, offsetx, offsety, type, brad, matte, + scale); + } else + throw TException("PerlinNoise: unsupported Pixel Type"); } //------------------------------------------------------------------ diff --git a/toonz/sources/stdfx/premultiplyfx.cpp b/toonz/sources/stdfx/premultiplyfx.cpp index 6dab55d..8fc09ea 100644 --- a/toonz/sources/stdfx/premultiplyfx.cpp +++ b/toonz/sources/stdfx/premultiplyfx.cpp @@ -1,7 +1,7 @@ #include "stdfx.h" -//#include "tfxparam.h" +// #include "tfxparam.h" #include "trop.h" //=================================================================== @@ -10,7 +10,10 @@ class PremultiplyFx final : public TStandardRasterFx { TRasterFxPort m_input; public: - PremultiplyFx() { addInputPort("Source", m_input); } + PremultiplyFx() { + addInputPort("Source", m_input); + enableComputeInFloat(true); + } ~PremultiplyFx(){}; bool doGetBBox(double frame, TRectD &bBox, diff --git a/toonz/sources/stdfx/raylitfx.cpp b/toonz/sources/stdfx/raylitfx.cpp index d5eea48..28cfd0e 100644 --- a/toonz/sources/stdfx/raylitfx.cpp +++ b/toonz/sources/stdfx/raylitfx.cpp @@ -44,6 +44,8 @@ public: addInputPort("Source", m_input); m_radius->setValueRange(0.0, std::numeric_limits::max()); + + enableComputeInFloat(true); } ~BaseRaylitFx() {} @@ -174,7 +176,10 @@ void RaylitFx::doCompute(TTile &tileOut, double frame, params.m_lightOriginSrc.y = params.m_lightOriginDst.y = p.y; params.m_lightOriginSrc.z = params.m_lightOriginDst.z = (int)m_z->getValue(frame); - params.m_color = m_color->getValue(frame); + // currently tile should be nonlinear + assert(!tileOut.getRaster()->isLinear()); + params.m_color = m_color->getValue(frame, tileOut.getRaster()->isLinear(), + ri.m_colorSpaceGamma); params.m_intensity = m_intensity->getValue(frame); params.m_decay = m_decay->getValue(frame); params.m_smoothness = m_smoothness->getValue(frame); diff --git a/toonz/sources/stdfx/rgbkeyfx.cpp b/toonz/sources/stdfx/rgbkeyfx.cpp index 28b5f46..e0c66bf 100644 --- a/toonz/sources/stdfx/rgbkeyfx.cpp +++ b/toonz/sources/stdfx/rgbkeyfx.cpp @@ -1,12 +1,13 @@ -//#include "trop.h" +// #include "trop.h" #include "tfxparam.h" #include #include "stdfx.h" #include "tparamset.h" #include "globalcontrollablefx.h" +#include "tpixelutils.h" class RGBKeyFx final : public GlobalControllableFx { FX_PLUGIN_DECLARATION(RGBKeyFx) @@ -34,6 +35,7 @@ public: m_grange->setValueRange(0.0, 255.0); m_brange->setValueRange(0.0, 255.0); addInputPort("Source", m_input); + enableComputeInFloat(true); } ~RGBKeyFx(){}; @@ -55,35 +57,20 @@ public: } }; -namespace { -void update_param(int ¶m, TRaster32P ras) { return; } - -void update_param(int ¶m, TRaster64P ras) { - param = param * 257; - return; -} -} // namespace - //------------------------------------------------------------------- -template -void doRGBKey(TRasterPT ras, int highR, int highG, int highB, int lowR, - int lowG, int lowB, bool gender) { - update_param(highR, ras); - update_param(highG, ras); - update_param(highB, ras); - update_param(lowR, ras); - update_param(lowG, ras); - update_param(lowB, ras); - +template +void doRGBKey(TRasterPT ras, PIXEL highColor, PIXEL lowColor, + bool gender) { int j; ras->lock(); for (j = 0; j < ras->getLy(); j++) { PIXEL *pix = ras->pixels(j); PIXEL *endPix = pix + ras->getLx(); while (pix < endPix) { - bool condition = pix->r >= lowR && pix->r <= highR && pix->g >= lowG && - pix->g <= highG && pix->b >= lowB && pix->b <= highB; + bool condition = pix->r >= lowColor.r && pix->r <= highColor.r && + pix->g >= lowColor.g && pix->g <= highColor.g && + pix->b >= lowColor.b && pix->b <= highColor.b; if (condition != gender) *pix = PIXEL::Transparent; pix++; } @@ -91,6 +78,29 @@ void doRGBKey(TRasterPT ras, int highR, int highG, int highB, int lowR, ras->unlock(); } +template <> +void doRGBKey(TRasterFP ras, TPixelF highColor, TPixelF lowColor, bool gender) { + int j; + ras->lock(); + for (j = 0; j < ras->getLy(); j++) { + TPixelF *pix = ras->pixels(j); + TPixelF *endPix = pix + ras->getLx(); + while (pix < endPix) { + // clamp 0.f to 1.f in case computing HDR + float clampedR = std::min(1.f, std::max(0.f, pix->r)); + float clampedG = std::min(1.f, std::max(0.f, pix->g)); + float clampedB = std::min(1.f, std::max(0.f, pix->b)); + + bool condition = clampedR >= lowColor.r && clampedR <= highColor.r && + clampedG >= lowColor.g && clampedG <= highColor.g && + clampedB >= lowColor.b && clampedB <= highColor.b; + if (condition != gender) *pix = TPixelF::Transparent; + pix++; + } + } + ras->unlock(); +} + //------------------------------------------------------------------------------ void RGBKeyFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { @@ -98,31 +108,36 @@ void RGBKeyFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { m_input->compute(tile, frame, ri); - int r_range = (int)m_rrange->getValue(frame); - int g_range = (int)m_grange->getValue(frame); - int b_range = (int)m_brange->getValue(frame); - bool gender = (int)m_gender->getValue(); - const TPixel32 Color = m_color->getPremultipliedValue(frame); - TRaster32P raster32 = tile.getRaster(); + double r_range = m_rrange->getValue(frame) / 255.0; + double g_range = m_grange->getValue(frame) / 255.0; + double b_range = m_brange->getValue(frame) / 255.0; + bool gender = m_gender->getValue(); - int lowR = std::max(0, Color.r - r_range); - int highR = std::min(255, Color.r + r_range); - int lowG = std::max(0, Color.g - g_range); - int highG = std::min(255, Color.g + g_range); - int lowB = std::max(0, Color.b - b_range); - int highB = std::min(255, Color.b + b_range); + const TPixelF color = premultiply(toPixelF(m_color->getValueD(frame))); - if (raster32) - doRGBKey(raster32, highR, highG, highB, lowR, lowG, lowB, - gender); - else { - TRaster64P raster64 = tile.getRaster(); - if (raster64) - doRGBKey(raster64, highR, highG, highB, lowR, lowG, - lowB, gender); - else - throw TException("RGBKeyFx: unsupported Pixel Type"); + TPixelF lowColor(color.r - r_range, color.g - g_range, color.b - b_range); + TPixelF highColor(color.r + r_range, color.g + g_range, color.b + b_range); + + // currently the tile should always be nonlinear + assert(!tile.getRaster()->isLinear()); + if (tile.getRaster()->isLinear()) { + lowColor = toLinear(lowColor, ri.m_colorSpaceGamma); + highColor = toLinear(highColor, ri.m_colorSpaceGamma); } + + TRaster32P raster32 = tile.getRaster(); + TRaster64P raster64 = tile.getRaster(); + TRasterFP rasterF = tile.getRaster(); + if (raster32) + doRGBKey(raster32, toPixel32(highColor), toPixel32(lowColor), + gender); + else if (raster64) + doRGBKey(raster64, toPixel64(highColor), toPixel64(lowColor), + gender); + else if (rasterF) + doRGBKey(rasterF, highColor, lowColor, gender); + else + throw TException("RGBKeyFx: unsupported Pixel Type"); } //------------------------------------------------------------------ diff --git a/toonz/sources/stdfx/rgbmfadefx.cpp b/toonz/sources/stdfx/rgbmfadefx.cpp index f0fdb96..0d658c5 100644 --- a/toonz/sources/stdfx/rgbmfadefx.cpp +++ b/toonz/sources/stdfx/rgbmfadefx.cpp @@ -21,6 +21,8 @@ public: m_intensity->setValueRange(0, 100); addInputPort("Source", m_input); m_color->enableMatte(false); + + enableComputeInFloat(true); } ~RGBMFadeFx(){}; @@ -67,6 +69,25 @@ pix->m=(UCHAR)(pix->m+intensity*(col.m-pix->m));*/ } ras->unlock(); } + +template <> +void doRGBMFade(TRasterFP &ras, const TPixelF &col, double intensity) { + int j; + ras->lock(); + for (j = 0; j < ras->getLy(); j++) { + TPixelF *pix = ras->pixels(j); + TPixelF *endPix = pix + ras->getLx(); + while (pix < endPix) { + double factor = pix->m; + pix->r = pix->r + intensity * (col.r * factor - pix->r); + pix->g = pix->g + intensity * (col.g * factor - pix->g); + pix->b = pix->b + intensity * (col.b * factor - pix->b); + ++pix; + } + } + ras->unlock(); +} + //------------------------------------------------------------------------------ void RGBMFadeFx::doCompute(TTile &tile, double frame, @@ -80,16 +101,17 @@ void RGBMFadeFx::doCompute(TTile &tile, double frame, double intensity = tcrop(m_intensity->getValue(frame), min, max) / 100; TRaster32P raster32 = tile.getRaster(); + TRaster64P raster64 = tile.getRaster(); + TRasterFP rasterF = tile.getRaster(); if (raster32) doRGBMFade(raster32, col, intensity); - else { - TRaster64P raster64 = tile.getRaster(); - if (raster64) - doRGBMFade(raster64, toPixel64(col), intensity); - else - throw TException("RGBAFadeFx: unsupported Pixel Type"); - } + else if (raster64) + doRGBMFade(raster64, toPixel64(col), intensity); + else if (rasterF) + doRGBMFade(rasterF, toPixelF(col), intensity); + else + throw TException("RGBAFadeFx: unsupported Pixel Type"); } FX_PLUGIN_IDENTIFIER(RGBMFadeFx, "rgbmFadeFx"); diff --git a/toonz/sources/stdfx/ripplefx.cpp b/toonz/sources/stdfx/ripplefx.cpp index 843281b..ee2658f 100644 --- a/toonz/sources/stdfx/ripplefx.cpp +++ b/toonz/sources/stdfx/ripplefx.cpp @@ -60,6 +60,8 @@ public: m_cycle->setValueRange(0, (std::numeric_limits::max)()); m_count->setValueRange(0, (std::numeric_limits::max)()); m_angle->setMeasureName("angle"); + + enableComputeInFloat(true); } virtual ~RippleFx() {} diff --git a/toonz/sources/stdfx/stdfx.cpp b/toonz/sources/stdfx/stdfx.cpp index 163d1da..ae89e1f 100644 --- a/toonz/sources/stdfx/stdfx.cpp +++ b/toonz/sources/stdfx/stdfx.cpp @@ -34,6 +34,7 @@ public: bindParam(this, "value", m_value); addInputPort("Source", m_input); + enableComputeInFloat(true); }; ~FadeFx(){}; @@ -221,6 +222,7 @@ public: m_count->setValueRange(0, (std::numeric_limits::max)()); m_period->setMeasureName("fxLength"); m_wave_amplitude->setMeasureName("fxLength"); + enableComputeInFloat(true); } ~MultiLinearGradientFx(){}; @@ -288,6 +290,7 @@ public: m_wave_amplitude->setValueRange(0, std::numeric_limits::max()); m_period->setMeasureName("fxLength"); m_wave_amplitude->setMeasureName("fxLength"); + enableComputeInFloat(true); } ~LinearGradientFx(){}; @@ -358,7 +361,8 @@ void doComputeT(TRasterPT ras, TPointD posTrasf, void LinearGradientFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { - assert((TRaster32P)tile.getRaster() || (TRaster64P)tile.getRaster()); + assert((TRaster32P)tile.getRaster() || (TRaster64P)tile.getRaster() || + (TRasterFP)tile.getRaster()); double period = m_period->getValue(frame) / ri.m_shrinkX; double count = 1.0; @@ -398,7 +402,8 @@ throw TException("MultiLinearGradientFx: unsupported Pixel Type"); void MultiLinearGradientFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { - assert((TRaster32P)tile.getRaster() || (TRaster64P)tile.getRaster()); + assert((TRaster32P)tile.getRaster() || (TRaster64P)tile.getRaster() || + (TRasterFP)tile.getRaster()); double period = m_period->getValue(frame) / ri.m_shrinkX; double count = m_count->getValue(frame); @@ -464,6 +469,8 @@ public: bindParam(this, "curveType", m_curveType); m_period->setValueRange(0.0, std::numeric_limits::max()); m_innerperiod->setValueRange(0.0, std::numeric_limits::max()); + + enableComputeInFloat(true); } ~RadialGradientFx(){}; @@ -532,6 +539,8 @@ public: m_period->setValueRange(0, (std::numeric_limits::max)()); m_cycle->setValueRange(0, (std::numeric_limits::max)()); m_count->setValueRange(0, (std::numeric_limits::max)()); + + enableComputeInFloat(true); } ~MultiRadialGradientFx(){}; @@ -561,7 +570,8 @@ public: void MultiRadialGradientFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { - assert((TRaster32P)tile.getRaster() || (TRaster64P)tile.getRaster()); + assert((TRaster32P)tile.getRaster() || (TRaster64P)tile.getRaster() || + (TRasterFP)tile.getRaster()); double period = m_period->getValue(frame) / ri.m_shrinkX; double count = m_count->getValue(frame); double cycle = m_cycle->getValue(frame) / ri.m_shrinkX; @@ -576,7 +586,8 @@ void MultiRadialGradientFx::doCompute(TTile &tile, double frame, void RadialGradientFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { - assert((TRaster32P)tile.getRaster() || (TRaster64P)tile.getRaster()); + assert((TRaster32P)tile.getRaster() || (TRaster64P)tile.getRaster() || + (TRasterFP)tile.getRaster()); double period = m_period->getValue(frame) / ri.m_shrinkX; double innerperiod = m_innerperiod->getValue(frame) / ri.m_shrinkX; double count = 1.0; diff --git a/toonz/sources/stdfx/tilefx.cpp b/toonz/sources/stdfx/tilefx.cpp index 262fe79..dab3b45 100644 --- a/toonz/sources/stdfx/tilefx.cpp +++ b/toonz/sources/stdfx/tilefx.cpp @@ -34,6 +34,11 @@ public: const TRenderSettings &infoOnOutput, TRectD &rectOnInput, TRenderSettings &infoOnInput) override; + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override { + return tileIsLinear; + } + private: void makeTile(const TTile &inputTile, const TTile &tile) const; }; @@ -53,6 +58,8 @@ TileFx::TileFx() bindParam(this, "margin", m_margin); m_mode->addItem(eTileHorizontally, "Tile Horizontally"); m_mode->addItem(eTileVertically, "Tile Vertically"); + + enableComputeInFloat(true); } //------------------------------------------------------------------------------ diff --git a/toonz/sources/stdfx/tonecurvefx.cpp b/toonz/sources/stdfx/tonecurvefx.cpp index 168ff0d..f22e9f8 100644 --- a/toonz/sources/stdfx/tonecurvefx.cpp +++ b/toonz/sources/stdfx/tonecurvefx.cpp @@ -119,6 +119,8 @@ public: ToneCurveFx() : m_toneCurve(new TToneCurveParam()) { bindParam(this, "curve", m_toneCurve); addInputPort("Source", m_input); + + enableComputeInFloat(true); } ~ToneCurveFx(){}; @@ -150,6 +152,11 @@ void update_param(double ¶m, TRaster64P ras) { return; } +void update_param(double ¶m, TRasterFP ras) { + param /= double(TPixel32::maxChannelValue); + return; +} + QList getParamSetPoints(const TParamSet *paramSet, int frame) { QList points; int i; @@ -236,6 +243,97 @@ void doToneCurveFx(TRasterPT ras, double frame, ras->unlock(); } +template <> +void doToneCurveFx(TRasterFP ras, double frame, + const TToneCurveParam *toneCurveParam) { + QList> pointsList; + int e; + for (e = 0; e < 6; e++) { + TParamSet *paramSet = + toneCurveParam->getParamSet(TToneCurveParam::ToneChannel(e)) + .getPointer(); + QList points = getParamSetPoints(paramSet, frame); + pointsList.push_back(points); + } + bool isLinear = toneCurveParam->isLinear(); + + int i, t; + for (i = 0; i < pointsList.size(); i++) { + QList &points = pointsList[i]; + for (t = 0; t < points.size(); t++) { + TPointD &p = points[t]; + double &x = p.x; + double &y = p.y; + update_param(x, TRaster64P()); + update_param(y, TRaster64P()); + } + } + + std::vector rgbaLut(TPixel64::maxChannelValue + 1); + std::vector rgbLut(TPixel64::maxChannelValue + 1); + std::vector rLut(TPixel64::maxChannelValue + 1); + std::vector gLut(TPixel64::maxChannelValue + 1); + std::vector bLut(TPixel64::maxChannelValue + 1); + std::vector aLut(TPixel64::maxChannelValue + 1); + + fill_lut(pointsList[0], rgbaLut, isLinear); + fill_lut(pointsList[1], rgbLut, isLinear); + fill_lut(pointsList[2], rLut, isLinear); + fill_lut(pointsList[3], gLut, isLinear); + fill_lut(pointsList[4], bLut, isLinear); + fill_lut(pointsList[5], aLut, isLinear); + + int lx = ras->getLx(); + int ly = ras->getLy(); + + std::vector rLutF(TPixel64::maxChannelValue + 1); + std::vector gLutF(TPixel64::maxChannelValue + 1); + std::vector bLutF(TPixel64::maxChannelValue + 1); + std::vector aLutF(TPixel64::maxChannelValue + 1); + + auto normalizeLut = [&](std::vector &dst, + std::vector &chanLut) { + for (int i = 0; i <= TPixel64::maxChannelValue; i++) { + int v = rgbaLut[rgbLut[chanLut[i]]]; + dst[i] = float(v) / float(TPixel64::maxChannelValue); + } + }; + normalizeLut(rLutF, rLut); + normalizeLut(gLutF, gLut); + normalizeLut(bLutF, bLut); + for (int i = 0; i <= TPixel64::maxChannelValue; i++) { + int v = rgbaLut[aLut[i]]; + aLutF[i] = float(v) / float(TPixel64::maxChannelValue); + } + + auto getLutValue = [&](std::vector &lut, float val) { + if (val < 0.f) + return lut[0]; + else if (val >= 1.f) + return lut[TPixel64::maxChannelValue]; + float v = val * float(TPixel64::maxChannelValue); + int id = (int)tfloor(v); + float ratio = v - float(id); + return lut[id] * (1.f - ratio) + lut[id + 1] * ratio; + }; + + int j; + ras->lock(); + for (j = 0; j < ly; j++) { + TPixelF *pix = ras->pixels(j); + TPixelF *endPix = pix + lx; + while (pix < endPix) { + if (pix->m > 0.f) *pix = depremultiply(*pix); + pix->r = getLutValue(rLutF, pix->r); + pix->g = getLutValue(gLutF, pix->g); + pix->b = getLutValue(bLutF, pix->b); + pix->m = getLutValue(aLutF, pix->m); + if (pix->m > 0.f) *pix = premultiply(*pix); + pix++; + } + } + ras->unlock(); +} //------------------------------------------------------------------- void ToneCurveFx::doCompute(TTile &tile, double frame, @@ -245,17 +343,17 @@ void ToneCurveFx::doCompute(TTile &tile, double frame, m_input->compute(tile, frame, ri); TRaster32P raster32 = tile.getRaster(); + TRaster64P raster64 = tile.getRaster(); + TRasterFP rasterF = tile.getRaster(); if (raster32) doToneCurveFx(raster32, frame, m_toneCurve.getPointer()); - else { - TRaster64P raster64 = tile.getRaster(); - if (raster64) - doToneCurveFx(raster64, frame, - m_toneCurve.getPointer()); - else - throw TException("Brightness&Contrast: unsupported Pixel Type"); - } + else if (raster64) + doToneCurveFx(raster64, frame, m_toneCurve.getPointer()); + else if (rasterF) + doToneCurveFx(rasterF, frame, m_toneCurve.getPointer()); + else + throw TException("Brightness&Contrast: unsupported Pixel Type"); } FX_PLUGIN_IDENTIFIER(ToneCurveFx, "toneCurveFx"); diff --git a/toonz/sources/stdfx/warp.cpp b/toonz/sources/stdfx/warp.cpp index 755afda..88cd6c5 100644 --- a/toonz/sources/stdfx/warp.cpp +++ b/toonz/sources/stdfx/warp.cpp @@ -22,7 +22,13 @@ template <> inline double convert(const TPixel64 &pixel) { return TPixelGR16::from(pixel).value; } + +template <> +inline double convert(const TPixelF &pixel) { + // clamp between 0 and 1 + return std::min(1.f, std::max(0.f, TPixelGRF::from(pixel).value)); } +} // namespace /*-----------------------------------------------------------------*/ @@ -297,6 +303,9 @@ void warp(TRasterP &tileRas, const TRasterP &rasIn, TRasterP &warper, TRaster64P rasIn64 = rasIn; TRaster64P tileRas64 = tileRas; TRaster64P warper64 = warper; + TRasterFP rasInF = rasIn; + TRasterFP tileRasF = tileRas; + TRasterFP warperF = warper; if (rasIn32 && tileRas32 && warper32) { Warper warper(rasInPos, warperPos, rasIn32, warper32, tileRas32, @@ -308,6 +317,11 @@ void warp(TRasterP &tileRas, const TRasterP &rasIn, TRasterP &warper, params); warper.createLattice(); warper.shepardWarp(); + } else if (rasInF && tileRasF && warperF) { + Warper warper(rasInPos, warperPos, rasInF, warperF, tileRasF, + params); + warper.createLattice(); + warper.shepardWarp(); } else throw TRopException("warp: unsupported raster types"); } diff --git a/toonz/sources/stdfx/warpfx.cpp b/toonz/sources/stdfx/warpfx.cpp index f2754c4..de99cfd 100644 --- a/toonz/sources/stdfx/warpfx.cpp +++ b/toonz/sources/stdfx/warpfx.cpp @@ -5,7 +5,7 @@ #include "trop.h" #include "warp.h" #include "trasterfx.h" -//#include "timage_io.h" +// #include "timage_io.h" //------------------------------------------------------------------- @@ -28,6 +28,8 @@ public: m_intensity->setValueRange(-1000, 1000); m_gridStep->setValueRange(2, 20); + + enableComputeInFloat(true); } virtual ~WarpFx() {} diff --git a/toonz/sources/tnzbase/trasterfx.cpp b/toonz/sources/tnzbase/trasterfx.cpp index 6af8fab..b87acde 100644 --- a/toonz/sources/tnzbase/trasterfx.cpp +++ b/toonz/sources/tnzbase/trasterfx.cpp @@ -22,7 +22,7 @@ #include "trenderer.h" // Diagnostics -//#define DIAGNOSTICS +// #define DIAGNOSTICS #ifdef DIAGNOSTICS #include "diagnostics.h" @@ -218,7 +218,7 @@ class TrFx final : public TBaseRasterFx { TRasterFx *m_fx; public: - TrFx() {} + TrFx() { enableComputeInFloat(true); } ~TrFx() {} //----------------------------------------------------------- @@ -309,6 +309,13 @@ public: return TRasterFx::memorySize(rectIn, info.m_bpp); } + //----------------------------------------------------------- + + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override { + return tileIsLinear; + } + private: bool buildInput(const TRectD &rectOut, double frame, const TRenderSettings &infoOut, TRectD &rectIn, @@ -430,8 +437,10 @@ void FxResourceBuilder::buildTileToCalculate(const TRectD &tileGeom) { TRect rect(0, 0, requiredSize.lx - 1, requiredSize.ly - 1); ras = outRas->extract(rect); ras->clear(); - } else + } else { ras = outRas->create(requiredSize.lx, requiredSize.ly); + ras->setLinear(outRas->isLinear()); + } m_newTile.setRaster(ras); @@ -489,7 +498,14 @@ public: std::string m_interactiveCacheId; mutable TThread::Mutex m_mutex; // brutto - TRasterFxImp() : m_cacheEnabled(false), m_isEnabled(true), m_cachedTile(0) {} + bool m_canComputeInFloat; + bool m_canComputeInLinearColorSpace; + + TRasterFxImp() + : m_cacheEnabled(false) + , m_isEnabled(true) + , m_cachedTile(0) + , m_canComputeInFloat(false) {} ~TRasterFxImp() {} @@ -516,6 +532,9 @@ public: QMutexLocker sl(&m_mutex); // a che serve m_isEnabled = on; } + + void enableComputeInFloat(bool on) { m_canComputeInFloat = on; } + bool canComputeInFloat() { return m_canComputeInFloat; } }; //-------------------------------------------------- @@ -680,7 +699,9 @@ void TRasterFx::dryCompute(TRectD &rect, double frame, } std::string alias = getAlias(frame, info) + "[" + ::traduce(info.m_affine) + - "][" + std::to_string(info.m_bpp) + "]"; + "][" + std::to_string(info.m_bpp) + "][" + + std::to_string(info.m_linearColorSpace) + "][" + + std::to_string(info.m_colorSpaceGamma) + "]"; int renderStatus = TRenderer::instance().getRenderStatus(TRenderer::renderId()); @@ -766,6 +787,8 @@ void TRasterFx::allocateAndCompute(TTile &tile, const TPointD &pos, if (templateRas) { TRaster32P ras32(templateRas); TRaster64P ras64(templateRas); + TRasterFP rasF(templateRas); + bool isLinear = templateRas->isLinear(); templateRas = 0; // Release the reference to templateRas before allocation TRasterP tileRas; @@ -773,11 +796,13 @@ void TRasterFx::allocateAndCompute(TTile &tile, const TPointD &pos, tileRas = TRaster32P(size.lx, size.ly); else if (ras64) tileRas = TRaster64P(size.lx, size.ly); + else if (rasF) + tileRas = TRasterFP(size.lx, size.ly); else { assert(false); return; } - + tileRas->setLinear(isLinear); tile.setRaster(tileRas); } else { if (info.m_bpp == 32) { @@ -786,8 +811,12 @@ void TRasterFx::allocateAndCompute(TTile &tile, const TPointD &pos, } else if (info.m_bpp == 64) { TRaster64P tileRas(size.lx, size.ly); tile.setRaster(tileRas); + } else if (info.m_bpp == 128) { + TRasterFP tileRas(size.lx, size.ly); + tile.setRaster(tileRas); } else assert(false); + tile.getRaster()->setLinear(info.m_linearColorSpace); } tile.m_pos = pos; @@ -862,11 +891,6 @@ void TRasterFx::compute(TTile &tile, double frame, // Retrieve tile's geometry TRectD tilePlacement = myConvert(tile.getRaster()->getBounds()) + tile.m_pos; - // Build the fx result alias (in other words, its name) - std::string alias = getAlias(frame, info) + "[" + ::traduce(info.m_affine) + - "][" + std::to_string(info.m_bpp) + - "]"; // To be moved below - TRectD bbox; getBBox(frame, bbox, info); enlargeToI(bbox); @@ -874,6 +898,39 @@ void TRasterFx::compute(TTile &tile, double frame, TRectD interestingRect(tilePlacement * bbox); if (myIsEmpty(interestingRect)) return; + TDimension tileSize = tile.getRaster()->getSize(); + // ひとつ前のノードが小数点対応しているかどうかによって、入ってくるラスタの形式が異なる + TRaster32P ras32 = tile.getRaster(); + TRaster64P ras64 = tile.getRaster(); + TRasterFP rasF = tile.getRaster(); + if (info.m_bpp == 128) { + if (rasF && !canComputeInFloat()) { + TRasterP rasAux = TRaster64P(tileSize); + TRop::convert(rasAux, tile.getRaster()); + tile.setRaster(rasAux); + } else if ((ras32 || ras64) && canComputeInFloat()) { + TRasterFP rasAuxF = TRasterFP(tileSize); + TRop::convert(rasAuxF, tile.getRaster()); + tile.setRaster(rasAuxF); + } + } + // linear化 + bool isLinear = tile.getRaster()->isLinear(); + bool computeInLinear = + toBeComputedInLinearColorSpace(info.m_linearColorSpace, isLinear); + if (isLinear != computeInLinear) { + if (isLinear) { // && !computeInLinear + TRop::tosRGB(tile.getRaster(), info.m_colorSpaceGamma); + } else // !isLinear && computeInLinear + TRop::toLinearRGB(tile.getRaster(), info.m_colorSpaceGamma); + } + + // Build the fx result alias (in other words, its name) + std::string alias = getAlias(frame, info) + "[" + ::traduce(info.m_affine) + + "][" + std::to_string(info.m_bpp) + "][" + + std::to_string(computeInLinear) + "][" + + std::to_string(info.m_colorSpaceGamma) + "]"; + // Extract the interesting tile from requested one TTile interestingTile; interestingTile.m_pos = interestingRect.getP00(); @@ -897,6 +954,29 @@ void TRasterFx::compute(TTile &tile, double frame, FxResourceBuilder rBuilder(alias, this, info, frame); rBuilder.build(interestingTile); + // linear化 + if (isLinear != computeInLinear) { + if (isLinear) // && !computeInLinear + TRop::toLinearRGB(tile.getRaster(), info.m_colorSpaceGamma); + else // !isLinear && computeInLinear + TRop::tosRGB(tile.getRaster(), info.m_colorSpaceGamma); + } + + if (info.m_bpp == 128) { + if (rasF && !canComputeInFloat()) { + TRop::convert(rasF, tile.getRaster()); + tile.setRaster(rasF); + } else if ((ras32 || ras64) && canComputeInFloat()) { + if (ras64) { + TRop::convert(ras64, tile.getRaster()); + tile.setRaster(ras64); + } else { + TRop::convert(ras32, tile.getRaster()); + tile.setRaster(ras32); + } + } + } + #ifdef DIAGNOSTICS sw.stop(); @@ -982,7 +1062,7 @@ TRasterP TRasterFx::applyAffine(TTile &tileOut, const TTile &tileIn, TRectD rectInAfter = aff * myConvert(src_ras->getBounds()); TAffine rasterAff = TTranslation((aff * rectIn).getP00() - rectOut.getP00() - - rectInAfter.getP00()) * + rectInAfter.getP00()) * aff; TRop::ResampleFilterType qual; @@ -1055,6 +1135,18 @@ bool TRasterFx::isCacheEnabled() const { return m_rasFxImp->m_cacheEnabled; } void TRasterFx::enableCache(bool on) { m_rasFxImp->enableCache(on); } +//-------------------------------------------------- + +bool TRasterFx::canComputeInFloat() const { + return m_rasFxImp->canComputeInFloat(); +} + +//-------------------------------------------------- + +void TRasterFx::enableComputeInFloat(bool on) { + m_rasFxImp->enableComputeInFloat(on); +} + //============================================================================== // // TRenderSettings @@ -1077,7 +1169,9 @@ TRenderSettings::TRenderSettings() , m_applyShrinkToViewer(false) , m_userCachable(true) , m_isCanceled(NULL) - , m_getFullSizeBBox(false) {} + , m_getFullSizeBBox(false) + , m_linearColorSpace(false) + , m_colorSpaceGamma(2.2) {} //------------------------------------------------------------------------------ @@ -1096,7 +1190,8 @@ std::string TRenderSettings::toString() const { "," + std::to_string(m_affine.a21) + "," + std::to_string(m_affine.a22) + "," + std::to_string(m_affine.a23) + ";" + std::to_string(m_maxTileSize) + ";" + std::to_string(m_isSwatch) + ";" + std::to_string(m_userCachable) + - ";{"; + ";" + std::to_string(m_linearColorSpace) + ";" + + std::to_string(m_colorSpaceGamma) + ";{"; if (!m_data.empty()) { ss += m_data[0]->toString(); for (int i = 1; i < (int)m_data.size(); i++) @@ -1119,7 +1214,9 @@ bool TRenderSettings::operator==(const TRenderSettings &rhs) const { m_applyShrinkToViewer != rhs.m_applyShrinkToViewer || m_maxTileSize != rhs.m_maxTileSize || m_affine != rhs.m_affine || m_mark != rhs.m_mark || m_isSwatch != rhs.m_isSwatch || - m_userCachable != rhs.m_userCachable) + m_userCachable != rhs.m_userCachable || + m_linearColorSpace != rhs.m_linearColorSpace || + m_colorSpaceGamma != rhs.m_colorSpaceGamma) return false; return std::equal(m_data.begin(), m_data.end(), rhs.m_data.begin(), areEqual); diff --git a/toonz/sources/tnztools/stylepicker.cpp b/toonz/sources/tnztools/stylepicker.cpp index d083aa3..e7639bb 100644 --- a/toonz/sources/tnztools/stylepicker.cpp +++ b/toonz/sources/tnztools/stylepicker.cpp @@ -180,18 +180,33 @@ TPixel64 StylePicker::pickColor16(const TPointD &pos, double radius, TVectorImageP vi = m_image; assert(ri && !ti && !vi); if (!ri || ti || vi) return TPixel64::Transparent; - TRasterP raster = ri->getRaster(); if (raster->getPixelSize() != 8) return TPixel64::Transparent; - TPoint point = getRasterPoint(pos); if (!raster->getBounds().contains(point)) return TPixel64::Transparent; - TRaster64P raster64 = raster; if (!raster64) return TPixel64::Transparent; - return raster64->pixels(point.y)[point.x]; } + +//--------------------------------------------------------- + +TPixelF StylePicker::pickColor32F(const TPointD &pos, double radius, + double scale2) const { + TToonzImageP ti = m_image; + TRasterImageP ri = m_image; + TVectorImageP vi = m_image; + assert(ri && !ti && !vi); + if (!ri || ti || vi) return TPixelF::Transparent; + TRasterP raster = ri->getRaster(); + if (raster->getPixelSize() != 16) return TPixelF::Transparent; + TPoint point = getRasterPoint(pos); + if (!raster->getBounds().contains(point)) return TPixelF::Transparent; + TRasterFP rasterF = raster; + if (!rasterF) return TPixelF::Transparent; + return rasterF->pixels(point.y)[point.x]; +} + //--------------------------------------------------------- TPixel32 StylePicker::pickAverageColor(const TRectD &rect) const { @@ -241,29 +256,68 @@ TPixel32 StylePicker::pickAverageColor(const TRectD &rect) const { TPixel64 StylePicker::pickAverageColor16(const TRectD &rect) const { TRasterImageP ri = m_image; assert(ri); - if (!ri) return TPixel64::Transparent; - TRasterP raster; - raster = ri->getRaster(); - if (raster->getPixelSize() != 8) return TPixel64::Transparent; + if (!!ri) { + TRasterP raster; + raster = ri->getRaster(); + + TPoint topLeft = getRasterPoint(rect.getP00()); + TPoint bottomRight = getRasterPoint(rect.getP11()); + + if (!raster->getBounds().overlaps(TRect(topLeft, bottomRight))) + return TPixel64::Transparent; + + topLeft.x = std::max(0, topLeft.x); + topLeft.y = std::max(0, topLeft.y); + bottomRight.x = std::min(raster->getLx(), bottomRight.x); + bottomRight.y = std::min(raster->getLy(), bottomRight.y); + + TRaster64P raster64 = raster; + assert(raster64); + if (raster64) { + UINT r = 0, g = 0, b = 0, m = 0, size = 0; + for (int y = topLeft.y; y < bottomRight.y; y++) { + TPixel64 *p = &raster64->pixels(y)[topLeft.x]; + for (int x = topLeft.x; x < bottomRight.x; x++, p++) { + r += p->r; + g += p->g; + b += p->b; + m += p->m; + size++; + } + } + + if (size) + return TPixel64(r / size, g / size, b / size, m / size); + else + return TPixel64::Transparent; + } + } + return TPixel64::Transparent; +} + +//--------------------------------------------------------- + +TPixelF StylePicker::pickAverageColor32F(const TRectD &rect) const { + TRasterImageP ri = m_image; + assert(ri); + if (!ri) return TPixelF::Transparent; + TRasterFP raster = ri->getRaster(); + if (!raster) return TPixelF::Transparent; TPoint topLeft = getRasterPoint(rect.getP00()); TPoint bottomRight = getRasterPoint(rect.getP11()); if (!raster->getBounds().overlaps(TRect(topLeft, bottomRight))) - return TPixel64::Transparent; + return TPixelF::Transparent; topLeft.x = std::max(0, topLeft.x); topLeft.y = std::max(0, topLeft.y); bottomRight.x = std::min(raster->getLx(), bottomRight.x); bottomRight.y = std::min(raster->getLy(), bottomRight.y); - TRaster64P raster64 = raster; - assert(raster64); - if (!raster64) return TPixel64::Transparent; - - uint64_t r = 0, g = 0, b = 0, m = 0, size = 0; + float r = 0, g = 0, b = 0, m = 0, size = 0; for (int y = topLeft.y; y < bottomRight.y; y++) { - TPixel64 *p = &raster64->pixels(y)[topLeft.x]; + TPixelF *p = &raster->pixels(y)[topLeft.x]; for (int x = topLeft.x; x < bottomRight.x; x++, p++) { r += p->r; g += p->g; @@ -274,9 +328,9 @@ TPixel64 StylePicker::pickAverageColor16(const TRectD &rect) const { } if (size) - return TPixel64(r / size, g / size, b / size, m / size); + return TPixelF(r / size, g / size, b / size, m / size); else - return TPixel64::Transparent; + return TPixelF::Transparent; } //--------------------------------------------------------- diff --git a/toonz/sources/toonz/flipbook.cpp b/toonz/sources/toonz/flipbook.cpp index 9900e22..ae5e341 100644 --- a/toonz/sources/toonz/flipbook.cpp +++ b/toonz/sources/toonz/flipbook.cpp @@ -266,7 +266,7 @@ void FlipBook::addFreezeButtonToTitleBar() { if (panel) { TPanelTitleBar *titleBar = panel->getTitleBar(); m_freezeButton = new TPanelTitleBarButton( - titleBar, getIconThemePath("actions/20/pane_freeze.svg")); + titleBar, getIconThemePath("actions/20/pane_freeze.svg")); m_freezeButton->setToolTip("Freeze"); titleBar->add(QPoint(-64, 0), m_freezeButton); connect(m_freezeButton, SIGNAL(toggled(bool)), this, SLOT(freeze(bool))); @@ -918,11 +918,11 @@ FlipBook *FlipBookPool::pop() { //! Saves the content of this flipbook pool. void FlipBookPool::save() const { QSettings history(toQString(m_historyPath), QSettings::IniFormat); - history.clear(); - history.setValue("count", m_overallFlipCount); history.beginGroup("flipbooks"); + // clear all contents in the group + history.remove(""); std::map::const_iterator it; for (it = m_pool.begin(); it != m_pool.end(); ++it) { @@ -1146,12 +1146,13 @@ void FlipBook::setLevel(const TFilePath &fp, TPalette *palette, int from, levelToPush.m_incrementalIndexing = incrementalIndexing; int formatIdx = Preferences::instance()->matchLevelFormat(fp); - if (formatIdx >= 0 && Preferences::instance() - ->levelFormat(formatIdx) - .m_options.m_premultiply) { - levelToPush.m_premultiply = true; - } + if (formatIdx >= 0) { + LevelOptions options = + Preferences::instance()->levelFormat(formatIdx).m_options; + levelToPush.m_premultiply = options.m_premultiply; + levelToPush.m_colorSpaceGamma = options.m_colorSpaceGamma; + } m_levels.push_back(levelToPush); // Get the frames count to be shown in this flipbook level @@ -1515,6 +1516,9 @@ TImageP FlipBook::getCurrentImage(int frame) { bool randomAccessRead = false; bool incrementalIndexing = false; bool premultiply = false; + TSceneProperties *sp = + TApp::instance()->getCurrentScene()->getScene()->getProperties(); + double colorSpaceGamma = LevelOptions::DefaultColorSpaceGamma; if (m_xl) // is an xsheet level { if (m_xl->getFrameCount() <= 0) return 0; @@ -1550,13 +1554,17 @@ TImageP FlipBook::getCurrentImage(int frame) { levelName = m_levelNames[i]; fid = m_levels[i].flipbookIndexToLevelFrame(frameIndex); premultiply = m_levels[i].m_premultiply; + colorSpaceGamma = m_levels[i].m_colorSpaceGamma; + if (fid == TFrameId()) return 0; id = levelName.toStdString() + fid.expand(TFrameId::NO_PAD) + ((m_isPreviewFx) ? "" : ::to_string(this)); - if (!m_isPreviewFx) + if (!m_isPreviewFx) { m_title1 = m_viewerTitle + " :: " + fp.withoutParentDir().withFrame(fid); - else + if (fp.getType() == "exr") + m_title1 += " :: " + tr("Gamma : %1").arg(colorSpaceGamma); + } else m_title1 = ""; } else if (m_levelNames.empty()) return 0; @@ -1594,6 +1602,7 @@ TImageP FlipBook::getCurrentImage(int frame) { } TImageReaderP ir = m_lr->getFrameReader(fid); ir->setShrink(m_shrink); + ir->setColorSpaceGamma(colorSpaceGamma); if (m_loadbox != TRect() && showSub) { ir->setRegion(m_loadbox); lx = m_loadbox.getLx(); @@ -1602,6 +1611,9 @@ TImageP FlipBook::getCurrentImage(int frame) { if (Preferences::instance()->is30bitDisplayEnabled()) ir->enable16BitRead(true); + // always enable to load float-format images + ir->enableFloatRead(true); + TImageP img = ir->load(); if (img) { @@ -1678,7 +1690,18 @@ void FlipBook::onDrawFrame(int frame, const ImagePainter::VisualSettings &vs, if (!img) return; - m_imageViewer->setImage(img); + // gain control ( for now the result is not cached ) + if (vs.m_gainStep != 0) { + TImageP gainedImg = img->cloneImage(); + TSceneProperties *sp = + TApp::instance()->getCurrentScene()->getScene()->getProperties(); + double colorSpaceGamma = + sp->getPreviewProperties()->getRenderSettings().m_colorSpaceGamma; + TRop::adjustGain(gainedImg->raster(), vs.m_gainStep, colorSpaceGamma); + + m_imageViewer->setImage(gainedImg, img); + } else + m_imageViewer->setImage(img); } catch (...) { m_imageViewer->setImage(TImageP()); } @@ -1716,8 +1739,7 @@ void FlipBook::clearCache() { for (it = m_levels[i].m_level->begin(); it != m_levels[i].m_level->end(); ++it) TImageCache::instance()->remove( - m_levelNames[i].toStdString() + - std::to_string(it->first.getNumber()) + + m_levelNames[i].toStdString() + it->first.expand(TFrameId::NO_PAD) + ((m_isPreviewFx) ? "" : ::to_string(this))); else { int from, to, step; @@ -1893,6 +1915,7 @@ void FlipBook::reset() { m_flipConsole->enableButton(FlipConsole::eDefineLoadBox, true); m_flipConsole->enableButton(FlipConsole::eUseLoadBox, true); + m_flipConsole->resetGain(true); m_lr = TLevelReaderP(); diff --git a/toonz/sources/toonz/flipbook.h b/toonz/sources/toonz/flipbook.h index 0e8d8c8..0c0922e 100644 --- a/toonz/sources/toonz/flipbook.h +++ b/toonz/sources/toonz/flipbook.h @@ -135,7 +135,8 @@ protected: , m_step(step) , m_randomAccessRead(false) , m_incrementalIndexing(false) - , m_premultiply(false) {} + , m_premultiply(false) + , m_colorSpaceGamma(LevelOptions::DefaultColorSpaceGamma) {} TLevelP m_level; int m_fromIndex, m_toIndex, m_step; bool m_incrementalIndexing; @@ -148,6 +149,11 @@ protected: // be displayed properly. bool m_premultiply; + // Gamma value to be used when converting EXR files to nonlinear. + // It may be set to some value in the Preferences > Loading > "Level + // Settings by File Format". + double m_colorSpaceGamma; + TFilePath m_fp; TFrameId flipbookIndexToLevelFrame(int index); diff --git a/toonz/sources/toonz/histogrampopup.cpp b/toonz/sources/toonz/histogrampopup.cpp index c34ae64..20b90e1 100644 --- a/toonz/sources/toonz/histogrampopup.cpp +++ b/toonz/sources/toonz/histogrampopup.cpp @@ -87,6 +87,9 @@ void HistogramPopup::updateInfo(const TPixel64 &pix, const TPointD &imagePos) { m_histogram->updateInfo(pix, imagePos); } +void HistogramPopup::updateInfo(const TPixelF &pix, const TPointD &imagePos) { + m_histogram->updateInfo(pix, imagePos); +} //----------------------------------------------------------------------------- /*! show the average-picked color */ @@ -97,6 +100,10 @@ void HistogramPopup::updateAverageColor(const TPixel32 &pix) { void HistogramPopup::updateAverageColor(const TPixel64 &pix) { m_histogram->updateAverageColor(pix); } + +void HistogramPopup::updateAverageColor(const TPixelF &pix) { + m_histogram->updateAverageColor(pix); +} //----------------------------------------------------------------------------- void HistogramPopup::setShowCompare(bool on) { diff --git a/toonz/sources/toonz/histogrampopup.h b/toonz/sources/toonz/histogrampopup.h index 53cf39e..44dcbfb 100644 --- a/toonz/sources/toonz/histogrampopup.h +++ b/toonz/sources/toonz/histogrampopup.h @@ -28,8 +28,10 @@ public: void updateInfo(const TPixel32 &pix, const TPointD &imagePos); void updateInfo(const TPixel64 &pix, const TPointD &imagePos); + void updateInfo(const TPixelF &pix, const TPointD &imagePos); void updateAverageColor(const TPixel32 &pix); void updateAverageColor(const TPixel64 &pix); + void updateAverageColor(const TPixelF &pix); void setShowCompare(bool on); void invalidateCompHisto(); void moveNextToWidget(QWidget *widget); diff --git a/toonz/sources/toonz/imageviewer.cpp b/toonz/sources/toonz/imageviewer.cpp index b11bdeb..7de173e 100644 --- a/toonz/sources/toonz/imageviewer.cpp +++ b/toonz/sources/toonz/imageviewer.cpp @@ -214,6 +214,7 @@ ImageViewer::ImageViewer(QWidget *parent, FlipBook *flipbook, , m_mouseButton(Qt::NoButton) , m_draggingZoomSelection(false) , m_image() + , m_orgImage() , m_FPS(0) , m_viewAff() , m_pos(0, 0) @@ -402,8 +403,9 @@ ImageViewer::~ImageViewer() { /*! Set current image to \b image and update. If Histogram is visible set its * image. */ -void ImageViewer::setImage(TImageP image) { - m_image = image; +void ImageViewer::setImage(TImageP image, TImageP orgImage) { + m_image = image; + m_orgImage = orgImage; if (m_image && m_firstImage) { m_firstImage = false; @@ -415,7 +417,7 @@ void ImageViewer::setImage(TImageP image) { } if (m_isHistogramEnable && m_histogramPopup->isVisible()) - m_histogramPopup->setImage(image); + m_histogramPopup->setImage((orgImage) ? orgImage : image); // make sure to redraw the frame here. // repaint() does NOT immediately redraw the frame for QOpenGLWidget @@ -945,6 +947,8 @@ TImageP ImageViewer::getPickedImage(QPointF mousePos) { } if (cursorIsInSnapShot) return TImageCache::instance()->get(QString("TnzCompareImg"), false); + else if (m_orgImage) + return m_orgImage; else return m_image; } @@ -977,8 +981,8 @@ void ImageViewer::pickColor(QMouseEvent *event, bool putValueToStyleEditor) { getViewAff().inv() * TPointD(curPos.x() - (qreal)(width()) / 2, -curPos.y() + (qreal)(height()) / 2); - TRectD imgRect = (img->raster()) ? convert(TRect(img->raster()->getSize())) - : img->getBBox(); + TRectD imgRect = (img->raster()) ? convert(TRect(img->raster()->getSize())) + : img->getBBox(); TPointD imagePos = (img->raster()) ? TPointD(0.5 * imgRect.getLx() + pos.x, 0.5 * imgRect.getLy() + pos.y) : pos; @@ -996,25 +1000,35 @@ void ImageViewer::pickColor(QMouseEvent *event, bool putValueToStyleEditor) { TPixel32 pix = picker.pickColor(area); m_histogramPopup->updateInfo(pix, imagePos); if (putValueToStyleEditor) setPickedColorToStyleEditor(pix); - } else if (img->raster()->getPixelSize() == 8) // 16bpc raster - { + } else { // for specifying pixel range on picking vector double scale2 = getViewAff().det(); - TPixel64 pix = picker.pickColor16(pos + TPointD(-0.5, -0.5), 10.0, scale2); - - // throw the picked color to the histogram - m_histogramPopup->updateInfo(pix, imagePos); - // throw it to the style editor as well - if (putValueToStyleEditor) setPickedColorToStyleEditor(toPixel32(pix)); - } else { // 8bpc raster - // for specifying pixel range on picking vector - double scale2 = getViewAff().det(); - TPixel32 pix = picker.pickColor(pos + TPointD(-0.5, -0.5), 10.0, scale2); + TPixel32 pixForStyleEditor; + + if (img->raster()->getPixelSize() == 8) // 16bpc raster + { + TPixel64 pix = + picker.pickColor16(pos + TPointD(-0.5, -0.5), 10.0, scale2); + // throw the picked color to the histogram + m_histogramPopup->updateInfo(pix, imagePos); + pixForStyleEditor = toPixel32(pix); + } else if (img->raster()->getPixelSize() == + 16) // 32bpc floating point raster + { + TPixelF pix = + picker.pickColor32F(pos + TPointD(-0.5, -0.5), 10.0, scale2); + // throw the picked color to the histogram + m_histogramPopup->updateInfo(pix, imagePos); + pixForStyleEditor = toPixel32(pix); + } else { // 8bpc raster + TPixel32 pix = picker.pickColor(pos + TPointD(-0.5, -0.5), 10.0, scale2); + // throw the picked color to the histogram + m_histogramPopup->updateInfo(pix, imagePos); + pixForStyleEditor = pix; + } - // throw the picked color to the histogram - m_histogramPopup->updateInfo(pix, imagePos); // throw it to the style editor as well - if (putValueToStyleEditor) setPickedColorToStyleEditor(pix); + if (putValueToStyleEditor) setPickedColorToStyleEditor(pixForStyleEditor); } } @@ -1045,7 +1059,7 @@ void ImageViewer::rectPickColor(bool putValueToStyleEditor) { if (!img->raster()) { // vector image TPointD pressedWinPos = convert(m_pressedMousePos) + m_winPosMousePosOffset; TPointD startPos = TPointD(pressedWinPos.x, - (double)(window()->height()) - pressedWinPos.y); + (double)(window()->height()) - pressedWinPos.y); TPointD currentWinPos = TPointD(m_pos.x(), m_pos.y()) + m_winPosMousePosOffset; TPointD endPos = TPointD(currentWinPos.x, @@ -1078,20 +1092,28 @@ void ImageViewer::rectPickColor(bool putValueToStyleEditor) { TPointD end = getViewAff().inv() * TPointD(endPos.x - width() / 2, endPos.y - height() / 2); + TPixel32 pixForStyleEditor; if (img->raster()->getPixelSize() == 8) // 16bpc raster { TPixel64 pix = picker.pickAverageColor16(TRectD(start, end)); // throw the picked color to the histogram m_histogramPopup->updateAverageColor(pix); - // throw it to the style editor as well - if (putValueToStyleEditor) setPickedColorToStyleEditor(toPixel32(pix)); + pixForStyleEditor = toPixel32(pix); + } else if (img->raster()->getPixelSize() == + 16) // 32bpc floating point raster + { + TPixelF pix = picker.pickAverageColor32F(TRectD(start, end)); + // throw the picked color to the histogram + m_histogramPopup->updateAverageColor(pix); + pixForStyleEditor = toPixel32(pix); } else { // 8bpc raster TPixel32 pix = picker.pickAverageColor(TRectD(start, end)); // throw the picked color to the histogram m_histogramPopup->updateAverageColor(pix); - // throw it to the style editor as well - if (putValueToStyleEditor) setPickedColorToStyleEditor(pix); + pixForStyleEditor = pix; } + // throw it to the style editor as well + if (putValueToStyleEditor) setPickedColorToStyleEditor(pixForStyleEditor); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/imageviewer.h b/toonz/sources/toonz/imageviewer.h index 5b2d639..ee87d89 100644 --- a/toonz/sources/toonz/imageviewer.h +++ b/toonz/sources/toonz/imageviewer.h @@ -54,7 +54,8 @@ class ImageViewer final : public GLWidgetForHighDpi { ImagePainter::VisualSettings m_visualSettings; ImagePainter::CompareSettings m_compareSettings; - TImageP m_image; + TImageP m_image; // displayed image + TImageP m_orgImage; // image without gain adjustment TAffine m_viewAff; QPoint m_pos; bool m_isHistogramEnable; @@ -104,7 +105,7 @@ public: void setVisual(const ImagePainter::VisualSettings &settings); - void setImage(TImageP image); + void setImage(TImageP image, TImageP orgImage = TImageP()); TImageP getImage() { return m_image; } TAffine getViewAff() { return m_viewAff; } diff --git a/toonz/sources/toonz/levelsettingspopup.cpp b/toonz/sources/toonz/levelsettingspopup.cpp index 87ef32e..b5cd29c 100644 --- a/toonz/sources/toonz/levelsettingspopup.cpp +++ b/toonz/sources/toonz/levelsettingspopup.cpp @@ -137,7 +137,8 @@ public: WhiteTransp, Softness, Subsampling, - LevelType + LevelType, + ColorSpaceGamma }; private: @@ -249,6 +250,20 @@ private: TApp::instance()->getCurrentLevel()->notifyLevelChange(); } + void setColorSpaceGamma(double gamma) const { + TXshSimpleLevelP sl = m_xl->getSimpleLevel(); + if (!sl || sl->getType() != OVL_XSHLEVEL) return; + sl->getProperties()->setColorSpaceGamma(gamma); + sl->invalidateFrames(); + TApp::instance()->getCurrentScene()->setDirtyFlag(true); + TApp::instance() + ->getCurrentXsheet() + ->getXsheet() + ->getStageObjectTree() + ->invalidateAll(); + TApp::instance()->getCurrentLevel()->notifyLevelChange(); + } + void setValue(const QVariant value) const { switch (m_type) { case Name: @@ -280,6 +295,9 @@ private: case WhiteTransp: setWhiteTransp(value.toBool()); break; + case ColorSpaceGamma: + setColorSpaceGamma(value.toDouble()); + break; default: break; } @@ -360,6 +378,10 @@ LevelSettingsPopup::LevelSettingsPopup() m_softnessLabel = new QLabel(tr("Antialias Softness:"), this); m_antialiasSoftness = new DVGui::IntLineEdit(0, 10, 0, 100); + m_colorSpaceGammaLabel = new QLabel(tr("Color Space Gamma:")); + m_colorSpaceGammaFld = new DoubleLineEdit(); + m_colorSpaceGammaFld->setRange(0.1, 10.); + //---- m_pathFld->setFileMode(QFileDialog::AnyFile); @@ -423,6 +445,10 @@ LevelSettingsPopup::LevelSettingsPopup() m_activateFlags[m_subsamplingLabel] = rasterWidgetsFlag; m_activateFlags[m_subsamplingFld] = rasterWidgetsFlag; + unsigned int linearFlag = LinearRaster | MultiSelection; + m_activateFlags[m_colorSpaceGammaLabel] = linearFlag; + m_activateFlags[m_colorSpaceGammaFld] = linearFlag; + //----layout m_topLayout->setMargin(5); m_topLayout->setSpacing(5); @@ -505,6 +531,9 @@ LevelSettingsPopup::LevelSettingsPopup() bottomLay->addWidget(m_subsamplingLabel, 1, 0); bottomLay->addWidget(m_subsamplingFld, 1, 1); + + bottomLay->addWidget(m_colorSpaceGammaLabel, 2, 0); + bottomLay->addWidget(m_colorSpaceGammaFld, 2, 1); } bottomLay->setColumnStretch(0, 0); bottomLay->setColumnStretch(1, 0); @@ -537,6 +566,8 @@ LevelSettingsPopup::LevelSettingsPopup() connect(m_whiteTransp, SIGNAL(clicked(bool)), SLOT(onWhiteTranspClicked())); + connect(m_colorSpaceGammaFld, SIGNAL(editingFinished()), + SLOT(onColorSpaceGammaFieldChanged())); updateLevelSettings(); } @@ -642,8 +673,12 @@ SelectedLevelType LevelSettingsPopup::getType(TXshLevelP level_p) { switch (level_p->getType()) { case TZP_XSHLEVEL: return ToonzRaster; - case OVL_XSHLEVEL: - return Raster; + case OVL_XSHLEVEL: { + if (level_p->getPath().getType() == "exr") + return LinearRaster; + else + return NonLinearRaster; + } case MESH_XSHLEVEL: return Mesh; case PLI_XSHLEVEL: @@ -683,7 +718,8 @@ LevelSettingsValues LevelSettingsPopup::getValues(TXshLevelP level) { case ToonzRaster: values.typeStr = tr("Toonz Raster level"); break; - case Raster: + case NonLinearRaster: + case LinearRaster: values.typeStr = tr("Raster level"); break; case Mesh: @@ -718,7 +754,7 @@ LevelSettingsValues LevelSettingsPopup::getValues(TXshLevelP level) { // image dpi values.imageDpi = dpiToString(sl->getImageDpi()); - if (levelType == ToonzRaster || levelType == Raster) { + if (levelType == ToonzRaster || levelType & Raster) { // size field TDimensionD size(0, 0); TDimension res = sl->getResolution(); @@ -748,6 +784,9 @@ LevelSettingsValues LevelSettingsPopup::getValues(TXshLevelP level) { ? Qt::Checked : Qt::Unchecked; values.softness = sl->getProperties()->antialiasSoftness(); + + if (levelType == LinearRaster) + values.colorSpaceGamma = sl->getProperties()->colorSpaceGamma(); } } @@ -883,6 +922,7 @@ void LevelSettingsPopup::updateLevelSettings() { uniteValue(values.doAntialias, new_val.doAntialias, isFirst); uniteValue(values.softness, new_val.softness, isFirst); uniteValue(values.subsampling, new_val.subsampling, isFirst); + uniteValue(values.colorSpaceGamma, new_val.colorSpaceGamma, isFirst); uniteValue(values.isDirty, new_val.isDirty, isFirst); ++lvl_itr; @@ -920,11 +960,22 @@ void LevelSettingsPopup::updateLevelSettings() { m_subsamplingFld->setText(""); else m_subsamplingFld->setValue(values.subsampling); + + if (values.colorSpaceGamma > 0) + m_colorSpaceGammaFld->setValue(values.colorSpaceGamma); + else if (m_colorSpaceGammaFld->isEnabled()) + m_colorSpaceGammaFld->setText(tr("[Various]")); + else + m_colorSpaceGammaFld->setText(""); + // disable the softness field when the antialias is not active if (m_doAntialias->checkState() != Qt::Checked) m_antialiasSoftness->setDisabled(true); // disable the subsampling field when dirty level is selected - if (values.isDirty != Qt::Unchecked) m_subsamplingFld->setDisabled(true); + if (values.isDirty != Qt::Unchecked) { + m_subsamplingFld->setDisabled(true); + m_colorSpaceGammaFld->setDisabled(true); + } } //----------------------------------------------------------------------------- @@ -1576,6 +1627,42 @@ void LevelSettingsPopup::onWhiteTranspClicked() { //----------------------------------------------------------------------------- +void LevelSettingsPopup::onColorSpaceGammaFieldChanged() { + double colorSpaceGamma = m_colorSpaceGammaFld->getValue(); + + bool somethingChanged = false; + TUndoManager::manager()->beginBlock(); + QSetIterator levelItr(m_selectedLevels); + while (levelItr.hasNext()) { + TXshLevelP levelP = levelItr.next(); + TXshSimpleLevelP sl = levelP->getSimpleLevel(); + if (!sl || sl->getType() != OVL_XSHLEVEL) continue; + if (sl->getProperties()->getDirtyFlag()) continue; + + double oldGamma = sl->getProperties()->colorSpaceGamma(); + if (areAlmostEqual(colorSpaceGamma, oldGamma)) continue; + + sl->getProperties()->setColorSpaceGamma(colorSpaceGamma); + sl->invalidateFrames(); + TUndoManager::manager()->add(new LevelSettingsUndo( + levelP.getPointer(), LevelSettingsUndo::ColorSpaceGamma, oldGamma, + colorSpaceGamma)); + somethingChanged = true; + } + TUndoManager::manager()->endBlock(); + if (!somethingChanged) return; + + TApp::instance()->getCurrentScene()->setDirtyFlag(true); + TApp::instance() + ->getCurrentXsheet() + ->getXsheet() + ->getStageObjectTree() + ->invalidateAll(); + TApp::instance()->getCurrentLevel()->notifyLevelChange(); +} + +//----------------------------------------------------------------------------- + OpenPopupCommandHandler openLevelSettingsPopup( MI_LevelSettings); diff --git a/toonz/sources/toonz/levelsettingspopup.h b/toonz/sources/toonz/levelsettingspopup.h index e10d36d..d5f3a68 100644 --- a/toonz/sources/toonz/levelsettingspopup.h +++ b/toonz/sources/toonz/levelsettingspopup.h @@ -30,20 +30,22 @@ class CheckBox; } // namespace DVGui enum SelectedLevelType { - None = 0x0, - ToonzRaster = 0x1, - Raster = 0x2, - Mesh = 0x4, - ToonzVector = 0x8, - Palette = 0x10, - SubXsheet = 0x20, - Sound = 0x40, - Others = 0x80, - - MultiSelection = 0x100, - HideOnPixelMode = 0x200, - NoSelection = 0x400, - + None = 0x0, + ToonzRaster = 0x1, + NonLinearRaster = 0x2, + Mesh = 0x4, + ToonzVector = 0x8, + Palette = 0x10, + SubXsheet = 0x20, + Sound = 0x40, + LinearRaster = 0x80, // EXR files only enables the "Color Space Gamma" field + Others = 0x100, + + MultiSelection = 0x1000, + HideOnPixelMode = 0x2000, + NoSelection = 0x4000, + + Raster = NonLinearRaster | LinearRaster, SimpleLevel = ToonzRaster | Raster | Mesh | ToonzVector, HasDPILevel = ToonzRaster | Raster | Mesh, AllTypes = SimpleLevel | Palette | SubXsheet | Sound @@ -55,7 +57,7 @@ struct LevelSettingsValues { TPointD dpi = TPointD(0, 0); Qt::CheckState doPremulti = Qt::Unchecked, whiteTransp = Qt::Unchecked, doAntialias = Qt::Unchecked, isDirty = Qt::Unchecked; - double width = 0.0, height = 0.0; + double width = 0.0, height = 0.0, colorSpaceGamma = -1.0; }; //============================================================================= @@ -90,6 +92,9 @@ class LevelSettingsPopup final : public DVGui::Dialog { QLabel *m_subsamplingLabel; DVGui::IntLineEdit *m_subsamplingFld; + QLabel *m_colorSpaceGammaLabel; + DVGui::DoubleLineEdit *m_colorSpaceGammaFld; + SelectedLevelType getType(TXshLevelP); LevelSettingsValues getValues(TXshLevelP); @@ -119,6 +124,7 @@ protected slots: void onDoAntialiasClicked(); void onAntialiasSoftnessChanged(); void onWhiteTranspClicked(); + void onColorSpaceGammaFieldChanged(); void onSceneChanged(); void onPreferenceChanged(const QString &); }; diff --git a/toonz/sources/toonz/outputsettingspopup.cpp b/toonz/sources/toonz/outputsettingspopup.cpp index e286cac..d4ef329 100644 --- a/toonz/sources/toonz/outputsettingspopup.cpp +++ b/toonz/sources/toonz/outputsettingspopup.cpp @@ -133,7 +133,7 @@ ResampleOption quality2index(TRenderSettings::ResampleQuality quality) { return c_standard; } -enum ChannelWidth { c_8bit, c_16bit }; +enum ChannelWidth { c_8bit, c_16bit, c_32bit }; // 32bit: compute in float enum DominantField { c_odd, c_even, c_none }; @@ -181,7 +181,8 @@ OutputSettingsPopup::OutputSettingsPopup(bool isPreview) , m_outputCameraOm(nullptr) , m_isPreviewSettings(isPreview) , m_allowMT(Preferences::instance()->getFfmpegMultiThread()) - , m_presetCombo(nullptr) { + , m_presetCombo(nullptr) + , m_syncColorSettingsButton(nullptr) { setWindowTitle(isPreview ? tr("Preview Settings") : tr("Output Settings")); if (!isPreview) setObjectName("OutputSettingsPopup"); // create panel @@ -204,14 +205,15 @@ OutputSettingsPopup::OutputSettingsPopup(bool isPreview) QString tooltip = tr("Save current output settings.\nThe parameters to be saved are:\n- " "Camera settings\n- Project folder to be saved in\n- File format\n- " - "File options\n- Resample Balance\n- Channel width"); + "File options\n- Resample Balance\n- Channel width\n- Linear Color " + "Space\n- Color Space Gamma"); addPresetButton->setToolTip(tooltip); /*-- プリセットフォルダを調べ、コンボボックスにアイテムを格納する --*/ updatePresetComboItems(); QListWidget *categoryList = new QListWidget(this); QStringList categories; - categories << tr("Camera") << tr("File") << tr("More"); + categories << tr("Camera") << tr("Color") << tr("File") << tr("More"); categoryList->addItems(categories); categoryList->setFixedWidth(100); categoryList->setCurrentRow(0); @@ -275,6 +277,9 @@ void OutputSettingsPopup::onCategoryActivated(QListWidgetItem *item) { if (item->text() == tr("Camera")) { label = m_cameraLabel; frame = m_cameraBox; + } else if (item->text() == tr("Color")) { + label = m_colorLabel; + frame = m_colorBox; } else if (item->text() == tr("File")) { label = m_fileLabel; frame = m_fileBox; @@ -299,9 +304,14 @@ QFrame *OutputSettingsPopup::createPanel(bool isPreview) { QFrame *panel = new QFrame(this); m_cameraLabel = new AnimatedLabel(tr("Camera Settings"), this); - m_fileLabel = new AnimatedLabel(tr("File Settings"), this); - m_cameraBox = createCameraSettingsBox(isPreview); - m_fileBox = createFileSettingsBox(isPreview); + m_colorLabel = new AnimatedLabel(tr("Color Settings"), this); + if (isPreview) + m_syncColorSettingsButton = + new DVGui::CheckBox(tr("Sync with Output Settings")); + m_fileLabel = new AnimatedLabel(tr("File Settings"), this); + m_cameraBox = createCameraSettingsBox(isPreview); + m_colorBox = createColorSettingsBox(isPreview); + m_fileBox = createFileSettingsBox(isPreview); if (!isPreview) { m_moreLabel = new AnimatedLabel(tr("More Settings"), this); m_moreBox = createMoreSettingsBox(); @@ -314,6 +324,19 @@ QFrame *OutputSettingsPopup::createPanel(bool isPreview) { lay->addWidget(m_cameraBox, 0); lay->addSpacing(10); + QHBoxLayout *colorLabelLay = new QHBoxLayout(); + colorLabelLay->setMargin(0); + colorLabelLay->setSpacing(0); + { + colorLabelLay->addWidget(m_colorLabel, 0); + colorLabelLay->addStretch(1); + if (isPreview && m_syncColorSettingsButton) + colorLabelLay->addWidget(m_syncColorSettingsButton, 0); + } + lay->addLayout(colorLabelLay, 0); + lay->addWidget(m_colorBox, 0); + lay->addSpacing(10); + lay->addWidget(m_fileLabel, 0); lay->addWidget(m_fileBox, 0); @@ -325,6 +348,13 @@ QFrame *OutputSettingsPopup::createPanel(bool isPreview) { lay->addStretch(1); } panel->setLayout(lay); + + if (isPreview && m_syncColorSettingsButton) { + bool ret = connect(m_syncColorSettingsButton, SIGNAL(stateChanged(int)), + SLOT(onSyncColorSettingsChecked(int))); + assert(ret); + } + return panel; } @@ -447,14 +477,86 @@ QFrame *OutputSettingsPopup::createCameraSettingsBox(bool isPreview) { //----------------------------------------------------------------------------- +QFrame *OutputSettingsPopup::createColorSettingsBox(bool isPreview) { + QFrame *colorSettingsBox = new QFrame(this); + colorSettingsBox->setObjectName("OutputSettingsBox"); + + // Channel Width + m_channelWidthOm = new QComboBox(); + // Linear Color Space + m_linearColorSpaceChk = new DVGui::CheckBox(); + // color space gamma + m_colorSpaceGammaFld = new DVGui::DoubleLineEdit(this, 2.2); + + // Channel Width + m_channelWidthOm->addItem(tr("8 bit"), "8 bit"); + m_channelWidthOm->addItem(tr("16 bit"), "16 bit"); + m_channelWidthOm->addItem(tr("32 bit Floating point"), "32 bit"); + + m_linearColorSpaceChk->setToolTip( + tr("On rendering, color values will be temporarily converted to linear " + "light from nonlinear RGB values by using color space gamma.")); + if (m_isPreviewSettings) + m_colorSpaceGammaFld->setRange(-1.0, 5.0); + else + m_colorSpaceGammaFld->setRange(1.0, 5.0); + m_colorSpaceGammaFld->setDecimals(3); + QString colorSpaceGammaTooltip = + tr("Color Space Gamma value is used for conversion between the linear " + "and nonlinear color spaces,\n" + "when the \"Linear Color Space\" option is enabled."); + if (m_isPreviewSettings) + colorSpaceGammaTooltip += + tr("\nInput less than 1.0 to sync the value with the output settings."); + m_colorSpaceGammaFld->setToolTip(colorSpaceGammaTooltip); + + if (!isPreview) { + m_channelWidthOm->setFocusPolicy(Qt::StrongFocus); + } + + QGridLayout *gridLay = new QGridLayout(); + gridLay->setMargin(5); + gridLay->setHorizontalSpacing(5); + gridLay->setVerticalSpacing(10); + { + // Channel Width + gridLay->addWidget(new QLabel(tr("Channel Width:"), this), 0, 0, + Qt::AlignRight | Qt::AlignVCenter); + gridLay->addWidget(m_channelWidthOm, 0, 1, 1, 2, + Qt::AlignLeft | Qt::AlignVCenter); + + // Linear Color Space and Color SpaceGamma + gridLay->addWidget(new QLabel(tr("Linear Color Space:"), this), 1, 0, + Qt::AlignRight | Qt::AlignVCenter); + gridLay->addWidget(m_linearColorSpaceChk, 1, 1, + Qt::AlignLeft | Qt::AlignVCenter); + gridLay->addWidget(new QLabel(tr("Color Space Gamma:"), this), 1, 2, + Qt::AlignRight | Qt::AlignVCenter); + gridLay->addWidget(m_colorSpaceGammaFld, 1, 3); + } + gridLay->setColumnStretch(2, 1); + colorSettingsBox->setLayout(gridLay); + + bool ret = true; + ret = ret && connect(m_channelWidthOm, SIGNAL(currentIndexChanged(int)), + SLOT(onChannelWidthChanged(int))); + ret = ret && connect(m_linearColorSpaceChk, SIGNAL(stateChanged(int)), + SLOT(onLinearColorSpaceChecked(int))); + + ret = ret && connect(m_colorSpaceGammaFld, SIGNAL(editingFinished()), + SLOT(onColorSpaceGammaEdited())); + assert(ret); + return colorSettingsBox; +} + +//----------------------------------------------------------------------------- + QFrame *OutputSettingsPopup::createFileSettingsBox(bool isPreview) { QFrame *fileSettingsBox = new QFrame(this); fileSettingsBox->setObjectName("OutputSettingsBox"); // Resample Balance m_resampleBalanceOm = new QComboBox(); - // Channel Width - m_channelWidthOm = new QComboBox(); // Threads m_threadsComboOm = new QComboBox(); // Granularity @@ -466,9 +568,6 @@ QFrame *OutputSettingsPopup::createFileSettingsBox(bool isPreview) { m_resampleBalanceOm->addItem(resampleInfoMap[(ResampleOption)i].uiString, resampleInfoMap[(ResampleOption)i].idString); } - // Channel Width - m_channelWidthOm->addItem(tr("8 bit"), "8 bit"); - m_channelWidthOm->addItem(tr("16 bit"), "16 bit"); QStringList threadsChoices; threadsChoices << tr("Single") << tr("Half") << tr("All"); @@ -496,7 +595,6 @@ QFrame *OutputSettingsPopup::createFileSettingsBox(bool isPreview) { m_fileFormat->addItems(formats); m_fileFormat->setFocusPolicy(Qt::StrongFocus); m_resampleBalanceOm->setFocusPolicy(Qt::StrongFocus); - m_channelWidthOm->setFocusPolicy(Qt::StrongFocus); m_threadsComboOm->setFocusPolicy(Qt::StrongFocus); m_rasterGranularityOm->setFocusPolicy(Qt::StrongFocus); m_fileFormat->installEventFilter(this); @@ -545,20 +643,17 @@ QFrame *OutputSettingsPopup::createFileSettingsBox(bool isPreview) { Qt::AlignRight | Qt::AlignVCenter); bottomGridLay->addWidget(m_resampleBalanceOm, 0, 1, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); - // Channel Width - bottomGridLay->addWidget(new QLabel(tr("Channel Width:"), this), 1, 0, - Qt::AlignRight | Qt::AlignVCenter); - bottomGridLay->addWidget(m_channelWidthOm, 1, 1); + // Threads - bottomGridLay->addWidget(new QLabel(tr("Dedicated CPUs:"), this), 2, 0, + bottomGridLay->addWidget(new QLabel(tr("Dedicated CPUs:"), this), 1, 0, Qt::AlignRight | Qt::AlignVCenter); - bottomGridLay->addWidget(m_threadsComboOm, 2, 1); + bottomGridLay->addWidget(m_threadsComboOm, 1, 1); // Granularity - bottomGridLay->addWidget(new QLabel(tr("Render Tile:"), this), 3, 0, + bottomGridLay->addWidget(new QLabel(tr("Render Tile:"), this), 2, 0, Qt::AlignRight | Qt::AlignVCenter); - bottomGridLay->addWidget(m_rasterGranularityOm, 3, 1); + bottomGridLay->addWidget(m_rasterGranularityOm, 2, 1); if (m_subcameraChk) { - bottomGridLay->addWidget(m_subcameraChk, 4, 1, 1, 2); + bottomGridLay->addWidget(m_subcameraChk, 3, 1, 1, 2); } } bottomGridLay->setColumnStretch(2, 1); @@ -583,8 +678,7 @@ QFrame *OutputSettingsPopup::createFileSettingsBox(bool isPreview) { } ret = ret && connect(m_resampleBalanceOm, SIGNAL(currentIndexChanged(int)), SLOT(onResampleChanged(int))); - ret = ret && connect(m_channelWidthOm, SIGNAL(currentIndexChanged(int)), - SLOT(onChannelWidthChanged(int))); + ret = ret && connect(m_threadsComboOm, SIGNAL(currentIndexChanged(int)), SLOT(onThreadsComboChanged(int))); ret = ret && connect(m_rasterGranularityOm, SIGNAL(currentIndexChanged(int)), @@ -834,6 +928,10 @@ void OutputSettingsPopup::updateField() { m_rasterGranularityOm->setCurrentIndex(0); if (m_subcameraChk) m_subcameraChk->setCheckState(Qt::Unchecked); + m_linearColorSpaceChk->setCheckState(Qt::Unchecked); + m_colorSpaceGammaFld->setText(QString()); + if (m_syncColorSettingsButton) + m_syncColorSettingsButton->setCheckState(Qt::Unchecked); return; } @@ -918,11 +1016,27 @@ void OutputSettingsPopup::updateField() { case 64: m_channelWidthOm->setCurrentIndex(c_16bit); break; + case 128: + m_channelWidthOm->setCurrentIndex(c_32bit); + break; default: m_channelWidthOm->setCurrentIndex(c_8bit); break; } + m_linearColorSpaceChk->setCheckState( + renderSettings.m_linearColorSpace ? Qt::Checked : Qt::Unchecked); + // currently bpp should be 128 when the linearColorSpace is ON + m_channelWidthOm->setDisabled(renderSettings.m_linearColorSpace && + renderSettings.m_bpp == 128); + + m_colorSpaceGammaFld->setValue(renderSettings.m_colorSpaceGamma); + + if (m_isPreviewSettings && m_syncColorSettingsButton) { + m_syncColorSettingsButton->setChecked( + getProperties()->isColorSettingsSynced()); + } + // Threads m_threadsComboOm->setCurrentIndex(prop->getThreadIndex()); @@ -1195,6 +1309,29 @@ void OutputSettingsPopup::onStereoChanged() { //---------------------------------------------- +void OutputSettingsPopup::onSyncColorSettingsChecked(int state) { + assert(m_isPreviewSettings); + bool doSync = (state == Qt::Checked); + m_colorBox->setDisabled(doSync); + getProperties()->syncColorSettings(doSync); + // take values from the output settings + if (doSync) { + TRenderSettings out_rs = getCurrentScene() + ->getProperties() + ->getOutputProperties() + ->getRenderSettings(); + TRenderSettings rs = getProperties()->getRenderSettings(); + rs.m_bpp = out_rs.m_bpp; + rs.m_linearColorSpace = out_rs.m_linearColorSpace; + rs.m_colorSpaceGamma = out_rs.m_colorSpaceGamma; + getProperties()->setRenderSettings(rs); + } + // dirty flag will be set here + TApp::instance()->getCurrentScene()->notifySceneChanged(); +} + +//---------------------------------------------- + void OutputSettingsPopup::onFrameFldEditFinished() { ToonzScene *scene = getCurrentScene(); if (!scene) return; @@ -1291,12 +1428,103 @@ void OutputSettingsPopup::onChannelWidthChanged(int type) { int old_bpp = rs.m_bpp; if (type == c_8bit) rs.m_bpp = 32; - else + else if (type == c_16bit) rs.m_bpp = 64; + else + rs.m_bpp = 128; + if (rs.m_bpp == old_bpp) return; + prop->setRenderSettings(rs); - TApp::instance()->getCurrentScene()->setDirtyFlag(true); + + // sync output settings value to the preview settings + if (!m_isPreviewSettings && getCurrentScene() + ->getProperties() + ->getPreviewProperties() + ->isColorSettingsSynced()) { + TRenderSettings prev_rs = getCurrentScene() + ->getProperties() + ->getPreviewProperties() + ->getRenderSettings(); + prev_rs.m_bpp = rs.m_bpp; + getCurrentScene() + ->getProperties() + ->getPreviewProperties() + ->setRenderSettings(prev_rs); + } + + if (m_presetCombo) m_presetCombo->setCurrentIndex(0); + // dirty flag will be set here + TApp::instance()->getCurrentScene()->notifySceneChanged(); +} +//----------------------------------------------------------------------------- + +void OutputSettingsPopup::onLinearColorSpaceChecked(int state) { + if (!getCurrentScene()) return; + TOutputProperties *prop = getProperties(); + TRenderSettings rs = prop->getRenderSettings(); + rs.m_linearColorSpace = (state == Qt::Checked); + // force floating point when compute in linear color space + if (rs.m_linearColorSpace) { + prop->setNonlinearBpp(rs.m_bpp); + rs.m_bpp = 128; + } else + rs.m_bpp = prop->getNonlinearBpp(); + prop->setRenderSettings(rs); + + // sync output settings value to the preview settings + TOutputProperties *prev_prop = + getCurrentScene()->getProperties()->getPreviewProperties(); + if (!m_isPreviewSettings && prev_prop->isColorSettingsSynced()) { + TRenderSettings prev_rs = prev_prop->getRenderSettings(); + prev_rs.m_linearColorSpace = (state == Qt::Checked); + if (prev_rs.m_linearColorSpace) { + prev_prop->setNonlinearBpp(prev_rs.m_bpp); + prev_rs.m_bpp = 128; + } else + prev_rs.m_bpp = prev_prop->getNonlinearBpp(); + getCurrentScene() + ->getProperties() + ->getPreviewProperties() + ->setRenderSettings(prev_rs); + } + + if (m_presetCombo) m_presetCombo->setCurrentIndex(0); + // dirty flag will be set here + TApp::instance()->getCurrentScene()->notifySceneChanged(); +} + +//----------------------------------------------------------------------------- + +void OutputSettingsPopup::onColorSpaceGammaEdited() { + if (!getCurrentScene()) return; + TOutputProperties *prop = getProperties(); + TRenderSettings rs = prop->getRenderSettings(); + + double colorSpaceGamma = m_colorSpaceGammaFld->getValue(); + + rs.m_colorSpaceGamma = colorSpaceGamma; + prop->setRenderSettings(rs); + + // sync output settings value to the preview settings + if (!m_isPreviewSettings && getCurrentScene() + ->getProperties() + ->getPreviewProperties() + ->isColorSettingsSynced()) { + TRenderSettings prev_rs = getCurrentScene() + ->getProperties() + ->getPreviewProperties() + ->getRenderSettings(); + prev_rs.m_colorSpaceGamma = colorSpaceGamma; + getCurrentScene() + ->getProperties() + ->getPreviewProperties() + ->setRenderSettings(prev_rs); + } + if (m_presetCombo) m_presetCombo->setCurrentIndex(0); + // dirty flag will be set here + TApp::instance()->getCurrentScene()->notifySceneChanged(); } //----------------------------------------------------------------------------- @@ -1488,6 +1716,10 @@ void OutputSettingsPopup::onAddPresetButtonPressed() { // Channel Width QString chanw = m_channelWidthOm->currentData().toString(); os.child("bpp") << chanw.toStdString(); + // Linear Color Space & Color Space Gamma + std::string linearStr = (m_linearColorSpaceChk->isChecked()) ? "1" : "0"; + os.child("linearColorSpace") << linearStr; + os.child("colorSpaceGamma") << m_colorSpaceGammaFld->text().toStdString(); // 140503 iwasawa Frame Rate (Scene Settings) os.child("frameRate") << m_frameRateFld->text().toStdString(); @@ -1605,6 +1837,13 @@ void OutputSettingsPopup::onPresetSelected(const QString &str) { TOutputProperties *prop = getProperties(); TRenderSettings rs = prop->getRenderSettings(); + + // set back the linear settings to default + // in order to make old presets properly reproduce the settings before the + // implementation of linear rendering + rs.m_linearColorSpace = false; + rs.m_colorSpaceGamma = 2.2; + while (is.matchTag(tagName)) { // Camera if (tagName == "camera") { @@ -1701,10 +1940,22 @@ void OutputSettingsPopup::onPresetSelected(const QString &str) { m_channelWidthOm->setCurrentIndex(index); if (index == c_8bit) rs.m_bpp = 32; - else + else if (index == c_16bit) rs.m_bpp = 64; + else + rs.m_bpp = 128; } } + // Linear Color Space & Color Space Gamma + else if (tagName == "linearColorSpace") { + std::string linearStr; + is >> linearStr; + rs.m_linearColorSpace = (linearStr != "0"); + } else if (tagName == "colorSpaceGamma") { + std::string gamma; + is >> gamma; + rs.m_colorSpaceGamma = QString::fromStdString(gamma).toDouble(); + } // Frame Rate (Scene Settings) else if (tagName == "frameRate") { diff --git a/toonz/sources/toonz/outputsettingspopup.h b/toonz/sources/toonz/outputsettingspopup.h index 2aab697..f646c4f 100644 --- a/toonz/sources/toonz/outputsettingspopup.h +++ b/toonz/sources/toonz/outputsettingspopup.h @@ -58,6 +58,8 @@ class OutputSettingsPopup : public DVGui::Dialog { QComboBox *m_multimediaOm; QComboBox *m_resampleBalanceOm; QComboBox *m_channelWidthOm; + DVGui::CheckBox *m_linearColorSpaceChk; + DVGui::DoubleLineEdit *m_colorSpaceGammaFld; DVGui::DoubleLineEdit *m_gammaFld; QComboBox *m_dominantFieldOm; DVGui::CheckBox *m_applyShrinkChk; @@ -79,8 +81,10 @@ class OutputSettingsPopup : public DVGui::Dialog { QPushButton *m_boardSettingsBtn; QScrollArea *m_scrollArea; - AnimatedLabel *m_cameraLabel, *m_fileLabel, *m_moreLabel; - QFrame *m_cameraBox, *m_fileBox, *m_moreBox; + AnimatedLabel *m_cameraLabel, *m_colorLabel, *m_fileLabel, *m_moreLabel; + QFrame *m_cameraBox, *m_colorBox, *m_fileBox, *m_moreBox; + + DVGui::CheckBox *m_syncColorSettingsButton; bool m_isPreviewSettings; @@ -89,6 +93,7 @@ class OutputSettingsPopup : public DVGui::Dialog { QFrame *createPanel(bool isPreview); QFrame *createCameraSettingsBox(bool isPreview); + QFrame *createColorSettingsBox(bool isPreview); QFrame *createFileSettingsBox(bool isPreview); QFrame *createMoreSettingsBox(); @@ -113,6 +118,8 @@ protected slots: void onFrameFldEditFinished(); void onResampleChanged(int type); void onChannelWidthChanged(int type); + void onLinearColorSpaceChecked(int state); + void onColorSpaceGammaEdited(); void onGammaFldEditFinished(); void onDominantFieldChanged(int type); void onStretchFldEditFinished(); @@ -123,6 +130,7 @@ protected slots: void onRasterGranularityChanged(int type); void onStereoChecked(int); void onStereoChanged(); + void onSyncColorSettingsChecked(int state); void onRenderClicked(); /*-- OutputSettingsのPreset登録/削除/選択 --*/ diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index 3ac59d3..bde6c83 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -198,6 +198,13 @@ PreferencesPopup::FormatProperties::FormatProperties(PreferencesPopup* parent) m_subsampling = new DVGui::IntLineEdit(this, 1, 1); gridLayout->addWidget(m_subsampling, row++, 1); + QLabel* gammaLabel = new QLabel(LevelSettingsPopup::tr("Color Space Gamma:")); + gridLayout->addWidget(gammaLabel, row, 0, Qt::AlignRight); + + m_colorSpaceGamma = new DVGui::DoubleLineEdit(this); + m_colorSpaceGamma->setRange(0.1, 10.); + gridLayout->addWidget(m_colorSpaceGamma, row++, 1); + addLayout(gridLayout); endVLayout(); @@ -205,6 +212,10 @@ PreferencesPopup::FormatProperties::FormatProperties(PreferencesPopup* parent) // Establish connections bool ret = true; + // enable gamma field only when the regexp field contains ".exr" + ret = connect(m_regExp, SIGNAL(editingFinished()), + SLOT(updateEnabledStatus())) && + ret; ret = connect(m_dpiPolicy, SIGNAL(currentIndexChanged(int)), SLOT(updateEnabledStatus())) && ret; @@ -220,6 +231,9 @@ PreferencesPopup::FormatProperties::FormatProperties(PreferencesPopup* parent) void PreferencesPopup::FormatProperties::updateEnabledStatus() { m_dpi->setEnabled(m_dpiPolicy->currentIndex() == DP_CustomDpi); m_antialias->setEnabled(m_doAntialias->isChecked()); + + // enable gamma field only when the regexp field contains ".exr" + m_colorSpaceGamma->setEnabled(m_regExp->text().contains(".exr")); } //----------------------------------------------------------------------------- @@ -240,6 +254,7 @@ void PreferencesPopup::FormatProperties::setLevelFormat( m_doAntialias->setChecked(lo.m_antialias > 0); m_antialias->setValue(lo.m_antialias); m_subsampling->setValue(lo.m_subsampling); + m_colorSpaceGamma->setValue(lo.m_colorSpaceGamma); updateEnabledStatus(); } @@ -265,6 +280,9 @@ Preferences::LevelFormat PreferencesPopup::FormatProperties::levelFormat() lf.m_options.m_premultiply = m_premultiply->isChecked(); lf.m_options.m_whiteTransp = m_whiteTransp->isChecked(); + if (m_colorSpaceGamma->isEnabled()) + lf.m_options.m_colorSpaceGamma = m_colorSpaceGamma->getValue(); + return lf; } @@ -374,7 +392,7 @@ PreferencesPopup::Display30bitChecker::Display30bitChecker( GLView* view10bit = new GLView(this, true); QPushButton* closeBtn = new QPushButton(tr("Close"), this); QString infoLabel = tr( - "If the lower gradient looks smooth and has no banding compared to the upper gradient,\n\ + "If the lower gradient looks smooth and has no banding compared to the upper gradient,\n\ 30bit display is available in the current configuration."); QVBoxLayout* lay = new QVBoxLayout(); diff --git a/toonz/sources/toonz/preferencespopup.h b/toonz/sources/toonz/preferencespopup.h index 7e15e49..25c2628 100644 --- a/toonz/sources/toonz/preferencespopup.h +++ b/toonz/sources/toonz/preferencespopup.h @@ -194,7 +194,7 @@ private: DVGui::LineEdit *m_name, *m_regExp; - DVGui::DoubleLineEdit* m_dpi; + DVGui::DoubleLineEdit *m_dpi, *m_colorSpaceGamma; DVGui::IntLineEdit *m_priority, *m_subsampling, *m_antialias; diff --git a/toonz/sources/toonz/previewer.cpp b/toonz/sources/toonz/previewer.cpp index bc96311..7192ebe 100644 --- a/toonz/sources/toonz/previewer.cpp +++ b/toonz/sources/toonz/previewer.cpp @@ -393,13 +393,11 @@ void Previewer::Imp::updateProgressBarStatus() { unsigned int i, pbSize = m_pbStatus.size(); std::map::iterator it; for (i = 0; i < pbSize; ++i) { - it = m_frames.find(i); - m_pbStatus[i] = - (it == m_frames.end()) - ? FlipSlider::PBFrameNotStarted - : ::contains(it->second.m_renderedRegion, m_previewRect) - ? FlipSlider::PBFrameFinished - : it->second.m_rectUnderRender.contains(m_previewRect) + it = m_frames.find(i); + m_pbStatus[i] = (it == m_frames.end()) ? FlipSlider::PBFrameNotStarted + : ::contains(it->second.m_renderedRegion, m_previewRect) + ? FlipSlider::PBFrameFinished + : it->second.m_rectUnderRender.contains(m_previewRect) ? FlipSlider::PBFrameStarted : FlipSlider::PBFrameNotStarted; } @@ -675,6 +673,11 @@ void Previewer::Imp::doOnRenderRasterCompleted(const RenderData &renderData) { TRasterP ras(renderData.m_rasA); + // Linear Color Space -> sRGB + if (ras->isLinear()) { + TRop::tosRGB(ras, m_renderSettings.m_colorSpaceGamma); + } + m_computingFrameCount--; // Find the render infos in the Previewer @@ -1152,7 +1155,7 @@ TRasterP Previewer::getRaster(int frame, bool renderIfNeeded) const { (TRasterImageP)TImageCache::instance()->get(str, false); if (rimg) { TRasterP ras = rimg->getRaster(); - assert((TRaster32P)ras || (TRaster64P)ras); + assert((TRaster32P)ras || (TRaster64P)ras || (TRasterFP)ras); return ras; } else // Weird case - the frame was declared rendered, but no raster is @@ -1171,7 +1174,7 @@ TRasterP Previewer::getRaster(int frame, bool renderIfNeeded) const { (TRasterImageP)TImageCache::instance()->get(str, false); if (rimg) { TRasterP ras = rimg->getRaster(); - assert((TRaster32P)ras || (TRaster64P)ras); + assert((TRaster32P)ras || (TRaster64P)ras || (TRasterFP)ras); return ras; } else return TRasterP(); diff --git a/toonz/sources/toonz/previewfxmanager.cpp b/toonz/sources/toonz/previewfxmanager.cpp index 169204a..e8df158 100644 --- a/toonz/sources/toonz/previewfxmanager.cpp +++ b/toonz/sources/toonz/previewfxmanager.cpp @@ -713,7 +713,7 @@ void PreviewFxInstance::updateRenderSettings() { m_subcamera = properties->isSubcameraPreview(); - const TRenderSettings &renderSettings = properties->getRenderSettings(); + TRenderSettings renderSettings = properties->getRenderSettings(); if (m_renderSettings != renderSettings) { m_renderSettings = renderSettings; @@ -1067,11 +1067,19 @@ void PreviewFxInstance::doOnRenderRasterCompleted( else ras = 0; - /*-- 16bpcで計算された場合、結果をDitheringする --*/ TRasterP rasA = renderData.m_rasA; TRasterP rasB = renderData.m_rasB; + + // Linear Color Space -> sRGB + if (rasA->isLinear()) { + TRop::tosRGB(rasA, m_renderSettings.m_colorSpaceGamma); + if (m_renderSettings.m_stereoscopic) + TRop::tosRGB(rasB, m_renderSettings.m_colorSpaceGamma); + } + + /*-- 16bpcで計算された場合、結果をDitheringする --*/ // dither the 16bpc image IF the "30bit display" preference option is OFF - if (rasA->getPixelSize() == 8 && + if ((rasA->getPixelSize() == 8 || rasA->getPixelSize() == 16) && !Preferences::instance()->is30bitDisplayEnabled()) // render in 64 bits { TRaster32P auxA(rasA->getLx(), rasA->getLy()); diff --git a/toonz/sources/toonz/scenesettingspopup.cpp b/toonz/sources/toonz/scenesettingspopup.cpp index 52fa68f..b3bddb5 100644 --- a/toonz/sources/toonz/scenesettingspopup.cpp +++ b/toonz/sources/toonz/scenesettingspopup.cpp @@ -339,7 +339,7 @@ SceneSettingsPopup::SceneSettingsPopup() mainLayout->setColumnStretch(2, 0); mainLayout->setColumnStretch(3, 0); mainLayout->setColumnStretch(4, 1); - mainLayout->setRowStretch(7, 1); + mainLayout->setRowStretch(9, 1); setLayout(mainLayout); // signal-slot connections diff --git a/toonz/sources/toonz/scenesettingspopup.h b/toonz/sources/toonz/scenesettingspopup.h index 6108497..7ea1756 100644 --- a/toonz/sources/toonz/scenesettingspopup.h +++ b/toonz/sources/toonz/scenesettingspopup.h @@ -58,6 +58,8 @@ class SceneSettingsPopup final : public QDialog { CellMarksPopup *m_cellMarksPopup; + DVGui::DoubleLineEdit *m_colorSpaceGammaFld; + public: SceneSettingsPopup(); void configureNotify(); diff --git a/toonz/sources/toonz/viewerpane.cpp b/toonz/sources/toonz/viewerpane.cpp index b2ef1f5..6238d77 100644 --- a/toonz/sources/toonz/viewerpane.cpp +++ b/toonz/sources/toonz/viewerpane.cpp @@ -92,9 +92,10 @@ BaseViewerPanel::BaseViewerPanel(QWidget *parent, Qt::WindowFlags flags) m_keyFrameButton->setObjectHandle(app->getCurrentObject()); m_keyFrameButton->setXsheetHandle(app->getCurrentXsheet()); - std::vector buttonMask = {FlipConsole::eFilledRaster, - FlipConsole::eDefineLoadBox, - FlipConsole::eUseLoadBox}; + std::vector buttonMask = { + FlipConsole::eFilledRaster, FlipConsole::eDefineLoadBox, + FlipConsole::eUseLoadBox, FlipConsole::eDecreaseGain, + FlipConsole::eIncreaseGain, FlipConsole::eResetGain}; m_flipConsole = new FlipConsole(m_mainLayout, buttonMask, false, m_keyFrameButton, diff --git a/toonz/sources/toonzlib/imagebuilders.cpp b/toonz/sources/toonzlib/imagebuilders.cpp index 57b972e..a8992ec 100644 --- a/toonz/sources/toonzlib/imagebuilders.cpp +++ b/toonz/sources/toonzlib/imagebuilders.cpp @@ -45,7 +45,11 @@ extern TOfflineGL *currentOfflineGL; //*************************************************************************************** ImageLoader::ImageLoader(const TFilePath &path, const TFrameId &fid) - : m_path(path), m_fid(fid), m_subsampling(0), m_64bitCompatible(false) {} + : m_path(path) + , m_fid(fid) + , m_subsampling(0) + , m_64bitCompatible(false) + , m_colorSpaceGamma(LevelOptions::DefaultColorSpaceGamma) {} //------------------------------------------------------------------------- @@ -69,13 +73,10 @@ bool ImageLoader::getInfo(TImageInfo &info, int imFlags, void *extData) { //------------------------------------------------------------------------- inline int ImageLoader::buildSubsampling(int imFlags, BuildExtData *data) { - return (imFlags & ImageManager::toBeModified) - ? 1 - : (data->m_subs > 0) - ? data->m_subs - : (m_subsampling > 0) - ? m_subsampling - : data->m_sl->getProperties()->getSubsampling(); + return (imFlags & ImageManager::toBeModified) ? 1 + : (data->m_subs > 0) ? data->m_subs + : (m_subsampling > 0) ? m_subsampling + : data->m_sl->getProperties()->getSubsampling(); } //------------------------------------------------------------------------- @@ -109,6 +110,16 @@ TImageP ImageLoader::build(int imFlags, void *extData) { bool enable64bit = (imFlags & ImageManager::is64bitEnabled); ir->enable16BitRead(enable64bit); // Set 64-bit loading if required + bool enableFloat = (imFlags & ImageManager::isFloatEnabled); + ir->enableFloatRead(enableFloat); // Set float loading if required + + double colorSpaceGamma = LevelOptions::DefaultColorSpaceGamma; + if (m_path.getType() == "exr") { + // gamma value to be used for converting linear-based image file to + // nonlinear raster. Curretly only used in EXR image levels. + colorSpaceGamma = data->m_sl->getProperties()->colorSpaceGamma(); + ir->setColorSpaceGamma(colorSpaceGamma); + } // Load the image TImageP img; @@ -121,6 +132,7 @@ TImageP ImageLoader::build(int imFlags, void *extData) { } ir->enable16BitRead(false); + ir->enableFloatRead(false); if (!img) return img; // There was an error loading the image. @@ -141,6 +153,9 @@ TImageP ImageLoader::build(int imFlags, void *extData) { m_subsampling = subsampling; m_64bitCompatible = data->m_sl->is16BitChannelLevel() ? enable64bit : true; + m_floatCompatible = + data->m_sl->isFloatChannelLevel() ? enableFloat : true; + if (m_path.getType() == "exr") m_colorSpaceGamma = colorSpaceGamma; } return img; @@ -167,11 +182,23 @@ bool ImageLoader::isImageCompatible(int imFlags, void *extData) { if (m_subsampling <= 0 || subsampling != m_subsampling) return false; - if (m_64bitCompatible || !(imFlags & ImageManager::is64bitEnabled)) { - return true; - } else { + if (m_path.getType() == "exr" && + !areAlmostEqual(m_colorSpaceGamma, + sl->getProperties()->colorSpaceGamma())) return false; - } + + if (!m_floatCompatible && (imFlags & ImageManager::isFloatEnabled)) + return false; + else if (!m_64bitCompatible && (imFlags & ImageManager::is64bitEnabled)) + return false; + else + return true; + + // if (m_64bitCompatible || !(imFlags & ImageManager::is64bitEnabled)) { + // return true; + // } else { + // return false; + // } } //------------------------------------------------------------------------- @@ -180,6 +207,7 @@ void ImageLoader::invalidate() { ImageBuilder::invalidate(); m_subsampling = 0; m_64bitCompatible = false; + m_colorSpaceGamma = LevelOptions::DefaultColorSpaceGamma; } //------------------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/imagebuilders.h b/toonz/sources/toonzlib/imagebuilders.h index 5284666..7ac8d81 100644 --- a/toonz/sources/toonzlib/imagebuilders.h +++ b/toonz/sources/toonzlib/imagebuilders.h @@ -76,8 +76,11 @@ private: TFrameId m_fid; //!< Frame of the level to load bool m_64bitCompatible; //!< Whether current image is 64-bit compatible + bool m_floatCompatible; //!< Whether current image is float compatible int m_subsampling; //!< Current image subsampling //!< NOTE: Should this be replaced by requests to the TImageCache? + + double m_colorSpaceGamma; // current gamma. only used in EXR levels }; //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/imagepainter.cpp b/toonz/sources/toonzlib/imagepainter.cpp index f4cc3d4..c03e5d9 100644 --- a/toonz/sources/toonzlib/imagepainter.cpp +++ b/toonz/sources/toonzlib/imagepainter.cpp @@ -28,23 +28,38 @@ namespace { //----------------------------------------------------------------------------- -TRaster32P keepChannels(const TRasterP &rin, TPalette *palette, UCHAR channel) { - TRaster32P rout(rin->getSize()); +TRasterP keepChannels(const TRasterP &rin, TPalette *palette, UCHAR channel) { + TRasterP rout; + if ((TRasterFP)rin) { + rout = rin->clone(); + + TPixelF *pix = (TPixelF *)rout->getRawData(); + + assert(channel & TRop::MChan); + int i; + for (i = 0; i < rout->getLx() * rout->getLy(); i++, pix++) { + if (!(channel & TRop::RChan)) pix->r = 0.f; + if (!(channel & TRop::GChan)) pix->g = 0.f; + if (!(channel & TRop::BChan)) pix->b = 0.f; + } + } else { + rout = TRaster32P(rin->getSize()); - if ((TRasterCM32P)rin) - TRop::convert(rout, (TRasterCM32P)rin, TPaletteP(palette)); - else - TRop::copy(rout, rin); + if ((TRasterCM32P)rin) + TRop::convert(rout, (TRasterCM32P)rin, TPaletteP(palette)); + else + TRop::copy(rout, rin); - TPixel32 *pix = (TPixel32 *)rout->getRawData(); + TPixel32 *pix = (TPixel32 *)rout->getRawData(); - assert(channel & TRop::MChan); - int i; + assert(channel & TRop::MChan); + int i; - for (i = 0; i < rout->getLx() * rout->getLy(); i++, pix++) { - if (!(channel & TRop::RChan)) pix->r = 0; - if (!(channel & TRop::GChan)) pix->g = 0; - if (!(channel & TRop::BChan)) pix->b = 0; + for (i = 0; i < rout->getLx() * rout->getLy(); i++, pix++) { + if (!(channel & TRop::RChan)) pix->r = 0; + if (!(channel & TRop::GChan)) pix->g = 0; + if (!(channel & TRop::BChan)) pix->b = 0; + } } return rout; } @@ -172,7 +187,8 @@ public: void onRasterImage(TRasterImage *ri); void onToonzImage(TToonzImage *ti); void drawBlank(); - TRaster32P buildCheckboard(int bg, const TDimension &dim); + TRasterP buildCheckboard(int bg, const TDimension &dim, + TRasterP templateRas = TRaster32P()); }; //----------------------------------------------------------------------------- @@ -303,35 +319,43 @@ void Painter::flushRasterImages(const TRect &loadbox, double compareX, //----------------------------------------------------------------------------- -TRaster32P Painter::buildCheckboard(int bg, const TDimension &dim) { - TRaster32P checkBoard = TRaster32P(100, 100); - if (bg == 0x100000) { - TPixel col1, col2; - Preferences::instance()->getChessboardColors(col1, col2); - TPointD p = TPointD(0, 0); - if (m_vSettings.m_useTexture) - p = TPointD(m_bbox.x0 > 0 ? 0 : -m_bbox.x0, - m_bbox.y0 > 0 ? 0 : -m_bbox.y0); - - assert(checkBoard.getPointer()); - TRop::checkBoard(checkBoard, col1, col2, TDimensionD(50, 50), p); - } else { - TPixel pix = bg == 0x40000 ? TPixel::Black : TPixel::White; - assert(checkBoard.getPointer()); - checkBoard->fill(pix); - } - - // TRaster32P textureBackGround; +TRasterP Painter::buildCheckboard(int bg, const TDimension &dim, + TRasterP templateRas) { + TRaster32P ras32(templateRas); + TRaster64P ras64(templateRas); + TRasterFP rasF(templateRas); + templateRas = 0; + TRasterP checkBoard; + if (ras32) + checkBoard = TRaster32P(100, 100); + else if (ras64) + checkBoard = TRaster64P(100, 100); + else if (rasF) + checkBoard = TRasterFP(100, 100); + + TPixel col1, col2; + Preferences::instance()->getChessboardColors(col1, col2); + TPointD p = TPointD(0, 0); + if (m_vSettings.m_useTexture) + p = TPointD(m_bbox.x0 > 0 ? 0 : -m_bbox.x0, m_bbox.y0 > 0 ? 0 : -m_bbox.y0); - // if(m_vSettings.m_useTexture) - // textureBackGround = TRaster32P(dim.lx,dim.ly); + assert(checkBoard.getPointer()); + TRop::checkBoard(checkBoard, col1, col2, TDimensionD(50, 50), p); assert(checkBoard.getPointer()); int lx = (m_imageSize.lx == 0 ? dim.lx : m_imageSize.lx); int ly = (m_imageSize.ly == 0 ? dim.ly : m_imageSize.ly); int x, y; - TRaster32P checkBoardRas(lx, ly); + TRasterP checkBoardRas; + + if (ras32) + checkBoardRas = TRaster32P(lx, ly); + else if (ras64) + checkBoardRas = TRaster64P(lx, ly); + else if (rasF) + checkBoardRas = TRasterFP(lx, ly); + for (y = 0; y < ly; y += 100) { for (x = 0; x < lx; x += 100) { // TAffine checkTrans = TTranslation(x,y); @@ -368,7 +392,8 @@ void Painter::doFlushRasterImages(const TRasterP &rin, int bg, // TRaster32P ras; TRasterP _rin = rin; TAffine aff; - bool is16bpc = false; + GLenum bpcType = TGL_TYPE; + // is16bpc = false; if (m_vSettings.m_useTexture) { ras = _rin; aff = m_aff; @@ -380,7 +405,10 @@ void Painter::doFlushRasterImages(const TRasterP &rin, int bg, // but is kept the channel depth as 16bpc. if (_rin->getPixelSize() == 8) { ras = TRaster64P(lx, ly); - is16bpc = true; + bpcType = TGL_TYPE16; + } else if (_rin->getPixelSize() == 16) { + ras = TRasterFP(lx, ly); + bpcType = TGL_TYPE32F; } else ras = TRaster32P(lx, ly); @@ -409,12 +437,14 @@ void Painter::doFlushRasterImages(const TRasterP &rin, int bg, // shifted of an half pixel...it's // a quickput approximation? if (bg == 0x100000) - quickput(ras, buildCheckboard(bg, _rin->getSize()), m_palette, aff, + quickput(ras, buildCheckboard(bg, _rin->getSize(), ras), m_palette, aff, false); else { - if (is16bpc) + if (bpcType == TGL_TYPE16) ((TRaster64P)ras) ->fill(bg == 0x40000 ? TPixel64::Black : TPixel64::White); + else if (bpcType == TGL_TYPE32F) + ((TRasterFP)ras)->fill(bg == 0x40000 ? TPixelF::Black : TPixelF::White); else ((TRaster32P)ras)->fill(bg == 0x40000 ? TPixel::Black : TPixel::White); } @@ -470,8 +500,7 @@ void Painter::doFlushRasterImages(const TRasterP &rin, int bg, glRasterPos2d(rect.x0, rect.y0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glDrawPixels(ras->getWrap(), ras->getLy(), TGL_FMT, - (is16bpc) ? TGL_TYPE16 : TGL_TYPE, + glDrawPixels(ras->getWrap(), ras->getLy(), TGL_FMT, bpcType, (GLvoid *)ras->getRawData()); CHECK_ERRORS_BY_GL @@ -593,7 +622,8 @@ ImagePainter::VisualSettings::VisualSettings() , m_sceneProperties(0) , m_recomputeIfNeeded(true) , m_drawBlankFrame(false) - , m_useChecks(false) { + , m_useChecks(false) + , m_gainStep(0) { if (FlipBookBlackBgToggle) m_bg = 0x40000; if (FlipBookWhiteBgToggle) m_bg = 0x80000; if (FlipBookCheckBgToggle) m_bg = 0x100000; @@ -606,7 +636,8 @@ bool ImagePainter::VisualSettings::needRepaint(const VisualSettings &vs) const { m_bg == vs.m_bg && m_doCompare == vs.m_doCompare && m_defineLoadbox == vs.m_defineLoadbox && m_useLoadbox == vs.m_useLoadbox && m_useTexture == vs.m_useTexture && - m_drawExternalBG == vs.m_drawExternalBG); + m_drawExternalBG == vs.m_drawExternalBG && + m_gainStep == vs.m_gainStep); } //============================================================================= diff --git a/toonz/sources/toonzlib/levelproperties.cpp b/toonz/sources/toonzlib/levelproperties.cpp index 3ec1db9..14cba2f 100644 --- a/toonz/sources/toonzlib/levelproperties.cpp +++ b/toonz/sources/toonzlib/levelproperties.cpp @@ -5,6 +5,8 @@ // TnzLib includes #include "toonz/stage.h" +const double LevelOptions::DefaultColorSpaceGamma = 2.2; + //********************************************************************************** // LevelProperties::Options implementation //********************************************************************************** @@ -16,7 +18,8 @@ LevelOptions::LevelOptions() , m_dpiPolicy(DP_ImageDpi) , m_whiteTransp(false) , m_premultiply(false) - , m_isStopMotionLevel(false) {} + , m_isStopMotionLevel(false) + , m_colorSpaceGamma(DefaultColorSpaceGamma) {} //----------------------------------------------------------------------------- @@ -26,7 +29,8 @@ bool LevelOptions::operator==(const LevelOptions &other) const { m_dpiPolicy == other.m_dpiPolicy && m_antialias == other.m_antialias && m_isStopMotionLevel == other.m_isStopMotionLevel && - (m_dpiPolicy == LevelOptions::DP_ImageDpi || m_dpi == other.m_dpi)); + (m_dpiPolicy == LevelOptions::DP_ImageDpi || m_dpi == other.m_dpi)) && + areAlmostEqual(m_colorSpaceGamma, other.m_colorSpaceGamma); } //********************************************************************************** diff --git a/toonz/sources/toonzlib/movierenderer.cpp b/toonz/sources/toonzlib/movierenderer.cpp index 73308da..8f56160 100644 --- a/toonz/sources/toonzlib/movierenderer.cpp +++ b/toonz/sources/toonzlib/movierenderer.cpp @@ -11,6 +11,8 @@ #include "trop.h" #include "tsop.h" +#include "tiio.h" + // TnzLib includes #include "toonz/toonzscene.h" #include "toonz/sceneproperties.h" @@ -81,10 +83,10 @@ void getRange(ToonzScene *scene, bool isPreview, int &from, int &to) { int r0, r1; xs->getCellRange(k, r0, r1); - TXshColumn *col = xs->getColumn(k); + TXshColumn *col = xs->getColumn(k); TXshSoundColumn *sndCol = col ? col->getSoundColumn() : 0; - if (sndCol) r0 = 0; + if (sndCol) r0 = 0; from = std::min(from, r0), to = std::max(to, r1); } } @@ -159,7 +161,15 @@ public: void prepareForStart(); void addSoundtrack(int r0, int r1, double fps, int boardDuration = 0); + + // writeInLinearColorSpace : Whether the format will save image in linear + // color space. (true only in EXR fromat) writingGamma : Color space gamma to + // be used for saving in the file ("Color Space Gamma" property in EXR format) + // renderingGamma : Color space gamma used on rendering ( "Color Space Gamma" + // value in the Render Settings ) void postProcessImage(const TRasterImageP &img, bool has64bitOutputSupport, + bool writeInLinearColorSpace, bool isFirstTime, + double writingGamma, double renderingGamma, const TRasterP &mark, int frame); //! Saves the specified rasters at the specified time; returns whether the @@ -318,7 +328,7 @@ void MovieRenderer::Imp::prepareForStart() { void MovieRenderer::Imp::addSoundtrack(int r0, int r1, double fps, int boardDuration) { - TCG_ASSERT(r0 <= r1, return ); + TCG_ASSERT(r0 <= r1, return); TXsheet::SoundProperties *prop = new TXsheet::SoundProperties(); // Ownership will be surrendered ... @@ -372,6 +382,9 @@ void MovieRenderer::Imp::onRenderRasterCompleted(const RenderData &renderData) { void MovieRenderer::Imp::postProcessImage(const TRasterImageP &img, bool has64bitOutputSupport, + bool writeInLinearColorSpace, + bool isFirstTime, double writingGamma, + double renderingGamma, const TRasterP &mark, int frame) { img->setDpi(m_xDpi, m_yDpi); @@ -381,6 +394,23 @@ void MovieRenderer::Imp::postProcessImage(const TRasterImageP &img, img->setRaster(aux); } + // raster will be converted to linear before saving in exr format + if (isFirstTime) { + if (img->getRaster()->isLinear()) { + if (!writeInLinearColorSpace) + TRop::tosRGB(img->getRaster(), renderingGamma); + // write in linear color space, but with different gamma + else if (!areAlmostEqual(renderingGamma, writingGamma)) { + double gammaAdjust = writingGamma / renderingGamma; + // temporarily release the linear flag in order to use toLinearRGB + img->getRaster()->setLinear(false); + TRop::toLinearRGB(img->getRaster(), gammaAdjust); + } + } else if (!img->getRaster()->isLinear() && writeInLinearColorSpace) { + TRop::toLinearRGB(img->getRaster(), writingGamma); + } + } + if (mark) addMark(mark, img); if (Preferences::instance()->isSceneNumberingEnabled()) @@ -412,12 +442,26 @@ std::pair MovieRenderer::Imp::saveFrame( assert(m_levelUpdaterB.get() || !rasters.second); // Analyze writer - bool has64bitOutputSupport = false; + bool has64bitOutputSupport = false; + bool writeInLinearColorSpace = false; + double writingGamma = 2.2; { if (TImageWriterP writerA = - m_levelUpdaterA->getLevelWriter()->getFrameWriter(fid)) + m_levelUpdaterA->getLevelWriter()->getFrameWriter(fid)) { has64bitOutputSupport = writerA->is64bitOutputSupported(); + const std::string &type = toLower(m_fp.getType()); + Tiio::Writer *writer = Tiio::makeWriter(type); + writeInLinearColorSpace = writer && writer->writeInLinearColorSpace(); + if (writeInLinearColorSpace) { + TDoubleProperty *gammaProp = + (TDoubleProperty *)(m_levelUpdaterA->getLevelWriter() + ->getProperties() + ->getProperty("Color Space Gamma")); + if (gammaProp) writingGamma = gammaProp->getValue(); + } + } + // NOTE: If the writer could not be retrieved, the updater will throw. // Failure will be caught then. } @@ -437,15 +481,19 @@ std::pair MovieRenderer::Imp::saveFrame( // Flush images try { TRasterImageP imgA(rasterA); - postProcessImage(imgA, has64bitOutputSupport, m_renderSettings.m_mark, - fid.getNumber()); + postProcessImage(imgA, has64bitOutputSupport, writeInLinearColorSpace, + m_toBeAppliedGamma[frame], writingGamma, + m_renderSettings.m_colorSpaceGamma, + m_renderSettings.m_mark, fid.getNumber()); m_levelUpdaterA->update(fid, imgA); if (rasterB) { TRasterImageP imgB(rasterB); - postProcessImage(imgB, has64bitOutputSupport, m_renderSettings.m_mark, - fid.getNumber()); + postProcessImage(imgB, has64bitOutputSupport, writeInLinearColorSpace, + m_toBeAppliedGamma[frame], writingGamma, + m_renderSettings.m_colorSpaceGamma, + m_renderSettings.m_mark, fid.getNumber()); m_levelUpdaterB->update(fid, imgB); } @@ -453,9 +501,10 @@ std::pair MovieRenderer::Imp::saveFrame( // Should no more throw from here on if (m_cacheResults) { - if (imgA->getRaster()->getPixelSize() == 8) { - // Convert 64-bit images to 32 - cached images are supposed to be - // 32-bit + if (imgA->getRaster()->getPixelSize() == 8 || + imgA->getRaster()->getPixelSize() == 16) { + // Convert 64-bit / float images to 32 - cached images are supposed to + // be 32-bit TRaster32P aux(imgA->getRaster()->getLx(), imgA->getRaster()->getLy()); diff --git a/toonz/sources/toonzlib/outputproperties.cpp b/toonz/sources/toonzlib/outputproperties.cpp index 6568f8f..5b71896 100644 --- a/toonz/sources/toonzlib/outputproperties.cpp +++ b/toonz/sources/toonzlib/outputproperties.cpp @@ -42,8 +42,10 @@ TOutputProperties::TOutputProperties() , m_threadIndex(2) , m_subcameraPreview(false) , m_boardSettings(new BoardSettings()) - , m_formatTemplateFId() { + , m_formatTemplateFId() + , m_syncColorSettings(true) { m_renderSettings = new TRenderSettings(); + m_nonlinearBpp = m_renderSettings->m_bpp; } //------------------------------------------------------------------- @@ -63,7 +65,9 @@ TOutputProperties::TOutputProperties(const TOutputProperties &src) , m_threadIndex(src.m_threadIndex) , m_subcameraPreview(src.m_subcameraPreview) , m_boardSettings(new BoardSettings(*src.m_boardSettings)) - , m_formatTemplateFId(src.m_formatTemplateFId) { + , m_formatTemplateFId(src.m_formatTemplateFId) + , m_syncColorSettings(src.m_syncColorSettings) + , m_nonlinearBpp(src.m_nonlinearBpp) { std::map::iterator ft, fEnd = m_formatProperties.end(); for (ft = m_formatProperties.begin(); ft != fEnd; ++ft) { @@ -189,7 +193,8 @@ void TOutputProperties::getFileFormatPropertiesExtensions( void TOutputProperties::setRenderSettings( const TRenderSettings &renderSettings) { - assert(renderSettings.m_bpp == 32 || renderSettings.m_bpp == 64); + assert(renderSettings.m_bpp == 32 || renderSettings.m_bpp == 64 || + renderSettings.m_bpp == 128); assert(renderSettings.m_gamma > 0); assert(renderSettings.m_quality == TRenderSettings::StandardResampleQuality || renderSettings.m_quality == TRenderSettings::ImprovedResampleQuality || diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index e4dae07..4861a61 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -47,7 +47,8 @@ const char *s_name = "name", *s_regexp = "regexp", *s_priority = "priority"; const char *s_dpiPolicy = "dpiPolicy", *s_dpi = "dpi", *s_subsampling = "subsampling", *s_antialias = "antialias", - *s_premultiply = "premultiply", *s_whiteTransp = "whiteTransp"; + *s_premultiply = "premultiply", *s_whiteTransp = "whiteTransp", + *s_colorSpaceGamma = "colorSpaceGamma"; //================================================================= @@ -124,6 +125,7 @@ void setValue(QSettings &settings, const LevelOptions &lo) { settings.setValue(s_antialias, lo.m_antialias); settings.setValue(s_premultiply, int(lo.m_premultiply)); settings.setValue(s_whiteTransp, int(lo.m_whiteTransp)); + settings.setValue(s_colorSpaceGamma, lo.m_colorSpaceGamma); } //----------------------------------------------------------------- @@ -138,6 +140,8 @@ void getValue(const QSettings &settings, LevelOptions &lo) { (settings.value(s_premultiply, lo.m_premultiply).toInt() != 0); lo.m_whiteTransp = (settings.value(s_whiteTransp, lo.m_whiteTransp).toInt() != 0); + lo.m_colorSpaceGamma = + settings.value(s_colorSpaceGamma, lo.m_colorSpaceGamma).toDouble(); } //----------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/scenefx.cpp b/toonz/sources/toonzlib/scenefx.cpp index 5eb07dd..386f798 100644 --- a/toonz/sources/toonzlib/scenefx.cpp +++ b/toonz/sources/toonzlib/scenefx.cpp @@ -77,6 +77,8 @@ public: TimeShuffleFx() : TRasterFx(), m_frame(0), m_timeRegion(), m_cellColumn(nullptr) { addInputPort("source", m_port); + + enableComputeInFloat(true); } ~TimeShuffleFx() {} @@ -143,6 +145,11 @@ public: TRasterFxP(m_port.getFx())->dryCompute(rect, getLevelFrame(frame), info); } + bool toBeComputedInLinearColorSpace(bool settingsIsLinear, + bool tileIsLinear) const override { + return tileIsLinear; + } + private: // not implemented TimeShuffleFx(const TimeShuffleFx &); diff --git a/toonz/sources/toonzlib/sceneproperties.cpp b/toonz/sources/toonzlib/sceneproperties.cpp index f167771..af18836 100644 --- a/toonz/sources/toonzlib/sceneproperties.cpp +++ b/toonz/sources/toonzlib/sceneproperties.cpp @@ -233,6 +233,16 @@ void TSceneProperties::saveData(TOStream &os) const { os.child("fps") << out.getFrameRate(); os.child("path") << outPath; os.child("bpp") << rs.m_bpp; + if (rs.m_linearColorSpace) { + os.child("linearColorSpace") << (rs.m_linearColorSpace ? 1 : 0); + os.child("nonlinearBpp") << out.getNonlinearBpp(); + } + if (rs.m_colorSpaceGamma >= 1. && + !areAlmostEqual(rs.m_colorSpaceGamma, 2.2)) + os.child("colorSpaceGamma") << rs.m_colorSpaceGamma; + if (i == 1) // preview + os.child("syncColorSettings") << (out.isColorSettingsSynced() ? 1 : 0); + os.child("multimedia") << out.getMultimediaRendering(); os.child("threadsIndex") << out.getThreadIndex(); os.child("maxTileSizeIndex") << out.getMaxTileSizeIndex(); @@ -387,7 +397,9 @@ void TSceneProperties::loadData(TIStream &is, bool isLoadingProject) { int globFrom = -1, globTo = 0, globStep = 1; double globFrameRate = -1; std::string tagName; - *m_outputProp = *m_previewProp = TOutputProperties(); + *m_outputProp = TOutputProperties(); + *m_previewProp = TOutputProperties(); + while (is.matchTag(tagName)) { if (tagName == "projectPath") { TFilePath projectPath; @@ -538,7 +550,24 @@ void TSceneProperties::loadData(TIStream &is, bool isLoadingProject) { } else if (tagName == "bpp") { int j; is >> j; - if (j == 32 || j == 64) renderSettings.m_bpp = j; + if (j == 32 || j == 64 || j == 128) renderSettings.m_bpp = j; + } else if (tagName == "linearColorSpace") { + int linearColorSpace; + is >> linearColorSpace; + renderSettings.m_linearColorSpace = (linearColorSpace != 0); + } else if (tagName == "nonlinearBpp") { + int j; + is >> j; + if (j == 32 || j == 64 || j == 128) out.setNonlinearBpp(j); + } else if (tagName == "colorSpaceGamma") { + double colorSpaceGamma; + is >> colorSpaceGamma; + renderSettings.m_colorSpaceGamma = colorSpaceGamma; + } else if (tagName == "syncColorSettings") { + assert(name == "preview"); + int syncColorSettings; + is >> syncColorSettings; + out.syncColorSettings(syncColorSettings != 0); } else if (tagName == "multimedia") { int j; is >> j; @@ -785,6 +814,13 @@ void TSceneProperties::loadData(TIStream &is, bool isLoadingProject) { } is.closeChild(); } + + // in order to support scenes made in previous development version + if (m_previewProp->getRenderSettings().m_colorSpaceGamma < 0.) { + TRenderSettings rs(m_previewProp->getRenderSettings()); + rs.m_colorSpaceGamma = m_outputProp->getRenderSettings().m_colorSpaceGamma; + m_previewProp->setRenderSettings(rs); + } } //----------------------------------------------------------------------------- @@ -885,4 +921,4 @@ bool TSceneProperties::hasDefaultCellMarks() const { // such as new raster level, captured images by camera capture feature, etc. TFrameId &TSceneProperties::formatTemplateFIdForInput() { return m_previewProp->formatTemplateFId(); -} +} \ No newline at end of file diff --git a/toonz/sources/toonzlib/tcolumnfx.cpp b/toonz/sources/toonzlib/tcolumnfx.cpp index 20877c7..345d3ea 100644 --- a/toonz/sources/toonzlib/tcolumnfx.cpp +++ b/toonz/sources/toonzlib/tcolumnfx.cpp @@ -87,8 +87,8 @@ namespace { void setMaxMatte(TRasterP r) { TRaster32P r32 = (TRaster32P)r; - TRaster64P r64 = (TRaster64P)r; + TRasterFP rF = (TRasterFP)r; if (r32) for (int i = 0; i < r32->getLy(); i++) { @@ -100,6 +100,12 @@ void setMaxMatte(TRasterP r) { TPixel64 *pix = r64->pixels(i); for (int j = 0; j < r64->getLx(); j++, pix++) pix->m = 65535; } + else if (rF) + for (int i = 0; i < rF->getLy(); i++) { + TPixelF *pix = rF->pixels(i); + for (int j = 0; j < rF->getLx(); j++, pix++) + pix->m = TPixelF::maxChannelValue; + } } //--------------------------------------------------------------------------------------------------------- @@ -332,12 +338,12 @@ inline bool fxLess(TRasterFxRenderDataP a, TRasterFxRenderDataP b) { dynamic_cast(b.getPointer()); if (!sandorDataB) return true; - int aIndex = sandorDataA->m_type == OutBorder - ? 2 - : sandorDataA->m_type == BlendTz ? 1 : 0; - int bIndex = sandorDataB->m_type == OutBorder - ? 2 - : sandorDataB->m_type == BlendTz ? 1 : 0; + int aIndex = sandorDataA->m_type == OutBorder ? 2 + : sandorDataA->m_type == BlendTz ? 1 + : 0; + int bIndex = sandorDataB->m_type == OutBorder ? 2 + : sandorDataB->m_type == BlendTz ? 1 + : 0; return aIndex < bIndex; } @@ -725,7 +731,9 @@ class LevelFxBuilder final : public ResourceBuilder { TXshSimpleLevel *m_sl; TFrameId m_fid; TRectD m_tileGeom; - bool m_64bit; + int m_bpp; + // bool m_linear; + // bool m_64bit; TRect m_rasBounds; @@ -737,23 +745,29 @@ public: , m_palette() , m_sl(sl) , m_fid(fid) - , m_64bit(rs.m_bpp == 64) {} + , m_bpp(rs.m_bpp) {} + //, m_linear(rs.m_linearColorSpace){} void setRasBounds(const TRect &rasBounds) { m_rasBounds = rasBounds; } void compute(const TRectD &tileRect) override { + UCHAR flag = ImageManager::dontPutInCache; + if (m_bpp == 64 || m_bpp == 128) flag = flag | ImageManager::is64bitEnabled; + if (m_bpp == 128) flag = flag | ImageManager::isFloatEnabled; + // if (m_linear) + // flag = flag | ImageManager::isLinearEnabled; + // Load the image - TImageP img(m_sl->getFullsampledFrame( - m_fid, (m_64bit ? ImageManager::is64bitEnabled : 0) | - ImageManager::dontPutInCache)); + TImageP img(m_sl->getFullsampledFrame(m_fid, flag)); if (!img) return; TRasterImageP rimg(img); TToonzImageP timg(img); - m_loadedRas = rimg ? (TRasterP)rimg->getRaster() - : timg ? (TRasterP)timg->getRaster() : TRasterP(); + m_loadedRas = rimg ? (TRasterP)rimg->getRaster() + : timg ? (TRasterP)timg->getRaster() + : TRasterP(); assert(m_loadedRas); if (timg) m_palette = timg->getPalette(); @@ -804,6 +818,7 @@ public: TLevelColumnFx::TLevelColumnFx() : m_levelColumn(0), m_isCachable(true), m_mutex(), m_offlineContext(0) { setName(L"LevelColumn"); + enableComputeInFloat(true); } //-------------------------------------------------- @@ -1087,13 +1102,15 @@ void TLevelColumnFx::doCompute(TTile &tile, double frame, ri = 0; TRaster32P ras32(ras); TRaster64P ras64(ras); + TRasterFP rasF(ras); // Ensure that ras is either a 32 or 64 fullcolor. // Otherwise, we have to convert it. - if (!ras32 && !ras64) { + if (!ras32 && !ras64 && !rasF) { TRasterP tileRas(tile.getRaster()); TRaster32P tileRas32(tileRas); TRaster64P tileRas64(tileRas); + TRasterFP tileRasF(tileRas); if (tileRas32) { ras32 = TRaster32P(ras->getLx(), ras->getLy()); @@ -1103,6 +1120,10 @@ void TLevelColumnFx::doCompute(TTile &tile, double frame, ras64 = TRaster64P(ras->getLx(), ras->getLy()); TRop::convert(ras64, ras); ras = ras64; + } else if (tileRasF) { + rasF = TRasterFP(ras->getLx(), ras->getLy()); + TRop::convert(rasF, ras); + ras = rasF; } else assert(0); } @@ -1127,7 +1148,8 @@ void TLevelColumnFx::doCompute(TTile &tile, double frame, TRop::whiteTransp(appRas); ras = appRas; } - if (levelProp->antialiasSoftness() > 0) { + if (levelProp->antialiasSoftness() > 0 && + !rasF) { // temporarily disabled with float raster TRasterP appRas = ras->create(ras->getLx(), ras->getLy()); TRop::antialias(ras, appRas, 10, levelProp->antialiasSoftness()); ras = appRas; diff --git a/toonz/sources/toonzlib/tdistort.cpp b/toonz/sources/toonzlib/tdistort.cpp index 098a701..9a77a40 100644 --- a/toonz/sources/toonzlib/tdistort.cpp +++ b/toonz/sources/toonzlib/tdistort.cpp @@ -14,7 +14,7 @@ namespace { inline double dist(const TPointD &a, const TPointD &b) { return norm(b - a); } -} +} // namespace //======================================================================================== @@ -27,11 +27,12 @@ typedef struct { //--------------------------------------------------------------------------------- TPixelCM32 filterPixel(const TPointD &pos, const TRasterCM32P &rasIn) { - TPointD distance = TPointD( - areAlmostEqual(pos.x, tfloor(pos.x), 0.001) ? 0.0 - : fabs(pos.x - tfloor(pos.x)), - areAlmostEqual(pos.y, tfloor(pos.y), 0.001) ? 0.0 : fabs(pos.y - - tfloor(pos.y))); + TPointD distance = TPointD(areAlmostEqual(pos.x, tfloor(pos.x), 0.001) + ? 0.0 + : fabs(pos.x - tfloor(pos.x)), + areAlmostEqual(pos.y, tfloor(pos.y), 0.001) + ? 0.0 + : fabs(pos.y - tfloor(pos.y))); TPoint nearPos(tfloor(pos.x), tfloor(pos.y)); if (distance == TPointD(0.0, 0.0)) { if (nearPos.x >= 0 && nearPos.x < rasIn->getLx() && nearPos.y >= 0 && @@ -45,7 +46,7 @@ TPixelCM32 filterPixel(const TPointD &pos, const TRasterCM32P &rasIn) { TPixelCM32 P[4]; for (j = 0; j < 2; ++j) - for (i = 0; i < 2; ++i) + for (i = 0; i < 2; ++i) P[i + 2 * j] = nearPos.x + i < rasIn->getLx() && nearPos.x + i >= 0 && nearPos.y + j < rasIn->getLy() && nearPos.y + j >= 0 @@ -56,7 +57,7 @@ TPixelCM32 filterPixel(const TPointD &pos, const TRasterCM32P &rasIn) { double w[4], sum = 0; for (j = 1; j >= 0; --j) - for (i = 1; i >= 0; --i) + for (i = 1; i >= 0; --i) sum += w[k++] = fabs(distance.x - i) * fabs(distance.y - j); for (i = 0; i < 4; i++) w[i] /= sum; @@ -339,7 +340,7 @@ PIXEL filterPixel(double a, double b, double c, double d, int xEnd = tceil(x1); for (int x = tfloor(x0); x < xEnd; ++x) - temp[x] = filterPixel( + temp[x] = filterPixel( c, d, rasIn->pixels(0) + x, rasIn->getLy(), rasIn->getWrap()); // Then, filter temp @@ -375,7 +376,7 @@ void resample(const TRasterPT &rasIn, TRasterPT &rasOut, { TPointD *currOldInv = invs[0].get(); int *oldCounts = counts[0].get(); - for (int x = 0; x <= rasOut->getLx(); currOldInv += invsCount, ++x) + for (int x = 0; x <= rasOut->getLx(); currOldInv += invsCount, ++x) oldCounts[x] = distorter.invMap(shift + TPointD(x, 0.0), currOldInv); } @@ -429,15 +430,19 @@ void distort(TRasterP &outRas, const TRasterP &inRas, TRaster32P inRas32 = inRas; TRaster64P inRas64 = inRas; TRasterCM32P inRasCM32 = inRas; + TRasterFP inRasF = inRas; TRaster32P outRas32 = outRas; TRaster64P outRas64 = outRas; TRasterCM32P outRasCM32 = outRas; + TRasterFP outRasF = outRas; if (filter == TRop::Bilinear) { if (inRas32) ::resample(inRas32, outRas32, distorter, dstPos); else if (inRas64) ::resample(inRas64, outRas64, distorter, dstPos); + else if (inRasF) + ::resample(inRasF, outRasF, distorter, dstPos); else if (inRasCM32 && outRasCM32) ::resample(inRasCM32, outRasCM32, distorter, dstPos); else @@ -449,6 +454,9 @@ void distort(TRasterP &outRas, const TRasterP &inRas, else if (inRas64) ::resampleClosestPixel(inRas64, outRas64, distorter, dstPos); + else if (inRasF) + ::resampleClosestPixel(inRasF, outRasF, distorter, + dstPos); else if (inRasCM32 && outRasCM32) ::resampleClosestPixel(inRasCM32, outRasCM32, distorter, dstPos); else @@ -592,7 +600,7 @@ TPointD BilinearDistorter::map(const TPointD &p) const { inline int BilinearDistorter::invMap(const TPointD &p, TPointD *results) const { int returnCount = m_refToDest.invMap(p, results); - for (int i = 0; i < returnCount; ++i) + for (int i = 0; i < returnCount; ++i) results[i] = m_refToSource.map(results[i]); return returnCount; } @@ -1064,20 +1072,21 @@ TRectD PerspectiveDistorter::invMap(const TRectD &rect) const { // If some maxD remain, no bound on that side was found. So replace with // the opposite (unlimited on that side) maxD. - if (positiveResult.x0 == maxD) positiveResult.x0 = -maxD; + if (positiveResult.x0 == maxD) positiveResult.x0 = -maxD; if (positiveResult.x1 == -maxD) positiveResult.x1 = maxD; - if (positiveResult.y0 == maxD) positiveResult.y0 = -maxD; + if (positiveResult.y0 == maxD) positiveResult.y0 = -maxD; if (positiveResult.y1 == -maxD) positiveResult.y1 = maxD; - if (negativeResult.x0 == maxD) negativeResult.x0 = -maxD; + if (negativeResult.x0 == maxD) negativeResult.x0 = -maxD; if (negativeResult.x1 == -maxD) negativeResult.x1 = maxD; - if (negativeResult.y0 == maxD) negativeResult.y0 = -maxD; + if (negativeResult.y0 == maxD) negativeResult.y0 = -maxD; if (negativeResult.y1 == -maxD) negativeResult.y1 = maxD; - return hasPositiveResults - ? hasNegativeResults ? positiveResult + negativeResult - : positiveResult - : hasNegativeResults ? negativeResult : TConsts::infiniteRectD; + return hasPositiveResults ? hasNegativeResults + ? positiveResult + negativeResult + : positiveResult + : hasNegativeResults ? negativeResult + : TConsts::infiniteRectD; } //================================================================================= diff --git a/toonz/sources/toonzlib/toonzscene.cpp b/toonz/sources/toonzlib/toonzscene.cpp index 8389496..01c5aa4 100644 --- a/toonz/sources/toonzlib/toonzscene.cpp +++ b/toonz/sources/toonzlib/toonzscene.cpp @@ -1181,6 +1181,8 @@ TXshLevel *ToonzScene::loadLevel(const TFilePath &actualPath, LevelProperties *lp = xl->getProperties(); assert(lp); + bool formatSpecified = false; + if (levelOptions) lp->options() = *levelOptions; else { @@ -1188,9 +1190,10 @@ TXshLevel *ToonzScene::loadLevel(const TFilePath &actualPath, int formatIdx = prefs.matchLevelFormat( levelPath); // Should I use actualPath here? It's mostly // irrelevant anyway, it's for old tzp/tzu... - if (formatIdx >= 0) - lp->options() = prefs.levelFormat(formatIdx).m_options; - else { + if (formatIdx >= 0) { + lp->options() = prefs.levelFormat(formatIdx).m_options; + formatSpecified = true; + } else { // Default subsampling values are assigned from scene properties if (xl->getType() == OVL_XSHLEVEL) lp->setSubsampling(getProperties()->getFullcolorSubsampling()); @@ -1218,6 +1221,17 @@ TXshLevel *ToonzScene::loadLevel(const TFilePath &actualPath, } } + // for EXR level, set the color space gamma to the same value as the output + // settings. skip if the loading gamma is specified in the preferences. + if (xl->getType() == OVL_XSHLEVEL && levelPath.getType() == "exr" && + !formatSpecified) { + double gamma = getProperties() + ->getOutputProperties() + ->getRenderSettings() + .m_colorSpaceGamma; + lp->setColorSpaceGamma(gamma); + } + m_levelSet->insertLevel(xl); return xl; diff --git a/toonz/sources/toonzlib/txshsimplelevel.cpp b/toonz/sources/toonzlib/txshsimplelevel.cpp index de23a7b..27375d0 100644 --- a/toonz/sources/toonzlib/txshsimplelevel.cpp +++ b/toonz/sources/toonzlib/txshsimplelevel.cpp @@ -119,8 +119,7 @@ bool isAreadOnlyLevel(const TFilePath &path) { if (path.getDots() == "." || (path.getDots() == ".." && (path.getType() == "tlv" || path.getType() == "tpl"))) { - if (path.isUneditable()) - return true; + if (path.isUneditable()) return true; if (!TSystem::doesExistFileOrLevel(path)) return false; TFileStatus fs(path); return !fs.isWritable(); @@ -195,6 +194,7 @@ TXshSimpleLevel::TXshSimpleLevel(const std::wstring &name) , m_editableRangeUserInfo(L"") , m_isSubsequence(false) , m_16BitChannelLevel(false) + , m_floatChannelLevel(false) , m_isReadOnly(false) , m_temporaryHookMerged(false) {} @@ -937,11 +937,12 @@ void TXshSimpleLevel::loadData(TIStream &is) { } else if (tagName == "info") { std::string v; double xdpi = 0, ydpi = 0; - int subsampling = 1; - int doPremultiply = 0; - int whiteTransp = 0; - int antialiasSoftness = 0; - int isStopMotionLevel = 0; + int subsampling = 1; + int doPremultiply = 0; + int whiteTransp = 0; + int antialiasSoftness = 0; + int isStopMotionLevel = 0; + double colorSpaceGamma = LevelOptions::DefaultColorSpaceGamma; LevelProperties::DpiPolicy dpiPolicy = LevelProperties::DP_ImageDpi; if (is.getTagParam("dpix", v)) xdpi = std::stod(v); if (is.getTagParam("dpiy", v)) ydpi = std::stod(v); @@ -955,6 +956,8 @@ void TXshSimpleLevel::loadData(TIStream &is) { if (is.getTagParam("whiteTransp", v)) whiteTransp = std::stoi(v); if (is.getTagParam("isStopMotionLevel", v)) isStopMotionLevel = std::stoi(v); + if (is.getTagParam("colorSpaceGamma", v)) + colorSpaceGamma = std::stod(v); m_properties->setDpiPolicy(dpiPolicy); m_properties->setDpi(TPointD(xdpi, ydpi)); @@ -963,6 +966,7 @@ void TXshSimpleLevel::loadData(TIStream &is) { m_properties->setDoAntialias(antialiasSoftness); m_properties->setWhiteTransp(whiteTransp); m_properties->setIsStopMotion(isStopMotionLevel); + m_properties->setColorSpaceGamma(colorSpaceGamma); if (isStopMotionLevel == 1) setIsReadOnly(true); } else throw TException("unexpected tag " + tagName); @@ -1187,7 +1191,10 @@ void TXshSimpleLevel::load() { return; } - if (info) set16BitChannelLevel(info->m_bitsPerSample == 16); + if (info) { + set16BitChannelLevel(info->m_bitsPerSample == 16); + setFloatChannelLevel(info->m_bitsPerSample == 32); + } } if ((getType() & FULLCOLOR_TYPE) && !is16BitChannelLevel()) setPalette(FullColorPalette::instance()->getPalette(getScene())); @@ -1330,7 +1337,10 @@ void TXshSimpleLevel::load(const std::vector &fIds) { setFrame(fIds[i], TImageP()); } const TImageInfo *info = lr->getImageInfo(fIds[0]); - if (info) set16BitChannelLevel(info->m_bitsPerSample == 16); + if (info) { + set16BitChannelLevel(info->m_bitsPerSample == 16); + setFloatChannelLevel(info->m_bitsPerSample == 32); + } } else { TLevelP level = lr->loadInfo(); for (TLevel::Iterator it = level->begin(); it != level->end(); it++) { @@ -1339,7 +1349,10 @@ void TXshSimpleLevel::load(const std::vector &fIds) { setFrame(it->first, TImageP()); } const TImageInfo *info = lr->getImageInfo(level->begin()->first); - if (info) set16BitChannelLevel(info->m_bitsPerSample == 16); + if (info) { + set16BitChannelLevel(info->m_bitsPerSample == 16); + setFloatChannelLevel(info->m_bitsPerSample == 32); + } } if ((getType() & FULLCOLOR_TYPE) && !is16BitChannelLevel()) @@ -1410,6 +1423,11 @@ void TXshSimpleLevel::saveData(TOStream &os) { attr["isStopMotionLevel"] = std::to_string(getProperties()->isStopMotionLevel()); } + if (!areAlmostEqual(getProperties()->colorSpaceGamma(), + LevelOptions::DefaultColorSpaceGamma)) { + attr["colorSpaceGamma"] = + std::to_string(getProperties()->colorSpaceGamma()); + } if (m_type == TZI_XSHLEVEL) attr["type"] = "s"; @@ -2426,9 +2444,8 @@ bool TXshSimpleLevel::isFrameReadOnly(TFrameId fid) { if (getType() == OVL_XSHLEVEL || getType() == TZI_XSHLEVEL || getType() == MESH_XSHLEVEL) { if (getProperties()->isStopMotionLevel()) return true; - TFilePath fullPath = getScene()->decodeFilePath(m_path); - if (fullPath.isUneditable()) - return true; + TFilePath fullPath = getScene()->decodeFilePath(m_path); + if (fullPath.isUneditable()) return true; TFilePath path = fullPath.getDots() == ".." ? fullPath.withFrame(fid) : fullPath; if (!TSystem::doesExistFileOrLevel(path)) return false; diff --git a/toonz/sources/toonzqt/combohistogram.cpp b/toonz/sources/toonzqt/combohistogram.cpp index a84494c..481954d 100644 --- a/toonz/sources/toonzqt/combohistogram.cpp +++ b/toonz/sources/toonzqt/combohistogram.cpp @@ -89,10 +89,18 @@ void ChannelHistoGraph::paintEvent(QPaintEvent *event) { int i; // draw scale marks - p.setPen(QColor(144, 144, 144)); - for (i = 1; i < 10; i++) { - int posx = rect().width() * i / 10; - p.drawLine(posx, 1, posx, COMBOHIST_RESOLUTION_H); + if (areAlmostEqual(m_range, 1.)) { + p.setPen(QColor(144, 144, 144)); + for (i = 1; i < 10; i++) { + int posx = rect().width() * i / 10; + p.drawLine(posx, 1, posx, COMBOHIST_RESOLUTION_H); + } + } else { + int range_i = (int)std::round(m_range); + for (i = 1; i < range_i; i++) { + int posx = rect().width() * i / range_i; + p.drawLine(posx, 1, posx, COMBOHIST_RESOLUTION_H); + } } QColor compColor = (m_channelIndex == 0) ? Qt::red @@ -242,12 +250,24 @@ void ChannelColorBar::paintEvent(QPaintEvent *event) { linearGrad.setColorAt(1, Qt::white); } else { linearGrad.setColorAt(0, Qt::black); + if (!areAlmostEqual(m_range, 1.)) + linearGrad.setColorAt(1. / m_range, m_color); linearGrad.setColorAt(1, m_color); } p.setBrush(QBrush(linearGrad)); p.setPen(Qt::NoPen); p.drawRect(rect()); + + if (!areAlmostEqual(m_range, 1.)) { + static const QPointF points[3] = {QPointF(0.0, 0.0), QPointF(3.0, 6.0), + QPointF(-3.0, 6.0)}; + + p.setPen(Qt::NoPen); + p.setBrush(Qt::black); + p.translate(COMBOHIST_RESOLUTION_W / m_range + 1, 0.); + p.drawPolygon(points, 3); + } } //============================================================================= @@ -447,7 +467,8 @@ ComboHistogram::ComboHistogram(QWidget *parent) , m_raster(0) , m_palette(0) , m_showCompare(false) - , m_compHistoIsValid(true) { + , m_compHistoIsValid(true) + , m_rangeStep(0) { for (int chan = 0; chan < 4; chan++) m_histograms[chan] = new ChannelHisto(chan, &m_showCompare, this); m_histograms[4] = new ChannelHisto(4, &m_showCompare, this); @@ -464,6 +485,13 @@ ComboHistogram::ComboHistogram(QWidget *parent) m_displayModeCombo = new QComboBox(this); + m_rangeControlContainer = new QWidget(this); + m_rangeUpBtn = new QPushButton("", this); + m_rangeDwnBtn = new QPushButton("", this); + m_rangeLabel = new QLabel("1.0", this); + + //----- + m_displayModeCombo->addItem( tr("8bit (0-255)"), (int)ComboHistoRGBLabel::DisplayMode::Display_8bit); m_displayModeCombo->addItem( @@ -472,6 +500,14 @@ ComboHistogram::ComboHistogram(QWidget *parent) m_displayModeCombo->addItem( tr("0.0-1.0"), (int)ComboHistoRGBLabel::DisplayMode::Display_0_1); + m_rangeUpBtn->setIcon(createQIcon("prevkey")); + m_rangeDwnBtn->setIcon(createQIcon("nextkey")); + m_rangeUpBtn->setFixedWidth(17); + m_rangeDwnBtn->setFixedWidth(17); + m_rangeDwnBtn->setEnabled(false); + m_rangeLabel->setFixedWidth(30); + m_rangeLabel->setAlignment(Qt::AlignCenter); + // layout QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setMargin(5); @@ -505,6 +541,18 @@ ComboHistogram::ComboHistogram(QWidget *parent) infoParamLay->addWidget(new QLabel(tr("Y:"), this), 1, Qt::AlignRight | Qt::AlignVCenter); infoParamLay->addWidget(m_yPosLabel, 2, Qt::AlignLeft | Qt::AlignVCenter); + + // range control + QHBoxLayout *rangeLay = new QHBoxLayout(); + rangeLay->setMargin(0); + rangeLay->setSpacing(0); + { + rangeLay->addWidget(m_rangeUpBtn, 0); + rangeLay->addWidget(m_rangeLabel, 0); + rangeLay->addWidget(m_rangeDwnBtn, 0); + } + m_rangeControlContainer->setLayout(rangeLay); + infoParamLay->addWidget(m_rangeControlContainer, 0); } mainLayout->addLayout(infoParamLay, 0); @@ -522,6 +570,8 @@ ComboHistogram::ComboHistogram(QWidget *parent) SLOT(onDisplayModeChanged())); connect(m_histograms[3], SIGNAL(showButtonToggled(bool)), this, SLOT(onShowAlphaButtonToggled(bool))); + connect(m_rangeUpBtn, SIGNAL(clicked()), this, SLOT(onRangeUp())); + connect(m_rangeDwnBtn, SIGNAL(clicked()), this, SLOT(onRangeDown())); } //----------------------------------------------------------------------------- @@ -536,13 +586,30 @@ void ComboHistogram::setRaster(const TRasterP &raster, const TPaletteP &palette) { if (palette.getPointer()) m_palette = palette; m_raster = raster; + + refreshHistogram(); + + m_rangeControlContainer->setVisible(!!((TRasterFP)raster)); + update(); +} + +//----------------------------------------------------------------------------- + +void ComboHistogram::refreshHistogram() { computeChannelsValue(&m_channelValue[0][0], sizeof(m_channelValue), m_raster); - for (int chan = 0; chan < 4; chan++) + float range = 1.f; + if (!!((TRasterFP)m_raster)) { + range = std::pow(2.f, (float)m_rangeStep); + } + + for (int chan = 0; chan < 4; chan++) { m_histograms[chan]->refleshValue(&m_channelValue[chan][0]); + if (chan != 3) // rgb channels + m_histograms[chan]->setRange(range); + } m_histograms[4]->refleshValue(&m_channelValue[0][0]); - - update(); + m_histograms[4]->setRange(range); } //----------------------------------------------------------------------------- @@ -557,6 +624,10 @@ void ComboHistogram::computeChannelsValue(int *buf, size_t size, TRasterP ras, TRaster64P raster64 = ras; bool is64bit = !!raster64; + TRasterFP rasF = ras; + + float factor = 1.f / std::pow(2.f, (float)m_rangeStep); + int lx = ras->getLx(); int ly = ras->getLy(); if (lx > 1 && ly > 1) { @@ -594,6 +665,19 @@ void ComboHistogram::computeChannelsValue(int *buf, size_t size, TRasterP ras, ++buf[idx(3, color.m)]; } } + } else if (rasF) { + for (j = 0; j < ly; j++) { + TPixelF *pixF = rasF->pixels(j); + for (i = 0; i < lx; i++, pixF++) { + int mValue = (int)byteFromFloat(pixF->m); + if (mValue != 0) { + ++buf[idx(0, (int)byteFromFloat(pixF->r * factor))]; + ++buf[idx(1, (int)byteFromFloat(pixF->g * factor))]; + ++buf[idx(2, (int)byteFromFloat(pixF->b * factor))]; + } + ++buf[idx(3, mValue)]; + } + } } else // 8bpc raster { for (j = 0; j < ly; j++) { @@ -662,6 +746,31 @@ void ComboHistogram::updateInfo(const TPixel64 &pix, const TPointD &imagePos) { //----------------------------------------------------------------------------- +void ComboHistogram::updateInfo(const TPixelF &pix, const TPointD &imagePos) { + if (pix == TPixelF::Transparent) { + m_histograms[0]->showCurrentChannelValue(-1); + m_histograms[1]->showCurrentChannelValue(-1); + m_histograms[2]->showCurrentChannelValue(-1); + m_histograms[3]->showCurrentChannelValue(-1); + m_rgbLabel->setColorAndUpdate(Qt::transparent); + m_xPosLabel->setText(""); + m_yPosLabel->setText(""); + } else { + float factor = std::pow(2.f, -(float)m_rangeStep); + TPixel32 pix32 = toPixel32( + TPixelF(pix.r * factor, pix.g * factor, pix.b * factor, pix.m)); + // show picked color's channel values + m_histograms[0]->showCurrentChannelValue((int)pix32.r); + m_histograms[1]->showCurrentChannelValue((int)pix32.g); + m_histograms[2]->showCurrentChannelValue((int)pix32.b); + m_histograms[3]->showCurrentChannelValue((int)pix32.m); + m_rgbLabel->setColorAndUpdate(QColor::fromRgbF(pix.r, pix.g, pix.b, pix.m)); + m_xPosLabel->setText(QString::number(tround(imagePos.x))); + m_yPosLabel->setText(QString::number(tround(imagePos.y))); + } +} +//----------------------------------------------------------------------------- + void ComboHistogram::updateAverageColor(const TPixel32 &pix) { if (pix == TPixel32::Transparent) { m_rectAverageRgbLabel->setColorAndUpdate(Qt::transparent); @@ -684,6 +793,17 @@ void ComboHistogram::updateAverageColor(const TPixel64 &pix) { //----------------------------------------------------------------------------- +void ComboHistogram::updateAverageColor(const TPixelF &pix) { + if (pix == TPixelF::Transparent) { + m_rectAverageRgbLabel->setColorAndUpdate(Qt::transparent); + } else { + m_rectAverageRgbLabel->setColorAndUpdate( + QColor::fromRgbF(pix.r, pix.g, pix.b, pix.m)); + } +} + +//----------------------------------------------------------------------------- + void ComboHistogram::onDisplayModeChanged() { ComboHistoRGBLabel::DisplayMode mode = static_cast( @@ -732,4 +852,36 @@ void ComboHistogram::onShowAlphaButtonToggled(bool alphaVisible) { m_rectAverageRgbLabel->setAlphaVisible(alphaVisible); m_rgbLabel->update(); m_rectAverageRgbLabel->update(); +} + +//----------------------------------------------------------------------------- + +void ComboHistogram::onRangeUp() { + m_rangeStep++; + m_rangeDwnBtn->setEnabled(true); + if (m_rangeStep == 3) m_rangeUpBtn->setDisabled(true); + + m_rangeLabel->setText(QString("%1.0").arg(std::pow(2, m_rangeStep))); + + refreshHistogram(); + m_compHistoIsValid = false; + if (m_showCompare) updateCompHistogram(); + + update(); +} + +//----------------------------------------------------------------------------- + +void ComboHistogram::onRangeDown() { + m_rangeStep--; + m_rangeUpBtn->setEnabled(true); + if (m_rangeStep == 0) m_rangeDwnBtn->setDisabled(true); + + m_rangeLabel->setText(QString("%1.0").arg(std::pow(2, m_rangeStep))); + + refreshHistogram(); + m_compHistoIsValid = false; + if (m_showCompare) updateCompHistogram(); + + update(); } \ No newline at end of file diff --git a/toonz/sources/toonzqt/flipconsole.cpp b/toonz/sources/toonzqt/flipconsole.cpp index 4359857..5f52a7f 100644 --- a/toonz/sources/toonzqt/flipconsole.cpp +++ b/toonz/sources/toonzqt/flipconsole.cpp @@ -11,6 +11,7 @@ // TnzLib includes #include "toonz/preferences.h" #include "toonz/tframehandle.h" +#include "toonz/toonzfolders.h" // TnzBase includes #include "tenv.h" @@ -75,6 +76,12 @@ QColor PBBaseColor = QColor(235, 235, 235); QColor PBNotStartedColor = QColor(210, 40, 40); QColor PBStartedColor = QColor(220, 160, 160); QColor PBFinishedColor = QColor(235, 235, 235); + +QString getFlipSettingsPath() { + return toQString(ToonzFolder::getMyModuleDir() + + TFilePath("fliphistory.ini")); +} + } // namespace //----------------------------------------------------------------------------- @@ -432,7 +439,8 @@ enum { eShowViewerControls = 0x1000, eShowSound = 0x2000, eShowLocator = 0x4000, - eShowHowMany = 0x8000 + eShowGainControls = 0x8000, + eShowHowMany = 0x10000 }; FlipConsole::FlipConsole(QVBoxLayout *mainLayout, std::vector gadgetsMask, @@ -466,7 +474,7 @@ FlipConsole::FlipConsole(QVBoxLayout *mainLayout, std::vector gadgetsMask, , m_blanksToDraw(0) , m_isLinkable(isLinkable) , m_customAction(0) - , m_customizeMask(eShowHowMany - 1) + , m_customizeMask((eShowHowMany - 1) & ~eShowGainControls) , m_fpsLabelAction(0) , m_fpsSliderAction(0) , m_fpsFieldAction(0) @@ -485,8 +493,13 @@ FlipConsole::FlipConsole(QVBoxLayout *mainLayout, std::vector gadgetsMask, , m_colorFilterGroup(0) , m_fpsLabel(0) , m_consoleOwner(consoleOwner) - , m_enableBlankFrameButton(0) { - QString s = QSettings().value(m_customizeId).toString(); + , m_enableBlankFrameButton(0) + , m_prevGainStep(0) + , m_gainSep(nullptr) + , m_resetGainBtn(nullptr) { + QSettings flipSettings(getFlipSettingsPath(), QSettings::IniFormat); + flipSettings.beginGroup("ConsoleCustomizeMasks"); + QString s = flipSettings.value(m_customizeId).toString(); if (s != "") m_customizeMask = s.toUInt(); if (m_gadgetsMask.size() == 0) return; @@ -1006,7 +1019,9 @@ void FlipConsole::onCustomizeButtonPressed(QAction *a) { else m_customizeMask = m_customizeMask & (~id); - QSettings().setValue(m_customizeId, QString::number(m_customizeMask)); + QSettings flipSettings(getFlipSettingsPath(), QSettings::IniFormat); + flipSettings.beginGroup("ConsoleCustomizeMasks"); + flipSettings.setValue(m_customizeId, QString::number(m_customizeMask)); applyCustomizeMask(); } @@ -1102,6 +1117,13 @@ void FlipConsole::applyCustomizeMask() { if (m_viewerSep) m_viewerSep->setVisible(m_customizeMask & eShowViewerControls); + if (m_resetGainBtn) { + enableButton(eDecreaseGain, m_customizeMask & eShowGainControls); + enableButton(eResetGain, m_customizeMask & eShowGainControls); + enableButton(eIncreaseGain, m_customizeMask & eShowGainControls); + m_gainSep->setVisible(m_customizeMask & eShowGainControls); + } + update(); } @@ -1170,6 +1192,9 @@ void FlipConsole::createCustomizeMenu(bool withCustomWidget) { if (hasButton(m_gadgetsMask, eRate)) addMenuItem(eShowFramerate, tr("Framerate"), menu); + if (hasButton(m_gadgetsMask, eDecreaseGain)) + addMenuItem(eShowGainControls, tr("Gain Controls"), menu); + bool ret = connect(menu, SIGNAL(triggered(QAction *)), this, SLOT(onCustomizeButtonPressed(QAction *))); assert(ret); @@ -1344,6 +1369,23 @@ void FlipConsole::createPlayToolBar(QWidget *customWidget) { m_viewerSep = m_playToolBar->addSeparator(); } + if (hasButton(m_gadgetsMask, eDecreaseGain)) { + createButton(eDecreaseGain, "prevkey", + tr("&Reduce gain 1/2 stop (divide by sqrt(2))"), false); + createButton(eResetGain, "", "f/8", false); + m_resetGainBtn = dynamic_cast( + m_playToolBar->widgetForAction(m_actions[eResetGain])); + m_resetGainBtn->setToolButtonStyle(Qt::ToolButtonTextOnly); + m_resetGainBtn->setToolTip( + tr("Toggle gain between 1 and the previous setting.\n" + "Gain is shown as an f-stop and the \"neutral\" or 1.0 gain f-stop " + "is f/8.")); + m_resetGainBtn->setFixedWidth(36); + createButton(eIncreaseGain, "nextkey", + tr("&Increase gain 1/2 stop (multiply by sqrt(2))"), false); + m_gainSep = m_playToolBar->addSeparator(); + } + // for all actions in this toolbar ret = ret && connect(m_playToolBar, SIGNAL(actionTriggered(QAction *)), this, SLOT(onButtonPressed(QAction *))); @@ -1463,7 +1505,7 @@ void FlipConsole::onButtonPressed(int button) { playingConsole->setChecked(eLoop, false); playingConsole->setChecked(ePause, true); stoppedOther = true; - m_stopAt = -1; + m_stopAt = -1; } } if (stoppedOther) { @@ -1538,8 +1580,7 @@ void FlipConsole::doButtonPressed(UINT button) { int from = m_from, to = m_to; // When the level editing mode, ignore the preview frame range marker if (m_markerFrom <= m_markerTo && m_frameHandle && - m_frameHandle->isEditingScene() && - m_stopAt == -1) + m_frameHandle->isEditingScene() && m_stopAt == -1) from = m_markerFrom, to = m_markerTo; bool linked = m_areLinked && m_isLinkable; @@ -1740,6 +1781,16 @@ void FlipConsole::doButtonPressed(UINT button) { case eResetView: return; + case eDecreaseGain: + adjustGain(false); + break; + case eIncreaseGain: + adjustGain(true); + break; + case eResetGain: + resetGain(); + break; + default: assert(false); break; @@ -2041,6 +2092,44 @@ void FlipConsole::onPreferenceChanged(const QString &prefName) { } } +//-------------------------------------------------------------------- + +void FlipConsole::adjustGain(bool increase) { + if (increase && m_settings.m_gainStep < 12) + m_settings.m_gainStep++; + else if (!increase && m_settings.m_gainStep > -12) + m_settings.m_gainStep--; + + // enable / disable buttons (-6 to +6 steps range) + enableButton(eDecreaseGain, m_settings.m_gainStep > -12, false); + enableButton(eIncreaseGain, m_settings.m_gainStep < 12, false); + + if (m_settings.m_gainStep != 0) m_prevGainStep = m_settings.m_gainStep; + + // update label + double fNumber = std::pow(2.0, 3.0 - (double)m_settings.m_gainStep * 0.25); + QString labelStr = QString("f/%1").arg(QString::number(fNumber, 'g', 2)); + double gain = std::pow(2.0, (double)m_settings.m_gainStep * 0.5); + m_resetGainBtn->setText(labelStr); + m_resetGainBtn->setToolTip(labelStr + tr(" (gain %1)").arg(gain)); +} + +void FlipConsole::resetGain(bool forceInit) { + if (forceInit) m_prevGainStep = 0; + + if (m_settings.m_gainStep == 0) + m_settings.m_gainStep = m_prevGainStep; + else + m_settings.m_gainStep = 0; + + // update label + double fNumber = std::pow(2.0, 3.0 - (double)m_settings.m_gainStep * 0.25); + QString labelStr = QString("f/%1").arg(QString::number(fNumber, 'g', 2)); + double gain = std::pow(2.0, (double)m_settings.m_gainStep * 0.5); + m_resetGainBtn->setText(labelStr); + m_resetGainBtn->setToolTip(labelStr + tr(" (gain %1)").arg(gain)); +} + //==================================================================== class FlipConsoleActionsCreator : AuxActionsCreator { diff --git a/toonz/sources/toonzqt/functiontreeviewer.cpp b/toonz/sources/toonzqt/functiontreeviewer.cpp index d43c794..cf39d18 100644 --- a/toonz/sources/toonzqt/functiontreeviewer.cpp +++ b/toonz/sources/toonzqt/functiontreeviewer.cpp @@ -272,7 +272,7 @@ QVariant StageObjectChannelGroup::data(int role) const { std::string name = (m_stageObject->getId().isTable()) ? FunctionTreeView::tr("Table").toStdString() : m_stageObject->getName(); - std::string id = m_stageObject->getId().toString(); + std::string id = m_stageObject->getId().toString(); return (name == id) ? QString::fromStdString(name) : QString::fromStdString(id + " (" + name + ")"); @@ -604,7 +604,8 @@ QVariant FunctionTreeModel::Channel::data(int role) const { if (isIgnored()) return isActive() ? paramIgnoredOn : paramIgnoredOff; return m_param->hasKeyframes() ? isActive() ? paramAnimOn : paramAnimOff - : isActive() ? paramOn : paramOff; + : isActive() ? paramOn + : paramOff; } else if (role == Qt::DisplayRole) { if (m_param->hasUILabel()) { return QString::fromStdString(m_param->getUILabel()); @@ -1144,8 +1145,11 @@ void FunctionTreeModel::addChannels(TFx *fx, ChannelGroup *groupItem, const std::string ¶mNamePref = fx->getFxType() + "."; int p, pCount = params->getParamCount(); - for (p = 0; p != pCount; ++p) + for (p = 0; p != pCount; ++p) { + // hidden parameter are not displayed in the tree + if (params->isParamHidden(p)) continue; addParameter(fxItem, paramNamePref, fxId, params->getParam(p)); + } } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/fxhistogramrender.cpp b/toonz/sources/toonzqt/fxhistogramrender.cpp index 537a903..00120c6 100644 --- a/toonz/sources/toonzqt/fxhistogramrender.cpp +++ b/toonz/sources/toonzqt/fxhistogramrender.cpp @@ -90,7 +90,8 @@ void FxHistogramRender::computeHistogram(TFxP fx, int frame) { if (!sceneProperties) return; TOutputProperties *outputProperties = sceneProperties->getPreviewProperties(); if (!outputProperties) return; - const TRenderSettings rs = outputProperties->getRenderSettings(); + TRenderSettings rs = outputProperties->getRenderSettings(); + TFxP buildedFx; if (m_isCameraViewMode) buildedFx = @@ -167,8 +168,9 @@ void FxHistogramRender::remakeRender() { TRectD area(TPointD(-0.5 * size.lx, -0.5 * size.ly), TDimensionD(size.lx, size.ly)); m_renderPort->setRenderArea(area); - const TRenderSettings rs = + TRenderSettings rs = m_scene->getProperties()->getPreviewProperties()->getRenderSettings(); + TFxP buildedFx = buildPartialSceneFx(m_scene, (double)m_lastFrameInfo.m_frame, m_lastFrameInfo.m_fx, rs.m_shrinkX, true); diff --git a/toonz/sources/toonzqt/fxsettings.cpp b/toonz/sources/toonzqt/fxsettings.cpp index 93931b0..5e900eb 100644 --- a/toonz/sources/toonzqt/fxsettings.cpp +++ b/toonz/sources/toonzqt/fxsettings.cpp @@ -65,43 +65,6 @@ bool hasEmptyInputPort(const TFxP ¤tFx) { if (currentFx->getInputPortCount() == 0) return false; return hasEmptyInputPort(currentFx->getInputPort(0)->getFx()); } -/* -TFxP cloneInputPort(const TFxP ¤tFx) -{ - int i; - for (i=0; igetInputPort(i)->getFx(); - if(inputFx) - { - if(TLevelColumnFx* affFx = dynamic_cast(inputFx)) - currentFx->getInputPort(i)->setFx(inputFx); - else - currentFx->getInputPort(i)->setFx(cloneInputPort()); - } - TFxPort *port = getInputPort(i); - if (port->getFx()) - fx->connect(getInputPortName(i), -cloneInputPort(port->getFx())); -} -void setLevelFxInputPort(const TFxP ¤tFx, const TFxP &sceneFx) -{ - for (int i=0; igetInputPortCount(); ++i) - { - TFx *inputFx = sceneFx->getInputPort(i)->getFx(); - if(inputFx) - { - if(TLevelColumnFx* affFx = dynamic_cast(inputFx)) - currentFx->getInputPort(i)->setFx(inputFx); - else - setLevelFxInputPort(currentFx->getInputPort(i)->getFx(), -inputFx); - } - } -} -*/ // find the field by parameter name and register the field and its label widget bool findItemByParamName(QLayout *layout, std::string name, @@ -131,7 +94,6 @@ bool findItemByParamName(QLayout *layout, std::string name, } return false; }; - } // namespace //============================================================================= @@ -192,6 +154,7 @@ void ParamsPage::setPageField(TIStream &is, const TFxP &fx, bool isVertical) { std::string name; is >> name; is.matchEndTag(); + /*-- Layout設定名とFxParameterの名前が一致するものを取得 --*/ TParamP param = fx->getParams()->getParam(name); bool isHidden = @@ -760,6 +723,13 @@ ParamsPageSet::ParamsPageSet(QWidget *parent, Qt::WFlags flags) m_helpButton->setObjectName("FxSettingsHelpButton"); m_helpButton->setFocusPolicy(Qt::NoFocus); + m_warningMark = new QLabel(this); + static QIcon warningIcon(":Resources/paramignored_on.svg"); + m_warningMark->setPixmap(warningIcon.pixmap(QSize(22, 22))); + m_warningMark->setFixedSize(22, 22); + m_warningMark->setStyleSheet( + "margin: 0px; padding: 0px; background-color: rgba(0,0,0,0);"); + //----layout QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setMargin(0); @@ -770,6 +740,7 @@ ParamsPageSet::ParamsPageSet(QWidget *parent, Qt::WFlags flags) hLayout->addSpacing(0); { hLayout->addWidget(m_tabBar); + hLayout->addWidget(m_warningMark); hLayout->addStretch(1); hLayout->addWidget(m_helpButton); } @@ -1047,6 +1018,49 @@ void ParamsPageSet::openHelpUrl() { QDesktopServices::openUrl(QUrl(QString(m_helpUrl.c_str()))); } +void ParamsPageSet::updateWarnings(const TFxP ¤tFx, bool isFloat) { + if (!isFloat) { + m_warningMark->hide(); + return; + } + + bool isFloatSupported = true; + + TMacroFx *currentFxMacro = dynamic_cast(currentFx.getPointer()); + if (currentFxMacro) { + const std::vector ¤tFxMacroFxs = currentFxMacro->getFxs(); + for (auto fxP : currentFxMacroFxs) { + TRasterFx *rasFx = dynamic_cast(fxP.getPointer()); + if (rasFx) { + isFloatSupported = isFloatSupported & rasFx->canComputeInFloat(); + } + if (!isFloatSupported) break; + } + } else { + TRasterFx *rasFx = dynamic_cast(currentFx.getPointer()); + if (rasFx) { + isFloatSupported = rasFx->canComputeInFloat(); + } + } + + bool showFloatWarning = isFloat && !isFloatSupported; + if (!showFloatWarning) { + m_warningMark->hide(); + return; + } + + QString warningTxt; + if (showFloatWarning) { + warningTxt += + tr("This Fx does not support rendering in floating point channel width " + "(32bit).\n" + "The output pixel values from this fx will be clamped to 0.0 - 1.0\n" + "and tone may be slightly discretized."); + } + m_warningMark->setToolTip(warningTxt); + m_warningMark->show(); +} + //============================================================================= // ParamViewer //----------------------------------------------------------------------------- @@ -1191,6 +1205,13 @@ ParamsPageSet *ParamViewer::getCurrentPageSet() const { return dynamic_cast(m_tablePageSet->currentWidget()); } +//----------------------------------------------------------------------------- +// show warning if the current Fx does not support float / linear rendering +void ParamViewer::updateWarnings(const TFxP ¤tFx, bool isFloat) { + if (getCurrentPageSet()) + getCurrentPageSet()->updateWarnings(currentFx, isFloat); +} + //============================================================================= // FxSettings //----------------------------------------------------------------------------- @@ -1396,11 +1417,24 @@ void FxSettings::setFx(const TFxP ¤tFx, const TFxP &actualFx) { ToonzScene *scene = 0; if (m_sceneHandle) scene = m_sceneHandle->getScene(); + // check if the current render settings are float + bool isFloat = false; + if (scene) { + const TRenderSettings ps = + scene->getProperties()->getPreviewProperties()->getRenderSettings(); + const TRenderSettings os = + scene->getProperties()->getOutputProperties()->getRenderSettings(); + isFloat = (ps.m_bpp == 128) || (os.m_bpp == 128); + } + int frameIndex = 0; if (m_frameHandle) frameIndex = m_frameHandle->getFrameIndex(); m_paramViewer->setFx(currentFxWithoutCamera, actualFx, frameIndex, scene); m_paramViewer->setIsCameraViewMode(m_isCameraModeView); + // show warning if the current Fx does not support float / linear rendering + m_paramViewer->updateWarnings(currentFxWithoutCamera, isFloat); + m_viewer->setCameraMode(m_isCameraModeView); TDimension cameraSize = TDimension(-1, -1); diff --git a/toonz/sources/toonzqt/lutcalibrator.cpp b/toonz/sources/toonzqt/lutcalibrator.cpp index 22de93d..df6a5b0 100644 --- a/toonz/sources/toonzqt/lutcalibrator.cpp +++ b/toonz/sources/toonzqt/lutcalibrator.cpp @@ -566,8 +566,11 @@ void LutManager::convert(float& r, float& g, float& b) { float ratio[3]; // RGB軸 int index[3][2]; // rgb インデックス float rawVal[3] = {r, g, b}; + // clamp values (for HDR image) + for (int c = 0; c < 3; c++) + rawVal[c] = (rawVal[c] < 0.f) ? 0.f : (rawVal[c] > 1.f) ? 1.f : rawVal[c]; - float vertex_color[2][2][2][3]; //補間用の1ボクセルの頂点色 + float vertex_color[2][2][2][3]; // 補間用の1ボクセルの頂点色 for (int c = 0; c < 3; c++) { float val = rawVal[c] * (float)(m_lut.meshSize - 1); @@ -615,9 +618,7 @@ void LutManager::convert(QColor& col) { float g = col.greenF(); float b = col.blueF(); convert(r, g, b); - // 0.5 offset is necessary for converting to 255 grading - col = QColor((int)(r * 255.0 + 0.5), (int)(g * 255.0 + 0.5), - (int)(b * 255.0 + 0.5), col.alpha()); + col = QColor::fromRgbF(r, g, b, col.alphaF()); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/pluginhost.cpp b/toonz/sources/toonzqt/pluginhost.cpp index 8bf7bef..5895fec 100644 --- a/toonz/sources/toonzqt/pluginhost.cpp +++ b/toonz/sources/toonzqt/pluginhost.cpp @@ -36,7 +36,7 @@ #include #include #include -//#include +// #include #include "plugin_param_traits.h" #include "../include/toonzqt/pluginloader.h" @@ -451,7 +451,7 @@ RasterFxPluginHost *RasterFxPluginHost::newInstance( } const TPersistDeclaration *RasterFxPluginHost::getDeclaration() const { - printf("RasterFxPluginHost::getDeclaration()\n"); + // printf("RasterFxPluginHost::getDeclaration()\n"); return pi_->decl_; } @@ -1560,11 +1560,10 @@ PluginLoadController::PluginLoadController(const std::string &basedir, connect(&work_entity, &QThread::finished, ld, &QObject::deleteLater); /* AddFxContextMenu から呼ばれていたが、プラグインの検索が load_entries() を通じて起動時に呼ばれるようにした関係で, - (あまりよくはないが)listener の有無によって receiver を分けるようにしている. - listener がいる場合は従来通り context menu の構築のために - AddFxContextMenu::fixup() に接続するが - それ以外では plugin_dict_ への追加のため PluginLoadController::finished - に接続する. + (あまりよくはないが)listener の有無によって receiver + を分けるようにしている. listener がいる場合は従来通り context menu + の構築のために AddFxContextMenu::fixup() に接続するが それ以外では + plugin_dict_ への追加のため PluginLoadController::finished に接続する. */ if (listener) { AddFxContextMenu *a = qobject_cast(listener); @@ -1619,4 +1618,4 @@ static bool copy_rendering_setting(toonz_rendering_setting_t *dst, return true; } -//#include "pluginhost.moc" +// #include "pluginhost.moc"