diff --git a/stuff/config/current.txt b/stuff/config/current.txt index 9810bda0..12f20f42 100644 --- a/stuff/config/current.txt +++ b/stuff/config/current.txt @@ -821,6 +821,8 @@ "STD_particlesFx.source_gradation" "Use Control Image Gradation" "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_adjust" "Gamma Adjust" @@ -842,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" @@ -1190,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" @@ -1203,9 +1251,12 @@ "STD_iwa_GradientWarpFx.h_maxlen" "H Length" "STD_iwa_GradientWarpFx.v_maxlen" "V Length" "STD_iwa_GradientWarpFx.scale" "Scale" + "STD_iwa_GradientWarpFx.sampling_size" "Sampling Size" "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" @@ -1297,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" @@ -1312,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" @@ -1320,37 +1377,60 @@ "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" + "STD_iwa_BokehAdvancedFx.doMedian1" "Use Median" "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" + "STD_iwa_BokehAdvancedFx.doMedian2" "Use Median" "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" + "STD_iwa_BokehAdvancedFx.doMedian3" "Use Median" "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" + "STD_iwa_BokehAdvancedFx.doMedian4" "Use Median" "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" + "STD_iwa_BokehAdvancedFx.doMedian5" "Use Median" "STD_iwa_TimeCodeFx" "TimeCode Iwa" "STD_iwa_TimeCodeFx.displayType" "Display Type" @@ -1417,7 +1497,26 @@ "STD_iwa_LinearGradientFx.wave_amplitude" "Amplitude" "STD_iwa_LinearGradientFx.wave_frequency" "Frequency" "STD_iwa_LinearGradientFx.wave_phase" "Phase" - + + "STD_iwa_FloorBumpFx" "Floor Bump Iwa" + "STD_iwa_FloorBumpFx.renderMode" "Render Mode" + "STD_iwa_FloorBumpFx.fov" "Fov" + "STD_iwa_FloorBumpFx.cameraAltitude" "Camera Altitude" + "STD_iwa_FloorBumpFx.eyeLevel" "Eye Level" + "STD_iwa_FloorBumpFx.drawLevel" "Draw Level" + "STD_iwa_FloorBumpFx.waveHeight" "Wave Height" + "STD_iwa_FloorBumpFx.textureOffsetAmount" "Amount" + "STD_iwa_FloorBumpFx.textureOffsetSpread" "Spread" + "STD_iwa_FloorBumpFx.sourcePrecision" "Precision" + "STD_iwa_FloorBumpFx.souceMargin" "Margin" + "STD_iwa_FloorBumpFx.lightAzimuth" "Azimuth" + "STD_iwa_FloorBumpFx.lightElevation" "Elevation" + "STD_iwa_FloorBumpFx.depth" "Depth" + "STD_iwa_FloorBumpFx.refractiveIndex" "Refractive Index" + "STD_iwa_FloorBumpFx.distanceLevel" "Distance Level" + "STD_iwa_FloorBumpFx.differenceMode" "Render difference from unbumped state" + "STD_iwa_FloorBumpFx.displacement" "Displacement" + "STD_iwa_GlareFx" "Glare Iwa" "STD_iwa_GlareFx.renderMode" "Render Mode" "STD_iwa_GlareFx.irisMode" "Iris Shape" @@ -1465,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" @@ -1482,6 +1582,52 @@ "STD_iwa_RainbowFx.secondary_rainbow" "Secondary Rainbow Intensity" "STD_iwa_RainbowFx.alpha_rendering" "Alpha Rendering" + "STD_iwa_TangentFlowFx" "Tangent Flow Iwa" + "STD_iwa_TangentFlowFx.iteration" "Iteration" + "STD_iwa_TangentFlowFx.kernelRadius" "Kernel Radius" + "STD_iwa_TangentFlowFx.threshold" "Threshold" + "STD_iwa_TangentFlowFx.alignDirection" "Align Direction" + "STD_iwa_TangentFlowFx.pivotAngle" "Pivot Angle" + + "STD_iwa_FlowBlurFx" "Flow Blur Iwa" + "STD_iwa_FlowBlurFx.length" "Length" + "STD_iwa_FlowBlurFx.filterType" "Filter Type" + "STD_iwa_FlowBlurFx.linear" "Linear Color Space" + "STD_iwa_FlowBlurFx.gamma" "Gamma" + "STD_iwa_FlowBlurFx.referenceMode" "Reference" + + "STD_iwa_FlowPaintBrushFx" "Flow Paint Brush Iwa" + "STD_iwa_FlowPaintBrushFx.h_density" "Horizontal Density" + "STD_iwa_FlowPaintBrushFx.v_density" "Vertical Density" + "STD_iwa_FlowPaintBrushFx.pos_randomness" "Position Randomness" + "STD_iwa_FlowPaintBrushFx.pos_wobble" "Position Wobble by Frame" + "STD_iwa_FlowPaintBrushFx.tip_width" "Tip Width" + "STD_iwa_FlowPaintBrushFx.tip_length" "Tip Length" + "STD_iwa_FlowPaintBrushFx.tip_alpha" "Tip Alpha" + "STD_iwa_FlowPaintBrushFx.tip_joints" "Tip Joints" + "STD_iwa_FlowPaintBrushFx.bidirectional" "Bidirectional" + "STD_iwa_FlowPaintBrushFx.width_randomness" "Width Randomness" + "STD_iwa_FlowPaintBrushFx.length_randomness" "Length Randomness" + "STD_iwa_FlowPaintBrushFx.angle_randomness" "Angle Randomness" + "STD_iwa_FlowPaintBrushFx.sustain_width_to_skew" "Sustain Width to Skew" + "STD_iwa_FlowPaintBrushFx.anti_jaggy" "Prevent Jaggies by Texture Margin" + "STD_iwa_FlowPaintBrushFx.origin_pos" "Origin" + "STD_iwa_FlowPaintBrushFx.horizontal_pos" "Horizontal" + "STD_iwa_FlowPaintBrushFx.vertical_pos" "Vertical" + "STD_iwa_FlowPaintBrushFx.curve_point" "Curve Anchor" + "STD_iwa_FlowPaintBrushFx.fill_gap_size" "Fill Gap Size" + "STD_iwa_FlowPaintBrushFx.reference_frame" "Reference Frame" + "STD_iwa_FlowPaintBrushFx.reference_prevalence" "Reference Prevalence" + "STD_iwa_FlowPaintBrushFx.random_seed" "Random Seed" + "STD_iwa_FlowPaintBrushFx.sort_by" "Sort By" + + "STD_iwa_MotionFlowFx" "Motion Flow Iwa" + "STD_iwa_MotionFlowFx.shutterLength" "Shutter Length" + "STD_iwa_MotionFlowFx.motionObjectType" "Reference Object" + "STD_iwa_MotionFlowFx.motionObjectIndex" "Index" + "STD_iwa_MotionFlowFx.normalizeType" "Normalize" + "STD_iwa_MotionFlowFx.normalizeRange" "Maximum Length" + STD_iwa_TiledParticlesFx "Tiled Particles Iwa" diff --git a/stuff/config/qss/Dark/Dark.qss b/stuff/config/qss/Dark/Dark.qss index c2f48617..8e7505d4 100644 --- a/stuff/config/qss/Dark/Dark.qss +++ b/stuff/config/qss/Dark/Dark.qss @@ -835,6 +835,7 @@ QTreeWidget { ----------------------------------------------------------------------------- */ .Button, QPushButton, +#CustomPanelButton, .ComboBox, #enableBlankFrameButton, QComboBox { @@ -849,6 +850,7 @@ QComboBox { } .Button:hover, QPushButton:hover, +#CustomPanelButton:hover, #ViewerFpsSlider::sub-line:horizontal:hover, #ViewerFpsSlider::add-line:horizontal:hover, #enableBlankFrameButton:hover { @@ -858,6 +860,7 @@ QPushButton:hover, } .Button:pressed, QPushButton:pressed, +#CustomPanelButton:pressed, #ViewerFpsSlider::sub-line:horizontal:pressed, #ViewerFpsSlider::add-line:horizontal:pressed, #enableBlankFrameButton:pressed { @@ -867,6 +870,7 @@ QPushButton:pressed, } .Button:checked, QPushButton:checked, +#CustomPanelButton:checked, #enableBlankFrameButton:checked { background-color: #141414; border-color: #0f0f0f; @@ -874,17 +878,20 @@ QPushButton:checked, } .Button:checked:hover, QPushButton:checked:hover, +#CustomPanelButton:checked:hover, #enableBlankFrameButton:checked:hover { background-color: #282828; border-color: #1c1c1c; } .Button:checked:hover:pressed, QPushButton:checked:hover:pressed, +#CustomPanelButton:checked:hover:pressed, #enableBlankFrameButton:checked:hover:pressed { background: #1c1c1c; } .Button:disabled, QPushButton:disabled, +#CustomPanelButton:disabled, .ComboBox:disabled, #ViewerFpsSlider::sub-line:horizontal:disabled, #ViewerFpsSlider::add-line:horizontal:disabled, @@ -897,6 +904,9 @@ QComboBox:disabled { #PushButton_NoPadding { padding: 3; } +#CustomPanelButton { + padding: 0; +} /* ----------------------------------------------------------------------------- ComboBox ----------------------------------------------------------------------------- */ @@ -1289,6 +1299,13 @@ QProgressBar::chunk { border: 1 solid #111111; border-radius: 2; } +/* ----------------------------------------------------------------------------- + Custom Panel Widgets +----------------------------------------------------------------------------- */ +MyScroller { + qproperty-ScrollerBorderColor: #8d8d8d; + qproperty-ScrollerBGColor: #1c1c1c; +} /* ----------------------------------------------------------------------------- File Browser ----------------------------------------------------------------------------- */ @@ -1525,6 +1542,12 @@ QStatusBar #StatusBarLabel { #HexagonalColorWheel { qproperty-BGColor: #303030; } +#StyleChooserPage { + qproperty-CommonChipBoxColor: black; + qproperty-SolidChipBoxColor: #e41000; + qproperty-SelectedChipBoxColor: white; + qproperty-SelectedChipBox2Color: #c7ca32; +} /* -------------------------------------------------------------------------- */ /* Horizontal QSlider */ #colorSlider::groove:horizontal { @@ -2262,9 +2285,13 @@ Ruler { XsheetViewer { qproperty-TextColor: #e6e6e6; qproperty-ErrorTextColor: #ff7b7b; + qproperty-SelectedTextColor: #e6e6e6; + qproperty-CurrentFrameTextColor: #e6e6e6; qproperty-BGColor: #303030; qproperty-LightLineColor: rgba(0, 0, 0, 0.3); qproperty-MarkerLineColor: rgba(255, 255, 255, 0.15); + qproperty-SecMarkerLineColor: rgba(255, 255, 255, 0.25); + qproperty-SelectedMarkerLineColor: rgba(255, 255, 255, 0.15); qproperty-VerticalLineColor: rgba(0, 0, 0, 0.4); qproperty-VerticalLineHeadColor: #0f0f0f; qproperty-PreviewFrameTextColor: #9fdaff; @@ -2398,8 +2425,8 @@ FunctionTreeView { } FunctionPanel { qproperty-BGColor: #303030; - qproperty-ValueLineColor: rgba(0, 0, 0, 0.1); - qproperty-FrameLineColor: rgba(0, 0, 0, 0.1); + qproperty-ValueLineColor: #3a3a3a; + qproperty-FrameLineColor: #444444; qproperty-OtherCurvesColor: #7d7d7d; qproperty-RulerBackground: #282828; qproperty-TextColor: #e6e6e6; @@ -2411,6 +2438,7 @@ SpreadsheetViewer { qproperty-CurrentRowBgColor: rgba(163, 82, 147, 0.7); qproperty-LightLineColor: rgba(0, 0, 0, 0.3); qproperty-MarkerLineColor: rgba(255, 255, 255, 0.15); + qproperty-SecMarkerLineColor: rgba(255, 255, 255, 0.25); qproperty-BGColor: #262626; qproperty-VerticalLineColor: rgba(0, 0, 0, 0.4); qproperty-KeyFrameColor: #995d1d; @@ -2426,6 +2454,7 @@ SpreadsheetViewer { qproperty-SelectedEmptyColor: rgba(106, 89, 102, 0.5); qproperty-SelectedSceneRangeEmptyColor: rgba(106, 89, 102, 0.5); qproperty-TextColor: #e6e6e6; + qproperty-CurrentRowTextColor: #e6e6e6; qproperty-ColumnHeaderBorderColor: #4a4a4a; } #ExpressionField { diff --git a/stuff/config/qss/Darker/Darker.qss b/stuff/config/qss/Darker/Darker.qss index 66284409..a5f5bf0d 100644 --- a/stuff/config/qss/Darker/Darker.qss +++ b/stuff/config/qss/Darker/Darker.qss @@ -835,6 +835,7 @@ QTreeWidget { ----------------------------------------------------------------------------- */ .Button, QPushButton, +#CustomPanelButton, .ComboBox, #enableBlankFrameButton, QComboBox { @@ -849,6 +850,7 @@ QComboBox { } .Button:hover, QPushButton:hover, +#CustomPanelButton:hover, #ViewerFpsSlider::sub-line:horizontal:hover, #ViewerFpsSlider::add-line:horizontal:hover, #enableBlankFrameButton:hover { @@ -858,6 +860,7 @@ QPushButton:hover, } .Button:pressed, QPushButton:pressed, +#CustomPanelButton:pressed, #ViewerFpsSlider::sub-line:horizontal:pressed, #ViewerFpsSlider::add-line:horizontal:pressed, #enableBlankFrameButton:pressed { @@ -867,6 +870,7 @@ QPushButton:pressed, } .Button:checked, QPushButton:checked, +#CustomPanelButton:checked, #enableBlankFrameButton:checked { background-color: #040404; border-color: #000000; @@ -874,17 +878,20 @@ QPushButton:checked, } .Button:checked:hover, QPushButton:checked:hover, +#CustomPanelButton:checked:hover, #enableBlankFrameButton:checked:hover { background-color: #181818; border-color: #0c0c0c; } .Button:checked:hover:pressed, QPushButton:checked:hover:pressed, +#CustomPanelButton:checked:hover:pressed, #enableBlankFrameButton:checked:hover:pressed { background: #0c0c0c; } .Button:disabled, QPushButton:disabled, +#CustomPanelButton:disabled, .ComboBox:disabled, #ViewerFpsSlider::sub-line:horizontal:disabled, #ViewerFpsSlider::add-line:horizontal:disabled, @@ -897,6 +904,9 @@ QComboBox:disabled { #PushButton_NoPadding { padding: 3; } +#CustomPanelButton { + padding: 0; +} /* ----------------------------------------------------------------------------- ComboBox ----------------------------------------------------------------------------- */ @@ -1289,6 +1299,13 @@ QProgressBar::chunk { border: 1 solid #060606; border-radius: 2; } +/* ----------------------------------------------------------------------------- + Custom Panel Widgets +----------------------------------------------------------------------------- */ +MyScroller { + qproperty-ScrollerBorderColor: #8d8d8d; + qproperty-ScrollerBGColor: #0c0c0c; +} /* ----------------------------------------------------------------------------- File Browser ----------------------------------------------------------------------------- */ @@ -1525,6 +1542,12 @@ QStatusBar #StatusBarLabel { #HexagonalColorWheel { qproperty-BGColor: #202020; } +#StyleChooserPage { + qproperty-CommonChipBoxColor: black; + qproperty-SolidChipBoxColor: #e41000; + qproperty-SelectedChipBoxColor: white; + qproperty-SelectedChipBox2Color: #c7ca32; +} /* -------------------------------------------------------------------------- */ /* Horizontal QSlider */ #colorSlider::groove:horizontal { @@ -2262,9 +2285,13 @@ Ruler { XsheetViewer { qproperty-TextColor: #e6e6e6; qproperty-ErrorTextColor: #ff7b7b; + qproperty-SelectedTextColor: #e6e6e6; + qproperty-CurrentFrameTextColor: #e6e6e6; qproperty-BGColor: #1b1b1b; qproperty-LightLineColor: rgba(0, 0, 0, 0.2); qproperty-MarkerLineColor: rgba(255, 255, 255, 0.15); + qproperty-SecMarkerLineColor: rgba(255, 255, 255, 0.25); + qproperty-SelectedMarkerLineColor: rgba(255, 255, 255, 0.15); qproperty-VerticalLineColor: rgba(0, 0, 0, 0.3); qproperty-VerticalLineHeadColor: #000000; qproperty-PreviewFrameTextColor: #9fdaff; @@ -2397,13 +2424,13 @@ FunctionTreeView { padding-left: 2; } FunctionPanel { - qproperty-BGColor: #282828; - qproperty-ValueLineColor: rgba(0, 0, 0, 0.1); - qproperty-FrameLineColor: rgba(0, 0, 0, 0.1); - qproperty-OtherCurvesColor: #747474; - qproperty-RulerBackground: #202020; + qproperty-BGColor: #131313; + qproperty-ValueLineColor: #1d1d1d; + qproperty-FrameLineColor: #282828; + qproperty-OtherCurvesColor: #606060; + qproperty-RulerBackground: #0c0c0c; qproperty-TextColor: #e6e6e6; - qproperty-SubColor: #282828; + qproperty-SubColor: #131313; qproperty-SelectedColor: #FFA500; } SpreadsheetViewer { @@ -2411,6 +2438,7 @@ SpreadsheetViewer { qproperty-CurrentRowBgColor: rgba(163, 82, 147, 0.7); qproperty-LightLineColor: rgba(0, 0, 0, 0.2); qproperty-MarkerLineColor: rgba(255, 255, 255, 0.15); + qproperty-SecMarkerLineColor: rgba(255, 255, 255, 0.25); qproperty-BGColor: #282828; qproperty-VerticalLineColor: rgba(0, 0, 0, 0.3); qproperty-KeyFrameColor: #995d1d; @@ -2426,6 +2454,7 @@ SpreadsheetViewer { qproperty-SelectedEmptyColor: rgba(99, 83, 96, 0.5); qproperty-SelectedSceneRangeEmptyColor: rgba(99, 83, 96, 0.5); qproperty-TextColor: #e6e6e6; + qproperty-CurrentRowTextColor: #e6e6e6; qproperty-ColumnHeaderBorderColor: #000000; } #ExpressionField { diff --git a/stuff/config/qss/Light/Light.qss b/stuff/config/qss/Light/Light.qss index 7fb0d894..17290f0d 100644 --- a/stuff/config/qss/Light/Light.qss +++ b/stuff/config/qss/Light/Light.qss @@ -835,6 +835,7 @@ QTreeWidget { ----------------------------------------------------------------------------- */ .Button, QPushButton, +#CustomPanelButton, .ComboBox, #enableBlankFrameButton, QComboBox { @@ -849,6 +850,7 @@ QComboBox { } .Button:hover, QPushButton:hover, +#CustomPanelButton:hover, #ViewerFpsSlider::sub-line:horizontal:hover, #ViewerFpsSlider::add-line:horizontal:hover, #enableBlankFrameButton:hover { @@ -858,6 +860,7 @@ QPushButton:hover, } .Button:pressed, QPushButton:pressed, +#CustomPanelButton:pressed, #ViewerFpsSlider::sub-line:horizontal:pressed, #ViewerFpsSlider::add-line:horizontal:pressed, #enableBlankFrameButton:pressed { @@ -867,6 +870,7 @@ QPushButton:pressed, } .Button:checked, QPushButton:checked, +#CustomPanelButton:checked, #enableBlankFrameButton:checked { background-color: #b5b5b5; border-color: #8e8e8e; @@ -874,17 +878,20 @@ QPushButton:checked, } .Button:checked:hover, QPushButton:checked:hover, +#CustomPanelButton:checked:hover, #enableBlankFrameButton:checked:hover { background-color: #c9c9c9; border-color: #bcbcbc; } .Button:checked:hover:pressed, QPushButton:checked:hover:pressed, +#CustomPanelButton:checked:hover:pressed, #enableBlankFrameButton:checked:hover:pressed { background: #bcbcbc; } .Button:disabled, QPushButton:disabled, +#CustomPanelButton:disabled, .ComboBox:disabled, #ViewerFpsSlider::sub-line:horizontal:disabled, #ViewerFpsSlider::add-line:horizontal:disabled, @@ -897,6 +904,9 @@ QComboBox:disabled { #PushButton_NoPadding { padding: 3; } +#CustomPanelButton { + padding: 0; +} /* ----------------------------------------------------------------------------- ComboBox ----------------------------------------------------------------------------- */ @@ -1289,6 +1299,13 @@ QProgressBar::chunk { border: 1 solid #a8a8a8; border-radius: 2; } +/* ----------------------------------------------------------------------------- + Custom Panel Widgets +----------------------------------------------------------------------------- */ +MyScroller { + qproperty-ScrollerBorderColor: #000000; + qproperty-ScrollerBGColor: #c7c7c7; +} /* ----------------------------------------------------------------------------- File Browser ----------------------------------------------------------------------------- */ @@ -1525,6 +1542,12 @@ QStatusBar #StatusBarLabel { #HexagonalColorWheel { qproperty-BGColor: #DBDBDB; } +#StyleChooserPage { + qproperty-CommonChipBoxColor: #c0c0c0; + qproperty-SolidChipBoxColor: #e49080; + qproperty-SelectedChipBoxColor: black; + qproperty-SelectedChipBox2Color: #c7ca32; +} /* -------------------------------------------------------------------------- */ /* Horizontal QSlider */ #colorSlider::groove:horizontal { @@ -2262,9 +2285,13 @@ Ruler { XsheetViewer { qproperty-TextColor: #000; qproperty-ErrorTextColor: #c01111; + qproperty-SelectedTextColor: #000; + qproperty-CurrentFrameTextColor: #000; qproperty-BGColor: #cecece; qproperty-LightLineColor: rgba(0, 0, 0, 0.15); qproperty-MarkerLineColor: rgba(0, 0, 0, 0.3); + qproperty-SecMarkerLineColor: rgba(0, 0, 0, 0.5); + qproperty-SelectedMarkerLineColor: rgba(0, 0, 0, 0.3); qproperty-VerticalLineColor: rgba(0, 0, 0, 0.15); qproperty-VerticalLineHeadColor: rgba(0, 0, 0, 0.3); qproperty-PreviewFrameTextColor: #2d42b9; @@ -2397,9 +2424,9 @@ FunctionTreeView { padding-left: 2; } FunctionPanel { - qproperty-BGColor: #808080; - qproperty-ValueLineColor: rgba(0, 0, 0, 0.1); - qproperty-FrameLineColor: rgba(0, 0, 0, 0.1); + qproperty-BGColor: #828282; + qproperty-ValueLineColor: #787878; + qproperty-FrameLineColor: #6d6d6d; qproperty-OtherCurvesColor: #dadada; qproperty-RulerBackground: #c2c2c2; qproperty-TextColor: #000; @@ -2411,6 +2438,7 @@ SpreadsheetViewer { qproperty-CurrentRowBgColor: rgba(216, 87, 177, 0.7); qproperty-LightLineColor: rgba(0, 0, 0, 0.15); qproperty-MarkerLineColor: rgba(0, 0, 0, 0.3); + qproperty-SecMarkerLineColor: rgba(0, 0, 0, 0.5); qproperty-BGColor: #cecece; qproperty-VerticalLineColor: rgba(0, 0, 0, 0.15); qproperty-KeyFrameColor: #edaa64; @@ -2426,6 +2454,7 @@ SpreadsheetViewer { qproperty-SelectedEmptyColor: rgba(157, 139, 152, 0.5); qproperty-SelectedSceneRangeEmptyColor: rgba(157, 139, 152, 0.5); qproperty-TextColor: #000; + qproperty-CurrentRowTextColor: #000; qproperty-ColumnHeaderBorderColor: #5b5b5b; } #ExpressionField { diff --git a/stuff/config/qss/Medium/Medium.qss b/stuff/config/qss/Medium/Medium.qss index 86a44981..afcd8a77 100644 --- a/stuff/config/qss/Medium/Medium.qss +++ b/stuff/config/qss/Medium/Medium.qss @@ -835,6 +835,7 @@ QTreeWidget { ----------------------------------------------------------------------------- */ .Button, QPushButton, +#CustomPanelButton, .ComboBox, #enableBlankFrameButton, QComboBox { @@ -849,6 +850,7 @@ QComboBox { } .Button:hover, QPushButton:hover, +#CustomPanelButton:hover, #ViewerFpsSlider::sub-line:horizontal:hover, #ViewerFpsSlider::add-line:horizontal:hover, #enableBlankFrameButton:hover { @@ -858,6 +860,7 @@ QPushButton:hover, } .Button:pressed, QPushButton:pressed, +#CustomPanelButton:pressed, #ViewerFpsSlider::sub-line:horizontal:pressed, #ViewerFpsSlider::add-line:horizontal:pressed, #enableBlankFrameButton:pressed { @@ -867,6 +870,7 @@ QPushButton:pressed, } .Button:checked, QPushButton:checked, +#CustomPanelButton:checked, #enableBlankFrameButton:checked { background-color: #2c2c2c; border-color: #272727; @@ -874,17 +878,20 @@ QPushButton:checked, } .Button:checked:hover, QPushButton:checked:hover, +#CustomPanelButton:checked:hover, #enableBlankFrameButton:checked:hover { background-color: #404040; border-color: #343434; } .Button:checked:hover:pressed, QPushButton:checked:hover:pressed, +#CustomPanelButton:checked:hover:pressed, #enableBlankFrameButton:checked:hover:pressed { background: #343434; } .Button:disabled, QPushButton:disabled, +#CustomPanelButton:disabled, .ComboBox:disabled, #ViewerFpsSlider::sub-line:horizontal:disabled, #ViewerFpsSlider::add-line:horizontal:disabled, @@ -897,6 +904,9 @@ QComboBox:disabled { #PushButton_NoPadding { padding: 3; } +#CustomPanelButton { + padding: 0; +} /* ----------------------------------------------------------------------------- ComboBox ----------------------------------------------------------------------------- */ @@ -1289,6 +1299,13 @@ QProgressBar::chunk { border: 1 solid #2c2c2c; border-radius: 2; } +/* ----------------------------------------------------------------------------- + Custom Panel Widgets +----------------------------------------------------------------------------- */ +MyScroller { + qproperty-ScrollerBorderColor: #8d8d8d; + qproperty-ScrollerBGColor: #343434; +} /* ----------------------------------------------------------------------------- File Browser ----------------------------------------------------------------------------- */ @@ -1525,6 +1542,12 @@ QStatusBar #StatusBarLabel { #HexagonalColorWheel { qproperty-BGColor: #484848; } +#StyleChooserPage { + qproperty-CommonChipBoxColor: black; + qproperty-SolidChipBoxColor: #e41000; + qproperty-SelectedChipBoxColor: white; + qproperty-SelectedChipBox2Color: #c7ca32; +} /* -------------------------------------------------------------------------- */ /* Horizontal QSlider */ #colorSlider::groove:horizontal { @@ -2262,9 +2285,13 @@ Ruler { XsheetViewer { qproperty-TextColor: #e6e6e6; qproperty-ErrorTextColor: #ff7b7b; + qproperty-SelectedTextColor: #e6e6e6; + qproperty-CurrentFrameTextColor: #e6e6e6; qproperty-BGColor: #404040; qproperty-LightLineColor: rgba(0, 0, 0, 0.2); qproperty-MarkerLineColor: rgba(255, 255, 255, 0.15); + qproperty-SecMarkerLineColor: rgba(255, 255, 255, 0.25); + qproperty-SelectedMarkerLineColor: rgba(255, 255, 255, 0.15); qproperty-VerticalLineColor: rgba(0, 0, 0, 0.3); qproperty-VerticalLineHeadColor: #272727; qproperty-PreviewFrameTextColor: #9fdaff; @@ -2397,13 +2424,13 @@ FunctionTreeView { padding-left: 2; } FunctionPanel { - qproperty-BGColor: #404040; - qproperty-ValueLineColor: rgba(0, 0, 0, 0.1); - qproperty-FrameLineColor: rgba(0, 0, 0, 0.1); - qproperty-OtherCurvesColor: #8d8d8d; - qproperty-RulerBackground: #393939; + qproperty-BGColor: #3b3b3b; + qproperty-ValueLineColor: #454545; + qproperty-FrameLineColor: #505050; + qproperty-OtherCurvesColor: #888888; + qproperty-RulerBackground: #343434; qproperty-TextColor: #e6e6e6; - qproperty-SubColor: #404040; + qproperty-SubColor: #3b3b3b; qproperty-SelectedColor: #FFA500; } SpreadsheetViewer { @@ -2411,6 +2438,7 @@ SpreadsheetViewer { qproperty-CurrentRowBgColor: rgba(163, 82, 147, 0.7); qproperty-LightLineColor: rgba(0, 0, 0, 0.2); qproperty-MarkerLineColor: rgba(255, 255, 255, 0.15); + qproperty-SecMarkerLineColor: rgba(255, 255, 255, 0.25); qproperty-BGColor: #404040; qproperty-VerticalLineColor: rgba(0, 0, 0, 0.3); qproperty-KeyFrameColor: #995d1d; @@ -2426,6 +2454,7 @@ SpreadsheetViewer { qproperty-SelectedEmptyColor: rgba(119, 103, 116, 0.5); qproperty-SelectedSceneRangeEmptyColor: rgba(119, 103, 116, 0.5); qproperty-TextColor: #e6e6e6; + qproperty-CurrentRowTextColor: #e6e6e6; qproperty-ColumnHeaderBorderColor: #272727; } #ExpressionField { diff --git a/stuff/config/qss/Medium/less/Medium.less b/stuff/config/qss/Medium/less/Medium.less index 6f6650ee..79abc73b 100644 --- a/stuff/config/qss/Medium/less/Medium.less +++ b/stuff/config/qss/Medium/less/Medium.less @@ -107,7 +107,7 @@ // ----------------------------------------------------------------------------- // Palette Window // ----------------------------------------------------------------------------- - + // All views (except list) @palette-SelectedBorderColor: rgb(255, 255, 255); @palette-NumpadShortcutBgColor: rgba(0, 0, 0, 0.3); @@ -119,6 +119,12 @@ @palette-CurrentCellColor: fade(@hl-bg-color, 50); @palette-ListNumpadShortcutBorderColor: rgb(175, 175, 175); +// StyleChooserPage Chips +@palette-CommonChipBoxColor: black; +@palette-SolidChipBoxColor: rgb(228, 16, 0); +@palette-SelectedChipBoxColor: white; +@palette-SelectedChipBox2Color: rgb(199, 202, 50); + // ----------------------------------------------------------------------------- // Button // ----------------------------------------------------------------------------- @@ -296,6 +302,13 @@ @slider-left-handle-disabled: @slider-handle-img-disabled; @slider-right-handle-disabled: @slider-handle-img-disabled; +// ----------------------------------------------------------------------------- +// Custom Panel Widgets +// ----------------------------------------------------------------------------- + +@customPanel-Scroller-border-color: darken(@text-color, 35); +@customPanel-Scroller-bg-color: darken(@bg, 8); + // ----------------------------------------------------------------------------- // Titlebars // ----------------------------------------------------------------------------- @@ -391,10 +404,14 @@ // XSheet Spreadsheet Viewer @xsheet-text-color: @text-color; @xsheet-error-text-color: rgb(255, 123, 123); +@xsheet-selected-text-color: @text-color; +@xsheet-currentFrame-text-color: @text-color; @xsheet-bg-color: darken(@bg, 3); @xsheet-empty-bg-color: @bg; @xsheet-LightLine-color: rgba(0, 0, 0, 0.2); @xsheet-MarkerLine-color: rgba(255, 255, 255, 0.15); +@xsheet-SecMarkerLine-color: rgba(255, 255, 255, 0.25); +@xsheet-SelectedMarkerLine-color: @xsheet-MarkerLine-color; @xsheet-VerticalLine-color: rgba(0, 0, 0, 0.3); @xsheet-VerticalLineHead-color: darken(@bg, 13); @xsheet-PreviewFrameText-color: @label-title; @@ -544,9 +561,9 @@ @function-treeview-text-color: @text-color; // Function Curve Panel -@function-panel-bg-color: @xsheet-NotEmptyColumn-color; -@function-panel-ValueLine-color: fade(@xsheet-LightLine-color, 10); -@function-panel-FrameLine-color: fade(@xsheet-VerticalLine-color, 10); +@function-panel-bg-color: darken(@bg, 5); +@function-panel-ValueLine-color: lighten(@function-panel-bg-color, 4); +@function-panel-FrameLine-color: lighten(@function-panel-bg-color, 8); @function-panel-OtherCurves-color: lighten(@function-panel-bg-color, 30); @function-panel-RulerBG-color: darken(@function-panel-bg-color, 3); @function-panel-Text-color: @text-color; diff --git a/stuff/config/qss/Medium/less/layouts/controls.less b/stuff/config/qss/Medium/less/layouts/controls.less index 3bf4c00d..4ca240ba 100644 --- a/stuff/config/qss/Medium/less/layouts/controls.less +++ b/stuff/config/qss/Medium/less/layouts/controls.less @@ -52,6 +52,11 @@ QPushButton { padding: 3; } +#CustomPanelButton { + &:extend(.Button all); + padding: 0; +} + /* ----------------------------------------------------------------------------- ComboBox ----------------------------------------------------------------------------- */ @@ -388,3 +393,12 @@ QProgressBar { border-radius: 2; } } + +/* ----------------------------------------------------------------------------- + Custom Panel Widgets +----------------------------------------------------------------------------- */ + +MyScroller { + qproperty-ScrollerBorderColor: @customPanel-Scroller-border-color; + qproperty-ScrollerBGColor: @customPanel-Scroller-bg-color; +} diff --git a/stuff/config/qss/Medium/less/layouts/palette.less b/stuff/config/qss/Medium/less/layouts/palette.less index 12d7b96e..bf0fc820 100644 --- a/stuff/config/qss/Medium/less/layouts/palette.less +++ b/stuff/config/qss/Medium/less/layouts/palette.less @@ -18,6 +18,13 @@ qproperty-BGColor: @bg; } +#StyleChooserPage { + qproperty-CommonChipBoxColor: @palette-CommonChipBoxColor; + qproperty-SolidChipBoxColor: @palette-SolidChipBoxColor; + qproperty-SelectedChipBoxColor: @palette-SelectedChipBoxColor; + qproperty-SelectedChipBox2Color: @palette-SelectedChipBox2Color; +} + /* -------------------------------------------------------------------------- */ /* Horizontal QSlider */ diff --git a/stuff/config/qss/Medium/less/layouts/xsheet.less b/stuff/config/qss/Medium/less/layouts/xsheet.less index 147834c9..ae824935 100644 --- a/stuff/config/qss/Medium/less/layouts/xsheet.less +++ b/stuff/config/qss/Medium/less/layouts/xsheet.less @@ -75,9 +75,13 @@ XsheetViewer { qproperty-TextColor: @xsheet-text-color; qproperty-ErrorTextColor: @xsheet-error-text-color; + qproperty-SelectedTextColor: @xsheet-selected-text-color; + qproperty-CurrentFrameTextColor: @xsheet-currentFrame-text-color; qproperty-BGColor: @xsheet-bg-color; qproperty-LightLineColor: @xsheet-LightLine-color; qproperty-MarkerLineColor: @xsheet-MarkerLine-color; + qproperty-SecMarkerLineColor: @xsheet-SecMarkerLine-color; + qproperty-SelectedMarkerLineColor: @xsheet-SelectedMarkerLine-color; qproperty-VerticalLineColor: @xsheet-VerticalLine-color; qproperty-VerticalLineHeadColor: @xsheet-VerticalLineHead-color; qproperty-PreviewFrameTextColor: @xsheet-PreviewFrameText-color; @@ -276,6 +280,7 @@ SpreadsheetViewer { qproperty-CurrentRowBgColor: @xsheet-CurrentRowBG-color; // paired qproperty-LightLineColor: @xsheet-LightLine-color; // paired qproperty-MarkerLineColor: @xsheet-MarkerLine-color; // paired + qproperty-SecMarkerLineColor: @xsheet-SecMarkerLine-color; qproperty-BGColor: @function-ColumnHeaderBG-color; qproperty-VerticalLineColor: @xsheet-VerticalLine-color; // paired qproperty-KeyFrameColor: @function-KeyFrame-color; @@ -291,6 +296,7 @@ SpreadsheetViewer { qproperty-SelectedEmptyColor: @xsheet-SelectedEmptyCell-color; // paired qproperty-SelectedSceneRangeEmptyColor: @function-SelectedSceneRangeEmpty-color; qproperty-TextColor: @xsheet-text-color; // paired + qproperty-CurrentRowTextColor: @xsheet-currentFrame-text-color; // paired qproperty-ColumnHeaderBorderColor: @function-ColumnHeaderBorder-color; // paired } diff --git a/stuff/config/qss/Medium/less/themes/Dark.less b/stuff/config/qss/Medium/less/themes/Dark.less index fbdcc490..93fd2e2c 100644 --- a/stuff/config/qss/Medium/less/themes/Dark.less +++ b/stuff/config/qss/Medium/less/themes/Dark.less @@ -106,6 +106,8 @@ // Function Editor // ----------------------------------------------------------------------------- +@function-panel-bg-color: darken(@bg, 0); + // Function Curve Panel @function-panel-OtherCurves-color: lighten(@function-panel-bg-color, 30); diff --git a/stuff/config/qss/Medium/less/themes/Light.less b/stuff/config/qss/Medium/less/themes/Light.less index 53a14dac..0f49922d 100644 --- a/stuff/config/qss/Medium/less/themes/Light.less +++ b/stuff/config/qss/Medium/less/themes/Light.less @@ -126,6 +126,11 @@ // List view @palette-ListNumpadShortcutBorderColor: rgb(105, 105, 105); +// StyleChooserPage Chips +@palette-CommonChipBoxColor: rgb(192, 192, 192); +@palette-SolidChipBoxColor: rgb(228, 144, 128); +@palette-SelectedChipBoxColor: black; + // ----------------------------------------------------------------------------- // File Browser / Trees // ----------------------------------------------------------------------------- @@ -173,6 +178,7 @@ @xsheet-VerticalLine-color: rgba(0, 0, 0, 0.15); @xsheet-ColumnIconLine-color: rgb(112, 112, 112); @xsheet-MarkerLine-color: rgba(0, 0, 0, 0.3); +@xsheet-SecMarkerLine-color: rgba(0, 0, 0, 0.5); @xsheet-OnionSkinAreaBG-color: darken(@bg, 10); @xsheet-PreviewFrameText-color: #2d42b9; @xsheet-CurrentRowBG-color: saturate(lighten(fade(@hl-bg-color, 70), -8), 8); @@ -230,7 +236,7 @@ // ----------------------------------------------------------------------------- // Function Curve Panel -@function-panel-bg-color: @schematic-viewer-bg-color; +@function-panel-bg-color: darken(@bg, 35); @function-panel-OtherCurves-color: rgb(218, 218, 218); @function-panel-Text-color: @text-color; diff --git a/stuff/config/qss/Medium/less/themes/Neutral.less b/stuff/config/qss/Medium/less/themes/Neutral.less index 7a78658e..4ceb1f7a 100644 --- a/stuff/config/qss/Medium/less/themes/Neutral.less +++ b/stuff/config/qss/Medium/less/themes/Neutral.less @@ -199,6 +199,7 @@ @xsheet-bg-color: darken(@bg, 4); @xsheet-LightLine-color: rgba(0, 0, 0, 0.15); @xsheet-MarkerLine-color: rgba(255, 255, 255, 0.2); +@xsheet-SecMarkerLine-color: rgba(255, 255, 255, 0.35); @xsheet-PreviewFrameText-color: #17239c; @xsheet-OnionSkinAreaBG-color: darken(@bg, 8); @xsheet-EmptyCell-color: @xsheet-OnionSkinAreaBG-color; @@ -263,6 +264,8 @@ // Function Curve Panel @function-panel-bg-color: darken(@bg, 15); +@function-panel-ValueLine-color: darken(@function-panel-bg-color, 4); +@function-panel-FrameLine-color: darken(@function-panel-bg-color, 8); @function-panel-OtherCurves-color: rgb(197, 197, 197); @function-panel-RulerBG-color: darken(@bg, 10); @function-panel-Sub-color: rgb(255, 255, 255); diff --git a/stuff/config/qss/Neutral/Neutral.qss b/stuff/config/qss/Neutral/Neutral.qss index f4909e7f..1e6a8dab 100644 --- a/stuff/config/qss/Neutral/Neutral.qss +++ b/stuff/config/qss/Neutral/Neutral.qss @@ -835,6 +835,7 @@ QTreeWidget { ----------------------------------------------------------------------------- */ .Button, QPushButton, +#CustomPanelButton, .ComboBox, #enableBlankFrameButton, QComboBox { @@ -849,6 +850,7 @@ QComboBox { } .Button:hover, QPushButton:hover, +#CustomPanelButton:hover, #ViewerFpsSlider::sub-line:horizontal:hover, #ViewerFpsSlider::add-line:horizontal:hover, #enableBlankFrameButton:hover { @@ -858,6 +860,7 @@ QPushButton:hover, } .Button:pressed, QPushButton:pressed, +#CustomPanelButton:pressed, #ViewerFpsSlider::sub-line:horizontal:pressed, #ViewerFpsSlider::add-line:horizontal:pressed, #enableBlankFrameButton:pressed { @@ -867,6 +870,7 @@ QPushButton:pressed, } .Button:checked, QPushButton:checked, +#CustomPanelButton:checked, #enableBlankFrameButton:checked { background-color: #5a5a5a; border-color: #404040; @@ -874,17 +878,20 @@ QPushButton:checked, } .Button:checked:hover, QPushButton:checked:hover, +#CustomPanelButton:checked:hover, #enableBlankFrameButton:checked:hover { background-color: #6e6e6e; border-color: #616161; } .Button:checked:hover:pressed, QPushButton:checked:hover:pressed, +#CustomPanelButton:checked:hover:pressed, #enableBlankFrameButton:checked:hover:pressed { background: #616161; } .Button:disabled, QPushButton:disabled, +#CustomPanelButton:disabled, .ComboBox:disabled, #ViewerFpsSlider::sub-line:horizontal:disabled, #ViewerFpsSlider::add-line:horizontal:disabled, @@ -897,6 +904,9 @@ QComboBox:disabled { #PushButton_NoPadding { padding: 3; } +#CustomPanelButton { + padding: 0; +} /* ----------------------------------------------------------------------------- ComboBox ----------------------------------------------------------------------------- */ @@ -1289,6 +1299,13 @@ QProgressBar::chunk { border: 1 solid #5a5a5a; border-radius: 2; } +/* ----------------------------------------------------------------------------- + Custom Panel Widgets +----------------------------------------------------------------------------- */ +MyScroller { + qproperty-ScrollerBorderColor: #000000; + qproperty-ScrollerBGColor: #6c6c6c; +} /* ----------------------------------------------------------------------------- File Browser ----------------------------------------------------------------------------- */ @@ -1525,6 +1542,12 @@ QStatusBar #StatusBarLabel { #HexagonalColorWheel { qproperty-BGColor: #808080; } +#StyleChooserPage { + qproperty-CommonChipBoxColor: black; + qproperty-SolidChipBoxColor: #e41000; + qproperty-SelectedChipBoxColor: white; + qproperty-SelectedChipBox2Color: #c7ca32; +} /* -------------------------------------------------------------------------- */ /* Horizontal QSlider */ #colorSlider::groove:horizontal { @@ -2262,9 +2285,13 @@ Ruler { XsheetViewer { qproperty-TextColor: #000; qproperty-ErrorTextColor: #c01111; + qproperty-SelectedTextColor: #000; + qproperty-CurrentFrameTextColor: #000; qproperty-BGColor: #767676; qproperty-LightLineColor: rgba(0, 0, 0, 0.15); qproperty-MarkerLineColor: rgba(255, 255, 255, 0.2); + qproperty-SecMarkerLineColor: rgba(255, 255, 255, 0.35); + qproperty-SelectedMarkerLineColor: rgba(255, 255, 255, 0.2); qproperty-VerticalLineColor: rgba(0, 0, 0, 0.3); qproperty-VerticalLineHeadColor: #4d4d4d; qproperty-PreviewFrameTextColor: #17239c; @@ -2398,8 +2425,8 @@ FunctionTreeView { } FunctionPanel { qproperty-BGColor: #5a5a5a; - qproperty-ValueLineColor: rgba(0, 0, 0, 0.1); - qproperty-FrameLineColor: rgba(0, 0, 0, 0.1); + qproperty-ValueLineColor: #505050; + qproperty-FrameLineColor: #454545; qproperty-OtherCurvesColor: #c5c5c5; qproperty-RulerBackground: #676767; qproperty-TextColor: #000; @@ -2411,6 +2438,7 @@ SpreadsheetViewer { qproperty-CurrentRowBgColor: rgba(251, 140, 205, 0.7); qproperty-LightLineColor: rgba(0, 0, 0, 0.15); qproperty-MarkerLineColor: rgba(255, 255, 255, 0.2); + qproperty-SecMarkerLineColor: rgba(255, 255, 255, 0.35); qproperty-BGColor: #767676; qproperty-VerticalLineColor: rgba(0, 0, 0, 0.3); qproperty-KeyFrameColor: #c4833e; @@ -2426,6 +2454,7 @@ SpreadsheetViewer { qproperty-SelectedEmptyColor: rgba(165, 146, 157, 0.5); qproperty-SelectedSceneRangeEmptyColor: rgba(165, 146, 157, 0.5); qproperty-TextColor: #000; + qproperty-CurrentRowTextColor: #000; qproperty-ColumnHeaderBorderColor: #343434; } #ExpressionField { diff --git a/stuff/doc/LICENSE/LICENSE_tinyexr_openexr.txt b/stuff/doc/LICENSE/LICENSE_tinyexr_openexr.txt new file mode 100644 index 00000000..74e3c247 --- /dev/null +++ b/stuff/doc/LICENSE/LICENSE_tinyexr_openexr.txt @@ -0,0 +1,62 @@ +tinyexr : Tiny OpenEXR image loader/saver library +https://github.com/syoyo/tinyexr + +Copyright (c) 2014 - 2021, Syoyo Fujita and many contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Syoyo Fujita nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +/////////////////////////////////////////////////////////////////////////// +tinyexr contains some OpenEXR code, which is licensed as follows. +Note that OpenEXR had joined Academy Software Foudation projects in 2019. +The latest code of OpenEXR can be found in +https://github.com/AcademySoftwareFoundation/openexr . +/////////////////////////////////////////////////////////////////////////// +OpenEXR + +Copyright (c) 2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Industrial Light & Magic nor the names of + its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/stuff/doc/español/particlesFx.html b/stuff/doc/español/particlesFx.html index c8c77686..e1f006cc 100644 --- a/stuff/doc/español/particlesFx.html +++ b/stuff/doc/español/particlesFx.html @@ -599,25 +599,6 @@ rt ○ - -   - Trail - Trail - Muestra una estela para cada partícula, que se desvanecerá a lo largo de la cantidad de fotogramas especificados en el parámetro Trail. - ― - - -   -   - Step - Permite definir cuántas imágenes se mostrarán en la estela. Cada estela estará compuesta por (Trail/Step) imágenes. - ― -   Lifetime @@ -855,6 +836,43 @@ rt ― + +   + Trail + Trail + Muestra una estela para cada partícula, que se desvanecerá a lo largo de la cantidad de fotogramas especificados en el parámetro Trail. + ― + + +   +   + Step + Permite definir cuántas imágenes se mostrarán en la estela. Cada estela estará compuesta por (Trail/Step) imágenes. + ― + + +   +   + Motion Blur + When turned ON, particles will be rendered with motion blur according to its movement. + ― + + +   +   + Gamma + Film gamma value used for the motion blur. + ― +   ○ - -   - Trail - Trail - Display a trail for each particle, that fades out over the number of frames specified in the Trail parameter. - ― - - -   -   - Step - The particle trail will be displayed every Step value. That is, there will be (Trail/Step) images displayed in the trail. - ― -   Lifetime @@ -849,7 +830,44 @@ rt Trail Opacity When a trail is defined in Birth Params → Trail, it specifies the minimum / maximum values for the opacity of the particles in the trail. + width:356pt'>When a trail is defined in Animation → Trail, it specifies the minimum / maximum values for the opacity of the particles in the trail. + ― + + +   + Trail + Trail + Display a trail for each particle, that fades out over the number of frames specified in the Trail parameter. + ― + + +   +   + Step + The particle trail will be displayed every Step value. That is, there will be (Trail/Step) images displayed in the trail. + ― + + +   +   + Motion Blur + When turned ON, particles will be rendered with motion blur according to its movement. + ― + + +   +   + Gamma + Film gamma value used for the motion blur. ― diff --git a/stuff/doc/日本語/particlesFx.html b/stuff/doc/日本語/particlesFx.html index 28ddffdc..15efa836 100644 --- a/stuff/doc/日本語/particlesFx.html +++ b/stuff/doc/日本語/particlesFx.html @@ -599,25 +599,6 @@ rt - - @ - Trail - Trail - OՂ\BTrailŎw肵t[ătF[hAEgB - \ - - - @ - @ - Step - OՂSteplɕ\BȂ킿AOՂ̉摜́iTrailj/(Frame)\邱ƂɂȂB - \ - @ Lifetime @@ -855,7 +836,44 @@ rt Trail Opacity BirthParamTrailTrailw肵ƂA̋OՂ̃p[eBN̕sx̍ŏ/ől߂B + width:356pt'>AnimationTrailTrailw肵ƂA̋OՂ̃p[eBN̕sx̍ŏ/ől߂B + \ + + + @ + Trail + Trail + OՂ\BTrailŎw肵t[ătF[hAEgB + \ + + + @ + @ + Step + OՂSteplɕ\BȂ킿AOՂ̉摜́iTrailj/(Frame)\邱ƂɂȂB + \ + + + @ + @ + Motion Blur + When turned ON, particles will be rendered with motion blur according to its movement. + \ + + + @ + @ + Gamma + Film gamma value used for the motion blur. \ diff --git a/stuff/library/custom panel templates/drawing toolbox.ui b/stuff/library/custom panel templates/drawing toolbox.ui new file mode 100644 index 00000000..a8fd7723 --- /dev/null +++ b/stuff/library/custom panel templates/drawing toolbox.ui @@ -0,0 +1,362 @@ + + + Form + + + + 0 + 0 + 127 + 516 + + + + + 0 + 0 + + + + Form + + + + 15 + + + QLayout::SetDefaultConstraint + + + + + 5 + + + + + + 0 + 0 + + + + + 50 + 90 + + + + + 16777215 + 16777215 + + + + Btn1 + + + + 40 + 40 + + + + + + + + + 0 + 0 + + + + + 50 + 90 + + + + + 16777215 + 16777215 + + + + Btn2 + + + + 40 + 40 + + + + + + + + + 0 + 0 + + + + + 50 + 90 + + + + + 16777215 + 16777215 + + + + Btn3 + + + + 40 + 40 + + + + + + + + + 0 + 0 + + + + + 50 + 90 + + + + + 16777215 + 16777215 + + + + Btn4 + + + + 40 + 40 + + + + + + + + + + 5 + + + + + + 0 + 0 + + + + + 50 + 90 + + + + + 16777215 + 16777215 + + + + Btn5 + + + + 40 + 40 + + + + + + + + + 0 + 0 + + + + + 50 + 90 + + + + + 16777215 + 16777215 + + + + Btn6 + + + + 40 + 40 + + + + + + + + + + 5 + + + + + + 0 + 0 + + + + + 105 + 90 + + + + + 16777215 + 16777215 + + + + Btn7 + + + + 40 + 40 + + + + + + + + 5 + + + + + + 0 + 0 + + + + + 40 + 90 + + + + + 16777215 + 16777215 + + + + Btn8 + + + + 30 + 30 + + + + + + + + + 0 + 0 + + + + + 15 + 90 + + + + + 16777215 + 16777215 + + + + + + + + + 0 + 0 + + + + + 40 + 90 + + + + + 16777215 + 16777215 + + + + Btn9 + + + + 30 + 30 + + + + + + + + + + + + + \ No newline at end of file diff --git a/stuff/library/custom panel templates/eight buttons.ui b/stuff/library/custom panel templates/eight buttons.ui new file mode 100644 index 00000000..043b62ad --- /dev/null +++ b/stuff/library/custom panel templates/eight buttons.ui @@ -0,0 +1,221 @@ + + + Form + + + + 0 + 0 + 236 + 124 + + + + Form + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + Btn2 + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + Btn1 + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + Btn4 + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + Btn3 + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + Btn5 + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + Btn6 + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + Btn7 + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + Btn8 + + + + 20 + 20 + + + + + + + + + \ No newline at end of file diff --git a/stuff/library/shaders/programs/HSLBlendGPU.frag b/stuff/library/shaders/programs/HSLBlendGPU.frag index 070ad7e6..2061a859 100644 --- a/stuff/library/shaders/programs/HSLBlendGPU.frag +++ b/stuff/library/shaders/programs/HSLBlendGPU.frag @@ -95,7 +95,7 @@ void main( void ) vec4 fg_frag = texture2D(inputImage[0], fg_texPos); vec4 bg_frag = texture2D(inputImage[1], bg_texPos); - // De-premultiplication + // De-premultiplication of textures vec3 fg_pix = vec3(0.0); if (fg_frag.a > 0.0) fg_pix = fg_frag.rgb / fg_frag.a; vec3 bg_pix = vec3(0.0); @@ -107,20 +107,12 @@ void main( void ) if (bmask) { gl_FragColor.a = bg_alpha; } else { - gl_FragColor.a = fg_alpha + bg_alpha * (1.0 - fg_alpha); + gl_FragColor.a = bg_alpha + fg_alpha * (1.0 - bg_alpha); } if (gl_FragColor.a <= 0.0) discard; - - // Perform blending - if (fg_alpha > 0.0 && bg_alpha > 0.0) { - vec3 o_pix = SetLumSat(bhue ? fg_pix : bg_pix, bsat ? fg_pix : bg_pix, blum ? fg_pix : bg_pix); - gl_FragColor.rgb = mix(bg_pix, o_pix, balpha); - } else if (fg_alpha > 0.0) { - gl_FragColor.rgb = fg_pix; - } else { - gl_FragColor.rgb = bg_pix; - } - // Premultiplication - gl_FragColor.rgb *= gl_FragColor.a; + // Perform blending + vec3 o_pix = SetLumSat(bhue ? fg_pix : bg_pix, bsat ? fg_pix : bg_pix, blum ? fg_pix : bg_pix); + vec3 b_pix = bmask ? vec3(0.0) : fg_pix; + gl_FragColor.rgb = bg_pix * bg_alpha * (1.0 - fg_alpha) + mix(b_pix, o_pix, bg_alpha) * fg_alpha; } diff --git a/stuff/library/textures/Denim2_s.bmp b/stuff/library/textures/Denim2_s.bmp new file mode 100644 index 00000000..6cfd5775 Binary files /dev/null and b/stuff/library/textures/Denim2_s.bmp differ diff --git a/stuff/library/textures/Knit_s.bmp b/stuff/library/textures/Knit_s.bmp new file mode 100644 index 00000000..97fa5baf Binary files /dev/null and b/stuff/library/textures/Knit_s.bmp differ diff --git a/stuff/library/textures/brush tips/dry_brush.0001.png b/stuff/library/textures/brush tips/dry_brush.0001.png new file mode 100644 index 00000000..2f37ed55 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0001.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0002.png b/stuff/library/textures/brush tips/dry_brush.0002.png new file mode 100644 index 00000000..e41dd2b8 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0002.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0003.png b/stuff/library/textures/brush tips/dry_brush.0003.png new file mode 100644 index 00000000..6471cbac Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0003.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0004.png b/stuff/library/textures/brush tips/dry_brush.0004.png new file mode 100644 index 00000000..9325baa8 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0004.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0005.png b/stuff/library/textures/brush tips/dry_brush.0005.png new file mode 100644 index 00000000..45271558 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0005.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0006.png b/stuff/library/textures/brush tips/dry_brush.0006.png new file mode 100644 index 00000000..9cfc1158 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0006.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0007.png b/stuff/library/textures/brush tips/dry_brush.0007.png new file mode 100644 index 00000000..b7e60b17 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0007.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0008.png b/stuff/library/textures/brush tips/dry_brush.0008.png new file mode 100644 index 00000000..b9d732e1 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0008.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0009.png b/stuff/library/textures/brush tips/dry_brush.0009.png new file mode 100644 index 00000000..2c8e93ee Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0009.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0010.png b/stuff/library/textures/brush tips/dry_brush.0010.png new file mode 100644 index 00000000..54b58227 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0010.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0011.png b/stuff/library/textures/brush tips/dry_brush.0011.png new file mode 100644 index 00000000..972ba977 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0011.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0012.png b/stuff/library/textures/brush tips/dry_brush.0012.png new file mode 100644 index 00000000..424e9153 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0012.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0013.png b/stuff/library/textures/brush tips/dry_brush.0013.png new file mode 100644 index 00000000..cd4e38f9 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0013.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0014.png b/stuff/library/textures/brush tips/dry_brush.0014.png new file mode 100644 index 00000000..efaacba0 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0014.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0015.png b/stuff/library/textures/brush tips/dry_brush.0015.png new file mode 100644 index 00000000..be012dd6 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0015.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0016.png b/stuff/library/textures/brush tips/dry_brush.0016.png new file mode 100644 index 00000000..bf91dab3 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0016.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0017.png b/stuff/library/textures/brush tips/dry_brush.0017.png new file mode 100644 index 00000000..41f41aa8 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0017.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0018.png b/stuff/library/textures/brush tips/dry_brush.0018.png new file mode 100644 index 00000000..3edce292 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0018.png differ diff --git a/stuff/library/textures/brush tips/dry_brush.0019.png b/stuff/library/textures/brush tips/dry_brush.0019.png new file mode 100644 index 00000000..a6ab20f7 Binary files /dev/null and b/stuff/library/textures/brush tips/dry_brush.0019.png differ diff --git a/stuff/library/textures/brush tips/fluffy.0001.png b/stuff/library/textures/brush tips/fluffy.0001.png new file mode 100644 index 00000000..e05568d9 Binary files /dev/null and b/stuff/library/textures/brush tips/fluffy.0001.png differ diff --git a/stuff/library/textures/brush tips/fluffy.0002.png b/stuff/library/textures/brush tips/fluffy.0002.png new file mode 100644 index 00000000..8e7f62e2 Binary files /dev/null and b/stuff/library/textures/brush tips/fluffy.0002.png differ diff --git a/stuff/library/textures/brush tips/fluffy.0003.png b/stuff/library/textures/brush tips/fluffy.0003.png new file mode 100644 index 00000000..9706411e Binary files /dev/null and b/stuff/library/textures/brush tips/fluffy.0003.png differ diff --git a/stuff/library/textures/brush tips/fluffy.0004.png b/stuff/library/textures/brush tips/fluffy.0004.png new file mode 100644 index 00000000..3c8f19ae Binary files /dev/null and b/stuff/library/textures/brush tips/fluffy.0004.png differ diff --git a/stuff/library/textures/brush tips/fluffy.0005.png b/stuff/library/textures/brush tips/fluffy.0005.png new file mode 100644 index 00000000..a151346a Binary files /dev/null and b/stuff/library/textures/brush tips/fluffy.0005.png differ diff --git a/stuff/library/textures/brush tips/fluffy.0006.png b/stuff/library/textures/brush tips/fluffy.0006.png new file mode 100644 index 00000000..076e227a Binary files /dev/null and b/stuff/library/textures/brush tips/fluffy.0006.png differ diff --git a/stuff/library/textures/brush tips/fluffy.0007.png b/stuff/library/textures/brush tips/fluffy.0007.png new file mode 100644 index 00000000..cc00c7a4 Binary files /dev/null and b/stuff/library/textures/brush tips/fluffy.0007.png differ diff --git a/stuff/library/textures/brush tips/single_line.0001.png b/stuff/library/textures/brush tips/single_line.0001.png new file mode 100644 index 00000000..0f40e8c5 Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0001.png differ diff --git a/stuff/library/textures/brush tips/single_line.0002.png b/stuff/library/textures/brush tips/single_line.0002.png new file mode 100644 index 00000000..609a5750 Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0002.png differ diff --git a/stuff/library/textures/brush tips/single_line.0003.png b/stuff/library/textures/brush tips/single_line.0003.png new file mode 100644 index 00000000..6b57ff07 Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0003.png differ diff --git a/stuff/library/textures/brush tips/single_line.0004.png b/stuff/library/textures/brush tips/single_line.0004.png new file mode 100644 index 00000000..c2e4fb3d Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0004.png differ diff --git a/stuff/library/textures/brush tips/single_line.0005.png b/stuff/library/textures/brush tips/single_line.0005.png new file mode 100644 index 00000000..faaeb1ee Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0005.png differ diff --git a/stuff/library/textures/brush tips/single_line.0006.png b/stuff/library/textures/brush tips/single_line.0006.png new file mode 100644 index 00000000..921de427 Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0006.png differ diff --git a/stuff/library/textures/brush tips/single_line.0007.png b/stuff/library/textures/brush tips/single_line.0007.png new file mode 100644 index 00000000..9c3b1105 Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0007.png differ diff --git a/stuff/library/textures/brush tips/single_line.0008.png b/stuff/library/textures/brush tips/single_line.0008.png new file mode 100644 index 00000000..9614aac8 Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0008.png differ diff --git a/stuff/library/textures/brush tips/single_line.0009.png b/stuff/library/textures/brush tips/single_line.0009.png new file mode 100644 index 00000000..639df940 Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0009.png differ diff --git a/stuff/library/textures/brush tips/single_line.0010.png b/stuff/library/textures/brush tips/single_line.0010.png new file mode 100644 index 00000000..e33798b2 Binary files /dev/null and b/stuff/library/textures/brush tips/single_line.0010.png differ diff --git a/stuff/library/textures/brush tips/streaky.0001.png b/stuff/library/textures/brush tips/streaky.0001.png new file mode 100644 index 00000000..5d60f30e Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0001.png differ diff --git a/stuff/library/textures/brush tips/streaky.0002.png b/stuff/library/textures/brush tips/streaky.0002.png new file mode 100644 index 00000000..714c178e Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0002.png differ diff --git a/stuff/library/textures/brush tips/streaky.0003.png b/stuff/library/textures/brush tips/streaky.0003.png new file mode 100644 index 00000000..de24bf71 Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0003.png differ diff --git a/stuff/library/textures/brush tips/streaky.0004.png b/stuff/library/textures/brush tips/streaky.0004.png new file mode 100644 index 00000000..84e92aee Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0004.png differ diff --git a/stuff/library/textures/brush tips/streaky.0005.png b/stuff/library/textures/brush tips/streaky.0005.png new file mode 100644 index 00000000..a1353617 Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0005.png differ diff --git a/stuff/library/textures/brush tips/streaky.0006.png b/stuff/library/textures/brush tips/streaky.0006.png new file mode 100644 index 00000000..db9dcb48 Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0006.png differ diff --git a/stuff/library/textures/brush tips/streaky.0007.png b/stuff/library/textures/brush tips/streaky.0007.png new file mode 100644 index 00000000..7696e86d Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0007.png differ diff --git a/stuff/library/textures/brush tips/streaky.0008.png b/stuff/library/textures/brush tips/streaky.0008.png new file mode 100644 index 00000000..f7a225e9 Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0008.png differ diff --git a/stuff/library/textures/brush tips/streaky.0009.png b/stuff/library/textures/brush tips/streaky.0009.png new file mode 100644 index 00000000..fcace4d6 Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0009.png differ diff --git a/stuff/library/textures/brush tips/streaky.0010.png b/stuff/library/textures/brush tips/streaky.0010.png new file mode 100644 index 00000000..ae27928b Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0010.png differ diff --git a/stuff/library/textures/brush tips/streaky.0011.png b/stuff/library/textures/brush tips/streaky.0011.png new file mode 100644 index 00000000..d70585c3 Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0011.png differ diff --git a/stuff/library/textures/brush tips/streaky.0012.png b/stuff/library/textures/brush tips/streaky.0012.png new file mode 100644 index 00000000..bc8aee0f Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0012.png differ diff --git a/stuff/library/textures/brush tips/streaky.0013.png b/stuff/library/textures/brush tips/streaky.0013.png new file mode 100644 index 00000000..16070ec7 Binary files /dev/null and b/stuff/library/textures/brush tips/streaky.0013.png differ diff --git a/stuff/library/textures/brush tips/thick.0001.png b/stuff/library/textures/brush tips/thick.0001.png new file mode 100644 index 00000000..01779c0d Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0001.png differ diff --git a/stuff/library/textures/brush tips/thick.0002.png b/stuff/library/textures/brush tips/thick.0002.png new file mode 100644 index 00000000..38d0cc32 Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0002.png differ diff --git a/stuff/library/textures/brush tips/thick.0003.png b/stuff/library/textures/brush tips/thick.0003.png new file mode 100644 index 00000000..1a1b7078 Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0003.png differ diff --git a/stuff/library/textures/brush tips/thick.0004.png b/stuff/library/textures/brush tips/thick.0004.png new file mode 100644 index 00000000..c9efb79f Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0004.png differ diff --git a/stuff/library/textures/brush tips/thick.0005.png b/stuff/library/textures/brush tips/thick.0005.png new file mode 100644 index 00000000..f30c4173 Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0005.png differ diff --git a/stuff/library/textures/brush tips/thick.0006.png b/stuff/library/textures/brush tips/thick.0006.png new file mode 100644 index 00000000..c304354b Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0006.png differ diff --git a/stuff/library/textures/brush tips/thick.0007.png b/stuff/library/textures/brush tips/thick.0007.png new file mode 100644 index 00000000..817e0b6d Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0007.png differ diff --git a/stuff/library/textures/brush tips/thick.0008.png b/stuff/library/textures/brush tips/thick.0008.png new file mode 100644 index 00000000..b3314924 Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0008.png differ diff --git a/stuff/library/textures/brush tips/thick.0009.png b/stuff/library/textures/brush tips/thick.0009.png new file mode 100644 index 00000000..85b9b43a Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0009.png differ diff --git a/stuff/library/textures/brush tips/thick.0010.png b/stuff/library/textures/brush tips/thick.0010.png new file mode 100644 index 00000000..4539a039 Binary files /dev/null and b/stuff/library/textures/brush tips/thick.0010.png differ diff --git a/stuff/library/textures/papercrump.bmp b/stuff/library/textures/papercrump.bmp index 7b6f0399..7f22a3e2 100644 Binary files a/stuff/library/textures/papercrump.bmp and b/stuff/library/textures/papercrump.bmp differ diff --git a/stuff/library/textures/snakeskin.bmp b/stuff/library/textures/snakeskin.bmp new file mode 100644 index 00000000..65321c7f Binary files /dev/null and b/stuff/library/textures/snakeskin.bmp differ diff --git a/stuff/library/textures/snakeskinred.bmp b/stuff/library/textures/snakeskinred.bmp new file mode 100644 index 00000000..eff6ec7b Binary files /dev/null and b/stuff/library/textures/snakeskinred.bmp differ diff --git a/stuff/library/textures/snow.bmp b/stuff/library/textures/snow.bmp new file mode 100644 index 00000000..204665ee Binary files /dev/null and b/stuff/library/textures/snow.bmp differ diff --git a/stuff/library/textures/wornleather.bmp b/stuff/library/textures/wornleather.bmp new file mode 100644 index 00000000..8ad5a5e8 Binary files /dev/null and b/stuff/library/textures/wornleather.bmp differ diff --git a/stuff/profiles/layouts/fxs/STD_inoAddFx.xml b/stuff/profiles/layouts/fxs/STD_inoAddFx.xml index dcb99ab3..09214178 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 6073f3c5..66b2dffb 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 3d4e87df..8e36f068 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 171b7395..d65ab81f 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 8041df33..77d29ec0 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 358c5a06..21dee73d 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 42eaca07..211d97a2 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 8b822ef4..21bb3e64 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 c69b3928..f157ee32 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 6c6a7600..08362045 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 a143e51d..5d33ebdc 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 ed64746a..47c2d8aa 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 70f34a5b..00f49896 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 303bd46c..8e314671 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 5064564c..1be34087 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 5d3f173e..5d5b2d5b 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 d87c028e..244216e1 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 d8332df0..62de5e29 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 5e301a89..733b2185 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 416be102..ff6f6604 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 4922cf04..3ce1166f 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 12eb439c..24c07994 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 71170ef3..a3056f7f 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 f6c57bff..e34a6a3d 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 473f9e48..e355c1cf 100644 --- a/stuff/profiles/layouts/fxs/STD_iwa_BokehAdvancedFx.xml +++ b/stuff/profiles/layouts/fxs/STD_iwa_BokehAdvancedFx.xml @@ -1,49 +1,106 @@ - + on_focus_distance bokeh_amount - masterHardness + linearizeMode + + masterHardness + + + masterGamma + masterGammaAdjust + hardnessPerSource distance1 bokeh_adjustment1 - hardness1 - depth_ref1 + + hardness1 + + + gamma1 + gammaAdjust1 + + + depth_ref1 + fillGap1 + doMedian1 + depthRange1 distance2 bokeh_adjustment2 - hardness2 - depth_ref2 + + hardness2 + + + gamma2 + gammaAdjust2 + + + depth_ref2 + fillGap2 + doMedian2 + depthRange2 distance3 bokeh_adjustment3 - hardness3 - depth_ref3 + + hardness3 + + + gamma3 + gammaAdjust3 + + + depth_ref3 + fillGap3 + doMedian3 + depthRange3 distance4 bokeh_adjustment4 - hardness4 - depth_ref4 + + hardness4 + + + gamma4 + gammaAdjust4 + + + depth_ref4 + fillGap4 + doMedian4 + depthRange4 distance5 bokeh_adjustment5 - hardness5 - depth_ref5 + + hardness5 + + + gamma5 + gammaAdjust5 + + + depth_ref5 + fillGap5 + doMedian5 + depthRange5 @@ -54,7 +111,38 @@ hardness3 hardness4 hardness5 + gamma1 + gamma2 + gamma3 + gamma4 + gamma5 + gammaAdjust1 + gammaAdjust2 + gammaAdjust3 + gammaAdjust4 + gammaAdjust5 + + + fillGap1 + doMedian1 + + + fillGap2 + doMedian2 + + + fillGap3 + doMedian3 + + + fillGap4 + doMedian4 + + + fillGap5 + doMedian5 + - \ No newline at end of file + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_BokehFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_BokehFx.xml index e5c7a8cc..c9716df9 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 6df0b34f..2e30573d 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_FloorBumpFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_FloorBumpFx.xml new file mode 100644 index 00000000..23fddf18 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_FloorBumpFx.xml @@ -0,0 +1,39 @@ + + + renderMode + fov + cameraAltitude + eyeLevel + drawLevel + waveHeight + souceMargin + displacement + + + sourcePrecision + + + + textureOffsetAmount + textureOffsetSpread + + + + lightAzimuth + lightElevation + + + + depth + refractiveIndex + + + + distanceLevel + + + + differenceMode + + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_FlowBlurFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_FlowBlurFx.xml new file mode 100644 index 00000000..e2cb0bf6 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_FlowBlurFx.xml @@ -0,0 +1,12 @@ + + + length + filterType + + linear + + gamma + + referenceMode + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_FlowPaintBrushFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_FlowPaintBrushFx.xml new file mode 100644 index 00000000..08c347a3 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_FlowPaintBrushFx.xml @@ -0,0 +1,32 @@ + + + + h_density + v_density + pos_randomness + pos_wobble + + origin_pos + horizontal_pos + vertical_pos + curve_point + fill_gap_size + + tip_width + width_randomness + sustain_width_to_skew + tip_length + length_randomness + angle_randomness + tip_alpha + tip_joints + bidirectional + anti_jaggy + + reference_frame + reference_prevalence + + random_seed + sort_by + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_GradientWarpFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_GradientWarpFx.xml index 629917aa..19dadb7b 100644 --- a/stuff/profiles/layouts/fxs/STD_iwa_GradientWarpFx.xml +++ b/stuff/profiles/layouts/fxs/STD_iwa_GradientWarpFx.xml @@ -3,5 +3,6 @@ h_maxlen v_maxlen scale + sampling_size diff --git a/stuff/profiles/layouts/fxs/STD_iwa_MotionBlurCompFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_MotionBlurCompFx.xml index 419bb001..190b9759 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_iwa_MotionFlowFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_MotionFlowFx.xml new file mode 100644 index 00000000..a41426c5 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_MotionFlowFx.xml @@ -0,0 +1,15 @@ + + + + + motionObjectType + motionObjectIndex + + shutterLength + normalizeType + + normalizeRange + + + + diff --git a/stuff/profiles/layouts/fxs/STD_iwa_TangentFlowFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_TangentFlowFx.xml new file mode 100644 index 00000000..f9c0db23 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_TangentFlowFx.xml @@ -0,0 +1,11 @@ + + + iteration + kernelRadius + threshold + alignDirection + + pivotAngle + + + diff --git a/stuff/profiles/layouts/fxs/STD_particlesFx.xml b/stuff/profiles/layouts/fxs/STD_particlesFx.xml index 01972ba7..1ae1fe0f 100644 --- a/stuff/profiles/layouts/fxs/STD_particlesFx.xml +++ b/stuff/profiles/layouts/fxs/STD_particlesFx.xml @@ -56,9 +56,6 @@ mass rot rot_ctrl - - trail - trail_step lifetime lifetime_ctrl @@ -119,6 +116,14 @@ fade_out trail_opacity + + trail + trail_step + + + motion_blur + motion_blur_gamma_adjust + scale_step scale_step_ctrl diff --git a/stuff/profiles/layouts/fxs/fxs.lst b/stuff/profiles/layouts/fxs/fxs.lst index cb844793..2de36847 100644 --- a/stuff/profiles/layouts/fxs/fxs.lst +++ b/stuff/profiles/layouts/fxs/fxs.lst @@ -23,6 +23,7 @@ STD_iwa_BokehFx STD_iwa_BokehRefFx STD_iwa_BokehAdvancedFx + STD_iwa_FlowBlurFx STD_freeDistortFx @@ -35,6 +36,7 @@ STD_warpFx STD_inoWarphvFx STD_iwa_BarrelDistortFx + STD_iwa_FloorBumpFx STD_diamondGradientFx @@ -155,6 +157,7 @@ STD_iwa_TiledParticlesFx STD_iwa_TimeCodeFx STD_iwa_TextFx + STD_iwa_FlowPaintBrushFx STD_colorEmbossFx @@ -163,7 +166,9 @@ STD_mosaicFx STD_inoMotionWindFx STD_posterizeFx - STD_solarizeFx + STD_solarizeFx + STD_iwa_TangentFlowFx + STD_iwa_MotionFlowFx STD_artContourFx diff --git a/thirdparty/tinyexr/tinyexr.h b/thirdparty/tinyexr/tinyexr.h new file mode 100644 index 00000000..969f07ad --- /dev/null +++ b/thirdparty/tinyexr/tinyexr.h @@ -0,0 +1,8530 @@ +#ifndef TINYEXR_H_ +#define TINYEXR_H_ +/* +Copyright (c) 2014 - 2021, Syoyo Fujita and many contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Syoyo Fujita nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// TinyEXR contains some OpenEXR code, which is licensed under ------------ + +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas +// Digital Ltd. LLC +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Industrial Light & Magic nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +// End of OpenEXR license ------------------------------------------------- + + +// +// +// Do this: +// #define TINYEXR_IMPLEMENTATION +// before you include this file in *one* C or C++ file to create the +// implementation. +// +// // i.e. it should look like this: +// #include ... +// #include ... +// #include ... +// #define TINYEXR_IMPLEMENTATION +// #include "tinyexr.h" +// +// + +#include // for size_t +#include // guess stdint.h is available(C99) + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ + defined(__i386) || defined(__i486__) || defined(__i486) || \ + defined(i386) || defined(__ia64__) || defined(__x86_64__) +#define TINYEXR_X86_OR_X64_CPU 1 +#else +#define TINYEXR_X86_OR_X64_CPU 0 +#endif + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || TINYEXR_X86_OR_X64_CPU +#define TINYEXR_LITTLE_ENDIAN 1 +#else +#define TINYEXR_LITTLE_ENDIAN 0 +#endif + +// Use miniz or not to decode ZIP format pixel. Linking with zlib +// required if this flas is 0. +#ifndef TINYEXR_USE_MINIZ +#define TINYEXR_USE_MINIZ (1) +#endif + +// Disable PIZ comporession when applying cpplint. +#ifndef TINYEXR_USE_PIZ +#define TINYEXR_USE_PIZ (1) +#endif + +#ifndef TINYEXR_USE_ZFP +#define TINYEXR_USE_ZFP (0) // TinyEXR extension. +// http://computation.llnl.gov/projects/floating-point-compression +#endif + +#ifndef TINYEXR_USE_THREAD +#define TINYEXR_USE_THREAD (0) // No threaded loading. +// http://computation.llnl.gov/projects/floating-point-compression +#endif + +#ifndef TINYEXR_USE_OPENMP +#ifdef _OPENMP +#define TINYEXR_USE_OPENMP (1) +#else +#define TINYEXR_USE_OPENMP (0) +#endif +#endif + +#define TINYEXR_SUCCESS (0) +#define TINYEXR_ERROR_INVALID_MAGIC_NUMBER (-1) +#define TINYEXR_ERROR_INVALID_EXR_VERSION (-2) +#define TINYEXR_ERROR_INVALID_ARGUMENT (-3) +#define TINYEXR_ERROR_INVALID_DATA (-4) +#define TINYEXR_ERROR_INVALID_FILE (-5) +#define TINYEXR_ERROR_INVALID_PARAMETER (-6) +#define TINYEXR_ERROR_CANT_OPEN_FILE (-7) +#define TINYEXR_ERROR_UNSUPPORTED_FORMAT (-8) +#define TINYEXR_ERROR_INVALID_HEADER (-9) +#define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-10) +#define TINYEXR_ERROR_CANT_WRITE_FILE (-11) +#define TINYEXR_ERROR_SERIALZATION_FAILED (-12) +#define TINYEXR_ERROR_LAYER_NOT_FOUND (-13) + +// @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf } + +// pixel type: possible values are: UINT = 0 HALF = 1 FLOAT = 2 +#define TINYEXR_PIXELTYPE_UINT (0) +#define TINYEXR_PIXELTYPE_HALF (1) +#define TINYEXR_PIXELTYPE_FLOAT (2) + +#define TINYEXR_MAX_HEADER_ATTRIBUTES (1024) +#define TINYEXR_MAX_CUSTOM_ATTRIBUTES (128) + +#define TINYEXR_COMPRESSIONTYPE_NONE (0) +#define TINYEXR_COMPRESSIONTYPE_RLE (1) +#define TINYEXR_COMPRESSIONTYPE_ZIPS (2) +#define TINYEXR_COMPRESSIONTYPE_ZIP (3) +#define TINYEXR_COMPRESSIONTYPE_PIZ (4) +#define TINYEXR_COMPRESSIONTYPE_ZFP (128) // TinyEXR extension + +#define TINYEXR_ZFP_COMPRESSIONTYPE_RATE (0) +#define TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION (1) +#define TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY (2) + +#define TINYEXR_TILE_ONE_LEVEL (0) +#define TINYEXR_TILE_MIPMAP_LEVELS (1) +#define TINYEXR_TILE_RIPMAP_LEVELS (2) + +#define TINYEXR_TILE_ROUND_DOWN (0) +#define TINYEXR_TILE_ROUND_UP (1) + +typedef struct _EXRVersion { + int version; // this must be 2 + // tile format image; + // not zero for only a single-part "normal" tiled file (according to spec.) + int tiled; + int long_name; // long name attribute + // deep image(EXR 2.0); + // for a multi-part file, indicates that at least one part is of type deep* (according to spec.) + int non_image; + int multipart; // multi-part(EXR 2.0) +} EXRVersion; + +typedef struct _EXRAttribute { + char name[256]; // name and type are up to 255 chars long. + char type[256]; + unsigned char *value; // uint8_t* + int size; + int pad0; +} EXRAttribute; + +typedef struct _EXRChannelInfo { + char name[256]; // less than 255 bytes long + int pixel_type; + int x_sampling; + int y_sampling; + unsigned char p_linear; + unsigned char pad[3]; +} EXRChannelInfo; + +typedef struct _EXRTile { + int offset_x; + int offset_y; + int level_x; + int level_y; + + int width; // actual width in a tile. + int height; // actual height int a tile. + + unsigned char **images; // image[channels][pixels] +} EXRTile; + +typedef struct _EXRBox2i { + int min_x; + int min_y; + int max_x; + int max_y; +} EXRBox2i; + +typedef struct _EXRHeader { + float pixel_aspect_ratio; + int line_order; + EXRBox2i data_window; + EXRBox2i display_window; + float screen_window_center[2]; + float screen_window_width; + + int chunk_count; + + // Properties for tiled format(`tiledesc`). + int tiled; + int tile_size_x; + int tile_size_y; + int tile_level_mode; + int tile_rounding_mode; + + int long_name; + // for a single-part file, agree with the version field bit 11 + // for a multi-part file, it is consistent with the type of part + int non_image; + int multipart; + unsigned int header_len; + + // Custom attributes(exludes required attributes(e.g. `channels`, + // `compression`, etc) + int num_custom_attributes; + EXRAttribute *custom_attributes; // array of EXRAttribute. size = + // `num_custom_attributes`. + + EXRChannelInfo *channels; // [num_channels] + + int *pixel_types; // Loaded pixel type(TINYEXR_PIXELTYPE_*) of `images` for + // each channel. This is overwritten with `requested_pixel_types` when + // loading. + int num_channels; + + int compression_type; // compression type(TINYEXR_COMPRESSIONTYPE_*) + int *requested_pixel_types; // Filled initially by + // ParseEXRHeaderFrom(Meomory|File), then users + // can edit it(only valid for HALF pixel type + // channel) + // name attribute required for multipart files; + // must be unique and non empty (according to spec.); + // use EXRSetNameAttr for setting value; + // max 255 character allowed - excluding terminating zero + char name[256]; +} EXRHeader; + +typedef struct _EXRMultiPartHeader { + int num_headers; + EXRHeader *headers; + +} EXRMultiPartHeader; + +typedef struct _EXRImage { + EXRTile *tiles; // Tiled pixel data. The application must reconstruct image + // from tiles manually. NULL if scanline format. + struct _EXRImage* next_level; // NULL if scanline format or image is the last level. + int level_x; // x level index + int level_y; // y level index + + unsigned char **images; // image[channels][pixels]. NULL if tiled format. + + int width; + int height; + int num_channels; + + // Properties for tile format. + int num_tiles; + +} EXRImage; + +typedef struct _EXRMultiPartImage { + int num_images; + EXRImage *images; + +} EXRMultiPartImage; + +typedef struct _DeepImage { + const char **channel_names; + float ***image; // image[channels][scanlines][samples] + int **offset_table; // offset_table[scanline][offsets] + int num_channels; + int width; + int height; + int pad0; +} DeepImage; + +// @deprecated { For backward compatibility. Not recommended to use. } +// Loads single-frame OpenEXR image. Assume EXR image contains A(single channel +// alpha) or RGB(A) channels. +// Application must free image data as returned by `out_rgba` +// Result image format is: float x RGBA x width x hight +// Returns negative value and may set error string in `err` when there's an +// error +extern int LoadEXR(float **out_rgba, int *width, int *height, + const char *filename, const char **err); + +// Loads single-frame OpenEXR image by specifying layer name. Assume EXR image +// contains A(single channel alpha) or RGB(A) channels. Application must free +// image data as returned by `out_rgba` Result image format is: float x RGBA x +// width x hight Returns negative value and may set error string in `err` when +// there's an error When the specified layer name is not found in the EXR file, +// the function will return `TINYEXR_ERROR_LAYER_NOT_FOUND`. +extern int LoadEXRWithLayer(float **out_rgba, int *width, int *height, + const char *filename, const char *layer_name, + const char **err); + +// +// Get layer infos from EXR file. +// +// @param[out] layer_names List of layer names. Application must free memory +// after using this. +// @param[out] num_layers The number of layers +// @param[out] err Error string(will be filled when the function returns error +// code). Free it using FreeEXRErrorMessage after using this value. +// +// @return TINYEXR_SUCCEES upon success. +// +extern int EXRLayers(const char *filename, const char **layer_names[], + int *num_layers, const char **err); + +// @deprecated { to be removed. } +// Simple wrapper API for ParseEXRHeaderFromFile. +// checking given file is a EXR file(by just look up header) +// @return TINYEXR_SUCCEES for EXR image, TINYEXR_ERROR_INVALID_HEADER for +// others +extern int IsEXR(const char *filename); + +// @deprecated { to be removed. } +// Saves single-frame OpenEXR image. Assume EXR image contains RGB(A) channels. +// components must be 1(Grayscale), 3(RGB) or 4(RGBA). +// Input image format is: `float x width x height`, or `float x RGB(A) x width x +// hight` +// Save image as fp16(HALF) format when `save_as_fp16` is positive non-zero +// value. +// Save image as fp32(FLOAT) format when `save_as_fp16` is 0. +// Use ZIP compression by default. +// Returns negative value and may set error string in `err` when there's an +// error +extern int SaveEXR(const float *data, const int width, const int height, + const int components, const int save_as_fp16, + const char *filename, const char **err); + +// Returns the number of resolution levels of the image (including the base) +extern int EXRNumLevels(const EXRImage* exr_image); + +// Initialize EXRHeader struct +extern void InitEXRHeader(EXRHeader *exr_header); + +// Set name attribute of EXRHeader struct (it makes a copy) +extern void EXRSetNameAttr(EXRHeader *exr_header, const char* name); + +// Initialize EXRImage struct +extern void InitEXRImage(EXRImage *exr_image); + +// Frees internal data of EXRHeader struct +extern int FreeEXRHeader(EXRHeader *exr_header); + +// Frees internal data of EXRImage struct +extern int FreeEXRImage(EXRImage *exr_image); + +// Frees error message +extern void FreeEXRErrorMessage(const char *msg); + +// Parse EXR version header of a file. +extern int ParseEXRVersionFromFile(EXRVersion *version, const char *filename); + +// Parse EXR version header from memory-mapped EXR data. +extern int ParseEXRVersionFromMemory(EXRVersion *version, + const unsigned char *memory, size_t size); + +// Parse single-part OpenEXR header from a file and initialize `EXRHeader`. +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int ParseEXRHeaderFromFile(EXRHeader *header, const EXRVersion *version, + const char *filename, const char **err); + +// Parse single-part OpenEXR header from a memory and initialize `EXRHeader`. +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int ParseEXRHeaderFromMemory(EXRHeader *header, + const EXRVersion *version, + const unsigned char *memory, size_t size, + const char **err); + +// Parse multi-part OpenEXR headers from a file and initialize `EXRHeader*` +// array. +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int ParseEXRMultipartHeaderFromFile(EXRHeader ***headers, + int *num_headers, + const EXRVersion *version, + const char *filename, + const char **err); + +// Parse multi-part OpenEXR headers from a memory and initialize `EXRHeader*` +// array +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int ParseEXRMultipartHeaderFromMemory(EXRHeader ***headers, + int *num_headers, + const EXRVersion *version, + const unsigned char *memory, + size_t size, const char **err); + +// Loads single-part OpenEXR image from a file. +// Application must setup `ParseEXRHeaderFromFile` before calling this function. +// Application can free EXRImage using `FreeEXRImage` +// Returns negative value and may set error string in `err` when there's an +// error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int LoadEXRImageFromFile(EXRImage *image, const EXRHeader *header, + const char *filename, const char **err); + +// Loads single-part OpenEXR image from a memory. +// Application must setup `EXRHeader` with +// `ParseEXRHeaderFromMemory` before calling this function. +// Application can free EXRImage using `FreeEXRImage` +// Returns negative value and may set error string in `err` when there's an +// error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int LoadEXRImageFromMemory(EXRImage *image, const EXRHeader *header, + const unsigned char *memory, + const size_t size, const char **err); + +// Loads multi-part OpenEXR image from a file. +// Application must setup `ParseEXRMultipartHeaderFromFile` before calling this +// function. +// Application can free EXRImage using `FreeEXRImage` +// Returns negative value and may set error string in `err` when there's an +// error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int LoadEXRMultipartImageFromFile(EXRImage *images, + const EXRHeader **headers, + unsigned int num_parts, + const char *filename, + const char **err); + +// Loads multi-part OpenEXR image from a memory. +// Application must setup `EXRHeader*` array with +// `ParseEXRMultipartHeaderFromMemory` before calling this function. +// Application can free EXRImage using `FreeEXRImage` +// Returns negative value and may set error string in `err` when there's an +// error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int LoadEXRMultipartImageFromMemory(EXRImage *images, + const EXRHeader **headers, + unsigned int num_parts, + const unsigned char *memory, + const size_t size, const char **err); + +// Saves multi-channel, single-frame OpenEXR image to a file. +// Returns negative value and may set error string in `err` when there's an +// error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int SaveEXRImageToFile(const EXRImage *image, + const EXRHeader *exr_header, const char *filename, + const char **err); + +// Saves multi-channel, single-frame OpenEXR image to a memory. +// Image is compressed using EXRImage.compression value. +// Return the number of bytes if success. +// Return zero and will set error string in `err` when there's an +// error. +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern size_t SaveEXRImageToMemory(const EXRImage *image, + const EXRHeader *exr_header, + unsigned char **memory, const char **err); + +// Saves multi-channel, multi-frame OpenEXR image to a memory. +// Image is compressed using EXRImage.compression value. +// File global attributes (eg. display_window) must be set in the first header. +// Returns negative value and may set error string in `err` when there's an +// error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int SaveEXRMultipartImageToFile(const EXRImage *images, + const EXRHeader **exr_headers, + unsigned int num_parts, + const char *filename, const char **err); + +// Saves multi-channel, multi-frame OpenEXR image to a memory. +// Image is compressed using EXRImage.compression value. +// File global attributes (eg. display_window) must be set in the first header. +// Return the number of bytes if success. +// Return zero and will set error string in `err` when there's an +// error. +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern size_t SaveEXRMultipartImageToMemory(const EXRImage *images, + const EXRHeader **exr_headers, + unsigned int num_parts, + unsigned char **memory, const char **err); +// Loads single-frame OpenEXR deep image. +// Application must free memory of variables in DeepImage(image, offset_table) +// Returns negative value and may set error string in `err` when there's an +// error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int LoadDeepEXR(DeepImage *out_image, const char *filename, + const char **err); + +// NOT YET IMPLEMENTED: +// Saves single-frame OpenEXR deep image. +// Returns negative value and may set error string in `err` when there's an +// error +// extern int SaveDeepEXR(const DeepImage *in_image, const char *filename, +// const char **err); + +// NOT YET IMPLEMENTED: +// Loads multi-part OpenEXR deep image. +// Application must free memory of variables in DeepImage(image, offset_table) +// extern int LoadMultiPartDeepEXR(DeepImage **out_image, int num_parts, const +// char *filename, +// const char **err); + +// For emscripten. +// Loads single-frame OpenEXR image from memory. Assume EXR image contains +// RGB(A) channels. +// Returns negative value and may set error string in `err` when there's an +// error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() +extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, + const unsigned char *memory, size_t size, + const char **err); + +#ifdef __cplusplus +} +#endif + +#endif // TINYEXR_H_ + +#ifdef TINYEXR_IMPLEMENTATION +#ifndef TINYEXR_IMPLEMENTATION_DEFINED +#define TINYEXR_IMPLEMENTATION_DEFINED + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include // for UTF-8 + +#endif + +#include +#include +#include +#include +#include +#include + +// #include // debug + +#include +#include +#include +#include + +// https://stackoverflow.com/questions/5047971/how-do-i-check-for-c11-support +#if __cplusplus > 199711L || (defined(_MSC_VER) && _MSC_VER >= 1900) +#define TINYEXR_HAS_CXX11 (1) +// C++11 +#include + +#if TINYEXR_USE_THREAD +#include +#include +#endif + +#endif // __cplusplus > 199711L + +#if TINYEXR_USE_OPENMP +#include +#endif + +#if TINYEXR_USE_MINIZ +#include +#else +// Issue #46. Please include your own zlib-compatible API header before +// including `tinyexr.h` +//#include "zlib.h" +#endif + +#if TINYEXR_USE_ZFP + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Weverything" +#endif + +#include "zfp.h" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif + +namespace tinyexr { + +#if __cplusplus > 199711L +// C++11 +typedef uint64_t tinyexr_uint64; +typedef int64_t tinyexr_int64; +#else +// Although `long long` is not a standard type pre C++11, assume it is defined +// as a compiler's extension. +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#endif +typedef unsigned long long tinyexr_uint64; +typedef long long tinyexr_int64; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif + +// static bool IsBigEndian(void) { +// union { +// unsigned int i; +// char c[4]; +// } bint = {0x01020304}; +// +// return bint.c[0] == 1; +//} + +static void SetErrorMessage(const std::string &msg, const char **err) { + if (err) { +#ifdef _WIN32 + (*err) = _strdup(msg.c_str()); +#else + (*err) = strdup(msg.c_str()); +#endif + } +} + +static const int kEXRVersionSize = 8; + +static void cpy2(unsigned short *dst_val, const unsigned short *src_val) { + unsigned char *dst = reinterpret_cast(dst_val); + const unsigned char *src = reinterpret_cast(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; +} + +static void swap2(unsigned short *val) { +#ifdef TINYEXR_LITTLE_ENDIAN + (void)val; +#else + unsigned short tmp = *val; + unsigned char *dst = reinterpret_cast(val); + unsigned char *src = reinterpret_cast(&tmp); + + dst[0] = src[1]; + dst[1] = src[0]; +#endif +} + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +static void cpy4(int *dst_val, const int *src_val) { + unsigned char *dst = reinterpret_cast(dst_val); + const unsigned char *src = reinterpret_cast(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; +} + +static void cpy4(unsigned int *dst_val, const unsigned int *src_val) { + unsigned char *dst = reinterpret_cast(dst_val); + const unsigned char *src = reinterpret_cast(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; +} + +static void cpy4(float *dst_val, const float *src_val) { + unsigned char *dst = reinterpret_cast(dst_val); + const unsigned char *src = reinterpret_cast(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; +} +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +static void swap4(unsigned int *val) { +#ifdef TINYEXR_LITTLE_ENDIAN + (void)val; +#else + unsigned int tmp = *val; + unsigned char *dst = reinterpret_cast(val); + unsigned char *src = reinterpret_cast(&tmp); + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +#endif +} + +static void swap4(int *val) { +#ifdef TINYEXR_LITTLE_ENDIAN + (void)val; +#else + int tmp = *val; + unsigned char *dst = reinterpret_cast(val); + unsigned char *src = reinterpret_cast(&tmp); + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +#endif +} + +static void swap4(float *val) { +#ifdef TINYEXR_LITTLE_ENDIAN + (void)val; +#else + float tmp = *val; + unsigned char *dst = reinterpret_cast(val); + unsigned char *src = reinterpret_cast(&tmp); + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +#endif +} + +#if 0 +static void cpy8(tinyexr::tinyexr_uint64 *dst_val, const tinyexr::tinyexr_uint64 *src_val) { + unsigned char *dst = reinterpret_cast(dst_val); + const unsigned char *src = reinterpret_cast(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; +} +#endif + +static void swap8(tinyexr::tinyexr_uint64 *val) { +#ifdef TINYEXR_LITTLE_ENDIAN + (void)val; +#else + tinyexr::tinyexr_uint64 tmp = (*val); + unsigned char *dst = reinterpret_cast(val); + unsigned char *src = reinterpret_cast(&tmp); + + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; +#endif +} + +// https://gist.github.com/rygorous/2156668 +union FP32 { + unsigned int u; + float f; + struct { +#if TINYEXR_LITTLE_ENDIAN + unsigned int Mantissa : 23; + unsigned int Exponent : 8; + unsigned int Sign : 1; +#else + unsigned int Sign : 1; + unsigned int Exponent : 8; + unsigned int Mantissa : 23; +#endif + } s; +}; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +union FP16 { + unsigned short u; + struct { +#if TINYEXR_LITTLE_ENDIAN + unsigned int Mantissa : 10; + unsigned int Exponent : 5; + unsigned int Sign : 1; +#else + unsigned int Sign : 1; + unsigned int Exponent : 5; + unsigned int Mantissa : 10; +#endif + } s; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +static FP32 half_to_float(FP16 h) { + static const FP32 magic = {113 << 23}; + static const unsigned int shifted_exp = 0x7c00 + << 13; // exponent mask after shift + FP32 o; + + o.u = (h.u & 0x7fffU) << 13U; // exponent/mantissa bits + unsigned int exp_ = shifted_exp & o.u; // just the exponent + o.u += (127 - 15) << 23; // exponent adjust + + // handle exponent special cases + if (exp_ == shifted_exp) // Inf/NaN? + o.u += (128 - 16) << 23; // extra exp adjust + else if (exp_ == 0) // Zero/Denormal? + { + o.u += 1 << 23; // extra exp adjust + o.f -= magic.f; // renormalize + } + + o.u |= (h.u & 0x8000U) << 16U; // sign bit + return o; +} + +static FP16 float_to_half_full(FP32 f) { + FP16 o = {0}; + + // Based on ISPC reference code (with minor modifications) + if (f.s.Exponent == 0) // Signed zero/denormal (which will underflow) + o.s.Exponent = 0; + else if (f.s.Exponent == 255) // Inf or NaN (all exponent bits set) + { + o.s.Exponent = 31; + o.s.Mantissa = f.s.Mantissa ? 0x200 : 0; // NaN->qNaN and Inf->Inf + } else // Normalized number + { + // Exponent unbias the single, then bias the halfp + int newexp = f.s.Exponent - 127 + 15; + if (newexp >= 31) // Overflow, return signed infinity + o.s.Exponent = 31; + else if (newexp <= 0) // Underflow + { + if ((14 - newexp) <= 24) // Mantissa might be non-zero + { + unsigned int mant = f.s.Mantissa | 0x800000; // Hidden 1 bit + o.s.Mantissa = mant >> (14 - newexp); + if ((mant >> (13 - newexp)) & 1) // Check for rounding + o.u++; // Round, might overflow into exp bit, but this is OK + } + } else { + o.s.Exponent = static_cast(newexp); + o.s.Mantissa = f.s.Mantissa >> 13; + if (f.s.Mantissa & 0x1000) // Check for rounding + o.u++; // Round, might overflow to inf, this is OK + } + } + + o.s.Sign = f.s.Sign; + return o; +} + +// NOTE: From OpenEXR code +// #define IMF_INCREASING_Y 0 +// #define IMF_DECREASING_Y 1 +// #define IMF_RAMDOM_Y 2 +// +// #define IMF_NO_COMPRESSION 0 +// #define IMF_RLE_COMPRESSION 1 +// #define IMF_ZIPS_COMPRESSION 2 +// #define IMF_ZIP_COMPRESSION 3 +// #define IMF_PIZ_COMPRESSION 4 +// #define IMF_PXR24_COMPRESSION 5 +// #define IMF_B44_COMPRESSION 6 +// #define IMF_B44A_COMPRESSION 7 + +#ifdef __clang__ +#pragma clang diagnostic push + +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + +#endif + +static const char *ReadString(std::string *s, const char *ptr, size_t len) { + // Read untile NULL(\0). + const char *p = ptr; + const char *q = ptr; + while ((size_t(q - ptr) < len) && (*q) != 0) { + q++; + } + + if (size_t(q - ptr) >= len) { + (*s).clear(); + return NULL; + } + + (*s) = std::string(p, q); + + return q + 1; // skip '\0' +} + +static bool ReadAttribute(std::string *name, std::string *type, + std::vector *data, size_t *marker_size, + const char *marker, size_t size) { + size_t name_len = strnlen(marker, size); + if (name_len == size) { + // String does not have a terminating character. + return false; + } + *name = std::string(marker, name_len); + + marker += name_len + 1; + size -= name_len + 1; + + size_t type_len = strnlen(marker, size); + if (type_len == size) { + return false; + } + *type = std::string(marker, type_len); + + marker += type_len + 1; + size -= type_len + 1; + + if (size < sizeof(uint32_t)) { + return false; + } + + uint32_t data_len; + memcpy(&data_len, marker, sizeof(uint32_t)); + tinyexr::swap4(reinterpret_cast(&data_len)); + + if (data_len == 0) { + if ((*type).compare("string") == 0) { + // Accept empty string attribute. + + marker += sizeof(uint32_t); + size -= sizeof(uint32_t); + + *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t); + + data->resize(1); + (*data)[0] = '\0'; + + return true; + } else { + return false; + } + } + + marker += sizeof(uint32_t); + size -= sizeof(uint32_t); + + if (size < data_len) { + return false; + } + + data->resize(static_cast(data_len)); + memcpy(&data->at(0), marker, static_cast(data_len)); + + *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t) + data_len; + return true; +} + +static void WriteAttributeToMemory(std::vector *out, + const char *name, const char *type, + const unsigned char *data, int len) { + out->insert(out->end(), name, name + strlen(name) + 1); + out->insert(out->end(), type, type + strlen(type) + 1); + + int outLen = len; + tinyexr::swap4(&outLen); + out->insert(out->end(), reinterpret_cast(&outLen), + reinterpret_cast(&outLen) + sizeof(int)); + out->insert(out->end(), data, data + len); +} + +typedef struct { + std::string name; // less than 255 bytes long + int pixel_type; + int requested_pixel_type; + int x_sampling; + int y_sampling; + unsigned char p_linear; + unsigned char pad[3]; +} ChannelInfo; + +typedef struct { + int min_x; + int min_y; + int max_x; + int max_y; +} Box2iInfo; + +struct HeaderInfo { + std::vector channels; + std::vector attributes; + + Box2iInfo data_window; + int line_order; + Box2iInfo display_window; + float screen_window_center[2]; + float screen_window_width; + float pixel_aspect_ratio; + + int chunk_count; + + // Tiled format + int tiled; // Non-zero if the part is tiled. + int tile_size_x; + int tile_size_y; + int tile_level_mode; + int tile_rounding_mode; + + unsigned int header_len; + + int compression_type; + + // required for multi-part or non-image files + std::string name; + // required for multi-part or non-image files + std::string type; + + void clear() { + channels.clear(); + attributes.clear(); + + data_window.min_x = 0; + data_window.min_y = 0; + data_window.max_x = 0; + data_window.max_y = 0; + line_order = 0; + display_window.min_x = 0; + display_window.min_y = 0; + display_window.max_x = 0; + display_window.max_y = 0; + screen_window_center[0] = 0.0f; + screen_window_center[1] = 0.0f; + screen_window_width = 0.0f; + pixel_aspect_ratio = 0.0f; + + chunk_count = 0; + + // Tiled format + tiled = 0; + tile_size_x = 0; + tile_size_y = 0; + tile_level_mode = 0; + tile_rounding_mode = 0; + + header_len = 0; + compression_type = 0; + + name.clear(); + type.clear(); + } +}; + +static bool ReadChannelInfo(std::vector &channels, + const std::vector &data) { + const char *p = reinterpret_cast(&data.at(0)); + + for (;;) { + if ((*p) == 0) { + break; + } + ChannelInfo info; + + tinyexr_int64 data_len = static_cast(data.size()) - + (p - reinterpret_cast(data.data())); + if (data_len < 0) { + return false; + } + + p = ReadString(&info.name, p, size_t(data_len)); + if ((p == NULL) && (info.name.empty())) { + // Buffer overrun. Issue #51. + return false; + } + + const unsigned char *data_end = + reinterpret_cast(p) + 16; + if (data_end >= (data.data() + data.size())) { + return false; + } + + memcpy(&info.pixel_type, p, sizeof(int)); + p += 4; + info.p_linear = static_cast(p[0]); // uchar + p += 1 + 3; // reserved: uchar[3] + memcpy(&info.x_sampling, p, sizeof(int)); // int + p += 4; + memcpy(&info.y_sampling, p, sizeof(int)); // int + p += 4; + + tinyexr::swap4(&info.pixel_type); + tinyexr::swap4(&info.x_sampling); + tinyexr::swap4(&info.y_sampling); + + channels.push_back(info); + } + + return true; +} + +static void WriteChannelInfo(std::vector &data, + const std::vector &channels) { + size_t sz = 0; + + // Calculate total size. + for (size_t c = 0; c < channels.size(); c++) { + sz += channels[c].name.length() + 1; // +1 for \0 + sz += 16; // 4 * int + } + data.resize(sz + 1); + + unsigned char *p = &data.at(0); + + for (size_t c = 0; c < channels.size(); c++) { + memcpy(p, channels[c].name.c_str(), channels[c].name.length()); + p += channels[c].name.length(); + (*p) = '\0'; + p++; + + int pixel_type = channels[c].requested_pixel_type; + int x_sampling = channels[c].x_sampling; + int y_sampling = channels[c].y_sampling; + tinyexr::swap4(&pixel_type); + tinyexr::swap4(&x_sampling); + tinyexr::swap4(&y_sampling); + + memcpy(p, &pixel_type, sizeof(int)); + p += sizeof(int); + + (*p) = channels[c].p_linear; + p += 4; + + memcpy(p, &x_sampling, sizeof(int)); + p += sizeof(int); + + memcpy(p, &y_sampling, sizeof(int)); + p += sizeof(int); + } + + (*p) = '\0'; +} + +static void CompressZip(unsigned char *dst, + tinyexr::tinyexr_uint64 &compressedSize, + const unsigned char *src, unsigned long src_size) { + std::vector tmpBuf(src_size); + + // + // Apply EXR-specific? postprocess. Grabbed from OpenEXR's + // ImfZipCompressor.cpp + // + + // + // Reorder the pixel data. + // + + const char *srcPtr = reinterpret_cast(src); + + { + char *t1 = reinterpret_cast(&tmpBuf.at(0)); + char *t2 = reinterpret_cast(&tmpBuf.at(0)) + (src_size + 1) / 2; + const char *stop = srcPtr + src_size; + + for (;;) { + if (srcPtr < stop) + *(t1++) = *(srcPtr++); + else + break; + + if (srcPtr < stop) + *(t2++) = *(srcPtr++); + else + break; + } + } + + // + // Predictor. + // + + { + unsigned char *t = &tmpBuf.at(0) + 1; + unsigned char *stop = &tmpBuf.at(0) + src_size; + int p = t[-1]; + + while (t < stop) { + int d = int(t[0]) - p + (128 + 256); + p = t[0]; + t[0] = static_cast(d); + ++t; + } + } + +#if TINYEXR_USE_MINIZ + // + // Compress the data using miniz + // + + mz_ulong outSize = mz_compressBound(src_size); + int ret = mz_compress( + dst, &outSize, static_cast(&tmpBuf.at(0)), + src_size); + assert(ret == MZ_OK); + (void)ret; + + compressedSize = outSize; +#else + uLong outSize = compressBound(static_cast(src_size)); + int ret = compress(dst, &outSize, static_cast(&tmpBuf.at(0)), + src_size); + assert(ret == Z_OK); + + compressedSize = outSize; +#endif + + // Use uncompressed data when compressed data is larger than uncompressed. + // (Issue 40) + if (compressedSize >= src_size) { + compressedSize = src_size; + memcpy(dst, src, src_size); + } +} + +static bool DecompressZip(unsigned char *dst, + unsigned long *uncompressed_size /* inout */, + const unsigned char *src, unsigned long src_size) { + if ((*uncompressed_size) == src_size) { + // Data is not compressed(Issue 40). + memcpy(dst, src, src_size); + return true; + } + std::vector tmpBuf(*uncompressed_size); + +#if TINYEXR_USE_MINIZ + int ret = + mz_uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size); + if (MZ_OK != ret) { + return false; + } +#else + int ret = uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size); + if (Z_OK != ret) { + return false; + } +#endif + + // + // Apply EXR-specific? postprocess. Grabbed from OpenEXR's + // ImfZipCompressor.cpp + // + + // Predictor. + { + unsigned char *t = &tmpBuf.at(0) + 1; + unsigned char *stop = &tmpBuf.at(0) + (*uncompressed_size); + + while (t < stop) { + int d = int(t[-1]) + int(t[0]) - 128; + t[0] = static_cast(d); + ++t; + } + } + + // Reorder the pixel data. + { + const char *t1 = reinterpret_cast(&tmpBuf.at(0)); + const char *t2 = reinterpret_cast(&tmpBuf.at(0)) + + (*uncompressed_size + 1) / 2; + char *s = reinterpret_cast(dst); + char *stop = s + (*uncompressed_size); + + for (;;) { + if (s < stop) + *(s++) = *(t1++); + else + break; + + if (s < stop) + *(s++) = *(t2++); + else + break; + } + } + + return true; +} + +// RLE code from OpenEXR -------------------------------------- + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" +#if __has_warning("-Wextra-semi-stmt") +#pragma clang diagnostic ignored "-Wextra-semi-stmt" +#endif +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) // nonstandard extension used : non-constant + // aggregate initializer (also supported by GNU + // C and C99, so no big deal) +#pragma warning(disable : 4244) // 'initializing': conversion from '__int64' to + // 'int', possible loss of data +#pragma warning(disable : 4267) // 'argument': conversion from '__int64' to + // 'int', possible loss of data +#pragma warning(disable : 4996) // 'strdup': The POSIX name for this item is + // deprecated. Instead, use the ISO C and C++ + // conformant name: _strdup. +#endif + +const int MIN_RUN_LENGTH = 3; +const int MAX_RUN_LENGTH = 127; + +// +// Compress an array of bytes, using run-length encoding, +// and return the length of the compressed data. +// + +static int rleCompress(int inLength, const char in[], signed char out[]) { + const char *inEnd = in + inLength; + const char *runStart = in; + const char *runEnd = in + 1; + signed char *outWrite = out; + + while (runStart < inEnd) { + while (runEnd < inEnd && *runStart == *runEnd && + runEnd - runStart - 1 < MAX_RUN_LENGTH) { + ++runEnd; + } + + if (runEnd - runStart >= MIN_RUN_LENGTH) { + // + // Compressible run + // + + *outWrite++ = static_cast(runEnd - runStart) - 1; + *outWrite++ = *(reinterpret_cast(runStart)); + runStart = runEnd; + } else { + // + // Uncompressable run + // + + while (runEnd < inEnd && + ((runEnd + 1 >= inEnd || *runEnd != *(runEnd + 1)) || + (runEnd + 2 >= inEnd || *(runEnd + 1) != *(runEnd + 2))) && + runEnd - runStart < MAX_RUN_LENGTH) { + ++runEnd; + } + + *outWrite++ = static_cast(runStart - runEnd); + + while (runStart < runEnd) { + *outWrite++ = *(reinterpret_cast(runStart++)); + } + } + + ++runEnd; + } + + return static_cast(outWrite - out); +} + +// +// Uncompress an array of bytes compressed with rleCompress(). +// Returns the length of the oncompressed data, or 0 if the +// length of the uncompressed data would be more than maxLength. +// + +static int rleUncompress(int inLength, int maxLength, const signed char in[], + char out[]) { + char *outStart = out; + + while (inLength > 0) { + if (*in < 0) { + int count = -(static_cast(*in++)); + inLength -= count + 1; + + // Fixes #116: Add bounds check to in buffer. + if ((0 > (maxLength -= count)) || (inLength < 0)) return 0; + + memcpy(out, in, count); + out += count; + in += count; + } else { + int count = *in++; + inLength -= 2; + + if (0 > (maxLength -= count + 1)) return 0; + + memset(out, *reinterpret_cast(in), count + 1); + out += count + 1; + + in++; + } + } + + return static_cast(out - outStart); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// End of RLE code from OpenEXR ----------------------------------- + +static void CompressRle(unsigned char *dst, + tinyexr::tinyexr_uint64 &compressedSize, + const unsigned char *src, unsigned long src_size) { + std::vector tmpBuf(src_size); + + // + // Apply EXR-specific? postprocess. Grabbed from OpenEXR's + // ImfRleCompressor.cpp + // + + // + // Reorder the pixel data. + // + + const char *srcPtr = reinterpret_cast(src); + + { + char *t1 = reinterpret_cast(&tmpBuf.at(0)); + char *t2 = reinterpret_cast(&tmpBuf.at(0)) + (src_size + 1) / 2; + const char *stop = srcPtr + src_size; + + for (;;) { + if (srcPtr < stop) + *(t1++) = *(srcPtr++); + else + break; + + if (srcPtr < stop) + *(t2++) = *(srcPtr++); + else + break; + } + } + + // + // Predictor. + // + + { + unsigned char *t = &tmpBuf.at(0) + 1; + unsigned char *stop = &tmpBuf.at(0) + src_size; + int p = t[-1]; + + while (t < stop) { + int d = int(t[0]) - p + (128 + 256); + p = t[0]; + t[0] = static_cast(d); + ++t; + } + } + + // outSize will be (srcSiz * 3) / 2 at max. + int outSize = rleCompress(static_cast(src_size), + reinterpret_cast(&tmpBuf.at(0)), + reinterpret_cast(dst)); + assert(outSize > 0); + + compressedSize = static_cast(outSize); + + // Use uncompressed data when compressed data is larger than uncompressed. + // (Issue 40) + if (compressedSize >= src_size) { + compressedSize = src_size; + memcpy(dst, src, src_size); + } +} + +static bool DecompressRle(unsigned char *dst, + const unsigned long uncompressed_size, + const unsigned char *src, unsigned long src_size) { + if (uncompressed_size == src_size) { + // Data is not compressed(Issue 40). + memcpy(dst, src, src_size); + return true; + } + + // Workaround for issue #112. + // TODO(syoyo): Add more robust out-of-bounds check in `rleUncompress`. + if (src_size <= 2) { + return false; + } + + std::vector tmpBuf(uncompressed_size); + + int ret = rleUncompress(static_cast(src_size), + static_cast(uncompressed_size), + reinterpret_cast(src), + reinterpret_cast(&tmpBuf.at(0))); + if (ret != static_cast(uncompressed_size)) { + return false; + } + + // + // Apply EXR-specific? postprocess. Grabbed from OpenEXR's + // ImfRleCompressor.cpp + // + + // Predictor. + { + unsigned char *t = &tmpBuf.at(0) + 1; + unsigned char *stop = &tmpBuf.at(0) + uncompressed_size; + + while (t < stop) { + int d = int(t[-1]) + int(t[0]) - 128; + t[0] = static_cast(d); + ++t; + } + } + + // Reorder the pixel data. + { + const char *t1 = reinterpret_cast(&tmpBuf.at(0)); + const char *t2 = reinterpret_cast(&tmpBuf.at(0)) + + (uncompressed_size + 1) / 2; + char *s = reinterpret_cast(dst); + char *stop = s + uncompressed_size; + + for (;;) { + if (s < stop) + *(s++) = *(t1++); + else + break; + + if (s < stop) + *(s++) = *(t2++); + else + break; + } + } + + return true; +} + +#if TINYEXR_USE_PIZ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wc++11-extensions" +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" + +#if __has_warning("-Wcast-qual") +#pragma clang diagnostic ignored "-Wcast-qual" +#endif + +#if __has_warning("-Wextra-semi-stmt") +#pragma clang diagnostic ignored "-Wextra-semi-stmt" +#endif + +#endif + +// +// PIZ compress/uncompress, based on OpenEXR's ImfPizCompressor.cpp +// +// ----------------------------------------------------------------- +// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas +// Digital Ltd. LLC) +// (3 clause BSD license) +// + +struct PIZChannelData { + unsigned short *start; + unsigned short *end; + int nx; + int ny; + int ys; + int size; +}; + +//----------------------------------------------------------------------------- +// +// 16-bit Haar Wavelet encoding and decoding +// +// The source code in this file is derived from the encoding +// and decoding routines written by Christian Rouet for his +// PIZ image file format. +// +//----------------------------------------------------------------------------- + +// +// Wavelet basis functions without modulo arithmetic; they produce +// the best compression ratios when the wavelet-transformed data are +// Huffman-encoded, but the wavelet transform works only for 14-bit +// data (untransformed data values must be less than (1 << 14)). +// + +inline void wenc14(unsigned short a, unsigned short b, unsigned short &l, + unsigned short &h) { + short as = static_cast(a); + short bs = static_cast(b); + + short ms = (as + bs) >> 1; + short ds = as - bs; + + l = static_cast(ms); + h = static_cast(ds); +} + +inline void wdec14(unsigned short l, unsigned short h, unsigned short &a, + unsigned short &b) { + short ls = static_cast(l); + short hs = static_cast(h); + + int hi = hs; + int ai = ls + (hi & 1) + (hi >> 1); + + short as = static_cast(ai); + short bs = static_cast(ai - hi); + + a = static_cast(as); + b = static_cast(bs); +} + +// +// Wavelet basis functions with modulo arithmetic; they work with full +// 16-bit data, but Huffman-encoding the wavelet-transformed data doesn't +// compress the data quite as well. +// + +const int NBITS = 16; +const int A_OFFSET = 1 << (NBITS - 1); +const int M_OFFSET = 1 << (NBITS - 1); +const int MOD_MASK = (1 << NBITS) - 1; + +inline void wenc16(unsigned short a, unsigned short b, unsigned short &l, + unsigned short &h) { + int ao = (a + A_OFFSET) & MOD_MASK; + int m = ((ao + b) >> 1); + int d = ao - b; + + if (d < 0) m = (m + M_OFFSET) & MOD_MASK; + + d &= MOD_MASK; + + l = static_cast(m); + h = static_cast(d); +} + +inline void wdec16(unsigned short l, unsigned short h, unsigned short &a, + unsigned short &b) { + int m = l; + int d = h; + int bb = (m - (d >> 1)) & MOD_MASK; + int aa = (d + bb - A_OFFSET) & MOD_MASK; + b = static_cast(bb); + a = static_cast(aa); +} + +// +// 2D Wavelet encoding: +// + +static void wav2Encode( + unsigned short *in, // io: values are transformed in place + int nx, // i : x size + int ox, // i : x offset + int ny, // i : y size + int oy, // i : y offset + unsigned short mx) // i : maximum in[x][y] value +{ + bool w14 = (mx < (1 << 14)); + int n = (nx > ny) ? ny : nx; + int p = 1; // == 1 << level + int p2 = 2; // == 1 << (level+1) + + // + // Hierarchical loop on smaller dimension n + // + + while (p2 <= n) { + unsigned short *py = in; + unsigned short *ey = in + oy * (ny - p2); + int oy1 = oy * p; + int oy2 = oy * p2; + int ox1 = ox * p; + int ox2 = ox * p2; + unsigned short i00, i01, i10, i11; + + // + // Y loop + // + + for (; py <= ey; py += oy2) { + unsigned short *px = py; + unsigned short *ex = py + ox * (nx - p2); + + // + // X loop + // + + for (; px <= ex; px += ox2) { + unsigned short *p01 = px + ox1; + unsigned short *p10 = px + oy1; + unsigned short *p11 = p10 + ox1; + + // + // 2D wavelet encoding + // + + if (w14) { + wenc14(*px, *p01, i00, i01); + wenc14(*p10, *p11, i10, i11); + wenc14(i00, i10, *px, *p10); + wenc14(i01, i11, *p01, *p11); + } else { + wenc16(*px, *p01, i00, i01); + wenc16(*p10, *p11, i10, i11); + wenc16(i00, i10, *px, *p10); + wenc16(i01, i11, *p01, *p11); + } + } + + // + // Encode (1D) odd column (still in Y loop) + // + + if (nx & p) { + unsigned short *p10 = px + oy1; + + if (w14) + wenc14(*px, *p10, i00, *p10); + else + wenc16(*px, *p10, i00, *p10); + + *px = i00; + } + } + + // + // Encode (1D) odd line (must loop in X) + // + + if (ny & p) { + unsigned short *px = py; + unsigned short *ex = py + ox * (nx - p2); + + for (; px <= ex; px += ox2) { + unsigned short *p01 = px + ox1; + + if (w14) + wenc14(*px, *p01, i00, *p01); + else + wenc16(*px, *p01, i00, *p01); + + *px = i00; + } + } + + // + // Next level + // + + p = p2; + p2 <<= 1; + } +} + +// +// 2D Wavelet decoding: +// + +static void wav2Decode( + unsigned short *in, // io: values are transformed in place + int nx, // i : x size + int ox, // i : x offset + int ny, // i : y size + int oy, // i : y offset + unsigned short mx) // i : maximum in[x][y] value +{ + bool w14 = (mx < (1 << 14)); + int n = (nx > ny) ? ny : nx; + int p = 1; + int p2; + + // + // Search max level + // + + while (p <= n) p <<= 1; + + p >>= 1; + p2 = p; + p >>= 1; + + // + // Hierarchical loop on smaller dimension n + // + + while (p >= 1) { + unsigned short *py = in; + unsigned short *ey = in + oy * (ny - p2); + int oy1 = oy * p; + int oy2 = oy * p2; + int ox1 = ox * p; + int ox2 = ox * p2; + unsigned short i00, i01, i10, i11; + + // + // Y loop + // + + for (; py <= ey; py += oy2) { + unsigned short *px = py; + unsigned short *ex = py + ox * (nx - p2); + + // + // X loop + // + + for (; px <= ex; px += ox2) { + unsigned short *p01 = px + ox1; + unsigned short *p10 = px + oy1; + unsigned short *p11 = p10 + ox1; + + // + // 2D wavelet decoding + // + + if (w14) { + wdec14(*px, *p10, i00, i10); + wdec14(*p01, *p11, i01, i11); + wdec14(i00, i01, *px, *p01); + wdec14(i10, i11, *p10, *p11); + } else { + wdec16(*px, *p10, i00, i10); + wdec16(*p01, *p11, i01, i11); + wdec16(i00, i01, *px, *p01); + wdec16(i10, i11, *p10, *p11); + } + } + + // + // Decode (1D) odd column (still in Y loop) + // + + if (nx & p) { + unsigned short *p10 = px + oy1; + + if (w14) + wdec14(*px, *p10, i00, *p10); + else + wdec16(*px, *p10, i00, *p10); + + *px = i00; + } + } + + // + // Decode (1D) odd line (must loop in X) + // + + if (ny & p) { + unsigned short *px = py; + unsigned short *ex = py + ox * (nx - p2); + + for (; px <= ex; px += ox2) { + unsigned short *p01 = px + ox1; + + if (w14) + wdec14(*px, *p01, i00, *p01); + else + wdec16(*px, *p01, i00, *p01); + + *px = i00; + } + } + + // + // Next level + // + + p2 = p; + p >>= 1; + } +} + +//----------------------------------------------------------------------------- +// +// 16-bit Huffman compression and decompression. +// +// The source code in this file is derived from the 8-bit +// Huffman compression and decompression routines written +// by Christian Rouet for his PIZ image file format. +// +//----------------------------------------------------------------------------- + +// Adds some modification for tinyexr. + +const int HUF_ENCBITS = 16; // literal (value) bit length +const int HUF_DECBITS = 14; // decoding bit size (>= 8) + +const int HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1; // encoding table size +const int HUF_DECSIZE = 1 << HUF_DECBITS; // decoding table size +const int HUF_DECMASK = HUF_DECSIZE - 1; + +struct HufDec { // short code long code + //------------------------------- + unsigned int len : 8; // code length 0 + unsigned int lit : 24; // lit p size + unsigned int *p; // 0 lits +}; + +inline long long hufLength(long long code) { return code & 63; } + +inline long long hufCode(long long code) { return code >> 6; } + +inline void outputBits(int nBits, long long bits, long long &c, int &lc, + char *&out) { + c <<= nBits; + lc += nBits; + + c |= bits; + + while (lc >= 8) *out++ = static_cast((c >> (lc -= 8))); +} + +inline long long getBits(int nBits, long long &c, int &lc, const char *&in) { + while (lc < nBits) { + c = (c << 8) | *(reinterpret_cast(in++)); + lc += 8; + } + + lc -= nBits; + return (c >> lc) & ((1 << nBits) - 1); +} + +// +// ENCODING TABLE BUILDING & (UN)PACKING +// + +// +// Build a "canonical" Huffman code table: +// - for each (uncompressed) symbol, hcode contains the length +// of the corresponding code (in the compressed data) +// - canonical codes are computed and stored in hcode +// - the rules for constructing canonical codes are as follows: +// * shorter codes (if filled with zeroes to the right) +// have a numerically higher value than longer codes +// * for codes with the same length, numerical values +// increase with numerical symbol values +// - because the canonical code table can be constructed from +// symbol lengths alone, the code table can be transmitted +// without sending the actual code values +// - see http://www.compressconsult.com/huffman/ +// + +static void hufCanonicalCodeTable(long long hcode[HUF_ENCSIZE]) { + long long n[59]; + + // + // For each i from 0 through 58, count the + // number of different codes of length i, and + // store the count in n[i]. + // + + for (int i = 0; i <= 58; ++i) n[i] = 0; + + for (int i = 0; i < HUF_ENCSIZE; ++i) n[hcode[i]] += 1; + + // + // For each i from 58 through 1, compute the + // numerically lowest code with length i, and + // store that code in n[i]. + // + + long long c = 0; + + for (int i = 58; i > 0; --i) { + long long nc = ((c + n[i]) >> 1); + n[i] = c; + c = nc; + } + + // + // hcode[i] contains the length, l, of the + // code for symbol i. Assign the next available + // code of length l to the symbol and store both + // l and the code in hcode[i]. + // + + for (int i = 0; i < HUF_ENCSIZE; ++i) { + int l = static_cast(hcode[i]); + + if (l > 0) hcode[i] = l | (n[l]++ << 6); + } +} + +// +// Compute Huffman codes (based on frq input) and store them in frq: +// - code structure is : [63:lsb - 6:msb] | [5-0: bit length]; +// - max code length is 58 bits; +// - codes outside the range [im-iM] have a null length (unused values); +// - original frequencies are destroyed; +// - encoding tables are used by hufEncode() and hufBuildDecTable(); +// + +struct FHeapCompare { + bool operator()(long long *a, long long *b) { return *a > *b; } +}; + +static void hufBuildEncTable( + long long *frq, // io: input frequencies [HUF_ENCSIZE], output table + int *im, // o: min frq index + int *iM) // o: max frq index +{ + // + // This function assumes that when it is called, array frq + // indicates the frequency of all possible symbols in the data + // that are to be Huffman-encoded. (frq[i] contains the number + // of occurrences of symbol i in the data.) + // + // The loop below does three things: + // + // 1) Finds the minimum and maximum indices that point + // to non-zero entries in frq: + // + // frq[im] != 0, and frq[i] == 0 for all i < im + // frq[iM] != 0, and frq[i] == 0 for all i > iM + // + // 2) Fills array fHeap with pointers to all non-zero + // entries in frq. + // + // 3) Initializes array hlink such that hlink[i] == i + // for all array entries. + // + + std::vector hlink(HUF_ENCSIZE); + std::vector fHeap(HUF_ENCSIZE); + + *im = 0; + + while (!frq[*im]) (*im)++; + + int nf = 0; + + for (int i = *im; i < HUF_ENCSIZE; i++) { + hlink[i] = i; + + if (frq[i]) { + fHeap[nf] = &frq[i]; + nf++; + *iM = i; + } + } + + // + // Add a pseudo-symbol, with a frequency count of 1, to frq; + // adjust the fHeap and hlink array accordingly. Function + // hufEncode() uses the pseudo-symbol for run-length encoding. + // + + (*iM)++; + frq[*iM] = 1; + fHeap[nf] = &frq[*iM]; + nf++; + + // + // Build an array, scode, such that scode[i] contains the number + // of bits assigned to symbol i. Conceptually this is done by + // constructing a tree whose leaves are the symbols with non-zero + // frequency: + // + // Make a heap that contains all symbols with a non-zero frequency, + // with the least frequent symbol on top. + // + // Repeat until only one symbol is left on the heap: + // + // Take the two least frequent symbols off the top of the heap. + // Create a new node that has first two nodes as children, and + // whose frequency is the sum of the frequencies of the first + // two nodes. Put the new node back into the heap. + // + // The last node left on the heap is the root of the tree. For each + // leaf node, the distance between the root and the leaf is the length + // of the code for the corresponding symbol. + // + // The loop below doesn't actually build the tree; instead we compute + // the distances of the leaves from the root on the fly. When a new + // node is added to the heap, then that node's descendants are linked + // into a single linear list that starts at the new node, and the code + // lengths of the descendants (that is, their distance from the root + // of the tree) are incremented by one. + // + + std::make_heap(&fHeap[0], &fHeap[nf], FHeapCompare()); + + std::vector scode(HUF_ENCSIZE); + memset(scode.data(), 0, sizeof(long long) * HUF_ENCSIZE); + + while (nf > 1) { + // + // Find the indices, mm and m, of the two smallest non-zero frq + // values in fHeap, add the smallest frq to the second-smallest + // frq, and remove the smallest frq value from fHeap. + // + + int mm = fHeap[0] - frq; + std::pop_heap(&fHeap[0], &fHeap[nf], FHeapCompare()); + --nf; + + int m = fHeap[0] - frq; + std::pop_heap(&fHeap[0], &fHeap[nf], FHeapCompare()); + + frq[m] += frq[mm]; + std::push_heap(&fHeap[0], &fHeap[nf], FHeapCompare()); + + // + // The entries in scode are linked into lists with the + // entries in hlink serving as "next" pointers and with + // the end of a list marked by hlink[j] == j. + // + // Traverse the lists that start at scode[m] and scode[mm]. + // For each element visited, increment the length of the + // corresponding code by one bit. (If we visit scode[j] + // during the traversal, then the code for symbol j becomes + // one bit longer.) + // + // Merge the lists that start at scode[m] and scode[mm] + // into a single list that starts at scode[m]. + // + + // + // Add a bit to all codes in the first list. + // + + for (int j = m;; j = hlink[j]) { + scode[j]++; + + assert(scode[j] <= 58); + + if (hlink[j] == j) { + // + // Merge the two lists. + // + + hlink[j] = mm; + break; + } + } + + // + // Add a bit to all codes in the second list + // + + for (int j = mm;; j = hlink[j]) { + scode[j]++; + + assert(scode[j] <= 58); + + if (hlink[j] == j) break; + } + } + + // + // Build a canonical Huffman code table, replacing the code + // lengths in scode with (code, code length) pairs. Copy the + // code table from scode into frq. + // + + hufCanonicalCodeTable(scode.data()); + memcpy(frq, scode.data(), sizeof(long long) * HUF_ENCSIZE); +} + +// +// Pack an encoding table: +// - only code lengths, not actual codes, are stored +// - runs of zeroes are compressed as follows: +// +// unpacked packed +// -------------------------------- +// 1 zero 0 (6 bits) +// 2 zeroes 59 +// 3 zeroes 60 +// 4 zeroes 61 +// 5 zeroes 62 +// n zeroes (6 or more) 63 n-6 (6 + 8 bits) +// + +const int SHORT_ZEROCODE_RUN = 59; +const int LONG_ZEROCODE_RUN = 63; +const int SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN; +const int LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN; + +static void hufPackEncTable( + const long long *hcode, // i : encoding table [HUF_ENCSIZE] + int im, // i : min hcode index + int iM, // i : max hcode index + char **pcode) // o: ptr to packed table (updated) +{ + char *p = *pcode; + long long c = 0; + int lc = 0; + + for (; im <= iM; im++) { + int l = hufLength(hcode[im]); + + if (l == 0) { + int zerun = 1; + + while ((im < iM) && (zerun < LONGEST_LONG_RUN)) { + if (hufLength(hcode[im + 1]) > 0) break; + im++; + zerun++; + } + + if (zerun >= 2) { + if (zerun >= SHORTEST_LONG_RUN) { + outputBits(6, LONG_ZEROCODE_RUN, c, lc, p); + outputBits(8, zerun - SHORTEST_LONG_RUN, c, lc, p); + } else { + outputBits(6, SHORT_ZEROCODE_RUN + zerun - 2, c, lc, p); + } + continue; + } + } + + outputBits(6, l, c, lc, p); + } + + if (lc > 0) *p++ = (unsigned char)(c << (8 - lc)); + + *pcode = p; +} + +// +// Unpack an encoding table packed by hufPackEncTable(): +// + +static bool hufUnpackEncTable( + const char **pcode, // io: ptr to packed table (updated) + int ni, // i : input size (in bytes) + int im, // i : min hcode index + int iM, // i : max hcode index + long long *hcode) // o: encoding table [HUF_ENCSIZE] +{ + memset(hcode, 0, sizeof(long long) * HUF_ENCSIZE); + + const char *p = *pcode; + long long c = 0; + int lc = 0; + + for (; im <= iM; im++) { + if (p - *pcode >= ni) { + return false; + } + + long long l = hcode[im] = getBits(6, c, lc, p); // code length + + if (l == (long long)LONG_ZEROCODE_RUN) { + if (p - *pcode > ni) { + return false; + } + + int zerun = getBits(8, c, lc, p) + SHORTEST_LONG_RUN; + + if (im + zerun > iM + 1) { + return false; + } + + while (zerun--) hcode[im++] = 0; + + im--; + } else if (l >= (long long)SHORT_ZEROCODE_RUN) { + int zerun = l - SHORT_ZEROCODE_RUN + 2; + + if (im + zerun > iM + 1) { + return false; + } + + while (zerun--) hcode[im++] = 0; + + im--; + } + } + + *pcode = const_cast(p); + + hufCanonicalCodeTable(hcode); + + return true; +} + +// +// DECODING TABLE BUILDING +// + +// +// Clear a newly allocated decoding table so that it contains only zeroes. +// + +static void hufClearDecTable(HufDec *hdecod) // io: (allocated by caller) +// decoding table [HUF_DECSIZE] +{ + for (int i = 0; i < HUF_DECSIZE; i++) { + hdecod[i].len = 0; + hdecod[i].lit = 0; + hdecod[i].p = NULL; + } + // memset(hdecod, 0, sizeof(HufDec) * HUF_DECSIZE); +} + +// +// Build a decoding hash table based on the encoding table hcode: +// - short codes (<= HUF_DECBITS) are resolved with a single table access; +// - long code entry allocations are not optimized, because long codes are +// unfrequent; +// - decoding tables are used by hufDecode(); +// + +static bool hufBuildDecTable(const long long *hcode, // i : encoding table + int im, // i : min index in hcode + int iM, // i : max index in hcode + HufDec *hdecod) // o: (allocated by caller) +// decoding table [HUF_DECSIZE] +{ + // + // Init hashtable & loop on all codes. + // Assumes that hufClearDecTable(hdecod) has already been called. + // + + for (; im <= iM; im++) { + long long c = hufCode(hcode[im]); + int l = hufLength(hcode[im]); + + if (c >> l) { + // + // Error: c is supposed to be an l-bit code, + // but c contains a value that is greater + // than the largest l-bit number. + // + + // invalidTableEntry(); + return false; + } + + if (l > HUF_DECBITS) { + // + // Long code: add a secondary entry + // + + HufDec *pl = hdecod + (c >> (l - HUF_DECBITS)); + + if (pl->len) { + // + // Error: a short code has already + // been stored in table entry *pl. + // + + // invalidTableEntry(); + return false; + } + + pl->lit++; + + if (pl->p) { + unsigned int *p = pl->p; + pl->p = new unsigned int[pl->lit]; + + for (int i = 0; i < pl->lit - 1; ++i) pl->p[i] = p[i]; + + delete[] p; + } else { + pl->p = new unsigned int[1]; + } + + pl->p[pl->lit - 1] = im; + } else if (l) { + // + // Short code: init all primary entries + // + + HufDec *pl = hdecod + (c << (HUF_DECBITS - l)); + + for (long long i = 1ULL << (HUF_DECBITS - l); i > 0; i--, pl++) { + if (pl->len || pl->p) { + // + // Error: a short code or a long code has + // already been stored in table entry *pl. + // + + // invalidTableEntry(); + return false; + } + + pl->len = l; + pl->lit = im; + } + } + } + + return true; +} + +// +// Free the long code entries of a decoding table built by hufBuildDecTable() +// + +static void hufFreeDecTable(HufDec *hdecod) // io: Decoding table +{ + for (int i = 0; i < HUF_DECSIZE; i++) { + if (hdecod[i].p) { + delete[] hdecod[i].p; + hdecod[i].p = 0; + } + } +} + +// +// ENCODING +// + +inline void outputCode(long long code, long long &c, int &lc, char *&out) { + outputBits(hufLength(code), hufCode(code), c, lc, out); +} + +inline void sendCode(long long sCode, int runCount, long long runCode, + long long &c, int &lc, char *&out) { + // + // Output a run of runCount instances of the symbol sCount. + // Output the symbols explicitly, or if that is shorter, output + // the sCode symbol once followed by a runCode symbol and runCount + // expressed as an 8-bit number. + // + + if (hufLength(sCode) + hufLength(runCode) + 8 < hufLength(sCode) * runCount) { + outputCode(sCode, c, lc, out); + outputCode(runCode, c, lc, out); + outputBits(8, runCount, c, lc, out); + } else { + while (runCount-- >= 0) outputCode(sCode, c, lc, out); + } +} + +// +// Encode (compress) ni values based on the Huffman encoding table hcode: +// + +static int hufEncode // return: output size (in bits) + (const long long *hcode, // i : encoding table + const unsigned short *in, // i : uncompressed input buffer + const int ni, // i : input buffer size (in bytes) + int rlc, // i : rl code + char *out) // o: compressed output buffer +{ + char *outStart = out; + long long c = 0; // bits not yet written to out + int lc = 0; // number of valid bits in c (LSB) + int s = in[0]; + int cs = 0; + + // + // Loop on input values + // + + for (int i = 1; i < ni; i++) { + // + // Count same values or send code + // + + if (s == in[i] && cs < 255) { + cs++; + } else { + sendCode(hcode[s], cs, hcode[rlc], c, lc, out); + cs = 0; + } + + s = in[i]; + } + + // + // Send remaining code + // + + sendCode(hcode[s], cs, hcode[rlc], c, lc, out); + + if (lc) *out = (c << (8 - lc)) & 0xff; + + return (out - outStart) * 8 + lc; +} + +// +// DECODING +// + +// +// In order to force the compiler to inline them, +// getChar() and getCode() are implemented as macros +// instead of "inline" functions. +// + +#define getChar(c, lc, in) \ + { \ + c = (c << 8) | *(unsigned char *)(in++); \ + lc += 8; \ + } + +#if 0 +#define getCode(po, rlc, c, lc, in, out, ob, oe) \ + { \ + if (po == rlc) { \ + if (lc < 8) getChar(c, lc, in); \ + \ + lc -= 8; \ + \ + unsigned char cs = (c >> lc); \ + \ + if (out + cs > oe) return false; \ + \ + /* TinyEXR issue 78 */ \ + unsigned short s = out[-1]; \ + \ + while (cs-- > 0) *out++ = s; \ + } else if (out < oe) { \ + *out++ = po; \ + } else { \ + return false; \ + } \ + } +#else +static bool getCode(int po, int rlc, long long &c, int &lc, const char *&in, + const char *in_end, unsigned short *&out, + const unsigned short *ob, const unsigned short *oe) { + (void)ob; + if (po == rlc) { + if (lc < 8) { + /* TinyEXR issue 78 */ + /* TinyEXR issue 160. in + 1 -> in */ + if (in >= in_end) { + return false; + } + + getChar(c, lc, in); + } + + lc -= 8; + + unsigned char cs = (c >> lc); + + if (out + cs > oe) return false; + + // Bounds check for safety + // Issue 100. + if ((out - 1) < ob) return false; + unsigned short s = out[-1]; + + while (cs-- > 0) *out++ = s; + } else if (out < oe) { + *out++ = po; + } else { + return false; + } + return true; +} +#endif + +// +// Decode (uncompress) ni bits based on encoding & decoding tables: +// + +static bool hufDecode(const long long *hcode, // i : encoding table + const HufDec *hdecod, // i : decoding table + const char *in, // i : compressed input buffer + int ni, // i : input size (in bits) + int rlc, // i : run-length code + int no, // i : expected output size (in bytes) + unsigned short *out) // o: uncompressed output buffer +{ + long long c = 0; + int lc = 0; + unsigned short *outb = out; // begin + unsigned short *oe = out + no; // end + const char *ie = in + (ni + 7) / 8; // input byte size + + // + // Loop on input bytes + // + + while (in < ie) { + getChar(c, lc, in); + + // + // Access decoding table + // + + while (lc >= HUF_DECBITS) { + const HufDec pl = hdecod[(c >> (lc - HUF_DECBITS)) & HUF_DECMASK]; + + if (pl.len) { + // + // Get short code + // + + lc -= pl.len; + // std::cout << "lit = " << pl.lit << std::endl; + // std::cout << "rlc = " << rlc << std::endl; + // std::cout << "c = " << c << std::endl; + // std::cout << "lc = " << lc << std::endl; + // std::cout << "in = " << in << std::endl; + // std::cout << "out = " << out << std::endl; + // std::cout << "oe = " << oe << std::endl; + if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) { + return false; + } + } else { + if (!pl.p) { + return false; + } + // invalidCode(); // wrong code + + // + // Search long code + // + + int j; + + for (j = 0; j < pl.lit; j++) { + int l = hufLength(hcode[pl.p[j]]); + + while (lc < l && in < ie) // get more bits + getChar(c, lc, in); + + if (lc >= l) { + if (hufCode(hcode[pl.p[j]]) == + ((c >> (lc - l)) & (((long long)(1) << l) - 1))) { + // + // Found : get long code + // + + lc -= l; + if (!getCode(pl.p[j], rlc, c, lc, in, ie, out, outb, oe)) { + return false; + } + break; + } + } + } + + if (j == pl.lit) { + return false; + // invalidCode(); // Not found + } + } + } + } + + // + // Get remaining (short) codes + // + + int i = (8 - ni) & 7; + c >>= i; + lc -= i; + + while (lc > 0) { + const HufDec pl = hdecod[(c << (HUF_DECBITS - lc)) & HUF_DECMASK]; + + if (pl.len) { + lc -= pl.len; + if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) { + return false; + } + } else { + return false; + // invalidCode(); // wrong (long) code + } + } + + if (out - outb != no) { + return false; + } + // notEnoughData (); + + return true; +} + +static void countFrequencies(std::vector &freq, + const unsigned short data[/*n*/], int n) { + for (int i = 0; i < HUF_ENCSIZE; ++i) freq[i] = 0; + + for (int i = 0; i < n; ++i) ++freq[data[i]]; +} + +static void writeUInt(char buf[4], unsigned int i) { + unsigned char *b = (unsigned char *)buf; + + b[0] = i; + b[1] = i >> 8; + b[2] = i >> 16; + b[3] = i >> 24; +} + +static unsigned int readUInt(const char buf[4]) { + const unsigned char *b = (const unsigned char *)buf; + + return (b[0] & 0x000000ff) | ((b[1] << 8) & 0x0000ff00) | + ((b[2] << 16) & 0x00ff0000) | ((b[3] << 24) & 0xff000000); +} + +// +// EXTERNAL INTERFACE +// + +static int hufCompress(const unsigned short raw[], int nRaw, + char compressed[]) { + if (nRaw == 0) return 0; + + std::vector freq(HUF_ENCSIZE); + + countFrequencies(freq, raw, nRaw); + + int im = 0; + int iM = 0; + hufBuildEncTable(freq.data(), &im, &iM); + + char *tableStart = compressed + 20; + char *tableEnd = tableStart; + hufPackEncTable(freq.data(), im, iM, &tableEnd); + int tableLength = tableEnd - tableStart; + + char *dataStart = tableEnd; + int nBits = hufEncode(freq.data(), raw, nRaw, iM, dataStart); + int data_length = (nBits + 7) / 8; + + writeUInt(compressed, im); + writeUInt(compressed + 4, iM); + writeUInt(compressed + 8, tableLength); + writeUInt(compressed + 12, nBits); + writeUInt(compressed + 16, 0); // room for future extensions + + return dataStart + data_length - compressed; +} + +static bool hufUncompress(const char compressed[], int nCompressed, + std::vector *raw) { + if (nCompressed == 0) { + if (raw->size() != 0) return false; + + return false; + } + + int im = readUInt(compressed); + int iM = readUInt(compressed + 4); + // int tableLength = readUInt (compressed + 8); + int nBits = readUInt(compressed + 12); + + if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE) return false; + + const char *ptr = compressed + 20; + + // + // Fast decoder needs at least 2x64-bits of compressed data, and + // needs to be run-able on this platform. Otherwise, fall back + // to the original decoder + // + + // if (FastHufDecoder::enabled() && nBits > 128) + //{ + // FastHufDecoder fhd (ptr, nCompressed - (ptr - compressed), im, iM, iM); + // fhd.decode ((unsigned char*)ptr, nBits, raw, nRaw); + //} + // else + { + std::vector freq(HUF_ENCSIZE); + std::vector hdec(HUF_DECSIZE); + + hufClearDecTable(&hdec.at(0)); + + hufUnpackEncTable(&ptr, nCompressed - (ptr - compressed), im, iM, + &freq.at(0)); + + { + if (nBits > 8 * (nCompressed - (ptr - compressed))) { + return false; + } + + hufBuildDecTable(&freq.at(0), im, iM, &hdec.at(0)); + hufDecode(&freq.at(0), &hdec.at(0), ptr, nBits, iM, raw->size(), + raw->data()); + } + // catch (...) + //{ + // hufFreeDecTable (hdec); + // throw; + //} + + hufFreeDecTable(&hdec.at(0)); + } + + return true; +} + +// +// Functions to compress the range of values in the pixel data +// + +const int USHORT_RANGE = (1 << 16); +const int BITMAP_SIZE = (USHORT_RANGE >> 3); + +static void bitmapFromData(const unsigned short data[/*nData*/], int nData, + unsigned char bitmap[BITMAP_SIZE], + unsigned short &minNonZero, + unsigned short &maxNonZero) { + for (int i = 0; i < BITMAP_SIZE; ++i) bitmap[i] = 0; + + for (int i = 0; i < nData; ++i) bitmap[data[i] >> 3] |= (1 << (data[i] & 7)); + + bitmap[0] &= ~1; // zero is not explicitly stored in + // the bitmap; we assume that the + // data always contain zeroes + minNonZero = BITMAP_SIZE - 1; + maxNonZero = 0; + + for (int i = 0; i < BITMAP_SIZE; ++i) { + if (bitmap[i]) { + if (minNonZero > i) minNonZero = i; + if (maxNonZero < i) maxNonZero = i; + } + } +} + +static unsigned short forwardLutFromBitmap( + const unsigned char bitmap[BITMAP_SIZE], unsigned short lut[USHORT_RANGE]) { + int k = 0; + + for (int i = 0; i < USHORT_RANGE; ++i) { + if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7)))) + lut[i] = k++; + else + lut[i] = 0; + } + + return k - 1; // maximum value stored in lut[], +} // i.e. number of ones in bitmap minus 1 + +static unsigned short reverseLutFromBitmap( + const unsigned char bitmap[BITMAP_SIZE], unsigned short lut[USHORT_RANGE]) { + int k = 0; + + for (int i = 0; i < USHORT_RANGE; ++i) { + if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7)))) lut[k++] = i; + } + + int n = k - 1; + + while (k < USHORT_RANGE) lut[k++] = 0; + + return n; // maximum k where lut[k] is non-zero, +} // i.e. number of ones in bitmap minus 1 + +static void applyLut(const unsigned short lut[USHORT_RANGE], + unsigned short data[/*nData*/], int nData) { + for (int i = 0; i < nData; ++i) data[i] = lut[data[i]]; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +static bool CompressPiz(unsigned char *outPtr, unsigned int *outSize, + const unsigned char *inPtr, size_t inSize, + const std::vector &channelInfo, + int data_width, int num_lines) { + std::vector bitmap(BITMAP_SIZE); + unsigned short minNonZero; + unsigned short maxNonZero; + +#if !TINYEXR_LITTLE_ENDIAN + // @todo { PIZ compression on BigEndian architecture. } + assert(0); + return false; +#endif + + // Assume `inSize` is multiple of 2 or 4. + std::vector tmpBuffer(inSize / sizeof(unsigned short)); + + std::vector channelData(channelInfo.size()); + unsigned short *tmpBufferEnd = &tmpBuffer.at(0); + + for (size_t c = 0; c < channelData.size(); c++) { + PIZChannelData &cd = channelData[c]; + + cd.start = tmpBufferEnd; + cd.end = cd.start; + + cd.nx = data_width; + cd.ny = num_lines; + // cd.ys = c.channel().ySampling; + + size_t pixelSize = sizeof(int); // UINT and FLOAT + if (channelInfo[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) { + pixelSize = sizeof(short); + } + + cd.size = static_cast(pixelSize / sizeof(short)); + + tmpBufferEnd += cd.nx * cd.ny * cd.size; + } + + const unsigned char *ptr = inPtr; + for (int y = 0; y < num_lines; ++y) { + for (size_t i = 0; i < channelData.size(); ++i) { + PIZChannelData &cd = channelData[i]; + + // if (modp (y, cd.ys) != 0) + // continue; + + size_t n = static_cast(cd.nx * cd.size); + memcpy(cd.end, ptr, n * sizeof(unsigned short)); + ptr += n * sizeof(unsigned short); + cd.end += n; + } + } + + bitmapFromData(&tmpBuffer.at(0), static_cast(tmpBuffer.size()), + bitmap.data(), minNonZero, maxNonZero); + + std::vector lut(USHORT_RANGE); + unsigned short maxValue = forwardLutFromBitmap(bitmap.data(), lut.data()); + applyLut(lut.data(), &tmpBuffer.at(0), static_cast(tmpBuffer.size())); + + // + // Store range compression info in _outBuffer + // + + char *buf = reinterpret_cast(outPtr); + + memcpy(buf, &minNonZero, sizeof(unsigned short)); + buf += sizeof(unsigned short); + memcpy(buf, &maxNonZero, sizeof(unsigned short)); + buf += sizeof(unsigned short); + + if (minNonZero <= maxNonZero) { + memcpy(buf, reinterpret_cast(&bitmap[0] + minNonZero), + maxNonZero - minNonZero + 1); + buf += maxNonZero - minNonZero + 1; + } + + // + // Apply wavelet encoding + // + + for (size_t i = 0; i < channelData.size(); ++i) { + PIZChannelData &cd = channelData[i]; + + for (int j = 0; j < cd.size; ++j) { + wav2Encode(cd.start + j, cd.nx, cd.size, cd.ny, cd.nx * cd.size, + maxValue); + } + } + + // + // Apply Huffman encoding; append the result to _outBuffer + // + + // length header(4byte), then huff data. Initialize length header with zero, + // then later fill it by `length`. + char *lengthPtr = buf; + int zero = 0; + memcpy(buf, &zero, sizeof(int)); + buf += sizeof(int); + + int length = + hufCompress(&tmpBuffer.at(0), static_cast(tmpBuffer.size()), buf); + memcpy(lengthPtr, &length, sizeof(int)); + + (*outSize) = static_cast( + (reinterpret_cast(buf) - outPtr) + + static_cast(length)); + + // Use uncompressed data when compressed data is larger than uncompressed. + // (Issue 40) + if ((*outSize) >= inSize) { + (*outSize) = static_cast(inSize); + memcpy(outPtr, inPtr, inSize); + } + return true; +} + +static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, + size_t tmpBufSizeInBytes, size_t inLen, int num_channels, + const EXRChannelInfo *channels, int data_width, + int num_lines) { + if (inLen == tmpBufSizeInBytes) { + // Data is not compressed(Issue 40). + memcpy(outPtr, inPtr, inLen); + return true; + } + + std::vector bitmap(BITMAP_SIZE); + unsigned short minNonZero; + unsigned short maxNonZero; + +#if !TINYEXR_LITTLE_ENDIAN + // @todo { PIZ compression on BigEndian architecture. } + assert(0); + return false; +#endif + + memset(bitmap.data(), 0, BITMAP_SIZE); + + const unsigned char *ptr = inPtr; + // minNonZero = *(reinterpret_cast(ptr)); + tinyexr::cpy2(&minNonZero, reinterpret_cast(ptr)); + // maxNonZero = *(reinterpret_cast(ptr + 2)); + tinyexr::cpy2(&maxNonZero, reinterpret_cast(ptr + 2)); + ptr += 4; + + if (maxNonZero >= BITMAP_SIZE) { + return false; + } + + if (minNonZero <= maxNonZero) { + memcpy(reinterpret_cast(&bitmap[0] + minNonZero), ptr, + maxNonZero - minNonZero + 1); + ptr += maxNonZero - minNonZero + 1; + } + + std::vector lut(USHORT_RANGE); + memset(lut.data(), 0, sizeof(unsigned short) * USHORT_RANGE); + unsigned short maxValue = reverseLutFromBitmap(bitmap.data(), lut.data()); + + // + // Huffman decoding + // + + int length; + + // length = *(reinterpret_cast(ptr)); + tinyexr::cpy4(&length, reinterpret_cast(ptr)); + ptr += sizeof(int); + + if (size_t((ptr - inPtr) + length) > inLen) { + return false; + } + + std::vector tmpBuffer(tmpBufSizeInBytes / sizeof(unsigned short)); + hufUncompress(reinterpret_cast(ptr), length, &tmpBuffer); + + // + // Wavelet decoding + // + + std::vector channelData(static_cast(num_channels)); + + unsigned short *tmpBufferEnd = &tmpBuffer.at(0); + + for (size_t i = 0; i < static_cast(num_channels); ++i) { + const EXRChannelInfo &chan = channels[i]; + + size_t pixelSize = sizeof(int); // UINT and FLOAT + if (chan.pixel_type == TINYEXR_PIXELTYPE_HALF) { + pixelSize = sizeof(short); + } + + channelData[i].start = tmpBufferEnd; + channelData[i].end = channelData[i].start; + channelData[i].nx = data_width; + channelData[i].ny = num_lines; + // channelData[i].ys = 1; + channelData[i].size = static_cast(pixelSize / sizeof(short)); + + tmpBufferEnd += channelData[i].nx * channelData[i].ny * channelData[i].size; + } + + for (size_t i = 0; i < channelData.size(); ++i) { + PIZChannelData &cd = channelData[i]; + + for (int j = 0; j < cd.size; ++j) { + wav2Decode(cd.start + j, cd.nx, cd.size, cd.ny, cd.nx * cd.size, + maxValue); + } + } + + // + // Expand the pixel data to their original range + // + + applyLut(lut.data(), &tmpBuffer.at(0), static_cast(tmpBufSizeInBytes / sizeof(unsigned short))); + + for (int y = 0; y < num_lines; y++) { + for (size_t i = 0; i < channelData.size(); ++i) { + PIZChannelData &cd = channelData[i]; + + // if (modp (y, cd.ys) != 0) + // continue; + + size_t n = static_cast(cd.nx * cd.size); + memcpy(outPtr, cd.end, static_cast(n * sizeof(unsigned short))); + outPtr += n * sizeof(unsigned short); + cd.end += n; + } + } + + return true; +} +#endif // TINYEXR_USE_PIZ + +#if TINYEXR_USE_ZFP + +struct ZFPCompressionParam { + double rate; + unsigned int precision; + unsigned int __pad0; + double tolerance; + int type; // TINYEXR_ZFP_COMPRESSIONTYPE_* + unsigned int __pad1; + + ZFPCompressionParam() { + type = TINYEXR_ZFP_COMPRESSIONTYPE_RATE; + rate = 2.0; + precision = 0; + tolerance = 0.0; + } +}; + +static bool FindZFPCompressionParam(ZFPCompressionParam *param, + const EXRAttribute *attributes, + int num_attributes, std::string *err) { + bool foundType = false; + + for (int i = 0; i < num_attributes; i++) { + if ((strcmp(attributes[i].name, "zfpCompressionType") == 0)) { + if (attributes[i].size == 1) { + param->type = static_cast(attributes[i].value[0]); + foundType = true; + break; + } else { + if (err) { + (*err) += + "zfpCompressionType attribute must be uchar(1 byte) type.\n"; + } + return false; + } + } + } + + if (!foundType) { + if (err) { + (*err) += "`zfpCompressionType` attribute not found.\n"; + } + return false; + } + + if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) { + for (int i = 0; i < num_attributes; i++) { + if ((strcmp(attributes[i].name, "zfpCompressionRate") == 0) && + (attributes[i].size == 8)) { + param->rate = *(reinterpret_cast(attributes[i].value)); + return true; + } + } + + if (err) { + (*err) += "`zfpCompressionRate` attribute not found.\n"; + } + + } else if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) { + for (int i = 0; i < num_attributes; i++) { + if ((strcmp(attributes[i].name, "zfpCompressionPrecision") == 0) && + (attributes[i].size == 4)) { + param->rate = *(reinterpret_cast(attributes[i].value)); + return true; + } + } + + if (err) { + (*err) += "`zfpCompressionPrecision` attribute not found.\n"; + } + + } else if (param->type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) { + for (int i = 0; i < num_attributes; i++) { + if ((strcmp(attributes[i].name, "zfpCompressionTolerance") == 0) && + (attributes[i].size == 8)) { + param->tolerance = *(reinterpret_cast(attributes[i].value)); + return true; + } + } + + if (err) { + (*err) += "`zfpCompressionTolerance` attribute not found.\n"; + } + } else { + if (err) { + (*err) += "Unknown value specified for `zfpCompressionType`.\n"; + } + } + + return false; +} + +// Assume pixel format is FLOAT for all channels. +static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines, + size_t num_channels, const unsigned char *src, + unsigned long src_size, + const ZFPCompressionParam ¶m) { + size_t uncompressed_size = + size_t(dst_width) * size_t(dst_num_lines) * num_channels; + + if (uncompressed_size == src_size) { + // Data is not compressed(Issue 40). + memcpy(dst, src, src_size); + } + + zfp_stream *zfp = NULL; + zfp_field *field = NULL; + + assert((dst_width % 4) == 0); + assert((dst_num_lines % 4) == 0); + + if ((size_t(dst_width) & 3U) || (size_t(dst_num_lines) & 3U)) { + return false; + } + + field = + zfp_field_2d(reinterpret_cast(const_cast(src)), + zfp_type_float, static_cast(dst_width), + static_cast(dst_num_lines) * + static_cast(num_channels)); + zfp = zfp_stream_open(NULL); + + if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) { + zfp_stream_set_rate(zfp, param.rate, zfp_type_float, /* dimension */ 2, + /* write random access */ 0); + } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) { + zfp_stream_set_precision(zfp, param.precision); + } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) { + zfp_stream_set_accuracy(zfp, param.tolerance); + } else { + assert(0); + } + + size_t buf_size = zfp_stream_maximum_size(zfp, field); + std::vector buf(buf_size); + memcpy(&buf.at(0), src, src_size); + + bitstream *stream = stream_open(&buf.at(0), buf_size); + zfp_stream_set_bit_stream(zfp, stream); + zfp_stream_rewind(zfp); + + size_t image_size = size_t(dst_width) * size_t(dst_num_lines); + + for (size_t c = 0; c < size_t(num_channels); c++) { + // decompress 4x4 pixel block. + for (size_t y = 0; y < size_t(dst_num_lines); y += 4) { + for (size_t x = 0; x < size_t(dst_width); x += 4) { + float fblock[16]; + zfp_decode_block_float_2(zfp, fblock); + for (size_t j = 0; j < 4; j++) { + for (size_t i = 0; i < 4; i++) { + dst[c * image_size + ((y + j) * size_t(dst_width) + (x + i))] = + fblock[j * 4 + i]; + } + } + } + } + } + + zfp_field_free(field); + zfp_stream_close(zfp); + stream_close(stream); + + return true; +} + +// Assume pixel format is FLOAT for all channels. +static bool CompressZfp(std::vector *outBuf, + unsigned int *outSize, const float *inPtr, int width, + int num_lines, int num_channels, + const ZFPCompressionParam ¶m) { + zfp_stream *zfp = NULL; + zfp_field *field = NULL; + + assert((width % 4) == 0); + assert((num_lines % 4) == 0); + + if ((size_t(width) & 3U) || (size_t(num_lines) & 3U)) { + return false; + } + + // create input array. + field = zfp_field_2d(reinterpret_cast(const_cast(inPtr)), + zfp_type_float, static_cast(width), + static_cast(num_lines * num_channels)); + + zfp = zfp_stream_open(NULL); + + if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_RATE) { + zfp_stream_set_rate(zfp, param.rate, zfp_type_float, 2, 0); + } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_PRECISION) { + zfp_stream_set_precision(zfp, param.precision); + } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) { + zfp_stream_set_accuracy(zfp, param.tolerance); + } else { + assert(0); + } + + size_t buf_size = zfp_stream_maximum_size(zfp, field); + + outBuf->resize(buf_size); + + bitstream *stream = stream_open(&outBuf->at(0), buf_size); + zfp_stream_set_bit_stream(zfp, stream); + zfp_field_free(field); + + size_t image_size = size_t(width) * size_t(num_lines); + + for (size_t c = 0; c < size_t(num_channels); c++) { + // compress 4x4 pixel block. + for (size_t y = 0; y < size_t(num_lines); y += 4) { + for (size_t x = 0; x < size_t(width); x += 4) { + float fblock[16]; + for (size_t j = 0; j < 4; j++) { + for (size_t i = 0; i < 4; i++) { + fblock[j * 4 + i] = + inPtr[c * image_size + ((y + j) * size_t(width) + (x + i))]; + } + } + zfp_encode_block_float_2(zfp, fblock); + } + } + } + + zfp_stream_flush(zfp); + (*outSize) = static_cast(zfp_stream_compressed_size(zfp)); + + zfp_stream_close(zfp); + + return true; +} + +#endif + +// +// ----------------------------------------------------------------- +// + +// heuristics +#define TINYEXR_DIMENSION_THRESHOLD (1024 * 8192) + +// TODO(syoyo): Refactor function arguments. +static bool DecodePixelData(/* out */ unsigned char **out_images, + const int *requested_pixel_types, + const unsigned char *data_ptr, size_t data_len, + int compression_type, int line_order, int width, + int height, int x_stride, int y, int line_no, + int num_lines, size_t pixel_data_size, + size_t num_attributes, + const EXRAttribute *attributes, size_t num_channels, + const EXRChannelInfo *channels, + const std::vector &channel_offset_list) { + if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { // PIZ +#if TINYEXR_USE_PIZ + if ((width == 0) || (num_lines == 0) || (pixel_data_size == 0)) { + // Invalid input #90 + return false; + } + + // Allocate original data size. + std::vector outBuf(static_cast( + static_cast(width * num_lines) * pixel_data_size)); + size_t tmpBufLen = outBuf.size(); + + bool ret = tinyexr::DecompressPiz( + reinterpret_cast(&outBuf.at(0)), data_ptr, tmpBufLen, + data_len, static_cast(num_channels), channels, width, num_lines); + + if (!ret) { + return false; + } + + // For PIZ_COMPRESSION: + // pixel sample data for channel 0 for scanline 0 + // pixel sample data for channel 1 for scanline 0 + // pixel sample data for channel ... for scanline 0 + // pixel sample data for channel n for scanline 0 + // pixel sample data for channel 0 for scanline 1 + // pixel sample data for channel 1 for scanline 1 + // pixel sample data for channel ... for scanline 1 + // pixel sample data for channel n for scanline 1 + // ... + for (size_t c = 0; c < static_cast(num_channels); c++) { + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { + for (size_t v = 0; v < static_cast(num_lines); v++) { + const unsigned short *line_ptr = reinterpret_cast( + &outBuf.at(v * pixel_data_size * static_cast(width) + + channel_offset_list[c] * static_cast(width))); + for (size_t u = 0; u < static_cast(width); u++) { + FP16 hf; + + // hf.u = line_ptr[u]; + // use `cpy` to avoid unaligned memory access when compiler's + // optimization is on. + tinyexr::cpy2(&(hf.u), line_ptr + u); + + tinyexr::swap2(reinterpret_cast(&hf.u)); + + if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { + unsigned short *image = + reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += static_cast( + (height - 1 - (line_no + static_cast(v)))) * + static_cast(x_stride) + + u; + } + *image = hf.u; + } else { // HALF -> FLOAT + FP32 f32 = half_to_float(hf); + float *image = reinterpret_cast(out_images)[c]; + size_t offset = 0; + if (line_order == 0) { + offset = (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + offset = static_cast( + (height - 1 - (line_no + static_cast(v)))) * + static_cast(x_stride) + + u; + } + image += offset; + *image = f32.f; + } + } + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { + assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT); + + for (size_t v = 0; v < static_cast(num_lines); v++) { + const unsigned int *line_ptr = reinterpret_cast( + &outBuf.at(v * pixel_data_size * static_cast(width) + + channel_offset_list[c] * static_cast(width))); + for (size_t u = 0; u < static_cast(width); u++) { + unsigned int val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); + + tinyexr::swap4(&val); + + unsigned int *image = + reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += static_cast( + (height - 1 - (line_no + static_cast(v)))) * + static_cast(x_stride) + + u; + } + *image = val; + } + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT); + for (size_t v = 0; v < static_cast(num_lines); v++) { + const float *line_ptr = reinterpret_cast(&outBuf.at( + v * pixel_data_size * static_cast(x_stride) + + channel_offset_list[c] * static_cast(x_stride))); + for (size_t u = 0; u < static_cast(width); u++) { + float val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); + + tinyexr::swap4(reinterpret_cast(&val)); + + float *image = reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += static_cast( + (height - 1 - (line_no + static_cast(v)))) * + static_cast(x_stride) + + u; + } + *image = val; + } + } + } else { + assert(0); + } + } +#else + assert(0 && "PIZ is enabled in this build"); + return false; +#endif + + } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS || + compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) { + // Allocate original data size. + std::vector outBuf(static_cast(width) * + static_cast(num_lines) * + pixel_data_size); + + unsigned long dstLen = static_cast(outBuf.size()); + assert(dstLen > 0); + if (!tinyexr::DecompressZip( + reinterpret_cast(&outBuf.at(0)), &dstLen, data_ptr, + static_cast(data_len))) { + return false; + } + + // For ZIP_COMPRESSION: + // pixel sample data for channel 0 for scanline 0 + // pixel sample data for channel 1 for scanline 0 + // pixel sample data for channel ... for scanline 0 + // pixel sample data for channel n for scanline 0 + // pixel sample data for channel 0 for scanline 1 + // pixel sample data for channel 1 for scanline 1 + // pixel sample data for channel ... for scanline 1 + // pixel sample data for channel n for scanline 1 + // ... + for (size_t c = 0; c < static_cast(num_channels); c++) { + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { + for (size_t v = 0; v < static_cast(num_lines); v++) { + const unsigned short *line_ptr = reinterpret_cast( + &outBuf.at(v * static_cast(pixel_data_size) * + static_cast(width) + + channel_offset_list[c] * static_cast(width))); + for (size_t u = 0; u < static_cast(width); u++) { + tinyexr::FP16 hf; + + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); + + tinyexr::swap2(reinterpret_cast(&hf.u)); + + if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { + unsigned short *image = + reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += (static_cast(height) - 1U - + (static_cast(line_no) + v)) * + static_cast(x_stride) + + u; + } + *image = hf.u; + } else { // HALF -> FLOAT + tinyexr::FP32 f32 = half_to_float(hf); + float *image = reinterpret_cast(out_images)[c]; + size_t offset = 0; + if (line_order == 0) { + offset = (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + offset = (static_cast(height) - 1U - + (static_cast(line_no) + v)) * + static_cast(x_stride) + + u; + } + image += offset; + + *image = f32.f; + } + } + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { + assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT); + + for (size_t v = 0; v < static_cast(num_lines); v++) { + const unsigned int *line_ptr = reinterpret_cast( + &outBuf.at(v * pixel_data_size * static_cast(width) + + channel_offset_list[c] * static_cast(width))); + for (size_t u = 0; u < static_cast(width); u++) { + unsigned int val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); + + tinyexr::swap4(&val); + + unsigned int *image = + reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += (static_cast(height) - 1U - + (static_cast(line_no) + v)) * + static_cast(x_stride) + + u; + } + *image = val; + } + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT); + for (size_t v = 0; v < static_cast(num_lines); v++) { + const float *line_ptr = reinterpret_cast( + &outBuf.at(v * pixel_data_size * static_cast(width) + + channel_offset_list[c] * static_cast(width))); + for (size_t u = 0; u < static_cast(width); u++) { + float val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); + + tinyexr::swap4(reinterpret_cast(&val)); + + float *image = reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += (static_cast(height) - 1U - + (static_cast(line_no) + v)) * + static_cast(x_stride) + + u; + } + *image = val; + } + } + } else { + assert(0); + return false; + } + } + } else if (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) { + // Allocate original data size. + std::vector outBuf(static_cast(width) * + static_cast(num_lines) * + pixel_data_size); + + unsigned long dstLen = static_cast(outBuf.size()); + if (dstLen == 0) { + return false; + } + + if (!tinyexr::DecompressRle( + reinterpret_cast(&outBuf.at(0)), dstLen, data_ptr, + static_cast(data_len))) { + return false; + } + + // For RLE_COMPRESSION: + // pixel sample data for channel 0 for scanline 0 + // pixel sample data for channel 1 for scanline 0 + // pixel sample data for channel ... for scanline 0 + // pixel sample data for channel n for scanline 0 + // pixel sample data for channel 0 for scanline 1 + // pixel sample data for channel 1 for scanline 1 + // pixel sample data for channel ... for scanline 1 + // pixel sample data for channel n for scanline 1 + // ... + for (size_t c = 0; c < static_cast(num_channels); c++) { + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { + for (size_t v = 0; v < static_cast(num_lines); v++) { + const unsigned short *line_ptr = reinterpret_cast( + &outBuf.at(v * static_cast(pixel_data_size) * + static_cast(width) + + channel_offset_list[c] * static_cast(width))); + for (size_t u = 0; u < static_cast(width); u++) { + tinyexr::FP16 hf; + + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); + + tinyexr::swap2(reinterpret_cast(&hf.u)); + + if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { + unsigned short *image = + reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += (static_cast(height) - 1U - + (static_cast(line_no) + v)) * + static_cast(x_stride) + + u; + } + *image = hf.u; + } else { // HALF -> FLOAT + tinyexr::FP32 f32 = half_to_float(hf); + float *image = reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += (static_cast(height) - 1U - + (static_cast(line_no) + v)) * + static_cast(x_stride) + + u; + } + *image = f32.f; + } + } + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { + assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT); + + for (size_t v = 0; v < static_cast(num_lines); v++) { + const unsigned int *line_ptr = reinterpret_cast( + &outBuf.at(v * pixel_data_size * static_cast(width) + + channel_offset_list[c] * static_cast(width))); + for (size_t u = 0; u < static_cast(width); u++) { + unsigned int val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); + + tinyexr::swap4(&val); + + unsigned int *image = + reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += (static_cast(height) - 1U - + (static_cast(line_no) + v)) * + static_cast(x_stride) + + u; + } + *image = val; + } + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT); + for (size_t v = 0; v < static_cast(num_lines); v++) { + const float *line_ptr = reinterpret_cast( + &outBuf.at(v * pixel_data_size * static_cast(width) + + channel_offset_list[c] * static_cast(width))); + for (size_t u = 0; u < static_cast(width); u++) { + float val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); + + tinyexr::swap4(reinterpret_cast(&val)); + + float *image = reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += (static_cast(height) - 1U - + (static_cast(line_no) + v)) * + static_cast(x_stride) + + u; + } + *image = val; + } + } + } else { + assert(0); + return false; + } + } + } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { +#if TINYEXR_USE_ZFP + tinyexr::ZFPCompressionParam zfp_compression_param; + std::string e; + if (!tinyexr::FindZFPCompressionParam(&zfp_compression_param, attributes, + int(num_attributes), &e)) { + // This code path should not be reachable. + assert(0); + return false; + } + + // Allocate original data size. + std::vector outBuf(static_cast(width) * + static_cast(num_lines) * + pixel_data_size); + + unsigned long dstLen = outBuf.size(); + assert(dstLen > 0); + tinyexr::DecompressZfp(reinterpret_cast(&outBuf.at(0)), width, + num_lines, num_channels, data_ptr, + static_cast(data_len), + zfp_compression_param); + + // For ZFP_COMPRESSION: + // pixel sample data for channel 0 for scanline 0 + // pixel sample data for channel 1 for scanline 0 + // pixel sample data for channel ... for scanline 0 + // pixel sample data for channel n for scanline 0 + // pixel sample data for channel 0 for scanline 1 + // pixel sample data for channel 1 for scanline 1 + // pixel sample data for channel ... for scanline 1 + // pixel sample data for channel n for scanline 1 + // ... + for (size_t c = 0; c < static_cast(num_channels); c++) { + assert(channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT); + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT); + for (size_t v = 0; v < static_cast(num_lines); v++) { + const float *line_ptr = reinterpret_cast( + &outBuf.at(v * pixel_data_size * static_cast(width) + + channel_offset_list[c] * static_cast(width))); + for (size_t u = 0; u < static_cast(width); u++) { + float val; + tinyexr::cpy4(&val, line_ptr + u); + + tinyexr::swap4(reinterpret_cast(&val)); + + float *image = reinterpret_cast(out_images)[c]; + if (line_order == 0) { + image += (static_cast(line_no) + v) * + static_cast(x_stride) + + u; + } else { + image += (static_cast(height) - 1U - + (static_cast(line_no) + v)) * + static_cast(x_stride) + + u; + } + *image = val; + } + } + } else { + assert(0); + return false; + } + } +#else + (void)attributes; + (void)num_attributes; + (void)num_channels; + assert(0); + return false; +#endif + } else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) { + for (size_t c = 0; c < num_channels; c++) { + for (size_t v = 0; v < static_cast(num_lines); v++) { + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { + const unsigned short *line_ptr = + reinterpret_cast( + data_ptr + v * pixel_data_size * size_t(width) + + channel_offset_list[c] * static_cast(width)); + + if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { + unsigned short *outLine = + reinterpret_cast(out_images[c]); + if (line_order == 0) { + outLine += (size_t(y) + v) * size_t(x_stride); + } else { + outLine += + (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride); + } + + for (int u = 0; u < width; u++) { + tinyexr::FP16 hf; + + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); + + tinyexr::swap2(reinterpret_cast(&hf.u)); + + outLine[u] = hf.u; + } + } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) { + float *outLine = reinterpret_cast(out_images[c]); + if (line_order == 0) { + outLine += (size_t(y) + v) * size_t(x_stride); + } else { + outLine += + (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride); + } + + if (reinterpret_cast(line_ptr + width) > + (data_ptr + data_len)) { + // Insufficient data size + return false; + } + + for (int u = 0; u < width; u++) { + tinyexr::FP16 hf; + + // address may not be aliged. use byte-wise copy for safety.#76 + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); + + tinyexr::swap2(reinterpret_cast(&hf.u)); + + tinyexr::FP32 f32 = half_to_float(hf); + + outLine[u] = f32.f; + } + } else { + assert(0); + return false; + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + const float *line_ptr = reinterpret_cast( + data_ptr + v * pixel_data_size * size_t(width) + + channel_offset_list[c] * static_cast(width)); + + float *outLine = reinterpret_cast(out_images[c]); + if (line_order == 0) { + outLine += (size_t(y) + v) * size_t(x_stride); + } else { + outLine += + (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride); + } + + if (reinterpret_cast(line_ptr + width) > + (data_ptr + data_len)) { + // Insufficient data size + return false; + } + + for (int u = 0; u < width; u++) { + float val; + tinyexr::cpy4(&val, line_ptr + u); + + tinyexr::swap4(reinterpret_cast(&val)); + + outLine[u] = val; + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { + const unsigned int *line_ptr = reinterpret_cast( + data_ptr + v * pixel_data_size * size_t(width) + + channel_offset_list[c] * static_cast(width)); + + unsigned int *outLine = + reinterpret_cast(out_images[c]); + if (line_order == 0) { + outLine += (size_t(y) + v) * size_t(x_stride); + } else { + outLine += + (size_t(height) - 1 - (size_t(y) + v)) * size_t(x_stride); + } + + for (int u = 0; u < width; u++) { + if (reinterpret_cast(line_ptr + u) >= + (data_ptr + data_len)) { + // Corrupsed data? + return false; + } + + unsigned int val; + tinyexr::cpy4(&val, line_ptr + u); + + tinyexr::swap4(reinterpret_cast(&val)); + + outLine[u] = val; + } + } + } + } + } + + return true; +} + +static bool DecodeTiledPixelData( + unsigned char **out_images, int *width, int *height, + const int *requested_pixel_types, const unsigned char *data_ptr, + size_t data_len, int compression_type, int line_order, int data_width, + int data_height, int tile_offset_x, int tile_offset_y, int tile_size_x, + int tile_size_y, size_t pixel_data_size, size_t num_attributes, + const EXRAttribute *attributes, size_t num_channels, + const EXRChannelInfo *channels, + const std::vector &channel_offset_list) { + // Here, data_width and data_height are the dimensions of the current (sub)level. + if (tile_size_x * tile_offset_x > data_width || + tile_size_y * tile_offset_y > data_height) { + return false; + } + + // Compute actual image size in a tile. + if ((tile_offset_x + 1) * tile_size_x >= data_width) { + (*width) = data_width - (tile_offset_x * tile_size_x); + } else { + (*width) = tile_size_x; + } + + if ((tile_offset_y + 1) * tile_size_y >= data_height) { + (*height) = data_height - (tile_offset_y * tile_size_y); + } else { + (*height) = tile_size_y; + } + + // Image size = tile size. + return DecodePixelData(out_images, requested_pixel_types, data_ptr, data_len, + compression_type, line_order, (*width), tile_size_y, + /* stride */ tile_size_x, /* y */ 0, /* line_no */ 0, + (*height), pixel_data_size, num_attributes, attributes, + num_channels, channels, channel_offset_list); +} + +static bool ComputeChannelLayout(std::vector *channel_offset_list, + int *pixel_data_size, size_t *channel_offset, + int num_channels, + const EXRChannelInfo *channels) { + channel_offset_list->resize(static_cast(num_channels)); + + (*pixel_data_size) = 0; + (*channel_offset) = 0; + + for (size_t c = 0; c < static_cast(num_channels); c++) { + (*channel_offset_list)[c] = (*channel_offset); + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { + (*pixel_data_size) += sizeof(unsigned short); + (*channel_offset) += sizeof(unsigned short); + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + (*pixel_data_size) += sizeof(float); + (*channel_offset) += sizeof(float); + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { + (*pixel_data_size) += sizeof(unsigned int); + (*channel_offset) += sizeof(unsigned int); + } else { + // ??? + return false; + } + } + return true; +} + +static unsigned char **AllocateImage(int num_channels, + const EXRChannelInfo *channels, + const int *requested_pixel_types, + int data_width, int data_height) { + unsigned char **images = + reinterpret_cast(static_cast( + malloc(sizeof(float *) * static_cast(num_channels)))); + + for (size_t c = 0; c < static_cast(num_channels); c++) { + size_t data_len = + static_cast(data_width) * static_cast(data_height); + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { + // pixel_data_size += sizeof(unsigned short); + // channel_offset += sizeof(unsigned short); + // Alloc internal image for half type. + if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { + images[c] = + reinterpret_cast(static_cast( + malloc(sizeof(unsigned short) * data_len))); + } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) { + images[c] = reinterpret_cast( + static_cast(malloc(sizeof(float) * data_len))); + } else { + assert(0); + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + // pixel_data_size += sizeof(float); + // channel_offset += sizeof(float); + images[c] = reinterpret_cast( + static_cast(malloc(sizeof(float) * data_len))); + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { + // pixel_data_size += sizeof(unsigned int); + // channel_offset += sizeof(unsigned int); + images[c] = reinterpret_cast( + static_cast(malloc(sizeof(unsigned int) * data_len))); + } else { + assert(0); + } + } + + return images; +} + +#ifdef _WIN32 +static inline std::wstring UTF8ToWchar(const std::string &str) { + int wstr_size = + MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), NULL, 0); + std::wstring wstr(wstr_size, 0); + MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], + (int)wstr.size()); + return wstr; +} +#endif + + +static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, + const EXRVersion *version, std::string *err, + const unsigned char *buf, size_t size) { + const char *marker = reinterpret_cast(&buf[0]); + + if (empty_header) { + (*empty_header) = false; + } + + if (version->multipart) { + if (size > 0 && marker[0] == '\0') { + // End of header list. + if (empty_header) { + (*empty_header) = true; + } + return TINYEXR_SUCCESS; + } + } + + // According to the spec, the header of every OpenEXR file must contain at + // least the following attributes: + // + // channels chlist + // compression compression + // dataWindow box2i + // displayWindow box2i + // lineOrder lineOrder + // pixelAspectRatio float + // screenWindowCenter v2f + // screenWindowWidth float + bool has_channels = false; + bool has_compression = false; + bool has_data_window = false; + bool has_display_window = false; + bool has_line_order = false; + bool has_pixel_aspect_ratio = false; + bool has_screen_window_center = false; + bool has_screen_window_width = false; + bool has_name = false; + bool has_type = false; + + info->name.clear(); + info->type.clear(); + + info->data_window.min_x = 0; + info->data_window.min_y = 0; + info->data_window.max_x = 0; + info->data_window.max_y = 0; + info->line_order = 0; // @fixme + info->display_window.min_x = 0; + info->display_window.min_y = 0; + info->display_window.max_x = 0; + info->display_window.max_y = 0; + info->screen_window_center[0] = 0.0f; + info->screen_window_center[1] = 0.0f; + info->screen_window_width = -1.0f; + info->pixel_aspect_ratio = -1.0f; + + info->tiled = 0; + info->tile_size_x = -1; + info->tile_size_y = -1; + info->tile_level_mode = -1; + info->tile_rounding_mode = -1; + + info->attributes.clear(); + + // Read attributes + size_t orig_size = size; + for (size_t nattr = 0; nattr < TINYEXR_MAX_HEADER_ATTRIBUTES; nattr++) { + if (0 == size) { + if (err) { + (*err) += "Insufficient data size for attributes.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } else if (marker[0] == '\0') { + size--; + break; + } + + std::string attr_name; + std::string attr_type; + std::vector data; + size_t marker_size; + if (!tinyexr::ReadAttribute(&attr_name, &attr_type, &data, &marker_size, + marker, size)) { + if (err) { + (*err) += "Failed to read attribute.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + marker += marker_size; + size -= marker_size; + + // For a multipart file, the version field 9th bit is 0. + if ((version->tiled || version->multipart || version->non_image) && attr_name.compare("tiles") == 0) { + unsigned int x_size, y_size; + unsigned char tile_mode; + assert(data.size() == 9); + memcpy(&x_size, &data.at(0), sizeof(int)); + memcpy(&y_size, &data.at(4), sizeof(int)); + tile_mode = data[8]; + tinyexr::swap4(&x_size); + tinyexr::swap4(&y_size); + + if (x_size > static_cast(std::numeric_limits::max()) || + y_size > static_cast(std::numeric_limits::max())) { + if (err) { + (*err) = "Tile sizes were invalid."; + } + return TINYEXR_ERROR_UNSUPPORTED_FORMAT; + } + + info->tile_size_x = static_cast(x_size); + info->tile_size_y = static_cast(y_size); + + // mode = levelMode + roundingMode * 16 + info->tile_level_mode = tile_mode & 0x3; + info->tile_rounding_mode = (tile_mode >> 4) & 0x1; + info->tiled = 1; + } else if (attr_name.compare("compression") == 0) { + bool ok = false; + if (data[0] < TINYEXR_COMPRESSIONTYPE_PIZ) { + ok = true; + } + + if (data[0] == TINYEXR_COMPRESSIONTYPE_PIZ) { +#if TINYEXR_USE_PIZ + ok = true; +#else + if (err) { + (*err) = "PIZ compression is not supported."; + } + return TINYEXR_ERROR_UNSUPPORTED_FORMAT; +#endif + } + + if (data[0] == TINYEXR_COMPRESSIONTYPE_ZFP) { +#if TINYEXR_USE_ZFP + ok = true; +#else + if (err) { + (*err) = "ZFP compression is not supported."; + } + return TINYEXR_ERROR_UNSUPPORTED_FORMAT; +#endif + } + + if (!ok) { + if (err) { + (*err) = "Unknown compression type."; + } + return TINYEXR_ERROR_UNSUPPORTED_FORMAT; + } + + info->compression_type = static_cast(data[0]); + has_compression = true; + + } else if (attr_name.compare("channels") == 0) { + // name: zero-terminated string, from 1 to 255 bytes long + // pixel type: int, possible values are: UINT = 0 HALF = 1 FLOAT = 2 + // pLinear: unsigned char, possible values are 0 and 1 + // reserved: three chars, should be zero + // xSampling: int + // ySampling: int + + if (!ReadChannelInfo(info->channels, data)) { + if (err) { + (*err) += "Failed to parse channel info.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + + if (info->channels.size() < 1) { + if (err) { + (*err) += "# of channels is zero.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + + has_channels = true; + + } else if (attr_name.compare("dataWindow") == 0) { + if (data.size() >= 16) { + memcpy(&info->data_window.min_x, &data.at(0), sizeof(int)); + memcpy(&info->data_window.min_y, &data.at(4), sizeof(int)); + memcpy(&info->data_window.max_x, &data.at(8), sizeof(int)); + memcpy(&info->data_window.max_y, &data.at(12), sizeof(int)); + tinyexr::swap4(&info->data_window.min_x); + tinyexr::swap4(&info->data_window.min_y); + tinyexr::swap4(&info->data_window.max_x); + tinyexr::swap4(&info->data_window.max_y); + has_data_window = true; + } + } else if (attr_name.compare("displayWindow") == 0) { + if (data.size() >= 16) { + memcpy(&info->display_window.min_x, &data.at(0), sizeof(int)); + memcpy(&info->display_window.min_y, &data.at(4), sizeof(int)); + memcpy(&info->display_window.max_x, &data.at(8), sizeof(int)); + memcpy(&info->display_window.max_y, &data.at(12), sizeof(int)); + tinyexr::swap4(&info->display_window.min_x); + tinyexr::swap4(&info->display_window.min_y); + tinyexr::swap4(&info->display_window.max_x); + tinyexr::swap4(&info->display_window.max_y); + + has_display_window = true; + } + } else if (attr_name.compare("lineOrder") == 0) { + if (data.size() >= 1) { + info->line_order = static_cast(data[0]); + has_line_order = true; + } + } else if (attr_name.compare("pixelAspectRatio") == 0) { + if (data.size() >= sizeof(float)) { + memcpy(&info->pixel_aspect_ratio, &data.at(0), sizeof(float)); + tinyexr::swap4(&info->pixel_aspect_ratio); + has_pixel_aspect_ratio = true; + } + } else if (attr_name.compare("screenWindowCenter") == 0) { + if (data.size() >= 8) { + memcpy(&info->screen_window_center[0], &data.at(0), sizeof(float)); + memcpy(&info->screen_window_center[1], &data.at(4), sizeof(float)); + tinyexr::swap4(&info->screen_window_center[0]); + tinyexr::swap4(&info->screen_window_center[1]); + has_screen_window_center = true; + } + } else if (attr_name.compare("screenWindowWidth") == 0) { + if (data.size() >= sizeof(float)) { + memcpy(&info->screen_window_width, &data.at(0), sizeof(float)); + tinyexr::swap4(&info->screen_window_width); + + has_screen_window_width = true; + } + } else if (attr_name.compare("chunkCount") == 0) { + if (data.size() >= sizeof(int)) { + memcpy(&info->chunk_count, &data.at(0), sizeof(int)); + tinyexr::swap4(&info->chunk_count); + } + } else if (attr_name.compare("name") == 0) { + if (!data.empty() && data[0]) { + data.push_back(0); + size_t len = strlen(reinterpret_cast(&data[0])); + info->name.resize(len); + info->name.assign(reinterpret_cast(&data[0]), len); + has_name = true; + } + } else if (attr_name.compare("type") == 0) { + if (!data.empty() && data[0]) { + data.push_back(0); + size_t len = strlen(reinterpret_cast(&data[0])); + info->type.resize(len); + info->type.assign(reinterpret_cast(&data[0]), len); + has_type = true; + } + } else { + // Custom attribute(up to TINYEXR_MAX_CUSTOM_ATTRIBUTES) + if (info->attributes.size() < TINYEXR_MAX_CUSTOM_ATTRIBUTES) { + EXRAttribute attrib; +#ifdef _MSC_VER + strncpy_s(attrib.name, attr_name.c_str(), 255); + strncpy_s(attrib.type, attr_type.c_str(), 255); +#else + strncpy(attrib.name, attr_name.c_str(), 255); + strncpy(attrib.type, attr_type.c_str(), 255); +#endif + attrib.name[255] = '\0'; + attrib.type[255] = '\0'; + attrib.size = static_cast(data.size()); + attrib.value = static_cast(malloc(data.size())); + memcpy(reinterpret_cast(attrib.value), &data.at(0), + data.size()); + info->attributes.push_back(attrib); + } + } + } + + // Check if required attributes exist + { + std::stringstream ss_err; + + if (!has_compression) { + ss_err << "\"compression\" attribute not found in the header." + << std::endl; + } + + if (!has_channels) { + ss_err << "\"channels\" attribute not found in the header." << std::endl; + } + + if (!has_line_order) { + ss_err << "\"lineOrder\" attribute not found in the header." << std::endl; + } + + if (!has_display_window) { + ss_err << "\"displayWindow\" attribute not found in the header." + << std::endl; + } + + if (!has_data_window) { + ss_err << "\"dataWindow\" attribute not found in the header or invalid." + << std::endl; + } + + if (!has_pixel_aspect_ratio) { + ss_err << "\"pixelAspectRatio\" attribute not found in the header." + << std::endl; + } + + if (!has_screen_window_width) { + ss_err << "\"screenWindowWidth\" attribute not found in the header." + << std::endl; + } + + if (!has_screen_window_center) { + ss_err << "\"screenWindowCenter\" attribute not found in the header." + << std::endl; + } + + if (version->multipart || version->non_image) { + if (!has_name) { + ss_err << "\"name\" attribute not found in the header." + << std::endl; + } + if (!has_type) { + ss_err << "\"type\" attribute not found in the header." + << std::endl; + } + } + + if (!(ss_err.str().empty())) { + if (err) { + (*err) += ss_err.str(); + } + return TINYEXR_ERROR_INVALID_HEADER; + } + } + + info->header_len = static_cast(orig_size - size); + + return TINYEXR_SUCCESS; +} + +// C++ HeaderInfo to C EXRHeader conversion. +static void ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info) { + exr_header->pixel_aspect_ratio = info.pixel_aspect_ratio; + exr_header->screen_window_center[0] = info.screen_window_center[0]; + exr_header->screen_window_center[1] = info.screen_window_center[1]; + exr_header->screen_window_width = info.screen_window_width; + exr_header->chunk_count = info.chunk_count; + exr_header->display_window.min_x = info.display_window.min_x; + exr_header->display_window.min_y = info.display_window.min_y; + exr_header->display_window.max_x = info.display_window.max_x; + exr_header->display_window.max_y = info.display_window.max_y; + exr_header->data_window.min_x = info.data_window.min_x; + exr_header->data_window.min_y = info.data_window.min_y; + exr_header->data_window.max_x = info.data_window.max_x; + exr_header->data_window.max_y = info.data_window.max_y; + exr_header->line_order = info.line_order; + exr_header->compression_type = info.compression_type; + exr_header->tiled = info.tiled; + exr_header->tile_size_x = info.tile_size_x; + exr_header->tile_size_y = info.tile_size_y; + exr_header->tile_level_mode = info.tile_level_mode; + exr_header->tile_rounding_mode = info.tile_rounding_mode; + + EXRSetNameAttr(exr_header, info.name.c_str()); + + if (!info.type.empty()) { + if (info.type == "scanlineimage") { + assert(!exr_header->tiled); + } else if (info.type == "tiledimage") { + assert(exr_header->tiled); + } else if (info.type == "deeptile") { + exr_header->non_image = 1; + assert(exr_header->tiled); + } else if (info.type == "deepscanline") { + exr_header->non_image = 1; + assert(!exr_header->tiled); + } else { + assert(false); + } + } + + exr_header->num_channels = static_cast(info.channels.size()); + + exr_header->channels = static_cast(malloc( + sizeof(EXRChannelInfo) * static_cast(exr_header->num_channels))); + for (size_t c = 0; c < static_cast(exr_header->num_channels); c++) { +#ifdef _MSC_VER + strncpy_s(exr_header->channels[c].name, info.channels[c].name.c_str(), 255); +#else + strncpy(exr_header->channels[c].name, info.channels[c].name.c_str(), 255); +#endif + // manually add '\0' for safety. + exr_header->channels[c].name[255] = '\0'; + + exr_header->channels[c].pixel_type = info.channels[c].pixel_type; + exr_header->channels[c].p_linear = info.channels[c].p_linear; + exr_header->channels[c].x_sampling = info.channels[c].x_sampling; + exr_header->channels[c].y_sampling = info.channels[c].y_sampling; + } + + exr_header->pixel_types = static_cast( + malloc(sizeof(int) * static_cast(exr_header->num_channels))); + for (size_t c = 0; c < static_cast(exr_header->num_channels); c++) { + exr_header->pixel_types[c] = info.channels[c].pixel_type; + } + + // Initially fill with values of `pixel_types` + exr_header->requested_pixel_types = static_cast( + malloc(sizeof(int) * static_cast(exr_header->num_channels))); + for (size_t c = 0; c < static_cast(exr_header->num_channels); c++) { + exr_header->requested_pixel_types[c] = info.channels[c].pixel_type; + } + + exr_header->num_custom_attributes = static_cast(info.attributes.size()); + + if (exr_header->num_custom_attributes > 0) { + // TODO(syoyo): Report warning when # of attributes exceeds + // `TINYEXR_MAX_CUSTOM_ATTRIBUTES` + if (exr_header->num_custom_attributes > TINYEXR_MAX_CUSTOM_ATTRIBUTES) { + exr_header->num_custom_attributes = TINYEXR_MAX_CUSTOM_ATTRIBUTES; + } + + exr_header->custom_attributes = static_cast(malloc( + sizeof(EXRAttribute) * size_t(exr_header->num_custom_attributes))); + + for (size_t i = 0; i < info.attributes.size(); i++) { + memcpy(exr_header->custom_attributes[i].name, info.attributes[i].name, + 256); + memcpy(exr_header->custom_attributes[i].type, info.attributes[i].type, + 256); + exr_header->custom_attributes[i].size = info.attributes[i].size; + // Just copy pointer + exr_header->custom_attributes[i].value = info.attributes[i].value; + } + + } else { + exr_header->custom_attributes = NULL; + } + + exr_header->header_len = info.header_len; +} + +struct OffsetData { + OffsetData() : num_x_levels(0), num_y_levels(0) {} + std::vector > > offsets; + int num_x_levels; + int num_y_levels; +}; + +int LevelIndex(int lx, int ly, int tile_level_mode, int num_x_levels) { + switch (tile_level_mode) { + case TINYEXR_TILE_ONE_LEVEL: + return 0; + + case TINYEXR_TILE_MIPMAP_LEVELS: + return lx; + + case TINYEXR_TILE_RIPMAP_LEVELS: + return lx + ly * num_x_levels; + + default: + assert(false); + } + return 0; +} + +static int LevelSize(int toplevel_size, int level, int tile_rounding_mode) { + assert(level >= 0); + + int b = (int)(1u << (unsigned)level); + int level_size = toplevel_size / b; + + if (tile_rounding_mode == TINYEXR_TILE_ROUND_UP && level_size * b < toplevel_size) + level_size += 1; + + return std::max(level_size, 1); +} + +static int DecodeTiledLevel(EXRImage* exr_image, const EXRHeader* exr_header, + const OffsetData& offset_data, + const std::vector& channel_offset_list, + int pixel_data_size, + const unsigned char* head, const size_t size, + std::string* err) { + int num_channels = exr_header->num_channels; + + int level_index = LevelIndex(exr_image->level_x, exr_image->level_y, exr_header->tile_level_mode, offset_data.num_x_levels); + int num_y_tiles = (int)offset_data.offsets[level_index].size(); + assert(num_y_tiles); + int num_x_tiles = (int)offset_data.offsets[level_index][0].size(); + assert(num_x_tiles); + int num_tiles = num_x_tiles * num_y_tiles; + + int err_code = TINYEXR_SUCCESS; + + enum { + EF_SUCCESS = 0, + EF_INVALID_DATA = 1, + EF_INSUFFICIENT_DATA = 2, + EF_FAILED_TO_DECODE = 4 + }; +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + std::atomic error_flag(EF_SUCCESS); +#else + unsigned error_flag(EF_SUCCESS); +#endif + + // Although the spec says : "...the data window is subdivided into an array of smaller rectangles...", + // the IlmImf library allows the dimensions of the tile to be larger (or equal) than the dimensions of the data window. +#if 0 + if ((exr_header->tile_size_x > exr_image->width || exr_header->tile_size_y > exr_image->height) && + exr_image->level_x == 0 && exr_image->level_y == 0) { + if (err) { + (*err) += "Failed to decode tile data.\n"; + } + err_code = TINYEXR_ERROR_INVALID_DATA; + } +#endif + exr_image->tiles = static_cast( + calloc(sizeof(EXRTile), static_cast(num_tiles))); + +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + std::vector workers; + std::atomic tile_count(0); + + int num_threads = std::max(1, int(std::thread::hardware_concurrency())); + if (num_threads > int(num_tiles)) { + num_threads = int(num_tiles); + } + + for (int t = 0; t < num_threads; t++) { + workers.emplace_back(std::thread([&]() + { + int tile_idx = 0; + while ((tile_idx = tile_count++) < num_tiles) { + +#else +#if TINYEXR_USE_OPENMP +#pragma omp parallel for +#endif + for (int tile_idx = 0; tile_idx < num_tiles; tile_idx++) { +#endif + // Allocate memory for each tile. + exr_image->tiles[tile_idx].images = tinyexr::AllocateImage( + num_channels, exr_header->channels, + exr_header->requested_pixel_types, exr_header->tile_size_x, + exr_header->tile_size_y); + + int x_tile = tile_idx % num_x_tiles; + int y_tile = tile_idx / num_x_tiles; + // 16 byte: tile coordinates + // 4 byte : data size + // ~ : data(uncompressed or compressed) + tinyexr::tinyexr_uint64 offset = offset_data.offsets[level_index][y_tile][x_tile]; + if (offset + sizeof(int) * 5 > size) { + // Insufficient data size. + error_flag |= EF_INSUFFICIENT_DATA; + continue; + } + + size_t data_size = + size_t(size - (offset + sizeof(int) * 5)); + const unsigned char* data_ptr = + reinterpret_cast(head + offset); + + int tile_coordinates[4]; + memcpy(tile_coordinates, data_ptr, sizeof(int) * 4); + tinyexr::swap4(&tile_coordinates[0]); + tinyexr::swap4(&tile_coordinates[1]); + tinyexr::swap4(&tile_coordinates[2]); + tinyexr::swap4(&tile_coordinates[3]); + + if (tile_coordinates[2] != exr_image->level_x) { + // Invalid data. + error_flag |= EF_INVALID_DATA; + continue; + } + if (tile_coordinates[3] != exr_image->level_y) { + // Invalid data. + error_flag |= EF_INVALID_DATA; + continue; + } + + int data_len; + memcpy(&data_len, data_ptr + 16, + sizeof(int)); // 16 = sizeof(tile_coordinates) + tinyexr::swap4(&data_len); + + if (data_len < 2 || size_t(data_len) > data_size) { + // Insufficient data size. + error_flag |= EF_INSUFFICIENT_DATA; + continue; + } + + // Move to data addr: 20 = 16 + 4; + data_ptr += 20; + bool ret = tinyexr::DecodeTiledPixelData( + exr_image->tiles[tile_idx].images, + &(exr_image->tiles[tile_idx].width), + &(exr_image->tiles[tile_idx].height), + exr_header->requested_pixel_types, data_ptr, + static_cast(data_len), exr_header->compression_type, + exr_header->line_order, + exr_image->width, exr_image->height, + tile_coordinates[0], tile_coordinates[1], exr_header->tile_size_x, + exr_header->tile_size_y, static_cast(pixel_data_size), + static_cast(exr_header->num_custom_attributes), + exr_header->custom_attributes, + static_cast(exr_header->num_channels), + exr_header->channels, channel_offset_list); + + if (!ret) { + // Failed to decode tile data. + error_flag |= EF_FAILED_TO_DECODE; + } + + exr_image->tiles[tile_idx].offset_x = tile_coordinates[0]; + exr_image->tiles[tile_idx].offset_y = tile_coordinates[1]; + exr_image->tiles[tile_idx].level_x = tile_coordinates[2]; + exr_image->tiles[tile_idx].level_y = tile_coordinates[3]; + +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + } + })); + } // num_thread loop + + for (auto& t : workers) { + t.join(); + } + +#else + } // parallel for +#endif + + // Even in the event of an error, the reserved memory may be freed. + exr_image->num_channels = num_channels; + exr_image->num_tiles = static_cast(num_tiles); + + if (error_flag) err_code = TINYEXR_ERROR_INVALID_DATA; + if (err) { + if (error_flag & EF_INSUFFICIENT_DATA) { + (*err) += "Insufficient data length.\n"; + } + if (error_flag & EF_FAILED_TO_DECODE) { + (*err) += "Failed to decode tile data.\n"; + } + } + return err_code; +} + +static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, + const OffsetData& offset_data, + const unsigned char *head, const size_t size, + std::string *err) { + int num_channels = exr_header->num_channels; + + int num_scanline_blocks = 1; + if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) { + num_scanline_blocks = 16; + } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { + num_scanline_blocks = 32; + } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { + num_scanline_blocks = 16; + +#if TINYEXR_USE_ZFP + tinyexr::ZFPCompressionParam zfp_compression_param; + if (!FindZFPCompressionParam(&zfp_compression_param, + exr_header->custom_attributes, + int(exr_header->num_custom_attributes), err)) { + return TINYEXR_ERROR_INVALID_HEADER; + } +#endif + } + + if (exr_header->data_window.max_x < exr_header->data_window.min_x || + exr_header->data_window.max_y < exr_header->data_window.min_y) { + if (err) { + (*err) += "Invalid data window.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + + int data_width = + exr_header->data_window.max_x - exr_header->data_window.min_x + 1; + int data_height = + exr_header->data_window.max_y - exr_header->data_window.min_y + 1; + + // Do not allow too large data_width and data_height. header invalid? + { + if ((data_width > TINYEXR_DIMENSION_THRESHOLD) || (data_height > TINYEXR_DIMENSION_THRESHOLD)) { + if (err) { + std::stringstream ss; + ss << "data_with or data_height too large. data_width: " << data_width + << ", " + << "data_height = " << data_height << std::endl; + (*err) += ss.str(); + } + return TINYEXR_ERROR_INVALID_DATA; + } + if (exr_header->tiled) { + if ((exr_header->tile_size_x > TINYEXR_DIMENSION_THRESHOLD) || (exr_header->tile_size_y > TINYEXR_DIMENSION_THRESHOLD)) { + if (err) { + std::stringstream ss; + ss << "tile with or tile height too large. tile width: " << exr_header->tile_size_x + << ", " + << "tile height = " << exr_header->tile_size_y << std::endl; + (*err) += ss.str(); + } + return TINYEXR_ERROR_INVALID_DATA; + } + } + } + + const std::vector& offsets = offset_data.offsets[0][0]; + size_t num_blocks = offsets.size(); + + std::vector channel_offset_list; + int pixel_data_size = 0; + size_t channel_offset = 0; + if (!tinyexr::ComputeChannelLayout(&channel_offset_list, &pixel_data_size, + &channel_offset, num_channels, + exr_header->channels)) { + if (err) { + (*err) += "Failed to compute channel layout.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + std::atomic invalid_data(false); +#else + bool invalid_data(false); +#endif + + if (exr_header->tiled) { + // value check + if (exr_header->tile_size_x < 0) { + if (err) { + std::stringstream ss; + ss << "Invalid tile size x : " << exr_header->tile_size_x << "\n"; + (*err) += ss.str(); + } + return TINYEXR_ERROR_INVALID_HEADER; + } + + if (exr_header->tile_size_y < 0) { + if (err) { + std::stringstream ss; + ss << "Invalid tile size y : " << exr_header->tile_size_y << "\n"; + (*err) += ss.str(); + } + return TINYEXR_ERROR_INVALID_HEADER; + } + if (exr_header->tile_level_mode != TINYEXR_TILE_RIPMAP_LEVELS) { + EXRImage* level_image = NULL; + for (int level = 0; level < offset_data.num_x_levels; ++level) { + if (!level_image) { + level_image = exr_image; + } else { + level_image->next_level = new EXRImage; + InitEXRImage(level_image->next_level); + level_image = level_image->next_level; + } + level_image->width = + LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level, exr_header->tile_rounding_mode); + level_image->height = + LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level, exr_header->tile_rounding_mode); + level_image->level_x = level; + level_image->level_y = level; + + int ret = DecodeTiledLevel(level_image, exr_header, + offset_data, + channel_offset_list, + pixel_data_size, + head, size, + err); + if (ret != TINYEXR_SUCCESS) return ret; + } + } else { + EXRImage* level_image = NULL; + for (int level_y = 0; level_y < offset_data.num_y_levels; ++level_y) + for (int level_x = 0; level_x < offset_data.num_x_levels; ++level_x) { + if (!level_image) { + level_image = exr_image; + } else { + level_image->next_level = new EXRImage; + InitEXRImage(level_image->next_level); + level_image = level_image->next_level; + } + + level_image->width = + LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level_x, exr_header->tile_rounding_mode); + level_image->height = + LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level_y, exr_header->tile_rounding_mode); + level_image->level_x = level_x; + level_image->level_y = level_y; + + int ret = DecodeTiledLevel(level_image, exr_header, + offset_data, + channel_offset_list, + pixel_data_size, + head, size, + err); + if (ret != TINYEXR_SUCCESS) return ret; + } + } + } else { // scanline format + // Don't allow too large image(256GB * pixel_data_size or more). Workaround + // for #104. + size_t total_data_len = + size_t(data_width) * size_t(data_height) * size_t(num_channels); + const bool total_data_len_overflown = + sizeof(void *) == 8 ? (total_data_len >= 0x4000000000) : false; + if ((total_data_len == 0) || total_data_len_overflown) { + if (err) { + std::stringstream ss; + ss << "Image data size is zero or too large: width = " << data_width + << ", height = " << data_height << ", channels = " << num_channels + << std::endl; + (*err) += ss.str(); + } + return TINYEXR_ERROR_INVALID_DATA; + } + + exr_image->images = tinyexr::AllocateImage( + num_channels, exr_header->channels, exr_header->requested_pixel_types, + data_width, data_height); + +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + std::vector workers; + std::atomic y_count(0); + + int num_threads = std::max(1, int(std::thread::hardware_concurrency())); + if (num_threads > int(num_blocks)) { + num_threads = int(num_blocks); + } + + for (int t = 0; t < num_threads; t++) { + workers.emplace_back(std::thread([&]() { + int y = 0; + while ((y = y_count++) < int(num_blocks)) { + +#else + +#if TINYEXR_USE_OPENMP +#pragma omp parallel for +#endif + for (int y = 0; y < static_cast(num_blocks); y++) { + +#endif + size_t y_idx = static_cast(y); + + if (offsets[y_idx] + sizeof(int) * 2 > size) { + invalid_data = true; + } else { + // 4 byte: scan line + // 4 byte: data size + // ~ : pixel data(uncompressed or compressed) + size_t data_size = + size_t(size - (offsets[y_idx] + sizeof(int) * 2)); + const unsigned char *data_ptr = + reinterpret_cast(head + offsets[y_idx]); + + int line_no; + memcpy(&line_no, data_ptr, sizeof(int)); + int data_len; + memcpy(&data_len, data_ptr + 4, sizeof(int)); + tinyexr::swap4(&line_no); + tinyexr::swap4(&data_len); + + if (size_t(data_len) > data_size) { + invalid_data = true; + + } else if ((line_no > (2 << 20)) || (line_no < -(2 << 20))) { + // Too large value. Assume this is invalid + // 2**20 = 1048576 = heuristic value. + invalid_data = true; + } else if (data_len == 0) { + // TODO(syoyo): May be ok to raise the threshold for example + // `data_len < 4` + invalid_data = true; + } else { + // line_no may be negative. + int end_line_no = (std::min)(line_no + num_scanline_blocks, + (exr_header->data_window.max_y + 1)); + + int num_lines = end_line_no - line_no; + + if (num_lines <= 0) { + invalid_data = true; + } else { + // Move to data addr: 8 = 4 + 4; + data_ptr += 8; + + // Adjust line_no with data_window.bmin.y + + // overflow check + tinyexr_int64 lno = + static_cast(line_no) - + static_cast(exr_header->data_window.min_y); + if (lno > std::numeric_limits::max()) { + line_no = -1; // invalid + } else if (lno < -std::numeric_limits::max()) { + line_no = -1; // invalid + } else { + line_no -= exr_header->data_window.min_y; + } + + if (line_no < 0) { + invalid_data = true; + } else { + if (!tinyexr::DecodePixelData( + exr_image->images, exr_header->requested_pixel_types, + data_ptr, static_cast(data_len), + exr_header->compression_type, exr_header->line_order, + data_width, data_height, data_width, y, line_no, + num_lines, static_cast(pixel_data_size), + static_cast( + exr_header->num_custom_attributes), + exr_header->custom_attributes, + static_cast(exr_header->num_channels), + exr_header->channels, channel_offset_list)) { + invalid_data = true; + } + } + } + } + } + +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + } + })); + } + + for (auto &t : workers) { + t.join(); + } +#else + } // omp parallel +#endif + } + + if (invalid_data) { + if (err) { + (*err) += "Invalid data found when decoding pixels.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + + // Overwrite `pixel_type` with `requested_pixel_type`. + { + for (int c = 0; c < exr_header->num_channels; c++) { + exr_header->pixel_types[c] = exr_header->requested_pixel_types[c]; + } + } + + { + exr_image->num_channels = num_channels; + + exr_image->width = data_width; + exr_image->height = data_height; + } + + return TINYEXR_SUCCESS; +} + +static bool ReconstructLineOffsets( + std::vector *offsets, size_t n, + const unsigned char *head, const unsigned char *marker, const size_t size) { + assert(head < marker); + assert(offsets->size() == n); + + for (size_t i = 0; i < n; i++) { + size_t offset = static_cast(marker - head); + // Offset should not exceed whole EXR file/data size. + if ((offset + sizeof(tinyexr::tinyexr_uint64)) >= size) { + return false; + } + + int y; + unsigned int data_len; + + memcpy(&y, marker, sizeof(int)); + memcpy(&data_len, marker + 4, sizeof(unsigned int)); + + if (data_len >= size) { + return false; + } + + tinyexr::swap4(&y); + tinyexr::swap4(&data_len); + + (*offsets)[i] = offset; + + marker += data_len + 8; // 8 = 4 bytes(y) + 4 bytes(data_len) + } + + return true; +} + + +static int FloorLog2(unsigned x) { + // + // For x > 0, floorLog2(y) returns floor(log(x)/log(2)). + // + int y = 0; + while (x > 1) { + y += 1; + x >>= 1u; + } + return y; +} + + +static int CeilLog2(unsigned x) { + // + // For x > 0, ceilLog2(y) returns ceil(log(x)/log(2)). + // + int y = 0; + int r = 0; + while (x > 1) { + if (x & 1) + r = 1; + + y += 1; + x >>= 1u; + } + return y + r; +} + +static int RoundLog2(int x, int tile_rounding_mode) { + return (tile_rounding_mode == TINYEXR_TILE_ROUND_DOWN) ? FloorLog2(static_cast(x)) : CeilLog2(static_cast(x)); +} + +static int CalculateNumXLevels(const EXRHeader* exr_header) { + int min_x = exr_header->data_window.min_x; + int max_x = exr_header->data_window.max_x; + int min_y = exr_header->data_window.min_y; + int max_y = exr_header->data_window.max_y; + + int num = 0; + switch (exr_header->tile_level_mode) { + case TINYEXR_TILE_ONE_LEVEL: + + num = 1; + break; + + case TINYEXR_TILE_MIPMAP_LEVELS: + + { + int w = max_x - min_x + 1; + int h = max_y - min_y + 1; + num = RoundLog2(std::max(w, h), exr_header->tile_rounding_mode) + 1; + } + break; + + case TINYEXR_TILE_RIPMAP_LEVELS: + + { + int w = max_x - min_x + 1; + num = RoundLog2(w, exr_header->tile_rounding_mode) + 1; + } + break; + + default: + + assert(false); + } + + return num; +} + +static int CalculateNumYLevels(const EXRHeader* exr_header) { + int min_x = exr_header->data_window.min_x; + int max_x = exr_header->data_window.max_x; + int min_y = exr_header->data_window.min_y; + int max_y = exr_header->data_window.max_y; + int num = 0; + + switch (exr_header->tile_level_mode) { + case TINYEXR_TILE_ONE_LEVEL: + + num = 1; + break; + + case TINYEXR_TILE_MIPMAP_LEVELS: + + { + int w = max_x - min_x + 1; + int h = max_y - min_y + 1; + num = RoundLog2(std::max(w, h), exr_header->tile_rounding_mode) + 1; + } + break; + + case TINYEXR_TILE_RIPMAP_LEVELS: + + { + int h = max_y - min_y + 1; + num = RoundLog2(h, exr_header->tile_rounding_mode) + 1; + } + break; + + default: + + assert(false); + } + + return num; +} + +static void CalculateNumTiles(std::vector& numTiles, + int toplevel_size, + int size, + int tile_rounding_mode) { + for (unsigned i = 0; i < numTiles.size(); i++) { + int l = LevelSize(toplevel_size, i, tile_rounding_mode); + assert(l <= std::numeric_limits::max() - size + 1); + + numTiles[i] = (l + size - 1) / size; + } +} + +static void PrecalculateTileInfo(std::vector& num_x_tiles, + std::vector& num_y_tiles, + const EXRHeader* exr_header) { + int min_x = exr_header->data_window.min_x; + int max_x = exr_header->data_window.max_x; + int min_y = exr_header->data_window.min_y; + int max_y = exr_header->data_window.max_y; + + int num_x_levels = CalculateNumXLevels(exr_header); + int num_y_levels = CalculateNumYLevels(exr_header); + + num_x_tiles.resize(num_x_levels); + num_y_tiles.resize(num_y_levels); + + CalculateNumTiles(num_x_tiles, + max_x - min_x + 1, + exr_header->tile_size_x, + exr_header->tile_rounding_mode); + + CalculateNumTiles(num_y_tiles, + max_y - min_y + 1, + exr_header->tile_size_y, + exr_header->tile_rounding_mode); +} + +static void InitSingleResolutionOffsets(OffsetData& offset_data, size_t num_blocks) { + offset_data.offsets.resize(1); + offset_data.offsets[0].resize(1); + offset_data.offsets[0][0].resize(num_blocks); + offset_data.num_x_levels = 1; + offset_data.num_y_levels = 1; +} + +// Return sum of tile blocks. +static int InitTileOffsets(OffsetData& offset_data, + const EXRHeader* exr_header, + const std::vector& num_x_tiles, + const std::vector& num_y_tiles) { + int num_tile_blocks = 0; + offset_data.num_x_levels = static_cast(num_x_tiles.size()); + offset_data.num_y_levels = static_cast(num_y_tiles.size()); + switch (exr_header->tile_level_mode) { + case TINYEXR_TILE_ONE_LEVEL: + case TINYEXR_TILE_MIPMAP_LEVELS: + assert(offset_data.num_x_levels == offset_data.num_y_levels); + offset_data.offsets.resize(offset_data.num_x_levels); + + for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) { + offset_data.offsets[l].resize(num_y_tiles[l]); + + for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) { + offset_data.offsets[l][dy].resize(num_x_tiles[l]); + num_tile_blocks += num_x_tiles[l]; + } + } + break; + + case TINYEXR_TILE_RIPMAP_LEVELS: + + offset_data.offsets.resize(static_cast(offset_data.num_x_levels) * static_cast(offset_data.num_y_levels)); + + for (int ly = 0; ly < offset_data.num_y_levels; ++ly) { + for (int lx = 0; lx < offset_data.num_x_levels; ++lx) { + int l = ly * offset_data.num_x_levels + lx; + offset_data.offsets[l].resize(num_y_tiles[ly]); + + for (size_t dy = 0; dy < offset_data.offsets[l].size(); ++dy) { + offset_data.offsets[l][dy].resize(num_x_tiles[lx]); + num_tile_blocks += num_x_tiles[lx]; + } + } + } + break; + + default: + assert(false); + } + return num_tile_blocks; +} + +static bool IsAnyOffsetsAreInvalid(const OffsetData& offset_data) { + for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) + for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) + for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) + if (reinterpret_cast(offset_data.offsets[l][dy][dx]) <= 0) + return true; + + return false; +} + +static bool isValidTile(const EXRHeader* exr_header, + const OffsetData& offset_data, + int dx, int dy, int lx, int ly) { + if (lx < 0 || ly < 0 || dx < 0 || dy < 0) return false; + int num_x_levels = offset_data.num_x_levels; + int num_y_levels = offset_data.num_y_levels; + switch (exr_header->tile_level_mode) { + case TINYEXR_TILE_ONE_LEVEL: + + if (lx == 0 && + ly == 0 && + offset_data.offsets.size() > 0 && + offset_data.offsets[0].size() > static_cast(dy) && + offset_data.offsets[0][dy].size() > static_cast(dx)) { + return true; + } + + break; + + case TINYEXR_TILE_MIPMAP_LEVELS: + + if (lx < num_x_levels && + ly < num_y_levels && + offset_data.offsets.size() > static_cast(lx) && + offset_data.offsets[lx].size() > static_cast(dy) && + offset_data.offsets[lx][dy].size() > static_cast(dx)) { + return true; + } + + break; + + case TINYEXR_TILE_RIPMAP_LEVELS: + { + size_t idx = static_cast(lx) + static_cast(ly)* static_cast(num_x_levels); + if (lx < num_x_levels && + ly < num_y_levels && + (offset_data.offsets.size() > idx) && + offset_data.offsets[idx].size() > static_cast(dy) && + offset_data.offsets[idx][dy].size() > static_cast(dx)) { + return true; + } + } + + break; + + default: + + return false; + } + + return false; +} + +static void ReconstructTileOffsets(OffsetData& offset_data, + const EXRHeader* exr_header, + const unsigned char* head, const unsigned char* marker, const size_t /*size*/, + bool isMultiPartFile, + bool isDeep) { + int numXLevels = offset_data.num_x_levels; + for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) { + for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) { + for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) { + tinyexr::tinyexr_uint64 tileOffset = marker - head; + + if (isMultiPartFile) { + //int partNumber; + marker += sizeof(int); + } + + int tileX; + memcpy(&tileX, marker, sizeof(int)); + tinyexr::swap4(&tileX); + marker += sizeof(int); + + int tileY; + memcpy(&tileY, marker, sizeof(int)); + tinyexr::swap4(&tileY); + marker += sizeof(int); + + int levelX; + memcpy(&levelX, marker, sizeof(int)); + tinyexr::swap4(&levelX); + marker += sizeof(int); + + int levelY; + memcpy(&levelY, marker, sizeof(int)); + tinyexr::swap4(&levelY); + marker += sizeof(int); + + if (isDeep) { + tinyexr::tinyexr_int64 packed_offset_table_size; + memcpy(&packed_offset_table_size, marker, sizeof(tinyexr::tinyexr_int64)); + tinyexr::swap8(reinterpret_cast(&packed_offset_table_size)); + marker += sizeof(tinyexr::tinyexr_int64); + + tinyexr::tinyexr_int64 packed_sample_size; + memcpy(&packed_sample_size, marker, sizeof(tinyexr::tinyexr_int64)); + tinyexr::swap8(reinterpret_cast(&packed_sample_size)); + marker += sizeof(tinyexr::tinyexr_int64); + + // next Int64 is unpacked sample size - skip that too + marker += packed_offset_table_size + packed_sample_size + 8; + + } else { + + int dataSize; + memcpy(&dataSize, marker, sizeof(int)); + tinyexr::swap4(&dataSize); + marker += sizeof(int); + marker += dataSize; + } + + if (!isValidTile(exr_header, offset_data, + tileX, tileY, levelX, levelY)) + return; + + int level_idx = LevelIndex(levelX, levelY, exr_header->tile_level_mode, numXLevels); + offset_data.offsets[level_idx][tileY][tileX] = tileOffset; + } + } + } +} + +// marker output is also +static int ReadOffsets(OffsetData& offset_data, + const unsigned char* head, + const unsigned char*& marker, + const size_t size, + const char** err) { + for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) { + for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) { + for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) { + tinyexr::tinyexr_uint64 offset; + if ((marker + sizeof(tinyexr_uint64)) >= (head + size)) { + tinyexr::SetErrorMessage("Insufficient data size in offset table.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + + memcpy(&offset, marker, sizeof(tinyexr::tinyexr_uint64)); + tinyexr::swap8(&offset); + if (offset >= size) { + tinyexr::SetErrorMessage("Invalid offset value in DecodeEXRImage.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + marker += sizeof(tinyexr::tinyexr_uint64); // = 8 + offset_data.offsets[l][dy][dx] = offset; + } + } + } + return TINYEXR_SUCCESS; +} + +static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, + const unsigned char *head, + const unsigned char *marker, const size_t size, + const char **err) { + if (exr_image == NULL || exr_header == NULL || head == NULL || + marker == NULL || (size <= tinyexr::kEXRVersionSize)) { + tinyexr::SetErrorMessage("Invalid argument for DecodeEXRImage().", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + int num_scanline_blocks = 1; + if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) { + num_scanline_blocks = 16; + } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { + num_scanline_blocks = 32; + } else if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { + num_scanline_blocks = 16; + } + + if (exr_header->data_window.max_x < exr_header->data_window.min_x || + exr_header->data_window.max_x - exr_header->data_window.min_x == + std::numeric_limits::max()) { + // Issue 63 + tinyexr::SetErrorMessage("Invalid data width value", err); + return TINYEXR_ERROR_INVALID_DATA; + } + int data_width = + exr_header->data_window.max_x - exr_header->data_window.min_x + 1; + + if (exr_header->data_window.max_y < exr_header->data_window.min_y || + exr_header->data_window.max_y - exr_header->data_window.min_y == + std::numeric_limits::max()) { + tinyexr::SetErrorMessage("Invalid data height value", err); + return TINYEXR_ERROR_INVALID_DATA; + } + int data_height = + exr_header->data_window.max_y - exr_header->data_window.min_y + 1; + + // Do not allow too large data_width and data_height. header invalid? + { + if (data_width > TINYEXR_DIMENSION_THRESHOLD) { + tinyexr::SetErrorMessage("data width too large.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + if (data_height > TINYEXR_DIMENSION_THRESHOLD) { + tinyexr::SetErrorMessage("data height too large.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + } + + if (exr_header->tiled) { + if (exr_header->tile_size_x > TINYEXR_DIMENSION_THRESHOLD) { + tinyexr::SetErrorMessage("tile width too large.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + if (exr_header->tile_size_y > TINYEXR_DIMENSION_THRESHOLD) { + tinyexr::SetErrorMessage("tile height too large.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + } + + // Read offset tables. + OffsetData offset_data; + size_t num_blocks = 0; + // For a multi-resolution image, the size of the offset table will be calculated from the other attributes of the header. + // If chunk_count > 0 then chunk_count must be equal to the calculated tile count. + if (exr_header->tiled) { + { + std::vector num_x_tiles, num_y_tiles; + PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_header); + num_blocks = InitTileOffsets(offset_data, exr_header, num_x_tiles, num_y_tiles); + if (exr_header->chunk_count > 0) { + if (exr_header->chunk_count != static_cast(num_blocks)) { + tinyexr::SetErrorMessage("Invalid offset table size.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + } + } + + int ret = ReadOffsets(offset_data, head, marker, size, err); + if (ret != TINYEXR_SUCCESS) return ret; + if (IsAnyOffsetsAreInvalid(offset_data)) { + ReconstructTileOffsets(offset_data, exr_header, + head, marker, size, + exr_header->multipart, exr_header->non_image); + } + } else if (exr_header->chunk_count > 0) { + // Use `chunkCount` attribute. + num_blocks = static_cast(exr_header->chunk_count); + InitSingleResolutionOffsets(offset_data, num_blocks); + } else { + num_blocks = static_cast(data_height) / + static_cast(num_scanline_blocks); + if (num_blocks * static_cast(num_scanline_blocks) < + static_cast(data_height)) { + num_blocks++; + } + + InitSingleResolutionOffsets(offset_data, num_blocks); + } + + if (!exr_header->tiled) { + std::vector& offsets = offset_data.offsets[0][0]; + for (size_t y = 0; y < num_blocks; y++) { + tinyexr::tinyexr_uint64 offset; + // Issue #81 + if ((marker + sizeof(tinyexr_uint64)) >= (head + size)) { + tinyexr::SetErrorMessage("Insufficient data size in offset table.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + + memcpy(&offset, marker, sizeof(tinyexr::tinyexr_uint64)); + tinyexr::swap8(&offset); + if (offset >= size) { + tinyexr::SetErrorMessage("Invalid offset value in DecodeEXRImage.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + marker += sizeof(tinyexr::tinyexr_uint64); // = 8 + offsets[y] = offset; + } + + // If line offsets are invalid, we try to reconstruct it. + // See OpenEXR/IlmImf/ImfScanLineInputFile.cpp::readLineOffsets() for details. + for (size_t y = 0; y < num_blocks; y++) { + if (offsets[y] <= 0) { + // TODO(syoyo) Report as warning? + // if (err) { + // stringstream ss; + // ss << "Incomplete lineOffsets." << std::endl; + // (*err) += ss.str(); + //} + bool ret = + ReconstructLineOffsets(&offsets, num_blocks, head, marker, size); + if (ret) { + // OK + break; + } else { + tinyexr::SetErrorMessage( + "Cannot reconstruct lineOffset table in DecodeEXRImage.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + } + } + } + + { + std::string e; + int ret = DecodeChunk(exr_image, exr_header, offset_data, head, size, &e); + + if (ret != TINYEXR_SUCCESS) { + if (!e.empty()) { + tinyexr::SetErrorMessage(e, err); + } + +#if 1 + FreeEXRImage(exr_image); +#else + // release memory(if exists) + if ((exr_header->num_channels > 0) && exr_image && exr_image->images) { + for (size_t c = 0; c < size_t(exr_header->num_channels); c++) { + if (exr_image->images[c]) { + free(exr_image->images[c]); + exr_image->images[c] = NULL; + } + } + free(exr_image->images); + exr_image->images = NULL; + } +#endif + } + + return ret; + } +} + +static void GetLayers(const EXRHeader &exr_header, + std::vector &layer_names) { + // Naive implementation + // Group channels by layers + // go over all channel names, split by periods + // collect unique names + layer_names.clear(); + for (int c = 0; c < exr_header.num_channels; c++) { + std::string full_name(exr_header.channels[c].name); + const size_t pos = full_name.find_last_of('.'); + if (pos != std::string::npos && pos != 0 && pos + 1 < full_name.size()) { + full_name.erase(pos); + if (std::find(layer_names.begin(), layer_names.end(), full_name) == + layer_names.end()) + layer_names.push_back(full_name); + } + } +} + +struct LayerChannel { + explicit LayerChannel(size_t i, std::string n) : index(i), name(n) {} + size_t index; + std::string name; +}; + +static void ChannelsInLayer(const EXRHeader &exr_header, + const std::string &layer_name, + std::vector &channels) { + channels.clear(); + for (int c = 0; c < exr_header.num_channels; c++) { + std::string ch_name(exr_header.channels[c].name); + if (layer_name.empty()) { + const size_t pos = ch_name.find_last_of('.'); + if (pos != std::string::npos && pos < ch_name.size()) { + ch_name = ch_name.substr(pos + 1); + } + } else { + const size_t pos = ch_name.find(layer_name + '.'); + if (pos == std::string::npos) continue; + if (pos == 0) { + ch_name = ch_name.substr(layer_name.size() + 1); + } + } + LayerChannel ch(size_t(c), ch_name); + channels.push_back(ch); + } +} + +} // namespace tinyexr + +int EXRLayers(const char *filename, const char **layer_names[], int *num_layers, + const char **err) { + EXRVersion exr_version; + EXRHeader exr_header; + InitEXRHeader(&exr_header); + + { + int ret = ParseEXRVersionFromFile(&exr_version, filename); + if (ret != TINYEXR_SUCCESS) { + tinyexr::SetErrorMessage("Invalid EXR header.", err); + return ret; + } + + if (exr_version.multipart || exr_version.non_image) { + tinyexr::SetErrorMessage( + "Loading multipart or DeepImage is not supported in LoadEXR() API", + err); + return TINYEXR_ERROR_INVALID_DATA; // @fixme. + } + } + + int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename, err); + if (ret != TINYEXR_SUCCESS) { + FreeEXRHeader(&exr_header); + return ret; + } + + std::vector layer_vec; + tinyexr::GetLayers(exr_header, layer_vec); + + (*num_layers) = int(layer_vec.size()); + (*layer_names) = static_cast( + malloc(sizeof(const char *) * static_cast(layer_vec.size()))); + for (size_t c = 0; c < static_cast(layer_vec.size()); c++) { +#ifdef _MSC_VER + (*layer_names)[c] = _strdup(layer_vec[c].c_str()); +#else + (*layer_names)[c] = strdup(layer_vec[c].c_str()); +#endif + } + + FreeEXRHeader(&exr_header); + return TINYEXR_SUCCESS; +} + +int LoadEXR(float **out_rgba, int *width, int *height, const char *filename, + const char **err) { + return LoadEXRWithLayer(out_rgba, width, height, filename, + /* layername */ NULL, err); +} + +int LoadEXRWithLayer(float **out_rgba, int *width, int *height, + const char *filename, const char *layername, + const char **err) { + if (out_rgba == NULL) { + tinyexr::SetErrorMessage("Invalid argument for LoadEXR()", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + EXRVersion exr_version; + EXRImage exr_image; + EXRHeader exr_header; + InitEXRHeader(&exr_header); + InitEXRImage(&exr_image); + + { + int ret = ParseEXRVersionFromFile(&exr_version, filename); + if (ret != TINYEXR_SUCCESS) { + std::stringstream ss; + ss << "Failed to open EXR file or read version info from EXR file. code(" + << ret << ")"; + tinyexr::SetErrorMessage(ss.str(), err); + return ret; + } + + if (exr_version.multipart || exr_version.non_image) { + tinyexr::SetErrorMessage( + "Loading multipart or DeepImage is not supported in LoadEXR() API", + err); + return TINYEXR_ERROR_INVALID_DATA; // @fixme. + } + } + + { + int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename, err); + if (ret != TINYEXR_SUCCESS) { + FreeEXRHeader(&exr_header); + return ret; + } + } + + // Read HALF channel as FLOAT. + for (int i = 0; i < exr_header.num_channels; i++) { + if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { + exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; + } + } + + // TODO: Probably limit loading to layers (channels) selected by layer index + { + int ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename, err); + if (ret != TINYEXR_SUCCESS) { + FreeEXRHeader(&exr_header); + return ret; + } + } + + // RGBA + int idxR = -1; + int idxG = -1; + int idxB = -1; + int idxA = -1; + + std::vector layer_names; + tinyexr::GetLayers(exr_header, layer_names); + + std::vector channels; + tinyexr::ChannelsInLayer( + exr_header, layername == NULL ? "" : std::string(layername), channels); + + if (channels.size() < 1) { + tinyexr::SetErrorMessage("Layer Not Found", err); + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + return TINYEXR_ERROR_LAYER_NOT_FOUND; + } + + size_t ch_count = channels.size() < 4 ? channels.size() : 4; + for (size_t c = 0; c < ch_count; c++) { + const tinyexr::LayerChannel &ch = channels[c]; + + if (ch.name == "R") { + idxR = int(ch.index); + } else if (ch.name == "G") { + idxG = int(ch.index); + } else if (ch.name == "B") { + idxB = int(ch.index); + } else if (ch.name == "A") { + idxA = int(ch.index); + } + } + + if (channels.size() == 1) { + int chIdx = int(channels.front().index); + // Grayscale channel only. + + (*out_rgba) = reinterpret_cast( + malloc(4 * sizeof(float) * static_cast(exr_image.width) * + static_cast(exr_image.height))); + + if (exr_header.tiled) { + for (int it = 0; it < exr_image.num_tiles; it++) { + for (int j = 0; j < exr_header.tile_size_y; j++) { + for (int i = 0; i < exr_header.tile_size_x; i++) { + const int ii = exr_image.tiles[it].offset_x * + static_cast(exr_header.tile_size_x) + + i; + const int jj = exr_image.tiles[it].offset_y * + static_cast(exr_header.tile_size_y) + + j; + const int idx = ii + jj * static_cast(exr_image.width); + + // out of region check. + if (ii >= exr_image.width) { + continue; + } + if (jj >= exr_image.height) { + continue; + } + const int srcIdx = i + j * exr_header.tile_size_x; + unsigned char **src = exr_image.tiles[it].images; + (*out_rgba)[4 * idx + 0] = + reinterpret_cast(src)[chIdx][srcIdx]; + (*out_rgba)[4 * idx + 1] = + reinterpret_cast(src)[chIdx][srcIdx]; + (*out_rgba)[4 * idx + 2] = + reinterpret_cast(src)[chIdx][srcIdx]; + (*out_rgba)[4 * idx + 3] = + reinterpret_cast(src)[chIdx][srcIdx]; + } + } + } + } else { + for (int i = 0; i < exr_image.width * exr_image.height; i++) { + const float val = + reinterpret_cast(exr_image.images)[chIdx][i]; + (*out_rgba)[4 * i + 0] = val; + (*out_rgba)[4 * i + 1] = val; + (*out_rgba)[4 * i + 2] = val; + (*out_rgba)[4 * i + 3] = val; + } + } + } else { + // Assume RGB(A) + + if (idxR == -1) { + tinyexr::SetErrorMessage("R channel not found", err); + + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + return TINYEXR_ERROR_INVALID_DATA; + } + + if (idxG == -1) { + tinyexr::SetErrorMessage("G channel not found", err); + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + return TINYEXR_ERROR_INVALID_DATA; + } + + if (idxB == -1) { + tinyexr::SetErrorMessage("B channel not found", err); + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + return TINYEXR_ERROR_INVALID_DATA; + } + + (*out_rgba) = reinterpret_cast( + malloc(4 * sizeof(float) * static_cast(exr_image.width) * + static_cast(exr_image.height))); + if (exr_header.tiled) { + for (int it = 0; it < exr_image.num_tiles; it++) { + for (int j = 0; j < exr_header.tile_size_y; j++) { + for (int i = 0; i < exr_header.tile_size_x; i++) { + const int ii = + exr_image.tiles[it].offset_x * exr_header.tile_size_x + i; + const int jj = + exr_image.tiles[it].offset_y * exr_header.tile_size_y + j; + const int idx = ii + jj * exr_image.width; + + // out of region check. + if (ii >= exr_image.width) { + continue; + } + if (jj >= exr_image.height) { + continue; + } + const int srcIdx = i + j * exr_header.tile_size_x; + unsigned char **src = exr_image.tiles[it].images; + (*out_rgba)[4 * idx + 0] = + reinterpret_cast(src)[idxR][srcIdx]; + (*out_rgba)[4 * idx + 1] = + reinterpret_cast(src)[idxG][srcIdx]; + (*out_rgba)[4 * idx + 2] = + reinterpret_cast(src)[idxB][srcIdx]; + if (idxA != -1) { + (*out_rgba)[4 * idx + 3] = + reinterpret_cast(src)[idxA][srcIdx]; + } else { + (*out_rgba)[4 * idx + 3] = 1.0; + } + } + } + } + } else { + for (int i = 0; i < exr_image.width * exr_image.height; i++) { + (*out_rgba)[4 * i + 0] = + reinterpret_cast(exr_image.images)[idxR][i]; + (*out_rgba)[4 * i + 1] = + reinterpret_cast(exr_image.images)[idxG][i]; + (*out_rgba)[4 * i + 2] = + reinterpret_cast(exr_image.images)[idxB][i]; + if (idxA != -1) { + (*out_rgba)[4 * i + 3] = + reinterpret_cast(exr_image.images)[idxA][i]; + } else { + (*out_rgba)[4 * i + 3] = 1.0; + } + } + } + } + + (*width) = exr_image.width; + (*height) = exr_image.height; + + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + + return TINYEXR_SUCCESS; +} + +int IsEXR(const char *filename) { + EXRVersion exr_version; + + int ret = ParseEXRVersionFromFile(&exr_version, filename); + if (ret != TINYEXR_SUCCESS) { + return ret; + } + + return TINYEXR_SUCCESS; +} + +int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version, + const unsigned char *memory, size_t size, + const char **err) { + if (memory == NULL || exr_header == NULL) { + tinyexr::SetErrorMessage( + "Invalid argument. `memory` or `exr_header` argument is null in " + "ParseEXRHeaderFromMemory()", + err); + + // Invalid argument + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + if (size < tinyexr::kEXRVersionSize) { + tinyexr::SetErrorMessage("Insufficient header/data size.\n", err); + return TINYEXR_ERROR_INVALID_DATA; + } + + const unsigned char *marker = memory + tinyexr::kEXRVersionSize; + size_t marker_size = size - tinyexr::kEXRVersionSize; + + tinyexr::HeaderInfo info; + info.clear(); + + std::string err_str; + int ret = ParseEXRHeader(&info, NULL, version, &err_str, marker, marker_size); + + if (ret != TINYEXR_SUCCESS) { + if (err && !err_str.empty()) { + tinyexr::SetErrorMessage(err_str, err); + } + } + + ConvertHeader(exr_header, info); + + exr_header->multipart = version->multipart ? 1 : 0; + exr_header->non_image = version->non_image ? 1 : 0; + + return ret; +} + +int LoadEXRFromMemory(float **out_rgba, int *width, int *height, + const unsigned char *memory, size_t size, + const char **err) { + if (out_rgba == NULL || memory == NULL) { + tinyexr::SetErrorMessage("Invalid argument for LoadEXRFromMemory", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + EXRVersion exr_version; + EXRImage exr_image; + EXRHeader exr_header; + + InitEXRHeader(&exr_header); + + int ret = ParseEXRVersionFromMemory(&exr_version, memory, size); + if (ret != TINYEXR_SUCCESS) { + std::stringstream ss; + ss << "Failed to parse EXR version. code(" << ret << ")"; + tinyexr::SetErrorMessage(ss.str(), err); + return ret; + } + + ret = ParseEXRHeaderFromMemory(&exr_header, &exr_version, memory, size, err); + if (ret != TINYEXR_SUCCESS) { + return ret; + } + + // Read HALF channel as FLOAT. + for (int i = 0; i < exr_header.num_channels; i++) { + if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { + exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; + } + } + + InitEXRImage(&exr_image); + ret = LoadEXRImageFromMemory(&exr_image, &exr_header, memory, size, err); + if (ret != TINYEXR_SUCCESS) { + return ret; + } + + // RGBA + int idxR = -1; + int idxG = -1; + int idxB = -1; + int idxA = -1; + for (int c = 0; c < exr_header.num_channels; c++) { + if (strcmp(exr_header.channels[c].name, "R") == 0) { + idxR = c; + } else if (strcmp(exr_header.channels[c].name, "G") == 0) { + idxG = c; + } else if (strcmp(exr_header.channels[c].name, "B") == 0) { + idxB = c; + } else if (strcmp(exr_header.channels[c].name, "A") == 0) { + idxA = c; + } + } + + // TODO(syoyo): Refactor removing same code as used in LoadEXR(). + if (exr_header.num_channels == 1) { + // Grayscale channel only. + + (*out_rgba) = reinterpret_cast( + malloc(4 * sizeof(float) * static_cast(exr_image.width) * + static_cast(exr_image.height))); + + if (exr_header.tiled) { + for (int it = 0; it < exr_image.num_tiles; it++) { + for (int j = 0; j < exr_header.tile_size_y; j++) { + for (int i = 0; i < exr_header.tile_size_x; i++) { + const int ii = + exr_image.tiles[it].offset_x * exr_header.tile_size_x + i; + const int jj = + exr_image.tiles[it].offset_y * exr_header.tile_size_y + j; + const int idx = ii + jj * exr_image.width; + + // out of region check. + if (ii >= exr_image.width) { + continue; + } + if (jj >= exr_image.height) { + continue; + } + const int srcIdx = i + j * exr_header.tile_size_x; + unsigned char **src = exr_image.tiles[it].images; + (*out_rgba)[4 * idx + 0] = + reinterpret_cast(src)[0][srcIdx]; + (*out_rgba)[4 * idx + 1] = + reinterpret_cast(src)[0][srcIdx]; + (*out_rgba)[4 * idx + 2] = + reinterpret_cast(src)[0][srcIdx]; + (*out_rgba)[4 * idx + 3] = + reinterpret_cast(src)[0][srcIdx]; + } + } + } + } else { + for (int i = 0; i < exr_image.width * exr_image.height; i++) { + const float val = reinterpret_cast(exr_image.images)[0][i]; + (*out_rgba)[4 * i + 0] = val; + (*out_rgba)[4 * i + 1] = val; + (*out_rgba)[4 * i + 2] = val; + (*out_rgba)[4 * i + 3] = val; + } + } + + } else { + // TODO(syoyo): Support non RGBA image. + + if (idxR == -1) { + tinyexr::SetErrorMessage("R channel not found", err); + + // @todo { free exr_image } + return TINYEXR_ERROR_INVALID_DATA; + } + + if (idxG == -1) { + tinyexr::SetErrorMessage("G channel not found", err); + // @todo { free exr_image } + return TINYEXR_ERROR_INVALID_DATA; + } + + if (idxB == -1) { + tinyexr::SetErrorMessage("B channel not found", err); + // @todo { free exr_image } + return TINYEXR_ERROR_INVALID_DATA; + } + + (*out_rgba) = reinterpret_cast( + malloc(4 * sizeof(float) * static_cast(exr_image.width) * + static_cast(exr_image.height))); + + if (exr_header.tiled) { + for (int it = 0; it < exr_image.num_tiles; it++) { + for (int j = 0; j < exr_header.tile_size_y; j++) + for (int i = 0; i < exr_header.tile_size_x; i++) { + const int ii = + exr_image.tiles[it].offset_x * exr_header.tile_size_x + i; + const int jj = + exr_image.tiles[it].offset_y * exr_header.tile_size_y + j; + const int idx = ii + jj * exr_image.width; + + // out of region check. + if (ii >= exr_image.width) { + continue; + } + if (jj >= exr_image.height) { + continue; + } + const int srcIdx = i + j * exr_header.tile_size_x; + unsigned char **src = exr_image.tiles[it].images; + (*out_rgba)[4 * idx + 0] = + reinterpret_cast(src)[idxR][srcIdx]; + (*out_rgba)[4 * idx + 1] = + reinterpret_cast(src)[idxG][srcIdx]; + (*out_rgba)[4 * idx + 2] = + reinterpret_cast(src)[idxB][srcIdx]; + if (idxA != -1) { + (*out_rgba)[4 * idx + 3] = + reinterpret_cast(src)[idxA][srcIdx]; + } else { + (*out_rgba)[4 * idx + 3] = 1.0; + } + } + } + } else { + for (int i = 0; i < exr_image.width * exr_image.height; i++) { + (*out_rgba)[4 * i + 0] = + reinterpret_cast(exr_image.images)[idxR][i]; + (*out_rgba)[4 * i + 1] = + reinterpret_cast(exr_image.images)[idxG][i]; + (*out_rgba)[4 * i + 2] = + reinterpret_cast(exr_image.images)[idxB][i]; + if (idxA != -1) { + (*out_rgba)[4 * i + 3] = + reinterpret_cast(exr_image.images)[idxA][i]; + } else { + (*out_rgba)[4 * i + 3] = 1.0; + } + } + } + } + + (*width) = exr_image.width; + (*height) = exr_image.height; + + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + + return TINYEXR_SUCCESS; +} + +int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header, + const char *filename, const char **err) { + if (exr_image == NULL) { + tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromFile", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + FILE *fp = NULL; +#ifdef _WIN32 +#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang. + errno_t errcode = + _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + // TODO(syoyo): return wfopen_s erro code + return TINYEXR_ERROR_CANT_OPEN_FILE; + } +#else + // Unknown compiler or MinGW without MINGW_HAS_SECURE_API. + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); +#endif + if (!fp) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } + + size_t filesize; + // Compute size + fseek(fp, 0, SEEK_END); + filesize = static_cast(ftell(fp)); + fseek(fp, 0, SEEK_SET); + + if (filesize < 16) { + tinyexr::SetErrorMessage("File size too short " + std::string(filename), + err); + return TINYEXR_ERROR_INVALID_FILE; + } + + std::vector buf(filesize); // @todo { use mmap } + { + size_t ret; + ret = fread(&buf[0], 1, filesize, fp); + assert(ret == filesize); + fclose(fp); + (void)ret; + } + + return LoadEXRImageFromMemory(exr_image, exr_header, &buf.at(0), filesize, + err); +} + +int LoadEXRImageFromMemory(EXRImage *exr_image, const EXRHeader *exr_header, + const unsigned char *memory, const size_t size, + const char **err) { + if (exr_image == NULL || memory == NULL || + (size < tinyexr::kEXRVersionSize)) { + tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromMemory", + err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + if (exr_header->header_len == 0) { + tinyexr::SetErrorMessage("EXRHeader variable is not initialized.", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + const unsigned char *head = memory; + const unsigned char *marker = reinterpret_cast( + memory + exr_header->header_len + + 8); // +8 for magic number + version header. + return tinyexr::DecodeEXRImage(exr_image, exr_header, head, marker, size, + err); +} + +namespace tinyexr +{ + +// out_data must be allocated initially with the block-header size +// of the current image(-part) type +static bool EncodePixelData(/* out */ std::vector& out_data, + const unsigned char* const* images, + int compression_type, + int /*line_order*/, + int width, // for tiled : tile.width + int /*height*/, // for tiled : header.tile_size_y + int x_stride, // for tiled : header.tile_size_x + int line_no, // for tiled : 0 + int num_lines, // for tiled : tile.height + size_t pixel_data_size, + const std::vector& channels, + const std::vector& channel_offset_list, + const void* compression_param = 0) // zfp compression param +{ + size_t buf_size = static_cast(width) * + static_cast(num_lines) * + static_cast(pixel_data_size); + //int last2bit = (buf_size & 3); + // buf_size must be multiple of four + //if(last2bit) buf_size += 4 - last2bit; + std::vector buf(buf_size); + + size_t start_y = static_cast(line_no); + for (size_t c = 0; c < channels.size(); c++) { + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { + if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + for (int y = 0; y < num_lines; y++) { + // Assume increasing Y + float *line_ptr = reinterpret_cast(&buf.at( + static_cast(pixel_data_size * y * width) + + channel_offset_list[c] * + static_cast(width))); + for (int x = 0; x < width; x++) { + tinyexr::FP16 h16; + h16.u = reinterpret_cast( + images)[c][(y + start_y) * x_stride + x]; + + tinyexr::FP32 f32 = half_to_float(h16); + + tinyexr::swap4(&f32.f); + + // line_ptr[x] = f32.f; + tinyexr::cpy4(line_ptr + x, &(f32.f)); + } + } + } else if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) { + for (int y = 0; y < num_lines; y++) { + // Assume increasing Y + unsigned short *line_ptr = reinterpret_cast( + &buf.at(static_cast(pixel_data_size * y * + width) + + channel_offset_list[c] * + static_cast(width))); + for (int x = 0; x < width; x++) { + unsigned short val = reinterpret_cast( + images)[c][(y + start_y) * x_stride + x]; + + tinyexr::swap2(&val); + + // line_ptr[x] = val; + tinyexr::cpy2(line_ptr + x, &val); + } + } + } else { + assert(0); + } + + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) { + for (int y = 0; y < num_lines; y++) { + // Assume increasing Y + unsigned short *line_ptr = reinterpret_cast( + &buf.at(static_cast(pixel_data_size * y * + width) + + channel_offset_list[c] * + static_cast(width))); + for (int x = 0; x < width; x++) { + tinyexr::FP32 f32; + f32.f = reinterpret_cast( + images)[c][(y + start_y) * x_stride + x]; + + tinyexr::FP16 h16; + h16 = float_to_half_full(f32); + + tinyexr::swap2(reinterpret_cast(&h16.u)); + + // line_ptr[x] = h16.u; + tinyexr::cpy2(line_ptr + x, &(h16.u)); + } + } + } else if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + for (int y = 0; y < num_lines; y++) { + // Assume increasing Y + float *line_ptr = reinterpret_cast(&buf.at( + static_cast(pixel_data_size * y * width) + + channel_offset_list[c] * + static_cast(width))); + for (int x = 0; x < width; x++) { + float val = reinterpret_cast( + images)[c][(y + start_y) * x_stride + x]; + + tinyexr::swap4(&val); + + // line_ptr[x] = val; + tinyexr::cpy4(line_ptr + x, &val); + } + } + } else { + assert(0); + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { + for (int y = 0; y < num_lines; y++) { + // Assume increasing Y + unsigned int *line_ptr = reinterpret_cast(&buf.at( + static_cast(pixel_data_size * y * width) + + channel_offset_list[c] * static_cast(width))); + for (int x = 0; x < width; x++) { + unsigned int val = reinterpret_cast( + images)[c][(y + start_y) * x_stride + x]; + + tinyexr::swap4(&val); + + // line_ptr[x] = val; + tinyexr::cpy4(line_ptr + x, &val); + } + } + } + } + + if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) { + // 4 byte: scan line + // 4 byte: data size + // ~ : pixel data(uncompressed) + out_data.insert(out_data.end(), buf.begin(), buf.end()); + + } else if ((compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) || + (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) { +#if TINYEXR_USE_MINIZ + std::vector block(mz_compressBound( + static_cast(buf.size()))); +#else + std::vector block( + compressBound(static_cast(buf.size()))); +#endif + tinyexr::tinyexr_uint64 outSize = block.size(); + + tinyexr::CompressZip(&block.at(0), outSize, + reinterpret_cast(&buf.at(0)), + static_cast(buf.size())); + + // 4 byte: scan line + // 4 byte: data size + // ~ : pixel data(compressed) + unsigned int data_len = static_cast(outSize); // truncate + + out_data.insert(out_data.end(), block.begin(), block.begin() + data_len); + + } else if (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) { + // (buf.size() * 3) / 2 would be enough. + std::vector block((buf.size() * 3) / 2); + + tinyexr::tinyexr_uint64 outSize = block.size(); + + tinyexr::CompressRle(&block.at(0), outSize, + reinterpret_cast(&buf.at(0)), + static_cast(buf.size())); + + // 4 byte: scan line + // 4 byte: data size + // ~ : pixel data(compressed) + unsigned int data_len = static_cast(outSize); // truncate + out_data.insert(out_data.end(), block.begin(), block.begin() + data_len); + + } else if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { +#if TINYEXR_USE_PIZ + unsigned int bufLen = + 8192 + static_cast( + 2 * static_cast( + buf.size())); // @fixme { compute good bound. } + std::vector block(bufLen); + unsigned int outSize = static_cast(block.size()); + + CompressPiz(&block.at(0), &outSize, + reinterpret_cast(&buf.at(0)), + buf.size(), channels, width, num_lines); + + // 4 byte: scan line + // 4 byte: data size + // ~ : pixel data(compressed) + unsigned int data_len = outSize; + out_data.insert(out_data.end(), block.begin(), block.begin() + data_len); + +#else + assert(0); +#endif + } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { +#if TINYEXR_USE_ZFP + const ZFPCompressionParam* zfp_compression_param = reinterpret_cast(compression_param); + std::vector block; + unsigned int outSize; + + tinyexr::CompressZfp( + &block, &outSize, reinterpret_cast(&buf.at(0)), + width, num_lines, static_cast(channels.size()), *zfp_compression_param); + + // 4 byte: scan line + // 4 byte: data size + // ~ : pixel data(compressed) + unsigned int data_len = outSize; + out_data.insert(out_data.end(), block.begin(), block.begin() + data_len); + +#else + (void)compression_param; + assert(0); +#endif + } else { + assert(0); + return false; + } + + return true; +} + +static int EncodeTiledLevel(const EXRImage* level_image, const EXRHeader* exr_header, + const std::vector& channels, + std::vector >& data_list, + size_t start_index, // for data_list + int num_x_tiles, int num_y_tiles, + const std::vector& channel_offset_list, + int pixel_data_size, + const void* compression_param, // must be set if zfp compression is enabled + std::string* err) { + int num_tiles = num_x_tiles * num_y_tiles; + assert(num_tiles == level_image->num_tiles); + + if ((exr_header->tile_size_x > level_image->width || exr_header->tile_size_y > level_image->height) && + level_image->level_x == 0 && level_image->level_y == 0) { + if (err) { + (*err) += "Failed to encode tile data.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + + +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + std::atomic invalid_data(false); +#else + bool invalid_data(false); +#endif + +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + std::vector workers; + std::atomic tile_count(0); + + int num_threads = std::max(1, int(std::thread::hardware_concurrency())); + if (num_threads > int(num_tiles)) { + num_threads = int(num_tiles); + } + + for (int t = 0; t < num_threads; t++) { + workers.emplace_back(std::thread([&]() { + int i = 0; + while ((i = tile_count++) < num_tiles) { + +#else + // Use signed int since some OpenMP compiler doesn't allow unsigned type for + // `parallel for` +#if TINYEXR_USE_OPENMP +#pragma omp parallel for +#endif + for (int i = 0; i < num_tiles; i++) { + +#endif + size_t tile_idx = static_cast(i); + size_t data_idx = tile_idx + start_index; + + int x_tile = i % num_x_tiles; + int y_tile = i / num_x_tiles; + + EXRTile& tile = level_image->tiles[tile_idx]; + + const unsigned char* const* images = + static_cast(tile.images); + + data_list[data_idx].resize(5*sizeof(int)); + size_t data_header_size = data_list[data_idx].size(); + bool ret = EncodePixelData(data_list[data_idx], + images, + exr_header->compression_type, + 0, // increasing y + tile.width, + exr_header->tile_size_y, + exr_header->tile_size_x, + 0, + tile.height, + pixel_data_size, + channels, + channel_offset_list, + compression_param); + if (!ret) { + invalid_data = true; + continue; + } + assert(data_list[data_idx].size() > data_header_size); + int data_len = static_cast(data_list[data_idx].size() - data_header_size); + //tileX, tileY, levelX, levelY // pixel_data_size(int) + memcpy(&data_list[data_idx][0], &x_tile, sizeof(int)); + memcpy(&data_list[data_idx][4], &y_tile, sizeof(int)); + memcpy(&data_list[data_idx][8], &level_image->level_x, sizeof(int)); + memcpy(&data_list[data_idx][12], &level_image->level_y, sizeof(int)); + memcpy(&data_list[data_idx][16], &data_len, sizeof(int)); + + swap4(reinterpret_cast(&data_list[data_idx][0])); + swap4(reinterpret_cast(&data_list[data_idx][4])); + swap4(reinterpret_cast(&data_list[data_idx][8])); + swap4(reinterpret_cast(&data_list[data_idx][12])); + swap4(reinterpret_cast(&data_list[data_idx][16])); + +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + } +})); + } + + for (auto &t : workers) { + t.join(); + } +#else + } // omp parallel +#endif + + if (invalid_data) { + if (err) { + (*err) += "Failed to encode tile data.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + return TINYEXR_SUCCESS; +} + +static int NumScanlines(int compression_type) { + int num_scanlines = 1; + if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) { + num_scanlines = 16; + } else if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { + num_scanlines = 32; + } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { + num_scanlines = 16; + } + return num_scanlines; +} + +static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header, + const std::vector& channels, + int num_blocks, + tinyexr_uint64 chunk_offset, // starting offset of current chunk + bool is_multipart, + OffsetData& offset_data, // output block offsets, must be initialized + std::vector >& data_list, // output + tinyexr_uint64& total_size, // output: ending offset of current chunk + std::string* err) { + int num_scanlines = NumScanlines(exr_header->compression_type); + + data_list.resize(num_blocks); + + std::vector channel_offset_list( + static_cast(exr_header->num_channels)); + + int pixel_data_size = 0; + { + size_t channel_offset = 0; + for (size_t c = 0; c < static_cast(exr_header->num_channels); c++) { + channel_offset_list[c] = channel_offset; + if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_HALF) { + pixel_data_size += sizeof(unsigned short); + channel_offset += sizeof(unsigned short); + } else if (channels[c].requested_pixel_type == + TINYEXR_PIXELTYPE_FLOAT) { + pixel_data_size += sizeof(float); + channel_offset += sizeof(float); + } else if (channels[c].requested_pixel_type == TINYEXR_PIXELTYPE_UINT) { + pixel_data_size += sizeof(unsigned int); + channel_offset += sizeof(unsigned int); + } else { + assert(0); + } + } + } + + const void* compression_param = 0; +#if TINYEXR_USE_ZFP + tinyexr::ZFPCompressionParam zfp_compression_param; + + // Use ZFP compression parameter from custom attributes(if such a parameter + // exists) + { + std::string e; + bool ret = tinyexr::FindZFPCompressionParam( + &zfp_compression_param, exr_header->custom_attributes, + exr_header->num_custom_attributes, &e); + + if (!ret) { + // Use predefined compression parameter. + zfp_compression_param.type = 0; + zfp_compression_param.rate = 2; + } + compression_param = &zfp_compression_param; + } +#endif + + tinyexr_uint64 offset = chunk_offset; + tinyexr_uint64 doffset = is_multipart ? 4u : 0u; + + if (exr_image->tiles) { + const EXRImage* level_image = exr_image; + size_t block_idx = 0; + tinyexr::tinyexr_uint64 block_data_size = 0; + int num_levels = (exr_header->tile_level_mode != TINYEXR_TILE_RIPMAP_LEVELS) ? + offset_data.num_x_levels : (offset_data.num_x_levels * offset_data.num_y_levels); + for (int level_index = 0; level_index < num_levels; ++level_index) { + if (!level_image) { + if (err) { + (*err) += "Invalid number of tiled levels for EncodeChunk\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + + int level_index_from_image = LevelIndex(level_image->level_x, level_image->level_y, + exr_header->tile_level_mode, offset_data.num_x_levels); + if (level_index_from_image != level_index) { + if (err) { + (*err) += "Incorrect level ordering in tiled image\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + int num_y_tiles = (int)offset_data.offsets[level_index].size(); + assert(num_y_tiles); + int num_x_tiles = (int)offset_data.offsets[level_index][0].size(); + assert(num_x_tiles); + + std::string e; + int ret = EncodeTiledLevel(level_image, + exr_header, + channels, + data_list, + block_idx, + num_x_tiles, + num_y_tiles, + channel_offset_list, + pixel_data_size, + compression_param, + &e); + if (ret != TINYEXR_SUCCESS) { + if (!e.empty() && err) { + (*err) += e; + } + return ret; + } + + for (size_t j = 0; j < static_cast(num_y_tiles); ++j) + for (size_t i = 0; i < static_cast(num_x_tiles); ++i) { + offset_data.offsets[level_index][j][i] = offset; + swap8(reinterpret_cast(&offset_data.offsets[level_index][j][i])); + offset += data_list[block_idx].size() + doffset; + block_data_size += data_list[block_idx].size(); + ++block_idx; + } + level_image = level_image->next_level; + } + assert(static_cast(block_idx) == num_blocks); + total_size = offset; + } else { // scanlines + std::vector& offsets = offset_data.offsets[0][0]; + +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + std::atomic invalid_data(false); + std::vector workers; + std::atomic block_count(0); + + int num_threads = std::min(std::max(1, int(std::thread::hardware_concurrency())), num_blocks); + + for (int t = 0; t < num_threads; t++) { + workers.emplace_back(std::thread([&]() { + int i = 0; + while ((i = block_count++) < num_blocks) { + +#else + bool invalid_data(false); +#if TINYEXR_USE_OPENMP +#pragma omp parallel for +#endif + for (int i = 0; i < num_blocks; i++) { + +#endif + int start_y = num_scanlines * i; + int end_Y = (std::min)(num_scanlines * (i + 1), exr_image->height); + int num_lines = end_Y - start_y; + + const unsigned char* const* images = + static_cast(exr_image->images); + + data_list[i].resize(2*sizeof(int)); + size_t data_header_size = data_list[i].size(); + + bool ret = EncodePixelData(data_list[i], + images, + exr_header->compression_type, + 0, // increasing y + exr_image->width, + exr_image->height, + exr_image->width, + start_y, + num_lines, + pixel_data_size, + channels, + channel_offset_list, + compression_param); + if (!ret) { + invalid_data = true; + continue; // "break" cannot be used with OpenMP + } + assert(data_list[i].size() > data_header_size); + int data_len = static_cast(data_list[i].size() - data_header_size); + memcpy(&data_list[i][0], &start_y, sizeof(int)); + memcpy(&data_list[i][4], &data_len, sizeof(int)); + + swap4(reinterpret_cast(&data_list[i][0])); + swap4(reinterpret_cast(&data_list[i][4])); +#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0) + } + })); + } + + for (auto &t : workers) { + t.join(); + } +#else + } // omp parallel +#endif + + if (invalid_data) { + if (err) { + (*err) += "Failed to encode scanline data.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + + for (size_t i = 0; i < static_cast(num_blocks); i++) { + offsets[i] = offset; + tinyexr::swap8(reinterpret_cast(&offsets[i])); + offset += data_list[i].size() + doffset; + } + + total_size = static_cast(offset); + } + return TINYEXR_SUCCESS; +} + +// can save a single or multi-part image (no deep* formats) +static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images, + const EXRHeader** exr_headers, + unsigned int num_parts, + unsigned char** memory_out, const char** err) { + if (exr_images == NULL || exr_headers == NULL || num_parts == 0 || + memory_out == NULL) { + SetErrorMessage("Invalid argument for SaveEXRNPartImageToMemory", + err); + return 0; + } + { + for (unsigned int i = 0; i < num_parts; ++i) { + if (exr_headers[i]->compression_type < 0) { + SetErrorMessage("Invalid argument for SaveEXRNPartImageToMemory", + err); + return 0; + } +#if !TINYEXR_USE_PIZ + if (exr_headers[i]->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { + SetErrorMessage("PIZ compression is not supported in this build", + err); + return 0; + } +#endif +#if !TINYEXR_USE_ZFP + if (exr_headers[i]->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { + SetErrorMessage("ZFP compression is not supported in this build", + err); + return 0; + } +#else + for (int c = 0; c < exr_header->num_channels; ++c) { + if (exr_headers[i]->requested_pixel_types[c] != TINYEXR_PIXELTYPE_FLOAT) { + SetErrorMessage("Pixel type must be FLOAT for ZFP compression", + err); + return 0; + } + } +#endif + } + } + + std::vector memory; + + // Header + { + const char header[] = { 0x76, 0x2f, 0x31, 0x01 }; + memory.insert(memory.end(), header, header + 4); + } + + // Version + // using value from the first header + int long_name = exr_headers[0]->long_name; + { + char marker[] = { 2, 0, 0, 0 }; + /* @todo + if (exr_header->non_image) { + marker[1] |= 0x8; + } + */ + // tiled + if (num_parts == 1 && exr_images[0].tiles) { + marker[1] |= 0x2; + } + // long_name + if (long_name) { + marker[1] |= 0x4; + } + // multipart + if (num_parts > 1) { + marker[1] |= 0x10; + } + memory.insert(memory.end(), marker, marker + 4); + } + + int total_chunk_count = 0; + std::vector chunk_count(num_parts); + std::vector offset_data(num_parts); + for (unsigned int i = 0; i < num_parts; ++i) { + if (!exr_images[i].tiles) { + int num_scanlines = NumScanlines(exr_headers[i]->compression_type); + chunk_count[i] = + (exr_images[i].height + num_scanlines - 1) / num_scanlines; + InitSingleResolutionOffsets(offset_data[i], chunk_count[i]); + total_chunk_count += chunk_count[i]; + } else { + { + std::vector num_x_tiles, num_y_tiles; + PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i]); + chunk_count[i] = + InitTileOffsets(offset_data[i], exr_headers[i], num_x_tiles, num_y_tiles); + total_chunk_count += chunk_count[i]; + } + } + } + // Write attributes to memory buffer. + std::vector< std::vector > channels(num_parts); + { + std::set partnames; + for (unsigned int i = 0; i < num_parts; ++i) { + //channels + { + std::vector data; + + for (int c = 0; c < exr_headers[i]->num_channels; c++) { + tinyexr::ChannelInfo info; + info.p_linear = 0; + info.pixel_type = exr_headers[i]->pixel_types[c]; + info.requested_pixel_type = exr_headers[i]->requested_pixel_types[c]; + info.x_sampling = 1; + info.y_sampling = 1; + info.name = std::string(exr_headers[i]->channels[c].name); + channels[i].push_back(info); + } + + tinyexr::WriteChannelInfo(data, channels[i]); + + tinyexr::WriteAttributeToMemory(&memory, "channels", "chlist", &data.at(0), + static_cast(data.size())); + } + + { + int comp = exr_headers[i]->compression_type; + swap4(&comp); + WriteAttributeToMemory( + &memory, "compression", "compression", + reinterpret_cast(&comp), 1); + } + + { + int data[4] = { 0, 0, exr_images[i].width - 1, exr_images[i].height - 1 }; + swap4(&data[0]); + swap4(&data[1]); + swap4(&data[2]); + swap4(&data[3]); + WriteAttributeToMemory( + &memory, "dataWindow", "box2i", + reinterpret_cast(data), sizeof(int) * 4); + + int data0[4] = { 0, 0, exr_images[0].width - 1, exr_images[0].height - 1 }; + swap4(&data0[0]); + swap4(&data0[1]); + swap4(&data0[2]); + swap4(&data0[3]); + // Note: must be the same across parts (currently, using value from the first header) + WriteAttributeToMemory( + &memory, "displayWindow", "box2i", + reinterpret_cast(data0), sizeof(int) * 4); + } + + { + unsigned char line_order = 0; // @fixme { read line_order from EXRHeader } + WriteAttributeToMemory(&memory, "lineOrder", "lineOrder", + &line_order, 1); + } + + { + // Note: must be the same across parts + float aspectRatio = 1.0f; + swap4(&aspectRatio); + WriteAttributeToMemory( + &memory, "pixelAspectRatio", "float", + reinterpret_cast(&aspectRatio), sizeof(float)); + } + + { + float center[2] = { 0.0f, 0.0f }; + swap4(¢er[0]); + swap4(¢er[1]); + WriteAttributeToMemory( + &memory, "screenWindowCenter", "v2f", + reinterpret_cast(center), 2 * sizeof(float)); + } + + { + float w = 1.0f; + swap4(&w); + WriteAttributeToMemory(&memory, "screenWindowWidth", "float", + reinterpret_cast(&w), + sizeof(float)); + } + + if (exr_images[i].tiles) { + unsigned char tile_mode = static_cast(exr_headers[i]->tile_level_mode & 0x3); + if (exr_headers[i]->tile_rounding_mode) tile_mode |= (1u << 4u); + //unsigned char data[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + unsigned int datai[3] = { 0, 0, 0 }; + unsigned char* data = reinterpret_cast(&datai[0]); + datai[0] = static_cast(exr_headers[i]->tile_size_x); + datai[1] = static_cast(exr_headers[i]->tile_size_y); + data[8] = tile_mode; + swap4(reinterpret_cast(&data[0])); + swap4(reinterpret_cast(&data[4])); + WriteAttributeToMemory( + &memory, "tiles", "tiledesc", + reinterpret_cast(data), 9); + } + + // must be present for multi-part files - according to spec. + if (num_parts > 1) { + // name + { + size_t len = 0; + if ((len = strlen(exr_headers[i]->name)) > 0) { + partnames.emplace(exr_headers[i]->name); + if (partnames.size() != i + 1) { + SetErrorMessage("'name' attributes must be unique for a multi-part file", err); + return 0; + } + WriteAttributeToMemory( + &memory, "name", "string", + reinterpret_cast(exr_headers[i]->name), + static_cast(len)); + } else { + SetErrorMessage("Invalid 'name' attribute for a multi-part file", err); + return 0; + } + } + // type + { + const char* type = "scanlineimage"; + if (exr_images[i].tiles) type = "tiledimage"; + WriteAttributeToMemory( + &memory, "type", "string", + reinterpret_cast(type), + static_cast(strlen(type))); + } + // chunkCount + { + WriteAttributeToMemory( + &memory, "chunkCount", "int", + reinterpret_cast(&chunk_count[i]), + 4); + } + } + + // Custom attributes + if (exr_headers[i]->num_custom_attributes > 0) { + for (int j = 0; j < exr_headers[i]->num_custom_attributes; j++) { + tinyexr::WriteAttributeToMemory( + &memory, exr_headers[i]->custom_attributes[j].name, + exr_headers[i]->custom_attributes[j].type, + reinterpret_cast( + exr_headers[i]->custom_attributes[j].value), + exr_headers[i]->custom_attributes[j].size); + } + } + + { // end of header + memory.push_back(0); + } + } + } + if (num_parts > 1) { + // end of header list + memory.push_back(0); + } + + tinyexr_uint64 chunk_offset = memory.size() + size_t(total_chunk_count) * sizeof(tinyexr_uint64); + + tinyexr_uint64 total_size = 0; + std::vector< std::vector< std::vector > > data_lists(num_parts); + for (unsigned int i = 0; i < num_parts; ++i) { + std::string e; + int ret = EncodeChunk(&exr_images[i], exr_headers[i], + channels[i], + chunk_count[i], + // starting offset of current chunk after part-number + chunk_offset, + num_parts > 1, + offset_data[i], // output: block offsets, must be initialized + data_lists[i], // output + total_size, // output + &e); + if (ret != TINYEXR_SUCCESS) { + if (!e.empty()) { + tinyexr::SetErrorMessage(e, err); + } + return 0; + } + chunk_offset = total_size; + } + + // Allocating required memory + if (total_size == 0) { // something went wrong + tinyexr::SetErrorMessage("Output memory size is zero", err); + return 0; + } + (*memory_out) = static_cast(malloc(total_size)); + + // Writing header + memcpy((*memory_out), &memory[0], memory.size()); + unsigned char* memory_ptr = *memory_out + memory.size(); + size_t sum = memory.size(); + + // Writing offset data for chunks + for (unsigned int i = 0; i < num_parts; ++i) { + if (exr_images[i].tiles) { + const EXRImage* level_image = &exr_images[i]; + int num_levels = (exr_headers[i]->tile_level_mode != TINYEXR_TILE_RIPMAP_LEVELS) ? + offset_data[i].num_x_levels : (offset_data[i].num_x_levels * offset_data[i].num_y_levels); + for (int level_index = 0; level_index < num_levels; ++level_index) { + for (size_t j = 0; j < offset_data[i].offsets[level_index].size(); ++j) { + size_t num_bytes = sizeof(tinyexr_uint64) * offset_data[i].offsets[level_index][j].size(); + sum += num_bytes; + assert(sum <= total_size); + memcpy(memory_ptr, + reinterpret_cast(&offset_data[i].offsets[level_index][j][0]), + num_bytes); + memory_ptr += num_bytes; + } + level_image = level_image->next_level; + } + } else { + size_t num_bytes = sizeof(tinyexr::tinyexr_uint64) * static_cast(chunk_count[i]); + sum += num_bytes; + assert(sum <= total_size); + std::vector& offsets = offset_data[i].offsets[0][0]; + memcpy(memory_ptr, reinterpret_cast(&offsets[0]), num_bytes); + memory_ptr += num_bytes; + } + } + + // Writing chunk data + for (unsigned int i = 0; i < num_parts; ++i) { + for (size_t j = 0; j < static_cast(chunk_count[i]); ++j) { + if (num_parts > 1) { + sum += 4; + assert(sum <= total_size); + unsigned int part_number = i; + swap4(&part_number); + memcpy(memory_ptr, &part_number, 4); + memory_ptr += 4; + } + sum += data_lists[i][j].size(); + assert(sum <= total_size); + memcpy(memory_ptr, &data_lists[i][j][0], data_lists[i][j].size()); + memory_ptr += data_lists[i][j].size(); + } + } + assert(sum == total_size); + return total_size; // OK +} + +} // tinyexr + +size_t SaveEXRImageToMemory(const EXRImage* exr_image, + const EXRHeader* exr_header, + unsigned char** memory_out, const char** err) { + return tinyexr::SaveEXRNPartImageToMemory(exr_image, &exr_header, 1, memory_out, err); +} + +int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header, + const char *filename, const char **err) { + if (exr_image == NULL || filename == NULL || + exr_header->compression_type < 0) { + tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToFile", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + +#if !TINYEXR_USE_PIZ + if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { + tinyexr::SetErrorMessage("PIZ compression is not supported in this build", + err); + return TINYEXR_ERROR_UNSUPPORTED_FEATURE; + } +#endif + +#if !TINYEXR_USE_ZFP + if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { + tinyexr::SetErrorMessage("ZFP compression is not supported in this build", + err); + return TINYEXR_ERROR_UNSUPPORTED_FEATURE; + } +#endif + + FILE *fp = NULL; +#ifdef _WIN32 +#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang + errno_t errcode = + _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"wb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename), + err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } +#else + // Unknown compiler or MinGW without MINGW_HAS_SECURE_API. + fp = fopen(filename, "wb"); +#endif +#else + fp = fopen(filename, "wb"); +#endif + if (!fp) { + tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename), + err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } + + unsigned char *mem = NULL; + size_t mem_size = SaveEXRImageToMemory(exr_image, exr_header, &mem, err); + if (mem_size == 0) { + return TINYEXR_ERROR_SERIALZATION_FAILED; + } + + size_t written_size = 0; + if ((mem_size > 0) && mem) { + written_size = fwrite(mem, 1, mem_size, fp); + } + free(mem); + + fclose(fp); + + if (written_size != mem_size) { + tinyexr::SetErrorMessage("Cannot write a file", err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } + + return TINYEXR_SUCCESS; +} + +size_t SaveEXRMultipartImageToMemory(const EXRImage* exr_images, + const EXRHeader** exr_headers, + unsigned int num_parts, + unsigned char** memory_out, const char** err) { + if (exr_images == NULL || exr_headers == NULL || num_parts < 2 || + memory_out == NULL) { + tinyexr::SetErrorMessage("Invalid argument for SaveEXRNPartImageToMemory", + err); + return 0; + } + return tinyexr::SaveEXRNPartImageToMemory(exr_images, exr_headers, num_parts, memory_out, err); +} + +int SaveEXRMultipartImageToFile(const EXRImage* exr_images, + const EXRHeader** exr_headers, + unsigned int num_parts, + const char* filename, + const char** err) { + if (exr_images == NULL || exr_headers == NULL || num_parts < 2) { + tinyexr::SetErrorMessage("Invalid argument for SaveEXRMultipartImageToFile", + err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + FILE *fp = NULL; +#ifdef _WIN32 +#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang. + errno_t errcode = + _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"wb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename), + err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } +#else + // Unknown compiler or MinGW without MINGW_HAS_SECURE_API. + fp = fopen(filename, "wb"); +#endif +#else + fp = fopen(filename, "wb"); +#endif + if (!fp) { + tinyexr::SetErrorMessage("Cannot write a file: " + std::string(filename), + err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } + + unsigned char *mem = NULL; + size_t mem_size = SaveEXRMultipartImageToMemory(exr_images, exr_headers, num_parts, &mem, err); + if (mem_size == 0) { + return TINYEXR_ERROR_SERIALZATION_FAILED; + } + + size_t written_size = 0; + if ((mem_size > 0) && mem) { + written_size = fwrite(mem, 1, mem_size, fp); + } + free(mem); + + fclose(fp); + + if (written_size != mem_size) { + tinyexr::SetErrorMessage("Cannot write a file", err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } + + return TINYEXR_SUCCESS; +} + +int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { + if (deep_image == NULL) { + tinyexr::SetErrorMessage("Invalid argument for LoadDeepEXR", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + +#ifdef _WIN32 + FILE *fp = NULL; +#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang. + errno_t errcode = + _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read a file " + std::string(filename), + err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } +#else + // Unknown compiler or MinGW without MINGW_HAS_SECURE_API. + fp = fopen(filename, "rb"); +#endif + if (!fp) { + tinyexr::SetErrorMessage("Cannot read a file " + std::string(filename), + err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } +#else + FILE *fp = fopen(filename, "rb"); + if (!fp) { + tinyexr::SetErrorMessage("Cannot read a file " + std::string(filename), + err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } +#endif + + size_t filesize; + // Compute size + fseek(fp, 0, SEEK_END); + filesize = static_cast(ftell(fp)); + fseek(fp, 0, SEEK_SET); + + if (filesize == 0) { + fclose(fp); + tinyexr::SetErrorMessage("File size is zero : " + std::string(filename), + err); + return TINYEXR_ERROR_INVALID_FILE; + } + + std::vector buf(filesize); // @todo { use mmap } + { + size_t ret; + ret = fread(&buf[0], 1, filesize, fp); + assert(ret == filesize); + (void)ret; + } + fclose(fp); + + const char *head = &buf[0]; + const char *marker = &buf[0]; + + // Header check. + { + const char header[] = {0x76, 0x2f, 0x31, 0x01}; + + if (memcmp(marker, header, 4) != 0) { + tinyexr::SetErrorMessage("Invalid magic number", err); + return TINYEXR_ERROR_INVALID_MAGIC_NUMBER; + } + marker += 4; + } + + // Version, scanline. + { + // ver 2.0, scanline, deep bit on(0x800) + // must be [2, 0, 0, 0] + if (marker[0] != 2 || marker[1] != 8 || marker[2] != 0 || marker[3] != 0) { + tinyexr::SetErrorMessage("Unsupported version or scanline", err); + return TINYEXR_ERROR_UNSUPPORTED_FORMAT; + } + + marker += 4; + } + + int dx = -1; + int dy = -1; + int dw = -1; + int dh = -1; + int num_scanline_blocks = 1; // 16 for ZIP compression. + int compression_type = -1; + int num_channels = -1; + std::vector channels; + + // Read attributes + size_t size = filesize - tinyexr::kEXRVersionSize; + for (;;) { + if (0 == size) { + return TINYEXR_ERROR_INVALID_DATA; + } else if (marker[0] == '\0') { + marker++; + size--; + break; + } + + std::string attr_name; + std::string attr_type; + std::vector data; + size_t marker_size; + if (!tinyexr::ReadAttribute(&attr_name, &attr_type, &data, &marker_size, + marker, size)) { + std::stringstream ss; + ss << "Failed to parse attribute\n"; + tinyexr::SetErrorMessage(ss.str(), err); + return TINYEXR_ERROR_INVALID_DATA; + } + marker += marker_size; + size -= marker_size; + + if (attr_name.compare("compression") == 0) { + compression_type = data[0]; + if (compression_type > TINYEXR_COMPRESSIONTYPE_PIZ) { + std::stringstream ss; + ss << "Unsupported compression type : " << compression_type; + tinyexr::SetErrorMessage(ss.str(), err); + return TINYEXR_ERROR_UNSUPPORTED_FORMAT; + } + + if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) { + num_scanline_blocks = 16; + } + + } else if (attr_name.compare("channels") == 0) { + // name: zero-terminated string, from 1 to 255 bytes long + // pixel type: int, possible values are: UINT = 0 HALF = 1 FLOAT = 2 + // pLinear: unsigned char, possible values are 0 and 1 + // reserved: three chars, should be zero + // xSampling: int + // ySampling: int + + if (!tinyexr::ReadChannelInfo(channels, data)) { + tinyexr::SetErrorMessage("Failed to parse channel info", err); + return TINYEXR_ERROR_INVALID_DATA; + } + + num_channels = static_cast(channels.size()); + + if (num_channels < 1) { + tinyexr::SetErrorMessage("Invalid channels format", err); + return TINYEXR_ERROR_INVALID_DATA; + } + + } else if (attr_name.compare("dataWindow") == 0) { + memcpy(&dx, &data.at(0), sizeof(int)); + memcpy(&dy, &data.at(4), sizeof(int)); + memcpy(&dw, &data.at(8), sizeof(int)); + memcpy(&dh, &data.at(12), sizeof(int)); + tinyexr::swap4(&dx); + tinyexr::swap4(&dy); + tinyexr::swap4(&dw); + tinyexr::swap4(&dh); + + } else if (attr_name.compare("displayWindow") == 0) { + int x; + int y; + int w; + int h; + memcpy(&x, &data.at(0), sizeof(int)); + memcpy(&y, &data.at(4), sizeof(int)); + memcpy(&w, &data.at(8), sizeof(int)); + memcpy(&h, &data.at(12), sizeof(int)); + tinyexr::swap4(&x); + tinyexr::swap4(&y); + tinyexr::swap4(&w); + tinyexr::swap4(&h); + } + } + + assert(dx >= 0); + assert(dy >= 0); + assert(dw >= 0); + assert(dh >= 0); + assert(num_channels >= 1); + + int data_width = dw - dx + 1; + int data_height = dh - dy + 1; + + // Read offset tables. + int num_blocks = data_height / num_scanline_blocks; + if (num_blocks * num_scanline_blocks < data_height) { + num_blocks++; + } + + std::vector offsets(static_cast(num_blocks)); + + for (size_t y = 0; y < static_cast(num_blocks); y++) { + tinyexr::tinyexr_int64 offset; + memcpy(&offset, marker, sizeof(tinyexr::tinyexr_int64)); + tinyexr::swap8(reinterpret_cast(&offset)); + marker += sizeof(tinyexr::tinyexr_int64); // = 8 + offsets[y] = offset; + } + +#if TINYEXR_USE_PIZ + if ((compression_type == TINYEXR_COMPRESSIONTYPE_NONE) || + (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) || + (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) || + (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP) || + (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ)) { +#else + if ((compression_type == TINYEXR_COMPRESSIONTYPE_NONE) || + (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) || + (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) || + (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) { +#endif + // OK + } else { + tinyexr::SetErrorMessage("Unsupported compression format", err); + return TINYEXR_ERROR_UNSUPPORTED_FORMAT; + } + + deep_image->image = static_cast( + malloc(sizeof(float **) * static_cast(num_channels))); + for (int c = 0; c < num_channels; c++) { + deep_image->image[c] = static_cast( + malloc(sizeof(float *) * static_cast(data_height))); + for (int y = 0; y < data_height; y++) { + } + } + + deep_image->offset_table = static_cast( + malloc(sizeof(int *) * static_cast(data_height))); + for (int y = 0; y < data_height; y++) { + deep_image->offset_table[y] = static_cast( + malloc(sizeof(int) * static_cast(data_width))); + } + + for (size_t y = 0; y < static_cast(num_blocks); y++) { + const unsigned char *data_ptr = + reinterpret_cast(head + offsets[y]); + + // int: y coordinate + // int64: packed size of pixel offset table + // int64: packed size of sample data + // int64: unpacked size of sample data + // compressed pixel offset table + // compressed sample data + int line_no; + tinyexr::tinyexr_int64 packedOffsetTableSize; + tinyexr::tinyexr_int64 packedSampleDataSize; + tinyexr::tinyexr_int64 unpackedSampleDataSize; + memcpy(&line_no, data_ptr, sizeof(int)); + memcpy(&packedOffsetTableSize, data_ptr + 4, + sizeof(tinyexr::tinyexr_int64)); + memcpy(&packedSampleDataSize, data_ptr + 12, + sizeof(tinyexr::tinyexr_int64)); + memcpy(&unpackedSampleDataSize, data_ptr + 20, + sizeof(tinyexr::tinyexr_int64)); + + tinyexr::swap4(&line_no); + tinyexr::swap8( + reinterpret_cast(&packedOffsetTableSize)); + tinyexr::swap8( + reinterpret_cast(&packedSampleDataSize)); + tinyexr::swap8( + reinterpret_cast(&unpackedSampleDataSize)); + + std::vector pixelOffsetTable(static_cast(data_width)); + + // decode pixel offset table. + { + unsigned long dstLen = + static_cast(pixelOffsetTable.size() * sizeof(int)); + if (!tinyexr::DecompressZip( + reinterpret_cast(&pixelOffsetTable.at(0)), + &dstLen, data_ptr + 28, + static_cast(packedOffsetTableSize))) { + return false; + } + + assert(dstLen == pixelOffsetTable.size() * sizeof(int)); + for (size_t i = 0; i < static_cast(data_width); i++) { + deep_image->offset_table[y][i] = pixelOffsetTable[i]; + } + } + + std::vector sample_data( + static_cast(unpackedSampleDataSize)); + + // decode sample data. + { + unsigned long dstLen = static_cast(unpackedSampleDataSize); + if (dstLen) { + if (!tinyexr::DecompressZip( + reinterpret_cast(&sample_data.at(0)), &dstLen, + data_ptr + 28 + packedOffsetTableSize, + static_cast(packedSampleDataSize))) { + return false; + } + assert(dstLen == static_cast(unpackedSampleDataSize)); + } + } + + // decode sample + int sampleSize = -1; + std::vector channel_offset_list(static_cast(num_channels)); + { + int channel_offset = 0; + for (size_t i = 0; i < static_cast(num_channels); i++) { + channel_offset_list[i] = channel_offset; + if (channels[i].pixel_type == TINYEXR_PIXELTYPE_UINT) { // UINT + channel_offset += 4; + } else if (channels[i].pixel_type == TINYEXR_PIXELTYPE_HALF) { // half + channel_offset += 2; + } else if (channels[i].pixel_type == + TINYEXR_PIXELTYPE_FLOAT) { // float + channel_offset += 4; + } else { + assert(0); + } + } + sampleSize = channel_offset; + } + assert(sampleSize >= 2); + + assert(static_cast( + pixelOffsetTable[static_cast(data_width - 1)] * + sampleSize) == sample_data.size()); + int samples_per_line = static_cast(sample_data.size()) / sampleSize; + + // + // Alloc memory + // + + // + // pixel data is stored as image[channels][pixel_samples] + // + { + tinyexr::tinyexr_uint64 data_offset = 0; + for (size_t c = 0; c < static_cast(num_channels); c++) { + deep_image->image[c][y] = static_cast( + malloc(sizeof(float) * static_cast(samples_per_line))); + + if (channels[c].pixel_type == 0) { // UINT + for (size_t x = 0; x < static_cast(samples_per_line); x++) { + unsigned int ui; + unsigned int *src_ptr = reinterpret_cast( + &sample_data.at(size_t(data_offset) + x * sizeof(int))); + tinyexr::cpy4(&ui, src_ptr); + deep_image->image[c][y][x] = static_cast(ui); // @fixme + } + data_offset += + sizeof(unsigned int) * static_cast(samples_per_line); + } else if (channels[c].pixel_type == 1) { // half + for (size_t x = 0; x < static_cast(samples_per_line); x++) { + tinyexr::FP16 f16; + const unsigned short *src_ptr = reinterpret_cast( + &sample_data.at(size_t(data_offset) + x * sizeof(short))); + tinyexr::cpy2(&(f16.u), src_ptr); + tinyexr::FP32 f32 = half_to_float(f16); + deep_image->image[c][y][x] = f32.f; + } + data_offset += sizeof(short) * static_cast(samples_per_line); + } else { // float + for (size_t x = 0; x < static_cast(samples_per_line); x++) { + float f; + const float *src_ptr = reinterpret_cast( + &sample_data.at(size_t(data_offset) + x * sizeof(float))); + tinyexr::cpy4(&f, src_ptr); + deep_image->image[c][y][x] = f; + } + data_offset += sizeof(float) * static_cast(samples_per_line); + } + } + } + } // y + + deep_image->width = data_width; + deep_image->height = data_height; + + deep_image->channel_names = static_cast( + malloc(sizeof(const char *) * static_cast(num_channels))); + for (size_t c = 0; c < static_cast(num_channels); c++) { +#ifdef _WIN32 + deep_image->channel_names[c] = _strdup(channels[c].name.c_str()); +#else + deep_image->channel_names[c] = strdup(channels[c].name.c_str()); +#endif + } + deep_image->num_channels = num_channels; + + return TINYEXR_SUCCESS; +} + +void InitEXRImage(EXRImage *exr_image) { + if (exr_image == NULL) { + return; + } + + exr_image->width = 0; + exr_image->height = 0; + exr_image->num_channels = 0; + + exr_image->images = NULL; + exr_image->tiles = NULL; + exr_image->next_level = NULL; + exr_image->level_x = 0; + exr_image->level_y = 0; + + exr_image->num_tiles = 0; +} + +void FreeEXRErrorMessage(const char *msg) { + if (msg) { + free(reinterpret_cast(const_cast(msg))); + } + return; +} + +void InitEXRHeader(EXRHeader *exr_header) { + if (exr_header == NULL) { + return; + } + + memset(exr_header, 0, sizeof(EXRHeader)); +} + +int FreeEXRHeader(EXRHeader *exr_header) { + if (exr_header == NULL) { + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + if (exr_header->channels) { + free(exr_header->channels); + } + + if (exr_header->pixel_types) { + free(exr_header->pixel_types); + } + + if (exr_header->requested_pixel_types) { + free(exr_header->requested_pixel_types); + } + + for (int i = 0; i < exr_header->num_custom_attributes; i++) { + if (exr_header->custom_attributes[i].value) { + free(exr_header->custom_attributes[i].value); + } + } + + if (exr_header->custom_attributes) { + free(exr_header->custom_attributes); + } + + EXRSetNameAttr(exr_header, NULL); + + return TINYEXR_SUCCESS; +} + +void EXRSetNameAttr(EXRHeader* exr_header, const char* name) { + if (exr_header == NULL) { + return; + } + memset(exr_header->name, 0, 256); + if (name != NULL) { + size_t len = std::min(strlen(name), (size_t)255); + if (len) { + memcpy(exr_header->name, name, len); + } + } +} + +int EXRNumLevels(const EXRImage* exr_image) { + if (exr_image == NULL) return 0; + if(exr_image->images) return 1; // scanlines + int levels = 1; + const EXRImage* level_image = exr_image; + while((level_image = level_image->next_level)) ++levels; + return levels; +} + +int FreeEXRImage(EXRImage *exr_image) { + if (exr_image == NULL) { + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + if (exr_image->next_level) { + FreeEXRImage(exr_image->next_level); + delete exr_image->next_level; + } + + for (int i = 0; i < exr_image->num_channels; i++) { + if (exr_image->images && exr_image->images[i]) { + free(exr_image->images[i]); + } + } + + if (exr_image->images) { + free(exr_image->images); + } + + if (exr_image->tiles) { + for (int tid = 0; tid < exr_image->num_tiles; tid++) { + for (int i = 0; i < exr_image->num_channels; i++) { + if (exr_image->tiles[tid].images && exr_image->tiles[tid].images[i]) { + free(exr_image->tiles[tid].images[i]); + } + } + if (exr_image->tiles[tid].images) { + free(exr_image->tiles[tid].images); + } + } + free(exr_image->tiles); + } + + return TINYEXR_SUCCESS; +} + +int ParseEXRHeaderFromFile(EXRHeader *exr_header, const EXRVersion *exr_version, + const char *filename, const char **err) { + if (exr_header == NULL || exr_version == NULL || filename == NULL) { + tinyexr::SetErrorMessage("Invalid argument for ParseEXRHeaderFromFile", + err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + FILE *fp = NULL; +#ifdef _WIN32 +#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang. + errno_t errcode = + _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_INVALID_FILE; + } +#else + // Unknown compiler or MinGW without MINGW_HAS_SECURE_API. + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); +#endif + if (!fp) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } + + size_t filesize; + // Compute size + fseek(fp, 0, SEEK_END); + filesize = static_cast(ftell(fp)); + fseek(fp, 0, SEEK_SET); + + std::vector buf(filesize); // @todo { use mmap } + { + size_t ret; + ret = fread(&buf[0], 1, filesize, fp); + assert(ret == filesize); + fclose(fp); + + if (ret != filesize) { + tinyexr::SetErrorMessage("fread() error on " + std::string(filename), + err); + return TINYEXR_ERROR_INVALID_FILE; + } + } + + return ParseEXRHeaderFromMemory(exr_header, exr_version, &buf.at(0), filesize, + err); +} + +int ParseEXRMultipartHeaderFromMemory(EXRHeader ***exr_headers, + int *num_headers, + const EXRVersion *exr_version, + const unsigned char *memory, size_t size, + const char **err) { + if (memory == NULL || exr_headers == NULL || num_headers == NULL || + exr_version == NULL) { + // Invalid argument + tinyexr::SetErrorMessage( + "Invalid argument for ParseEXRMultipartHeaderFromMemory", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + if (size < tinyexr::kEXRVersionSize) { + tinyexr::SetErrorMessage("Data size too short", err); + return TINYEXR_ERROR_INVALID_DATA; + } + + const unsigned char *marker = memory + tinyexr::kEXRVersionSize; + size_t marker_size = size - tinyexr::kEXRVersionSize; + + std::vector infos; + + for (;;) { + tinyexr::HeaderInfo info; + info.clear(); + + std::string err_str; + bool empty_header = false; + int ret = ParseEXRHeader(&info, &empty_header, exr_version, &err_str, + marker, marker_size); + + if (ret != TINYEXR_SUCCESS) { + tinyexr::SetErrorMessage(err_str, err); + return ret; + } + + if (empty_header) { + marker += 1; // skip '\0' + break; + } + + // `chunkCount` must exist in the header. + if (info.chunk_count == 0) { + tinyexr::SetErrorMessage( + "`chunkCount' attribute is not found in the header.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + + infos.push_back(info); + + // move to next header. + marker += info.header_len; + size -= info.header_len; + } + + // allocate memory for EXRHeader and create array of EXRHeader pointers. + (*exr_headers) = + static_cast(malloc(sizeof(EXRHeader *) * infos.size())); + for (size_t i = 0; i < infos.size(); i++) { + EXRHeader *exr_header = static_cast(malloc(sizeof(EXRHeader))); + memset(exr_header, 0, sizeof(EXRHeader)); + + ConvertHeader(exr_header, infos[i]); + + exr_header->multipart = exr_version->multipart ? 1 : 0; + + (*exr_headers)[i] = exr_header; + } + + (*num_headers) = static_cast(infos.size()); + + return TINYEXR_SUCCESS; +} + +int ParseEXRMultipartHeaderFromFile(EXRHeader ***exr_headers, int *num_headers, + const EXRVersion *exr_version, + const char *filename, const char **err) { + if (exr_headers == NULL || num_headers == NULL || exr_version == NULL || + filename == NULL) { + tinyexr::SetErrorMessage( + "Invalid argument for ParseEXRMultipartHeaderFromFile()", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + FILE *fp = NULL; +#ifdef _WIN32 +#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang. + errno_t errcode = + _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_INVALID_FILE; + } +#else + // Unknown compiler or MinGW without MINGW_HAS_SECURE_API. + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); +#endif + if (!fp) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } + + size_t filesize; + // Compute size + fseek(fp, 0, SEEK_END); + filesize = static_cast(ftell(fp)); + fseek(fp, 0, SEEK_SET); + + std::vector buf(filesize); // @todo { use mmap } + { + size_t ret; + ret = fread(&buf[0], 1, filesize, fp); + assert(ret == filesize); + fclose(fp); + + if (ret != filesize) { + tinyexr::SetErrorMessage("`fread' error. file may be corrupted.", err); + return TINYEXR_ERROR_INVALID_FILE; + } + } + + return ParseEXRMultipartHeaderFromMemory( + exr_headers, num_headers, exr_version, &buf.at(0), filesize, err); +} + +int ParseEXRVersionFromMemory(EXRVersion *version, const unsigned char *memory, + size_t size) { + if (version == NULL || memory == NULL) { + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + if (size < tinyexr::kEXRVersionSize) { + return TINYEXR_ERROR_INVALID_DATA; + } + + const unsigned char *marker = memory; + + // Header check. + { + const char header[] = {0x76, 0x2f, 0x31, 0x01}; + + if (memcmp(marker, header, 4) != 0) { + return TINYEXR_ERROR_INVALID_MAGIC_NUMBER; + } + marker += 4; + } + + version->tiled = false; + version->long_name = false; + version->non_image = false; + version->multipart = false; + + // Parse version header. + { + // must be 2 + if (marker[0] != 2) { + return TINYEXR_ERROR_INVALID_EXR_VERSION; + } + + if (version == NULL) { + return TINYEXR_SUCCESS; // May OK + } + + version->version = 2; + + if (marker[1] & 0x2) { // 9th bit + version->tiled = true; + } + if (marker[1] & 0x4) { // 10th bit + version->long_name = true; + } + if (marker[1] & 0x8) { // 11th bit + version->non_image = true; // (deep image) + } + if (marker[1] & 0x10) { // 12th bit + version->multipart = true; + } + } + + return TINYEXR_SUCCESS; +} + +int ParseEXRVersionFromFile(EXRVersion *version, const char *filename) { + if (filename == NULL) { + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + FILE *fp = NULL; +#ifdef _WIN32 +#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang. + errno_t err = _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (err != 0) { + // TODO(syoyo): return wfopen_s erro code + return TINYEXR_ERROR_CANT_OPEN_FILE; + } +#else + // Unknown compiler or MinGW without MINGW_HAS_SECURE_API. + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); +#endif + if (!fp) { + return TINYEXR_ERROR_CANT_OPEN_FILE; + } + + size_t file_size; + // Compute size + fseek(fp, 0, SEEK_END); + file_size = static_cast(ftell(fp)); + fseek(fp, 0, SEEK_SET); + + if (file_size < tinyexr::kEXRVersionSize) { + return TINYEXR_ERROR_INVALID_FILE; + } + + unsigned char buf[tinyexr::kEXRVersionSize]; + size_t ret = fread(&buf[0], 1, tinyexr::kEXRVersionSize, fp); + fclose(fp); + + if (ret != tinyexr::kEXRVersionSize) { + return TINYEXR_ERROR_INVALID_FILE; + } + + return ParseEXRVersionFromMemory(version, buf, tinyexr::kEXRVersionSize); +} + +int LoadEXRMultipartImageFromMemory(EXRImage *exr_images, + const EXRHeader **exr_headers, + unsigned int num_parts, + const unsigned char *memory, + const size_t size, const char **err) { + if (exr_images == NULL || exr_headers == NULL || num_parts == 0 || + memory == NULL || (size <= tinyexr::kEXRVersionSize)) { + tinyexr::SetErrorMessage( + "Invalid argument for LoadEXRMultipartImageFromMemory()", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + // compute total header size. + size_t total_header_size = 0; + for (unsigned int i = 0; i < num_parts; i++) { + if (exr_headers[i]->header_len == 0) { + tinyexr::SetErrorMessage("EXRHeader variable is not initialized.", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + total_header_size += exr_headers[i]->header_len; + } + + const char *marker = reinterpret_cast( + memory + total_header_size + 4 + + 4); // +8 for magic number and version header. + + marker += 1; // Skip empty header. + + // NOTE 1: + // In multipart image, There is 'part number' before chunk data. + // 4 byte : part number + // 4+ : chunk + // + // NOTE 2: + // EXR spec says 'part number' is 'unsigned long' but actually this is + // 'unsigned int(4 bytes)' in OpenEXR implementation... + // http://www.openexr.com/openexrfilelayout.pdf + + // Load chunk offset table. + std::vector chunk_offset_table_list; + chunk_offset_table_list.reserve(num_parts); + for (size_t i = 0; i < static_cast(num_parts); i++) { + chunk_offset_table_list.resize(chunk_offset_table_list.size() + 1); + tinyexr::OffsetData& offset_data = chunk_offset_table_list.back(); + if (!exr_headers[i]->tiled || exr_headers[i]->tile_level_mode == TINYEXR_TILE_ONE_LEVEL) { + tinyexr::InitSingleResolutionOffsets(offset_data, exr_headers[i]->chunk_count); + std::vector& offset_table = offset_data.offsets[0][0]; + + for (size_t c = 0; c < offset_table.size(); c++) { + tinyexr::tinyexr_uint64 offset; + memcpy(&offset, marker, 8); + tinyexr::swap8(&offset); + + if (offset >= size) { + tinyexr::SetErrorMessage("Invalid offset size in EXR header chunks.", + err); + return TINYEXR_ERROR_INVALID_DATA; + } + + offset_table[c] = offset + 4; // +4 to skip 'part number' + marker += 8; + } + } else { + { + std::vector num_x_tiles, num_y_tiles; + tinyexr::PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i]); + int num_blocks = InitTileOffsets(offset_data, exr_headers[i], num_x_tiles, num_y_tiles); + if (num_blocks != exr_headers[i]->chunk_count) { + tinyexr::SetErrorMessage("Invalid offset table size.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + } + for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) { + for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) { + for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) { + tinyexr::tinyexr_uint64 offset; + memcpy(&offset, marker, sizeof(tinyexr::tinyexr_uint64)); + tinyexr::swap8(&offset); + if (offset >= size) { + tinyexr::SetErrorMessage("Invalid offset size in EXR header chunks.", + err); + return TINYEXR_ERROR_INVALID_DATA; + } + offset_data.offsets[l][dy][dx] = offset + 4; // +4 to skip 'part number' + marker += sizeof(tinyexr::tinyexr_uint64); // = 8 + } + } + } + } + } + + // Decode image. + for (size_t i = 0; i < static_cast(num_parts); i++) { + tinyexr::OffsetData &offset_data = chunk_offset_table_list[i]; + + // First check 'part number' is identitical to 'i' + for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) + for (unsigned int dy = 0; dy < offset_data.offsets[l].size(); ++dy) + for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) { + + const unsigned char *part_number_addr = + memory + offset_data.offsets[l][dy][dx] - 4; // -4 to move to 'part number' field. + unsigned int part_no; + memcpy(&part_no, part_number_addr, sizeof(unsigned int)); // 4 + tinyexr::swap4(&part_no); + + if (part_no != i) { + tinyexr::SetErrorMessage("Invalid `part number' in EXR header chunks.", + err); + return TINYEXR_ERROR_INVALID_DATA; + } + } + + std::string e; + int ret = tinyexr::DecodeChunk(&exr_images[i], exr_headers[i], offset_data, + memory, size, &e); + if (ret != TINYEXR_SUCCESS) { + if (!e.empty()) { + tinyexr::SetErrorMessage(e, err); + } + return ret; + } + } + + return TINYEXR_SUCCESS; +} + +int LoadEXRMultipartImageFromFile(EXRImage *exr_images, + const EXRHeader **exr_headers, + unsigned int num_parts, const char *filename, + const char **err) { + if (exr_images == NULL || exr_headers == NULL || num_parts == 0) { + tinyexr::SetErrorMessage( + "Invalid argument for LoadEXRMultipartImageFromFile", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + FILE *fp = NULL; +#ifdef _WIN32 +#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) // MSVC, MinGW GCC, or Clang. + errno_t errcode = + _wfopen_s(&fp, tinyexr::UTF8ToWchar(filename).c_str(), L"rb"); + if (errcode != 0) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } +#else + // Unknown compiler or MinGW without MINGW_HAS_SECURE_API. + fp = fopen(filename, "rb"); +#endif +#else + fp = fopen(filename, "rb"); +#endif + if (!fp) { + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } + + size_t filesize; + // Compute size + fseek(fp, 0, SEEK_END); + filesize = static_cast(ftell(fp)); + fseek(fp, 0, SEEK_SET); + + std::vector buf(filesize); // @todo { use mmap } + { + size_t ret; + ret = fread(&buf[0], 1, filesize, fp); + assert(ret == filesize); + fclose(fp); + (void)ret; + } + + return LoadEXRMultipartImageFromMemory(exr_images, exr_headers, num_parts, + &buf.at(0), filesize, err); +} + +int SaveEXR(const float *data, int width, int height, int components, + const int save_as_fp16, const char *outfilename, const char **err) { + if ((components == 1) || components == 3 || components == 4) { + // OK + } else { + std::stringstream ss; + ss << "Unsupported component value : " << components << std::endl; + + tinyexr::SetErrorMessage(ss.str(), err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + EXRHeader header; + InitEXRHeader(&header); + + if ((width < 16) && (height < 16)) { + // No compression for small image. + header.compression_type = TINYEXR_COMPRESSIONTYPE_NONE; + } else { + header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP; + } + + EXRImage image; + InitEXRImage(&image); + + image.num_channels = components; + + std::vector images[4]; + + if (components == 1) { + images[0].resize(static_cast(width * height)); + memcpy(images[0].data(), data, sizeof(float) * size_t(width * height)); + } else { + images[0].resize(static_cast(width * height)); + images[1].resize(static_cast(width * height)); + images[2].resize(static_cast(width * height)); + images[3].resize(static_cast(width * height)); + + // Split RGB(A)RGB(A)RGB(A)... into R, G and B(and A) layers + for (size_t i = 0; i < static_cast(width * height); i++) { + images[0][i] = data[static_cast(components) * i + 0]; + images[1][i] = data[static_cast(components) * i + 1]; + images[2][i] = data[static_cast(components) * i + 2]; + if (components == 4) { + images[3][i] = data[static_cast(components) * i + 3]; + } + } + } + + float *image_ptr[4] = {0, 0, 0, 0}; + if (components == 4) { + image_ptr[0] = &(images[3].at(0)); // A + image_ptr[1] = &(images[2].at(0)); // B + image_ptr[2] = &(images[1].at(0)); // G + image_ptr[3] = &(images[0].at(0)); // R + } else if (components == 3) { + image_ptr[0] = &(images[2].at(0)); // B + image_ptr[1] = &(images[1].at(0)); // G + image_ptr[2] = &(images[0].at(0)); // R + } else if (components == 1) { + image_ptr[0] = &(images[0].at(0)); // A + } + + image.images = reinterpret_cast(image_ptr); + image.width = width; + image.height = height; + + header.num_channels = components; + header.channels = static_cast(malloc( + sizeof(EXRChannelInfo) * static_cast(header.num_channels))); + // Must be (A)BGR order, since most of EXR viewers expect this channel order. + if (components == 4) { +#ifdef _MSC_VER + strncpy_s(header.channels[0].name, "A", 255); + strncpy_s(header.channels[1].name, "B", 255); + strncpy_s(header.channels[2].name, "G", 255); + strncpy_s(header.channels[3].name, "R", 255); +#else + strncpy(header.channels[0].name, "A", 255); + strncpy(header.channels[1].name, "B", 255); + strncpy(header.channels[2].name, "G", 255); + strncpy(header.channels[3].name, "R", 255); +#endif + header.channels[0].name[strlen("A")] = '\0'; + header.channels[1].name[strlen("B")] = '\0'; + header.channels[2].name[strlen("G")] = '\0'; + header.channels[3].name[strlen("R")] = '\0'; + } else if (components == 3) { +#ifdef _MSC_VER + strncpy_s(header.channels[0].name, "B", 255); + strncpy_s(header.channels[1].name, "G", 255); + strncpy_s(header.channels[2].name, "R", 255); +#else + strncpy(header.channels[0].name, "B", 255); + strncpy(header.channels[1].name, "G", 255); + strncpy(header.channels[2].name, "R", 255); +#endif + header.channels[0].name[strlen("B")] = '\0'; + header.channels[1].name[strlen("G")] = '\0'; + header.channels[2].name[strlen("R")] = '\0'; + } else { +#ifdef _MSC_VER + strncpy_s(header.channels[0].name, "A", 255); +#else + strncpy(header.channels[0].name, "A", 255); +#endif + header.channels[0].name[strlen("A")] = '\0'; + } + + header.pixel_types = static_cast( + malloc(sizeof(int) * static_cast(header.num_channels))); + header.requested_pixel_types = static_cast( + malloc(sizeof(int) * static_cast(header.num_channels))); + for (int i = 0; i < header.num_channels; i++) { + header.pixel_types[i] = + TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image + + if (save_as_fp16 > 0) { + header.requested_pixel_types[i] = + TINYEXR_PIXELTYPE_HALF; // save with half(fp16) pixel format + } else { + header.requested_pixel_types[i] = + TINYEXR_PIXELTYPE_FLOAT; // save with float(fp32) pixel format(i.e. + // no precision reduction) + } + } + + int ret = SaveEXRImageToFile(&image, &header, outfilename, err); + if (ret != TINYEXR_SUCCESS) { + return ret; + } + + free(header.channels); + free(header.pixel_types); + free(header.requested_pixel_types); + + return ret; +} + +#ifdef __clang__ +// zero-as-null-ppinter-constant +#pragma clang diagnostic pop +#endif + +#endif // TINYEXR_IMPLEMENTATION_DEFINED +#endif // TINYEXR_IMPLEMENTATION diff --git a/toonz/sources/CMakeLists.txt b/toonz/sources/CMakeLists.txt index 0034aa94..6b4466d2 100644 --- a/toonz/sources/CMakeLists.txt +++ b/toonz/sources/CMakeLists.txt @@ -260,6 +260,7 @@ find_package(Qt5 REQUIRED Multimedia MultimediaWidgets SerialPort + UiTools ) set(QT_MINIMUM_VERSION 5.5.0) @@ -621,6 +622,9 @@ if(BUILD_ENV_MSVC) # place Toonz.exe and dlls in the same directory set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + # use secure functions by defaualt + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") elseif(BUILD_ENV_APPLE) # pass elseif(BUILD_ENV_UNIXLIKE) diff --git a/toonz/sources/colorfx/CMakeLists.txt b/toonz/sources/colorfx/CMakeLists.txt index a6c4b646..9f88b790 100644 --- a/toonz/sources/colorfx/CMakeLists.txt +++ b/toonz/sources/colorfx/CMakeLists.txt @@ -5,6 +5,7 @@ set(HEADERS regionstyles.h strokestyles.h zigzagstyles.h + flowlinestrokestyle.h ) set(SOURCES @@ -14,6 +15,7 @@ set(SOURCES regionstyles.cpp strokestyles.cpp zigzagstyles.cpp + flowlinestrokestyle.cpp ) if(WITH_TRANSLATION) @@ -40,4 +42,4 @@ _find_toonz_library(EXTRA_LIBS "tnzcore;tnzbase") message("FIND_FILE:" ${EXTRA_LIBS}) -target_link_libraries(colorfx Qt5::Core ${GL_LIB} ${EXTRA_LIBS}) +target_link_libraries(colorfx Qt5::Core Qt5::Gui ${GL_LIB} ${EXTRA_LIBS}) diff --git a/toonz/sources/colorfx/colorfx.cpp b/toonz/sources/colorfx/colorfx.cpp index 492b3b26..e6f67090 100644 --- a/toonz/sources/colorfx/colorfx.cpp +++ b/toonz/sources/colorfx/colorfx.cpp @@ -6,6 +6,8 @@ #include "rasterstyles.h" #include "colorfx.h" +#include "flowlinestrokestyle.h" + // static TPluginInfo info("ColorFxPlugin"); //------------------------------------------------------------------- @@ -98,4 +100,6 @@ void initColorFx() { add(new TAirbrushRasterStyle(TPixel32::Black, 10)); add(new TBlendRasterStyle(TPixel32::Black, 10)); add(new TNoColorRasterStyle()); + + add(new FlowLineStrokeStyle()); } diff --git a/toonz/sources/colorfx/flowlinestrokestyle.cpp b/toonz/sources/colorfx/flowlinestrokestyle.cpp new file mode 100644 index 00000000..0159c0ec --- /dev/null +++ b/toonz/sources/colorfx/flowlinestrokestyle.cpp @@ -0,0 +1,364 @@ + +#include "flowlinestrokestyle.h" + +#include "tcolorfunctions.h" +#include "tcurves.h" + +#define BUFFER_OFFSET(bytes) ((GLubyte *)NULL + (bytes)) +#define V_BUFFER_SIZE 1000 + +namespace { +double getMaxThickness(const TStroke *s) { + int count = s->getControlPointCount(); + double maxThickness = -1; + + for (int i = 0; i < s->getControlPointCount(); i++) { + double thick = s->getControlPoint(i).thick; + if (thick > maxThickness) maxThickness = thick; + } + return maxThickness; +} + +struct float2 { + float x; + float y; +}; + +inline double dot(const TPointD &v1, const TPointD &v2) { + return v1.x * v2.x + v1.y * v2.y; +} + +//----------------------------------------------------------------------------- + +inline bool isLinearPoint(const TPointD &p0, const TPointD &p1, + const TPointD &p2) { + return (tdistance(p0, p1) < 0.02) && (tdistance(p1, p2) < 0.02); +} + +//----------------------------------------------------------------------------- + +//! Ritorna \b true se il punto \b p1 e' una cuspide. +bool isCuspPoint(const TPointD &p0, const TPointD &p1, const TPointD &p2) { + TPointD p0_p1(p0 - p1), p2_p1(p2 - p1); + double n1 = norm(p0_p1), n2 = norm(p2_p1); + + // Partial linear points are ALWAYS cusps (since directions from them are + // determined by neighbours, not by the points themselves) + if ((n1 < 0.02) || (n2 < 0.02)) return true; + + p0_p1 = p0_p1 * (1.0 / n1); + p2_p1 = p2_p1 * (1.0 / n2); + + return (p0_p1 * p2_p1 > 0) || + (fabs(cross(p0_p1, p2_p1)) > 0.09); // more than 5 is yes + + // Distance-based check. Unscalable... + // return + // !areAlmostEqual(tdistance(p0,p2),tdistance(p0,p1)+tdistance(p1,p2),2); +} + +//----------------------------------------------------------------------------- +/*! Insert a point in the most long chunk between chunk \b indexA and chunk \b + * indexB. */ +void insertPoint(TStroke *stroke, int indexA, int indexB) { + assert(stroke); + int j = 0; + int chunkCount = indexB - indexA; + if (chunkCount % 2 == 0) return; + double length = 0; + double firstW, lastW; + for (j = indexA; j < indexB; j++) { + // cerco il chunk piu' lungo + double w0 = stroke->getW(stroke->getChunk(j)->getP0()); + double w1; + if (j == stroke->getChunkCount() - 1) + w1 = 1; + else + w1 = stroke->getW(stroke->getChunk(j)->getP2()); + double length0 = stroke->getLength(w0); + double length1 = stroke->getLength(w1); + if (length < length1 - length0) { + firstW = w0; + lastW = w1; + length = length1 - length0; + } + } + stroke->insertControlPoints((firstW + lastW) * 0.5); +} +}; // namespace +//----------------------------------------------------------------------------- + +FlowLineStrokeStyle::FlowLineStrokeStyle() + : m_color(TPixel32(100, 200, 200, 255)) + , m_density(0.25) + , m_extension(5.0) + , m_widthScale(5.0) + , m_straightenEnds(true) {} + +//----------------------------------------------------------------------------- + +TColorStyle *FlowLineStrokeStyle::clone() const { + return new FlowLineStrokeStyle(*this); +} + +//----------------------------------------------------------------------------- + +int FlowLineStrokeStyle::getParamCount() const { return ParamCount; } + +//----------------------------------------------------------------------------- + +TColorStyle::ParamType FlowLineStrokeStyle::getParamType(int index) const { + assert(0 <= index && index < getParamCount()); + switch (index) { + case Density: + case Extension: + case WidthScale: + return TColorStyle::DOUBLE; + case StraightenEnds: + return TColorStyle::BOOL; + } + return TColorStyle::DOUBLE; +} + +//----------------------------------------------------------------------------- + +QString FlowLineStrokeStyle::getParamNames(int index) const { + assert(0 <= index && index < ParamCount); + switch (index) { + case Density: + return QCoreApplication::translate("FlowLineStrokeStyle", "Density"); + case Extension: + return QCoreApplication::translate("FlowLineStrokeStyle", "Extension"); + case WidthScale: + return QCoreApplication::translate("FlowLineStrokeStyle", "Width Scale"); + case StraightenEnds: + return QCoreApplication::translate("FlowLineStrokeStyle", + "Straighten Ends"); + } + return QString(); +} + +//----------------------------------------------------------------------------- + +void FlowLineStrokeStyle::getParamRange(int index, double &min, + double &max) const { + assert(0 <= index && index < ParamCount); + switch (index) { + case Density: + min = 0.2; + max = 5.0; + break; + case Extension: + min = 0.0; + max = 20.0; + break; + case WidthScale: + min = 1.0; + max = 50.0; + break; + } +} + +//----------------------------------------------------------------------------- + +double FlowLineStrokeStyle::getParamValue(TColorStyle::double_tag, + int index) const { + assert(0 <= index && index < ParamCount); + switch (index) { + case Density: + return m_density; + case Extension: + return m_extension; + case WidthScale: + return m_widthScale; + } + return 0.0; +} + +//----------------------------------------------------------------------------- + +void FlowLineStrokeStyle::setParamValue(int index, double value) { + assert(0 <= index && index < ParamCount); + switch (index) { + case Density: + m_density = value; + break; + case Extension: + m_extension = value; + break; + case WidthScale: + m_widthScale = value; + break; + } + updateVersionNumber(); +} + +//----------------------------------------------------------------------------- + +bool FlowLineStrokeStyle::getParamValue(TColorStyle::bool_tag, + int index) const { + assert(index == StraightenEnds); + return m_straightenEnds; +} + +//----------------------------------------------------------------------------- + +void FlowLineStrokeStyle::setParamValue(int index, bool value) { + assert(index == StraightenEnds); + m_straightenEnds = value; +} + +//----------------------------------------------------------------------------- + +void FlowLineStrokeStyle::drawStroke(const TColorFunction *cf, + const TStroke *stroke) const { + auto lerp = [](float val1, float val2, float ratio) { + return val1 * (1.0 - ratio) + val2 * ratio; + }; + auto length2 = [](TPointD p) { return p.x * p.x + p.y * p.y; }; + + // Length and position of the stroke is in "actual size" (i.e. not in pixels). + // 1unit = 1/53.3333inches + // Stroke̒W͎iPPʁ1/53.3333C`j + double length = stroke->getLength(); + if (length <= 0) return; + + double maxThickness = getMaxThickness(stroke) * m_widthScale; + if (maxThickness <= 0) return; + + TStroke *tmpStroke = new TStroke(*stroke); + + // straighten ends. replace the control point only if the new handle will + // become longer + if (m_straightenEnds && stroke->getControlPointCount() >= 5 && + !stroke->isSelfLoop()) { + TPointD newPos = tmpStroke->getControlPoint(0) * 0.75 + + tmpStroke->getControlPoint(3) * 0.25; + TPointD oldVec = + tmpStroke->getControlPoint(1) - tmpStroke->getControlPoint(0); + TPointD newVec = newPos - tmpStroke->getControlPoint(0); + if (length2(oldVec) < length2(newVec)) + tmpStroke->setControlPoint(1, newPos); + + int lastId = stroke->getControlPointCount() - 1; + newPos = tmpStroke->getControlPoint(lastId) * 0.75 + + tmpStroke->getControlPoint(lastId - 3) * 0.25; + oldVec = tmpStroke->getControlPoint(lastId - 1) - + tmpStroke->getControlPoint(lastId); + newVec = newPos - tmpStroke->getControlPoint(lastId); + if (length2(oldVec) < length2(newVec)) + tmpStroke->setControlPoint(lastId - 1, newPos); + } + + // see ControlPointEditorStroke::adjustChunkParity() in + // controlpointselection.cpp + int firstChunk; + int secondChunk = tmpStroke->getChunkCount(); + int i; + for (i = tmpStroke->getChunkCount() - 1; i > 0; i--) { + if (tdistance(tmpStroke->getChunk(i - 1)->getP0(), + tmpStroke->getChunk(i)->getP2()) < 0.5) + continue; + TPointD p0 = tmpStroke->getChunk(i - 1)->getP1(); + TPointD p1 = tmpStroke->getChunk(i - 1)->getP2(); + TPointD p2 = tmpStroke->getChunk(i)->getP1(); + if (isCuspPoint(p0, p1, p2) || isLinearPoint(p0, p1, p2)) { + firstChunk = i; + insertPoint(tmpStroke, firstChunk, secondChunk); + secondChunk = firstChunk; + } + } + insertPoint(tmpStroke, 0, secondChunk); + + // thin lines amount א̖{ + int count = 2 * (int)std::ceil(maxThickness * m_density) - 1; + + TPixel32 color; + if (cf) + color = (*(cf))(m_color); + else + color = m_color; + + float2 *vertBuf = new float2[V_BUFFER_SIZE]; + int maxVertCount = 0; + + glEnableClientState(GL_VERTEX_ARRAY); + + int centerId = (count - 1) / 2; + + TThickPoint p0 = tmpStroke->getThickPoint(0); + TThickPoint p1 = tmpStroke->getThickPoint(1); + double d01 = tdistance(p0, p1); + if (d01 == 0) return; + + int len = (int)(d01); + if (len == 0) return; + + TPointD v0 = tmpStroke->getSpeed(0); + TPointD v1 = tmpStroke->getSpeed(1); + + if (norm2(v0) == 0 || norm2(v1) == 0) return; + + TPointD startVec = -normalize(v0); + TPointD endVec = normalize(v1); + + for (int i = 0; i < count; i++) { + double widthRatio = + (count == 1) ? 0.0 : (double)(i - centerId) / (double)centerId; + double extensionLength = + (1.0 - std::abs(widthRatio)) * maxThickness * m_extension; + + glColor4ub(color.r, color.g, color.b, color.m); + + int divAmount = len * 5; + if (divAmount + 3 > V_BUFFER_SIZE) divAmount = V_BUFFER_SIZE - 3; + + float2 *vp = vertBuf; + + for (int j = 0; j <= divAmount; j++, vp++) { + // current position ratio ̍W̊ʒu + double t = double(j) / double(divAmount); + + // position ratio on the stroke Xg[Nratioʒu + double w = t; + // unit vector perpendicular to the stroke ̒PʃxNg + TPointD v = rotate90(normalize(tmpStroke->getSpeed(w))); + assert(0 <= w && w <= 1); + // position on the stroke Xg[N̈ʒu + TPointD p = tmpStroke->getPoint(w); + + TPointD pos = p + v * maxThickness * widthRatio; + + if (j == 0) { + TPointD sPos = pos + startVec * extensionLength; + *vp = {float(sPos.x), float(sPos.y)}; + vp++; + } + *vp = {float(pos.x), float(pos.y)}; + if (j == divAmount) { + vp++; + TPointD ePos = pos + endVec * extensionLength; + *vp = {float(ePos.x), float(ePos.y)}; + } + } + + glVertexPointer(2, GL_FLOAT, 0, vertBuf); + // draw + glDrawArrays(GL_LINE_STRIP, 0, divAmount + 3); + } + glColor4d(0, 0, 0, 1); + glDisableClientState(GL_VERTEX_ARRAY); + delete[] vertBuf; + delete tmpStroke; +} + +//----------------------------------------------------------------------------- + +TRectD FlowLineStrokeStyle::getStrokeBBox(const TStroke *stroke) const { + TRectD rect = TColorStyle::getStrokeBBox(stroke); + double margin = + getMaxThickness(stroke) * m_widthScale * std::max(1.0, m_extension); + return rect.enlarge(margin); +} + +//----------------------------------------------------------------------------- diff --git a/toonz/sources/colorfx/flowlinestrokestyle.h b/toonz/sources/colorfx/flowlinestrokestyle.h new file mode 100644 index 00000000..25ebcd2e --- /dev/null +++ b/toonz/sources/colorfx/flowlinestrokestyle.h @@ -0,0 +1,98 @@ +#pragma once + +#ifndef FLOWLINESTROKESTYLE_H +#define FLOWLINESTROKESTYLE_H + +// TnzCore includes +#include "tsimplecolorstyles.h" +#include "tvectorimage.h" +#include "tstrokeprop.h" +#include "tgl.h" + +#include "toonz/imagestyles.h" + +#include +class TVectorRendeData; +class TRandom; + +#undef DVAPI +#undef DVVAR + +#ifdef COLORFX_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +class FlowLineStrokeStyle final : public TSimpleStrokeStyle { + TPixel32 m_color; + + enum PARAMID { + Density = 0, + Extension, + WidthScale, + StraightenEnds, + ParamCount + }; + + // thin line's density א̖x + double m_density; + // extend the edges [̐L΂ + double m_extension; + // extend the widths + // ̊gB̐̕𑝂₷regioňvZɖcȎԂ + double m_widthScale; + + bool m_straightenEnds; + +public: + FlowLineStrokeStyle(); + + TColorStyle *clone() const override; + + void invalidate() {} + + QString getDescription() const override { + return QCoreApplication::translate("FlowLineStrokeStyle", "Flow Line"); + } + std::string getBrushIdName() const override { return "FlowLineStrokeStyle"; } + + bool hasMainColor() const override { return true; } + TPixel32 getMainColor() const override { return m_color; } + void setMainColor(const TPixel32 &color) override { m_color = color; } + + int getParamCount() const override; + TColorStyle::ParamType getParamType(int index) const override; + + QString getParamNames(int index) const override; + + void getParamRange(int index, double &min, double &max) const override; + double getParamValue(TColorStyle::double_tag, int index) const override; + void setParamValue(int index, double value) override; + + bool getParamValue(TColorStyle::bool_tag, int index) const override; + void setParamValue(int index, bool value) override; + + void drawStroke(const TColorFunction *cf, + const TStroke *stroke) const override; + + void loadData(TInputStreamInterface &is) override { + int straightenEnds; + is >> m_color >> m_density >> m_extension >> m_widthScale >> straightenEnds; + m_straightenEnds = (straightenEnds == 0) ? false : true; + } + void saveData(TOutputStreamInterface &os) const override { + int straightenEnds = m_straightenEnds ? 1 : 0; + os << m_color << m_density << m_extension << m_widthScale + << m_straightenEnds; + } + bool isSaveSupported() { return true; } + + int getTagId() const override { return 201; } + + TRectD getStrokeBBox(const TStroke *stroke) const override; +}; + +#endif // FLOWLINESTROKESTYLE_H diff --git a/toonz/sources/colorfx/rasterstyles.h b/toonz/sources/colorfx/rasterstyles.h index 797fd35d..febe3828 100644 --- a/toonz/sources/colorfx/rasterstyles.h +++ b/toonz/sources/colorfx/rasterstyles.h @@ -57,6 +57,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TAirbrushRasterStyle", "Airbrush"); } + std::string getBrushIdName() const override { return "AirbrushRasterStyle"; } int getParamCount() const override { return 1; } TColorStyle::ParamType getParamType(int index) const override { @@ -121,6 +122,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TBlendRasterStyle", "Blend"); } + std::string getBrushIdName() const override { return "BlendRasterStyle"; } void makeIcon(const TDimension &d) override; @@ -150,6 +152,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TNoColorRasterStyle", "Markup"); } + std::string getBrushIdName() const override { return "NoColorRasterStyle"; } bool hasMainColor() { return false; } // TPixel32 getMainColor() const {return m_color;} diff --git a/toonz/sources/colorfx/regionstyles.h b/toonz/sources/colorfx/regionstyles.h index a9e88820..fe59156e 100644 --- a/toonz/sources/colorfx/regionstyles.h +++ b/toonz/sources/colorfx/regionstyles.h @@ -72,6 +72,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("MovingSolidColor", "Offset"); } + std::string getBrushIdName() const override { return "MovingSolidColor"; } void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const override; @@ -119,6 +120,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("ShadowStyle", "Hatched Shading"); } + std::string getBrushIdName() const override { return "ShadowStyle"; } void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const override; @@ -168,6 +170,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("ShadowStyle2", "Plain Shadow"); } + std::string getBrushIdName() const override { return "ShadowStyle2"; } void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const override; @@ -229,6 +232,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TRubberFillStyle", "Blob"); } + std::string getBrushIdName() const override { return "RubberFillStyle"; } void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const override; @@ -283,6 +287,7 @@ void setMainColor(const TPixel32 &color){ m_shadowColor=color; } return QCoreApplication::translate("TPointShadowFillStyle", "Sponge Shading"); } + std::string getBrushIdName() const override { return "PointShadowFillStyle"; } void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const override; @@ -345,6 +350,7 @@ void setMainColor(const TPixel32 &color){ m_pointColor=color; } QString getDescription() const override { return QCoreApplication::translate("TDottedFillStyle", "Polka Dots"); } + std::string getBrushIdName() const override { return "DottedFillStyle"; } protected: void loadData(TInputStreamInterface &is) override; @@ -398,6 +404,7 @@ void setMainColor(const TPixel32 &color){ m_pointColor=color; } QString getDescription() const override { return QCoreApplication::translate("TCheckedFillStyle", "Square"); } + std::string getBrushIdName() const override { return "CheckedFillStyle"; } private: void getHThickline(const TPointD &lc, const double lx, TPointD &p0, @@ -459,6 +466,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("ArtisticSolidColor", "Irregular"); } + std::string getBrushIdName() const override { return "ArtisticSolidColor"; } void drawRegion(const TColorFunction *cf, const bool antiAliasing, TRegionOutline &boundary) const override; @@ -507,6 +515,8 @@ void setMainColor(const TPixel32 &color){ m_color0=color; } QString getDescription() const override { return QCoreApplication::translate("TChalkFillStyle", "Chalk"); } + std::string getBrushIdName() const override { return "ChalkFillStyle"; } + void loadData(int oldId, TInputStreamInterface &) override; void getObsoleteTagIds(std::vector &ids) const override { ids.push_back(1133); @@ -558,6 +568,7 @@ void setMainColor(const TPixel32 &color){ m_pointColor=color; } QString getDescription() const override { return QCoreApplication::translate("TChessFillStyle", "Chessboard"); } + std::string getBrushIdName() const override { return "ChessFillStyle"; } protected: void loadData(TInputStreamInterface &is) override; @@ -609,6 +620,7 @@ void setMainColor(const TPixel32 &color){ m_pointColor=color; } QString getDescription() const override { return QCoreApplication::translate("TStripeFillStyle", "Banded"); } + std::string getBrushIdName() const override { return "StripeFillStyle"; } protected: void loadData(TInputStreamInterface &is) override; @@ -664,6 +676,7 @@ void setMainColor(const TPixel32 &color){ m_pointColor=color; } QString getDescription() const override { return QCoreApplication::translate("TLinGradFillStyle", "Linear Gradient"); } + std::string getBrushIdName() const override { return "LinGradFillStyle"; } protected: void loadData(TInputStreamInterface &is) override; @@ -717,6 +730,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TRadGradFillStyle", "Radial Gradient"); } + std::string getBrushIdName() const override { return "RadGradFillStyle"; } protected: void loadData(TInputStreamInterface &is) override; @@ -765,6 +779,9 @@ void setMainColor(const TPixel32 &color){ m_pointColor=color; } QString getDescription() const override { return QCoreApplication::translate("TCircleStripeFillStyle", "Concentric"); } + std::string getBrushIdName() const override { + return "CircleStripeFillStyle"; + } protected: void loadData(TInputStreamInterface &is) override; @@ -821,6 +838,7 @@ void setMainColor(const TPixel32 &color){ m_pointColor[0]=color; } QString getDescription() const override { return QCoreApplication::translate("TMosaicFillStyle", "Stained Glass"); } + std::string getBrushIdName() const override { return "MosaicFillStyle"; } protected: void loadData(TInputStreamInterface &is) override; @@ -871,6 +889,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TPatchFillStyle", "Beehive"); } + std::string getBrushIdName() const override { return "PatchFillStyle"; } private: void preaprePos(const TRectD &box, std::vector &v, int &lX, int &lY, diff --git a/toonz/sources/colorfx/strokestyles.h b/toonz/sources/colorfx/strokestyles.h index 6337f471..6658a458 100644 --- a/toonz/sources/colorfx/strokestyles.h +++ b/toonz/sources/colorfx/strokestyles.h @@ -90,6 +90,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TFurStrokeStyle", "Herringbone"); } + std::string getBrushIdName() const override { return "FurStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -132,6 +133,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TChainStrokeStyle", "Chain"); } + std::string getBrushIdName() const override { return "ChainStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -163,6 +165,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TSprayStrokeStyle", "Circlets"); } + std::string getBrushIdName() const override { return "SprayStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -205,6 +208,9 @@ public: QString getDescription() const override { return QCoreApplication::translate("TGraphicPenStrokeStyle", "Dashes"); } + std::string getBrushIdName() const override { + return "GraphicPenStrokeStyle"; + } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -253,6 +259,9 @@ public: QString getDescription() const override { return QCoreApplication::translate("TDottedLineStrokeStyle", "Vanishing"); } + std::string getBrushIdName() const override { + return "DottedLineStrokeStyle"; + } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -298,6 +307,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TRopeStrokeStyle", "Rope"); } + std::string getBrushIdName() const override { return "RopeStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -341,6 +351,9 @@ public: QString getDescription() const override { return QCoreApplication::translate("TCrystallizeStrokeStyle", "Tulle"); } + std::string getBrushIdName() const override { + return "CrystallizeStrokeStyle"; + } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -384,6 +397,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TBraidStrokeStyle", "Plait"); } + std::string getBrushIdName() const override { return "BraidStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_colors[0]; } @@ -432,6 +446,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TSketchStrokeStyle", "Fuzz"); } + std::string getBrushIdName() const override { return "SketchStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -474,6 +489,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TBubbleStrokeStyle", "Bubbles"); } + std::string getBrushIdName() const override { return "BubbleStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color0; } @@ -529,6 +545,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TTissueStrokeStyle", "Gauze"); } + std::string getBrushIdName() const override { return "TissueStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -576,6 +593,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TBiColorStrokeStyle", "Shade"); } + std::string getBrushIdName() const override { return "BiColorStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color0; } @@ -628,6 +646,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TNormal2StrokeStyle", "Bump"); } + std::string getBrushIdName() const override { return "Normal2StrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -671,6 +690,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TChalkStrokeStyle2", "Chalk"); } + std::string getBrushIdName() const override { return "ChalkStrokeStyle2"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -724,6 +744,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TBlendStrokeStyle2", "Fade"); } + std::string getBrushIdName() const override { return "BlendStrokeStyle2"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -774,6 +795,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TTwirlStrokeStyle", "Ribbon"); } + std::string getBrushIdName() const override { return "TwirlStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -826,6 +848,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TSawToothStrokeStyle", "Jagged"); } + std::string getBrushIdName() const override { return "SawToothStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -871,6 +894,9 @@ public: QString getDescription() const override { return QCoreApplication::translate("TMultiLineStrokeStyle2", "Gouache"); } + std::string getBrushIdName() const override { + return "MultiLineStrokeStyle2"; + } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color0; } @@ -941,6 +967,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TZigzagStrokeStyle", "Zigzag"); } + std::string getBrushIdName() const override { return "ZigzagStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -989,6 +1016,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TSinStrokeStyle", "Wave"); } + std::string getBrushIdName() const override { return "SinStrokeStyle"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -1028,6 +1056,7 @@ public: QString getDescription() const override { return QCoreApplication::translate("TFriezeStrokeStyle2", "Curl"); } + std::string getBrushIdName() const override { return "FriezeStrokeStyle2"; } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -1089,6 +1118,9 @@ public: QString getDescription() const override { return QCoreApplication::translate("TDualColorStrokeStyle2", "Striped"); } + std::string getBrushIdName() const override { + return "DualColorStrokeStyle2"; + } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color0; } @@ -1151,6 +1183,9 @@ public: QString getDescription() const override { return QCoreApplication::translate("TLongBlendStrokeStyle2", "Watercolor"); } + std::string getBrushIdName() const override { + return "LongBlendStrokeStyle2"; + } bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color0; } @@ -1240,6 +1275,7 @@ public: return QCoreApplication::translate("OutlineViewerStyle", "OutlineViewer(OnlyDebug)"); } + std::string getBrushIdName() const override { return "OutlineViewerStyle"; } int getTagId() const override { return 99; }; }; @@ -1303,6 +1339,8 @@ public: QString getDescription() const override { return QCoreApplication::translate("TMatrioskaStrokeStyle", "Toothpaste"); } + std::string getBrushIdName() const override { return "MatrioskaStrokeStyle"; } + int getTagId() const override { return 141; }; }; diff --git a/toonz/sources/common/psdlib/psdutils.cpp b/toonz/sources/common/psdlib/psdutils.cpp index 881be2fe..79e97e52 100644 --- a/toonz/sources/common/psdlib/psdutils.cpp +++ b/toonz/sources/common/psdlib/psdutils.cpp @@ -19,16 +19,17 @@ void readrow(FILE *psd, TPSDChannelInfo *chan, switch (chan->comptype) { case RAWDATA: /* uncompressed */ - pos = chan->filepos + chan->rowbytes * row; - seekres = fseek(psd, pos, SEEK_SET); - if (seekres != -1) n = fread(inbuffer, 1, chan->rowbytes, psd); + pos = chan->filepos + chan->rowbytes * row; + seekres = fseek(psd, pos, SEEK_SET); + if (seekres != -1) n = (psdPixel)fread(inbuffer, 1, chan->rowbytes, psd); break; case RLECOMP: pos = chan->rowpos[row]; seekres = fseek(psd, pos, SEEK_SET); if (seekres != -1) { - rlebytes = fread(tmpbuffer, 1, chan->rowpos[row + 1] - pos, psd); - n = unpackrow(inbuffer, tmpbuffer, chan->rowbytes, rlebytes); + rlebytes = + (psdPixel)fread(tmpbuffer, 1, chan->rowpos[row + 1] - pos, psd); + n = unpackrow(inbuffer, tmpbuffer, chan->rowbytes, rlebytes); } break; case ZIPWITHPREDICTION: diff --git a/toonz/sources/common/tapptools/tcolorutils.cpp b/toonz/sources/common/tapptools/tcolorutils.cpp index 73951243..0b1f93f4 100644 --- a/toonz/sources/common/tapptools/tcolorutils.cpp +++ b/toonz/sources/common/tapptools/tcolorutils.cpp @@ -1,6 +1,6 @@ -//#include "traster.h" +// #include "traster.h" #include "tcolorutils.h" #include "tmathutil.h" @@ -12,8 +12,8 @@ typedef float KEYER_FLOAT; //------------------------------------------------------------------------------ -//#define CLUSTER_ELEM_CONTAINER_IS_A_SET -//#define WITH_ALPHA_IN_STATISTICS +// #define CLUSTER_ELEM_CONTAINER_IS_A_SET +// #define WITH_ALPHA_IN_STATISTICS //------------------------------------------------------------------------------ @@ -214,7 +214,7 @@ void chooseLeafToClusterize(ClusterContainer::iterator &itRet, const KEYER_FLOAT *clusterCovariance = clusterFound->statistic.covariance; int i = 0; - for (; i < 9; ++i) tmpMatrixM[i] = clusterCovariance[i]; + for (; i < 9; ++i) tmpMatrixM[i] = clusterCovariance[i]; tmpMatrixM[0] -= maxEigenValue; tmpMatrixM[4] -= maxEigenValue; @@ -412,7 +412,7 @@ void SolveCubic(KEYER_FLOAT a, /* coefficient of x^3 */ x[0] = (KEYER_FLOAT)(-2.0 * sqrt(Q) * cos(theta / 3.0) - a1 / 3.0); x[1] = (KEYER_FLOAT)(-2.0 * sqrt(Q) * cos((theta + 2.0 * PI) / 3.0) - a1 / 3.0); - x[2] = (KEYER_FLOAT)(-2.0 * sqrt(Q) * cos((theta + 4.0 * PI) / 3.0) - + x[2] = (KEYER_FLOAT)(-2.0 * sqrt(Q) * cos((theta + 4.0 * PI) / 3.0) - a1 / 3.0); assert(!std::isnan(x[0])); @@ -530,7 +530,7 @@ static void clusterize(ClusterContainer &clusters, int clustersCount) { #endif - for (j = 0; j < 9; ++j) + for (j = 0; j < 9; ++j) subcluster2->statistic.matrixR[j] = choosedCluster->statistic.matrixR[j] - subcluster1->statistic.matrixR[j]; @@ -624,7 +624,7 @@ void Cluster::computeStatistics() { statistic.sumCoords = TPoint(0, 0); - int i = 0; + int i = 0; for (; i < 3; ++i) statistic.sumComponents[i] = 0.0; for (i = 0; i < 9; ++i) statistic.matrixR[i] = 0.0; @@ -729,7 +729,7 @@ void Cluster::getMeanAxis(KEYER_FLOAT axis[3]) { //------------------------------------------------------------------------------ -//#define METODO_USATO_SU_TOONZ46 +// #define METODO_USATO_SU_TOONZ46 static void buildPaletteForBlendedImages(std::set &palette, const TRaster32P &raster, @@ -850,11 +850,11 @@ struct EdgePoint { // identify available corners if (info & UpperEdge) { if (info & RightEdge) info = info | RightUpper; - if (info & LeftEdge) info = info | LeftUpper; + if (info & LeftEdge) info = info | LeftUpper; } if (info & LowerEdge) { if (info & RightEdge) info = info | RightLower; - if (info & LeftEdge) info = info | LeftLower; + if (info & LeftEdge) info = info | LeftLower; } } @@ -932,7 +932,7 @@ bool colorChipLeftUpperThan(const ColorChip &chip1, const ColorChip &chip2) { } // namespace -/*-- 似ている色をまとめて1つのStyleにする --*/ +/*-- Combine similar colors into one style. --*/ void TColorUtils::buildPalette(std::set &palette, const TRaster32P &raster, int maxColorCount) { int lx = raster->getLx(); @@ -973,7 +973,7 @@ void TColorUtils::buildPalette(std::set &palette, } //------------------------------------------------------------------------------ -/*-- 全ての異なるピクセルの色を別のStyleにする --*/ +/*-- Make each different pixel color a separate style --*/ void TColorUtils::buildPrecisePalette(std::set &palette, const TRaster32P &raster, int maxColorCount) { @@ -997,9 +997,10 @@ void TColorUtils::buildPrecisePalette(std::set &palette, raster->unlock(); - /*-- 色数が最大値を超えたら、似ている色をまとめて1つのStyleにする手法を行う + /*-- If the maximum number of colors is exceeded, perform the method of + * combining similar colors into a single style. * --*/ - if (count == 0) { + if (count == 0) { palette.clear(); buildPalette(palette, raster, maxColorCount); } diff --git a/toonz/sources/common/tcolor/tcolorfunctions.cpp b/toonz/sources/common/tcolor/tcolorfunctions.cpp index 757c485b..56782507 100644 --- a/toonz/sources/common/tcolor/tcolorfunctions.cpp +++ b/toonz/sources/common/tcolor/tcolorfunctions.cpp @@ -28,10 +28,10 @@ TGenericColorFunction::TGenericColorFunction(const double m[4], //--------------------------------------- TPixel32 TGenericColorFunction::operator()(const TPixel32 &color) const { - return TPixel32(tcrop(m_m[0] * color.r + m_c[0], 0.0, 255.0), - tcrop(m_m[1] * color.g + m_c[1], 0.0, 255.0), - tcrop(m_m[2] * color.b + m_c[2], 0.0, 255.0), - tcrop(m_m[3] * color.m + m_c[3], 0.0, 255.0)); + return TPixel32((int)tcrop(m_m[0] * color.r + m_c[0], 0.0, 255.0), + (int)tcrop(m_m[1] * color.g + m_c[1], 0.0, 255.0), + (int)tcrop(m_m[2] * color.b + m_c[2], 0.0, 255.0), + (int)tcrop(m_m[3] * color.m + m_c[3], 0.0, 255.0)); } //--------------------------------------- @@ -46,7 +46,7 @@ bool TGenericColorFunction::getParameters(Parameters &p) const { //--------------------------------------- TPixel32 TTranspFader::operator()(const TPixel32 &color) const { - return TPixel32(color.r, color.g, color.b, m_transp * color.m); + return TPixel32(color.r, color.g, color.b, (int)(m_transp * (double)color.m)); } //--------------------------------------- diff --git a/toonz/sources/common/tcolor/tpixel.cpp b/toonz/sources/common/tcolor/tpixel.cpp index 00a64fca..ffd82acd 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 e04ea8e3..c17ba35c 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; } } @@ -172,7 +172,7 @@ void rgb2hsv(int dstHsv[3], const TPixel32 &srcRgb, int maxHsv) { double max, min; double delta; double r, g, b; - double v, s, h; + double v, s, h = 0.; r = srcRgb.r / 255.; g = srcRgb.g / 255.; b = srcRgb.b / 255.; @@ -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,16 @@ TPixel64 toPixel64(const TPixelGR8 &src) { //----------------------------------------------------------------------------- +TPixel64 toPixel64(const TPixelF &src) { + const double factor = 65535.0; + return TPixel64(wordCrop(tround((double)src.r * factor)), + wordCrop(tround((double)src.g * factor)), + wordCrop(tround((double)src.b * factor)), + wordCrop(tround((double)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 +368,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((float)src.r, (float)src.g, (float)src.b, (float)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 3efc6242..7c3b3685 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 5bff0825..3f313694 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/tfx.cpp b/toonz/sources/common/tfx/tfx.cpp index ab0d8ba7..c0b344bd 100644 --- a/toonz/sources/common/tfx/tfx.cpp +++ b/toonz/sources/common/tfx/tfx.cpp @@ -1002,7 +1002,10 @@ TFx *TFx::getLinkedFx() const { //-------------------------------------------------- -void TFx::setFxVersion(int v) { m_imp->m_attributes.setFxVersion(v); } +void TFx::setFxVersion(int v) { + m_imp->m_attributes.setFxVersion(v); + onFxVersionSet(); +} //-------------------------------------------------- diff --git a/toonz/sources/common/tfx/tmacrofx.cpp b/toonz/sources/common/tfx/tmacrofx.cpp index 69e645d7..a62a6c3c 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 15600685..1493103c 100644 --- a/toonz/sources/common/tfx/trenderer.cpp +++ b/toonz/sources/common/tfx/trenderer.cpp @@ -28,8 +28,8 @@ #include // Debug -//#define DIAGNOSTICS -//#include "diagnostics.h" +// #define DIAGNOSTICS +// #include "diagnostics.h" #include #include @@ -129,6 +129,8 @@ public: raster = TRaster32P(size); else if (bpp == 64) raster = TRaster64P(size); + else if (bpp == 128) + raster = TRasterFP(size); else assert(false); @@ -988,14 +990,8 @@ void RenderTask::run() { if (!m_fieldRender && !m_stereoscopic) { // Common case - just build the first tile buildTile(m_tileA); - // static int iCount = 0; - // QString qPath("C:\\butta\\image_" + - // QString::number(++iCount).rightJustified(3, '0') + ".tif"); - // TImageWriter::save(TFilePath(qPath.toStdWString()), m_tileA.getRaster()); - /*-- Normally this is the Fx rendering process --*/ + /*-- Normally, Fx rendering process is performed here --*/ m_fx.m_frameA->compute(m_tileA, t, m_info); - // The tile now has the image. - // TImageWriter::save(TFilePath(qPath.toStdWString()), m_tileA.getRaster()); } else { assert(!(m_stereoscopic && m_fieldRender)); // Field rendering or stereoscopic case @@ -1051,6 +1047,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); } //--------------------------------------------------------- @@ -1134,7 +1132,7 @@ void RenderTask::onFinished(TThread::RunnableP) { // If the render instance has just expired if (instanceExpires) { - /*-- キャンセルされた場合はm_overallRenderedRegionの更新をしない --*/ + /*-- Do not update m_overallRenderedRegion if canceled --*/ // Inform the render ports rendererImp->notifyRenderFinished(isCanceled); @@ -1187,8 +1185,8 @@ void TRendererStartInvoker::doStartRender(TRendererImp *renderer, } std::vector calculateSortedFxs(TRasterFxP rootFx) { - std::map> E; - std::set Sources; + std::map> E; /* information on the edges */ + std::set Sources; /* Node group with no input */ std::queue Q; Q.push(rootFx.getPointer()); @@ -1202,8 +1200,7 @@ std::vector calculateSortedFxs(TRasterFxP rootFx) { continue; } - /* Visit the Fx beyond the connected input port -Exit if there is no input port */ + /* If there is no input port to visit Fx connected to input port, exit */ int portCount = vptr->getInputPortCount(); if (portCount < 1) { Sources.insert(vptr); @@ -1226,7 +1223,7 @@ Exit if there is no input port */ } } - /* Topological sort */ + /* topological sorting */ std::set visited; std::vector L; std::function visit = [&visit, &visited, &E, @@ -1405,10 +1402,10 @@ void TRendererImp::startRendering( // Build the frame's description alias const TRenderer::RenderData &renderData = *it; - /*--- Camera size (used for Level Auto and noise) ---*/ + /*--- Camera size (used for LevelAuto and noise) ---*/ TRenderSettings rs = renderData.m_info; rs.m_cameraBox = camBox; - /*--- Flag when Preview calculation is canceled in the middle ---*/ + /*--- Flag when Preview calculation is canceled during the process ---*/ rs.m_isCanceled = &renderInfos->m_canceled; TRasterFxP fx = renderData.m_fxRoot.m_frameA; @@ -1424,7 +1421,8 @@ void TRendererImp::startRendering( // If the render contains offscreen render, then prepare the // QOffscreenSurface // in main (GUI) thread. For now it is used only in the plasticDeformerFx. - if (alias.find("plasticDeformerFx") != std::string::npos && + if ((alias.find("plasticDeformerFx") != std::string::npos || + alias.find("iwa_FlowPaintBrushFx") != std::string::npos) && QThread::currentThread() == qGuiApp->thread()) { rs.m_offScreenSurface.reset(new QOffscreenSurface()); rs.m_offScreenSurface->setFormat(QSurfaceFormat::defaultFormat()); diff --git a/toonz/sources/common/tfx/unaryFx.cpp b/toonz/sources/common/tfx/unaryFx.cpp index 86260d3d..ad4deabf 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 3864aaa7..e6534ea9 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/tiio/tiio_jpg_exif.cpp b/toonz/sources/common/tiio/tiio_jpg_exif.cpp index 7da7d8ec..8fe85ee1 100644 --- a/toonz/sources/common/tiio/tiio_jpg_exif.cpp +++ b/toonz/sources/common/tiio/tiio_jpg_exif.cpp @@ -504,7 +504,9 @@ void JpgExifReader::ProcessExifDir(unsigned char *DirStart, unsigned char *OffsetBase, unsigned ExifLength, int NestingLevel) { int de; +#ifdef ReadAllTags int a; +#endif int NumDirEntries; unsigned ThumbnailOffset = 0; unsigned ThumbnailSize = 0; @@ -524,7 +526,7 @@ void JpgExifReader::ProcessExifDir(unsigned char *DirStart, { unsigned char *DirEnd; - DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries); + DirEnd = DIR_ENTRY_ADDR(DirStart, static_cast(NumDirEntries)); if (DirEnd + 4 > (OffsetBase + ExifLength)) { if (DirEnd + 2 == OffsetBase + ExifLength || DirEnd == OffsetBase + ExifLength) { @@ -551,7 +553,7 @@ void JpgExifReader::ProcessExifDir(unsigned char *DirStart, unsigned char *ValuePtr; int ByteCount; unsigned char *DirEntry; - DirEntry = DIR_ENTRY_ADDR(DirStart, de); + DirEntry = DIR_ENTRY_ADDR(DirStart, static_cast(de)); Tag = Get16u(DirEntry); Format = Get16u(DirEntry + 2); diff --git a/toonz/sources/common/timage_io/timage_io.cpp b/toonz/sources/common/timage_io/timage_io.cpp index 43ddc9df..46b871a7 100644 --- a/toonz/sources/common/timage_io/timage_io.cpp +++ b/toonz/sources/common/timage_io/timage_io.cpp @@ -1,4 +1,4 @@ - + // TnzCore includes #include "tsystem.h" @@ -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) {} //----------------------------------------------------------- @@ -179,6 +181,12 @@ struct pixel_traits { typedef short buffer_type; }; +template <> +struct pixel_traits { + typedef TPixelF rgbm_pixel_type; + typedef float buffer_type; +}; + template <> struct pixel_traits { typedef TPixel32 rgbm_pixel_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 = @@ -420,7 +431,18 @@ TImageP TImageReader::load0() { // assert(info.m_samplePerPixel == 3 || info.m_samplePerPixel == 4); TRasterP _ras; - if (info.m_bitsPerSample == 16) { + // currently m_bitsPerSample == 32 is only possible when loading + // full-float / uint EXR images + // EDIT: half float EXR images also set m_bitsPerSample to 32 + // to obtain floating point images + // 画像側の情報がinfo + if (info.m_bitsPerSample == 32 && m_isFloatEnabled) { + // いったんノンリニアに変換して読み込む + 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. @@ -548,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); } @@ -574,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 @@ -587,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? @@ -616,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"); @@ -632,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()); @@ -641,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 } @@ -650,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)); @@ -657,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)); @@ -686,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/tipc/tipc.cpp b/toonz/sources/common/tipc/tipc.cpp index bf2703a7..d61b9734 100644 --- a/toonz/sources/common/tipc/tipc.cpp +++ b/toonz/sources/common/tipc/tipc.cpp @@ -3,7 +3,7 @@ // Qt includes #include #include -#include +#include #include #include #include @@ -54,7 +54,7 @@ maximum // Diagnostics Stuff //******************************************************** -//#define TIPC_DEBUG +// #define TIPC_DEBUG #ifdef TIPC_DEBUG #define tipc_debug(expr) expr @@ -75,7 +75,7 @@ int shm_max = -1; int shm_all = -1; int shm_seg = -1; int shm_mni = -1; -} +} // namespace //******************************************************** // tipc Stream Implementation @@ -260,10 +260,12 @@ QString tipc::applicationSpecificServerName(QString srvName) { //------------------------------------------------------------- -bool tipc::startBackgroundProcess(QString cmdline) { +bool tipc::startBackgroundProcess(QString cmdlineProgram, + QStringList cmdlineArguments) { #ifdef _WIN32 QProcess *proc = new QProcess; - proc->start(cmdline); + + proc->start(cmdlineProgram, cmdlineArguments); if (proc->state() == QProcess::NotRunning) { delete proc; return false; @@ -275,7 +277,7 @@ bool tipc::startBackgroundProcess(QString cmdline) { SLOT(deleteLater())); return true; #else - return QProcess::startDetached(cmdline); + return QProcess::startDetached(cmdlineProgram, cmdlineArguments); ; #endif } @@ -295,8 +297,10 @@ bool tipc::startBackgroundProcess(QString cmdline) { \warning Please, observe that a correct slave server name should be ensured to be unique to the system. */ -bool tipc::startSlaveServer(QString srvName, QString cmdline) { - if (!tipc::startBackgroundProcess(cmdline)) return false; +bool tipc::startSlaveServer(QString srvName, QString cmdlineProgram, + QStringList cmdlineArguments) { + if (!tipc::startBackgroundProcess(cmdlineProgram, cmdlineArguments)) + return false; QString mainSrvName(srvName + "_main"); @@ -364,9 +368,10 @@ bool tipc::startSlaveServer(QString srvName, QString cmdline) { ensured to be unique to the parent process. */ bool tipc::startSlaveConnection(QLocalSocket *socket, QString srvName, - int msecs, QString cmdline, + int msecs, QString cmdlineProgram, + QStringList cmdlineArguments, QString threadName) { - QTime time; + QElapsedTimer time; time.start(); if (msecs == -1) msecs = (std::numeric_limits::max)(); @@ -377,7 +382,8 @@ bool tipc::startSlaveConnection(QLocalSocket *socket, QString srvName, // If the socket is not connecting, the server lookup table returned that the // no server with // the passed name exists. This means that a server must be created. - if (socket->state() == QLocalSocket::UnconnectedState && !cmdline.isEmpty()) { + if (socket->state() == QLocalSocket::UnconnectedState && + !cmdlineProgram.isEmpty()) { // Completely serialize the server start static QMutex mutex; QMutexLocker locker(&mutex); @@ -387,7 +393,8 @@ bool tipc::startSlaveConnection(QLocalSocket *socket, QString srvName, if (socket->state() != QLocalSocket::UnconnectedState) goto connecting; // Invoke the supplied command line to start the server - if (!tipc::startSlaveServer(srvName, cmdline)) return false; + if (!tipc::startSlaveServer(srvName, cmdlineProgram, cmdlineArguments)) + return false; // Reconnect to the server socket->connectToServer(fullSrvName); diff --git a/toonz/sources/common/tmsgcore.cpp b/toonz/sources/common/tmsgcore.cpp index ab1f3736..8eca8505 100644 --- a/toonz/sources/common/tmsgcore.cpp +++ b/toonz/sources/common/tmsgcore.cpp @@ -10,7 +10,7 @@ #include TMsgCore *TMsgCore::instance() { - static TMsgCore *theInstance = 0; + static TMsgCore *theInstance = 0; if (!theInstance) theInstance = new TMsgCore(); return theInstance; } @@ -133,7 +133,11 @@ void TMsgCore::readFromSocket(QTcpSocket *socket) // server side message.chop(lastbegin); } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList messages = message.split("#TMSG", Qt::SkipEmptyParts); +#else QStringList messages = message.split("#TMSG", QString::SkipEmptyParts); +#endif for (int i = 0; i < messages.size(); i++) { QString str = messages.at(i).simplified(); @@ -177,11 +181,7 @@ bool TMsgCore::send(DVGui::MsgType type, const QString &message) // client side : (type == DVGui::WARNING ? "#TMSG WARNING " : "#TMSG INFO ")) + message + " #END\n"; -#if QT_VERSION >= 0x050000 m_clientSocket->write(socketMessage.toLatin1()); -#else - m_clientSocket->write(socketMessage.toAscii()); -#endif m_clientSocket->flush(); // m_clientSocket->waitForBytesWritten (1000); } else diff --git a/toonz/sources/common/tparam/tdoubleparam.cpp b/toonz/sources/common/tparam/tdoubleparam.cpp index 13397025..ffeaf854 100644 --- a/toonz/sources/common/tparam/tdoubleparam.cpp +++ b/toonz/sources/common/tparam/tdoubleparam.cpp @@ -40,7 +40,7 @@ public: } TActualDoubleKeyframe &operator=(const TDoubleKeyframe &src) { TDoubleKeyframe::operator=(src); - m_unit = 0; + m_unit = 0; if (m_type == Expression || m_type == SimilarShape) { m_expression.setText(m_expressionText); } else if (m_type == File) { @@ -96,16 +96,15 @@ inline double getConstantValue(const TActualDoubleKeyframe &k0, inline double getLinearValue(const TActualDoubleKeyframe &k0, const TActualDoubleKeyframe &k1, double f) { - return k0.m_value + - (f - k0.m_frame) * (k1.m_value - k0.m_value) / - (k1.m_frame - k0.m_frame); + return k0.m_value + (f - k0.m_frame) * (k1.m_value - k0.m_value) / + (k1.m_frame - k0.m_frame); } //--------------------------------------------------------- static void truncateSpeeds(double aFrame, double bFrame, TPointD &aSpeedTrunc, TPointD &bSpeedTrunc) { - double deltaX = bFrame - aFrame; + double deltaX = bFrame - aFrame; if (aSpeedTrunc.x < 0) aSpeedTrunc.x = 0; if (bSpeedTrunc.x > 0) bSpeedTrunc.x = 0; @@ -262,7 +261,7 @@ inline double getExpressionValue(const TActualDoubleKeyframe &k0, const TActualDoubleKeyframe &k1, double frame, const TMeasure *measure) { double t = 0, rframe = frame - k0.m_frame; - if (k1.m_frame > k0.m_frame) t = rframe / (k1.m_frame - k0.m_frame); + if (k1.m_frame > k0.m_frame) t = rframe / (k1.m_frame - k0.m_frame); TSyntax::Calculator *calculator = k0.m_expression.getCalculator(); if (calculator) { calculator->setUnit( @@ -543,7 +542,7 @@ double TDoubleParam::getValue(double frame, bool leftmost) const { if (frame < f0) frame = f0; else if (frame > f1 && !m_imp->m_cycleEnabled) - frame = f1; + frame = f1; double valueOffset = 0; if (m_imp->m_cycleEnabled) { @@ -598,7 +597,7 @@ double TDoubleParam::getValue(double frame, bool leftmost) const { if (b->m_type != TDoubleKeyframe::Expression || !b->m_expression.isCycling()) tmpKeyframe[0].m_value = getValue(b->m_frame); - b = tmpKeyframe.begin(); + b = tmpKeyframe.begin(); } // .. and/or if prev segment is not then update the a value if (a != keyframes.begin() && @@ -616,7 +615,7 @@ double TDoubleParam::getValue(double frame, bool leftmost) const { int relPos = tfloor(b->m_frame - a->m_frame), step = std::min(a->m_step, relPos); - tmpKeyframe[2].m_frame = a->m_frame + tfloor(relPos, step); + tmpKeyframe[2].m_frame = a->m_frame + tfloor(relPos, step); if (frame > b->m_frame) frame = b->m_frame; frame = a->m_frame + tfloor(tfloor(frame - a->m_frame), step); @@ -680,7 +679,7 @@ bool TDoubleParam::setValue(double frame, double value) { it = std::lower_bound(keyframes.begin(), keyframes.end(), k); int index = 0; bool created = false; - /*-- キーフレームが見つかった場合 --*/ + /*-- If a keyframe is found --*/ if (it != keyframes.end() && it->m_frame == frame) { // changing a keyframe value index = std::distance(keyframes.begin(), it); @@ -693,7 +692,7 @@ bool TDoubleParam::setValue(double frame, double value) { m_imp->notify(TParamChange(this, 0, 0, true, false, false)); } - /*-- セグメントの部分なので、新たにキーフレームを作る --*/ + /*-- It is a segment, so create a new keyframe. --*/ else { assert(it == keyframes.end() || it->m_frame > frame); @@ -713,7 +712,8 @@ bool TDoubleParam::setValue(double frame, double value) { it->m_prevType = TDoubleKeyframe::None; else { it->m_prevType = it[-1].m_type; - /*-- FxGuiでSegment内にKeyを打った場合は、Step値も引き継ぐ --*/ + /*-- If you create Key in Segment in FxGui, the Step value is also + * inherited. --*/ it->m_step = it[-1].m_step; } if (it + 1 != keyframes.end()) it[1].m_prevType = it->m_type; @@ -786,7 +786,7 @@ void TDoubleParam::setKeyframes(const std::map &ks) { } if (!keyframes.empty()) { keyframes[0].m_prevType = TDoubleKeyframe::None; - for (int i = 1; i < (int)keyframes.size(); i++) + for (int i = 1; i < (int)keyframes.size(); i++) keyframes[i].m_prevType = keyframes[i - 1].m_type; } @@ -919,8 +919,8 @@ void TDoubleParam::deleteKeyframe(double frame) { TDoubleKeyframe(frame)); if (it == keyframes.end() || it->m_frame != frame) return; - TDoubleKeyframe::Type type = it->m_prevType; - it = m_imp->m_keyframes.erase(it); + TDoubleKeyframe::Type type = it->m_prevType; + it = m_imp->m_keyframes.erase(it); if (it != keyframes.end()) it->m_prevType = type; m_imp->notify(TParamChange(this, 0, 0, true, false, false)); diff --git a/toonz/sources/common/tparam/tpixelparam.cpp b/toonz/sources/common/tparam/tpixelparam.cpp index 73a5ca2a..95e3c0c0 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 f0d502dd..ee98870d 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 84e5b341..c2760589 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) == @@ -239,8 +239,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); @@ -311,7 +310,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 718f4325..1f2700bf 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 a9c9ae1f..09e8c194 100644 --- a/toonz/sources/common/trop/quickput.cpp +++ b/toonz/sources/common/trop/quickput.cpp @@ -40,11 +40,13 @@ inline TPixel32 applyColorScale(const TPixel32 &color, const TPixel32 &colorScale, bool toBePremultiplied = false) { /*-- - * 半透明のラスタをViewer上で半透明にquickputするとき、色が暗くなってしまうのを防ぐ + * Prevent colors from being darkened when quickputting a semi-transparent + * raster on the Viewer * --*/ if (colorScale.r == 0 && colorScale.g == 0 && colorScale.b == 0) { /*-- - * toBePremultipliedがONのときは、後でPremultiplyをするので、ここでは行わない + * When toBePremultiplied is ON, Premultiply is done later, so it is not + * done here. * --*/ if (toBePremultiplied) return TPixel32(color.r, color.g, color.b, color.m * colorScale.m / 255); @@ -1166,6 +1168,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, @@ -1878,7 +2330,7 @@ void doQuickPutNoFilter(const TRaster32P &dn, const TRaster32P &up, double sx, TPixel32 upPix = *(upBasePix + (yI * upWrap + xI)); - if (firstColumn) upPix.m = 65535; + if (firstColumn) upPix.m = 255; if (upPix.m == 0 || (whiteTransp && upPix == TPixel::White)) continue; @@ -4718,6 +5170,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 +5201,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 d6d5f572..d4cb2a91 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,15 @@ 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 +808,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 +842,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 +916,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 +995,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 71afc4a0..a9be5f73 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 413ae34d..72a877a7 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); @@ -329,6 +329,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 0208a8bc..667e3e79 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,34 @@ 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 a1bb38b8..d0b8f45b 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; - - if (up64 && down64 && out64) { - FOR_EACH_PIXEL_64_BEGIN_LOOP - - 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); - - 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; - - 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 - } else - throw TRopException("TRop::add invalid raster combination"); - } + return; } + + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_LOOP + + 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); + + 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 * 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; - - 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; - - 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; - - 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 - } else - throw TRopException("TRop::add invalid raster combination"); - } + return; } + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + 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; + + 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 + + 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; - - if (up64 && down64 && out64) { - FOR_EACH_PIXEL_64_BEGIN_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 { - 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; - } - - FOR_EACH_PIXEL_64_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"); - } + return; } + + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + if (up64 && down64 && out64) { + FOR_EACH_PIXEL_64_BEGIN_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 { + 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; + } + + 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 + + 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; - - 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 - } else - throw TRopException("TRop::sub invalid raster combination"); - } + return; } + + 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 + 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; - - 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 - } else - throw TRopException("TRop::sub invalid raster combination"); - } + return; } + + 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 + 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; - - 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 + } 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 - throw TRopException("TRop::min invalid raster combination"); + *outPix = *downPix; + FOR_EACH_PIXEL_32_END_LOOP } + return; } + + TRasterFP upF = rup; + TRasterFP downF = rdown; + TRasterFP outF = rout; + + if (upF && downF && outF) { + if (matte) { + 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; + + 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 + *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; - - 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; - - FOR_EACH_PIXEL_64_END_LOOP - } else { - 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 - } else - throw TRopException("TRop::max invalid raster combination"); - } + return; } + TRaster64P up64 = rup; + TRaster64P down64 = rdown; + TRaster64P out64 = rout; + + 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; + + 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 = 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 + 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::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 aae900fa..1be7f0f9 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 a0a09c9a..d1b5a739 100644 --- a/toonz/sources/common/trop/traylit.cpp +++ b/toonz/sources/common/trop/traylit.cpp @@ -40,11 +40,11 @@ of the ray we're tracing // Build colors-related variables int max = T::maxChannelValue; - /*-- 透明部分の色 --*/ + /*-- Color of transparent part --*/ int transp_val = (params.m_invert) ? max : 0, opaque_val = max - transp_val; int value, val_r, val_g, val_b, val_m; double lightness, r_fac, g_fac, b_fac, m_fac; - /*-- 8bit/16bitの違いを吸収する係数 --*/ + /*-- Coefficients to absorb 8bit/16bit difference --*/ double factor = max / 255.0; // NOTE: These variable initializations are, well, @@ -60,13 +60,16 @@ of the ray we're tracing double smoothness = log(params.m_smoothness * 5.0 / 100.0 + 1.0); double radius = params.m_radius; - /*-- 1ステップ進んだ時、次のピクセルで光源が無かったときの光の弱まる割合 --*/ + /*-- The rate at which light diminishes when there is no light source at the + * next pixel when advancing one step. --*/ double neg_delta_p = smoothness * intensity; - /*-- 1ステップ進んだ時、次のピクセルで光源が有ったときの光の強まる割合 --*/ + /*-- The rate at which light intensifies when there is a light source at the + * next pixel when advancing one step. --*/ double quot_delta_p = intensity / max; // /*-- - * m_colorはRaylitFxのColor値。r_fac、g_fac、b_facは各チャンネルをPremultiplyした値 + * m_color is the Color value of RaylitFx. r_fac, g_fac, b_fac are the + * premultiplied values of each channel * --*/ m_fac = (params.m_color.m / 255.0); r_fac = m_fac * (params.m_color.r / 255.0); @@ -151,7 +154,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 +190,179 @@ 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; + /*-- Color of transparent part --*/ + 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; + /*-- Coefficients to absorb 8bit/16bit difference --*/ + 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; + + /*-- The rate at which light diminishes when there is no light source at the + * next pixel when advancing one step. --*/ + double neg_delta_p = smoothness * intensity; + /*-- The rate at which light intensifies when there is a light source at the + * next pixel when advancing one step. --*/ + double quot_delta_p = intensity / max; // + + /*-- + * m_color is the Color value of RaylitFx. r_fac, g_fac, b_fac are the + * premultiplied values of each channel + * --*/ + 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,8 +498,143 @@ 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つを計算する --*/ +/*-- Calculate one of the 8 pizza-shaped regions --*/ template void computeOctant(const TRasterPT &src, const TRasterPT &dst, int octant, const TRop::RaylitParams ¶ms, @@ -346,7 +657,7 @@ void computeOctant(const TRasterPT &src, const TRasterPT &dst, int octant, lxIn = src->getLx(), lxOut = dst->getLx(); lyIn = src->getLy(), lyOut = dst->getLy(); - /*-- 1ピクセルずつ進むときの移動値 --*/ + /*-- Movement value when moving forward by 1 pixel --*/ // Vertical octant pairs if (octant == 1 || octant == 8) dxIn = 1, dxOut = 1, x0 = tfloor(pOut.x), x1 = lxOut; @@ -375,7 +686,8 @@ void computeOctant(const TRasterPT &src, const TRasterPT &dst, int octant, x1 = lyOut, std::swap(srcRect.y0, srcRect.y1), srcRect.y0 = lyOut - srcRect.y0, srcRect.y1 = lyOut - srcRect.y1; - /*-- 縦向きのピザ領域を計算する場合は、90度回転してから --*/ + /*-- To calculate the pizza area in vertical orientation, rotate 90 degrees in + * advance --*/ // Swap x and y axis where necessary if (octant == 2 || octant == 3 || octant == 6 || octant == 7) { std::swap(lxIn, lyIn), std::swap(lxOut, lyOut); @@ -461,6 +773,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 +787,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 fd92cc73..8a1d9cc4 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,76 @@ 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( 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 (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; + 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); + inv_sum_weights = 1.0f / sum_weights; - 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)); + __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); - 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; - } + __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 +1942,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 +1965,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 +2040,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 +2065,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 +2203,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 +2222,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 +2260,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 +2275,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 +2412,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 +2431,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 +2469,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 +2484,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 +2810,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 +2831,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 +3274,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 +3319,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 +3425,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 +3846,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 +4035,7 @@ void resample_main_cm32_rgbm_bigradius( if (calc) delete[] calc; } -} +} // namespace /*---------------------------------------------------------------------------*/ @@ -3871,7 +4131,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 +4400,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 +4845,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 +4923,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 f782d50f..6ce30dd8 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,100 @@ 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); + + 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,15 +303,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 +351,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 +400,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 6d247b79..b27f2050 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/common/tsound/tsop.cpp b/toonz/sources/common/tsound/tsop.cpp index 5e79e8cc..2db635ba 100644 --- a/toonz/sources/common/tsound/tsop.cpp +++ b/toonz/sources/common/tsound/tsop.cpp @@ -298,8 +298,7 @@ T *resampleT(T &src, TINT32 sampleRate, FLT_TYPE flt_type) { T *dst = new TSoundTrackT( sampleRate, src.getChannelCount(), (TINT32)(src.getSampleCount() * - (sampleRate / (double)src.getSampleRate())), - src.getFormatType()); + (sampleRate / (double)src.getSampleRate()))); double src_rad, f, src_f0, src_to_f; double weight, weightsum; @@ -395,8 +394,12 @@ dstSample += tmp; // assert(dstSample.getValue(0) == dstChannel[0]); } + // Removed tround() since it was destroying 32-bits float data + // for (int i = 0; i < src.getChannelCount(); i++) + // dstSample.setValue(i, (ChannelValueType)(tround(dstChannel[i]))); + for (int i = 0; i < src.getChannelCount(); i++) - dstSample.setValue(i, (ChannelValueType)(tround(dstChannel[i]))); + dstSample.setValue(i, (ChannelValueType)(dstChannel[i])); *(dst->samples() + id) = dstSample; @@ -499,16 +502,16 @@ public: return TSoundTrackP(dst); } - TSoundTrackP compute(const TSoundTrackMono32float &src) override { - TSoundTrackMono32float *dst = resampleT( - const_cast(src), m_sampleRate, m_filterType); + TSoundTrackP compute(const TSoundTrackMono32Float &src) override { + TSoundTrackMono32Float *dst = resampleT( + const_cast(src), m_sampleRate, m_filterType); return TSoundTrackP(dst); } - TSoundTrackP compute(const TSoundTrackStereo32float &src) override { - TSoundTrackStereo32float *dst = - resampleT(const_cast(src), m_sampleRate, + TSoundTrackP compute(const TSoundTrackStereo32Float &src) override { + TSoundTrackStereo32Float *dst = + resampleT(const_cast(src), m_sampleRate, m_filterType); return TSoundTrackP(dst); @@ -603,18 +606,18 @@ TSoundTrackP doConvertWithoutResamplingT(SRC *src, return dstS32; } - TSoundTrackMono32float *dstM32f = - dynamic_cast(dst.getPointer()); - if (dstM32f) { - convertSamplesT(*dstM32f, *src); - return dstM32f; + TSoundTrackMono32Float *dstM32F = + dynamic_cast(dst.getPointer()); + if (dstM32F) { + convertSamplesT(*dstM32F, *src); + return dstM32F; } - TSoundTrackStereo32float *dstS32f = - dynamic_cast(dst.getPointer()); - if (dstS32f) { - convertSamplesT(*dstS32f, *src); - return dstS32f; + TSoundTrackStereo32Float *dstS32F = + dynamic_cast(dst.getPointer()); + if (dstS32F) { + convertSamplesT(*dstS32F, *src); + return dstS32F; } return 0; @@ -654,6 +657,7 @@ public: TSoundTrackP compute(const TSoundTrackStereo16 &src) override { return doConvertWithoutResamplingT(&src, m_format); } + TSoundTrackP compute(const TSoundTrackMono24 &src) override { return doConvertWithoutResamplingT(&src, m_format); } @@ -670,11 +674,11 @@ public: return doConvertWithoutResamplingT(&src, m_format); } - TSoundTrackP compute(const TSoundTrackMono32float &src) override { + TSoundTrackP compute(const TSoundTrackMono32Float &src) override { return doConvertWithoutResamplingT(&src, m_format); } - TSoundTrackP compute(const TSoundTrackStereo32float &src) override { + TSoundTrackP compute(const TSoundTrackStereo32Float &src) override { return doConvertWithoutResamplingT(&src, m_format); } }; @@ -779,7 +783,8 @@ void TSop::convert(TSoundTrackP &dst, const TSoundTrackP &src) { bitPerSample = dst->getBitPerSample(); tmp = TSoundTrack::create((int)src->getSampleRate(), bitPerSample, chans, - src_reslen * src->getSampleSize()); + src_reslen * src->getSampleSize(), + src->getSampleType()); convertWithoutResampling(tmp, src); tmq = TSop::resample(tmp, (TINT32)dst->getSampleRate()); @@ -800,7 +805,7 @@ void TSop::convert(TSoundTrackP &dst, const TSoundTrackP &src) { tmp = TSoundTrack::create((int)src->getSampleRate(), dst->getBitPerSample(), dst->getChannelCount(), src_reslen * dst->getSampleSize(), - dst->isSampleSigned()); + dst->getSampleType()); convertWithoutResampling(tmp, src); dst = TSop::resample(tmp, (TINT32)dst->getSampleRate()); @@ -828,9 +833,8 @@ TSoundTrackP doReverb(TSoundTrackT *src, double delayTime, TINT32 dstSampleCount = src->getSampleCount() + (TINT32)(src->getSampleRate() * extendTime); - TSoundTrackT *dst = - new TSoundTrackT(src->getSampleRate(), src->getChannelCount(), - dstSampleCount, src->getFormatType()); + TSoundTrackT *dst = new TSoundTrackT( + src->getSampleRate(), src->getChannelCount(), dstSampleCount); TINT32 sampleRate = (TINT32)src->getSampleRate(); TINT32 k = (TINT32)(sampleRate * delayTime); @@ -930,13 +934,13 @@ public: m_decayFactor, m_extendTime); } - TSoundTrackP compute(const TSoundTrackMono32float &src) override { - return doReverb(const_cast(&src), m_delayTime, + TSoundTrackP compute(const TSoundTrackMono32Float &src) override { + return doReverb(const_cast(&src), m_delayTime, m_decayFactor, m_extendTime); } - TSoundTrackP compute(const TSoundTrackStereo32float &src) override { - return doReverb(const_cast(&src), m_delayTime, + TSoundTrackP compute(const TSoundTrackStereo32Float &src) override { + return doReverb(const_cast(&src), m_delayTime, m_decayFactor, m_extendTime); } }; @@ -958,9 +962,8 @@ TSoundTrackP TSop::reverb(TSoundTrackP src, double delayTime, template TSoundTrackP doGate(TSoundTrackT *src, double threshold, double holdTime, double /*releaseTime*/) { - TSoundTrackT *dst = - new TSoundTrackT(src->getSampleRate(), src->getChannelCount(), - src->getSampleCount(), src->getFormatType()); + TSoundTrackT *dst = new TSoundTrackT( + src->getSampleRate(), src->getChannelCount(), src->getSampleCount()); double sampleExcursion_inv = 1.0 / (double)(src->getMaxPressure(0, src->getSampleCount() - 1, 0) - @@ -1058,13 +1061,13 @@ public: m_holdTime, m_releaseTime); } - TSoundTrackP compute(const TSoundTrackMono32float &src) override { - return doGate(const_cast(&src), m_threshold, + TSoundTrackP compute(const TSoundTrackMono32Float &src) override { + return doGate(const_cast(&src), m_threshold, m_holdTime, m_releaseTime); } - TSoundTrackP compute(const TSoundTrackStereo32float &src) override { - return doGate(const_cast(&src), m_threshold, + TSoundTrackP compute(const TSoundTrackStereo32Float &src) override { + return doGate(const_cast(&src), m_threshold, m_holdTime, m_releaseTime); } }; @@ -1111,9 +1114,8 @@ TSoundTrackP doEcho(TSoundTrackT *src, double delayTime, double decayFactor, TINT32 dstSampleCount = src->getSampleCount() + (TINT32)(src->getSampleRate() * extendTime); - TSoundTrackT *dst = - new TSoundTrackT(src->getSampleRate(), src->getChannelCount(), - dstSampleCount, src->getFormatType()); + TSoundTrackT *dst = new TSoundTrackT( + src->getSampleRate(), src->getChannelCount(), dstSampleCount); TINT32 sampleRate = (TINT32)src->getSampleRate(); TINT32 k = (TINT32)(sampleRate * delayTime); @@ -1233,19 +1235,24 @@ void TSop::echo(TSoundTrackP &dst, const TSoundTrackP &src, double delayTime, if (srcS32) dst = doEcho(srcS32, delayTime, decayFactor, extendTime); else { - TSoundTrackMono32float *srcM32f; - srcM32f = dynamic_cast( + TSoundTrackMono32Float *srcM32F; + srcM32F = dynamic_cast( src.getPointer()); - if (srcM32f) + if (srcM32F) dst = - doEcho(srcM32f, delayTime, decayFactor, extendTime); + doEcho(srcM32F, delayTime, decayFactor, extendTime); else { - TSoundTrackStereo32float *srcS32f; - srcS32f = dynamic_cast( + TSoundTrackStereo32Float *srcS32F; + srcS32F = dynamic_cast( src.getPointer()); - if (srcS32f) - dst = doEcho(srcS32f, delayTime, decayFactor, + if (srcS32F) + dst = doEcho(srcS32F, delayTime, decayFactor, extendTime); + // Yo Dawg, I herd you like nesting ifs, so I put an + // nest if + // in your nesting ifs so you can nest if while you + // nesting + // ifs } } } @@ -1275,7 +1282,7 @@ TSoundTrackP TSop::insertBlank(TSoundTrackP src, TINT32 s0, TINT32 len) { int bytePerSample = dst->getSampleSize(); memcpy(dstRawData, srcRawData, ss0 * bytePerSample); - if (format.m_signedSample) + if (format.m_sampleType != TSound::UINT) memset(dstRawData + ss0 * bytePerSample, 0, len * bytePerSample); else memset(dstRawData + ss0 * bytePerSample, 127, len * bytePerSample); @@ -1340,9 +1347,8 @@ TSoundTrackP mixT(TSoundTrackT *st1, double a1, TSoundTrackT *st2, double a2) { TINT32 sampleCount = std::max(st1->getSampleCount(), st2->getSampleCount()); - TSoundTrackT *dst = - new TSoundTrackT(st1->getSampleRate(), st1->getChannelCount(), - sampleCount); // , st1->getFormatType()); + TSoundTrackT *dst = new TSoundTrackT( + st1->getSampleRate(), st1->getChannelCount(), sampleCount); T *dstSample = dst->samples(); T *endDstSample = @@ -1461,21 +1467,21 @@ public: m_alpha2)); } - TSoundTrackP compute(const TSoundTrackMono32float &src) override { + TSoundTrackP compute(const TSoundTrackMono32Float &src) override { assert(src.getFormat() == m_sndtrack->getFormat()); return ( - mixT(const_cast(&src), m_alpha1, - dynamic_cast(m_sndtrack.getPointer()), + mixT(const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); } - TSoundTrackP compute(const TSoundTrackStereo32float &src) override { + TSoundTrackP compute(const TSoundTrackStereo32Float &src) override { assert(src.getFormat() == m_sndtrack->getFormat()); return ( - mixT(const_cast(&src), m_alpha1, - dynamic_cast(m_sndtrack.getPointer()), + mixT(const_cast(&src), m_alpha1, + dynamic_cast(m_sndtrack.getPointer()), m_alpha2)); } }; @@ -1505,8 +1511,8 @@ TSoundTrackP doFadeIn(const TSoundTrackT &track, double riseFactor) { assert(sampleCount); int channelCount = track.getChannelCount(); - TSoundTrackT *out = new TSoundTrackT( - track.getSampleRate(), channelCount, sampleCount, track.getFormatType()); + TSoundTrackT *out = + new TSoundTrackT(track.getSampleRate(), channelCount, sampleCount); double val[2], step[2]; @@ -1556,8 +1562,8 @@ public: TSoundTrackP compute(const TSoundTrackStereo24 &) override; TSoundTrackP compute(const TSoundTrackMono32 &) override; TSoundTrackP compute(const TSoundTrackStereo32 &) override; - TSoundTrackP compute(const TSoundTrackMono32float &) override; - TSoundTrackP compute(const TSoundTrackStereo32float &) override; + TSoundTrackP compute(const TSoundTrackMono32Float &) override; + TSoundTrackP compute(const TSoundTrackStereo32Float &) override; double m_riseFactor; }; @@ -1624,13 +1630,14 @@ TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackStereo32 &track) { //------------------------------------------------------------------------------ -TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackMono32float &track) { +TSoundTrackP TSoundTrackFaderIn::compute(const TSoundTrackMono32Float &track) { return doFadeIn(track, m_riseFactor); } + //------------------------------------------------------------------------------ TSoundTrackP TSoundTrackFaderIn::compute( - const TSoundTrackStereo32float &track) { + const TSoundTrackStereo32Float &track) { return doFadeIn(track, m_riseFactor); } @@ -1657,8 +1664,8 @@ TSoundTrackP doFadeOut(const TSoundTrackT &track, double decayFactor) { assert(sampleCount); int channelCount = track.getChannelCount(); - TSoundTrackT *out = new TSoundTrackT( - track.getSampleRate(), channelCount, sampleCount, track.getFormatType()); + TSoundTrackT *out = + new TSoundTrackT(track.getSampleRate(), channelCount, sampleCount); double val[2], step[2]; ChannelValueType chan[2]; @@ -1705,8 +1712,8 @@ public: TSoundTrackP compute(const TSoundTrackStereo24 &) override; TSoundTrackP compute(const TSoundTrackMono32 &) override; TSoundTrackP compute(const TSoundTrackStereo32 &) override; - TSoundTrackP compute(const TSoundTrackMono32float &) override; - TSoundTrackP compute(const TSoundTrackStereo32float &) override; + TSoundTrackP compute(const TSoundTrackMono32Float &) override; + TSoundTrackP compute(const TSoundTrackStereo32Float &) override; double m_decayFactor; }; @@ -1754,6 +1761,7 @@ TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackStereo16 &track) { TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackMono24 &track) { return doFadeOut(track, m_decayFactor); } + //------------------------------------------------------------------------------ TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackStereo24 &track) { @@ -1773,13 +1781,14 @@ TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackStereo32 &track) { //------------------------------------------------------------------------------ -TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackMono32float &track) { +TSoundTrackP TSoundTrackFaderOut::compute(const TSoundTrackMono32Float &track) { return doFadeOut(track, m_decayFactor); } + //------------------------------------------------------------------------------ TSoundTrackP TSoundTrackFaderOut::compute( - const TSoundTrackStereo32float &track) { + const TSoundTrackStereo32Float &track) { return doFadeOut(track, m_decayFactor); } @@ -1825,8 +1834,7 @@ TSoundTrackP doCrossFade(const TSoundTrackT &track1, TSoundTrackT *track2, } TSoundTrackT *out = - new TSoundTrackT(track2->getSampleRate(), channelCount, sampleCount, - track2->getFormatType()); + new TSoundTrackT(track2->getSampleRate(), channelCount, sampleCount); T *psample = out->samples(); T *end = psample + out->getSampleCount(); @@ -1865,8 +1873,8 @@ public: TSoundTrackP compute(const TSoundTrackStereo24 &) override; TSoundTrackP compute(const TSoundTrackMono32 &) override; TSoundTrackP compute(const TSoundTrackStereo32 &) override; - TSoundTrackP compute(const TSoundTrackMono32float &) override; - TSoundTrackP compute(const TSoundTrackStereo32float &) override; + TSoundTrackP compute(const TSoundTrackMono32Float &) override; + TSoundTrackP compute(const TSoundTrackStereo32Float &) override; TSoundTrackP m_st; double m_crossFactor; @@ -1964,20 +1972,20 @@ TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackStereo32 &src) { //------------------------------------------------------------------------------ -TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackMono32float &src) { +TSoundTrackP TSoundTrackCrossFader::compute(const TSoundTrackMono32Float &src) { assert(src.getFormat() == m_st->getFormat()); return doCrossFade(src, - dynamic_cast(m_st.getPointer()), + dynamic_cast(m_st.getPointer()), m_crossFactor); } //------------------------------------------------------------------------------ TSoundTrackP TSoundTrackCrossFader::compute( - const TSoundTrackStereo32float &src) { + const TSoundTrackStereo32Float &src) { assert(src.getFormat() == m_st->getFormat()); return doCrossFade( - src, dynamic_cast(m_st.getPointer()), + src, dynamic_cast(m_st.getPointer()), m_crossFactor); } @@ -2031,8 +2039,7 @@ TSoundTrackP doCrossFadeOverWrite(const TSoundTrackT &track1, } TSoundTrackT *out = - new TSoundTrackT(track2->getSampleRate(), channelCount, sampleCountT2, - track2->getFormatType()); + new TSoundTrackT(track2->getSampleRate(), channelCount, sampleCountT2); T *psample = out->samples(); T *end = psample + sampleCount; @@ -2072,8 +2079,8 @@ public: TSoundTrackP compute(const TSoundTrackStereo24 &) override; TSoundTrackP compute(const TSoundTrackMono32 &) override; TSoundTrackP compute(const TSoundTrackStereo32 &) override; - TSoundTrackP compute(const TSoundTrackMono32float &) override; - TSoundTrackP compute(const TSoundTrackStereo32float &) override; + TSoundTrackP compute(const TSoundTrackMono32Float &) override; + TSoundTrackP compute(const TSoundTrackStereo32Float &) override; TSoundTrackP m_st; double m_crossFactor; @@ -2179,20 +2186,20 @@ TSoundTrackP TSoundTrackCrossFaderOverWrite::compute( //------------------------------------------------------------------------------ TSoundTrackP TSoundTrackCrossFaderOverWrite::compute( - const TSoundTrackMono32float &src) { + const TSoundTrackMono32Float &src) { assert(src.getFormat() == m_st->getFormat()); return doCrossFadeOverWrite( - src, dynamic_cast(m_st.getPointer()), + src, dynamic_cast(m_st.getPointer()), m_crossFactor); } //------------------------------------------------------------------------------ TSoundTrackP TSoundTrackCrossFaderOverWrite::compute( - const TSoundTrackStereo32float &src) { + const TSoundTrackStereo32Float &src) { assert(src.getFormat() == m_st->getFormat()); return doCrossFadeOverWrite( - src, dynamic_cast(m_st.getPointer()), + src, dynamic_cast(m_st.getPointer()), m_crossFactor); } diff --git a/toonz/sources/common/tsound/tsound.cpp b/toonz/sources/common/tsound/tsound.cpp index 9977636e..651848df 100644 --- a/toonz/sources/common/tsound/tsound.cpp +++ b/toonz/sources/common/tsound/tsound.cpp @@ -28,8 +28,7 @@ TSoundTrack::TSoundTrack() //------------------------------------------------------------------------------ TSoundTrack::TSoundTrack(TUINT32 sampleRate, int bitPerSample, int channelCount, - int sampleSize, TINT32 sampleCount, - bool isSampleSigned, int formatType) + int sampleSize, TINT32 sampleCount, int sampleType) : TSmartObject(m_classCode) , m_sampleRate(sampleRate) @@ -38,13 +37,12 @@ TSoundTrack::TSoundTrack(TUINT32 sampleRate, int bitPerSample, int channelCount, , m_sampleCount(sampleCount) , m_channelCount(channelCount) , m_parent(0) - , m_bufferOwner(true) - , m_formatType(formatType) { + , m_bufferOwner(true) { m_buffer = (UCHAR *)malloc(sampleCount * m_sampleSize); if (!m_buffer) return; // m_buffer = new UCHAR[sampleCount*m_sampleSize]; - if (isSampleSigned) + if (sampleType != TSound::UINT) memset(m_buffer, 0, sampleCount * sampleSize); else memset(m_buffer, 127, sampleCount * sampleSize); @@ -53,8 +51,8 @@ TSoundTrack::TSoundTrack(TUINT32 sampleRate, int bitPerSample, int channelCount, //------------------------------------------------------------------------------ TSoundTrack::TSoundTrack(TUINT32 sampleRate, int bitPerSample, int channelCount, - int sampleSize, TINT32 sampleCount, UCHAR *buffer, - TSoundTrack *parent, int formatType) + int sampleSize, TINT32 sampleCount, int sampleType, + UCHAR *buffer, TSoundTrack *parent) : TSmartObject(m_classCode) , m_sampleRate(sampleRate) @@ -62,10 +60,10 @@ TSoundTrack::TSoundTrack(TUINT32 sampleRate, int bitPerSample, int channelCount, , m_bitPerSample(bitPerSample) , m_sampleCount(sampleCount) , m_channelCount(channelCount) + , m_sampleType(sampleType) , m_parent(parent) , m_buffer(buffer) - , m_bufferOwner(false) - , m_formatType(formatType) { + , m_bufferOwner(false) { if (m_parent) m_parent->addRef(); } @@ -81,66 +79,64 @@ TSoundTrack::~TSoundTrack() { TSoundTrackP TSoundTrack::create(TUINT32 sampleRate, int bitPerSample, int channelCount, TINT32 sampleCount, - bool signedSample, int formatType) { - TSoundTrackP st; - int type = bitPerSample + channelCount; + int sampleType) { + TSoundTrackP st = 0; + int type = bitPerSample + channelCount; switch (type) { case TRK_M8: - if (signedSample) - st = new TSoundTrackMono8Signed(sampleRate, channelCount, sampleCount, - formatType); - else - st = new TSoundTrackMono8Unsigned(sampleRate, channelCount, sampleCount, - formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackMono8Signed(sampleRate, channelCount, sampleCount); + else if (sampleType == TSound::UINT) + st = new TSoundTrackMono8Unsigned(sampleRate, channelCount, sampleCount); break; + case TRK_S8: - if (signedSample) - st = new TSoundTrackStereo8Signed(sampleRate, channelCount, sampleCount, - formatType); - else - st = new TSoundTrackStereo8Unsigned(sampleRate, channelCount, sampleCount, - formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackStereo8Signed(sampleRate, channelCount, sampleCount); + else if (sampleType == TSound::UINT) + st = + new TSoundTrackStereo8Unsigned(sampleRate, channelCount, sampleCount); break; case TRK_M16: - st = new TSoundTrackMono16(sampleRate, channelCount, sampleCount, - formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackMono16(sampleRate, channelCount, sampleCount); break; case TRK_S16: - st = new TSoundTrackStereo16(sampleRate, channelCount, sampleCount, - formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackStereo16(sampleRate, channelCount, sampleCount); break; case TRK_M24: - st = new TSoundTrackMono24(sampleRate, channelCount, sampleCount, - formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackMono24(sampleRate, channelCount, sampleCount); break; case TRK_S24: - st = new TSoundTrackStereo24(sampleRate, channelCount, sampleCount, - formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackStereo24(sampleRate, channelCount, sampleCount); break; case TRK_M32: - if (formatType == WAVE_FORMAT_PCM) - st = new TSoundTrackMono32(sampleRate, channelCount, sampleCount, - formatType); - else - st = new TSoundTrackMono32float(sampleRate, channelCount, sampleCount, - formatType); + if (sampleType == TSound::FLOAT) + st = new TSoundTrackMono32Float(sampleRate, channelCount, sampleCount); + else if (sampleType == TSound::INT) + st = new TSoundTrackMono32(sampleRate, channelCount, sampleCount); break; case TRK_S32: - if (formatType == WAVE_FORMAT_PCM) - st = new TSoundTrackStereo32(sampleRate, channelCount, sampleCount, - formatType); - else - st = new TSoundTrackStereo32float(sampleRate, channelCount, sampleCount, - formatType); + if (sampleType == TSound::FLOAT) + st = new TSoundTrackStereo32Float(sampleRate, channelCount, sampleCount); + else if (sampleType == TSound::INT) + st = new TSoundTrackStereo32(sampleRate, channelCount, sampleCount); break; default: + break; + } + + if (!st) { std::string s; s = "Type " + std::to_string(sampleRate) + " Hz " + std::to_string(bitPerSample) + " bits "; @@ -163,68 +159,75 @@ TSoundTrackP TSoundTrack::create(TUINT32 sampleRate, int bitPerSample, TSoundTrackP TSoundTrack::create(TUINT32 sampleRate, int bitPerSample, int channelCount, TINT32 sampleCount, - void *buffer, bool signedSample, - int formatType) { - TSoundTrackP st; - int type = bitPerSample + channelCount; + int sampleType, void *buffer) { + TSoundTrackP st = 0; + int type = bitPerSample + channelCount; switch (type) { case TRK_M8: - if (signedSample) + if (sampleType == TSound::INT) st = new TSoundTrackMono8Signed(sampleRate, channelCount, sampleCount, - (TMono8SignedSample *)buffer, 0, - formatType); - else + (TMono8SignedSample *)buffer, 0); + else if (sampleType == TSound::UINT) st = new TSoundTrackMono8Unsigned(sampleRate, channelCount, sampleCount, - (TMono8UnsignedSample *)buffer, 0, - formatType); + (TMono8UnsignedSample *)buffer, 0); break; + case TRK_S8: - if (signedSample) + if (sampleType == TSound::INT) st = new TSoundTrackStereo8Signed(sampleRate, channelCount, sampleCount, - (TStereo8SignedSample *)buffer, 0, - formatType); - else + (TStereo8SignedSample *)buffer, 0); + else if (sampleType == TSound::UINT) st = new TSoundTrackStereo8Unsigned(sampleRate, channelCount, sampleCount, - (TStereo8UnsignedSample *)buffer, 0, - formatType); + (TStereo8UnsignedSample *)buffer, 0); break; case TRK_M16: - st = new TSoundTrackMono16(sampleRate, channelCount, sampleCount, - (TMono16Sample *)buffer, 0, formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackMono16(sampleRate, channelCount, sampleCount, + (TMono16Sample *)buffer, 0); break; case TRK_S16: - st = new TSoundTrackStereo16(sampleRate, channelCount, sampleCount, - (TStereo16Sample *)buffer, 0, formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackStereo16(sampleRate, channelCount, sampleCount, + (TStereo16Sample *)buffer, 0); break; case TRK_M24: - st = new TSoundTrackMono24(sampleRate, channelCount, sampleCount, - (TMono24Sample *)buffer, 0, formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackMono24(sampleRate, channelCount, sampleCount, + (TMono24Sample *)buffer, 0); break; case TRK_S24: - st = new TSoundTrackStereo24(sampleRate, channelCount, sampleCount, - (TStereo24Sample *)buffer, 0, formatType); + if (sampleType == TSound::INT) + st = new TSoundTrackStereo24(sampleRate, channelCount, sampleCount, + (TStereo24Sample *)buffer, 0); break; case TRK_M32: - st = new TSoundTrackMono32(sampleRate, channelCount, sampleCount, - (TMono32Sample *)buffer, 0, formatType); + if (sampleType == TSound::FLOAT) + st = new TSoundTrackMono32Float(sampleRate, channelCount, sampleCount, + (TMono32FloatSample *)buffer, 0); + else if (sampleType == TSound::INT) + st = new TSoundTrackMono32(sampleRate, channelCount, sampleCount, + (TMono32Sample *)buffer, 0); break; case TRK_S32: - if (formatType == WAVE_FORMAT_PCM) + if (sampleType == TSound::FLOAT) + st = new TSoundTrackStereo32Float(sampleRate, channelCount, sampleCount, + (TStereo32FloatSample *)buffer, 0); + else if (sampleType == TSound::INT) st = new TSoundTrackStereo32(sampleRate, channelCount, sampleCount, - (TStereo32Sample *)buffer, 0, formatType); - else - st = new TSoundTrackStereo32float(sampleRate, channelCount, sampleCount, - (TStereo32floatSample *)buffer, 0, - formatType); + (TStereo32Sample *)buffer, 0); break; default: + break; + } + + if (!st) { std::string s; s = "Type " + std::to_string(sampleRate) + " Hz " + std::to_string(bitPerSample) + " bits "; @@ -245,8 +248,8 @@ TSoundTrackP TSoundTrack::create(TUINT32 sampleRate, int bitPerSample, TSoundTrackP TSoundTrack::create(const TSoundTrackFormat &format, TINT32 sampleCount, void *buffer) { return TSoundTrack::create((int)format.m_sampleRate, format.m_bitPerSample, - format.m_channelCount, sampleCount, buffer, - format.m_signedSample, format.m_formatType); + format.m_channelCount, sampleCount, + format.m_sampleType, buffer); } //------------------------------------------------------------------------------ @@ -255,7 +258,7 @@ TSoundTrackP TSoundTrack::create(const TSoundTrackFormat &format, TINT32 sampleCount) { return TSoundTrack::create((int)format.m_sampleRate, format.m_bitPerSample, format.m_channelCount, sampleCount, - format.m_signedSample, format.m_formatType); + format.m_sampleType); } //------------------------------------------------------------------------------ @@ -279,8 +282,7 @@ bool TSoundTrackFormat::operator==(const TSoundTrackFormat &rhs) { return (m_sampleRate == rhs.m_sampleRate && m_bitPerSample == rhs.m_bitPerSample && m_channelCount == rhs.m_channelCount && - m_signedSample == rhs.m_signedSample && - m_formatType == rhs.m_formatType); + m_sampleType == rhs.m_sampleType); } //------------------------------------------------------------------------------ @@ -299,8 +301,7 @@ double TSoundTrack::getDuration() const { TSoundTrackFormat TSoundTrack::getFormat() const { return TSoundTrackFormat(getSampleRate(), getBitPerSample(), - getChannelCount(), isSampleSigned(), - getFormatType()); + getChannelCount(), getSampleType()); } //------------------------------------------------------------------------------ diff --git a/toonz/sources/common/tsound/tsound_nt.cpp b/toonz/sources/common/tsound/tsound_nt.cpp index 2d59dace..470adc68 100644 --- a/toonz/sources/common/tsound/tsound_nt.cpp +++ b/toonz/sources/common/tsound/tsound_nt.cpp @@ -20,8 +20,8 @@ class TSoundInputDeviceImp; //========================================================= namespace { -void CALLBACK recordCB(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, - DWORD dwParam2); +void CALLBACK recordCB(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD dwParam2); bool setRecordLine(TSoundInputDevice::Source typeInput); @@ -39,11 +39,11 @@ MMRESULT getControlDetails(HMIXEROBJ hMixer, DWORD dwSelectControlID, MIXERCONTROLDETAILS_UNSIGNED *mxcdSelectValue); MMRESULT isaFormatSupported(int sampleRate, int channelCount, int bitPerSample, - bool input); + int sampleType, bool input); DWORD WINAPI MyWaveOutCallbackThread(LPVOID lpParameter); void getAmplitude(int &litude, const TSoundTrackP st, TINT32 sample); -} +} // namespace //============================================================================== // Class to send the message that a playback is completed @@ -490,7 +490,7 @@ TSoundOutputDeviceImp::~TSoundOutputDeviceImp() { delete m_whdrQueue; } bool TSoundOutputDeviceImp::doOpenDevice(const TSoundTrackFormat &format) { WAVEFORMATEX wf; - wf.wFormatTag = format.m_formatType; // WAVE_FORMAT_PCM; + wf.wFormatTag = format.m_sampleType & TSound::WMASK; wf.nChannels = format.m_channelCount; wf.nSamplesPerSec = format.m_sampleRate; wf.wBitsPerSample = format.m_bitPerSample; @@ -503,8 +503,9 @@ bool TSoundOutputDeviceImp::doOpenDevice(const TSoundTrackFormat &format) { &m_notifyThreadId)); MMRESULT ret; - if ((ret = waveOutOpen(&m_wout, WAVE_MAPPER, &wf, (DWORD)m_notifyThreadId, - (DWORD)this, CALLBACK_THREAD)) != MMSYSERR_NOERROR) { + if ((ret = waveOutOpen(&m_wout, WAVE_MAPPER, &wf, (DWORD_PTR)m_notifyThreadId, + (DWORD_PTR)this, CALLBACK_THREAD)) != + MMSYSERR_NOERROR) { while (!PostThreadMessage(m_notifyThreadId, WM_QUIT, 0, 0)) ; } @@ -560,6 +561,8 @@ void TSoundOutputDeviceImp::insertAllRate() { m_supportedRate.insert(32000); m_supportedRate.insert(44100); m_supportedRate.insert(48000); + m_supportedRate.insert(96000); + m_supportedRate.insert(192000); } //---------------------------------------------------------------------------- @@ -686,7 +689,7 @@ void getAmplitude(int &litude, const TSoundTrackP st, TINT32 sample) { amplitude += (int)snd->getPressure(sample, k); amplitude /= k; } -} +} // namespace //------------------------------------------------------------------------------ @@ -801,7 +804,7 @@ void TSoundOutputDevice::setLooping(bool loop) { TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(TUINT32 sampleRate, int channelCount, int bitPerSample, - int formatType) { + int sampleType) { TSoundTrackFormat fmt; // avvvicinarsi al sample rate => dovrebbe esser OK avendo selezionato i piu' @@ -827,11 +830,6 @@ TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(TUINT32 sampleRate, else bitPerSample = 32; - if (bitPerSample >= 16) - fmt.m_signedSample = true; - else - fmt.m_signedSample = false; - // switch mono/stereo if (channelCount <= 1) channelCount = 1; @@ -841,7 +839,7 @@ TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(TUINT32 sampleRate, fmt.m_bitPerSample = bitPerSample; fmt.m_channelCount = channelCount; fmt.m_sampleRate = sampleRate; - fmt.m_formatType = formatType; + fmt.m_sampleType = sampleType; return fmt; } @@ -852,7 +850,7 @@ TSoundTrackFormat TSoundOutputDevice::getPreferredFormat( const TSoundTrackFormat &format) { try { return getPreferredFormat(format.m_sampleRate, format.m_channelCount, - format.m_bitPerSample, format.m_formatType); + format.m_bitPerSample, format.m_sampleType); } catch (TSoundDeviceException &e) { throw TSoundDeviceException(TSoundDeviceException::UnsupportedFormat, e.getMessage()); @@ -871,14 +869,14 @@ class WaveFormat final : public WAVEFORMATEX { public: WaveFormat(){}; WaveFormat(unsigned char channelCount, TUINT32 sampleRate, - unsigned char bitPerSample, WORD formatType); + unsigned char bitPerSample, int sampleFormat); ~WaveFormat(){}; }; WaveFormat::WaveFormat(unsigned char channelCount, TUINT32 sampleRate, - unsigned char bitPerSample, WORD formatType) { - wFormatTag = formatType; // WAVE_FORMAT_PCM; + unsigned char bitPerSample, int sampleFormat) { + wFormatTag = sampleFormat & TSound::WMASK; nChannels = channelCount; nSamplesPerSec = sampleRate; wBitsPerSample = bitPerSample; @@ -933,8 +931,8 @@ WinSoundInputDevice::~WinSoundInputDevice() { CloseHandle(m_hBlockDone); } void WinSoundInputDevice::open(const WaveFormat &wf) { if (m_hWaveIn) close(); - MMRESULT ret = waveInOpen(&m_hWaveIn, WAVE_MAPPER, &wf, (DWORD)recordCB, - (DWORD)m_hBlockDone, CALLBACK_FUNCTION); + MMRESULT ret = waveInOpen(&m_hWaveIn, WAVE_MAPPER, &wf, (DWORD_PTR)recordCB, + (DWORD_PTR)m_hBlockDone, CALLBACK_FUNCTION); if (ret != MMSYSERR_NOERROR) { throw TException("Error to open the input device"); @@ -1108,6 +1106,8 @@ void TSoundInputDeviceImp::insertAllRate() { m_supportedRate.insert(32000); m_supportedRate.insert(44100); m_supportedRate.insert(48000); + m_supportedRate.insert(96000); + m_supportedRate.insert(192000); } //---------------------------------------------------------------------------- @@ -1145,15 +1145,15 @@ bool TSoundInputDeviceImp::verifyRate() { //==================================================================== namespace { -void CALLBACK recordCB(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, - DWORD dwParam2) { +void CALLBACK recordCB(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD dwParam2) { WAVEHDR *whdr = (WAVEHDR *)dwParam1; HANDLE *blockDone = (HANDLE *)dwInstance; if (uMsg != MM_WIM_DATA) return; SetEvent(blockDone); } -} +} // namespace //============================================================================== @@ -1219,7 +1219,7 @@ throw TException("This format is not supported for recording");*/ try { WaveFormat wf(m_imp->m_format.m_channelCount, m_imp->m_format.m_sampleRate, - m_imp->m_format.m_bitPerSample, m_imp->m_format.m_formatType); + m_imp->m_format.m_bitPerSample, m_imp->m_format.m_sampleType); m_imp->open(wf); } catch (TException &e) { @@ -1291,7 +1291,7 @@ throw TException("This format is not supported for recording");*/ m_imp->m_byteRecorded = 0; try { WaveFormat wf(m_imp->m_format.m_channelCount, m_imp->m_format.m_sampleRate, - m_imp->m_format.m_bitPerSample, m_imp->m_format.m_formatType); + m_imp->m_format.m_bitPerSample, m_imp->m_format.m_sampleType); m_imp->open(wf); m_imp->prepareHeader( @@ -1546,7 +1546,7 @@ bool TSoundInputDevice::setVolume(double value) { double delta = (double)(dwMaximum / (mxc.Metrics.cSteps - 1)); newValue = (int)(tround(fattProp) * delta); - MIXERCONTROLDETAILS_UNSIGNED mxcdVolume = {newValue}; + MIXERCONTROLDETAILS_UNSIGNED mxcdVolume = {(DWORD)newValue}; ret = setControlDetails((HMIXEROBJ)0, dwVolumeControlID, mxc.cMultipleItems, &mxcdVolume); if (ret != MMSYSERR_NOERROR) @@ -1703,7 +1703,7 @@ vicini TSoundTrackFormat TSoundInputDevice::getPreferredFormat(TUINT32 sampleRate, int channelCount, int bitPerSample, - int formatType) { + int sampleType) { TSoundTrackFormat fmt; // avvvicinarsi al sample rate => dovrebbe esser OK avendo selezionato i piu' @@ -1729,11 +1729,6 @@ TSoundTrackFormat TSoundInputDevice::getPreferredFormat(TUINT32 sampleRate, else bitPerSample = 32; - if (bitPerSample >= 16) - fmt.m_signedSample = true; - else - fmt.m_signedSample = false; - // switch mono/stereo if (channelCount <= 1) channelCount = 1; @@ -1743,7 +1738,7 @@ TSoundTrackFormat TSoundInputDevice::getPreferredFormat(TUINT32 sampleRate, fmt.m_bitPerSample = bitPerSample; fmt.m_channelCount = channelCount; fmt.m_sampleRate = sampleRate; - fmt.m_formatType = formatType; + fmt.m_sampleType = sampleType; return fmt; } @@ -1754,7 +1749,7 @@ TSoundTrackFormat TSoundInputDevice::getPreferredFormat( const TSoundTrackFormat &format) { try { return getPreferredFormat(format.m_sampleRate, format.m_channelCount, - format.m_bitPerSample, format.m_formatType); + format.m_bitPerSample, format.m_sampleType); } catch (TSoundDeviceException &e) { throw TSoundDeviceException(TSoundDeviceException::UnsupportedFormat, e.getMessage()); @@ -1799,7 +1794,7 @@ MMRESULT getLineInfo(HMIXEROBJ hMixer, MIXERLINE &mxl, DWORD destination, mxl.dwDestination = destination; mxl.dwSource = source; ret = mixerGetLineInfo(0, &mxl, - MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_SOURCE); + MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_SOURCE); return ret; } @@ -1813,7 +1808,7 @@ MMRESULT getLineInfo(HMIXEROBJ hMixer, MIXERLINE &mxl, DWORD dwLineID) { mxl.cbStruct = sizeof(mxl); mxl.dwLineID = dwLineID; ret = mixerGetLineInfo((HMIXEROBJ)hMixer, &mxl, - MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID); + MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID); return ret; } @@ -1911,7 +1906,7 @@ MMRESULT getControlDetails(HMIXEROBJ hMixer, DWORD dwSelectControlID, mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); mxcd.paDetails = pmxcdSelectText; ret = mixerGetControlDetails((HMIXEROBJ)0, &mxcd, - MIXER_GETCONTROLDETAILSF_LISTTEXT); + MIXER_GETCONTROLDETAILSF_LISTTEXT); return ret; } @@ -2117,7 +2112,8 @@ bool setRecordLine(TSoundInputDevice::Source typeInput) { case TSoundInputDevice::LineIn: dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_LINE /*| MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY | - MIXERLINE_COMPONENTTYPE_SRC_ANALOG*/; + MIXERLINE_COMPONENTTYPE_SRC_ANALOG*/ + ; break; case TSoundInputDevice::DigitalIn: dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_DIGITAL; @@ -2168,11 +2164,11 @@ bool setRecordLine(TSoundInputDevice::Source typeInput) { //------------------------------------------------------------------------------ MMRESULT isaFormatSupported(int sampleRate, int channelCount, int bitPerSample, - bool input) { + int sampleType, bool input) { WAVEFORMATEX wf; MMRESULT ret; - wf.wFormatTag = WAVE_FORMAT_PCM; + wf.wFormatTag = sampleType & TSound::WMASK; wf.nChannels = channelCount; wf.nSamplesPerSec = sampleRate; wf.wBitsPerSample = bitPerSample; @@ -2186,4 +2182,4 @@ MMRESULT isaFormatSupported(int sampleRate, int channelCount, int bitPerSample, ret = waveOutOpen(NULL, WAVE_MAPPER, &wf, NULL, NULL, WAVE_FORMAT_QUERY); return ret; } -} +} // namespace diff --git a/toonz/sources/common/tsound/tsound_qt.cpp b/toonz/sources/common/tsound/tsound_qt.cpp index 9c14ba22..3e90e71f 100644 --- a/toonz/sources/common/tsound/tsound_qt.cpp +++ b/toonz/sources/common/tsound/tsound_qt.cpp @@ -157,9 +157,19 @@ public: format.setCodec("audio/pcm"); format.setChannelCount(st->getChannelCount()); format.setByteOrder(QAudioFormat::LittleEndian); - format.setSampleType( st->getFormat().m_signedSample - ? QAudioFormat::SignedInt - : QAudioFormat::UnSignedInt ); + switch (st->getSampleType()) { + case TSound::INT: + format.setSampleType(QAudioFormat::SignedInt); + break; + case TSound::UINT: + format.setSampleType(QAudioFormat::UnSignedInt); + break; + case TSound::FLOAT: + format.setSampleType(QAudioFormat::Float); + break; + default: + break; + } format.setSampleRate(st->getSampleRate()); QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); @@ -314,8 +324,8 @@ void TSoundOutputDevice::setLooping(bool loop) { m_imp->setLooping(loop); } TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(TUINT32 sampleRate, int channelCount, int bitPerSample, - int formatType) { - TSoundTrackFormat fmt(sampleRate, bitPerSample, channelCount, true, formatType); + int sampleType) { + TSoundTrackFormat fmt(sampleRate, bitPerSample, channelCount, sampleType); return fmt; } @@ -324,7 +334,7 @@ TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(TUINT32 sampleRate, TSoundTrackFormat TSoundOutputDevice::getPreferredFormat( const TSoundTrackFormat &format) { return getPreferredFormat(format.m_sampleRate, format.m_channelCount, - format.m_bitPerSample, format.m_formatType); + format.m_bitPerSample, format.m_sampleType); } //============================================================================== @@ -437,7 +447,7 @@ bool TSoundInputDevice::supportsVolume() { return true; } TSoundTrackFormat TSoundInputDevice::getPreferredFormat(TUINT32 sampleRate, int channelCount, int bitPerSample, - int formatType) { + int sampleType) { TSoundTrackFormat fmt; return fmt; } @@ -450,7 +460,7 @@ TSoundTrackFormat TSoundInputDevice::getPreferredFormat( try { */ return getPreferredFormat(format.m_sampleRate, format.m_channelCount, - format.m_bitPerSample, format.m_formatType); + format.m_bitPerSample, format.m_sampleType); /*} catch (TSoundDeviceException &e) { diff --git a/toonz/sources/common/tsystem/tfilepath.cpp b/toonz/sources/common/tsystem/tfilepath.cpp index 362b5c15..d2774af1 100644 --- a/toonz/sources/common/tsystem/tfilepath.cpp +++ b/toonz/sources/common/tsystem/tfilepath.cpp @@ -39,9 +39,8 @@ int TFilePath::m_letterCountForSuffix = 1; namespace { -/*-- fromSeg位置 と - * toSeg位置は含まず、それらの間に挟まれている文字列が「数字4ケタ」ならtrueを返す - * --*/ +// Returns true if the string between the fromSeg position and the toSeg +// position is "4 digits". bool isNumbers(std::wstring str, int fromSeg, int toSeg) { /* if (toSeg - fromSeg != 5) return false; @@ -772,7 +771,8 @@ TFrameId TFilePath::getFrame() const { if (j == (int)std::wstring::npos) return TFrameId(TFrameId::NO_FRAME); if (i == j + 1) return TFrameId(TFrameId::EMPTY_FRAME); - // 間が数字でない場合(ファイル名にまぎれた"_" や "."がある場合)を除外する + // Exclude cases with non-numeric characters inbetween. (In case the file name + // contains "_" or ".") if (!checkForSeqNum(type) || !isNumbers(str, j, i)) return TFrameId(TFrameId::NO_FRAME); @@ -799,10 +799,15 @@ TFrameId TFilePath::getFrame() const { bool TFilePath::isFfmpegType() const { QString type = QString::fromStdString(getType()).toLower(); - if (type == "gif" || type == "mp4" || type == "webm" || type == "mov") - return true; - else - return false; + return (type == "gif" || type == "mp4" || type == "webm" || type == "mov"); +} + +//----------------------------------------------------------------------------- + +bool TFilePath::isUneditable() const { + QString type = QString::fromStdString(getType()).toLower(); + return (type == "psd" || type == "gif" || type == "mp4" || type == "webm" || + type == "mov"); } //----------------------------------------------------------------------------- @@ -974,7 +979,7 @@ TFilePath TFilePath::withFrame(const TFrameId &frame, (k == j - 1 || (checkForSeqNum(type) && isNumbers(str, k, - j)))) //-- "_." の並びか、"_[数字]."の並びのとき -- + j)))) // -- In case of "_." or "_[numbers]." -- return TFilePath(m_path.substr(0, k + i + 1) + ((frame.isNoFrame()) ? L"" @@ -1093,7 +1098,7 @@ TFilePath::TFilePathInfo TFilePath::analyzePath() const { // Frame Number and Suffix QString fIdRegExp = TFilePath::fidRegExpStr(); - // Extension:letters other than "._" or \/:,;*?"<>| or " "(space) + // Extension: letters other than "._" or \/:,;*?"<>| or " "(space) const QString extensionRegExp("([^\\._ \\\\/:,;*?\"<>|]+)"); // ignore frame numbers on non-sequential (i.e. movie) extension case : @@ -1104,7 +1109,7 @@ TFilePath::TFilePathInfo TFilePath::analyzePath() const { // if (!checkForSeqNum(ext)) { // info.levelName = rx_mf.cap(1); // info.sepChar = QChar(); - // info.fId = TFrameId(TFrameId::NO_FRAME, 0, 0); //NO_PADで初期化する + // info.fId = TFrameId(TFrameId::NO_FRAME, 0, 0); // initialize with NO_PAD // info.extension = ext; // return info; // } diff --git a/toonz/sources/common/tsystem/tsystem.cpp b/toonz/sources/common/tsystem/tsystem.cpp index 3d703091..c7b05c07 100644 --- a/toonz/sources/common/tsystem/tsystem.cpp +++ b/toonz/sources/common/tsystem/tsystem.cpp @@ -87,13 +87,17 @@ QDateTime TFileStatus::getLastModificationTime() const { QDateTime TFileStatus::getCreationTime() const { if (!m_exist) return QDateTime(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + return m_fileInfo.birthTime(); +#else return m_fileInfo.created(); +#endif } //----------------------------------------------------------------------------------- QFile::Permissions TFileStatus::getPermissions() const { - if (!m_exist) return 0; + if (!m_exist) return QFileDevice::Permissions(); return m_fileInfo.permissions(); } @@ -160,7 +164,11 @@ TFilePath TSystem::getTestDir(string name) { //------------------------------------------------------------ QString TSystem::getSystemValue(const TFilePath &name) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList strlist = toQString(name).split("\\", Qt::SkipEmptyParts); +#else QStringList strlist = toQString(name).split("\\", QString::SkipEmptyParts); +#endif assert(strlist.size() > 3); assert(strlist.at(0) == "SOFTWARE"); @@ -228,7 +236,7 @@ void setPathsPermissions(const TFilePathSet &pathSet, f.setPermissions(permissions); } } -} +} // namespace // gestire exception void TSystem::mkDir(const TFilePath &path) { @@ -445,7 +453,7 @@ public: //------------------------------------------------------------ /*! return the folder path list which is readable and executable -*/ + */ void TSystem::readDirectory_Dir_ReadExe(TFilePathSet &dst, const TFilePath &path) { QStringList dirItems; @@ -503,7 +511,7 @@ void TSystem::readDirectory_DirItems(QStringList &dst, const TFilePath &path) { //------------------------------------------------------------ /*! to retrieve the both lists with groupFrames option = on and off. -*/ + */ void TSystem::readDirectory(TFilePathSet &groupFpSet, TFilePathSet &allFpSet, const TFilePath &path) { if (!TFileStatus(path).isDirectory()) @@ -627,7 +635,7 @@ void TSystem::readDirectory(TFilePathSet &dst, const QDir &dir, while (FindNextFile(hFind, &find_dir_data)) addEntry(); } #else - entries = (dir.entryList(dir.filter() | QDir::NoDotAndDotDot)); + entries = (dir.entryList(dir.filter() | QDir::NoDotAndDotDot)); #endif TFilePath dirPath(dir.path().toStdWString()); @@ -697,6 +705,8 @@ void TSystem::readDirectoryTree(TFilePathSet &dst, const TFilePath &path, if (!TFileStatus(path).isDirectory()) throw TSystemException(path, " is not a directory"); + std::set fpSet; + QFileInfoList fil = QDir(toQString(path)).entryInfoList(); int i; for (i = 0; i < fil.size(); i++) { @@ -707,9 +717,15 @@ void TSystem::readDirectoryTree(TFilePathSet &dst, const TFilePath &path, if (TFileStatus(son).isDirectory()) { if (!onlyFiles) dst.push_back(son); readDirectoryTree(dst, son, groupFrames, onlyFiles); - } else - dst.push_back(son); + } else { + if (groupFrames && son.getDots() == "..") { + son = son.withFrame(); + } + fpSet.insert(son); + } } + + dst.insert(dst.end(), fpSet.begin(), fpSet.end()); } //------------------------------------------------------------ @@ -820,10 +836,10 @@ bool TSystem::doesExistFileOrLevel(const TFilePath &fp) { QString name(QString::fromStdWString(fp.getWideName())); name.append(QString::fromStdString(fp.getDottedType())); - int sepPos = name.indexOf("#"); - int dotPos = name.indexOf(".", sepPos); - int removeChars = dotPos - sepPos; - int doubleUnderscorePos = name.indexOf("__", sepPos); + int sepPos = name.indexOf("#"); + int dotPos = name.indexOf(".", sepPos); + int removeChars = dotPos - sepPos; + int doubleUnderscorePos = name.indexOf("__", sepPos); if (doubleUnderscorePos > 0) removeChars = doubleUnderscorePos - sepPos; name.remove(sepPos, removeChars); @@ -1015,8 +1031,8 @@ bool TSystem::touchParentDir(const TFilePath &fp) { bool TSystem::showDocument(const TFilePath &path) { #ifdef _WIN32 - int ret = (int)ShellExecuteW(0, L"open", path.getWideString().c_str(), 0, 0, - SW_SHOWNORMAL); + unsigned long long ret = (unsigned long long)ShellExecuteW( + 0, L"open", path.getWideString().c_str(), 0, 0, SW_SHOWNORMAL); if (ret <= 32) { return false; throw TSystemException(path, "Can't open"); @@ -1030,7 +1046,7 @@ bool TSystem::showDocument(const TFilePath &path) { char newPath[2048]; while (pos < thePath.size()) { - char c = thePath[pos]; + char c = thePath[pos]; if (c == ' ') newPath[count++] = '\\'; newPath[count++] = c; diff --git a/toonz/sources/common/tsystem/uncpath.cpp b/toonz/sources/common/tsystem/uncpath.cpp index ca3dbcad..7b4488d6 100644 --- a/toonz/sources/common/tsystem/uncpath.cpp +++ b/toonz/sources/common/tsystem/uncpath.cpp @@ -4,7 +4,9 @@ #include "tconvert.h" #ifdef _WIN32 +#ifndef UNICODE #define UNICODE // per le funzioni di conversione da/a UNC +#endif #include #include #endif @@ -82,14 +84,14 @@ TFilePath TSystem::toUNC(const TFilePath &fp) { // Loop through the entries; for (i = 1; i <= er; i++) { if (p->shi502_type == STYPE_DISKTREE) { - //#ifdef IS_DOTNET - // shi502_path e' una wstring, aanche se la dichiarazione di - // PSHARE_INFO_502 non lo sa! + // #ifdef IS_DOTNET + // shi502_path e' una wstring, aanche se la dichiarazione di + // PSHARE_INFO_502 non lo sa! std::wstring shareLocalPathW = (LPWSTR)(p->shi502_path); std::string shareLocalPath = ::to_string(shareLocalPathW); - //#else - // string shareLocalPath = toString(p->shi502_path); - //#endif + // #else + // string shareLocalPath = toString(p->shi502_path); + // #endif if (toLower(fpStr).find(toLower(shareLocalPath)) == 0) { std::string hostName = TSystem::getHostName().toStdString(); @@ -100,7 +102,7 @@ TFilePath TSystem::toUNC(const TFilePath &fp) { std::string shareNetName = ::to_string(shareNetNameW); // #else // string shareNetName = toString(p->shi502_netname); - //#endif + // #endif shareNetName.append("\\"); std::string fp(fpStr); @@ -176,19 +178,19 @@ TFilePath TSystem::toLocalPath(const TFilePath &fp) { // Loop through the entries; for (int i = 1; i <= (int)er; i++) { if (p->shi502_type == STYPE_DISKTREE) { - //#ifdef IS_DOTNET - // shi502_netname e' una wstring, anche se la dichiarazione di - // PSHARE_INFO_502 non lo sa! + // #ifdef IS_DOTNET + // shi502_netname e' una wstring, anche se la dichiarazione di + // PSHARE_INFO_502 non lo sa! std::wstring shareNetNameW = (LPWSTR)(p->shi502_netname); std::string shareNetName = ::to_string(shareNetNameW); // #else // string shareNetName = toString(p->shi502_netname); - //#endif + // #endif if (toLower(fpShareName) == toLower(shareNetName)) { - //#ifdef IS_DOTNET - // shi502_path e' una wstring, anche se la dichiarazione di - // PSHARE_INFO_502 non lo sa! + // #ifdef IS_DOTNET + // shi502_path e' una wstring, anche se la dichiarazione di + // PSHARE_INFO_502 non lo sa! std::wstring shareLocalPathW = (LPWSTR)(p->shi502_path); return TFilePath(shareLocalPathW) + TFilePath(path); } diff --git a/toonz/sources/common/tunit/tunit.cpp b/toonz/sources/common/tunit/tunit.cpp index 4cde0ece..9dccb95f 100644 --- a/toonz/sources/common/tunit/tunit.cpp +++ b/toonz/sources/common/tunit/tunit.cpp @@ -31,7 +31,7 @@ static std::pair dummyCurrentDpiGetter() { CurrentDpiGetter currentDpiGetter = &dummyCurrentDpiGetter; void setCurrentDpiGetter(CurrentDpiGetter f) { currentDpiGetter = f; } -} +} // namespace UnitParameters //------------------------------------------------------------------- @@ -66,7 +66,7 @@ void setFieldGuideAspectRatio(double ar) { double getFieldGuideAspectRatio() { return VerticalFldUnitConverter::m_fieldGuideAspectRatio; } -} +} // namespace UnitParameters //=================================================================== @@ -148,9 +148,8 @@ bool TUnit::isExtension(std::wstring ext) const { //------------------------------------------------------------------- void TUnit::setDefaultExtension(std::wstring ext) { - if (!ext.empty() && - std::find(m_extensions.begin(), m_extensions.end(), ext) == - m_extensions.end()) + if (!ext.empty() && std::find(m_extensions.begin(), m_extensions.end(), + ext) == m_extensions.end()) m_extensions.push_back(ext); m_defaultExtension = ext; } @@ -278,13 +277,13 @@ TMeasureManager::TMeasureManager() { length = m = new TMeasure("length", inch.clone()); m->add(cm.clone()); /*--- - Fxの寸法パラメータは単位なし(実際にはStageInch(1 StageInch = 1/53.33333 - inch)という値) - Fxの寸法パラメータからExpressionで単位のあるパラメータを参照すると、 - カレントUnitによってFxの計算結果が変わってしまう。 - tcomposerで用いられるカレントUnitはデフォルト値なので、 - ここでデフォルトのカレントUnitをmmにしておくことで、 - Unit = mm でシーンを作っておけば、作業時と同じRender結果が得られるようにする。 + The dimensional parameters of Fx are unitless (actually it uses the unit of + StageInch (1 StageInch = 1/53.33333 inch)). If you refer to a parameter with a + unit in Expression from the Fx dimension parameter, the Fx calculation results + will vary depending on the current Unit. Since the current Unit used in + tcomposer is the default value, we can set the default current Unit to mm here + so that if we create a scene with Unit = mm, we will get the same Render + results as when we work. ---*/ TUnit *mmUnit = mm.clone(); m->add(mmUnit); @@ -486,9 +485,9 @@ bool TMeasuredValue::setValue(std::wstring s, int *pErr) { value = std::stod(s.substr(j, i - j)); } // handle exceptions - catch (const std::invalid_argument &e) { + catch (const std::invalid_argument &) { return false; - } catch (const std::out_of_range &e) { + } catch (const std::out_of_range &) { return false; } @@ -599,7 +598,7 @@ public: }; //=================================================================== -/*-- Zのカーブのハンドルの長さは0=0となるようにしなければならない --*/ +/*-- The length of the Z curve handle must be 0=0 --*/ class ZDepthHandleUnitConverter final : public TUnitConverter { TMeasureManager::CameraSizeProvider *m_cameraSizeProvider; diff --git a/toonz/sources/common/tvectorimage/tvectorimage.cpp b/toonz/sources/common/tvectorimage/tvectorimage.cpp index 74890968..c7138f52 100644 --- a/toonz/sources/common/tvectorimage/tvectorimage.cpp +++ b/toonz/sources/common/tvectorimage/tvectorimage.cpp @@ -1,16 +1,16 @@ #include "tcurves.h" -//#include "tpalette.h" +// #include "tpalette.h" #include "tvectorimage.h" #include "tvectorimageP.h" #include "tstroke.h" -//#include "tgl.h" +// #include "tgl.h" #include "tvectorrenderdata.h" #include "tmathutil.h" -//#include "tdebugmessage.h" +// #include "tdebugmessage.h" #include "tofflinegl.h" -//#include "tcolorstyles.h" +// #include "tcolorstyles.h" #include "tpaletteutil.h" #include "tthreadmessage.h" #include "tsimplecolorstyles.h" @@ -591,16 +591,14 @@ TRectD TVectorImage::getBBox() const { TRectD bbox; for (UINT i = 0; i < strokeCount; ++i) { - TRectD r = m_imp->m_strokes[i]->m_s->getBBox(); + TStroke *stroke = m_imp->m_strokes[i]->m_s; TColorStyle *style = 0; if (plt) style = plt->getStyle(m_imp->m_strokes[i]->m_s->getStyle()); - if (dynamic_cast(style) || - dynamic_cast( - style)) // con i pattern style, il render a volte taglia sulla bbox - // dello stroke.... - // aumento la bbox della meta' delle sue dimensioni:pezzaccia. - r = r.enlarge(std::max(r.getLx() * 0.25, r.getLy() * 0.25)); - bbox = ((i == 0) ? r : bbox + r); + if (!style) continue; + // reimplemented in TRasterImagePatternStrokeStyle, + // TVectorImagePatternStrokeStyle and FlowLineStrokeStyle + TRectD r = style->getStrokeBBox(stroke); + bbox = ((i == 0) ? r : bbox + r); } return bbox; @@ -643,7 +641,7 @@ void TVectorImage::render(const TVectorRenderData &rd, TRaster32P &ras) { #endif //----------------------------------------------------------------------------- -//#include "timage_io.h" +// #include "timage_io.h" TRaster32P TVectorImage::render(bool onlyStrokes) { TRect bBox = convert(getBBox()); @@ -845,8 +843,7 @@ bool TVectorImage::Imp::selectFill(const TRectD &selArea, TStroke *s, for (UINT i = 0; i < m_regions.size(); i++) { int index, j = 0; - do - index = m_regions[i]->getEdge(j++)->m_index; + do index = m_regions[i]->getEdge(j++)->m_index; while (index < 0 && j < (int)m_regions[i]->getEdgeCount()); // if index<0, means that the region is purely of autoclose strokes! if (m_insideGroup != TGroupId() && index >= 0 && @@ -2011,7 +2008,7 @@ static void computeEdgeList(TStroke *newS, const std::list &edgeList1, //----------------------------------------------------------------------------- #ifdef _DEBUG -//#include "tpalette.h" +// #include "tpalette.h" #include "tcolorstyles.h" void printEdges(std::ofstream &os, char *str, TPalette *plt, diff --git a/toonz/sources/common/tvrender/qtofflinegl.cpp b/toonz/sources/common/tvrender/qtofflinegl.cpp index 5053c8af..277f67c5 100644 --- a/toonz/sources/common/tvrender/qtofflinegl.cpp +++ b/toonz/sources/common/tvrender/qtofflinegl.cpp @@ -140,7 +140,7 @@ void QtOfflineGL::createContext(TDimension rasterSize, #elif defined(MACOSX) fmt = QGLFormat::defaultFormat(); // printf("GL Version: %s\n",glGetString(GL_VERSION)); - fmt.setVersion(2, 1); /* OSX10.8 では 3.2 だめかも */ + fmt.setVersion(2, 1); /* 3.2 might not work on OSX10.8 */ #if 0 fmt.setAlphaBufferSize(8); fmt.setAlpha(true); diff --git a/toonz/sources/common/tvrender/tcolorstyles.cpp b/toonz/sources/common/tvrender/tcolorstyles.cpp index 1a444e0a..529601d3 100644 --- a/toonz/sources/common/tvrender/tcolorstyles.cpp +++ b/toonz/sources/common/tvrender/tcolorstyles.cpp @@ -50,6 +50,7 @@ TColorStyle::TColorStyle() , m_icon(0) , m_validIcon(false) , m_isEditedFromOriginal(false) + , m_hash(0) , m_pickedPosition() , m_isCustom(false) {} @@ -68,6 +69,7 @@ TColorStyle::TColorStyle(const TColorStyle &other) , m_enabled(other.m_enabled) , m_validIcon(false) , m_isEditedFromOriginal(other.m_isEditedFromOriginal) + , m_hash(other.m_hash) , m_pickedPosition(other.m_pickedPosition) , m_isCustom(other.m_isCustom) {} @@ -82,6 +84,7 @@ TColorStyle &TColorStyle::operator=(const TColorStyle &other) { m_enabled = other.m_enabled; m_validIcon = false; m_isEditedFromOriginal = other.m_isEditedFromOriginal; + m_hash = other.m_hash; m_pickedPosition = other.m_pickedPosition; m_isCustom = other.m_isCustom; @@ -142,6 +145,13 @@ bool TColorStyle::operator==(const TColorStyle &cs) const { //------------------------------------------------------------------- +QString TColorStyle::getDescription() const { + assert(false); + return QString(""); +} + +//------------------------------------------------------------------- + QString TColorStyle::getParamNames(int index) const { assert(false); return QString(""); @@ -426,6 +436,23 @@ public: return it->second.m_style->clone(); } + TColorStyle *create(std::string brushIdName) { + int index = brushIdName.find(':'); + std::string brushIdCategory = + (index != -1) ? brushIdName.substr(0, index) : brushIdName; + + for (auto &table : m_table) { + std::string name = table.second.m_style->getBrushIdName(); + int index = name.find(':'); + if (index != -1 && brushIdCategory == name.substr(0, index)) { + return table.second.m_style->clone(brushIdName); + } else if (index == -1 && brushIdCategory == name) { + return table.second.m_style->clone(brushIdName); + } + } + return nullptr; + } + void getAllTags(std::vector &tags) { tags.clear(); tags.reserve(m_table.size()); @@ -576,6 +603,60 @@ TColorStyle *TColorStyle::create(int tagId) { //------------------------------------------------------------------- +TColorStyle *TColorStyle::create(std::string brushIdName) { + return ColorStyleList::instance()->create(brushIdName); +} + +//------------------------------------------------------------------- + void TColorStyle::getAllTags(std::vector &tags) { ColorStyleList::instance()->getAllTags(tags); } + +//------------------------------------------------------------------- + +std::string TColorStyle::getBrushIdName() const { + assert(false); + return "InvalidStyle"; +} + +//------------------------------------------------------------------- + +std::size_t TColorStyle::generateHash(std::string brushIdName) { + std::hash hasher; + std::size_t v_hash = hasher(brushIdName); + return v_hash; +} + +//------------------------------------------------------------------- + +std::size_t TColorStyle::getBrushIdHash() { + if (m_hash) return m_hash; + + std::hash hasher; + std::string brushId = getBrushIdName(); + m_hash = hasher(brushId); + return m_hash; +} + +//------------------------------------------------------------------- + +std::string TColorStyle::getBrushIdNameClass(std::string brushIdName) { + int index = brushIdName.find(':'); + if (index >= 0) return brushIdName.substr(0, index); + return brushIdName; +} + +//------------------------------------------------------------------- + +std::string TColorStyle::getBrushIdNameParam(std::string brushIdName) { + int index = brushIdName.find(':'); + if (index < 0) return ""; + return brushIdName.substr(index + 1); +} + +//------------------------------------------------------------------- + +TRectD TColorStyle::getStrokeBBox(const TStroke *stroke) const { + return stroke->getBBox(); +} diff --git a/toonz/sources/common/tvrender/tfont_qt.cpp b/toonz/sources/common/tvrender/tfont_qt.cpp index 83ef8ae7..bd56cb56 100644 --- a/toonz/sources/common/tvrender/tfont_qt.cpp +++ b/toonz/sources/common/tvrender/tfont_qt.cpp @@ -285,7 +285,11 @@ TPoint TFont::drawChar(TRaster32P &outImage, TPoint &unused, TPixel32 color, TPoint TFont::getDistance(wchar_t firstChar, wchar_t secondChar) const { QFontMetrics metrics(m_pimpl->m_font); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + return TPoint(metrics.horizontalAdvance(QChar(firstChar)), 0); +#else return TPoint(metrics.width(QChar(firstChar)), 0); +#endif } //----------------------------------------------------------------------------- diff --git a/toonz/sources/common/tvrender/tofflinegl.cpp b/toonz/sources/common/tvrender/tofflinegl.cpp index 3e89013b..3b50a5ae 100644 --- a/toonz/sources/common/tvrender/tofflinegl.cpp +++ b/toonz/sources/common/tvrender/tofflinegl.cpp @@ -65,7 +65,7 @@ namespace { // on particular configurations (notably, Windows 7). So we mutex them as // a temporary workaround. static QMutex win32ImpMutex; -} +} // namespace //------------------------------- @@ -183,23 +183,33 @@ public: static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number - 0 | (false ? (PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER) + 0 | + (false ? (PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER) : (PFD_DRAW_TO_BITMAP | PFD_SUPPORT_GDI)) | PFD_SUPPORT_OPENGL, // support OpenGL PFD_TYPE_RGBA, // RGBA type 32, // 32-bit color depth 0, - 0, 0, 0, 0, 0, // color bits ignored - 8, // no alpha buffer /*===*/ - 0, // shift bit ignored - 0, // no accumulation buffer - 0, 0, 0, 0, // accum bits ignored + 0, + 0, + 0, + 0, + 0, // color bits ignored + 8, // no alpha buffer /*===*/ + 0, // shift bit ignored + 0, // no accumulation buffer + 0, + 0, + 0, + 0, // accum bits ignored 32, // 32-bit z-buffer 32, // max stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved - 0, 0, 0 // layer masks ignored + 0, + 0, + 0 // layer masks ignored }; // get the best available match of pixel format for the device context @@ -241,7 +251,7 @@ public: void *b = buffer; // Pointer To The Buffer #if !defined(x64) && defined(_MSC_VER) - __asm // Assembler Code To Follow + __asm // Assembler Code To Follow { mov ecx, bufferSize // Counter Set To Dimensions Of Our Memory Block mov ebx, b // Points ebx To Our Data (b) @@ -312,7 +322,7 @@ namespace { // The XScopedLock stuff doesn't seem finished, // why not just do the same as with win32 and use a Qt lock?? static QMutex linuxImpMutex; -} +} // namespace class XImplementation final : public TOfflineGL::Imp { public: @@ -348,14 +358,14 @@ public: static TThread::Mutex mutex; QMutexLocker sl(&mutex); - pthread_t self = pthread_self(); + pthread_t self = pthread_self(); std::map::iterator it = m_glxContext.find(self); if (((it != m_glxContext.end()) && (it->second != m_context)) || (it == m_glxContext.end())) { // cout << "calling GLXMakeCurrent " << self << " " << m_context << // endl; Bool ret; - if (!isDtor) ret = glXMakeCurrent(m_dpy, m_pixmap, m_context); + if (!isDtor) ret = glXMakeCurrent(m_dpy, m_pixmap, m_context); m_glxContext[self] = m_context; return ret; } @@ -432,7 +442,7 @@ Bool ret = glXMakeCurrent(m_dpy, m_raster = raster; } -//----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- #if defined(MACOSX) #if defined(powerpc) @@ -545,10 +555,10 @@ TOfflineGL::TOfflineGL(TDimension dim, const TOfflineGL *shared) : m_imp(0) { #endif /* - 元のコードは(別スレッドから呼び出すための) offline renderer を作って main - thread に dispatch するという訳のわからないことをしていたが Q*GLContext は - thread context を超えられないので直接生成してこのコンテキストで閉じる. - 別スレッドには dispatch しない. + The original code did some incomprehensible things like creating an offline + renderer (to be called from another thread) and dispatching it to the main + thread, but Q*GLContext can't go beyond the thread context, so it is created + directly and closed in this context. It does not dispatch to another thread. */ m_imp = currentImpGenerator(dim, shared ? shared->m_imp : 0); @@ -594,7 +604,7 @@ TOfflineGL::ImpGenerator *TOfflineGL::defineImpGenerator( void TOfflineGL::makeCurrent() { if (currentContextManager) currentContextManager->store(); - // Tutto il codice è stato spostato dentro Imp + // All the code was moved inside Imp m_imp->makeCurrent(); assert(glGetError() == GL_NO_ERROR); } diff --git a/toonz/sources/common/tvrender/tpalette.cpp b/toonz/sources/common/tvrender/tpalette.cpp index c1371b53..c8188199 100644 --- a/toonz/sources/common/tvrender/tpalette.cpp +++ b/toonz/sources/common/tvrender/tpalette.cpp @@ -92,10 +92,18 @@ std::string fidsToString(const std::vector &fids) { // convert loaded string to refLevelFids std::vector strToFids(std::string fidsStr) { std::vector ret; - QString str = QString::fromStdString(fidsStr); + QString str = QString::fromStdString(fidsStr); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList chunks = str.split(',', Qt::SkipEmptyParts); +#else QStringList chunks = str.split(',', QString::SkipEmptyParts); +#endif for (const auto &chunk : chunks) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList nums = chunk.split('-', Qt::SkipEmptyParts); +#else QStringList nums = chunk.split('-', QString::SkipEmptyParts); +#endif assert(nums.count() > 0 && nums.count() <= 2); if (nums.count() == 1) ret.push_back(TFrameId(nums[0].toInt())); diff --git a/toonz/sources/common/tvrender/tsimplecolorstyles.cpp b/toonz/sources/common/tvrender/tsimplecolorstyles.cpp index 3881c8aa..60fc2b47 100644 --- a/toonz/sources/common/tvrender/tsimplecolorstyles.cpp +++ b/toonz/sources/common/tvrender/tsimplecolorstyles.cpp @@ -132,9 +132,9 @@ public: next_stage(x, y, z); else { if (stage == 2) next_stage(x, y, z); - double l = fabs(ax + bx) > 1e-9 - ? -(ay - by) / (ax + bx) - : fabs(ay + by) > 1e-9 ? (ax - bx) / (ay + by) : 0.0; + double l = fabs(ax + bx) > 1e-9 ? -(ay - by) / (ax + bx) + : fabs(ay + by) > 1e-9 ? (ax - bx) / (ay + by) + : 0.0; if (fabs(l) > maxl || fabs(l) < 1.0 || l > 0.0) { vertices.resize(vertices.size() + 12); double *p = &vertices.back() - 11; @@ -723,6 +723,19 @@ QString TSolidColorStyle::getDescription() const { return "SolidColorStyle"; } //----------------------------------------------------------------------------- +std::string TSolidColorStyle::getBrushIdName() const { + return "SolidColorStyle"; +} + +//----------------------------------------------------------------------------- + +std::size_t TSolidColorStyle::staticBrushIdHash() { + static std::size_t bidHash = TColorStyle::generateHash("SolidColorStyle"); + return bidHash; +} + +//----------------------------------------------------------------------------- + int TSolidColorStyle::getTagId() const { return 3; } //----------------------------------------------------------------------------- @@ -888,6 +901,12 @@ QString TCenterLineStrokeStyle::getDescription() const { //----------------------------------------------------------------------------- +std::string TCenterLineStrokeStyle::getBrushIdName() const { + return "CenterLineStrokeStyle"; +} + +//----------------------------------------------------------------------------- + int TCenterLineStrokeStyle::getTagId() const { return 2; } //----------------------------------------------------------------------------- @@ -991,6 +1010,29 @@ TColorStyle *TRasterImagePatternStrokeStyle::clone() const { //----------------------------------------------------------------------------- +TColorStyle *TRasterImagePatternStrokeStyle::clone( + std::string brushIdName) const { + TRasterImagePatternStrokeStyle *style = + new TRasterImagePatternStrokeStyle(*this); + std::string patternName = getBrushIdNameParam(brushIdName); + if (patternName != "") style->loadLevel(patternName); + return style; +} + +//----------------------------------------------------------------------------- + +QString TRasterImagePatternStrokeStyle::getDescription() const { + return "TRasterImagePatternStrokeStyle"; +} + +//----------------------------------------------------------------------------- + +std::string TRasterImagePatternStrokeStyle::getBrushIdName() const { + return "RasterImagePatternStrokeStyle:" + m_name; +} + +//----------------------------------------------------------------------------- + void TRasterImagePatternStrokeStyle::makeIcon(const TDimension &size) { if (!m_level) loadLevel(m_name); m_icon = TRaster32P(); @@ -1365,6 +1407,14 @@ void TRasterImagePatternStrokeStyle::getObsoleteTagIds( ids.push_back(100); } +//----------------------------------------------------------------------------- + +TRectD TRasterImagePatternStrokeStyle::getStrokeBBox( + const TStroke *stroke) const { + TRectD rect = TColorStyle::getStrokeBBox(stroke); + return rect.enlarge(std::max(rect.getLx() * 0.25, rect.getLy() * 0.25)); +} + //************************************************************************************* // TVectorImagePatternStrokeStyle implementation //************************************************************************************* @@ -1410,6 +1460,29 @@ TColorStyle *TVectorImagePatternStrokeStyle::clone() const { //----------------------------------------------------------------------------- +TColorStyle *TVectorImagePatternStrokeStyle::clone( + std::string brushIdName) const { + TVectorImagePatternStrokeStyle *style = + new TVectorImagePatternStrokeStyle(*this); + std::string patternName = getBrushIdNameParam(brushIdName); + if (patternName != "") style->loadLevel(patternName); + return style; +} + +//----------------------------------------------------------------------------- + +QString TVectorImagePatternStrokeStyle::getDescription() const { + return "TVectorImagePatternStrokeStyle"; +} + +//----------------------------------------------------------------------------- + +std::string TVectorImagePatternStrokeStyle::getBrushIdName() const { + return "VectorImagePatternStrokeStyle:" + m_name; +} + +//----------------------------------------------------------------------------- + void TVectorImagePatternStrokeStyle::makeIcon(const TDimension &size) { TLevel::Iterator frameIt = m_level->begin(); if (frameIt == m_level->end()) { @@ -1796,6 +1869,14 @@ void TVectorImagePatternStrokeStyle::getObsoleteTagIds( // ids.push_back(100); } +//----------------------------------------------------------------------------- + +TRectD TVectorImagePatternStrokeStyle::getStrokeBBox( + const TStroke *stroke) const { + TRectD rect = TColorStyle::getStrokeBBox(stroke); + return rect.enlarge(std::max(rect.getLx() * 0.25, rect.getLy() * 0.25)); +} + //************************************************************************************* // Style declarations instances //************************************************************************************* diff --git a/toonz/sources/common/tvrender/tstrokeutil.cpp b/toonz/sources/common/tvrender/tstrokeutil.cpp index 7d2a87ce..a824236e 100644 --- a/toonz/sources/common/tvrender/tstrokeutil.cpp +++ b/toonz/sources/common/tvrender/tstrokeutil.cpp @@ -73,45 +73,37 @@ double findMinimum(const TStrokeDeformation &def, const TStroke &stroke, //--------------------------------------------------------------------------- /** - * Rationale: - * Supponiamo di voler modellare un segmento (rappresentato da una stroke) - * in modo che assuma la forma di una parabola (caso abituale offerto dal - * modificatore). - * Poniamo il che: - * (o) i punti della stroke si trovino lungo l'asse y=-100; - * (o) le x che corrisponderanno siano x1=-10 e x2=+10 (ovvio - * dall'equazione). - * - * La parabola potrà essere rappresentata sul lato sx da una quadratica con - * punti di controllo: - * P0=(-10,-100), - * P1=(-5, 0), - * P2=( 0, 0). - * Se conosciamo il numero di tratti lineari che rappresentano questa - * parabola, - * sappiamo anche quanti "campioni" sono richiesti per la sua linearizzazione. - * Questo parametro può essere utilizzato per stabilire in modo qualitativo - * il valore con cui campionare la stroke da testare; ci dovranno essere tanti - * punti da spostare per quanti campioni sono presenti nel riferimento. - */ + * Rationale: + * Suppose we want to model a segment (represented by a stroke) so that it + * takes the shape of a parabola (the usual case offered by the modifier). We + * assume that: (o) stroke points lie along the y=-100 axis; (o) the x's that + * will correspond are x1=-10 and x2=+10 (obvious from the equation). + * + * The parabola may be represented on the left side by a quadratic with control + * points: P0=(-10,-100), P1=(-5, 0), P2=( 0, 0). If we know the number of + * linear strokes representing this parabola, we also know how many "samples" + * are required for its linearization. This parameter can be used to + * qualitatively determine the value with which to sample the stroke to be + * tested; there will need to be as many points to move as there are samples in + * the reference. + */ double computeIncrement(double strokeLength, double pixelSize) { assert(pixelSize > 0 && "Pixel size is negative!!!"); assert(strokeLength > 0 && "Stroke Length size is negative!!!"); - // altezza della parabola (va verso il basso) + // height of the parabola (goes downward) double height = 100; - // suppongo di fare almeno un drag di 100 pixel + // I suppose I'm doing at least a 100-pixel drag assert(height >= 100.0); double x = sqrt(height); - // il punto p1 dovra' essere all'intersezione - // tra le tangenti ai due estremi. - // La tangente del punto p2 e l'asse x, - // l'altra avra' versore dato dal gradiente in p0, - // cioe': grad(x,-2 x) - // e se y = m x + q + // the point p1 will have to be at the intersection of the tangents to the two + // extremes. The tangent of the point p2 and the x-axis, the other will have + // versor given by the gradient at p0, + // ie: grad(x,-2 x) + // and if y = m x + q // m = double m = 2.0 * x; @@ -131,7 +123,7 @@ double computeIncrement(double strokeLength, double pixelSize) { double step = computeStep(quadratic, pixelSize); - // giusto per aggiungere punti anche nel caso peggiore. + // just to add points even in the worst case. if (step >= 1.0) step = 0.1; return step; @@ -141,39 +133,35 @@ double computeIncrement(double strokeLength, double pixelSize) { void detectEdges(const std::vector &pointArray, std::vector &edgeIndexArray) { - // ASSUNZIONE: sharpPointArray non contiene punti coincidenti adiacenti + // ASSUMPTION: sharpPointArray does not contain adjacent coincident points int size = pointArray.size(); - // controllo che ci siano piu' di tre elementi + // I check that there are more than three elements if (size < 3) return; - // scorre pointArray e per ogni suo punto cerca di inscrivere triangoli - // (utilizzando i - // punti a sinistra e a destra) considerando potenziali corner quelli con - // lati l tale - // che dMin <= l <= dMax (in realta' alla prima volta che l > dMax: breack) e - // con apertura - // angolare alpha <= alphaMax. Poi cerca i max locali tra i potenziali corner - // in una - // finestra di semiampiezza dMax (al solito alla prima volta che si supera - // dMax: breack) - - // valori di default: dMin = 7; dMax = dMin + 2; alphaMax = 2.6 (150°) + // runs pointArray and for each of its points tries to inscribe triangles + // (using left and right points) considering potential corners those with + // sides l such that dMin <= l <= dMax (actually at the first time that l > + // dMax: breack) and with angular aperture alpha <= alphaMax. + // Then it looks for local maxes among the potential corners in a window of + // semiamplitude dMax(actually at the first time dMax : breack is exceeded) + // default values: dMin = 7; dMax = dMin + 2; alphaMax = 2.6 (150 degrees) const double dMin = 4; const double dMax = dMin + 3; - const double alphaMax = 2.4; // ( 137.5°) + const double alphaMax = 2.4; // ( 137.5 degrees) const double dMin2 = dMin * dMin; const double dMax2 = dMax * dMax; std::vector sharpnessArray; - sharpnessArray.push_back(M_PI); // il primo punto e' un corner + sharpnessArray.push_back(M_PI); // the first point is a corner int nodeCount; for (nodeCount = 1; nodeCount < size - 1; - ++nodeCount) { // scorre la sharpPointArray escludendo gli estremi + ++nodeCount) { // scrolls the sharpPointArray excluding the extremes sharpnessArray.push_back(0); TPointD point(pointArray[nodeCount]); int leftCount; for (leftCount = nodeCount - 1; leftCount >= 0; - --leftCount) { // calcola i lati "left" dei triangoli inscritti... + --leftCount) { // Calculates the "left" sides of the inscribed + // triangles... TPointD left = pointArray[leftCount]; double dLeft2 = norm2(left - point); if (dLeft2 < dMin2) @@ -182,8 +170,8 @@ void detectEdges(const std::vector &pointArray, break; int rightCount; for (rightCount = nodeCount + 1; rightCount < size; - ++rightCount) { // calcola i lati "right" dei triangoli - // inscritti... + ++rightCount) { // Calculates the "right" sides of the inscribed + // triangles... TPointD right = pointArray[rightCount]; double dRight2 = norm2(right - point); if (dRight2 < dMin2) @@ -191,7 +179,7 @@ void detectEdges(const std::vector &pointArray, else if (dMax2 < dRight2) break; - // calcola i lati "center" dei triangoli inscritti + // Calculates the "center" sides of the inscribed triangles double dCenter2 = norm2(left - right); assert(dLeft2 != 0.0 && dRight2 != 0.0); @@ -208,16 +196,17 @@ void detectEdges(const std::vector &pointArray, } } - edgeIndexArray.push_back(0); // il primo punto e' un corner + edgeIndexArray.push_back(0); // the first point is a corner - // trovo i massimi locali escludendo gli estremi + // I find local maxima by excluding extremes for (nodeCount = 1; nodeCount < size - 1; - ++nodeCount) { // scorre la lista escludendo gli estremi + ++nodeCount) { // scroll through the list excluding the extremes bool isCorner = true; TPointD point(pointArray[nodeCount]); int leftCount; for (leftCount = nodeCount - 1; leftCount >= 0; - --leftCount) { // scorre la lista di sharpPoint a sinistra di node... + --leftCount) { // scrolls down the list of sharpPoints to the left of + // node... TPointD left = pointArray[leftCount]; double dLeft2 = norm2(left - point); if (dLeft2 > dMax2) break; @@ -229,7 +218,8 @@ void detectEdges(const std::vector &pointArray, if (isCorner) continue; int rightCount; for (rightCount = nodeCount + 1; rightCount < size; - ++rightCount) { // scorre la lista di sharpPoint a destra di node.. + ++rightCount) { // scrolls the list of sharpPoints to the right of + // node.. TPointD right = pointArray[rightCount]; double dRight2 = norm2(right - point); if (dRight2 > dMax2) break; @@ -240,9 +230,8 @@ void detectEdges(const std::vector &pointArray, } if (isCorner) edgeIndexArray.push_back(nodeCount); } - edgeIndexArray.push_back(size - 1); // l'ultimo punto e' un corner + edgeIndexArray.push_back(size - 1); // the last point is a corner } - } // namespace //******************************************************************************* @@ -271,8 +260,7 @@ bool increaseControlPoints(TStroke &stroke, const TStrokeDeformation &deformer, // step 2: // increase control point checking delta of deformer double maxDifference = - deformer - .getMaxDiff(); // sopra questo valore di delta, si aggiungono punti + deformer.getMaxDiff(); // above this delta value, points are added int strokeControlPoint = stroke.getControlPointCount(); @@ -313,8 +301,9 @@ bool increaseControlPoints(TStroke &stroke, const TStrokeDeformation &deformer, // find the position of step minimum = findMinimum( deformer, stroke, x1, x2, TConsts::epsilon, offset, - 20); // tra x1 e x2 va messo un nuovo punto di controllo. dove? - // questa funzione trova il punto in cui si supera il valore maxdifference + 20); // A new control point should be put between x1 and x2. where? + // this function finds the point at which the maxdifference value is + // exceeded // if minimum is not found or is equal to previous value // use a heuristic... @@ -324,8 +313,9 @@ bool increaseControlPoints(TStroke &stroke, const TStrokeDeformation &deformer, } //... else insert a control point in minimum - w = minimum; // la scansione riprende dal nuovo punto, in questo modo si - // infittisce... + w = minimum; // scanning resumes from the new point, in this way it + // thickens ... + stroke.insertControlPoints(minimum); // update of step diff --git a/toonz/sources/common/tvrender/tvectorbrushstyle.cpp b/toonz/sources/common/tvrender/tvectorbrushstyle.cpp index 15212387..2d96ca3a 100644 --- a/toonz/sources/common/tvrender/tvectorbrushstyle.cpp +++ b/toonz/sources/common/tvrender/tvectorbrushstyle.cpp @@ -241,10 +241,26 @@ TColorStyle *TVectorBrushStyle::clone() const { //----------------------------------------------------------------- +TColorStyle *TVectorBrushStyle::clone(std::string brushIdName) const { + std::string patternName = getBrushIdNameParam(brushIdName); + TVectorBrushStyle *theClone = new TVectorBrushStyle(patternName); + theClone->assignNames(this); + theClone->setFlags(getFlags()); + return theClone; +} + +//----------------------------------------------------------------- + QString TVectorBrushStyle::getDescription() const { return "VectorBrushStyle"; } //----------------------------------------------------------------- +std::string TVectorBrushStyle::getBrushIdName() const { + return "VectorBrushStyle:" + m_brushName; +} + +//----------------------------------------------------------------------------- + TStrokeProp *TVectorBrushStyle::makeStrokeProp(const TStroke *stroke) { return new VectorBrushProp(stroke, this); } @@ -358,7 +374,7 @@ TPixel32 TVectorBrushStyle::getColorParamValue(int index) const { TPalette *pal = m_brush->getPalette(); assert(pal); - int styleId = getColorStyleId(index); + int styleId = getColorStyleId(index); if (styleId < 0) styleId = 1; return pal->getStyle(styleId)->getMainColor(); @@ -370,7 +386,7 @@ void TVectorBrushStyle::setColorParamValue(int index, const TPixel32 &color) { TPalette *pal = m_brush->getPalette(); assert(pal); - int styleId = getColorStyleId(index); + int styleId = getColorStyleId(index); if (styleId < 0) styleId = 1; return pal->getStyle(styleId)->setMainColor(color); diff --git a/toonz/sources/image/CMakeLists.txt b/toonz/sources/image/CMakeLists.txt index 6cb955ae..43361a73 100644 --- a/toonz/sources/image/CMakeLists.txt +++ b/toonz/sources/image/CMakeLists.txt @@ -16,11 +16,14 @@ set(HEADERS ../include/tnzimage.h ffmpeg/tiio_gif.h ffmpeg/tiio_webm.h + ffmpeg/tiio_apng.h ffmpeg/tiio_mp4.h - ffmpeg/tiio_mov.h ffmpeg/tiio_ffmpeg.h + ffmpeg/tiio_ff_mov.h sprite/tiio_sprite.h mesh/tiio_mesh.h + exr/tinyexr_otmod.h + exr/tiio_exr.h ) set(SOURCES @@ -42,11 +45,13 @@ set(SOURCES tzl/tiio_tzl.cpp ffmpeg/tiio_gif.cpp ffmpeg/tiio_webm.cpp + ffmpeg/tiio_apng.cpp ffmpeg/tiio_mp4.cpp - ffmpeg/tiio_mov.cpp ffmpeg/tiio_ffmpeg.cpp + ffmpeg/tiio_ff_mov.cpp sprite/tiio_sprite.cpp mesh/tiio_mesh.cpp + exr/tiio_exr.cpp ) @@ -121,6 +126,10 @@ if(BUILD_TARGET_WIN AND PLATFORM EQUAL 32) ) endif() +include_directories( + ${SDKROOT}/tinyexr +) + _find_toonz_library(TNZLIBS "tnzcore;tnzbase;toonzlib") diff --git a/toonz/sources/image/exr/tiio_exr.cpp b/toonz/sources/image/exr/tiio_exr.cpp new file mode 100644 index 00000000..91303698 --- /dev/null +++ b/toonz/sources/image/exr/tiio_exr.cpp @@ -0,0 +1,530 @@ +#define TINYEXR_USE_MINIZ 0 +#include "zlib.h" + +#define TINYEXR_OTMOD_IMPLEMENTATION +#include "tinyexr_otmod.h" + +#include "tiio_exr.h" +#include "tpixel.h" + +#include +#include + +namespace { +inline unsigned char ftouc(float f, float gamma = 2.2f) { + int i = static_cast(255.0f * powf(f, 1.0f / gamma)); + if (i > 255) i = 255; + if (i < 0) i = 0; + + return static_cast(i); +} +inline float uctof(unsigned char uc, float gamma = 2.2f) { + return powf(static_cast(uc) / 255.0f, gamma); +} + +inline unsigned short ftous(float f, float gamma = 2.2f) { + int i = static_cast(65535.0f * powf(f, 1.0f / gamma)); + if (i > 65535) i = 65535; + if (i < 0) i = 0; + + return static_cast(i); +} +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"}, + {TINYEXR_COMPRESSIONTYPE_ZIPS, L"ZIPS"}, + {TINYEXR_COMPRESSIONTYPE_ZIP, L"ZIP"}, + {TINYEXR_COMPRESSIONTYPE_PIZ, L"PIZ"}}; + +const std::wstring EXR_STORAGETYPE_SCANLINE = L"Store Image as Scanlines"; +const std::wstring EXR_STORAGETYPE_TILE = L"Store Image as Tiles"; +} // namespace + +//************************************************************************** +// ExrReader implementation +//************************************************************************** + +class ExrReader final : public Tiio::Reader { + float* m_rgbaBuf; + int m_row; + EXRHeader* m_exr_header; + FILE* m_fp; + + float m_colorSpaceGamma; + +public: + ExrReader(); + ~ExrReader(); + + void open(FILE* file) override; + Tiio::RowOrder getRowOrder() const override; + bool read16BitIsEnabled() const override; + int skipLines(int lineCount) override; + ; + 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) + , m_colorSpaceGamma(2.2f) {} + +ExrReader::~ExrReader() { + if (m_rgbaBuf) free(m_rgbaBuf); + if (m_exr_header) FreeEXRHeader(m_exr_header); +} + +void ExrReader::open(FILE* file) { + m_fp = file; + m_exr_header = new EXRHeader(); + const char* err; + { + int ret = LoadEXRHeaderFromFileHandle(*m_exr_header, file, &err); + if (ret != 0) { + m_exr_header = nullptr; + throw(std::string(err)); + } + } + m_info.m_lx = + m_exr_header->data_window.max_x - m_exr_header->data_window.min_x + 1; + m_info.m_ly = + m_exr_header->data_window.max_y - m_exr_header->data_window.min_y + 1; + + m_info.m_samplePerPixel = m_exr_header->num_channels; + + int bps = 16; + switch (m_exr_header->pixel_types[0]) { + case TINYEXR_PIXELTYPE_UINT: + case TINYEXR_PIXELTYPE_FLOAT: + bps = 32; + break; + case TINYEXR_PIXELTYPE_HALF: + // bps = 16; + bps = 32; // set 32bps in order to return float raster + break; + } + m_info.m_bitsPerSample = bps; +} + +Tiio::RowOrder ExrReader::getRowOrder() const { return Tiio::TOP2BOTTOM; } + +bool ExrReader::read16BitIsEnabled() const { return true; } + +int ExrReader::skipLines(int lineCount) { + m_row += lineCount; + return lineCount; +} + +void ExrReader::loadImage() { + assert(!m_rgbaBuf); + const char* err; + { + int ret = + LoadEXRImageBufFromFileHandle(&m_rgbaBuf, *m_exr_header, m_fp, &err); + if (ret != 0) { + m_exr_header = nullptr; + throw(std::string(err)); + } + } + // header memory is freed after loading image + m_exr_header = nullptr; +} + +void ExrReader::readLine(char* buffer, int x0, int x1, int shrink) { + const int pixelSize = 4; + if (m_row < 0 || m_row >= m_info.m_ly) { + memset(buffer, 0, (x1 - x0 + 1) * pixelSize); + m_row++; + return; + } + + if (!m_rgbaBuf) loadImage(); + + TPixel32* pix = (TPixel32*)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; + + for (int i = 0; i < width; i++) { + 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; + } + + m_row++; +} + +void ExrReader::readLine(short* buffer, int x0, int x1, int shrink) { + const int pixelSize = 8; + if (m_row < 0 || m_row >= m_info.m_ly) { + memset(buffer, 0, (x1 - x0 + 1) * pixelSize); + m_row++; + return; + } + + if (!m_rgbaBuf) loadImage(); + + TPixel64* pix = (TPixel64*)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; + + for (int i = 0; i < width; i++) { + 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; + pix += 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; + // いったんノンリニアで読み込む。リニア計算のときはあとでリニアに戻す + 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_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)); + m_compressionType.addValue(ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_RLE)); + m_compressionType.addValue( + ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_ZIPS)); + m_compressionType.addValue(ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_ZIP)); + m_compressionType.addValue(ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_PIZ)); + + m_compressionType.setValue( + ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_NONE)); + + m_storageType.addValue(EXR_STORAGETYPE_SCANLINE); + m_storageType.addValue(EXR_STORAGETYPE_TILE); + m_storageType.setValue(EXR_STORAGETYPE_SCANLINE); + + bind(m_bitsPerPixel); + bind(m_compressionType); + bind(m_storageType); + bind(m_colorSpaceGamma); +} + +void Tiio::ExrWriterProperties::updateTranslation() { + m_bitsPerPixel.setQStringName(tr("Bits Per Pixel")); + // 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( + ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_NONE), tr("No compression")); + m_compressionType.setItemUIName( + ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_RLE), + tr("Run Length Encoding (RLE)")); + m_compressionType.setItemUIName( + ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_ZIPS), + tr("ZIP compression per Scanline (ZIPS)")); + m_compressionType.setItemUIName( + ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_ZIP), + tr("ZIP compression per scanline band (ZIP)")); + m_compressionType.setItemUIName( + ExrCompTypeStr.value(TINYEXR_COMPRESSIONTYPE_PIZ), + tr("PIZ-based wavelet compression (PIZ)")); + + 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")); +} + +//============================================================ + +class ExrWriter final : public Tiio::Writer { + std::vector m_imageBuf[4]; + EXRHeader m_header; + EXRImage m_image; + int m_row; + FILE* m_fp; + int m_bpp; + +public: + ExrWriter(); + ~ExrWriter(); + + 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 == 128; } + bool writeInLinearColorSpace() const override { return true; } +}; + +ExrWriter::ExrWriter() : m_row(0), m_bpp(96) {} + +ExrWriter::~ExrWriter() { + free(m_header.channels); + free(m_header.pixel_types); + free(m_header.requested_pixel_types); +} + +void ExrWriter::open(FILE* file, const TImageInfo& info) { + m_fp = file; + m_info = info; + InitEXRHeader(&m_header); + InitEXRImage(&m_image); + + if (!m_properties) m_properties = new Tiio::ExrWriterProperties(); + + TEnumProperty* bitsPerPixel = + (TEnumProperty*)(m_properties->getProperty("Bits Per Pixel")); + m_bpp = bitsPerPixel ? std::stoi(bitsPerPixel->getValue()) : 128; + assert(m_bpp == 96 || m_bpp == 128); + + std::wstring compressionType = + ((TEnumProperty*)(m_properties->getProperty("Compression Type"))) + ->getValue(); + m_header.compression_type = ExrCompTypeStr.key(compressionType); + + std::wstring storageType = + ((TEnumProperty*)(m_properties->getProperty("Storage Type")))->getValue(); + if (storageType == EXR_STORAGETYPE_TILE) { + m_header.tiled = 1; + m_header.tile_size_x = 128; + m_header.tile_size_y = 128; + m_header.tile_level_mode = TINYEXR_TILE_ONE_LEVEL; + } else + m_header.tiled = 0; + + 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); + + m_image.width = m_info.m_lx; + m_image.height = m_info.m_ly; + + m_header.num_channels = m_image.num_channels; + 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 == 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'; + 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'; + } + + 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); + for (int i = 0; i < m_header.num_channels; i++) { + m_header.pixel_types[i] = + TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image + m_header.requested_pixel_types[i] = + 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; + + 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) { + *r_p++ = uctof(pix->r); + *g_p++ = uctof(pix->g); + *b_p++ = uctof(pix->b); + 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; + + 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) { + *r_p++ = ustof(pix->r); + *g_p++ = ustof(pix->g); + *b_p++ = ustof(pix->b); + 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 == 128) { + float* image_ptr[4]; + 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); + if (ret != TINYEXR_SUCCESS) { + throw(std::string(err)); + } + } else { + float* image_ptr[3]; + 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 + m_image.images = (unsigned char**)image_ptr; + const char* err; + int ret = SaveEXRImageToFileHandle(&m_image, &m_header, m_fp, &err); + if (ret != TINYEXR_SUCCESS) { + throw(std::string(err)); + } + } +} + +//============================================================ + +Tiio::Reader* Tiio::makeExrReader() { return new ExrReader(); } + +//------------------------------------------------------------ + +Tiio::Writer* Tiio::makeExrWriter() { return new ExrWriter(); } diff --git a/toonz/sources/image/exr/tiio_exr.h b/toonz/sources/image/exr/tiio_exr.h new file mode 100644 index 00000000..60f19146 --- /dev/null +++ b/toonz/sources/image/exr/tiio_exr.h @@ -0,0 +1,33 @@ +#pragma once + +#ifndef TTIO_EXR_INCLUDED +#define TTIO_EXR_INCLUDED + +#include "tiio.h" +#include "tproperty.h" + +#include +namespace Tiio { + +//=========================================================================== + +class ExrWriterProperties final : public TPropertyGroup { + Q_DECLARE_TR_FUNCTIONS(ExrWriterProperties) +public: + TEnumProperty m_compressionType; + TEnumProperty m_storageType; + TEnumProperty m_bitsPerPixel; + TDoubleProperty m_colorSpaceGamma; + + ExrWriterProperties(); + + void updateTranslation() override; +}; + +//=========================================================================== + +Tiio::Reader* makeExrReader(); +Tiio::Writer* makeExrWriter(); +} // namespace Tiio + +#endif \ No newline at end of file diff --git a/toonz/sources/image/exr/tinyexr_otmod.h b/toonz/sources/image/exr/tinyexr_otmod.h new file mode 100644 index 00000000..ece31755 --- /dev/null +++ b/toonz/sources/image/exr/tinyexr_otmod.h @@ -0,0 +1,496 @@ +#ifndef TINYEXR_OTMOD_H_ +#define TINYEXR_OTMOD_H_ + +#define TINYEXR_IMPLEMENTATION +#include "tinyexr.h" + +/* + * This source is based on TinyEXR code, enabling to use file handle + * as an argument instead of file path in order to fit usage in OpenToonz. + * TinyEXR code is licensed under the following: + */ + +// Start of TinyEXR license ------------------------------------------------- +/* +Copyright (c) 2014 - 2021, Syoyo Fujita and many contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Syoyo Fujita nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// End of TinyEXR license ------------------------------------------------- +// TinyEXR contains some OpenEXR code, which is licensed under ------------ + +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas +// Digital Ltd. LLC +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Industrial Light & Magic nor the names of +// its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////// + +// End of OpenEXR license ------------------------------------------------- + +#ifdef __cplusplus +extern "C" { +#endif + +extern int ParseEXRVersionFromFileHandle(EXRVersion *version, FILE *fp); + +extern int ParseEXRHeaderFromFileHandle(EXRHeader *exr_header, + const EXRVersion *exr_version, FILE *fp, + const char **err); + +extern int LoadEXRImageFromFileHandle(EXRImage *exr_image, + const EXRHeader *exr_header, FILE *fp, + const char **err); + +extern int LoadEXRHeaderFromFileHandle(EXRHeader &exr_header, FILE *file, + const char **err); + +extern int LoadEXRImageBufFromFileHandle(float **out_rgba, + EXRHeader &exr_header, FILE *file, + const char **err); + +extern int SaveEXRImageToFileHandle(const EXRImage *exr_image, + const EXRHeader *exr_header, FILE *fp, + const char **err); + +#ifdef __cplusplus +} +#endif + +#endif // TINYEXR_OTMOD_H_ + +#ifdef TINYEXR_OTMOD_IMPLEMENTATION +#ifndef TINYEXR_OTMOD_IMPLEMENTATION_DEFINED +#define TINYEXR_OTMOD_IMPLEMENTATION_DEFINED + +int ParseEXRVersionFromFileHandle(EXRVersion *version, FILE *fp) { + if (!fp) { + return TINYEXR_ERROR_CANT_OPEN_FILE; + } + + size_t file_size; + // Compute size + fseek(fp, 0, SEEK_END); + file_size = static_cast(ftell(fp)); + fseek(fp, 0, SEEK_SET); + + if (file_size < tinyexr::kEXRVersionSize) { + return TINYEXR_ERROR_INVALID_FILE; + } + + unsigned char buf[tinyexr::kEXRVersionSize]; + size_t ret = fread(&buf[0], 1, tinyexr::kEXRVersionSize, fp); + // fclose(fp); + + if (ret != tinyexr::kEXRVersionSize) { + return TINYEXR_ERROR_INVALID_FILE; + } + + return ParseEXRVersionFromMemory(version, buf, tinyexr::kEXRVersionSize); +} + +int ParseEXRHeaderFromFileHandle(EXRHeader *exr_header, + const EXRVersion *exr_version, FILE *fp, + const char **err) { + if (exr_header == NULL || exr_version == NULL) { + tinyexr::SetErrorMessage("Invalid argument for ParseEXRHeaderFromFile", + err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + if (!fp) { + tinyexr::SetErrorMessage("Cannot read file ", err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } + + size_t filesize; + // Compute size + fseek(fp, 0, SEEK_END); + filesize = static_cast(ftell(fp)); + fseek(fp, 0, SEEK_SET); + + std::vector buf(filesize); // @todo { use mmap } + { + size_t ret; + ret = fread(&buf[0], 1, filesize, fp); + assert(ret == filesize); + // fclose(fp); + + if (ret != filesize) { + tinyexr::SetErrorMessage("fread() error", err); + return TINYEXR_ERROR_INVALID_FILE; + } + } + + return ParseEXRHeaderFromMemory(exr_header, exr_version, &buf.at(0), filesize, + err); +} + +int LoadEXRImageFromFileHandle(EXRImage *exr_image, const EXRHeader *exr_header, + FILE *fp, const char **err) { + if (exr_image == NULL) { + tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromFile", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + if (!fp) { + tinyexr::SetErrorMessage("Cannot read file", err); + return TINYEXR_ERROR_CANT_OPEN_FILE; + } + + size_t filesize; + // Compute size + fseek(fp, 0, SEEK_END); + filesize = static_cast(ftell(fp)); + fseek(fp, 0, SEEK_SET); + + if (filesize < 16) { + tinyexr::SetErrorMessage("File size too short", err); + return TINYEXR_ERROR_INVALID_FILE; + } + + std::vector buf(filesize); // @todo { use mmap } + { + size_t ret; + ret = fread(&buf[0], 1, filesize, fp); + assert(ret == filesize); + // fclose(fp); + (void)ret; + } + + return LoadEXRImageFromMemory(exr_image, exr_header, &buf.at(0), filesize, + err); +} + +int LoadEXRHeaderFromFileHandle(EXRHeader &exr_header, FILE *file, + const char **err) { + EXRVersion exr_version; + InitEXRHeader(&exr_header); + + { + FILE *_fp = file; + int ret = ParseEXRVersionFromFileHandle(&exr_version, _fp); + if (ret != TINYEXR_SUCCESS) { + std::stringstream ss; + ss << "Failed to open EXR file or read version info from EXR file. code(" + << ret << ")"; + tinyexr::SetErrorMessage(ss.str(), err); + return ret; + } + + if (exr_version.multipart || exr_version.non_image) { + tinyexr::SetErrorMessage( + "Loading multipart or DeepImage is not supported in LoadEXR() API", + err); + return TINYEXR_ERROR_INVALID_DATA; // @fixme. + } + } + + { + FILE *_fp = file; + int ret = ParseEXRHeaderFromFileHandle(&exr_header, &exr_version, _fp, err); + if (ret != TINYEXR_SUCCESS) { + FreeEXRHeader(&exr_header); + return ret; + } + } + return TINYEXR_SUCCESS; +} + +int LoadEXRImageBufFromFileHandle(float **out_rgba, EXRHeader &exr_header, + FILE *file, const char **err) { + if (out_rgba == NULL) { + tinyexr::SetErrorMessage("Invalid argument for LoadEXR()", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + + EXRImage exr_image; + InitEXRImage(&exr_image); + + // Read HALF channel as FLOAT. + for (int i = 0; i < exr_header.num_channels; i++) { + if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { + exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; + } + } + + // TODO: Probably limit loading to layers (channels) selected by layer index + { + FILE *_fp = file; + int ret = LoadEXRImageFromFileHandle(&exr_image, &exr_header, _fp, err); + if (ret != TINYEXR_SUCCESS) { + FreeEXRHeader(&exr_header); + return ret; + } + } + + // RGBA + int idxR = -1; + int idxG = -1; + int idxB = -1; + int idxA = -1; + + std::vector layer_names; + tinyexr::GetLayers(exr_header, layer_names); + + std::vector channels; + tinyexr::ChannelsInLayer(exr_header, "", channels); + + if (channels.size() < 1) { + tinyexr::SetErrorMessage("Layer Not Found", err); + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + return TINYEXR_ERROR_LAYER_NOT_FOUND; + } + + size_t ch_count = channels.size() < 4 ? channels.size() : 4; + for (size_t c = 0; c < ch_count; c++) { + const tinyexr::LayerChannel &ch = channels[c]; + + if (ch.name == "R") { + idxR = int(ch.index); + } else if (ch.name == "G") { + idxG = int(ch.index); + } else if (ch.name == "B") { + idxB = int(ch.index); + } else if (ch.name == "A") { + idxA = int(ch.index); + } + } + + if (channels.size() == 1) { + int chIdx = int(channels.front().index); + // Grayscale channel only. + + (*out_rgba) = reinterpret_cast( + malloc(4 * sizeof(float) * static_cast(exr_image.width) * + static_cast(exr_image.height))); + + if (exr_header.tiled) { + for (int it = 0; it < exr_image.num_tiles; it++) { + for (int j = 0; j < exr_header.tile_size_y; j++) { + for (int i = 0; i < exr_header.tile_size_x; i++) { + const int ii = exr_image.tiles[it].offset_x * + static_cast(exr_header.tile_size_x) + + i; + const int jj = exr_image.tiles[it].offset_y * + static_cast(exr_header.tile_size_y) + + j; + const int idx = ii + jj * static_cast(exr_image.width); + + // out of region check. + if (ii >= exr_image.width) { + continue; + } + if (jj >= exr_image.height) { + continue; + } + const int srcIdx = i + j * exr_header.tile_size_x; + unsigned char **src = exr_image.tiles[it].images; + (*out_rgba)[4 * idx + 0] = + reinterpret_cast(src)[chIdx][srcIdx]; + (*out_rgba)[4 * idx + 1] = + reinterpret_cast(src)[chIdx][srcIdx]; + (*out_rgba)[4 * idx + 2] = + reinterpret_cast(src)[chIdx][srcIdx]; + (*out_rgba)[4 * idx + 3] = + reinterpret_cast(src)[chIdx][srcIdx]; + } + } + } + } else { + for (int i = 0; i < exr_image.width * exr_image.height; i++) { + const float val = + reinterpret_cast(exr_image.images)[chIdx][i]; + (*out_rgba)[4 * i + 0] = val; + (*out_rgba)[4 * i + 1] = val; + (*out_rgba)[4 * i + 2] = val; + (*out_rgba)[4 * i + 3] = val; + } + } + } else { + // Assume RGB(A) + + if (idxR == -1) { + tinyexr::SetErrorMessage("R channel not found", err); + + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + return TINYEXR_ERROR_INVALID_DATA; + } + + if (idxG == -1) { + tinyexr::SetErrorMessage("G channel not found", err); + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + return TINYEXR_ERROR_INVALID_DATA; + } + + if (idxB == -1) { + tinyexr::SetErrorMessage("B channel not found", err); + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + return TINYEXR_ERROR_INVALID_DATA; + } + + (*out_rgba) = reinterpret_cast( + malloc(4 * sizeof(float) * static_cast(exr_image.width) * + static_cast(exr_image.height))); + if (exr_header.tiled) { + for (int it = 0; it < exr_image.num_tiles; it++) { + for (int j = 0; j < exr_header.tile_size_y; j++) { + for (int i = 0; i < exr_header.tile_size_x; i++) { + const int ii = + exr_image.tiles[it].offset_x * exr_header.tile_size_x + i; + const int jj = + exr_image.tiles[it].offset_y * exr_header.tile_size_y + j; + const int idx = ii + jj * exr_image.width; + + // out of region check. + if (ii >= exr_image.width) { + continue; + } + if (jj >= exr_image.height) { + continue; + } + const int srcIdx = i + j * exr_header.tile_size_x; + unsigned char **src = exr_image.tiles[it].images; + (*out_rgba)[4 * idx + 0] = + reinterpret_cast(src)[idxR][srcIdx]; + (*out_rgba)[4 * idx + 1] = + reinterpret_cast(src)[idxG][srcIdx]; + (*out_rgba)[4 * idx + 2] = + reinterpret_cast(src)[idxB][srcIdx]; + if (idxA != -1) { + (*out_rgba)[4 * idx + 3] = + reinterpret_cast(src)[idxA][srcIdx]; + } else { + (*out_rgba)[4 * idx + 3] = 1.0; + } + } + } + } + } else { + for (int i = 0; i < exr_image.width * exr_image.height; i++) { + (*out_rgba)[4 * i + 0] = + reinterpret_cast(exr_image.images)[idxR][i]; + (*out_rgba)[4 * i + 1] = + reinterpret_cast(exr_image.images)[idxG][i]; + (*out_rgba)[4 * i + 2] = + reinterpret_cast(exr_image.images)[idxB][i]; + if (idxA != -1) { + (*out_rgba)[4 * i + 3] = + reinterpret_cast(exr_image.images)[idxA][i]; + } else { + (*out_rgba)[4 * i + 3] = 1.0; + } + } + } + } + + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + + return TINYEXR_SUCCESS; +} + +int SaveEXRImageToFileHandle(const EXRImage *exr_image, + const EXRHeader *exr_header, FILE *fp, + const char **err) { + if (exr_image == NULL || exr_header->compression_type < 0) { + tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToFile", err); + return TINYEXR_ERROR_INVALID_ARGUMENT; + } + +#if !TINYEXR_USE_PIZ + if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { + tinyexr::SetErrorMessage("PIZ compression is not supported in this build", + err); + return TINYEXR_ERROR_UNSUPPORTED_FEATURE; + } +#endif + +#if !TINYEXR_USE_ZFP + if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { + tinyexr::SetErrorMessage("ZFP compression is not supported in this build", + err); + return TINYEXR_ERROR_UNSUPPORTED_FEATURE; + } +#endif + + if (!fp) { + tinyexr::SetErrorMessage("Cannot write a file", err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } + + unsigned char *mem = NULL; + size_t mem_size = SaveEXRImageToMemory(exr_image, exr_header, &mem, err); + if (mem_size == 0) { + return TINYEXR_ERROR_SERIALZATION_FAILED; + } + + size_t written_size = 0; + if ((mem_size > 0) && mem) { + written_size = fwrite(mem, 1, mem_size, fp); + } + free(mem); + + // fclose(fp); + + if (written_size != mem_size) { + tinyexr::SetErrorMessage("Cannot write a file", err); + return TINYEXR_ERROR_CANT_WRITE_FILE; + } + + return TINYEXR_SUCCESS; +} + +#endif // TINYEXR_OTMOD_IMPLEMENTATION_DEFINED +#endif // TINYEXR_OTMOD_IMPLEMENTATION \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_apng.cpp b/toonz/sources/image/ffmpeg/tiio_apng.cpp new file mode 100644 index 00000000..ca82c668 --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_apng.cpp @@ -0,0 +1,228 @@ + +#include "tiio_apng.h" +#include "tsystem.h" +#include "trasterimage.h" +#include "tsound.h" +#include "timageinfo.h" +#include "toonz/stage.h" +#include + +//=========================================================== +// +// TImageWriterAPng +// +//=========================================================== + +class TImageWriterAPng : public TImageWriter { +public: + int m_frameIndex; + + TImageWriterAPng(const TFilePath &path, int frameIndex, TLevelWriterAPng *lwg) + : TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) { + m_lwg->addRef(); + } + ~TImageWriterAPng() { m_lwg->release(); } + + bool is64bitOutputSupported() override { return false; } + void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); } + +private: + TLevelWriterAPng *m_lwg; +}; + +//=========================================================== +// +// TLevelWriterAPng; +// +//=========================================================== + +TLevelWriterAPng::TLevelWriterAPng(const TFilePath &path, TPropertyGroup *winfo) + : TLevelWriter(path, winfo) { + if (!m_properties) m_properties = new Tiio::APngWriterProperties(); + + std::string scale = m_properties->getProperty("Scale")->getValueAsString(); + m_scale = QString::fromStdString(scale).toInt(); + + TBoolProperty *extPng = (TBoolProperty *)m_properties->getProperty("ExtPng"); + m_extPng = extPng->getValue(); + + TBoolProperty *loop = (TBoolProperty *)m_properties->getProperty("Looping"); + m_looping = loop->getValue(); + + if (m_extPng) { + m_path = m_path.getParentDir() + TFilePath(m_path.getWideName() + L".png"); + } + + ffmpegWriter = new Ffmpeg(); + ffmpegWriter->setPath(m_path); + if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path); +} + +//----------------------------------------------------------- + +TLevelWriterAPng::~TLevelWriterAPng() { + QStringList preIArgs; + QStringList postIArgs; + + int outLx = m_lx; + int outLy = m_ly; + + // set scaling + if (m_scale != 0) { + outLx = m_lx * m_scale / 100; + outLy = m_ly * m_scale / 100; + } + // ffmpeg doesn't like resolutions that aren't divisible by 2. + if (outLx % 2 != 0) outLx++; + if (outLy % 2 != 0) outLy++; + + preIArgs << "-framerate"; + preIArgs << QString::number(m_frameRate); + postIArgs << "-plays"; + postIArgs << (m_looping ? "0" : "1"); + postIArgs << "-f"; + postIArgs << "apng"; + postIArgs << "-s"; + postIArgs << QString::number(outLx) + "x" + QString::number(outLy); + + ffmpegWriter->runFfmpeg(preIArgs, postIArgs, false, false, true); + ffmpegWriter->cleanUpFiles(); +} + +//----------------------------------------------------------- + +TImageWriterP TLevelWriterAPng::getFrameWriter(TFrameId fid) { + if (!fid.getLetter().isEmpty()) return TImageWriterP(0); + int index = fid.getNumber(); + TImageWriterAPng *iwg = new TImageWriterAPng(m_path, index, this); + return TImageWriterP(iwg); +} + +//----------------------------------------------------------- +void TLevelWriterAPng::setFrameRate(double fps) { + m_frameRate = fps; + ffmpegWriter->setFrameRate(fps); +} + +void TLevelWriterAPng::saveSoundTrack(TSoundTrack *st) { + ffmpegWriter->saveSoundTrack(st); +} + +//----------------------------------------------------------- + +void TLevelWriterAPng::save(const TImageP &img, int frameIndex) { + TRasterImageP image(img); + m_lx = image->getRaster()->getLx(); + m_ly = image->getRaster()->getLy(); + ffmpegWriter->createIntermediateImage(img, frameIndex); +} + +//=========================================================== +// +// TImageReaderAPng +// +//=========================================================== + +class TImageReaderAPng final : public TImageReader { +public: + int m_frameIndex; + + TImageReaderAPng(const TFilePath &path, int index, TLevelReaderAPng *lra, + TImageInfo *info) + : TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) { + m_lra->addRef(); + } + ~TImageReaderAPng() { m_lra->release(); } + + TImageP load() override { return m_lra->load(m_frameIndex); } + TDimension getSize() const { return m_lra->getSize(); } + TRect getBBox() const { return TRect(); } + const TImageInfo *getImageInfo() const override { return m_info; } + +private: + TLevelReaderAPng *m_lra; + TImageInfo *m_info; + + // not implemented + TImageReaderAPng(const TImageReaderAPng &); + TImageReaderAPng &operator=(const TImageReaderAPng &src); +}; + +//=========================================================== +// +// TLevelReaderAPng +// +//=========================================================== + +TLevelReaderAPng::TLevelReaderAPng(const TFilePath &path) : TLevelReader(path) { + ffmpegReader = new Ffmpeg(); + ffmpegReader->setPath(m_path); + ffmpegReader->disablePrecompute(); + ffmpegFileInfo tempInfo = ffmpegReader->getInfo(); + double fps = tempInfo.m_frameRate; + m_frameCount = tempInfo.m_frameCount; + m_size = TDimension(tempInfo.m_lx, tempInfo.m_ly); + m_lx = m_size.lx; + m_ly = m_size.ly; + + // set values + m_info = new TImageInfo(); + m_info->m_frameRate = fps; + m_info->m_lx = m_lx; + m_info->m_ly = m_ly; + m_info->m_bitsPerSample = 8; + m_info->m_samplePerPixel = 4; + m_info->m_dpix = Stage::standardDpi; + m_info->m_dpiy = Stage::standardDpi; +} +//----------------------------------------------------------- + +TLevelReaderAPng::~TLevelReaderAPng() {} + +//----------------------------------------------------------- + +TLevelP TLevelReaderAPng::loadInfo() { + if (m_frameCount == -1) return TLevelP(); + TLevelP level; + for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP()); + return level; +} + +//----------------------------------------------------------- + +TImageReaderP TLevelReaderAPng::getFrameReader(TFrameId fid) { + if (!fid.getLetter().isEmpty()) return TImageReaderP(0); + int index = fid.getNumber(); + + TImageReaderAPng *irm = new TImageReaderAPng(m_path, index, this, m_info); + return TImageReaderP(irm); +} + +//------------------------------------------------------------------------------ + +TDimension TLevelReaderAPng::getSize() { return m_size; } + +//------------------------------------------------ + +TImageP TLevelReaderAPng::load(int frameIndex) { + if (!ffmpegFramesCreated) { + ffmpegReader->getFramesFromMovie(); + ffmpegFramesCreated = true; + } + return ffmpegReader->getImage(frameIndex); +} + +Tiio::APngWriterProperties::APngWriterProperties() + : m_scale("Scale", 1, 100, 100) + , m_looping("Looping", true) + , m_extPng("ExtPng", false) { + bind(m_scale); + bind(m_looping); + bind(m_extPng); +} + +void Tiio::APngWriterProperties::updateTranslation() { + m_scale.setQStringName(tr("Scale")); + m_looping.setQStringName(tr("Looping")); + m_extPng.setQStringName(tr("Write as .png")); +} \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_apng.h b/toonz/sources/image/ffmpeg/tiio_apng.h new file mode 100644 index 00000000..7227a52d --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_apng.h @@ -0,0 +1,86 @@ +#pragma once + +#ifndef TTIO_APNG_INCLUDED +#define TTIO_APNG_INCLUDED + +#include "tproperty.h" +#include "tlevel_io.h" +#include "tiio_ffmpeg.h" +#include + +//=========================================================== +// +// TLevelWriterAPng +// +//=========================================================== + +class TLevelWriterAPng : public TLevelWriter { +public: + TLevelWriterAPng(const TFilePath &path, TPropertyGroup *winfo); + ~TLevelWriterAPng(); + void setFrameRate(double fps) override; + + TImageWriterP getFrameWriter(TFrameId fid) override; + void save(const TImageP &image, int frameIndex); + + void saveSoundTrack(TSoundTrack *st) override; + + static TLevelWriter *create(const TFilePath &path, TPropertyGroup *winfo) { + return new TLevelWriterAPng(path, winfo); + } + +private: + Ffmpeg *ffmpegWriter; + int m_lx, m_ly; + int m_scale; + bool m_looping; + bool m_extPng; +}; + +//=========================================================== +// +// TLevelReaderAPng +// +//=========================================================== + +class TLevelReaderAPng final : public TLevelReader { +public: + TLevelReaderAPng(const TFilePath &path); + ~TLevelReaderAPng(); + TImageReaderP getFrameReader(TFrameId fid) override; + + static TLevelReader *create(const TFilePath &f) { + return new TLevelReaderAPng(f); + } + + TLevelP loadInfo() override; + TImageP load(int frameIndex); + TDimension getSize(); +private: + Ffmpeg *ffmpegReader; + bool ffmpegFramesCreated = false; + TDimension m_size; + int m_frameCount, m_lx, m_ly; +}; + +//=========================================================================== + +namespace Tiio { + +//=========================================================================== + +class APngWriterProperties : public TPropertyGroup { + Q_DECLARE_TR_FUNCTIONS(APngWriterProperties) +public: + TIntProperty m_scale; + TBoolProperty m_looping; + TBoolProperty m_extPng; + APngWriterProperties(); + void updateTranslation() override; +}; + +//=========================================================================== + +} // namespace Tiio + +#endif \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_mov.cpp b/toonz/sources/image/ffmpeg/tiio_ff_mov.cpp similarity index 72% rename from toonz/sources/image/ffmpeg/tiio_mov.cpp rename to toonz/sources/image/ffmpeg/tiio_ff_mov.cpp index 7ded78c4..d06bfd8e 100644 --- a/toonz/sources/image/ffmpeg/tiio_mov.cpp +++ b/toonz/sources/image/ffmpeg/tiio_ff_mov.cpp @@ -1,6 +1,6 @@ #include "tsystem.h" -#include "tiio_mov.h" +#include "tiio_ff_mov.h" #include "trasterimage.h" #include "timageinfo.h" #include "tsound.h" @@ -9,36 +9,38 @@ //=========================================================== // -// TImageWriterMov +// TImageWriterFFMov // //=========================================================== -class TImageWriterMov : public TImageWriter { +class TImageWriterFFMov : public TImageWriter { public: int m_frameIndex; - TImageWriterMov(const TFilePath &path, int frameIndex, TLevelWriterMov *lwg) + TImageWriterFFMov(const TFilePath &path, int frameIndex, + TLevelWriterFFMov *lwg) : TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) { m_lwg->addRef(); } - ~TImageWriterMov() { m_lwg->release(); } + ~TImageWriterFFMov() { m_lwg->release(); } bool is64bitOutputSupported() override { return false; } void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); } private: - TLevelWriterMov *m_lwg; + TLevelWriterFFMov *m_lwg; }; //=========================================================== // -// TLevelWriterMov; +// TLevelWriterFFMov; // //=========================================================== -TLevelWriterMov::TLevelWriterMov(const TFilePath &path, TPropertyGroup *winfo) +TLevelWriterFFMov::TLevelWriterFFMov(const TFilePath &path, + TPropertyGroup *winfo) : TLevelWriter(path, winfo) { - if (!m_properties) m_properties = new Tiio::MovWriterProperties(); + if (!m_properties) m_properties = new Tiio::FFMovWriterProperties(); if (m_properties->getPropertyCount() == 0) { m_scale = 100; m_vidQuality = 100; @@ -56,8 +58,7 @@ TLevelWriterMov::TLevelWriterMov(const TFilePath &path, TPropertyGroup *winfo) //----------------------------------------------------------- -TLevelWriterMov::~TLevelWriterMov() { - // QProcess createMov; +TLevelWriterFFMov::~TLevelWriterFFMov() { QStringList preIArgs; QStringList postIArgs; @@ -84,12 +85,12 @@ TLevelWriterMov::~TLevelWriterMov() { preIArgs << "-framerate"; preIArgs << QString::number(m_frameRate); - postIArgs << "-pix_fmt"; - postIArgs << "yuva444p10le"; postIArgs << "-c:v"; postIArgs << "prores_ks"; - postIArgs << "-profile"; - postIArgs << "4444"; + postIArgs << "-pix_fmt"; + postIArgs << "yuva444p10le"; + postIArgs << "-profile:v"; + postIArgs << "4"; postIArgs << "-s"; postIArgs << QString::number(outLx) + "x" + QString::number(outLy); postIArgs << "-b"; @@ -101,28 +102,26 @@ TLevelWriterMov::~TLevelWriterMov() { //----------------------------------------------------------- -TImageWriterP TLevelWriterMov::getFrameWriter(TFrameId fid) { - // if (IOError != 0) - // throw TImageException(m_path, buildMovExceptionString(IOError)); +TImageWriterP TLevelWriterFFMov::getFrameWriter(TFrameId fid) { if (!fid.getLetter().isEmpty()) return TImageWriterP(0); - int index = fid.getNumber(); - TImageWriterMov *iwg = new TImageWriterMov(m_path, index, this); + int index = fid.getNumber(); + TImageWriterFFMov *iwg = new TImageWriterFFMov(m_path, index, this); return TImageWriterP(iwg); } //----------------------------------------------------------- -void TLevelWriterMov::setFrameRate(double fps) { +void TLevelWriterFFMov::setFrameRate(double fps) { m_frameRate = fps; ffmpegWriter->setFrameRate(fps); } -void TLevelWriterMov::saveSoundTrack(TSoundTrack *st) { +void TLevelWriterFFMov::saveSoundTrack(TSoundTrack *st) { ffmpegWriter->saveSoundTrack(st); } //----------------------------------------------------------- -void TLevelWriterMov::save(const TImageP &img, int frameIndex) { +void TLevelWriterFFMov::save(const TImageP &img, int frameIndex) { TRasterImageP image(img); m_lx = image->getRaster()->getLx(); m_ly = image->getRaster()->getLy(); @@ -131,20 +130,20 @@ void TLevelWriterMov::save(const TImageP &img, int frameIndex) { //=========================================================== // -// TImageReaderMov +// TImageReaderFFMov // //=========================================================== -class TImageReaderMov final : public TImageReader { +class TImageReaderFFMov final : public TImageReader { public: int m_frameIndex; - TImageReaderMov(const TFilePath &path, int index, TLevelReaderMov *lra, - TImageInfo *info) + TImageReaderFFMov(const TFilePath &path, int index, TLevelReaderFFMov *lra, + TImageInfo *info) : TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) { m_lra->addRef(); } - ~TImageReaderMov() { m_lra->release(); } + ~TImageReaderFFMov() { m_lra->release(); } TImageP load() override { return m_lra->load(m_frameIndex); } TDimension getSize() const { return m_lra->getSize(); } @@ -152,21 +151,22 @@ public: const TImageInfo *getImageInfo() const override { return m_info; } private: - TLevelReaderMov *m_lra; + TLevelReaderFFMov *m_lra; TImageInfo *m_info; // not implemented - TImageReaderMov(const TImageReaderMov &); - TImageReaderMov &operator=(const TImageReaderMov &src); + TImageReaderFFMov(const TImageReaderFFMov &); + TImageReaderFFMov &operator=(const TImageReaderFFMov &src); }; //=========================================================== // -// TLevelReaderMov +// TLevelReaderFFMov // //=========================================================== -TLevelReaderMov::TLevelReaderMov(const TFilePath &path) : TLevelReader(path) { +TLevelReaderFFMov::TLevelReaderFFMov(const TFilePath &path) + : TLevelReader(path) { ffmpegReader = new Ffmpeg(); ffmpegReader->setPath(m_path); ffmpegReader->disablePrecompute(); @@ -189,13 +189,11 @@ TLevelReaderMov::TLevelReaderMov(const TFilePath &path) : TLevelReader(path) { } //----------------------------------------------------------- -TLevelReaderMov::~TLevelReaderMov() { - // ffmpegReader->cleanUpFiles(); -} +TLevelReaderFFMov::~TLevelReaderFFMov() {} //----------------------------------------------------------- -TLevelP TLevelReaderMov::loadInfo() { +TLevelP TLevelReaderFFMov::loadInfo() { if (m_frameCount == -1) return TLevelP(); TLevelP level; for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP()); @@ -204,23 +202,21 @@ TLevelP TLevelReaderMov::loadInfo() { //----------------------------------------------------------- -TImageReaderP TLevelReaderMov::getFrameReader(TFrameId fid) { - // if (IOError != 0) - // throw TImageException(m_path, buildAVIExceptionString(IOError)); +TImageReaderP TLevelReaderFFMov::getFrameReader(TFrameId fid) { if (!fid.getLetter().isEmpty()) return TImageReaderP(0); int index = fid.getNumber(); - TImageReaderMov *irm = new TImageReaderMov(m_path, index, this, m_info); + TImageReaderFFMov *irm = new TImageReaderFFMov(m_path, index, this, m_info); return TImageReaderP(irm); } //------------------------------------------------------------------------------ -TDimension TLevelReaderMov::getSize() { return m_size; } +TDimension TLevelReaderFFMov::getSize() { return m_size; } //------------------------------------------------ -TImageP TLevelReaderMov::load(int frameIndex) { +TImageP TLevelReaderFFMov::load(int frameIndex) { if (!ffmpegFramesCreated) { ffmpegReader->getFramesFromMovie(); ffmpegFramesCreated = true; @@ -228,16 +224,13 @@ TImageP TLevelReaderMov::load(int frameIndex) { return ffmpegReader->getImage(frameIndex); } -Tiio::MovWriterProperties::MovWriterProperties() +Tiio::FFMovWriterProperties::FFMovWriterProperties() : m_vidQuality("Quality", 1, 100, 90), m_scale("Scale", 1, 100, 100) { bind(m_vidQuality); bind(m_scale); } -void Tiio::MovWriterProperties::updateTranslation() { +void Tiio::FFMovWriterProperties::updateTranslation() { m_vidQuality.setQStringName(tr("Quality")); m_scale.setQStringName(tr("Scale")); -} - -// Tiio::Reader* Tiio::makeMovReader(){ return nullptr; } -// Tiio::Writer* Tiio::makeMovWriter(){ return nullptr; } \ No newline at end of file +} \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_mov.h b/toonz/sources/image/ffmpeg/tiio_ff_mov.h similarity index 65% rename from toonz/sources/image/ffmpeg/tiio_mov.h rename to toonz/sources/image/ffmpeg/tiio_ff_mov.h index 777d9328..ab2c0b26 100644 --- a/toonz/sources/image/ffmpeg/tiio_mov.h +++ b/toonz/sources/image/ffmpeg/tiio_ff_mov.h @@ -1,7 +1,7 @@ #pragma once -#ifndef TTIO_MOV_INCLUDED -#define TTIO_MOV_INCLUDED +#ifndef TTIO_FFMPEG_MOV_INCLUDED +#define TTIO_FFMPEG_MOV_INCLUDED #include "tproperty.h" #include "tlevel_io.h" @@ -11,14 +11,14 @@ //=========================================================== // -// TLevelWriterMov +// TLevelWriterFFMov // //=========================================================== -class TLevelWriterMov : public TLevelWriter { +class TLevelWriterFFMov : public TLevelWriter { public: - TLevelWriterMov(const TFilePath &path, TPropertyGroup *winfo); - ~TLevelWriterMov(); + TLevelWriterFFMov(const TFilePath &path, TPropertyGroup *winfo); + ~TLevelWriterFFMov(); void setFrameRate(double fps) override; TImageWriterP getFrameWriter(TFrameId fid) override; @@ -27,7 +27,7 @@ public: void saveSoundTrack(TSoundTrack *st) override; static TLevelWriter *create(const TFilePath &path, TPropertyGroup *winfo) { - return new TLevelWriterMov(path, winfo); + return new TLevelWriterFFMov(path, winfo); } private: @@ -35,30 +35,27 @@ private: int m_lx, m_ly; int m_scale; int m_vidQuality; - // void *m_buffer; }; //=========================================================== // -// TLevelReaderMov +// TLevelReaderFFMov // //=========================================================== -class TLevelReaderMov final : public TLevelReader { +class TLevelReaderFFMov final : public TLevelReader { public: - TLevelReaderMov(const TFilePath &path); - ~TLevelReaderMov(); + TLevelReaderFFMov(const TFilePath &path); + ~TLevelReaderFFMov(); TImageReaderP getFrameReader(TFrameId fid) override; static TLevelReader *create(const TFilePath &f) { - return new TLevelReaderMov(f); + return new TLevelReaderFFMov(f); } TLevelP loadInfo() override; TImageP load(int frameIndex); TDimension getSize(); - // TThread::Mutex m_mutex; - // void *m_decompressedBuffer; private: Ffmpeg *ffmpegReader; bool ffmpegFramesCreated = false; @@ -72,22 +69,17 @@ namespace Tiio { //=========================================================================== -class MovWriterProperties : public TPropertyGroup { - Q_DECLARE_TR_FUNCTIONS(MovWriterProperties) +class FFMovWriterProperties : public TPropertyGroup { + Q_DECLARE_TR_FUNCTIONS(FFMovWriterProperties) public: - // TEnumProperty m_pixelSize; - // TBoolProperty m_matte; TIntProperty m_vidQuality; TIntProperty m_scale; - MovWriterProperties(); + FFMovWriterProperties(); void updateTranslation() override; }; //=========================================================================== -// Tiio::Reader *makeMovReader(); -// Tiio::Writer *makeMovWriter(); +} // namespace Tiio -} // namespace - -#endif +#endif \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp b/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp index 828d1718..c054b9b3 100644 --- a/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp +++ b/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp @@ -3,6 +3,8 @@ #include "tsystem.h" #include "tsound.h" #include "tenv.h" +#include "timageinfo.h" +#include "toonz/stage.h" #include #include @@ -13,136 +15,50 @@ #include "toonz/preferences.h" #include "toonz/toonzfolders.h" #include "tmsgcore.h" +#include "thirdparty.h" Ffmpeg::Ffmpeg() { - m_ffmpegPath = Preferences::instance()->getFfmpegPath(); - m_ffmpegTimeout = Preferences::instance()->getFfmpegTimeout(); - if (m_ffmpegTimeout > 0) - m_ffmpegTimeout *= 1000; - else + m_ffmpegTimeout = ThirdParty::getFFmpegTimeout() * 1000; + if (m_ffmpegTimeout <= 0) m_ffmpegTimeout = -1; - std::string strPath = m_ffmpegPath.toStdString(); m_intermediateFormat = "png"; + m_startNumber = 2147483647; // Lowest frame determines starting frame } Ffmpeg::~Ffmpeg() {} -bool Ffmpeg::checkFfmpeg() { - QString exe = "ffmpeg"; -#if defined(_WIN32) - exe = exe + ".exe"; -#endif - - // check the user defined path in preferences first - QString path = Preferences::instance()->getFfmpegPath() + "/" + exe; - if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; - - // Let's try and autodetect the exe included with release - QStringList folderList; - - folderList.append("."); - folderList.append("./ffmpeg"); // ffmpeg folder - -#ifdef MACOSX - // Look inside app - folderList.append("./" + - QString::fromStdString(TEnv::getApplicationFileName()) + - ".app/ffmpeg"); // ffmpeg folder -#elif defined(LINUX) || defined(FREEBSD) - // Need to account for symbolic links - folderList.append(TEnv::getWorkingDirectory().getQString() + - "/ffmpeg"); // ffmpeg folder -#endif - - QString exePath = TSystem::findFileLocation(folderList, exe); - - if (!exePath.isEmpty()) { - Preferences::instance()->setValue(ffmpegPath, exePath); - return true; - } - - // give up - return false; -} - -bool Ffmpeg::checkFfprobe() { - QString exe = "ffprobe"; -#if defined(_WIN32) - exe = exe + ".exe"; -#endif - - // check the user defined path in preferences first - QString path = Preferences::instance()->getFfmpegPath() + "/" + exe; - if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; - - // Let's try and autodetect the exe included with release - QStringList folderList; - - folderList.append("."); - folderList.append("./ffmpeg"); // ffmpeg folder - -#ifdef MACOSX - // Look inside app - folderList.append("./" + - QString::fromStdString(TEnv::getApplicationFileName()) + - ".app/ffmpeg"); // ffmpeg folder -#elif defined(LINUX) || defined(FREEBSD) - // Need to account for symbolic links - folderList.append(TEnv::getWorkingDirectory().getQString() + - "/ffmpeg"); // ffmpeg folder -#endif - - QString exePath = TSystem::findFileLocation(folderList, exe); - - if (!exePath.isEmpty()) { - Preferences::instance()->setValue(ffmpegPath, exePath); - return true; - } - - // give up - return false; -} - bool Ffmpeg::checkFormat(std::string format) { - QString path = Preferences::instance()->getFfmpegPath() + "/ffmpeg"; -#if defined(_WIN32) - path = path + ".exe"; -#endif QStringList args; args << "-formats"; QProcess ffmpeg; - ffmpeg.start(path, args); - if (waitFfmpeg(ffmpeg, 60000)) { // 1 minute timeout - QString results = ffmpeg.readAllStandardError(); - results += ffmpeg.readAllStandardOutput(); - ffmpeg.close(); - std::string strResults = results.toStdString(); - std::string::size_type n; - n = strResults.find(format); - if (n != std::string::npos) - return true; - } + ThirdParty::runFFmpeg(ffmpeg, args); + ffmpeg.waitForFinished(); + QString results = ffmpeg.readAllStandardError(); + results += ffmpeg.readAllStandardOutput(); + ffmpeg.close(); + std::string strResults = results.toStdString(); + std::string::size_type n; + n = strResults.find(format); + if (n != std::string::npos) + return true; + return false; } bool Ffmpeg::checkCodecs(std::string codec) { - QString path = Preferences::instance()->getFfmpegPath() + "/ffmpeg"; -#if defined(_WIN32) - path = path + ".exe"; -#endif QStringList args; args << "-codecs"; QProcess ffmpeg; - ffmpeg.start(path, args); - if (waitFfmpeg(ffmpeg, 60000)) { // 1 minute timeout - QString results = ffmpeg.readAllStandardError(); - results += ffmpeg.readAllStandardOutput(); - ffmpeg.close(); - std::string strResults = results.toStdString(); - std::string::size_type n; - n = strResults.find(codec); - if (n != std::string::npos) - return true; - } + ThirdParty::runFFmpeg(ffmpeg, args); + ffmpeg.waitForFinished(); + QString results = ffmpeg.readAllStandardError(); + results += ffmpeg.readAllStandardOutput(); + ffmpeg.close(); + std::string strResults = results.toStdString(); + std::string::size_type n; + n = strResults.find(codec); + if (n != std::string::npos) + return true; + return false; } @@ -162,11 +78,11 @@ void Ffmpeg::setPath(TFilePath path) { m_path = path; } void Ffmpeg::createIntermediateImage(const TImageP &img, int frameIndex) { m_frameCount++; - if (m_frameNumberOffset == -1) m_frameNumberOffset = frameIndex - 1; + frameIndex--; // ffmpeg start at 0 by default + if (frameIndex < m_startNumber) m_startNumber = frameIndex; QString tempPath = getFfmpegCache().getQString() + "//" + QString::fromStdString(m_path.getName()) + "tempOut" + - QString::number(frameIndex - m_frameNumberOffset) + "." + - m_intermediateFormat; + QString::number(frameIndex) + "." + m_intermediateFormat; std::string saveStatus = ""; TRasterImageP tempImage(img); TRasterImage *image = (TRasterImage *)tempImage->cloneImage(); @@ -201,7 +117,7 @@ void Ffmpeg::createIntermediateImage(const TImageP &img, int frameIndex) { void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs, bool includesInPath, bool includesOutPath, - bool overWriteFiles) { + bool overWriteFiles, bool asyncProcess) { QString tempName = "//" + QString::fromStdString(m_path.getName()) + "tempOut%d." + m_intermediateFormat; tempName = getFfmpegCache().getQString() + tempName; @@ -210,6 +126,10 @@ void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs, args = args + preIArgs; if (!includesInPath) { // NOTE: if including the in path, it needs to be in // the preIArgs argument. + if (m_startNumber != 0) { + args << "-start_number"; + args << QString::number(m_startNumber); + } args << "-i"; args << tempName; } @@ -226,8 +146,8 @@ void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs, // write the file QProcess ffmpeg; - ffmpeg.start(m_ffmpegPath + "/ffmpeg", args); - if (waitFfmpeg(ffmpeg, m_ffmpegTimeout)) { + ThirdParty::runFFmpeg(ffmpeg, args); + if (waitFfmpeg(ffmpeg, asyncProcess)) { QString results = ffmpeg.readAllStandardError(); results += ffmpeg.readAllStandardOutput(); int exitCode = ffmpeg.exitCode(); @@ -238,8 +158,8 @@ void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs, QString Ffmpeg::runFfprobe(QStringList args) { QProcess ffmpeg; - ffmpeg.start(m_ffmpegPath + "/ffprobe", args); - if (!waitFfmpeg(ffmpeg, m_ffmpegTimeout)) { + ThirdParty::runFFprobe(ffmpeg, args); + if (!waitFfmpeg(ffmpeg, false)) { throw TImageException(m_path, "error accessing ffprobe."); } QString results = ffmpeg.readAllStandardError(); @@ -253,19 +173,20 @@ QString Ffmpeg::runFfprobe(QStringList args) { return results; } -bool Ffmpeg::waitFfmpeg(const QProcess &ffmpeg, int timeout) { - QEventLoop eloop; - QTimer timer; - timer.connect(&timer, &QTimer::timeout, &eloop, [&eloop] { eloop.exit(-2); }); - ffmpeg.connect(&ffmpeg, &QProcess::errorOccurred, &eloop, - [&eloop] { eloop.exit(-1); }); - ffmpeg.connect(&ffmpeg, - static_cast( - &QProcess::finished), - &eloop, &QEventLoop::quit); - timer.start(timeout); +bool Ffmpeg::waitFfmpeg(QProcess &ffmpeg, bool asyncProcess) { + if (!asyncProcess) { + bool status = ffmpeg.waitForFinished(m_ffmpegTimeout); + if (!status) { + DVGui::warning( + QObject::tr("FFmpeg timed out.\n" + "Please check the file for errors.\n" + "If the file doesn't play or is incomplete, \n" + "Please try raising the FFmpeg timeout in Preferences.")); + } + return status; + } - int exitCode = eloop.exec(); + int exitCode = ThirdParty::waitAsyncProcess(ffmpeg, m_ffmpegTimeout); if (exitCode == 0) return true; if (exitCode == -1) { DVGui::warning( @@ -336,10 +257,13 @@ ffmpegFileInfo Ffmpeg::getInfo() { QByteArray ba = infoText.readAll(); infoText.close(); QString text = QString::fromStdString(ba.toStdString()); - m_lx = text.split(" ")[0].toInt(); - m_ly = text.split(" ")[1].toInt(); - m_frameRate = text.split(" ")[2].toDouble(); - m_frameCount = text.split(" ")[3].toInt(); + QStringList textlist = text.split(" "); + if (textlist.count() >= 4) { + m_lx = textlist[0].toInt(); + m_ly = textlist[1].toInt(); + m_frameRate = textlist[2].toDouble(); + m_frameCount = textlist[3].toInt(); + } } else { QFile infoText(tempPath); getSize(); @@ -535,7 +459,7 @@ void Ffmpeg::getFramesFromMovie(int frame) { postIFrameArgs << tempName; - runFfmpeg(preIFrameArgs, postIFrameArgs, true, true, true); + runFfmpeg(preIFrameArgs, postIFrameArgs, true, true, true, false); for (int i = 1; i <= m_frameCount; i++) { QString number = QString("%1").arg(i, 4, 10, QChar('0')); @@ -586,3 +510,99 @@ void Ffmpeg::cleanUpFiles() { void Ffmpeg::disablePrecompute() { Preferences::instance()->setPrecompute(false); } + +//=========================================================== +// +// TImageReaderFFmpeg +// +//=========================================================== + +class TImageReaderFFmpeg final : public TImageReader { +public: + int m_frameIndex; + + TImageReaderFFmpeg(const TFilePath &path, int index, TLevelReaderFFmpeg *lra, + TImageInfo *info) + : TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) { + m_lra->addRef(); + } + ~TImageReaderFFmpeg() { m_lra->release(); } + + TImageP load() override { return m_lra->load(m_frameIndex); } + TDimension getSize() const { return m_lra->getSize(); } + TRect getBBox() const { return TRect(); } + const TImageInfo *getImageInfo() const override { return m_info; } + +private: + TLevelReaderFFmpeg *m_lra; + TImageInfo *m_info; + + // not implemented + TImageReaderFFmpeg(const TImageReaderFFmpeg &); + TImageReaderFFmpeg &operator=(const TImageReaderFFmpeg &src); +}; + +//=========================================================== +// +// TLevelReaderFFmpeg +// +//=========================================================== + +TLevelReaderFFmpeg::TLevelReaderFFmpeg(const TFilePath &path) + : TLevelReader(path) { + ffmpegReader = new Ffmpeg(); + ffmpegReader->setPath(m_path); + ffmpegReader->disablePrecompute(); + ffmpegFileInfo tempInfo = ffmpegReader->getInfo(); + double fps = tempInfo.m_frameRate; + m_frameCount = tempInfo.m_frameCount; + m_size = TDimension(tempInfo.m_lx, tempInfo.m_ly); + m_lx = m_size.lx; + m_ly = m_size.ly; + + // set values + m_info = new TImageInfo(); + m_info->m_frameRate = fps; + m_info->m_lx = m_lx; + m_info->m_ly = m_ly; + m_info->m_bitsPerSample = 8; + m_info->m_samplePerPixel = 4; + m_info->m_dpix = Stage::standardDpi; + m_info->m_dpiy = Stage::standardDpi; +} +//----------------------------------------------------------- + +TLevelReaderFFmpeg::~TLevelReaderFFmpeg() {} + +//----------------------------------------------------------- + +TLevelP TLevelReaderFFmpeg::loadInfo() { + if (m_frameCount == -1) return TLevelP(); + TLevelP level; + for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP()); + return level; +} + +//----------------------------------------------------------- + +TImageReaderP TLevelReaderFFmpeg::getFrameReader(TFrameId fid) { + if (!fid.getLetter().isEmpty()) return TImageReaderP(0); + int index = fid.getNumber(); + + TImageReaderFFmpeg *irm = new TImageReaderFFmpeg(m_path, index, this, m_info); + return TImageReaderP(irm); +} + +//------------------------------------------------------------------------------ + +TDimension TLevelReaderFFmpeg::getSize() { return m_size; } + +//------------------------------------------------ + +TImageP TLevelReaderFFmpeg::load(int frameIndex) { + if (!ffmpegFramesCreated) { + ffmpegReader->getFramesFromMovie(); + ffmpegFramesCreated = true; + } + return ffmpegReader->getImage(frameIndex); +} \ No newline at end of file diff --git a/toonz/sources/image/ffmpeg/tiio_ffmpeg.h b/toonz/sources/image/ffmpeg/tiio_ffmpeg.h index d6bd9b39..0f4cfcc8 100644 --- a/toonz/sources/image/ffmpeg/tiio_ffmpeg.h +++ b/toonz/sources/image/ffmpeg/tiio_ffmpeg.h @@ -21,8 +21,8 @@ public: ~Ffmpeg(); void createIntermediateImage(const TImageP &image, int frameIndex); void runFfmpeg(QStringList preIArgs, QStringList postIArgs, - bool includesInPath, bool includesOutPath, - bool overWriteFiles); + bool includesInPath, bool includesOutPath, bool overWriteFiles, + bool asyncProcess = true); void runFfmpeg(QStringList preIArgs, QStringList postIArgs, TFilePath path); QString runFfprobe(QStringList args); void cleanUpFiles(); @@ -31,8 +31,6 @@ public: void setPath(TFilePath path); void saveSoundTrack(TSoundTrack *st); bool checkFilesExist(); - static bool checkFfmpeg(); - static bool checkFfprobe(); static bool checkFormat(std::string format); static bool checkCodecs(std::string format); double getFrameRate(); @@ -46,17 +44,46 @@ public: int getGifFrameCount(); private: - QString m_intermediateFormat, m_ffmpegPath, m_audioPath, m_audioFormat; + QString m_intermediateFormat, m_audioPath, m_audioFormat; int m_frameCount = 0, m_lx, m_ly, m_bpp, m_bitsPerSample, m_channelCount, - m_ffmpegTimeout = -1, m_frameNumberOffset = -1; - double m_frameRate = 24.0; - bool m_ffmpegExists = false, m_ffprobeExists = false, m_hasSoundTrack = false; + m_ffmpegTimeout = -1, m_startNumber = 2147483647; + double m_frameRate = 24.0; + bool m_hasSoundTrack = false; TFilePath m_path; QVector m_cleanUpList; QStringList m_audioArgs; TUINT32 m_sampleRate; QString cleanPathSymbols(); - static bool waitFfmpeg(const QProcess &ffmpeg, int timeout); + bool waitFfmpeg(QProcess &ffmpeg, bool asyncProcess); }; +//=========================================================== +// +// TLevelReaderFFmpeg +// +//=========================================================== + +class TLevelReaderFFmpeg final : public TLevelReader { +public: + TLevelReaderFFmpeg(const TFilePath &path); + ~TLevelReaderFFmpeg(); + TImageReaderP getFrameReader(TFrameId fid) override; + + static TLevelReader *create(const TFilePath &f) { + return new TLevelReaderFFmpeg(f); + } + + TLevelP loadInfo() override; + TImageP load(int frameIndex); + TDimension getSize(); + +private: + Ffmpeg *ffmpegReader; + bool ffmpegFramesCreated = false; + TDimension m_size; + int m_frameCount, m_lx, m_ly; +}; + +//=========================================================================== + #endif diff --git a/toonz/sources/image/tiio.cpp b/toonz/sources/image/tiio.cpp index 7fab617b..598eb2d1 100644 --- a/toonz/sources/image/tiio.cpp +++ b/toonz/sources/image/tiio.cpp @@ -2,6 +2,7 @@ #include "tnzimage.h" #include "tiio.h" #include "tfiletype.h" +#include "thirdparty.h" //------------------------------------------------------------------- @@ -57,9 +58,11 @@ #include "./ffmpeg/tiio_gif.h" #include "./ffmpeg/tiio_webm.h" #include "./ffmpeg/tiio_mp4.h" -#include "./ffmpeg/tiio_mov.h" +#include "./ffmpeg/tiio_apng.h" +#include "./ffmpeg/tiio_ff_mov.h" #include "./mesh/tiio_mesh.h" #include "./sprite/tiio_sprite.h" +#include "./exr/tiio_exr.h" //------------------------------------------------------------------- @@ -152,34 +155,52 @@ void initImageIo(bool lightVersion) { Tiio::defineWriterProperties("spritesheet", new Tiio::SpriteWriterProperties()); + Tiio::defineReaderMaker("exr", Tiio::makeExrReader); + Tiio::defineWriterMaker("exr", Tiio::makeExrWriter, true); + TFileType::declare("exr", TFileType::RASTER_IMAGE); + Tiio::defineWriterProperties("exr", new Tiio::ExrWriterProperties()); + // ffmpeg #if !defined(_WIN32) || defined(x64) || (defined(_WIN32) && defined(__GNUC__)) - if (Ffmpeg::checkFfmpeg()) { - bool ffprobe = Ffmpeg::checkFfprobe(); + if (ThirdParty::checkFFmpeg()) { if (Ffmpeg::checkFormat("webm")) { TLevelWriter::define("webm", TLevelWriterWebm::create, true); - if (ffprobe) TLevelReader::define("webm", TLevelReaderWebm::create); + TLevelReader::define("webm", TLevelReaderWebm::create); TFileType::declare("webm", TFileType::RASTER_LEVEL); Tiio::defineWriterProperties("webm", new Tiio::WebmWriterProperties()); } if (Ffmpeg::checkFormat("gif")) { TLevelWriter::define("gif", TLevelWriterGif::create, true); - if (ffprobe) TLevelReader::define("gif", TLevelReaderGif::create); + TLevelReader::define("gif", TLevelReaderGif::create); TFileType::declare("gif", TFileType::RASTER_LEVEL); Tiio::defineWriterProperties("gif", new Tiio::GifWriterProperties()); } if (Ffmpeg::checkFormat("mp4")) { TLevelWriter::define("mp4", TLevelWriterMp4::create, true); - if (ffprobe) TLevelReader::define("mp4", TLevelReaderMp4::create); + TLevelReader::define("mp4", TLevelReaderMp4::create); TFileType::declare("mp4", TFileType::RASTER_LEVEL); Tiio::defineWriterProperties("mp4", new Tiio::Mp4WriterProperties()); } - if (Ffmpeg::checkFormat("mov")) { - TLevelWriter::define("mov", TLevelWriterMov::create, true); - if (ffprobe) TLevelReader::define("mov", TLevelReaderMov::create); - TFileType::declare("mov", TFileType::RASTER_LEVEL); - Tiio::defineWriterProperties("mov", new Tiio::MovWriterProperties()); + if (Ffmpeg::checkFormat("apng")) { + TLevelWriter::define("apng", TLevelWriterAPng::create, true); + TLevelReader::define("apng", TLevelReaderAPng::create); + TFileType::declare("apng", TFileType::RASTER_LEVEL); + Tiio::defineWriterProperties("apng", new Tiio::APngWriterProperties()); } + if (Ffmpeg::checkFormat("mov")) { + TLevelWriter::define("mov", TLevelWriterFFMov::create, true); + TLevelReader::define("mov", TLevelReaderFFMov::create); + TFileType::declare("mov", TFileType::RASTER_LEVEL); + Tiio::defineWriterProperties("mov", new Tiio::FFMovWriterProperties()); + } + if (Ffmpeg::checkFormat("3gp")) { + TLevelReader::define("3gp", TLevelReaderFFmpeg::create); + TFileType::declare("3gp", TFileType::RASTER_LEVEL); + } + TLevelReader::define("webp", TLevelReaderFFmpeg::create); + TFileType::declare("webp", TFileType::RASTER_LEVEL); + TLevelReader::define("ffvideo", TLevelReaderFFmpeg::create); + TFileType::declare("ffvideo", TFileType::RASTER_LEVEL); } #endif // end ffmpeg diff --git a/toonz/sources/image/tzl/tiio_tzl.cpp b/toonz/sources/image/tzl/tiio_tzl.cpp index 9dc177db..306c8031 100644 --- a/toonz/sources/image/tzl/tiio_tzl.cpp +++ b/toonz/sources/image/tzl/tiio_tzl.cpp @@ -114,17 +114,9 @@ bool writeVersionAndCreator(FILE *chan, const char *version, QString creator) { if (creator.length() == 0) creator = "UNKNOWN"; memset(s, 0, sizeof s); if (creator.length() > CREATOR_LENGTH - 1) -#if QT_VERSION >= 0x050000 memcpy(s, creator.toLatin1(), CREATOR_LENGTH - 1); -#else - memcpy(s, creator.toAscii(), CREATOR_LENGTH - 1); -#endif else -#if QT_VERSION >= 0x050000 memcpy(s, creator.toLatin1(), creator.length()); -#else - memcpy(s, creator.toAscii(), creator.length()); -#endif tfwrite(s, CREATOR_LENGTH, chan); return true; @@ -1076,9 +1068,9 @@ void TLevelWriterTzl::saveImage(const TImageP &img, const TFrameId &_fid, } rCompressed->unlock(); - //#if !TNZ_LITTLE_ENDIAN - // delete [] buff; - //#endif + // #if !TNZ_LITTLE_ENDIAN + // delete [] buff; + // #endif } //------------------------------------------------------------------- @@ -2194,7 +2186,7 @@ TImageP TImageReaderTzl::load14() { TINT32 iconsbx0 = tfloor((double)iconLx * sbx0 / m_lrp->m_res.lx); TINT32 iconsby0 = tfloor((double)iconLy * sby0 / m_lrp->m_res.ly); savebox = TRect(TPoint(iconsbx0, iconsby0), - TDimension(tmp_savebox.getLx(), tmp_savebox.getLy())); + TDimension(tmp_savebox.getLx(), tmp_savebox.getLy())); } // int ly = tround((double)m_lrp->m_res.ly*iconLx/m_lrp->m_res.lx); diff --git a/toonz/sources/image/tzp/tiio_plt.cpp b/toonz/sources/image/tzp/tiio_plt.cpp index a92c16c0..55a3166d 100644 --- a/toonz/sources/image/tzp/tiio_plt.cpp +++ b/toonz/sources/image/tzp/tiio_plt.cpp @@ -1,7 +1,7 @@ #include "tiio_plt.h" -//#include "tiio.h" +// #include "tiio.h" #include "trastercm.h" #include "toonztags.h" @@ -93,8 +93,8 @@ static int decode_group_name(char group_name[], char **name, int *key, *sister_index = atoi(s + 1); else if (s[0] >= '0' && s[0] <= '9') *key = atoi(s); - *t = '!'; - s = t + 1; + *t = '!'; + s = t + 1; } *name = s; return 1; @@ -213,6 +213,11 @@ void PltReader::open(FILE *file) { m_nColor = palette[10]; m_nPencil = palette[11]; + bool isOldCmap13Plt = false; + if (m_nColor == 128 && m_nPencil == 32) { + isOldCmap13Plt = true; + } + std::string colorNames; if (TIFFGetField(m_tiff, TIFFTAG_TOONZCOLORNAMES, &count, &data)) colorNames = data; @@ -234,7 +239,7 @@ void PltReader::open(FILE *file) { if (strcmp(pageName, "_UNUSED_PAGE") == 0) continue; if (sisterIndex == -1 || i < m_nColor) m_infoRow[i].r = 255; - if (i < m_nColor) m_infoRow[i].g = 255; + if (i < m_nColor) m_infoRow[i].g = 255; while (isdigit(item->name[0])) // in toonz colors cannot begin with digits item->name++; @@ -245,8 +250,17 @@ void PltReader::open(FILE *file) { m_pltNames[i] = std::pair(pageName, item->name); if (sisterIndex != -1) { - int comboindex = (i < 256) ? (sisterIndex >> 16) + 256 : sisterIndex >> 8; - if (i >= 256) ComboInkIndex[i - 256] = comboindex; + int comboindex; + if (isOldCmap13Plt) { + comboindex = (i < 128) ? (sisterIndex >> 11) + 128 : sisterIndex >> 4; + if (i == 128) + ComboInkIndex[0] = 0; + else if (i > 128) + ComboInkIndex[i - 128] = comboindex; + } else { + comboindex = (i < 256) ? (sisterIndex >> 16) + 256 : sisterIndex >> 8; + if (i >= 256) ComboInkIndex[i - 256] = comboindex; + } std::map>::iterator it; if ((it = m_pltNames.find(comboindex)) == m_pltNames.end() || isDefaultName(it->second.second)) { @@ -285,7 +299,7 @@ void PltReader::readLine(char *buffer, int x0, int x1, int shrink) { int i; for (i = 0; i < m_info.m_lx; i++) pix[i] = TPixelRGBM32(); - int y = m_row++; + int y = m_row++; if (y == 1) { for (i = 0; i < m_info.m_lx; i++) pix[i] = m_infoRow[i]; diff --git a/toonz/sources/image/tzp/tiio_tzp.cpp b/toonz/sources/image/tzp/tiio_tzp.cpp index 6bbd5244..080f9bdf 100644 --- a/toonz/sources/image/tzp/tiio_tzp.cpp +++ b/toonz/sources/image/tzp/tiio_tzp.cpp @@ -1,14 +1,14 @@ #include "tiio_tzp.h" -//#include "tiio.h" +// #include "tiio.h" #include "trastercm.h" #include "toonztags.h" #include "texception.h" #include "tiffio.h" #include "tiffiop.h" -//#include "tspecialstyleid.h" +// #include "tspecialstyleid.h" #include #ifdef _MSC_VER @@ -37,6 +37,8 @@ class TzpReader final : public Tiio::Reader { bool m_isBigEndian; bool m_isFirstLineRead; + bool m_isOldCmap13; + public: TzpReader(); ~TzpReader(); @@ -63,6 +65,7 @@ TzpReader::TzpReader() , m_lx(0) , m_ly(0) , m_isCmap24(false) + , m_isOldCmap13(false) , m_nColor(0) , m_nPencil(0) , m_isBigEndian(false) @@ -145,8 +148,9 @@ void TzpReader::open(FILE *file) { m_nColor = palette[10]; m_nPencil = palette[11]; + // Old 4.1 palette : 4bit paint, 4bit tone, 5bit ink if (m_nColor == 128 && m_nPencil == 32) { - throw TException("Old 4.1 Palette"); + m_isOldCmap13 = true; } if (bps == 32) @@ -180,7 +184,7 @@ extern int ComboInkIndex[]; // a bad patch.... void TzpReader::readLine(char *buffer, int x0, int x1, int shrink) { TPixelCM32 *pix = (TPixelCM32 *)buffer; for (int i = 0; i < m_info.m_lx; i++) pix[i] = TPixelCM32(); - int y = m_row++; + int y = m_row++; const int paintOffset = 0; // paint#1 --> 1 const int inkOffset = 1 + m_nColor - 1; // ink#0 --> nColor-1 @@ -213,7 +217,7 @@ void TzpReader::readLine(char *buffer, int x0, int x1, int shrink) { pix[i] = TPixelCM32(ink, paint, tone); } } - } else { + } else if (!m_isOldCmap13) { if (m_y <= y && y < m_y + m_ly) { std::vector line(m_lx); TIFFReadScanline(m_tiff, (char *)&line[0], y - m_y, 0); @@ -243,6 +247,39 @@ void TzpReader::readLine(char *buffer, int x0, int x1, int shrink) { } } } + } else { + if (m_y <= y && y < m_y + m_ly) { + std::vector line(m_lx); + TIFFReadScanline(m_tiff, (char *)&line[0], y - m_y, 0); + pix += m_x; + static std::set table; + + /// per le tzp che vengono da Irix + bool bigEndian = + (m_tiff->tif_header.classic.tiff_magic == TIFF_BIGENDIAN); + + for (int i = 0; i < m_lx; i++) { + unsigned short inPix = line[i]; + if (bigEndian) inPix = swapUshort(inPix); + + int ink = ((inPix >> 3) & 0x1F); + int tone = ((inPix >> 8) & 0xF); + int paint = ((inPix >> 12) & 0xF); + tone |= tone << 4; + if (paint > 0) paint += paintOffset; + if (ComboInkIndex[ink] != -1) + ink = ComboInkIndex[ink]; + else + ink += inkOffset; + pix[i] = TPixelCM32(ink, paint, tone); + if (tone < 255) { + // int w = ink; + } + if (table.find(ink) == table.end()) { + table.insert(ink); + } + } + } } } diff --git a/toonz/sources/include/orientation.h b/toonz/sources/include/orientation.h index 29777a47..5381d7c0 100644 --- a/toonz/sources/include/orientation.h +++ b/toonz/sources/include/orientation.h @@ -159,7 +159,9 @@ enum class PredefinedDimension { QBOXLAYOUT_DIRECTION, //! direction of QBoxLayout CENTER_ALIGN, //! horizontal / vertical align CAMERA_LAYER, //! width of a camera column / height of camera row - SCALE_THRESHOLD //! scale threshold to simplify the view + SCALE_THRESHOLD, //! scale threshold to simplify the view + FRAME_AREA_EXPANSION //! expansion of the frame area if it can have extra + //! space }; enum class PredefinedPath { DRAG_HANDLE_CORNER, //! triangle corner at drag sidebar @@ -179,8 +181,8 @@ enum class PredefinedPath { }; enum class PredefinedPoint { KEY_HIDDEN, //! move extender handle that much if key icons are disabled - EXTENDER_XY_RADIUS, //! x and y radius for rounded rectangle - VOLUME_DIVISIONS_TOP_LEFT //! where to draw volume slider + EXTENDER_XY_RADIUS, //! x and y radius for rounded rectangle + VOLUME_DIVISIONS_TOP_LEFT, //! where to draw volume slider }; enum class PredefinedRange { HEADER_FRAME, //! size of of column header height(v) / row header width(h) diff --git a/toonz/sources/include/tcacheresource.h b/toonz/sources/include/tcacheresource.h index 2844b156..d3f87b66 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/tcg/hpp/mesh.hpp b/toonz/sources/include/tcg/hpp/mesh.hpp index b8ac2a1b..74411a50 100644 --- a/toonz/sources/include/tcg/hpp/mesh.hpp +++ b/toonz/sources/include/tcg/hpp/mesh.hpp @@ -337,9 +337,9 @@ int TriMesh::collapseEdge(int e) { { F &fc = this->face(*ft); - (fc.edge(0) == srcE) - ? fc.setEdge(0, dstE) - : (fc.edge(1) == srcE) ? fc.setEdge(1, dstE) : fc.setEdge(2, dstE); + (fc.edge(0) == srcE) ? fc.setEdge(0, dstE) + : (fc.edge(1) == srcE) ? fc.setEdge(1, dstE) + : fc.setEdge(2, dstE); dstEd.addFace(*ft); ft = srcEd.eraseFace(ft); // here @@ -356,7 +356,7 @@ int TriMesh::collapseEdge(int e) { // Ensure that there is no remaining edge which would be duplicated // after vDelete and vKeep merge -/* FIXME: edgeInciding がないと言われるのでとりあえず省略 */ +/* FIXME: Omitted for now because it is warned that there is no edgeInciding */ #if 0 assert("Detected vertex adjacent to collapsed edge's endpoints, but not to its faces." && edgeInciding(ed.otherVertex(vDelete), vKeep) < 0); @@ -394,7 +394,7 @@ int TriMesh::splitEdge(int e) { fCount = ed.facesCount(); // MORE than 2 adjacent faces, the new faces // should be inserted BEFORE removing the split - for (f = 0; f != fCount; ++f) // edge. + for (f = 0; f != fCount; ++f) // edge. otherV[f] = otherFaceVertex(ed.face(f), e); // Remove e diff --git a/toonz/sources/include/tcolorstyles.h b/toonz/sources/include/tcolorstyles.h index 7f5400a1..85cf3b10 100644 --- a/toonz/sources/include/tcolorstyles.h +++ b/toonz/sources/include/tcolorstyles.h @@ -133,6 +133,8 @@ private: m_pickedPosition; // picked position from color model by using style // picker tool with "organize palette" option. + std::size_t m_hash; //!< Hash value for quick comparison. + bool m_isCustom; // for Custom Texture protected: @@ -149,6 +151,7 @@ public: virtual ~TColorStyle(); virtual TColorStyle *clone() const = 0; //!< Polymorphic clone of the style. + virtual TColorStyle *clone(std::string brushIdName) const { return clone(); } virtual TColorStyle ©( const TColorStyle &other) //!< Polymorphic copy of the style. { @@ -368,10 +371,21 @@ For example for a stroke style we have "Constant", "Chain", "Rope", "Tulle", etc... */ virtual QString getDescription() - const = 0; //!< Return a brief description of the style. + const; //!< Return a brief description of the style. virtual QString getParamNames(int index) const; //!< Return the string that identifies the \a index-th parameter. + virtual std::string getBrushIdName() + const; //!< Return a name identifying the brush. + + static std::size_t generateHash(std::string brushIdName); + std::size_t getBrushIdHash(); //!< Hash for quick comparison (cached). + + static std::string getBrushIdNameClass( + std::string brushIdName); //!< Get class inside brush id name + static std::string getBrushIdNameParam( + std::string brushIdName); //!< Get parameter inside brush id name + // I/O-related functions virtual int getTagId() @@ -380,15 +394,16 @@ etc... void save(TOutputStreamInterface &) const; //!< Calls the local //! implementation of saveData() - //! passing it also the name and - //! the tagId of the style. + //! passing it also the name and the tagId of the style. + static TColorStyle *load(TInputStreamInterface &); //!< Loads the style from //! disk. Calls the local - //! implementation of - //! loadData(). + //! implementation of loadData(). + static TColorStyle *create( int tagId); //!< Creates a new style with identifier equal to \a tagId. + static TColorStyle *create(std::string brushIdName); static void declare( TColorStyle *style); //!< Puts the style in the table of actual styles. @@ -416,6 +431,8 @@ It is used when updates must be done after changes or creation of new styles. return m_versionNumber; } //!< Returns the version number of the style. + virtual TRectD getStrokeBBox(const TStroke *stroke) const; + protected: virtual void makeIcon(const TDimension &d); diff --git a/toonz/sources/include/tcolorutils.h b/toonz/sources/include/tcolorutils.h index a279ace0..dcaf5775 100644 --- a/toonz/sources/include/tcolorutils.h +++ b/toonz/sources/include/tcolorutils.h @@ -5,7 +5,7 @@ //------------------------------------------------------------------------------ #include -//#include "tpixel.h" +// #include "tpixel.h" #include "traster.h" #include @@ -22,10 +22,10 @@ namespace TColorUtils { -/*-- 似ている色をまとめて1つのStyleにする --*/ +/*-- Combine similar colors into one style --*/ DVAPI void buildPalette(std::set &palette, const TRaster32P &raster, int maxColorCount); -/*-- 全ての異なるピクセルの色を別のStyleにする --*/ +/*-- Make each different pixel color a separate style --*/ DVAPI void buildPrecisePalette(std::set &palette, const TRaster32P &raster, int maxColorCount); // pick up color chip sorrounded by frames with specified color @@ -34,7 +34,7 @@ DVAPI void buildColorChipPalette(QList> &palette, const TPixel32 &gridColor, const int gridLineWidth, const int colorChipOrder); -} +} // namespace TColorUtils //------------------------------------------------------------------------------ diff --git a/toonz/sources/include/tcommon.h b/toonz/sources/include/tcommon.h index 86f6f7ad..f23bc336 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/tfarmtask.h b/toonz/sources/include/tfarmtask.h index ae7b709c..23a1bcd1 100644 --- a/toonz/sources/include/tfarmtask.h +++ b/toonz/sources/include/tfarmtask.h @@ -133,6 +133,8 @@ public: virtual int getTaskCount() const { return 1; } virtual TFarmTask *getTask(int index) { return this; } + QString getCommandLinePrgName() const; + QString getCommandLineArguments() const; QString getCommandLine(bool isFarmTask = false) const; void parseCommandLine(QString commandLine); diff --git a/toonz/sources/include/tfilepath.h b/toonz/sources/include/tfilepath.h index 301dfde4..d34b444f 100644 --- a/toonz/sources/include/tfilepath.h +++ b/toonz/sources/include/tfilepath.h @@ -247,6 +247,7 @@ If the path is ":" a slash will be added*/ TFrameId getFrame() const; bool isFfmpegType() const; + bool isUneditable() const; bool isLevelName() const; //{return getFrame() == TFrameId(TFrameId::EMPTY_FRAME);}; bool isAbsolute() const; diff --git a/toonz/sources/include/tfx.h b/toonz/sources/include/tfx.h index 379bb66f..7a2145d4 100644 --- a/toonz/sources/include/tfx.h +++ b/toonz/sources/include/tfx.h @@ -488,10 +488,10 @@ public: virtual void compatibilityTranslatePort(int majorVersion, int minorVersion, std::string &portName) {} - /*-- Rendering(目玉)ボタンがOFFのときに使用されるInputPort --*/ + /*-- InputPort used when the Rendering (eye) button is OFF --*/ virtual int getPreferredInputPort() { return 0; } - /* RasterFxPluginHost 用の仮想関数 */ + /* Virtual function for RasterFxPluginHost */ virtual void callStartRenderHandler() {} virtual void callEndRenderHandler() {} virtual void callStartRenderFrameHandler(const TRenderSettings *rs, @@ -505,6 +505,7 @@ public: void setFxVersion(int); int getFxVersion() const; + virtual void onFxVersionSet() {} public: // Id-related functions diff --git a/toonz/sources/include/tfxattributes.h b/toonz/sources/include/tfxattributes.h index c8c361c7..ca70715a 100644 --- a/toonz/sources/include/tfxattributes.h +++ b/toonz/sources/include/tfxattributes.h @@ -31,8 +31,9 @@ class DVAPI TFxAttributes { int m_groupSelector; - /*-- MotionBlurなどのFxのために、オブジェクトの軌跡のデータを取得する --*/ + /*-- Get object trajectory data for MotionBlur and other Fxs --*/ QList m_motionPoints; + TAffine m_motionAffine[2]; // to maintain backward compatibility in the fx int m_fxVersion; @@ -67,6 +68,16 @@ public: m_motionPoints = motionPoints; } QList getMotionPoints() { return m_motionPoints; } + + void setMotionAffines(TAffine aff_Before, TAffine aff_After) { + m_motionAffine[0] = aff_Before; + m_motionAffine[1] = aff_After; + } + void getMotionAffines(TAffine &aff_Before, TAffine &aff_After) { + aff_Before = m_motionAffine[0]; + aff_After = m_motionAffine[1]; + } + void setFxVersion(int version) { m_fxVersion = version; } int getFxVersion() const { return m_fxVersion; }; diff --git a/toonz/sources/include/tfxutil.h b/toonz/sources/include/tfxutil.h index e672781c..039d4e2c 100644 --- a/toonz/sources/include/tfxutil.h +++ b/toonz/sources/include/tfxutil.h @@ -18,7 +18,8 @@ DVAPI TFxP makeCheckboard(); DVAPI TFxP makeCheckboard(TPixel32 c0, TPixel32 c1, double squareSize); /*-- - * Preferenceオプションにより、Xsheetノードに繋がった素材を「比較暗」合成して表示する + * Darken composite display of nodes connected to the Xsheet node according to + * the Preference option * --*/ DVAPI TFxP makeDarken(const TFxP &dn, const TFxP &up); @@ -38,6 +39,6 @@ DVAPI void deleteKeyframes(const TFxP &fx, int frame); DVAPI void setKeyframe(const TFxP &dstFx, int dstFrame, const TFxP &srcFx, int srcFrame, bool changedOnly = false); -} +} // namespace TFxUtil #endif diff --git a/toonz/sources/include/tgl.h b/toonz/sources/include/tgl.h index b13c754e..09e64fb0 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/thirdparty.h b/toonz/sources/include/thirdparty.h new file mode 100644 index 00000000..a79a1813 --- /dev/null +++ b/toonz/sources/include/thirdparty.h @@ -0,0 +1,71 @@ +#pragma once + +#ifndef THIRDPARTY_INCLUDED +#define THIRDPARTY_INCLUDED + +#include "tcommon.h" + +#include +#include +#include + +#undef DVAPI +#ifdef TOONZLIB_EXPORTS +#define DVAPI DV_EXPORT_API +#else +#define DVAPI DV_IMPORT_API +#endif + +namespace ThirdParty { + +//----------------------------------------------------------------------------- + +DVAPI void initialize(); + +//----------------------------------------------------------------------------- + +DVAPI void getFFmpegVideoSupported(QStringList &exts); +DVAPI void getFFmpegAudioSupported(QStringList &exts); + +DVAPI bool findFFmpeg(QString dir); +DVAPI bool checkFFmpeg(); +DVAPI QString autodetectFFmpeg(); + +DVAPI QString getFFmpegDir(); +DVAPI void setFFmpegDir(const QString &dir); +DVAPI int getFFmpegTimeout(); +DVAPI void setFFmpegTimeout(int secs); + +DVAPI void runFFmpeg(QProcess &process, const QStringList &arguments); +DVAPI void runFFprobe(QProcess &process, const QStringList &arguments); + +DVAPI void runFFmpegAudio(QProcess &process, QString srcPath, QString dstPath, + int samplerate = 44100, int bpp = 16, + int channels = 2); +DVAPI bool readFFmpegAudio(QProcess &process, QByteArray &rawData); + +//----------------------------------------------------------------------------- + +DVAPI bool findRhubarb(QString dir); +DVAPI bool checkRhubarb(); +DVAPI QString autodetectRhubarb(); + +DVAPI QString getRhubarbDir(); +DVAPI void setRhubarbDir(const QString &dir); +DVAPI int getRhubarbTimeout(); +DVAPI void setRhubarbTimeout(int secs); + +DVAPI void runRhubarb(QProcess &process, const QStringList &arguments); + +//----------------------------------------------------------------------------- + +// return 0 = No error +// return -1 = error code +// return -2 = timed out +DVAPI int waitAsyncProcess(const QProcess &process, int timeout); + +//----------------------------------------------------------------------------- + +} // namespace ThirdParty + +#endif \ No newline at end of file diff --git a/toonz/sources/include/tiio.h b/toonz/sources/include/tiio.h index e130fcc2..1a6c131d 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); @@ -174,6 +183,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 07fecf34..a5d371d0 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; + } }; //----------------------------------------------------------- @@ -227,6 +236,7 @@ private: TImageWriter &operator=(const TImageWriter &src); public: + void setFilePath(const TFilePath &path) { m_path = path; } TFilePath getFilePath() const { return m_path; }; // don't get ownership diff --git a/toonz/sources/include/tipc.h b/toonz/sources/include/tipc.h index 885f8b52..3e8cc5d0 100644 --- a/toonz/sources/include/tipc.h +++ b/toonz/sources/include/tipc.h @@ -192,13 +192,17 @@ namespace tipc { //---------------------- Connection-message utilities ---------------------- -DVAPI bool startBackgroundProcess(QString cmdline); +DVAPI bool startBackgroundProcess(QString cmdlineProgram, + QStringList cmdlineArguments); DVAPI QString applicationSpecificServerName(QString srvName); DVAPI bool startSlaveConnection(QLocalSocket *socket, QString srvName, - int msecs = -1, QString cmdline = QString(), - QString threadName = QString()); -DVAPI bool startSlaveServer(QString srvName, QString cmdline); + int msecs = -1, + QString cmdlineProgram = QString(), + QStringList cmdlineArguments = QStringList(), + QString threadName = QString()); +DVAPI bool startSlaveServer(QString srvName, QString cmdlineProgram, + QStringList cmdlineArguments); DVAPI QString readMessage(Stream &stream, Message &msg, int msecs = -1); DVAPI QString diff --git a/toonz/sources/include/tlevel_io.h b/toonz/sources/include/tlevel_io.h index 51bde9e2..b167c3a2 100644 --- a/toonz/sources/include/tlevel_io.h +++ b/toonz/sources/include/tlevel_io.h @@ -237,8 +237,8 @@ public: // Some useful utility inlines inline bool isMovieType(std::string type) { - return (type == "avi" || type == "webm" || - type == "mp4" || type == "mov"); + return (type == "mov" || type == "avi" || type == "3gp" || type == "webm" || + type == "mp4" || type == "apng"); } //----------------------------------------------------------- @@ -250,8 +250,21 @@ inline bool isMovieType(const TFilePath &fp) { //----------------------------------------------------------- +inline bool isMovieTypeOpaque(std::string type) { + return (type == "avi" || type == "3gp" || type == "mp4"); +} + +//----------------------------------------------------------- + +inline bool isMovieTypeOpaque(const TFilePath &fp) { + std::string type(fp.getType()); + return isMovieTypeOpaque(type); +} + +//----------------------------------------------------------- + inline bool isSequencialRequired(std::string type) { - return (type == "avi" || type == "3gp"); + return (type == "mov" || type == "avi" || type == "3gp"); } //----------------------------------------------------------- @@ -263,6 +276,21 @@ inline bool isSequencialRequired(const TFilePath &fp) { //----------------------------------------------------------- +inline bool isMultipleFrameType(std::string type) { + return (type == "tlv" || type == "tzl" || type == "pli" || type == "mov" || + type == "avi" || type == "3gp" || type == "gif" || type == "mp4" || + type == "webm" || type == "apng"); +} + +//----------------------------------------------------------- + +inline bool isMultipleFrameType(const TFilePath &fp) { + std::string type(fp.getType()); + return isMultipleFrameType(type); +} + +//----------------------------------------------------------- + inline bool doesSupportRandomAccess(const TFilePath &fp, bool isToonzOutput = false) { return (fp.getDots() == ".."); diff --git a/toonz/sources/include/tools/cursors.h b/toonz/sources/include/tools/cursors.h index b1569977..2069bbd6 100644 --- a/toonz/sources/include/tools/cursors.h +++ b/toonz/sources/include/tools/cursors.h @@ -19,6 +19,14 @@ enum { PenCursor, PenLargeCursor, PenCrosshairCursor, + PenTriangleTopLeftCursor, + PenTriangleTopRightCursor, + PenTriangleBottomLeftCursor, + PenTriangleBottomRightCursor, + PenTriangleUpCursor, + PenTriangleDownCursor, + PenTriangleLeftCursor, + PenTriangleRightCursor, BenderCursor, CutterCursor, DistortCursor, @@ -99,6 +107,7 @@ enum { Ex_Precise = 0x200000, Ex_Prev = 0x400000, Ex_Next = 0x800000, + Ex_FreePick = 0x1000000, // This section is for cursors that have fixed text that needs to // be handled separately when flipping for left-handed cursors. diff --git a/toonz/sources/include/tools/stylepicker.h b/toonz/sources/include/tools/stylepicker.h index 03b67aea..5b471286 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/tools/toolhandle.h b/toonz/sources/include/tools/toolhandle.h index e68dbb12..4bdf60bf 100644 --- a/toonz/sources/include/tools/toolhandle.h +++ b/toonz/sources/include/tools/toolhandle.h @@ -7,7 +7,7 @@ #include "timage.h" #include #include -#include +#include // forward declaration class TTool; @@ -35,7 +35,7 @@ class DVAPI ToolHandle final : public QObject { QString m_toolName; int m_toolTargetType; QString m_storedToolName; - QTime m_storedToolTime; + QElapsedTimer m_storedToolTime; QString m_oldToolName; bool m_toolIsBusy; diff --git a/toonz/sources/include/toonz/cleanupparameters.h b/toonz/sources/include/toonz/cleanupparameters.h index 897ee764..16a93c00 100644 --- a/toonz/sources/include/toonz/cleanupparameters.h +++ b/toonz/sources/include/toonz/cleanupparameters.h @@ -157,7 +157,7 @@ public: TPaletteP m_cleanupPalette; TFilePath m_path; - /*--- オフセットを軸ごとにロックする ---*/ + /*--- Lock offsets by axis ---*/ bool m_offx_lock, m_offy_lock; // hold brightness and contrast values for each line processing modes (grey diff --git a/toonz/sources/include/toonz/hook.h b/toonz/sources/include/toonz/hook.h index 520bbb4d..41fbce8e 100644 --- a/toonz/sources/include/toonz/hook.h +++ b/toonz/sources/include/toonz/hook.h @@ -97,7 +97,7 @@ private: TPointD m_delta; int m_id; - // Proprietà relative alla trackerRegion + // Properties related to trackerRegion //! If Hook is also a Trackeregion then m_trackerObjectId>=0, else is -1 int m_trackerObjectId; double m_width; // trackerRegion width diff --git a/toonz/sources/include/toonz/imagemanager.h b/toonz/sources/include/toonz/imagemanager.h index dd5aba7a..6c9ecc21 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 4f521c23..6a07eae1 100644 --- a/toonz/sources/include/toonz/imagepainter.h +++ b/toonz/sources/include/toonz/imagepainter.h @@ -69,6 +69,9 @@ public: bool m_forSceneIcon = false; // whether it is rendered for the scene icons bool m_forReference = false; // whether it is rendered for reference operations + + int m_gainStep; + public: VisualSettings(); @@ -110,6 +113,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/imagestyles.h b/toonz/sources/include/toonz/imagestyles.h index fc4c913f..9e84125d 100644 --- a/toonz/sources/include/toonz/imagestyles.h +++ b/toonz/sources/include/toonz/imagestyles.h @@ -132,7 +132,11 @@ public: void setColorParamValue(int index, const TPixel32 &color) override; TColorStyle *clone() const override; + TColorStyle *clone(std::string brushIdName) const override; + QString getDescription() const override; + std::string getBrushIdName() const override; + static std::string staticBrushIdName(std::wstring texturePath); bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_averageColor; } diff --git a/toonz/sources/include/toonz/levelproperties.h b/toonz/sources/include/toonz/levelproperties.h index da15a0f3..c1816646 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; + std::vector m_vanishingPoints; public: @@ -231,6 +238,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; } + void setVanishingPoints(std::vector vanishingPoints) { m_options.m_vanishingPoints = vanishingPoints; } diff --git a/toonz/sources/include/toonz/mypaintbrushstyle.h b/toonz/sources/include/toonz/mypaintbrushstyle.h index bcede243..229f03dc 100644 --- a/toonz/sources/include/toonz/mypaintbrushstyle.h +++ b/toonz/sources/include/toonz/mypaintbrushstyle.h @@ -44,6 +44,7 @@ public: ~TMyPaintBrushStyle(); TColorStyle *clone() const override { return new TMyPaintBrushStyle(*this); } + TColorStyle *clone(std::string brushIdName) const override; TColorStyle ©(const TColorStyle &other) override; @@ -67,6 +68,7 @@ public: int getTagId() const override { return 4001; } QString getDescription() const override; + std::string getBrushIdName() const override; void setBaseValue(MyPaintBrushSetting id, bool enable, float value); void resetBaseValues(); diff --git a/toonz/sources/include/toonz/stylemanager.h b/toonz/sources/include/toonz/stylemanager.h index 48993e25..6176bcea 100644 --- a/toonz/sources/include/toonz/stylemanager.h +++ b/toonz/sources/include/toonz/stylemanager.h @@ -40,17 +40,19 @@ class DVAPI CustomStyleManager final : public QObject { public: struct DVAPI PatternData { QImage *m_image; - std::string m_patternName; + QString m_patternName; bool m_isVector; bool m_isGenerated; TFilePath m_path; + std::string m_idName; // brush id name PatternData() : m_image(0) , m_patternName("") , m_isVector(false) , m_isGenerated(false) - , m_path(TFilePath()) {} + , m_path(TFilePath()) + , m_idName("") {} }; class StyleLoaderTask; @@ -62,6 +64,10 @@ private: QString m_filters; QSize m_chipSize; + bool m_isIndexed; + QList m_indexes; + QString m_searchText; + TThread::Executor m_executor; bool m_started; std::vector m_activeLoads; @@ -92,6 +98,13 @@ public: emit itemsUpdated(); } + void applyFilter(); + void applyFilter(const QString text) { + m_searchText = text; + applyFilter(); + } + QString getSearchText() const { return m_searchText; } + private: void addPattern(const TFilePath &path); @@ -110,10 +123,11 @@ class DVAPI TextureStyleManager final : public QObject { public: struct DVAPI TextureData { TRaster32P m_raster; - std::string m_textureName; + QString m_textureName; TFilePath m_path; + std::string m_idName; // brush id name - TextureData() : m_raster(0), m_textureName(""), m_path(TFilePath()) {} + TextureData() : m_raster(0), m_textureName(""), m_path(TFilePath()), m_idName("") {} }; private: @@ -122,6 +136,10 @@ private: QString m_filters; QSize m_chipSize; + bool m_isIndexed; + QList m_indexes; + QString m_searchText; + public: TextureStyleManager(const TFilePath &stylesFolder, QString filters = QString(), @@ -138,6 +156,13 @@ public: void loadTexture(TFilePath &fp); void loadItems(); + void applyFilter(); + void applyFilter(const QString text) { + m_searchText = text; + applyFilter(); + } + QString getSearchText() const { return m_searchText; } + private: void addTexture(const TFilePath &path); @@ -156,10 +181,12 @@ class DVAPI BrushStyleManager final : public QObject { public: struct DVAPI BrushData { TMyPaintBrushStyle m_brush; - std::string m_brushName; + QString m_brushName; TFilePath m_path; + std::string m_idName; // brush id name - BrushData() : m_brush(), m_brushName(""), m_path(TFilePath()) {} + BrushData() + : m_brush(), m_brushName(""), m_path(TFilePath()), m_idName("") {} }; private: @@ -168,6 +195,10 @@ private: QString m_filters; QSize m_chipSize; + bool m_isIndexed; + QList m_indexes; + QString m_searchText; + public: BrushStyleManager(const TFilePath &stylesFolder, QString filters = QString(), QSize chipSize = QSize(30, 30)); @@ -182,6 +213,13 @@ public: void loadItems(); + void applyFilter(); + void applyFilter(const QString text) { + m_searchText = text; + applyFilter(); + } + QString getSearchText() const { return m_searchText; } + signals: void itemsUpdated(); diff --git a/toonz/sources/include/toonz/tproject.h b/toonz/sources/include/toonz/tproject.h index 7c76a472..7ff4b565 100644 --- a/toonz/sources/include/toonz/tproject.h +++ b/toonz/sources/include/toonz/tproject.h @@ -145,6 +145,7 @@ public: void saveTemplate(ToonzScene *scene); + // void clearProjectsRoot(); // void addProjectsRoot(const TFilePath &fp); void addSVNProjectsRoot(const TFilePath &fp); diff --git a/toonz/sources/include/toonz/txshcolumn.h b/toonz/sources/include/toonz/txshcolumn.h index 8b8c6f83..6cc957fd 100644 --- a/toonz/sources/include/toonz/txshcolumn.h +++ b/toonz/sources/include/toonz/txshcolumn.h @@ -130,6 +130,9 @@ Constructs a TXshColumn with default value. //! Returns the column type used to store levels of the specified type. static ColumnType toColumnType(int levelType); + //! Returns true if the column can be parent of another. + bool canBeParent() const; + //! Creates an empty TXshColumn of the specified column type. static TXshColumn *createEmpty(int colType); diff --git a/toonz/sources/include/toonz/txshsimplelevel.h b/toonz/sources/include/toonz/txshsimplelevel.h index 2ed4b8e4..6ac68545 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/toonz4.6/tcm.h b/toonz/sources/include/toonz4.6/tcm.h index 4d4cb142..ca0c0e86 100644 --- a/toonz/sources/include/toonz4.6/tcm.h +++ b/toonz/sources/include/toonz4.6/tcm.h @@ -66,6 +66,6 @@ static const TCM_INFO Tcm_32_default_info = { #define TCM_CMAP_COLBUFFER_SIZE(TCM) (1 << ((TCM).color_bits + (TCM).tone_bits)) #define TCM_CMAP_PENBUFFER_SIZE(TCM) \ - (1 << ((TCM).pencil_bits + (TCM).tone_bits)) + (uint64_t(1) << ((TCM).pencil_bits + (TCM).tone_bits)) #endif diff --git a/toonz/sources/include/toonzqt/combohistogram.h b/toonz/sources/include/toonzqt/combohistogram.h index 338961b4..3ae3b7d7 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 @@ -51,6 +52,7 @@ private: QColor m_color; DisplayMode m_mode; + bool m_alphaVisible; public: ComboHistoRGBLabel(QColor color, QWidget *parent); @@ -59,6 +61,7 @@ public: void setColorAndUpdate(QColor color); void setDisplayMode(DisplayMode mode) { m_mode = mode; } + void setAlphaVisible(bool visible) { m_alphaVisible = visible; } protected: void paintEvent(QPaintEvent *pe) override; @@ -74,6 +77,7 @@ class DVAPI ChannelHistoGraph : public QWidget { int m_pickedValue; int m_channelIndex; + float m_range; public: bool *m_showComparePtr; @@ -85,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; @@ -113,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; @@ -140,8 +147,16 @@ public: void showCurrentChannelValue(int val); + void setRange(float range) { + m_histogramGraph->setRange(range); + m_colorBar->setRange(range); + } + protected slots: void onShowAlphaButtonToggled(bool visible); + +signals: + void showButtonToggled(bool); }; //----------------------------------------------------------------------------- @@ -165,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; @@ -179,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) { @@ -192,6 +215,8 @@ public: if (isVisible() && m_showCompare) updateCompHistogram(); } + void refreshHistogram(); + protected: void computeChannelsValue(int *buf, size_t size, TRasterP ras, TPalette *extPlt = nullptr); @@ -199,6 +224,9 @@ protected: protected slots: void onDisplayModeChanged(); + void onShowAlphaButtonToggled(bool); + void onRangeUp(); + void onRangeDown(); }; #endif diff --git a/toonz/sources/include/toonzqt/dvdialog.h b/toonz/sources/include/toonzqt/dvdialog.h index 30bc9063..8c119c0f 100644 --- a/toonz/sources/include/toonzqt/dvdialog.h +++ b/toonz/sources/include/toonzqt/dvdialog.h @@ -280,7 +280,7 @@ class DVAPI RadioButtonDialog final : public DVGui::Dialog { public: RadioButtonDialog(const QString &labelText, const QList &radioButtonList, QWidget *parent = 0, - Qt::WindowFlags f = 0); + Qt::WindowFlags f = Qt::WindowFlags()); public Q_SLOTS: void onButtonClicked(int id); @@ -308,7 +308,7 @@ protected: public: ProgressDialog(const QString &labelText, const QString &cancelButtonText, int minimum, int maximum, QWidget *parent = 0, - Qt::WindowFlags f = 0); + Qt::WindowFlags f = Qt::WindowFlags()); void setLabelText(const QString &text); void setCancelButton(QPushButton *cancelButton); diff --git a/toonz/sources/include/toonzqt/flipconsole.h b/toonz/sources/include/toonzqt/flipconsole.h index 601370d8..bee23ed8 100644 --- a/toonz/sources/include/toonzqt/flipconsole.h +++ b/toonz/sources/include/toonzqt/flipconsole.h @@ -47,7 +47,8 @@ enum { eShowViewerControls = 0x1000, eShowSound = 0x2000, eShowLocator = 0x4000, - eShowHowMany = 0x8000 + eShowGainControls = 0x8000, + eShowHowMany = 0x10000 }; class QToolBar; @@ -245,6 +246,10 @@ public: eFlipHorizontal, eFlipVertical, eResetView, + eBlankFrames, + eDecreaseGain, + eResetGain, + eIncreaseGain, // following values are hard-coded in ImagePainter eBlackBg = 0x40000, eWhiteBg = 0x80000, @@ -295,6 +300,7 @@ public: UINT getCustomizeMask() { return m_customizeMask; } void setCustomizemask(UINT mask); void setStopAt(int frame); + void setStartAt(int frame); // the main (currently the only) use for current flipconsole and setActive is // to @@ -338,6 +344,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); @@ -354,7 +361,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; @@ -371,10 +378,13 @@ 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; int m_stopAt = -1; + int m_startAt = + -1; // used in the "play selection" mode of the viewer preview ImagePainter::VisualSettings m_settings; bool m_isPlay; @@ -387,6 +397,9 @@ private: int m_blanksToDraw; bool m_isLinkable; + QToolButton *m_resetGainBtn; + int m_prevGainStep; + QMenu *m_menu; QMap m_buttons; @@ -422,6 +435,8 @@ private: FlipConsoleOwner *m_consoleOwner; TFrameHandle *m_frameHandle; + void adjustGain(bool increase); + protected slots: void OnSetCurrentFrame(); diff --git a/toonz/sources/include/toonzqt/functionviewer.h b/toonz/sources/include/toonzqt/functionviewer.h index dcc2de6e..9b5107b1 100644 --- a/toonz/sources/include/toonzqt/functionviewer.h +++ b/toonz/sources/include/toonzqt/functionviewer.h @@ -74,11 +74,8 @@ public: enum IoType { eSaveCurve, eLoadCurve, eExportCurve }; public: -#if QT_VERSION >= 0x050500 - FunctionViewer(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - FunctionViewer(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + FunctionViewer(QWidget *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); ~FunctionViewer(); void setXsheetHandle(TXsheetHandle *xshHandle); //!< Associates an xsheet to diff --git a/toonz/sources/include/toonzqt/fxschematicnode.h b/toonz/sources/include/toonzqt/fxschematicnode.h index abf2de7b..44ab23cb 100644 --- a/toonz/sources/include/toonzqt/fxschematicnode.h +++ b/toonz/sources/include/toonzqt/fxschematicnode.h @@ -72,6 +72,8 @@ protected: class FxPalettePainter final : public QObject, public QGraphicsItem { Q_OBJECT + Q_INTERFACES(QGraphicsItem) + FxSchematicPaletteNode *m_parent; double m_width, m_height; QString m_name; diff --git a/toonz/sources/include/toonzqt/fxschematicscene.h b/toonz/sources/include/toonzqt/fxschematicscene.h index 47211053..518cd4e6 100644 --- a/toonz/sources/include/toonzqt/fxschematicscene.h +++ b/toonz/sources/include/toonzqt/fxschematicscene.h @@ -190,6 +190,8 @@ private: bool isAnEmptyZone_withParentFx(const QRectF &rect, const TFx *parent); + // snap to neighbor nodes on dragging + void updateSnapTarget(QGraphicsItem *item) override; signals: void showPreview(TFxP); void cacheFx(TFxP); diff --git a/toonz/sources/include/toonzqt/fxsettings.h b/toonz/sources/include/toonzqt/fxsettings.h index f2ffb396..efabbe7d 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,13 +154,12 @@ 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 - ParamsPageSet(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - ParamsPageSet(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + ParamsPageSet(QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags()); ~ParamsPageSet(); void setFx(const TFxP ¤tFx, const TFxP &actualFx, int frame); @@ -179,6 +179,8 @@ public: QSize getPreferredSize() { return m_preferredSize; } + void updateWarnings(const TFxP ¤tFx, bool isFloat); + protected: void createPage(TIStream &is, const TFxP &fx, int index); @@ -204,11 +206,7 @@ class DVAPI ParamViewer final : public QFrame { QMap m_tableFxIndex; public: -#if QT_VERSION >= 0x050500 - ParamViewer(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - ParamViewer(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + ParamViewer(QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags()); ~ParamViewer(); void setFx(const TFxP ¤tFx, const TFxP &actualFx, int frame, @@ -224,6 +222,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/toonzqt/gutil.h b/toonz/sources/include/toonzqt/gutil.h index 6bf79f82..40680021 100644 --- a/toonz/sources/include/toonzqt/gutil.h +++ b/toonz/sources/include/toonzqt/gutil.h @@ -118,6 +118,8 @@ QPixmap DVAPI recolorPixmap( : Qt::white); QIcon DVAPI createQIcon(const char *iconSVGName, bool useFullOpacity = false, bool isForMenuItem = false); +void DVAPI addSpecifiedSizedImageToIcon(QIcon &icon, const char *iconSVGName, + QSize newSize); QIcon DVAPI createQIconPNG(const char *iconPNGName); QIcon DVAPI createQIconOnOffPNG(const char *iconPNGName, bool withOver = true); QIcon DVAPI createTemporaryIconFromName(const char *commandName); diff --git a/toonz/sources/include/toonzqt/lutcalibrator.h b/toonz/sources/include/toonzqt/lutcalibrator.h index 012488b7..4e4cf40b 100644 --- a/toonz/sources/include/toonzqt/lutcalibrator.h +++ b/toonz/sources/include/toonzqt/lutcalibrator.h @@ -89,7 +89,7 @@ public: bool isValid() { return m_isValid; } int meshSize() const { return m_lut.meshSize; } - float* data() const { return m_lut.data; } + const float* data() const { return m_lut.data; } bool loadLutFile(const QString& fp); diff --git a/toonz/sources/include/toonzqt/menubarcommand.h b/toonz/sources/include/toonzqt/menubarcommand.h index fa30ea1d..c9e46a4e 100644 --- a/toonz/sources/include/toonzqt/menubarcommand.h +++ b/toonz/sources/include/toonzqt/menubarcommand.h @@ -63,10 +63,11 @@ enum CommandType { ToolModifierCommandType, ZoomCommandType, MiscCommandType, + StopMotionCommandType, + CellMarkCommandType, MenuCommandType, VisualizationButtonCommandType, - StopMotionCommandType, - CellMarkCommandType + HiddenCommandType }; //----------------------------------------------------------------------------- @@ -110,13 +111,15 @@ class DVAPI CommandManager { // singleton bool m_enabled; QString m_onText, m_offText; // for toggle commands. e.g. show/hide something + const char *m_iconSVGName; Node(CommandId id) : m_id(id) , m_type(UndefinedCommandType) , m_qaction(0) , m_handler(0) - , m_enabled(true) {} + , m_enabled(true) + , m_iconSVGName("") {} ~Node() { if (m_handler) delete m_handler; @@ -140,7 +143,7 @@ public: void setHandler(CommandId id, CommandHandlerInterface *handler); void define(CommandId id, CommandType type, std::string defaultShortcutString, - QAction *action); + QAction *action, const char *iconSVGName = ""); QAction *createAction(const char *id, const char *name, const char *defaultShortcut); @@ -179,6 +182,8 @@ public: const QString &offText); std::string getIdFromAction(QAction *action); + const char *getIconSVGName(CommandId id); + void enlargeIcon(CommandId id, const QSize size); // load user defined shortcuts void loadShortcuts(); @@ -242,9 +247,15 @@ public: void execute() override { if (!m_popup) m_popup = new T(); - m_popup->show(); - m_popup->raise(); - m_popup->activateWindow(); + + // close popup when using the command while open + if (m_popup->isVisible()) + m_popup->hide(); + else { + m_popup->show(); + m_popup->raise(); + m_popup->activateWindow(); + } } }; @@ -266,9 +277,11 @@ class DVAPI DVMenuAction final : public QMenu { Q_OBJECT int m_triggeredActionIndex; + bool m_isForRecentFiles; public: - DVMenuAction(const QString &text, QWidget *parent, QList actions); + DVMenuAction(const QString &text, QWidget *parent, QList actions, + bool isForRecentFiles = true); void setActions(QList actions); int getTriggeredActionIndex() { return m_triggeredActionIndex; } diff --git a/toonz/sources/include/toonzqt/paletteviewer.h b/toonz/sources/include/toonzqt/paletteviewer.h index 6ce1e657..9e5ccc75 100644 --- a/toonz/sources/include/toonzqt/paletteviewer.h +++ b/toonz/sources/include/toonzqt/paletteviewer.h @@ -4,6 +4,7 @@ #define PALETTEVIEWER_H #include "paletteviewergui.h" +#include "saveloadqsettings.h" #include "toonz/tpalettehandle.h" #include "toonz/tapplication.h" @@ -41,7 +42,7 @@ class TXsheetHandle; // PaletteViewer //----------------------------------------------------------------------------- -class DVAPI PaletteViewer final : public QFrame { +class DVAPI PaletteViewer final : public QFrame, public SaveLoadQSettings { Q_OBJECT public: @@ -50,7 +51,16 @@ public: bool hasPasteColors = true); ~PaletteViewer(); - const TPaletteHandle *getPaletteHandle() const { return m_paletteHandle; } + enum ToolbarButtons : int //! Toolbar buttons to display + { + TBVisKeyframe, + TBVisNewStylePage, + TBVisPaletteGizmo, + TBVisNameEditor, + TBVisTotal + }; + + TPaletteHandle *getPaletteHandle() const { return m_paletteHandle; } void setPaletteHandle(TPaletteHandle *paletteHandle); const TFrameHandle *getFrameHandle() const { return m_frameHandle; } @@ -86,6 +96,11 @@ public: int geCurrentPageIndex() { return m_currentIndexPage; } + // SaveLoadQSettings + virtual void save(QSettings &settings, + bool forPopupIni = false) const override; + virtual void load(QSettings &settings) override; + protected: TPaletteHandle *m_paletteHandle; TFrameHandle *m_frameHandle; @@ -123,6 +138,16 @@ protected: TApplication *m_app; + StyleNameEditor *m_styleNameEditor; + QAction *m_sharedGizmoAction; + + int m_toolbarVisibleOtherParts; + QMultiMap m_toolbarParts; + QAction *m_visibleKeysAction; +// QAction *m_visibleNewAction; + QAction *m_visibleGizmoAction; + QAction *m_visibleNameAction; + protected: void createTabBar(); @@ -157,6 +182,8 @@ protected: void clearStyleSelection(); + void applyToolbarPartVisibility(int part, bool visible); + protected slots: void setPageView(int currentIndexPage); @@ -186,6 +213,10 @@ protected slots: void onSwitchToPage(int pageIndex); void onShowNewStyleButtonToggled(); + void toggleKeyframeVisibility(bool); +// void toggleNewStylePageVisibility(bool); + void togglePaletteGizmoVisibility(bool); + void toggleNameEditorVisibility(bool); signals: void frozenChanged(bool frozen); diff --git a/toonz/sources/include/toonzqt/paletteviewergui.h b/toonz/sources/include/toonzqt/paletteviewergui.h index b1bcb028..72b64b83 100644 --- a/toonz/sources/include/toonzqt/paletteviewergui.h +++ b/toonz/sources/include/toonzqt/paletteviewergui.h @@ -51,9 +51,10 @@ class StyleNameEditor; namespace PaletteViewerGUI { enum PaletteViewType //! Possible palette contents of a Palette Viewer. -{ LEVEL_PALETTE, //!< Content palette is from a level. - CLEANUP_PALETTE, //!< Content palette is from cleanup settings. - STUDIO_PALETTE //!< Content palette is from a Studio Palette panel. +{ + LEVEL_PALETTE, //!< Content palette is from a level. + CLEANUP_PALETTE, //!< Content palette is from cleanup settings. + STUDIO_PALETTE //!< Content palette is from a Studio Palette panel. }; //**************************************************************************** @@ -102,8 +103,9 @@ class DVAPI PageViewer final : public QFrame, public TSelection::View { WRITE setListNumpadShortcutBorderColor) public: - enum ViewMode //! Possible view modes for a Palette Viewer. - { SmallChips, //!< Small icons. + enum ViewMode //! Possible view modes for a Palette Viewer. + { + SmallChips, //!< Small icons. MediumChips, //!< Medium icons. LargeChips, //!< Large icons with style names. List, //!< Top-down list of all icons. @@ -348,11 +350,8 @@ class DVAPI PaletteIconWidget final : public QWidget { Q_OBJECT public: -#if QT_VERSION >= 0x050500 - PaletteIconWidget(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - PaletteIconWidget(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + PaletteIconWidget(QWidget *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); ~PaletteIconWidget(); signals: diff --git a/toonz/sources/include/toonzqt/paramfield.h b/toonz/sources/include/toonzqt/paramfield.h index ca892c42..23a046c3 100644 --- a/toonz/sources/include/toonzqt/paramfield.h +++ b/toonz/sources/include/toonzqt/paramfield.h @@ -389,6 +389,7 @@ public: void updateField(double value) override; QSize getPreferredSize() override { return QSize(260, 26); } + void setPrecision(int precision) override; protected slots: @@ -565,6 +566,8 @@ public: QSize getPreferredSize() override { return QSize(150, 20); } + int getValue() const; + protected slots: void onChange(const QString &str); }; diff --git a/toonz/sources/include/toonzqt/schematicgroupeditor.h b/toonz/sources/include/toonzqt/schematicgroupeditor.h index 5bf7ee3f..b5a18341 100644 --- a/toonz/sources/include/toonzqt/schematicgroupeditor.h +++ b/toonz/sources/include/toonzqt/schematicgroupeditor.h @@ -34,6 +34,7 @@ class SchematicName; class DVAPI SchematicWindowEditor : public QObject, public QGraphicsItem { Q_OBJECT + Q_INTERFACES(QGraphicsItem) QPointF m_lastPos; @@ -52,7 +53,7 @@ public: QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, - QWidget *widget = 0) override; + QWidget *widget = 0) override; virtual QRectF boundingSceneRect() const = 0; virtual void setGroupedNodeZValue(int zValue) = 0; bool contains(SchematicNode *node) const { diff --git a/toonz/sources/include/toonzqt/schematicnode.h b/toonz/sources/include/toonzqt/schematicnode.h index bc85196b..b8f2984d 100644 --- a/toonz/sources/include/toonzqt/schematicnode.h +++ b/toonz/sources/include/toonzqt/schematicnode.h @@ -35,7 +35,7 @@ public: bool eventFilter(QObject *object, QEvent *event) override; - void setName(const QString &name); // Act as default name + void setName(const QString &name); // Act as default name void acceptName(const QString &name); protected: @@ -236,6 +236,8 @@ protected: */ class SchematicLink : public QObject, public QGraphicsItem { Q_OBJECT + Q_INTERFACES(QGraphicsItem) + SchematicPort *m_startPort, *m_endPort; QPainterPath m_path, m_hitPath; bool m_lineShaped; @@ -475,4 +477,23 @@ signals: void nodeChangedSize(); }; +//======================================================== +// +// class SnapTargetItem +// +//======================================================== + +class SnapTargetItem : public QGraphicsItem { + QRectF m_rect; + QPointF m_theOtherEndPos, m_portEndOffset; + +public: + SnapTargetItem(const QPointF &pos, const QRectF &rect, + const QPointF &theOtherEndPos, const QPointF &portEndOffset); + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget = 0) override; +}; + #endif // SCHEMATICNODE_H diff --git a/toonz/sources/include/toonzqt/schematicviewer.h b/toonz/sources/include/toonzqt/schematicviewer.h index 9a52e40e..2a2d6884 100644 --- a/toonz/sources/include/toonzqt/schematicviewer.h +++ b/toonz/sources/include/toonzqt/schematicviewer.h @@ -57,6 +57,7 @@ class QTouchEvent; class QGestureEvent; class FxSelection; class StageObjectSelection; +class SnapTargetItem; //==================================================== namespace { @@ -72,6 +73,8 @@ enum CursorMode { Select, Zoom, Hand }; class DVAPI SchematicScene : public QGraphicsScene { Q_OBJECT + QPointF m_mousePos, m_clickedPos; + public: SchematicScene(QWidget *parent); ~SchematicScene(); @@ -82,9 +85,20 @@ public: virtual void reorderScene() = 0; virtual void updateScene() = 0; + QPointF mousePos() { return m_mousePos; } + void setMousePos(QPointF pos) { m_mousePos = pos; } + virtual void updateSnapTarget(QGraphicsItem *item){}; + QPointF clickedPos() { return m_clickedPos; } + void setClickedPos(QPointF pos) { m_clickedPos = pos; } + void computeSnap(SchematicNode *node, QPointF &delta, bool enable); + protected: QList m_highlightedLinks; enum GridDimension { eLarge, eSmall }; + QList m_snapTargets; + + static int snapVInterval; + static int snapHSpacing; protected: //! Returns \b true if no nodes intersects \b rect. @@ -97,6 +111,9 @@ protected: void showEvent(QShowEvent *se); void hideEvent(QHideEvent *se); + void addSnapTarget(const QPointF &pos, const QRectF &rect, + const QPointF &theOtherEndPos, const QPointF &endPos); + void clearSnapTargets(); protected slots: virtual void onSelectionSwitched(TSelection *, TSelection *) {} diff --git a/toonz/sources/include/toonzqt/spreadsheetviewer.h b/toonz/sources/include/toonzqt/spreadsheetviewer.h index 72458816..04902a22 100644 --- a/toonz/sources/include/toonzqt/spreadsheetviewer.h +++ b/toonz/sources/include/toonzqt/spreadsheetviewer.h @@ -144,11 +144,7 @@ class DVAPI ScrollArea final : public QScrollArea { Q_OBJECT public: -#if QT_VERSION >= 0x050500 - ScrollArea(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - ScrollArea(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + ScrollArea(QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags()); virtual ~ScrollArea(); protected: @@ -238,15 +234,22 @@ class DVAPI SpreadsheetViewer : public QDialog { Q_PROPERTY( QColor LightLineColor READ getLightLineColor WRITE setLightLineColor) - QColor m_currentRowBgColor; // current frame, column - QColor m_markerLineColor; // marker interval (0, 255, 246) - QColor m_textColor; // text (black) - QColor m_verticalLineColor; // vertical line (black) + QColor m_currentRowBgColor; // current frame, column + QColor m_markerLineColor; // marker interval (0, 255, 246) + QColor m_secMarkerLineColor; // second marker lines + QColor m_textColor; // text (black) + QColor m_currentRowTextColor; // text color for the current row + QColor m_verticalLineColor; // vertical line (black) + Q_PROPERTY(QColor CurrentRowBgColor READ getCurrentRowBgColor WRITE setCurrentRowBgColor) Q_PROPERTY( QColor MarkerLineColor READ getMarkerLineColor WRITE setMarkerLineColor) + Q_PROPERTY(QColor SecMarkerLineColor READ getSecMarkerLineColor WRITE + setSecMarkerLineColor) Q_PROPERTY(QColor TextColor READ getTextColor WRITE setTextColor) + Q_PROPERTY(QColor CurrentRowTextColor READ getCurrentRowTextColor WRITE + setCurrentRowTextColor) Q_PROPERTY(QColor VerticalLineColor READ getVerticalLineColor WRITE setVerticalLineColor) @@ -355,8 +358,16 @@ public: QColor getCurrentRowBgColor() const { return m_currentRowBgColor; } void setMarkerLineColor(const QColor &color) { m_markerLineColor = color; } QColor getMarkerLineColor() const { return m_markerLineColor; } + void setSecMarkerLineColor(const QColor &color) { + m_secMarkerLineColor = color; + } + QColor getSecMarkerLineColor() const { return m_secMarkerLineColor; } void setTextColor(const QColor &color) { m_textColor = color; } QColor getTextColor() const { return m_textColor; } + void setCurrentRowTextColor(const QColor &color) { + m_currentRowTextColor = color; + } + QColor getCurrentRowTextColor() const { return m_currentRowTextColor; } void setVerticalLineColor(const QColor &color) { m_verticalLineColor = color; } @@ -473,6 +484,7 @@ public: return m_markSecRowDistance > 0 && ((row - m_markRowOffset) % m_markSecRowDistance) == 0 && row > 0; } + bool isSecMarkerActive() const { return m_markSecRowDistance > 0; } void setFrameHandle(TFrameHandle *frameHandle); TFrameHandle *getFrameHandle() const { return m_frameHandle; } diff --git a/toonz/sources/include/toonzqt/stageschematicscene.h b/toonz/sources/include/toonzqt/stageschematicscene.h index 19c5eb07..37249f32 100644 --- a/toonz/sources/include/toonzqt/stageschematicscene.h +++ b/toonz/sources/include/toonzqt/stageschematicscene.h @@ -184,6 +184,9 @@ private: void updateSplinePositionOnResize(TStageObjectSpline *spl, bool maximizedNode); + // snap to neighbor nodes on dragging + void updateSnapTarget(QGraphicsItem *item) override; + protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent *cme) override; void mousePressEvent(QGraphicsSceneMouseEvent *me) override; diff --git a/toonz/sources/include/toonzqt/styleeditor.h b/toonz/sources/include/toonzqt/styleeditor.h index 943a9146..f169d1e4 100644 --- a/toonz/sources/include/toonzqt/styleeditor.h +++ b/toonz/sources/include/toonzqt/styleeditor.h @@ -553,6 +553,43 @@ protected: bool m_allowFavorite = false; bool m_external = false; + enum ChipType { + COMMONCHIP = 0, // Common chip + SOLIDCHIP = 1 // Solid/Nobrush chip + }; + + QColor m_commonChipBoxColor; + QColor m_solidChipBoxColor; + QColor m_selectedChipBoxColor; + QColor m_selectedChipBox2Color; + + Q_PROPERTY(QColor CommonChipBoxColor READ getCommonChipBoxColor WRITE + setCommonChipBoxColor) + Q_PROPERTY(QColor SolidChipBoxColor READ getSolidChipBoxColor WRITE + setSolidChipBoxColor) + Q_PROPERTY(QColor SelectedChipBoxColor READ getSelectedChipBoxColor WRITE + setSelectedChipBoxColor) + Q_PROPERTY(QColor SelectedChipBox2Color READ getSelectedChipBox2Color WRITE + setSelectedChipBox2Color) + + QColor getCommonChipBoxColor() const { return m_commonChipBoxColor; } + QColor getSolidChipBoxColor() const { return m_solidChipBoxColor; } + QColor getSelectedChipBoxColor() const { return m_selectedChipBoxColor; } + QColor getSelectedChipBox2Color() const { return m_selectedChipBox2Color; } + + void setSolidChipBoxColor(const QColor &color) { + m_solidChipBoxColor = color; + } + void setCommonChipBoxColor(const QColor &color) { + m_commonChipBoxColor = color; + } + void setSelectedChipBoxColor(const QColor &color) { + m_selectedChipBoxColor = color; + } + void setSelectedChipBox2Color(const QColor &color) { + m_selectedChipBox2Color = color; + } + public: StyleChooserPage(TFilePath styleFolder, QWidget *parent = 0); @@ -586,7 +623,11 @@ public: virtual bool isLoading() { return false; } virtual int getChipCount() const = 0; - virtual void drawChip(QPainter &p, QRect rect, int index) = 0; + virtual int drawChip(QPainter &p, QRect rect, int index) = 0; + + virtual void applyFilter(){}; + virtual void applyFilter(const QString text){}; + virtual void onSelect(int index) {} virtual void removeSelectedStylesFromSet(std::vector selection){}; @@ -621,13 +662,15 @@ protected: void resizeEvent(QResizeEvent *) override { computeSize(); } void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override {} + void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override; void enterEvent(QEvent *event) override; -protected slots: +public slots: void computeSize(); + +protected slots: void onTogglePage(bool toggled); void onRemoveStyleFromSet(); void onEmptySet(); @@ -641,6 +684,7 @@ protected slots: void onReloadStyleSet(); void onRenameStyleSet(); void onLabelContextMenu(const QPoint &pos); + signals: void styleSelected(const TColorStyle &style); void refreshFavorites(); @@ -828,8 +872,19 @@ class DVAPI StyleEditor final : public QWidget, public SaveLoadQSettings { QAction *m_alphaAction; QAction *m_rgbAction; QAction *m_hexAction; + QAction *m_searchAction; QAction *m_hexEditorAction; + QFrame *m_textureSearchFrame; + QFrame *m_vectorsSearchFrame; + QFrame *m_mypaintSearchFrame; + QLineEdit *m_textureSearchText; + QLineEdit *m_vectorsSearchText; + QLineEdit *m_mypaintSearchText; + QPushButton *m_textureSearchClear; + QPushButton *m_vectorsSearchClear; + QPushButton *m_mypaintSearchClear; + TColorStyleP m_oldStyle; //!< A copy of current style \a before the last change. TColorStyleP m_editedStyle; //!< The currently edited style. Please observe @@ -1005,11 +1060,22 @@ protected slots: void onHexChanged(); void onHexEditor(); + void onSearchVisible(bool on); + void onHexEdited(const QString &text); void onHideMenu(); void onPageChanged(int index); void onToggleAutoApply(); + void onTextureSearch(const QString &); + void onTextureClearSearch(); + + void onVectorsSearch(const QString &); + void onVectorsClearSearch(); + + void onMyPaintSearch(const QString &); + void onMyPaintClearSearch(); + void onToggleTextureSet(int checkedState); void onToggleVectorSet(int checkedState); void onToggleRasterSet(int checkedState); diff --git a/toonz/sources/include/toonzqt/swatchviewer.h b/toonz/sources/include/toonzqt/swatchviewer.h index 2f725993..5e406e33 100644 --- a/toonz/sources/include/toonzqt/swatchviewer.h +++ b/toonz/sources/include/toonzqt/swatchviewer.h @@ -175,11 +175,7 @@ public: void onCanceled(TThread::RunnableP task) override; }; -#if QT_VERSION >= 0x050500 - SwatchViewer(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - SwatchViewer(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + SwatchViewer(QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags()); ~SwatchViewer(); static void suspendRendering(bool suspend, bool blocking = true); diff --git a/toonz/sources/include/toutputproperties.h b/toonz/sources/include/toutputproperties.h index 5647a113..0002bec6 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 1cfd3a4c..66751ced 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 8f399859..88d57230 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/tparamuiconcept.h b/toonz/sources/include/tparamuiconcept.h index 167008ea..8a929437 100644 --- a/toonz/sources/include/tparamuiconcept.h +++ b/toonz/sources/include/tparamuiconcept.h @@ -72,6 +72,10 @@ public: ELLIPSE, // used in spin blur ino and radial blur ino + VERTICAL_POS, // A horizontal line at given height + // { [TDoubleParamP] } + PARALLELOGRAM, + TYPESCOUNT }; diff --git a/toonz/sources/include/tpixel.h b/toonz/sources/include/tpixel.h index 0b8fe5b4..45a67a11 100644 --- a/toonz/sources/include/tpixel.h +++ b/toonz/sources/include/tpixel.h @@ -41,7 +41,7 @@ class TPixelGR16; Note that channel ordering is platform depending. */ class DVAPI DV_ALIGNED(4) TPixelRGBM32 { - TPixelRGBM32(TUINT32 mask) { *(TUINT32 *)this = mask; }; + TPixelRGBM32(TUINT32 mask) : TPixelRGBM32() { *(TUINT32 *)this = mask; }; public: static const int maxChannelValue; @@ -82,7 +82,7 @@ public: : r(rr), g(gg), b(bb), m(mm){}; // Copy constructor and operator= - TPixelRGBM32(const TPixelRGBM32 &pix) { + TPixelRGBM32(const TPixelRGBM32 &pix) : TPixelRGBM32() { *(TUINT32 *)this = *(const TUINT32 *)&pix; } @@ -195,7 +195,7 @@ undefined machine order !!!! #endif // Copy constructor and operator= - TPixelRGBM64(const TPixelRGBM64 &pix) { + TPixelRGBM64(const TPixelRGBM64 &pix) : TPixelRGBM64() { *(TUINT64 *)this = *(const TUINT64 *)&pix; } @@ -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){}; @@ -318,6 +317,68 @@ static inline TPixelD from(const TPixelD &pix) {return pix;}; static const TPixelD Transparent; }; +//----------------------------------------------------------------------------- +// 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 { diff --git a/toonz/sources/include/tpixelgr.h b/toonz/sources/include/tpixelgr.h index 23066cfc..720d219d 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 6ffd0be1..5382e36e 100644 --- a/toonz/sources/include/tpixelutils.h +++ b/toonz/sources/include/tpixelutils.h @@ -29,6 +29,14 @@ 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((float)(1. - t) * a.r + (float)t * b.r, + (float)(1. - t) * a.g + (float)t * b.g, + (float)(1. - t) * a.b + (float)t * b.b, + (float)(1. - t) * a.m + (float)t * b.m); +} + //----------------------------------------------------------------------------- /*! this template function computes a linear interpolation between @@ -76,6 +84,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 +131,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 +154,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 +255,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 +295,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 +334,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) { @@ -346,25 +421,36 @@ inline void premult(TPixel32 &pix) { } inline void premult(TPixel64 &pix) { - pix.r = pix.r * pix.m / 65535.0; - pix.g = pix.g * pix.m / 65535.0; - pix.b = pix.b * pix.m / 65535.0; + pix.r = (typename TPixel64::Channel)((double)pix.r * (double)pix.m / 65535.0); + pix.g = (typename TPixel64::Channel)((double)pix.g * (double)pix.m / 65535.0); + pix.b = (typename TPixel64::Channel)((double)pix.b * (double)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); - pix.g = std::min(pix.g * fac, 255.0f); - pix.b = std::min(pix.b * fac, 255.0f); + float fac = 255.f / (float)pix.m; + pix.r = (typename TPixel32::Channel)(std::min((float)pix.r * fac, 255.f)); + pix.g = (typename TPixel32::Channel)(std::min((float)pix.g * fac, 255.f)); + pix.b = (typename TPixel32::Channel)(std::min((float)pix.b * fac, 255.f)); } inline void depremult(TPixel64 &pix) { - double fac = 65535.0 / pix.m; - pix.r = std::min(pix.r * fac, 65535.0); - pix.g = std::min(pix.g * fac, 65535.0); - pix.b = std::min(pix.b * fac, 65535.0); + double fac = 65535. / (double)pix.m; + pix.r = (typename TPixel64::Channel)(std::min((double)pix.r * fac, 65535.)); + pix.g = (typename TPixel64::Channel)(std::min((double)pix.g * fac, 65535.)); + pix.b = (typename TPixel64::Channel)(std::min((double)pix.b * fac, 65535.)); } +inline void depremult(TPixelF &pix) { + pix.r = pix.r / pix.m; + pix.g = pix.g / pix.m; + pix.b = pix.b / pix.m; +} //----------------------------------------------------------------------------- template @@ -385,18 +471,31 @@ inline TPixel32 premultiply(const TPixel32 &pix) { } inline TPixel64 premultiply(const TPixel64 &pix) { - return TPixel64(pix.r * pix.m / 65535.0, pix.g * pix.m / 65535.0, - pix.b * pix.m / 65535.0, pix.m); + return TPixel64((int)((double)pix.r * (double)pix.m / 65535.), + (int)((double)pix.g * (double)pix.m / 65535.), + (int)((double)pix.b * (double)pix.m / 65535.), 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); + return TPixel32((int)((double)pix.r * 255. / (double)pix.m), + (int)((double)pix.g * 255. / (double)pix.m), + (int)((double)pix.b * 255. / (double)pix.m), pix.m); } inline TPixel64 depremultiply(const TPixel64 &pix) { - return TPixel64(pix.r * 65535.0 / pix.m, pix.g * 65535.0 / pix.m, - pix.b * 65535.0 / pix.m, pix.m); + return TPixel64((int)((double)pix.r * 65535. / (double)pix.m), + (int)((double)pix.g * 65535. / (double)pix.m), + (int)((double)pix.b * 65535. / (double)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); } //----------------------------------------------------------------------------- @@ -443,15 +542,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 +579,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 +589,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 +599,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 +609,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 378fc724..ba8e8648 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 8542bb04..2a6125a4 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 @@ -151,9 +154,9 @@ public: // Raster levels. Currently used only in Tile Fx Iwa. (see iwa_tilefx.cpp) bool m_getFullSizeBBox; - /*-- カメラサイズ --*/ + /*-- camera size --*/ TRectD m_cameraBox; - /*-- 途中でPreview計算がキャンセルされたときのフラグ --*/ + /*-- Flag when Preview calculation is canceled during the process --*/ int *m_isCanceled; // pointer to QOffscreenSurface which is created on @@ -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 d9d71cc6..ecc90542 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); + } // TRop namespace diff --git a/toonz/sources/include/tsimplecolorstyles.h b/toonz/sources/include/tsimplecolorstyles.h index 1cf5fe51..c637d6ff 100644 --- a/toonz/sources/include/tsimplecolorstyles.h +++ b/toonz/sources/include/tsimplecolorstyles.h @@ -156,6 +156,8 @@ public: TColorStyle *clone() const override; QString getDescription() const override; + std::string getBrushIdName() const override; + static std::size_t staticBrushIdHash(); bool hasMainColor() const override { return true; } TPixel32 getMainColor() const override { return m_color; } @@ -203,6 +205,7 @@ public: TColorStyle *clone() const override; QString getDescription() const override; + std::string getBrushIdName() const override; TPixel32 getColor() const { return m_color; } USHORT getStipple() const { return m_stipple; } @@ -269,10 +272,10 @@ public: void invalidate(){}; TColorStyle *clone() const override; + TColorStyle *clone(std::string brushIdName) const override; - QString getDescription() const override { - return "TRasterImagePatternStrokeStyle"; - } + QString getDescription() const override; + std::string getBrushIdName() const override; bool hasMainColor() const override { return false; } TPixel32 getMainColor() const override { return TPixel32::Black; } @@ -301,6 +304,8 @@ public: double getParamValue(TColorStyle::double_tag, int index) const override; void setParamValue(int index, double value) override; + TRectD getStrokeBBox(const TStroke *stroke) const override; + protected: void makeIcon(const TDimension &d) override; @@ -348,10 +353,10 @@ public: void invalidate(){}; TColorStyle *clone() const override; + TColorStyle *clone(std::string brushIdName) const override; - QString getDescription() const override { - return "TVectorImagePatternStrokeStyle"; - } + QString getDescription() const override; + std::string getBrushIdName() const override; bool hasMainColor() const override { return false; } TPixel32 getMainColor() const override { return TPixel32::Black; } @@ -382,6 +387,8 @@ public: static void clearGlDisplayLists(); + TRectD getStrokeBBox(const TStroke *stroke) const override; + protected: void makeIcon(const TDimension &d) override; diff --git a/toonz/sources/include/tsound.h b/toonz/sources/include/tsound.h index 4cd6c7c1..225646ef 100644 --- a/toonz/sources/include/tsound.h +++ b/toonz/sources/include/tsound.h @@ -19,10 +19,6 @@ #define DVVAR DV_IMPORT_VAR #endif -#ifndef _WIN32 -#define WAVE_FORMAT_PCM 1 -#endif - //========================================================= namespace TSound { @@ -31,7 +27,12 @@ typedef UCHAR Channel; const int MONO = 0; const int LEFT = 0; const int RIGHT = LEFT + 1; -} + +const int WMASK = 7; // Mask for wFormat +const int INT = 1; // WAVE_FORMAT_PCM +const int UINT = 9; // WAVE_FORMAT_PCM (Unsigned 8-Bits) +const int FLOAT = 3; // WAVE_FORMAT_IEEE_FLOAT +} // namespace TSound //========================================================= @@ -56,17 +57,14 @@ public: TUINT32 m_sampleRate; // frequenza di campionamento int m_bitPerSample; // numero di bit per campione int m_channelCount; // numero di canali - bool m_signedSample; - int m_formatType; + int m_sampleType; // integer or float samples TSoundTrackFormat(TUINT32 sampleRate = 0, int bitPerSample = 0, - int channelCount = 0, bool signedSample = true, - int formatType = WAVE_FORMAT_PCM) + int channelCount = 0, int sampleType = TSound::INT) : m_sampleRate(sampleRate) , m_bitPerSample(bitPerSample) , m_channelCount(channelCount) - , m_signedSample(signedSample) - , m_formatType(formatType) {} + , m_sampleType(sampleType) {} ~TSoundTrackFormat() {} @@ -91,7 +89,7 @@ protected: int m_bitPerSample; // numero di bit per campione TINT32 m_sampleCount; // numero di campioni int m_channelCount; // numero di canali - int m_formatType; + int m_sampleType; // integer or float samples TSoundTrack *m_parent; // nel caso di sotto-traccie @@ -101,12 +99,11 @@ protected: TSoundTrack(); TSoundTrack(TUINT32 sampleRate, int bitPerSample, int channelCount, - int sampleSize, TINT32 sampleCount, bool isSampleSigned, - int formatType); + int sampleSize, TINT32 sampleCount, int sampleType); TSoundTrack(TUINT32 sampleRate, int bitPerSample, int channelCount, - int sampleSize, TINT32 sampleCount, UCHAR *buffer, - TSoundTrack *parent, int formatType); + int sampleSize, TINT32 sampleCount, int sampleType, UCHAR *buffer, + TSoundTrack *parent); public: /*! @@ -116,13 +113,11 @@ signedSample must be true for tracks whose samples are signed */ static TSoundTrackP create(TUINT32 sampleRate, int bitPerSample, int channelCount, TINT32 sampleCount, - bool signedSample = true, - int formatType = WAVE_FORMAT_PCM); + int sampleFormat); static TSoundTrackP create(TUINT32 sampleRate, int bitPerSample, - int channelCount, TINT32 sampleCount, void *buffer, - bool signedSample = true, - int formatType = WAVE_FORMAT_PCM); + int channelCount, TINT32 sampleCount, + int sampleFormat, void *buffer); /*! Create a new soundtrack according to the format and number of samples @@ -147,7 +142,6 @@ specified as inputs int getChannelCount() const { return m_channelCount; } int getBitPerSample() const { return m_bitPerSample; } TINT32 getSampleCount() const { return m_sampleCount; } - int getFormatType() const { return m_formatType; } //! Returns the duration of the soundtrack in seconds. double getDuration() const; @@ -155,6 +149,9 @@ specified as inputs //! Returns true if the samples of the soundtrack are signed, false otherwise virtual bool isSampleSigned() const = 0; + //! Returns sample format + virtual int getSampleType() const = 0; + //! Returns the soundtrack format TSoundTrackFormat getFormat() const; @@ -309,7 +306,7 @@ Returns true if on the machine there is an audio card installed correctly //! Returns the best format supported near the given parameters TSoundTrackFormat getPreferredFormat(TUINT32 sampleRate, int channelCount, - int bitPerSample, int formatType); + int bitPerSample, int sampleType); //! Returns the best format supported near the given format TSoundTrackFormat getPreferredFormat(const TSoundTrackFormat &); @@ -378,7 +375,7 @@ Returns true if on the machine there is an audio card installed correctly //! Returns the best format supported near the given parameters TSoundTrackFormat getPreferredFormat(TUINT32 sampleRate, int channelCount, - int bitPerSample, int formatType); + int bitPerSample, int sampleType); //! Returns the best format supported near the given format TSoundTrackFormat getPreferredFormat(const TSoundTrackFormat &); diff --git a/toonz/sources/include/tsound_t.h b/toonz/sources/include/tsound_t.h index 72ad6cf2..a35b5f69 100644 --- a/toonz/sources/include/tsound_t.h +++ b/toonz/sources/include/tsound_t.h @@ -24,20 +24,18 @@ public: //---------------------------------------------------------------------------- - TSoundTrackT(TUINT32 sampleRate, int channelCount, TINT32 sampleCount, - int formatType = WAVE_FORMAT_PCM) + TSoundTrackT(TUINT32 sampleRate, int channelCount, TINT32 sampleCount) : TSoundTrack(sampleRate, T::getBitPerSample(), channelCount, sizeof(T), - sampleCount, T::isSampleSigned(), formatType) {} + sampleCount, T::getSampleType()) {} //---------------------------------------------------------------------------- TSoundTrackT(TUINT32 sampleRate, int channelCount, TINT32 sampleCount, - T *buffer, TSoundTrackT *parent, - int formatType = WAVE_FORMAT_PCM) + T *buffer, TSoundTrackT *parent) : TSoundTrack(sampleRate, T::getBitPerSample(), channelCount, sizeof(T), - sampleCount, reinterpret_cast(buffer), parent, - formatType) {} + sampleCount, T::getSampleType(), + reinterpret_cast(buffer), parent) {} //---------------------------------------------------------------------------- @@ -45,7 +43,13 @@ public: //---------------------------------------------------------------------------- - bool isSampleSigned() const override { return T::isSampleSigned(); } + bool isSampleSigned() const override { + return T::getSampleType() != TSound::UINT; + } + + //---------------------------------------------------------------------------- + + int getSampleType() const override { return T::getSampleType(); } //---------------------------------------------------------------------------- @@ -80,10 +84,9 @@ public: ss0 = tcrop(s0, (TINT32)0, getSampleCount() - 1); ss1 = tcrop(s1, (TINT32)0, getSampleCount() - 1); - return TSoundTrackP( - new TSoundTrackT(getSampleRate(), getChannelCount(), ss1 - ss0 + 1, - (T *)(m_buffer + (long)(ss0 * getSampleSize())), - this, getFormatType())); + return TSoundTrackP(new TSoundTrackT( + getSampleRate(), getChannelCount(), ss1 - ss0 + 1, + (T *)(m_buffer + (long)(ss0 * getSampleSize())), this)); } //---------------------------------------------------------------------------- @@ -98,8 +101,8 @@ from which it's created. It has no reference to the object. return clone(); else { typedef typename T::ChannelSampleType TCST; - TSoundTrackT *dst = new TSoundTrackT( - m_sampleRate, 1, getSampleCount(), getFormatType()); + TSoundTrackT *dst = + new TSoundTrackT(m_sampleRate, 1, getSampleCount()); const T *sample = samples(); const T *endSample = sample + getSampleCount(); @@ -357,8 +360,8 @@ template class DVAPI TSoundTrackT; template class DVAPI TSoundTrackT; template class DVAPI TSoundTrackT; template class DVAPI TSoundTrackT; -template class DVAPI TSoundTrackT; -template class DVAPI TSoundTrackT; +template class DVAPI TSoundTrackT; +template class DVAPI TSoundTrackT; #endif typedef TSoundTrackT TSoundTrackMono8Signed; @@ -371,8 +374,8 @@ typedef TSoundTrackT TSoundTrackMono24; typedef TSoundTrackT TSoundTrackStereo24; typedef TSoundTrackT TSoundTrackMono32; typedef TSoundTrackT TSoundTrackStereo32; -typedef TSoundTrackT TSoundTrackMono32float; -typedef TSoundTrackT TSoundTrackStereo32float; +typedef TSoundTrackT TSoundTrackMono32Float; +typedef TSoundTrackT TSoundTrackStereo32Float; //============================================================================== @@ -393,8 +396,8 @@ public: virtual TSoundTrackP compute(const TSoundTrackStereo24 &) { return 0; }; virtual TSoundTrackP compute(const TSoundTrackMono32 &) { return 0; }; virtual TSoundTrackP compute(const TSoundTrackStereo32 &) { return 0; }; - virtual TSoundTrackP compute(const TSoundTrackMono32float &) { return 0; }; - virtual TSoundTrackP compute(const TSoundTrackStereo32float &) { return 0; }; + virtual TSoundTrackP compute(const TSoundTrackMono32Float &) { return 0; }; + virtual TSoundTrackP compute(const TSoundTrackStereo32Float &) { return 0; }; }; //============================================================================== diff --git a/toonz/sources/include/tsoundsample.h b/toonz/sources/include/tsoundsample.h index c02863b4..2c3ba7ca 100644 --- a/toonz/sources/include/tsoundsample.h +++ b/toonz/sources/include/tsoundsample.h @@ -26,8 +26,8 @@ class TMono24Sample; class TStereo24Sample; class TMono32Sample; class TStereo32Sample; -class TMono32floatSample; -class TStereo32floatSample; +class TMono32FloatSample; +class TStereo32FloatSample; //============================================================================== @@ -43,6 +43,7 @@ public: static bool isSampleSigned() { return true; } static int getBitPerSample() { return 8; } + static int getSampleType() { return TSound::INT; } inline SCHAR getValue(TSound::Channel /*chan*/) const { return value; } @@ -82,8 +83,8 @@ public: static inline TMono8SignedSample from(const TStereo24Sample &sample); static inline TMono8SignedSample from(const TMono32Sample &sample); static inline TMono8SignedSample from(const TStereo32Sample &sample); - static inline TMono8SignedSample from(const TMono32floatSample &sample); - static inline TMono8SignedSample from(const TStereo32floatSample &sample); + static inline TMono8SignedSample from(const TMono32FloatSample &sample); + static inline TMono8SignedSample from(const TStereo32FloatSample &sample); }; inline TMono8SignedSample operator+(const TMono8SignedSample &s1, @@ -106,6 +107,7 @@ public: static bool isSampleSigned() { return false; } static int getBitPerSample() { return 8; } + static int getSampleType() { return TSound::UINT; } inline UCHAR getValue(TSound::Channel /*chan*/) const { return value; } @@ -146,8 +148,8 @@ public: static inline TMono8UnsignedSample from(const TStereo24Sample &sample); static inline TMono8UnsignedSample from(const TMono32Sample &sample); static inline TMono8UnsignedSample from(const TStereo32Sample &sample); - static inline TMono8UnsignedSample from(const TMono32floatSample &sample); - static inline TMono8UnsignedSample from(const TStereo32floatSample &sample); + static inline TMono8UnsignedSample from(const TMono32FloatSample &sample); + static inline TMono8UnsignedSample from(const TStereo32FloatSample &sample); }; inline TMono8UnsignedSample operator+(const TMono8UnsignedSample &s1, @@ -179,6 +181,7 @@ public: static bool isSampleSigned() { return true; } static int getBitPerSample() { return 8; } + static int getSampleType() { return TSound::INT; } inline SCHAR getValue(TSound::Channel chan) const { assert(chan <= 1); @@ -231,8 +234,8 @@ public: static inline TStereo8SignedSample from(const TStereo24Sample &sample); static inline TStereo8SignedSample from(const TMono32Sample &sample); static inline TStereo8SignedSample from(const TStereo32Sample &sample); - static inline TStereo8SignedSample from(const TMono32floatSample &sample); - static inline TStereo8SignedSample from(const TStereo32floatSample &sample); + static inline TStereo8SignedSample from(const TMono32FloatSample &sample); + static inline TStereo8SignedSample from(const TStereo32FloatSample &sample); }; inline TStereo8SignedSample operator+(const TStereo8SignedSample &s1, @@ -264,6 +267,7 @@ public: static bool isSampleSigned() { return false; } static int getBitPerSample() { return 8; } + static int getSampleType() { return TSound::UINT; } inline UCHAR getValue(TSound::Channel chan) const { assert(chan <= 1); @@ -321,8 +325,8 @@ public: static inline TStereo8UnsignedSample from(const TStereo24Sample &sample); static inline TStereo8UnsignedSample from(const TMono32Sample &sample); static inline TStereo8UnsignedSample from(const TStereo32Sample &sample); - static inline TStereo8UnsignedSample from(const TMono32floatSample &sample); - static inline TStereo8UnsignedSample from(const TStereo32floatSample &sample); + static inline TStereo8UnsignedSample from(const TMono32FloatSample &sample); + static inline TStereo8UnsignedSample from(const TStereo32FloatSample &sample); }; inline TStereo8UnsignedSample operator+(const TStereo8UnsignedSample &s1, @@ -345,6 +349,7 @@ public: static bool isSampleSigned() { return true; } static int getBitPerSample() { return 16; } + static int getSampleType() { return TSound::INT; } inline short getValue(TSound::Channel) const { return value; } @@ -384,8 +389,8 @@ public: static inline TMono16Sample from(const TStereo24Sample &sample); static inline TMono16Sample from(const TMono32Sample &sample); static inline TMono16Sample from(const TStereo32Sample &sample); - static inline TMono16Sample from(const TMono32floatSample &sample); - static inline TMono16Sample from(const TStereo32floatSample &sample); + static inline TMono16Sample from(const TMono32FloatSample &sample); + static inline TMono16Sample from(const TStereo32FloatSample &sample); }; inline TMono16Sample operator+(const TMono16Sample &s1, @@ -417,6 +422,7 @@ public: static bool isSampleSigned() { return true; } static int getBitPerSample() { return 16; } + static int getSampleType() { return TSound::INT; } inline short getValue(TSound::Channel chan) const { assert(chan <= 1); @@ -469,8 +475,8 @@ public: static inline TStereo16Sample from(const TStereo24Sample &sample); static inline TStereo16Sample from(const TMono32Sample &sample); static inline TStereo16Sample from(const TStereo32Sample &sample); - static inline TStereo16Sample from(const TMono32floatSample &sample); - static inline TStereo16Sample from(const TStereo32floatSample &sample); + static inline TStereo16Sample from(const TMono32FloatSample &sample); + static inline TStereo16Sample from(const TStereo32FloatSample &sample); }; inline TStereo16Sample operator+(const TStereo16Sample &s1, @@ -482,38 +488,42 @@ inline TStereo16Sample operator+(const TStereo16Sample &s1, //========================================================= class DVAPI TMono24Sample { - TINT32 value; + UCHAR byte[3]; // LSB first public: typedef TINT32 ChannelValueType; typedef TMono24Sample ChannelSampleType; - TMono24Sample(TINT32 v = 0) - : value(tcrop(v, -2147483648, 2147483647)) {} + TMono24Sample(TINT32 v = 0) { setValue(0, v); } ~TMono24Sample(){}; static bool isSampleSigned() { return true; } - static int getBitPerSample() { return 32; } // We converted on import + static int getBitPerSample() { return 24; } + static int getSampleType() { return TSound::INT; } - inline TINT32 getValue(TSound::Channel) const { return value; } + inline TINT32 getValue(TSound::Channel) const { + return byte[0] | (byte[1] << 8) | (byte[2] << 16) | + (byte[2] & 0x80 ? ~0xFFFFFF : 0); + } inline void setValue(TSound::Channel /*chan*/, TINT32 v) { - value = tcrop(v, -2147483648, 2147483647); + int iVal = tcrop(v, -8388608, 8388607); + byte[0] = iVal; + byte[1] = iVal >> 8; + byte[2] = iVal >> 16; } inline double getPressure(TSound::Channel chan) const { - return (getValue(chan) << 8); // Drop back to 24bit + return (getValue(chan)); } inline TMono24Sample &operator+=(const TMono24Sample &s) { - int iVal = value + s.value; - value = tcrop(iVal, -2147483648, 2147483647); + setValue(0, getValue(0) + s.getValue(0)); return *this; } inline TMono24Sample &operator*=(double a) { - int iVal = (int)(value * a); - value = tcrop(iVal, -2147483648, 2147483647); + setValue(0, getValue(0) * a); return *this; } @@ -521,8 +531,8 @@ public: static TMono24Sample mix(const TMono24Sample &s1, double a1, const TMono24Sample &s2, double a2) { - return TMono24Sample(tcrop((int)(s1.value * a1 + s2.value * a2), - -2147483648, 2147483647)); + int iVal = s1.getValue(0) * a1 + s2.getValue(0) * a2; + return TMono24Sample(iVal); } static inline TMono24Sample from(const TMono8UnsignedSample &sample); @@ -535,8 +545,8 @@ public: static inline TMono24Sample from(const TStereo24Sample &sample); static inline TMono24Sample from(const TMono32Sample &sample); static inline TMono24Sample from(const TStereo32Sample &sample); - static inline TMono24Sample from(const TMono32floatSample &sample); - static inline TMono24Sample from(const TStereo32floatSample &sample); + static inline TMono24Sample from(const TMono32FloatSample &sample); + static inline TMono24Sample from(const TStereo32FloatSample &sample); }; inline TMono24Sample operator+(const TMono24Sample &s1, @@ -548,49 +558,55 @@ inline TMono24Sample operator+(const TMono24Sample &s1, //========================================================= class DVAPI TStereo24Sample { - TINT32 channel[2]; // l'ordine dei canali e' left,right + typedef struct { + UCHAR byte[3]; // LSB first + } TSample; + TSample channel[2]; // l'ordine dei canali e' left,right public: typedef TINT32 ChannelValueType; typedef TMono24Sample ChannelSampleType; TStereo24Sample(TINT32 lchan = 0, TINT32 rchan = 0) { - channel[0] = tcrop(lchan, -2147483648, 2147483647); - channel[1] = tcrop(rchan, -2147483648, 2147483647); + setValue(0, lchan); + setValue(1, rchan); } ~TStereo24Sample(){}; static bool isSampleSigned() { return true; } - static int getBitPerSample() { return 32; } // We converted on import + static int getBitPerSample() { return 24; } + static int getSampleType() { return TSound::INT; } inline TINT32 getValue(TSound::Channel chan) const { assert(chan <= 1); - return channel[chan]; + const TSample &s = channel[chan]; + return s.byte[0] | (s.byte[1] << 8) | (s.byte[2] << 16) | + (s.byte[2] & 0x80 ? ~0xFFFFFF : 0); } inline void setValue(TSound::Channel chan, TINT32 v) { assert(chan <= 1); - channel[chan] = tcrop(v, -2147483648, 2147483647); + int iVal = tcrop(v, -8388608, 8388607); + TSample &s = channel[chan]; + s.byte[0] = iVal; + s.byte[1] = iVal >> 8; + s.byte[2] = iVal >> 16; } inline double getPressure(TSound::Channel chan) const { - return (getValue(chan) << 8); // Drop back to 24bit + return (getValue(chan)); } inline TStereo24Sample &operator+=(const TStereo24Sample &s) { - int iLeftVal = channel[0] + s.channel[0]; - int iRightVal = channel[1] + s.channel[1]; - channel[0] = tcrop(iLeftVal, -2147483648, 2147483647); - channel[1] = tcrop(iRightVal, -2147483648, 2147483647); + setValue(0, getValue(0) + s.getValue(0)); + setValue(1, getValue(1) + s.getValue(1)); return *this; } inline TStereo24Sample &operator*=(double a) { - int iLeftVal = (int)(a * channel[0]); - int iRightVal = (int)(a * channel[1]); - channel[0] = tcrop(iLeftVal, -2147483648, 2147483647); - channel[1] = tcrop(iRightVal, -2147483648, 2147483647); + setValue(0, getValue(0) * a); + setValue(1, getValue(1) * a); return *this; } @@ -600,11 +616,9 @@ public: static TStereo24Sample mix(const TStereo24Sample &s1, double a1, const TStereo24Sample &s2, double a2) { - return TStereo24Sample( - tcrop((int)(s1.channel[0] * a1 + s2.channel[0] * a2), - -2147483648, 2147483647), - tcrop((int)(s1.channel[1] * a1 + s2.channel[1] * a2), - -2147483648, 2147483647)); + int iValL = s1.getValue(0) * a1 + s2.getValue(0) * a2; + int iValR = s1.getValue(1) * a1 + s2.getValue(1) * a2; + return TStereo24Sample(iValL, iValR); } static inline TStereo24Sample from(const TMono8UnsignedSample &sample); @@ -617,8 +631,8 @@ public: static inline TStereo24Sample from(const TStereo24Sample &sample); static inline TStereo24Sample from(const TMono32Sample &sample); static inline TStereo24Sample from(const TStereo32Sample &sample); - static inline TStereo24Sample from(const TMono32floatSample &sample); - static inline TStereo24Sample from(const TStereo32floatSample &sample); + static inline TStereo24Sample from(const TMono32FloatSample &sample); + static inline TStereo24Sample from(const TStereo32FloatSample &sample); }; inline TStereo24Sample operator+(const TStereo24Sample &s1, @@ -636,18 +650,16 @@ public: typedef TINT32 ChannelValueType; typedef TMono32Sample ChannelSampleType; - TMono32Sample(TINT32 v = 0) - : value(tcrop(v, -2147483648, 2147483647)) {} + TMono32Sample(TINT32 v = 0) : value(v) {} ~TMono32Sample(){}; static bool isSampleSigned() { return true; } static int getBitPerSample() { return 32; } + static int getSampleType() { return TSound::INT; } inline TINT32 getValue(TSound::Channel) const { return value; } - inline void setValue(TSound::Channel /*chan*/, TINT32 v) { - value = tcrop(v, -2147483648, 2147483647); - } + inline void setValue(TSound::Channel /*chan*/, TINT32 v) { value = v; } inline double getPressure(TSound::Channel chan) const { return (getValue(chan)); @@ -683,8 +695,8 @@ public: static inline TMono32Sample from(const TStereo24Sample &sample); static inline TMono32Sample from(const TMono32Sample &sample); static inline TMono32Sample from(const TStereo32Sample &sample); - static inline TMono32Sample from(const TMono32floatSample &sample); - static inline TMono32Sample from(const TStereo32floatSample &sample); + static inline TMono32Sample from(const TMono32FloatSample &sample); + static inline TMono32Sample from(const TStereo32FloatSample &sample); }; inline TMono32Sample operator+(const TMono32Sample &s1, @@ -703,14 +715,15 @@ public: typedef TMono32Sample ChannelSampleType; TStereo32Sample(TINT32 lchan = 0, TINT32 rchan = 0) { - channel[0] = tcrop(lchan, -2147483648, 2147483647); - channel[1] = tcrop(rchan, -2147483648, 2147483647); + channel[0] = lchan; + channel[1] = rchan; } ~TStereo32Sample(){}; static bool isSampleSigned() { return true; } static int getBitPerSample() { return 32; } + static int getSampleType() { return TSound::INT; } inline TINT32 getValue(TSound::Channel chan) const { assert(chan <= 1); @@ -765,8 +778,8 @@ public: static inline TStereo32Sample from(const TStereo24Sample &sample); static inline TStereo32Sample from(const TMono32Sample &sample); static inline TStereo32Sample from(const TStereo32Sample &sample); - static inline TStereo32Sample from(const TMono32floatSample &sample); - static inline TStereo32Sample from(const TStereo32floatSample &sample); + static inline TStereo32Sample from(const TMono32FloatSample &sample); + static inline TStereo32Sample from(const TStereo32FloatSample &sample); }; inline TStereo32Sample operator+(const TStereo32Sample &s1, @@ -777,89 +790,86 @@ inline TStereo32Sample operator+(const TStereo32Sample &s1, //========================================================= -class DVAPI TMono32floatSample { +class DVAPI TMono32FloatSample { float value; public: typedef float ChannelValueType; - typedef TMono32floatSample ChannelSampleType; + typedef TMono32FloatSample ChannelSampleType; - TMono32floatSample(float v = 0.0) : value(tcrop(v, -1.0, 1.0)) {} - ~TMono32floatSample(){}; + TMono32FloatSample(float v = 0.0f) : value(v) {} + ~TMono32FloatSample(){}; static bool isSampleSigned() { return true; } static int getBitPerSample() { return 32; } + static int getSampleType() { return TSound::FLOAT; } inline float getValue(TSound::Channel) const { return value; } inline void setValue(TSound::Channel /*chan*/, float v) { - value = tcrop(v, -1.0, 1.0); + value = v; } inline double getPressure(TSound::Channel chan) const { return (getValue(chan)); } - inline TMono32floatSample &operator+=(const TMono32floatSample &s) { - float fVal = value + s.value; - value = tcrop(fVal, -1.0, 1.0); + inline TMono32FloatSample &operator+=(const TMono32FloatSample &s) { + value += s.value; return *this; } - inline TMono32floatSample &operator*=(double a) { - float fVal = (value * a); - value = tcrop(fVal, -1.0, 1.0); + inline TMono32FloatSample &operator*=(double a) { + value *= a; return *this; } - inline TMono32floatSample operator*(double a) { - return TMono32floatSample(*this) *= a; + inline TMono32FloatSample operator*(double a) { return TMono32FloatSample(*this) *= a; } + + static TMono32FloatSample mix(const TMono32FloatSample &s1, double a1, + const TMono32FloatSample &s2, double a2) { + return TMono32FloatSample(s1.value * a1 + s2.value * a2); } - static TMono32floatSample mix(const TMono32floatSample &s1, double a1, - const TMono32floatSample &s2, double a2) { - return TMono32floatSample( - tcrop((s1.value * a1 + s2.value * a2), -1.0, 1.0)); - } - - static inline TMono32floatSample from(const TMono8UnsignedSample &sample); - static inline TMono32floatSample from(const TMono8SignedSample &sample); - static inline TMono32floatSample from(const TStereo8SignedSample &sample); - static inline TMono32floatSample from(const TStereo8UnsignedSample &sample); - static inline TMono32floatSample from(const TMono16Sample &sample); - static inline TMono32floatSample from(const TStereo16Sample &sample); - static inline TMono32floatSample from(const TMono24Sample &sample); - static inline TMono32floatSample from(const TStereo24Sample &sample); - static inline TMono32floatSample from(const TMono32Sample &sample); - static inline TMono32floatSample from(const TStereo32Sample &sample); - static inline TMono32floatSample from(const TMono32floatSample &sample); - static inline TMono32floatSample from(const TStereo32floatSample &sample); + static inline TMono32FloatSample from(const TMono8UnsignedSample &sample); + static inline TMono32FloatSample from(const TMono8SignedSample &sample); + static inline TMono32FloatSample from(const TStereo8SignedSample &sample); + static inline TMono32FloatSample from(const TStereo8UnsignedSample &sample); + static inline TMono32FloatSample from(const TMono16Sample &sample); + static inline TMono32FloatSample from(const TStereo16Sample &sample); + static inline TMono32FloatSample from(const TMono24Sample &sample); + static inline TMono32FloatSample from(const TStereo24Sample &sample); + static inline TMono32FloatSample from(const TMono32Sample &sample); + static inline TMono32FloatSample from(const TStereo32Sample &sample); + static inline TMono32FloatSample from(const TMono32FloatSample &sample); + static inline TMono32FloatSample from(const TStereo32FloatSample &sample); }; -inline TMono32floatSample operator+(const TMono32floatSample &s1, - const TMono32floatSample &s2) { - TMono32floatSample s = s1; +inline TMono32FloatSample operator+(const TMono32FloatSample &s1, + const TMono32FloatSample &s2) { + TMono32FloatSample s = s1; return s += s2; } //========================================================= -class DVAPI TStereo32floatSample { +class DVAPI TStereo32FloatSample { float channel[2]; // l'ordine dei canali e' left,right public: typedef float ChannelValueType; - typedef TMono32floatSample ChannelSampleType; + typedef TMono32FloatSample ChannelSampleType; - TStereo32floatSample(float lchan = 0.0, float rchan = 0.0) { - channel[0] = tcrop(lchan, -1.0, 1.0); - channel[1] = tcrop(rchan, -1.0, 1.0); + TStereo32FloatSample(float lchan = 0.0f, float rchan = 0.0f) { + channel[0] = lchan; + channel[1] = rchan; } - ~TStereo32floatSample(){}; + ~TStereo32FloatSample(){}; static bool isSampleSigned() { return true; } static int getBitPerSample() { return 32; } + static int getSampleType() { return TSound::FLOAT; } inline float getValue(TSound::Channel chan) const { assert(chan <= 1); @@ -868,57 +878,52 @@ public: inline void setValue(TSound::Channel chan, float v) { assert(chan <= 1); - channel[chan] = tcrop(v, -1.0, 1.0); + channel[chan] = v; } inline double getPressure(TSound::Channel chan) const { return (getValue(chan)); } - inline TStereo32floatSample &operator+=(const TStereo32floatSample &s) { - float fLeftVal = channel[0] + s.channel[0]; - float fRightVal = channel[1] + s.channel[1]; - channel[0] = tcrop(fLeftVal, -1.0, 1.0); - channel[1] = tcrop(fRightVal, -1.0, 1.0); + inline TStereo32FloatSample &operator+=(const TStereo32FloatSample &s) { + channel[0] += s.channel[0]; + channel[1] += s.channel[1]; return *this; } - inline TStereo32floatSample &operator*=(double a) { - float fLeftVal = (a * channel[0]); - float fRightVal = (a * channel[1]); - channel[0] = tcrop(fLeftVal, -1.0, 1.0); - channel[1] = tcrop(fRightVal, -1.0, 1.0); + inline TStereo32FloatSample &operator*=(double a) { + channel[0] *= a; + channel[1] *= a; return *this; } - inline TStereo32floatSample operator*(double a) { - return TStereo32floatSample(*this) *= a; + inline TStereo32FloatSample operator*(double a) { + return TStereo32FloatSample(*this) *= a; } - static TStereo32floatSample mix(const TStereo32floatSample &s1, double a1, - const TStereo32floatSample &s2, double a2) { - return TStereo32floatSample( - tcrop((s1.channel[0] * a1 + s2.channel[0] * a2), -1.0, 1.0), - tcrop((s1.channel[1] * a1 + s2.channel[1] * a2), -1.0, 1.0)); + static TStereo32FloatSample mix(const TStereo32FloatSample &s1, double a1, + const TStereo32FloatSample &s2, double a2) { + return TStereo32FloatSample(s1.channel[0] * a1 + s2.channel[0] * a2, + s1.channel[1] * a1 + s2.channel[1] * a2); } - static inline TStereo32floatSample from(const TMono8UnsignedSample &sample); - static inline TStereo32floatSample from(const TMono8SignedSample &sample); - static inline TStereo32floatSample from(const TStereo8SignedSample &sample); - static inline TStereo32floatSample from(const TStereo8UnsignedSample &sample); - static inline TStereo32floatSample from(const TMono16Sample &sample); - static inline TStereo32floatSample from(const TStereo16Sample &sample); - static inline TStereo32floatSample from(const TMono24Sample &sample); - static inline TStereo32floatSample from(const TStereo24Sample &sample); - static inline TStereo32floatSample from(const TMono32Sample &sample); - static inline TStereo32floatSample from(const TStereo32Sample &sample); - static inline TStereo32floatSample from(const TMono32floatSample &sample); - static inline TStereo32floatSample from(const TStereo32floatSample &sample); + static inline TStereo32FloatSample from(const TMono8UnsignedSample &sample); + static inline TStereo32FloatSample from(const TMono8SignedSample &sample); + static inline TStereo32FloatSample from(const TStereo8SignedSample &sample); + static inline TStereo32FloatSample from(const TStereo8UnsignedSample &sample); + static inline TStereo32FloatSample from(const TMono16Sample &sample); + static inline TStereo32FloatSample from(const TStereo16Sample &sample); + static inline TStereo32FloatSample from(const TMono24Sample &sample); + static inline TStereo32FloatSample from(const TStereo24Sample &sample); + static inline TStereo32FloatSample from(const TMono32Sample &sample); + static inline TStereo32FloatSample from(const TStereo32Sample &sample); + static inline TStereo32FloatSample from(const TMono32FloatSample &sample); + static inline TStereo32FloatSample from(const TStereo32FloatSample &sample); }; -inline TStereo32floatSample operator+(const TStereo32floatSample &s1, - const TStereo32floatSample &s2) { - TStereo32floatSample s = s1; +inline TStereo32FloatSample operator+(const TStereo32FloatSample &s1, + const TStereo32FloatSample &s2) { + TStereo32FloatSample s = s1; return s += s2; } @@ -933,7 +938,7 @@ inline TMono8SignedSample TMono8SignedSample::from( inline TMono8SignedSample TMono8SignedSample::from( const TMono8UnsignedSample &sample) { - return TMono8SignedSample(sample.getValue(TSound::LEFT) - 128); + return TMono8SignedSample(sample.getValue(TSound::MONO) - 128); } //------------------------------------------------------------------------------ @@ -957,7 +962,7 @@ inline TMono8SignedSample TMono8SignedSample::from( inline TMono8SignedSample TMono8SignedSample::from( const TMono16Sample &sample) { - int val = (sample.getValue(TSound::LEFT) >> 8); + int val = (sample.getValue(TSound::MONO) >> 8); return TMono8SignedSample(val); } @@ -973,7 +978,7 @@ inline TMono8SignedSample TMono8SignedSample::from( inline TMono8SignedSample TMono8SignedSample::from( const TMono24Sample &sample) { - int val = (sample.getValue(TSound::LEFT) >> 24); + int val = (sample.getValue(TSound::MONO) >> 16); return TMono8SignedSample(val); } @@ -982,7 +987,7 @@ inline TMono8SignedSample TMono8SignedSample::from( inline TMono8SignedSample TMono8SignedSample::from( const TStereo24Sample &sample) { int val = - (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 25; + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 17; return TMono8SignedSample(val); } @@ -990,7 +995,7 @@ inline TMono8SignedSample TMono8SignedSample::from( inline TMono8SignedSample TMono8SignedSample::from( const TMono32Sample &sample) { - int val = (sample.getValue(TSound::LEFT) >> 24); + int val = (sample.getValue(TSound::MONO) >> 24); return TMono8SignedSample(val); } @@ -1006,27 +1011,25 @@ inline TMono8SignedSample TMono8SignedSample::from( //------------------------------------------------------------------------------ inline TMono8SignedSample TMono8SignedSample::from( - const TMono32floatSample &sample) { - float val = (TINT32)(sample.getValue(TSound::LEFT) * 2147483648) >> 24; - return TMono8SignedSample(val); + const TMono32FloatSample &sample) { + float fval = sample.getValue(TSound::MONO) * 128.0; + return TMono8SignedSample(tcrop((int)fval, -128, 127)); } //------------------------------------------------------------------------------ inline TMono8SignedSample TMono8SignedSample::from( - const TStereo32floatSample &sample) { - float val = (TINT32)((sample.getValue(TSound::LEFT) + - sample.getValue(TSound::RIGHT)) * - 2147483648) >> - 25; - return TMono8SignedSample(val); + const TStereo32FloatSample &sample) { + float fval = + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) * 64.0; + return TMono8SignedSample(tcrop((int)fval, -128, 127)); } //============================================================================== inline TMono8UnsignedSample TMono8UnsignedSample::from( const TMono8SignedSample &sample) { - return TMono8UnsignedSample(sample.getValue(TSound::LEFT) + 128); + return TMono8UnsignedSample(sample.getValue(TSound::MONO) + 128); } //------------------------------------------------------------------------------ @@ -1073,7 +1076,7 @@ inline TMono8UnsignedSample TMono8UnsignedSample::from( inline TMono8UnsignedSample TMono8UnsignedSample::from( const TMono24Sample &sample) { return TMono8UnsignedSample( - (unsigned char)(sample.getValue(TSound::MONO) >> 24) + 128); + (unsigned char)(sample.getValue(TSound::MONO) >> 16) + 128); } //------------------------------------------------------------------------------ @@ -1083,7 +1086,7 @@ inline TMono8UnsignedSample TMono8UnsignedSample::from( return TMono8UnsignedSample( (unsigned char)((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> - 25) + + 17) + 128); } @@ -1109,30 +1112,25 @@ inline TMono8UnsignedSample TMono8UnsignedSample::from( //------------------------------------------------------------------------------ inline TMono8UnsignedSample TMono8UnsignedSample::from( - const TMono32floatSample &sample) { - return TMono8UnsignedSample( - (unsigned char)((TINT32)(sample.getValue(TSound::MONO) * 2147483648) >> - 24) + - 128); + const TMono32FloatSample &sample) { + float fval = sample.getValue(TSound::MONO) * 128.0; + return TMono8UnsignedSample(tcrop((int)fval + 128, 0, 255)); } //------------------------------------------------------------------------------ inline TMono8UnsignedSample TMono8UnsignedSample::from( - const TStereo32floatSample &sample) { - return TMono8UnsignedSample( - (unsigned char)((TINT32)((sample.getValue(TSound::LEFT) + - sample.getValue(TSound::RIGHT)) * - 2147483648) >> - 25) + - 128); + const TStereo32FloatSample &sample) { + float fval = + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) * 64.0; + return TMono8UnsignedSample(tcrop((int)fval + 128, 0, 255)); } //============================================================================== inline TStereo8SignedSample TStereo8SignedSample::from( const TMono8UnsignedSample &sample) { - int srcval = sample.getValue(TSound::LEFT) - 128; + int srcval = sample.getValue(TSound::MONO) - 128; return TStereo8SignedSample(srcval, srcval); } @@ -1140,7 +1138,7 @@ inline TStereo8SignedSample TStereo8SignedSample::from( inline TStereo8SignedSample TStereo8SignedSample::from( const TMono8SignedSample &sample) { - int srcval = sample.getValue(TSound::LEFT); + int srcval = sample.getValue(TSound::MONO); return TStereo8SignedSample(srcval, srcval); } @@ -1155,17 +1153,16 @@ inline TStereo8SignedSample TStereo8SignedSample::from( inline TStereo8SignedSample TStereo8SignedSample::from( const TStereo8UnsignedSample &sample) { - int srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 1) - - 128; - return TStereo8SignedSample(srcval, srcval); + int srcvalL = sample.getValue(TSound::LEFT) - 128; + int srcvalR = sample.getValue(TSound::RIGHT) - 128; + return TStereo8SignedSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo8SignedSample TStereo8SignedSample::from( const TMono16Sample &sample) { - int srcval = sample.getValue(TSound::LEFT) >> 8; + int srcval = sample.getValue(TSound::MONO) >> 8; return TStereo8SignedSample(srcval, srcval); } @@ -1173,16 +1170,16 @@ inline TStereo8SignedSample TStereo8SignedSample::from( inline TStereo8SignedSample TStereo8SignedSample::from( const TStereo16Sample &sample) { - int srcval = - (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 9; - return TStereo8SignedSample(srcval, srcval); + int srcvalL = sample.getValue(TSound::LEFT) >> 8; + int srcvalR = sample.getValue(TSound::RIGHT) >> 8; + return TStereo8SignedSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo8SignedSample TStereo8SignedSample::from( const TMono24Sample &sample) { - int srcval = sample.getValue(TSound::LEFT) >> 24; + int srcval = sample.getValue(TSound::MONO) >> 16; return TStereo8SignedSample(srcval, srcval); } @@ -1190,9 +1187,9 @@ inline TStereo8SignedSample TStereo8SignedSample::from( inline TStereo8SignedSample TStereo8SignedSample::from( const TStereo24Sample &sample) { - int srcval = - (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 25; - return TStereo8SignedSample(srcval, srcval); + int srcvalL = sample.getValue(TSound::LEFT) >> 16; + int srcvalR = sample.getValue(TSound::RIGHT) >> 16; + return TStereo8SignedSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ @@ -1215,27 +1212,27 @@ inline TStereo8SignedSample TStereo8SignedSample::from( //------------------------------------------------------------------------------ inline TStereo8SignedSample TStereo8SignedSample::from( - const TMono32floatSample &sample) { - int srcval = (TINT32)(sample.getValue(TSound::LEFT) * 2147483648) >> 24; + const TMono32FloatSample &sample) { + float fval = sample.getValue(TSound::MONO) * 128.0; + int srcval = tcrop((int)fval, -128, 127); return TStereo8SignedSample(srcval, srcval); } //------------------------------------------------------------------------------ inline TStereo8SignedSample TStereo8SignedSample::from( - const TStereo32floatSample &sample) { - int srcval = (TINT32)((sample.getValue(TSound::LEFT) + - sample.getValue(TSound::RIGHT)) * - 2147483648) >> - 25; - return TStereo8SignedSample(srcval, srcval); + const TStereo32FloatSample &sample) { + float fvalL = sample.getValue(TSound::LEFT) * 128.0; + float fvalR = sample.getValue(TSound::RIGHT) * 128.0; + return TStereo8SignedSample(tcrop((int)fvalL, -128, 127), + tcrop((int)fvalR, -128, 127)); } //============================================================================== inline TStereo8UnsignedSample TStereo8UnsignedSample::from( const TMono8UnsignedSample &sample) { - int srcval = sample.getValue(TSound::LEFT); + int srcval = sample.getValue(TSound::MONO); return TStereo8UnsignedSample(srcval, srcval); } @@ -1244,7 +1241,7 @@ inline TStereo8UnsignedSample TStereo8UnsignedSample::from( inline TStereo8UnsignedSample TStereo8UnsignedSample::from( const TMono8SignedSample &sample) { - int srcval = sample.getValue(TSound::LEFT) + 128; + int srcval = sample.getValue(TSound::MONO) + 128; return TStereo8UnsignedSample(srcval, srcval); } @@ -1260,17 +1257,16 @@ inline TStereo8UnsignedSample TStereo8UnsignedSample::from( inline TStereo8UnsignedSample TStereo8UnsignedSample::from( const TStereo8SignedSample &sample) { - int srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 1) + - 128; - return TStereo8UnsignedSample(srcval, srcval); + int srcvalL = sample.getValue(TSound::LEFT) + 128; + int srcvalR = sample.getValue(TSound::RIGHT) + 128; + return TStereo8UnsignedSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo8UnsignedSample TStereo8UnsignedSample::from( const TMono16Sample &sample) { - int srcval = (sample.getValue(TSound::LEFT) >> 8) + 128; + int srcval = (sample.getValue(TSound::MONO) >> 8) + 128; return TStereo8UnsignedSample(srcval, srcval); } @@ -1278,17 +1274,16 @@ inline TStereo8UnsignedSample TStereo8UnsignedSample::from( inline TStereo8UnsignedSample TStereo8UnsignedSample::from( const TStereo16Sample &sample) { - int srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 9) + - 128; - return TStereo8UnsignedSample(srcval, srcval); + int srcvalL = (sample.getValue(TSound::LEFT) >> 8) + 128; + int srcvalR = (sample.getValue(TSound::RIGHT) >> 8) + 128; + return TStereo8UnsignedSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo8UnsignedSample TStereo8UnsignedSample::from( const TMono24Sample &sample) { - int srcval = (sample.getValue(TSound::LEFT) >> 24) + 128; + int srcval = (sample.getValue(TSound::MONO) >> 16) + 128; return TStereo8UnsignedSample(srcval, srcval); } @@ -1296,10 +1291,9 @@ inline TStereo8UnsignedSample TStereo8UnsignedSample::from( inline TStereo8UnsignedSample TStereo8UnsignedSample::from( const TStereo24Sample &sample) { - int srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 25) + - 128; - return TStereo8UnsignedSample(srcval, srcval); + int srcvalL = (sample.getValue(TSound::LEFT) >> 16) + 128; + int srcvalR = (sample.getValue(TSound::RIGHT) >> 16) + 128; + return TStereo8UnsignedSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ @@ -1323,34 +1317,32 @@ inline TStereo8UnsignedSample TStereo8UnsignedSample::from( //------------------------------------------------------------------------------ inline TStereo8UnsignedSample TStereo8UnsignedSample::from( - const TMono32floatSample &sample) { - int srcval = - ((TINT32)(sample.getValue(TSound::LEFT) * 2147483648) >> 24) + 128; + const TMono32FloatSample &sample) { + float fval = sample.getValue(TSound::MONO) * 128.0; + int srcval = tcrop((int)fval + 128, 0, 255); return TStereo8UnsignedSample(srcval, srcval); } //------------------------------------------------------------------------------ inline TStereo8UnsignedSample TStereo8UnsignedSample::from( - const TStereo32floatSample &sample) { - int srcval = ((TINT32)((sample.getValue(TSound::LEFT) + - sample.getValue(TSound::RIGHT)) * - 2147483648) >> - 25) + - 128; - return TStereo8UnsignedSample(srcval, srcval); + const TStereo32FloatSample &sample) { + float fvalL = sample.getValue(TSound::LEFT) * 128.0; + float fvalR = sample.getValue(TSound::RIGHT) * 128.0; + return TStereo8UnsignedSample(tcrop((int)fvalL + 128, 0, 255), + tcrop((int)fvalR + 128, 0, 255)); } //============================================================================== inline TMono16Sample TMono16Sample::from(const TMono8UnsignedSample &sample) { - return TMono16Sample((sample.getValue(TSound::LEFT) - 128) << 8); + return TMono16Sample((sample.getValue(TSound::MONO) - 128) << 8); } //------------------------------------------------------------------------------ inline TMono16Sample TMono16Sample::from(const TMono8SignedSample &sample) { - return TMono16Sample(sample.getValue(TSound::LEFT) << 8); + return TMono16Sample(sample.getValue(TSound::MONO) << 8); } //------------------------------------------------------------------------------ @@ -1384,7 +1376,7 @@ inline TMono16Sample TMono16Sample::from(const TStereo16Sample &sample) { //------------------------------------------------------------------------------ inline TMono16Sample TMono16Sample::from(const TMono24Sample &sample) { - int srcval = (sample.getValue(TSound::LEFT) >> 16); + int srcval = (sample.getValue(TSound::MONO) >> 8); return TMono16Sample(srcval); } @@ -1392,14 +1384,14 @@ inline TMono16Sample TMono16Sample::from(const TMono24Sample &sample) { inline TMono16Sample TMono16Sample::from(const TStereo24Sample &sample) { int srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 17); + ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 9); return TMono16Sample(srcval); } //------------------------------------------------------------------------------ inline TMono16Sample TMono16Sample::from(const TMono32Sample &sample) { - int srcval = (sample.getValue(TSound::LEFT) >> 16); + int srcval = (sample.getValue(TSound::MONO) >> 16); return TMono16Sample(srcval); } @@ -1413,33 +1405,32 @@ inline TMono16Sample TMono16Sample::from(const TStereo32Sample &sample) { //------------------------------------------------------------------------------ -inline TMono16Sample TMono16Sample::from(const TMono32floatSample &sample) { - int srcval = (TINT32)(sample.getValue(TSound::LEFT) * 2147483648) >> 16; - return TMono16Sample(srcval); +inline TMono16Sample TMono16Sample::from(const TMono32FloatSample &sample) { + float fval = sample.getValue(TSound::MONO) * 32768.0; + return TMono16Sample(tcrop((int)fval, -32768, 32767)); } //------------------------------------------------------------------------------ -inline TMono16Sample TMono16Sample::from(const TStereo32floatSample &sample) { - int srcval = (TINT32)((sample.getValue(TSound::LEFT) + - sample.getValue(TSound::RIGHT)) * - 2147483648) >> - 17; - return TMono16Sample(srcval); +inline TMono16Sample TMono16Sample::from(const TStereo32FloatSample &sample) { + float fval = + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) * + 16384.0; + return TMono16Sample(tcrop((int)fval, -32768, 32767)); } //============================================================================== inline TStereo16Sample TStereo16Sample::from( const TMono8UnsignedSample &sample) { - int srcval = (sample.getValue(TSound::LEFT) - 128) << 8; + int srcval = (sample.getValue(TSound::MONO) - 128) << 8; return TStereo16Sample(srcval, srcval); } //------------------------------------------------------------------------------ inline TStereo16Sample TStereo16Sample::from(const TMono8SignedSample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 8; + int srcval = sample.getValue(TSound::MONO) << 8; return TStereo16Sample(srcval, srcval); } @@ -1447,25 +1438,24 @@ inline TStereo16Sample TStereo16Sample::from(const TMono8SignedSample &sample) { inline TStereo16Sample TStereo16Sample::from( const TStereo8SignedSample &sample) { - int srcval = (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) - << 7; - return TStereo16Sample(srcval, srcval); + int srcvalL = sample.getValue(TSound::LEFT) << 8; + int srcvalR = sample.getValue(TSound::RIGHT) << 8; + return TStereo16Sample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo16Sample TStereo16Sample::from( const TStereo8UnsignedSample &sample) { - int srcval = - (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT) - 256) - << 7; - return TStereo16Sample(srcval, srcval); + int srcvalL = (sample.getValue(TSound::LEFT) - 128) << 8; + int srcvalR = (sample.getValue(TSound::RIGHT) - 128) << 8; + return TStereo16Sample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo16Sample TStereo16Sample::from(const TMono16Sample &sample) { - int srcval = sample.getValue(TSound::LEFT); + int srcval = sample.getValue(TSound::MONO); return TStereo16Sample(srcval, srcval); } @@ -1478,22 +1468,22 @@ inline TStereo16Sample TStereo16Sample::from(const TStereo16Sample &sample) { //------------------------------------------------------------------------------ inline TStereo16Sample TStereo16Sample::from(const TMono24Sample &sample) { - int srcval = (sample.getValue(TSound::LEFT) >> 16); + int srcval = (sample.getValue(TSound::MONO) >> 8); return TStereo16Sample(srcval, srcval); } //------------------------------------------------------------------------------ inline TStereo16Sample TStereo16Sample::from(const TStereo24Sample &sample) { - int srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) >> 17); - return TStereo16Sample(srcval, srcval); + int srcvalL = sample.getValue(TSound::LEFT) >> 8; + int srcvalR = sample.getValue(TSound::RIGHT) >> 8; + return TStereo16Sample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo16Sample TStereo16Sample::from(const TMono32Sample &sample) { - int srcval = (sample.getValue(TSound::LEFT) >> 16); + int srcval = (sample.getValue(TSound::MONO) >> 16); return TStereo16Sample(srcval, srcval); } @@ -1507,33 +1497,32 @@ inline TStereo16Sample TStereo16Sample::from(const TStereo32Sample &sample) { //------------------------------------------------------------------------------ -inline TStereo16Sample TStereo16Sample::from(const TMono32floatSample &sample) { - int srcval = (TINT32)(sample.getValue(TSound::LEFT) * 2147483648) >> 16; +inline TStereo16Sample TStereo16Sample::from(const TMono32FloatSample &sample) { + float fval = sample.getValue(TSound::MONO) * 32768.0; + int srcval = tcrop((int)fval, -32768, 32767); return TStereo16Sample(srcval, srcval); } //------------------------------------------------------------------------------ -inline TStereo16Sample TStereo16Sample::from( - const TStereo32floatSample &sample) { - int srcval = (TINT32)((sample.getValue(TSound::LEFT) + - sample.getValue(TSound::RIGHT)) * - 2147483648) >> - 17; - return TStereo16Sample(srcval, srcval); +inline TStereo16Sample TStereo16Sample::from(const TStereo32FloatSample &sample) { + float fvalL = sample.getValue(TSound::LEFT) * 32768.0; + float fvalR = sample.getValue(TSound::RIGHT) * 32768.0; + return TStereo16Sample(tcrop((int)fvalL, -32768, 32767), + tcrop((int)fvalR, -32768, 32767)); } //============================================================================== inline TMono24Sample TMono24Sample::from(const TMono8UnsignedSample &sample) { - int srcval = (sample.getValue(TSound::LEFT) - 128) << 16; + int srcval = (sample.getValue(TSound::MONO) - 128) << 16; return TMono24Sample(srcval); } //------------------------------------------------------------------------------ inline TMono24Sample TMono24Sample::from(const TMono8SignedSample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 16; + int srcval = sample.getValue(TSound::MONO) << 16; return TMono24Sample(srcval); } @@ -1557,7 +1546,7 @@ inline TMono24Sample TMono24Sample::from(const TStereo8UnsignedSample &sample) { //------------------------------------------------------------------------------ inline TMono24Sample TMono24Sample::from(const TMono16Sample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 8; + int srcval = sample.getValue(TSound::MONO) << 8; return TMono24Sample(srcval); } @@ -1586,7 +1575,7 @@ inline TMono24Sample TMono24Sample::from(const TStereo24Sample &sample) { //------------------------------------------------------------------------------ inline TMono24Sample TMono24Sample::from(const TMono32Sample &sample) { - int srcval = sample.getValue(TSound::LEFT) >> 8; + int srcval = sample.getValue(TSound::MONO) >> 8; return TMono24Sample(srcval); } @@ -1600,33 +1589,32 @@ inline TMono24Sample TMono24Sample::from(const TStereo32Sample &sample) { //------------------------------------------------------------------------------ -inline TMono24Sample TMono24Sample::from(const TMono32floatSample &sample) { - int srcval = (TINT32)(sample.getValue(TSound::LEFT) * 2147483648) >> 8; - return TMono24Sample(srcval); +inline TMono24Sample TMono24Sample::from(const TMono32FloatSample &sample) { + float fval = sample.getValue(TSound::MONO) * 8388608.0; + return TMono24Sample(tcrop((int)fval, -8388608, 8388607)); } //------------------------------------------------------------------------------ -inline TMono24Sample TMono24Sample::from(const TStereo32floatSample &sample) { - int srcval = (TINT32)((sample.getValue(TSound::LEFT) + - sample.getValue(TSound::RIGHT)) * - 2147483648) >> - 9; - return TMono24Sample(srcval); +inline TMono24Sample TMono24Sample::from(const TStereo32FloatSample &sample) { + float fval = + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) * + 4194304.0; + return TMono24Sample(tcrop((int)fval, -8388608, 8388607)); } //============================================================================== inline TStereo24Sample TStereo24Sample::from( const TMono8UnsignedSample &sample) { - int srcval = (sample.getValue(TSound::LEFT) - 128) << 16; + int srcval = (sample.getValue(TSound::MONO) - 128) << 16; return TStereo24Sample(srcval, srcval); } //------------------------------------------------------------------------------ inline TStereo24Sample TStereo24Sample::from(const TMono8SignedSample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 16; + int srcval = sample.getValue(TSound::MONO) << 16; return TStereo24Sample(srcval, srcval); } @@ -1634,40 +1622,39 @@ inline TStereo24Sample TStereo24Sample::from(const TMono8SignedSample &sample) { inline TStereo24Sample TStereo24Sample::from( const TStereo8SignedSample &sample) { - int srcval = (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) - << 15; - return TStereo24Sample(srcval, srcval); + int srcvalL = sample.getValue(TSound::LEFT) << 16; + int srcvalR = sample.getValue(TSound::RIGHT) << 16; + return TStereo24Sample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo24Sample TStereo24Sample::from( const TStereo8UnsignedSample &sample) { - int srcval = - (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT) - 256) - << 15; - return TStereo24Sample(srcval, srcval); + int srcvalL = (sample.getValue(TSound::LEFT) - 128) << 16; + int srcvalR = (sample.getValue(TSound::RIGHT) - 128) << 16; + return TStereo24Sample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo24Sample TStereo24Sample::from(const TMono16Sample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 8; + int srcval = sample.getValue(TSound::MONO) << 8; return TStereo24Sample(srcval, srcval); } //------------------------------------------------------------------------------ inline TStereo24Sample TStereo24Sample::from(const TStereo16Sample &sample) { - int srcval = (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) - << 7; - return TStereo24Sample(srcval, srcval); + int srcvalL = sample.getValue(TSound::LEFT) << 8; + int srcvalR = sample.getValue(TSound::RIGHT) << 8; + return TStereo24Sample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ inline TStereo24Sample TStereo24Sample::from(const TMono24Sample &sample) { - int srcval = sample.getValue(TSound::LEFT); + int srcval = sample.getValue(TSound::MONO); return TStereo24Sample(srcval, srcval); } @@ -1694,33 +1681,32 @@ inline TStereo24Sample TStereo24Sample::from(const TStereo32Sample &sample) { //------------------------------------------------------------------------------ -inline TStereo24Sample TStereo24Sample::from(const TMono32floatSample &sample) { - int srcval = (TINT32)(sample.getValue(TSound::LEFT) * 2147483648) >> 8; +inline TStereo24Sample TStereo24Sample::from(const TMono32FloatSample &sample) { + float fval = sample.getValue(TSound::MONO) * 8388608.0; + int srcval = tcrop((int)fval, -8388608, 8388607); return TStereo24Sample(srcval, srcval); } //------------------------------------------------------------------------------ -inline TStereo24Sample TStereo24Sample::from( - const TStereo32floatSample &sample) { - int srcval = (TINT32)((sample.getValue(TSound::LEFT) + - sample.getValue(TSound::RIGHT)) * - 2147483648) >> - 9; - return TStereo24Sample(srcval, srcval); +inline TStereo24Sample TStereo24Sample::from(const TStereo32FloatSample &sample) { + float fvalL = sample.getValue(TSound::LEFT) * 8388608.0; + float fvalR = sample.getValue(TSound::RIGHT) * 8388608.0; + return TStereo24Sample(tcrop((int)fvalL, -8388608, 8388607), + tcrop((int)fvalR, -8388608, 8388607)); } //============================================================================== inline TMono32Sample TMono32Sample::from(const TMono8UnsignedSample &sample) { - int srcval = (sample.getValue(TSound::LEFT) - 128) << 24; + int srcval = (sample.getValue(TSound::MONO) - 128) << 24; return TMono32Sample(srcval); } //------------------------------------------------------------------------------ inline TMono32Sample TMono32Sample::from(const TMono8SignedSample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 24; + int srcval = sample.getValue(TSound::MONO) << 24; return TMono32Sample(srcval); } @@ -1744,7 +1730,7 @@ inline TMono32Sample TMono32Sample::from(const TStereo8UnsignedSample &sample) { //------------------------------------------------------------------------------ inline TMono32Sample TMono32Sample::from(const TMono16Sample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 16; + int srcval = sample.getValue(TSound::MONO) << 16; return TMono32Sample(srcval); } @@ -1759,7 +1745,7 @@ inline TMono32Sample TMono32Sample::from(const TStereo16Sample &sample) { //------------------------------------------------------------------------------ inline TMono32Sample TMono32Sample::from(const TMono24Sample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 8; + int srcval = sample.getValue(TSound::MONO) << 8; return TMono32Sample(srcval); } @@ -1786,13 +1772,13 @@ inline TMono32Sample TMono32Sample::from(const TStereo32Sample &sample) { //------------------------------------------------------------------------------ -inline TMono32Sample TMono32Sample::from(const TMono32floatSample &sample) { - return TMono32Sample(sample.getValue(TSound::LEFT) * 2147483648); +inline TMono32Sample TMono32Sample::from(const TMono32FloatSample &sample) { + return TMono32Sample(sample.getValue(TSound::MONO) * 2147483648); } //------------------------------------------------------------------------------ -inline TMono32Sample TMono32Sample::from(const TStereo32floatSample &sample) { +inline TMono32Sample TMono32Sample::from(const TStereo32FloatSample &sample) { return TMono32Sample( (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) * 2147483648); @@ -1802,14 +1788,14 @@ inline TMono32Sample TMono32Sample::from(const TStereo32floatSample &sample) { inline TStereo32Sample TStereo32Sample::from( const TMono8UnsignedSample &sample) { - int srcval = (sample.getValue(TSound::LEFT) - 128) << 24; + int srcval = (sample.getValue(TSound::MONO) - 128) << 24; return TStereo32Sample(srcval, srcval); } //------------------------------------------------------------------------------ inline TStereo32Sample TStereo32Sample::from(const TMono8SignedSample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 24; + int srcval = sample.getValue(TSound::MONO) << 24; return TStereo32Sample(srcval, srcval); } @@ -1835,7 +1821,7 @@ inline TStereo32Sample TStereo32Sample::from( //------------------------------------------------------------------------------ inline TStereo32Sample TStereo32Sample::from(const TMono16Sample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 16; + int srcval = sample.getValue(TSound::MONO) << 16; return TStereo32Sample(srcval, srcval); } @@ -1850,7 +1836,7 @@ inline TStereo32Sample TStereo32Sample::from(const TStereo16Sample &sample) { //------------------------------------------------------------------------------ inline TStereo32Sample TStereo32Sample::from(const TMono24Sample &sample) { - int srcval = sample.getValue(TSound::LEFT) << 8; + int srcval = sample.getValue(TSound::MONO) << 8; return TStereo32Sample(srcval, srcval); } @@ -1865,7 +1851,7 @@ inline TStereo32Sample TStereo32Sample::from(const TStereo24Sample &sample) { //------------------------------------------------------------------------------ inline TStereo32Sample TStereo32Sample::from(const TMono32Sample &sample) { - int srcval = sample.getValue(TSound::LEFT); + int srcval = sample.getValue(TSound::MONO); return TStereo32Sample(srcval, srcval); } @@ -1877,15 +1863,15 @@ inline TStereo32Sample TStereo32Sample::from(const TStereo32Sample &sample) { //------------------------------------------------------------------------------ -inline TStereo32Sample TStereo32Sample::from(const TMono32floatSample &sample) { - int srcval = sample.getValue(TSound::LEFT) * 2147483648; +inline TStereo32Sample TStereo32Sample::from(const TMono32FloatSample &sample) { + int srcval = sample.getValue(TSound::MONO) * 2147483648; return TStereo32Sample(srcval, srcval); } //------------------------------------------------------------------------------ inline TStereo32Sample TStereo32Sample::from( - const TStereo32floatSample &sample) { + const TStereo32FloatSample &sample) { int srcval = (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) * 2147483648; @@ -1894,213 +1880,205 @@ inline TStereo32Sample TStereo32Sample::from( //============================================================================== -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TMono8UnsignedSample &sample) { - float srcval = ((sample.getValue(TSound::LEFT) - 128) << 24) / 2147483648; - return TMono32floatSample(srcval); + float srcval = (sample.getValue(TSound::MONO) - 128) / 128.0; + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TMono8SignedSample &sample) { - float srcval = (sample.getValue(TSound::LEFT) << 24) / 2147483648; - return TMono32floatSample(srcval); + float srcval = sample.getValue(TSound::MONO) / 128.0; + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TStereo8SignedSample &sample) { float srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) << 23) / - 2147483648; - return TMono32floatSample(srcval); + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) / 256.0; + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TStereo8UnsignedSample &sample) { float srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT) - 256) - << 23) / - 2147483648; - return TMono32floatSample(srcval); + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT) - 256) / + 256.0; + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TMono16Sample &sample) { - float srcval = (sample.getValue(TSound::LEFT) << 16) / 2147483648; - return TMono32floatSample(srcval); + float srcval = sample.getValue(TSound::MONO) / 32768.0; + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TStereo16Sample &sample) { float srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) << 15) / - 2147483648; - return TMono32floatSample(srcval); + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) / + 65536.0; + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TMono24Sample &sample) { - float srcval = (sample.getValue(TSound::LEFT) << 8) / 2147483648; - return TMono32floatSample(srcval); + float srcval = sample.getValue(TSound::MONO) / 8388608.0; + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TStereo24Sample &sample) { float srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) << 7) / - 2147483648; - return TMono32floatSample(srcval); + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) / + 16777216.0; + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TMono32Sample &sample) { - float srcval = sample.getValue(TSound::LEFT) / 2147483648; - return TMono32floatSample(srcval); + float srcval = sample.getValue(TSound::MONO) / 2147483648; + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( +inline TMono32FloatSample TMono32FloatSample::from( const TStereo32Sample &sample) { float srcval = (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) / 2147483648; - return TMono32floatSample(srcval); + return TMono32FloatSample(srcval); } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( - const TMono32floatSample &sample) { +inline TMono32FloatSample TMono32FloatSample::from( + const TMono32FloatSample &sample) { return sample; } //------------------------------------------------------------------------------ -inline TMono32floatSample TMono32floatSample::from( - const TStereo32floatSample &sample) { - return TMono32floatSample( - (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT))); +inline TMono32FloatSample TMono32FloatSample::from( + const TStereo32FloatSample &sample) { + float srcval = + (sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) / 2.0; + return TMono32FloatSample(srcval); } //============================================================================== -inline TStereo32floatSample TStereo32floatSample::from( +inline TStereo32FloatSample TStereo32FloatSample::from( const TMono8UnsignedSample &sample) { - float srcval = ((sample.getValue(TSound::LEFT) - 128) << 24) / 2147483648; - return TStereo32floatSample(srcval, srcval); + float srcval = (sample.getValue(TSound::MONO) - 128) / 128.0; + return TStereo32FloatSample(srcval, srcval); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( - const TMono8SignedSample &sample) { - float srcval = (sample.getValue(TSound::LEFT) << 24) / 2147483648; - return TStereo32floatSample(srcval, srcval); +inline TStereo32FloatSample TStereo32FloatSample::from(const TMono8SignedSample &sample) { + float srcval = sample.getValue(TSound::MONO) / 128.0; + return TStereo32FloatSample(srcval, srcval); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( +inline TStereo32FloatSample TStereo32FloatSample::from( const TStereo8SignedSample &sample) { - float srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) << 23) / - 2147483648; - return TStereo32floatSample(srcval, srcval); + float srcvalL = sample.getValue(TSound::LEFT) / 128.0; + float srcvalR = sample.getValue(TSound::RIGHT) / 128.0; + return TStereo32FloatSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( +inline TStereo32FloatSample TStereo32FloatSample::from( const TStereo8UnsignedSample &sample) { - float srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT) - 256) - << 23) / - 2147483648; - return TStereo32floatSample(srcval, srcval); + float srcvalL = (sample.getValue(TSound::LEFT) - 128) / 128.0; + float srcvalR = (sample.getValue(TSound::RIGHT) - 128) / 128.0; + return TStereo32FloatSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( +inline TStereo32FloatSample TStereo32FloatSample::from( const TMono16Sample &sample) { - float srcval = (sample.getValue(TSound::LEFT) << 16) / 2147483648; - return TStereo32floatSample(srcval, srcval); + float srcval = sample.getValue(TSound::MONO) / 32768.0; + return TStereo32FloatSample(srcval, srcval); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( +inline TStereo32FloatSample TStereo32FloatSample::from( const TStereo16Sample &sample) { - float srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) << 15) / - 2147483648; - return TStereo32floatSample(srcval, srcval); + float srcvalL = sample.getValue(TSound::LEFT) / 32768.0; + float srcvalR = sample.getValue(TSound::RIGHT) / 32768.0; + return TStereo32FloatSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( +inline TStereo32FloatSample TStereo32FloatSample::from( const TMono24Sample &sample) { - float srcval = (sample.getValue(TSound::LEFT) << 8) / 2147483648; - return TStereo32floatSample(srcval, srcval); + float srcval = sample.getValue(TSound::MONO) / 8388608.0; + return TStereo32FloatSample(srcval, srcval); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( +inline TStereo32FloatSample TStereo32FloatSample::from( const TStereo24Sample &sample) { - float srcval = - ((sample.getValue(TSound::LEFT) + sample.getValue(TSound::RIGHT)) << 7) / - 2147483648; - return TStereo32floatSample(srcval, srcval); + float srcvalL = sample.getValue(TSound::LEFT) / 8388608.0; + float srcvalR = sample.getValue(TSound::RIGHT) / 8388608.0; + return TStereo32FloatSample(srcvalL, srcvalR); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( +inline TStereo32FloatSample TStereo32FloatSample::from( const TMono32Sample &sample) { - float srcval = sample.getValue(TSound::LEFT) / 2147483648; - return TStereo32floatSample(srcval, srcval); + float srcval = sample.getValue(TSound::MONO) / 2147483648; + return TStereo32FloatSample(srcval, srcval); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( +inline TStereo32FloatSample TStereo32FloatSample::from( const TStereo32Sample &sample) { float srcval = (sample.getValue(TSound::LEFT) + sample.getValue(TSound::LEFT)) / 2147483648; - return TStereo32floatSample(srcval, srcval); + return TStereo32FloatSample(srcval, srcval); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( - const TMono32floatSample &sample) { - float srcval = sample.getValue(TSound::LEFT); - return TStereo32floatSample(srcval, srcval); +inline TStereo32FloatSample TStereo32FloatSample::from( + const TMono32FloatSample &sample) { + float srcval = sample.getValue(TSound::MONO); + return TStereo32FloatSample(srcval, srcval); } //------------------------------------------------------------------------------ -inline TStereo32floatSample TStereo32floatSample::from( - const TStereo32floatSample &sample) { +inline TStereo32FloatSample TStereo32FloatSample::from(const TStereo32FloatSample &sample) { return sample; } diff --git a/toonz/sources/include/tspectrum.h b/toonz/sources/include/tspectrum.h index f48174a0..9cfe85e6 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 8eed363e..175faa10 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/include/tvectorbrushstyle.h b/toonz/sources/include/tvectorbrushstyle.h index 2a39d6a5..c0ab93cb 100644 --- a/toonz/sources/include/tvectorbrushstyle.h +++ b/toonz/sources/include/tvectorbrushstyle.h @@ -39,7 +39,10 @@ public: ~TVectorBrushStyle(); TColorStyle *clone() const override; + TColorStyle *clone(std::string brushIdName) const override; + QString getDescription() const override; + std::string getBrushIdName() const override; static TFilePath getRootDir() { return m_rootDir; } static void setRootDir(const TFilePath &path) { diff --git a/toonz/sources/include/tversion.h b/toonz/sources/include/tversion.h index 5e4d637e..60e79dba 100644 --- a/toonz/sources/include/tversion.h +++ b/toonz/sources/include/tversion.h @@ -18,7 +18,7 @@ public: private: const char *applicationName = "Tahoma2D"; - const float applicationVersion = 1.3; + const float applicationVersion = 1.3f; const float applicationRevision = 1; const char *applicationNote = ""; }; diff --git a/toonz/sources/sound/CMakeLists.txt b/toonz/sources/sound/CMakeLists.txt index f2a5cece..09a4a1f7 100644 --- a/toonz/sources/sound/CMakeLists.txt +++ b/toonz/sources/sound/CMakeLists.txt @@ -2,9 +2,8 @@ set(HEADERS wav/tsio_wav.h aiff/tsio_aiff.h raw/tsio_raw.h - mp3/tsio_mp3.h + ffmpeg/tsio_ffmpeg.h ../include/tnzsound.h - tsio.h ) set(SOURCES @@ -13,7 +12,7 @@ set(SOURCES wav/tsio_wav.cpp aiff/tsio_aiff.cpp raw/tsio_raw.cpp - mp3/tsio_mp3.cpp + ffmpeg/tsio_ffmpeg.cpp ) add_library(sound SHARED ${HEADERS} ${SOURCES}) diff --git a/toonz/sources/sound/aiff/tsio_aiff.cpp b/toonz/sources/sound/aiff/tsio_aiff.cpp index b27f1225..3ccd2fd3 100644 --- a/toonz/sources/sound/aiff/tsio_aiff.cpp +++ b/toonz/sources/sound/aiff/tsio_aiff.cpp @@ -78,6 +78,7 @@ public: TCOMMChunk(string name, TINT32 length) : TAIFFChunk(name, length) {} bool read(ifstream &is) override { + long pos = is.tellg(); is.read((char *)&m_chans, sizeof(m_chans)); is.read((char *)&m_frames, sizeof(m_frames)); is.read((char *)&m_bitPerSample, sizeof(m_bitPerSample)); @@ -92,6 +93,7 @@ public: memset(sampleRateBuffer, 0, 10); is.read((char *)&sampleRateBuffer, sizeof(sampleRateBuffer)); m_sampleRate = convertToLong(sampleRateBuffer); + is.seekg(pos + (long)m_length, is.beg); return true; } @@ -311,7 +313,7 @@ TSoundTrackP TSoundTrackReaderAiff::load() { formType[4] = '\0'; // il formType DEVE essere uguale a "AIFF" - if ((string(formType, 4) != "AIFF")) + if ((string(formType, 4) != "AIFF") && (string(formType, 4) != "AIFC")) throw TException("The AIFF file doesn't contain the AIFF form"); TCOMMChunk *commChunk = 0; @@ -388,7 +390,7 @@ TSoundTrackP TSoundTrackReaderAiff::load() { (void *)(ssndChunk->m_waveData.get() + ssndChunk->m_offset), commChunk->m_frames * track->getSampleSize()); else - swapAndCopySamples( + swapAndCopy16Bits( (short *)(ssndChunk->m_waveData.get() + ssndChunk->m_offset), (short *)track->getRawData(), (TINT32)(commChunk->m_frames * commChunk->m_chans)); @@ -402,44 +404,34 @@ TSoundTrackP TSoundTrackReaderAiff::load() { track = new TSoundTrackStereo24(commChunk->m_sampleRate, 2, (TINT32)commChunk->m_frames); - if (!TNZ_LITTLE_ENDIAN) { - UCHAR *begin = (UCHAR *)track->getRawData(); - for (int i = 0; i < (int)(commChunk->m_frames * commChunk->m_chans); - ++i) { // dovrebbe andare bene anche adesso - *(begin + 4 * i) = 0; - *(begin + 4 * i + 1) = - *(ssndChunk->m_waveData.get() + ssndChunk->m_offset + 3 * i); - *(begin + 4 * i + 2) = - *(ssndChunk->m_waveData.get() + ssndChunk->m_offset + 3 * i + 1); - *(begin + 4 * i + 3) = - *(ssndChunk->m_waveData.get() + ssndChunk->m_offset + 3 * i + 2); - } - } else { - UCHAR *begin = (UCHAR *)track->getRawData(); - for (int i = 0; i < (int)(commChunk->m_frames * commChunk->m_chans); - ++i) { - *(begin + 4 * i) = - *(ssndChunk->m_waveData.get() + ssndChunk->m_offset + 3 * i + 2); - *(begin + 4 * i + 1) = - *(ssndChunk->m_waveData.get() + ssndChunk->m_offset + 3 * i + 1); - *(begin + 4 * i + 2) = - *(ssndChunk->m_waveData.get() + ssndChunk->m_offset + 3 * i); - *(begin + 4 * i + 3) = 0; + if (!TNZ_LITTLE_ENDIAN) + memcpy((void *)track->getRawData(), + (void *)(ssndChunk->m_waveData.get() + ssndChunk->m_offset), + commChunk->m_frames * track->getSampleSize()); + else + swapAndCopy24Bits( + (void *)(ssndChunk->m_waveData.get() + ssndChunk->m_offset), + (void *)track->getRawData(), + (TINT32)(commChunk->m_frames * commChunk->m_chans)); + break; + case 32: + if (commChunk->m_chans == 1) + track = new TSoundTrackMono32Float(commChunk->m_sampleRate, 1, + (TINT32)commChunk->m_frames); + else // due canali + track = new TSoundTrackStereo32Float(commChunk->m_sampleRate, 2, + (TINT32)commChunk->m_frames); - /* -*(begin + 4*i) = 0; -*(begin + 4*i + 3) = -*(ssndChunk->m_waveData+ssndChunk->m_offset + 3*i + 2); - -// sono i due byte che vengono invertiti -*(begin + 4*i + 1) = -*(ssndChunk->m_waveData+ssndChunk->m_offset + 3*i + 1); -*(begin + 4*i + 2) = -*(ssndChunk->m_waveData+ssndChunk->m_offset + 3*i); -*/ - } - } - break; + if (!TNZ_LITTLE_ENDIAN) + memcpy((void *)track->getRawData(), + (void *)(ssndChunk->m_waveData.get() + ssndChunk->m_offset), + commChunk->m_frames * track->getSampleSize()); + else + swapAndCopy32Bits( + (TINT32 *)(ssndChunk->m_waveData.get() + ssndChunk->m_offset), + (TINT32 *)track->getRawData(), + (TINT32)(commChunk->m_frames * commChunk->m_chans)); + break; } if (commChunk) delete commChunk; @@ -498,40 +490,29 @@ bool TSoundTrackWriterAiff::save(const TSoundTrackP &st) { if (TNZ_LITTLE_ENDIAN) { postHeadData = swapTINT32(postHeadData); - if (commChunk.m_bitPerSample == 16) { - swapAndCopySamples((short *)sndtrack->getRawData(), - (short *)waveData.get(), - (TINT32)(commChunk.m_frames * commChunk.m_chans)); - } else if (commChunk.m_bitPerSample == 24) { - UCHAR *begin = (UCHAR *)sndtrack->getRawData(); - for (int i = 0; i < (int)commChunk.m_frames * commChunk.m_chans; ++i) { - *(waveData.get() + 3 * i) = *(begin + 4 * i + 2); - *(waveData.get() + 3 * i + 1) = *(begin + 4 * i + 1); - *(waveData.get() + 3 * i + 2) = *(begin + 4 * i); - - /* -*(waveData + 3*i + 2) = *(begin + 4*i + 3); - -// posiziona in modo corretto i due byte prima invertiti -*(waveData + 3*i) = *(begin + 4*i + 2); -*(waveData + 3*i + 1) = *(begin + 4*i + 1); -*/ - } - } else + switch (commChunk.m_bitPerSample) { + case 16: + swapAndCopy16Bits((short *)sndtrack->getRawData(), + (short *)waveData.get(), + (TINT32)(commChunk.m_frames * commChunk.m_chans)); + break; + case 24: + swapAndCopy24Bits((void *)sndtrack->getRawData(), (void *)waveData.get(), + (TINT32)(commChunk.m_frames * commChunk.m_chans)); + break; + case 32: + swapAndCopy32Bits((TINT32 *)sndtrack->getRawData(), + (TINT32 *)waveData.get(), + (TINT32)(commChunk.m_frames * commChunk.m_chans)); + break; + default: memcpy((void *)waveData.get(), (void *)sndtrack->getRawData(), soundDataCount); - } else { - if (commChunk.m_bitPerSample != 24) - memcpy((void *)waveData.get(), (void *)sndtrack->getRawData(), - soundDataCount); - else { - UCHAR *begin = (UCHAR *)sndtrack->getRawData(); - for (int i = 0; i < (int)commChunk.m_frames * commChunk.m_chans; ++i) { - *(waveData.get() + 3 * i) = *(begin + 4 * i + 1); - *(waveData.get() + 3 * i + 1) = *(begin + 4 * i + 2); - *(waveData.get() + 3 * i + 2) = *(begin + 4 * i + 3); - } + break; } + } else { + memcpy((void *)waveData.get(), (void *)sndtrack->getRawData(), + soundDataCount); } ssndChunk.m_waveData = std::move(waveData); diff --git a/toonz/sources/sound/ffmpeg/tsio_ffmpeg.cpp b/toonz/sources/sound/ffmpeg/tsio_ffmpeg.cpp new file mode 100644 index 00000000..f6984fa7 --- /dev/null +++ b/toonz/sources/sound/ffmpeg/tsio_ffmpeg.cpp @@ -0,0 +1,35 @@ +#include + +#include "tmachine.h" +#include "tsio_ffmpeg.h" +#include "tsystem.h" +#include "tfilepath_io.h" +#include "tsound_t.h" +#include "toonz/preferences.h" +#include "toonz/toonzfolders.h" +#include "thirdparty.h" + +#include +#include + +//============================================================================== + +TSoundTrackReaderFFmpeg::TSoundTrackReaderFFmpeg(const TFilePath &fp) + : TSoundTrackReader(fp) {} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackReaderFFmpeg::load() { + QProcess ffmpeg; + QByteArray rawAudio; + + // Pipe the audio through ffmpeg + ThirdParty::runFFmpegAudio(ffmpeg, m_path.getQString(), "-"); + if (!ThirdParty::readFFmpegAudio(ffmpeg, rawAudio)) return nullptr; + + long sampleCount = rawAudio.size() / 4; + + TSoundTrack *track = new TSoundTrackStereo16(44100, 2, sampleCount); + memcpy((char *)track->getRawData(), rawAudio.constData(), sampleCount * 4); + return track; +} \ No newline at end of file diff --git a/toonz/sources/sound/ffmpeg/tsio_ffmpeg.h b/toonz/sources/sound/ffmpeg/tsio_ffmpeg.h new file mode 100644 index 00000000..c2a84e65 --- /dev/null +++ b/toonz/sources/sound/ffmpeg/tsio_ffmpeg.h @@ -0,0 +1,31 @@ +#pragma once + +#ifndef TSIO_FFMPEG_INCLUDED +#define TSIO_FFMPEG_INCLUDED + +#include "tsound_io.h" + +//========================================================== +/*! +The class TSoundTrackReaderFFmpeg reads audio files +*/ +class TSoundTrackReaderFFmpeg final : public TSoundTrackReader { +public: + TSoundTrackReaderFFmpeg(const TFilePath &fp); + ~TSoundTrackReaderFFmpeg() {} + + /*! +Loads audio file whose path has been specified in the constructor. +It returns a TSoundTrackP created from the audio file +*/ + TSoundTrackP load() override; + + /*! +Returns a soundtrack reader able to read audio files +*/ + static TSoundTrackReader *create(const TFilePath &fp) { + return new TSoundTrackReaderFFmpeg(fp); + } +}; + +#endif \ No newline at end of file diff --git a/toonz/sources/sound/mp3/tsio_mp3.cpp b/toonz/sources/sound/mp3/tsio_mp3.cpp index b6d7e58e..58d7244e 100644 --- a/toonz/sources/sound/mp3/tsio_mp3.cpp +++ b/toonz/sources/sound/mp3/tsio_mp3.cpp @@ -8,6 +8,8 @@ #include "tenv.h" #include "toonz/preferences.h" #include "toonz/toonzfolders.h" +#include "thirdparty.h" + #include #include @@ -37,50 +39,6 @@ TSoundTrackP TSoundTrackReaderMp3::load() { return track; } -bool FfmpegAudio::checkFfmpeg() { - // check the user defined path in preferences first - QString path = Preferences::instance()->getFfmpegPath() + "/ffmpeg"; -#if defined(_WIN32) - path = path + ".exe"; -#endif - if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; - - // check the Tahoma2D root directory next - path = QDir::currentPath() + "/ffmpeg"; -#if defined(_WIN32) - path = path + ".exe"; -#endif - if (TSystem::doesExistFileOrLevel(TFilePath(path))) { - Preferences::instance()->setValue(ffmpegPath, QDir::currentPath()); - return true; - } - -#ifdef MACOSX - path = QDir::currentPath() + "/" + - QString::fromStdString(TEnv::getApplicationFileName()) + - ".app/ffmpeg/ffmpeg"; - if (TSystem::doesExistFileOrLevel(TFilePath(path))) { - Preferences::instance()->setValue( - ffmpegPath, QDir::currentPath() + "/" + - QString::fromStdString(TEnv::getApplicationFileName()) + - ".app/ffmpeg/"); - return true; - } -#endif - -#if defined(LINUX) || defined(FREEBSD) - QString currentPath = TEnv::getWorkingDirectory().getQString(); - path = currentPath + "/ffmpeg/ffmpeg"; - if (TSystem::doesExistFileOrLevel(TFilePath(path))) { - Preferences::instance()->setValue(ffmpegPath, currentPath + "/ffmpeg/"); - return true; - } -#endif - - // give up - return false; -} - TFilePath FfmpegAudio::getFfmpegCache() { QString cacheRoot = ToonzFolder::getCacheRootFolder().getQString(); if (!TSystem::doesExistFileOrLevel(TFilePath(cacheRoot + "/ffmpeg"))) { @@ -93,10 +51,8 @@ TFilePath FfmpegAudio::getFfmpegCache() { void FfmpegAudio::runFfmpeg(QStringList args) { // write the file - QString m_ffmpegPath = Preferences::instance()->getFfmpegPath(); - std::string strFfmpegPath = m_ffmpegPath.toStdString(); QProcess ffmpeg; - ffmpeg.start(m_ffmpegPath + "/ffmpeg", args); + ThirdParty::runFFmpeg(ffmpeg, args); ffmpeg.waitForFinished(30000); QString results = ffmpeg.readAllStandardError(); results += ffmpeg.readAllStandardOutput(); diff --git a/toonz/sources/sound/mp3/tsio_mp3.h b/toonz/sources/sound/mp3/tsio_mp3.h index 60797c6e..a5d0a47a 100644 --- a/toonz/sources/sound/mp3/tsio_mp3.h +++ b/toonz/sources/sound/mp3/tsio_mp3.h @@ -32,7 +32,6 @@ Returns a soundtrack reader able to read .mp3 audio files class FfmpegAudio { public: TFilePath getRawAudio(TFilePath path); - static bool checkFfmpeg(); private: TFilePath getFfmpegCache(); diff --git a/toonz/sources/sound/tsio.cpp b/toonz/sources/sound/tsio.cpp index 9c48f8aa..a6b8093c 100644 --- a/toonz/sources/sound/tsio.cpp +++ b/toonz/sources/sound/tsio.cpp @@ -1,10 +1,15 @@ #include "tnzsound.h" -#include "tsio.h" // #include "tpluginmanager.h" #include "tsound_io.h" #include "tfiletype.h" +#include "thirdparty.h" + +#include "wav/tsio_wav.h" +#include "aiff/tsio_aiff.h" +#include "raw/tsio_raw.h" +#include "ffmpeg/tsio_ffmpeg.h" // static TPluginInfo info("soundIOPlugin"); @@ -25,10 +30,19 @@ void initSoundIo() { TSoundTrackWriter::define("raw", TSoundTrackWriterRaw::create); TFileType::declare("raw", TFileType::AUDIO_LEVEL); - if (FfmpegAudio::checkFfmpeg()) { - TSoundTrackReader::define("mp3", TSoundTrackReaderMp3::create); - // TSoundTrackWriter::define("mp3", TSoundTrackWriterMp3::create); + if (ThirdParty::checkFFmpeg()) { + TSoundTrackReader::define("mp3", TSoundTrackReaderFFmpeg::create); TFileType::declare("mp3", TFileType::AUDIO_LEVEL); + TSoundTrackReader::define("ogg", TSoundTrackReaderFFmpeg::create); + TFileType::declare("ogg", TFileType::AUDIO_LEVEL); + TSoundTrackReader::define("flac", TSoundTrackReaderFFmpeg::create); + TFileType::declare("flac", TFileType::AUDIO_LEVEL); + TSoundTrackReader::define("m4a", TSoundTrackReaderFFmpeg::create); + TFileType::declare("m4a", TFileType::AUDIO_LEVEL); + TSoundTrackReader::define("aac", TSoundTrackReaderFFmpeg::create); + TFileType::declare("aac", TFileType::AUDIO_LEVEL); + TSoundTrackReader::define("ffaudio", TSoundTrackReaderFFmpeg::create); + TFileType::declare("ffaudio", TFileType::AUDIO_LEVEL); } // return &info; } diff --git a/toonz/sources/sound/tsio.h b/toonz/sources/sound/tsio.h deleted file mode 100644 index 4c35aa47..00000000 --- a/toonz/sources/sound/tsio.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#ifndef TSIO_INCLUDED -#define TSIO_INCLUDED - -#include "wav/tsio_wav.h" -#include "aiff/tsio_aiff.h" -#include "raw/tsio_raw.h" -#include "mp3/tsio_mp3.h" - -#endif diff --git a/toonz/sources/sound/tsioutils.cpp b/toonz/sources/sound/tsioutils.cpp index b7c6b3d4..b775a76f 100644 --- a/toonz/sources/sound/tsioutils.cpp +++ b/toonz/sources/sound/tsioutils.cpp @@ -5,11 +5,36 @@ #include "tsioutils.h" //------------------------------------------------------------------------------ -void swapAndCopySamples(short *srcBuffer, short *dstBuffer, +void swapAndCopy16Bits(const short *srcBuffer, short *dstBuffer, TINT32 sampleCount) { - short *srcSample = srcBuffer; + const short *srcSample = srcBuffer; short *dstSample = dstBuffer; - short *endSrcSample = srcSample + sampleCount; + const short *endSrcSample = srcSample + sampleCount; while (srcSample < endSrcSample) *dstSample++ = swapShort(*srcSample++); } + +//------------------------------------------------------------------------------ +void swapAndCopy32Bits(const TINT32 *srcBuffer, TINT32 *dstBuffer, + TINT32 sampleCount) { + const TINT32 *srcSample = srcBuffer; + TINT32 *dstSample = dstBuffer; + + const TINT32 *endSrcSample = srcSample + sampleCount; + while (srcSample < endSrcSample) *dstSample++ = swapTINT32(*srcSample++); +} + +//------------------------------------------------------------------------------ +void swapAndCopy24Bits(const void *srcBuffer, void *dstBuffer, + TINT32 sampleCount) { + const UCHAR *srcData = (const UCHAR *)srcBuffer; + UCHAR *dstData = (UCHAR *)dstBuffer; + if (sampleCount <= 0) return; + while (--sampleCount) { + dstData[0] = srcData[2]; + dstData[1] = srcData[1]; + dstData[2] = srcData[0]; + srcData += 3; + dstData += 3; + } +} diff --git a/toonz/sources/sound/tsioutils.h b/toonz/sources/sound/tsioutils.h index abf75c0d..054f3940 100644 --- a/toonz/sources/sound/tsioutils.h +++ b/toonz/sources/sound/tsioutils.h @@ -3,6 +3,13 @@ #ifndef TSIOUTILS_INCLUDED #define TSIOUTILS_INCLUDED -void swapAndCopySamples(short *srcBuffer, short *dstBuffer, TINT32 sampleCount); +void swapAndCopy16Bits(const short *srcBuffer, short *dstBuffer, + TINT32 sampleCount); + +void swapAndCopy32Bits(const TINT32 *srcBuffer, TINT32 *dstBuffer, + TINT32 sampleCount); + +void swapAndCopy24Bits(const void *srcBuffer, void *dstBuffer, + TINT32 sampleCount); #endif diff --git a/toonz/sources/sound/wav/tsio_wav.cpp b/toonz/sources/sound/wav/tsio_wav.cpp index 996383c1..e3567327 100644 --- a/toonz/sources/sound/wav/tsio_wav.cpp +++ b/toonz/sources/sound/wav/tsio_wav.cpp @@ -4,7 +4,7 @@ #include "tsio_wav.h" #include "tsystem.h" #include "tfilepath_io.h" -#include "tsop.h" +#include "tsioutils.h" using namespace std; @@ -12,11 +12,6 @@ using namespace std; TNZ_LITTLE_ENDIAN undefined !! #endif - //------------------------------------------------------------------------------ - - void - swapAndCopySamples(short *srcBuffer, short *dstBuffer, TINT32 sampleCount); - //============================================================================== // TWAVChunk: classe base per i vari chunk WAV @@ -248,94 +243,48 @@ TSoundTrackP TSoundTrackReaderWav::load() { if (fmtChunk && dataChunk) { TINT32 sampleCount = dataChunk->m_length / fmtChunk->m_bytesPerSample; - bool signedSample = (fmtChunk->m_bitPerSample != 8); + int sampleType = 0; - track = TSoundTrack::create( - (int)fmtChunk->m_sampleRate, fmtChunk->m_bitPerSample, - fmtChunk->m_chans, sampleCount, signedSample, fmtChunk->m_encodingType); + if (fmtChunk->m_encodingType == 1) // WAVE_FORMAT_PCM + sampleType = (fmtChunk->m_bitPerSample == 8) ? TSound::UINT : TSound::INT; + else if (fmtChunk->m_encodingType == 3) // WAVE_FORMAT_IEEE_FLOAT + sampleType = TSound::FLOAT; + + if (sampleType) // valid sample type + track = TSoundTrack::create((int)fmtChunk->m_sampleRate, + fmtChunk->m_bitPerSample, fmtChunk->m_chans, + sampleCount, sampleType); if (track) { - switch (fmtChunk->m_bitPerSample) { - case 8: + if (!TNZ_LITTLE_ENDIAN) { + switch (fmtChunk->m_bitPerSample) { + case 16: + swapAndCopy16Bits((short *)dataChunk->m_samples.get(), + (short *)track->getRawData(), + sampleCount * fmtChunk->m_chans); + break; + case 24: + swapAndCopy24Bits((short *)dataChunk->m_samples.get(), + (short *)track->getRawData(), + sampleCount * fmtChunk->m_chans); + break; + case 32: + swapAndCopy32Bits((TINT32 *)dataChunk->m_samples.get(), + (TINT32 *)track->getRawData(), + sampleCount * fmtChunk->m_chans); + break; + default: + memcpy((void *)track->getRawData(), + (void *)(dataChunk->m_samples.get()), + sampleCount * fmtChunk->m_bytesPerSample); + break; + } + } else { memcpy((void *)track->getRawData(), (void *)(dataChunk->m_samples.get()), sampleCount * fmtChunk->m_bytesPerSample); - break; - case 16: - if (!TNZ_LITTLE_ENDIAN) - swapAndCopySamples((short *)dataChunk->m_samples.get(), - (short *)track->getRawData(), - sampleCount * fmtChunk->m_chans); - else - memcpy((void *)track->getRawData(), - (void *)(dataChunk->m_samples.get()), - sampleCount * fmtChunk->m_bytesPerSample); - //#endif - break; - case 24: - // NOTE: This effectively changes from 24bit to 32bit bitPerSample - if (!TNZ_LITTLE_ENDIAN) { - UCHAR *begin = (UCHAR *)track->getRawData(); - for (int i = 0; i < (int)(sampleCount * fmtChunk->m_chans); ++i) { - *(begin + 4 * i) = 0; - *(begin + 4 * i + 1) = *(dataChunk->m_samples.get() + 3 * i + 2); - *(begin + 4 * i + 2) = *(dataChunk->m_samples.get() + 3 * i + 1); - *(begin + 4 * i + 3) = *(dataChunk->m_samples.get() + 3 * i); - } - } else { - UCHAR *begin = (UCHAR *)track->getRawData(); - for (int i = 0; i < (int)(sampleCount * fmtChunk->m_chans); ++i) { - memcpy((void *)(begin + 4 * i), - (void *)(dataChunk->m_samples.get() + 3 * i), 3); - *(begin + 4 * i + 3) = 0; - } - } - //#endif - break; - case 32: - if (!TNZ_LITTLE_ENDIAN) { - swapAndCopySamples((short *)dataChunk->m_samples.get(), - (short *)track->getRawData(), - sampleCount * fmtChunk->m_chans); - } else { - memcpy((void *)track->getRawData(), - (void *)(dataChunk->m_samples.get()), - sampleCount * fmtChunk->m_bytesPerSample); - } - break; - } - - // Convert all WAV to 32bit PCM - if (fmtChunk->m_bitPerSample != 32 || fmtChunk->m_encodingType != WAVE_FORMAT_PCM) { - TSoundTrackP origTrack = track; - TSoundTrackFormat fmt = track->getFormat(); - fmt.m_bitPerSample = 32; - fmt.m_formatType = WAVE_FORMAT_PCM; - track = TSop::convert(origTrack, fmt); } } - - /*if (!TNZ_LITTLE_ENDIAN) -{ -if (fmtChunk->m_bitPerSample > 8) -{ -assert(fmtChunk->m_bitPerSample <= 16); -swapAndCopySamples( -(short*)dataChunk->m_samples, -(short*)track->getRawData(), -sampleCount*fmtChunk->m_chans); -} -else -memcpy( - (void*)track->getRawData(), -(void*)(dataChunk->m_samples), -sampleCount*fmtChunk->m_bytesPerSample); -} -else -memcpy( -(void*)track->getRawData(), -(void*)(dataChunk->m_samples), -sampleCount*fmtChunk->m_bytesPerSample);*/ } if (fmtChunk) delete fmtChunk; @@ -375,7 +324,7 @@ bool TSoundTrackWriterWav::save(const TSoundTrackP &sndtrack) { TFMTChunk fmtChunk(16); - fmtChunk.m_encodingType = 1; // PCM + fmtChunk.m_encodingType = sndtrack->getSampleType() & TSound::WMASK; fmtChunk.m_chans = sndtrack->getChannelCount(); fmtChunk.m_sampleRate = sndtrack->getSampleRate(); fmtChunk.m_avgBytesPerSecond = (sndtrack->getBitPerSample() / 8) * @@ -391,40 +340,32 @@ bool TSoundTrackWriterWav::save(const TSoundTrackP &sndtrack) { if (!TNZ_LITTLE_ENDIAN) RIFFChunkLength = swapTINT32(RIFFChunkLength); -// era if defined(MACOSX) -#if (!TNZ_LITTLE_ENDIAN) - { - if (fmtChunk.m_bitPerSample == 8) - memcpy((void *)waveData.get(), (void *)sndtrack->getRawData(), soundDataLength); - else if (fmtChunk.m_bitPerSample == 16) { - swapAndCopySamples((short *)sndtrack->getRawData(), (short *)waveData.get(), - sndtrack->getSampleCount() * fmtChunk.m_chans); - } else if (fmtChunk.m_bitPerSample == 24) { // swap e togliere quarto byte - UCHAR *begin = (UCHAR *)sndtrack->getRawData(); - for (int i = 0; i < (int)sndtrack->getSampleCount() * fmtChunk.m_chans; - ++i) { - *(waveData.get() + 3 * i) = *(begin + 4 * i + 3); - *(waveData.get() + 3 * i + 1) = *(begin + 4 * i + 2); - *(waveData.get() + 3 * i + 2) = *(begin + 4 * i + 1); - } - } - } -#else - { - if (fmtChunk.m_bitPerSample != 24) + if (!TNZ_LITTLE_ENDIAN) { + switch (fmtChunk.m_bitPerSample) { + case 16: + swapAndCopy16Bits((short *)sndtrack->getRawData(), + (short *)waveData.get(), + sndtrack->getSampleCount() * fmtChunk.m_chans); + break; + case 24: + swapAndCopy24Bits((void *)sndtrack->getRawData(), (void *)waveData.get(), + sndtrack->getSampleCount() * fmtChunk.m_chans); + break; + case 32: + swapAndCopy32Bits((TINT32 *)sndtrack->getRawData(), + (TINT32 *)waveData.get(), + sndtrack->getSampleCount() * fmtChunk.m_chans); + break; + default: memcpy((void *)waveData.get(), (void *)sndtrack->getRawData(), soundDataLength); - else { // togliere quarto byte - UCHAR *begin = (UCHAR *)sndtrack->getRawData(); - for (int i = 0; i < (int)sndtrack->getSampleCount() * fmtChunk.m_chans; - ++i) { - *(waveData.get() + 3 * i) = *(begin + 4 * i); - *(waveData.get() + 3 * i + 1) = *(begin + 4 * i + 1); - *(waveData.get() + 3 * i + 2) = *(begin + 4 * i + 2); - } + break; } + } else { + memcpy((void *)waveData.get(), (void *)sndtrack->getRawData(), + soundDataLength); } -#endif + dataChunk.m_samples = std::move(waveData); os.write("RIFF", 4); diff --git a/toonz/sources/stdfx/CMakeLists.txt b/toonz/sources/stdfx/CMakeLists.txt index b29fd9ac..4de06ec8 100644 --- a/toonz/sources/stdfx/CMakeLists.txt +++ b/toonz/sources/stdfx/CMakeLists.txt @@ -86,6 +86,11 @@ set(HEADERS iwa_bokeh_advancedfx.h iwa_bokeh_util.h globalcontrollablefx.h + iwa_floorbumpfx.h + iwa_tangentflowfx.h + iwa_flowblurfx.h + iwa_flowpaintbrushfx.h + iwa_motionflowfx.h ) if(OpenCV_FOUND) @@ -280,6 +285,11 @@ set(SOURCES iwa_rainbowfx.cpp iwa_bokeh_advancedfx.cpp iwa_bokeh_util.cpp + iwa_floorbumpfx.cpp + iwa_tangentflowfx.cpp + iwa_flowblurfx.cpp + iwa_flowpaintbrushfx.cpp + iwa_motionflowfx.cpp ) if(OpenCV_FOUND) diff --git a/toonz/sources/stdfx/adjustlevelsfx.cpp b/toonz/sources/stdfx/adjustlevelsfx.cpp index d2df1c59..7be71476 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での計算が可能になるにあたって、 + // 値の範囲を0-255で制限したくない + // → + // スライダは0-255のまま、入力フィールドは好きな数字を入れられるようにするか? + // (Nuke の Histogramエフェクトのように) 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 dde4f3ed..d2b541f4 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 3e4c62bc..568371fd 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 29a52f6f..e9aabf1f 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 48491308..83304d14 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 89d516ad..00d1e1ae 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 56533c93..3fec09e0 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 46286009..acde3657 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 68803502..60983fee 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 a2514943..9d22ddc4 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 8b0bad83..3f5eb247 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 c5ddd90a..03b9b160 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縺ァ縺ェ縺代l縺ー縺ェ繧峨↑縺・ */ + 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 820947b3..5608568a 100644 --- a/toonz/sources/stdfx/igs_density.h +++ b/toonz/sources/stdfx/igs_density.h @@ -10,23 +10,11 @@ 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縺ァ縺ェ縺代l縺ー縺ェ繧峨↑縺・ */ + 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 15bede50..1b9d702b 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 /* 豎ゅa繧狗判蜒・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 /* 豎ゅa繧狗判蜒・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, /* 豎ゅa繧狗判蜒・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); + } + /* 荳€縺、蜑阪→蜷後§逕サ蜒上↑繧牙・逅・○縺壻スソ縺・屓縺励※鬮倬€溷喧縺吶k */ + 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, /* 豎ゅa繧狗判蜒・out)縺ィ蜷後§鬮倥€∝ケ・€…h謨ー */ /* 計算バッファ */ - , - 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 0f8fdb7a..ea6214fc 100644 --- a/toonz/sources/stdfx/igs_gaussian_blur.h +++ b/toonz/sources/stdfx/igs_gaussian_blur.h @@ -15,33 +15,17 @@ 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 */ - - /* 計算バッファ */ - , - void *buffer, int buffer_bytes - // Must be igs::gaussian_blur_hv::buffer_bytes(-) - + const float* ref, /* 豎ゅa繧狗判蜒・out)縺ィ蜷後§鬮倥€∝ケ・€…h謨ー */ + 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 3ef717a6..ad778401 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逕サ蜒上↓繝弱う繧コ繧偵・縺帙k*/ +void change_(float *image_array, const int height, const int width, + const int channels, const noise_ref_ &noi, + const float *ref, /* 豎ゅa繧狗判蜒・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,37 +136,35 @@ 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) { using namespace igs::image::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,12 +203,11 @@ 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; } } } @@ -254,27 +215,13 @@ Alpha値がゼロでもRGB値は存在する(してもよい) */ } 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・€…hannels謨ー */ 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,42 @@ 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 - - , - noi_height, noi_width, noi_channels, noi_bits - - , - xoffset, yoffset, from_rgba); + noise_ref_ noi(noi_image_array, height, width, 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 013e2d30..cbb82b2a 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・€…hannels謨ー */ + 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 58de2237..6d6add48 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逕サ蜒上↓繝弱う繧コ繧偵・縺帙k*/ +void change_(float *image_array, const int height, const int width, + const int channels, + const float *ref, /* 豎ゅa繧狗判蜒・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); + // 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 != 0) { - rr2 = rr1 + (rr2 - rr1) * refv; - gg2 = gg1 + (gg2 - gg1) * refv; - bb2 = bb1 + (bb2 - bb1) * refv; + 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・€…hannels謨ー */ 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 99d9c850..810af3e8 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・€…hannels謨ー */ + 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 7abdcd3a..1a7dbac6 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逕サ蜒上↓繝弱う繧コ繧偵・縺帙k ------*/ +void change_(float *image_array, const int width, const int height, + const int channels, + const float *ref, /* 豎ゅa繧狗判蜒・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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・*/ /* image_arrayに余白が変化してもノイズパターンが変わらない - ようにするためにカメラエリアを指定する */ - , + 繧医≧縺ォ縺吶k縺溘a縺ォ繧ォ繝。繝ゥ繧ィ繝ェ繧「繧呈欠螳壹☆繧・*/ 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 36e16abf..31d358a9 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・/ /* 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 95eba740..9471d4b9 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逕サ蜒上↓繝弱う繧コ繧偵・縺帙k */ +void change_(float *image_array, const int height, const int width, + const int channels, const noise_ref_ &noi, + const float *ref, /* 豎ゅa繧狗判蜒・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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・€…hannels謨ー */ 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 + 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, + 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 d6655010..09b76498 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・€…hannels謨ー */ + 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 be67ae8b..f6af0626 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,137 @@ 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逕サ蜒上↓繝弱う繧コ繧偵・縺帙k */ +void change_(float *image_array, const int height, const int width, + const int channels, + const float *ref, /* 豎ゅa繧狗判蜒・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); + // 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 != 0) { - rr2 = rr1 + (rr2 - rr1) * refv; - gg2 = gg1 + (gg2 - gg1) * refv; - bb2 = bb1 + (bb2 - bb1) * refv; + 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・€…hannels謨ー */ 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 +184,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 +222,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 234f5df7..25b56855 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・€…hannels謨ー */ + 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 63805bf0..eddf7830 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逕サ蜒上↓繝弱う繧コ繧偵・縺帙k ------*/ + +void change_(float *image_array, const int width, const int height, + const int channels, + const float *ref, /* 豎ゅa繧狗判蜒・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; + + /* 蜿ら・逕サ蜒上≠繧後・繝斐け繧サ繝ォ蜊倅ス阪・逕サ蜒丞、牙喧驥上r蠕励k */ + if (ref != nullptr) { + refv *= (*ref); + ref++; /* continue;縺ョ蜑阪↓陦後≧縺薙→ */ + } + /* 蜉邂怜粋謌舌〒縲、lpha蛟、繧シ繝ュ縺ェ繧嘘GB蛟、繧定ィ育ョ励☆繧句ソ・ヲ√・縺ェ縺・*/ + if (add_blend_sw && (0 == image_array[alp])) { + continue; + } + /* 蜉邂怜粋謌舌〒縺ェ縺就lpha蜷域・縺ョ譎ゅ・縲・ +Alpha蛟、縺後ぞ繝ュ縺ァ繧3GB蛟、縺ッ蟄伜惠縺吶k(縺励※繧ゅh縺・ */ + + /* 繝槭せ繧ッSW縺薫N縲√↑繧牙、牙喧繧樽ask */ + 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())) /* 繝弱う繧コ縺敬sv縺ョ縺ゥ繧後°荳€縺、縺ッ縺ゅk */ + ) { + 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())) /* 繝弱う繧コ縺敬sv縺ョ縺ゥ繧後°荳€縺、縺ッ縺ゅk */ + ) { + for (int yy = 0; yy < height; ++yy) { + for (int xx = 0; xx < width; ++xx, image_array += channels) { + /* 螟牙喧驥丞・譛溷€、 */ + float refv = 1.f; + + /* 蜿ら・逕サ蜒上≠繧後・繝斐け繧サ繝ォ蜊倅ス阪・逕サ蜒丞、牙喧驥上r蠕励k */ + 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; + + /* 蜿ら・逕サ蜒上≠繧後・繝斐け繧サ繝ォ蜊倅ス阪・逕サ蜒丞、牙喧驥上r蠕励k */ + 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・*/ /* 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)縺ァ繝峨ャ繝医ヮ繧、繧コ繧貞刈縺医k */ + // 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 a37e339f..3093067c 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, /* 豎ゅa繧狗判蜒上→蜷後§鬮倥€∝ケ・*/ /* 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_ifx_common.h b/toonz/sources/stdfx/igs_ifx_common.h index 64754f72..39a35bdc 100644 --- a/toonz/sources/stdfx/igs_ifx_common.h +++ b/toonz/sources/stdfx/igs_ifx_common.h @@ -55,7 +55,7 @@ void copy_except_margin(const T *in, const int margin, T *out, const int hh, } } } -} +} // namespace image namespace color { template double ref_value(const T *ref, const int cc, const int ref_max, @@ -64,49 +64,55 @@ double ref_value(const T *ref, const int cc, const int ref_max, using namespace igs::image::rgba; switch (ref_mode) { case 0: - return static_cast(ref[red]) / ref_max; + return static_cast(ref[red]) / static_cast(ref_max); break; case 1: - return static_cast(ref[gre]) / ref_max; + return static_cast(ref[gre]) / static_cast(ref_max); break; case 2: - return static_cast(ref[blu]) / ref_max; + return static_cast(ref[blu]) / static_cast(ref_max); break; case 3: - return static_cast(ref[alp]) / ref_max; + return static_cast(ref[alp]) / static_cast(ref_max); break; case 4: return /* 輝度(Luminance)(CCIR Rec.601) */ - 0.298912 * static_cast(ref[red]) / ref_max + - 0.586611 * static_cast(ref[gre]) / ref_max + - 0.114478 * static_cast(ref[blu]) / ref_max; + 0.298912 * static_cast(ref[red]) / + static_cast(ref_max) + + 0.586611 * static_cast(ref[gre]) / + static_cast(ref_max) + + 0.114478 * static_cast(ref[blu]) / + static_cast(ref_max); break; } } else if (igs::image::rgb::siz == cc) { using namespace igs::image::rgb; switch (ref_mode) { case 0: - return static_cast(ref[red]) / ref_max; + return static_cast(ref[red]) / static_cast(ref_max); break; case 1: - return static_cast(ref[gre]) / ref_max; + return static_cast(ref[gre]) / static_cast(ref_max); break; case 2: - return static_cast(ref[blu]) / ref_max; + return static_cast(ref[blu]) / static_cast(ref_max); break; case 3: return /* 輝度(Luminance)(CCIR Rec.601) */ - 0.298912 * static_cast(ref[red]) / ref_max + - 0.586611 * static_cast(ref[gre]) / ref_max + - 0.114478 * static_cast(ref[blu]) / ref_max; + 0.298912 * static_cast(ref[red]) / + static_cast(ref_max) + + 0.586611 * static_cast(ref[gre]) / + static_cast(ref_max) + + 0.114478 * static_cast(ref[blu]) / + static_cast(ref_max); break; } } else if (1 == cc) { - return static_cast(ref[0]) / ref_max; + return static_cast(ref[0]) / static_cast(ref_max); } return 1.0; } -} -} +} // namespace color +} // namespace igs #endif /* !igs_ifx_common_h */ diff --git a/toonz/sources/stdfx/igs_level_auto_in_camera.cpp b/toonz/sources/stdfx/igs_level_auto_in_camera.cpp index ca08959d..42836422 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.縺セ縺喞amera繧ィ繝ェ繧「蜀・・譛€螟ァ蛟、縲∵怙蟆丞€、繧呈アゅa繧・*/ +#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.譛€螟ァ蛟、縲∵怙蟆丞€、縺九i螟画鋤繝・・繝悶Ν繧呈アゅa繧・*/ + // 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); + + /* 逕サ蜒丞・菴薙rlevel螟画鋤縺吶k */ + 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 89409d74..71758ed8 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 60a7ec0a..0421e1e4 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); } + // 繧ャ繝ウ繝櫁」懈ュ」縺ョ邱壹r逶エ邱壹〒蟒カ髟キ縺吶k + 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, /* 豎ゅa繧狗判蜒・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 譛€螟ァ蛟、縲∵怙蟆丞€、縺九i螟画鋤繝・・繝悶Ν繧呈アゅa繧・*/ + 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縲・.0 --> 0縲徇ul_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); + } + } + + // 繝・・繝悶Ν蛟、繧定ソ斐☆繝ゥ繝繝€蠑上€・n縺・繧医j螟ァ縺阪>蝣エ蜷医・max縺ョ邱壹r逶エ邱壹〒蟒カ髟キ + auto getTableVal = [&](int channel, float in) { + if (in <= 0.f) { // 蜈磯ュ縺ョ蛟、繧定ソ斐☆ + return tables[channel][0]; + } else if (in <= + 1.f) { // 繝・・繝悶Ν遽・峇縺ォ蜿弱∪縺」縺ヲ縺・k蝣エ蜷医€∝燕蠕後・蛟、縺ァ邱壼ス「陬憺俣 + 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縺・繧医j螟ァ縺阪>蝣エ蜷・ + // 蛯セ縺・ + 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螟画鋤縺吶k */ + 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; + + /* 蜿ら・逕サ蜒上≠繧後・繝斐け繧サ繝ォ蜊倅ス阪・逕サ蜒丞、牙喧驥上r蠕励k */ + 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蜃ヲ逅・@縺溷€、繧池efv縺ョ螟牙喧驥乗ッ斐〒蜉縺医k */ + if (alpha_sw) { + image_array[alp] = + refchk_(image_array[alp], getTableVal(alp, image_array[alp]), refv); + } + + /* 蜉邂怜粋謌舌〒縲、lpha蛟、繧シ繝ュ縺ェ繧嘘GB蛟、繧定ィ育ョ励☆繧句ソ・ヲ√・縺ェ縺・*/ + if (add_blend_sw && areAlmostEqual(image_array[alp], 0.)) { + continue; + } + /* 蜉邂怜粋謌舌〒縺ェ縺就lpha蜷域・縺ョ譎ゅ・縲、lpha蛟、縺後ぞ繝ュ縺ァ繧・ + RGB蛟、縺ッ蟄伜惠縺吶k(縺励※繧ゅh縺・縺ョ險育ョ励☆繧・*/ + + /* 蜉邂怜粋謌舌〒縲√◎縺ョ蛟、縺勲ax縺ァ縺ェ縺代l縺ー螟牙喧驥上↓荵礼ョ・*/ + if (add_blend_sw && (image_array[alp] < 1.f)) { + refv *= static_cast(image_array[alp]); + } + + /* RGB縺ョLevel蜃ヲ逅・@縺溷€、繧池efv縺ョ螟牙喧驥乗ッ斐〒蜉縺医k */ + 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蜃ヲ逅・@縺溷€、繧池efv縺ョ螟牙喧驥乗ッ斐〒蜉縺医k */ + 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_line_blur.cpp b/toonz/sources/stdfx/igs_line_blur.cpp index 95db3eb7..087b4dc1 100644 --- a/toonz/sources/stdfx/igs_line_blur.cpp +++ b/toonz/sources/stdfx/igs_line_blur.cpp @@ -88,7 +88,7 @@ void pri_funct_cv_end(void) { /* Windowsではvsnprintf()の頭にアンダーバーが付く!!! */ #if defined _WIN32 -#define vsnprintf(buf, len, fmt, ap) _vsnprintf(buf, len, fmt, ap) +#define vsnprintf(buf, len, fmt, ap) _vsnprintf_s(buf, len, fmt, ap) #endif static const char *pri_param_cp_com_name = "#"; @@ -1660,9 +1660,9 @@ void pixel_line_node::_get_link_line_selecter_vector(pixel_point_node *clp_crnt, pixel_point_node *pixel_line_node::_get_link_line_selecter( double d_xv, double d_yv, pixel_point_node *clp_crnt, int32_t i32_count) { - int32_t ii, i32_pos; - double da_xv[LINK_NEAR_COUNT], da_yv[LINK_NEAR_COUNT], - da_radian[LINK_NEAR_COUNT], d_radian; + int32_t ii, i32_pos = 0; + double da_xv[LINK_NEAR_COUNT] = {0.}, da_yv[LINK_NEAR_COUNT] = {0.}, + da_radian[LINK_NEAR_COUNT] = {0.}, d_radian; /* あってはならないプログラムバグのチェック */ assert((0.0 != d_xv) || (0.0 != d_yv)); diff --git a/toonz/sources/stdfx/igs_maxmin.cpp b/toonz/sources/stdfx/igs_maxmin.cpp index 59401cef..dd1ab3f3 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 a5157486..003555d4 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蛟、繧段n縺ィ縺励※菴ソ逕ィ */ + , + 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 /* 蜈・€、繧偵>繧後→縺・※縲∫オ先棡繧貞・繧後k */ +) { + const double div_val = 1.; + + /* 險育ョ礼ッ・峇縺ョ逕サ蜒丞€、繧定ィ育ョ励ヰ繝・ヵ繧。(tracks)縺ォ蜈・繧後k */ + 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繝√Ε繝ウ繝阪Ν縺後↑縺・エ蜷医・縺薙%縺ァ邨ゅo繧・*/ + 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蛟、繧段n縺ィ縺励※菴ソ逕ィ */ + , + const int hh, const int ww, const int ch, + const float *ref /* 豎ゅa繧狗判蜒・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 /* 蜈・€、繧偵>繧後→縺・※縲∫オ先棡繧貞・繧後k */ +) { + 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繝√Ε繝ウ繝阪Ν縺後↑縺・エ蜷医・縺薙%縺ァ邨ゅo繧・*/ + 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 772893a9..793317a9 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_radial_blur.cpp b/toonz/sources/stdfx/igs_radial_blur.cpp index 5a5d63d7..52e2a863 100644 --- a/toonz/sources/stdfx/igs_radial_blur.cpp +++ b/toonz/sources/stdfx/igs_radial_blur.cpp @@ -119,9 +119,8 @@ public: const QPointF center(this->center_.x, this->center_.y); - /* Pixel位置(0.5 1.5 2.5 ...) */ - const QPointF p(static_cast(xx) + 0.5f, - static_cast(yy) + 0.5f); + /* Pixel位置 */ + const QPointF p(static_cast(xx), static_cast(yy)); /* 中心からPixel位置へのベクトルと長さ */ const QVector2D v(p - center); diff --git a/toonz/sources/stdfx/igs_rotate_blur.cpp b/toonz/sources/stdfx/igs_rotate_blur.cpp index 4e0684ac..ff9c6fcf 100644 --- a/toonz/sources/stdfx/igs_rotate_blur.cpp +++ b/toonz/sources/stdfx/igs_rotate_blur.cpp @@ -111,9 +111,8 @@ public: } const QPointF center(this->center_.x, this->center_.y); - /* Pixel位置(0.5 1.5 2.5 ...) */ - const QPointF p(static_cast(xx) + 0.5f, - static_cast(yy) + 0.5f); + /* Pixel位置 */ + const QPointF p(static_cast(xx), static_cast(yy)); /* 中心からPixel位置へのベクトルと長さ */ const QVector2D v(p - center); diff --git a/toonz/sources/stdfx/igs_warp.h b/toonz/sources/stdfx/igs_warp.h index b0efa239..76eb330e 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縺ョ繧ケ繧ュ繝」繝ウ繝ゥ繧、繝ウ縺ョ繝斐け繧サ繝ォ蛟、繧偵>繧後k 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縺ォ蜿ら・逕サ蜒上・謖・ョ壹メ繝」繝ウ繝阪Ν縺ョ蛟、繧貞・繧後k 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)); - } - } } } -} +} // namespace //-------------------------------------------------------------------- -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 - - , - 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); } diff --git a/toonz/sources/stdfx/igs_warp_vert.cpp b/toonz/sources/stdfx/igs_warp_vert.cpp index 05fcc06b..7cea8cb9 100644 --- a/toonz/sources/stdfx/igs_warp_vert.cpp +++ b/toonz/sources/stdfx/igs_warp_vert.cpp @@ -5,40 +5,28 @@ #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 +34,56 @@ 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)); - } - } } } -} +} // namespace //-------------------------------------------------------------------- -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 - - , - 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); } diff --git a/toonz/sources/stdfx/ino_blend_add.cpp b/toonz/sources/stdfx/ino_blend_add.cpp index fd5df256..2ca6aba6 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 8f1741a0..40256854 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 7104cbbf..c13ba53e 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 7aa8f13b..ce4a3632 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 eb7a533d..daaafea0 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 1b140557..c4932227 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 d08b27bc..4da2578e 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 68b5d968..e85760e8 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 a09ccd38..2834ebe6 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 f1ac5518..8e0f92bb 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 e0e0fb37..acf2ceb3 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 03e036ff..cd4f0ad0 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 86a8c262..580eb8fe 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 2bbe0b21..26b947f7 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 961f21b8..c3d595c7 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 e053a436..5098f63b 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 a7b3244b..cde4294c 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 bbfdb403..db42255a 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 d7b82c8f..f182a47d 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 09da6db6..1cf7eee6 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 7fe57f3e..00c2236b 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 23cd4593..e1fa91e8 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 0fba236c..f95053a0 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,49 @@ 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(); + 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())); + + TRasterGR8P out_buffer(out_ras->getLy(), + out_ras->getLx() * ino::channels() * sizeof(float)); + out_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 - - , - 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(); + 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 +156,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 cb2d4e5d..ba3825f1 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 7136dbf0..0ea93e19 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::linearTmpl(TRasterPT dn_ras_out, - const TRasterPT& up_ras, - const double up_opacity, - const double gamma) { +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; - bool premultiplied_sw = this->m_premultiplied->getValue(); + + 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 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); @@ -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 66964606..76d1b5a0 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 9eb2d32d..395493bf 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 96018f94..21fafe58 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 a83d23bb..ee24a6bc 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 2e8aed76..0deac4ec 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() + 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)); - , - in_ras->getLy(), in_ras->getLx() // Must 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 - - , - 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 319c6bf8..4361885d 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 811df675..86bba9fb 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 1997d4bf..ab9da802 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() + 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); - , - in_ras->getLy(), in_ras->getLx() // Must 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 - - , - 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 824cb338..ae89bdca 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 6f85fc18..f18a44e8 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 ae5a45c7..ef6a1f70 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 e431e0e0..9f673e10 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 b67b447a..c6275646 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 d04ea0d1..8655fc93 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 74027b17..69d159ef 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 6aa8841f..38757cb6 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 92d7d3a5..487d5445 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 < 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); + } +} - /*- クランプ -*/ - 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); +//------------------------------------------------ + +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 { + // 荳区オ√′繝ェ繝九い險育ョ励☆繧九°縺ゥ縺・°縺ァ蛻、譁ュ縲ゅ%繧後〒螟画鋤繧呈怙蟆城剞縺ォ縺ァ縺阪k縺銀€ヲ・・ + 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 61269bfd..e5ffb05a 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; /*- 譏弱k縺輔・繧ケ繧ア繝シ繝ォ蛟、 -*/ + TDoubleParamP m_offset; /*- 譏弱k縺輔・繧ェ繝輔そ繝・ヨ蛟、 -*/ /*- タイルの画像を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 ed80d1ed..f8294527 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 a2018e89..6541d93f 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 8153898a..a7322d8a 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 7fd6e433..ae0ce580 100644 --- a/toonz/sources/stdfx/iwa_bokeh_advancedfx.cpp +++ b/toonz/sources/stdfx/iwa_bokeh_advancedfx.cpp @@ -134,16 +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_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); @@ -152,21 +159,67 @@ 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(), m_layerParams[layer].m_depth_ref); bindParam(this, QString("depthRange%1").arg(layer + 1).toStdString(), m_layerParams[layer].m_depthRange); + bindParam(this, QString("fillGap%1").arg(layer + 1).toStdString(), + m_layerParams[layer].m_fillGap); + bindParam(this, QString("doMedian%1").arg(layer + 1).toStdString(), + m_layerParams[layer].m_doMedian); m_layerParams[layer].m_distance->setValueRange(0.0, 1.0); 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); + } } //-------------------------------------------- @@ -232,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; } @@ -270,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, @@ -277,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; @@ -284,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) { @@ -292,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); @@ -303,8 +383,8 @@ void Iwa_BokehAdvancedFx::doCompute(TTile& tile, double frame, m_layerParams[index].m_bokehAdjustment->getValue(frame); layerValue.depthRange = m_layerParams[index].m_depthRange->getValue(frame); layerValue.distancePrecision = 10; - layerValue.fillGap = true; - layerValue.doMedian = false; + layerValue.fillGap = m_layerParams[index].m_fillGap->getValue(); + layerValue.doMedian = m_layerParams[index].m_doMedian->getValue(); layerValues.append(layerValue); } diff --git a/toonz/sources/stdfx/iwa_bokeh_advancedfx.h b/toonz/sources/stdfx/iwa_bokeh_advancedfx.h index 06d03c85..3cb44e79 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,16 @@ 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; }; std::array m_layerParams; @@ -65,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 1ba6fee1..79c3961b 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); } } @@ -802,8 +818,7 @@ void doSingleExtend(const double4* source_buff, // continue if the current pixel is already extended if ((*gen_p) > 0) continue; - // check out the neighbor pixels. store brightness in neighbor[x].w - double4 neighbor[8]; + // check out the neighbor pixels. bool neighbor_found = false; for (int ky = posY - 1; ky <= posY + 1; ky++) { for (int kx = posX - 1; kx <= posX + 1; kx++) { @@ -1040,7 +1055,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 +1142,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 +1151,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 +1159,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 +1170,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 +1219,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 +1236,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 +1255,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 +1333,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 +1378,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 +1391,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 +1435,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 +1468,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 +1484,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 +1557,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 +1684,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 +1709,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 +1730,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 +1738,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 +1839,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,12 +2006,15 @@ 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); diff --git a/toonz/sources/stdfx/iwa_bokeh_util.h b/toonz/sources/stdfx/iwa_bokeh_util.h index 6740d1ea..0262f921 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•Û + // メモリ確保 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 0a154e7a..dda2eda2 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 04f2c450..ed622630 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; @@ -49,6 +48,8 @@ public: Iwa_BokehFx(); 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 fe967eaf..840e28c1 100644 --- a/toonz/sources/stdfx/iwa_bokehreffx.cpp +++ b/toonz/sources/stdfx/iwa_bokehreffx.cpp @@ -80,11 +80,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); } //-------------------------------------------- @@ -146,13 +180,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 @@ -173,6 +206,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 @@ -197,6 +242,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); @@ -208,12 +254,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 653a2251..604a8965 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 96e826c7..a113498d 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 206f4be1..e02c1ffd 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_floorbumpfx.cpp b/toonz/sources/stdfx/iwa_floorbumpfx.cpp new file mode 100644 index 00000000..3869eaf3 --- /dev/null +++ b/toonz/sources/stdfx/iwa_floorbumpfx.cpp @@ -0,0 +1,1476 @@ +#include "iwa_floorbumpfx.h" + +#include "tparamuiconcept.h" +#include "iwa_fresnel.h" + +#include + +namespace { + +inline double cross(const QPointF a, const QPointF b) { + return a.x() * b.y() - a.y() * b.x(); +}; + +inline float lerp(const float v1, const float v2, const float r) { + return v1 * (1.0f - r) + v2 * r; +}; +inline QPointF lerpUV(const QPointF p1, const QPointF p2, const float r) { + return p1 * (1.0 - r) + p2 * r; +}; +float4 lerp4(const float4 v1, const float4 v2, const float r) { + return float4{v1.x * (1.0f - r) + v2.x * r, v1.y * (1.0f - r) + v2.y * r, + v1.z * (1.0f - r) + v2.z * r, v1.w * (1.0f - r) + v2.w * r}; +}; + +inline QVector3D lerpPos(const QVector3D v1, const QVector3D v2, + const float r) { + return (v1 * (1.0 - r) + v2 * r); +}; + +inline QVector3D lerpNormal(const QVector3D v1, const QVector3D v2, + const float r) { + return lerpPos(v1, v2, r).normalized(); +}; + +inline float fresnelFactor(const float radian) { + float degree = radian / M_PI_180; + if (degree < 0.0f) + return fresnel[0]; + else if (degree >= 90.0f) + return fresnel[90]; + int id = int(std::floor(degree)); + float ratio = degree - float(id); + return lerp(fresnel[id], fresnel[id + 1], ratio); +}; + +inline bool isTextureUsed(int renderMode) { + return renderMode == Iwa_FloorBumpFx::TextureMode // Texture + || renderMode == Iwa_FloorBumpFx::RefractionMode // Refraction + || renderMode == Iwa_FloorBumpFx::ReflectionMode; // Reflection +} + +// pixel height projected on the projection plane +inline double getVPos(const QPointF &zyPos, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + QPointF a1 = QPointF(0, vars.P_y) - zyPos; + QPointF a2 = vars.A - vars.B; + QPointF b1 = vars.A - zyPos; + QPointF b2 = vars.B - zyPos; + double s1 = cross(b2, a1) / 2.0; + double s2 = cross(a1, b1) / 2.0; + double ratio = s1 / (s1 + s2); + return vars.H * ratio; +}; + +inline float4 getSourcePix(QPointF p, float4 *source_host, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + auto source = [&](QPoint p) { + return source_host[p.y() * vars.sourceDim.lx + p.x()]; + }; + + QPointF uv = (p + QPointF(vars.margin, vars.margin)) * vars.precision; + int inc_x = 1, inc_y = 1; + if (uv.x() < 0.0) + uv.setX(0.0); + else if (uv.x() >= vars.sourceDim.lx - 1) { + uv.setX(vars.sourceDim.lx - 1); + inc_x = 0; + } + if (uv.y() < 0.0) + uv.setY(0.0); + else if (uv.y() >= vars.sourceDim.ly - 1) { + uv.setY(vars.sourceDim.ly - 1); + inc_y = 0; + } + QPoint p00(int(std::floor(uv.x())), int(std::floor(uv.y()))); + QPointF frac = uv - QPointF(p00); + if (frac.isNull()) return source(p00); + QPoint p01 = p00 + QPoint(inc_x, 0); + QPoint p10 = p00 + QPoint(0, inc_y); + QPoint p11 = p00 + QPoint(inc_x, inc_y); + + return lerp4(lerp4(source(p00), source(p01), frac.x()), + lerp4(source(p10), source(p11), frac.x()), frac.y()); +}; + +inline float getMapValue(QPointF p, float *map_host, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + auto map = [&](QPoint p) { return map_host[p.y() * vars.refDim.lx + p.x()]; }; + QPointF uv = p + QPointF(vars.margin, vars.margin); + int inc_x = 1, inc_y = 1; + if (uv.x() < 0.0) + uv.setX(0.0); + else if (uv.x() >= vars.refDim.lx - 1) { + uv.setX(vars.refDim.lx - 1); + inc_x = 0; + } + if (uv.y() < 0.0) + uv.setY(0.0); + else if (uv.y() >= vars.refDim.ly - 1) { + uv.setY(vars.refDim.ly - 1); + inc_y = 0; + } + QPoint p00(int(std::floor(uv.x())), int(std::floor(uv.y()))); + QPointF frac = uv - QPointF(p00); + if (frac.isNull()) return map(p00); + QPoint p01 = p00 + QPoint(inc_x, 0); + QPoint p10 = p00 + QPoint(0, inc_y); + QPoint p11 = p00 + QPoint(inc_x, inc_y); + + return lerp(lerp(map(p00), map(p01), frac.x()), + lerp(map(p10), map(p11), frac.x()), frac.y()); +}; + +// compute horizontal position of the projected point on the projection +// plane +inline double getXPos(QVector3D pos, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + double angle_Q = vars.angle_el - atan((vars.P_y - pos.y()) / pos.z()); + double dist = + vars.d_PT * cos(vars.angle_el - angle_Q) / (cos(angle_Q) * pos.z()); + return pos.x() * dist + double(vars.resultDim.lx) * 0.5; +}; + +inline float4 getTextureOffsetColor(double preTexOff, double texOff, + float ratio) { + float val = lerp(preTexOff, texOff, ratio); + return float4{val, val, val, 1.0}; +}; +inline float4 getDiffuseColor(QVector3D n, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + float val = QVector3D::dotProduct(n, vars.sunVec); + if (val < 0.0) val = 0.0; + return float4{val, val, val, 1.0}; +}; +inline float4 getDiffuseDifferenceColor( + QVector3D n, QVector3D base_n, const Iwa_FloorBumpFx::FloorBumpVars &vars) { + float val = QVector3D::dotProduct(n, vars.sunVec); + float base_val = QVector3D::dotProduct(base_n, vars.sunVec); + val = (val - base_val) * 0.5 + 0.5; + if (val < 0.0) + val = 0.0; + else if (val > 1.0) + val = 1.0; + return float4{val, val, val, 1.0}; +}; +inline float4 getSpecularColor(QVector3D n, QVector3D pos, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + QVector3D halfVector = + ((vars.eyePos - pos).normalized() + vars.sunVec).normalized(); + float val = QVector3D::dotProduct(n, halfVector); + if (val < 0.0) val = 0.0; + val = std::pow(val, 50.0); + return float4{val, val, val, 1.0}; +}; +inline float4 getSpecularDifferenceColor( + QVector3D n, QVector3D base_n, QVector3D pos, QVector3D base_pos, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + QVector3D halfVector = + ((vars.eyePos - pos).normalized() + vars.sunVec).normalized(); + float ang = std::acos(QVector3D::dotProduct(n, halfVector)); + halfVector = + ((vars.eyePos - base_pos).normalized() + vars.sunVec).normalized(); + float base_ang = std::acos(QVector3D::dotProduct(base_n, halfVector)); + float val = std::abs(ang - base_ang) / M_PI_2; + if (val > 1.0) val = 1.0; + return float4{val, val, val, 1.0}; +}; +inline float4 getFresnelColor(QVector3D n, QVector3D pos, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + float angle = + acos(QVector3D::dotProduct(n, (vars.eyePos - pos).normalized())); + float val = (fresnelFactor(angle) - vars.base_fresnel_ref) / + (1.0f - vars.base_fresnel_ref); + if (val < 0.0) val = 0.0; + return float4{val, val, val, 1.0}; +}; +inline float4 getFresnelDifferenceColor( + QVector3D n, QVector3D base_n, QVector3D pos, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + float val = + getFresnelColor(n, pos, vars).x - getFresnelColor(base_n, pos, vars).x; + if (val < 0.0) val = 0.0; + return float4{val, val, val, 1.0}; +}; +// currently the texture is assumed as an image of the bottom WITHOUT WATER. +// would it be nice to have a mode to handle the texture as +// an image of the bottom refracted on the surface WITHOUT WAVES? +inline float4 getRefractionColor(QVector3D n, QVector3D pos, + float4 *source_host, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + QVector3D eyeVec = (vars.eyePos - pos).normalized(); + // incident angle + float n_eye = QVector3D::dotProduct(n, eyeVec); + double angle_inci = acos(n_eye); + // refraction angle + double angle_refr = asin(sin(angle_inci) / vars.r_index); + QVector3D refrRay = + sin(angle_refr) * (n_eye * n - eyeVec).normalized() - cos(angle_refr) * n; + double travelLength = -(vars.depth + pos.y()) / refrRay.y(); + QVector3D bottomPos = pos + refrRay * travelLength; + return getSourcePix( + QPointF(getXPos(bottomPos, vars), + getVPos(QPointF(bottomPos.z(), bottomPos.y()), vars)), + source_host, vars); +}; + +inline float4 getReflectionColor(QVector3D n, QVector3D pos, + float4 *source_host, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + // draw nothing behind the reflected object + if (vars.distance > 0.0 && pos.z() >= vars.distance) + return float4{0.0, 0.0, 0.0, 0.0}; + QVector3D eyeVec = (vars.eyePos - pos).normalized(); + QVector3D refVec = -eyeVec + 2.0 * QVector3D::dotProduct(eyeVec, n) * n; + if (refVec.z() <= 0.0) return float4{0.0, 0.0, 0.0, 0.0}; + // if the object is at infinite length, compute the reflected UV by + // using only the angles of reflection vector + if (vars.distance < 0.0) { + double angle_ref_azim = asin(refVec.x()); + double angle_ref_elev = asin(refVec.y()); + double ref_v = + vars.d_PT * tan(vars.angle_el - angle_ref_elev) + vars.H / 2.0; + double ref_u = + vars.d_PT * tan(angle_ref_azim) / cos(vars.angle_el - angle_ref_elev) + + vars.W / 2.0; + return getSourcePix(QPointF(ref_u, ref_v), source_host, vars); + } + // compute the reflected UV + double length = (vars.distance - pos.z()) / refVec.z(); + QVector3D boardPos = pos + length * refVec; + // invert Y coordinate since the texture image is already vertically + // "reflected" + boardPos.setY(-boardPos.y()); + return getSourcePix( + QPointF(getXPos(boardPos, vars), + getVPos(QPointF(boardPos.z(), boardPos.y()), vars)), + source_host, vars); +}; + +inline float4 getColor(QVector3D pre_n, QVector3D cur_n, QVector3D pre_p, + QVector3D cur_p, float ratio, float4 *source_host, + const Iwa_FloorBumpFx::FloorBumpVars &vars, + QVector3D pre_base_n = QVector3D(0, 1, 0), + QVector3D cur_base_n = QVector3D(0, 1, 0), + QVector3D pre_base_pos = QVector3D(), + QVector3D cur_base_pos = QVector3D()) { + if (vars.renderMode == Iwa_FloorBumpFx::DiffuseMode) { + if (vars.differenceMode) + return getDiffuseDifferenceColor( + lerpNormal(pre_n, cur_n, ratio), + lerpNormal(pre_base_n, cur_base_n, ratio), vars); + else + return getDiffuseColor(lerpNormal(pre_n, cur_n, ratio), vars); + } else if (vars.renderMode == Iwa_FloorBumpFx::SpecularMode) { + if (vars.differenceMode) + return getSpecularDifferenceColor( + lerpNormal(pre_n, cur_n, ratio), + lerpNormal(pre_base_n, cur_base_n, ratio), + lerpPos(pre_p, cur_p, ratio), + lerpPos(pre_base_pos, cur_base_pos, ratio), vars); + else + return getSpecularColor(lerpNormal(pre_n, cur_n, ratio), + lerpPos(pre_p, cur_p, ratio), vars); + } else if (vars.renderMode == Iwa_FloorBumpFx::FresnelMode) { + if (vars.differenceMode) + return getFresnelDifferenceColor( + lerpNormal(pre_n, cur_n, ratio), + lerpNormal(pre_base_n, cur_base_n, ratio), + lerpPos(pre_p, cur_p, ratio), vars); + else + return getFresnelColor(lerpNormal(pre_n, cur_n, ratio), + lerpPos(pre_p, cur_p, ratio), vars); + } else if (vars.renderMode == Iwa_FloorBumpFx::RefractionMode) + return getRefractionColor(lerpNormal(pre_n, cur_n, ratio), + lerpPos(pre_p, cur_p, ratio), source_host, vars); + else if (vars.renderMode == Iwa_FloorBumpFx::ReflectionMode) + return getReflectionColor(lerpNormal(pre_n, cur_n, ratio), + lerpPos(pre_p, cur_p, ratio), source_host, vars); + return float4(); +}; + +QList getSubPointsList(int subAmount, + const Iwa_FloorBumpFx::FloorBumpVars &vars) { + QList ret; + if (!areAlmostEqual(vars.textureOffsetAmount, 0.0)) { + for (int su = -subAmount; su <= subAmount; su++) { + double sub_u = float(su) / float(subAmount); + for (int sv = -subAmount; sv <= subAmount; sv++) { + double sub_v = float(sv) / float(subAmount); + // 円の外ならcontinue + if (sub_u * sub_u + sub_v * sub_v > 1.0) continue; + if (su == 0 && sv == 0) continue; + ret.append(QPointF(vars.spread * sub_u, vars.spread * sub_v)); + } + } + } + return ret; +} + +} // namespace + +//------------------------------------ + +Iwa_FloorBumpFx::Iwa_FloorBumpFx() + : m_renderMode(new TIntEnumParam(TextureMode, "Texture")) + , m_eyeLevel(0.0) + , m_drawLevel(-50.0) + , m_fov(30) + , m_cameraAltitude(0.0) + , m_waveHeight(10.0) + , m_differenceMode(false) + , m_textureOffsetAmount(0.0) + , m_textureOffsetSpread(10.0) + , m_sourcePrecision(300.0 / 162.5) + , m_souceMargin(0.0) + , m_displacement(0.0) + , m_lightAzimuth(-135.0) + , m_lightElevation(30.0) // default angle_elev will shade horizontal plane + // 50% gray (cos(60)=0.5) + , m_depth(30.0) + , m_refractiveIndex(1.33333) + , m_distanceLevel(-100.0) { + addInputPort("Height", m_heightRef); + addInputPort("Texture", m_texture); + addInputPort("Displacement", m_dispRef); + bindParam(this, "renderMode", m_renderMode); + bindParam(this, "fov", m_fov); + bindParam(this, "cameraAltitude", m_cameraAltitude); + bindParam(this, "eyeLevel", m_eyeLevel); + bindParam(this, "drawLevel", m_drawLevel); + bindParam(this, "waveHeight", m_waveHeight); + bindParam(this, "differenceMode", m_differenceMode); + bindParam(this, "textureOffsetAmount", m_textureOffsetAmount); + bindParam(this, "textureOffsetSpread", m_textureOffsetSpread); + bindParam(this, "sourcePrecision", m_sourcePrecision); + bindParam(this, "souceMargin", m_souceMargin); + bindParam(this, "displacement", m_displacement); + bindParam(this, "lightAzimuth", m_lightAzimuth); + bindParam(this, "lightElevation", m_lightElevation); + bindParam(this, "depth", m_depth); + bindParam(this, "refractiveIndex", m_refractiveIndex); + bindParam(this, "distanceLevel", m_distanceLevel); + + m_renderMode->addItem(DiffuseMode, "Diffuse"); + m_renderMode->addItem(SpecularMode, "Specular"); + m_renderMode->addItem(FresnelMode, "Fresnel reflectivity"); + m_renderMode->addItem(RefractionMode, "Refraction"); + m_renderMode->addItem(ReflectionMode, "Reflection"); + + m_fov->setValueRange(10, 90); + m_cameraAltitude->setMeasureName("fxLength"); + m_cameraAltitude->setValueRange(0.0, 300.0); + m_eyeLevel->setMeasureName("fxLength"); + m_drawLevel->setMeasureName("fxLength"); + + m_waveHeight->setMeasureName("fxLength"); + m_waveHeight->setValueRange(-1000.0, 1000.0); + m_textureOffsetAmount->setMeasureName("fxLength"); + m_textureOffsetAmount->setValueRange(-2000.0, 2000.0); + m_textureOffsetSpread->setMeasureName("fxLength"); + m_textureOffsetSpread->setValueRange(1.0, 300.0); + + m_sourcePrecision->setValueRange(1.0, 2.0); + m_souceMargin->setMeasureName("fxLength"); + m_souceMargin->setValueRange(0.0, 100.0); + m_displacement->setMeasureName("fxLength"); + m_displacement->setValueRange(-50.0, 50.0); + + m_lightAzimuth->setValueRange(-360.0, 360.0); + m_lightElevation->setValueRange(0.0, 90.0); + + m_depth->setMeasureName("fxLength"); + m_depth->setValueRange(0.0, 1000.0); + m_refractiveIndex->setValueRange(1.0, 3.0); + + m_distanceLevel->setMeasureName("fxLength"); +} + +//------------------------------------ + +bool Iwa_FloorBumpFx::doGetBBox(double frame, TRectD &bBox, + const TRenderSettings &info) { + if (m_heightRef.isConnected()) { + bool ret = m_heightRef->doGetBBox(frame, bBox, info); + if (ret) bBox = TConsts::infiniteRectD; + return ret; + } + return false; +} + +//------------------------------------ + +bool Iwa_FloorBumpFx::canHandle(const TRenderSettings &info, double frame) { + return false; +} + +//------------------------------------------------------------ +// convert output values (in float4) to channel value +//------------------------------------------------------------ +template +void Iwa_FloorBumpFx::setOutputRaster(float4 *srcMem, const RASTER dstRas, + TDimensionI dim, int drawLevel) { + typename PIXEL::Channel halfChan = + (typename PIXEL::Channel)(PIXEL::maxChannelValue / 2); + + dstRas->fill(PIXEL::Transparent); + + for (int j = 0; j < drawLevel; j++) { + if (j >= dstRas->getLy()) break; + + PIXEL *pix = dstRas->pixels(j); + float4 *chan_p = &srcMem[j]; + for (int i = 0; i < dstRas->getLx(); i++, chan_p += drawLevel, pix++) { + float val; + val = (*chan_p).x * (float)PIXEL::maxChannelValue + 0.5f; + pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = (*chan_p).y * (float)PIXEL::maxChannelValue + 0.5f; + pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = (*chan_p).z * (float)PIXEL::maxChannelValue + 0.5f; + pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + val = (*chan_p).w * (float)PIXEL::maxChannelValue + 0.5f; + pix->m = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue) + ? (float)PIXEL::maxChannelValue + : val); + } + } +} + +//------------------------------------ + +inline void Iwa_FloorBumpFx::initVars(FloorBumpVars &vars, TTile &tile, + const TRenderSettings &settings, + double frame) { + TAffine aff = settings.m_affine; + double factor = sqrt(std::abs(settings.m_affine.det())); + double eyeLevel = m_eyeLevel->getValue(frame) * factor; + double refLevel = m_drawLevel->getValue(frame) * factor; + vars.waveHeight = m_waveHeight->getValue(frame) * factor; + vars.displacement = m_displacement->getValue(frame) * factor; + if (eyeLevel - 1.0 < refLevel) refLevel = eyeLevel - 1.0; + int drawHeight = std::max(refLevel, vars.waveHeight) - tile.m_pos.y; + vars.refHeight = int(refLevel - tile.m_pos.y); + // convert the pixel coordinate from the bottom-left of the camera box + eyeLevel = eyeLevel - (tile.m_pos.y + tile.getRaster()->getCenterD().y) + + settings.m_cameraBox.getLy() / 2.0; + refLevel = refLevel - (tile.m_pos.y + tile.getRaster()->getCenterD().y) + + settings.m_cameraBox.getLy() / 2.0; + + TRectD rectOut(tile.m_pos, TDimensionD(tile.getRaster()->getLx(), + tile.getRaster()->getLy())); + vars.outDim = TDimensionI(rectOut.getLx(), rectOut.getLy()); + vars.resultDim = TDimensionI(rectOut.getLx(), + std::min(drawHeight, tile.getRaster()->getLy())); + + // get texture image + vars.margin = tceil(m_souceMargin->getValue(frame) * factor); + vars.precision = m_sourcePrecision->getValue(frame); + + // add margins to all ends and multiply by precision value + vars.sourceDim = TDimensionI( + tceil(vars.precision * double(vars.resultDim.lx + vars.margin * 2)), + tceil(vars.precision * double(vars.resultDim.ly + vars.margin * 2))); + // only add margins for height image + vars.refDim = TDimensionI(vars.resultDim.lx + vars.margin * 2, + vars.refHeight + vars.margin * 2); + + // collecting parameters + double angle_halfFov = m_fov->getValue(frame) * M_PI_180 / 2.0; + vars.textureOffsetAmount = + 100.0 * m_textureOffsetAmount->getValue(frame) * factor * factor; + vars.spread = m_textureOffsetSpread->getValue(frame) * factor; + if (vars.spread < 1.0) vars.spread = 1.0; + vars.camAltitude = m_cameraAltitude->getValue(frame) * factor; + vars.differenceMode = m_differenceMode->getValue(); + + // making pixels in gray128 to be zero level height + // ( 128/255. IT'S NOT 0.5! ) + vars.zeroLevel = double(128) / double(TPixel32::maxChannelValue); + + vars.H = settings.m_cameraBox.getLy(); + vars.W = settings.m_cameraBox.getLx(); + + double el = eyeLevel - vars.H / 2.0; + // angle between the optical axis and the horizontal axis + vars.angle_el = + std::atan(2.0 * el * tan(angle_halfFov) / double(vars.outDim.ly)); + // distance between the Eye (P) and + // the center of bottom edge of the projection plane (B) + double d_PB = double(vars.outDim.ly) / (2.0 * sin(angle_halfFov)); + // Y coordinate of the Eye position (P) + vars.P_y = vars.camAltitude + d_PB * sin((angle_halfFov) + vars.angle_el); + // distance from the Eye (P) to the center of the projection plane (T) + vars.d_PT = vars.H / (2.0 * tan(angle_halfFov)); + + QMatrix cam_tilt; + cam_tilt.rotate(-vars.angle_el / M_PI_180); + // Z-Y position of the center of top edge of the projection plane (A) + vars.A = + cam_tilt.map(QPointF(vars.d_PT, vars.H / 2.0)) + QPointF(0, vars.P_y); + // Z-Y position of the center of bottom edge of the projection plane (B) + vars.B = + cam_tilt.map(QPointF(vars.d_PT, -vars.H / 2.0)) + QPointF(0, vars.P_y); + + vars.C_z = vars.P_y / tan(angle_halfFov + vars.angle_el); + vars.eyePos = QVector3D(0.0, vars.P_y, 0.0); + + switch (vars.renderMode) { + // for shading modes ( diffuse & specular ) + case DiffuseMode: + case SpecularMode: { + double angle_azim = m_lightAzimuth->getValue(frame) * M_PI_180; + double angle_elev = m_lightElevation->getValue(frame) * M_PI_180; + vars.sunVec = QVector3D(sin(angle_azim) * cos(angle_elev), sin(angle_elev), + cos(angle_azim) * cos(angle_elev)); + break; + } + case FresnelMode: { // for fresnel mode + double angle_base = 0.5 * M_PI - (angle_halfFov + vars.angle_el); + vars.base_fresnel_ref = fresnelFactor(angle_base); + break; + } + case RefractionMode: { + // for refraction mode + vars.depth = m_depth->getValue(frame) * factor; + vars.r_index = m_refractiveIndex->getValue(frame); + break; + } + case ReflectionMode: { + // for reflection mode + double distanceLevel = m_distanceLevel->getValue(frame) * factor; + double angle_dl = atan(distanceLevel / vars.d_PT); + // Z-axis distance to the reflected object. + // distance = -1 means the object is at infinity + vars.distance = (angle_dl >= vars.angle_el) + ? -1 + : vars.P_y / tan(vars.angle_el - angle_dl); + break; + } + } +} +//------------------------------------ + +void Iwa_FloorBumpFx::doCompute_CPU(TTile &tile, const double frame, + const TRenderSettings &settings, + const FloorBumpVars &vars, + float4 *source_host, float *ref_host, + float4 *result_host) { + //----------- + // Texture Mode + if (vars.renderMode == TextureMode) { + // テクスチャの勾配を調べるサンプリング点 + int subAmount = 10; // これ、パラメータ化するか…? + QList subPoints = getSubPointsList(subAmount, vars); + + // render pixels in column-major order, from bottom to top + for (int i = 0; i < vars.resultDim.lx; i++) { + // cancel check + if (settings.m_isCanceled && *settings.m_isCanceled) return; + + double maxVPos = 0.0; // maximum height of drawn pixels + double preVPos = 0.0; // height of the previous segment + QPointF preUV = QPointF(i, -vars.margin); + float4 *result_p = &result_host[i * vars.resultDim.ly]; + float4 *end_p = &result_host[(i + 1) * vars.resultDim.ly - 1]; + float preTextureOffset = 0.0; + + bool backled = false; + double preGrad = 100.0; + + // get height pixels from bottom to top + for (int j = -vars.margin; j < vars.refHeight; j++) { + // angle between the light axis and the line between (Q) and the eye (P) + // (Q) is a point on the projection plane at distance of j from (B) + double angle_Q = + std::atan((float(j) - (vars.outDim.ly / 2.0)) / vars.d_PT); + // (R) is an intersection between the XZ plane and the line P->Q + double R_z = vars.P_y / tan(vars.angle_el - angle_Q); + + // compute (S), a point on the bumped surface in Y-axis direction from + // (R) + float refVal = + getMapValue(QPointF(double(i), double(j)), ref_host, vars); + QPointF S(R_z, vars.waveHeight * (refVal - vars.zeroLevel) * 2.0); + // height of (S) projected on the projection plane + double vPos = getVPos(S, vars); + + // UV coordinate of the texture image + QPointF currentUV; + float currentTextureOffset = 0.0; + if (areAlmostEqual(vars.textureOffsetAmount, 0.0)) + currentUV = QPointF(double(i), getVPos(QPointF(R_z, 0), vars)); + else { + // length of (1,0,0) vector on the XZ plane, projected on the + // projection plane + double dist = (vars.d_PT * sin(vars.angle_el - angle_Q)) / vars.P_y * + cos(angle_Q); + // approximate the gradient by difference of the heights of neighbor + // points + + // compute gradient of the height image + QPointF offset; + for (const QPointF &subPoint : subPoints) { + // この地点のRefの値を取る + QPointF subRefPos(double(i) + dist * subPoint.x(), + getVPos(QPointF(R_z + subPoint.y(), 0), vars)); + + int sign_u = (subPoint.x() > 0) - (subPoint.x() < 0); // -1, 0, 1 + int sign_v = (subPoint.y() > 0) - (subPoint.y() < 0); // -1, 0, 1 + + float subRefValue = getMapValue(subRefPos, ref_host, vars); + offset += subRefValue * QPointF(sign_u, sign_v); + } + + offset *= 1.0 / float(subPoints.count() - subAmount); // サンプル数 + offset *= vars.textureOffsetAmount / + (vars.spread * (0.5 + 0.5 / float(subAmount))); // 距離 + currentUV = QPointF(double(i) + offset.x() * dist, + getVPos(QPointF(R_z + offset.y(), 0), vars)); + if (vars.differenceMode) + currentTextureOffset = + std::sqrt(offset.x() * offset.x() + offset.y() * offset.y()) / + vars.textureOffsetAmount; + } + + // continue if the current point is behind the forward bumps + if (vPos <= maxVPos) { + preVPos = vPos; + preUV = currentUV; + preTextureOffset = currentTextureOffset; + continue; + } + + // putting colors on pixels + int currentY = int(std::floor(maxVPos)); + float current_frac = maxVPos - float(currentY); + int toY = int(std::floor(vPos)); + float to_frac = vPos - float(toY); + // if the entire current segment is in the same pixel, + // then add colors with percentage of the fraction part + if (currentY == toY) { + float ratio = to_frac - current_frac; + if (vars.differenceMode) + *result_p += getTextureOffsetColor(preTextureOffset, + currentTextureOffset, 0.5) * + ratio; + else + *result_p += + getSourcePix(lerpUV(preUV, currentUV, 0.5), source_host, vars) * + ratio; + preVPos = vPos; + maxVPos = vPos; + preUV = currentUV; + preTextureOffset = currentTextureOffset; + continue; + } + + // fill fraction part of the start pixel (currentY) + float k = + (maxVPos + (1.0 - current_frac) / 2.0 - preVPos) / (vPos - preVPos); + float ratio = 1.0 - current_frac; + if (vars.differenceMode) + *result_p += + getTextureOffsetColor(preTextureOffset, currentTextureOffset, k) * + ratio; + else + *result_p += + getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars) * + ratio; + + if (result_p == end_p) break; + result_p++; + + // fill pixels between currentY and toY + for (int y = currentY + 1; y < toY; y++) { + k = (float(y) + 0.5 - preVPos) / (vPos - preVPos); + if (vars.differenceMode) + *result_p += getTextureOffsetColor(preTextureOffset, + currentTextureOffset, k); + else + *result_p = + getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars); + if (result_p == end_p) break; + result_p++; + } + + // fill fraction part of the end pixel (toY) + k = (float(toY) + to_frac * 0.5 - preVPos) / (vPos - preVPos); + if (vars.differenceMode) + *result_p += + getTextureOffsetColor(preTextureOffset, currentTextureOffset, k) * + to_frac; + else + *result_p += + getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars) * + to_frac; + + preVPos = vPos; + maxVPos = vPos; + preUV = currentUV; + preTextureOffset = currentTextureOffset; + } + } + } + // Other modes + // (Diffuse, Specular, Fresnel, Refraction, Reflection) + else { + // render pixels in column-major order, from bottom to top + for (int i = 0; i < vars.resultDim.lx; i++) { + // cancel check + if (settings.m_isCanceled && *settings.m_isCanceled) return; + + double maxVPos = 0.0; // maximum height of drawn pixels + double preVPos = 0.0; // height of the previous segment + QVector3D preNormal(0, 1, 0); + QVector3D prePos(i, 0, + vars.C_z); // actually the x coordinate is not i at the + // initial position.., but no problem. + float4 *result_p = &result_host[i * vars.resultDim.ly]; + float4 *end_p = &result_host[(i + 1) * vars.resultDim.ly - 1]; + // get height pixels from bottom to top + for (int j = -vars.margin; j < vars.refHeight; j++) { + // angle between the light axis and the line between (Q) and the eye (P) + // (Q) is a point on the projection plane at distance of j from (B) + double angle_Q = + std::atan((float(j) - (vars.outDim.ly / 2.0)) / vars.d_PT); + // (R) is an intersection between the XZ plane and the line P->Q + double R_z = vars.P_y / tan(vars.angle_el - angle_Q); + // In reflection mode, do not draw surface behind the Distance Level + if (vars.renderMode == ReflectionMode && vars.distance > 0.0 && + R_z >= vars.distance) + break; + // compute normal vector from cross-product of surface vectors + QVector3D currentNormal; + // length of (1,0,0) vector on the XZ plane, projected on the projection + // plane + double dist = (vars.d_PT * sin(vars.angle_el - angle_Q)) / vars.P_y * + cos(angle_Q); + double xRefGrad = + getMapValue(QPointF(double(i) + dist, double(j)), ref_host, vars) - + getMapValue(QPointF(double(i) - dist, double(j)), ref_host, vars); + double zRefGrad = + getMapValue(QPointF(double(i), getVPos(QPointF(R_z + 1, 0), vars)), + ref_host, vars) - + getMapValue(QPointF(double(i), getVPos(QPointF(R_z - 1, 0), vars)), + ref_host, vars); + currentNormal.setX(-xRefGrad * vars.waveHeight * 2.0); + currentNormal.setY(4.0); + currentNormal.setZ(-zRefGrad * vars.waveHeight * 2.0); + currentNormal.normalize(); + + // compute (S), a point on the bumped surface in Y-axis direction from + // (R) + float refVal = + getMapValue(QPointF(double(i), double(j)), ref_host, vars); + QPointF S(R_z, vars.waveHeight * (refVal - vars.zeroLevel) * 2.0); + // height of (S) projected on the projection plane + double vPos = getVPos(S, vars); + QVector3D currentPos(double(i - vars.resultDim.lx / 2) / dist, S.y(), + S.x()); + + // continue if the current point is behind the forward bumps + if (vPos <= maxVPos) { + preVPos = vPos; + preNormal = currentNormal; + prePos = currentPos; + continue; + } + + // putting colors on pixels + int currentY = int(std::floor(maxVPos)); + float current_frac = maxVPos - float(currentY); + int toY = int(std::floor(vPos)); + float to_frac = vPos - float(toY); + // if the entire current segment is in the same pixel, + // then add colors with percentage of the fraction part + if (currentY == toY) { + float ratio = to_frac - current_frac; + *result_p += getColor(preNormal, currentNormal, prePos, currentPos, + 0.5, source_host, vars) * + ratio; + preVPos = vPos; + maxVPos = vPos; + preNormal = currentNormal; + prePos = currentPos; + continue; + } + + // fill fraction part of the start pixel (currentY) + float k = + (maxVPos + (1.0 - current_frac) / 2.0 - preVPos) / (vPos - preVPos); + float4 color = getColor(preNormal, currentNormal, prePos, currentPos, k, + source_host, vars); + float ratio = 1.0 - current_frac; + *result_p += color * ratio; + + if (result_p == end_p) break; + result_p++; + + // fill pixels between currentY and toY + for (int y = currentY + 1; y < toY; y++) { + k = (float(y) + 0.5 - preVPos) / (vPos - preVPos); + *result_p = getColor(preNormal, currentNormal, prePos, currentPos, k, + source_host, vars); + if (result_p == end_p) break; + result_p++; + } + + // fill fraction part of the end pixel (toY) + k = (float(toY) + to_frac * 0.5 - preVPos) / (vPos - preVPos); + color = getColor(preNormal, currentNormal, prePos, currentPos, k, + source_host, vars); + *result_p += color * to_frac; + + preVPos = vPos; + maxVPos = vPos; + preNormal = currentNormal; + prePos = currentPos; + } + } + } +} + +//------------------------------------ + +void Iwa_FloorBumpFx::doCompute_with_Displacement( + TTile &tile, const double frame, const TRenderSettings &settings, + const FloorBumpVars &vars, float4 *source_host, float *ref_host, + float *disp_host, float4 *result_host) { + //----- Lambdas ------ + + // テクスチャの勾配を調べるサンプリング点 + int subAmount = 10; // これ、パラメータ化するか…? + QList subPoints = getSubPointsList(subAmount, vars); + + //----------- + // Texture Mode + if (vars.renderMode == TextureMode) { + // render pixels in column-major order, from bottom to top + for (int i = 0; i < vars.resultDim.lx; i++) { + // cancel check + if (settings.m_isCanceled && *settings.m_isCanceled) return; + + double maxVPos = 0.0; // maximum height of drawn pixels + double preVPos = 0.0; // height of the previous segment + QPointF preUV = QPointF(i, -vars.margin); + float4 *result_p = &result_host[i * vars.resultDim.ly]; + float4 *end_p = &result_host[(i + 1) * vars.resultDim.ly - 1]; + float preTextureOffset = 0.0; + // get height pixels from bottom to top + for (int j = -vars.margin; j < vars.refHeight; j++) { + // angle between the light axis and the line between (Q) and the eye (P) + // (Q) is a point on the projection plane at distance of j from (B) + double angle_Q = + std::atan((float(j) - (vars.outDim.ly / 2.0)) / vars.d_PT); + // (R) is an intersection between the XZ plane and the line P->Q + double R_z = vars.P_y / tan(vars.angle_el - angle_Q); + + // compute (S), a point on the bumped surface in Y-axis direction from + // (R) + float refVal = + getMapValue(QPointF(double(i), double(j)), ref_host, vars); + QPointF S(R_z, vars.waveHeight * (refVal - vars.zeroLevel) * 2.0); + + // length of (1,0,0) vector on the XZ plane, projected on the + // projection plane + double dist = (vars.d_PT * sin(vars.angle_el - angle_Q)) / vars.P_y * + cos(angle_Q); + + // UV coordinate of the texture image + QVector3D uvOffset; + float currentTextureOffset = 0.0; + if (vars.textureOffsetAmount == 0.0) { + // currentUV = QPointF(double(i), getVPos(QPointF(R_z, 0))); + } else { + // approximate the gradient by difference of the heights of neighbor + // points + + // compute gradient of the height image + QPointF offset; + for (const QPointF &subPoint : subPoints) { + // この地点のRefの値を取る + QPointF subRefPos(double(i) + dist * subPoint.x(), + getVPos(QPointF(R_z + subPoint.y(), 0), vars)); + + int sign_u = (subPoint.x() > 0) - (subPoint.x() < 0); // -1, 0, 1 + int sign_v = (subPoint.y() > 0) - (subPoint.y() < 0); // -1, 0, 1 + + float subRefValue = getMapValue(subRefPos, ref_host, vars); + offset += subRefValue * QPointF(sign_u, sign_v); + } + offset *= 1.0 / float(subPoints.count() - subAmount); // サンプル数 + offset *= vars.textureOffsetAmount / + (vars.spread * (0.5 + 0.5 / float(subAmount))); // 距離 + + uvOffset.setX(offset.x() * dist); + uvOffset.setZ(offset.y()); + // currentUV = QPointF(double(i) + offset.x() * dist, + // getVPos(QPointF(R_z + offset.y(), 0))); + if (vars.differenceMode) + currentTextureOffset = + 1000 * + std::sqrt(offset.x() * offset.x() + offset.y() * offset.y()) / + vars.textureOffsetAmount; + } + QPointF currentDispUV = + QPointF(double(i) + uvOffset.x(), + getVPos(QPointF(R_z + uvOffset.z(), 0), vars)); + + // この地点の変位の値を取得する + float dispValue = getMapValue(currentDispUV, disp_host, vars); + float dispHeight = dispValue * vars.displacement; + + // この地点の法線を取得 + QVector3D currentNormal; + double xRefGrad = + getMapValue(QPointF(double(i) + dist, double(j)), ref_host, vars) - + getMapValue(QPointF(double(i) - dist, double(j)), ref_host, vars); + double zRefGrad = + getMapValue(QPointF(double(i), getVPos(QPointF(R_z + 1, 0), vars)), + ref_host, vars) - + getMapValue(QPointF(double(i), getVPos(QPointF(R_z - 1, 0), vars)), + ref_host, vars); + currentNormal.setX(-xRefGrad * vars.waveHeight * 2.0); + currentNormal.setY(4.0); + currentNormal.setZ(-zRefGrad * vars.waveHeight * 2.0); + currentNormal.normalize(); + + // 変位後の表面座標 (M) + // ※法線のX成分は強引にY座標の変位に持ち込んで近似する + float dispXY = std::sqrt(currentNormal.x() * currentNormal.x() + + currentNormal.y() * currentNormal.y()); + QPointF M = S + QPointF(currentNormal.z(), dispXY) * dispHeight; + // float dispXRatio = std::sqrt(currentNormal.x()*currentNormal.x() + + // currentNormal.y()*currentNormal.y()) / currentNormal.y(); QPointF M = + // S + QPointF(currentNormal.z(), currentNormal.y()* dispXRatio) * + // dispHeight; + + // height of (M) projected on the projection plane + double vPos = getVPos(M, vars); + + uvOffset.setY(dispHeight); + QPointF currentUV = + QPointF(double(i) + uvOffset.x(), + getVPos(QPointF(R_z + uvOffset.z(), uvOffset.y()), vars)); + + // 手前のDisplacementで隠されている部分かどうかの判定 + bool isBehind = false; + + if (preUV.y() > currentUV.y()) isBehind = true; + /* + if ((vars.displacement > 0.0 && dispValue < 1.0) || + (vars.displacement < 0.0 && dispValue > 0.0)) { + QPointF dUV = (currentDispUV - currentUV) * 0.1; + QPointF tmpUV = currentUV + dUV; + float dHeight = dispHeight * 0.1; + float tmpHeight = dHeight; + for (int k = 1; k < 10; k++, tmpUV += dUV, tmpHeight += dHeight) { + if (getMapValue(tmpUV, disp_host, vars) * vars.displacement > + tmpHeight) { + isBehind = true; + break; + } + } + }*/ + + // continue if the current point is behind the forward bumps + if (vPos <= maxVPos) { + preVPos = vPos; + if (!isBehind) preUV = currentUV; + preTextureOffset = currentTextureOffset; + continue; + } + + // putting colors on pixels + int currentY = int(std::floor(maxVPos)); + float current_frac = maxVPos - float(currentY); + int toY = int(std::floor(vPos)); + float to_frac = vPos - float(toY); + // if the entire current segment is in the same pixel, + // then add colors with percentage of the fraction part + if (currentY == toY) { + if (!isBehind) { + float ratio = to_frac - current_frac; + if (vars.differenceMode) + *result_p += getTextureOffsetColor(preTextureOffset, + currentTextureOffset, 0.5) * + ratio; + else + *result_p += getSourcePix(lerpUV(preUV, currentUV, 0.5), + source_host, vars) * + ratio; + preUV = currentUV; + } + preVPos = vPos; + maxVPos = vPos; + preTextureOffset = currentTextureOffset; + continue; + } + + // fill fraction part of the start pixel (currentY) + if (!isBehind) { + float k = (maxVPos + (1.0 - current_frac) / 2.0 - preVPos) / + (vPos - preVPos); + float ratio = 1.0 - current_frac; + if (vars.differenceMode) + *result_p += getTextureOffsetColor(preTextureOffset, + currentTextureOffset, k) * + ratio; + else + *result_p += + getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars) * + ratio; + } + + if (result_p == end_p) break; + result_p++; + + // fill pixels between currentY and toY + for (int y = currentY + 1; y < toY; y++) { + if (!isBehind) { + float k = (float(y) + 0.5 - preVPos) / (vPos - preVPos); + if (vars.differenceMode) + *result_p += getTextureOffsetColor(preTextureOffset, + currentTextureOffset, k); + else + *result_p = + getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars); + } + if (result_p == end_p) break; + result_p++; + } + + // fill fraction part of the end pixel (toY) + if (!isBehind) { + float k = (float(toY) + to_frac * 0.5 - preVPos) / (vPos - preVPos); + if (vars.differenceMode) + *result_p += getTextureOffsetColor(preTextureOffset, + currentTextureOffset, k) * + to_frac; + else + *result_p += + getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars) * + to_frac; + preUV = currentUV; + } + + preVPos = vPos; + maxVPos = vPos; + preTextureOffset = currentTextureOffset; + } + } + } + // Other modes + // (Diffuse, Specular, Fresnel, Refraction, Reflection) + else { + // render pixels in column-major order, from bottom to top + for (int i = 0; i < vars.resultDim.lx; i++) { + // cancel check + if (settings.m_isCanceled && *settings.m_isCanceled) return; + + double maxVPos = 0.0; // maximum height of drawn pixels + double preVPos = 0.0; // height of the previous segment + QVector3D preDispNormal(0, 1, 0); + QVector3D preBaseNormal(0, 1, 0); + QVector3D prePos(i, 0, + vars.C_z); // actually the x coordinate is not i at the + // initial position.., but no problem. + QVector3D preBasePos(i, 0, vars.C_z); + + float4 *result_p = &result_host[i * vars.resultDim.ly]; + float4 *end_p = &result_host[(i + 1) * vars.resultDim.ly - 1]; + // get height pixels from bottom to top + for (int j = -vars.margin; j < vars.refHeight; j++) { + // angle between the light axis and the line between (Q) and the eye (P) + // (Q) is a point on the projection plane at distance of j from (B) + double angle_Q = + std::atan((float(j) - (vars.outDim.ly / 2.0)) / vars.d_PT); + // (R) is an intersection between the XZ plane and the line P->Q + double R_z = vars.P_y / tan(vars.angle_el - angle_Q); + // In reflection mode, do not draw surface behind the Distance Level + if (vars.renderMode == ReflectionMode && vars.distance > 0.0 && + R_z >= vars.distance) + break; + + // compute (S), a point on the bumped surface in Y-axis direction from + // (R) + float refVal = + getMapValue(QPointF(double(i), double(j)), ref_host, vars); + QPointF S(R_z, vars.waveHeight * (refVal - vars.zeroLevel) * 2.0); + // length of (1,0,0) vector on the XZ plane, projected on the projection + // plane + double dist = (vars.d_PT * sin(vars.angle_el - angle_Q)) / vars.P_y * + cos(angle_Q); + + // UV coordinate of the texture image + QVector3D uvOffset; + if (vars.textureOffsetAmount == 0.0) { + // currentUV = QPointF(double(i), getVPos(QPointF(R_z, 0))); + } else { + // approximate the gradient by difference of the heights of neighbor + // points + + // compute gradient of the height image + QPointF offset; + for (const QPointF &subPoint : subPoints) { + // この地点のRefの値を取る + QPointF subRefPos(double(i) + dist * subPoint.x(), + getVPos(QPointF(R_z + subPoint.y(), 0), vars)); + + int sign_u = (subPoint.x() > 0) - (subPoint.x() < 0); // -1, 0, 1 + int sign_v = (subPoint.y() > 0) - (subPoint.y() < 0); // -1, 0, 1 + + float subRefValue = getMapValue(subRefPos, ref_host, vars); + offset += subRefValue * QPointF(sign_u, sign_v); + } + offset *= 1.0 / float(subPoints.count() - subAmount); // サンプル数 + offset *= vars.textureOffsetAmount / + (vars.spread * (0.5 + 0.5 / float(subAmount))); // 距離 + + uvOffset.setX(offset.x() * dist); + uvOffset.setZ(offset.y()); + // currentUV = QPointF(double(i) + offset.x() * dist, + // getVPos(QPointF(R_z + offset.y(), 0))); + } + QPointF currentDispUV = + QPointF(double(i) + uvOffset.x(), + getVPos(QPointF(R_z + uvOffset.z(), 0), vars)); + + // この地点の変位の値を取得する + float dispHeight = + getMapValue(currentDispUV, disp_host, vars) * vars.displacement; + + // compute normal vector from cross-product of surface vectors + QVector3D rightPos( + 1.0, + getMapValue(QPointF(double(i) + dist, double(j)), ref_host, vars) * + vars.waveHeight, + 0.0); + QVector3D leftPos( + -1.0, + getMapValue(QPointF(double(i) - dist, double(j)), ref_host, vars) * + vars.waveHeight, + 0.0); + QVector3D farPos( + 0.0, + getMapValue(QPointF(double(i), getVPos(QPointF(R_z + 1, 0), vars)), + ref_host, vars) * + vars.waveHeight, + 1.0); + QVector3D nearPos( + 0.0, + getMapValue(QPointF(double(i), getVPos(QPointF(R_z - 1, 0), vars)), + ref_host, vars) * + vars.waveHeight, + -1.0); + // 左手系座標なので外積を反転する + QVector3D currentNormal = + -QVector3D::crossProduct(rightPos - leftPos, farPos - nearPos); + currentNormal.normalize(); + + // 変位後の表面座標 (M) + // ※法線のX成分は強引にY座標の変位に持ち込んで近似する + float dispXY = std::sqrt(currentNormal.x() * currentNormal.x() + + currentNormal.y() * currentNormal.y()); + QPointF M = S + QPointF(currentNormal.z(), dispXY) * dispHeight; + + // 前後左右の座標に変位を加える + QPointF rightDispUV = currentDispUV + QPointF(dist, 0); + QPointF leftDispUV = currentDispUV + QPointF(-dist, 0); + QPointF farDispUV(currentDispUV.x(), + getVPos(QPointF(R_z + uvOffset.z() + 1, 0), vars)); + QPointF nearDispUV(currentDispUV.x(), + getVPos(QPointF(R_z + uvOffset.z() - 1, 0), vars)); + float rightDispHeight = + getMapValue(rightDispUV, disp_host, vars) * vars.displacement; + float leftDispHeight = + getMapValue(leftDispUV, disp_host, vars) * vars.displacement; + float farDispHeight = + getMapValue(farDispUV, disp_host, vars) * vars.displacement; + float nearDispHeight = + getMapValue(nearDispUV, disp_host, vars) * vars.displacement; + + rightPos += currentNormal * rightDispHeight; + leftPos += currentNormal * leftDispHeight; + farPos += currentNormal * farDispHeight; + nearPos += currentNormal * nearDispHeight; + + // 外積で法線を求める 左手系座標なので外積を反転する + QVector3D dispNormal = + -QVector3D::crossProduct(rightPos - leftPos, farPos - nearPos); + dispNormal.normalize(); + + // height of (M) projected on the projection plane + double vPos = getVPos(M, vars); + QVector3D currentPos(double(i - vars.resultDim.lx / 2) / dist, M.y(), + M.x()); + + // Diffuse差分取得のために、変形無しの状態の法線を取得する + QVector3D baseNormal; + // Specular差分取得のために、変形無しの状態の位置も取得 + QVector3D basePos; + if (vars.differenceMode) { + baseNormal = -QVector3D::crossProduct( + QVector3D(2.0, rightDispHeight - leftDispHeight, 0.0), + QVector3D(0.0, farDispHeight - nearDispHeight, 2.0)); + basePos = QVector3D(currentPos.x() + uvOffset.x() / dist, dispHeight, + currentPos.z() + uvOffset.z()); + } + + // continue if the current point is behind the forward bumps + if (vPos <= maxVPos) { + preVPos = vPos; + preDispNormal = dispNormal; + preBaseNormal = baseNormal; + prePos = currentPos; + preBasePos = basePos; + continue; + } + + // putting colors on pixels + int currentY = int(std::floor(maxVPos)); + float current_frac = maxVPos - float(currentY); + int toY = int(std::floor(vPos)); + float to_frac = vPos - float(toY); + // if the entire current segment is in the same pixel, + // then add colors with percentage of the fraction part + if (currentY == toY) { + float ratio = to_frac - current_frac; + *result_p += getColor(preDispNormal, dispNormal, prePos, currentPos, + 0.5, source_host, vars, preBaseNormal, + baseNormal, preBasePos, basePos) * + ratio; + preVPos = vPos; + maxVPos = vPos; + preDispNormal = dispNormal; + preBaseNormal = baseNormal; + prePos = currentPos; + preBasePos = basePos; + continue; + } + + // fill fraction part of the start pixel (currentY) + float k = + (maxVPos + (1.0 - current_frac) / 2.0 - preVPos) / (vPos - preVPos); + float4 color = getColor(preDispNormal, dispNormal, prePos, currentPos, + k, source_host, vars, preBaseNormal, baseNormal, + preBasePos, basePos); + float ratio = 1.0 - current_frac; + *result_p += color * ratio; + + if (result_p == end_p) break; + result_p++; + + // fill pixels between currentY and toY + for (int y = currentY + 1; y < toY; y++) { + k = (float(y) + 0.5 - preVPos) / (vPos - preVPos); + *result_p = getColor(preDispNormal, dispNormal, prePos, currentPos, k, + source_host, vars, preBaseNormal, baseNormal, + preBasePos, basePos); + if (result_p == end_p) break; + result_p++; + } + + // fill fraction part of the end pixel (toY) + k = (float(toY) + to_frac * 0.5 - preVPos) / (vPos - preVPos); + color = getColor(preDispNormal, dispNormal, prePos, currentPos, k, + source_host, vars, preBaseNormal, baseNormal, + preBasePos, basePos); + *result_p += color * to_frac; + + preVPos = vPos; + maxVPos = vPos; + preDispNormal = dispNormal; + preBaseNormal = baseNormal; + prePos = currentPos; + preBasePos = basePos; + } + } + } +} + +//------------------------------------------------------------ +// convert input tile's channel values to float4 values +//------------------------------------------------------------ + +template +void Iwa_FloorBumpFx::setSourceRaster(const RASTER srcRas, float4 *srcMem, + TDimensionI dim) { + float4 *s_p = srcMem; + for (int j = 0; j < dim.ly; j++) { + PIXEL *s_pix = srcRas->pixels(j); + for (int i = 0; i < dim.lx; i++, s_pix++, s_p++) { + (*s_p).x = (float)s_pix->r / (float)PIXEL::maxChannelValue; + (*s_p).y = (float)s_pix->g / (float)PIXEL::maxChannelValue; + (*s_p).z = (float)s_pix->b / (float)PIXEL::maxChannelValue; + (*s_p).w = (float)s_pix->m / (float)PIXEL::maxChannelValue; + } + } +} + +void Iwa_FloorBumpFx::setRefRaster(const TRaster64P refRas, float *refMem, + TDimensionI dim, bool isRef) { + float zeroLevel = + (isRef) ? float(128) / float(TPixel32::maxChannelValue) : 0.0; + float *r_p = refMem; + for (int j = 0; j < dim.ly; j++) { + TPixel64 *r_pix = refRas->pixels(j); + for (int i = 0; i < dim.lx; i++, r_pix++, r_p++) { + float r = (float)r_pix->r / (float)TPixel64::maxChannelValue; + float g = (float)r_pix->g / (float)TPixel64::maxChannelValue; + float b = (float)r_pix->b / (float)TPixel64::maxChannelValue; + float m = (float)r_pix->m / (float)TPixel64::maxChannelValue; + // taking brightness + float brightness = 0.298912f * r + 0.586610f * g + 0.114478f * b; + (*r_p) = brightness * m + zeroLevel * (1.0 - m); + } + } +} + +//------------------------------------ + +void Iwa_FloorBumpFx::doCompute(TTile &tile, double frame, + const TRenderSettings &rend_sets) { + FloorBumpVars vars; + vars.renderMode = m_renderMode->getValue(); + if ((isTextureUsed(vars.renderMode) && !m_texture.isConnected()) || + !m_heightRef.isConnected()) { + tile.getRaster()->clear(); + return; + } + + initVars(vars, tile, rend_sets, frame); + + // メモリの確保 + float4 *source_host = nullptr; + float *ref_host; + float *disp_host = nullptr; + + TRasterGR8P source_host_ras; + if (isTextureUsed(vars.renderMode)) { + source_host_ras = + TRasterGR8P(vars.sourceDim.lx * sizeof(float4), vars.sourceDim.ly); + source_host_ras->lock(); + source_host = (float4 *)source_host_ras->getRawData(); + } + + TRasterGR8P ref_host_ras(vars.refDim.lx * sizeof(float), vars.refDim.ly); + ref_host_ras->lock(); + ref_host = (float *)ref_host_ras->getRawData(); + + TRasterGR8P disp_host_ras; + if (vars.displacement != 0.0 && + m_dispRef.isConnected()) { // renderMode すべてに対応させるかはTBD + disp_host_ras = TRasterGR8P(vars.refDim.lx * sizeof(float), vars.refDim.ly); + disp_host_ras->lock(); + disp_host = (float *)disp_host_ras->getRawData(); + } + + // 画像の取得、格納 + { + if (isTextureUsed(vars.renderMode)) { + TRenderSettings source_sets(rend_sets); + TPointD sourceTilePos = + (tile.m_pos - TPointD(vars.margin, vars.margin)) * vars.precision; + source_sets.m_affine *= TScale(vars.precision, vars.precision); + + TTile sourceTile; + m_texture->allocateAndCompute(sourceTile, sourceTilePos, vars.sourceDim, + tile.getRaster(), frame, source_sets); + + TRaster32P ras32 = (TRaster32P)sourceTile.getRaster(); + TRaster64P ras64 = (TRaster64P)sourceTile.getRaster(); + if (ras32) + setSourceRaster(ras32, source_host, + vars.sourceDim); + else if (ras64) + setSourceRaster(ras64, source_host, + vars.sourceDim); + } + // height image is always computed in 16bpc regardless of the current render + // settings in order to get smooth gradient + TRenderSettings ref_sets(rend_sets); + TPointD refTilePos = tile.m_pos - TPointD(vars.margin, vars.margin); + ref_sets.m_bpp = 64; + TTile refTile; + m_heightRef->allocateAndCompute(refTile, refTilePos, vars.refDim, + TRasterP(), frame, ref_sets); + setRefRaster((TRaster64P)refTile.getRaster(), ref_host, vars.refDim, true); + + // disp imageも同様 + if (disp_host) { + TTile dispTile; + m_dispRef->allocateAndCompute(dispTile, refTilePos, vars.refDim, + TRasterP(), frame, ref_sets); + setRefRaster((TRaster64P)dispTile.getRaster(), disp_host, vars.refDim, + false); + } + } + + // prepare the memory for result image + TRasterGR8P result_host_ras(vars.resultDim.lx * sizeof(float4), + vars.resultDim.ly); + float4 *result_host; + result_host_ras->lock(); + result_host = (float4 *)result_host_ras->getRawData(); + + // compute + if (disp_host) { + doCompute_with_Displacement(tile, frame, rend_sets, vars, source_host, + ref_host, disp_host, result_host); + } else { + doCompute_CPU(tile, frame, rend_sets, vars, source_host, ref_host, + result_host); + } + + if (isTextureUsed(vars.renderMode)) source_host_ras->unlock(); + ref_host_ras->unlock(); + if (disp_host) disp_host_ras->unlock(); + + // cancel check + if (rend_sets.m_isCanceled && *rend_sets.m_isCanceled) { + tile.getRaster()->clear(); + return; + } + + // output the result + TRaster32P outRas32 = (TRaster32P)tile.getRaster(); + TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + if (outRas32) + setOutputRaster(result_host, outRas32, vars.outDim, + vars.resultDim.ly); + else if (outRas64) + setOutputRaster(result_host, outRas64, vars.outDim, + vars.resultDim.ly); + + result_host_ras->unlock(); +} + +//------------------------------------ + +void Iwa_FloorBumpFx::getParamUIs(TParamUIConcept *&concepts, int &length) { + concepts = new TParamUIConcept[length = 3]; + + concepts[0].m_type = TParamUIConcept::VERTICAL_POS; + concepts[0].m_label = "Eye Level"; + concepts[0].m_params.push_back(m_eyeLevel); + + concepts[1].m_type = TParamUIConcept::VERTICAL_POS; + concepts[1].m_label = "Draw Level"; + concepts[1].m_params.push_back(m_drawLevel); + + concepts[2].m_type = TParamUIConcept::VERTICAL_POS; + concepts[2].m_label = "Distance Level"; + concepts[2].m_params.push_back(m_distanceLevel); + concepts[2].m_params.push_back(m_renderMode); +} + +//------------------------------------ + +FX_PLUGIN_IDENTIFIER(Iwa_FloorBumpFx, "iwa_FloorBumpFx") diff --git a/toonz/sources/stdfx/iwa_floorbumpfx.h b/toonz/sources/stdfx/iwa_floorbumpfx.h new file mode 100644 index 00000000..3a47d514 --- /dev/null +++ b/toonz/sources/stdfx/iwa_floorbumpfx.h @@ -0,0 +1,163 @@ +#pragma once + +#ifndef IWA_FLOORBUMPFX_H +#define IWA_FLOORBUMPFX_H + +#include "tfxparam.h" +#include "stdfx.h" +#include "tparamset.h" + +#include +struct float4 { + float x, y, z, w; + float4 operator*(const float &v) const { + return float4{x * v, y * v, z * v, w * v}; + } + float4 &operator+=(const float4 &v) { + x += v.x; + y += v.y; + z += v.z; + w += v.w; + return *this; + } +}; + +class Iwa_FloorBumpFx final : public TStandardRasterFx { + FX_PLUGIN_DECLARATION(Iwa_FloorBumpFx) +public: + enum RenderMode { + TextureMode = 0, + DiffuseMode, + SpecularMode, + FresnelMode, + RefractionMode, + ReflectionMode + }; + + struct FloorBumpVars { + double waveHeight; + double displacement; + int refHeight; + TDimensionI outDim; + TDimensionI resultDim; + int margin; + double precision; + // add margins to all ends and multiply by precision value + TDimensionI sourceDim; // u + // only add margins for height image + TDimensionI refDim; // u + + // collecting parameters + double textureOffsetAmount; // u + double spread; // u + double camAltitude; + int renderMode; // u + bool differenceMode; // u + + // making pixels in gray128 to be zero level height + // ( 128/255. IT'S NOT 0.5! ) + double zeroLevel; // u + double H; // u + double W; // u + // angle between the optical axis and the horizontal axis + double angle_el; // u + // Y coordinate of the Eye position (P) + double P_y; // u + // distance from the Eye (P) to the center of the projection plane (T) + double d_PT; // u + + // Z-Y position of the center of top edge of the projection plane (A) + QPointF A; // u + // Z-Y position of the center of bottom edge of the projection plane (B) + QPointF B; // u + + // (C) is an intersection between the XZ plane and the line P->B + double C_z; // u + QVector3D sunVec; // u + double base_fresnel_ref; // u + double depth, r_index; // uu + double distance; // u + QVector3D eyePos; // u + }; + +protected: + TRasterFxPort m_heightRef; // height reference image + TRasterFxPort m_texture; // texture image + TRasterFxPort m_dispRef; // displacement image + + TIntEnumParamP m_renderMode; + + TDoubleParamP m_fov; // camera fov (degrees) + TDoubleParamP + m_cameraAltitude; // height of the bottom edge of projection plane + + TDoubleParamP m_eyeLevel; // height of the vanishing point + TDoubleParamP m_drawLevel; // upper rendering boundary + + TDoubleParamP m_waveHeight; // height of waves to the both sides (i.e. + // amplitude becomes 2*waveHeight) + + TBoolParamP + m_differenceMode; // available in diffuse and fresnel mode, + // render brightness difference from unbumped state + + // Texture mode parameters + TDoubleParamP m_textureOffsetAmount; // amount of texture trailing along with + // gradient of the bump + TDoubleParamP + m_textureOffsetSpread; // adding "blur" to the gradient distribution + + TDoubleParamP m_sourcePrecision; // to load the texture with higher dpi + TDoubleParamP m_souceMargin; // margins to be added to all edges for both the + // height reference and the texture images + + TDoubleParamP m_displacement; + + // Shading (Diffuse and Specular) modes parameters + TDoubleParamP m_lightAzimuth; // light is in front of camera with azimuth=0. + // clockwise angle (degrees) + TDoubleParamP m_lightElevation; // (degrees) + + // Refraction mode parameters + TDoubleParamP m_depth; // water depth. the bottom will be placed at -depth + TDoubleParamP + m_refractiveIndex; // refractive index of the medium under the surface + + // Reflection mode parameter + TDoubleParamP m_distanceLevel; // the distance of the reflected object + // specified by the postion on the surface + + // convert output values (in float4) to channel value + template + void setOutputRaster(float4 *srcMem, const RASTER dstRas, TDimensionI dim, + int drawLevel); + + // convert input tile's channel values to float4 values + template + void setSourceRaster(const RASTER srcRas, float4 *srcMem, TDimensionI dim); + + void setRefRaster(const TRaster64P refRas, float *refMem, TDimensionI dim, + bool isRef); + + inline void initVars(FloorBumpVars &vars, TTile &tile, + const TRenderSettings &settings, double frame); + +public: + Iwa_FloorBumpFx(); + bool doGetBBox(double frame, TRectD &bBox, + const TRenderSettings &info) override; + bool canHandle(const TRenderSettings &info, double frame) override; + void doCompute(TTile &tile, double frame, + const TRenderSettings &rend_sets) override; + void doCompute_CPU(TTile &tile, const double frame, + const TRenderSettings &settings, const FloorBumpVars &vars, + float4 *source_host, float *ref_host, float4 *result_host); + void doCompute_with_Displacement(TTile &tile, const double frame, + const TRenderSettings &settings, + const FloorBumpVars &vars, + float4 *source_host, float *ref_host, + float *disp_host, float4 *result_host); + void getParamUIs(TParamUIConcept *&concepts, int &length) override; +}; + +#endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_flowblurfx.cpp b/toonz/sources/stdfx/iwa_flowblurfx.cpp new file mode 100644 index 00000000..d6774035 --- /dev/null +++ b/toonz/sources/stdfx/iwa_flowblurfx.cpp @@ -0,0 +1,507 @@ +//-------------------------------------------------------------- +// This Fx is based on & modified from the source code by Zhanping Liu, licensed +// with the terms as follows: + +//----------------[Start of the License Notice]----------------- +//////////////////////////////////////////////////////////////////////////// +/// Line Integral Convolution for Flow Visualization /// +/// Initial Version /// +/// May 15, 1999 /// +/// by Zhanping Liu /// +/// (zhanping@erc.msstate.edu) /// +/// while with Graphics Laboratory, /// +/// Peking University, P. R. China /// +///----------------------------------------------------------------------/// +/// Later Condensed /// +/// May 4, 2002 /// +/// /// +/// VAIL (Visualization Analysis & Imaging Laboratory) /// +/// ERC (Engineering Research Center) /// +/// Mississippi State University /// +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +/// This code was developed based on the original algorithm proposed by/// +/// Brian Cabral and Leith (Casey) Leedom in the paper "Imaging Vector/// +/// Fields Using Line Integral Convolution", published in Proceedings of/// +/// ACM SigGraph 93, Aug 2-6, Anaheim, California, pp. 263-270, 1993./// +/// Permission to use, copy, modify, distribute and sell this code for any/// +/// purpose is hereby granted without fee, provided that the above notice/// +/// appears in all copies and that both that notice and this permission/// +/// appear in supporting documentation. The developer of this code makes/// +/// no representations about the suitability of this code for any/// +/// purpose. It is provided "as is" without express or implied warranty./// +//////////////////////////////////////////////////////////////////////////// +//-----------------[End of the License Notice]------------------ + +//-------------------------------------------------------------- +#include "iwa_flowblurfx.h" +#include + +namespace { +const double LINE_SQUARE_CLIP_MAX = 100000.0; +const double VECTOR_COMPONENT_MIN = 0.05; + +inline double clamp01(double val) { + return (val > 1.0) ? 1.0 : (val < 0.0) ? 0.0 : 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); +} + +} // namespace +//------------------------------------------------------------ +template +void Iwa_FlowBlurFx::setSourceTileToBuffer(const RASTER srcRas, double4 *buf, + bool isLinear, double gamma) { + double4 *buf_p = buf; + for (int j = 0; j < srcRas->getLy(); j++) { + PIXEL *pix = srcRas->pixels(j); + for (int i = 0; i < srcRas->getLx(); i++, pix++, buf_p++) { + (*buf_p).x = double(pix->r) / double(PIXEL::maxChannelValue); + (*buf_p).y = double(pix->g) / double(PIXEL::maxChannelValue); + (*buf_p).z = double(pix->b) / double(PIXEL::maxChannelValue); + (*buf_p).w = double(pix->m) / double(PIXEL::maxChannelValue); + if (isLinear && (*buf_p).w > 0.0) { + (*buf_p).x = + to_linear_color_space((*buf_p).x / (*buf_p).w, 1.0, gamma) * + (*buf_p).w; + (*buf_p).y = + to_linear_color_space((*buf_p).y / (*buf_p).w, 1.0, gamma) * + (*buf_p).w; + (*buf_p).z = + to_linear_color_space((*buf_p).z / (*buf_p).w, 1.0, gamma) * + (*buf_p).w; + } + } + } +} +//------------------------------------------------------------ + +template +void Iwa_FlowBlurFx::setFlowTileToBuffer(const RASTER flowRas, double2 *buf, + double *refBuf) { + double2 *buf_p = buf; + double *refBuf_p = refBuf; + for (int j = 0; j < flowRas->getLy(); j++) { + PIXEL *pix = flowRas->pixels(j); + for (int i = 0; i < flowRas->getLx(); i++, pix++, buf_p++) { + double val = double(pix->r) / double(PIXEL::maxChannelValue); + (*buf_p).x = val * 2.0 - 1.0; + val = double(pix->g) / double(PIXEL::maxChannelValue); + (*buf_p).y = val * 2.0 - 1.0; + if (refBuf != nullptr) { + *refBuf_p = double(pix->b) / double(PIXEL::maxChannelValue); + refBuf_p++; + } + } + } +} + +//------------------------------------------------------------ + +template +void Iwa_FlowBlurFx::setReferenceTileToBuffer(const RASTER refRas, + double *buf) { + double *buf_p = buf; + for (int j = 0; j < refRas->getLy(); j++) { + PIXEL *pix = refRas->pixels(j); + for (int i = 0; i < refRas->getLx(); i++, pix++, buf_p++) { + // Value = 0.3R 0.59G 0.11B + (*buf_p) = (double(pix->r) * 0.3 + double(pix->g) * 0.59 + + double(pix->b) * 0.11) / + double(PIXEL::maxChannelValue); + } + } +} + +//------------------------------------------------------------ + +template +void Iwa_FlowBlurFx::setOutputRaster(double4 *out_buf, const RASTER dstRas, + bool isLinear, double gamma) { + double4 *buf_p = out_buf; + for (int j = 0; j < dstRas->getLy(); j++) { + PIXEL *pix = dstRas->pixels(j); + for (int i = 0; i < dstRas->getLx(); i++, pix++, buf_p++) { + if (!isLinear || (*buf_p).w == 0.0) { + pix->r = (typename PIXEL::Channel)(clamp01((*buf_p).x) * + (double)PIXEL::maxChannelValue); + pix->g = (typename PIXEL::Channel)(clamp01((*buf_p).y) * + (double)PIXEL::maxChannelValue); + pix->b = (typename PIXEL::Channel)(clamp01((*buf_p).z) * + (double)PIXEL::maxChannelValue); + } else { + double val; + val = to_nonlinear_color_space((*buf_p).x / (*buf_p).w, 1.0, gamma) * + (*buf_p).w; + pix->r = (typename PIXEL::Channel)(clamp01(val) * + (double)PIXEL::maxChannelValue); + val = to_nonlinear_color_space((*buf_p).y / (*buf_p).w, 1.0, gamma) * + (*buf_p).w; + pix->g = (typename PIXEL::Channel)(clamp01(val) * + (double)PIXEL::maxChannelValue); + val = to_nonlinear_color_space((*buf_p).z / (*buf_p).w, 1.0, gamma) * + (*buf_p).w; + pix->b = (typename PIXEL::Channel)(clamp01(val) * + (double)PIXEL::maxChannelValue); + } + + pix->m = (typename PIXEL::Channel)(clamp01((*buf_p).w) * + (double)PIXEL::maxChannelValue); + } + } +} +//------------------------------------------------------------ + +void FlowBlurWorker::run() { + auto sourceAt = [&](int x, int y) { + if (x < 0 || x >= m_dim.lx || y < 0 || y >= m_dim.ly) return double4(); + return m_source_buf[y * m_dim.lx + x]; + }; + auto flowAt = [&](int x, int y) { + if (x < 0 || x >= m_dim.lx || y < 0 || y >= m_dim.ly) return double2(); + return m_flow_buf[y * m_dim.lx + x]; + }; + + // WXeBbNz̊mx֐i[(s = 1/3A0-1͈̔͂100) + double logDist[101]; + if (m_filterType == Gaussian) { + double scale = 1.0 / 3.0; + for (int i = 0; i <= 101; i++) { + double x = (double)i / 100.0; + logDist[i] = std::tanh(x / (2.0 * scale)); + } + } + // 0-1ɐKϐ̗ݐϒlԂ + auto getCumulative = [&](double pos) { + if (pos > 1.0) return 1.0; + if (m_filterType == Linear) + return 2.0 * pos - pos * pos; + else if (m_filterType == Gaussian) + return logDist[(int)(std::ceil(pos * 100.0))]; + else // Flat + return pos; + }; + + int max_ADVCTS = int(m_krnlen * 3); // MAXIMUM number of advection steps per + // direction to break dead loops + + double4 *out_p = &m_out_buf[m_yFrom * m_dim.lx]; + + double *ref_p = + (m_reference_buf) ? &m_reference_buf[m_yFrom * m_dim.lx] : nullptr; + + // for each pixel in the 2D output LIC image + for (int j = m_yFrom; j < m_yTo; j++) { + for (int i = 0; i < m_dim.lx; i++, out_p++) { + double4 t_acum[2]; // texture accumulators zero-initialized + double w_acum[2] = {0., 0.}; // weight accumulators zero-initialized + + double blurLength = (ref_p) ? (*ref_p) * m_krnlen : m_krnlen; + + if (blurLength == 0.0) { + (*out_p) = sourceAt(i, j); + if (ref_p) ref_p++; + continue; + } + + // for either advection direction + for (int advDir = 0; advDir < 2; advDir++) { + // init the step counter, curve-length measurer, and streamline seed/// + int advcts = + 0; // number of ADVeCTion stepS per direction (a step counter) + double curLen = 0.0; // CURrent LENgth of the streamline + double clp0_x = + (double)i + 0.5; // x-coordinate of CLiP point 0 (current) + double clp0_y = + (double)j + 0.5; // y-coordinate of CLiP point 0 (current) + + // until the streamline is advected long enough or a tightly spiralling + // center / focus is encountered/// + + double2 pre_vctr = flowAt(int(clp0_x), int(clp0_y)); + if (advDir == 1) { + pre_vctr.x = -pre_vctr.x; + pre_vctr.y = -pre_vctr.y; + } + + while (curLen < blurLength && advcts < max_ADVCTS) { + // access the vector at the sample + double2 vctr = flowAt(int(clp0_x), int(clp0_y)); + + // in case of a critical point + if (vctr.x == 0.0 && vctr.y == 0.0) { + w_acum[advDir] = (advcts == 0) ? 1.0 : w_acum[advDir]; + break; + } + + // negate the vector for the backward-advection case + if (advDir == 1) { + vctr.x = -vctr.x; + vctr.y = -vctr.y; + } + + // assuming that the flow field vector is bi-directional + // if the vector is at an acute angle to the previous vector, negate + // it. + if (vctr.x * pre_vctr.x + vctr.y * pre_vctr.y < 0) { + vctr.x = -vctr.x; + vctr.y = -vctr.y; + } + + // clip the segment against the pixel boundaries --- find the shorter + // from the two clipped segments replace all if-statements whenever + // possible as they might affect the computational speed + double tmpLen; + double segLen = LINE_SQUARE_CLIP_MAX; // SEGment LENgth + segLen = (vctr.x < -VECTOR_COMPONENT_MIN) + ? (std::floor(clp0_x) - clp0_x) / vctr.x + : segLen; + segLen = + (vctr.x > VECTOR_COMPONENT_MIN) + ? (std::floor(std::floor(clp0_x) + 1.5) - clp0_x) / vctr.x + : segLen; + segLen = (vctr.y < -VECTOR_COMPONENT_MIN) + ? (((tmpLen = (std::floor(clp0_y) - clp0_y) / vctr.y) < + segLen) + ? tmpLen + : segLen) + : segLen; + segLen = (vctr.y > VECTOR_COMPONENT_MIN) + ? (((tmpLen = (std::floor(std::floor(clp0_y) + 1.5) - + clp0_y) / + vctr.y) < segLen) + ? tmpLen + : segLen) + : segLen; + + // update the curve-length measurers + double prvLen = curLen; + curLen += segLen; + segLen += 0.0004; + + // check if the filter has reached either end + segLen = + (curLen > m_krnlen) ? ((curLen = m_krnlen) - prvLen) : segLen; + + // obtain the next clip point + double clp1_x = clp0_x + vctr.x * segLen; + double clp1_y = clp0_y + vctr.y * segLen; + + // obtain the middle point of the segment as the texture-contributing + // sample + double2 samp; + samp.x = (clp0_x + clp1_x) * 0.5; + samp.y = (clp0_y + clp1_y) * 0.5; + + // obtain the texture value of the sample + double4 texVal = sourceAt(int(samp.x), int(samp.y)); + + // update the accumulated weight and the accumulated composite texture + // (texture x weight) + double W_ACUM = getCumulative(curLen / blurLength); + // double W_ACUM = curLen; + // W_ACUM = wgtLUT[int(curLen * len2ID)]; + + double smpWgt = W_ACUM - w_acum[advDir]; + w_acum[advDir] = W_ACUM; + t_acum[advDir].x += texVal.x * smpWgt; + t_acum[advDir].y += texVal.y * smpWgt; + t_acum[advDir].z += texVal.z * smpWgt; + t_acum[advDir].w += texVal.w * smpWgt; + + // update the step counter and the "current" clip point + advcts++; + clp0_x = clp1_x; + clp0_y = clp1_y; + + pre_vctr = vctr; + // check if the streamline has gone beyond the flow field/// + if (clp0_x < 0.0 || clp0_x >= double(m_dim.lx) || clp0_y < 0.0 || + clp0_y >= double(m_dim.ly)) + break; + } + } + + // normalize the accumulated composite texture + (*out_p).x = (t_acum[0].x + t_acum[1].x) / (w_acum[0] + w_acum[1]); + (*out_p).y = (t_acum[0].y + t_acum[1].y) / (w_acum[0] + w_acum[1]); + (*out_p).z = (t_acum[0].z + t_acum[1].z) / (w_acum[0] + w_acum[1]); + (*out_p).w = (t_acum[0].w + t_acum[1].w) / (w_acum[0] + w_acum[1]); + + if (ref_p) ref_p++; + } + } +} + +Iwa_FlowBlurFx::Iwa_FlowBlurFx() + : m_length(5.0) + , m_linear(false) + , m_gamma(2.2) + , m_filterType(new TIntEnumParam(Linear, "Linear")) + , m_referenceMode(new TIntEnumParam(REFERENCE, "Reference Image")) { + addInputPort("Source", m_source); + addInputPort("Flow", m_flow); + addInputPort("Reference", m_reference); + + bindParam(this, "length", m_length); + bindParam(this, "linear", m_linear); + bindParam(this, "gamma", m_gamma); + bindParam(this, "filterType", m_filterType); + bindParam(this, "referenceMode", m_referenceMode); + + m_length->setMeasureName("fxLength"); + m_length->setValueRange(0., 100.0); + m_gamma->setValueRange(0.2, 5.0); + + m_filterType->addItem(Gaussian, "Gaussian"); + m_filterType->addItem(Flat, "Flat"); + + m_referenceMode->addItem(FLOW_BLUE_CHANNEL, "Blue Channel of Flow Image"); +} + +void Iwa_FlowBlurFx::doCompute(TTile &tile, double frame, + const TRenderSettings &settings) { + // do nothing if Source or Flow port is not connected + if (!m_source.isConnected() || !m_flow.isConnected()) { + tile.getRaster()->clear(); + return; + } + + // output size + TDimension dim = tile.getRaster()->getSize(); + + double fac = sqrt(fabs(settings.m_affine.det())); + double krnlen = fac * m_length->getValue(frame); + int max_ADVCTS = int(krnlen * 3); // MAXIMUM number of advection steps per + // direction to break dead loops + + if (max_ADVCTS == 0) { + // No blur will be done. The underlying fx may pass by. + m_source->compute(tile, frame, settings); + return; + } + + bool isLinear = m_linear->getValue(); + double gamma = m_gamma->getValue(frame); + + // obtain Source memory buffer (RGBA) + TTile sourceTile; + m_source->allocateAndCompute(sourceTile, tile.m_pos, dim, tile.getRaster(), + frame, settings); + // allocate buffer + double4 *source_buf; + TRasterGR8P source_buf_ras(dim.lx * dim.ly * sizeof(double4), 1); + source_buf_ras->lock(); + source_buf = (double4 *)source_buf_ras->getRawData(); + TRaster32P ras32 = tile.getRaster(); + TRaster64P ras64 = tile.getRaster(); + if (ras32) + setSourceTileToBuffer(sourceTile.getRaster(), + source_buf, isLinear, gamma); + else if (ras64) + setSourceTileToBuffer(sourceTile.getRaster(), + source_buf, isLinear, gamma); + + // obtain Flow memory buffer (XY) + TTile flowTile; + m_flow->allocateAndCompute(flowTile, tile.m_pos, dim, tile.getRaster(), frame, + settings); + // allocate buffer + double2 *flow_buf; + TRasterGR8P flow_buf_ras(dim.lx * dim.ly * sizeof(double2), 1); + flow_buf_ras->lock(); + flow_buf = (double2 *)flow_buf_ras->getRawData(); + + double *reference_buf = nullptr; + TRasterGR8P reference_buf_ras; + if (m_referenceMode->getValue() == FLOW_BLUE_CHANNEL || + m_reference.isConnected()) { + reference_buf_ras = TRasterGR8P(dim.lx * dim.ly * sizeof(double), 1); + reference_buf_ras->lock(); + reference_buf = (double *)reference_buf_ras->getRawData(); + } + + if (ras32) + setFlowTileToBuffer(flowTile.getRaster(), flow_buf, + reference_buf); + else if (ras64) + setFlowTileToBuffer(flowTile.getRaster(), flow_buf, + reference_buf); + + if (m_referenceMode->getValue() == REFERENCE && m_reference.isConnected()) { + TTile referenceTile; + m_reference->allocateAndCompute(referenceTile, tile.m_pos, dim, + tile.getRaster(), frame, settings); + if (ras32) + setReferenceTileToBuffer(referenceTile.getRaster(), + reference_buf); + else if (ras64) + setReferenceTileToBuffer(referenceTile.getRaster(), + reference_buf); + } + + // buffer for output raster + double4 *out_buf; + TRasterGR8P out_buf_ras(dim.lx * dim.ly * sizeof(double4), 1); + out_buf_ras->lock(); + out_buf = (double4 *)out_buf_ras->getRawData(); + + int activeThreadCount = QThreadPool::globalInstance()->activeThreadCount(); + // use half of the available threads + int threadAmount = std::max(1, activeThreadCount / 2); + FILTER_TYPE filterType = (FILTER_TYPE)m_filterType->getValue(); + QList threadList; + int tmpStart = 0; + for (int t = 0; t < threadAmount; t++) { + int tmpEnd = + (int)std::round((float)(dim.ly * (t + 1)) / (float)threadAmount); + + FlowBlurWorker *worker = + new FlowBlurWorker(source_buf, flow_buf, out_buf, reference_buf, dim, + krnlen, tmpStart, tmpEnd, filterType); + worker->start(); + threadList.append(worker); + tmpStart = tmpEnd; + } + + for (auto worker : threadList) { + worker->wait(); + delete worker; + } + + source_buf_ras->unlock(); + flow_buf_ras->unlock(); + if (reference_buf) reference_buf_ras->unlock(); + + // render vector field with red & green channels + if (ras32) + setOutputRaster(out_buf, ras32, isLinear, gamma); + else if (ras64) + setOutputRaster(out_buf, ras64, isLinear, gamma); + + out_buf_ras->unlock(); +} + +bool Iwa_FlowBlurFx::doGetBBox(double frame, TRectD &bBox, + const TRenderSettings &info) { + bBox = TConsts::infiniteRectD; + return true; +} + +bool Iwa_FlowBlurFx::canHandle(const TRenderSettings &info, double frame) { + return true; +} + +FX_PLUGIN_IDENTIFIER(Iwa_FlowBlurFx, "iwa_FlowBlurFx") diff --git a/toonz/sources/stdfx/iwa_flowblurfx.h b/toonz/sources/stdfx/iwa_flowblurfx.h new file mode 100644 index 00000000..80f7ddeb --- /dev/null +++ b/toonz/sources/stdfx/iwa_flowblurfx.h @@ -0,0 +1,92 @@ +#pragma once + +#ifndef IWA_FLOWBLURFX +#define IWA_FLOWBLURFX + +#include "stdfx.h" +#include "tfxparam.h" + +#include + +struct double2 { + double x = 0., y = 0.; +}; + +struct double4 { + double x = 0., y = 0., z = 0, w = 0.; +}; + +enum FILTER_TYPE { Linear = 0, Gaussian, Flat }; + +class FlowBlurWorker : public QThread { + double4 *m_source_buf; + double2 *m_flow_buf; + double4 *m_out_buf; + double *m_reference_buf; + TDimension m_dim; + double m_krnlen; + int m_yFrom, m_yTo; + FILTER_TYPE m_filterType; + +public: + FlowBlurWorker(double4 *source_buf, double2 *flow_buf, double4 *out_buf, + double *ref_buf, TDimension dim, double krnlen, int yFrom, + int yTo, FILTER_TYPE filterType) + : m_source_buf(source_buf) + , m_flow_buf(flow_buf) + , m_out_buf(out_buf) + , m_reference_buf(ref_buf) + , m_dim(dim) + , m_krnlen(krnlen) + , m_yFrom(yFrom) + , m_yTo(yTo) + , m_filterType(filterType) {} + + void run(); +}; + +class Iwa_FlowBlurFx final : public TStandardRasterFx { + FX_PLUGIN_DECLARATION(Iwa_FlowBlurFx) + +protected: + TRasterFxPort m_source; + TRasterFxPort m_flow; + TRasterFxPort m_reference; + + TDoubleParamP m_length; + + TBoolParamP m_linear; + TDoubleParamP m_gamma; + + /*- リニア/ガウシアン/平均化 -*/ + TIntEnumParamP m_filterType; + + // Reference, Blue Channel of Flow Image + TIntEnumParamP m_referenceMode; + + enum ReferenceMode { REFERENCE = 0, FLOW_BLUE_CHANNEL }; + + template + void setSourceTileToBuffer(const RASTER srcRas, double4 *buf, bool isLinear, + double gamma); + template + void setFlowTileToBuffer(const RASTER flowRas, double2 *buf, double *refBuf); + template + void setReferenceTileToBuffer(const RASTER srcRas, double *buf); + + template + void setOutputRaster(double4 *out_buf, const RASTER dstRas, bool isLinear, + double gamma); + +public: + Iwa_FlowBlurFx(); + + void doCompute(TTile &tile, double frame, + const TRenderSettings &settings) override; + + bool doGetBBox(double frame, TRectD &bBox, + const TRenderSettings &info) override; + + bool canHandle(const TRenderSettings &info, double frame) override; +}; +#endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_flowpaintbrushfx.cpp b/toonz/sources/stdfx/iwa_flowpaintbrushfx.cpp new file mode 100644 index 00000000..b922dab7 --- /dev/null +++ b/toonz/sources/stdfx/iwa_flowpaintbrushfx.cpp @@ -0,0 +1,1089 @@ +#include "iwa_flowpaintbrushfx.h" +#include "tparamuiconcept.h" +#include "tlevel.h" +#include "trop.h" + +#include + +#include "tgl.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +inline double lerp(DoublePair range, double t) { + return range.first * (1.0 - t) + range.second * t; +} + +bool strokeStackGraterThan(const BrushStroke &stroke1, + const BrushStroke &stroke2) { + return stroke1.stack > stroke2.stack; +} + +} // namespace + +//------------------------------------------------------------ +// obtain raster data of brush tips uV^b`̃X^[f[^擾 +void Iwa_FlowPaintBrushFx::getBrushRasters(std::vector &brushRasters, + TDimension &b_size, int &lastFrame, + TTile &tile, + const TRenderSettings &ri) { + // uVeNX` + TPointD b_offset; + const TFxTimeRegion &tr = m_brush->getTimeRegion(); + lastFrame = tr.getLastFrame() + 1; + TLevelP partLevel = new TLevel(); + partLevel->setName(m_brush->getAlias(0, ri)); + + // The particles offset must be calculated without considering the + // affine's translational component + TRenderSettings riZero(ri); + riZero.m_affine.a13 = riZero.m_affine.a23 = 0; + + // Calculate the bboxes union + TRectD brushBox; + for (int t = 0; t < lastFrame; ++t) { + TRectD inputBox; + m_brush->getBBox(t, inputBox, riZero); + brushBox += inputBox; + } + if (brushBox.isEmpty()) { + lastFrame = 0; + return; + } + if (brushBox == TConsts::infiniteRectD) brushBox *= ri.m_cameraBox; + + b_size.lx = (int)brushBox.getLx() + 1; + b_size.ly = (int)brushBox.getLy() + 1; + b_offset = TPointD(0.5 * (brushBox.x0 + brushBox.x1), + 0.5 * (brushBox.y0 + brushBox.y1)); + + for (int t = 0; t < lastFrame; ++t) { + TRasterP ras; + std::string alias; + TRasterImageP rimg; + rimg = partLevel->frame(t); + if (rimg) { + ras = rimg->getRaster(); + } else { + alias = "BRUSH: " + m_brush->getAlias(t, ri); + rimg = TImageCache::instance()->get(alias, false); + if (rimg) { + ras = rimg->getRaster(); + + // Check that the raster resolution is sufficient for our purposes + if (ras->getLx() < b_size.lx || ras->getLy() < b_size.ly) + ras = 0; + else + b_size = TDimension(ras->getLx(), ras->getLy()); + } + } + if (!ras) { + TTile auxTile; + TRenderSettings auxRi(ri); + auxRi.m_bpp = 32; + m_brush->allocateAndCompute(auxTile, brushBox.getP00(), b_size, 0, t, + auxRi); + ras = auxTile.getRaster(); + addRenderCache(alias, TRasterImageP(ras)); + } + if (ras) brushRasters.push_back(ras); + } +} + +//------------------------------------------------------------ + +template +void Iwa_FlowPaintBrushFx::setFlowTileToBuffer(const RASTER flowRas, + double2 *buf) { + double2 *buf_p = buf; + for (int j = 0; j < flowRas->getLy(); j++) { + PIXEL *pix = flowRas->pixels(j); + for (int i = 0; i < flowRas->getLx(); i++, pix++, buf_p++) { + double val = double(pix->r) / double(PIXEL::maxChannelValue); + (*buf_p).x = val * 2.0 - 1.0; + val = double(pix->g) / double(PIXEL::maxChannelValue); + (*buf_p).y = val * 2.0 - 1.0; + } + } +} + +//------------------------------------------------------------ + +template +void Iwa_FlowPaintBrushFx::setAreaTileToBuffer(const RASTER areaRas, + double *buf) { + double *buf_p = buf; + for (int j = 0; j < areaRas->getLy(); j++) { + PIXEL *pix = areaRas->pixels(j); + for (int i = 0; i < areaRas->getLx(); i++, pix++, buf_p++) { + (*buf_p) = double(pix->m) / double(PIXEL::maxChannelValue); + } + } +} + +//------------------------------------------------------------ + +template +void Iwa_FlowPaintBrushFx::setColorTileToBuffer(const RASTER colorRas, + colorRGBA *buf) { + colorRGBA *buf_p = buf; + for (int j = 0; j < colorRas->getLy(); j++) { + PIXEL *pix = colorRas->pixels(j); + for (int i = 0; i < colorRas->getLx(); i++, pix++, buf_p++) { + (*buf_p).r = double(pix->r) / double(PIXEL::maxChannelValue); + (*buf_p).g = double(pix->g) / double(PIXEL::maxChannelValue); + (*buf_p).b = double(pix->b) / double(PIXEL::maxChannelValue); + (*buf_p).a = double(pix->m) / double(PIXEL::maxChannelValue); + } + } +} + +//------------------------------------------------------------ +template +void Iwa_FlowPaintBrushFx::setOutRaster(const RASTER outRas, double *buf) { + double *buf_p = buf; + for (int j = 0; j < outRas->getLy(); j++) { + PIXEL *pix = outRas->pixels(j); + for (int i = 0; i < outRas->getLx(); i++, pix++, buf_p++) { + typename PIXEL::Channel val = + (typename PIXEL::Channel)(*buf_p * double(PIXEL::maxChannelValue)); + pix->r = val; + pix->g = val; + pix->b = val; + pix->m = PIXEL::maxChannelValue; + } + } +} + +//------------------------------------------------------------ +Iwa_FlowPaintBrushFx::Iwa_FlowPaintBrushFx() + : m_h_density(10.0) + , m_v_density(10.0) + , m_pos_randomness(1.0) + , m_pos_wobble(0.0) + , m_tip_width(DoublePair(0.02, 0.05)) + , m_tip_length(DoublePair(0.08, 0.2)) + , m_tip_alpha(DoublePair(0.8, 1.0)) + , m_tip_joints(3) + , m_bidirectional(true) + , m_width_randomness(0.0) + , m_length_randomness(0.0) + , m_angle_randomness(0.0) + , m_sustain_width_to_skew(0.0) + , m_anti_jaggy(false) + , m_origin_pos(TPointD(0.0, 0.0)) + , m_horizontal_pos(TPointD(100.0, 0.0)) + , m_vertical_pos(TPointD(0.0, 100.0)) + , m_curve_point(TPointD(0.0, 0.0)) + , m_fill_gap_size(0.0) + , m_reference_frame(0.0) + , m_reference_prevalence(0.0) + , m_random_seed(1) + , m_sortBy(new TIntEnumParam(NoSort, "None")) { + addInputPort("Brush", m_brush); + addInputPort("Flow", m_flow); + addInputPort("Area", m_area); + addInputPort("Color", m_color); + + bindParam(this, "h_density", m_h_density); + bindParam(this, "v_density", m_v_density); + bindParam(this, "pos_randomness", m_pos_randomness); + bindParam(this, "pos_wobble", m_pos_wobble); + bindParam(this, "tip_width", m_tip_width); + bindParam(this, "tip_length", m_tip_length); + bindParam(this, "tip_alpha", m_tip_alpha); + bindParam(this, "tip_joints", m_tip_joints); + bindParam(this, "bidirectional", m_bidirectional); + + bindParam(this, "width_randomness", m_width_randomness); + bindParam(this, "length_randomness", m_length_randomness); + bindParam(this, "angle_randomness", m_angle_randomness); + bindParam(this, "sustain_width_to_skew", m_sustain_width_to_skew); + bindParam(this, "anti_jaggy", m_anti_jaggy); + + bindParam(this, "origin_pos", m_origin_pos); + bindParam(this, "horizontal_pos", m_horizontal_pos); + bindParam(this, "vertical_pos", m_vertical_pos); + bindParam(this, "curve_point", m_curve_point); + bindParam(this, "fill_gap_size", m_fill_gap_size); + + bindParam(this, "reference_frame", m_reference_frame); + bindParam(this, "reference_prevalence", m_reference_prevalence); + + bindParam(this, "random_seed", m_random_seed); + bindParam(this, "sort_by", m_sortBy); + + m_h_density->setValueRange(1.0, 300.0); + m_v_density->setValueRange(1.0, 300.0); + m_pos_randomness->setValueRange(0.0, 2.0); + m_pos_wobble->setValueRange(0.0, 1.0); + m_tip_width->getMin()->setValueRange(0.0, 1.0); + m_tip_width->getMax()->setValueRange(0.0, 1.0); + m_tip_length->getMin()->setValueRange(0.0, 1.0); + m_tip_length->getMax()->setValueRange(0.0, 1.0); + m_tip_alpha->getMin()->setValueRange(0.0, 1.0); + m_tip_alpha->getMax()->setValueRange(0.0, 1.0); + m_tip_joints->setValueRange(0, 20); + + m_width_randomness->setValueRange(0.0, 0.9); + m_length_randomness->setValueRange(0.0, 0.9); + m_angle_randomness->setValueRange(0.0, 180.0); // degree + + m_sustain_width_to_skew->setValueRange(0.0, 1.0); + + m_origin_pos->getX()->setMeasureName("fxLength"); + m_origin_pos->getY()->setMeasureName("fxLength"); + m_horizontal_pos->getX()->setMeasureName("fxLength"); + m_horizontal_pos->getY()->setMeasureName("fxLength"); + m_vertical_pos->getX()->setMeasureName("fxLength"); + m_vertical_pos->getY()->setMeasureName("fxLength"); + m_curve_point->getX()->setValueRange(-0.5, 0.5); + m_curve_point->getY()->setValueRange(-0.5, 0.5); + + m_fill_gap_size->setMeasureName("fxLength"); + m_fill_gap_size->setValueRange(0.0, 50.0); + + m_reference_frame->setValueRange(0, (std::numeric_limits::max)()); + m_reference_prevalence->setValueRange(0.0, 1.0); + m_random_seed->setValueRange(0, (std::numeric_limits::max)()); + m_sortBy->addItem(Smaller, "Size - Smaller on Top"); + m_sortBy->addItem(Larger, "Size - Larger on Top"); + m_sortBy->addItem(Darker, "Brightness - Darker on Top"); + m_sortBy->addItem(Brighter, "Brightness - Brighter on Top"); + m_sortBy->addItem(Random, "Random"); + + // Version 1 (development version) : strokes are placed upward direction if + // there is no Flow input. + // Version 2: strokes are directed parallel to the + // vertical vector of the parallelogram if there is no Flow input. + setFxVersion(2); +} + +//------------------------------------------------------------ +bool Iwa_FlowPaintBrushFx::doGetBBox(double frame, TRectD &bBox, + const TRenderSettings &info) { + if (!m_brush.isConnected()) { + bBox = TRectD(); + return false; + } + TPointD origin_pos = m_origin_pos->getValue(frame); + TPointD horiz_pos = m_horizontal_pos->getValue(frame); + TPointD vert_pos = m_vertical_pos->getValue(frame); + TPointD opposite_pos = horiz_pos + vert_pos - origin_pos; + bBox = boundingBox(origin_pos, horiz_pos, vert_pos, opposite_pos); + return true; +} + +//-------------------------------------------------------------- +double Iwa_FlowPaintBrushFx::getSizePixelAmount(const double val, + const TAffine affine) { + /*--- Convert to vector --- */ + TPointD vect; + vect.x = val; + vect.y = 0.0; + /*--- Apply geometrical transformation ---*/ + // For the following lines I referred to lines 586-592 of + // sources/stdfx/motionblurfx.cpp + TAffine aff(affine); + aff.a13 = aff.a23 = 0; /* ignore translation */ + vect = aff * vect; + /*--- return the length of the vector ---*/ + return sqrt(vect.x * vect.x + vect.y * vect.y); +} +//------------------------------------------------------------ + +FlowPaintBrushFxParam Iwa_FlowPaintBrushFx::getParam( + TTile &tile, double frame, const TRenderSettings &ri) { + FlowPaintBrushFxParam p; + + TAffine aff = ri.m_affine; + p.origin_pos = aff * m_origin_pos->getValue(frame); + p.horiz_pos = aff * m_horizontal_pos->getValue(frame); + p.vert_pos = aff * m_vertical_pos->getValue(frame); + TPointD opposite_pos = p.horiz_pos + p.vert_pos - p.origin_pos; + p.bbox = boundingBox(p.origin_pos, p.horiz_pos, p.vert_pos, opposite_pos); + p.dim.lx = (int)p.bbox.getLx() + 1; + p.dim.ly = (int)p.bbox.getLy() + 1; + p.origin_pos -= p.bbox.getP00(); + p.horiz_pos -= p.bbox.getP00(); + p.vert_pos -= p.bbox.getP00(); + + p.fill_gap_size = + (int)getSizePixelAmount(m_fill_gap_size->getValue(frame), aff); + + p.h_density = m_h_density->getValue(frame); + p.v_density = m_v_density->getValue(frame); + p.pos_randomness = m_pos_randomness->getValue(frame); + p.pos_wobble = m_pos_wobble->getValue(frame); + + p.random_seed = m_random_seed->getValue(); + p.tipLength = m_tip_length->getValue(frame); + p.tipWidth = m_tip_width->getValue(frame); + p.tipAlpha = m_tip_alpha->getValue(frame); + p.width_random = m_width_randomness->getValue(frame); + p.length_random = m_length_randomness->getValue(frame); + p.angle_random = m_angle_randomness->getValue(frame); + + p.reso = m_tip_joints->getValue(); + p.anti_jaggy = m_anti_jaggy->getValue(); + + p.hVec = p.horiz_pos - p.origin_pos; + p.vVec = p.vert_pos - p.origin_pos; + p.vVec_unit = double2{normalize(p.vVec).x, normalize(p.vVec).y}; + + p.brushAff = TAffine(p.hVec.x, p.vVec.x, p.origin_pos.x, p.hVec.y, p.vVec.y, + p.origin_pos.y); + return p; +} + +//------------------------------------------------------------ + +void Iwa_FlowPaintBrushFx::fillGapByDilateAndErode(double *buf, + const TDimension &dim, + const int fill_gap_size) { + TRasterGR8P tmp_buf_ras = TRasterGR8P(dim.lx * dim.ly * sizeof(double), 1); + tmp_buf_ras->lock(); + double *tmp_buf = (double *)tmp_buf_ras->getRawData(); + + // dilate, then erode + for (int mode = 0; mode < 2; mode++) { + for (int i = 0; i < fill_gap_size; i++) { + double *fromBuf = (i % 2 == 0) ? buf : tmp_buf; + double *toBuf = (i % 2 == 0) ? tmp_buf : buf; + + double *to_p = toBuf; + double *frm_p = fromBuf; + for (int y = 0; y < dim.ly; y++) { + double *n_up = + (y == 0) ? &fromBuf[y * dim.lx] : &fromBuf[(y - 1) * dim.lx]; + double *n_dn = (y == dim.ly - 1) ? &fromBuf[y * dim.lx] + : &fromBuf[(y + 1) * dim.lx]; + for (int x = 0; x < dim.lx; x++, n_up++, n_dn++, to_p++, frm_p++) { + if (mode == 0) { + *to_p = std::max(*frm_p, *n_up); + *to_p = std::max(*to_p, *n_dn); + if (x != 0) *to_p = std::max(*to_p, *(frm_p - 1)); + if (x != dim.lx - 1) *to_p = std::max(*to_p, *(frm_p + 1)); + } else { + *to_p = std::min(*frm_p, *n_up); + *to_p = std::min(*to_p, *n_dn); + if (x != 0) *to_p = std::min(*to_p, *(frm_p - 1)); + if (x != dim.lx - 1) *to_p = std::min(*to_p, *(frm_p + 1)); + } + } + } + } + } + + // ̃tB^ȌꍇAvZʂtmp_bufɓĂ̂bufɃRs[ + if (fill_gap_size % 2 == 1) { + memcpy(buf, tmp_buf, dim.lx * dim.ly * sizeof(double)); + } + + tmp_buf_ras->unlock(); +} + +//------------------------------------------------------------ +void Iwa_FlowPaintBrushFx::computeBrushVertices( + QVector &brushVertices, QList &brushStrokes, + FlowPaintBrushFxParam &p, TTile &tile, double frame, + const TRenderSettings &ri) { + // Œ肷^b`Ft[AreaAColorpĐAJgt[Flowpė + // ^b`FAreaAColorAFlowׂăJgt[pB + int referenceFrame = (int)std::round(m_reference_frame->getValue(frame)) - 1; + double reference_prevalence = m_reference_prevalence->getValue(frame); + bool bidirectional = m_bidirectional->getValue(); + + FlowPaintBrushFxParam pivP = + getParam(tile, referenceFrame, ri); // TODO: AxԖh + pivP.lastFrame = p.lastFrame; + FlowPaintBrushFxParam *param[2] = {&p, &pivP}; + + double ref_frame[2] = {frame, (double)referenceFrame}; + double2 *flow_buf[2] = {nullptr}; + double *area_buf[2] = {nullptr}; + colorRGBA *color_buf[2] = {nullptr}; + TRasterGR8P flow_buf_ras[2], area_buf_ras[2], color_buf_ras[2]; + + TRaster32P ras32 = tile.getRaster(); + TRaster64P ras64 = tile.getRaster(); + + // ꂼ̎QƉ摜擾 + for (int f = 0; f < 2; f++) { + // Referencet[̓t[-1prevalenceȌꍇvZȂ + int tmp_f = ref_frame[f]; + if (f == 1 && (tmp_f < 0 || reference_prevalence == 0.0)) continue; + + // ܂AFlowvZ + if (m_flow.isConnected()) { + // obtain Flow memory buffer (XY) + TTile flowTile; + m_flow->allocateAndCompute(flowTile, param[f]->bbox.getP00(), + param[f]->dim, tile.getRaster(), tmp_f, ri); + // allocate buffer + flow_buf_ras[f] = + TRasterGR8P(param[f]->dim.lx * param[f]->dim.ly * sizeof(double2), 1); + flow_buf_ras[f]->lock(); + flow_buf[f] = (double2 *)flow_buf_ras[f]->getRawData(); + if (ras32) + setFlowTileToBuffer(flowTile.getRaster(), + flow_buf[f]); + else if (ras64) + setFlowTileToBuffer(flowTile.getRaster(), + flow_buf[f]); + } + // Jgt[prevalenceP̏ꍇFloŵ݌vZ + if (f == 0 && reference_prevalence == 1.0) continue; + // AreaColorvZ + if (m_area.isConnected()) { + TTile areaTile; + m_area->allocateAndCompute(areaTile, param[f]->bbox.getP00(), + param[f]->dim, tile.getRaster(), tmp_f, ri); + // allocate buffer + area_buf_ras[f] = + TRasterGR8P(param[f]->dim.lx * param[f]->dim.ly * sizeof(double), 1); + area_buf_ras[f]->lock(); + area_buf[f] = (double *)area_buf_ras[f]->getRawData(); + if (ras32) + setAreaTileToBuffer(areaTile.getRaster(), + area_buf[f]); + else if (ras64) + setAreaTileToBuffer(areaTile.getRaster(), + area_buf[f]); + + // Ԃ߂ + if (param[f]->fill_gap_size > 0) + fillGapByDilateAndErode(area_buf[f], param[f]->dim, + param[f]->fill_gap_size); + } + if (m_color.isConnected()) { + TTile colorTile; + m_color->allocateAndCompute(colorTile, param[f]->bbox.getP00(), + param[f]->dim, tile.getRaster(), tmp_f, ri); + // allocate buffer + color_buf_ras[f] = TRasterGR8P( + param[f]->dim.lx * param[f]->dim.ly * sizeof(colorRGBA), 1); + color_buf_ras[f]->lock(); + color_buf[f] = (colorRGBA *)color_buf_ras[f]->getRawData(); + if (ras32) + setColorTileToBuffer(colorTile.getRaster(), + color_buf[f]); + else if (ras64) + setColorTileToBuffer(colorTile.getRaster(), + color_buf[f]); + } + } + + enum FRAMETYPE { CURRENT = 0, REFERENCE }; + + auto getFlow = [&](TPointD pos, FRAMETYPE fType) { + if (!flow_buf[fType]) { + if (getFxVersion() == 1) + return double2{0, 1}; + else + return param[CURRENT]->vVec_unit; + } + int u = (int)std::round(pos.x); + int v = (int)std::round(pos.y); + if (u < 0 || u >= param[fType]->dim.lx || v < 0 || + v >= param[fType]->dim.ly) { + if (getFxVersion() == 1) + return double2{0, 1}; + else + return param[CURRENT]->vVec_unit; + } + return flow_buf[fType][v * param[fType]->dim.lx + u]; + }; + auto getArea = [&](TPointD pos, FRAMETYPE fType) { + if (!area_buf[fType]) return 1.0; + int u = (int)std::round(pos.x); + int v = (int)std::round(pos.y); + if (u < 0 || u >= param[fType]->dim.lx || v < 0 || + v >= param[fType]->dim.ly) + return 0.0; + return area_buf[fType][v * param[fType]->dim.lx + u]; + }; + auto getColor = [&](TPointD pos, FRAMETYPE fType) { + if (!color_buf[fType]) return colorRGBA{1.0, 1.0, 1.0, 1.0}; + int u = (int)std::round(pos.x); + int v = (int)std::round(pos.y); + if (u < 0 || u >= param[fType]->dim.lx || v < 0 || + v >= param[fType]->dim.ly) + return colorRGBA{1.0, 1.0, 1.0, 0.0}; + return color_buf[fType][v * param[fType]->dim.lx + u]; + }; + + // _̐ݒ + std::mt19937_64 mt, mt_cur; // , mt_ref; + mt.seed(p.random_seed); + mt_cur.seed( + (unsigned int)(p.random_seed + + (int)std::round(frame))); // t[Ƀo‚铮 + std::uniform_real_distribution<> random01(0.0, 1.0); + std::uniform_int_distribution<> random_textureId(0, p.lastFrame - 1); + std::uniform_real_distribution<> random_plusminus1(-1.0, 1.0); + + // ^b`̒_Wo^Ă + double v_incr = 1.0 / p.v_density; + double h_incr = 1.0 / p.h_density; + double v_base_pos = v_incr * 0.5; + double segmentAmount = (double)p.reso * 2 - 1; + + int id = 0; // Map̃L[ɂȂʂԍ + for (int v = 0; v < (int)p.v_density; v++, v_base_pos += v_incr) { + double h_base_pos = h_incr * 0.5; + for (int h = 0; h < (int)p.h_density; h++, h_base_pos += h_incr, id++) { + BrushStroke brushStroke; + + // referenceJg + FRAMETYPE frameType = + (referenceFrame >= 0 && random01(mt) <= reference_prevalence) + ? REFERENCE + : CURRENT; + + double pos_x = h_base_pos; + double pos_y = v_base_pos; + if (frameType == CURRENT) { + pos_x += (random01(mt) - 0.5) * p.pos_randomness * h_incr; + pos_y += (random01(mt) - 0.5) * p.pos_randomness * v_incr; + } else { // REFERENCE case + pos_x += (random01(mt) - 0.5) * pivP.pos_randomness * h_incr; + pos_y += (random01(mt) - 0.5) * pivP.pos_randomness * v_incr; + } + if (pos_x < 0.0 || pos_x > 1.0 || pos_y < 0.0 || pos_y > 1.0) { + mt.discard(7); // Ǎ̃_񐔕 + mt_cur.discard(2); + continue; + } + + // ArealEW + TPointD areaSamplePos = + param[frameType]->brushAff * TPointD(pos_x, pos_y); + // ͈͂̎QƉ摜̋Px擾 + double areaVal = getArea(areaSamplePos, frameType); + // 邩Ȃ + if (random01(mt) > areaVal) { + mt.discard(6); // ɂǍ̃_񐔕 + mt_cur.discard(2); + continue; + } + + double2 wobble; + wobble.x = (random01(mt_cur) - 0.5) * p.pos_wobble * h_incr; + wobble.y = (random01(mt_cur) - 0.5) * p.pos_wobble * v_incr; + // ݂̃ZOg̃_OW + brushStroke.originPos = TPointD(pos_x + wobble.x, pos_y + wobble.y); + + brushStroke.color = getColor(areaSamplePos, frameType); + double alpha = lerp(param[frameType]->tipAlpha, areaVal); + // premultiply Ȃ̂RGBlɂ + brushStroke.color.r *= alpha; + brushStroke.color.g *= alpha; + brushStroke.color.b *= alpha; + brushStroke.color.a *= alpha; + + brushStroke.length = + lerp(param[frameType]->tipLength, areaVal) * + (1.0 + random_plusminus1(mt) * param[frameType]->length_random); + brushStroke.widthHalf = + lerp(param[frameType]->tipWidth, areaVal) * + (1.0 + random_plusminus1(mt) * param[frameType]->width_random) * 0.5; + + if (p.anti_jaggy) brushStroke.widthHalf *= 3.0; + + brushStroke.angle = + random_plusminus1(mt) * param[frameType]->angle_random; + TAffine flow_rot = TRotation(brushStroke.angle); + + brushStroke.textureId = random_textureId(mt); + brushStroke.randomVal = random01(mt); + + bool inv = random01(mt) < 0.5; + brushStroke.invert = (bidirectional) ? inv : false; + + // ܂AcɂȂ镔̍WvZ + QVector centerPosHalf[2]; + + double segLen = + brushStroke.length / + segmentAmount; // ZOgЂƂ•̒BiuVWnj + + bool tooCurve[2] = {false, false}; + TPointD originFlow; + // O + for (int dir = 0; dir < 2; dir++) { + // curPosʒu + TPointD curPos(brushStroke.originPos); + TPointD preFlowUV; + TPointD startFlow; + for (int s = 0; s < param[frameType]->reso; s++) { + // @݂̃ZOg̃_OW + TPointD samplePos = param[CURRENT]->brushAff * curPos; + + // _OW痬xNg擾 + double2 tmp_flow = getFlow(samplePos, CURRENT); + + // @xNguVWnɕϊ + TPointD flow_uv = + param[CURRENT]->brushAff.inv() * TPointD(tmp_flow.x, tmp_flow.y) - + param[CURRENT]->brushAff.inv() * TPointD(0., 0.); + if (s == 0 && dir == 0) originFlow = flow_uv; + + // xNg𐳋K + flow_uv = normalize(flow_uv); + + // ߂̂Ƃ̓xNg𔽓] + if (dir == 1) flow_uv = -flow_uv; + + // ̕] + flow_uv = flow_rot * flow_uv; + + if (s != 0) { + double dot = flow_uv.x * preFlowUV.x + flow_uv.y * preFlowUV.y; + if (dot < 0.0) flow_uv = -flow_uv; + + double dotVsStart = + startFlow.x * flow_uv.x + startFlow.y * flow_uv.y; + if (dotVsStart < 0.3) { + tooCurve[dir] = true; + break; + } + } + preFlowUV = flow_uv; + + if (s == 0) startFlow = flow_uv; + + // xNgZOĝ΂A̓_ƂBŏ̃ZOg͔̒B + TPointD nextPos = curPos + flow_uv * segLen * ((s == 0) ? 0.5 : 1.0); + + // _i[ + centerPosHalf[dir].push_back(nextPos); + + // curPosi߂ + curPos = nextPos; + } + } + + if (tooCurve[0] && tooCurve[1]) continue; + // 炩ȏꍇ + if (!tooCurve[0] && !tooCurve[1]) { + brushStroke.centerPos = centerPosHalf[1]; + for (auto pos : centerPosHalf[0]) brushStroke.centerPos.push_front(pos); + } + // Е}J[ȕꍇA炩ȕ̃J[u𔽑Αɔ]ĉ + else { + int OkId = (tooCurve[0]) ? 1 : 0; + TPointD origin(pos_x, pos_y); + TPointD okInitialVec = centerPosHalf[OkId].at(0) - origin; + double thetaDeg = std::atan2(okInitialVec.y, okInitialVec.x) / M_PI_180; + TAffine reflectAff = TTranslation(origin) * TRotation(thetaDeg) * + TScale(-1.0, 1.0) * TRotation(-thetaDeg) * + TTranslation(-origin); + brushStroke.centerPos = centerPosHalf[OkId]; + for (auto pos : centerPosHalf[OkId]) { + TPointD refPos = reflectAff * pos; + brushStroke.centerPos.push_front(reflectAff * pos); + } + } + + // ŁAt[̗̌ɏ]ăeNX`̌낦Ă݂ + if (referenceFrame >= 0) { + TPointD referenceSamplePos = + param[REFERENCE]->brushAff * TPointD(pos_x, pos_y); + double2 reference_flow = getFlow(referenceSamplePos, REFERENCE); + // @xNguVWnɕϊ + TPointD reference_flow_uv = + param[REFERENCE]->brushAff.inv() * + TPointD(reference_flow.x, reference_flow.y) - + param[REFERENCE]->brushAff.inv() * TPointD(0., 0.); + // ςA]邩f + double dot = originFlow.x * reference_flow_uv.x + + originFlow.y * reference_flow_uv.y; + if (dot < 0.0) { + brushStroke.invert = !brushStroke.invert; + } + } + // o^ + brushStrokes.append(brushStroke); + } + } + + // X^[ + for (int f = 0; f < 2; f++) { + if (flow_buf[f]) flow_buf_ras[f]->unlock(); + if (area_buf[f]) area_buf_ras[f]->unlock(); + if (color_buf[f]) color_buf_ras[f]->unlock(); + } + + // \[g + StackMode stackMode = (StackMode)m_sortBy->getValue(); + if (stackMode != NoSort) { + // 傫zőOAȂ킿ijɕ`悤ɂ + for (auto &stroke : brushStrokes) { + switch (stackMode) { + case Smaller: + stroke.stack = stroke.length * stroke.widthHalf; + break; + case Larger: + stroke.stack = -stroke.length * stroke.widthHalf; + break; + // Value = 0.3R 0.59G 0.11B + case Darker: + stroke.stack = 0.3 * stroke.color.r + 0.59 * stroke.color.g + + 0.11 * stroke.color.b; + break; + case Brighter: + stroke.stack = -(0.3 * stroke.color.r + 0.59 * stroke.color.g + + 0.11 * stroke.color.b); + break; + case Random: + stroke.stack = stroke.randomVal; + break; + } + } + std::sort(brushStrokes.begin(), brushStrokes.end(), strokeStackGraterThan); + } + + // ^b`fɑ΂Ăđ銄 + double sustain_width_to_skew = m_sustain_width_to_skew->getValue(frame); + TPointD curve_point = m_curve_point->getValue(frame); + // _Wo^Ă + for (auto stroke : brushStrokes) { + int jointCount = stroke.centerPos.size(); + + // _ʒuJ[uĂ䂪߂eXg + if (curve_point != TPointD()) { + for (int s = 0; s < stroke.centerPos.size(); s++) { + TPointD p = stroke.centerPos[s]; + TPointD A = (1.0 - p.x) * (1.0 - p.x) * TPointD(0.0, 0.5) + + 2.0 * (1.0 - p.x) * p.x * + TPointD(0.5 + curve_point.x, 0.5 + curve_point.y) + + p.x * p.x * TPointD(1.0, 0.5); + stroke.centerPos[s] = (1.0 - p.y) * (1.0 - p.y) * TPointD(p.x, 0.0) + + 2.0 * (1.0 - p.y) * p.y * TPointD(A.x, A.y) + + p.y * p.y * TPointD(p.x, 1.0); + } + } + + for (int s = 0; s < jointCount; s++) { + // O̓_ + TPointD back = (s == 0) ? stroke.centerPos[0] : stroke.centerPos[s - 1]; + TPointD fore = (s == jointCount - 1) ? stroke.centerPos[jointCount - 1] + : stroke.centerPos[s + 1]; + TPointD vec = normalize(fore - back); + TPointD n(-vec.y * stroke.widthHalf, vec.x * stroke.widthHalf); + + // ŁAeNX`Ɍ鏈 + if (sustain_width_to_skew > 0.0) { + // ʍWɕϊ + TPointD nr = param[CURRENT]->brushAff * n - + param[CURRENT]->brushAff * TPointD(0, 0); + TPointD vr = param[CURRENT]->brushAff * vec - + param[CURRENT]->brushAff * TPointD(0, 0); + // nr vrɂÂ悤ɁAnr] + double nr_len = norm(nr); + + nr = (1.0 / nr_len) * nr; + vr = normalize(vr); + + // + double theta = std::acos(nr * vr); + double new_theta = sustain_width_to_skew * M_PI * 0.5 + + (1.0 - sustain_width_to_skew) * theta; + // Oς̐Anrvr̂ǂɂ邩f + if (cross(vr, nr) < 0) new_theta = -new_theta; + TPointD new_nr(vr.x * cos(new_theta) - vr.y * sin(new_theta), + vr.x * sin(new_theta) + vr.y * cos(new_theta)); + new_nr = nr_len * new_nr; + + // uVWɂǂ + n = param[CURRENT]->brushAff.inv() * + (new_nr + param[CURRENT]->brushAff * TPointD(0, 0)); + } + + // ͂ + if (p.anti_jaggy && s == 0) { + TPointD edgePos = stroke.centerPos[0] * 2.0 - stroke.originPos; + brushVertices.append( + BrushVertex(edgePos + n, 0.0, (stroke.invert) ? 1.0 : 0.0)); + brushVertices.append( + BrushVertex(edgePos - n, 1.0, (stroke.invert) ? 1.0 : 0.0)); + } + + double texCoord_v = (stroke.invert) ? 1.0 - (double(s) / segmentAmount) + : double(s) / segmentAmount; + + if (p.anti_jaggy) texCoord_v = 0.25 + 0.5 * texCoord_v; + + brushVertices.append( + BrushVertex(stroke.centerPos[s] + n, 0.0, texCoord_v)); + brushVertices.append( + BrushVertex(stroke.centerPos[s] - n, 1.0, texCoord_v)); + + // ΂̂͂ + if (p.anti_jaggy && s == jointCount - 1) { + TPointD edgePos = + stroke.centerPos[jointCount - 1] * 2.0 - stroke.originPos; + brushVertices.append( + BrushVertex(edgePos + n, 0.0, (stroke.invert) ? 0.0 : 1.0)); + brushVertices.append( + BrushVertex(edgePos - n, 1.0, (stroke.invert) ? 0.0 : 1.0)); + } + } + } +} +//------------------------------------------------------------ +void Iwa_FlowPaintBrushFx::doCompute(TTile &tile, double frame, + const TRenderSettings &ri) { + if (!m_brush.isConnected()) { + tile.getRaster()->clear(); + return; + } + + TDimension b_size(0, 0); + int lastFrame = 0; + std::vector brushRasters; + // uV^b`̃X^[f[^擾 + getBrushRasters(brushRasters, b_size, lastFrame, tile, ri); + + if (lastFrame == 0) { + tile.getRaster()->clear(); + return; + } + + // p[^擾 + FlowPaintBrushFxParam p = getParam(tile, frame, ri); + p.lastFrame = lastFrame; + + int vCount = p.reso * 4; + // ㉺Ƀ}[Wp̒_lj + if (p.anti_jaggy) vCount += 4; + + QVector brushVertices; + QList brushStrokes; + computeBrushVertices(brushVertices, brushStrokes, p, tile, frame, ri); + + GLfloat m[16] = {(float)p.hVec.x, + (float)p.hVec.y, + 0.f, + 0.f, + (float)p.vVec.x, + (float)p.vVec.y, + 0.f, + 0.f, + 0.f, + 0.f, + 1.f, + 0.f, + (float)p.origin_pos.x, + (float)p.origin_pos.y, + 0.f, + 1.f}; + + QOpenGLContext *context = new QOpenGLContext(); + if (QOpenGLContext::currentContext()) + context->setShareContext(QOpenGLContext::currentContext()); + context->setFormat(QSurfaceFormat::defaultFormat()); + context->create(); + context->makeCurrent(ri.m_offScreenSurface.get()); + + // eNX`̊m + std::vector brushTextures; + for (auto texRas : brushRasters) { + QImage texImg(texRas->getRawData(), b_size.lx, b_size.ly, + QImage::Format_RGBA8888); + // 3{ɂiEx1̃}[Wj + if (p.anti_jaggy) { + QImage resizedImage(texImg.width() * 3, texImg.height() * 2, + QImage::Format_RGBA8888); + QPainter painter(&resizedImage); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(resizedImage.rect(), Qt::transparent); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawImage(QPoint(texImg.width(), texImg.height() / 2), texImg); + painter.end(); + texImg = resizedImage; + } + + QOpenGLTexture *texture = new QOpenGLTexture(texImg.rgbSwapped()); + texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); + texture->setMagnificationFilter(QOpenGLTexture::Linear); + brushTextures.push_back(texture); + } + + TDimensionI outDim = tile.getRaster()->getSize(); + + // ` + { + std::unique_ptr fb( + new QOpenGLFramebufferObject(p.dim.lx, p.dim.ly)); + + fb->bind(); + + glViewport(0, 0, p.dim.lx, p.dim.ly); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, p.dim.lx, 0, p.dim.ly); + + glMatrixMode(GL_MODELVIEW); + + glLoadIdentity(); + glLoadMatrixf(m); + + glDisable(GL_POLYGON_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + { + glVertexPointer(2, GL_DOUBLE, sizeof(BrushVertex), brushVertices.data()); + glTexCoordPointer(2, GL_DOUBLE, sizeof(BrushVertex), + (double *)brushVertices.data() + 2); + } + + int first = 0; + + for (auto stroke : brushStrokes) { + brushTextures[stroke.textureId]->bind(); + + glColor4d(stroke.color.r, stroke.color.g, stroke.color.b, stroke.color.a); + glDrawArrays(GL_TRIANGLE_STRIP, first, vCount); + first += vCount; + } + + glFlush(); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + QImage img = + fb->toImage().scaled(QSize(p.dim.lx, p.dim.ly), Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + // yW͏㉺]Ă邱ƂɒӁII + QRect subRect(tile.m_pos.x - (int)std::round(p.bbox.getP00().x), + (int)std::round(p.bbox.getP01().y) - tile.m_pos.y - outDim.ly, + outDim.lx, outDim.ly); + QImage subImg = img.copy(subRect); + + TRaster32P ras32 = tile.getRaster(); + TRaster64P ras64 = tile.getRaster(); + TRaster32P resultRas; + if (ras32) + resultRas = ras32; + else if (ras64) { + resultRas = TRaster32P(outDim.lx, outDim.ly); + resultRas->lock(); + } + + for (int y = 0; y < outDim.ly; y++) { + QRgb *rgb_p = + reinterpret_cast(subImg.scanLine(outDim.ly - 1 - y)); + TPixel32 *dst_p = resultRas->pixels(y); + for (int x = 0; x < outDim.lx; x++, rgb_p++, dst_p++) { + (*dst_p).r = (unsigned char)qRed(*rgb_p); + (*dst_p).g = (unsigned char)qGreen(*rgb_p); + (*dst_p).b = (unsigned char)qBlue(*rgb_p); + (*dst_p).m = (unsigned char)qAlpha(*rgb_p); + } + } + + if (ras64) { + TRop::convert(ras64, resultRas); + resultRas->unlock(); + } + + fb->release(); + } + context->deleteLater(); + + for (auto tex : brushTextures) { + delete tex; + } +} + +//------------------------------------------------------------ + +void Iwa_FlowPaintBrushFx::getParamUIs(TParamUIConcept *&concepts, + int &length) { + concepts = new TParamUIConcept[length = 4]; + + concepts[0].m_type = TParamUIConcept::POINT; + concepts[0].m_label = "Origin"; + concepts[0].m_params.push_back(m_origin_pos); + + concepts[1].m_type = TParamUIConcept::POINT; + concepts[1].m_label = "Horizontal Range"; + concepts[1].m_params.push_back(m_horizontal_pos); + + concepts[2].m_type = TParamUIConcept::POINT; + concepts[2].m_label = "Vertical Range"; + concepts[2].m_params.push_back(m_vertical_pos); + + concepts[3].m_type = TParamUIConcept::PARALLELOGRAM; + concepts[3].m_params.push_back(m_origin_pos); + concepts[3].m_params.push_back(m_horizontal_pos); + concepts[3].m_params.push_back(m_vertical_pos); + concepts[3].m_params.push_back(m_curve_point); +} + +//------------------------------------------------------------ + +std::string Iwa_FlowPaintBrushFx::getAlias(double frame, + const TRenderSettings &info) const { + double refFrame = m_reference_frame->getValue(frame); + double prevalence = m_reference_prevalence->getValue(frame); + if (refFrame < 0 || prevalence == 0.0) { + // check if the brush is wobbled + double wobble = m_pos_wobble->getValue(frame); + std::string alias = TStandardRasterFx::getAlias(frame, info); + if (areAlmostEqual(wobble, 0.0)) + return alias; + else { + alias.insert(getFxType().length() + 1, std::to_string(frame) + ","); + return alias; + } + } + + std::string alias = getFxType(); + alias += "["; + + // alias degli effetti connessi alle porte di input separati da virgole + // una porta non connessa da luogo a un alias vuoto (stringa vuota) + for (int i = 0; i < getInputPortCount(); ++i) { + TFxPort *port = getInputPort(i); + if (port->isConnected()) { + TRasterFxP ifx = port->getFx(); + assert(ifx); + alias += ifx->getAlias(frame, info); + // add alias of flow, area and color map in the reference frame + if (getInputPortName(i) != "Brush") { + alias += ","; + alias += ifx->getAlias(refFrame, info); + } + } + alias += ","; + } + + std::string paramalias(""); + for (int i = 0; i < getParams()->getParamCount(); ++i) { + TParam *param = getParams()->getParam(i); + paramalias += param->getName() + "=" + param->getValueAlias(frame, 3); + } + + return alias + std::to_string(frame) + "," + std::to_string(getIdentifier()) + + paramalias + "]"; +} + +FX_PLUGIN_IDENTIFIER(Iwa_FlowPaintBrushFx, "iwa_FlowPaintBrushFx") diff --git a/toonz/sources/stdfx/iwa_flowpaintbrushfx.h b/toonz/sources/stdfx/iwa_flowpaintbrushfx.h new file mode 100644 index 00000000..57e1b847 --- /dev/null +++ b/toonz/sources/stdfx/iwa_flowpaintbrushfx.h @@ -0,0 +1,179 @@ +#pragma once + +#ifndef IWA_PAINTBRUSHFX_H +#define IWA_PAINTBRUSHFX_H + +#include "stdfx.h" +#include "tfxparam.h" +#include "tparamset.h" + +#include +#include + +struct double2 { + double x, y; +}; + +struct colorRGBA { + double r, g, b, a; +}; + +struct BrushStroke { + QVector centerPos; + TPointD originPos; + colorRGBA color; + double length; + double widthHalf; + double angle; + int textureId; + bool invert; + double randomVal; + double stack; // p̒l +}; + +struct BrushVertex { + double pos[2]; + double texCoord[2]; + BrushVertex(const TPointD _pos, double u, double v) { + pos[0] = _pos.x; + pos[1] = _pos.y; + texCoord[0] = u; + texCoord[1] = v; + } + BrushVertex() : BrushVertex(TPointD(), 0, 0) {} +}; + +struct FlowPaintBrushFxParam { + TDimensionI dim; + TPointD origin_pos; + TPointD horiz_pos; + TPointD vert_pos; + TRectD bbox; + int fill_gap_size; + + double h_density; + double v_density; + double pos_randomness; + double pos_wobble; + + int random_seed; + DoublePair tipLength; + DoublePair tipWidth; + DoublePair tipAlpha; + double width_random; + double length_random; + double angle_random; + + int reso; + bool anti_jaggy; + + TPointD hVec; + TPointD vVec; + double2 vVec_unit; + + TAffine brushAff; + + int lastFrame; +}; + +class Iwa_FlowPaintBrushFx final : public TStandardRasterFx { + FX_PLUGIN_DECLARATION(Iwa_FlowPaintBrushFx) +public: + enum StackMode { + NoSort = 0, + Smaller, + Larger, + Darker, + Brighter, + Random + //,TestModeArea + }; + +private: + TRasterFxPort m_brush; + TRasterFxPort m_flow; + TRasterFxPort m_area; + TRasterFxPort m_color; + + // x + TDoubleParamP m_h_density; + TDoubleParamP m_v_density; + // ʒũ_i0Fiq 1FlɃ_ <1: ΂‚j + TDoubleParamP m_pos_randomness; + TDoubleParamP m_pos_wobble; + // ^b`̃TCYiArea̒lɂĕωj + TRangeParamP m_tip_width; + TRangeParamP m_tip_length; + // ^b`̕sx + TRangeParamP m_tip_alpha; + TIntParamP m_tip_joints; + TBoolParamP m_bidirectional; + + // ΂‚ + TDoubleParamP m_width_randomness; + TDoubleParamP m_length_randomness; + TDoubleParamP m_angle_randomness; // degree + + TDoubleParamP m_sustain_width_to_skew; + TBoolParamP m_anti_jaggy; + + // ͈ + TPointParamP m_origin_pos; + TPointParamP m_horizontal_pos; + TPointParamP m_vertical_pos; + TPointParamP m_curve_point; + TDoubleParamP m_fill_gap_size; + + // t[ + TDoubleParamP m_reference_frame; + // t[gĐ^b`̊ + TDoubleParamP m_reference_prevalence; + + // _V[h + TIntParamP m_random_seed; + // בւ + TIntEnumParamP m_sortBy; + + // uV^b`̃X^[f[^擾 + void getBrushRasters(std::vector &brushRasters, TDimension &b_size, + int &lastFrame, TTile &tile, const TRenderSettings &ri); + + template + void setFlowTileToBuffer(const RASTER flowRas, double2 *buf); + template + void setAreaTileToBuffer(const RASTER areaRas, double *buf); + template + void setColorTileToBuffer(const RASTER colorRas, colorRGBA *buf); + + // ߂ + template + void setOutRaster(const RASTER outRas, double *buf); + + void fillGapByDilateAndErode(double *buf, const TDimension &dim, + const int fill_gap_size); + + void computeBrushVertices(QVector &brushVertices, + QList &brushStrokes, + FlowPaintBrushFxParam &p, TTile &tile, double frame, + const TRenderSettings &ri); + + double getSizePixelAmount(const double val, const TAffine affine); + FlowPaintBrushFxParam getParam(TTile &tile, double frame, + const TRenderSettings &ri); + +public: + Iwa_FlowPaintBrushFx(); + + bool canHandle(const TRenderSettings &info, double frame) override { + return true; + } + bool doGetBBox(double frame, TRectD &bBox, + const TRenderSettings &info) override; + void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override; + void getParamUIs(TParamUIConcept *&concepts, int &length) override; + + std::string getAlias(double frame, + const TRenderSettings &info) const override; +}; + +#endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_fractalnoisefx.cpp b/toonz/sources/stdfx/iwa_fractalnoisefx.cpp index 57166904..c144b99f 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 6e558daf..ae0f5dcb 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 63c6b1f6..02de8fb2 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, @@ -122,6 +124,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 b3879456..0cc2c0c8 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,22 +80,53 @@ 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() - : m_h_maxlen(0.0), m_v_maxlen(0.0), m_scale(1.0) { + : m_h_maxlen(0.0), m_v_maxlen(0.0), m_scale(1.0), m_sampling_size(1.0) { /*- 共通パラメータのバインド -*/ addInputPort("Source", m_source); addInputPort("Warper", m_warper); bindParam(this, "h_maxlen", m_h_maxlen); bindParam(this, "v_maxlen", m_v_maxlen); bindParam(this, "scale", m_scale); + bindParam(this, "sampling_size", m_sampling_size); m_h_maxlen->setMeasureName("fxLength"); m_v_maxlen->setMeasureName("fxLength"); m_h_maxlen->setValueRange(-100.0, 100.0); m_v_maxlen->setValueRange(-100.0, 100.0); m_scale->setValueRange(1.0, 100.0); + 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()) + setFxVersion(2); +} + +//-------------------------------------------- + +void Iwa_GradientWarpFx::onFxVersionSet() { + getParams()->getParamVar("sampling_size")->setIsHidden(getFxVersion() == 1); } //------------------------------------ @@ -119,14 +153,21 @@ void Iwa_GradientWarpFx::doCompute(TTile &tile, double frame, double scale = m_scale->getValue(frame); + double sampling_size = m_sampling_size->getValue(frame); + double grad_factor = 1. / sampling_size; + sampling_size *= k; + /*- ワープ距離が0なら、ソース画像をそのまま返す -*/ if (hLength == 0.0 && vLength == 0.0) { m_source->compute(tile, frame, settings); return; } - int margin = static_cast( - ceil((std::abs(hLength) < std::abs(vLength)) ? std::abs(vLength) : std::abs(hLength))); + 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); /*- 素材計算範囲を計算 -*/ /*- 出力範囲 -*/ @@ -136,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(); + { + /*- 繧ソ繧、繝ォ縺ョ逕サ蜒上r・舌€懶シ代↓豁」隕丞喧縺励※繝帙せ繝医Γ繝「繝ェ縺ォ隱ュ縺ソ霎シ繧€ -*/ + 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()); } /*- 参照画像を正規化して格納 -*/ @@ -168,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倍して増やす -*/ @@ -190,7 +243,8 @@ void Iwa_GradientWarpFx::doCompute(TTile &tile, double frame, result_host_ras->lock(); result_host = (float4 *)result_host_ras->getRawData(); doCompute_CPU(tile, frame, settings, hLength, vLength, margin, enlargedDim, - source_host, warper_host, result_host); + source_host, warper_host, result_host, sampling_size, + grad_factor); /*- ポインタ入れ替え -*/ source_host = result_host; @@ -200,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(); @@ -218,25 +276,52 @@ void Iwa_GradientWarpFx::doCompute(TTile &tile, double frame, Fx計算 ------------------------------------*/ -void Iwa_GradientWarpFx::doCompute_CPU(TTile &tile, const double frame, - const TRenderSettings &settings, - double hLength, double vLength, - int margin, TDimensionI &enlargedDim, - float4 *source_host, float *warper_host, - float4 *result_host) { +void Iwa_GradientWarpFx::doCompute_CPU( + TTile &tile, const double frame, const TRenderSettings &settings, + double hLength, double vLength, int margin, TDimensionI &enlargedDim, + float4 *source_host, float *warper_host, float4 *result_host, + double sampling_size, double grad_factor) { + auto lerp = [](float val0, float val1, double ratio) -> float { + return val0 * (1.f - ratio) + val1 * ratio; + }; + float4 *res_p = result_host + margin * enlargedDim.lx + margin; - float *warp_up = warper_host + (margin + 1) * enlargedDim.lx + margin; - float *warp_down = warper_host + (margin - 1) * enlargedDim.lx + margin; - float *warp_right = warper_host + margin * enlargedDim.lx + 1 + margin; - float *warp_left = warper_host + margin * enlargedDim.lx - 1 + margin; + if (getFxVersion() == 1) { + sampling_size = 1.0; + grad_factor = 1.0; + } + int size_i = static_cast(std::floor(sampling_size)); + double ratio = sampling_size - (double)size_i; + + float *warp_up_0 = warper_host + (margin + size_i) * enlargedDim.lx + margin; + float *warp_up_1 = + warper_host + (margin + size_i + 1) * enlargedDim.lx + margin; + + float *warp_down_0 = + warper_host + (margin - size_i) * enlargedDim.lx + margin; + float *warp_down_1 = + warper_host + (margin - size_i - 1) * enlargedDim.lx + margin; + + float *warp_right_0 = warper_host + margin * enlargedDim.lx + size_i + margin; + float *warp_right_1 = + warper_host + margin * enlargedDim.lx + size_i + 1 + margin; + + float *warp_left_0 = warper_host + margin * enlargedDim.lx - size_i + margin; + float *warp_left_1 = + warper_host + margin * enlargedDim.lx - size_i - 1 + margin; for (int j = margin; j < enlargedDim.ly - margin; j++) { - for (int i = margin; i < enlargedDim.lx - margin; - i++, res_p++, warp_up++, warp_down++, warp_right++, warp_left++) { + for (int i = margin; i < enlargedDim.lx - margin; i++, res_p++, warp_up_0++, + warp_up_1++, warp_down_0++, warp_down_1++, warp_right_0++, + warp_right_1++, warp_left_0++, warp_left_1++) { /*- 勾配を得る -*/ - float2 grad = {static_cast((*warp_right) - (*warp_left)), - static_cast((*warp_up) - (*warp_down))}; + float2 grad = { + lerp(*warp_right_0 - *warp_left_0, *warp_right_1 - *warp_left_1, + ratio), + lerp(*warp_up_0 - *warp_down_0, *warp_up_1 - *warp_down_1, ratio)}; + grad.x *= grad_factor; + grad.y *= grad_factor; /*- 参照点 -*/ float2 samplePos = {static_cast(i + grad.x * hLength), static_cast(j + grad.y * vLength)}; @@ -260,10 +345,14 @@ void Iwa_GradientWarpFx::doCompute_CPU(TTile &tile, const double frame, } res_p += 2 * margin; - warp_up += 2 * margin; - warp_down += 2 * margin; - warp_right += 2 * margin; - warp_left += 2 * margin; + warp_up_0 += 2 * margin; + warp_up_1 += 2 * margin; + warp_down_0 += 2 * margin; + warp_down_1 += 2 * margin; + warp_right_0 += 2 * margin; + warp_right_1 += 2 * margin; + warp_left_0 += 2 * margin; + warp_left_1 += 2 * margin; } } diff --git a/toonz/sources/stdfx/iwa_gradientwarpfx.h b/toonz/sources/stdfx/iwa_gradientwarpfx.h index 6629390b..e4fd585c 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; @@ -33,6 +41,7 @@ protected: TDoubleParamP m_scale; /*- ワープ量を増やすスカラー値。この値はマージン値には影響しない -*/ + TDoubleParamP m_sampling_size; void get_render_real_hv(const double frame, const TAffine affine, double &h_maxlen, double &v_maxlen); @@ -57,7 +66,8 @@ protected: const TRenderSettings &settings, double hLength, double vLength, int margin, TDimensionI &enlargedDim, float4 *source_host, float *warper_host, - float4 *result_host); + float4 *result_host, double sampling_size, + double grad_factor); float4 getSourceVal_CPU(float4 *source_host, TDimensionI &enlargedDim, int pos_x, int pos_y); @@ -74,6 +84,8 @@ public: const TRenderSettings &info) override; bool canHandle(const TRenderSettings &info, double frame) override; + + void onFxVersionSet() final override; }; #endif \ No newline at end of file diff --git a/toonz/sources/stdfx/iwa_lineargradientfx.cpp b/toonz/sources/stdfx/iwa_lineargradientfx.cpp index 173aa895..bc334eff 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 3b62aa44..983de221 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 f7a0a31a..62715e68 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_motionflowfx.cpp b/toonz/sources/stdfx/iwa_motionflowfx.cpp new file mode 100644 index 00000000..b08e4ca2 --- /dev/null +++ b/toonz/sources/stdfx/iwa_motionflowfx.cpp @@ -0,0 +1,150 @@ +#include "iwa_motionflowfx.h" + +#include "tfxattributes.h" + +#include "toonz/tstageobject.h" + +#include "trop.h" + +namespace { +double getSizePixelAmount(const double val, const TAffine affine) { + /*--- Convert to vector --- */ + TPointD vect; + vect.x = val; + vect.y = 0.0; + /*--- Apply geometrical transformation ---*/ + // For the following lines I referred to lines 586-592 of + // sources/stdfx/motionblurfx.cpp + TAffine aff(affine); + aff.a13 = aff.a23 = 0; /* ignore translation */ + vect = aff * vect; + /*--- return the length of the vector ---*/ + return std::sqrt(vect.x * vect.x + vect.y * vect.y); +} + +} // namespace + +//------------------------------------------------------------ + +Iwa_MotionFlowFx::Iwa_MotionFlowFx() + : m_normalizeType(new TIntEnumParam(NORMALIZE_AUTO, "Auto")) + , m_normalizeRange(1.0) { + bindParam(this, "shutterLength", m_shutterLength); + bindParam(this, "motionObjectType", m_motionObjectType); + bindParam(this, "motionObjectIndex", m_motionObjectIndex); + bindParam(this, "normalizeType", m_normalizeType); + bindParam(this, "normalizeRange", m_normalizeRange); + + m_normalizeType->addItem(NORMALIZE_MANUAL, "Manual"); + m_normalizeRange->setMeasureName("fxLength"); + m_normalizeRange->setValueRange(0.01, 1000.0); + + getAttributes()->setIsSpeedAware(true); +} + +//------------------------------------------------------------ + +void Iwa_MotionFlowFx::doCompute(TTile& tile, double frame, + const TRenderSettings& settings) { + /* Get parameters */ + TAffine aff_Before, aff_After; + getAttributes()->getMotionAffines(aff_Before, aff_After); + + TDimensionI dimOut(tile.getRaster()->getLx(), tile.getRaster()->getLy()); + /* output */ + TRasterGR8P out_ras(sizeof(double3) * dimOut.lx * dimOut.ly, 1); + out_ras->lock(); + double3* out_ras_p = (double3*)out_ras->getRawData(); + + double3* out_p = out_ras_p; + + double norm_range = 0.0; + + for (int y = 0; y < dimOut.ly; y++) { + for (int x = 0; x < dimOut.lx; x++, out_p++) { + TPointD pos = TPointD((double)x, (double)y) + tile.m_pos; + TPointD vec = pos - (aff_Before * aff_After.inv() * pos); + out_p->z = std::sqrt(vec.x * vec.x + vec.y * vec.y); + if (out_p->z > 0.0) { + out_p->x = vec.x / out_p->z; + out_p->y = vec.y / out_p->z; + norm_range = std::max(norm_range, out_p->z); + } else { + out_p->x = 0.0; + out_p->y = 0.0; + } + } + } + + if (m_normalizeType->getValue() == NORMALIZE_MANUAL) + norm_range = getSizePixelAmount(m_normalizeRange->getValue(frame), + settings.m_affine); + + tile.getRaster()->clear(); + TRaster32P outRas32 = (TRaster32P)tile.getRaster(); + TRaster64P outRas64 = (TRaster64P)tile.getRaster(); + if (outRas32) + setOutRas(outRas32, out_ras_p, norm_range); + else if (outRas64) + setOutRas(outRas64, out_ras_p, norm_range); + + out_ras->unlock(); +} + +//------------------------------------------------------------ + +template +void Iwa_MotionFlowFx::setOutRas(RASTER ras, double3* outBuf, + double norm_range) { + double3* buf_p = outBuf; + for (int j = 0; j < ras->getLy(); j++) { + PIXEL* pix = ras->pixels(j); + for (int i = 0; i < ras->getLx(); i++, pix++, buf_p++) { + double val = 0.5 + buf_p->x * 0.5; + pix->r = (typename PIXEL::Channel)(val * double(PIXEL::maxChannelValue)); + val = 0.5 + buf_p->y * 0.5; + pix->g = (typename PIXEL::Channel)(val * double(PIXEL::maxChannelValue)); + val = std::min(1.0, buf_p->z / norm_range); + pix->b = (typename PIXEL::Channel)(val * double(PIXEL::maxChannelValue)); + pix->m = PIXEL::maxChannelValue; + } + } +} + +//------------------------------------------------------------ + +bool Iwa_MotionFlowFx::doGetBBox(double frame, TRectD& bBox, + const TRenderSettings& info) { + bBox = TConsts::infiniteRectD; + return true; +} + +//------------------------------------------------------------ + +bool Iwa_MotionFlowFx::canHandle(const TRenderSettings& info, double frame) { + return true; +} + +/*------------------------------------------------------------ + Since there is a possibility that the reference object is moving, + Change the alias every frame +------------------------------------------------------------*/ + +std::string Iwa_MotionFlowFx::getAlias(double frame, + const TRenderSettings& info) const { + std::string alias = getFxType(); + alias += "["; + + // alias of the effects related to the input ports separated by commas + // a port that is not connected to an alias blank (empty string) + + std::string paramalias(""); + for (int i = 0; i < getParams()->getParamCount(); i++) { + TParam* param = getParams()->getParam(i); + paramalias += param->getName() + "=" + param->getValueAlias(frame, 3); + } + unsigned long id = getIdentifier(); + return alias + std::to_string(frame) + "," + std::to_string(id) + paramalias + + "]"; +} +FX_PLUGIN_IDENTIFIER(Iwa_MotionFlowFx, "iwa_MotionFlowFx") diff --git a/toonz/sources/stdfx/iwa_motionflowfx.h b/toonz/sources/stdfx/iwa_motionflowfx.h new file mode 100644 index 00000000..651d44e1 --- /dev/null +++ b/toonz/sources/stdfx/iwa_motionflowfx.h @@ -0,0 +1,40 @@ +#pragma once + +#ifndef IWA_MOTION_FLOW_FX_H +#define IWA_MOTION_FLOW_FX_H + +#include "tfxparam.h" +#include "stdfx.h" + +#include "motionawarebasefx.h" + +struct double3 { + double x, y, z; +}; + +class Iwa_MotionFlowFx final : public MotionAwareAffineFx { + FX_PLUGIN_DECLARATION(Iwa_MotionFlowFx) + + TIntEnumParamP m_normalizeType; + TDoubleParamP m_normalizeRange; + + enum NormalizeType { NORMALIZE_AUTO = 0, NORMALIZE_MANUAL }; + +public: + Iwa_MotionFlowFx(); + + void doCompute(TTile& tile, double frame, + const TRenderSettings& settings) override; + + template + void setOutRas(RASTER ras, double3* outBuf, double norm_range); + + bool doGetBBox(double frame, TRectD& bBox, + const TRenderSettings& info) override; + bool canHandle(const TRenderSettings& info, double frame) override; + // Since there is a possibility that the reference object is moving, + // Change the alias every frame + std::string getAlias(double frame, + const TRenderSettings& info) const override; +}; +#endif diff --git a/toonz/sources/stdfx/iwa_perspectivedistortfx.cpp b/toonz/sources/stdfx/iwa_perspectivedistortfx.cpp index e38f043a..0a5f375c 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; - double sourcePosX = offs + precision * (tile.m_pos.x - offs); + TRenderSettings new_sets(rend_sets); - TTile sourceTile; - m_source->allocateAndCompute(sourceTile, TPointD(sourcePosX, tile.m_pos.y), - sourceDim, tile.getRaster(), frame, new_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); + + 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 c479bbca..5f17e46f 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 60f2cede..66085bdc 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 7808e36e..dcbeace4 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 7087fa40..270ee8df 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 3547e98e..6b521001 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 eb189c81..eb849767 100644 --- a/toonz/sources/stdfx/iwa_soapbubblefx.cpp +++ b/toonz/sources/stdfx/iwa_soapbubblefx.cpp @@ -64,247 +64,6 @@ static float* dt(float* f, int n, float a = 1.0f) { //------------------------------------ -Iwa_SoapBubbleFx::Iwa_SoapBubbleFx() - : Iwa_SpectrumFx() - , m_renderMode(new TIntEnumParam(RENDER_MODE_BUBBLE, "Bubble")) - , m_binarize_threshold(0.5) - , m_shape_aspect_ratio(1.0) - , m_blur_radius(5.0) - , m_blur_power(0.5) - , m_multi_source(false) - , m_mask_center(false) // obsolete - , m_center_opacity(1.0) - , m_fit_thickness(false) - , m_normal_sample_distance(1) - , m_noise_sub_depth(3) - , m_noise_resolution_s(18.0) - , m_noise_resolution_t(5.0) - , m_noise_sub_composite_ratio(0.5) - , m_noise_evolution(0.0) - , m_noise_depth_mix_ratio(0.05) - , m_noise_thickness_mix_ratio(0.05) { - removeInputPort("Source"); - removeInputPort("Light"); /* not used */ - addInputPort("Thickness", m_input); - addInputPort("Shape", m_shape); - addInputPort("Depth", m_depth); - - bindParam(this, "renderMode", m_renderMode); - m_renderMode->addItem(RENDER_MODE_THICKNESS, "Thickness"); - m_renderMode->addItem(RENDER_MODE_DEPTH, "Depth"); - - bindParam(this, "binarizeThresold", m_binarize_threshold); - bindParam(this, "shapeAspectRatio", m_shape_aspect_ratio); - bindParam(this, "blurRadius", m_blur_radius); - bindParam(this, "blurPower", m_blur_power); - bindParam(this, "multiSource", m_multi_source); - bindParam(this, "maskCenter", m_mask_center, false, true); // obsolete - bindParam(this, "centerOpacity", m_center_opacity); - bindParam(this, "fitThickness", m_fit_thickness); - bindParam(this, "normalSampleDistance", m_normal_sample_distance); - bindParam(this, "noiseSubDepth", m_noise_sub_depth); - bindParam(this, "noiseResolutionS", m_noise_resolution_s); - bindParam(this, "noiseResolutionT", m_noise_resolution_t); - bindParam(this, "noiseSubCompositeRatio", m_noise_sub_composite_ratio); - bindParam(this, "noiseEvolution", m_noise_evolution); - bindParam(this, "noiseDepthMixRatio", m_noise_depth_mix_ratio); - bindParam(this, "noiseThicknessMixRatio", m_noise_thickness_mix_ratio); - - m_binarize_threshold->setValueRange(0.01, 0.99); - m_shape_aspect_ratio->setValueRange(0.2, 5.0); - m_blur_radius->setMeasureName("fxLength"); - m_blur_radius->setValueRange(0.0, 25.0); - m_blur_power->setValueRange(0.01, 5.0); - m_center_opacity->setValueRange(0.0, 1.0); - - m_normal_sample_distance->setValueRange(1, 20); - m_noise_sub_depth->setValueRange(1, 5); - m_noise_resolution_s->setValueRange(1.0, 40.0); - m_noise_resolution_t->setValueRange(1.0, 20.0); - m_noise_sub_composite_ratio->setValueRange(0.0, 5.0); - m_noise_depth_mix_ratio->setValueRange(0.0, 1.0); - m_noise_thickness_mix_ratio->setValueRange(0.0, 1.0); -} - -//------------------------------------ - -void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame, - const TRenderSettings& settings) { - if (!m_input.isConnected()) return; - if (!m_shape.isConnected() && !m_depth.isConnected()) return; - - TDimensionI dim(tile.getRaster()->getLx(), tile.getRaster()->getLy()); - TRectD bBox(tile.m_pos, TPointD(dim.lx, dim.ly)); - QList allocatedRasList; - - if (m_renderMode->getValue() == RENDER_MODE_DEPTH && m_depth.isConnected()) { - m_depth->allocateAndCompute(tile, bBox.getP00(), dim, tile.getRaster(), - frame, settings); - return; - } - - /* soap bubble color map */ - TRasterGR8P bubbleColor_ras(sizeof(float3) * 256 * 256, 1); - bubbleColor_ras->lock(); - allocatedRasList.append(bubbleColor_ras); - float3* bubbleColor_p = (float3*)bubbleColor_ras->getRawData(); - if (m_renderMode->getValue() == RENDER_MODE_BUBBLE) - calcBubbleMap(bubbleColor_p, frame, true); - - if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; - - /* depth map */ - TRasterGR8P depth_map_ras(sizeof(float) * dim.lx * dim.ly, 1); - depth_map_ras->lock(); - allocatedRasList.append(depth_map_ras); - float* depth_map_p = (float*)depth_map_ras->getRawData(); - - /* alpha map */ - TRasterGR8P alpha_map_ras(sizeof(float) * dim.lx * dim.ly, 1); - alpha_map_ras->lock(); - allocatedRasList.append(alpha_map_ras); - float* alpha_map_p = (float*)alpha_map_ras->getRawData(); - - /* region indices */ - TRasterGR8P regionIds_ras(sizeof(USHORT) * dim.lx * dim.ly, 1); - regionIds_ras->lock(); - regionIds_ras->clear(); - allocatedRasList.append(regionIds_ras); - USHORT* regionIds_p = (USHORT*)regionIds_ras->getRawData(); - QList regionBoundingRects; - - /* if the depth image is connected, use it */ - if (m_depth.isConnected()) { - TTile depth_tile; - m_depth->allocateAndCompute(depth_tile, bBox.getP00(), dim, - tile.getRaster(), frame, settings); - - if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; - - TRasterP depthRas = depth_tile.getRaster(); - depthRas->lock(); - - TRaster32P depthRas32 = (TRaster32P)depthRas; - TRaster64P depthRas64 = (TRaster64P)depthRas; - { - if (depthRas32) - convertToBrightness(depthRas32, depth_map_p, - alpha_map_p, dim); - else if (depthRas64) - convertToBrightness(depthRas64, depth_map_p, - alpha_map_p, dim); - } - depthRas->unlock(); - - // set one region covering whole camera rect - regionBoundingRects.append(QRect(0, 0, dim.lx, dim.ly)); - } - /* or, use the shape image to obtain pseudo depth */ - else { /* m_shape.isConnected */ - /* obtain shape image */ - TTile shape_tile; - { - TRenderSettings settings_aux(settings); - settings_aux.m_bpp = 32; - m_shape->allocateAndCompute(shape_tile, bBox.getP00(), dim, nullptr, - frame, settings_aux); - } - - if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; - - processShape(frame, shape_tile, depth_map_p, alpha_map_p, regionIds_p, - regionBoundingRects, dim, settings); - } - - if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; - - // conpute thickness - TRasterGR8P thickness_map_ras(sizeof(float) * dim.lx * dim.ly, 1); - thickness_map_ras->lock(); - allocatedRasList.append(thickness_map_ras); - float* thickness_map_p = (float*)thickness_map_ras->getRawData(); - - TRasterP tileRas = tile.getRaster(); - TRaster32P ras32 = (TRaster32P)tileRas; - TRaster64P ras64 = (TRaster64P)tileRas; - - if (m_fit_thickness->getValue()) { - // Get the original bbox of thickness image - TRectD thickBBox; - m_input->getBBox(frame, thickBBox, settings); - if (thickBBox == TConsts::infiniteRectD) - thickBBox = TRectD(tile.m_pos, TDimensionD(tile.getRaster()->getLx(), - tile.getRaster()->getLy())); - // Compute the thickenss tile. - TTile thicknessTile; - TDimension thickDim(static_cast(thickBBox.getLx() + 0.5), - static_cast(thickBBox.getLy() + 0.5)); - m_input->allocateAndCompute(thicknessTile, thickBBox.getP00(), thickDim, - tile.getRaster(), frame, settings); - - if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; - - TRasterP thickRas = thicknessTile.getRaster(); - - fitThicknessPatches(thickRas, thickDim, thickness_map_p, dim, regionIds_p, - regionBoundingRects); - } else { - /* compute the thickness input and temporarily store to the tile */ - m_input->compute(tile, frame, settings); - - if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; - - if (ras32) - convertToBrightness(ras32, thickness_map_p, nullptr, - dim); - else if (ras64) - convertToBrightness(ras64, thickness_map_p, nullptr, - dim); - } - - if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; - - /* process noise */ - processNoise(thickness_map_p, depth_map_p, dim, frame, settings); - - if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; - - if (ras32) - convertToRaster(ras32, thickness_map_p, depth_map_p, - alpha_map_p, dim, bubbleColor_p); - else if (ras64) - convertToRaster(ras64, thickness_map_p, depth_map_p, - alpha_map_p, dim, bubbleColor_p); - - for (int i = 0; i < allocatedRasList.size(); i++) - allocatedRasList.at(i)->unlock(); -} - -//------------------------------------ - -template -void Iwa_SoapBubbleFx::convertToBrightness(const RASTER srcRas, float* dst, - float* alpha, TDimensionI dim) { - float* dst_p = dst; - float* alpha_p = alpha; - for (int j = 0; j < dim.ly; j++) { - PIXEL* pix = srcRas->pixels(j); - for (int i = 0; i < dim.lx; i++, dst_p++, pix++) { - float r = (float)pix->r / (float)PIXEL::maxChannelValue; - float g = (float)pix->g / (float)PIXEL::maxChannelValue; - float b = (float)pix->b / (float)PIXEL::maxChannelValue; - /* brightness */ - *dst_p = 0.298912f * r + 0.586611f * g + 0.114478f * b; - if (alpha) { - *alpha_p = (float)pix->m / (float)PIXEL::maxChannelValue; - alpha_p++; - } - } - } -} - -//------------------------------------ - template void Iwa_SoapBubbleFx::convertToRaster(const RASTER ras, float* thickness_map_p, float* depth_map_p, float* alpha_map_p, @@ -405,6 +164,340 @@ void Iwa_SoapBubbleFx::convertToRaster(const RASTER ras, float* thickness_map_p, } } +// 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")) + , m_binarize_threshold(0.5) + , m_shape_aspect_ratio(1.0) + , m_blur_radius(5.0) + , m_blur_power(0.5) + , m_multi_source(false) + , m_mask_center(false) // obsolete + , m_center_opacity(1.0) + , m_fit_thickness(false) + , m_normal_sample_distance(1) + , m_noise_sub_depth(3) + , m_noise_resolution_s(18.0) + , m_noise_resolution_t(5.0) + , m_noise_sub_composite_ratio(0.5) + , m_noise_evolution(0.0) + , m_noise_depth_mix_ratio(0.05) + , m_noise_thickness_mix_ratio(0.05) { + removeInputPort("Source"); + removeInputPort("Light"); /* not used */ + addInputPort("Thickness", m_input); + addInputPort("Shape", m_shape); + addInputPort("Depth", m_depth); + + bindParam(this, "renderMode", m_renderMode); + m_renderMode->addItem(RENDER_MODE_THICKNESS, "Thickness"); + m_renderMode->addItem(RENDER_MODE_DEPTH, "Depth"); + + bindParam(this, "binarizeThresold", m_binarize_threshold); + bindParam(this, "shapeAspectRatio", m_shape_aspect_ratio); + bindParam(this, "blurRadius", m_blur_radius); + bindParam(this, "blurPower", m_blur_power); + bindParam(this, "multiSource", m_multi_source); + bindParam(this, "maskCenter", m_mask_center, false, true); // obsolete + bindParam(this, "centerOpacity", m_center_opacity); + bindParam(this, "fitThickness", m_fit_thickness); + bindParam(this, "normalSampleDistance", m_normal_sample_distance); + bindParam(this, "noiseSubDepth", m_noise_sub_depth); + bindParam(this, "noiseResolutionS", m_noise_resolution_s); + bindParam(this, "noiseResolutionT", m_noise_resolution_t); + bindParam(this, "noiseSubCompositeRatio", m_noise_sub_composite_ratio); + bindParam(this, "noiseEvolution", m_noise_evolution); + bindParam(this, "noiseDepthMixRatio", m_noise_depth_mix_ratio); + bindParam(this, "noiseThicknessMixRatio", m_noise_thickness_mix_ratio); + + m_binarize_threshold->setValueRange(0.01, 0.99); + m_shape_aspect_ratio->setValueRange(0.2, 5.0); + m_blur_radius->setMeasureName("fxLength"); + m_blur_radius->setValueRange(0.0, 25.0); + m_blur_power->setValueRange(0.01, 5.0); + m_center_opacity->setValueRange(0.0, 1.0); + + m_normal_sample_distance->setValueRange(1, 20); + m_noise_sub_depth->setValueRange(1, 5); + m_noise_resolution_s->setValueRange(1.0, 40.0); + m_noise_resolution_t->setValueRange(1.0, 20.0); + m_noise_sub_composite_ratio->setValueRange(0.0, 5.0); + m_noise_depth_mix_ratio->setValueRange(0.0, 1.0); + m_noise_thickness_mix_ratio->setValueRange(0.0, 1.0); +} + +//------------------------------------ + +void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame, + const TRenderSettings& settings) { + if (!m_input.isConnected()) return; + if (!m_shape.isConnected() && !m_depth.isConnected()) return; + + TDimensionI dim(tile.getRaster()->getLx(), tile.getRaster()->getLy()); + TRectD bBox(tile.m_pos, TPointD(dim.lx, dim.ly)); + QList allocatedRasList; + + if (m_renderMode->getValue() == RENDER_MODE_DEPTH && m_depth.isConnected()) { + m_depth->allocateAndCompute(tile, bBox.getP00(), dim, tile.getRaster(), + frame, settings); + return; + } + + /* soap bubble color map */ + TRasterGR8P bubbleColor_ras(sizeof(float3) * 256 * 256, 1); + bubbleColor_ras->lock(); + allocatedRasList.append(bubbleColor_ras); + float3* bubbleColor_p = (float3*)bubbleColor_ras->getRawData(); + if (m_renderMode->getValue() == RENDER_MODE_BUBBLE) + calcBubbleMap(bubbleColor_p, frame, tile.getRaster()->isLinear(), true); + + if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; + + /* depth map */ + TRasterGR8P depth_map_ras(sizeof(float) * dim.lx * dim.ly, 1); + depth_map_ras->lock(); + allocatedRasList.append(depth_map_ras); + float* depth_map_p = (float*)depth_map_ras->getRawData(); + + /* alpha map */ + TRasterGR8P alpha_map_ras(sizeof(float) * dim.lx * dim.ly, 1); + alpha_map_ras->lock(); + allocatedRasList.append(alpha_map_ras); + float* alpha_map_p = (float*)alpha_map_ras->getRawData(); + + /* region indices */ + TRasterGR8P regionIds_ras(sizeof(USHORT) * dim.lx * dim.ly, 1); + regionIds_ras->lock(); + regionIds_ras->clear(); + allocatedRasList.append(regionIds_ras); + USHORT* regionIds_p = (USHORT*)regionIds_ras->getRawData(); + QList regionBoundingRects; + + /* if the depth image is connected, use it */ + if (m_depth.isConnected()) { + TTile depth_tile; + m_depth->allocateAndCompute(depth_tile, bBox.getP00(), dim, + tile.getRaster(), frame, settings); + + if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; + + TRasterP depthRas = depth_tile.getRaster(); + depthRas->lock(); + + TRaster32P depthRas32 = (TRaster32P)depthRas; + TRaster64P depthRas64 = (TRaster64P)depthRas; + TRasterFP depthRasF = (TRasterFP)depthRas; + { + if (depthRas32) + convertToBrightness(depthRas32, depth_map_p, + alpha_map_p, dim); + 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(); + + // set one region covering whole camera rect + regionBoundingRects.append(QRect(0, 0, dim.lx, dim.ly)); + } + /* or, use the shape image to obtain pseudo depth */ + else { /* m_shape.isConnected */ + /* obtain shape image */ + TTile shape_tile; + { + TRenderSettings settings_aux(settings); + settings_aux.m_bpp = 32; + m_shape->allocateAndCompute(shape_tile, bBox.getP00(), dim, nullptr, + frame, settings_aux); + } + + if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; + + processShape(frame, shape_tile, depth_map_p, alpha_map_p, regionIds_p, + regionBoundingRects, dim, settings); + } + + if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; + + // conpute thickness + TRasterGR8P thickness_map_ras(sizeof(float) * dim.lx * dim.ly, 1); + thickness_map_ras->lock(); + allocatedRasList.append(thickness_map_ras); + float* thickness_map_p = (float*)thickness_map_ras->getRawData(); + + 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 + TRectD thickBBox; + m_input->getBBox(frame, thickBBox, settings); + if (thickBBox == TConsts::infiniteRectD) + thickBBox = TRectD(tile.m_pos, TDimensionD(tile.getRaster()->getLx(), + tile.getRaster()->getLy())); + // Compute the thickenss tile. + TTile thicknessTile; + TDimension thickDim(static_cast(thickBBox.getLx() + 0.5), + static_cast(thickBBox.getLy() + 0.5)); + m_input->allocateAndCompute(thicknessTile, thickBBox.getP00(), thickDim, + tile.getRaster(), frame, settings); + + if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; + + TRasterP thickRas = thicknessTile.getRaster(); + + fitThicknessPatches(thickRas, thickDim, thickness_map_p, dim, regionIds_p, + regionBoundingRects); + } else { + /* compute the thickness input and temporarily store to the tile */ + m_input->compute(tile, frame, settings); + + if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; + + if (ras32) + convertToBrightness(ras32, thickness_map_p, nullptr, + dim); + 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; + + /* process noise */ + processNoise(thickness_map_p, depth_map_p, dim, frame, settings); + + if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return; + + if (ras32) + convertToRaster(ras32, thickness_map_p, depth_map_p, + alpha_map_p, dim, bubbleColor_p); + 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(); +} + +//------------------------------------ + +template +void Iwa_SoapBubbleFx::convertToBrightness(const RASTER srcRas, float* dst, + float* alpha, TDimensionI dim) { + float* dst_p = dst; + float* alpha_p = alpha; + for (int j = 0; j < dim.ly; j++) { + PIXEL* pix = srcRas->pixels(j); + for (int i = 0; i < dim.lx; i++, dst_p++, pix++) { + float r = (float)pix->r / (float)PIXEL::maxChannelValue; + float g = (float)pix->g / (float)PIXEL::maxChannelValue; + 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++; + } + } + } +} + //------------------------------------ void Iwa_SoapBubbleFx::processShape(double frame, TTile& shape_tile, diff --git a/toonz/sources/stdfx/iwa_spectrumfx.cpp b/toonz/sources/stdfx/iwa_spectrumfx.cpp index 74bcb46c..e0bbc011 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 6be4562c..08754f68 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 812be16d..07819dd0 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_tangentflowfx.cpp b/toonz/sources/stdfx/iwa_tangentflowfx.cpp new file mode 100644 index 00000000..ceb4fefd --- /dev/null +++ b/toonz/sources/stdfx/iwa_tangentflowfx.cpp @@ -0,0 +1,485 @@ +#include "iwa_tangentflowfx.h" + +#include + +namespace { +inline double dotProduct(const double2 v1, const double2 v2) { + return v1.x * v2.x + v1.y * v2.y; +} +inline double clamp01(double val) { + return (val > 1.0) ? 1.0 : (val < 0.0) ? 0.0 : val; +} + +const int MAX_OFFSET = 10000; +}; // namespace +//------------------------------------------------------------ +// obtain source tile brightness, normalizing 0 to 1 +template +void Iwa_TangentFlowFx::setSourceTileToBuffer(const RASTER srcRas, + double* buf) { + double* buf_p = buf; + for (int j = 0; j < srcRas->getLy(); j++) { + PIXEL* pix = srcRas->pixels(j); + for (int i = 0; i < srcRas->getLx(); i++, pix++, buf_p++) { + // Value = 0.3R 0.59G 0.11B + (*buf_p) = (double(pix->r) * 0.3 + double(pix->g) * 0.59 + + double(pix->b) * 0.11) / + double(PIXEL::maxChannelValue); + } + } +} + +//------------------------------------------------------------ + +void Iwa_TangentFlowFx::alignFlowDirection(double2* flow_buf, + const TDimension dim, + const double2& pivotVec) { + double2* fbuf_p = flow_buf; + for (int i = 0; i < dim.lx * dim.ly; i++, fbuf_p++) { + // 基準ベクトルに揃える + if ((*fbuf_p).x * pivotVec.x + (*fbuf_p).y * pivotVec.y < 0.0) { + (*fbuf_p).x = -(*fbuf_p).x; + (*fbuf_p).y = -(*fbuf_p).y; + } + } +} + +//------------------------------------------------------------ +// render vector field in red & green channels +template +void Iwa_TangentFlowFx::setOutputRaster(double2* flow_buf, double* grad_buf, + const RASTER dstRas) { + double2* fbuf_p = flow_buf; + double* gbuf_p = grad_buf; + for (int j = 0; j < dstRas->getLy(); j++) { + PIXEL* pix = dstRas->pixels(j); + for (int i = 0; i < dstRas->getLx(); i++, pix++, fbuf_p++, gbuf_p++) { + double val; + val = clamp01((*fbuf_p).x * 0.5 + 0.5) * (double)PIXEL::maxChannelValue; + pix->r = (typename PIXEL::Channel)(val); + val = clamp01((*fbuf_p).y * 0.5 + 0.5) * (double)PIXEL::maxChannelValue; + pix->g = (typename PIXEL::Channel)(val); + val = clamp01(*gbuf_p) * (double)PIXEL::maxChannelValue; + pix->b = (typename PIXEL::Channel)(val); + pix->m = (typename PIXEL::Channel)PIXEL::maxChannelValue; + } + } +} + +//------------------------------------------------------------ +// compute the initial vector field. +// apply sobel filter, rotate by 90 degrees and normalize. +// also obtaining gradient magnitude (length of the vector length) +// empty regions will be filled by using Fast Sweeping Method + +void SobelFilterWorker::run() { + // the 5x5 Sobel filter is already rotated by 90 degrees. + // ( i.e. converted as X = -y, Y = x ) + const double kernel_x[5][5] = {{0.25, 0.4, 0.5, 0.4, 0.25}, + {0.2, 0.5, 1., 0.5, 0.2}, + {0., 0., 0., 0., 0.}, + {-0.2, -0.5, -1., -0.5, -0.2}, + {-0.25, -0.4, -0.5, -0.4, -0.25}}; + const double kernel_y[5][5] = {{-0.25, -0.2, 0., 0.2, 0.25}, + {-0.4, -0.5, 0., 0.5, 0.4}, + {-0.5, -1., 0., 1., 0.5}, + {-0.4, -0.5, 0., 0.5, 0.4}, + {-0.25, -0.2, 0., 0.2, 0.25}}; + /* + const double kernel_x[5][5] = { + { 0.0297619, 0.047619, 0.0595238, 0.047619, 0.0297619 }, + { 0.0238, 0.0595238, 0.119, 0.0595238, 0.0238 }, + { 0., 0., 0., 0., 0. }, + { -0.0238, -0.0595238, -0.119, -0.0595238, -0.0238 }, + { -0.0297619, -0.047619, -0.0595238, -0.047619, -0.0297619 } + }; + const double kernel_y[5][5] = { + { -0.0297619, -0.0238, 0., 0.0238, 0.0297619 }, + { -0.047619, -0.0595238, 0., 0.0595238, 0.047619 }, + { -0.0595238, -0.119, 0., 0.119, 0.0595238 }, + { -0.047619, -0.0595238, 0., 0.0595238, 0.047619 }, + { -0.0297619, -0.0238, 0., 0.0238, 0.0297619 } + }; + */ + + auto source = [&](int xPos, int yPos) { + return m_source_buf[yPos * m_dim.lx + xPos]; + }; + + double2* flow_p = &m_flow_buf[m_yFrom * m_dim.lx]; + double* grad_mag_p = &m_grad_mag_buf[m_yFrom * m_dim.lx]; + int2* offset_p = &m_offset_buf[m_yFrom * m_dim.lx]; + + for (int y = m_yFrom; y < m_yTo; y++) { + for (int x = 0; x < m_dim.lx; x++, flow_p++, grad_mag_p++, offset_p++) { + double x_accum = 0.; + double y_accum = 0.; + for (int ky = 0; ky < 5; ky++) { + int yPos = y + ky - 2; // y coordinate of sample pos + if (yPos < 0) continue; + if (yPos >= m_dim.ly) break; + for (int kx = 0; kx < 5; kx++) { + int xPos = x + kx - 2; // x coordinate of sample pos + if (xPos < 0) continue; + if (xPos >= m_dim.lx) break; + if (kx == 2 && ky == 2) continue; + + double sourceVal = source(xPos, yPos); + x_accum += kernel_x[ky][kx] * sourceVal; + y_accum += kernel_y[ky][kx] * sourceVal; + } + } + // storing Gradient Magnitude + *grad_mag_p = std::sqrt(x_accum * x_accum + y_accum * y_accum); + (*flow_p).x = (*grad_mag_p == 0.) ? 0.0 : (x_accum / (*grad_mag_p)); + (*flow_p).y = (*grad_mag_p == 0.) ? 0.0 : (y_accum / (*grad_mag_p)); + + // offset will be used later in Fast Sweeping Method + if (*grad_mag_p < m_mag_threshold) { + (*offset_p).x = MAX_OFFSET; + (*offset_p).y = MAX_OFFSET; + m_hasEmptyVector = true; + } else { + (*offset_p).x = 0; + (*offset_p).y = 0; + } + } + } +} + +void Iwa_TangentFlowFx::computeInitialFlow(double* source_buf, + double2* flow_buf, + double* grad_mag_buf, + const TDimension dim, + double mag_threshold) { + // empty regions will be filled by using Fast Sweeping Method + // allocate offset buffer + int2* offset_buf; + TRasterGR8P offset_buf_ras(dim.lx * dim.ly * sizeof(int2), 1); + offset_buf_ras->lock(); + offset_buf = (int2*)offset_buf_ras->getRawData(); + + int activeThreadCount = QThreadPool::globalInstance()->activeThreadCount(); + // use half of the available threads + int threadAmount = std::max(1, activeThreadCount / 2); + QList threadList; + + int tmpStart = 0; + for (int t = 0; t < threadAmount; t++) { + int tmpEnd = + (int)std::round((float)(dim.ly * (t + 1)) / (float)threadAmount); + + SobelFilterWorker* worker = + new SobelFilterWorker(source_buf, flow_buf, grad_mag_buf, offset_buf, + mag_threshold, dim, tmpStart, tmpEnd); + worker->start(); + threadList.append(worker); + tmpStart = tmpEnd; + } + + bool hasEmptyVector = false; + for (auto worker : threadList) { + worker->wait(); + hasEmptyVector = hasEmptyVector | worker->hasEmptyVector(); + delete worker; + } + + // return if there is no empty region + if (!hasEmptyVector) { + offset_buf_ras->unlock(); + return; + } + + auto getFlow = [&](int2 xy) { return &flow_buf[xy.y * dim.lx + xy.x]; }; + auto getOffset = [&](int2 xy) { return &offset_buf[xy.y * dim.lx + xy.x]; }; + auto getGradMag = [&](int2 xy) { + return &grad_mag_buf[xy.y * dim.lx + xy.x]; + }; + + // update vector field by using Fast Sweeping Method + double2* neighbor_flow[2]; + int2* neighbor_offset[2]; + double* neighbor_gradMag[2]; + double2* flow_p; + int2* offset_p; + double* gradMag_p; + int2 offset_incr[2]; + // positive/negative in y axis + for (int ny = -1; ny <= 1; ny += 2) { + offset_incr[0] = {0, ny}; + int y_start = (ny == 1) ? 1 : dim.ly - 2; + int y_end = (ny == 1) ? dim.ly : 0; + + // positive/negative in x axis + for (int nx = -1; nx <= 1; nx += 2) { + offset_incr[1] = {nx, 0}; + int x_start = (nx == 1) ? 1 : dim.lx - 2; + int x_end = (nx == 1) ? dim.lx : 0; + + for (int y = y_start; y != y_end; y += ny) { + int2 startPos(x_start, y); + for (int n = 0; n < 2; n++) { + neighbor_flow[n] = getFlow(startPos - offset_incr[n]); + neighbor_offset[n] = getOffset(startPos - offset_incr[n]); + neighbor_gradMag[n] = getGradMag(startPos - offset_incr[n]); + } + flow_p = getFlow(startPos); + offset_p = getOffset(startPos); + gradMag_p = getGradMag(startPos); + + for (int x = x_start; x != x_end; x += nx, neighbor_flow[0] += nx, + neighbor_flow[1] += nx, flow_p += nx, neighbor_offset[0] += nx, + neighbor_offset[1] += nx, offset_p += nx, + neighbor_gradMag[0] += nx, neighbor_gradMag[1] += nx, + gradMag_p += nx) { + // continue if this pixel is already filled + if ((*offset_p).x == 0 && (*offset_p).y == 0) continue; + // for each neighbor pixel + for (int n = 0; n < 2; n++) { + // continue if the neighbor is still empty + if ((*neighbor_offset[n]).x == MAX_OFFSET || + (*neighbor_offset[n]).y == MAX_OFFSET) + continue; + int2 tmpOffset = (*neighbor_offset[n]) + offset_incr[n]; + if (tmpOffset.len2() < (*offset_p).len2()) { + (*offset_p) = tmpOffset; + (*flow_p) = (*neighbor_flow[n]); + (*gradMag_p) = (*neighbor_gradMag[n]); + } + } + } + } + } + } + + offset_buf_ras->unlock(); +} + +//------------------------------------------------------------ + +Iwa_TangentFlowFx::Iwa_TangentFlowFx() + : m_iteration(4) + , m_kernelRadius(2.5) + , m_threshold(0.15) + , m_alignDirection(false) + , m_pivotAngle(45.0) { + addInputPort("Source", m_source); + + bindParam(this, "iteration", m_iteration); + bindParam(this, "kernelRadius", m_kernelRadius); + bindParam(this, "threshold", m_threshold); + bindParam(this, "alignDirection", m_alignDirection); + bindParam(this, "pivotAngle", m_pivotAngle); + + m_iteration->setValueRange(0, 10); + m_kernelRadius->setMeasureName("fxLength"); + m_kernelRadius->setValueRange(0.5, 10); + m_threshold->setValueRange(0.0, 1.0); + + m_pivotAngle->setValueRange(-180.0, 180.0); +} + +//------------------------------------------------------------ + +void TangentFlowWorker::run() { + // flow to be computed + double2* flow_new_p = &m_flow_new_buf[m_yFrom * m_dim.lx]; + // current flow + double2* flow_cur_p = &m_flow_cur_buf[m_yFrom * m_dim.lx]; + // Gradient Magnitude of the current pos + double* grad_mag_p = &m_grad_mag_buf[m_yFrom * m_dim.lx]; + int kr2 = m_kernelRadius * m_kernelRadius; + + // loop for Y + for (int y = m_yFrom; y < m_yTo; y++) { + // loop for X + for (int x = 0; x < m_dim.lx; + x++, flow_new_p++, grad_mag_p++, flow_cur_p++) { + // accumulation vector + double2 flow_new_accum; + + // loop for kernel y + for (int ky = -m_kernelRadius; ky <= m_kernelRadius; ky++) { + int yPos = y + ky; + // boundary condition + if (yPos < 0) continue; + if (yPos >= m_dim.ly) break; + + // loop for kernel x + for (int kx = -m_kernelRadius; kx <= m_kernelRadius; kx++) { + int xPos = x + kx; + // boundary condition + if (xPos < 0) continue; + if (xPos >= m_dim.lx) break; + + // Eq.(2) continue if the sample pos is outside of the kernel + if (kx * kx + ky * ky > kr2) continue; + double2 flow_k = m_flow_cur_buf[yPos * m_dim.lx + xPos]; + if (flow_k.x == 0.0 && flow_k.y == 0.0) continue; + + // Eq.(3) calculate the magnitude weight function (wm) + double grad_mag_k = m_grad_mag_buf[yPos * m_dim.lx + xPos]; + double w_m = (1.0 + std::tanh(grad_mag_k - (*grad_mag_p))) / 2.0; + if (w_m == 0.0) continue; + + // Eq.(4) calculate the direction weight function (wd) + double flowDot = dotProduct((*flow_cur_p), flow_k); + double w_d = std::abs(flowDot); + + // Eq.(5) calculate phi + double phi = (flowDot > 0.) ? 1.0 : -1.0; + + // accumulate vector + flow_new_accum.x += phi * w_m * w_d * flow_k.x; + flow_new_accum.y += phi * w_m * w_d * flow_k.y; + } + } + + // normalize vector + double len = std::sqrt(flow_new_accum.x * flow_new_accum.x + + flow_new_accum.y * flow_new_accum.y); + if (len != 0.0) { + flow_new_accum.x /= len; + flow_new_accum.y /= len; + } + + // update vector + (*flow_new_p).x = flow_new_accum.x; + (*flow_new_p).y = flow_new_accum.y; + } + } +} + +void Iwa_TangentFlowFx::doCompute(TTile& tile, double frame, + const TRenderSettings& settings) { + if (!m_source.isConnected()) { + tile.getRaster()->clear(); + return; + } + + TDimension dim = tile.getRaster()->getSize(); + + // std::cout << "Iwa_TangentFlowFx dim = (" << dim.lx << ", " << dim.ly << + // ")"; + + double fac = sqrt(fabs(settings.m_affine.det())); + int kernelRadius = std::round(fac * m_kernelRadius->getValue(frame)); + if (kernelRadius == 0) kernelRadius = 1; + int iterationCount = m_iteration->getValue(); + + double mag_threshold = m_threshold->getValue(frame) / fac; + + TTile sourceTile; + m_source->allocateAndCompute(sourceTile, tile.m_pos, dim, tile.getRaster(), + frame, settings); + + // allocate source image buffer + double* source_buf; + TRasterGR8P source_buf_ras(dim.lx * dim.ly * sizeof(double), 1); + source_buf_ras->lock(); + source_buf = (double*)source_buf_ras->getRawData(); + + // obtain source tile brightness, normalizing 0 to 1 + TRaster32P ras32 = tile.getRaster(); + TRaster64P ras64 = tile.getRaster(); + if (ras32) + setSourceTileToBuffer(sourceTile.getRaster(), + source_buf); + else if (ras64) + setSourceTileToBuffer(sourceTile.getRaster(), + source_buf); + + // allocate flow buffers (for both current and new) + double2 *flow_cur_buf, *flow_new_buf; + TRasterGR8P flow_cur_buf_ras(dim.lx * dim.ly * sizeof(double2), 1); + TRasterGR8P flow_new_buf_ras(dim.lx * dim.ly * sizeof(double2), 1); + flow_cur_buf_ras->lock(); + flow_new_buf_ras->lock(); + flow_cur_buf = (double2*)flow_cur_buf_ras->getRawData(); + flow_new_buf = (double2*)flow_new_buf_ras->getRawData(); + + // allocate Gradient Magnitude image buffer + double* grad_mag_buf; + TRasterGR8P grad_mag_buf_ras(dim.lx * dim.ly * sizeof(double), 1); + grad_mag_buf_ras->lock(); + grad_mag_buf = (double*)grad_mag_buf_ras->getRawData(); + + // compute the initial vector field. + // apply sobel filter, rotate by 90 degrees and normalize. + // also obtaining gradient magnitude (length of the vector length) + // empty regions will be filled by using Fast Sweeping Method + computeInitialFlow(source_buf, flow_cur_buf, grad_mag_buf, dim, + mag_threshold); + + source_buf_ras->unlock(); + + int activeThreadCount = QThreadPool::globalInstance()->activeThreadCount(); + // use half of the available threads + int threadAmount = std::max(1, activeThreadCount / 2); + + // start iteration + for (int i = 0; i < iterationCount; i++) { + QList threadList; + int tmpStart = 0; + for (int t = 0; t < threadAmount; t++) { + int tmpEnd = + (int)std::round((float)(dim.ly * (t + 1)) / (float)threadAmount); + + TangentFlowWorker* worker = + new TangentFlowWorker(flow_cur_buf, flow_new_buf, grad_mag_buf, dim, + kernelRadius, tmpStart, tmpEnd); + worker->start(); + threadList.append(worker); + tmpStart = tmpEnd; + } + + for (auto worker : threadList) { + worker->wait(); + delete worker; + } + + // swap buffer pointers + double2* tmp = flow_cur_buf; + flow_cur_buf = flow_new_buf; + flow_new_buf = tmp; + } + + flow_new_buf_ras->unlock(); + + // 基準の角度に向きを合わせる + if (m_alignDirection->getValue()) { + double pivotAngle = + m_pivotAngle->getValue(frame) * M_PI_180; // convert to radian + double2 pivotVec = {std::cos(pivotAngle), std::sin(pivotAngle)}; + alignFlowDirection(flow_cur_buf, dim, pivotVec); + } + + // render vector field in red & green channels + if (ras32) + setOutputRaster(flow_cur_buf, grad_mag_buf, ras32); + else if (ras64) + setOutputRaster(flow_cur_buf, grad_mag_buf, ras64); + + flow_cur_buf_ras->unlock(); + grad_mag_buf_ras->unlock(); +} + +//------------------------------------------------------------ + +bool Iwa_TangentFlowFx::doGetBBox(double frame, TRectD& bBox, + const TRenderSettings& info) { + if (m_source.isConnected()) { + bBox = TConsts::infiniteRectD; + return true; + // return m_source->doGetBBox(frame, bBox, info); + } + return false; +} + +//------------------------------------------------------------ + +bool Iwa_TangentFlowFx::canHandle(const TRenderSettings& info, double frame) { + return false; +} + +FX_PLUGIN_IDENTIFIER(Iwa_TangentFlowFx, "iwa_TangentFlowFx") diff --git a/toonz/sources/stdfx/iwa_tangentflowfx.h b/toonz/sources/stdfx/iwa_tangentflowfx.h new file mode 100644 index 00000000..e191f4dc --- /dev/null +++ b/toonz/sources/stdfx/iwa_tangentflowfx.h @@ -0,0 +1,137 @@ +#pragma once + +/*-------------------------------- +Iwa_TangentFlowFx +Computing a smooth, feature-preserving local edge flow (edge tangent flow, ETF) +Implementation of "Coherent Line Drawing" by H.Kang et al, Proc. NPAR 2007. +----------------------------------*/ + +#ifndef IWA_TANGENT_FLOW_FX_H +#define IWA_TANGENT_FLOW_FX_H + +#include "stdfx.h" +#include "tfxparam.h" + +#include + +struct double2 { + double x, y; + double2(double _x = 0., double _y = 0.) { + x = _x; + y = _y; + } +}; +struct int2 { + int x, y; + + int2 operator+(const int2& other) const { + return int2{x + other.x, y + other.y}; + } + int2 operator-(const int2& other) const { + return int2{x - other.x, y - other.y}; + } + int len2() const { return x * x + y * y; } + int2(int _x = 0, int _y = 0) { + x = _x; + y = _y; + } +}; + +class SobelFilterWorker : public QThread { + double* m_source_buf; + double2* m_flow_buf; + double* m_grad_mag_buf; + int2* m_offset_buf; + double m_mag_threshold; + TDimension m_dim; + int m_yFrom, m_yTo; + + bool m_hasEmptyVector = false; + +public: + SobelFilterWorker(double* source_buf, double2* flow_buf, double* grad_mag_buf, + int2* offset_buf, double mag_threshold, TDimension dim, + int yFrom, int yTo) + : m_source_buf(source_buf) + , m_flow_buf(flow_buf) + , m_grad_mag_buf(grad_mag_buf) + , m_offset_buf(offset_buf) + , m_mag_threshold(mag_threshold) + , m_dim(dim) + , m_yFrom(yFrom) + , m_yTo(yTo) {} + + void run(); + + bool hasEmptyVector() { return m_hasEmptyVector; } +}; + +class TangentFlowWorker : public QThread { + double2* m_flow_cur_buf; + double2* m_flow_new_buf; + double* m_grad_mag_buf; + TDimension m_dim; + int m_kernelRadius; + int m_yFrom, m_yTo; + +public: + TangentFlowWorker(double2* flow_cur_buf, double2* flow_new_buf, + double* grad_mag_buf, TDimension dim, int kernelRadius, + int yFrom, int yTo) + : m_flow_cur_buf(flow_cur_buf) + , m_flow_new_buf(flow_new_buf) + , m_grad_mag_buf(grad_mag_buf) + , m_dim(dim) + , m_kernelRadius(kernelRadius) + , m_yFrom(yFrom) + , m_yTo(yTo) {} + + void run(); +}; + +class Iwa_TangentFlowFx final : public TStandardRasterFx { + FX_PLUGIN_DECLARATION(Iwa_TangentFlowFx) + +protected: + TRasterFxPort m_source; + + TIntParamP m_iteration; + TDoubleParamP m_kernelRadius; + + TDoubleParamP m_threshold; + + TBoolParamP m_alignDirection; + TDoubleParamP m_pivotAngle; + + // obtain source tile brightness, normalizing 0 to 1 + template + void setSourceTileToBuffer(const RASTER srcRas, double* buf); + + // render vector field in red & green channels + template + void setOutputRaster(double2* buf, double* grad_buf, const RASTER dstRas); + + // compute the initial vector field. + // apply sobel filter, rotate by 90 degrees and normalize. + // also obtaining gradient magnitude (length of the vector length) + // empty regions will be filled by using Fast Sweeping Method + void computeInitialFlow(double* source_buf, double2* flow_buf, + double* grad_mag_buf, const TDimension dim, + double mag_threshold); + // 基準の角度に向きを合わせる + void alignFlowDirection(double2* flow_buf, const TDimension dim, + const double2& pivotVec); + +public: + Iwa_TangentFlowFx(); + + void doCompute(TTile& tile, double frame, + const TRenderSettings& settings) override; + + bool doGetBBox(double frame, TRectD& bBox, + const TRenderSettings& info) override; + + bool canHandle(const TRenderSettings& info, double frame) override; +}; + +#endif diff --git a/toonz/sources/stdfx/iwa_tilefx.cpp b/toonz/sources/stdfx/iwa_tilefx.cpp index 5daa1cca..1babe4cc 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/iwa_timecodefx.cpp b/toonz/sources/stdfx/iwa_timecodefx.cpp index 700d6c3c..e0c89fdc 100644 --- a/toonz/sources/stdfx/iwa_timecodefx.cpp +++ b/toonz/sources/stdfx/iwa_timecodefx.cpp @@ -60,7 +60,11 @@ void Iwa_TimeCodeFx::doCompute(TTile &tile, double frame, font.setWeight(QFont::Normal); QFontMetrics fm(font); QString timeCodeStr = getTimeCodeStr(frame, ri); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int width = fm.horizontalAdvance(timeCodeStr); +#else int width = fm.width(timeCodeStr); +#endif int height = fm.height(); QImage img(width, height, QImage::Format_ARGB32); diff --git a/toonz/sources/stdfx/kaleido.cpp b/toonz/sources/stdfx/kaleido.cpp index 10646fb5..ffbb974d 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 f978700e..7d3f6891 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/motionawarebasefx.h b/toonz/sources/stdfx/motionawarebasefx.h index 9c2d994e..ed24b16f 100644 --- a/toonz/sources/stdfx/motionawarebasefx.h +++ b/toonz/sources/stdfx/motionawarebasefx.h @@ -54,4 +54,35 @@ public: TIntParamP getMotionObjectIndex() { return m_motionObjectIndex; } }; +// flow motion blurで使う + +class MotionAwareAffineFx : public TStandardZeraryFx { +protected: + TDoubleParamP m_shutterLength; // 前後のシャッター解放時間 + + TIntEnumParamP m_motionObjectType; + TIntParamP m_motionObjectIndex; + +public: + MotionAwareAffineFx() + : m_shutterLength(0.1) + , m_motionObjectType(new TIntEnumParam(OBJTYPE_OWN, "Own Motion")) + , m_motionObjectIndex(1) { + m_shutterLength->setValueRange(0.01, 1.0); + m_motionObjectType->addItem(OBJTYPE_COLUMN, "Column"); + m_motionObjectType->addItem(OBJTYPE_PEGBAR, "Pegbar"); + m_motionObjectType->addItem(OBJTYPE_TABLE, "Table"); + m_motionObjectType->addItem(OBJTYPE_CAMERA, "Camera"); + + getAttributes()->setIsSpeedAware(true); + } + + /*-- 軌跡情報を得るのに必要なパラメータを取得させる --*/ + TDoubleParamP getShutterLength() { return m_shutterLength; } + MotionObjectType getMotionObjectType() { + return (MotionObjectType)m_motionObjectType->getValue(); + } + TIntParamP getMotionObjectIndex() { return m_motionObjectIndex; } +}; + #endif diff --git a/toonz/sources/stdfx/motionblurfx.cpp b/toonz/sources/stdfx/motionblurfx.cpp index 613b1496..957fdc35 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 d82650bb..81740eb4 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.cpp b/toonz/sources/stdfx/particles.cpp index d7ef4925..046411d1 100644 --- a/toonz/sources/stdfx/particles.cpp +++ b/toonz/sources/stdfx/particles.cpp @@ -174,10 +174,12 @@ Particle::Particle(int g_lifetime, int seed, std::map porttiles, trail = (int)(values.trail_val.first + (ranges.trail_range) * random.getFloat()); - vx = random_speed * sin(random_s_a_range); - vy = -random_speed * cos(random_s_a_range); - oldx = 0; - oldy = 0; + vx = random_speed * sin(random_s_a_range); + vy = -random_speed * cos(random_s_a_range); + for (int i = 0; i < 3; i++) { + oldx[i] = 0.; + oldy[i] = 0.; + } mass = values.mass_val.first + (ranges.mass_range) * random.getFloat(); if (values.scale_ctrl_val && (porttiles.find(values.scale_ctrl_val) != porttiles.end())) { @@ -670,8 +672,13 @@ void Particle::move(std::map porttiles, if (values.scalestep_ctrl_val) scalestepreference = imagereferences[values.scalestep_ctrl_val]; lifetime--; - oldx = x; - oldy = y; + // slide the old positions + for (int i = 2; i >= 1; i--) { + oldx[i] = oldx[i - 1]; + oldy[i] = oldy[i - 1]; + } + oldx[0] = x; + oldy[0] = y; // time=genlifetime-lifetime-1; // if(time<0) time=0; if (values.gravity_ctrl_val && diff --git a/toonz/sources/stdfx/particles.h b/toonz/sources/stdfx/particles.h index da353f44..ba9834c4 100644 --- a/toonz/sources/stdfx/particles.h +++ b/toonz/sources/stdfx/particles.h @@ -56,8 +56,6 @@ struct particles_values { bool scale_ctrl_all_val; DoublePair rot_val; int rot_ctrl_val; - DoublePair trail_val; - double trailstep_val; int rotswingmode_val; double rotspeed_val; DoublePair rotsca_val; @@ -66,7 +64,8 @@ struct particles_values { DoublePair opacity_val; int opacity_ctrl_val; DoublePair trailopacity_val; - double mblur_val; + DoublePair trail_val; + double trailstep_val; DoublePair scalestep_val; int scalestep_ctrl_val; double fadein_val; @@ -93,6 +92,8 @@ struct particles_values { bool reset_random_for_every_frame_val; bool pick_color_for_every_frame_val; bool perspective_distribution_val; + bool motion_blur_val; + double motion_blur_gamma_adjust_val; }; //------------------------------------------------------------------------------ @@ -133,8 +134,8 @@ class Particle { public: double x; double y; - double oldx; - double oldy; + double oldx[3]; + double oldy[3]; double vx; /*sono le velox iniziali*/ double vy; /*sono le velox iniziali*/ double mass; diff --git a/toonz/sources/stdfx/particlesengine.cpp b/toonz/sources/stdfx/particlesengine.cpp index 261529e9..f4fc5a8b 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" @@ -27,6 +27,9 @@ #include +#include +#include + /*-----------------------------------------------------------------*/ Particles_Engine::Particles_Engine(ParticlesFx *parent, double frame) @@ -135,6 +138,9 @@ void Particles_Engine::fill_value_struct(struct particles_values &myvalues, m_parent->pick_color_for_every_frame_val->getValue(); myvalues.perspective_distribution_val = m_parent->perspective_distribution_val->getValue(); + myvalues.motion_blur_val = m_parent->motion_blur_val->getValue(); + myvalues.motion_blur_gamma_adjust_val = + m_parent->motion_blur_gamma_adjust_val->getValue(frame); } /*-----------------------------------------------------------------*/ @@ -411,7 +417,6 @@ void Particles_Engine::normalize_values(struct particles_values &values, (values.opacity_val.second) = (values.opacity_val.second) * 0.01; (values.trailopacity_val.first) = (values.trailopacity_val.first) * 0.01; (values.trailopacity_val.second) = (values.trailopacity_val.second) * 0.01; - (values.mblur_val) = (values.mblur_val) * 0.01; (values.friction_val) = -(values.friction_val) * 0.01; (values.windangle_val) = (values.windangle_val) * M_PI_180; (values.g_angle_val) = (values.g_angle_val + 180) * M_PI_180; @@ -506,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; @@ -768,10 +774,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())); @@ -856,6 +863,14 @@ void Particles_Engine::do_render( // Particles parameters M = ri.m_affine * M * TScale(1.0 / partScale); + // render with motion blur + if (values.motion_blur_val) { + if (do_render_motion_blur(part, tile, tileRas, rfinalpart, M, bbox, + values.trailopacity_val, + values.motion_blur_gamma_adjust_val, ri)) + return; + } + // Then, retrieve the particle position in current reference. TPointD pos(part->x, part->y); pos = ri.m_affine * pos; @@ -875,6 +890,378 @@ 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_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); + + TRect partBBox = convert(partBBoxD); + + TRaster32P partRas(partBBox.getSize()); + TRop::over( + partRas, rfinalpart, + TTranslation(-partBBoxD.getP00()) * M * TTranslation(bbox.getP00())); + + // create trace vertices list + // render straight trace in the first 4 frames + if (part->genlifetime - part->lifetime < 3) { + TPointD p0(part->x, part->y); + TPointD p1(part->oldx[0], part->oldy[0]); + p0 = ri.m_affine * p0; + p1 = ri.m_affine * p1; + points.append(TPointD(0, 0)); + points.append(p1 - p0); + } + // Cubic spline interpolation + else { + // divide into 8 lines (= 9 segments) + int pointAmount = 9; + TPointD keyP[4] = {TPointD(part->x, part->y), + TPointD(part->oldx[0], part->oldy[0]), + TPointD(part->oldx[1], part->oldy[1]), + TPointD(part->oldx[2], part->oldy[2])}; + for (int i = 0; i < 4; i++) { + keyP[i] = ri.m_affine * keyP[i]; + if (i > 0) { + keyP[i] -= keyP[0]; + } + } + keyP[0] = TPointD(0, 0); + + double u[4]; + u[0] = 0.; + u[1] = u[0] + norm(keyP[1] - keyP[0]); + u[2] = u[1] + norm(keyP[2] - keyP[1]); + u[3] = u[2] + norm(keyP[3] - keyP[2]); + + QMatrix4x4 mat(u[0] * u[0] * u[0], u[0] * u[0], u[0], 1., + u[1] * u[1] * u[1], u[1] * u[1], u[1], 1., + u[2] * u[2] * u[2], u[2] * u[2], u[2], 1., + u[3] * u[3] * u[3], u[3] * u[3], u[3], 1.); + + bool ok; + mat = mat.inverted(&ok); + if (!ok) return false; + + QPointF coeff[4]; + for (int i = 0; i < 4; i++) { + coeff[i] = mat(i, 0) * QPointF(keyP[0].x, keyP[0].y) + + mat(i, 1) * QPointF(keyP[1].x, keyP[1].y) + + mat(i, 2) * QPointF(keyP[2].x, keyP[2].y) + + mat(i, 3) * QPointF(keyP[3].x, keyP[3].y); + } + + for (int p = 0; p <= pointAmount; p++) { + double ratio = (double)p / (double)pointAmount; + double cur_u = u[0] * ratio + u[1] * (1 - ratio); + + points.append(TPointD( + cur_u * cur_u * cur_u * coeff[0].x() + cur_u * cur_u * coeff[1].x() + + cur_u * coeff[2].x() + coeff[3].x(), + cur_u * cur_u * cur_u * coeff[0].y() + cur_u * cur_u * coeff[1].y() + + cur_u * coeff[2].y() + coeff[3].y())); + } + } + + // compute lengths + for (int p = 0; p < points.size() - 1; p++) { + TPointD vec = points[p + 1] - points[p]; + lengths.append(norm(vec)); + } + + /* Get upper, lower, left and right margin */ + double minX = 0.0; + double maxX = 0.0; + double minY = 0.0; + double maxY = 0.0; + for (int p = 0; p < points.size(); p++) { + if (points.at(p).x > maxX) maxX = points.at(p).x; + if (points.at(p).x < minX) minX = points.at(p).x; + if (points.at(p).y > maxY) maxY = points.at(p).y; + if (points.at(p).y < minY) minY = points.at(p).y; + } + int marginLeft = (int)std::ceil(std::abs(minX)); + int marginRight = (int)std::ceil(std::abs(maxX)); + int marginTop = (int)std::ceil(std::abs(maxY)); + int marginBottom = (int)std::ceil(std::abs(minY)); + if (marginLeft == 0 && marginRight == 0 && marginTop == 0 && + marginBottom == 0) + return false; + + // end opacity is computed so that the trace will be connected smoothly in the + // trail + float end_opacity = + trailOpacity.second + + (trailOpacity.first - trailOpacity.second) / std::max(1, part->trail); + + // create the blur filter + TDimensionI filterDim(marginLeft + marginRight + 1, + marginTop + marginBottom + 1); + TRasterGR8P filter_ras(sizeof(float) * filterDim.lx, filterDim.ly); + filter_ras->lock(); + float *filter_p = (float *)filter_ras->getRawData(); + /* Variable for adding filter value*/ + float fil_val_sum = 0.0f; + /* The current filter position to be looped in the 'for' statement */ + float *current_fil_p = filter_p; + /* For each coordinate in the filter */ + for (int fily = 0; fily < filterDim.ly; fily++) { + for (int filx = 0; filx < filterDim.lx; filx++, current_fil_p++) { + /* Get filter coordinates */ + TPointD pos(static_cast(filx - marginLeft), + static_cast(fily - marginBottom)); + /* Value to be updated */ + float nearestDist2 = 100.0f; + int nearestIndex = -1; + float nearestFramePosRatio = 0.0f; + + /* Find the nearest point for each pair of sample points */ + for (int v = 0; v < points.count() - 1; v++) { + TPointD p0 = points[v]; + TPointD p1 = points[v + 1]; + + /* If it is not within the range, continue */ + if (pos.x < std::min(p0.x, p1.x) - 1.0f || + pos.x > std::max(p0.x, p1.x) + 1.0f || + pos.y < std::min(p0.y, p1.y) - 1.0f || + pos.y > std::max(p0.y, p1.y) + 1.0f) + continue; + + /* Since it is within the range, obtain the distance between the line + * segment and the point. */ + /* Calculate the inner product of 'p0'->sampling point and 'p0'->'p1' */ + TPointD vec_p0_sample(static_cast(pos.x - p0.x), + static_cast(pos.y - p0.y)); + TPointD 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 */ + float dist2; + float framePosRatio; + /* If it is before 'p0' */ + if (dot <= 0.0f) { + dist2 = vec_p0_sample.x * vec_p0_sample.x + + vec_p0_sample.y * vec_p0_sample.y; + framePosRatio = 0.0f; + } else { + /* Calculate the square of the length of the trajectory vector */ + float length2 = lengths[v] * lengths[v]; + + /* If it is between 'p0' and 'p1' + * If the trajectory at p is a point, + * 'length2' becomes 0, so it will never fall into this condition. + * So, there should not be worry of becoming ZeroDivide. */ + if (dot < length2) { + float p0_sample_dist2 = vec_p0_sample.x * vec_p0_sample.x + + vec_p0_sample.y * vec_p0_sample.y; + dist2 = p0_sample_dist2 - dot * dot / length2; + framePosRatio = dot / length2; + } + /* If it is before 'p1' */ + else { + TPointD vec_p1_sample = pos - p1; + dist2 = vec_p1_sample.x * vec_p1_sample.x + + vec_p1_sample.y * vec_p1_sample.y; + framePosRatio = 1.0f; + } + } + /* If the distance is farther than (√ 2 + 1) / 2, continue + * Because it is a comparison with dist2, the value is squared */ + if (dist2 > 1.4571f) continue; + + /* Update if distance is closer */ + if (dist2 < nearestDist2) { + nearestDist2 = dist2; + nearestIndex = v; + nearestFramePosRatio = framePosRatio; + } + } + + /* If neighborhood vector of the current pixel can not be found, + * set the filter value to 0 and return */ + if (nearestIndex == -1) { + *current_fil_p = 0.0f; + continue; + } + + /* Count how many subpixels (16 * 16) of the current pixel + * are in the range 0.5 from the neighborhood vector. + */ + int count = 0; + TPointD np0 = points[nearestIndex]; + TPointD np1 = points[nearestIndex + 1]; + for (int yy = 0; yy < 16; yy++) { + /* Y coordinate of the subpixel */ + float subPosY = pos.y + ((float)yy - 7.5f) / 16.0f; + for (int xx = 0; xx < 16; xx++) { + /* X coordinate of the subpixel */ + float subPosX = pos.x + ((float)xx - 7.5f) / 16.0f; + + TPointD vec_np0_sub = TPointD(subPosX, subPosY) - np0; + TPointD vec_np0_np1 = np1 - np0; + float dot = + vec_np0_sub.x * vec_np0_np1.x + vec_np0_sub.y * vec_np0_np1.y; + /* Calculate the square of the distance */ + float dist2; + /* If it is before 'p0' */ + if (dot <= 0.0f) + dist2 = + vec_np0_sub.x * vec_np0_sub.x + vec_np0_sub.y * vec_np0_sub.y; + else { + /* Compute the square of the length of the trajectory vector */ + float length2 = lengths[nearestIndex] * lengths[nearestIndex]; + /* If it is between 'p0' and 'p1' */ + if (dot < length2) { + float np0_sub_dist2 = + vec_np0_sub.x * vec_np0_sub.x + vec_np0_sub.y * vec_np0_sub.y; + dist2 = np0_sub_dist2 - dot * dot / length2; + } + /* if it is before 'p1' */ + else { + TPointD vec_np1_sub = TPointD(subPosX, subPosY) - np1; + dist2 = + vec_np1_sub.x * vec_np1_sub.x + vec_np1_sub.y * vec_np1_sub.y; + } + } + /* Increment count if squared distance is less than 0.25 */ + if (dist2 <= 0.25f) count++; + } + } + /* 'safeguard' - If the count is 0, set the field value to 0 and return. + */ + if (count == 0) { + *current_fil_p = 0.0f; + continue; + } + + /* Count is 256 at a maximum */ + float countRatio = (float)count / 256.0f; + + /* The brightness of the filter value is inversely proportional + * to the area of ​​the line of width 1 made by the vector. + * + * Since there are semicircular caps with radius 0.5 + * before and after the vector, it will never be 0-divide + * even if the length of vector is 0. + */ + + /* Area of the neighborhood vector when width = 1 */ + float vecMenseki = 0.25f * 3.14159265f + lengths[nearestIndex]; + + float opacity = 1.0f; + if (end_opacity < 1.0f) { + float ratio = ((float)nearestIndex + nearestFramePosRatio) / + (float)(points.size() - 1); + opacity = ratio + end_opacity * (1.f - ratio); + } + /* Store field value */ + *current_fil_p = opacity * countRatio / vecMenseki; + fil_val_sum += *current_fil_p; + } + } + + /* Normalization */ + current_fil_p = filter_p; + for (int f = 0; f < filterDim.lx * filterDim.ly; f++, current_fil_p++) { + *current_fil_p /= fil_val_sum; + } + + // apply the filter + TDimension blurredSize = + partRas->getSize() + + TDimension(marginLeft + marginRight, marginTop + marginBottom); + float4 *blurred_p; + TRasterGR8P blurred_ras(sizeof(float4) * blurredSize.lx, blurredSize.ly); + blurred_ras->lock(); + blurred_ras->clear(); + blurred_p = (float4 *)blurred_ras->getRawData(); + // for each texture pixels, + // distribute to all pixels in the filter + for (int ty = 0; ty < partRas->getLy(); ty++) { + TPixel32 *t_p = partRas->pixels(ty); + for (int tx = 0; tx < partRas->getLx(); tx++, t_p++) { + if (t_p->m == 0) continue; + + float4 tex = {(float)t_p->r / (float)(TPixel32::maxChannelValue), + (float)t_p->g / (float)(TPixel32::maxChannelValue), + (float)t_p->b / (float)(TPixel32::maxChannelValue), + (float)t_p->m / (float)(TPixel32::maxChannelValue)}; + if (gamma > 1.f) { + tex.x = std::pow(tex.x / tex.w, gamma) * tex.w; + tex.y = std::pow(tex.y / tex.w, gamma) * tex.w; + tex.z = std::pow(tex.z / tex.w, gamma) * tex.w; + } + + current_fil_p = filter_p; + for (int outy = ty; outy < ty + filterDim.ly; outy++) { + for (int outx = tx; outx < tx + filterDim.lx; outx++, current_fil_p++) { + if (*current_fil_p == 0.f) continue; + int outIndex = outy * blurredSize.lx + outx; + blurred_p[outIndex].x += *current_fil_p * tex.x; + blurred_p[outIndex].y += *current_fil_p * tex.y; + blurred_p[outIndex].z += *current_fil_p * tex.z; + blurred_p[outIndex].w += *current_fil_p * tex.w; + } + } + } + } + + filter_ras->unlock(); + + float4 *b_p = blurred_p; + TRaster32P blurred(blurredSize); + for (int by = 0; by < blurredSize.ly; by++) { + TPixel32 *b_ras_p = blurred->pixels(by); + for (int bx = 0; bx < blurredSize.lx; bx++, b_ras_p++, b_p++) { + if (gamma > 1.f && (*b_p).w > 0.f) { + (*b_p).x = std::pow((*b_p).x / (*b_p).w, 1.f / gamma) * (*b_p).w; + (*b_p).y = std::pow((*b_p).y / (*b_p).w, 1.f / gamma) * (*b_p).w; + (*b_p).z = std::pow((*b_p).z / (*b_p).w, 1.f / gamma) * (*b_p).w; + } + + b_ras_p->r = (TPixel32::Channel)std::round( + (*b_p).x * (float)(TPixel32::maxChannelValue)); + b_ras_p->g = (TPixel32::Channel)std::round( + (*b_p).y * (float)(TPixel32::maxChannelValue)); + b_ras_p->b = (TPixel32::Channel)std::round( + (*b_p).z * (float)(TPixel32::maxChannelValue)); + b_ras_p->m = (TPixel32::Channel)std::round( + (*b_p).w * (float)(TPixel32::maxChannelValue)); + } + } + + blurred_ras->unlock(); + + // composite particle + TPointD pos(part->x, part->y); + pos = ri.m_affine * pos; + M = TTranslation(pos - tile->m_pos) * + TTranslation(-TPointD(marginLeft, marginBottom) + partBBoxD.getP00()); + + if (TRaster32P myras32 = tileRas) + TRop::over(tileRas, blurred, M); + else if (TRaster64P myras64 = tileRas) + TRop::over(tileRas, blurred, M); + else + throw TException("ParticlesFx: unsupported Pixel Type"); + + return true; +} + +/*-----------------------------------------------------------------*/ + void Particles_Engine::fill_array(TTile *ctrl1, int ®ioncount, std::vector &myarray, std::vector &lista, diff --git a/toonz/sources/stdfx/particlesengine.h b/toonz/sources/stdfx/particlesengine.h index 645ae652..c5dab55d 100644 --- a/toonz/sources/stdfx/particlesengine.h +++ b/toonz/sources/stdfx/particlesengine.h @@ -7,6 +7,9 @@ #include "particles.h" #include "particlesfx.h" +struct float4 { + float x, y, z, w; +}; class Particle; class Particles_Engine { @@ -51,6 +54,12 @@ public: int curr_frame, std::map, double> &partScales); + bool do_render_motion_blur(Particle *part, TTile *tile, TRasterP tileRas, + TRaster32P rfinalpart, TAffine &M, + const TRectD &bbox, const DoublePair &trailOpacity, + 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); bool port_is_used_for_gradient(int i, struct particles_values &values); diff --git a/toonz/sources/stdfx/particlesfx.cpp b/toonz/sources/stdfx/particlesfx.cpp index 3cb9d983..a9b394dd 100644 --- a/toonz/sources/stdfx/particlesfx.cpp +++ b/toonz/sources/stdfx/particlesfx.cpp @@ -98,7 +98,9 @@ ParticlesFx::ParticlesFx() , foutfadecol_val(0.0) , source_gradation_val(false) , pick_color_for_every_frame_val(false) - , perspective_distribution_val(false) { + , perspective_distribution_val(false) + , motion_blur_val(false) + , motion_blur_gamma_adjust_val(0.) { addInputPort("Texture1", new TRasterFxPort, 0); addInputPort("Control1", new TRasterFxPort, 1); @@ -264,6 +266,9 @@ ParticlesFx::ParticlesFx() bindParam(this, "source_gradation", source_gradation_val); 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_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 27dfb053..93ded4b8 100644 --- a/toonz/sources/stdfx/particlesfx.h +++ b/toonz/sources/stdfx/particlesfx.h @@ -56,8 +56,6 @@ public: TBoolParamP scale_ctrl_all_val; TRangeParamP rot_val; TIntParamP rot_ctrl_val; - TRangeParamP trail_val; - TDoubleParamP trailstep_val; TIntEnumParamP rotswingmode_val; TDoubleParamP rotspeed_val; TRangeParamP rotsca_val; @@ -66,6 +64,8 @@ public: TRangeParamP opacity_val; TIntParamP opacity_ctrl_val; TRangeParamP trailopacity_val; + TRangeParamP trail_val; + TDoubleParamP trailstep_val; TRangeParamP scalestep_val; TIntParamP scalestep_ctrl_val; TDoubleParamP fadein_val; @@ -90,6 +90,8 @@ public: TBoolParamP source_gradation_val; TBoolParamP pick_color_for_every_frame_val; TBoolParamP perspective_distribution_val; + TBoolParamP motion_blur_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 f4c38fd6..7012f668 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 6dab55db..8fc09ea8 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 d5eea486..28cfd0ea 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 28b5f468..e0c66bf4 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 f0fdb964..0d658c50 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 843281b0..ee2658fd 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/shaderfx.cpp b/toonz/sources/stdfx/shaderfx.cpp index a3c19512..5954883d 100644 --- a/toonz/sources/stdfx/shaderfx.cpp +++ b/toonz/sources/stdfx/shaderfx.cpp @@ -35,7 +35,7 @@ #include // Diagnostics include -//#define DIAGNOSTICS +// #define DIAGNOSTICS #ifdef DIAGNOSTICS #include "diagnostics.h" #endif @@ -799,8 +799,7 @@ void ShaderFx::bindParameters(QOpenGLShaderProgram *program, double frame) { void ShaderFx::bindWorldTransform(QOpenGLShaderProgram *program, const TAffine &worldToDst) { -// Bind transformation affine -#if QT_VERSION >= 0x050500 + // Bind transformation affine float qwToD[9] = {static_cast(worldToDst.a11), static_cast(worldToDst.a12), static_cast(worldToDst.a13), @@ -810,34 +809,18 @@ void ShaderFx::bindWorldTransform(QOpenGLShaderProgram *program, 0.0f, 0.0f, 1.0f}; -#else - qreal qwToD[9] = {worldToDst.a11, - worldToDst.a12, - worldToDst.a13, - worldToDst.a21, - worldToDst.a22, - worldToDst.a23, - 0.0, - 0.0, - 1.0}; -#endif program->setUniformValue("worldToOutput", QMatrix3x3(qwToD)); const TAffine &dToW = worldToDst.inv(); -#if QT_VERSION >= 0x050500 - float qdToW[9] = {static_cast(dToW.a11), - static_cast(dToW.a12), - static_cast(dToW.a13), - static_cast(dToW.a21), - static_cast(dToW.a22), - static_cast(dToW.a23), - 0.0f, - 0.0f, - 1.0f}; -#else - qreal qdToW[9] = {dToW.a11, dToW.a12, dToW.a13, dToW.a21, dToW.a22, - dToW.a23, 0.0, 0.0, 1.0}; -#endif + float qdToW[9] = {static_cast(dToW.a11), + static_cast(dToW.a12), + static_cast(dToW.a13), + static_cast(dToW.a21), + static_cast(dToW.a22), + static_cast(dToW.a23), + 0.0f, + 0.0f, + 1.0f}; program->setUniformValue("outputToWorld", QMatrix3x3(qdToW)); } @@ -1095,7 +1078,6 @@ void ShaderFx::doCompute(TTile &tile, double frame, TAffine sToI(iToS.inv()); -#if QT_VERSION >= 0x050500 float qiToS[9] = {static_cast(iToS.a11), static_cast(iToS.a12), static_cast(iToS.a13), @@ -1114,13 +1096,6 @@ void ShaderFx::doCompute(TTile &tile, double frame, 0.0f, 0.0f, 1.0f}; -#else - qreal qiToS[9] = {iToS.a11, iToS.a12, iToS.a13, iToS.a21, iToS.a22, - iToS.a23, 0.0, 0.0, 1.0}; - - qreal qsToI[9] = {sToI.a11, sToI.a12, sToI.a13, sToI.a21, sToI.a22, - sToI.a23, 0.0, 0.0, 1.0}; -#endif inputs[p] = p, screenToInput[p] = QMatrix3x3(qsToI), inputToScreen[p] = QMatrix3x3(qiToS); } diff --git a/toonz/sources/stdfx/stdfx.cpp b/toonz/sources/stdfx/stdfx.cpp index 163d1da6..ae89e1fe 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 262fe794..dab3b45e 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 168ff0d0..f22e9f8f 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 755afda5..88cd6c52 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 f2754c48..de99cfdd 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/tconverter/tconverter.cpp b/toonz/sources/tconverter/tconverter.cpp index 4edbfb84..159b74a7 100644 --- a/toonz/sources/tconverter/tconverter.cpp +++ b/toonz/sources/tconverter/tconverter.cpp @@ -449,7 +449,7 @@ int main(int argc, char *argv[]) { exit(1); } } - if (ext != "pli") { + if (ext != "3gp" && ext != "pli") { convert(srcFilePath, dstFilePath, range, width, prop, resQuality); } else { msg = "Cannot convert to ." + ext + " format."; diff --git a/toonz/sources/tnzbase/tfxutil.cpp b/toonz/sources/tnzbase/tfxutil.cpp index 48b91995..8b78efd0 100644 --- a/toonz/sources/tnzbase/tfxutil.cpp +++ b/toonz/sources/tnzbase/tfxutil.cpp @@ -2,7 +2,7 @@ #include "tfxutil.h" #include "tbasefx.h" -//#include "timage_io.h" +// #include "timage_io.h" #include "trasterimage.h" #include "tdoubleparam.h" #include "tparamset.h" @@ -82,8 +82,8 @@ TFxP TFxUtil::makeDarken(const TFxP &dn, const TFxP &up) { assert(dn); assert(up); - /*-- TODO: FxId名変更となる可能性が高い。DarkenFx実装後に修正のこと。2016/2/3 - * shun_iwasawa --*/ + /*-- TODO: FxId name will likely be changed; correct after DarkenFx + * implementation. 2016/2/3 shun_iwasawa --*/ TFxP darkenFx = TFx::create("STD_inoDarkenFx"); assert(darkenFx); if (!darkenFx) return 0; diff --git a/toonz/sources/tnzbase/trasterfx.cpp b/toonz/sources/tnzbase/trasterfx.cpp index 0c3b55f5..f051c69e 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; @@ -833,9 +862,9 @@ void TRasterFx::compute(TTile &tile, double frame, tfloor(fracInfoTranslation.y)); TPointD newTilePos(intTilePos.x - intInfoTranslation.x, intTilePos.y - intInfoTranslation.y); - /*-- If the position of the input tile has a decimal value --*/ + /*-- If the position of the input tile had a fractional value --*/ if (tile.m_pos != newTilePos) { - /*-- Add misalignment to the affine matrix of Render Settings --*/ + /*-- Add the offset to the affine matrix in RenderSettings --*/ TRenderSettings newInfo(info); newInfo.m_affine.a13 = fracInfoTranslation.x - intInfoTranslation.x; newInfo.m_affine.a23 = fracInfoTranslation.y - intInfoTranslation.y; @@ -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,40 @@ void TRasterFx::compute(TTile &tile, double frame, TRectD interestingRect(tilePlacement * bbox); if (myIsEmpty(interestingRect)) return; + TDimension tileSize = tile.getRaster()->getSize(); + // The format of the incoming raster depends on whether the previous node + // supports floating point rendering or not. + 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); + } + } + // convert to 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 +955,29 @@ void TRasterFx::compute(TTile &tile, double frame, FxResourceBuilder rBuilder(alias, this, info, frame); rBuilder.build(interestingTile); + // convert to 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 +1063,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 +1136,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 +1170,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 +1191,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 +1215,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/Resources/brush_triangle_bottom_left.png b/toonz/sources/tnztools/Resources/brush_triangle_bottom_left.png new file mode 100644 index 00000000..45d269c4 Binary files /dev/null and b/toonz/sources/tnztools/Resources/brush_triangle_bottom_left.png differ diff --git a/toonz/sources/tnztools/Resources/brush_triangle_bottom_right.png b/toonz/sources/tnztools/Resources/brush_triangle_bottom_right.png new file mode 100644 index 00000000..9c3207dc Binary files /dev/null and b/toonz/sources/tnztools/Resources/brush_triangle_bottom_right.png differ diff --git a/toonz/sources/tnztools/Resources/brush_triangle_down.png b/toonz/sources/tnztools/Resources/brush_triangle_down.png new file mode 100644 index 00000000..0adc8369 Binary files /dev/null and b/toonz/sources/tnztools/Resources/brush_triangle_down.png differ diff --git a/toonz/sources/tnztools/Resources/brush_triangle_left.png b/toonz/sources/tnztools/Resources/brush_triangle_left.png new file mode 100644 index 00000000..c5b03b4f Binary files /dev/null and b/toonz/sources/tnztools/Resources/brush_triangle_left.png differ diff --git a/toonz/sources/tnztools/Resources/brush_triangle_right.png b/toonz/sources/tnztools/Resources/brush_triangle_right.png new file mode 100644 index 00000000..a78f7e1b Binary files /dev/null and b/toonz/sources/tnztools/Resources/brush_triangle_right.png differ diff --git a/toonz/sources/tnztools/Resources/brush_triangle_top_left.png b/toonz/sources/tnztools/Resources/brush_triangle_top_left.png new file mode 100644 index 00000000..9070cb05 Binary files /dev/null and b/toonz/sources/tnztools/Resources/brush_triangle_top_left.png differ diff --git a/toonz/sources/tnztools/Resources/brush_triangle_top_right.png b/toonz/sources/tnztools/Resources/brush_triangle_top_right.png new file mode 100644 index 00000000..c681d575 Binary files /dev/null and b/toonz/sources/tnztools/Resources/brush_triangle_top_right.png differ diff --git a/toonz/sources/tnztools/Resources/brush_triangle_up.png b/toonz/sources/tnztools/Resources/brush_triangle_up.png new file mode 100644 index 00000000..ecad7099 Binary files /dev/null and b/toonz/sources/tnztools/Resources/brush_triangle_up.png differ diff --git a/toonz/sources/tnztools/Resources/ex_freepick.png b/toonz/sources/tnztools/Resources/ex_freepick.png new file mode 100644 index 00000000..b47c3ca8 Binary files /dev/null and b/toonz/sources/tnztools/Resources/ex_freepick.png differ diff --git a/toonz/sources/tnztools/Resources/ex_freepick_left.png b/toonz/sources/tnztools/Resources/ex_freepick_left.png new file mode 100644 index 00000000..3aabc356 Binary files /dev/null and b/toonz/sources/tnztools/Resources/ex_freepick_left.png differ diff --git a/toonz/sources/tnztools/bluredbrush.cpp b/toonz/sources/tnztools/bluredbrush.cpp index 3e75e366..1425cc3e 100644 --- a/toonz/sources/tnztools/bluredbrush.cpp +++ b/toonz/sources/tnztools/bluredbrush.cpp @@ -51,15 +51,15 @@ void putOnRasterCM(const TRasterCM32P &out, const TRaster32P &in, int styleId, outPix->getTone()); continue; } - bool sameStyleId = styleId == outPix->getInk(); + bool sameStyleId = styleId == outPix->getInk(); // line with the same style : multiply tones // line with different style : pick darker tone int tone = sameStyleId ? outPix->getTone() * (255 - inPix->m) / 255 : std::min(255 - inPix->m, outPix->getTone()); - int ink = !sameStyleId && outPix->getTone() < 255 - inPix->m - ? outPix->getInk() - : styleId; - *outPix = TPixelCM32(ink, outPix->getPaint(), tone); + int ink = !sameStyleId && outPix->getTone() < 255 - inPix->m + ? outPix->getInk() + : styleId; + *outPix = TPixelCM32(ink, outPix->getPaint(), tone); } } } else if (drawOrderMode == 1) { // UnderAll @@ -80,15 +80,15 @@ void putOnRasterCM(const TRasterCM32P &out, const TRaster32P &in, int styleId, outPix->getTone()); continue; } - bool sameStyleId = styleId == outPix->getInk(); + bool sameStyleId = styleId == outPix->getInk(); // line with the same style : multiply tones // line with different style : pick darker tone int tone = sameStyleId ? outPix->getTone() * (255 - inPix->m) / 255 : std::min(255 - inPix->m, outPix->getTone()); - int ink = !sameStyleId && outPix->getTone() <= 255 - inPix->m - ? outPix->getInk() - : styleId; - *outPix = TPixelCM32(ink, outPix->getPaint(), tone); + int ink = !sameStyleId && outPix->getTone() <= 255 - inPix->m + ? outPix->getInk() + : styleId; + *outPix = TPixelCM32(ink, outPix->getPaint(), tone); } } } else { // PaletteOrder @@ -103,7 +103,7 @@ void putOnRasterCM(const TRasterCM32P &out, const TRaster32P &in, int styleId, outPix->getTone()); continue; } - bool sameStyleId = styleId == outPix->getInk(); + bool sameStyleId = styleId == outPix->getInk(); // line with the same style : multiply tones // line with different style : pick darker tone int tone = sameStyleId ? outPix->getTone() * (255 - inPix->m) / 255 @@ -144,9 +144,9 @@ void eraseFromRasterCM(const TRasterCM32P &out, const TRaster32P &in, !selective || (selective && selectedStyleId == outPix->getPaint()); int paint = eraseAreas && erasePaint ? 0 : outPix->getPaint(); int tone = inPix->m > 0 && eraseLine && eraseInk - ? std::max(outPix->getTone(), (int)inPix->m) - : outPix->getTone(); - *outPix = TPixelCM32(outPix->getInk(), paint, tone); + ? std::max(outPix->getTone(), (int)inPix->m) + : outPix->getTone(); + *outPix = TPixelCM32(outPix->getInk(), paint, tone); } } } @@ -166,7 +166,7 @@ TRasterP rasterFromQImage( (TPixelGR8 *)image.bits(), false); return TRasterP(); } -} +} // namespace //======================================================= // @@ -218,9 +218,9 @@ void BluredBrush::addPoint(const TThickPoint &p, double opacity) { painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); painter.setBrush(m_gradient); - painter.setMatrix( - QMatrix(scaleFactor, 0.0, 0.0, scaleFactor, p.x - radius, p.y - radius), - false); + painter.setTransform(QTransform(scaleFactor, 0.0, 0.0, scaleFactor, + p.x - radius, p.y - radius), + false); if (m_enableDynamicOpacity) painter.setOpacity(opacity); painter.drawEllipse(0, 0, m_size, m_size); painter.end(); @@ -249,9 +249,9 @@ void BluredBrush::addArc(const TThickPoint &pa, const TThickPoint &pb, double radius = point.thick * 0.5; double scaleFactor = radius / brushRadius; - painter.setMatrix(QMatrix(scaleFactor, 0.0, 0.0, scaleFactor, - point.x - radius, point.y - radius), - false); + painter.setTransform(QTransform(scaleFactor, 0.0, 0.0, scaleFactor, + point.x - radius, point.y - radius), + false); if (m_enableDynamicOpacity) { double opacity = opacityA + ((opacityC - opacityA) * t); if (fabs(opacity - m_oldOpacity) > 0.01) diff --git a/toonz/sources/tnztools/cursormanager.cpp b/toonz/sources/tnztools/cursormanager.cpp index 24531fab..da14dc84 100644 --- a/toonz/sources/tnztools/cursormanager.cpp +++ b/toonz/sources/tnztools/cursormanager.cpp @@ -28,6 +28,22 @@ const struct { {ToolCursor::PenCursor, "brush", 16, 15, false}, {ToolCursor::PenLargeCursor, "brush_large", 16, 15, false}, {ToolCursor::PenCrosshairCursor, "brush_crosshair", 16, 15, false}, + {ToolCursor::PenTriangleTopLeftCursor, "brush_triangle_top_left", + 16, 15, false}, + {ToolCursor::PenTriangleTopRightCursor, "brush_triangle_top_right", + 16, 15, false}, + {ToolCursor::PenTriangleBottomLeftCursor, "brush_triangle_bottom_left", + 16, 15, false}, + {ToolCursor::PenTriangleBottomRightCursor, "brush_triangle_bottom_right", + 16, 15, false}, + {ToolCursor::PenTriangleUpCursor, "brush_triangle_up", + 16, 15, false}, + {ToolCursor::PenTriangleDownCursor, "brush_triangle_down", + 16, 15, false}, + {ToolCursor::PenTriangleLeftCursor, "brush_triangle_left", + 16, 15, false}, + {ToolCursor::PenTriangleRightCursor, "brush_triangle_right", + 16, 15, false}, {ToolCursor::BenderCursor, "bender", 9, 7, true}, {ToolCursor::CutterCursor, "cutter", 6, 24, true}, // 12,20, ???}, {ToolCursor::EraserCursor, "eraser", 7, 21, true}, // 15,16, ???}, @@ -119,6 +135,7 @@ const struct { {ToolCursor::Ex_Precise, "ex_precise"}, {ToolCursor::Ex_Prev, "ex_prev"}, {ToolCursor::Ex_Next, "ex_next"}, + {ToolCursor::Ex_FreePick, "ex_freepick"}, {0, 0}}; }; // namespace @@ -174,6 +191,22 @@ public: cursorType = ToolCursor::PenLargeCursor; else if (brushType == "Crosshair") cursorType = ToolCursor::PenCrosshairCursor; + else if (brushType == "Triangle Top Left") + cursorType = ToolCursor::PenTriangleTopLeftCursor; + else if (brushType == "Triangle Top Right") + cursorType = ToolCursor::PenTriangleTopRightCursor; + else if (brushType == "Triangle Bottom Left") + cursorType = ToolCursor::PenTriangleBottomLeftCursor; + else if (brushType == "Triangle Bottom Right") + cursorType = ToolCursor::PenTriangleBottomRightCursor; + else if (brushType == "Triangle Up") + cursorType = ToolCursor::PenTriangleUpCursor; + else if (brushType == "Triangle Down") + cursorType = ToolCursor::PenTriangleDownCursor; + else if (brushType == "Triangle Left") + cursorType = ToolCursor::PenTriangleLeftCursor; + else if (brushType == "Triangle Right") + cursorType = ToolCursor::PenTriangleRightCursor; } bool useLeft = diff --git a/toonz/sources/tnztools/edittoolgadgets.cpp b/toonz/sources/tnztools/edittoolgadgets.cpp index b0e52a30..42757541 100644 --- a/toonz/sources/tnztools/edittoolgadgets.cpp +++ b/toonz/sources/tnztools/edittoolgadgets.cpp @@ -82,7 +82,8 @@ void drawSpinField(const TRectD geom, const TPointD center, int minId = (int)std::ceil(minDist / lineInterval); int maxId = (int)std::floor(maxDist / lineInterval); - glColor3d(0, 0, 1); + glColor3dv(FxGadget::m_selectedColor); + glEnableClientState(GL_VERTEX_ARRAY); glLineStipple(1, 0x00FF); glEnable(GL_LINE_STIPPLE); @@ -97,6 +98,11 @@ void drawSpinField(const TRectD geom, const TPointD center, for (int id = minId; id <= maxId; id++) { if (id == 0) continue; + if (id % 2 == 0) + glColor3dv(FxGadget::m_selectedColor); + else + glColor3d(0, 0, 1); + glPushMatrix(); glScaled((double)id, (double)id, 1.); // draw using vertex array @@ -2360,6 +2366,257 @@ void EllipseFxGadget::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { void EllipseFxGadget::leftButtonUp() { m_handle = None; } +//============================================================================= + +class VerticalPosFxGadget final : public FxGadget { + TPointD m_pos; + TDoubleParamP m_yParam; + TIntEnumParamP m_mode; + +public: + VerticalPosFxGadget(FxGadgetController *controller, + const TDoubleParamP ¶m, const TIntEnumParamP &mode) + : FxGadget(controller), m_yParam(param), m_mode(mode) { + addParam(m_yParam); + } + + void draw(bool picking) override; + + bool isVisible(); + void leftButtonDown(const TPointD &pos, const TMouseEvent &) override; + void leftButtonDrag(const TPointD &pos, const TMouseEvent &) override; +}; + +//--------------------------------------------------------------------------- +// Dirty resolution to hide gadget when selecting unrelated modes + +bool VerticalPosFxGadget::isVisible() { + if (!m_mode) return true; + // condition for Distance Level parameter of Iwa_FloorBumpFx + if (m_yParam->getName() == "distanceLevel" && m_mode->getValue() != 5) + return false; + return true; +} + +//--------------------------------------------------------------------------- + +void VerticalPosFxGadget::draw(bool picking) { + if (!isVisible()) return; + setPixelSize(); + if (isSelected()) + glColor3dv(m_selectedColor); + else + glColor3d(0, 0, 1); + glPushName(getId()); + double vPos = getValue(m_yParam); + double unit = getPixelSize(); + glPushMatrix(); + glTranslated(0, vPos, 0); + double r = unit * 3; + double d = unit * 300; + glBegin(GL_LINES); + glVertex2d(0, r); + glVertex2d(0, -r); + glVertex2d(-d, 0); + glVertex2d(d, 0); + glEnd(); + drawTooltip(TPointD(7, 7) * unit, getLabel()); + + glPopMatrix(); + glPopName(); +} + +//--------------------------------------------------------------------------- + +void VerticalPosFxGadget::leftButtonDown(const TPointD &pos, + const TMouseEvent &) {} + +//--------------------------------------------------------------------------- + +void VerticalPosFxGadget::leftButtonDrag(const TPointD &pos, + const TMouseEvent &) { + if (m_yParam) setValue(m_yParam, pos.y); +} + +//============================================================================= + +class ParallelogramFxGadget final : public FxGadget { + TPointParamP m_pcenter, m_phoriz, m_pvert; + VectorFxGadget *m_hVecGadget, *m_vVecGadget; + TPointD m_clickedPos; + TPointParamP m_pcurve; + + enum HANDLE { Body = 0, CurveAnchor, Rotation, None } m_handle = None; + +public: + ParallelogramFxGadget(FxGadgetController *controller, const TPointParamP &pc, + const TPointParamP &ph, const TPointParamP &pv) + : FxGadget(controller) + , m_pcenter(pc) + , m_phoriz(ph) + , m_pvert(pv) + , m_hVecGadget(new VectorFxGadget(controller, pc, ph)) + , m_vVecGadget(new VectorFxGadget(controller, pc, pv)) { + addParam(pc->getX()); + addParam(pc->getY()); + addParam(ph->getX()); + addParam(ph->getY()); + addParam(pv->getX()); + addParam(pv->getY()); + } + + ParallelogramFxGadget(FxGadgetController *controller, const TPointParamP &pc, + const TPointParamP &ph, const TPointParamP &pv, + const TPointParamP &pcurve) + : FxGadget(controller, 3) + , m_pcenter(pc) + , m_phoriz(ph) + , m_pvert(pv) + , m_pcurve(pcurve) + , m_hVecGadget(new VectorFxGadget(controller, pc, ph)) + , m_vVecGadget(new VectorFxGadget(controller, pc, pv)) { + addParam(pc->getX()); + addParam(pc->getY()); + addParam(ph->getX()); + addParam(ph->getY()); + addParam(pv->getX()); + addParam(pv->getY()); + } + + ~ParallelogramFxGadget() { + delete m_hVecGadget; + delete m_vVecGadget; + } + + void draw(bool picking) override { + auto setColorById = [&](int id) { + if (isSelected(id)) + glColor3dv(m_selectedColor); + else + glColor3d(0, 0, 1); + }; + + setPixelSize(); + setColorById(Body); + glPushName(getId() + Body); + + double pixelSize = getPixelSize(); + double c = pixelSize * 4; + TPointD pc = getValue(m_pcenter); + TPointD ph = getValue(m_phoriz); + TPointD pv = getValue(m_pvert); + + TPointD vec_h = ph - pc; + TPointD vec_v = pv - pc; + TPointD po = ph + vec_v; + TPointD unit_h = vec_h * (1.0 / sqrt(norm2(vec_h))); + TPointD unit_v = vec_v * (1.0 / sqrt(norm2(vec_v))); + + glLineStipple(1, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + tglDrawSegment(ph + unit_v * c, po); + tglDrawSegment(pv + unit_h * c, po); + glDisable(GL_LINE_STIPPLE); + glPopName(); + + if (m_pcurve.getPointer()) { + TPointD pcurve = getValue(m_pcurve); + TPointD ppivot = pc + (pcurve.x + 0.5) * vec_h + (pcurve.y + 0.5) * vec_v; + + setColorById(Body); + glPushName(getId() + Body); + glEnable(GL_LINE_STIPPLE); + if (pcurve == TPointD()) { + tglDrawSegment((pc + ph) * 0.5, vec_v + (pc + ph) * 0.5); + tglDrawSegment((pc + pv) * 0.5, vec_h + (pc + pv) * 0.5); + } else { + TPointD p[2][2] = {{pc + vec_h * 0.5, pc + vec_h * 0.5 + vec_v}, + {pc + vec_v * 0.5, pc + vec_v * 0.5 + vec_h}}; + for (int k = 0; k < 2; k++) { // + glBegin(GL_LINE_STRIP); + for (int i = 0; i <= 10; i++) { // + double t = (double)i * 0.1; + tglVertex((1.0 - t) * (1.0 - t) * p[k][0] + + 2.0 * (1.0 - t) * t * ppivot + t * t * p[k][1]); + } + glEnd(); + } + } + glDisable(GL_LINE_STIPPLE); + glPopName(); + + setColorById(CurveAnchor); + glPushName(getId() + CurveAnchor); + glPushMatrix(); + glTranslated(ppivot.x, ppivot.y, 0); + double r = pixelSize * 3; + tglDrawRect(-r, -r, r, r); + glPopMatrix(); + glPopName(); + } + + setColorById(Rotation); + glPushName(getId() + Rotation); + double a = pixelSize * 10, b = pixelSize * 3; + TPointD diagonal = normalize(po - pc); + TPointD v = rotate90(diagonal); + tglDrawSegment(po + v * a, po - v * a); + tglDrawSegment(po + diagonal * b + v * a, po + diagonal * b - v * a); + glPopName(); + + m_hVecGadget->draw(picking); + m_vVecGadget->draw(picking); + } + + void leftButtonDown(const TPointD &pos, const TMouseEvent &) override { + m_handle = (HANDLE)m_selected; + if (m_handle == None) return; + m_clickedPos = pos; + } + void leftButtonDrag(const TPointD &pos, const TMouseEvent &) override { + if (m_handle == None) return; + if (m_handle == Body) { + TPointD d = pos - m_clickedPos; + setValue(m_pcenter, getValue(m_pcenter) + d); + setValue(m_phoriz, getValue(m_phoriz) + d); + setValue(m_pvert, getValue(m_pvert) + d); + } else if (m_handle == CurveAnchor && m_pcurve.getPointer()) { + TPointD pc = getValue(m_pcenter); + TPointD ph = getValue(m_phoriz); + TPointD pv = getValue(m_pvert); + TPointD vec_h = ph - pc; + TPointD vec_v = pv - pc; + TAffine aff(vec_h.x, vec_v.x, pc.x, vec_h.y, vec_v.y, pc.y); + TPointD p_conv = aff.inv() * pos; + if (p_conv.x < 0.0) + p_conv.x = 0.0; + else if (p_conv.x > 1.0) + p_conv.x = 1.0; + if (p_conv.y < 0.0) + p_conv.y = 0.0; + else if (p_conv.y > 1.0) + p_conv.y = 1.0; + setValue(m_pcurve, p_conv - TPointD(0.5, 0.5)); + } else if (m_handle == Rotation) { + TPointD ph = getValue(m_phoriz); + TPointD pv = getValue(m_pvert); + TPointD pivot = (ph + pv) * 0.5; + TPointD before = m_clickedPos - pivot; + TPointD after = pos - pivot; + double angle = + std::atan2(after.y, after.x) - std::atan2(before.y, before.x); + TAffine aff = TTranslation(pivot) * TRotation(angle * M_180_PI) * + TTranslation(-pivot); + + setValue(m_pcenter, aff * getValue(m_pcenter)); + setValue(m_phoriz, aff * getValue(m_phoriz)); + setValue(m_pvert, aff * getValue(m_pvert)); + } + m_clickedPos = pos; + } + void leftButtonUp() override { m_handle = None; } +}; + //************************************************************************************* // FxGadgetController implementation //************************************************************************************* @@ -2593,6 +2850,28 @@ FxGadget *FxGadgetController::allocateGadget(const TParamUIConcept &uiConcept) { break; } + case TParamUIConcept::VERTICAL_POS: { + assert(uiConcept.m_params.size() >= 1 && uiConcept.m_params.size() <= 2); + TIntEnumParamP mode((uiConcept.m_params.size() >= 2) + ? (TIntEnumParamP)uiConcept.m_params[1] + : TIntEnumParamP()); + gadget = new VerticalPosFxGadget(this, uiConcept.m_params[0], mode); + break; + } + + case TParamUIConcept::PARALLELOGRAM: { + assert(uiConcept.m_params.size() == 3 || uiConcept.m_params.size() == 4); + if (uiConcept.m_params.size() == 3) { + gadget = new ParallelogramFxGadget(this, uiConcept.m_params[0], + uiConcept.m_params[1], + uiConcept.m_params[2]); + } else + gadget = new ParallelogramFxGadget( + this, uiConcept.m_params[0], uiConcept.m_params[1], + uiConcept.m_params[2], uiConcept.m_params[3]); + break; + } + default: break; } diff --git a/toonz/sources/tnztools/filltool.cpp b/toonz/sources/tnztools/filltool.cpp index 9d485145..0539e25a 100644 --- a/toonz/sources/tnztools/filltool.cpp +++ b/toonz/sources/tnztools/filltool.cpp @@ -63,6 +63,7 @@ using namespace ToolUtils; #define RECTFILL L"Rectangular" #define FREEHANDFILL L"Freehand" #define POLYLINEFILL L"Polyline" +#define FREEPICKFILL L"Freepick" TEnv::IntVar MinFillDepth("InknpaintMinFillDepth", 1); TEnv::IntVar MaxFillDepth("InknpaintMaxFillDepth", 10); @@ -1577,7 +1578,8 @@ void AreaFillTool::draw() { } else drawRect(m_selectingRect, color, 0xFFFF, true); } - } else if ((m_type == FREEHAND || m_type == POLYLINE) && m_frameRange) { + } else if ((m_type == FREEHAND || m_type == POLYLINE || m_type == FREEPICK) && + m_frameRange) { tglColor(color); for(int i = 0; i < m_firstStrokes.size(); i++) drawStrokeCenterline(*m_firstStrokes[i], 1); @@ -1587,7 +1589,7 @@ void AreaFillTool::draw() { glPushMatrix(); m_polyline.drawPolyline(m_mousePosition, color); glPopMatrix(); - } else if (m_type == FREEHAND && !m_track.isEmpty()) { + } else if ((m_type == FREEHAND || m_type == FREEPICK) && !m_track.isEmpty()) { tglColor(color); glPushMatrix(); m_track.drawAllFragments(); @@ -1595,6 +1597,36 @@ void AreaFillTool::draw() { } } +int AreaFillTool::pick(const TImageP &image, const TPointD &pos, + const int frame, int mode) { + TToonzImageP ti = image; + TVectorImageP vi = image; + if (!ti && !vi) return 0; + + TTool::Viewer *viewer = m_parent->getViewer(); + + StylePicker picker(viewer->viewerWidget(), image); + double scale2 = 1.0; + if (vi) { + TAffine aff = + viewer->getViewMatrix() * m_parent->getCurrentColumnMatrix(frame); + scale2 = aff.det(); + } + TPointD pickPos = pos; + // in case that the column is animated in scene-editing mode + if (frame > 0) { + TPointD dpiScale = viewer->getDpiScale(); + pickPos.x *= dpiScale.x; + pickPos.y *= dpiScale.y; + TPointD worldPos = m_parent->getCurrentColumnMatrix() * pickPos; + pickPos = m_parent->getCurrentColumnMatrix(frame).inv() * worldPos; + pickPos.x /= dpiScale.x; + pickPos.y /= dpiScale.y; + } + // thin stroke can be picked with 10 pixel range + return picker.pickStyleId(pickPos, 10.0, scale2, mode); +} + void AreaFillTool::resetMulti() { m_firstFrameSelected = false; m_firstRect.empty(); @@ -1606,7 +1638,7 @@ void AreaFillTool::resetMulti() { m_firstStrokes.clear(); } -void AreaFillTool::leftButtonDown(const TPointD &pos, const TMouseEvent &, +void AreaFillTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e, TImage *img) { TVectorImageP vi = TImageP(img); TToonzImageP ti = TToonzImageP(img); @@ -1621,6 +1653,16 @@ void AreaFillTool::leftButtonDown(const TPointD &pos, const TMouseEvent &, TPointD dpiScale = m_parent->getViewer()->getDpiScale(); SymmetryObject symmObj = symmetryTool->getSymmetryObject(); + if (m_type == FREEPICK) { + TTool::Application *app = TTool::getApplication(); + if (!app) return; + + int fllmode = e.isCtrlPressed() ? 2 : 0; // Line+Area : Area + int styleId = pick(img, pos, -1, fllmode); + if (!m_isLeftButtonPressed) m_bckStyleId = app->getCurrentLevelStyleIndex(); + app->setCurrentLevelStyleIndex(styleId); + } + m_selecting = true; if (m_type == RECT) { m_selectingRect.x0 = pos.x; @@ -1638,7 +1680,7 @@ void AreaFillTool::leftButtonDown(const TPointD &pos, const TMouseEvent &, TPointD(m_selectingRect.x1, m_selectingRect.y1)); m_mousePosition = pos; } - } else if (m_type == FREEHAND || m_type == POLYLINE) { + } else if (m_type == FREEHAND || m_type == POLYLINE || m_type == FREEPICK) { int col = TTool::getApplication()->getCurrentColumn()->getColumnIndex(); m_isPath = TTool::getApplication() ->getCurrentObject() @@ -1801,7 +1843,7 @@ void AreaFillTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { } m_parent->invalidate(); - } else if (m_type == FREEHAND) { + } else if (m_type == FREEHAND || m_type == FREEPICK) { if (!m_enabled || !m_active) return; double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize(); @@ -1948,7 +1990,7 @@ void AreaFillTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e, TTool *t = app->getCurrentTool()->getTool(); if (t) t->notifyImageChanged(); } - } else if (m_type == FREEHAND) { + } else if (m_type == FREEHAND || m_type == FREEPICK) { #if defined(MACOSX) // m_parent->m_viewer->enableRedraw(true); #endif @@ -2058,6 +2100,8 @@ void AreaFillTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e, } m_track.reset(); } + + if (m_type == FREEPICK) app->setCurrentLevelStyleIndex(m_bckStyleId); } void AreaFillTool::onImageChanged() { @@ -2075,7 +2119,7 @@ void AreaFillTool::onImageChanged() { // stato iniziale else { // cambio stato. m_firstFrameSelected = true; - if (m_type != FREEHAND && m_type != POLYLINE) { + if (m_type != FREEHAND && m_type != POLYLINE && m_type != FREEPICK) { assert(!m_selectingRect.isEmpty()); m_firstRect = m_selectingRect; } @@ -2279,6 +2323,7 @@ FillTool::FillTool(int targetType) m_fillType.addValue(RECTFILL); m_fillType.addValue(FREEHANDFILL); m_fillType.addValue(POLYLINEFILL); + m_fillType.addValue(FREEPICKFILL); m_prop.bind(m_colorType); m_colorType.addValue(LINES); @@ -2340,6 +2385,8 @@ int FillTool::getCursorId() const { ret = ret | ToolCursor::Ex_PolyLine; else if (m_fillType.getValue() == RECTFILL) ret = ret | ToolCursor::Ex_Rectangle; + if (m_fillType.getValue() == FREEPICKFILL) + ret = ret | ToolCursor::Ex_FreePick; if (ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg) ret = ret | ToolCursor::Ex_Negate; @@ -2356,6 +2403,7 @@ void FillTool::updateTranslation() { m_fillType.setItemUIName(RECTFILL, tr("Rectangular")); m_fillType.setItemUIName(FREEHANDFILL, tr("Freehand")); m_fillType.setItemUIName(POLYLINEFILL, tr("Polyline")); + m_fillType.setItemUIName(FREEPICKFILL, tr("Pick+Freehand")); m_selective.setQStringName(tr("Selective")); @@ -2728,6 +2776,8 @@ bool FillTool::onPropertyChanged(std::string propertyName) { type = AreaFillTool::FREEHAND; else if (m_fillType.getValue() == POLYLINEFILL) type = AreaFillTool::POLYLINE; + else if (m_fillType.getValue() == FREEPICKFILL) + type = AreaFillTool::FREEPICK; else assert(false); @@ -2951,6 +3001,8 @@ void FillTool::onActivate() { type = AreaFillTool::FREEHAND; else if (m_fillType.getValue() == POLYLINEFILL) type = AreaFillTool::POLYLINE; + else if (m_fillType.getValue() == FREEPICKFILL) + type = AreaFillTool::FREEPICK; else assert(false); diff --git a/toonz/sources/tnztools/filltool.h b/toonz/sources/tnztools/filltool.h index 00ef46f7..b529e315 100644 --- a/toonz/sources/tnztools/filltool.h +++ b/toonz/sources/tnztools/filltool.h @@ -30,7 +30,7 @@ namespace { class AreaFillTool { public: - enum Type { RECT, FREEHAND, POLYLINE }; + enum Type { RECT, FREEHAND, POLYLINE, FREEPICK }; private: bool m_frameRange; @@ -61,9 +61,12 @@ private: bool m_autopaintLines; bool m_fillOnlySavebox; + int m_bckStyleId; + public: AreaFillTool(TTool *Parent); void draw(); + int pick(const TImageP &image, const TPointD &pos, const int frame, int mode); void resetMulti(); void leftButtonDown(const TPointD &pos, const TMouseEvent &, TImage *img); void leftButtonDoubleClick(const TPointD &pos, const TMouseEvent &e, diff --git a/toonz/sources/tnztools/stylepicker.cpp b/toonz/sources/tnztools/stylepicker.cpp index f0638cdf..624c8a6f 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; - - UINT 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/tnztools/tnztools.qrc b/toonz/sources/tnztools/tnztools.qrc index 7370ccc4..5cdaeb24 100644 --- a/toonz/sources/tnztools/tnztools.qrc +++ b/toonz/sources/tnztools/tnztools.qrc @@ -1,5 +1,5 @@ - + Resources/brush.png Resources/bender.png Resources/cutter.png @@ -33,59 +33,69 @@ Resources/selection_add.png Resources/selection_convert.png Resources/selection_distort.png - Resources/move_ew.png - Resources/move_ns.png - Resources/disable.png - Resources/move_z.png - Resources/picker_style_line.png - Resources/picker_style_area.png - Resources/picker_style.png - Resources/scale_global.png - Resources/scale_hv.png - Resources/normaleraser.png - Resources/recteraser.png - Resources/picker_style_organize.png - Resources/picker_rgb.png - Resources/picker_rgb_white.png - Resources/pointing_hand.png - Resources/karasu.png - Resources/ruler_modify.png - Resources/ruler_new.png - Resources/ex_freehand.png - Resources/ex_freehand_left.png - Resources/ex_polyline.png - Resources/ex_polyline_left.png - Resources/ex_rectangle.png - Resources/ex_rectangle_left.png - Resources/ex_line.png - Resources/ex_line_left.png - Resources/ex_area.png - Resources/ex_area_left.png - Resources/ex_fill_no_autopaint.png - Resources/ex_fill_no_autopaint_left.png - Resources/edit_FX_notext.png - Resources/move_z_notext.png - Resources/scale_hv_notext.png - Resources/ex_FX.png - Resources/ex_FX_left.png - Resources/ex_hv.png - Resources/ex_hv_left.png - Resources/ex_rgb.png - Resources/ex_rgb_left.png - Resources/ex_style_area.png - Resources/ex_style_area_left.png - Resources/ex_style_line.png - Resources/ex_style_line_left.png - Resources/ex_z.png - Resources/ex_z_left.png - Resources/ex_precise.png - Resources/ex_precise_left.png - Resources/brush_large.png - Resources/brush_crosshair.png - Resources/tracker.png - Resources/ex_prev.png - Resources/ex_prev_left.png - Resources/ex_next.png - Resources/ex_next_left.png - + Resources/move_ew.png + Resources/move_ns.png + Resources/disable.png + Resources/move_z.png + Resources/picker_style_line.png + Resources/picker_style_area.png + Resources/picker_style.png + Resources/scale_global.png + Resources/scale_hv.png + Resources/normaleraser.png + Resources/recteraser.png + Resources/picker_style_organize.png + Resources/picker_rgb.png + Resources/picker_rgb_white.png + Resources/pointing_hand.png + Resources/karasu.png + Resources/ruler_modify.png + Resources/ruler_new.png + Resources/ex_freehand.png + Resources/ex_freehand_left.png + Resources/ex_polyline.png + Resources/ex_polyline_left.png + Resources/ex_rectangle.png + Resources/ex_rectangle_left.png + Resources/ex_line.png + Resources/ex_line_left.png + Resources/ex_area.png + Resources/ex_area_left.png + Resources/ex_fill_no_autopaint.png + Resources/ex_fill_no_autopaint_left.png + Resources/edit_FX_notext.png + Resources/move_z_notext.png + Resources/scale_hv_notext.png + Resources/ex_FX.png + Resources/ex_FX_left.png + Resources/ex_hv.png + Resources/ex_hv_left.png + Resources/ex_rgb.png + Resources/ex_rgb_left.png + Resources/ex_style_area.png + Resources/ex_style_area_left.png + Resources/ex_style_line.png + Resources/ex_style_line_left.png + Resources/ex_freepick.png + Resources/ex_freepick_left.png + Resources/ex_z.png + Resources/ex_z_left.png + Resources/ex_precise.png + Resources/ex_precise_left.png + Resources/brush_large.png + Resources/brush_crosshair.png + Resources/brush_triangle_top_left.png + Resources/brush_triangle_top_right.png + Resources/brush_triangle_bottom_left.png + Resources/brush_triangle_bottom_right.png + Resources/brush_triangle_up.png + Resources/brush_triangle_down.png + Resources/brush_triangle_left.png + Resources/brush_triangle_right.png + Resources/tracker.png + Resources/ex_prev.png + Resources/ex_prev_left.png + Resources/ex_next.png + Resources/ex_next_left.png + \ No newline at end of file diff --git a/toonz/sources/tnztools/tool.cpp b/toonz/sources/tnztools/tool.cpp index 1abe0116..a72e7e10 100644 --- a/toonz/sources/tnztools/tool.cpp +++ b/toonz/sources/tnztools/tool.cpp @@ -1108,12 +1108,7 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) { "The current frame is locked: any editing is forbidden.")); // Check level type write support - if (sl->getPath().getType() == - "psd" || // We don't have the API to write psd files - sl->getPath().getType() == "gif" || - sl->getPath().getType() == "mp4" || - sl->getPath().getType() == "webm" || - sl->getPath().getType() == "mov" || + if (sl->getPath().isUneditable() || sl->is16BitChannelLevel() || // Inherited by previous // implementation. // Could be fixed? diff --git a/toonz/sources/tnztools/tooloptions.cpp b/toonz/sources/tnztools/tooloptions.cpp index 69d21b39..9d551625 100644 --- a/toonz/sources/tnztools/tooloptions.cpp +++ b/toonz/sources/tnztools/tooloptions.cpp @@ -1045,12 +1045,12 @@ void ArrowToolOptionsBox::onCurrentStageObjectComboActivated(int index) { return; } // switch the current object - m_objHandle->setObjectId(id); if (id.isCamera()) { TXsheet *xsh = m_xshHandle->getXsheet(); if (xsh->getCameraColumnIndex() != id.getIndex()) m_xshHandle->changeXsheetCamera(id.getIndex()); } + m_objHandle->setObjectId(id); } //------------------------------------------------------------------------------ diff --git a/toonz/sources/tnztools/toonzvectorbrushtool.cpp b/toonz/sources/tnztools/toonzvectorbrushtool.cpp index 3f575244..7b9e9ccb 100644 --- a/toonz/sources/tnztools/toonzvectorbrushtool.cpp +++ b/toonz/sources/tnztools/toonzvectorbrushtool.cpp @@ -1407,9 +1407,9 @@ bool ToonzVectorBrushTool::doFrameRangeStrokes( assert(m > 0); if (withUndo) TUndoManager::manager()->beginBlock(); - int row = getApplication()->getCurrentFrame()->isEditingScene() - ? getApplication()->getCurrentFrame()->getFrameIndex() - : -1; + int row = getApplication()->getCurrentFrame()->isEditingScene() + ? getApplication()->getCurrentFrame()->getFrameIndex() + : -1; TFrameId cFid = getApplication()->getCurrentFrame()->getFid(); for (int i = 0; i < m; ++i) { TFrameId fid = fids[i]; @@ -1714,7 +1714,7 @@ void ToonzVectorBrushTool::checkStrokeSnapping(bool beforeMousePress, stroke = vi->getStroke(i); if (stroke->getNearestW(m_mousePos, outW, distance2) && distance2 < minDistance2) { - minDistance2 = distance2; + minDistance2 = distance2; beforeMousePress ? m_strokeIndex1 = i : m_strokeIndex2 = i; if (areAlmostEqual(outW, 0.0, 1e-3)) beforeMousePress ? m_w1 = 0.0 : m_w2 = 0.0; @@ -1725,7 +1725,7 @@ void ToonzVectorBrushTool::checkStrokeSnapping(bool beforeMousePress, beforeMousePress ? point1 = stroke->getPoint(m_w1) : point1 = stroke->getPoint(m_w2); - snapFound = true; + snapFound = true; } } // compare to first point of current stroke @@ -1826,8 +1826,8 @@ void ToonzVectorBrushTool::checkGuideSnapping(bool beforeMousePress, snapPoint.x = hGuide; } beforeMousePress ? m_foundFirstSnap = true : m_foundLastSnap = true; - beforeMousePress ? m_firstSnapPoint = snapPoint : m_lastSnapPoint = - snapPoint; + beforeMousePress ? m_firstSnapPoint = snapPoint + : m_lastSnapPoint = snapPoint; } } } diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt index 9e7bcb04..ac22b709 100644 --- a/toonz/sources/toonz/CMakeLists.txt +++ b/toonz/sources/toonz/CMakeLists.txt @@ -79,6 +79,7 @@ set(MOC_HEADERS reslist.h ruler.h savepresetpopup.h + scenebrowser.h scenesettingspopup.h sceneviewer.h sceneviewercontextmenu.h @@ -123,6 +124,9 @@ set(MOC_HEADERS expressionreferencemanager.h tooloptionsshortcutinvoker.h exportxsheetpdf.h + custompanelmanager.h + custompaneleditorpopup.h + convertfolderpopup.h motionpathpanel.h graphwidget.h ../stopmotion/stopmotion.h @@ -178,6 +182,7 @@ set(HEADERS ../include/orientation.h ../include/saveloadqsettings.h xdtsio.h + ocaio.h levelcommand.h # Tracker file ObjectTracker.h @@ -267,6 +272,8 @@ set(SOURCES renumberpopup.cpp runscriptcommand.cpp savepresetpopup.cpp + scenebrowser.cpp + scenebrowserversioncontrol.cpp sceneviewercontextmenu.cpp scenesettingspopup.cpp scriptconsolepanel.cpp @@ -351,11 +358,15 @@ set(SOURCES separatecolorsswatch.cpp separatecolorspopup.cpp xdtsio.cpp + ocaio.cpp xdtsimportpopup.cpp expressionreferencemanager.cpp tooloptionsshortcutinvoker.cpp tvpjson_io.cpp exportxsheetpdf.cpp + custompanelmanager.cpp + custompaneleditorpopup.cpp + convertfolderpopup.cpp # Tracker file dummyprocessor.cpp metnum.cpp @@ -480,7 +491,7 @@ if(BUILD_ENV_MSVC) target_link_libraries(Tahoma2D Qt5::WinMain Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml - Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::SerialPort + Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::SerialPort Qt5::UiTools ${GL_LIB} ${GLUT_LIB} ${TURBOJPEG_LIB} ${OpenCV_LIBS} ${EXTRA_LIBS} strmiids tnzcore tnzbase toonzlib colorfx tnzext image sound toonzqt tnztools tnzstdfx tfarm ) @@ -500,7 +511,7 @@ elseif(BUILD_ENV_APPLE AND WITH_CANON) target_link_libraries(Tahoma2D Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml - Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::MultimediaWidgets Qt5::SerialPort + Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::MultimediaWidgets Qt5::SerialPort Qt5::UiTools ${GL_LIB} ${GLUT_LIB} ${CANON_LIB} ${TURBOJPEG_LIB} ${OpenCV_LIBS} ${COCOA_LIB} ${EXTRA_LIBS} mousedragfilter ) @@ -521,7 +532,7 @@ elseif(BUILD_ENV_APPLE) target_link_libraries(Tahoma2D Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml - Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::MultimediaWidgets Qt5::SerialPort + Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::MultimediaWidgets Qt5::SerialPort Qt5::UiTools ${GL_LIB} ${GLUT_LIB} ${TURBOJPEG_LIB} ${OpenCV_LIBS} ${COCOA_LIB} ${EXTRA_LIBS} mousedragfilter ) @@ -544,7 +555,7 @@ elseif(BUILD_ENV_UNIXLIKE) target_link_libraries( Tahoma2D Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml - Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::SerialPort + Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::SerialPort Qt5::UiTools ${GL_LIB} ${GLUT_LIB} ${GLU_LIB} ${TURBOJPEG_LIB} ${OpenCV_LIBS} ${EXTRA_LIBS} ) diff --git a/toonz/sources/toonz/Resources/no_texturestyle.png b/toonz/sources/toonz/Resources/no_texturestyle.png new file mode 100644 index 00000000..964ed69c Binary files /dev/null and b/toonz/sources/toonz/Resources/no_texturestyle.png differ diff --git a/toonz/sources/toonz/audiorecordingpopup.cpp b/toonz/sources/toonz/audiorecordingpopup.cpp index 760b8717..37e11544 100644 --- a/toonz/sources/toonz/audiorecordingpopup.cpp +++ b/toonz/sources/toonz/audiorecordingpopup.cpp @@ -51,7 +51,13 @@ #include #include -// +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM 1 +#endif +#ifndef WAVE_FORMAT_IEEE_FLOAT +#define WAVE_FORMAT_IEEE_FLOAT 3 +#endif + //============================================================================= AudioRecordingPopup::AudioRecordingPopup() @@ -67,15 +73,15 @@ AudioRecordingPopup::AudioRecordingPopup() m_pauseRecordingButton = new QPushButton(this); m_pausePlaybackButton = new QPushButton(this); m_refreshDevicesButton = new QPushButton(this); - m_duration = new QLabel("00:00.000"); - m_playDuration = new QLabel("00:00.000"); - m_deviceListCB = new QComboBox(); - m_audioLevelsDisplay = new AudioLevelsDisplay(this); - m_playXSheetCB = new QCheckBox(tr("Sync with XSheet/Timeline"), this); - m_timer = new QElapsedTimer(); - m_recordedLevels = QMap(); - m_player = new QMediaPlayer(this); - m_console = FlipConsole::getCurrent(); + m_duration = new QLabel("00:00.000"); + m_playDuration = new QLabel("00:00.000"); + m_deviceListCB = new QComboBox(); + m_audioLevelsDisplay = new AudioLevelsDisplay(this); + m_playXSheetCB = new QCheckBox(tr("Sync with XSheet/Timeline"), this); + m_timer = new QElapsedTimer(); + m_recordedLevels = QMap(); + m_player = new QMediaPlayer(this); + m_console = FlipConsole::getCurrent(); m_labelDevice = new QLabel(tr("Device: ")); m_labelSamplerate = new QLabel(tr("Sample rate: ")); @@ -88,11 +94,16 @@ AudioRecordingPopup::AudioRecordingPopup() m_comboSamplerate->addItem(tr("44100 Hz"), QVariant::fromValue(44100)); m_comboSamplerate->addItem(tr("48000 Hz"), QVariant::fromValue(48000)); m_comboSamplerate->addItem(tr("96000 Hz"), QVariant::fromValue(96000)); + m_comboSamplerate->addItem(tr("192000 Hz"), QVariant::fromValue(192000)); m_comboSamplerate->setCurrentIndex(3); // 44.1KHz m_comboSamplefmt->addItem(tr("Mono 8-Bits"), QVariant::fromValue(9)); m_comboSamplefmt->addItem(tr("Stereo 8-Bits"), QVariant::fromValue(10)); m_comboSamplefmt->addItem(tr("Mono 16-Bits"), QVariant::fromValue(17)); m_comboSamplefmt->addItem(tr("Stereo 16-Bits"), QVariant::fromValue(18)); + m_comboSamplefmt->addItem(tr("Mono 24-Bits"), QVariant::fromValue(25)); + m_comboSamplefmt->addItem(tr("Stereo 24-Bits"), QVariant::fromValue(26)); + m_comboSamplefmt->addItem(tr("Mono 32-Bits"), QVariant::fromValue(33)); + m_comboSamplefmt->addItem(tr("Stereo 32-Bits"), QVariant::fromValue(34)); m_comboSamplefmt->setCurrentIndex(2); // Mono 16-Bits m_recordButton->setMaximumWidth(32); @@ -101,10 +112,10 @@ AudioRecordingPopup::AudioRecordingPopup() m_pausePlaybackButton->setMaximumWidth(32); m_refreshDevicesButton->setMaximumWidth(25); - QString playDisabled = QString(":Resources/play_disabled.svg"); - QString pauseDisabled = QString(":Resources/pause_disabled.svg"); - QString stopDisabled = QString(":Resources/stop_disabled.svg"); - QString recordDisabled = QString(":Resources/record_disabled.svg"); + QString playDisabled = QString(":Resources/play_disabled.svg"); + QString pauseDisabled = QString(":Resources/pause_disabled.svg"); + QString stopDisabled = QString(":Resources/stop_disabled.svg"); + QString recordDisabled = QString(":Resources/record_disabled.svg"); QString refreshDisabled = QString(":Resources/repeat_icon.svg"); m_pauseIcon = createQIcon("pause"); @@ -143,15 +154,21 @@ AudioRecordingPopup::AudioRecordingPopup() format = m_audioDeviceInfo.nearestFormat(format); } m_audioInput = new QAudioInput(m_audioDeviceInfo, format); + + // WAV Writter m_audioWriterWAV = new AudioWriterWAV(format); // Tool tips to provide additional info to the user m_deviceListCB->setToolTip(tr("Audio input device to record")); - m_comboSamplerate->setToolTip(tr("Number of samples per second, 44.1KHz = CD Quality")); - m_comboSamplefmt->setToolTip(tr("Number of channels and bits per sample, 16-bits recommended")); - m_playXSheetCB->setToolTip(tr("Play animation from current frame while recording/playback")); + m_comboSamplerate->setToolTip( + tr("Number of samples per second, 44.1KHz = CD Quality")); + m_comboSamplefmt->setToolTip( + tr("Number of channels and bits per sample, 16-bits recommended")); + m_playXSheetCB->setToolTip( + tr("Play animation from current frame while recording/playback")); m_saveButton->setToolTip(tr("Save recording and insert into new column")); - m_refreshDevicesButton->setToolTip(tr("Refresh list of connected audio input devices")); + m_refreshDevicesButton->setToolTip( + tr("Refresh list of connected audio input devices")); m_topLayout->setMargin(5); m_topLayout->setSpacing(8); @@ -218,28 +235,40 @@ AudioRecordingPopup::AudioRecordingPopup() m_playXSheetCB->setChecked(true); - connect(m_playXSheetCB, SIGNAL(stateChanged(int)), this, - SLOT(onPlayXSheetCBChanged(int))); - connect(m_saveButton, SIGNAL(clicked()), this, SLOT(onSaveButtonPressed())); - connect(m_recordButton, SIGNAL(clicked()), this, - SLOT(onRecordButtonPressed())); - connect(m_playButton, SIGNAL(clicked()), this, SLOT(onPlayButtonPressed())); - connect(m_pauseRecordingButton, SIGNAL(clicked()), this, - SLOT(onPauseRecordingButtonPressed())); - connect(m_pausePlaybackButton, SIGNAL(clicked()), this, - SLOT(onPausePlaybackButtonPressed())); - connect(m_audioWriterWAV, SIGNAL(update(qint64)), this, - SLOT(updateRecordDuration(qint64))); - if (m_console) connect(m_console, SIGNAL(playStateChanged(bool)), this, - SLOT(onPlayStateChanged(bool))); - connect(m_deviceListCB, SIGNAL(currentTextChanged(const QString)), this, - SLOT(onInputDeviceChanged())); - connect(m_refreshDevicesButton, SIGNAL(clicked()), this, - SLOT(onRefreshButtonPressed())); - connect(m_comboSamplerate, SIGNAL(currentTextChanged(const QString)), this, - SLOT(onAudioSettingChanged())); - connect(m_comboSamplefmt, SIGNAL(currentTextChanged(const QString)), this, - SLOT(onAudioSettingChanged())); + bool ret = connect(m_playXSheetCB, SIGNAL(stateChanged(int)), this, + SLOT(onPlayXSheetCBChanged(int))); + + ret = ret && connect(m_saveButton, SIGNAL(clicked()), this, + SLOT(onSaveButtonPressed())); + ret = ret && connect(m_recordButton, SIGNAL(clicked()), this, + SLOT(onRecordButtonPressed())); + ret = ret && connect(m_playButton, SIGNAL(clicked()), this, + SLOT(onPlayButtonPressed())); + ret = ret && connect(m_pauseRecordingButton, SIGNAL(clicked()), this, + SLOT(onPauseRecordingButtonPressed())); + ret = ret && connect(m_pausePlaybackButton, SIGNAL(clicked()), this, + SLOT(onPausePlaybackButtonPressed())); + ret = ret && connect(m_audioWriterWAV, SIGNAL(update(qint64)), this, + SLOT(updateRecordDuration(qint64))); + + if (m_console) { + ret = ret && connect(m_console, SIGNAL(playStateChanged(bool)), this, + SLOT(onPlayStateChanged(bool))); + } + + ret = + ret && connect(m_deviceListCB, SIGNAL(currentTextChanged(const QString)), + this, SLOT(onInputDeviceChanged())); + ret = ret && connect(m_refreshDevicesButton, SIGNAL(clicked()), this, + SLOT(onRefreshButtonPressed())); + ret = ret && + connect(m_comboSamplerate, SIGNAL(currentTextChanged(const QString)), + this, SLOT(onAudioSettingChanged())); + ret = ret && + connect(m_comboSamplefmt, SIGNAL(currentTextChanged(const QString)), + this, SLOT(onAudioSettingChanged())); + + assert(ret); } //----------------------------------------------------------------------------- @@ -249,24 +278,11 @@ AudioRecordingPopup::~AudioRecordingPopup() {} //----------------------------------------------------------------------------- void AudioRecordingPopup::onRecordButtonPressed() { -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - if (m_audioInput->state() == QAudio::InterruptedState) { - DVGui::warning( - tr("The microphone is not available: " - "\nPlease select a different device or check the microphone.")); - return; - } else if (m_audioInput->state() == QAudio::StoppedState) { + if (m_audioInput->state() == QAudio::StoppedState) { if (!m_console) { DVGui::warning( tr("Record failed: " "\nMake sure there's XSheet or Timeline in the room.")); -#else - if (m_audioInput->state() == QAudio::StoppedState) { - if (!m_console) { - DVGui::warning( - tr("The microphone is not available: " - "\nPlease select a different device or check the microphone.")); -#endif return; } // clear the player in case the file is open there @@ -286,7 +302,7 @@ void AudioRecordingPopup::onRecordButtonPressed() { // The audio writer support either writing to buffer or directly to disk // each method have their own pros and cons // For now using false to mimic previous QAudioRecorder behaviour - m_audioWriterWAV->restart(m_audioInput->format()); + m_audioWriterWAV->reset(m_audioInput->format()); if (!m_audioWriterWAV->start(m_filePath.getQString(), false)) { DVGui::warning( tr("Failed to save WAV file:\nMake sure you have write permissions " @@ -342,8 +358,9 @@ void AudioRecordingPopup::onRecordButtonPressed() { } m_isPlaying = false; if (!success) { - DVGui::warning(tr( - "Failed to save WAV file:\nMake sure you have write permissions in folder.")); + DVGui::warning( + tr("Failed to save WAV file:\nMake sure you have write permissions " + "in folder.")); } } } @@ -520,15 +537,11 @@ void AudioRecordingPopup::onRefreshButtonPressed() { //----------------------------------------------------------------------------- -void AudioRecordingPopup::onInputDeviceChanged() { - reinitAudioInput(); -} +void AudioRecordingPopup::onInputDeviceChanged() { reinitAudioInput(); } //----------------------------------------------------------------------------- -void AudioRecordingPopup::onAudioSettingChanged() { - reinitAudioInput(); -} +void AudioRecordingPopup::onAudioSettingChanged() { reinitAudioInput(); } //----------------------------------------------------------------------------- @@ -644,7 +657,8 @@ void AudioRecordingPopup::hideEvent(QHideEvent *event) { //----------------------------------------------------------------------------- -void AudioRecordingPopup::enumerateAudioDevices(const QString &selectedDeviceName) { +void AudioRecordingPopup::enumerateAudioDevices( + const QString &selectedDeviceName) { const QAudioDeviceInfo &defaultDeviceInfo = QAudioDeviceInfo::defaultInputDevice(); @@ -680,15 +694,19 @@ void AudioRecordingPopup::reinitAudioInput() { .value(); int sampletype = m_comboSamplefmt->itemData(m_comboSamplefmt->currentIndex()).value(); - int bitdepth = sampletype & 56; - int channels = sampletype & 7; + int bitdepth = sampletype & 60; + int channels = sampletype & 3; QAudioFormat format; format.setSampleRate(samplerate); format.setChannelCount(channels); format.setSampleSize(bitdepth); - format.setSampleType(bitdepth == 8 ? QAudioFormat::UnSignedInt - : QAudioFormat::SignedInt); + if (bitdepth == 32) + format.setSampleType(QAudioFormat::Float); + else if (bitdepth == 8) + format.setSampleType(QAudioFormat::UnSignedInt); + else + format.setSampleType(QAudioFormat::SignedInt); format.setByteOrder(QAudioFormat::LittleEndian); format.setCodec("audio/pcm"); if (!m_audioDeviceInfo.isFormatSupported(format)) { @@ -700,7 +718,7 @@ void AudioRecordingPopup::reinitAudioInput() { // Recreate input delete m_audioInput; m_audioInput = new QAudioInput(m_audioDeviceInfo, format); - m_audioWriterWAV->restart(format); + m_audioWriterWAV->reset(format); } //----------------------------------------------------------------------------- @@ -715,30 +733,39 @@ void AudioRecordingPopup::reinitAudioInput() { AudioWriterWAV::AudioWriterWAV(const QAudioFormat &format) : m_level(0.0) , m_peakL(0.0) - , m_maxAmp(0.0) + , m_state(false) , m_wrRawB(0) , m_wavFile(NULL) , m_wavBuff(NULL) { - restart(format); + reset(format); } -bool AudioWriterWAV::restart(const QAudioFormat &format) { +bool AudioWriterWAV::reset(const QAudioFormat &format) { m_format = format; + + int samplesPerSec = m_format.sampleRate() * m_format.channelCount(); if (m_format.sampleSize() == 8) { - m_rbytesms = 1000.0 / (m_format.sampleRate() * m_format.channelCount()); - m_maxAmp = 127.0; + m_rbytesms = 1000.0 / samplesPerSec; } else if (m_format.sampleSize() == 16) { - m_rbytesms = 500.0 / (m_format.sampleRate() * m_format.channelCount()); - m_maxAmp = 32767.0; - } else { - // 32-bits isn't supported - m_rbytesms = 250.0 / (m_format.sampleRate() * m_format.channelCount()); - m_maxAmp = 1.0; + m_rbytesms = 1000.0 / 2.0 / samplesPerSec; + } else if (m_format.sampleSize() == 24) { + m_rbytesms = 1000.0 / 3.0 / samplesPerSec; + } else { // 32-bits + m_rbytesms = 1000.0 / 4.0 / samplesPerSec; } + m_wrRawB = 0; m_peakL = 0.0; + m_state = false; + if (m_wavBuff) m_wavBuff->clear(); - return this->reset(); + if (m_wavFile) { + m_wavFile->close(); + delete m_wavFile; + m_wavFile = NULL; + } + + return QIODevice::reset(); } // Just a tiny define to avoid a magic number @@ -746,6 +773,8 @@ bool AudioWriterWAV::restart(const QAudioFormat &format) { #define AWWAV_HEADER_SIZE 44 bool AudioWriterWAV::start(const QString &filename, bool useMem) { + if (m_state) return false; + open(QIODevice::WriteOnly); m_filename = filename; @@ -755,15 +784,17 @@ bool AudioWriterWAV::start(const QString &filename, bool useMem) { m_wavFile = new QFile(m_filename); if (!m_wavFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; - m_wavFile->seek(AWWAV_HEADER_SIZE); // skip header + m_wavFile->seek(AWWAV_HEADER_SIZE); // skip header } m_wrRawB = 0; m_peakL = 0.0; + m_state = true; return true; } bool AudioWriterWAV::stop() { + if (!m_state) return false; close(); if (m_wavBuff) { @@ -785,6 +816,7 @@ bool AudioWriterWAV::stop() { m_wrRawB = 0; m_peakL = 0.0; + m_state = false; return true; } @@ -793,7 +825,7 @@ void AudioWriterWAV::writeWAVHeader(QFile &file) { quint32 samplerate = m_format.sampleRate(); quint16 bitrate = m_format.sampleSize(); - qint64 pos = file.pos(); + qint64 pos = file.pos(); file.seek(0); QDataStream out(&file); @@ -801,7 +833,8 @@ void AudioWriterWAV::writeWAVHeader(QFile &file) { out.writeRawData("RIFF", 4); out << (quint32)(m_wrRawB + AWWAV_HEADER_SIZE); out.writeRawData("WAVEfmt ", 8); - out << (quint32)16 << (quint16)1; // magic numbers! + out << (quint32)16; // Chunk size + out << (quint16)(bitrate == 32 ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM); out << channels << samplerate; out << quint32(samplerate * channels * bitrate / 8); out << quint16(channels * bitrate / 8); @@ -827,6 +860,7 @@ qint64 AudioWriterWAV::writeData(const char *data, qint64 len) { tmp = qAbs(sdata[i] - 128); if (tmp > peak) peak = tmp; } + m_level = qreal(peak) / 127.0; } else if (m_format.sampleSize() == 16) { const qint16 *sdata = (const qint16 *)data; int slen = len / 2; @@ -834,11 +868,24 @@ qint64 AudioWriterWAV::writeData(const char *data, qint64 len) { tmp = qAbs(sdata[i]); if (tmp > peak) peak = tmp; } - } else { - // 32-bits isn't supported - peak = -1; + m_level = qreal(peak) / 32767.0; + } else if (m_format.sampleSize() == 24) { + const qint8 *sdata = (const qint8 *)data; + int slen = len / 3; + for (int i = 0; i < slen; ++i) { + tmp = qAbs(sdata[i * 3 + 2]); + if (tmp > peak) peak = tmp; + } + m_level = qreal(peak) / 127.0; + } else { // 32-bits + const float *sdata = (const float *)data; + int slen = len / 4; + for (int i = 0; i < slen; ++i) { + tmp = qAbs(sdata[i] * 32767.0f); + if (tmp > peak) peak = tmp; + } + m_level = qreal(peak) / 32767.0; } - m_level = qreal(peak) / m_maxAmp; if (m_level > m_peakL) m_peakL = m_level; // Write to memory or disk @@ -877,8 +924,9 @@ void AudioLevelsDisplay::paintEvent(QPaintEvent *event) { QPainter painter(this); QColor color; + if (m_level < 0.0) { - return; // draw nothing... + return; // draw nothing... } else if (m_level < 0.5) { color = Qt::green; } else if (m_level < 0.75) { diff --git a/toonz/sources/toonz/audiorecordingpopup.h b/toonz/sources/toonz/audiorecordingpopup.h index e0149572..6a07f66f 100644 --- a/toonz/sources/toonz/audiorecordingpopup.h +++ b/toonz/sources/toonz/audiorecordingpopup.h @@ -30,9 +30,7 @@ class AudioWriterWAV; class AudioRecordingPopup : public DVGui::Dialog { Q_OBJECT - QPushButton - *m_recordButton, *m_refreshDevicesButton, - *m_playButton, + QPushButton *m_recordButton, *m_refreshDevicesButton, *m_playButton, *m_pauseRecordingButton, *m_pausePlaybackButton, *m_saveButton; QComboBox *m_deviceListCB; QAudioInput *m_audioInput; @@ -95,7 +93,7 @@ class AudioWriterWAV : public QIODevice { Q_OBJECT public: AudioWriterWAV(const QAudioFormat &format); - bool restart(const QAudioFormat &format); + bool reset(const QAudioFormat &format); bool start(const QString &filename, bool useMem); bool stop(); @@ -109,12 +107,12 @@ public: private: QString m_filename; QFile *m_wavFile; - QByteArray *m_wavBuff; // if not null then use memory + QByteArray *m_wavBuff; // if not null then use memory QAudioFormat m_format; - quint64 m_wrRawB; // Written raw bytes + quint64 m_wrRawB; // Written raw bytes qreal m_rbytesms; - qreal m_maxAmp; qreal m_level, m_peakL; + bool m_state; void writeWAVHeader(QFile &file); diff --git a/toonz/sources/toonz/batches.cpp b/toonz/sources/toonz/batches.cpp index 7587304c..38cf798a 100644 --- a/toonz/sources/toonz/batches.cpp +++ b/toonz/sources/toonz/batches.cpp @@ -243,7 +243,19 @@ commandline += " -id " + task->m_id;*/ RunningTasks[task->m_id] = process; } - process->start(task->getCommandLine()); + process->setProgram(task->getCommandLinePrgName()); +#if defined(_WIN32) + process->setNativeArguments(task->getCommandLineArguments()); +#else +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + process->setArguments( + task->getCommandLineArguments().split(" ", Qt::SkipEmptyParts)); +#else + process->setArguments( + task->getCommandLineArguments().split(" ", QString::SkipEmptyParts)); +#endif +#endif + process->start(); process->waitForFinished(-1); { @@ -288,7 +300,10 @@ void BatchesController::setTasksTree(TaskTreeModel *tree) { //------------------------------------------------------------------------------ -inline bool isMovieType(std::string type) { return (type == "avi" || type == "mp4" || type == "webm" || type == "mov"); } +inline bool isMovieType(std::string type) { + return (type == "mov" || type == "avi" || type == "3gp" || type == "mp4" || + type == "webm"); +} //------------------------------------------------------------------------------ @@ -345,7 +360,7 @@ void BatchesController::addComposerTask(const TFilePath &_taskFilePath) { out.getRange(r0, r1, step); int sceneFrameCount = scene.getFrameCount(); - if (r0 < 0) r0 = 0; + if (r0 < 0) r0 = 0; if (r1 >= sceneFrameCount) r1 = sceneFrameCount - 1; else if (r1 < r0) @@ -476,7 +491,7 @@ namespace { void DeleteTask(const std::pair &mapItem) { if (mapItem.second->m_parentId.isEmpty()) delete mapItem.second; } -} +} // namespace void BatchesController::removeAllTasks() { std::map::iterator tt, tEnd(m_tasks.end()); @@ -555,7 +570,7 @@ void BatchesController::setDirtyFlag(bool state) { if (FirstTime) { FirstTime = false; bool ret = connect(TApp::instance()->getMainWindow(), SIGNAL(exit(bool &)), - SLOT(onExit(bool &))); + SLOT(onExit(bool &))); assert(ret); } @@ -707,7 +722,7 @@ void BatchesController::stop(const QString &taskId) { int count = task->getTaskCount(); if (count > 1) { for (int i = 0; i < count; ++i) { - TFarmTask *subtask = task->getTask(i); + TFarmTask *subtask = task->getTask(i); if (subtask->m_status == Waiting) subtask->m_status = Suspended; if ((it = RunningTasks.find(subtask->m_id)) != RunningTasks.end()) { it->second->kill(); @@ -870,7 +885,7 @@ void BatchesController::saveas() { } static SaveTaskListPopup *popup = 0; - if (!popup) popup = new SaveTaskListPopup(); + if (!popup) popup = new SaveTaskListPopup(); popup->exec(); } @@ -929,7 +944,7 @@ void BatchesController::detach(BatchesController::Observer *obs) { namespace { void notifyObserver(BatchesController::Observer *obs) { obs->update(); } -} +} // namespace void BatchesController::notify() { std::for_each(m_observers.begin(), m_observers.end(), notifyObserver); diff --git a/toonz/sources/toonz/batchserversviewer.cpp b/toonz/sources/toonz/batchserversviewer.cpp index 425915b4..539a0ef9 100644 --- a/toonz/sources/toonz/batchserversviewer.cpp +++ b/toonz/sources/toonz/batchserversviewer.cpp @@ -255,11 +255,7 @@ static LineEdit *create(QGridLayout *layout, const QString &name, int &row, return ret; } -#if QT_VERSION >= 0x050500 BatchServersViewer::BatchServersViewer(QWidget *parent, Qt::WindowFlags flags) -#else -BatchServersViewer::BatchServersViewer(QWidget *parent, Qt::WFlags flags) -#endif : QFrame(parent) { int row = 0; diff --git a/toonz/sources/toonz/batchserversviewer.h b/toonz/sources/toonz/batchserversviewer.h index a28eaf2f..c8a54536 100644 --- a/toonz/sources/toonz/batchserversviewer.h +++ b/toonz/sources/toonz/batchserversviewer.h @@ -44,11 +44,7 @@ class BatchServersViewer final : public QFrame { Q_OBJECT public: -#if QT_VERSION >= 0x050500 BatchServersViewer(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - BatchServersViewer(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif ~BatchServersViewer(); void updateSelected(); diff --git a/toonz/sources/toonz/castviewer.cpp b/toonz/sources/toonz/castviewer.cpp index 139d3f82..4acdcda0 100644 --- a/toonz/sources/toonz/castviewer.cpp +++ b/toonz/sources/toonz/castviewer.cpp @@ -93,7 +93,7 @@ public: return QObject::tr("Move Level to Cast Folder"); } }; -} +} // namespace //============================================================================= // @@ -454,11 +454,7 @@ void CastTreeViewer::deleteFolder() { // //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 CastBrowser::CastBrowser(QWidget *parent, Qt::WindowFlags flags) -#else -CastBrowser::CastBrowser(QWidget *parent, Qt::WFlags flags) -#endif : QSplitter(parent) , m_treeViewer(0) , m_folderName(0) @@ -884,7 +880,8 @@ void CastBrowser::viewFile() { if (!TFileType::isViewable(TFileType::getInfo(filePath))) return; if (Preferences::instance()->isDefaultViewerEnabled() && - (filePath.getType() == "avi")) + (filePath.getType() == "mov" || filePath.getType() == "avi" || + filePath.getType() == "3gp")) QDesktopServices::openUrl(QUrl("file:///" + toQString(filePath))); else ::viewFile(filePath); diff --git a/toonz/sources/toonz/castviewer.h b/toonz/sources/toonz/castviewer.h index b39b9397..46054ee1 100644 --- a/toonz/sources/toonz/castviewer.h +++ b/toonz/sources/toonz/castviewer.h @@ -78,11 +78,7 @@ class CastBrowser final : public QSplitter, public DvItemListModel { std::unique_ptr m_castItems; public: -#if QT_VERSION >= 0x050500 CastBrowser(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - CastBrowser(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif ~CastBrowser(); CastItems const &getCastItems() const { return *m_castItems; } diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index 14892109..bba3cdcf 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -494,10 +494,7 @@ bool pasteStrokesInCellWithoutUndo( } else { vi = cell.getImage(true); sl = cell.getSimpleLevel(); - if (sl->getType() == OVL_XSHLEVEL && - (sl->getPath().getType() == "psd" || sl->getPath().getType() == "gif" || - sl->getPath().getType() == "mp4" || sl->getPath().getType() == "webm" || - sl->getPath().getType() == "mov")) + if (sl->getType() == OVL_XSHLEVEL && sl->getPath().isUneditable()) return false; fid = cell.getFrameId(); if (!vi) { @@ -1765,19 +1762,14 @@ static void pasteRasterImageInCell(int row, int col, } else { TXshSimpleLevel *sl = cell.getSimpleLevel(); // don't do anything to ffmpeg level types - if (sl->getType() == OVL_XSHLEVEL && (sl->getPath().getType() == "psd" || - sl->getPath().getType() == "gif" || - sl->getPath().getType() == "mp4" || - sl->getPath().getType() == "webm" || - sl->getPath().getType() == "mov")) - return; + if (sl->getType() == OVL_XSHLEVEL && sl->getPath().isUneditable()) return; oldPalette = sl->getPalette(); } } if (oldPalette) oldPalette = oldPalette->clone(); - TTileSet *tiles = 0; - bool isPaste = pasteRasterImageInCellWithoutUndo(row, col, rasterImageData, - &tiles, isLevelCreated); + TTileSet *tiles = 0; + bool isPaste = pasteRasterImageInCellWithoutUndo(row, col, rasterImageData, + &tiles, isLevelCreated); if (isLevelCreated && oldPalette.getPointer()) oldPalette = 0; if (!isPaste) return; cell = xsh->getCell(row, col); @@ -3957,7 +3949,7 @@ void TCellSelection::convertToToonzRaster() { TApp *app = TApp::instance(); int row = app->getCurrentFrame()->getFrame(); int col = app->getCurrentColumn()->getColumnIndex(); - int i, j; + int i; ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = scene->getXsheet(); @@ -4072,7 +4064,7 @@ void TCellSelection::convertVectortoVector() { TApp *app = TApp::instance(); int row = app->getCurrentFrame()->getFrame(); int col = app->getCurrentColumn()->getColumnIndex(); - int i, j; + int i; ToonzScene *scene = app->getCurrentScene()->getScene(); TXsheet *xsh = scene->getXsheet(); diff --git a/toonz/sources/toonz/cellselectioncommand.cpp b/toonz/sources/toonz/cellselectioncommand.cpp index c54c32b3..e10a03e6 100644 --- a/toonz/sources/toonz/cellselectioncommand.cpp +++ b/toonz/sources/toonz/cellselectioncommand.cpp @@ -1653,11 +1653,7 @@ void CloneLevelUndo::cloneLevels() const { assert(lt->first && !lt->second.empty()); TXshSimpleLevel *srcSl = lt->first; - if (srcSl->getPath().getType() == "psd" || - srcSl->getPath().getType() == "gif" || - srcSl->getPath().getType() == "mp4" || - srcSl->getPath().getType() == "webm" || - srcSl->getPath().getType() == "mov") + if (srcSl->getPath().isUneditable()) continue; const TFilePath &srcPath = srcSl->getPath(); diff --git a/toonz/sources/toonz/cleanupswatch.cpp b/toonz/sources/toonz/cleanupswatch.cpp index d46c124e..fd63e837 100644 --- a/toonz/sources/toonz/cleanupswatch.cpp +++ b/toonz/sources/toonz/cleanupswatch.cpp @@ -83,7 +83,7 @@ void CleanupSwatch::CleanupSwatchArea::mousePressEvent(QMouseEvent *event) { // TRop::addBackground(m_sw->m_lastRasCleanupped, TPixel::White); m_pos = event->pos(); - if (event->button() != Qt::MidButton || !m_sw->m_resampledRaster) { + if (event->button() != Qt::MiddleButton || !m_sw->m_resampledRaster) { event->ignore(); return; m_panning = false; @@ -189,7 +189,7 @@ void CleanupSwatch::CleanupSwatchArea::mouseMoveEvent(QMouseEvent *event) { void CleanupSwatch::CleanupSwatchArea::wheelEvent(QWheelEvent *event) { if (!m_sw->m_resampledRaster || m_sw->m_lx == 0 || m_sw->m_ly == 0) return; - int step = event->delta() > 0 ? 120 : -120; + int step = event->angleDelta().y() > 0 ? 120 : -120; double factor = exp(0.001 * step); if (factor == 1.0) return; double scale = m_sw->m_viewAff.det(); @@ -199,7 +199,11 @@ void CleanupSwatch::CleanupSwatchArea::wheelEvent(QWheelEvent *event) { if ((factor < 1 && sqrt(scale) < minZoom) || (factor > 1 && scale > 1200.0)) return; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + TPointD delta(event->position().x(), height() - event->position().y()); +#else TPointD delta(event->pos().x(), height() - event->pos().y()); +#endif m_sw->m_viewAff = (TTranslation(delta) * TScale(factor) * TTranslation(-delta)) * m_sw->m_viewAff; diff --git a/toonz/sources/toonz/columnselection.cpp b/toonz/sources/toonz/columnselection.cpp index c3c517f9..f24fe020 100644 --- a/toonz/sources/toonz/columnselection.cpp +++ b/toonz/sources/toonz/columnselection.cpp @@ -249,11 +249,7 @@ static bool canMergeColumns(int column, int mColumn, bool forMatchlines) { return false; // Check level type write support. Based on TTool::updateEnabled() if (level->getType() == OVL_XSHLEVEL && - (level->getPath().getType() == "psd" || // PSD files. - level->getPath().getType() == "gif" || - level->getPath().getType() == "mp4" || - level->getPath().getType() == "webm" || - level->getPath().getType() == "mov" || + (level->getPath().isUneditable() || level->is16BitChannelLevel() || // 16bpc images. level->getProperties()->getBpp() == 1)) { // Black & White images. return false; diff --git a/toonz/sources/toonz/comboviewerpane.cpp b/toonz/sources/toonz/comboviewerpane.cpp index 8a338733..825f9018 100644 --- a/toonz/sources/toonz/comboviewerpane.cpp +++ b/toonz/sources/toonz/comboviewerpane.cpp @@ -1,78 +1,30 @@ - -// TnzCore includes -#include "tconvert.h" -#include "tgeometry.h" -#include "tgl.h" -#include "trop.h" -#include "tstopwatch.h" -#include "tsystem.h" - -// TnzLib includes -#include "toonz/tscenehandle.h" -#include "toonz/txsheethandle.h" -#include "toonz/tframehandle.h" -#include "toonz/tcolumnhandle.h" -#include "toonz/txshlevelhandle.h" -#include "toonz/toonzscene.h" -#include "toonz/sceneproperties.h" -#include "toonz/txsheet.h" -#include "toonz/stage.h" -#include "toonz/stage2.h" -#include "toonz/txshlevel.h" -#include "toonz/txshcell.h" -#include "toonz/tcamera.h" -#include "toonz/tstageobjecttree.h" -#include "toonz/tobjecthandle.h" -#include "toonz/tpalettehandle.h" -#include "toonz/tonionskinmaskhandle.h" -#include "toutputproperties.h" -#include "toonz/palettecontroller.h" -#include "toonz/toonzfolders.h" -#include "toonz/preferences.h" -#include "toonz/tproject.h" - // TnzQt includes -#include "toonzqt/menubarcommand.h" -#include "toonzqt/dvdialog.h" -#include "toonzqt/gutil.h" #include "toonzqt/imageutils.h" -#include "toonzqt/flipconsole.h" - // TnzTools includes -#include "tools/toolhandle.h" #include "tools/tooloptions.h" - // Tnz6 includes #include "tapp.h" -#include "mainwindow.h" -#include "sceneviewer.h" -#include "xsheetdragtool.h" +#include "toolbar.h" #include "ruler.h" #include "menubarcommandids.h" -#include "toolbar.h" - // Qt includes -#include #include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include #include "comboviewerpane.h" -using namespace DVGui; - +// this enum is to keep comaptibility with older versions +enum CV_Parts { + CVPARTS_None = 0, + CVPARTS_TOOLBAR = 0x1, + CVPARTS_TOOLOPTIONS = 0x2, + CVPARTS_FLIPCONSOLE = 0x4, + CVPARTS_End = 0x8, + CVPARTS_ALL = CVPARTS_TOOLBAR | CVPARTS_TOOLOPTIONS | CVPARTS_FLIPCONSOLE +}; //============================================================================= // // ComboViewerPanel @@ -81,60 +33,26 @@ using namespace DVGui; //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 ComboViewerPanel::ComboViewerPanel(QWidget *parent, Qt::WindowFlags flags) -#else -ComboViewerPanel::ComboViewerPanel(QWidget *parent, Qt::WFlags flags) -#endif - : QFrame(parent) { - TApp *app = TApp::instance(); - - setFrameStyle(QFrame::StyledPanel); + : BaseViewerPanel(parent, flags) { setObjectName("ComboViewerPanel"); + TApp *app = TApp::instance(); + // ToolBar m_toolbar = new Toolbar(this, true); // Tool Options m_toolOptions = new ToolOptions(); m_toolOptions->setObjectName("ComboViewerToolOptions"); - - // Viewer - ImageUtils::FullScreenWidget *fsWidget = - new ImageUtils::FullScreenWidget(this); - fsWidget->setWidget(m_sceneViewer = new SceneViewer(fsWidget)); - m_sceneViewer->setIsStyleShortcutSwitchable(); - -#if defined(Q_OS_WIN) && (QT_VERSION >= 0x050500) && (QT_VERSION < 0x050600) - // Workaround for QTBUG-48288 - // This code should be removed after updating Qt. - // Qt may crash in handling WM_SIZE of m_sceneViewer in splash.finish(&w) - // in main.cpp. To suppress sending WM_SIZE, set window position here. - // WM_SIZE will not be sent if window position is not changed. - ::SetWindowPos(reinterpret_cast(m_sceneViewer->winId()), HWND_TOP, 0, 0, - 0, 0, SWP_NOMOVE | SWP_NOSIZE); -#endif - + // Rulers m_vRuler = new Ruler(this, m_sceneViewer, true); m_hRuler = new Ruler(this, m_sceneViewer, false); - m_sceneViewer->setRulers(m_vRuler, m_hRuler); - m_keyFrameButton = new ViewerKeyframeNavigator(this, app->getCurrentFrame()); - m_keyFrameButton->setObjectHandle(app->getCurrentObject()); - m_keyFrameButton->setXsheetHandle(app->getCurrentXsheet()); - - // FlipConsole - std::vector buttonMask = {FlipConsole::eFilledRaster, - FlipConsole::eDefineLoadBox, - FlipConsole::eUseLoadBox}; - /* --- layout --- */ - QVBoxLayout *mainLayout = new QVBoxLayout(); - mainLayout->setMargin(0); - mainLayout->setSpacing(0); { - mainLayout->addWidget(m_toolbar, 0); - mainLayout->addWidget(m_toolOptions, 0); + m_mainLayout->insertWidget(0, m_toolbar, 0); + m_mainLayout->insertWidget(1, m_toolOptions, 0); QGridLayout *viewerL = new QGridLayout(); viewerL->setMargin(0); @@ -142,68 +60,20 @@ ComboViewerPanel::ComboViewerPanel(QWidget *parent, Qt::WFlags flags) { viewerL->addWidget(m_vRuler, 1, 0); viewerL->addWidget(m_hRuler, 0, 1); - viewerL->addWidget(fsWidget, 1, 1); + viewerL->addWidget(m_fsWidget, 1, 1); } - mainLayout->addLayout(viewerL, 1); - m_flipConsole = - new FlipConsole(mainLayout, buttonMask, false, m_keyFrameButton, - "SceneViewerConsole", this, true); + viewerL->setRowStretch(1, 1); + viewerL->setColumnStretch(1, 1); + m_mainLayout->insertLayout(2, viewerL, 1); } - setLayout(mainLayout); - - m_flipConsole->enableButton(FlipConsole::eMatte, false, false); - m_flipConsole->enableButton(FlipConsole::eSave, false, false); - m_flipConsole->enableButton(FlipConsole::eCompare, false, false); - m_flipConsole->enableButton(FlipConsole::eSaveImg, false, false); - m_flipConsole->enableButton(FlipConsole::eGRed, false, false); - m_flipConsole->enableButton(FlipConsole::eGGreen, false, false); - m_flipConsole->enableButton(FlipConsole::eGBlue, false, false); - // m_flipConsole->enableButton(FlipConsole::eSound, false, false); - - m_flipConsole->setFrameRate(app->getCurrentScene() - ->getScene() - ->getProperties() - ->getOutputProperties() - ->getFrameRate()); - m_flipConsole->setFrameHandle(TApp::instance()->getCurrentFrame()); + setLayout(m_mainLayout); bool ret = true; - // When zoom changed, only if the viewer is active, change window title. - ret = - ret && connect(m_flipConsole, SIGNAL(buttonPressed(FlipConsole::EGadget)), - this, SLOT(onButtonPressed(FlipConsole::EGadget))); - ret = connect(m_sceneViewer, SIGNAL(onZoomChanged()), - SLOT(changeWindowTitle())); ret = ret && connect(m_sceneViewer, SIGNAL(previewToggled()), SLOT(changeWindowTitle())); - ret = ret && - connect(m_flipConsole, SIGNAL(playStateChanged(bool)), - TApp::instance()->getCurrentFrame(), SLOT(setPlaying(bool))); - ret = ret && connect(m_flipConsole, SIGNAL(playStateChanged(bool)), this, - SLOT(onPlayingStatusChanged(bool))); - ret = ret && - connect(m_flipConsole, SIGNAL(buttonPressed(FlipConsole::EGadget)), - m_sceneViewer, SLOT(onButtonPressed(FlipConsole::EGadget))); - ret = ret && connect(m_sceneViewer, SIGNAL(previewStatusChanged()), this, - SLOT(update())); - ret = ret && connect(m_sceneViewer, SIGNAL(onFlipHChanged(bool)), this, - SLOT(setFlipHButtonChecked(bool))); - ret = ret && connect(m_sceneViewer, SIGNAL(onFlipVChanged(bool)), this, - SLOT(setFlipVButtonChecked(bool))); - ret = ret && connect(app->getCurrentScene(), SIGNAL(sceneSwitched()), this, - SLOT(onSceneSwitched())); - assert(ret); - m_flipConsole->setChecked(FlipConsole::eSound, true); - m_playSound = m_flipConsole->isChecked(FlipConsole::eSound); - m_flipConsole->setFrameRate(app->getCurrentScene() - ->getScene() - ->getProperties() - ->getOutputProperties() - ->getFrameRate()); - UINT mask = 0; mask = mask | eShowVcr; mask = mask | eShowFramerate; @@ -216,10 +86,8 @@ ComboViewerPanel::ComboViewerPanel(QWidget *parent, Qt::WFlags flags) m_flipConsole->setCustomizemask(mask); // initial state of the parts - m_visiblePartsFlag = CVPARTS_ALL; + m_visiblePartsFlag = VPPARTS_COMBO_ALL; updateShowHide(); - - setFocusProxy(m_sceneViewer); } //----------------------------------------------------------------------------- @@ -228,25 +96,13 @@ ComboViewerPanel::ComboViewerPanel(QWidget *parent, Qt::WFlags flags) void ComboViewerPanel::updateShowHide() { // toolbar - m_toolbar->setVisible(m_visiblePartsFlag & CVPARTS_TOOLBAR); + m_toolbar->setVisible(m_visiblePartsFlag & VPPARTS_TOOLBAR); // tool options bar - m_toolOptions->setVisible(m_visiblePartsFlag & CVPARTS_TOOLOPTIONS); + m_toolOptions->setVisible(m_visiblePartsFlag & VPPARTS_TOOLOPTIONS); // flip console - m_flipConsole->showHidePlaybar(m_visiblePartsFlag & CVPARTS_PLAYBAR); - m_flipConsole->showHideFrameSlider(m_visiblePartsFlag & CVPARTS_FRAMESLIDER); - update(); + BaseViewerPanel::updateShowHide(); } -//----------------------------------------------------------------------------- -/*! showing the show/hide commands - */ - -// void ComboViewerPanel::contextMenuEvent(QContextMenuEvent *event) { -// QMenu *menu = new QMenu(this); -// addShowHideContextMenu(menu); -// menu->exec(event->globalPos()); -//} - //----------------------------------------------------------------------------- void ComboViewerPanel::addShowHideContextMenu(QMenu *menu) { @@ -258,20 +114,20 @@ void ComboViewerPanel::addShowHideContextMenu(QMenu *menu) { QAction *frameSliderSHAct = showHideMenu->addAction(tr("Frame Slider")); toolbarSHAct->setCheckable(true); - toolbarSHAct->setChecked(m_visiblePartsFlag & CVPARTS_TOOLBAR); - toolbarSHAct->setData((UINT)CVPARTS_TOOLBAR); + toolbarSHAct->setChecked(m_visiblePartsFlag & VPPARTS_TOOLBAR); + toolbarSHAct->setData((UINT)VPPARTS_TOOLBAR); toolOptionsSHAct->setCheckable(true); - toolOptionsSHAct->setChecked(m_visiblePartsFlag & CVPARTS_TOOLOPTIONS); - toolOptionsSHAct->setData((UINT)CVPARTS_TOOLOPTIONS); + toolOptionsSHAct->setChecked(m_visiblePartsFlag & VPPARTS_TOOLOPTIONS); + toolOptionsSHAct->setData((UINT)VPPARTS_TOOLOPTIONS); playbarSHAct->setCheckable(true); - playbarSHAct->setChecked(m_visiblePartsFlag & CVPARTS_PLAYBAR); - playbarSHAct->setData((UINT)CVPARTS_PLAYBAR); + playbarSHAct->setChecked(m_visiblePartsFlag & VPPARTS_PLAYBAR); + playbarSHAct->setData((UINT)VPPARTS_PLAYBAR); frameSliderSHAct->setCheckable(true); - frameSliderSHAct->setChecked(m_visiblePartsFlag & CVPARTS_FRAMESLIDER); - frameSliderSHAct->setData((UINT)CVPARTS_FRAMESLIDER); + frameSliderSHAct->setChecked(m_visiblePartsFlag & VPPARTS_FRAMESLIDER); + frameSliderSHAct->setData((UINT)VPPARTS_FRAMESLIDER); QActionGroup *showHideActGroup = new QActionGroup(this); showHideActGroup->setExclusive(false); @@ -295,630 +151,18 @@ void ComboViewerPanel::addShowHideContextMenu(QMenu *menu) { } //----------------------------------------------------------------------------- -/*! slot function for show/hide the parts - */ -void ComboViewerPanel::onShowHideActionTriggered(QAction *act) { - CV_Parts part = (CV_Parts)act->data().toUInt(); - assert(part < CVPARTS_End); - - m_visiblePartsFlag ^= part; - - updateShowHide(); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::onDrawFrame(int frame, - const ImagePainter::VisualSettings &settings, - QElapsedTimer *, qint64) { - TApp *app = TApp::instance(); - m_sceneViewer->setVisual(settings); - - TFrameHandle *frameHandle = app->getCurrentFrame(); - - if (m_sceneViewer->isPreviewEnabled()) { - class Previewer *pr = Previewer::instance(m_sceneViewer->getPreviewMode() == - SceneViewer::SUBCAMERA_PREVIEW); - - pr->getRaster(frame - 1); // the 'getRaster' starts the render of the frame - // is not already started - int curFrame = frame; - if (frameHandle->isPlaying() && - !pr->isFrameReady( - frame - 1)) // stops on last rendered frame until current is ready! - { - while (frame > 0 && !pr->isFrameReady(frame - 1)) frame--; - if (frame == 0) - frame = curFrame; // if no frame is ready, I stay on current...no use - // to rewind - m_flipConsole->setCurrentFrame(frame); - } - } - - assert(frame >= 0); - if (frame != frameHandle->getFrameIndex() + 1) { - int oldFrame = frameHandle->getFrame(); - frameHandle->setCurrentFrame(frame); - if (!frameHandle->isPlaying() && !frameHandle->isEditingLevel() && - oldFrame != frameHandle->getFrame()) - frameHandle->scrubXsheet( - frame - 1, frame - 1, - TApp::instance()->getCurrentXsheet()->getXsheet()); - } - - else if (settings.m_blankColor != TPixel::Transparent) - m_sceneViewer->update(); -} - -//----------------------------------------------------------------------------- - -ComboViewerPanel::~ComboViewerPanel() {} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::showEvent(QShowEvent *event) { - TApp *app = TApp::instance(); - TFrameHandle *frameHandle = app->getCurrentFrame(); - TSceneHandle *sceneHandle = app->getCurrentScene(); - TXshLevelHandle *levelHandle = app->getCurrentLevel(); - TObjectHandle *objectHandle = app->getCurrentObject(); - TXsheetHandle *xshHandle = app->getCurrentXsheet(); - - bool ret = true; - - /*! - onSceneChanged(): called when the scene changed - - set new scene's FPS - - update the range of frame slider with a new framehandle - - set the marker - - update key frames - */ - ret = - connect(xshHandle, SIGNAL(xsheetChanged()), this, SLOT(onSceneChanged())); - ret = ret && connect(sceneHandle, SIGNAL(sceneSwitched()), this, - SLOT(onSceneChanged())); - ret = ret && connect(sceneHandle, SIGNAL(sceneChanged()), this, - SLOT(onSceneChanged())); - - /*! - changeWindowTitle(): called when the scene / level / frame is changed - - chenge the title text - */ - ret = ret && connect(sceneHandle, SIGNAL(nameSceneChanged()), this, - SLOT(changeWindowTitle())); - ret = - ret && connect(sceneHandle, SIGNAL(preferenceChanged(const QString &)), - m_flipConsole, SLOT(onPreferenceChanged(const QString &))); - - ret = ret && connect(levelHandle, SIGNAL(xshLevelChanged()), this, - SLOT(changeWindowTitle())); - ret = ret && connect(frameHandle, SIGNAL(frameSwitched()), this, - SLOT(changeWindowTitle())); - // onXshLevelSwitched(TXshLevel*): changeWindowTitle() + updateFrameRange() - ret = ret && connect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), this, - SLOT(onXshLevelSwitched(TXshLevel *))); - ret = ret && connect(levelHandle, SIGNAL(xshLevelTitleChanged()), this, - SLOT(changeWindowTitle())); - // updateFrameRange(): update the frame slider's range - ret = ret && connect(levelHandle, SIGNAL(xshLevelChanged()), this, - SLOT(updateFrameRange())); - - // onFrameTypeChanged(): reset the marker positions in the flip console - ret = ret && connect(frameHandle, SIGNAL(frameTypeChanged()), this, - SLOT(onFrameTypeChanged())); - - // onFrameChanged(): update the flipconsole according to the current frame - ret = ret && connect(frameHandle, SIGNAL(frameSwitched()), this, - SLOT(onFrameChanged())); - - ret = ret && connect(app->getCurrentTool(), SIGNAL(toolSwitched()), - m_sceneViewer, SLOT(onToolSwitched())); - - assert(ret); - - m_flipConsole->setActive(true); - m_flipConsole->onPreferenceChanged(""); - - // refresh - onSceneChanged(); - changeWindowTitle(); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::hideEvent(QHideEvent *event) { - TApp *app = TApp::instance(); - disconnect(app->getCurrentFrame(), 0, this, 0); - disconnect(app->getCurrentScene(), 0, this, 0); - disconnect(app->getCurrentLevel(), 0, this, 0); - disconnect(app->getCurrentObject(), 0, this, 0); - disconnect(app->getCurrentXsheet(), 0, this, 0); - - disconnect(app->getCurrentTool(), SIGNAL(toolSwitched()), m_sceneViewer, - SLOT(onToolSwitched())); - disconnect(app->getCurrentScene(), SIGNAL(preferenceChanged(const QString &)), - m_flipConsole, SLOT(onPreferenceChanged(const QString &))); - - m_flipConsole->setActive(false); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::initializeTitleBar(TPanelTitleBar *titleBar) { - bool ret = true; - - TPanelTitleBarButtonSet *viewModeButtonSet; - m_referenceModeBs = viewModeButtonSet = new TPanelTitleBarButtonSet(); - int x = -272; - int iconWidth = 20; - TPanelTitleBarButton *button; - - // buttons for show / hide toggle for the field guide and the safe area - TPanelTitleBarButtonForSafeArea *safeAreaButton = - new TPanelTitleBarButtonForSafeArea( - titleBar, getIconThemePath("actions/20/pane_safe.svg")); - safeAreaButton->setToolTip(tr("Safe Area (Right Click to Select)")); - titleBar->add(QPoint(x, 0), safeAreaButton); - ret = ret && connect(safeAreaButton, SIGNAL(toggled(bool)), - CommandManager::instance()->getAction(MI_SafeArea), - SLOT(trigger())); - ret = ret && connect(CommandManager::instance()->getAction(MI_SafeArea), - SIGNAL(triggered(bool)), safeAreaButton, - SLOT(setPressed(bool))); - // initialize state - safeAreaButton->setPressed( - CommandManager::instance()->getAction(MI_SafeArea)->isChecked()); - - button = new TPanelTitleBarButton( - titleBar, getIconThemePath("actions/20/pane_grid.svg")); - button->setToolTip(tr("Grids and Overlays\nRight click to adjust.")); - x += 1 + iconWidth; - titleBar->add(QPoint(x, 0), button); - ret = ret && connect(button, SIGNAL(toggled(bool)), - CommandManager::instance()->getAction(MI_FieldGuide), - SLOT(trigger())); - ret = ret && connect(CommandManager::instance()->getAction(MI_FieldGuide), - SIGNAL(triggered(bool)), button, SLOT(setPressed(bool))); - // initialize state - button->setPressed( - CommandManager::instance()->getAction(MI_FieldGuide)->isChecked()); - - TPanelTitleBarButtonForGrids *gridMoreButton = - new TPanelTitleBarButtonForGrids( - titleBar, getIconThemePath("actions/9/pane_more.svg")); - gridMoreButton->setToolTip(tr("Grids and Overlays Settings")); - x += 1 + iconWidth; - titleBar->add(QPoint(x, 0), gridMoreButton); - connect(gridMoreButton, &TPanelTitleBarButtonForGrids::updateViewer, - [=]() { m_sceneViewer->update(); }); - - // view mode toggles - button = new TPanelTitleBarButton( - titleBar, getIconThemePath("actions/20/pane_table.svg")); - button->setToolTip(tr("Camera Stand View")); - x += 10 + iconWidth; - titleBar->add(QPoint(x, 0), button); - button->setButtonSet(viewModeButtonSet, SceneViewer::NORMAL_REFERENCE); - button->setPressed(true); - - button = new TPanelTitleBarButton(titleBar, - getIconThemePath("actions/20/pane_3d.svg")); - button->setToolTip(tr("3D View")); - x += +1 + iconWidth; - titleBar->add(QPoint(x, 0), button); - button->setButtonSet(viewModeButtonSet, SceneViewer::CAMERA3D_REFERENCE); - - button = new TPanelTitleBarButton( - titleBar, getIconThemePath("actions/20/pane_cam.svg")); - button->setToolTip(tr("Camera View")); - x += +1 + iconWidth; - titleBar->add(QPoint(x, 0), button); - button->setButtonSet(viewModeButtonSet, SceneViewer::CAMERA_REFERENCE); - - TPanelTitleBarButtonForCameraView *camTransparencyButton = - new TPanelTitleBarButtonForCameraView( - titleBar, getIconThemePath("actions/9/pane_more.svg")); - camTransparencyButton->setToolTip(tr("Change camera view transparency.")); - x += 1 + iconWidth; - titleBar->add(QPoint(x, 0), camTransparencyButton); - connect(camTransparencyButton, - &TPanelTitleBarButtonForCameraView::updateViewer, - [=]() { m_sceneViewer->update(); }); - - ret = ret && connect(viewModeButtonSet, SIGNAL(selected(int)), m_sceneViewer, - SLOT(setReferenceMode(int))); - - // freeze button - button = new TPanelTitleBarButton( - titleBar, getIconThemePath("actions/20/pane_freeze.svg")); - x += 10 + iconWidth; - - button->setToolTip(tr("Freeze")); - titleBar->add(QPoint(x, 0), button); - ret = ret && connect(button, SIGNAL(toggled(bool)), m_sceneViewer, - SLOT(freeze(bool))); - - // preview toggles - m_previewButton = new TPanelTitleBarButton( - titleBar, getIconThemePath("actions/20/pane_preview.svg")); - x += 10 + iconWidth; - titleBar->add(QPoint(x, 0), m_previewButton); - m_previewButton->setToolTip(tr("Preview")); - ret = ret && connect(m_previewButton, SIGNAL(toggled(bool)), - SLOT(enableFullPreview(bool))); - - m_subcameraPreviewButton = new TPanelTitleBarButton( - titleBar, getIconThemePath("actions/20/pane_subpreview.svg")); - x += +1 + 24; // width of pane_preview.svg = 24px - - titleBar->add(QPoint(x, 0), m_subcameraPreviewButton); - m_subcameraPreviewButton->setToolTip(tr("Sub-camera Preview")); - ret = ret && connect(m_subcameraPreviewButton, SIGNAL(toggled(bool)), - SLOT(enableSubCameraPreview(bool))); - - assert(ret); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::enableFullPreview(bool enabled) { - m_subcameraPreviewButton->setPressed(false); - m_sceneViewer->enablePreview(enabled ? SceneViewer::FULL_PREVIEW - : SceneViewer::NO_PREVIEW); - m_flipConsole->setProgressBarStatus( - &Previewer::instance(false)->getProgressBarStatus()); - enableFlipConsoleForCamerastand(enabled); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::enableSubCameraPreview(bool enabled) { - m_previewButton->setPressed(false); - m_sceneViewer->enablePreview(enabled ? SceneViewer::SUBCAMERA_PREVIEW - : SceneViewer::NO_PREVIEW); - m_flipConsole->setProgressBarStatus( - &Previewer::instance(true)->getProgressBarStatus()); - enableFlipConsoleForCamerastand(enabled); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::enableFlipConsoleForCamerastand(bool on) { - m_flipConsole->enableButton(FlipConsole::eMatte, on, false); - m_flipConsole->enableButton(FlipConsole::eSave, on, false); - m_flipConsole->enableButton(FlipConsole::eCompare, on, false); - m_flipConsole->enableButton(FlipConsole::eSaveImg, on, false); - m_flipConsole->enableButton(FlipConsole::eGRed, on, false); - m_flipConsole->enableButton(FlipConsole::eGGreen, on, false); - m_flipConsole->enableButton(FlipConsole::eGBlue, on, false); - m_flipConsole->enableButton(FlipConsole::eBlackBg, on, false); - m_flipConsole->enableButton(FlipConsole::eWhiteBg, on, false); - m_flipConsole->enableButton(FlipConsole::eCheckBg, on, false); - - m_flipConsole->enableProgressBar(on); - update(); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::onXshLevelSwitched(TXshLevel *) { - changeWindowTitle(); - m_sceneViewer->update(); - // If the level is switched by using the combobox in the film strip, the - // current level switches without change in the frame type (level or scene). - // For such case, update the frame range of the console here. - if (TApp::instance()->getCurrentFrame()->isEditingLevel()) updateFrameRange(); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::onPlayingStatusChanged(bool playing) { - if (playing) { - m_playing = true; - } else { - m_playing = false; - m_first = true; - } - if (Preferences::instance()->getOnionSkinDuringPlayback()) return; - OnionSkinMask osm = - TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask(); - if (playing) { - m_onionSkinActive = osm.isEnabled(); - if (m_onionSkinActive) { - osm.enable(false); - TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm); - TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged(); - } - } else { - if (m_onionSkinActive) { - osm.enable(true); - TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm); - TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged(); - } - } - m_sceneViewer->invalidateToolStatus(); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::changeWindowTitle() { - TApp *app = TApp::instance(); - ToonzScene *scene = app->getCurrentScene()->getScene(); - if (!scene) return; - if (!parentWidget()) return; - int frame = app->getCurrentFrame()->getFrame(); - - // put the titlebar texts in this string - QString name; - - // if the frame type is "scene editing" - if (app->getCurrentFrame()->isEditingScene()) { - TProject *project = scene->getProject(); - QString sceneName = QString::fromStdWString(scene->getSceneName()); - if (sceneName.isEmpty()) sceneName = tr("Untitled"); - - if (app->getCurrentScene()->getDirtyFlag()) sceneName += QString("*"); - name = tr("Scene: ") + sceneName; - if (frame >= 0) - name = - name + tr(" :: Frame: ") + tr(std::to_string(frame + 1).c_str()); - int col = app->getCurrentColumn()->getColumnIndex(); - if (col < 0) { - if ((m_sceneViewer->getIsFlippedX() || m_sceneViewer->getIsFlippedY()) && - !m_sceneViewer->is3DView()) { - name = name + tr(" (Flipped)"); - } - parentWidget()->setWindowTitle(name); - return; - } - TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); - TXshCell cell = xsh->getCell(frame, col); - if (cell.isEmpty()) { - if ((m_sceneViewer->getIsFlippedX() || m_sceneViewer->getIsFlippedY()) && - !m_sceneViewer->is3DView()) { - name = name + tr(" (Flipped)"); - } - parentWidget()->setWindowTitle(name); - return; - } - assert(cell.m_level.getPointer()); - TFilePath fp(cell.m_level->getName()); - QString imageName = - QString::fromStdWString(fp.withFrame(cell.m_frameId).getWideString()); - name = name + tr(" :: Level: ") + imageName; - - if (!m_sceneViewer->is3DView()) { - TAffine aff = m_sceneViewer->getViewMatrix() * - m_sceneViewer->getNormalZoomScale().inv(); - if (m_sceneViewer->getIsFlippedX()) aff = aff * TScale(-1, 1); - if (m_sceneViewer->getIsFlippedY()) aff = aff * TScale(1, -1); - name = name + " :: Zoom : " + - QString::number(tround(100.0 * sqrt(aff.det()))) + "%"; - } - - // If the current level exists and some option is set in the preference, - // set the zoom value to the current level's dpi - else if (Preferences::instance() - ->isActualPixelViewOnSceneEditingModeEnabled() && - TApp::instance()->getCurrentLevel()->getSimpleLevel() && - !CleanupPreviewCheck::instance() - ->isEnabled() // cleanup preview must be OFF - && !CameraTestCheck::instance() // camera test mode must be OFF - // neither - ->isEnabled() && - !m_sceneViewer->is3DView()) { - TAffine aff = m_sceneViewer->getViewMatrix() * - m_sceneViewer->getNormalZoomScale().inv(); - if (m_sceneViewer->getIsFlippedX()) aff = aff * TScale(-1, 1); - if (m_sceneViewer->getIsFlippedY()) aff = aff * TScale(1, -1); - name = name + " :: Zoom : " + - QString::number(tround(100.0 * sqrt(aff.det()))) + "%"; - } - - } - // if the frame type is "level editing" - else { - TXshLevel *level = app->getCurrentLevel()->getLevel(); - if (level) { - TFilePath fp(level->getName()); - QString imageName = QString::fromStdWString( - fp.withFrame(app->getCurrentFrame()->getFid()).getWideString()); - - name = name + tr("Level: ") + imageName; - if (!m_sceneViewer->is3DView()) { - TAffine aff = m_sceneViewer->getViewMatrix() * - m_sceneViewer->getNormalZoomScale().inv(); - if (m_sceneViewer->getIsFlippedX()) aff = aff * TScale(-1, 1); - if (m_sceneViewer->getIsFlippedY()) aff = aff * TScale(1, -1); - name = name + " :: Zoom : " + - QString::number(tround(100.0 * sqrt(aff.det()))) + "%"; - } - } - } - if ((m_sceneViewer->getIsFlippedX() || m_sceneViewer->getIsFlippedY()) && - !m_sceneViewer->is3DView()) { - name = name + tr(" (Flipped)"); - } - parentWidget()->setWindowTitle(name); -} - -//----------------------------------------------------------------------------- -/*! update the frame range according to the current frame type - */ -void ComboViewerPanel::updateFrameRange() { - TFrameHandle *fh = TApp::instance()->getCurrentFrame(); - int frameIndex = fh->getFrameIndex(); - int maxFrameIndex = fh->getMaxFrameIndex(); - if (frameIndex > maxFrameIndex) frameIndex = maxFrameIndex; - m_flipConsole->setFrameRange(1, maxFrameIndex + 1, 1, frameIndex + 1); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::onSceneChanged() { - TFrameHandle *fh = TApp::instance()->getCurrentFrame(); - int frameIndex = fh->getFrameIndex(); - int maxFrameIndex = fh->getMaxFrameIndex(); - if (frameIndex > maxFrameIndex) maxFrameIndex = frameIndex; - // update fps only when the scene settings is changed - m_flipConsole->setFrameRate(TApp::instance() - ->getCurrentScene() - ->getScene() - ->getProperties() - ->getOutputProperties() - ->getFrameRate(), - false); - // update the frame slider's range with new frameHandle - m_flipConsole->setFrameRange(1, maxFrameIndex + 1, 1, frameIndex + 1); - - // set the markers - int fromIndex, toIndex, dummy; - XsheetGUI::getPlayRange(fromIndex, toIndex, dummy); - m_flipConsole->setMarkers(fromIndex, toIndex); - - // update the key frames - if (m_keyFrameButton && (m_keyFrameButton->getCurrentFrame() != frameIndex)) - m_keyFrameButton->setCurrentFrame(frameIndex); - hasSoundtrack(); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::onSceneSwitched() { - m_previewButton->setPressed(false); - m_subcameraPreviewButton->setPressed(false); - enableFlipConsoleForCamerastand(false); - m_sceneViewer->enablePreview(SceneViewer::NO_PREVIEW); - m_flipConsole->setChecked(FlipConsole::eDefineSubCamera, false); - m_flipConsole->setFrameRate(TApp::instance() - ->getCurrentScene() - ->getScene() - ->getProperties() - ->getOutputProperties() - ->getFrameRate()); - m_sceneViewer->setEditPreviewSubcamera(false); - onSceneChanged(); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::onFrameChanged() { - int frameIndex = TApp::instance()->getCurrentFrame()->getFrameIndex(); - m_flipConsole->setCurrentFrame(frameIndex + 1); - if (m_keyFrameButton && (m_keyFrameButton->getCurrentFrame() != frameIndex)) - m_keyFrameButton->setCurrentFrame(frameIndex); - - if (m_playing && m_playSound) { - if (m_first == true && hasSoundtrack()) { - playAudioFrame(frameIndex); - } else if (m_hasSoundtrack) { - playAudioFrame(frameIndex); - } - } -} - -//----------------------------------------------------------------------------- -/*! reset the marker positions in the flip console - */ -void ComboViewerPanel::onFrameTypeChanged() { - if (TApp::instance()->getCurrentFrame()->getFrameType() == - TFrameHandle::LevelFrame && - m_sceneViewer->isPreviewEnabled()) { - m_previewButton->setPressed(false); - m_subcameraPreviewButton->setPressed(false); - enableFlipConsoleForCamerastand(false); - m_sceneViewer->enablePreview(SceneViewer::NO_PREVIEW); - } - m_flipConsole->setChecked(FlipConsole::eDefineSubCamera, false); - m_sceneViewer->setEditPreviewSubcamera(false); - - updateFrameRange(); - - // if in the scene editing mode, get the preview marker positions - if (TApp::instance()->getCurrentFrame()->isEditingScene()) { - // set the markers - int fromIndex, toIndex, dummy; - XsheetGUI::getPlayRange(fromIndex, toIndex, dummy); - m_flipConsole->setMarkers(fromIndex, toIndex); - } - // if in the level editing mode, ignore the preview marker - else - m_flipConsole->setMarkers(0, -1); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::playAudioFrame(int frame) { - if (m_first) { - m_first = false; - m_fps = TApp::instance() - ->getCurrentScene() - ->getScene() - ->getProperties() - ->getOutputProperties() - ->getFrameRate(); - m_samplesPerFrame = m_sound->getSampleRate() / std::abs(m_fps); - } - if (!m_sound) return; - m_viewerFps = m_flipConsole->getCurrentFps(); - double s0 = frame * m_samplesPerFrame, s1 = s0 + m_samplesPerFrame; - // make the sound stop if the viewerfps is higher so the next sound can play - // on time. - if (m_fps < m_viewerFps) - TApp::instance()->getCurrentXsheet()->getXsheet()->stopScrub(); - TApp::instance()->getCurrentXsheet()->getXsheet()->play(m_sound, s0, s1, - false); -} - -//----------------------------------------------------------------------------- - -bool ComboViewerPanel::hasSoundtrack() { - if (m_sound != NULL) { - m_sound = NULL; - m_hasSoundtrack = false; - m_first = true; - } - TXsheetHandle *xsheetHandle = TApp::instance()->getCurrentXsheet(); - TXsheet::SoundProperties *prop = new TXsheet::SoundProperties(); - if (!m_sceneViewer->isPreviewEnabled()) prop->m_isPreview = true; - m_sound = xsheetHandle->getXsheet()->makeSound(prop); - if (m_sound == NULL) { - m_hasSoundtrack = false; - return false; - } else { - m_hasSoundtrack = true; - return true; - } -} - -void ComboViewerPanel::onButtonPressed(FlipConsole::EGadget button) { - if (button == FlipConsole::eSound) { - m_playSound = !m_playSound; - } -} - -void ComboViewerPanel::setFlipHButtonChecked(bool checked) { - m_flipConsole->setChecked(FlipConsole::eFlipHorizontal, checked); -} - -void ComboViewerPanel::setFlipVButtonChecked(bool checked) { - m_flipConsole->setChecked(FlipConsole::eFlipVertical, checked); -} - -//----------------------------------------------------------------------------- - -void ComboViewerPanel::setVisiblePartsFlag(UINT flag) { - m_visiblePartsFlag = flag; - updateShowHide(); -} - -// SaveLoadQSettings -void ComboViewerPanel::save(QSettings &settings, bool forPopupIni) const { - settings.setValue("visibleParts", m_visiblePartsFlag); -} - -void ComboViewerPanel::load(QSettings &settings) { - m_visiblePartsFlag = settings.value("visibleParts", CVPARTS_ALL).toUInt(); - updateShowHide(); +void ComboViewerPanel::checkOldVersionVisblePartsFlags(QSettings &settings) { + if (settings.contains("viewerVisibleParts") || + !settings.contains("visibleParts")) + return; + UINT oldVisiblePartsFlag = + settings.value("visibleParts", CVPARTS_ALL).toUInt(); + m_visiblePartsFlag = VPPARTS_None; + if (oldVisiblePartsFlag & CVPARTS_TOOLBAR) + m_visiblePartsFlag |= VPPARTS_TOOLBAR; + if (oldVisiblePartsFlag & CVPARTS_TOOLOPTIONS) + m_visiblePartsFlag |= VPPARTS_TOOLOPTIONS; + if (oldVisiblePartsFlag & CVPARTS_FLIPCONSOLE) + m_visiblePartsFlag |= VPPARTS_PLAYBAR | VPPARTS_FRAMESLIDER; } \ No newline at end of file diff --git a/toonz/sources/toonz/comboviewerpane.h b/toonz/sources/toonz/comboviewerpane.h index feecebb5..963cbc42 100644 --- a/toonz/sources/toonz/comboviewerpane.h +++ b/toonz/sources/toonz/comboviewerpane.h @@ -3,140 +3,34 @@ #ifndef COMBOVIEWER_PANE_INCLUDED #define COMBOVIEWER_PANE_INCLUDED -#include "sceneviewer.h" -#include "toonzqt/intfield.h" -#include "toonzqt/keyframenavigator.h" - -#include "toonzqt/flipconsoleowner.h" -#include "saveloadqsettings.h" - -#include - -class QPoint; -class QToolBar; -class QLabel; -class QSlider; -class QActionGroup; -class QButtonGroup; -class QToolBar; -class Ruler; +#include "viewerpane.h" class Toolbar; -class TPanel; -class Ruler; -class FlipConsole; -class TXshLevel; class ToolOptions; //============================================================================= // ComboViewerPanel //----------------------------------------------------------------------------- -enum CV_Parts { - CVPARTS_None = 0, - CVPARTS_TOOLBAR = 0x1, - CVPARTS_TOOLOPTIONS = 0x2, - CVPARTS_PLAYBAR = 0x4, - CVPARTS_FRAMESLIDER = 0x8, - CVPARTS_End = 0x16, - CVPARTS_ALL = CVPARTS_TOOLBAR | CVPARTS_TOOLOPTIONS | CVPARTS_PLAYBAR | - CVPARTS_FRAMESLIDER -}; -//----------------------------------------------------------------------------- - -class ComboViewerPanel final : public QFrame, - public FlipConsoleOwner, - public SaveLoadQSettings { +class ComboViewerPanel final : public BaseViewerPanel { Q_OBJECT - SceneViewer *m_sceneViewer; - FlipConsole *m_flipConsole; - ViewerKeyframeNavigator *m_keyFrameButton; - TPanelTitleBarButtonSet *m_referenceModeBs; - Toolbar *m_toolbar; ToolOptions *m_toolOptions; Ruler *m_vRuler; Ruler *m_hRuler; - UINT m_visiblePartsFlag; - bool m_onionSkinActive = false; - bool m_playSound = true; - bool m_hasSoundtrack = false; - bool m_playing = false; - double m_fps; - int m_viewerFps; - double m_samplesPerFrame; - bool m_first = true; - TSoundTrack *m_sound = NULL; - - TPanelTitleBarButton *m_previewButton; - TPanelTitleBarButton *m_subcameraPreviewButton; public: -#if QT_VERSION >= 0x050500 ComboViewerPanel(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - ComboViewerPanel(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif - ~ComboViewerPanel(); + ~ComboViewerPanel() {} - SceneViewer *getSceneViewer() { return m_sceneViewer; } ToolOptions *getToolOptions() { return m_toolOptions; } - // toggle show/hide of the widgets according to m_visiblePartsFlag - void setVisiblePartsFlag(UINT flag); - void updateShowHide(); - void addShowHideContextMenu(QMenu *); - - void onDrawFrame(int frame, const ImagePainter::VisualSettings &settings, - QElapsedTimer *timer, qint64 targetInstant) override; - - void onEnterPanel() { - m_sceneViewer->setFocus(Qt::OtherFocusReason); - // activate shortcut key for this flipconsole - m_flipConsole->makeCurrent(); - } - void onLeavePanel() { m_sceneViewer->clearFocus(); } - - // SaveLoadQSettings - virtual void save(QSettings &settings, - bool forPopupIni = false) const override; - virtual void load(QSettings &settings) override; - - void initializeTitleBar(TPanelTitleBar *titleBar); + void updateShowHide() override; + void addShowHideContextMenu(QMenu *) override; protected: - void showEvent(QShowEvent *) override; - void hideEvent(QHideEvent *) override; - void createFrameToolBar(); - void createPlayToolBar(); - void addColorMaskButton(QWidget *parent, const char *iconSVGName, int id); - // void contextMenuEvent(QContextMenuEvent *event) override; - void playAudioFrame(int frame); - bool hasSoundtrack(); - -public slots: - void onSceneChanged(); - void changeWindowTitle(); - void updateFrameRange(); - void onXshLevelSwitched(TXshLevel *); - void onPlayingStatusChanged(bool playing); - // for showing/hiding the parts - void onShowHideActionTriggered(QAction *); - void enableFlipConsoleForCamerastand(bool on); - void onButtonPressed(FlipConsole::EGadget button); - void setFlipHButtonChecked(bool checked); - void setFlipVButtonChecked(bool checked); - -protected slots: - void onFrameChanged(); - - // need to update the preview marker as well as the frame range in flipconsole - void onFrameTypeChanged(); - - void onSceneSwitched(); - void enableFullPreview(bool enabled); - void enableSubCameraPreview(bool enabled); + void checkOldVersionVisblePartsFlags(QSettings &settings) override; }; #endif diff --git a/toonz/sources/toonz/commandbar.cpp b/toonz/sources/toonz/commandbar.cpp index 9103f76b..a8e8fb42 100644 --- a/toonz/sources/toonz/commandbar.cpp +++ b/toonz/sources/toonz/commandbar.cpp @@ -29,12 +29,8 @@ // Toolbar //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 CommandBar::CommandBar(QWidget *parent, Qt::WindowFlags flags, bool isCollapsible, bool isQuickToolbar) -#else -CommandBar::CommandBar(QWidget *parent, Qt::WFlags flags) -#endif : QToolBar(parent) , m_isCollapsible(isCollapsible) , m_isQuickToolbar(isQuickToolbar) @@ -46,7 +42,7 @@ CommandBar::CommandBar(QWidget *parent, Qt::WFlags flags) m_barId = date.toString("yyyyMMddhhmmss"); } // Sets up default. - fillToolbar(this); + fillToolbar(this, m_isQuickToolbar, m_barId); setIconSize(QSize(20, 20)); QIcon moreIcon(":Resources/more.svg"); QToolButton *more = findChild("qt_toolbar_ext_button"); diff --git a/toonz/sources/toonz/commandbar.h b/toonz/sources/toonz/commandbar.h index 8dbf57f5..3e3ba2aa 100644 --- a/toonz/sources/toonz/commandbar.h +++ b/toonz/sources/toonz/commandbar.h @@ -29,12 +29,8 @@ protected: QString m_barId; public: -#if QT_VERSION >= 0x050500 CommandBar(QWidget *parent = 0, Qt::WindowFlags flags = 0, bool isCollapsible = false, bool isQuickToolbar = false); -#else - CommandBar(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif QString getBarId() { return m_barId; } diff --git a/toonz/sources/toonz/commandbarpopup.cpp b/toonz/sources/toonz/commandbarpopup.cpp index 024bb13e..11fe9f40 100644 --- a/toonz/sources/toonz/commandbarpopup.cpp +++ b/toonz/sources/toonz/commandbarpopup.cpp @@ -522,7 +522,6 @@ CommandBarPopup::CommandBarPopup(QString barId, bool isQuickToolbar) QLineEdit* searchEdit = new QLineEdit(this); //--- layout - QVBoxLayout* mainLay = new QVBoxLayout(); m_topLayout->setMargin(0); m_topLayout->setSpacing(0); { diff --git a/toonz/sources/toonz/convertfolderpopup.cpp b/toonz/sources/toonz/convertfolderpopup.cpp new file mode 100644 index 00000000..bc02389c --- /dev/null +++ b/toonz/sources/toonz/convertfolderpopup.cpp @@ -0,0 +1,512 @@ + +#include "convertfolderpopup.h" + +// Tnz6 includes +#include "menubarcommandids.h" +#include "tapp.h" +#include "filebrowser.h" +#include "filebrowserpopup.h" + +// TnzQt includes +#include "toonzqt/gutil.h" +#include "toonzqt/imageutils.h" +#include "toonzqt/menubarcommand.h" +#include "toonzqt/filefield.h" +#include "toonzqt/checkbox.h" +#include "toonzqt/icongenerator.h" + +// TnzLib includes +#include "toonz/tscenehandle.h" +#include "toonz/toonzscene.h" +#include "toonz/sceneproperties.h" +#include "toonz/tproject.h" + +// TnzCore includes +#include "tsystem.h" +#include "tfiletype.h" +#include "tlevel_io.h" +#include "tiio.h" +#include "tenv.h" + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ConvertFolderPopup::Converter final : public QThread { + int m_skippedCount; + int m_doneCount; + ConvertFolderPopup* m_parent; + QString m_logTxt; + +public: + Converter(ConvertFolderPopup* parent, QString logTxt) + : m_parent(parent), m_skippedCount(0), m_doneCount(0), m_logTxt(logTxt) {} + void run() override; + void convertLevel(const TFilePath& fp); + int getSkippedCount() const { return m_skippedCount; } + int getDoneCount() const { return m_doneCount; } + QString logTxt() const { return m_logTxt; } +}; + +void ConvertFolderPopup::Converter::run() { + ToonzScene* sc = TApp::instance()->getCurrentScene()->getScene(); + DVGui::ProgressDialog* progressDialog = m_parent->m_progressDialog; + int levelCount = m_parent->m_srcFilePaths.size(); + + for (int i = 0; !m_parent->m_notifier->abortTask() && i < levelCount; i++) { + TFilePath sourceLevelPath = sc->decodeFilePath(m_parent->m_srcFilePaths[i]); + QString levelName = QString::fromStdString(sourceLevelPath.getLevelName()); + + TFilePath dstFolder = sourceLevelPath.getParentDir(); + // check already existent levels + TFilePath dstFilePath = + m_parent->getDestinationFilePath(m_parent->m_srcFilePaths[i]); + + if (TSystem::doesExistFileOrLevel(dstFilePath)) { + if (m_parent->m_skip->isChecked()) { + m_logTxt.append(tr("Level %1 already exists; skipped.\n") + .arg(dstFilePath.getQString())); + m_skippedCount++; + continue; + } else { + bool ok = TSystem::removeFileOrLevel(dstFilePath); + if (!ok) { + m_logTxt.append(tr("Failed to remove existing level %1; skipped.\n") + .arg(dstFilePath.getQString())); + m_skippedCount++; + continue; + } + } + } + + progressDialog->setLabelText(QString(tr("Converting level %1 of %2: %3") + .arg(i + 1) + .arg(levelCount) + .arg(levelName))); + + convertLevel(sourceLevelPath); + } + if (m_parent->m_notifier->abortTask()) + m_logTxt.append(tr("Convert aborted.\n")); +} + +void ConvertFolderPopup::Converter::convertLevel( + const TFilePath& sourceFileFullPath) { + ToonzScene* sc = TApp::instance()->getCurrentScene()->getScene(); + ConvertFolderPopup* popup = m_parent; + + QString levelName = QString::fromStdString(sourceFileFullPath.getLevelName()); + TFilePath dstFileFullPath = popup->getDestinationFilePath(sourceFileFullPath); + + TFrameId from, to; + + if (TFileType::isLevelFilePath(sourceFileFullPath)) { + popup->getFrameRange(sourceFileFullPath, from, to); + + if (from == TFrameId() || to == TFrameId()) { + m_logTxt.append(tr("Level %1 has no frame; skipped.") + .arg(sourceFileFullPath.getQString())); + popup->m_notifier->notifyError(); + return; + } + } + // convert old levels (tzp/tzu) to tlv + ImageUtils::convertOldLevel2Tlv(sourceFileFullPath, dstFileFullPath, from, to, + m_parent->m_notifier); + m_doneCount++; + popup->m_notifier->notifyLevelCompleted(dstFileFullPath); +} + +//================================================================== +// SaveLogTxtPopup +//================================================================== + +class SaveLogTxtPopup final : public FileBrowserPopup { + QString m_logTxt; + +public: + SaveLogTxtPopup(QString txt) + : FileBrowserPopup(QObject::tr("Save log text")), m_logTxt(txt) { + setModal(true); + addFilterType("txt"); + setOkText(QObject::tr("Save")); + } + + bool execute() override { + if (m_selectedPaths.empty()) return false; + + TFilePath savePath(*m_selectedPaths.begin()); + if (savePath.isEmpty()) return false; + + savePath = savePath.withNoFrame().withType("txt"); // Just to be sure + if (TFileStatus(savePath).doesExist()) { + int ret = DVGui::MsgBox( + QObject::tr( + "The log file already exists.\n Do you want to overwrite it?") + .arg(toQString(savePath.withoutParentDir())), + QObject::tr("Overwrite"), QObject::tr("Don't Overwrite"), 0); + + if (ret == 2) return false; + } + + ToonzScene* sc = TApp::instance()->getCurrentScene()->getScene(); + savePath = sc->decodeFilePath(savePath); + + QFile file(savePath.getQString()); + if (!file.open(QIODevice::WriteOnly)) { + DVGui::error(tr("Failed to open the file %1").arg(savePath.getQString())); + return false; + } + QTextStream out(&file); + out << m_logTxt; + file.close(); + + return true; + } +}; +//============================================================================= + +ConvertResultPopup::ConvertResultPopup(QString log, TFilePath path) + : QDialog(), m_logTxt(log), m_targetPath(path) { + setModal(true); + + QTextEdit* edit = new QTextEdit(this); + QPushButton* saveLogButton = new QPushButton(tr("Save log file.."), this); + QPushButton* closeButton = new QPushButton(tr("Close"), this); + + edit->setPlainText(m_logTxt); + edit->setReadOnly(true); + + QVBoxLayout* mainLay = new QVBoxLayout(); + mainLay->setMargin(5); + mainLay->setSpacing(10); + { + mainLay->addWidget(edit, 1); + mainLay->addWidget(new QLabel(tr("Do you want to save the log?")), 0); + + QHBoxLayout* buttonsLay = new QHBoxLayout(); + buttonsLay->setMargin(0); + buttonsLay->setSpacing(10); + buttonsLay->setAlignment(Qt::AlignCenter); + { + buttonsLay->addWidget(saveLogButton, 0); + buttonsLay->addWidget(closeButton, 0); + } + mainLay->addLayout(buttonsLay, 0); + } + setLayout(mainLay); + + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + connect(saveLogButton, SIGNAL(clicked()), this, SLOT(onSaveLog())); +} + +void ConvertResultPopup::onSaveLog() { + SaveLogTxtPopup popup(m_logTxt); + + popup.setFolder(m_targetPath); + popup.setFilename(TFilePath("tzp_convert_log.txt")); + if (popup.exec() == QDialog::Accepted) close(); +} + +//============================================================================= + +ConvertFolderPopup::ConvertFolderPopup() + : Dialog(TApp::instance()->getMainWindow(), true, false, + "ConvertTZPInFolder") + , m_converter(0) + , m_isConverting(false) { + setModal(false); + setWindowTitle(tr("Convert TZP In Folder")); + + m_okBtn = new QPushButton(tr("Convert"), this); + m_cancelBtn = new QPushButton(tr("Cancel"), this); + + m_notifier = new ImageUtils::FrameTaskNotifier(); + + m_progressDialog = new DVGui::ProgressDialog("", tr("Cancel"), 0, 0); + m_skip = new DVGui::CheckBox(tr("Skip Existing Files"), this); + m_subfolder = new DVGui::CheckBox(tr("Apply to Subfolder"), this); + + m_convertFolderFld = new DVGui::FileField(0, QString(""), true); + + m_srcFileList = new QListWidget(this); + + m_convertFolderFld->setFileMode( + QFileDialog::Directory); // implies ShowDirsOnly + //----------------------- + + m_progressDialog->setWindowTitle(tr("Convert TZP in Folder")); + m_progressDialog->setWindowFlags( + Qt::Dialog | Qt::WindowTitleHint); // Don't show ? and X buttons + m_progressDialog->setWindowModality(Qt::WindowModal); + + //----layout + m_topLayout->setMargin(5); + m_topLayout->setSpacing(5); + { + QHBoxLayout* folderLay = new QHBoxLayout(); + folderLay->setMargin(0); + folderLay->setSpacing(5); + { + folderLay->addWidget(new QLabel(tr("Folder to convert:"), this), 0); + folderLay->addWidget(m_convertFolderFld, 1); + } + m_topLayout->addLayout(folderLay, 0); + + QHBoxLayout* mainLay = new QHBoxLayout(); + mainLay->setMargin(0); + mainLay->setSpacing(5); + { + QVBoxLayout* leftLay = new QVBoxLayout(); + leftLay->setMargin(0); + leftLay->setSpacing(5); + { + leftLay->addWidget(m_skip, 0); + leftLay->addWidget(m_subfolder, 0); + leftLay->addStretch(1); + } + mainLay->addLayout(leftLay, 0); + + mainLay->addWidget(m_srcFileList, 1); + } + m_topLayout->addLayout(mainLay, 1); + } + m_buttonLayout->setMargin(0); + m_buttonLayout->setSpacing(20); + { + m_buttonLayout->addWidget(m_okBtn); + m_buttonLayout->addWidget(m_cancelBtn); + } + + //--- signal-slot connections + qRegisterMetaType("TFilePath"); + + bool ret = true; + ret = ret && connect(m_okBtn, SIGNAL(clicked()), this, SLOT(apply())); + ret = ret && connect(m_cancelBtn, SIGNAL(clicked()), this, SLOT(reject())); + ret = ret && connect(m_notifier, SIGNAL(frameCompleted(int)), + m_progressDialog, SLOT(setValue(int))); + ret = ret && connect(m_notifier, SIGNAL(levelCompleted(const TFilePath&)), + this, SLOT(onLevelConverted(const TFilePath&))); + ret = ret && connect(m_progressDialog, SIGNAL(canceled()), m_notifier, + SLOT(onCancelTask())); + + ret = ret && connect(m_convertFolderFld, SIGNAL(pathChanged()), this, + SLOT(onFileInFolderChanged())); + ret = ret && connect(m_skip, SIGNAL(clicked()), this, SLOT(onSkipChanged())); + ret = ret && connect(m_subfolder, SIGNAL(clicked()), this, + SLOT(onSubfolderChanged())); + + assert(ret); +} + +//------------------------------------------------------------------ + +ConvertFolderPopup::~ConvertFolderPopup() { + delete m_notifier; + delete m_progressDialog; + delete m_converter; +} + +//------------------------------------------------------------------ + +void ConvertFolderPopup::setFiles() { + if (m_convertFolderFld->getPath().isEmpty()) { + m_srcFilePaths.clear(); + m_srcFileList->clear(); + return; + } + ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene(); + TFilePath srcFolderPath = scene->decodeFilePath( + TFilePath(m_convertFolderFld->getPath().toStdString())); + + bool skip = m_skip->isChecked(); + bool subFolder = m_subfolder->isChecked(); + + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + TFilePathSet fps; + if (subFolder) + TSystem::readDirectoryTree(fps, srcFolderPath, true, true); + else + TSystem::readDirectory(fps, srcFolderPath, true, true); + + m_srcFilePaths.clear(); + QStringList tgtItems; + QStringList skipItems; + for (const auto fp : fps) { + // applies to only tzp + if (fp.getType() != "tzp") continue; + + // check the destination file existence + TFilePath dstFp = getDestinationFilePath(fp); + TFilePath relPath = fp - srcFolderPath; + if (!TSystem::doesExistFileOrLevel(dstFp)) { + m_srcFilePaths.push_back(fp); + tgtItems.append(relPath.getQString()); + } else if (skip) + skipItems.append(tr("[SKIP] ") + relPath.getQString()); + else { + tgtItems.append(tr("[OVERWRITE] ") + relPath.getQString()); + m_srcFilePaths.push_back(fp); + } + } + + m_srcFileList->clear(); + m_srcFileList->addItems(tgtItems); + m_srcFileList->addItems(skipItems); + + QGuiApplication::restoreOverrideCursor(); +} + +//------------------------------------------------------------------ + +void ConvertFolderPopup::apply() { + if (m_convertFolderFld->getPath().isEmpty()) { + DVGui::error(tr("Target folder is not specified.")); + return; + } + if (m_srcFilePaths.empty()) { + DVGui::error(tr("No files will be converted.")); + return; + } + + QMessageBox::StandardButton btn = QMessageBox::question( + this, tr("Cofirmation"), + tr("Converting %1 files. Are you sure?").arg(m_srcFilePaths.size()), + QMessageBox::Yes | QMessageBox::No); + if (btn != QMessageBox::Yes) return; + + // parameters are ok: close the dialog first + close(); + + m_isConverting = true; + m_progressDialog->reset(); + m_progressDialog->setMinimum(0); + m_progressDialog->setMaximum(100); + m_progressDialog->show(); + m_notifier->reset(); + QApplication::setOverrideCursor(Qt::WaitCursor); + + QString logTxt = + tr("Convert TZP in folder\n") + + tr("Target Folder: %1\n").arg(m_convertFolderFld->getPath()) + + tr("Skip Existing Files: %1\n") + .arg((m_skip->isChecked()) ? "True" : "False") + + tr("Apply to Subfolder: %1\n") + .arg((m_subfolder->isChecked()) ? "True" : "False") + + tr("Approx. levels to be converted: %1\n\n").arg(m_srcFilePaths.size()) + + tr("Started: ") + QDateTime::currentDateTime().toString() + "\n"; + m_converter = new Converter(this, logTxt); + bool ret = + connect(m_converter, SIGNAL(finished()), this, SLOT(onConvertFinished())); + Q_ASSERT(ret); + + // start converting. Conversion end is handled by onConvertFinished() slot + m_converter->start(); +} + +//------------------------------------------------------------------ + +void ConvertFolderPopup::onConvertFinished() { + m_isConverting = false; + TFilePath dstFolderPath(m_convertFolderFld->getPath().toStdWString()); + FileBrowser::refreshFolder(dstFolderPath); + + m_progressDialog->close(); + QApplication::restoreOverrideCursor(); + + QString logTxt = m_converter->logTxt(); + // opens result + + int errorCount = m_notifier->getErrorCount(); + int skippedCount = m_converter->getSkippedCount(); + int doneCount = m_converter->getDoneCount(); + + if (m_notifier->abortTask()) + logTxt.append(tr("Convert aborted:")); + else + logTxt.append(tr("Convert completed:")); + + logTxt.append( + tr(" %1 level(s) done, %2 level(s) skipped with %3 error(s).\n") + .arg(doneCount) + .arg(skippedCount) + .arg(errorCount)); + logTxt.append(tr("Ended: ") + QDateTime::currentDateTime().toString()); + + delete m_converter; + m_converter = 0; + + ConvertResultPopup resultPopup(logTxt, dstFolderPath); + resultPopup.exec(); +} + +//------------------------------------------------------------------ + +void ConvertFolderPopup::onLevelConverted(const TFilePath& fullPath) { + IconGenerator::instance()->invalidate(fullPath); +} + +//------------------------------------------------------------------ + +void ConvertFolderPopup::onFileInFolderChanged() { setFiles(); } + +//------------------------------------------------------------------ + +void ConvertFolderPopup::onSkipChanged() { setFiles(); } + +//------------------------------------------------------------------ + +void ConvertFolderPopup::onSubfolderChanged() { setFiles(); } + +//------------------------------------------------------------------ + +TFilePath ConvertFolderPopup::getDestinationFilePath( + const TFilePath& sourceFilePath) { + TFilePath destFolder = sourceFilePath.getParentDir(); + ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene(); + + // Build the output level name + const std::string& ext = "tlv"; + const std::wstring& name = sourceFilePath.getWideName(); + + TFilePath destName = TFilePath(name).withType(ext); + + // Merge the two + return destFolder + destName; +} + +//------------------------------------------------------------------ + +void ConvertFolderPopup::getFrameRange(const TFilePath& sourceFilePath, + TFrameId& from, TFrameId& to) { + from = to = TFrameId(); + + if (!TFileType::isLevelFilePath(sourceFilePath)) return; + + TLevelReaderP lr(sourceFilePath); + if (!lr) return; + + TLevelP level = lr->loadInfo(); + if (level->begin() == level->end()) return; + + from = level->begin()->first; + to = (--level->end())->first; +} + +//============================================================================= +// ConvertFolderPopup +//----------------------------------------------------------------------------- + +OpenPopupCommandHandler openConvertTZPInFolderPopup( + MI_ConvertTZPInFolder); \ No newline at end of file diff --git a/toonz/sources/toonz/convertfolderpopup.h b/toonz/sources/toonz/convertfolderpopup.h new file mode 100644 index 00000000..2d25b48e --- /dev/null +++ b/toonz/sources/toonz/convertfolderpopup.h @@ -0,0 +1,107 @@ +#pragma once + +#ifndef CONVERTFOLDERPOPUP_H +#define CONVERTFOLDERPOPUP_H + +// TnzQt includes +#include "toonzqt/dvdialog.h" + +// TnzCore includes +#include "tfilepath.h" + +// Qt includes +#include +#include +#include + +//============================================================== + +// Forward declarations + +class TPalette; +class TPropertyGroup; +class ToonzScene; + +class QCheckBox; +class QLabel; +class QListWidget; + +namespace DVGui { +class FileField; +class ProgressDialog; +class CheckBox; +} // namespace DVGui + +namespace ImageUtils { +class FrameTaskNotifier; +} + +//============================================================== + +//***************************************************************************** +// ConvertPopup declaration +//***************************************************************************** + +/*! + \brief Window class used in the conversion of levels between different + file formats. +*/ + +class ConvertFolderPopup : public DVGui::Dialog { + Q_OBJECT + +public: + ConvertFolderPopup(); + ~ConvertFolderPopup(); + + void setFiles(); + bool isConverting() const { return m_isConverting; } + +public slots: + + void apply(); //!< Starts the conversion. + void onConvertFinished(); + void onLevelConverted(const TFilePath& fullPath); + void onFileInFolderChanged(); + void onSkipChanged(); + void onSubfolderChanged(); + +protected: + TFilePath getDestinationFilePath(const TFilePath& sourceFilePath); + void getFrameRange(const TFilePath& sourceFilePath, TFrameId& from, + TFrameId& to); + + void showEvent(QShowEvent* e) override { setFiles(); } + +private: + DVGui::FileField* m_convertFolderFld; + DVGui::CheckBox *m_skip, *m_subfolder; + + QPushButton *m_okBtn, *m_cancelBtn; + + QListWidget* m_srcFileList; + + class Converter; + Converter* m_converter; + + ImageUtils::FrameTaskNotifier* m_notifier; + DVGui::ProgressDialog* m_progressDialog; + + std::vector m_srcFilePaths; + + bool m_isConverting; +}; + +class ConvertResultPopup : public QDialog { + Q_OBJECT + + QString m_logTxt; + TFilePath m_targetPath; + +public: + ConvertResultPopup(QString log, TFilePath path); +protected slots: + void onSaveLog(); +}; + +#endif // CONVERTPOPUP_H \ No newline at end of file diff --git a/toonz/sources/toonz/convertpopup.cpp b/toonz/sources/toonz/convertpopup.cpp index 6c1fddaf..5636dfdd 100644 --- a/toonz/sources/toonz/convertpopup.cpp +++ b/toonz/sources/toonz/convertpopup.cpp @@ -585,7 +585,8 @@ QFrame *ConvertPopup::createTlvSettings() { m_dpiMode = new QComboBox(); m_dpiFld = new DVGui::DoubleLineEdit(); - m_unpaintedFolder->setFileMode(QFileDialog::DirectoryOnly); + m_unpaintedFolder->setFileMode( + QFileDialog::Directory); // implies ShowDirsOnly m_unpaintedSuffix->setMaximumWidth(40); QStringList items1; items1 << tr("Keep Original Antialiasing") @@ -664,11 +665,11 @@ QFrame *ConvertPopup::createTlvSettings() { bool ret = true; ret = ret && connect(m_antialias, SIGNAL(currentIndexChanged(int)), this, - SLOT(onAntialiasSelected(int))); + SLOT(onAntialiasSelected(int))); ret = ret && connect(m_palettePath, SIGNAL(pathChanged()), this, - SLOT(onPalettePathChanged())); + SLOT(onPalettePathChanged())); ret = ret && connect(m_dpiMode, SIGNAL(currentIndexChanged(int)), this, - SLOT(onDpiModeSelected(int))); + SLOT(onDpiModeSelected(int))); assert(ret); @@ -1186,13 +1187,8 @@ void ConvertPopup::apply() { QApplication::setOverrideCursor(Qt::WaitCursor); m_converter = new Converter(this); -#if QT_VERSION >= 0x050000 bool ret = connect(m_converter, SIGNAL(finished()), this, SLOT(onConvertFinished())); -#else - int ret = - connect(m_converter, SIGNAL(finished()), this, SLOT(onConvertFinished())); -#endif Q_ASSERT(ret); // TODO: salvare il vecchio stato diff --git a/toonz/sources/toonz/custompaneleditorpopup.cpp b/toonz/sources/toonz/custompaneleditorpopup.cpp new file mode 100644 index 00000000..6024c471 --- /dev/null +++ b/toonz/sources/toonz/custompaneleditorpopup.cpp @@ -0,0 +1,893 @@ +#include "custompaneleditorpopup.h" + +// Tnz includes +#include "tapp.h" +#include "menubarcommandids.h" +#include "shortcutpopup.h" +#include "custompanelmanager.h" + +// TnzQt includes +#include "toonzqt/gutil.h" + +// ToonzLib +#include "toonz/toonzfolders.h" + +// ToonzCore +#include "tsystem.h" + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +const TFilePath CustomPanelTemplateFolderName("custom panel templates"); +const TFilePath customPaneTemplateFolderPath() { + return ToonzFolder::getLibraryFolder() + CustomPanelTemplateFolderName; +} +const TFilePath CustomPanelFolderName("custompanels"); +const TFilePath customPaneFolderPath() { + return ToonzFolder::getMyModuleDir() + CustomPanelFolderName; +} + +QPoint relativePos(QWidget* child, QWidget* refParent) { + if (child->parentWidget() == refParent) + return child->pos(); + else + return child->pos() + relativePos(child->parentWidget(), refParent); +} + +} // namespace + +//============================================================================= +// CustomPanelUIField +//----------------------------------------------------------------------------- + +CustomPanelUIField::CustomPanelUIField(const int objId, + const QString objectName, + QWidget* parent, bool isFirst) + : QLabel(tr("Drag and set command"), parent), m_id(objId) { + QFont fnt = font(); + fnt.setPointSize(12); + setFont(fnt); + setStyleSheet("background-color: rgb(255, 255, 128); color: black;"); + setAcceptDrops(true); + + // objName may be a commandId + if (setCommand(objectName)) return; + + if (objectName.startsWith("HScroller") || + objectName.startsWith("VScroller")) { + QStringList ids = objectName.split("__"); + if (ids.size() == 3) { + setCommand((isFirst) ? ids[1] : ids[2]); + } + } +} + +bool CustomPanelUIField::setCommand(QString commandId) { + if (m_commandId == commandId) return false; + if (commandId.isEmpty()) { + m_commandId = commandId; + setText(tr("Drag and set command")); + setStyleSheet("background-color: rgb(255, 255, 128); color: black;"); + return true; + } + + QAction* action = + CommandManager::instance()->getAction(commandId.toStdString().c_str()); + if (!action) return false; + + m_commandId = commandId; + QString tempText = action->text(); + // removing accelerator key indicator + tempText = tempText.replace(QRegExp("&([^& ])"), "\\1"); + // removing doubled &s + tempText = tempText.replace("&&", "&"); + setText(tempText); + setStyleSheet("background-color: rgb(230, 230, 230); color: black;"); + return true; +} + +void CustomPanelUIField::enterEvent(QEvent* event) { emit highlight(m_id); } +void CustomPanelUIField::leaveEvent(QEvent* event) { emit highlight(-1); } +void CustomPanelUIField::dragEnterEvent(QDragEnterEvent* event) { + QString txt = event->mimeData()->text(); + if (CommandManager::instance()->getAction(txt.toStdString().c_str())) { + event->setDropAction(Qt::CopyAction); + event->accept(); + } + emit highlight(m_id); +} + +void CustomPanelUIField::dragLeaveEvent(QDragLeaveEvent* event) { + emit highlight(-1); +} + +void CustomPanelUIField::dropEvent(QDropEvent* event) { + QString oldCommandId = m_commandId; + QString commandId = event->mimeData()->text(); + if (setCommand(commandId)) { + // if dragged from the command tree, command can be duplicated + if (event->dropAction() == Qt::CopyAction) + emit commandChanged(QString(), QString()); + else + emit commandChanged(oldCommandId, m_commandId); + } +} + +void CustomPanelUIField::mousePressEvent(QMouseEvent* event) { + if (m_commandId.isEmpty()) return; + QMimeData* mimeData = new QMimeData; + mimeData->setText(m_commandId); + + QString dragPixmapTxt = text(); + QFontMetrics fm(QApplication::font()); + QPixmap pix(fm.boundingRect(dragPixmapTxt).adjusted(-2, -2, 2, 2).size()); + QPainter painter(&pix); + painter.fillRect(pix.rect(), Qt::white); + painter.setPen(Qt::black); + painter.drawText(pix.rect(), Qt::AlignCenter, dragPixmapTxt); + + QDrag* drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(pix); + + drag->exec(Qt::MoveAction); +} + +//============================================================================= +// UiPreviewWidget +//----------------------------------------------------------------------------- + +UiPreviewWidget::UiPreviewWidget(QPixmap uiPixmap, QList& uiEntries, + QWidget* parent) + : QWidget(parent), m_highlightUiId(-1), m_uiPixmap(uiPixmap) { + for (auto entry : uiEntries) m_rectTable.append(entry.rect); + setFixedSize(m_uiPixmap.size()); + setAcceptDrops(true); + setMouseTracking(true); +} + +void UiPreviewWidget::onViewerResize(QSize size) { + if (m_uiPixmap.isNull()) return; + setFixedSize(std::max(size.width(), m_uiPixmap.width()), + std::max(size.height(), m_uiPixmap.height())); +} + +void UiPreviewWidget::paintEvent(QPaintEvent*) { + QPainter p(this); + p.translate((width() - m_uiPixmap.width()) / 2, + (height() - m_uiPixmap.height()) / 2); + p.drawPixmap(0, 0, m_uiPixmap); + + for (int id = 0; id < m_rectTable.count(); id++) { + QRect uiRect = m_rectTable.at(id); + if (id == m_highlightUiId) + p.setBrush(QColor(0, 255, 255, 64)); + else + p.setBrush(Qt::NoBrush); + p.setPen(Qt::cyan); + p.drawRect(uiRect); + } +} + +void UiPreviewWidget::highlightUi(const int objId) { + m_highlightUiId = objId; + update(); +} + +void UiPreviewWidget::mousePressEvent(QMouseEvent* event) { + if (m_highlightUiId >= 0) emit clicked(m_highlightUiId); +} + +void UiPreviewWidget::onMove(const QPoint pos) { + QPoint offset((width() - m_uiPixmap.width()) / 2, + (height() - m_uiPixmap.height()) / 2); + + for (int id = 0; id < m_rectTable.size(); id++) { + if (m_rectTable.at(id).contains(pos - offset)) { + highlightUi(id); + return; + } + } + highlightUi(-1); +} + +void UiPreviewWidget::mouseMoveEvent(QMouseEvent* event) { + onMove(event->pos()); +} + +void UiPreviewWidget::dragEnterEvent(QDragEnterEvent* event) { + QString txt = event->mimeData()->text(); + if (CommandManager::instance()->getAction(txt.toStdString().c_str())) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } +} + +void UiPreviewWidget::dragMoveEvent(QDragMoveEvent* event) { + onMove(event->pos()); + + if (m_highlightUiId < 0) { + event->ignore(); + return; + } + + QString txt = event->mimeData()->text(); + if (CommandManager::instance()->getAction(txt.toStdString().c_str())) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } +} + +void UiPreviewWidget::dropEvent(QDropEvent* event) { + QString commandId = event->mimeData()->text(); + bool isDraggdFromTree = (event->dropAction() == Qt::CopyAction); + + emit dropped(m_highlightUiId, commandId, isDraggdFromTree); +} + +//----------------------------------------------------------------------------- + +UiPreviewArea::UiPreviewArea(QWidget* parent) : QScrollArea(parent) {} + +void UiPreviewArea::resizeEvent(QResizeEvent* event) { + if (widget()) { + UiPreviewWidget* previewWidget = dynamic_cast(widget()); + if (previewWidget) { + previewWidget->onViewerResize(event->size()); + } + } + + QScrollArea::resizeEvent(event); +} + +//============================================================================= +// CommandBarCommandItem +//----------------------------------------------------------------------------- + +class CustomPanelCommandItem final : public QTreeWidgetItem { + QAction* m_action; + +public: + CustomPanelCommandItem(QTreeWidgetItem* parent, QAction* action) + : QTreeWidgetItem(parent, UserType), m_action(action) { + setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | + Qt::ItemNeverHasChildren); + QString tempText = m_action->text(); + // removing accelerator key indicator + tempText = tempText.replace(QRegExp("&([^& ])"), "\\1"); + // removing doubled &s + tempText = tempText.replace("&&", "&"); + setText(0, tempText); + setToolTip(0, QObject::tr("[Drag] to move position")); + } + QAction* getAction() const { return m_action; } +}; + +//============================================================================= +// CustomPanelCommandListTree +//----------------------------------------------------------------------------- + +void CustomPanelCommandListTree::addFolder(const QString& title, + int commandType, + QTreeWidgetItem* parentFolder) { + QTreeWidgetItem* folder; + if (!parentFolder) + folder = new QTreeWidgetItem(this); + else + folder = new QTreeWidgetItem(parentFolder); + assert(folder); + folder->setText(0, title); + folder->setIcon(0, invisibleRootItem()->icon(0)); + + std::vector actions; + CommandManager::instance()->getActions((CommandType)commandType, actions); + for (int i = 0; i < (int)actions.size(); i++) { + CustomPanelCommandItem* item = + new CustomPanelCommandItem(folder, actions[i]); + item->setToolTip( + 0, QObject::tr( + "[Drag&Drop] to set command to control in the custom panel")); + } +} + +CustomPanelCommandListTree::CustomPanelCommandListTree(QWidget* parent) + : QTreeWidget(parent) { + setObjectName("SolidLineFrame"); + setAlternatingRowColors(true); + setDragEnabled(true); + setDragDropMode(QAbstractItemView::DragOnly); + setColumnCount(1); + setIconSize(QSize(21, 18)); + header()->close(); + + QIcon menuFolderIcon(createQIcon("folder_project", true)); + invisibleRootItem()->setIcon(0, menuFolderIcon); + + QTreeWidgetItem* menuCommandFolder = new QTreeWidgetItem(this); + menuCommandFolder->setFlags(Qt::ItemIsEnabled); + menuCommandFolder->setText(0, ShortcutTree::tr("Menu Commands")); + menuCommandFolder->setExpanded(true); + menuCommandFolder->setIcon(0, invisibleRootItem()->icon(0)); + + addFolder(ShortcutTree::tr("File"), MenuFileCommandType, menuCommandFolder); + addFolder(ShortcutTree::tr("Edit"), MenuEditCommandType, menuCommandFolder); + addFolder(ShortcutTree::tr("Scan & Cleanup"), MenuScanCleanupCommandType, + menuCommandFolder); + addFolder(ShortcutTree::tr("Level"), MenuLevelCommandType, menuCommandFolder); + addFolder(ShortcutTree::tr("Xsheet"), MenuXsheetCommandType, + menuCommandFolder); + addFolder(ShortcutTree::tr("Cells"), MenuCellsCommandType, menuCommandFolder); + addFolder(ShortcutTree::tr("Play"), MenuPlayCommandType, menuCommandFolder); + addFolder(ShortcutTree::tr("Render"), MenuRenderCommandType, + menuCommandFolder); + addFolder(ShortcutTree::tr("View"), MenuViewCommandType, menuCommandFolder); + addFolder(ShortcutTree::tr("Windows"), MenuWindowsCommandType, + menuCommandFolder); + addFolder(ShortcutTree::tr("Help"), MenuHelpCommandType, menuCommandFolder); + + addFolder(ShortcutTree::tr("Tools"), ToolCommandType); + addFolder(ShortcutTree::tr("Fill"), FillCommandType); + addFolder(ShortcutTree::tr("Right-click Menu Commands"), + RightClickMenuCommandType); + QTreeWidgetItem* rcmSubFolder = + invisibleRootItem()->child(invisibleRootItem()->childCount() - 1); + addFolder(ShortcutTree::tr("Cell Mark"), CellMarkCommandType, rcmSubFolder); + addFolder(ShortcutTree::tr("Tool Modifiers"), ToolModifierCommandType); + addFolder(ShortcutTree::tr("Visualization"), VisualizationButtonCommandType); + addFolder(ShortcutTree::tr("Misc"), MiscCommandType); + addFolder(ShortcutTree::tr("RGBA Channels"), RGBACommandType); + + sortItems(0, Qt::AscendingOrder); +} + +void CustomPanelCommandListTree::mousePressEvent(QMouseEvent* event) { + setCurrentItem(itemAt(event->pos())); + CustomPanelCommandItem* commandItem = + dynamic_cast(itemAt(event->pos())); + + if (commandItem) { + std::string dragStr; + QString dragPixmapTxt; + dragStr = + CommandManager::instance()->getIdFromAction(commandItem->getAction()); + dragPixmapTxt = commandItem->getAction()->text(); + dragPixmapTxt.remove("&"); + + QMimeData* mimeData = new QMimeData; + mimeData->setText(QString::fromStdString(dragStr)); + + QFontMetrics fm(QApplication::font()); + QPixmap pix(fm.boundingRect(dragPixmapTxt).adjusted(-2, -2, 2, 2).size()); + QPainter painter(&pix); + painter.fillRect(pix.rect(), Qt::white); + painter.setPen(Qt::black); + painter.drawText(pix.rect(), Qt::AlignCenter, dragPixmapTxt); + + QDrag* drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(pix); + + drag->exec(Qt::CopyAction); + } + + QTreeWidget::mousePressEvent(event); +} + +//============================================================================= +// CustomPanelEditorPopup +//----------------------------------------------------------------------------- + +bool CustomPanelEditorPopup::loadTemplateList() { + // library / custom panel templates‚Ì’†‚ðƒ`ƒFƒbƒN + TFilePath customPanelTemplateFolder = customPaneTemplateFolderPath(); + if (!TSystem::doesExistFileOrLevel(customPanelTemplateFolder)) { + DVGui::warning(tr("Template folder %1 not found.") + .arg(customPanelTemplateFolder.getQString())); + return false; + } + TFilePathSet fileList = + TSystem::readDirectory(customPanelTemplateFolder, false, true, false); + if (fileList.empty()) { + DVGui::warning(tr("Template files not found.")); + return false; + } + m_templateCombo->clear(); + QList fileNames; + for (auto file : fileList) { + // accept only .ui files + if (file.getType() != "ui") continue; + m_templateCombo->addItem(QString::fromStdString(file.getName()), + file.getQString()); + } + + int templateCount = m_templateCombo->count(); + + if (templateCount == 0) { + DVGui::warning(tr("Template files not found.")); + return false; + } + + // then, insert user custom panel + TFilePath customPanelsFolder = customPaneFolderPath(); + if (TSystem::doesExistFileOrLevel(customPanelsFolder)) { + TFilePathSet fileList2 = + TSystem::readDirectory(customPanelsFolder, false, true, false); + for (auto file : fileList2) { + // accept only .ui files + if (file.getType() != "ui") continue; + m_templateCombo->addItem( + tr("%1 (Edit)").arg(QString::fromStdString(file.getName())), + file.getQString()); + } + } + + if (m_templateCombo->count() > templateCount) + m_templateCombo->insertSeparator(templateCount); + + return true; +} + +//----------------------------------------------------------------------------- + +void CustomPanelEditorPopup::createFields() { + QList widgets = m_UiFieldsContainer->findChildren(); + foreach (QWidget* child, widgets) { + delete child; + } + + QGridLayout* gridLay; + if (m_UiFieldsContainer->layout()) + gridLay = dynamic_cast(m_UiFieldsContainer->layout()); + else { + gridLay = new QGridLayout(); + gridLay->setMargin(15); + gridLay->setHorizontalSpacing(10); + gridLay->setVerticalSpacing(15); + gridLay->setColumnStretch(0, 0); + gridLay->setColumnStretch(1, 1); + gridLay->setColumnStretch(2, 1); + m_UiFieldsContainer->setLayout(gridLay); + } + + // for each entry + int uiCounts[2] = {0, 0}; + QString labelStr[2] = {tr("Button"), tr("Scroller")}; + int id = 0; + int row = 0; + for (auto& entry : m_uiEntries) { + if (entry.type == Button || entry.type == Scroller_Back) { + entry.field = new CustomPanelUIField(id, entry.objectName, this); + QString label = + labelStr[(int)entry.type] + QString::number(uiCounts[entry.type] + 1); + uiCounts[entry.type]++; + gridLay->addWidget(new QLabel(label, this), row, 0, Qt::AlignRight); + gridLay->addWidget(entry.field, row, 1); + } else { // Scroller_Fore + entry.field = new CustomPanelUIField(id, entry.objectName, this, false); + gridLay->addWidget(entry.field, row, 2); + } + if (entry.type == Button || entry.type == Scroller_Fore) row++; + + connect(entry.field, SIGNAL(highlight(int)), this, SLOT(onHighlight(int))); + connect(entry.field, SIGNAL(commandChanged(QString, QString)), this, + SLOT(onCommandChanged(QString, QString))); + id++; + } +} + +//----------------------------------------------------------------------------- + +// create entries from a widget just loaded from .ui file +void CustomPanelEditorPopup::buildEntries(QWidget* customWidget) { + m_uiEntries.clear(); + + // this will define child widgets positions + customWidget->grab(); + + QList allWidgets = customWidget->findChildren(); + std::sort(allWidgets.begin(), allWidgets.end(), + [](const QWidget* a, const QWidget* b) -> bool { + return (a->pos().y() == b->pos().y()) + ? (a->pos().x() < b->pos().x()) + : (a->pos().y() < b->pos().y()); + }); + + for (auto widget : allWidgets) { + if (widget->objectName().isEmpty()) continue; + if (widget->layout() != nullptr) continue; + UiEntry entry; + + entry.type = (dynamic_cast(widget)) + ? (UiType)Button + : (UiType)Scroller_Back; + + entry.objectName = widget->objectName(); + if (entry.type == Button) + entry.rect = QRect(relativePos(widget, customWidget), widget->size()); + else { // Sroller_Back + entry.orientation = + (widget->width() > widget->height()) ? Qt::Horizontal : Qt::Vertical; + if (entry.orientation == Qt::Horizontal) + entry.rect = QRect(relativePos(widget, customWidget), + QSize(widget->width() / 2, widget->height())); + else + entry.rect = QRect(relativePos(widget, customWidget), + QSize(widget->width(), widget->height() / 2)); + } + m_uiEntries.append(entry); + + // register Scroller_Fore + if (entry.type == Scroller_Back) { + entry.type = (UiType)Scroller_Fore; + if (entry.orientation == Qt::Horizontal) + entry.rect.translate(widget->width() / 2, 0); + else + entry.rect.translate(0, widget->height() / 2); + + m_uiEntries.append(entry); + } + } +} + +//----------------------------------------------------------------------------- + +// update widget using the current entries +void CustomPanelEditorPopup::updateControls(QWidget* customWidget) { + QList allWidgets = customWidget->findChildren(); + for (auto widget : allWidgets) { + QList entryIds = entryIdByObjName(widget->objectName()); + if (entryIds.isEmpty()) continue; + UiEntry entry = m_uiEntries.at(entryIds.at(0)); + if (entry.type == Button) { + QString commandId = entry.field->commandId(); + QAction* action = CommandManager::instance()->getAction( + commandId.toStdString().c_str()); + if (!action) continue; + QAbstractButton* button = dynamic_cast(widget); + QToolButton* tb = dynamic_cast(widget); + CommandManager::instance()->enlargeIcon(commandId.toStdString().c_str(), + button->iconSize()); + if (tb) + tb->setDefaultAction(action); + else if (button) + button->setIcon(action->icon()); + } + } +} + +//----------------------------------------------------------------------------- + +void CustomPanelEditorPopup::onTemplateSwitched() { + QUiLoader loader; + QString fp = m_templateCombo->currentData().toString(); + QFile file(fp); + + file.open(QFile::ReadOnly); + QWidget* customWidget = loader.load(&file, 0); + file.close(); + + // create entries from a widget just loaded from .ui file + buildEntries(customWidget); + + // objectName of each widget will be overwritten in this function + CustomPanelManager::instance()->initializeControl(customWidget); + + // create UI fields + createFields(); + + UiPreviewWidget* previewWidget = + new UiPreviewWidget(customWidget->grab(), m_uiEntries, this); + + if (m_previewArea->widget()) { + UiPreviewWidget* oldPreview = + dynamic_cast(m_previewArea->widget()); + if (oldPreview) { + disconnect(oldPreview, SIGNAL(clicked(int)), this, + SLOT(onPreviewClicked(int))); + disconnect(oldPreview, SIGNAL(dropped(int, QString, bool)), this, + SLOT(onPreviewDropped(int, QString, bool))); + } + delete m_previewArea->widget(); + } + + connect(previewWidget, SIGNAL(clicked(int)), this, + SLOT(onPreviewClicked(int))); + connect(previewWidget, SIGNAL(dropped(int, QString, bool)), this, + SLOT(onPreviewDropped(int, QString, bool))); + + m_previewArea->setWidget(previewWidget); + previewWidget->onViewerResize(m_previewArea->size()); + + delete customWidget; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + if (customPaneFolderPath().isAncestorOf(TFilePath(fp))) + m_panelNameEdit->setText(m_templateCombo->currentText().chopped(7)); +#else + if (customPaneFolderPath().isAncestorOf(TFilePath(fp))) + m_panelNameEdit->setText(m_templateCombo->currentText().left(7)); +#endif + + updateGeometry(); +} +//----------------------------------------------------------------------------- + +void CustomPanelEditorPopup::onHighlight(int id) { + if (!m_previewArea->widget()) return; + UiPreviewWidget* previewWidget = + dynamic_cast(m_previewArea->widget()); + if (!previewWidget) return; + previewWidget->highlightUi(id); +} + +//----------------------------------------------------------------------------- +// set pixmap of updated ui to the preview +void CustomPanelEditorPopup::onCommandChanged(QString oldCmdId, + QString newCmdId) { + // If the command is dragged from another field, then swap the commands. + if (!newCmdId.isEmpty()) { + CustomPanelUIField* senderField = + dynamic_cast(sender()); + for (auto entry : m_uiEntries) { + if (!entry.field || entry.field == senderField) continue; + if (entry.field->commandId() == newCmdId) { + entry.field->setCommand(oldCmdId); + break; + } + } + } + + QString fp = m_templateCombo->currentData().toString(); + QFile tmplFile(fp); + tmplFile.open(QFile::ReadOnly); + + QUiLoader loader; + QWidget* customWidget = loader.load(&tmplFile, 0); + tmplFile.close(); + + updateControls(customWidget); + + UiPreviewWidget* previewWidget = + dynamic_cast(m_previewArea->widget()); + previewWidget->setUiPixmap(customWidget->grab()); + delete customWidget; +} + +void CustomPanelEditorPopup::onPreviewClicked(int id) { + CustomPanelUIField* field = m_uiEntries.at(id).field; + if (!field) return; + QString commandId = field->commandId(); + if (commandId.isEmpty()) return; + QMimeData* mimeData = new QMimeData; + mimeData->setText(commandId); + + QString dragPixmapTxt = field->text(); + QFontMetrics fm(QApplication::font()); + QPixmap pix(fm.boundingRect(dragPixmapTxt).adjusted(-2, -2, 2, 2).size()); + QPainter painter(&pix); + painter.fillRect(pix.rect(), Qt::white); + painter.setPen(Qt::black); + painter.drawText(pix.rect(), Qt::AlignCenter, dragPixmapTxt); + + QDrag* drag = new QDrag(sender()); + drag->setMimeData(mimeData); + drag->setPixmap(pix); + + drag->exec(Qt::MoveAction); +} + +void CustomPanelEditorPopup::onPreviewDropped(int id, QString cmdId, + bool fromTree) { + CustomPanelUIField* field = m_uiEntries.at(id).field; + if (!field) return; + + QString oldCommandId = field->commandId(); + if (field->setCommand(cmdId)) { + if (fromTree) + field->notifyCommandChanged(QString(), QString()); + else + field->notifyCommandChanged(oldCommandId, cmdId); + } +} + +//----------------------------------------------------------------------------- + +QList CustomPanelEditorPopup::entryIdByObjName(const QString objName) { + QList ret; + for (int i = 0; i < m_uiEntries.size(); i++) { + if (m_uiEntries[i].objectName == objName) ret.append(i); + } + return ret; +} + +void CustomPanelEditorPopup::replaceObjectNames(QDomElement& element) { + QDomNode n = element.firstChild(); + while (!n.isNull()) { + if (n.isElement()) { + QDomElement e = n.toElement(); + //Ž©•ªŽ©g‚ðƒ`ƒFƒbƒN + if (e.tagName() == "widget" && e.hasAttribute("name")) { + QString objName = e.attribute("name"); + QList entryIds = entryIdByObjName(objName); + if (!entryIds.isEmpty()) { + UiEntry entry = m_uiEntries.at(entryIds[0]); + + if (entry.type == Button) + e.setAttribute("name", entry.field->commandId()); + else { // Scroller + UiEntry entryFore = m_uiEntries.at(entryIds[1]); + QStringList newNameList; + newNameList.append((entry.orientation == Qt::Horizontal) + ? "HScroller" + : "VScroller"); + newNameList.append(entry.field->commandId()); + newNameList.append(entryFore.field->commandId()); + e.setAttribute("name", newNameList.join("__")); + } + } + } + // check recursively + replaceObjectNames(e); + } + n = n.nextSibling(); + } +} + +void CustomPanelEditorPopup::onRegister() { + QString panelName = m_panelNameEdit->text(); + if (panelName.isEmpty()) { + DVGui::warning(tr("Please input the panel name.")); + return; + } + // overwrite confirmation + TFilePath customPanelPath = + customPaneFolderPath() + TFilePath(panelName + ".ui"); + if (TSystem::doesExistFileOrLevel(customPanelPath)) { + QString question = + tr("The custom panel %1 already exists. Do you want to overwrite?") + .arg(panelName); + int ret = DVGui::MsgBox(question, tr("Overwrite"), tr("Cancel"), 0); + if (ret == 0 || ret == 2) { + return; + } + } + + // create folder if not exist + if (!TSystem::touchParentDir(customPanelPath)) { + DVGui::warning(tr("Failed to create folder.")); + return; + } + + // base template file + QDomDocument doc(panelName); + QFile tmplFile(m_templateCombo->currentData().toString()); + if (!tmplFile.open(QIODevice::ReadOnly)) { + DVGui::warning(tr("Failed to open the template.")); + return; + } + if (!doc.setContent(&tmplFile)) { + tmplFile.close(); + return; + } + tmplFile.close(); + + QDomElement docElem = doc.documentElement(); + replaceObjectNames(docElem); + + QFile file(customPanelPath.getQString()); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + DVGui::warning(tr("Failed to open the file for writing.")); + return; + } + QTextStream stream(&file); + stream.setCodec("UTF-8"); + stream << doc.toString(); + file.close(); + + CustomPanelManager::instance()->loadCustomPanelEntries(); + + close(); +} + +//----------------------------------------------------------------------------- + +CustomPanelEditorPopup::CustomPanelEditorPopup() + : Dialog(TApp::instance()->getMainWindow(), true, false, + "CustomPanelEditorPopup") { + setWindowTitle(tr("Custom Panel Editor")); + + m_commandListTree = new CustomPanelCommandListTree(this); + + QLabel* commandItemListLabel = new QLabel(tr("Command List"), this); + QFont f("Arial", 15, QFont::Bold); + commandItemListLabel->setFont(f); + + m_previewArea = new UiPreviewArea(this); + m_UiFieldsContainer = new QWidget(this); + m_templateCombo = new QComboBox(this); + m_panelNameEdit = new QLineEdit("My Custom Panel", this); + + QPushButton* registerButton = new QPushButton(tr("Register"), this); + QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); + + m_previewArea->setStyleSheet("background-color: black;"); + + beginHLayout(); + + QVBoxLayout* leftLay = new QVBoxLayout(); + leftLay->setMargin(0); + leftLay->setSpacing(10); + { + QHBoxLayout* templateLay = new QHBoxLayout(); + templateLay->setMargin(0); + templateLay->setSpacing(5); + { + templateLay->addWidget(new QLabel(tr("Template:"), this), 0); + templateLay->addWidget(m_templateCombo, 1, Qt::AlignLeft); + } + leftLay->addLayout(templateLay, 0); + leftLay->addWidget(m_UiFieldsContainer, 0); + leftLay->addWidget(m_previewArea, 1); + } + addLayout(leftLay); + + QVBoxLayout* rightLay = new QVBoxLayout(); + rightLay->setMargin(0); + rightLay->setSpacing(10); + { + rightLay->addWidget(commandItemListLabel, 0); + rightLay->addWidget(m_commandListTree, 1); + } + addLayout(rightLay); + + endHLayout(); + + m_buttonLayout->addStretch(1); + QHBoxLayout* nameLay = new QHBoxLayout(); + nameLay->setMargin(0); + nameLay->setSpacing(3); + { + nameLay->addWidget(new QLabel(tr("Panel name:"), this), 0); + nameLay->addWidget(m_panelNameEdit, 1); + } + m_buttonLayout->addLayout(nameLay, 0); + m_buttonLayout->addWidget(registerButton, 0); + m_buttonLayout->addSpacing(10); + m_buttonLayout->addWidget(cancelButton, 0); + + bool ret = true; + ret = ret && connect(cancelButton, SIGNAL(clicked()), this, SLOT(close())); + ret = ret && connect(m_templateCombo, SIGNAL(currentIndexChanged(int)), this, + SLOT(onTemplateSwitched())); + ret = ret && + connect(registerButton, SIGNAL(clicked()), this, SLOT(onRegister())); + assert(ret); + + // load template + bool ok = loadTemplateList(); + if (!ok) { + // show some warning? + } +} + +//----------------------------------------------------------------------------- + +OpenPopupCommandHandler openCustomPanelEditorPopup( + MI_CustomPanelEditor); \ No newline at end of file diff --git a/toonz/sources/toonz/custompaneleditorpopup.h b/toonz/sources/toonz/custompaneleditorpopup.h new file mode 100644 index 00000000..b82ae630 --- /dev/null +++ b/toonz/sources/toonz/custompaneleditorpopup.h @@ -0,0 +1,160 @@ +#pragma once + +#ifndef CUSTOMPANELEDITORPOPUP_H +#define CUSTOMPANELEDITORPOPUP_H + +#include "toonzqt/dvdialog.h" +// ToonzQt +#include "toonzqt/menubarcommand.h" + +#include +#include +#include +#include +#include + +#include +#include +class QComboBox; +class CustomPanelUIField; + +enum UiType { Button = 0, Scroller_Back, Scroller_Fore, TypeCount }; +struct UiEntry { + QString objectName; // object name before editing + UiType type; + QRect rect; + CustomPanelUIField* field; + Qt::Orientation orientation = Qt::Horizontal; +}; + +//============================================================================= +// CustomPanelUIField +//----------------------------------------------------------------------------- +class CustomPanelUIField : public QLabel { + Q_OBJECT + int m_id; + QString m_commandId; + +public: + CustomPanelUIField(const int id, const QString objectName, + QWidget* parent = nullptr, bool isFirst = true); + QString commandId() { return m_commandId; } + bool setCommand(QString commandId); + +protected: + void enterEvent(QEvent* event) override; + void leaveEvent(QEvent* event) override; + void dragEnterEvent(QDragEnterEvent* event) override; + void dragLeaveEvent(QDragLeaveEvent* event) override; + void dropEvent(QDropEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; +signals: + void highlight(int); + void commandChanged(QString, QString); + +public: + void notifyCommandChanged(QString oldCmdID, QString newCmdId) { + emit commandChanged(oldCmdID, newCmdId); + } +}; +//============================================================================= +// UiPreviewWidget +//----------------------------------------------------------------------------- + +class UiPreviewWidget final : public QWidget { + Q_OBJECT + int m_highlightUiId; + QList m_rectTable; + QPixmap m_uiPixmap; + + void onMove(const QPoint pos); + +public: + UiPreviewWidget(QPixmap uiPixmap, QList& uiEntries, + QWidget* parent = nullptr); + void onViewerResize(QSize size); + void highlightUi(const int objId); + void setUiPixmap(QPixmap uiPixmap) { + m_uiPixmap = uiPixmap; + update(); + } + +protected: + void paintEvent(QPaintEvent*) override; + + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void dragEnterEvent(QDragEnterEvent* event) override; + void dragMoveEvent(QDragMoveEvent* event) override; + void dropEvent(QDropEvent* event) override; + +signals: + void clicked(int id); + void dropped(int id, QString cmdId, bool fromTree); +}; + +class UiPreviewArea final : public QScrollArea { + Q_OBJECT +public: + UiPreviewArea(QWidget* parent = nullptr); + +protected: + void resizeEvent(QResizeEvent* event) override; +}; + +//============================================================================= +// CustomPanelCommandListTree +//----------------------------------------------------------------------------- + +class CustomPanelCommandListTree final : public QTreeWidget { + Q_OBJECT + + void addFolder(const QString& title, int commandType, + QTreeWidgetItem* parentFolder = 0); + +public: + CustomPanelCommandListTree(QWidget* parent = 0); + +protected: + void mousePressEvent(QMouseEvent*) override; +}; + +//============================================================================= +// CustomPanelEditorPopup +//----------------------------------------------------------------------------- + +class CustomPanelEditorPopup : public DVGui::Dialog { + Q_OBJECT +private: + CustomPanelCommandListTree* m_commandListTree; + QWidget* m_UiFieldsContainer; + UiPreviewArea* m_previewArea; + + QComboBox* m_templateCombo; + QLineEdit* m_panelNameEdit; + QList m_uiEntries; + + QList entryIdByObjName(const QString objName); + + bool loadTemplateList(); + void createFields(); + + void replaceObjectNames(QDomElement& element); + + // create entries from a widget just loaded from .ui file + void buildEntries(QWidget* customWidget); + // update widget using the current entries + void updateControls(QWidget* customWidget); + +public: + CustomPanelEditorPopup(); +protected slots: + void onTemplateSwitched(); + void onHighlight(int id); + void onCommandChanged(QString, QString); + void onPreviewClicked(int id); + void onPreviewDropped(int id, QString cmdId, bool fromTree); + void onRegister(); +}; + +#endif \ No newline at end of file diff --git a/toonz/sources/toonz/custompanelmanager.cpp b/toonz/sources/toonz/custompanelmanager.cpp new file mode 100644 index 00000000..e512d1eb --- /dev/null +++ b/toonz/sources/toonz/custompanelmanager.cpp @@ -0,0 +1,243 @@ +#include "custompanelmanager.h" + +#include "menubarcommandids.h" +#include "floatingpanelcommand.h" +#include "pane.h" + +// ToonzLib +#include "toonz/toonzfolders.h" +// ToonzCore +#include "tsystem.h" + +#include +#include +#include +#include +#include +#include + +namespace { + +const TFilePath CustomPanelFolderName("custompanels"); +const TFilePath customPaneFolderPath() { + return ToonzFolder::getMyModuleDir() + CustomPanelFolderName; +} +} // namespace + +//----------------------------------------------------------------------------- + +MyScroller::MyScroller(Qt::Orientation orientation, CommandId command1, + CommandId command2, QWidget* parent) + : QWidget(parent), m_orientation(orientation) { + m_actions[0] = CommandManager::instance()->getAction(command1); + m_actions[1] = CommandManager::instance()->getAction(command2); +} + +void MyScroller::paintEvent(QPaintEvent*) { + QPainter p(this); + + p.setPen(m_scrollerBorderColor); + p.setBrush(m_scrollerBGColor); + + p.drawRect(rect().adjusted(0, 0, -1, -1)); + + if (m_orientation == Qt::Horizontal) { + for (int i = 1; i <= 7; i++) { + int xPos = width() * i / 8; + p.drawLine(xPos, 0, xPos, height()); + } + } else { // vertical + for (int i = 1; i <= 7; i++) { + int yPos = height() * i / 8; + p.drawLine(0, yPos, width(), yPos); + } + } +} + +void MyScroller::mousePressEvent(QMouseEvent* event) { + m_anchorPos = + (m_orientation == Qt::Horizontal) ? event->pos().x() : event->pos().y(); +} + +void MyScroller::mouseMoveEvent(QMouseEvent* event) { + int currentPos = + (m_orientation == Qt::Horizontal) ? event->pos().x() : event->pos().y(); + static int threshold = 5; + if (m_anchorPos - currentPos >= threshold && m_actions[0]) { + m_actions[0]->trigger(); + m_anchorPos = currentPos; + } else if (currentPos - m_anchorPos >= threshold && m_actions[1]) { + m_actions[1]->trigger(); + m_anchorPos = currentPos; + } +} + +//----------------------------------------------------------------------------- + +CustomPanelManager* CustomPanelManager::instance() { + static CustomPanelManager _instance; + return &_instance; +} + +//----------------------------------------------------------------------------- +// browse the custom panel settings and regisiter to the menu +void CustomPanelManager::loadCustomPanelEntries() { + QAction* menuAct = CommandManager::instance()->getAction(MI_OpenCustomPanels); + if (!menuAct) return; + DVMenuAction* menu = dynamic_cast(menuAct->menu()); + if (!menu) return; + + if (!menu->isEmpty()) menu->clear(); + + TFilePath customPanelsFolder = customPaneFolderPath(); + if (TSystem::doesExistFileOrLevel(customPanelsFolder)) { + TFilePathSet fileList = + TSystem::readDirectory(customPanelsFolder, false, true, false); + if (!fileList.empty()) { + QList fileNames; + for (auto file : fileList) { + // accept only .ui files + if (file.getType() != "ui") continue; + fileNames.append(QString::fromStdString(file.getName())); + } + if (!fileNames.isEmpty()) { + menu->setActions(fileNames); + menu->addSeparator(); + } + } + } + // register an empty action with the label "Custom Panel Editor". + // actual command will be called in OpenCustomPanelCommandHandler + // in order to prevent double calling of the command + menu->addAction( + CommandManager::instance()->getAction(MI_CustomPanelEditor)->text()); +} + +//----------------------------------------------------------------------------- + +TPanel* CustomPanelManager::createCustomPanel(const QString panelName, + QWidget* parent) { + TPanel* panel = new TPanel(parent); + QString panelType = "Custom_" + panelName; + panel->setPanelType(panelType.toStdString()); + panel->setObjectName(panelType); + + TFilePath customPanelsFp = + customPaneFolderPath() + TFilePath(panelName + ".ui"); + QUiLoader loader; + QFile file(customPanelsFp.getQString()); + + file.open(QFile::ReadOnly); + QWidget* customWidget = loader.load(&file, panel); + file.close(); + + initializeControl(customWidget); + + panel->setWindowTitle(panelName); + panel->setWidget(customWidget); + + return panel; +} + +//----------------------------------------------------------------------------- + +void CustomPanelManager::initializeControl(QWidget* customWidget) { + // connect buttons and commands + QList allButtons = + customWidget->findChildren(); + for (auto button : allButtons) { + std::cout << button->objectName().toStdString() << std::endl; + QAction* action = CommandManager::instance()->getAction( + button->objectName().toStdString().c_str()); + if (!action) continue; + + CommandManager::instance()->enlargeIcon( + button->objectName().toStdString().c_str(), button->iconSize()); + + if (QToolButton* tb = dynamic_cast(button)) { + tb->setDefaultAction(action); + tb->setObjectName("CustomPanelButton"); + if (tb->toolButtonStyle() == Qt::ToolButtonTextUnderIcon) { + int padding = (tb->height() - button->iconSize().height() - + tb->font().pointSize() * 1.33) / + 3; + if (padding > 0) + tb->setStyleSheet(QString("padding-top: %1;").arg(padding)); + } + continue; + } + + if (action->isCheckable()) { + button->setCheckable(true); + button->setChecked(action->isChecked()); + customWidget->connect(button, SIGNAL(clicked(bool)), action, + SLOT(setChecked(bool))); + customWidget->connect(action, SIGNAL(toggled(bool)), button, + SLOT(setChecked(bool))); + } else { + customWidget->connect(button, SIGNAL(clicked(bool)), action, + SLOT(trigger())); + } + if (!button->text().isEmpty()) button->setText(action->text()); + + button->setIcon(action->icon()); + // button->addAction(action); + } + + // other custom controls + QList allWidgets = customWidget->findChildren(); + for (auto widget : allWidgets) { + // ignore buttons + if (dynamic_cast(widget)) continue; + // ignore if the widget already has a layout + if (widget->layout() != nullptr) continue; + + QString name = widget->objectName(); + QWidget* customControl = nullptr; + if (name.startsWith("HScroller")) { + QStringList ids = name.split("__"); + if (ids.size() != 3 || ids[0] != "HScroller") continue; + customControl = + new MyScroller(Qt::Horizontal, ids[1].toStdString().c_str(), + ids[2].toStdString().c_str(), customWidget); + } else if (name.startsWith("VScroller")) { + QStringList ids = name.split("__"); + if (ids.size() != 3 || ids[0] != "VScroller") continue; + customControl = + new MyScroller(Qt::Vertical, ids[1].toStdString().c_str(), + ids[2].toStdString().c_str(), customWidget); + } + + if (customControl) { + QHBoxLayout* lay = new QHBoxLayout(); + lay->setMargin(0); + lay->setSpacing(0); + lay->addWidget(customControl); + widget->setLayout(lay); + } + } +} + +//----------------------------------------------------------------------------- + +class OpenCustomPanelCommandHandler final : public MenuItemHandler { +public: + OpenCustomPanelCommandHandler() : MenuItemHandler(MI_OpenCustomPanels) {} + void execute() override { + QAction* act = CommandManager::instance()->getAction(MI_OpenCustomPanels); + DVMenuAction* menu = dynamic_cast(act->menu()); + int index = menu->getTriggeredActionIndex(); + + // the last action is for opening custom panel editor, in which the index is + // not set. + if (index == -1) { + CommandManager::instance()->getAction(MI_CustomPanelEditor)->trigger(); + return; + } + + QString panelId = menu->actions()[index]->text(); + + OpenFloatingPanel::getOrOpenFloatingPanel("Custom_" + + panelId.toStdString()); + } +} openCustomPanelCommandHandler; \ No newline at end of file diff --git a/toonz/sources/toonz/custompanelmanager.h b/toonz/sources/toonz/custompanelmanager.h new file mode 100644 index 00000000..996a08a3 --- /dev/null +++ b/toonz/sources/toonz/custompanelmanager.h @@ -0,0 +1,59 @@ +#pragma once +#ifndef CUSTOM_PANEL_MANAGER_H +#define CUSTOM_PANEL_MANAGER_H + +// ToonzQt +#include "toonzqt/menubarcommand.h" + +#include +#include + +class TPanel; + +//============================================================================= +// original widgets for the custom panel +class MyScroller : public QWidget { + Q_OBJECT + Qt::Orientation m_orientation; + QAction* m_actions[2]; + int m_anchorPos; + QColor m_scrollerBorderColor; + QColor m_scrollerBGColor; + + Q_PROPERTY(QColor ScrollerBorderColor READ getScrollerBorderColor WRITE + setScrollerBorderColor); + Q_PROPERTY(QColor ScrollerBGColor READ getScrollerBGColor WRITE + setScrollerBGColor); + +public: + MyScroller(Qt::Orientation orientation, CommandId command1, + CommandId command2, QWidget* parent = nullptr); + + void setScrollerBorderColor(const QColor& color) { + m_scrollerBorderColor = color; + } + QColor getScrollerBorderColor() const { return m_scrollerBorderColor; } + void setScrollerBGColor(const QColor& color) { m_scrollerBGColor = color; } + QColor getScrollerBGColor() const { return m_scrollerBGColor; } + +protected: + void paintEvent(QPaintEvent*) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; +}; + +//============================================================================= + +class CustomPanelManager { // singleton + CustomPanelManager(){}; + +public: + static CustomPanelManager* instance(); + void loadCustomPanelEntries(); + + TPanel* createCustomPanel(const QString panelName, QWidget* parent); + + void initializeControl(QWidget* customWidget); +}; + +#endif \ No newline at end of file diff --git a/toonz/sources/toonz/dvdirtreeview.cpp b/toonz/sources/toonz/dvdirtreeview.cpp index db894e34..9218dbab 100644 --- a/toonz/sources/toonz/dvdirtreeview.cpp +++ b/toonz/sources/toonz/dvdirtreeview.cpp @@ -118,11 +118,7 @@ QWidget *DvDirTreeViewDelegate::createEditor(QWidget *parent, if (!fnode || fnode->isProjectFolder()) return 0; QPixmap px = node->getPixmap(m_treeView->isExpanded(index)); QRect rect = option.rect; -#if QT_VERSION >= 0x050000 if (index.data().canConvert(QMetaType::QString)) { -#else - if (qVariantCanConvert(index.data())) { -#endif NodeEditor *editor = new NodeEditor(parent, rect, px.width()); editor->setText(index.data().toString()); connect(editor, SIGNAL(editingFinished()), this, @@ -266,11 +262,7 @@ void DvDirTreeViewDelegate::paint(QPainter *painter, void DvDirTreeViewDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { -#if QT_VERSION >= 0x050000 if (index.data().canConvert(QMetaType::QString)) -#else - if (qVariantCanConvert(index.data())) -#endif NodeEditor *nodeEditor = qobject_cast(editor); else QAbstractItemDelegate::setEditorData(editor, index); @@ -281,12 +273,7 @@ void DvDirTreeViewDelegate::setEditorData(QWidget *editor, void DvDirTreeViewDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { -#if QT_VERSION >= 0x050000 - if (index.data().canConvert(QMetaType::QString)) -#else - if (qVariantCanConvert(index.data())) -#endif - { + if (index.data().canConvert(QMetaType::QString)) { NodeEditor *nodeEditor = qobject_cast(editor); model->setData(index, qVariantFromValue( nodeEditor->getText())); // starEditor->text())); @@ -342,7 +329,7 @@ DvDirTreeView::DvDirTreeView(QWidget *parent) // bottom horizontal scrollbar to resize contents... bool ret = true; ret = ret && connect(this, SIGNAL(expanded(const QModelIndex &)), this, - SLOT(resizeToConts())); + SLOT(resizeToConts())); ret = ret && connect(this, SIGNAL(collapsed(const QModelIndex &)), this, SLOT(resizeToConts())); @@ -450,8 +437,7 @@ void DvDirTreeView::dropEvent(QDropEvent *e) { NameBuilder *nameBuilder = NameBuilder::getBuilder(::to_wstring(path.getName())); std::wstring levelNameOut; - do - levelNameOut = nameBuilder->getNext(); + do levelNameOut = nameBuilder->getNext(); while (TSystem::doesExistFileOrLevel(path.withName(levelNameOut))); dstFp = path.withName(levelNameOut); diff --git a/toonz/sources/toonz/dvitemview.cpp b/toonz/sources/toonz/dvitemview.cpp index 81405567..4c74889a 100644 --- a/toonz/sources/toonz/dvitemview.cpp +++ b/toonz/sources/toonz/dvitemview.cpp @@ -98,7 +98,11 @@ void getFileFids(TFilePath path, std::vector &fids) { QString hyphenText(const QString &srcText, const QFont &font, int width) { QFontMetrics metrics(font); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int srcWidth = metrics.horizontalAdvance(srcText); +#else int srcWidth = metrics.width(srcText); +#endif if (srcWidth < width) return srcText; int count = double(srcWidth) / double(width); @@ -110,8 +114,13 @@ QString hyphenText(const QString &srcText, const QFont &font, int width) { int hyphenCount = 1; for (i = 0; i < srcText.size(); i++) { QChar c = srcText.at(i); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int cWidth = metrics.horizontalAdvance(c); + int textWidth = metrics.horizontalAdvance(text) + cWidth; +#else int cWidth = metrics.width(c); int textWidth = metrics.width(text) + cWidth; +#endif if ((c.isSpace() && textWidth > (hyphenCount - 1) * width + diff) || (textWidth > hyphenCount * width)) { ++hyphenCount; @@ -177,10 +186,10 @@ QString DvItemListModel::getItemDataAsString(int index, DataType dataType) { case Icon: return ""; case CreationDate: - return value.toDateTime().toString(Qt::SystemLocaleShortDate); + return QLocale::system().toString(value.toDateTime()); break; case ModifiedDate: - return value.toDateTime().toString(Qt::SystemLocaleShortDate); + return QLocale::system().toString(value.toDateTime()); break; case FileSize: { if (getItemData(index, IsFolder).toBool()) return QString(""); @@ -1195,7 +1204,7 @@ void DvItemViewerPanel::mousePressEvent(QMouseEvent *event) { update(); } return; - } else if (event->button() == Qt::MidButton) { + } else if (event->button() == Qt::MiddleButton) { m_lastMousePos = event->globalPos(); event->accept(); return; @@ -1259,7 +1268,7 @@ void DvItemViewerPanel::mousePressEvent(QMouseEvent *event) { //----------------------------------------------------------------------------- void DvItemViewerPanel::mouseMoveEvent(QMouseEvent *event) { - if (event->buttons() == Qt::MidButton) { + if (event->buttons() == Qt::MiddleButton) { QPoint d = event->globalPos() - m_lastMousePos; m_lastMousePos = event->globalPos(); if (m_viewer) { @@ -1381,7 +1390,7 @@ bool DvItemViewerPanel::event(QEvent *event) { //----------------------------------------------------------------------------- void DvItemViewerPanel::setListView() { - m_viewType = ListView; + m_viewType = ListView; m_viewer->m_windowType == DvItemViewer::Cast ? CastView = ListView : BrowserView = ListView; emit viewTypeChange(m_viewType); @@ -1392,7 +1401,7 @@ void DvItemViewerPanel::setListView() { //----------------------------------------------------------------------------- void DvItemViewerPanel::setTableView() { - m_viewType = TableView; + m_viewType = TableView; m_viewer->m_windowType == DvItemViewer::Cast ? CastView = TableView : BrowserView = TableView; emit viewTypeChange(m_viewType); @@ -1403,7 +1412,7 @@ void DvItemViewerPanel::setTableView() { //----------------------------------------------------------------------------- void DvItemViewerPanel::setThumbnailsView() { - m_viewType = ThumbnailView; + m_viewType = ThumbnailView; m_viewer->m_windowType == DvItemViewer::Cast ? CastView = ThumbnailView : BrowserView = ThumbnailView; emit viewTypeChange(m_viewType); diff --git a/toonz/sources/toonz/dvitemview.h b/toonz/sources/toonz/dvitemview.h index 4aeafe43..dbde5f7f 100644 --- a/toonz/sources/toonz/dvitemview.h +++ b/toonz/sources/toonz/dvitemview.h @@ -255,6 +255,9 @@ public: } QColor getSelectedItemBackground() const { return m_selectedItemBackground; } + //exposed view parameters + void setIconSize(QSize size) { m_iconSize = size; } + private: ViewType to_enum(int n) { switch (n) { diff --git a/toonz/sources/toonz/exportpanel.cpp b/toonz/sources/toonz/exportpanel.cpp index 4b5c3f3b..45dea7d0 100644 --- a/toonz/sources/toonz/exportpanel.cpp +++ b/toonz/sources/toonz/exportpanel.cpp @@ -94,9 +94,8 @@ bool checkForMeshColumns(TXsheet *xsh, cells[i].m_level->getType() == CHILD_XSHLEVEL) { TXshChildLevel *level = cells[i].m_level->getChildLevel(); // make sure we haven't already checked the level - if (level && - std::find(childLevels.begin(), childLevels.end(), level) == - childLevels.end()) { + if (level && std::find(childLevels.begin(), childLevels.end(), + level) == childLevels.end()) { childLevels.push_back(level); TXsheet *subXsh = level->getXsheet(); foundMesh = checkForMeshColumns(subXsh, childLevels); @@ -171,7 +170,7 @@ bool RenderController::addScene(MovieGenerator &g, ToonzScene *scene) { } if (r1 < r0) return false; TPixel color = scene->getProperties()->getBgColor(); - if (isMovieType(m_movieExt) && m_movieExt != "mov" && m_movieExt != "webm") color.m = 255; + if (isMovieTypeOpaque(m_movieExt)) color.m = 255; g.setBackgroundColor(color); g.addScene(*scene, r0, r1); return true; @@ -371,7 +370,8 @@ you want to do?";*/ if (m_frame >= totalFrameCount && Preferences::instance()->isGeneratedMovieViewEnabled()) { if (Preferences::instance()->isDefaultViewerEnabled() && - (outPath.getType() == "avi")) { + (outPath.getType() == "mov" || outPath.getType() == "avi" || + outPath.getType() == "3gp")) { QString name = QString::fromStdString(outPath.getName()); if (!TSystem::showDocument(outPath)) { @@ -691,7 +691,7 @@ void ClipListViewer::dragMoveEvent(QDragMoveEvent *event) { void ClipListViewer::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls()) { - int j = m_dropInsertionPoint; + int j = m_dropInsertionPoint; if (j < 0) j = getItemCount(); for (const QUrl &url : event->mimeData()->urls()) { TFilePath fp(url.toLocalFile().toStdString()); @@ -771,11 +771,7 @@ void ClipListViewer::loadScene() { // //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 ExportPanel::ExportPanel(QWidget *parent, Qt::WindowFlags flags) -#else -ExportPanel::ExportPanel(QWidget *parent, Qt::WFlags flags) -#endif : TPanel(parent) , m_clipListViewer(0) , m_saveInFileFld(0) diff --git a/toonz/sources/toonz/exportpanel.h b/toonz/sources/toonz/exportpanel.h index 5fd971f2..3436304e 100644 --- a/toonz/sources/toonz/exportpanel.h +++ b/toonz/sources/toonz/exportpanel.h @@ -31,7 +31,7 @@ namespace DVGui { class ProgressDialog; class FileField; class LineEdit; -} +} // namespace DVGui //----------------------------------------------------------------------------- @@ -90,11 +90,7 @@ class ExportPanel final : public TPanel { QCheckBox *m_useMarker; public: -#if QT_VERSION >= 0x050500 ExportPanel(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - ExportPanel(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif ~ExportPanel(); void loadExportSettings(); void saveExportSettings(); diff --git a/toonz/sources/toonz/exportxsheetpdf.cpp b/toonz/sources/toonz/exportxsheetpdf.cpp index 1a71505e..6181b8d5 100644 --- a/toonz/sources/toonz/exportxsheetpdf.cpp +++ b/toonz/sources/toonz/exportxsheetpdf.cpp @@ -12,6 +12,7 @@ #include "toonzqt/gutil.h" #include "toonzqt/filefield.h" #include "toonzqt/colorfield.h" +#include "toonzqt/intfield.h" // TnzLib includes #include "toonz/tscenehandle.h" @@ -58,6 +59,7 @@ #include #include #include +#include // Template TEnv::StringVar XShPdfExportTemplate("XShPdfExportTemplate", "B4_6sec"); @@ -1234,7 +1236,7 @@ void XSheetPDFTemplate::drawDialogue(QPainter& painter, int framePage) { if (row < drawStart) { int partialBlockLength = rowTo - drawStart + 1; int partialTextCount = (int)std::round( - (double)(textCount * partialBlockLength) / (double)blockLength); + (double)(textCount * partialBlockLength) / (double)blockLength); text = text.mid(textCount - partialTextCount); textCount = partialTextCount; row = drawStart; @@ -1250,7 +1252,7 @@ void XSheetPDFTemplate::drawDialogue(QPainter& painter, int framePage) { if (rowTo > drawEnd) { int partialBlockLength = drawEnd - row + 1; int partialTextCount = (int)std::round( - (double)(textCount * partialBlockLength) / (double)blockLength); + (double)(textCount * partialBlockLength) / (double)blockLength); text = text.mid(0, partialTextCount); textCount = partialTextCount; rowTo = drawEnd; @@ -1432,6 +1434,8 @@ void XSheetPDFTemplate::drawXsheetContents(QPainter& painter, int framePage, drawLevelName(painter, m_colLabelRects_bottom[c][r / 72], columnName, true); + if (m_duration == 0) break; + TXshCell cell = column->getCell(f); if (cell.m_level != level) cell.m_level = nullptr; @@ -1584,8 +1588,8 @@ QPixmap XSheetPDFTemplate::initializePreview() { } int XSheetPDFTemplate::framePageCount() { - int ret = m_duration / param(FrameLength); - if (m_duration % param(FrameLength) != 0 || m_duration == 0) ret += 1; + int ret = m_duration / param(FrameLength, 1); + if (m_duration % param(FrameLength, 1) != 0 || m_duration == 0) ret += 1; return ret; } @@ -1887,6 +1891,7 @@ ExportXsheetPdfPopup::ExportXsheetPdfPopup() m_templateCombo = new QComboBox(this); m_exportAreaCombo = new QComboBox(this); m_continuousLineCombo = new QComboBox(this); + m_durationFld = new DVGui::IntLineEdit(this, 0, 0); m_pageInfoLbl = new QLabel(this); m_lineColorFld = new DVGui::ColorField(this, false, TPixel32(128, 128, 128)); @@ -1932,7 +1937,7 @@ ExportXsheetPdfPopup::ExportXsheetPdfPopup() //------ QStringList pdfFileTypes = {"pdf"}; m_pathFld->setFilters(pdfFileTypes); - m_pathFld->setFileMode(QFileDialog::DirectoryOnly); + m_pathFld->setFileMode(QFileDialog::Directory); // implies ShowDirOnly m_fileNameFld->setFixedWidth(100); m_previewArea->setWidget(m_previewPane); m_previewArea->setAlignment(Qt::AlignCenter); @@ -2042,19 +2047,23 @@ ExportXsheetPdfPopup::ExportXsheetPdfPopup() exportLay->setHorizontalSpacing(5); exportLay->setVerticalSpacing(10); { - exportLay->addWidget(new QLabel(tr("Output area:"), this), 0, 0, + exportLay->addWidget(new QLabel(tr("Frame length:"), this), 0, 0, Qt::AlignRight | Qt::AlignVCenter); - exportLay->addWidget(m_exportAreaCombo, 0, 1); - exportLay->addWidget(m_pageInfoLbl, 0, 2); + exportLay->addWidget(m_durationFld, 0, 1); - exportLay->addWidget(new QLabel(tr("Output font:"), this), 1, 0, + exportLay->addWidget(new QLabel(tr("Output area:"), this), 1, 0, Qt::AlignRight | Qt::AlignVCenter); - exportLay->addWidget(m_contentsFontCB, 1, 1, 1, 2, + exportLay->addWidget(m_exportAreaCombo, 1, 1); + exportLay->addWidget(m_pageInfoLbl, 1, 2); + + exportLay->addWidget(new QLabel(tr("Output font:"), this), 2, 0, + Qt::AlignRight | Qt::AlignVCenter); + exportLay->addWidget(m_contentsFontCB, 2, 1, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); - exportLay->addWidget(new QLabel(tr("Continuous line:"), this), 2, 0, + exportLay->addWidget(new QLabel(tr("Continuous line:"), this), 3, 0, Qt::AlignRight | Qt::AlignVCenter); - exportLay->addWidget(m_continuousLineCombo, 2, 1, 1, 2, + exportLay->addWidget(m_continuousLineCombo, 3, 1, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); QGridLayout* checksLay = new QGridLayout(); @@ -2076,25 +2085,25 @@ ExportXsheetPdfPopup::ExportXsheetPdfPopup() checksLay->setColumnStretch(0, 2); checksLay->setColumnStretch(1, 1); checksLay->setColumnStretch(2, 1); - exportLay->addLayout(checksLay, 3, 0, 1, 3); + exportLay->addLayout(checksLay, 4, 0, 1, 3); - exportLay->addWidget(new QLabel(tr("Inbetween mark 1:"), this), 4, 0, + exportLay->addWidget(new QLabel(tr("Inbetween mark 1:"), this), 5, 0, Qt::AlignRight | Qt::AlignVCenter); - exportLay->addWidget(m_tick1IdCombo, 4, 1); - exportLay->addWidget(m_tick1MarkCombo, 4, 2, + exportLay->addWidget(m_tick1IdCombo, 5, 1); + exportLay->addWidget(m_tick1MarkCombo, 5, 2, Qt::AlignLeft | Qt::AlignVCenter); - exportLay->addWidget(new QLabel(tr("Inbetween mark 2:"), this), 5, 0, + exportLay->addWidget(new QLabel(tr("Inbetween mark 2:"), this), 6, 0, Qt::AlignRight | Qt::AlignVCenter); - exportLay->addWidget(m_tick2IdCombo, 5, 1); - exportLay->addWidget(m_tick2MarkCombo, 5, 2, + exportLay->addWidget(m_tick2IdCombo, 6, 1); + exportLay->addWidget(m_tick2MarkCombo, 6, 2, Qt::AlignLeft | Qt::AlignVCenter); - exportLay->addWidget(new QLabel(tr("Keyframe mark:"), this), 6, 0, + exportLay->addWidget(new QLabel(tr("Keyframe mark:"), this), 7, 0, Qt::AlignRight | Qt::AlignVCenter); - exportLay->addWidget(m_keyIdCombo, 6, 1); + exportLay->addWidget(m_keyIdCombo, 7, 1); - exportLay->addWidget(new QLabel(tr("Memo:"), this), 7, 0, + exportLay->addWidget(new QLabel(tr("Memo:"), this), 8, 0, Qt::AlignRight | Qt::AlignTop); - exportLay->addWidget(m_memoEdit, 7, 1, 1, 2); + exportLay->addWidget(m_memoEdit, 8, 1, 1, 2); } exportLay->setColumnStretch(2, 1); exportGBox->setLayout(exportLay); @@ -2144,6 +2153,8 @@ ExportXsheetPdfPopup::ExportXsheetPdfPopup() connect(exportPngBtn, SIGNAL(clicked()), this, SLOT(onExportPNG())); connect(cancelBtn, SIGNAL(clicked()), this, SLOT(close())); + connect(m_durationFld, SIGNAL(editingFinished()), this, + SLOT(onDurationEdited())); connect(m_templateCombo, SIGNAL(activated(int)), this, SLOT(initTemplate())); connect(m_exportAreaCombo, SIGNAL(activated(int)), this, @@ -2277,6 +2288,8 @@ void ExportXsheetPdfPopup::initialize() { else m_duration = xsheet->getFrameCount(); + m_durationFld->setValue(m_duration); + m_columns.clear(); m_soundColumns.clear(); m_noteColumns.clear(); @@ -2461,7 +2474,7 @@ void ExportXsheetPdfPopup::setInfo() { info.lineColor = QColor(col.r, col.g, col.b); info.dateTimeText = (m_addDateTimeCB->isChecked()) - ? QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate) + ? QLocale::system().toString(QDateTime::currentDateTime()) : ""; ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene(); info.scenePathText = @@ -2773,6 +2786,13 @@ void ExportXsheetPdfPopup::onTickIdComboActivated() { updatePreview(); } +void ExportXsheetPdfPopup::onDurationEdited() { + int newDuration = m_durationFld->getValue(); + if (m_duration == newDuration) return; + m_duration = newDuration; + initTemplate(); +} + //----------------------------------------------------------------------------- OpenPopupCommandHandler openExportXsheetPdfPopup( diff --git a/toonz/sources/toonz/exportxsheetpdf.h b/toonz/sources/toonz/exportxsheetpdf.h index e0020af9..be3f6b92 100644 --- a/toonz/sources/toonz/exportxsheetpdf.h +++ b/toonz/sources/toonz/exportxsheetpdf.h @@ -25,6 +25,7 @@ class TXshSoundTextColumn; namespace DVGui { class FileField; class ColorField; + class IntLineEdit; } // namespace DVGui class XSheetPDFTemplate; class QFontComboBox; @@ -289,6 +290,7 @@ class ExportXsheetPdfPopup final : public DVGui::Dialog { QComboBox *m_templateCombo, *m_exportAreaCombo, *m_continuousLineCombo, *m_dialogueColCombo; DVGui::ColorField* m_lineColorFld; + DVGui::IntLineEdit* m_durationFld; QCheckBox *m_addDateTimeCB, *m_addScenePathCB, *m_drawSoundCB, *m_addSceneNameCB, *m_serialFrameNumberCB, *m_levelNameOnBottomCB, @@ -347,6 +349,7 @@ protected slots: void onNext(); void onTickIdComboActivated(); + void onDurationEdited(); }; #endif \ No newline at end of file diff --git a/toonz/sources/toonz/filebrowser.cpp b/toonz/sources/toonz/filebrowser.cpp index f2d4708a..3fed4988 100644 --- a/toonz/sources/toonz/filebrowser.cpp +++ b/toonz/sources/toonz/filebrowser.cpp @@ -144,22 +144,12 @@ QMutex levelFileMutex; } // namespace -inline bool isMultipleFrameType(std::string type) { - return (type == "tlv" || type == "tzl" || type == "pli" || type == "avi" || - type == "gif" || type == "mp4" || type == "webm" || type == "mov"); -} - //============================================================================= // FileBrowser //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 FileBrowser::FileBrowser(QWidget *parent, Qt::WindowFlags flags, bool noContextMenu, bool multiSelectionEnabled) -#else -FileBrowser::FileBrowser(QWidget *parent, Qt::WFlags flags, bool noContextMenu, - bool multiSelectionEnabled) -#endif : QFrame(parent), m_folderName(0), m_itemViewer(0) { // style sheet setObjectName("FileBrowser"); @@ -1237,13 +1227,13 @@ QMenu *FileBrowser::getContextMenu(QWidget *parent, int index) { files[1].getType() == "TIFF" || files[1].getType() == "PNG")) { QAction *action = new QAction(tr("Convert to Painted TLV"), menu); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(convertToPaintedTlv())); + SLOT(convertToPaintedTlv())); menu->addAction(action); } if (areFullcolor) { QAction *action = new QAction(tr("Convert to Unpainted TLV"), menu); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(convertToUnpaintedTlv())); + SLOT(convertToUnpaintedTlv())); menu->addAction(action); menu->addSeparator(); } @@ -1276,42 +1266,42 @@ QMenu *FileBrowser::getContextMenu(QWidget *parent, int index) { if (status == DvItemListModel::VC_ReadOnly) { action = vcMenu->addAction(tr("Edit")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(editVersionControl())); + SLOT(editVersionControl())); TFilePath path = files.at(0); std::string fileType = path.getType(); if (fileType == "tlv" || fileType == "pli" || path.getDots() == "..") { action = vcMenu->addAction(tr("Edit Frame Range...")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(editFrameRangeVersionControl())); + SLOT(editFrameRangeVersionControl())); } } else { action = vcMenu->addAction(tr("Edit")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(updateAndEditVersionControl())); + SLOT(updateAndEditVersionControl())); } } if (status == DvItemListModel::VC_Modified) { action = vcMenu->addAction(tr("Put...")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(putVersionControl())); + SLOT(putVersionControl())); action = vcMenu->addAction(tr("Revert")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(revertVersionControl())); + SLOT(revertVersionControl())); } if (status == DvItemListModel::VC_ReadOnly || status == DvItemListModel::VC_ToUpdate) { action = vcMenu->addAction(tr("Get")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(getVersionControl())); + SLOT(getVersionControl())); if (status == DvItemListModel::VC_ReadOnly) { action = vcMenu->addAction(tr("Delete")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(deleteVersionControl())); + SLOT(deleteVersionControl())); } vcMenu->addSeparator(); @@ -1328,36 +1318,36 @@ QMenu *FileBrowser::getContextMenu(QWidget *parent, int index) { } else if (files.size() > 1) { action = vcMenu->addAction("Get Revision..."); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(getRevisionVersionControl())); + SLOT(getRevisionVersionControl())); } } if (status == DvItemListModel::VC_Edited) { action = vcMenu->addAction(tr("Unlock")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(unlockVersionControl())); + SLOT(unlockVersionControl())); } if (status == DvItemListModel::VC_Unversioned) { action = vcMenu->addAction(tr("Put...")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(putVersionControl())); + SLOT(putVersionControl())); } if (status == DvItemListModel::VC_Locked && files.size() == 1) { action = vcMenu->addAction(tr("Unlock")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(unlockVersionControl())); + SLOT(unlockVersionControl())); action = vcMenu->addAction(tr("Edit Info")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(showLockInformation())); + SLOT(showLockInformation())); } if (status == DvItemListModel::VC_Missing) { action = vcMenu->addAction(tr("Get")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(getVersionControl())); + SLOT(getVersionControl())); if (files.size() == 1) { vcMenu->addSeparator(); @@ -1375,44 +1365,44 @@ QMenu *FileBrowser::getContextMenu(QWidget *parent, int index) { if (status == DvItemListModel::VC_PartialLocked) { action = vcMenu->addAction(tr("Get")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(getVersionControl())); + SLOT(getVersionControl())); if (files.size() == 1) { action = vcMenu->addAction(tr("Edit Frame Range...")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(editFrameRangeVersionControl())); + SLOT(editFrameRangeVersionControl())); action = vcMenu->addAction(tr("Edit Info")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(showFrameRangeLockInfo())); + SLOT(showFrameRangeLockInfo())); } } else if (status == DvItemListModel::VC_PartialEdited) { action = vcMenu->addAction(tr("Get")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(getVersionControl())); + SLOT(getVersionControl())); if (files.size() == 1) { action = vcMenu->addAction(tr("Unlock Frame Range")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(unlockFrameRangeVersionControl())); + SLOT(unlockFrameRangeVersionControl())); action = vcMenu->addAction(tr("Edit Info")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(showFrameRangeLockInfo())); + SLOT(showFrameRangeLockInfo())); } } else if (status == DvItemListModel::VC_PartialModified) { action = vcMenu->addAction(tr("Get")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(getVersionControl())); + SLOT(getVersionControl())); if (files.size() == 1) { action = vcMenu->addAction(tr("Put...")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(putFrameRangeVersionControl())); + SLOT(putFrameRangeVersionControl())); action = vcMenu->addAction(tr("Revert")); ret = ret && connect(action, SIGNAL(triggered()), this, - SLOT(revertFrameRangeVersionControl())); + SLOT(revertFrameRangeVersionControl())); } } @@ -1575,8 +1565,7 @@ bool FileBrowser::drop(const QMimeData *mimeData) { TFilePath path = folderPath; NameBuilder *nameBuilder = NameBuilder::getBuilder(::to_wstring(path.getName())); - do - levelName = nameBuilder->getNext(); + do levelName = nameBuilder->getNext(); while (TSystem::doesExistFileOrLevel(path.withName(levelName))); folderPath = path.withName(levelName); } @@ -1770,11 +1759,7 @@ QString getFrame(const QString &filename) { QString number = filename.mid(from + 1, to - from); for (i = 0; i < 4 - number.size(); i++) padStr[i] = '0'; for (i = 0; i < number.size(); i++) -#if QT_VERSION >= 0x050500 padStr[4 - number.size() + i] = number.at(i).toLatin1(); -#else - padStr[4 - number.size() + i] = number.at(i).toAscii(); -#endif return QString(padStr); } @@ -2362,7 +2347,7 @@ calculateTask: //----------------------------------------------------------------------------- -inline void FrameCountReader::stopReading() { m_executor.cancelAll(); } +void FrameCountReader::stopReading() { m_executor.cancelAll(); } //============================================================================= // FrameCountTask methods @@ -2433,4 +2418,4 @@ void FrameCountTask::onCanceled(TThread::RunnableP thisTask) { //============================================================================= OpenFloatingPanel openBrowserPane(MI_OpenFileBrowser, "Browser", - QObject::tr("File Browser")); + QObject::tr("File Browser")); \ No newline at end of file diff --git a/toonz/sources/toonz/filebrowser.h b/toonz/sources/toonz/filebrowser.h index 860c2fd6..7e52151f 100644 --- a/toonz/sources/toonz/filebrowser.h +++ b/toonz/sources/toonz/filebrowser.h @@ -60,13 +60,8 @@ class FileBrowser final : public QFrame, public DvItemListModel { Q_OBJECT public: -#if QT_VERSION >= 0x050500 FileBrowser(QWidget *parent, Qt::WindowFlags flags = 0, bool noContextMenu = false, bool multiSelectionEnabled = false); -#else - FileBrowser(QWidget *parent, Qt::WFlags flags = 0, bool noContextMenu = false, - bool multiSelectionEnabled = false); -#endif ~FileBrowser(); void sortByDataModel(DataType dataType, bool isDiscendent) override; @@ -100,8 +95,8 @@ types to be displayed in the file browser. void setFolder(const TFilePath &fp, bool expandNode = false, bool forceUpdate = false, bool collapseAll = false); - // process when inputting the folder which is not registered in the folder tree - // (e.g. UNC path in Windows) + // process when inputting the folder which is not registered in the folder + // tree (e.g. UNC path in Windows) void setUnregisteredFolder(const TFilePath &fp); void setHistoryDay(std::string dayDateString); diff --git a/toonz/sources/toonz/filebrowsermodel.cpp b/toonz/sources/toonz/filebrowsermodel.cpp index eae52921..01455832 100644 --- a/toonz/sources/toonz/filebrowsermodel.cpp +++ b/toonz/sources/toonz/filebrowsermodel.cpp @@ -1059,7 +1059,7 @@ void DvDirModelStuffFolderNode::refreshChildren() { DvDirModelSpecialFileFolderNode *child = new DvDirModelSpecialFileFolderNode( this, L"Library", ToonzFolder::getLibraryFolder()); child->setPixmap( - recolorPixmap(svgToPixmap(getIconThemePath("actions/16/library.svg")))); + recolorPixmap(svgToPixmap(getIconThemePath("actions/16/library.svg"), QSize(16,16)))); addChild(child); child = new DvDirModelSpecialFileFolderNode( @@ -1077,8 +1077,8 @@ void DvDirModelStuffFolderNode::refreshChildren() { child = new DvDirModelSpecialFileFolderNode( this, L"Studio Palettes", ToonzFolder::getStudioPaletteFolder()); - child->setPixmap( - recolorPixmap(svgToPixmap(getIconThemePath("actions/16/palette.svg")))); + child->setPixmap(recolorPixmap( + svgToPixmap(getIconThemePath("actions/16/palette.svg"), QSize(16, 16)))); addChild(child); } @@ -1165,15 +1165,15 @@ void DvDirModelRootNode::refreshChildren() { DvDirModelSpecialFileFolderNode *child; child = new DvDirModelSpecialFileFolderNode(this, L"My Documents", getMyDocumentsPath()); - child->setPixmap(recolorPixmap( - svgToPixmap(getIconThemePath("actions/16/my_documents.svg")))); + child->setPixmap(recolorPixmap(svgToPixmap( + getIconThemePath("actions/16/my_documents.svg"), QSize(16, 16)))); m_specialNodes.push_back(child); addChild(child); child = new DvDirModelSpecialFileFolderNode(this, L"Desktop", getDesktopPath()); - child->setPixmap( - recolorPixmap(svgToPixmap(getIconThemePath("actions/16/desktop.svg")))); + child->setPixmap(recolorPixmap(svgToPixmap( + getIconThemePath("actions/16/desktop.svg"), QSize(16, 16)))); m_specialNodes.push_back(child); addChild(child); diff --git a/toonz/sources/toonz/fileselection.cpp b/toonz/sources/toonz/fileselection.cpp index bf3e0e5f..72ad5e58 100644 --- a/toonz/sources/toonz/fileselection.cpp +++ b/toonz/sources/toonz/fileselection.cpp @@ -393,7 +393,8 @@ void FileSelection::viewFile() { continue; if (Preferences::instance()->isDefaultViewerEnabled() && - (files[i].getType() == "avi")) + (files[i].getType() == "mov" || files[i].getType() == "avi" || + files[i].getType() == "3gp")) QDesktopServices::openUrl(QUrl("file:///" + toQString(files[i]))); else if (files[i].getType() == "tpl") { viewedPalette = StudioPalette::instance()->getPalette(files[i], false); diff --git a/toonz/sources/toonz/filmstrip.cpp b/toonz/sources/toonz/filmstrip.cpp index 74073142..75e917af 100644 --- a/toonz/sources/toonz/filmstrip.cpp +++ b/toonz/sources/toonz/filmstrip.cpp @@ -94,11 +94,7 @@ QString fidToFrameNumberWithLetter(int f) { // Filmstrip //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 FilmstripFrames::FilmstripFrames(QScrollArea *parent, Qt::WindowFlags flags) -#else -FilmstripFrames::FilmstripFrames(QScrollArea *parent, Qt::WFlags flags) -#endif : QFrame(parent, flags) , m_scrollArea(parent) , m_selection(new TFilmstripSelection()) @@ -1297,11 +1293,8 @@ void FilmstripFrames::contextMenuEvent(QContextMenuEvent *event) { if (sl && sl->getType() == TZP_XSHLEVEL) menu->addAction(cm->getAction(MI_RevertToCleanedUp)); } - if (sl && - (sl->getType() == TZP_XSHLEVEL || sl->getType() == PLI_XSHLEVEL || - (sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() != "gif" && - sl->getPath().getType() != "mp4" && sl->getPath().getType() != "webm" && - sl->getPath().getType() != "mov"))) + if (sl && (sl->getType() == TZP_XSHLEVEL || sl->getType() == PLI_XSHLEVEL || + (sl->getType() == OVL_XSHLEVEL && !sl->getPath().isUneditable()))) menu->addAction(cm->getAction(MI_RevertToLastSaved)); menu->addSeparator(); createSelectLevelMenu(menu); @@ -1530,12 +1523,7 @@ void FilmstripFrames::inbetween() { // Filmstrip //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 -Filmstrip::Filmstrip(QWidget *parent, Qt::WindowFlags flags) -#else -Filmstrip::Filmstrip(QWidget *parent, Qt::WFlags flags) -#endif - : QWidget(parent) { +Filmstrip::Filmstrip(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_frameArea = new QScrollArea(this); diff --git a/toonz/sources/toonz/filmstrip.h b/toonz/sources/toonz/filmstrip.h index b35d4209..5ae91e81 100644 --- a/toonz/sources/toonz/filmstrip.h +++ b/toonz/sources/toonz/filmstrip.h @@ -42,11 +42,8 @@ class FilmstripFrames final : public QFrame, public TSelection::View { Q_OBJECT public: -#if QT_VERSION >= 0x050500 - FilmstripFrames(QScrollArea *parent = 0, Qt::WindowFlags flags = 0); -#else - FilmstripFrames(QScrollArea *parent = 0, Qt::WFlags flags = 0); -#endif + FilmstripFrames(QScrollArea *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); ~FilmstripFrames(); bool m_isVertical = true; @@ -239,11 +236,7 @@ class Filmstrip final : public QWidget, public SaveLoadQSettings { bool m_showComboBox = true; public: -#if QT_VERSION >= 0x050500 Filmstrip(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - Filmstrip(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif ~Filmstrip(); // SaveLoadQSettings diff --git a/toonz/sources/toonz/filmstripselection.cpp b/toonz/sources/toonz/filmstripselection.cpp index f22ff4f0..6c5fba12 100644 --- a/toonz/sources/toonz/filmstripselection.cpp +++ b/toonz/sources/toonz/filmstripselection.cpp @@ -57,9 +57,8 @@ void TFilmstripSelection::enableCommands() { bool doEnable = (type == PLI_XSHLEVEL || type == TZP_XSHLEVEL || type == MESH_XSHLEVEL || - (type == OVL_XSHLEVEL && path.getType() != "psd" && - path.getType() != "gif" && path.getType() != "mp4" && - path.getType() != "webm" && path.getType() != "mov")); + (type == OVL_XSHLEVEL && !path.isUneditable())); + TRasterImageP ri = (TRasterImageP)sl->getSimpleLevel()->getFrame( sl->getSimpleLevel()->getFirstFid(), false); @@ -227,7 +226,10 @@ void TFilmstripSelection::cutFrames() { TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel(); if (sl) { int firstSelectedIndex = sl->fid2index(*m_selectedFrames.begin()); - assert(firstSelectedIndex >= 0); + if(firstSelectedIndex < 0 || firstSelectedIndex > sl->getFrameCount()) { + selectNone(); + return; + } FilmstripCmd::cut(sl, m_selectedFrames); selectNone(); TFrameId fId = (firstSelectedIndex == 0) diff --git a/toonz/sources/toonz/flipbook.cpp b/toonz/sources/toonz/flipbook.cpp index a88a46f7..dea1dbf1 100644 --- a/toonz/sources/toonz/flipbook.cpp +++ b/toonz/sources/toonz/flipbook.cpp @@ -370,7 +370,9 @@ LoadImagesPopup::LoadImagesPopup(FlipBook *flip) m_shrinkField = new DVGui::LineEdit("1", this); // Define the append/load filter types - m_appendFilterTypes << "jpg" + m_appendFilterTypes << "3gp" + << "mov" + << "jpg" << "png" << "tga" << "tif" @@ -921,11 +923,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) { @@ -1149,12 +1151,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 @@ -1518,6 +1521,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; @@ -1553,13 +1559,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; @@ -1597,6 +1607,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(); @@ -1605,6 +1616,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) { @@ -1681,7 +1695,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()); } @@ -1719,8 +1744,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; @@ -1766,9 +1790,12 @@ void FlipBook::onCloseButtonPressed() { void ImageViewer::showHistogram() { if (!m_isHistogramEnable) return; + + // close the popup when using the command while open if (m_histogramPopup->isVisible()) - m_histogramPopup->raise(); + m_histogramPopup->hide(); else { + m_histogramPopup->moveNextToWidget(this); m_histogramPopup->setImage(getImage()); m_histogramPopup->show(); } @@ -1893,6 +1920,7 @@ void FlipBook::reset() { m_flipConsole->pressButton(FlipConsole::eDefineLoadBox); if (m_flipConsole->isChecked(FlipConsole::eUseLoadBox)) m_flipConsole->pressButton(FlipConsole::eUseLoadBox); + m_flipConsole->resetGain(true); m_flipConsole->enableButton(FlipConsole::eDefineLoadBox, true); m_flipConsole->enableButton(FlipConsole::eUseLoadBox, true); @@ -2243,7 +2271,8 @@ FlipBook *viewFile(const TFilePath &path, int from, int to, int step, } // Movie files must not have the ".." extension - if ((path.getType() == "avi") && + if ((path.getType() == "mov" || path.getType() == "avi" || + path.getType() == "3gp") && path.isLevelName()) { DVGui::warning(QObject::tr("%1 has an invalid extension format.") .arg(QString::fromStdString(path.getLevelName()))); diff --git a/toonz/sources/toonz/flipbook.h b/toonz/sources/toonz/flipbook.h index ef3cf82b..8f68e1be 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 248ad32c..49ccb193 100644 --- a/toonz/sources/toonz/histogrampopup.cpp +++ b/toonz/sources/toonz/histogrampopup.cpp @@ -6,6 +6,7 @@ #include "menubarcommandids.h" #include "tapp.h" #include "previewer.h" +#include "sceneviewer.h" // TnzQt includes #include "toonzqt/menubarcommand.h" @@ -22,6 +23,9 @@ // Qt includes #include #include +#include +#include +#include using namespace DVGui; @@ -84,6 +88,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 */ @@ -94,6 +101,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) { @@ -106,6 +117,39 @@ void HistogramPopup::invalidateCompHisto() { m_histogram->invalidateCompHisto(); } +//----------------------------------------------------------------------------- + +void HistogramPopup::moveNextToWidget(QWidget *widget) { + if (!widget) return; + const int margin = 5; + + if (minimumSize().isEmpty()) grab(); + QSize popupSize = frameSize(); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QRect screenRect = widget->screen()->availableGeometry(); +#else + int currentScreen = QApplication::desktop()->screenNumber(widget); + QRect screenRect = QApplication::desktop()->availableGeometry(currentScreen); +#endif + QRect viewerRect = widget->rect(); + viewerRect.moveTo(widget->mapToGlobal(QPoint(0, 0))); + // decide which side to open the popup + QPoint popupPos = widget->mapToGlobal(QPoint(0, 0)); + // open at the left + if (viewerRect.left() - screenRect.left() > + screenRect.right() - viewerRect.right()) + popupPos.setX(std::max(viewerRect.left() - popupSize.width() - margin, 0)); + // open at the right + else + popupPos.setX(std::min(viewerRect.right() + margin, + screenRect.right() - popupSize.width())); + // adjust vertical position + popupPos.setY(std::min(std::max(popupPos.y(), screenRect.top()), + screenRect.bottom() - popupSize.height() - margin)); + move(popupPos); +} + //============================================================================= /*! \class ViewerHistogramPopup \brief The ViewerHistogramPopup show the histogram pertain to @@ -127,6 +171,7 @@ void ViewerHistogramPopup::showEvent(QShowEvent *e) { SLOT(setCurrentRaster())); setCurrentRaster(); + moveNextToWidget(TApp::instance()->getActiveViewer()); } //----------------------------------------------------------------------------- @@ -160,4 +205,5 @@ void ViewerHistogramPopup::setCurrentRaster() { //============================================================================= -OpenPopupCommandHandler openHistogramPopup(MI_Histogram); +OpenPopupCommandHandler openHistogramPopup( + MI_ViewerHistogram); \ No newline at end of file diff --git a/toonz/sources/toonz/histogrampopup.h b/toonz/sources/toonz/histogrampopup.h index 2e755c0a..25704fb9 100644 --- a/toonz/sources/toonz/histogrampopup.h +++ b/toonz/sources/toonz/histogrampopup.h @@ -28,11 +28,14 @@ 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); + }; //============================================================================= // ViewerHistogramPopup diff --git a/toonz/sources/toonz/history.cpp b/toonz/sources/toonz/history.cpp index f43f5d0c..668b59c1 100644 --- a/toonz/sources/toonz/history.cpp +++ b/toonz/sources/toonz/history.cpp @@ -3,11 +3,13 @@ #include "history.h" #include "tsystem.h" #include "tenv.h" -//#include "tutil.h" +// #include "tutil.h" #include "tfilepath_io.h" #include "toonz/toonzfolders.h" -//#include +#include + +// #include inline TFilePath getHistoryFile() { return ToonzFolder::getMyModuleDir() + L"file_history.txt"; @@ -21,7 +23,8 @@ std::string History::Day::getDate() const { else if (m_timeStamp == yesterday) return "yesterday"; else - return m_timeStamp.toString(Qt::SystemLocaleDate) + return QLocale::system() + .toString(m_timeStamp) .toStdString(); // "%d %b %A %x"); } diff --git a/toonz/sources/toonz/historypane.cpp b/toonz/sources/toonz/historypane.cpp index a0964c6a..6290d59a 100644 --- a/toonz/sources/toonz/historypane.cpp +++ b/toonz/sources/toonz/historypane.cpp @@ -38,7 +38,7 @@ const struct { {HistoryType::FilmStrip, "history_filmstrip"}, {0, 0}}; -}; +}; // namespace class HistoryPixmapManager { // singleton @@ -72,11 +72,7 @@ public: } }; //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 HistoryField::HistoryField(QScrollArea *parent, Qt::WindowFlags flags) -#else -HistoryField::HistoryField(QScrollArea *parent, Qt::WFlags flags) -#endif : QFrame(parent, flags), m_scrollArea(parent) { setObjectName("filmStripFrames"); setFrameStyle(QFrame::StyledPanel); @@ -94,8 +90,8 @@ void HistoryField::updateContentHeight(int minimumHeight) { int contentHeight = TUndoManager::manager()->getHistoryCount() * HISTORY_ITEM_HEIGHT; if (contentHeight < minimumHeight) contentHeight = minimumHeight; - int parentHeight = parentWidget()->height(); - if (contentHeight < parentHeight) contentHeight = parentHeight; + int parentHeight = parentWidget()->height(); + if (contentHeight < parentHeight) contentHeight = parentHeight; if (contentHeight != height()) setFixedHeight(contentHeight); } @@ -191,11 +187,7 @@ void HistoryField::exposeCurrent() { //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 HistoryPane::HistoryPane(QWidget *parent, Qt::WindowFlags flags) -#else -HistoryPane::HistoryPane(QWidget *parent, Qt::WFlags flags) -#endif : QWidget(parent) { m_frameArea = new QScrollArea(this); m_field = new HistoryField(m_frameArea); diff --git a/toonz/sources/toonz/historypane.h b/toonz/sources/toonz/historypane.h index 0376539b..e68a6b0b 100644 --- a/toonz/sources/toonz/historypane.h +++ b/toonz/sources/toonz/historypane.h @@ -11,11 +11,8 @@ class HistoryField final : public QFrame { QScrollArea *m_scrollArea; public: -#if QT_VERSION >= 0x050500 - HistoryField(QScrollArea *parent = 0, Qt::WindowFlags flags = 0); -#else - HistoryField(QScrollArea *parent = 0, Qt::WFlags flags = 0); -#endif + HistoryField(QScrollArea *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); ~HistoryField(){}; void updateContentHeight(int minimumHeight = -1); @@ -36,11 +33,7 @@ class HistoryPane final : public QWidget { QScrollArea *m_frameArea; public: -#if QT_VERSION >= 0x050500 HistoryPane(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - HistoryPane(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif ~HistoryPane(){}; protected: diff --git a/toonz/sources/toonz/icons/dark/actions/16/add_cell.svg b/toonz/sources/toonz/icons/dark/actions/16/add_cell.svg index 1b69200b..728a8ff7 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/add_cell.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/add_cell.svg @@ -1,14 +1,12 @@ - - - - - - - - - - + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/add_cells.svg b/toonz/sources/toonz/icons/dark/actions/16/add_cells.svg index ae748e30..08fa1fa7 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/add_cells.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/add_cells.svg @@ -1,20 +1,9 @@ - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/apply_match_lines.svg b/toonz/sources/toonz/icons/dark/actions/16/apply_match_lines.svg index 7826e742..09379ea0 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/apply_match_lines.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/apply_match_lines.svg @@ -1,23 +1,11 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/batchservers.svg b/toonz/sources/toonz/icons/dark/actions/16/batchservers.svg index 01970b02..0a4e2805 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/batchservers.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/batchservers.svg @@ -1,14 +1,8 @@ - - - - - - - - - - + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/blankframes.svg b/toonz/sources/toonz/icons/dark/actions/16/blankframes.svg new file mode 100644 index 00000000..3a00b2a9 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/blankframes.svg @@ -0,0 +1,130 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/cache_fx.svg b/toonz/sources/toonz/icons/dark/actions/16/cache_fx.svg new file mode 100644 index 00000000..f0a46924 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/cache_fx.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/camera_capture.svg b/toonz/sources/toonz/icons/dark/actions/16/camera_capture.svg index 220b2d4f..6dc5a9a5 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/camera_capture.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/camera_capture.svg @@ -1,24 +1,19 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/camera_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/camera_settings.svg index 0b7496cf..7ab3238f 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/camera_settings.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/camera_settings.svg @@ -1,22 +1,9 @@ - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/camera_test.svg b/toonz/sources/toonz/icons/dark/actions/16/camera_test.svg index 4df6712f..4ee61ec9 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/camera_test.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/camera_test.svg @@ -1,15 +1,9 @@ - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/clapboard.svg b/toonz/sources/toonz/icons/dark/actions/16/clapboard.svg index 90305ca2..d1a76b1d 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/clapboard.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/clapboard.svg @@ -1,12 +1,10 @@ - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/cleanup_add.svg b/toonz/sources/toonz/icons/dark/actions/16/cleanup_add.svg index 68f739ae..2a702eff 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/cleanup_add.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/cleanup_add.svg @@ -1,23 +1,10 @@ - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/cleanup_preview.svg b/toonz/sources/toonz/icons/dark/actions/16/cleanup_preview.svg index 26c38fa0..3304a00c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/cleanup_preview.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/cleanup_preview.svg @@ -1,23 +1,10 @@ - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/cleanup_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/cleanup_settings.svg index 827cb255..26513000 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/cleanup_settings.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/cleanup_settings.svg @@ -1,26 +1,9 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/clear_cache.svg b/toonz/sources/toonz/icons/dark/actions/16/clear_cache.svg index 5e5bfc3c..e70d9a43 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/clear_cache.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/clear_cache.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/collect_assets.svg b/toonz/sources/toonz/icons/dark/actions/16/collect_assets.svg new file mode 100644 index 00000000..0ffde693 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/collect_assets.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/colorfade.svg b/toonz/sources/toonz/icons/dark/actions/16/colorfade.svg index 8bdc6d03..c381abe7 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/colorfade.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/colorfade.svg @@ -1,26 +1,9 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/colormodel.svg b/toonz/sources/toonz/icons/dark/actions/16/colormodel.svg index d392270a..3962d99b 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/colormodel.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/colormodel.svg @@ -1,44 +1,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/compare.svg b/toonz/sources/toonz/icons/dark/actions/16/compare.svg index dfbe9326..d1851c3f 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/compare.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/compare.svg @@ -1,14 +1,12 @@ - - - - - - - - - - + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/connect.svg b/toonz/sources/toonz/icons/dark/actions/16/connect.svg new file mode 100644 index 00000000..32b3948c --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/connect.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/define_scanner.svg b/toonz/sources/toonz/icons/dark/actions/16/define_scanner.svg deleted file mode 100644 index f42d5615..00000000 --- a/toonz/sources/toonz/icons/dark/actions/16/define_scanner.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/toonz/sources/toonz/icons/dark/actions/16/delete.svg b/toonz/sources/toonz/icons/dark/actions/16/delete.svg index db18f9a7..e07bb680 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/delete.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/delete.svg @@ -1,24 +1,13 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/delete_lines.svg b/toonz/sources/toonz/icons/dark/actions/16/delete_lines.svg index 7a2a880a..22f73f74 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/delete_lines.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/delete_lines.svg @@ -1,17 +1,10 @@ - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/delete_match_lines.svg b/toonz/sources/toonz/icons/dark/actions/16/delete_match_lines.svg index 0de04718..fc96c00e 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/delete_match_lines.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/delete_match_lines.svg @@ -1,32 +1,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/delete_unused_styles.svg b/toonz/sources/toonz/icons/dark/actions/16/delete_unused_styles.svg new file mode 100644 index 00000000..78ad8c47 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/delete_unused_styles.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/desktop.svg b/toonz/sources/toonz/icons/dark/actions/16/desktop.svg index a6e80ecb..8af3a38c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/desktop.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/desktop.svg @@ -1,10 +1,11 @@ - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/dialogue_import.svg b/toonz/sources/toonz/icons/dark/actions/16/dialogue_import.svg index 6467533a..ceda9a70 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/dialogue_import.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/dialogue_import.svg @@ -1,22 +1,9 @@ - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/disconnect.svg b/toonz/sources/toonz/icons/dark/actions/16/disconnect.svg new file mode 100644 index 00000000..12052421 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/disconnect.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/duplicate.svg b/toonz/sources/toonz/icons/dark/actions/16/duplicate.svg index aaafcbfd..9a411f62 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/duplicate.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/duplicate.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/duplicate_drawing.svg b/toonz/sources/toonz/icons/dark/actions/16/duplicate_drawing.svg index 71b283c4..dcdc430c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/duplicate_drawing.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/duplicate_drawing.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/enter_group.svg b/toonz/sources/toonz/icons/dark/actions/16/enter_group.svg index 0e806788..b3b5839c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/enter_group.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/enter_group.svg @@ -1,35 +1,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/export_level.svg b/toonz/sources/toonz/icons/dark/actions/16/export_level.svg index 95cda039..f35c7a77 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/export_level.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/export_level.svg @@ -1,18 +1,9 @@ - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/export_scene.svg b/toonz/sources/toonz/icons/dark/actions/16/export_scene.svg new file mode 100644 index 00000000..d73dbe05 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/export_scene.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/filebrowser.svg b/toonz/sources/toonz/icons/dark/actions/16/filebrowser.svg index 753dfc26..b919cf71 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/filebrowser.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/filebrowser.svg @@ -1,14 +1,10 @@ - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/fill_auto.svg b/toonz/sources/toonz/icons/dark/actions/16/fill_auto.svg deleted file mode 100644 index abd6a349..00000000 --- a/toonz/sources/toonz/icons/dark/actions/16/fill_auto.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/toonz/sources/toonz/icons/dark/actions/16/fill_empty_cells.svg b/toonz/sources/toonz/icons/dark/actions/16/fill_empty_cells.svg index 972e5fa7..c1a7a5d9 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/fill_empty_cells.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/fill_empty_cells.svg @@ -1,23 +1,13 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/folder_new.svg b/toonz/sources/toonz/icons/dark/actions/16/folder_new.svg index 6e6e3b13..abc3557b 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/folder_new.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/folder_new.svg @@ -1,11 +1,9 @@ - - - + + + + + - - - - diff --git a/toonz/sources/toonz/icons/dark/actions/16/fx_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/fx_settings.svg index b61a78ba..1d38aa33 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/fx_settings.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/fx_settings.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/gap_check.svg b/toonz/sources/toonz/icons/dark/actions/16/gap_check.svg index a003255a..9bf603ba 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/gap_check.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/gap_check.svg @@ -1,29 +1,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/group.svg b/toonz/sources/toonz/icons/dark/actions/16/group.svg index 1001aff1..c992b76a 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/group.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/group.svg @@ -1,38 +1,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/history.svg b/toonz/sources/toonz/icons/dark/actions/16/history.svg index 75831140..9a914e98 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/history.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/history.svg @@ -1,22 +1,10 @@ - - + + - - - - - - - - - - - - - - - + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/insert.svg b/toonz/sources/toonz/icons/dark/actions/16/insert.svg index d0301086..fafbd6fd 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/insert.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/insert.svg @@ -1,14 +1,8 @@ - - - - - - - - - - + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/insert_above_after.svg b/toonz/sources/toonz/icons/dark/actions/16/insert_above_after.svg index f2bcbc28..f2dd3576 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/insert_above_after.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/insert_above_after.svg @@ -1,18 +1,9 @@ - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/insert_frame.svg b/toonz/sources/toonz/icons/dark/actions/16/insert_frame.svg index 198ea244..ade338a6 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/insert_frame.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/insert_frame.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/invert_selection.svg b/toonz/sources/toonz/icons/dark/actions/16/invert_selection.svg index b7719cf2..7da786d7 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/invert_selection.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/invert_selection.svg @@ -1,14 +1,8 @@ - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/leave_group.svg b/toonz/sources/toonz/icons/dark/actions/16/leave_group.svg index a33f77b0..5967d842 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/leave_group.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/leave_group.svg @@ -1,35 +1,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/level_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/level_settings.svg index 3649d774..057758cf 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/level_settings.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/level_settings.svg @@ -1,18 +1,9 @@ - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/level_strip.svg b/toonz/sources/toonz/icons/dark/actions/16/level_strip.svg index a682b3a7..bf9ef1dc 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/level_strip.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/level_strip.svg @@ -1,23 +1,8 @@ - - - - - - - - - - - - - - - - - - - + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/library.svg b/toonz/sources/toonz/icons/dark/actions/16/library.svg index 03c8cdb9..558f452c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/library.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/library.svg @@ -1,10 +1,10 @@ - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/link.svg b/toonz/sources/toonz/icons/dark/actions/16/link.svg new file mode 100644 index 00000000..46e44123 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/link.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/load_as_sub_xsheet.svg b/toonz/sources/toonz/icons/dark/actions/16/load_as_sub_xsheet.svg index c9929f13..b401d723 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/load_as_sub_xsheet.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/load_as_sub_xsheet.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/load_colormodel.svg b/toonz/sources/toonz/icons/dark/actions/16/load_colormodel.svg index fa63a6a8..d1470528 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/load_colormodel.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/load_colormodel.svg @@ -1,47 +1,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/load_folder.svg b/toonz/sources/toonz/icons/dark/actions/16/load_folder.svg index b0e26db6..c424840c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/load_folder.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/load_folder.svg @@ -1,13 +1,9 @@ - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/load_level.svg b/toonz/sources/toonz/icons/dark/actions/16/load_level.svg index 5b55411d..38ebee56 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/load_level.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/load_level.svg @@ -1,18 +1,9 @@ - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/load_scene.svg b/toonz/sources/toonz/icons/dark/actions/16/load_scene.svg index cc5e0064..e031eb77 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/load_scene.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/load_scene.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/macro.svg b/toonz/sources/toonz/icons/dark/actions/16/macro.svg new file mode 100644 index 00000000..cd6b6174 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/macro.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/merge.svg b/toonz/sources/toonz/icons/dark/actions/16/merge.svg index 1d0e1c6a..4f81635e 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/merge.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/merge.svg @@ -1,23 +1,11 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/merge_levels.svg b/toonz/sources/toonz/icons/dark/actions/16/merge_levels.svg index a209f43c..0d1efc26 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/merge_levels.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/merge_levels.svg @@ -1,23 +1,11 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/merge_levels_tlv.svg b/toonz/sources/toonz/icons/dark/actions/16/merge_levels_tlv.svg index db391753..1baa1a7f 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/merge_levels_tlv.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/merge_levels_tlv.svg @@ -1,26 +1,12 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/messagecenter.svg b/toonz/sources/toonz/icons/dark/actions/16/messagecenter.svg index fed28613..bfb83379 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/messagecenter.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/messagecenter.svg @@ -1,14 +1,8 @@ - - - - - - - - - - + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/my_computer.svg b/toonz/sources/toonz/icons/dark/actions/16/my_computer.svg index 04dcf1a6..443d3942 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/my_computer.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/my_computer.svg @@ -1,10 +1,9 @@ - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/my_documents.svg b/toonz/sources/toonz/icons/dark/actions/16/my_documents.svg index d8cb3f40..1d94da23 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/my_documents.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/my_documents.svg @@ -1,15 +1,9 @@ - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/new_document.svg b/toonz/sources/toonz/icons/dark/actions/16/new_document.svg deleted file mode 100644 index e5df3fc2..00000000 --- a/toonz/sources/toonz/icons/dark/actions/16/new_document.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/toonz/sources/toonz/icons/dark/actions/16/new_level.svg b/toonz/sources/toonz/icons/dark/actions/16/new_level.svg index f2baafa1..a8a66471 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/new_level.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/new_level.svg @@ -1,18 +1,9 @@ - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/new_note_level.svg b/toonz/sources/toonz/icons/dark/actions/16/new_note_level.svg index 50bc63e0..892069e3 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/new_note_level.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/new_note_level.svg @@ -1,23 +1,11 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/new_project.svg b/toonz/sources/toonz/icons/dark/actions/16/new_project.svg new file mode 100644 index 00000000..19c3602b --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/new_project.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/new_scene.svg b/toonz/sources/toonz/icons/dark/actions/16/new_scene.svg index c7b09f2d..c4289024 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/new_scene.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/new_scene.svg @@ -1,15 +1,9 @@ - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/newpage.svg b/toonz/sources/toonz/icons/dark/actions/16/newpage.svg new file mode 100644 index 00000000..14eac346 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/newpage.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/newstyle.svg b/toonz/sources/toonz/icons/dark/actions/16/newstyle.svg new file mode 100644 index 00000000..f2c48955 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/newstyle.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/toonz/sources/toonz/icons/dark/actions/16/next_drawing.svg b/toonz/sources/toonz/icons/dark/actions/16/next_drawing.svg index 9cbe1cd2..f188042c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/next_drawing.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/next_drawing.svg @@ -1,20 +1,13 @@ - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/output_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/output_settings.svg index 8755aaf6..39dae1b6 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/output_settings.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/output_settings.svg @@ -1,15 +1,9 @@ - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/palette.svg b/toonz/sources/toonz/icons/dark/actions/16/palette.svg index 83a7652a..4fcdf7f0 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/palette.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/palette.svg @@ -1,7 +1,8 @@ - - - + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/palette_tab.svg b/toonz/sources/toonz/icons/dark/actions/16/palette_tab.svg index 8d8f0453..a112280a 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/palette_tab.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/palette_tab.svg @@ -1,7 +1,7 @@ - - - + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/paste.svg b/toonz/sources/toonz/icons/dark/actions/16/paste.svg index 23b6e183..0d3ab215 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/paste.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/paste.svg @@ -1,15 +1,10 @@ - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/paste_above_after.svg b/toonz/sources/toonz/icons/dark/actions/16/paste_above_after.svg index e41f0b39..388388a3 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/paste_above_after.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/paste_above_after.svg @@ -1,19 +1,10 @@ - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/paste_color.svg b/toonz/sources/toonz/icons/dark/actions/16/paste_color.svg new file mode 100644 index 00000000..054d6e4a --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/paste_color.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/paste_color_and_name.svg b/toonz/sources/toonz/icons/dark/actions/16/paste_color_and_name.svg new file mode 100644 index 00000000..4246a94b --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/paste_color_and_name.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/paste_duplicate.svg b/toonz/sources/toonz/icons/dark/actions/16/paste_duplicate.svg index e06b6d54..55b395c7 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/paste_duplicate.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/paste_duplicate.svg @@ -1,15 +1,10 @@ - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/paste_into.svg b/toonz/sources/toonz/icons/dark/actions/16/paste_into.svg index 50758a31..4bb1d441 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/paste_into.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/paste_into.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/paste_name.svg b/toonz/sources/toonz/icons/dark/actions/16/paste_name.svg new file mode 100644 index 00000000..d89d8e97 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/paste_name.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/paste_numbers.svg b/toonz/sources/toonz/icons/dark/actions/16/paste_numbers.svg index e09c247d..d8d6cf17 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/paste_numbers.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/paste_numbers.svg @@ -1,14 +1,10 @@ - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/preset.svg b/toonz/sources/toonz/icons/dark/actions/16/preset.svg new file mode 100644 index 00000000..8087fdf4 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/preset.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/prev_drawing.svg b/toonz/sources/toonz/icons/dark/actions/16/prev_drawing.svg index 49a78af8..c4817a5d 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/prev_drawing.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/prev_drawing.svg @@ -1,20 +1,13 @@ - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/preview_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/preview_settings.svg index d3c4e86c..49412dfe 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/preview_settings.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/preview_settings.svg @@ -1,20 +1,9 @@ - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/project_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/project_settings.svg new file mode 100644 index 00000000..03fa3cd2 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/project_settings.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/quit.svg b/toonz/sources/toonz/icons/dark/actions/16/quit.svg index e466aa97..fba141fc 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/quit.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/quit.svg @@ -1,14 +1,9 @@ - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/recordaudio.svg b/toonz/sources/toonz/icons/dark/actions/16/recordaudio.svg index f8fac984..4a38502f 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/recordaudio.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/recordaudio.svg @@ -1,14 +1,10 @@ - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/reload_level.svg b/toonz/sources/toonz/icons/dark/actions/16/reload_level.svg index e7b39e26..d74e9e6d 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/reload_level.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/reload_level.svg @@ -1,20 +1,9 @@ - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/remove_cell.svg b/toonz/sources/toonz/icons/dark/actions/16/remove_cell.svg index d9cc5ca3..6ca17d32 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/remove_cell.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/remove_cell.svg @@ -1,17 +1,13 @@ - - - - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/remove_cells.svg b/toonz/sources/toonz/icons/dark/actions/16/remove_cells.svg index e0cf675f..25a6c16a 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/remove_cells.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/remove_cells.svg @@ -1,19 +1,11 @@ - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/remove_empty_columns.svg b/toonz/sources/toonz/icons/dark/actions/16/remove_empty_columns.svg index 6a3815e7..39ca154e 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/remove_empty_columns.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/remove_empty_columns.svg @@ -1,26 +1,12 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/remove_frame.svg b/toonz/sources/toonz/icons/dark/actions/16/remove_frame.svg index ca4cfe4c..b45728c1 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/remove_frame.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/remove_frame.svg @@ -1,17 +1,10 @@ - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/remove_level.svg b/toonz/sources/toonz/icons/dark/actions/16/remove_level.svg index af39baf8..c88c6530 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/remove_level.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/remove_level.svg @@ -1,18 +1,10 @@ - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/remove_multiple_keys.svg b/toonz/sources/toonz/icons/dark/actions/16/remove_multiple_keys.svg index 2bb9fccd..62db36ae 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/remove_multiple_keys.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/remove_multiple_keys.svg @@ -1,17 +1,10 @@ - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/remove_unused_levels.svg b/toonz/sources/toonz/icons/dark/actions/16/remove_unused_levels.svg index ce8e98b2..36c9c5a3 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/remove_unused_levels.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/remove_unused_levels.svg @@ -1,20 +1,9 @@ - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/rename.svg b/toonz/sources/toonz/icons/dark/actions/16/rename.svg index a281a386..2d9952a8 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/rename.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/rename.svg @@ -1,12 +1,9 @@ - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/render.svg b/toonz/sources/toonz/icons/dark/actions/16/render.svg index e2ed72ff..851a2e71 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/render.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/render.svg @@ -1,20 +1,8 @@ - - - - - - - - - - - - - - - - + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/render_add.svg b/toonz/sources/toonz/icons/dark/actions/16/render_add.svg deleted file mode 100644 index 2f30e318..00000000 --- a/toonz/sources/toonz/icons/dark/actions/16/render_add.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/toonz/sources/toonz/icons/dark/actions/16/render_clapboard.svg b/toonz/sources/toonz/icons/dark/actions/16/render_clapboard.svg deleted file mode 100644 index 58993416..00000000 --- a/toonz/sources/toonz/icons/dark/actions/16/render_clapboard.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/toonz/sources/toonz/icons/dark/actions/16/renumber.svg b/toonz/sources/toonz/icons/dark/actions/16/renumber.svg index bf709943..bb50761c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/renumber.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/renumber.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/replace_parent_directory.svg b/toonz/sources/toonz/icons/dark/actions/16/replace_parent_directory.svg index d18ee7d1..39d26e34 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/replace_parent_directory.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/replace_parent_directory.svg @@ -1,26 +1,11 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/resequence.svg b/toonz/sources/toonz/icons/dark/actions/16/resequence.svg index 29f93ad5..f7ba94d9 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/resequence.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/resequence.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/reset_cropbox.svg b/toonz/sources/toonz/icons/dark/actions/16/reset_cropbox.svg index 0cd4c238..839823b3 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/reset_cropbox.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/reset_cropbox.svg @@ -1,17 +1,10 @@ - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/revert_level_to_cleanup.svg b/toonz/sources/toonz/icons/dark/actions/16/revert_level_to_cleanup.svg index 90cf6c17..9e3344d3 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/revert_level_to_cleanup.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/revert_level_to_cleanup.svg @@ -1,20 +1,9 @@ - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/revert_scene.svg b/toonz/sources/toonz/icons/dark/actions/16/revert_scene.svg index 0971d2d6..267e7135 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/revert_scene.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/revert_scene.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/rotate_reset.svg b/toonz/sources/toonz/icons/dark/actions/16/rotate_reset.svg new file mode 100644 index 00000000..af08ff6d --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/rotate_reset.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/run_script.svg b/toonz/sources/toonz/icons/dark/actions/16/run_script.svg index 29fc781c..b89551cf 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/run_script.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/run_script.svg @@ -1,21 +1,10 @@ - - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/save.svg b/toonz/sources/toonz/icons/dark/actions/16/save.svg index 1e6e13f7..a1a37f61 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/save.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/save.svg @@ -1,20 +1,9 @@ - - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/save_all_levels.svg b/toonz/sources/toonz/icons/dark/actions/16/save_all_levels.svg index 83525358..86a604ba 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/save_all_levels.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/save_all_levels.svg @@ -1,29 +1,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/save_and_render.svg b/toonz/sources/toonz/icons/dark/actions/16/save_and_render.svg new file mode 100644 index 00000000..d8c37088 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/save_and_render.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/save_default_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/save_default_settings.svg new file mode 100644 index 00000000..d2ed3654 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/save_default_settings.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/save_level.svg b/toonz/sources/toonz/icons/dark/actions/16/save_level.svg index 65c0edf8..6572cea5 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/save_level.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/save_level.svg @@ -1,18 +1,9 @@ - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/save_level_as.svg b/toonz/sources/toonz/icons/dark/actions/16/save_level_as.svg index d93482bc..77bebe96 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/save_level_as.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/save_level_as.svg @@ -1,23 +1,9 @@ - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/save_scene.svg b/toonz/sources/toonz/icons/dark/actions/16/save_scene.svg index d667f027..6dd17425 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/save_scene.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/save_scene.svg @@ -1,13 +1,9 @@ - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/save_scene_as.svg b/toonz/sources/toonz/icons/dark/actions/16/save_scene_as.svg index ba51fe16..7c414072 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/save_scene_as.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/save_scene_as.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/saveall.svg b/toonz/sources/toonz/icons/dark/actions/16/saveall.svg index 6e9bc670..cbc3358f 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/saveall.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/saveall.svg @@ -1,26 +1,10 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/saveas.svg b/toonz/sources/toonz/icons/dark/actions/16/saveas.svg index afe150f5..6574c3c5 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/saveas.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/saveas.svg @@ -1,23 +1,10 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/scanner_define.svg b/toonz/sources/toonz/icons/dark/actions/16/scanner_define.svg new file mode 100644 index 00000000..c9c2c972 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/scanner_define.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/scanner_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/scanner_settings.svg index 2a3490a5..767952cc 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/scanner_settings.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/scanner_settings.svg @@ -1,20 +1,9 @@ - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/scene_export.svg b/toonz/sources/toonz/icons/dark/actions/16/scene_export.svg index 4797df94..4dc1547a 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/scene_export.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/scene_export.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/scene_settings.svg b/toonz/sources/toonz/icons/dark/actions/16/scene_settings.svg index aaa79eba..04e8055f 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/scene_settings.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/scene_settings.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/scenecast.svg b/toonz/sources/toonz/icons/dark/actions/16/scenecast.svg index 41cb192e..b1a2d5ac 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/scenecast.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/scenecast.svg @@ -1,17 +1,10 @@ - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/schematic.svg b/toonz/sources/toonz/icons/dark/actions/16/schematic.svg index da9af49d..f2081952 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/schematic.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/schematic.svg @@ -1,14 +1,14 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/select_all.svg b/toonz/sources/toonz/icons/dark/actions/16/select_all.svg index 5c2e0091..8f7ea1b7 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/select_all.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/select_all.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/separate_colors.svg b/toonz/sources/toonz/icons/dark/actions/16/separate_colors.svg index 9c718a61..0c686e27 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/separate_colors.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/separate_colors.svg @@ -1,25 +1,16 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/set_cropbox.svg b/toonz/sources/toonz/icons/dark/actions/16/set_cropbox.svg index 79515ed2..bec1376b 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/set_cropbox.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/set_cropbox.svg @@ -1,12 +1,8 @@ - - - - - - - - + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/settings_reset.svg b/toonz/sources/toonz/icons/dark/actions/16/settings_reset.svg index 98299198..9e9d6981 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/settings_reset.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/settings_reset.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace.svg b/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace.svg index 5b96e26f..2f014bb0 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace.svg @@ -1,14 +1,8 @@ - - - - - - - - - - + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_edit.svg b/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_edit.svg index 89f26ffa..43ccb61d 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_edit.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_edit.svg @@ -1,13 +1,9 @@ - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_no_shift.svg b/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_no_shift.svg index 64353111..fdca80e5 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_no_shift.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_no_shift.svg @@ -1,15 +1,9 @@ - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_reset.svg b/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_reset.svg index beab9a27..c003d0fd 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_reset.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/shift_and_trace_reset.svg @@ -1,15 +1,9 @@ - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/shift_keys_down.svg b/toonz/sources/toonz/icons/dark/actions/16/shift_keys_down.svg index ea16d899..a0870788 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/shift_keys_down.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/shift_keys_down.svg @@ -1,23 +1,11 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/shift_keys_up.svg b/toonz/sources/toonz/icons/dark/actions/16/shift_keys_up.svg index 1d417909..213222f6 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/shift_keys_up.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/shift_keys_up.svg @@ -1,23 +1,11 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/shortcuts.svg b/toonz/sources/toonz/icons/dark/actions/16/shortcuts.svg index 9246c404..0ff64dd8 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/shortcuts.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/shortcuts.svg @@ -1,12 +1,10 @@ - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/show_folder_contents.svg b/toonz/sources/toonz/icons/dark/actions/16/show_folder_contents.svg index 59bb6698..8255de3c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/show_folder_contents.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/show_folder_contents.svg @@ -1,14 +1,9 @@ - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/studiopalette.svg b/toonz/sources/toonz/icons/dark/actions/16/studiopalette.svg index 6d4758a4..e7c764f1 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/studiopalette.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/studiopalette.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/sub_clone.svg b/toonz/sources/toonz/icons/dark/actions/16/sub_clone.svg index 5304ea44..6c78a24a 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/sub_clone.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/sub_clone.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/sub_collapse.svg b/toonz/sources/toonz/icons/dark/actions/16/sub_collapse.svg index 1a442101..440ee845 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/sub_collapse.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/sub_collapse.svg @@ -1,26 +1,12 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/sub_edit_in_place.svg b/toonz/sources/toonz/icons/dark/actions/16/sub_edit_in_place.svg index 0f0a0a7e..5d5e8fe3 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/sub_edit_in_place.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/sub_edit_in_place.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/sub_enter.svg b/toonz/sources/toonz/icons/dark/actions/16/sub_enter.svg index 7fc769bf..931b5e23 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/sub_enter.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/sub_enter.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/sub_explode.svg b/toonz/sources/toonz/icons/dark/actions/16/sub_explode.svg index 14f9460b..12daa8e8 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/sub_explode.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/sub_explode.svg @@ -1,26 +1,12 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/sub_leave.svg b/toonz/sources/toonz/icons/dark/actions/16/sub_leave.svg index 74e3c82b..4275cbea 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/sub_leave.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/sub_leave.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/sub_xsheet.svg b/toonz/sources/toonz/icons/dark/actions/16/sub_xsheet.svg index 7bf17541..23eaea5c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/sub_xsheet.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/sub_xsheet.svg @@ -1,19 +1,9 @@ - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/sub_xsheet_saveas.svg b/toonz/sources/toonz/icons/dark/actions/16/sub_xsheet_saveas.svg index daabc7b4..fa185fba 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/sub_xsheet_saveas.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/sub_xsheet_saveas.svg @@ -1,22 +1,10 @@ - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/subcamera.svg b/toonz/sources/toonz/icons/dark/actions/16/subcamera.svg index 682d3d9b..7139defb 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/subcamera.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/subcamera.svg @@ -1,17 +1,13 @@ - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/tape_end_to_end.svg b/toonz/sources/toonz/icons/dark/actions/16/tape_end_to_end.svg new file mode 100644 index 00000000..8807609b --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/tape_end_to_end.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/tape_end_to_line.svg b/toonz/sources/toonz/icons/dark/actions/16/tape_end_to_line.svg new file mode 100644 index 00000000..54d149f2 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/tape_end_to_line.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/tape_line_to_line.svg b/toonz/sources/toonz/icons/dark/actions/16/tape_line_to_line.svg new file mode 100644 index 00000000..d65f88ec --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/tape_line_to_line.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/tasks.svg b/toonz/sources/toonz/icons/dark/actions/16/tasks.svg index 035a49a4..873a70b7 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/tasks.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/tasks.svg @@ -1,14 +1,13 @@ - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/timeline.svg b/toonz/sources/toonz/icons/dark/actions/16/timeline.svg index 78964289..d41a53db 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/timeline.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/timeline.svg @@ -1,14 +1,10 @@ - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/toggle_autofill.svg b/toonz/sources/toonz/icons/dark/actions/16/toggle_autofill.svg new file mode 100644 index 00000000..3ad03c54 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/toggle_autofill.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/toonz/sources/toonz/icons/dark/actions/16/toggle_fullscreen.svg b/toonz/sources/toonz/icons/dark/actions/16/toggle_fullscreen.svg index 364868d9..a4333b92 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/toggle_fullscreen.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/toggle_fullscreen.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/tracking_options.svg b/toonz/sources/toonz/icons/dark/actions/16/tracking_options.svg deleted file mode 100644 index c0bf0edf..00000000 --- a/toonz/sources/toonz/icons/dark/actions/16/tracking_options.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/toonz/sources/toonz/icons/dark/actions/16/uncache_fx.svg b/toonz/sources/toonz/icons/dark/actions/16/uncache_fx.svg new file mode 100644 index 00000000..889f0071 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/uncache_fx.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/ungroup.svg b/toonz/sources/toonz/icons/dark/actions/16/ungroup.svg index d2a34982..aa29584c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/ungroup.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/ungroup.svg @@ -1,69 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/unlink.svg b/toonz/sources/toonz/icons/dark/actions/16/unlink.svg new file mode 100644 index 00000000..92f3d52a --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/unlink.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/view_file.svg b/toonz/sources/toonz/icons/dark/actions/16/view_file.svg index ef6d2748..da0c7b1c 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/view_file.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/view_file.svg @@ -1,22 +1,9 @@ - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/view_vector_as_raster.svg b/toonz/sources/toonz/icons/dark/actions/16/view_vector_as_raster.svg index 9b2cd10f..be43b7ec 100644 --- a/toonz/sources/toonz/icons/dark/actions/16/view_vector_as_raster.svg +++ b/toonz/sources/toonz/icons/dark/actions/16/view_vector_as_raster.svg @@ -1,17 +1,13 @@ - - - - - - - - - - - - - + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/xsheet_connect.svg b/toonz/sources/toonz/icons/dark/actions/16/xsheet_connect.svg new file mode 100644 index 00000000..4906d132 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/xsheet_connect.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/xsheet_disconnect.svg b/toonz/sources/toonz/icons/dark/actions/16/xsheet_disconnect.svg new file mode 100644 index 00000000..7e941386 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/xsheet_disconnect.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/zero_thick_lines.svg b/toonz/sources/toonz/icons/dark/actions/16/zero_thick_lines.svg new file mode 100644 index 00000000..f405c20a --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/zero_thick_lines.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/16/zoom_reset.svg b/toonz/sources/toonz/icons/dark/actions/16/zoom_reset.svg new file mode 100644 index 00000000..85a56466 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/zoom_reset.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/eraser_polyline.svg b/toonz/sources/toonz/icons/dark/actions/20/eraser_polyline.svg index 51e29234..f1aaec92 100644 --- a/toonz/sources/toonz/icons/dark/actions/20/eraser_polyline.svg +++ b/toonz/sources/toonz/icons/dark/actions/20/eraser_polyline.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/eraser_segment.svg b/toonz/sources/toonz/icons/dark/actions/20/eraser_segment.svg index c99e55bd..8a92d3b4 100644 --- a/toonz/sources/toonz/icons/dark/actions/20/eraser_segment.svg +++ b/toonz/sources/toonz/icons/dark/actions/20/eraser_segment.svg @@ -1,26 +1,19 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/fill_freepick.svg b/toonz/sources/toonz/icons/dark/actions/20/fill_freepick.svg new file mode 100644 index 00000000..81f3e1e3 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/fill_freepick.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/fill_polyline.svg b/toonz/sources/toonz/icons/dark/actions/20/fill_polyline.svg index 6ad9ea83..cd9fb347 100644 --- a/toonz/sources/toonz/icons/dark/actions/20/fill_polyline.svg +++ b/toonz/sources/toonz/icons/dark/actions/20/fill_polyline.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/newpage.svg b/toonz/sources/toonz/icons/dark/actions/20/newpage.svg deleted file mode 100644 index 62b8d95a..00000000 --- a/toonz/sources/toonz/icons/dark/actions/20/newpage.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/toonz/sources/toonz/icons/dark/actions/20/stylepicker_areas.svg b/toonz/sources/toonz/icons/dark/actions/20/stylepicker_areas.svg new file mode 100644 index 00000000..d6eae334 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/stylepicker_areas.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/toonz/sources/toonz/icons/dark/actions/20/stylepicker_lines.svg b/toonz/sources/toonz/icons/dark/actions/20/stylepicker_lines.svg new file mode 100644 index 00000000..51e0ac9d --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/stylepicker_lines.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/toonz/sources/toonz/icons/dark/actions/20/stylepicker_lines_areas.svg b/toonz/sources/toonz/icons/dark/actions/20/stylepicker_lines_areas.svg new file mode 100644 index 00000000..b2a2c945 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/stylepicker_lines_areas.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/toonz/sources/toonz/icons/dark/actions/20/tape_freehand.svg b/toonz/sources/toonz/icons/dark/actions/20/tape_freehand.svg new file mode 100644 index 00000000..030a38a1 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/tape_freehand.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/tape_normal.svg b/toonz/sources/toonz/icons/dark/actions/20/tape_normal.svg new file mode 100644 index 00000000..79a7c634 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/tape_normal.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/tape_polyline.svg b/toonz/sources/toonz/icons/dark/actions/20/tape_polyline.svg new file mode 100644 index 00000000..4d1731fa --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/tape_polyline.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/tape_rectangular.svg b/toonz/sources/toonz/icons/dark/actions/20/tape_rectangular.svg new file mode 100644 index 00000000..5e9e46d1 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/tape_rectangular.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/type_erase_segment.svg b/toonz/sources/toonz/icons/dark/actions/20/type_erase_segment.svg new file mode 100644 index 00000000..361d8068 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/type_erase_segment.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/type_lasso.svg b/toonz/sources/toonz/icons/dark/actions/20/type_lasso.svg index d5a26857..99ae89f3 100644 --- a/toonz/sources/toonz/icons/dark/actions/20/type_lasso.svg +++ b/toonz/sources/toonz/icons/dark/actions/20/type_lasso.svg @@ -1,20 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/type_normal.svg b/toonz/sources/toonz/icons/dark/actions/20/type_normal.svg index ce8ae3b6..b9c6f9b8 100644 --- a/toonz/sources/toonz/icons/dark/actions/20/type_normal.svg +++ b/toonz/sources/toonz/icons/dark/actions/20/type_normal.svg @@ -1,28 +1,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/type_pickerlasso.svg b/toonz/sources/toonz/icons/dark/actions/20/type_pickerlasso.svg new file mode 100644 index 00000000..1a91c97e --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/20/type_pickerlasso.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/type_polyline.svg b/toonz/sources/toonz/icons/dark/actions/20/type_polyline.svg index 813f8c01..f347a4c1 100644 --- a/toonz/sources/toonz/icons/dark/actions/20/type_polyline.svg +++ b/toonz/sources/toonz/icons/dark/actions/20/type_polyline.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/20/type_rectangular.svg b/toonz/sources/toonz/icons/dark/actions/20/type_rectangular.svg index 96c14a33..b007df0e 100644 --- a/toonz/sources/toonz/icons/dark/actions/20/type_rectangular.svg +++ b/toonz/sources/toonz/icons/dark/actions/20/type_rectangular.svg @@ -1,17 +1,9 @@ - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/35/task_render_completed.svg b/toonz/sources/toonz/icons/dark/actions/35/task_render_completed.svg index 6eb65b6a..d433e195 100644 --- a/toonz/sources/toonz/icons/dark/actions/35/task_render_completed.svg +++ b/toonz/sources/toonz/icons/dark/actions/35/task_render_completed.svg @@ -1,14 +1,8 @@ - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/35/task_render_completed_with_errors.svg b/toonz/sources/toonz/icons/dark/actions/35/task_render_completed_with_errors.svg index ebf5523f..5af9935a 100644 --- a/toonz/sources/toonz/icons/dark/actions/35/task_render_completed_with_errors.svg +++ b/toonz/sources/toonz/icons/dark/actions/35/task_render_completed_with_errors.svg @@ -1,24 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/35/task_render_computing.svg b/toonz/sources/toonz/icons/dark/actions/35/task_render_computing.svg index e85ac507..d7129869 100644 --- a/toonz/sources/toonz/icons/dark/actions/35/task_render_computing.svg +++ b/toonz/sources/toonz/icons/dark/actions/35/task_render_computing.svg @@ -1,25 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/35/task_render_failed.svg b/toonz/sources/toonz/icons/dark/actions/35/task_render_failed.svg index 017699e3..dfa59056 100644 --- a/toonz/sources/toonz/icons/dark/actions/35/task_render_failed.svg +++ b/toonz/sources/toonz/icons/dark/actions/35/task_render_failed.svg @@ -1,20 +1,8 @@ - - - - - - - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/actions/35/task_render_suspended.svg b/toonz/sources/toonz/icons/dark/actions/35/task_render_suspended.svg index 39b2163c..783c9547 100644 --- a/toonz/sources/toonz/icons/dark/actions/35/task_render_suspended.svg +++ b/toonz/sources/toonz/icons/dark/actions/35/task_render_suspended.svg @@ -1,14 +1,8 @@ - - - - - - - - - - - + + + + + diff --git a/toonz/sources/toonz/icons/dark/tools/20/tape.svg b/toonz/sources/toonz/icons/dark/tools/20/tape.svg index 7ba95573..854b5aa3 100644 --- a/toonz/sources/toonz/icons/dark/tools/20/tape.svg +++ b/toonz/sources/toonz/icons/dark/tools/20/tape.svg @@ -1,11 +1,7 @@ - + - - - - - - + + diff --git a/toonz/sources/toonz/icons/templates/20x20_tool_command.svg b/toonz/sources/toonz/icons/templates/20x20_tool_command.svg deleted file mode 100644 index a831d658..00000000 --- a/toonz/sources/toonz/icons/templates/20x20_tool_command.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/toonz/sources/toonz/icons/templates/60x60_mimetype.svg b/toonz/sources/toonz/icons/templates/60x60_mimetype.svg deleted file mode 100644 index 08a64a9b..00000000 --- a/toonz/sources/toonz/icons/templates/60x60_mimetype.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/toonz/sources/toonz/icons/templates/misc_emblems_shapes.svg b/toonz/sources/toonz/icons/templates/misc_emblems_shapes.svg deleted file mode 100644 index adf26a7c..00000000 --- a/toonz/sources/toonz/icons/templates/misc_emblems_shapes.svg +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/toonz/sources/toonz/imageviewer.cpp b/toonz/sources/toonz/imageviewer.cpp index 45e332d7..60ea867a 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) @@ -407,8 +408,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; @@ -420,7 +422,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 @@ -842,7 +844,7 @@ void ImageViewer::mouseMoveEvent(QMouseEvent *event) { if (m_visualSettings.m_defineLoadbox && m_flipbook) { if (m_mouseButton == Qt::LeftButton) updateLoadbox(curPos); - else if (m_mouseButton == Qt::MidButton) + else if (m_mouseButton == Qt::MiddleButton) panQt(curQPos - m_pos); else updateCursor(curPos); @@ -871,7 +873,7 @@ void ImageViewer::mouseMoveEvent(QMouseEvent *event) { if (m_compareSettings.m_dragCompareX || m_compareSettings.m_dragCompareY) dragCompare(curQPos - m_pos); - else if (m_mouseButton == Qt::MidButton) + else if (m_mouseButton == Qt::MiddleButton) panQt(curQPos - m_pos); m_pos = curQPos; @@ -950,6 +952,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; } @@ -982,8 +986,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; @@ -1001,25 +1005,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); + TPixel32 pixForStyleEditor; - // 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); + 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); } } @@ -1050,7 +1064,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, @@ -1083,20 +1097,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); } //----------------------------------------------------------------------------- @@ -1251,7 +1273,6 @@ void ImageViewer::mouseReleaseEvent(QMouseEvent *event) { */ void ImageViewer::wheelEvent(QWheelEvent *event) { if (!m_image) return; - if (event->orientation() == Qt::Horizontal) return; int delta = 0; switch (event->source()) { case Qt::MouseEventNotSynthesized: { @@ -1285,14 +1306,19 @@ void ImageViewer::wheelEvent(QWheelEvent *event) { } // end switch - if (abs(delta) > 0) { + if (delta != 0) { if ((m_gestureActive == true && m_touchDevice == QTouchDevice::TouchScreen) || m_gestureActive == false) { - int delta = event->delta() > 0 ? 120 : -120; + int d = delta > 0 ? 120 : -120; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QPoint center(event->position().x() * getDevPixRatio() - width() / 2, + -event->position().y() * getDevPixRatio() + height() / 2); +#else QPoint center(event->pos().x() * getDevPixRatio() - width() / 2, -event->pos().y() * getDevPixRatio() + height() / 2); - zoomQt(center, exp(0.001 * delta)); +#endif + zoomQt(center, exp(0.001 * d)); } } event->accept(); diff --git a/toonz/sources/toonz/imageviewer.h b/toonz/sources/toonz/imageviewer.h index 4d5ee160..61e09966 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/kis_tablet_support_win8.cpp b/toonz/sources/toonz/kis_tablet_support_win8.cpp index 798be5ab..6c94e80c 100644 --- a/toonz/sources/toonz/kis_tablet_support_win8.cpp +++ b/toonz/sources/toonz/kis_tablet_support_win8.cpp @@ -95,13 +95,13 @@ class Win8PointerInputApi { public: #define DEFINE_FP_FROM_WINAPI(func) \ - \ + \ public: \ using p##func##_t = std::add_pointer::type; \ - \ + \ private: \ p##func##_t m_p##func = nullptr; \ - \ + \ public: \ const p##func##_t &func = m_p##func; // const fp ref to member @@ -523,8 +523,8 @@ QTabletEvent makeProximityTabletEvent(const QEvent::Type eventType, Qt::NoModifier, // keyState reinterpret_cast(penInfo.pointerInfo.sourceDevice), // uniqueID Qt::NoButton, // button - (Qt::MouseButtons)0 // buttons - ); + Qt::MouseButtons() // buttons + ); } // void rotateTiltAngles(int &tiltX, int &tiltY, const DISPLAYCONFIG_ROTATION @@ -660,7 +660,7 @@ QTabletEvent makePositionalTabletEvent(const QWidget *targetWidget, reinterpret_cast(penInfo.pointerInfo.sourceDevice), // uniqueID mouseButton, // button mouseButtons // buttons - ); + ); } bool sendProximityTabletEvent(const QEvent::Type eventType, diff --git a/toonz/sources/toonz/layerfooterpanel.h b/toonz/sources/toonz/layerfooterpanel.h index 22e4e3ad..e24725cb 100644 --- a/toonz/sources/toonz/layerfooterpanel.h +++ b/toonz/sources/toonz/layerfooterpanel.h @@ -33,13 +33,8 @@ private: XsheetViewer *m_viewer; public: -#if QT_VERSION >= 0x050500 LayerFooterPanel(XsheetViewer *viewer, QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - LayerFooterPanel(XsheetViewer *viewer, QWidget *parent = 0, - Qt::WFlags flags = 0); -#endif ~LayerFooterPanel(); void showOrHide(const Orientation *o); diff --git a/toonz/sources/toonz/layerheaderpanel.cpp b/toonz/sources/toonz/layerheaderpanel.cpp index ac97b710..37be369e 100644 --- a/toonz/sources/toonz/layerheaderpanel.cpp +++ b/toonz/sources/toonz/layerheaderpanel.cpp @@ -15,13 +15,8 @@ using XsheetGUI::ColumnArea; -#if QT_VERSION >= 0x050500 LayerHeaderPanel::LayerHeaderPanel(XsheetViewer *viewer, QWidget *parent, Qt::WindowFlags flags) -#else -LayerHeaderPanel::LayerHeaderPanel(XsheetViewer *viewer, QWidget *parent, - Qt::WFlags flags) -#endif : QWidget(parent, flags), m_viewer(viewer) { const Orientation *o = Orientations::leftToRight(); QRect rect = o->rect(PredefinedRect::LAYER_HEADER_PANEL); @@ -52,7 +47,7 @@ QRect shorter(const QRect original) { return original.adjusted(0, 2, 0, -2); } QLine leftSide(const QRect &r) { return QLine(r.topLeft(), r.bottomLeft()); } QLine rightSide(const QRect &r) { return QLine(r.topRight(), r.bottomRight()); } -} +} // namespace void LayerHeaderPanel::paintEvent(QPaintEvent *event) { QPainter p(this); @@ -66,15 +61,15 @@ void LayerHeaderPanel::paintEvent(QPaintEvent *event) { //QRect rect = QRect(QPoint(0, 0), size()); //p.fillRect(rect.adjusted(0, 0, -3, 0), slightlyLighter); - QImage preview = (m_buttonHighlighted == PreviewButton - ? m_viewer->getLayerHeaderPreviewOverImage() - : m_viewer->getLayerHeaderPreviewImage()); + QImage preview = (m_buttonHighlighted == PreviewButton + ? m_viewer->getLayerHeaderPreviewOverImage() + : m_viewer->getLayerHeaderPreviewImage()); QImage camstand = (m_buttonHighlighted == CamstandButton ? m_viewer->getLayerHeaderCamstandOverImage() : m_viewer->getLayerHeaderCamstandImage()); - QImage lock = (m_buttonHighlighted == LockButton - ? m_viewer->getLayerHeaderLockOverImage() - : m_viewer->getLayerHeaderLockImage()); + QImage lock = (m_buttonHighlighted == LockButton + ? m_viewer->getLayerHeaderLockOverImage() + : m_viewer->getLayerHeaderLockImage()); drawIcon(p, PredefinedRect::PANEL_EYE, boost::none, preview); drawIcon(p, PredefinedRect::PANEL_PREVIEW_LAYER, boost::none, camstand); diff --git a/toonz/sources/toonz/layerheaderpanel.h b/toonz/sources/toonz/layerheaderpanel.h index 1f6b49ef..110fa5c9 100644 --- a/toonz/sources/toonz/layerheaderpanel.h +++ b/toonz/sources/toonz/layerheaderpanel.h @@ -29,13 +29,8 @@ private: XsheetViewer *m_viewer; public: -#if QT_VERSION >= 0x050500 LayerHeaderPanel(XsheetViewer *viewer, QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - LayerHeaderPanel(XsheetViewer *viewer, QWidget *parent = 0, - Qt::WFlags flags = 0); -#endif ~LayerHeaderPanel(); void showOrHide(const Orientation *o); diff --git a/toonz/sources/toonz/levelsettingspopup.cpp b/toonz/sources/toonz/levelsettingspopup.cpp index 476710c4..4a87aecb 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,7 +250,21 @@ private: TApp::instance()->getCurrentLevel()->notifyLevelChange(); } - void setValue(const QVariant value) const { + 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: setName(value.toString()); @@ -280,6 +295,9 @@ private: case WhiteTransp: setWhiteTransp(value.toBool()); break; + case ColorSpaceGamma: + setColorSpaceGamma(value.toDouble()); + break; default: break; } @@ -367,6 +385,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); @@ -430,6 +452,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); @@ -523,6 +549,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); @@ -555,6 +584,8 @@ LevelSettingsPopup::LevelSettingsPopup() connect(m_whiteTransp, SIGNAL(clicked(bool)), SLOT(onWhiteTranspClicked())); + connect(m_colorSpaceGammaFld, SIGNAL(editingFinished()), + SLOT(onColorSpaceGammaFieldChanged())); updateLevelSettings(); } @@ -664,8 +695,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: @@ -705,7 +740,8 @@ LevelSettingsValues LevelSettingsPopup::getValues(TXshLevelP level) { case ToonzRaster: values.typeStr = tr("Smart Raster level"); break; - case Raster: + case NonLinearRaster: + case LinearRaster: values.typeStr = tr("Raster level"); break; case Mesh: @@ -740,7 +776,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(); @@ -770,6 +806,9 @@ LevelSettingsValues LevelSettingsPopup::getValues(TXshLevelP level) { ? Qt::Checked : Qt::Unchecked; values.softness = sl->getProperties()->antialiasSoftness(); + + if (levelType == LinearRaster) + values.colorSpaceGamma = sl->getProperties()->colorSpaceGamma(); } } @@ -905,6 +944,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; @@ -942,11 +982,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); + } } //----------------------------------------------------------------------------- @@ -1621,6 +1672,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 41bc5ee1..f1d8c135 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, + 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 = 0x100, - HideOnPixelMode = 0x200, - NoSelection = 0x400, + 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); bool m_hideAlreadyCalled = false; @@ -120,6 +125,7 @@ protected slots: void onDoAntialiasClicked(); void onAntialiasSoftnessChanged(); void onWhiteTranspClicked(); + void onColorSpaceGammaFieldChanged(); void onSceneChanged(); void onPreferenceChanged(const QString &); }; diff --git a/toonz/sources/toonz/lipsyncpopup.cpp b/toonz/sources/toonz/lipsyncpopup.cpp index 2101420f..b2ce8bbc 100644 --- a/toonz/sources/toonz/lipsyncpopup.cpp +++ b/toonz/sources/toonz/lipsyncpopup.cpp @@ -25,6 +25,7 @@ #include "tsound_io.h" #include "toutputproperties.h" #include "toonz/tproject.h" +#include "thirdparty.h" // TnzCore includes #include "filebrowsermodel.h" @@ -530,7 +531,7 @@ void LipSyncPopup::showEvent(QShowEvent *) { m_startAt->setValue(row + 1); m_startAt->clearFocus(); - if (checkRhubarb()) m_rhubarbPath = Preferences::instance()->getRhubarbPath(); + if (ThirdParty::checkRhubarb()) m_rhubarbPath = ThirdParty::getRhubarbDir(); TXshLevelHandle *level = app->getCurrentLevel(); m_sl = level->getSimpleLevel(); @@ -702,46 +703,6 @@ void LipSyncPopup::saveAudio() { //----------------------------------------------------------------------------- -bool LipSyncPopup::checkRhubarb() { - QString exe = "rhubarb"; -#if defined(_WIN32) - exe = exe + ".exe"; -#endif - - // check the user defined path in preferences first - QString path = Preferences::instance()->getRhubarbPath() + "/" + exe; - if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; - - // Let's try and autodetect the exe included with release - QStringList folderList; - - folderList.append("."); - folderList.append("./rhubarb"); // rhubarb folder - -#ifdef MACOSX - // Look inside app - folderList.append("./" + - QString::fromStdString(TEnv::getApplicationFileName()) + - ".app/rhubarb"); // rhubarb folder -#elif defined(LINUX) || defined(FREEBSD) - // Need to account for symbolic links - folderList.append(TEnv::getWorkingDirectory().getQString() + - "/rhubarb"); // rhubarb folder -#endif - - QString exePath = TSystem::findFileLocation(folderList, exe); - - if (!exePath.isEmpty()) { - Preferences::instance()->setValue(rhubarbPath, exePath); - return true; - } - - // give up - return false; -} - -//----------------------------------------------------------------------------- - void LipSyncPopup::runRhubarb() { QString cacheRoot = ToonzFolder::getCacheRootFolder().getQString(); if (!TSystem::doesExistFileOrLevel(TFilePath(cacheRoot + "/rhubarb"))) { @@ -932,7 +893,7 @@ void LipSyncPopup::onApplyButton() { int lastFrame = m_textLines.at(m_textLines.size() - 2).toInt() + startFrame; if (m_restToEnd->isChecked()) { - int r0, r1, step; + int r0, r1; TApp::instance()->getCurrentXsheet()->getXsheet()->getCellRange(m_col, r0, r1); if (lastFrame < r1 + 1) lastFrame = r1 + 1; @@ -976,7 +937,7 @@ void LipSyncPopup::imageNavClicked(int id) { else if (frameIndex == 0 && direction == -1) newIndex = m_levelFrameIds.size() - 1; else - newIndex = frameIndex + direction; + newIndex = frameIndex + direction; m_activeFrameIds[frameNumber] = m_levelFrameIds.at(newIndex); TXshCell newCell = TApp::instance()->getCurrentScene()->getScene()->getXsheet()->getCell( diff --git a/toonz/sources/toonz/lipsyncpopup.h b/toonz/sources/toonz/lipsyncpopup.h index 406e04c0..8caf5902 100644 --- a/toonz/sources/toonz/lipsyncpopup.h +++ b/toonz/sources/toonz/lipsyncpopup.h @@ -94,7 +94,6 @@ protected: void refreshSoundLevels(); void saveAudio(); void runRhubarb(); - bool checkRhubarb(); public slots: void onApplyButton(); diff --git a/toonz/sources/toonz/main.cpp b/toonz/sources/toonz/main.cpp index cb63c5e1..f664f628 100644 --- a/toonz/sources/toonz/main.cpp +++ b/toonz/sources/toonz/main.cpp @@ -12,6 +12,7 @@ #include "cleanupsettingspopup.h" #include "filebrowsermodel.h" #include "expressionreferencemanager.h" +#include "thirdparty.h" #include "startuppopup.h" // TnzTools includes @@ -332,9 +333,7 @@ int main(int argc, char *argv[]) { // Enables high-DPI scaling. This attribute must be set before QApplication is // constructed. Available from Qt 5.6. -#if QT_VERSION >= 0x050600 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#endif QApplication a(argc, argv); @@ -507,6 +506,9 @@ int main(int argc, char *argv[]) { TBigMemoryManager::instance()->setRunOutOfContiguousMemoryHandler( &toonzRunOutOfContMemHandler); + // Setup third party + ThirdParty::initialize(); + // Toonz environment initToonzEnv(argumentPathValues); diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index fab37c2c..33dff193 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -13,6 +13,7 @@ #include "tapp.h" #include "viewerpane.h" #include "tooloptionsshortcutinvoker.h" +#include "custompanelmanager.h" #include "statusbar.h" #include "aboutpopup.h" @@ -201,8 +202,8 @@ int get_version_code_from(std::string ver) { // minor version: assume that the minor version is less than 255. std::string::size_type const b = ver.find('.', a + 1); std::string const minor = (b == std::string::npos) - ? ver.substr(a + 1) - : ver.substr(a + 1, b - a - 1); + ? ver.substr(a + 1) + : ver.substr(a + 1, b - a - 1); version += std::stoi(minor) << 16; if ((b == std::string::npos) || (b + 1 == ver.length())) { return version; @@ -373,13 +374,8 @@ void Room::reload() { // MainWindow //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 MainWindow::MainWindow(const QString &argumentLayoutFileName, QWidget *parent, Qt::WindowFlags flags) -#else -MainWindow::MainWindow(const QString &argumentLayoutFileName, QWidget *parent, - Qt::WFlags flags) -#endif : QMainWindow(parent, flags) , m_saveSettingsOnQuit(true) , m_oldRoomIndex(0) @@ -488,6 +484,9 @@ centralWidget->setLayout(centralWidgetLayout);*/ if (TSystem::doesExistFileOrLevel(TFilePath(ffmpegCachePath))) { TSystem::rmDirTree(TFilePath(ffmpegCachePath)); } + + connect(TApp::instance(), SIGNAL(activeViewerChanged()), this, + SLOT(onActiveViewerChanged())); } //----------------------------------------------------------------------------- @@ -587,9 +586,9 @@ void MainWindow::readSettings(const QString &argumentLayoutFileName) { /*-- * タイトルバーに表示するレイアウト名を作る:_layoutがあればそこから省く。無ければ.txtのみ省く * --*/ - int pos = (argumentLayoutFileName.indexOf("_layout") == -1) - ? argumentLayoutFileName.indexOf(".txt") - : argumentLayoutFileName.indexOf("_layout"); + int pos = (argumentLayoutFileName.indexOf("_layout") == -1) + ? argumentLayoutFileName.indexOf(".txt") + : argumentLayoutFileName.indexOf("_layout"); m_layoutName = argumentLayoutFileName.left(pos); } } @@ -664,6 +663,7 @@ void MainWindow::readSettings(const QString &argumentLayoutFileName) { } RecentFiles::instance()->loadRecentFiles(); + CustomPanelManager::instance()->loadCustomPanelEntries(); } //----------------------------------------------------------------------------- @@ -1356,6 +1356,27 @@ void MainWindow::onUpdateCheckerDone(bool error) { disconnect(m_updateChecker); m_updateChecker->deleteLater(); } + +//----------------------------------------------------------------------------- + +void MainWindow::onActiveViewerChanged() { + // sync the command state to the button state of the activated viewer + SceneViewer *activeViewer = TApp::instance()->getActiveViewer(); + if (!activeViewer) return; + BaseViewerPanel *bvp = qobject_cast( + activeViewer->parentWidget()->parentWidget()); + if (!bvp) return; + bool prev, subCamPrev; + bvp->getPreviewButtonStates(prev, subCamPrev); + + CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->setChecked(prev); + CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->setChecked(subCamPrev); +} + //----------------------------------------------------------------------------- void MainWindow::closeEvent(QCloseEvent *event) { @@ -1451,7 +1472,7 @@ QAction *MainWindow::createAction(const char *id, const char *name, action->setMenuRole(QAction::NoRole); #endif CommandManager::instance()->define(id, type, defaultShortcut.toStdString(), - action); + action, iconSVGName); action->setStatusTip(newStatusTip); return action; } @@ -1601,9 +1622,9 @@ QAction *MainWindow::createFillAction(const char *id, const char *name, //----------------------------------------------------------------------------- QAction *MainWindow::createMenuAction(const char *id, const char *name, - QList list, - QString newStatusTip) { - QMenu *menu = new DVMenuAction(tr(name), this, list); + QList list, QString newStatusTip, + bool isForRecentFiles) { + QMenu *menu = new DVMenuAction(tr(name), this, list, isForRecentFiles); QAction *action = menu->menuAction(); action->setStatusTip(newStatusTip); CommandManager::instance()->define(id, MenuCommandType, "", action); @@ -1710,8 +1731,8 @@ QAction *MainWindow::createToolAction(const char *id, const char *iconName, // Adding tool actions to the main window solve this problem! addAction(action); - CommandManager::instance()->define(id, ToolCommandType, - defaultShortcut.toStdString(), action); + CommandManager::instance()->define( + id, ToolCommandType, defaultShortcut.toStdString(), action, iconName); return action; } @@ -1758,24 +1779,29 @@ void MainWindow::defineActions() { tr("Remove everything from the recent level list.")); createMenuFileAction(MI_ConvertFileWithInput, QT_TR_NOOP("&Convert File..."), "", "convert", tr("Convert an existing file or image sequence to another format.")); + createMenuFileAction(MI_ConvertTZPInFolder, + QT_TR_NOOP("&Convert TZP Files In Folder..."), ""); createMenuFileAction(MI_LoadColorModel, QT_TR_NOOP("&Load Color Model..."), "", "load_colormodel", tr("Load an image as a color guide.")); createMenuFileAction(MI_ImportMagpieFile, QT_TR_NOOP("&Import Toonz Lip Sync File..."), "", "dialogue_import", tr("Import a lip sync file to be applied to a level.")); createMenuFileAction(MI_NewProject, QT_TR_NOOP("&New Project..."), "", - "", tr("Create a new project.") + separator + - tr("A project is a container for a collection of " - "related scenes and drawings.")); + "new_project", + tr("Create a new project.") + separator + + tr("A project is a container for a collection of " + "related scenes and drawings.")); createMenuAction(MI_OpenRecentProject, QT_TR_NOOP("&Open Recent Project"), files, ""); createMenuFileAction(MI_LoadProject, QT_TR_NOOP("&Load Project..."), "", "", tr("Load an existing project.")); createMenuFileAction(MI_ProjectSettings, QT_TR_NOOP("&Project Settings..."), - ""); - createMenuFileAction(MI_SaveDefaultSettings, QT_TR_NOOP("&Save Default Settings"), "", - "", tr("Use the current scene's settings as a template for " - "all new scenes in the current project.")); + "", "project_settings"); + createMenuFileAction(MI_SaveDefaultSettings, + QT_TR_NOOP("&Save Default Settings"), "", + "save_default_settings", + tr("Use the current scene's settings as a template for " + "all new scenes in the current project.")); createMenuFileAction(MI_SoundTrack, QT_TR_NOOP("&Export Soundtrack"), "", "", tr("Exports the soundtrack to the current scene as a wav file.")); createMenuFileAction(MI_Preferences, QT_TR_NOOP("&Preferences..."), "Ctrl+U", @@ -1793,6 +1819,9 @@ void MainWindow::defineActions() { QT_TRANSLATE_NOOP("MainWindow", "Export Exchange Digital Time Sheet (XDTS)"), ""); + createMenuFileAction( + MI_ExportOCA, + QT_TRANSLATE_NOOP("MainWindow", "Export Open Cel Animation (OCA)"), ""); createMenuFileAction( MI_ExportTvpJson, QT_TRANSLATE_NOOP("MainWindow", "Export TVPaint JSON File"), ""); @@ -1884,7 +1913,7 @@ void MainWindow::defineActions() { // Menu - Level createMenuLevelAction(MI_NewLevel, QT_TR_NOOP("&New Level..."), "Alt+N", - "new_document", tr("Create a new drawing layer.")); + "new_level", tr("Create a new drawing layer.")); createMenuLevelAction(MI_NewVectorLevel, QT_TR_NOOP("&New Vector Level"), "", "new_vector_level", tr("Create a new vector level.") + separator + tr("Vectors can be manipulated easily and have " @@ -1946,7 +1975,7 @@ void MainWindow::defineActions() { menuAct = createMenuLevelAction(MI_CanvasSize, QT_TR_NOOP("&Canvas Size..."), "", "resize"); menuAct->setDisabled(true); - createMenuLevelAction(MI_FileInfo, QT_TR_NOOP("&Info..."), "", "level_info"); + createMenuLevelAction(MI_FileInfo, QT_TR_NOOP("&Info..."), "", "info"); createMenuLevelAction(MI_RemoveUnused, QT_TR_NOOP("&Remove All Unused Levels"), "", "remove_unused_levels"); @@ -1955,8 +1984,8 @@ void MainWindow::defineActions() { "replace_parent_directory"); createMenuLevelAction(MI_NewNoteLevel, QT_TR_NOOP("New Note Level"), "", "new_note_level"); - createMenuLevelAction(MI_ConvertToVectors, - QT_TR_NOOP("Convert to Vectors..."), ""); + createMenuLevelAction(MI_ConvertToVectors, + QT_TR_NOOP("Convert to Vectors..."), "", "convert"); createMenuLevelAction(MI_ConvertToToonzRaster, QT_TR_NOOP("Vectors to Smart Raster"), ""); createMenuLevelAction( @@ -1964,8 +1993,7 @@ void MainWindow::defineActions() { QT_TRANSLATE_NOOP("MainWindow", "Replace Vectors with Simplified Vectors"), ""); - createMenuLevelAction(MI_Tracking, QT_TR_NOOP("Tracking..."), "", - "tracking_options"); + createMenuLevelAction(MI_Tracking, QT_TR_NOOP("Tracking..."), "", "focus"); createMenuLevelAction(MI_NewSpline, QT_TR_NOOP("&New Motion Path"), "", "menu_toggle", tr("Create a new motion path.") + separator + tr("Motion paths can be used as animation guides, or you can animate " @@ -2142,6 +2170,8 @@ void MainWindow::defineActions() { "nextkey"); createMenuPlayAction(MI_PrevKeyframe, QT_TR_NOOP("Previous Key"), "Ctrl+,", "prevkey"); + createMenuPlayAction(MI_ToggleBlankFrames, QT_TR_NOOP("Toggle Blank Frames"), + "", "blankframes"); // Menu - Render @@ -2153,8 +2183,8 @@ void MainWindow::defineActions() { "", "preview_settings", tr("Control the settings that will be used to preview the scene.")); createMenuRenderAction(MI_Render, QT_TR_NOOP("&Render"), "Ctrl+Shift+R", - "render_clapboard", tr("Renders according to the settings and " - "location set in Output Settings.")); + "render", tr("Renders according to the settings and " + "location set in Output Settings.")); createMenuRenderAction(MI_FastRender, QT_TR_NOOP("&Fast Render to MP4"), "Alt+R", "fast_render_mp4", tr("Exports an MP4 file to the location specified in the preferences.") + separator + tr("This is quicker than going into the Output Settings " @@ -2165,6 +2195,12 @@ void MainWindow::defineActions() { createMenuRenderAction(MI_SavePreviewedFrames, QT_TR_NOOP("&Save Previewed Frames"), "", "save_previewed_frames", tr("Save the images created during preview to a specified location.")); + createToggle(MI_ToggleViewerPreview, QT_TR_NOOP("Toggle Viewer Preview"), "", + false, MenuRenderCommandType, "pane_preview"); + createToggle(MI_ToggleViewerSubCameraPreview, + QT_TR_NOOP("Toggle Viewer Sub-camera Preview"), "", false, + MenuRenderCommandType, "pane_subpreview"); + createMenuRenderAction(MI_SaveAndRender, QT_TR_NOOP("&Save and Render"), "", "render", tr("Saves the current scene and renders according to the settings and " "location set in Output Settings.")); @@ -2237,6 +2273,8 @@ void MainWindow::defineActions() { createMenuWindowsAction(MI_OpenFileBrowser, QT_TR_NOOP("&File Browser"), "", "filebrowser"); + createMenuWindowsAction(MI_OpenPreproductionBoard, QT_TR_NOOP("&Preproduction Board"), "", + "scenebrowser"); createMenuWindowsAction(MI_OpenFileViewer, QT_TR_NOOP("&Flipbook"), "", "flipbook"); createMenuWindowsAction(MI_OpenFunctionEditor, QT_TR_NOOP("&Function Editor"), @@ -2296,6 +2334,12 @@ void MainWindow::defineActions() { createMenuWindowsAction(MI_OpenGuidedDrawingControls, QT_TR_NOOP("Guided Tweening Controls"), "", "guided_drawing"); + + createMenuAction(MI_OpenCustomPanels, QT_TR_NOOP("&Custom Panels"), files, "", + false); + createMenuWindowsAction(MI_CustomPanelEditor, + QT_TR_NOOP("&Custom Panel Editor..."), "", ""); + // menuAct = createToggle(MI_DockingCheck, QT_TR_NOOP("&Lock Room Panes"), "", // DockingCheckToggleAction ? 1 : 0, MenuWindowsCommandType); // DockingCheck::instance()->setToggle(menuAct); @@ -2323,7 +2367,7 @@ void MainWindow::defineActions() { MI_AutoFillToggle, QT_TRANSLATE_NOOP("MainWindow", "Toggle Autofill on Current Palette Color"), - "Shift+A"); + "Shift+A", "toggle_autofill"); // Right Click @@ -2348,9 +2392,11 @@ void MainWindow::defineActions() { createRightClickMenuAction(MI_SavePreset, QT_TR_NOOP("&Save As Preset"), ""); createRightClickMenuAction(MI_PreviewFx, QT_TR_NOOP("Preview Fx"), ""); createRightClickMenuAction(MI_PasteValues, QT_TR_NOOP("&Paste Color && Name"), - ""); - createRightClickMenuAction(MI_PasteColors, QT_TR_NOOP("Paste Color"), ""); - createRightClickMenuAction(MI_PasteNames, QT_TR_NOOP("Paste Name"), ""); + "", "paste_color_and_name"); + createRightClickMenuAction(MI_PasteColors, QT_TR_NOOP("Paste Color"), "", + "paste_color"); + createRightClickMenuAction(MI_PasteNames, QT_TR_NOOP("Paste Name"), "", + "paste_name"); createRightClickMenuAction(MI_GetColorFromStudioPalette, QT_TR_NOOP("Get Color from Studio Palette"), ""); createRightClickMenuAction(MI_ToggleLinkToStudioPalette, @@ -2373,11 +2419,16 @@ void MainWindow::defineActions() { createRightClickMenuAction(MI_PasteNumbers, QT_TR_NOOP("&Paste Numbers"), "", "paste_numbers"); createRightClickMenuAction(MI_Histogram, QT_TR_NOOP("&Histogram"), ""); + // MI_ViewerHistogram command is used as a proxy. It will be called when + // the MI_Histogram is used while the current flip console is in viewer. + createAction(MI_ViewerHistogram, QT_TR_NOOP("&Viewer Histogram"), "", "", + HiddenCommandType); + createRightClickMenuAction(MI_BlendColors, QT_TR_NOOP("&Blend colors"), ""); createToggle(MI_OnionSkin, QT_TR_NOOP("Onion Skin Toggle"), "/", false, RightClickMenuCommandType, "onionskin_toggle"); createToggle(MI_ZeroThick, QT_TR_NOOP("Zero Thick Lines"), "Shift+/", false, - RightClickMenuCommandType); + RightClickMenuCommandType, "zero_thick_lines"); createToggle(MI_CursorOutline, QT_TR_NOOP("Toggle Cursor Size Outline"), "", false, RightClickMenuCommandType); createRightClickMenuAction(MI_ToggleCurrentTimeIndicator, @@ -2389,17 +2440,16 @@ void MainWindow::defineActions() { "show_folder_contents"); createRightClickMenuAction(MI_ConvertFiles, QT_TR_NOOP("Convert..."), "", "convert"); - createRightClickMenuAction(MI_CollectAssets, QT_TR_NOOP("Collect Assets"), - ""); + createRightClickMenuAction(MI_CollectAssets, QT_TR_NOOP("Collect Assets"), "", + "collect_assets"); createRightClickMenuAction(MI_ImportScenes, QT_TR_NOOP("Import Scene"), "", "load_scene"); createRightClickMenuAction(MI_ExportScenes, QT_TR_NOOP("Export Scene..."), "", - "scene_export"); + "export_scene"); createRightClickMenuAction(MI_RemoveLevel, QT_TR_NOOP("Remove Level"), "", "remove_level"); createRightClickMenuAction(MI_AddToBatchRenderList, - QT_TR_NOOP("Add As Render Task"), "", - "render_add"); + QT_TR_NOOP("Add As Render Task"), "", "new_scene"); createRightClickMenuAction(MI_AddToBatchCleanupList, QT_TR_NOOP("Add As Cleanup Task"), "", "cleanup_add"); @@ -2497,7 +2547,8 @@ void MainWindow::defineActions() { createRightClickMenuAction(MI_OpenPltGizmo, QT_TR_NOOP("&Palette Gizmo"), "", "palettegizmo"); createRightClickMenuAction(MI_EraseUnusedStyles, - QT_TR_NOOP("&Delete Unused Styles"), ""); + QT_TR_NOOP("&Delete Unused Styles"), "", + "delete_unused_styles"); // createRightClickMenuAction(MI_LoadSubSceneFile, QT_TR_NOOP("Load As Sub-xsheet"), ""); // createRightClickMenuAction(MI_LoadResourceFile, QT_TR_NOOP("Load"), ""); // createRightClickMenuAction(MI_PremultiplyFile, QT_TR_NOOP("Premultiply"), ""); @@ -2664,6 +2715,17 @@ void MainWindow::defineActions() { createAction(MI_TypeBold, QT_TR_NOOP("Type Tool - Bold"), "", "", ToolCommandType); + /*-- Paint Brush tool + mode swicthing shortcuts --*/ + createAction(MI_PaintBrushNextMode, QT_TR_NOOP("Paint Brush - Next Mode"), "", + "", ToolCommandType); + createAction(MI_PaintBrushAreas, QT_TR_NOOP("Paint Brush - Areas"), "", "", + ToolCommandType, "paintbrush_mode_areas"); + createAction(MI_PaintBrushLines, QT_TR_NOOP("Paint Brush - Lines"), "", "", + ToolCommandType, "paintbrush_mode_lines"); + createAction(MI_PaintBrushLinesAndAreas, + QT_TR_NOOP("Paint Brush - Lines & Areas"), "", "", + ToolCommandType, "paintbrush_mode_lines_areas"); + /*-- Fill tool + type/mode switching shortcuts --*/ createAction(MI_FillNextType, QT_TR_NOOP("Fill Tool - Next Type"), "", "", ToolCommandType); @@ -2675,6 +2737,8 @@ void MainWindow::defineActions() { ToolCommandType, "fill_freehand"); createAction(MI_FillPolyline, QT_TR_NOOP("Fill Tool - Polyline"), "", "", ToolCommandType, "fill_polyline"); + createAction(MI_FillFreepick, QT_TR_NOOP("Fill Tool - Pick+Freehand"), "", "", + ToolCommandType, "fill_freepick"); createAction(MI_FillNextMode, QT_TR_NOOP("Fill Tool - Next Mode"), "", "", ToolCommandType); createAction(MI_FillAreas, QT_TR_NOOP("Fill Tool - Areas"), "", "", @@ -2702,31 +2766,31 @@ void MainWindow::defineActions() { createAction(MI_TapeNextType, QT_TR_NOOP("Tape Tool - Next Type"), "", "", ToolCommandType); createAction(MI_TapeNormal, QT_TR_NOOP("Tape Tool - Normal"), "", "", - ToolCommandType); - createAction(MI_TapeRectangular, QT_TR_NOOP("Tape Tool - Rectangular"), "", "", - ToolCommandType); + ToolCommandType, "tape_normal"); + createAction(MI_TapeRectangular, QT_TR_NOOP("Tape Tool - Rectangular"), "", + "", ToolCommandType, "tape_rectangular"); createAction(MI_TapeNextMode, QT_TR_NOOP("Tape Tool - Next Mode"), "", "", ToolCommandType); createAction(MI_TapeEndpointToEndpoint, QT_TR_NOOP("Tape Tool - Endpoint to Endpoint"), "", "", - ToolCommandType); + ToolCommandType, "tape_end_to_end"); createAction(MI_TapeEndpointToLine, QT_TR_NOOP("Tape Tool - Endpoint to Line"), "", "", - ToolCommandType); - createAction(MI_TapeLineToLine, QT_TR_NOOP("Tape Tool - Line to Line"), "", "", - ToolCommandType); + ToolCommandType, "tape_end_to_line"); + createAction(MI_TapeLineToLine, QT_TR_NOOP("Tape Tool - Line to Line"), "", + "", ToolCommandType, "tape_line_to_line"); /*-- Style Picker tool + mode switching shortcuts --*/ createAction(MI_PickStyleNextMode, QT_TR_NOOP("Style Picker Tool - Next Mode"), "", "", ToolCommandType); - createAction(MI_PickStyleAreas, QT_TR_NOOP("Style Picker Tool - Areas"), "", "", - ToolCommandType); - createAction(MI_PickStyleLines, QT_TR_NOOP("Style Picker Tool - Lines"), "", "", - ToolCommandType); + createAction(MI_PickStyleAreas, QT_TR_NOOP("Style Picker Tool - Areas"), "", + "", ToolCommandType, "stylepicker_areas"); + createAction(MI_PickStyleLines, QT_TR_NOOP("Style Picker Tool - Lines"), "", + "", ToolCommandType, "stylepicker_lines"); createAction(MI_PickStyleLinesAndAreas, QT_TR_NOOP("Style Picker Tool - Lines & Areas"), "", "", - ToolCommandType); + ToolCommandType, "stylepicker_lines_areas"); /*-- RGB Picker tool + type switching shortcuts --*/ createAction(MI_RGBPickerNextType, QT_TR_NOOP("RGB Picker Tool - Next Type"), @@ -2904,6 +2968,10 @@ void MainWindow::defineActions() { QT_TR_NOOP("Type - Polyline"), ""); menuAct->setIcon(createQIcon("type_polyline")); + menuAct = createToolOptionsAction("A_ToolOption_Type:Freepick", + QT_TR_NOOP("Type - Pick+Freehand"), ""); + menuAct->setIcon(createQIcon("type_pickerlasso")); + menuAct = createToolOptionsAction("A_ToolOption_Type:Segment", QT_TR_NOOP("Type - Segment"), ""); menuAct->setIcon(createQIcon("type_erase_segment")); @@ -3022,8 +3090,10 @@ void MainWindow::defineActions() { "reset"); createVisualizationButtonAction(VB_ZoomFit, QT_TR_NOOP("Fit to Window"), "fit_to_window"); - createVisualizationButtonAction(VB_ZoomReset, QT_TR_NOOP("Reset Zoom")); - createVisualizationButtonAction(VB_RotateReset, QT_TR_NOOP("Reset Rotation")); + createVisualizationButtonAction(VB_ZoomReset, QT_TR_NOOP("Reset Zoom"), + "zoom_reset"); + createVisualizationButtonAction(VB_RotateReset, QT_TR_NOOP("Reset Rotation"), + "rotate_reset"); createVisualizationButtonAction(VB_PositionReset, QT_TR_NOOP("Reset Position")); createVisualizationButtonAction( diff --git a/toonz/sources/toonz/mainwindow.h b/toonz/sources/toonz/mainwindow.h index df6ecb0c..ff591932 100644 --- a/toonz/sources/toonz/mainwindow.h +++ b/toonz/sources/toonz/mainwindow.h @@ -6,11 +6,7 @@ #include "tfilepath.h" #include "toonzqt/menubarcommand.h" -#if QT_VERSION >= 0x050000 #include -#else -#include -#endif #include #include #include @@ -33,13 +29,8 @@ class Room final : public TMainWindow { QString m_name; public: -#if QT_VERSION >= 0x050500 - Room(QWidget *parent = 0, Qt::WindowFlags flags = 0) -#else - Room(QWidget *parent = 0, Qt::WFlags flags = 0) -#endif - : TMainWindow(parent, flags) { - } + Room(QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags()) + : TMainWindow(parent, flags) {} ~Room() {} @@ -79,13 +70,8 @@ class MainWindow final : public QMainWindow { std::vector> m_panelStates; public: -#if QT_VERSION >= 0x050500 MainWindow(const QString &argumentLayoutFileName, QWidget *parent = 0, - Qt::WindowFlags flags = 0); -#else - MainWindow(const QString &argumentLayoutFileName, QWidget *parent = 0, - Qt::WFlags flags = 0); -#endif + Qt::WindowFlags flags = Qt::WindowFlags()); ~MainWindow(); void startupFloatingPanels(); @@ -204,7 +190,8 @@ private: const char *iconSVGName = "", QString newStatusTip = ""); QAction *createMenuAction(const char *id, const char *name, - QList list, QString newStatusTip = ""); + QList list, QString newStatusTip = "", + bool isForRecentFiles = true); QAction *createToggle(const char *id, const char *name, const QString &defaultShortcut, bool startStatus, CommandType type, const char *iconSVGName = "", @@ -245,6 +232,7 @@ protected slots: void onInk1CheckTriggered(bool on); void onUpdateCheckerDone(bool); + void onActiveViewerChanged(); void toggleStatusBar(bool); void toggleTransparency(bool); diff --git a/toonz/sources/toonz/matchlinecommand.cpp b/toonz/sources/toonz/matchlinecommand.cpp index aa8d9381..8b678019 100644 --- a/toonz/sources/toonz/matchlinecommand.cpp +++ b/toonz/sources/toonz/matchlinecommand.cpp @@ -336,11 +336,7 @@ void doCloneLevelNoSave(const TCellSelection::Range &range, if (!img && !fid.isStopFrame()) continue; if (cell.getSimpleLevel() == 0 || - cell.getSimpleLevel()->getPath().getType() == "psd" || - cell.getSimpleLevel()->getPath().getType() == "gif" || - cell.getSimpleLevel()->getPath().getType() == "mp4" || - cell.getSimpleLevel()->getPath().getType() == "webm" || - cell.getSimpleLevel()->getPath().getType() == "mov") + cell.getSimpleLevel()->getPath().isUneditable()) continue; std::map::iterator it = diff --git a/toonz/sources/toonz/menubar.cpp b/toonz/sources/toonz/menubar.cpp index ec7e6af2..174a4e28 100644 --- a/toonz/sources/toonz/menubar.cpp +++ b/toonz/sources/toonz/menubar.cpp @@ -344,6 +344,7 @@ void TopBar::loadMenubar() { addMenuItem(exportMenu, MI_ExportCurrentScene); addMenuItem(exportMenu, MI_SoundTrack); addMenuItem(exportMenu, MI_ExportXDTS); + addMenuItem(exportMenu, MI_ExportOCA); addMenuItem(exportMenu, MI_ExportXsheetPDF); addMenuItem(exportMenu, MI_StopMotionExportImageSequence); addMenuItem(exportMenu, MI_ExportTvpJson); @@ -653,6 +654,7 @@ void TopBar::loadMenubar() { addMenuItem(windowsMenu, MI_OpenFilmStrip); windowsMenu->addSeparator(); addMenuItem(windowsMenu, MI_OpenFileBrowser); + addMenuItem(windowsMenu, MI_OpenPreproductionBoard); addMenuItem(windowsMenu, MI_OpenFileBrowser2); windowsMenu->addSeparator(); addMenuItem(windowsMenu, MI_OpenCleanupSettings); @@ -665,6 +667,7 @@ void TopBar::loadMenubar() { addMenuItem(windowsMenu, MI_OpenMotionPathPanel); addMenuItem(windowsMenu, MI_StartupPopup); addMenuItem(windowsMenu, MI_OpenGuidedDrawingControls); + addMenuItem(windowsMenu, MI_OpenCustomPanels); // windowsMenu->addSeparator(); // addMenuItem(windowsMenu, MI_OpenExport); windowsMenu->addSeparator(); diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index 427414d0..e70e0503 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -63,7 +63,9 @@ #define MI_ClonePreview "MI_ClonePreview" #define MI_FreezePreview "MI_FrezzePreview" #define MI_SavePreviewedFrames "MI_SavePreviewedFrames" -//#define MI_SavePreview "MI_SavePreview" +// #define MI_SavePreview "MI_SavePreview" +#define MI_ToggleViewerPreview "MI_ToggleViewerPreview" +#define MI_ToggleViewerSubCameraPreview "MI_ToggleViewerSubCameraPreview" #define MI_Print "MI_Print" #define MI_Preferences "MI_Preferences" #define MI_SavePreset "MI_SavePreset" @@ -86,8 +88,9 @@ #define MI_CanvasSize "MI_CanvasSize" #define MI_RemoveUnused "MI_RemoveUnused" -//#define MI_OpenCurrentScene "MI_OpenCurrentScene" +// #define MI_OpenCurrentScene "MI_OpenCurrentScene" #define MI_OpenFileBrowser "MI_OpenFileBrowser" +#define MI_OpenPreproductionBoard "MI_OpenPreproductionBoard" #define MI_OpenFileViewer "MI_OpenFileViewer" #define MI_OpenFilmStrip "MI_OpenFilmStrip" #define MI_OpenPalette "MI_OpenPalette" @@ -204,6 +207,7 @@ #define MI_NoShift "MI_NoShift" #define MI_ResetShift "MI_ResetShift" #define MI_Histogram "MI_Histogram" +#define MI_ViewerHistogram "MI_ViewerHistogram" #define MI_FxParamEditor "MI_FxParamEditor" #define MI_Link "MI_Link" @@ -221,6 +225,7 @@ #define MI_PrevStep "MI_PrevStep" #define MI_NextKeyframe "MI_NextKeyframe" #define MI_PrevKeyframe "MI_PrevKeyframe" +#define MI_ToggleBlankFrames "MI_ToggleBlankFrames" #define MI_RedChannel "MI_RedChannel" #define MI_GreenChannel "MI_GreenChannel" @@ -252,7 +257,7 @@ #define MI_CursorOutline "MI_CursorOutline" #define MI_ViewerIndicator "MI_ViewerIndicator" -//#define MI_LoadResourceFile "MI_LoadResourceFile" +// #define MI_LoadResourceFile "MI_LoadResourceFile" #define MI_DuplicateFile "MI_DuplicateFile" #define MI_ViewFile "MI_ViewFile" #define MI_ConvertFiles "MI_ConvertFiles" @@ -374,11 +379,17 @@ #define MI_TypeBoldOblique "MI_TypeBoldOblique" #define MI_TypeBold "MI_TypeBold" +#define MI_PaintBrushNextMode "MI_PaintBrushNextMode" +#define MI_PaintBrushAreas "MI_PaintBrushAreas" +#define MI_PaintBrushLines "MI_PaintBrushLines" +#define MI_PaintBrushLinesAndAreas "MI_PaintBrushLinesAndAreas" + #define MI_FillNextType "MI_FillNextType" #define MI_FillNormal "MI_FillNormal" #define MI_FillRectangular "MI_FillRectangular" #define MI_FillFreehand "MI_FillFreehand" #define MI_FillPolyline "MI_FillPolyline" +#define MI_FillFreepick "MI_FillFreepick" #define MI_FillNextMode "MI_FillNextMode" #define MI_FillAreas "MI_FillAreas" #define MI_FillLines "MI_FillLines" @@ -474,12 +485,18 @@ #define MI_FlipPrevGuideStroke "MI_FlipPrevGuideStroke" #define MI_ExportXDTS "MI_ExportXDTS" +#define MI_ExportOCA "MI_ExportOCA" #define MI_ExportTvpJson "MI_ExportTvpJson" #define MI_ExportXsheetPDF "MI_ExportXsheetPDF" // mark id is added for each actual command (i.g. MI_SetCellMark1) #define MI_SetCellMark "MI_SetCellMark" +#define MI_OpenCustomPanels "MI_OpenCustomPanels" +#define MI_CustomPanelEditor "MI_CustomPanelEditor" + +#define MI_ConvertTZPInFolder "MI_ConvertTZPInFolder" + #define MI_ToggleAutoCreate "MI_ToggleAutoCreate" #define MI_ToggleCreationInHoldCells "MI_ToggleCreationInHoldCells" #define MI_ToggleAutoStretch "MI_ToggleAutoStretch" diff --git a/toonz/sources/toonz/mergecmapped.cpp b/toonz/sources/toonz/mergecmapped.cpp index fc9ee2c9..fdaa00b8 100644 --- a/toonz/sources/toonz/mergecmapped.cpp +++ b/toonz/sources/toonz/mergecmapped.cpp @@ -6,7 +6,7 @@ #include "toonz/txshsimplelevel.h" #include "toonz/txshlevelcolumn.h" #include "toonz/txshcell.h" -//#include "tw/action.h" +// #include "tw/action.h" #include "tropcm.h" #include "ttoonzimage.h" #include "matchline.h" @@ -62,8 +62,6 @@ void mergeCmapped(const std::vector &matchingLevels) { TPalette *matchPalette = matchingLevels[0].m_mcell->getImage(false)->getPalette(); - TPalette::Page *page; - // upInkId -> downInkId std::map usedColors; @@ -239,17 +237,17 @@ public: DeleteMatchlineUndo( TXshLevel *xl, TXshSimpleLevel *sl, const std::vector &fids, const std::vector &indexes) //, TPalette*matchPalette) - : TUndo(), - m_xl(xl), - m_sl(sl), - m_fids(fids), - m_indexes(indexes) + : TUndo() + , m_xl(xl) + , m_sl(sl) + , m_fids(fids) + , m_indexes(indexes) //, m_matchlinePalette(matchPalette->clone()) { // assert(matchPalette); int i; for (i = 0; i < fids.size(); i++) { - QString id = "DeleteMatchlineUndo" + QString::number((uintptr_t) this) + + QString id = "DeleteMatchlineUndo" + QString::number((uintptr_t)this) + "-" + QString::number(i); TToonzImageP image = sl->getFrame(fids[i], false); assert(image); @@ -262,7 +260,7 @@ public: // TPalette *palette = m_matchlinePalette->clone(); // m_sl->setPalette(palette); for (i = 0; i < m_fids.size(); i++) { - QString id = "DeleteMatchlineUndo" + QString::number((uintptr_t) this) + + QString id = "DeleteMatchlineUndo" + QString::number((uintptr_t)this) + "-" + QString::number(i); TImageP img = TImageCache::instance()->get(id, false)->cloneImage(); @@ -297,7 +295,7 @@ public: int i; for (i = 0; i < m_fids.size(); i++) TImageCache::instance()->remove("DeleteMatchlineUndo" + - QString::number((uintptr_t) this) + "-" + + QString::number((uintptr_t)this) + "-" + QString::number(i)); } }; @@ -413,8 +411,8 @@ public: std::map::const_iterator it = m_images.begin(); for (; it != m_images.end(); ++it) //, ++mit) { - QString id = "MergeCmappedUndo" + QString::number((uintptr_t) this) + - "-" + QString::number(it->first.getNumber()); + QString id = "MergeCmappedUndo" + QString::number((uintptr_t)this) + "-" + + QString::number(it->first.getNumber()); TImageCache::instance()->remove(id); } delete m_palette; diff --git a/toonz/sources/toonz/messagepanel.cpp b/toonz/sources/toonz/messagepanel.cpp index 9df0d5fb..61fd6463 100644 --- a/toonz/sources/toonz/messagepanel.cpp +++ b/toonz/sources/toonz/messagepanel.cpp @@ -30,7 +30,11 @@ protected: QFontMetrics fm = p.fontMetrics(); QString elidedText = fm.elidedText(m_text, Qt::ElideRight, width()); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + qreal textWidth = fm.horizontalAdvance(elidedText); +#else qreal textWidth = fm.width(elidedText); +#endif p.drawText((width() - textWidth) * 0.5, (height() - fm.height()) * 0.5, elidedText); } @@ -86,11 +90,7 @@ void MessagePanel::setMessage(QString text) { m_messageBox->setText(text); } Inherits \b TPanel. */ -#if QT_VERSION >= 0x050500 LogPanel::LogPanel(QWidget *parent, Qt::WindowFlags flags) -#else -LogPanel::LogPanel(QWidget *parent, Qt::WFlags flags) -#endif : TPanel(parent), TLogger::Listener() { TLogger::instance()->addListener(this); TLogger::instance()->clearMessages(); @@ -149,7 +149,7 @@ void LogPanel::onLogChanged() { //----------------------------------------------------------------------------- /*! Clear panel text box from all messages. -*/ + */ void LogPanel::clear() { TLogger::instance()->clearMessages(); m_messageBox->clear(); diff --git a/toonz/sources/toonz/messagepanel.h b/toonz/sources/toonz/messagepanel.h index 4cbe7631..dac810b3 100644 --- a/toonz/sources/toonz/messagepanel.h +++ b/toonz/sources/toonz/messagepanel.h @@ -54,11 +54,7 @@ class LogPanel final : public TPanel, public TLogger::Listener { int m_poolIndex; public: -#if QT_VERSION >= 0x050500 LogPanel(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - LogPanel(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif ~LogPanel(); void onLogChanged() override; diff --git a/toonz/sources/toonz/moviegenerator.cpp b/toonz/sources/toonz/moviegenerator.cpp index ada632d9..db62b32c 100644 --- a/toonz/sources/toonz/moviegenerator.cpp +++ b/toonz/sources/toonz/moviegenerator.cpp @@ -159,7 +159,7 @@ public: TPointD center(0.5 * cameraSize.lx, 0.5 * cameraSize.ly); m_viewAff = TTranslation(center); std::string ext = fp.getType(); - m_isFrames = ext != "avi"; + m_isFrames = ext != "avi" && ext != "mov" && ext != "3gp"; m_fileOptions = properties.getFileFormatProperties(ext); } diff --git a/toonz/sources/toonz/ocaio.cpp b/toonz/sources/toonz/ocaio.cpp new file mode 100644 index 00000000..0de7ee10 --- /dev/null +++ b/toonz/sources/toonz/ocaio.cpp @@ -0,0 +1,534 @@ +#include "ocaio.h" + +#include "tsystem.h" +#include "tenv.h" + +#include "toonz/tcamera.h" + +#include "toonz/toonzscene.h" +#include "toonz/tproject.h" +#include "toonz/levelset.h" +#include "toonz/txsheet.h" +#include "toonz/txshcell.h" +#include "toonz/txshsimplelevel.h" +#include "toonz/txshchildlevel.h" +#include "toonz/txsheethandle.h" +#include "toonz/tscenehandle.h" +#include "toonz/preferences.h" +#include "toonz/sceneproperties.h" +#include "toonz/tstageobject.h" +#include "toutputproperties.h" + +#include "toonzqt/menubarcommand.h" +#include "toonzqt/gutil.h" + +#include "tapp.h" +#include "menubarcommandids.h" +#include "filebrowserpopup.h" + +#include "tiio.h" +#include "timage_io.h" +#include "tlevel_io.h" +#include "tvectorimage.h" +#include "exportlevelcommand.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace OCAIo; + +void OCAData::write(QJsonObject &json) const { + // Header + json["name"] = m_name; + json["frameRate"] = m_framerate; + json["width"] = m_width; + json["height"] = m_height; + json["startTime"] = m_startTime + m_stOff; + json["endTime"] = m_endTime + m_stOff; + json["colorDepth"] = m_raEXR ? "U16" : "U8"; + + // Background color + QJsonArray bgColorArray; + bgColorArray.append(m_bgRed); + bgColorArray.append(m_bgGreen); + bgColorArray.append(m_bgBlue); + bgColorArray.append(m_bgAlpha); + json["backgroundColor"] = bgColorArray; + + // Layers + json["layers"] = m_layers; + + // Versions + json["originApp"] = "Tahoma2D"; + json["originAppVersion"] = + QString::fromStdString(TEnv::getApplicationVersion()); + json["ocaVersion"] = "1.1.0"; +} + +int OCAData::frameLen(TXshCellColumn *column, const QList &rows, int index) { + // Next cells must match same level and frame-id + int length = 0; + const TXshCell &stc = column->getCell(rows[index]); + bool isStcStopFrame = stc.getFrameId().isStopFrame(); + for (int i = index; i < rows.count(); i++) { + int currentRow = rows[i]; + const TXshCell &cell = column->getCell(currentRow); + if ((isStcStopFrame && !cell.isEmpty() && + !cell.getFrameId().isStopFrame()) || + (!isStcStopFrame && + (stc.m_frameId != cell.m_frameId || stc.m_level != cell.m_level))) + break; + length++; + } + return length; +} + +bool OCAData::isBlank(TXshCellColumn *column, int row) { + TXshCell cell = column->getCell(row); + return cell.isEmpty() || cell.getFrameId().isStopFrame(); +} + +bool OCAData::getLayerName(TXshCellColumn *column, QString &out) { + // Layer name will be the first cel that occur on the column + TXshCell cell = column->getCell(column->getFirstRow()); + if (cell.isEmpty() || cell.getFrameId().isStopFrame()) return false; + + out = QString::fromStdWString(cell.m_level->getName()); + return true; +} + +bool OCAData::getCellName(TXshCellColumn *column, int row, QString &out) { + TXshCell cell = column->getCell(row); + if (cell.isEmpty() || cell.getFrameId().isStopFrame()) return false; + + TFilePath fp(cell.m_level->getName()); + fp = fp.withFrame(cell.getFrameId(), TFrameId::FrameFormat::FOUR_ZEROS); + out = fp.getQString(); + return true; + } + +bool OCAData::saveCell(TXshCellColumn *column, int row, + const QString &cellname, OCAAsset &out) { + TXshCell cell = column->getCell(row); + TXshSimpleLevel *sl = cell.getSimpleLevel(); + + std::string format = m_raEXR ? "exr" : "png"; + + // Rasterize cell + TImageP image = IoCmd::exportedImage(format, *sl, cell.getFrameId()); + if (!image) return false; + + if (TRasterImageP ri = (TRasterImageP)(image)) { + TRasterP raster; + raster = ri->getRaster(); + out.width = raster->getLx(); + out.height = raster->getLy(); + if (!raster) return false; + } else { + return false; + } + + out.fileExt = QString::fromStdString(format); + + if (m_veSVG) { + // Get vector image directly and discard rasterized image + TVectorImageP vectorImg = cell.getImage(false); + if (vectorImg) { + image = vectorImg; + out.fileExt = "svg"; + } + } + + QString filename = QString("/%1.%2").arg(cellname, out.fileExt); + TFilePath fpr(m_path); + TFilePath fpa(m_path + filename); + out.fileName = QString::fromStdWString(fpr.getWideName()) + filename; + + TLevelWriterP lw(fpa); + lw->setFrameRate(m_framerate); + TImageWriterP iw = lw->getFrameWriter(cell.getFrameId()); + iw->setFilePath(fpa); // Added to aid my own sanity! + iw->save(image); + + return true; +} + +bool OCAData::isGroup(TXshCellColumn *column) { + TXshCell firstcell = column->getCell(column->getFirstRow()); + TXshChildLevel *cl = firstcell.getChildLevel(); + return cl; +} + +bool OCAData::buildGroup(QJsonObject &json, const QList &rows, + TXshCellColumn *column) { + QString layername; + if (!getLayerName(column, layername)) return false; + + // Get sub-xsheet + int firstrow = column->getFirstRow(); + TXshCell firstcell = column->getCell(firstrow); + TXshChildLevel *cl = firstcell.getChildLevel(); + TXsheet *xsheet = cl->getXsheet(); + if (!xsheet) return false; + + // Build a list of child rows + QList crows; + for (int i = m_startTime; i <= m_endTime; i++) { + TXshCell cell = column->getCell(i); + if (cell.isEmpty() || cell.getFrameId().isStopFrame()) + crows.append(-1); + else + crows.append(cell.getFrameId().getNumber() - 1); + } + + // Build all columns from sub-xsheet + int subID = ++m_subId; + QJsonArray layers; + for (int col = 0; col < xsheet->getFirstFreeColumnIndex(); col++) { + if (xsheet->isColumnEmpty(col)) continue; + TXshCellColumn *column = xsheet->getColumn(col)->getCellColumn(); + if (!column) continue; // skip non-cell column + if (!column->isPreviewVisible()) continue; // skip inactive column + + if (column->getColumnType() == column->eLevelType) { + QJsonObject json; + if (isGroup(column)) { + if (buildGroup(json, crows, column)) layers.append(json); + } else { + if (buildLayer(json, crows, column)) layers.append(json); + } + } + } + + QJsonArray jsonBlankPos; + jsonBlankPos.append(m_width / 2); + jsonBlankPos.append(m_height / 2); + json["name"] = layername; + json["position"] = jsonBlankPos; + json["width"] = m_width; + json["height"] = m_height; + json["frames"] = QJsonArray(); + json["childLayers"] = layers; + json["type"] = "grouplayer"; + json["fileType"] = "png"; + json["blendingMode"] = "normal"; + json["animated"] = false; + json["label"] = 0; + json["opacity"] = column->getOpacity() / 255.0; + json["visible"] = column->isCamstandVisible(); + json["passThrough"] = false; + json["reference"] = false; + json["inheritAlpha"] = false; + + return true; +} + +bool OCAData::buildLayer(QJsonObject &json, const QList &rows, + TXshCellColumn *column) { + QJsonArray jsonFrames; + bool firstcel = true; + + // Layer name will be the first cel that occur on the column + int firstrow = column->getFirstRow(); + QString layername; + QString cellname; + if (!getLayerName(column, layername)) return false; + if (!getCellName(column, firstrow, cellname)) return false; + + QJsonArray jsonBlankPos; + jsonBlankPos.append(m_width / 2); + jsonBlankPos.append(m_height / 2); + json["name"] = layername; + json["position"] = jsonBlankPos; + json["width"] = 0; + json["height"] = 0; + + for (int i = 0; i < rows.count(); i++) { + int row = rows[i]; + + QJsonObject frame; + QJsonArray jsonPosition; + jsonPosition.append(m_width / 2); + jsonPosition.append(m_height / 2); + int len = frameLen(column, rows, i); + + frame["frameNumber"] = i + m_stOff; + frame["position"] = jsonPosition; + frame["opacity"] = 1.0; // OT uses Transparency Fx node + frame["duration"] = len; + + if (isBlank(column, row)) { + frame["name"] = "_blank"; + frame["fileName"] = ""; + frame["width"] = 0; + frame["height"] = 0; + } else { + // Cell name will be used for the frame name + if (!getCellName(column, row, cellname)) continue; + + // Save cell image as an asset + OCAAsset asset; + if (!m_assets.contains(cellname)) { + if (!saveCell(column, row, cellname, asset)) return false; + m_assets.insert(cellname, asset); + } else { + asset = m_assets.value(cellname); + } + + frame["name"] = cellname; + frame["fileName"] = asset.fileName; + frame["width"] = asset.width; + frame["height"] = asset.height; + + if (firstcel) { + // Layer size and type will depend of the first cell too + json["width"] = asset.width; + json["height"] = asset.height; + json["fileType"] = asset.fileExt; + firstcel = false; + } + } + jsonFrames.append(frame); + + i += len - 1; + } + + json["frames"] = jsonFrames; + json["childLayers"] = QJsonArray(); + json["type"] = "paintlayer"; + json["blendingMode"] = "normal"; // OT uses nodes Fx to make blending + // possible, how to approach this? + json["animated"] = jsonFrames.count() >= 2; + json["label"] = 0; // OT doesn't support labels + json["opacity"] = column->getOpacity() / 255.0; + json["visible"] = column->isCamstandVisible(); + json["passThrough"] = false; + json["reference"] = false; + json["inheritAlpha"] = false; + + return true; +} + +void OCAData::setProgressDialog(DVGui::ProgressDialog *dialog) { + m_progressDialog = dialog; +} + +void OCAData::build(ToonzScene *scene, TXsheet *xsheet, QString name, + QString path, int startOffset, bool useEXR, + bool vectorAsSVG) { + m_name = name; + m_path = path; + m_subId = 0; + m_raEXR = useEXR; + m_veSVG = vectorAsSVG; + m_stOff = startOffset; + + // if the current xsheet is top xsheet in the scene and the output + // frame range is specified, set the "to" frame value as duration + TOutputProperties *oprop = scene->getProperties()->getOutputProperties(); + int from, to, step; + if (scene->getTopXsheet() == xsheet && oprop->getRange(from, to, step)) { + m_startTime = from; + m_endTime = to; + //m_stepTime = step; + } else { + m_startTime = 0; + m_endTime = xsheet->getFrameCount() - 1; + //m_stepTime = 1; + } + if (m_endTime < 0) m_endTime = 0; + + // Build a list of rows + QList rows; + for (int i = m_startTime; i <= m_endTime; i++) { + rows.append(i); + } + + m_framerate = oprop->getFrameRate(); + m_width = scene->getCurrentCamera()->getRes().lx; + m_height = scene->getCurrentCamera()->getRes().ly; + + TPixel bgc = scene->getProperties()->getBgColor(); + m_bgRed = bgc.r / double(TPixel::maxChannelValue); + m_bgGreen = bgc.g / double(TPixel::maxChannelValue); + m_bgBlue = bgc.b / double(TPixel::maxChannelValue); + m_bgAlpha = bgc.m / double(TPixel::maxChannelValue); + + // Build all columns from current xsheet + m_layers.empty(); + for (int col = 0; col < xsheet->getColumnCount(); col++) { + if (xsheet->isColumnEmpty(col)) continue; + TXshCellColumn *column = xsheet->getColumn(col)->getCellColumn(); + if (!column) continue; // skip non-cell column + if (!column->isPreviewVisible()) continue; // skip inactive column + + if (column->getColumnType() == column->eLevelType) { + if (m_progressDialog) { + QString layername; + getLayerName(column, layername); + m_progressDialog->setLabelText(QObject::tr("Layer: ") + layername); + m_progressDialog->setMaximum(xsheet->getColumnCount()); + m_progressDialog->setValue(col); + QCoreApplication::processEvents(); + } + + QJsonObject json; + if (isGroup(column)) { + if (buildGroup(json, rows, column)) m_layers.append(json); + } else { + if (buildLayer(json, rows, column)) m_layers.append(json); + } + } + } +} + +class ExportOCACommand final : public MenuItemHandler { +public: + ExportOCACommand() : MenuItemHandler(MI_ExportOCA) {} + void execute() override; +} exportOCACommand; + +void ExportOCACommand::execute() { + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet(); + TFilePath fp = scene->getScenePath().withType("oca"); + + static GenericSaveFilePopup *savePopup = 0; + static QCheckBox *exrImageFormat = nullptr; + static QCheckBox *rasVectors = nullptr; + static QCheckBox *doKeyframing = nullptr; + static QHBoxLayout *startingOffsetLay = nullptr; + static QLabel *startingOffsetLab = nullptr; + static QSpinBox *startingOffsetSpin = nullptr; + + static DVGui::ProgressDialog *progressDialog = nullptr; + + if (!savePopup) { + QWidget *customWidget = new QWidget(); + + exrImageFormat = new QCheckBox(tr("Save Images in EXR Format")); + rasVectors = new QCheckBox(tr("Rasterize Vectors")); + + startingOffsetLay = new QHBoxLayout(); + startingOffsetLab = new QLabel(tr("Frame Offset: ")); + startingOffsetSpin = new QSpinBox(); + + exrImageFormat->setToolTip( + tr("Checked: Images are saved as EXR\nUnchecked: Images are " + "saved as PNG")); + rasVectors->setToolTip( + tr("Checked: Rasterize into EXR/PNG\nUnchecked: Vectors are " + "saved as SVG")); + startingOffsetLab->setToolTip(tr("Starting Frame Offset")); + startingOffsetSpin->setToolTip(tr("Starting Frame Offset")); + rasVectors->setChecked(true); + + startingOffsetSpin->setValue(1); + startingOffsetSpin->setMinimum(0); + startingOffsetSpin->setMaximum(1000000); + startingOffsetLab->adjustSize(); + startingOffsetLay->setMargin(0); + startingOffsetLay->setSpacing(0); + startingOffsetLay->addWidget(startingOffsetLab, 0, Qt::AlignRight); + startingOffsetLay->addWidget(startingOffsetSpin); + + QGridLayout *customLay = new QGridLayout(); + customLay->setMargin(5); + customLay->setSpacing(5); + customLay->addWidget(exrImageFormat, 0, 0); + customLay->addWidget(rasVectors, 1, 0); + customLay->addLayout(startingOffsetLay, 0, 1); + customWidget->setLayout(customLay); + + progressDialog = new DVGui::ProgressDialog("", tr("Hide"), 0, 0); + progressDialog->setWindowTitle(tr("Exporting...")); + progressDialog->setWindowFlags( + Qt::Dialog | Qt::WindowTitleHint); // Don't show ? and X buttons + progressDialog->setWindowModality(Qt::WindowModal); + + savePopup = new GenericSaveFilePopup( + QObject::tr("Export Open Cel Animation (OCA)"), customWidget); + savePopup->addFilterType("oca"); + } + if (!scene->isUntitled()) + savePopup->setFolder(fp.getParentDir()); + else + savePopup->setFolder( + TProjectManager::instance()->getCurrentProject()->getScenesPath()); + + savePopup->setFilename(fp.withoutParentDir()); + fp = savePopup->getPath(); + if (fp.isEmpty()) return; + + int frameOffset = startingOffsetSpin->value(); + bool exrImageFmt = exrImageFormat->isChecked(); + bool rasterVecs = rasVectors->isChecked(); + + // Export + + progressDialog->setLabelText(tr("Starting...")); + progressDialog->setValue(0); + progressDialog->show(); + QCoreApplication::processEvents(); + + QString ocafile = fp.getQString(); + QString ocafolder(ocafile); + ocafolder.replace(".", "_"); + + QFile saveFile(ocafile); + QDir saveDir(ocafolder); + + if (!saveFile.open(QIODevice::WriteOnly)) { + qWarning("Couldn't open save file."); + return; + } + if (!saveDir.exists()) { + if (!saveDir.mkdir(".")) { + qWarning("Couldn't create folder."); + return; + } + } + + OCAData ocaData; + ocaData.setProgressDialog(progressDialog); + ocaData.build(scene, xsheet, QString::fromStdString(fp.getName()), ocafolder, + frameOffset, exrImageFmt, !rasterVecs); + if (ocaData.isEmpty()) { + DVGui::error(QObject::tr("No columns can be exported.")); + return; + } + + QJsonObject ocaObject; + ocaData.write(ocaObject); + QJsonDocument saveDoc(ocaObject); + QByteArray jsonByteArrayData = saveDoc.toJson(); + saveFile.write(jsonByteArrayData); + + progressDialog->close(); + + // Done + + QString str = QObject::tr("%1 has been exported successfully.") + .arg(QString::fromStdString(fp.getLevelName())); + + std::vector buttons = {QObject::tr("OK"), + QObject::tr("Open containing folder")}; + int ret = DVGui::MsgBox(DVGui::INFORMATION, str, buttons); + + if (ret == 2) { + TFilePath folderPath = fp.getParentDir(); + if (TSystem::isUNC(folderPath)) + QDesktopServices::openUrl(QUrl(folderPath.getQString())); + else + QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath.getQString())); + } +} \ No newline at end of file diff --git a/toonz/sources/toonz/ocaio.h b/toonz/sources/toonz/ocaio.h new file mode 100644 index 00000000..98426fb7 --- /dev/null +++ b/toonz/sources/toonz/ocaio.h @@ -0,0 +1,67 @@ +#pragma once +#ifndef OCAIO_H +#define OCAIO_H + +#include "toonzqt/dvdialog.h" + +#include +#include +#include + +#include +#include +#include + +class ToonzScene; +class TXshCellColumn; +class TXsheet; + +class TFrameId; + +namespace OCAIo { + +struct OCAAsset { + int width; + int height; + QString fileName; + QString fileExt; +}; + +class OCAData { + QString m_path; + QString m_name; + double m_framerate; + int m_width, m_height; + int m_startTime, m_endTime; + double m_bgRed, m_bgGreen, m_bgBlue, m_bgAlpha; + QJsonArray m_layers; + int m_subId; + QMap m_assets; + bool m_raEXR, m_veSVG; + int m_stOff; + + DVGui::ProgressDialog *m_progressDialog; + +public: + void write(QJsonObject &json) const; + bool isBlank(TXshCellColumn *column, int row); + bool getLayerName(TXshCellColumn *column, QString &out); + bool getCellName(TXshCellColumn *column, int row, QString &out); + bool saveCell(TXshCellColumn *column, int row, const QString &cellname, + OCAAsset &out); + int frameLen(TXshCellColumn *column, const QList &rows, int index); + bool isGroup(TXshCellColumn *column); + bool buildGroup(QJsonObject &json, const QList &rows, + TXshCellColumn *column); + bool buildLayer(QJsonObject &json, const QList &rows, + TXshCellColumn *column); + + void setProgressDialog(DVGui::ProgressDialog *dialog); + void build(ToonzScene *scene, TXsheet *xsheet, QString name, QString path, + int startOffset, bool useEXR, bool vectorAsSVG); + bool isEmpty() { return m_layers.isEmpty(); } +}; + +} // namespace OCAIo + +#endif \ No newline at end of file diff --git a/toonz/sources/toonz/outputsettingspopup.cpp b/toonz/sources/toonz/outputsettingspopup.cpp index a66c3284..acc7a72b 100644 --- a/toonz/sources/toonz/outputsettingspopup.cpp +++ b/toonz/sources/toonz/outputsettingspopup.cpp @@ -134,7 +134,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 }; @@ -182,7 +182,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 @@ -218,14 +219,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("General") << tr("Camera") << tr("Advanced") << tr("More"); +// categories << tr("Camera") << tr("Color") << tr("File") << tr("More"); // categoryList->addItems(categories); // categoryList->setFixedWidth(100); // categoryList->setCurrentRow(0); @@ -300,6 +302,9 @@ void OutputSettingsPopup::onCategoryActivated(QListWidgetItem *item) { } else 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("Advanced")) { label = m_advancedLabel; frame = m_advancedBox; @@ -327,15 +332,25 @@ QFrame *OutputSettingsPopup::createPanel(bool isPreview) { m_generalLabel = new QLabel(tr("General Settings"), this); m_generalBox = createGeneralSettingsBox(isPreview); } + m_cameraLabel = new QLabel(tr("Camera Settings"), this); m_cameraBox = createCameraSettingsBox(isPreview); + + m_colorLabel = new QLabel(tr("Color Settings"), this); + m_colorBox = createColorSettingsBox(isPreview); + m_advancedLabel = new QLabel(tr("Advanced Settings"), this); m_advancedBox = createAdvancedSettingsBox(isPreview); + if (!isPreview) { m_moreLabel = new QLabel(tr("More Settings"), this); m_moreBox = createMoreSettingsBox(); } + if (isPreview) + m_syncColorSettingsButton = + new DVGui::CheckBox(tr("Sync with Output Settings")); + if (!isPreview) { m_showCameraSettingsButton = new QPushButton("", this); m_showCameraSettingsButton->setObjectName("menuToggleButton"); @@ -345,6 +360,14 @@ QFrame *OutputSettingsPopup::createPanel(bool isPreview) { m_showCameraSettingsButton->setChecked(false); m_showCameraSettingsButton->setFocusPolicy(Qt::NoFocus); + m_showColorSettingsButton = new QPushButton("", this); + m_showColorSettingsButton->setObjectName("menuToggleButton"); + m_showColorSettingsButton->setFixedSize(15, 15); + m_showColorSettingsButton->setIcon(createQIcon("menu_toggle")); + m_showColorSettingsButton->setCheckable(true); + m_showColorSettingsButton->setChecked(false); + m_showColorSettingsButton->setFocusPolicy(Qt::NoFocus); + m_showAdvancedSettingsButton = new QPushButton("", this); m_showAdvancedSettingsButton->setObjectName("OutputSettingsShowButton"); m_showAdvancedSettingsButton->setFixedSize(15, 15); @@ -385,6 +408,21 @@ QFrame *OutputSettingsPopup::createPanel(bool isPreview) { lay->addWidget(m_cameraBox, 0); lay->addSpacing(10); + QHBoxLayout *colorLabelLay = new QHBoxLayout(); + colorLabelLay->setMargin(0); + colorLabelLay->setSpacing(3); + { + if (!isPreview) colorLabelLay->addWidget(m_showColorSettingsButton, 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); + + QHBoxLayout *advancedSettingsLabelLay = new QHBoxLayout(); advancedSettingsLabelLay->setMargin(0); advancedSettingsLabelLay->setSpacing(3); @@ -415,10 +453,18 @@ QFrame *OutputSettingsPopup::createPanel(bool isPreview) { } panel->setLayout(lay); + if (isPreview && m_syncColorSettingsButton) { + bool ret = connect(m_syncColorSettingsButton, SIGNAL(stateChanged(int)), + SLOT(onSyncColorSettingsChecked(int))); + assert(ret); + } + if (!isPreview) { bool ret = true; ret = ret && connect(m_showCameraSettingsButton, SIGNAL(toggled(bool)), m_cameraBox, SLOT(setVisible(bool))); + ret = ret && connect(m_showColorSettingsButton, SIGNAL(toggled(bool)), + m_colorBox, SLOT(setVisible(bool))); ret = ret && connect(m_showAdvancedSettingsButton, SIGNAL(toggled(bool)), m_advancedBox, SLOT(setVisible(bool))); ret = ret && connect(m_showMoreSettingsButton, SIGNAL(toggled(bool)), @@ -428,6 +474,7 @@ QFrame *OutputSettingsPopup::createPanel(bool isPreview) { if (!isPreview) { m_cameraBox->setVisible(false); + m_colorBox->setVisible(false); m_advancedBox->setVisible(false); m_moreBox->setVisible(false); } @@ -685,14 +732,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::createAdvancedSettingsBox(bool isPreview) { QFrame *advancedSettingsBox = new QFrame(this); advancedSettingsBox->setObjectName("OutputSettingsBox"); // Resample Balance m_resampleBalanceOm = new QComboBox(); - // Channel Width - m_channelWidthOm = new QComboBox(); // Threads m_threadsComboOm = new QComboBox(); // Granularity @@ -704,9 +823,6 @@ QFrame *OutputSettingsPopup::createAdvancedSettingsBox(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"); @@ -717,7 +833,6 @@ QFrame *OutputSettingsPopup::createAdvancedSettingsBox(bool isPreview) { m_rasterGranularityOm->addItems(granularityChoices); m_resampleBalanceOm->setFocusPolicy(Qt::StrongFocus); - m_channelWidthOm->setFocusPolicy(Qt::StrongFocus); m_threadsComboOm->setFocusPolicy(Qt::StrongFocus); m_rasterGranularityOm->setFocusPolicy(Qt::StrongFocus); m_resampleBalanceOm->installEventFilter(this); @@ -741,20 +856,17 @@ QFrame *OutputSettingsPopup::createAdvancedSettingsBox(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); // 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); @@ -768,8 +880,7 @@ QFrame *OutputSettingsPopup::createAdvancedSettingsBox(bool isPreview) { bool ret = true; 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)), @@ -1032,6 +1143,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; } @@ -1118,11 +1233,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()); @@ -1275,7 +1406,10 @@ void OutputSettingsPopup::onFormatChanged(const QString &str) { if (checkForSeqNum(str)) fp = fp.withFrame(prop->formatTemplateFId()); prop->setPath(fp); - TApp::instance()->getCurrentScene()->setDirtyFlag(true); + if (prop->getPath() != fp) { + prop->setPath(fp); + TApp::instance()->getCurrentScene()->setDirtyFlag(true); + } m_allowMT = Preferences::instance()->getFfmpegMultiThread(); if (m_presetCombo) m_presetCombo->setCurrentIndex(0); @@ -1394,6 +1528,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; @@ -1405,7 +1562,9 @@ void OutputSettingsPopup::onFrameFldEditFinished() { int step = (int)m_stepFld->getValue(); int shrink = (int)m_shrinkFld->getValue(); - bool error = false; + bool error = false; + bool somethingChanged = false; + if (r0 < 0) { error = true; r0 = 0; @@ -1438,15 +1597,27 @@ void OutputSettingsPopup::onFrameFldEditFinished() { r0 = 0; r1 = -1; } - prop->setRange(r0, r1, step); - TRenderSettings rs = prop->getRenderSettings(); + int oldR0, oldR1, oldStep; + prop->getRange(oldR0, oldR1, oldStep); + if (r0 != oldR0 || r1 != oldR1 || step != oldStep) { + prop->setRange(r0, r1, step); + somethingChanged = true; + } + TRenderSettings rs = prop->getRenderSettings(); + int oldShrink = rs.m_shrinkX; + bool old_applyShrink = rs.m_applyShrinkToViewer; + rs.m_shrinkX = rs.m_shrinkY = shrink; rs.m_applyShrinkToViewer = m_applyShrinkChk ? m_applyShrinkChk->checkState() == Qt::Checked : false; - prop->setRenderSettings(rs); + if (rs.m_shrinkX != oldShrink || + rs.m_applyShrinkToViewer != old_applyShrink) { + prop->setRenderSettings(rs); + somethingChanged = true; + } - TApp::instance()->getCurrentScene()->setDirtyFlag(true); + if (somethingChanged) TApp::instance()->getCurrentScene()->setDirtyFlag(true); } //----------------------------------------------------------------------------- @@ -1455,10 +1626,12 @@ void OutputSettingsPopup::onFrameFldEditFinished() { */ void OutputSettingsPopup::onResampleChanged(int type) { if (!getCurrentScene()) return; - TOutputProperties *prop = getProperties(); - TRenderSettings rs = prop->getRenderSettings(); + TOutputProperties *prop = getProperties(); + TRenderSettings rs = prop->getRenderSettings(); + TRenderSettings::ResampleQuality old_quality = rs.m_quality; rs.m_quality = resampleInfoMap[(ResampleOption)type].quality; + if (rs.m_quality == old_quality) return; prop->setRenderSettings(rs); TApp::instance()->getCurrentScene()->setDirtyFlag(true); if (m_presetCombo) m_presetCombo->setCurrentIndex(0); @@ -1472,13 +1645,106 @@ void OutputSettingsPopup::onChannelWidthChanged(int type) { if (!getCurrentScene()) return; TOutputProperties *prop = getProperties(); TRenderSettings rs = prop->getRenderSettings(); + 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(); } //----------------------------------------------------------------------------- @@ -1495,7 +1761,8 @@ void OutputSettingsPopup::onGammaFldEditFinished() { m_gammaFld->setValue(gamma); } TRenderSettings rs = prop->getRenderSettings(); - rs.m_gamma = gamma; + if (rs.m_gamma == gamma) return; + rs.m_gamma = gamma; prop->setRenderSettings(rs); TApp::instance()->getCurrentScene()->setDirtyFlag(true); } @@ -1510,6 +1777,7 @@ void OutputSettingsPopup::onDominantFieldChanged(int type) { if (!getCurrentScene()) return; TOutputProperties *prop = getProperties(); TRenderSettings rs = prop->getRenderSettings(); + TRenderSettings::FieldPrevalence oldFieldPrevalence = rs.m_fieldPrevalence; if (type != c_none && m_stretchFromFld->getValue() != m_stretchToFld->getValue()) { DVGui::error("Can't apply field rendering in a time stretched scene"); @@ -1523,6 +1791,7 @@ void OutputSettingsPopup::onDominantFieldChanged(int type) { rs.m_fieldPrevalence = TRenderSettings::NoField; m_doStereoscopy->setEnabled(rs.m_fieldPrevalence == TRenderSettings::NoField); m_stereoShift->setEnabled(rs.m_fieldPrevalence == TRenderSettings::NoField); + if (rs.m_fieldPrevalence == oldFieldPrevalence) return; prop->setRenderSettings(rs); TApp::instance()->getCurrentScene()->setDirtyFlag(true); } @@ -1541,12 +1810,16 @@ void OutputSettingsPopup::onStretchFldEditFinished() { DVGui::error("Can't stretch time in a field rendered scene\n"); m_stretchFromFld->setValue(rs.m_timeStretchFrom); m_stretchToFld->setValue(rs.m_timeStretchTo); - } else { - rs.m_timeStretchFrom = m_stretchFromFld->getValue(); - rs.m_timeStretchTo = m_stretchToFld->getValue(); - prop->setRenderSettings(rs); + return; + } else if (rs.m_timeStretchFrom == m_stretchFromFld->getValue() && + rs.m_timeStretchTo == m_stretchToFld->getValue()) { + return; } + rs.m_timeStretchFrom = m_stretchFromFld->getValue(); + rs.m_timeStretchTo = m_stretchToFld->getValue(); + prop->setRenderSettings(rs); + TApp::instance()->getCurrentScene()->setDirtyFlag(true); } @@ -1555,6 +1828,7 @@ void OutputSettingsPopup::onStretchFldEditFinished() { void OutputSettingsPopup::onMultimediaChanged(int state) { if (!getCurrentScene()) return; TOutputProperties *prop = getProperties(); + if (prop->getMultimediaRendering() == state) return; prop->setMultimediaRendering(state); TApp::instance()->getCurrentScene()->setDirtyFlag(true); @@ -1565,6 +1839,7 @@ void OutputSettingsPopup::onMultimediaChanged(int state) { void OutputSettingsPopup::onThreadsComboChanged(int type) { if (!getCurrentScene()) return; TOutputProperties *prop = getProperties(); + if (prop->getThreadIndex() == type) return; prop->setThreadIndex(type); TApp::instance()->getCurrentScene()->setDirtyFlag(true); @@ -1575,6 +1850,7 @@ void OutputSettingsPopup::onThreadsComboChanged(int type) { void OutputSettingsPopup::onRasterGranularityChanged(int type) { if (!getCurrentScene()) return; TOutputProperties *prop = getProperties(); + if (prop->getMaxTileSizeIndex() == type) return; prop->setMaxTileSizeIndex(type); TApp::instance()->getCurrentScene()->setDirtyFlag(true); @@ -1660,6 +1936,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(); @@ -1777,6 +2057,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") { @@ -1873,10 +2160,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 b2390aa6..4a1cc0c0 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,10 +81,13 @@ class OutputSettingsPopup : public DVGui::Dialog { QPushButton *m_boardSettingsBtn; QScrollArea *m_scrollArea; - QLabel *m_generalLabel, *m_cameraLabel, *m_advancedLabel, *m_moreLabel; - QFrame *m_generalBox, *m_cameraBox, *m_advancedBox, *m_moreBox; - QPushButton *m_showCameraSettingsButton, *m_showAdvancedSettingsButton, - *m_showMoreSettingsButton; + QLabel *m_generalLabel, *m_cameraLabel, *m_colorLabel, *m_advancedLabel, + *m_moreLabel; + QFrame *m_generalBox, *m_cameraBox, *m_colorBox, *m_advancedBox, *m_moreBox; + + DVGui::CheckBox *m_syncColorSettingsButton; + QPushButton *m_showCameraSettingsButton, *m_showColorSettingsButton, + *m_showAdvancedSettingsButton, *m_showMoreSettingsButton; bool m_isPreviewSettings; bool m_hideAlreadyCalled = false; @@ -93,6 +98,7 @@ class OutputSettingsPopup : public DVGui::Dialog { QFrame *createPanel(bool isPreview); QFrame *createGeneralSettingsBox(bool isPreview); QFrame *createCameraSettingsBox(bool isPreview); + QFrame *createColorSettingsBox(bool isPreview); QFrame *createAdvancedSettingsBox(bool isPreview); QFrame *createMoreSettingsBox(); @@ -117,6 +123,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(); @@ -127,6 +135,7 @@ protected slots: void onRasterGranularityChanged(int type); void onStereoChecked(int); void onStereoChanged(); + void onSyncColorSettingsChecked(int state); void onRenderClicked(); void onSaveAndRenderClicked(); diff --git a/toonz/sources/toonz/pane.cpp b/toonz/sources/toonz/pane.cpp index b2e58097..5b2a7ce2 100644 --- a/toonz/sources/toonz/pane.cpp +++ b/toonz/sources/toonz/pane.cpp @@ -7,6 +7,7 @@ #include "mainwindow.h" #include "tenv.h" #include "saveloadqsettings.h" +#include "custompanelmanager.h" #include "toonzqt/gutil.h" #include "toonzqt/dvdialog.h" @@ -43,6 +44,7 @@ #include extern TEnv::StringVar EnvSafeAreaName; +extern TEnv::IntVar EnvViewerPreviewBehavior; extern TEnv::IntVar CameraViewTransparency; extern TEnv::IntVar ShowRuleOfThirds; extern TEnv::IntVar ShowGoldenRatio; @@ -264,9 +266,10 @@ void TPanelTitleBarButton::paintEvent(QPaintEvent *event) { QPixmap panePixmapOn = compositePixmap(panePixmap, 1, QSize(), 0, 0, bgColor); QPainter painter(this); - painter.drawPixmap( - 0, 0, - m_pressed ? panePixmapOn : m_rollover ? panePixmapOver : panePixmapOff); + painter.drawPixmap(0, 0, + m_pressed ? panePixmapOn + : m_rollover ? panePixmapOver + : panePixmapOff); painter.end(); } @@ -473,6 +476,58 @@ void TPanelTitleBarButtonForGrids::mousePressEvent(QMouseEvent *e) { m_menu->exec(e->globalPos() + QPoint(-100, 12)); } +//============================================================================= +// TPanelTitleBarButtonForPreview +//----------------------------------------------------------------------------- + +void TPanelTitleBarButtonForPreview::mousePressEvent(QMouseEvent *e) { + if (e->button() != Qt::RightButton) { + m_pressed = !m_pressed; + emit toggled(m_pressed); + update(); + } +} + +//----------------------------------------------------------------------------- + +void TPanelTitleBarButtonForPreview::contextMenuEvent(QContextMenuEvent *e) { + QMenu menu(this); + + // 0: current frame + // 1: all frames in the preview range + // 2: selected cell, auto play once & stop + QStringList behaviorsStrList = {tr("Current frame"), + tr("All preview range frames"), + tr("Selected cells - Auto play")}; + + QActionGroup *behaviorGroup = new QActionGroup(this); + + for (int i = 0; i < behaviorsStrList.size(); i++) { + QAction *action = menu.addAction(behaviorsStrList.at(i)); + action->setData(i); + connect(action, SIGNAL(triggered()), this, SLOT(onSetPreviewBehavior())); + action->setCheckable(true); + behaviorGroup->addAction(action); + if (i == EnvViewerPreviewBehavior) action->setChecked(true); + } + + menu.exec(e->globalPos()); +} + +//----------------------------------------------------------------------------- + +void TPanelTitleBarButtonForPreview::onSetPreviewBehavior() { + int behaviorId = qobject_cast(sender())->data().toInt(); + // change safearea if the different one is selected + if (EnvViewerPreviewBehavior != behaviorId) { + EnvViewerPreviewBehavior = behaviorId; + // emit sceneChanged without setting dirty flag + TApp::instance()->getCurrentScene()->notifySceneChanged(false); + } +} + +//----------------------------------------------------------------------------- + //============================================================================= // TPanelTitleBarButtonSet //----------------------------------------------------------------------------- @@ -653,6 +708,12 @@ TPanel *TPanelFactory::createPanel(QWidget *parent, QString panelType) { QMap::iterator it = tableInstance().find(panelType); if (it == tableInstance().end()) { + if (panelType.startsWith("Custom_")) { + panelType = panelType.right(panelType.size() - 7); + return CustomPanelManager::instance()->createCustomPanel(panelType, + parent); + } + TPanel *panel = new TPanel(parent); panel->setPanelType(panelType.toStdString()); return panel; diff --git a/toonz/sources/toonz/pane.h b/toonz/sources/toonz/pane.h index e9387401..5d05285b 100644 --- a/toonz/sources/toonz/pane.h +++ b/toonz/sources/toonz/pane.h @@ -5,7 +5,7 @@ // TODO: cambiare il nome del file in tpanel.h -//#include +// #include #include "../toonzqt/tdockwindows.h" class TPanelTitleBarButtonSet; @@ -75,7 +75,7 @@ signals: }; //----------------------------------------------------------------------------- -/*! specialized button for sage area which enables to choose safe area size by +/*! specialized button for safe area which enables to choose safe area size by * context menu */ @@ -130,6 +130,27 @@ signals: void updateViewer(); }; +//----------------------------------------------------------------------------- +/*! specialized button for safe area which enables to choose safe area size by + * context menu + */ + +class TPanelTitleBarButtonForPreview final : public TPanelTitleBarButton { + Q_OBJECT +public: + TPanelTitleBarButtonForPreview(QWidget *parent, + const QString &standardPixmapName) + : TPanelTitleBarButton(parent, standardPixmapName) {} + + bool isChecked() { return m_pressed; } + +protected: + void contextMenuEvent(QContextMenuEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; +protected slots: + void onSetPreviewBehavior(); +}; + //----------------------------------------------------------------------------- //! a buttonset can group different TPanelTitleBarButton @@ -242,7 +263,7 @@ class TPanel : public TDockWidget { QByteArray m_currentRoomOldState; public: - TPanel(QWidget *parent = 0, Qt::WindowFlags flags = 0, + TPanel(QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags(), TDockWidget::Orientation orientation = TDockWidget::vertical); ~TPanel(); diff --git a/toonz/sources/toonz/pltgizmopopup.cpp b/toonz/sources/toonz/pltgizmopopup.cpp index 73f531a2..d482cff9 100644 --- a/toonz/sources/toonz/pltgizmopopup.cpp +++ b/toonz/sources/toonz/pltgizmopopup.cpp @@ -448,11 +448,7 @@ void modifyColor(const T &modifier) { // ValueAdjuster //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 ValueAdjuster::ValueAdjuster(QWidget *parent, Qt::WindowFlags flags) -#else -ValueAdjuster::ValueAdjuster(QWidget *parent, Qt::WFlags flags) -#endif : QWidget(parent) { QPushButton *plusBut = new QPushButton(QString("+"), this); QPushButton *minusBut = new QPushButton(QString("-"), this); @@ -501,11 +497,7 @@ void ValueAdjuster::onClickedMinus() { // ValueShifter //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 ValueShifter::ValueShifter(bool isHue, QWidget *parent, Qt::WindowFlags flags) -#else -ValueShifter::ValueShifter(bool isHue, QWidget *parent, Qt::WFlags flags) -#endif : QWidget(parent) { QPushButton *plusBut = new QPushButton(QString("+"), this); QPushButton *minusBut = new QPushButton(QString("-"), this); @@ -553,11 +545,7 @@ void ValueShifter::onClickedMinus() { // ColorFader //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 ColorFader::ColorFader(QString name, QWidget *parent, Qt::WindowFlags flags) -#else -ColorFader::ColorFader(QString name, QWidget *parent, Qt::WFlags flags) -#endif : QWidget(parent) { QHBoxLayout *layout = new QHBoxLayout(this); layout->setMargin(0); diff --git a/toonz/sources/toonz/pltgizmopopup.h b/toonz/sources/toonz/pltgizmopopup.h index 2f4528e3..af6db441 100644 --- a/toonz/sources/toonz/pltgizmopopup.h +++ b/toonz/sources/toonz/pltgizmopopup.h @@ -18,11 +18,7 @@ class ValueAdjuster final : public QWidget { DVGui::DoubleLineEdit *m_valueLineEdit; public: -#if QT_VERSION >= 0x050500 - ValueAdjuster(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - ValueAdjuster(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + ValueAdjuster(QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags()); ~ValueAdjuster(); protected slots: @@ -43,11 +39,8 @@ class ValueShifter final : public QWidget { DVGui::DoubleLineEdit *m_valueLineEdit; public: -#if QT_VERSION >= 0x050500 - ValueShifter(bool isHue, QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - ValueShifter(bool isHue, QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + ValueShifter(bool isHue, QWidget *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); ~ValueShifter(); protected slots: @@ -70,11 +63,8 @@ class ColorFader final : public QWidget { DVGui::DoubleLineEdit *m_valueLineEdit; public: -#if QT_VERSION >= 0x050500 - ColorFader(QString name = "", QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - ColorFader(QString name = "", QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + ColorFader(QString name = "", QWidget *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); ~ColorFader(); protected slots: diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index ddafd860..6d7d4eab 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -202,6 +202,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(); @@ -209,6 +216,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; @@ -224,6 +235,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")); } //----------------------------------------------------------------------------- @@ -244,6 +258,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(); } @@ -269,6 +284,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; } @@ -1454,7 +1472,15 @@ QList PreferencesPopup::getComboItemList( {cursorBrushType, {{tr("Small"), "Small"}, {tr("Large"), "Large"}, - {tr("Crosshair"), "Crosshair"}}}, + {tr("Crosshair"), "Crosshair"}, + {tr("Triangle Top Left"), "Triangle Top Left"}, + {tr("Triangle Top Right"), "Triangle Top Right"}, + {tr("Triangle Bottom Left"), "Triangle Bottom Left"}, + {tr("Triangle Bottom Right"), "Triangle Bottom Right"}, + {tr("Triangle Up"), "Triangle Up"}, + {tr("Triangle Down"), "Triangle Down"}, + {tr("Triangle Left"), "Triangle Left"}, + {tr("Triangle Right"), "Triangle Right"}}}, {cursorBrushStyle, {{tr("Default"), "Default"}, {tr("Left-Handed"), "Left-Handed"}, diff --git a/toonz/sources/toonz/preferencespopup.h b/toonz/sources/toonz/preferencespopup.h index 4fabd574..5f040a9f 100644 --- a/toonz/sources/toonz/preferencespopup.h +++ b/toonz/sources/toonz/preferencespopup.h @@ -216,7 +216,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 28c67174..39eb6890 100644 --- a/toonz/sources/toonz/previewer.cpp +++ b/toonz/sources/toonz/previewer.cpp @@ -196,6 +196,9 @@ public: // are assumed correct. void refreshFrame(int frame); + void addRenderData(std::vector &datas, int frame); + void addFramesToRenderQueue(const std::vector frames); + // TRenderPort methods void onRenderRasterStarted(const RenderData &renderData) override; void onRenderRasterCompleted(const RenderData &renderData) override; @@ -393,13 +396,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 +676,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 @@ -706,21 +712,33 @@ void Previewer::Imp::doOnRenderRasterCompleted(const RenderData &renderData) { it->second .m_rectUnderRender); // Extract may MODIFY IT! E.g. with shrinks..! cachedRas = cachedRas->extract(rectUnderRender); + if (cachedRas) { cachedRas->copy(ras); - TImageCache::instance()->add(str, ri); } - // Update the FrameInfo - it->second.m_renderedRegion += toQRect(it->second.m_rectUnderRender); - it->second.m_rectUnderRender = TRect(); + // Submit the image to the cache, for all cluster's frames + unsigned int i, size = renderData.m_frames.size(); + for (i = 0; i < size; ++i) { + int f = renderData.m_frames[i]; + std::map::iterator f_it = m_frames.find(f); + if (f_it == m_frames.end()) continue; - // Update the progress bar status - if (frame < m_pbStatus.size()) - m_pbStatus[frame] = FlipSlider::PBFrameFinished; + if (cachedRas) { + std::string f_str = m_cachePrefix + std::to_string(f); + TImageCache::instance()->add(f_str, ri); + } - // Notify listeners - notifyCompleted(frame); + // Update the FrameInfo + f_it->second.m_renderedRegion += toQRect(f_it->second.m_rectUnderRender); + f_it->second.m_rectUnderRender = TRect(); + + // Update the progress bar status + if (f < m_pbStatus.size()) m_pbStatus[f] = FlipSlider::PBFrameFinished; + + // Notify listeners + notifyCompleted(f); + } } //----------------------------------------------------------------------------- @@ -974,6 +992,69 @@ void Previewer::Imp::saveFrame() { savedFrames = 0; } +//----------------------------------------------------------------------------- + +void Previewer::Imp::addRenderData(std::vector &datas, + int frame) { + // Build the TFxPair to be passed to TRenderer + TFxPair fxPair = buildSceneFx(frame); + + // Update the RenderInfos associated with frame + m_frames[frame].m_rectUnderRender = m_previewRect; + m_frames[frame].m_alias = fxPair.m_frameA->getAlias(frame, m_renderSettings); + if (fxPair.m_frameB) + m_frames[frame].m_alias = + m_frames[frame].m_alias + + fxPair.m_frameB->getAlias(frame, m_renderSettings); + + // Retrieve the renderId of the rendering instance + m_frames[frame].m_renderId = m_renderer.nextRenderId(); + std::string contextName("P"); + contextName += m_subcamera ? "SC" : "FU"; + contextName += std::to_string(frame); + TPassiveCacheManager::instance()->setContextName(m_frames[frame].m_renderId, + contextName); + + datas.push_back(TRenderer::RenderData(frame, m_renderSettings, fxPair)); +} + +//----------------------------------------------------------------------------- + +void Previewer::Imp::addFramesToRenderQueue(const std::vector frames) { + if (suspendedRendering) return; + // Build the region to render + updatePreviewRect(); + if (m_previewRect.getLx() <= 0 || m_previewRect.getLy() <= 0) return; + + RenderDataVector *renderDatas = new RenderDataVector; + + for (const auto &f : frames) { + std::map::iterator it = m_frames.find(f); + if (it == m_frames.end()) { + it = m_frames.insert(std::make_pair(f, FrameInfo())).first; + // In case the frame is not in the frame range, we add a temporary + // supplement + // to the progress bar. + if (f >= (int)m_pbStatus.size()) m_pbStatus.resize(f + 1); + addRenderData(*renderDatas, f); + } else if (f < m_pbStatus.size() && + m_pbStatus[f] == FlipSlider::PBFrameNotStarted) { + // In case the rect we would render is contained in the frame's rendered + // region, quit + if (::contains(it->second.m_renderedRegion, m_previewRect)) return; + // Then, check the m_previewRect against the frame's m_rectUnderRendering. + // Ensure that we're not re-launching the very same render. + if (it->second.m_rectUnderRender == m_previewRect) return; + // Stop any frame's previously running render process + m_renderer.abortRendering(it->second.m_renderId); + addRenderData(*renderDatas, f); + } + } + + // Finally, start rendering all frames which were not found in cache + m_renderer.startRendering(renderDatas); +} + //============================================================================= // Previewer //----------------------------------------------------------------------------- @@ -1135,8 +1216,8 @@ void Previewer::saveRenderedFrames() { //----------------------------------------------------------------------------- -/*! Restituisce un puntatore al raster randerizzato se il frame e' disponibile, - altrimenti comincia a calcolarlo*/ +/*! Returns a pointer to the rendered raster if the frame is available, + otherwise start calculating it */ TRasterP Previewer::getRaster(int frame, bool renderIfNeeded) const { if (frame < 0) return TRasterP(); std::map::iterator it = m_imp->m_frames.find(frame); @@ -1149,7 +1230,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 @@ -1168,7 +1249,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(); @@ -1181,6 +1262,13 @@ TRasterP Previewer::getRaster(int frame, bool renderIfNeeded) const { //----------------------------------------------------------------------------- +void Previewer::addFramesToRenderQueue(const std::vector frames) const { + if (suspendedRendering) return; + m_imp->addFramesToRenderQueue(frames); +} + +//----------------------------------------------------------------------------- + //! Verifica se \b frame e' nella cache, cioe' se il frame e' disponibile bool Previewer::isFrameReady(int frame) const { if (frame < 0 || frame >= (int)m_imp->m_pbStatus.size()) return false; diff --git a/toonz/sources/toonz/previewer.h b/toonz/sources/toonz/previewer.h index 566d77b1..6cb270e4 100644 --- a/toonz/sources/toonz/previewer.h +++ b/toonz/sources/toonz/previewer.h @@ -70,6 +70,7 @@ public: void removeListener(Listener *); TRasterP getRaster(int frame, bool renderIfNeeded = true) const; + void addFramesToRenderQueue(const std::vector frames) const; bool isFrameReady(int frame) const; bool doSaveRenderedFrames(TFilePath fp); diff --git a/toonz/sources/toonz/previewfxmanager.cpp b/toonz/sources/toonz/previewfxmanager.cpp index 169204a0..efba8cef 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縺ァ險育ョ励&繧後◆蝣エ蜷医€∫オ先棡繧奪ithering縺吶k --*/ // 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/quicktoolbar.cpp b/toonz/sources/toonz/quicktoolbar.cpp index f60b34d6..17efe14f 100644 --- a/toonz/sources/toonz/quicktoolbar.cpp +++ b/toonz/sources/toonz/quicktoolbar.cpp @@ -23,12 +23,8 @@ namespace XsheetGUI { // Toolbar //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 QuickToolbar::QuickToolbar(XsheetViewer *parent, Qt::WindowFlags flags, bool isCollapsible) -#else -QuickToolbar::QuickToolbar(XsheetViewer *parent, Qt::WFlags flags) -#endif : CommandBar(parent, flags, isCollapsible, true), m_viewer(parent) { setObjectName("cornerWidget"); setFixedHeight(29); diff --git a/toonz/sources/toonz/quicktoolbar.h b/toonz/sources/toonz/quicktoolbar.h index ff39e97f..0fb500d6 100644 --- a/toonz/sources/toonz/quicktoolbar.h +++ b/toonz/sources/toonz/quicktoolbar.h @@ -31,12 +31,8 @@ class QuickToolbar final : public CommandBar { XsheetViewer *m_viewer; public: -#if QT_VERSION >= 0x050500 QuickToolbar(XsheetViewer *parent = 0, Qt::WindowFlags flags = 0, bool isCollapsible = false); -#else - QuickToolbar(XsheetViewer *parent = 0, Qt::WFlags flags = 0); -#endif static void toggleQuickToolbar(); void showToolbar(bool show); @@ -48,6 +44,6 @@ protected slots: void doCustomizeCommandBar(); }; -} // namespace XsheetGUI; +} // namespace XsheetGUI #endif // QUICKTOOLBAR_H diff --git a/toonz/sources/toonz/rendercommand.cpp b/toonz/sources/toonz/rendercommand.cpp index 91b19a67..5bfa9676 100644 --- a/toonz/sources/toonz/rendercommand.cpp +++ b/toonz/sources/toonz/rendercommand.cpp @@ -87,10 +87,9 @@ public: if (Preferences::instance()->isGeneratedMovieViewEnabled()) { if (!isPreview && (Preferences::instance()->isDefaultViewerEnabled()) && - (m_fp.getType() == "avi" || - m_fp.getType() == "mp4" || - m_fp.getType() == "gif" || m_fp.getType() == "webm" || - m_fp.getType() == "mov")) { + (m_fp.getType() == "mov" || m_fp.getType() == "avi" || + m_fp.getType() == "3gp" || m_fp.getType() == "mp4" || + m_fp.getType() == "gif" || m_fp.getType() == "webm")) { QString name = QString::fromStdString(m_fp.getName()); int index; if ((index = name.indexOf("#RENDERID")) != -1) //! quite ugly I @@ -447,7 +446,7 @@ void RenderCommand::rasterRender(bool isPreview) { // depth). I tried to make OT to detect the mov settings and adaptively switch // the behavior, but ended in vain :-( // So I just omitted every mov from applying solid background as a quick fix. - if (isMovieType(ext) && ext != "mov" && ext != "webm") { + if (isMovieTypeOpaque(ext)) { scene->getProperties()->setBgColor(currBgColor); } // for non alpha-enabled images (like jpg), background color will be inserted diff --git a/toonz/sources/toonz/scenebrowser.cpp b/toonz/sources/toonz/scenebrowser.cpp new file mode 100644 index 00000000..573ffc58 --- /dev/null +++ b/toonz/sources/toonz/scenebrowser.cpp @@ -0,0 +1,2200 @@ + + +#include "scenebrowser.h" + +// Tnz6 includes +#include "dvdirtreeview.h" +#include "filebrowser.h" +#include "filebrowsermodel.h" +#include "fileselection.h" +#include "filmstripselection.h" +#include "castselection.h" +#include "menubarcommandids.h" +#include "floatingpanelcommand.h" +#include "iocommand.h" +#include "history.h" +#include "tapp.h" + +// TnzQt includes +#include "toonzqt/dvdialog.h" +#include "toonzqt/icongenerator.h" +#include "toonzqt/menubarcommand.h" +#include "toonzqt/gutil.h" +#include "toonzqt/trepetitionguard.h" + +// TnzLib includes +#include "toonz/tscenehandle.h" +#include "toonz/toonzscene.h" +#include "toonz/txshsimplelevel.h" +#include "toonz/txshsoundlevel.h" +#include "toonz/tproject.h" +#include "toonz/txshlevelhandle.h" +#include "toonz/namebuilder.h" +#include "toonz/toonzimageutils.h" +#include "toonz/preferences.h" + +// TnzBase includes +#include "tenv.h" + +// TnzCore includes +#include "tsystem.h" +#include "tconvert.h" +#include "tfiletype.h" +#include "tlevel_io.h" + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// tcg includes +#include "tcg/boost/range_utility.h" +#include "tcg/boost/permuted_range.h" + +// boost includes +#include +#include +#include +#include + +namespace ba = boost::adaptors; + +using namespace DVGui; + +//============================================================================= +// Local namespace +//============================================================================= + +namespace { +std::set activePreproductionBoards; +// std::map frameCountMap; +// QMutex frameCountMapMutex; +QMutex levelFileMutex; + +} // namespace + +//============================================================================= +// +// SceneBrowserButtonBar +// +//----------------------------------------------------------------------------- + +SceneBrowserButtonBar::SceneBrowserButtonBar(DvItemViewer *itemViewer, + QWidget *parent) + : QToolBar(parent) { + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setIconSize(QSize(17, 17)); + setObjectName("buttonBar"); + // buttonBar->setIconSize(QSize(10,10)); + + QIcon newFolderIcon = createQIcon("newfolder"); + QAction *newScene = new QAction(newFolderIcon, tr("Create new scene"), this); + newScene->setIconText(tr("Create scene")); + addAction(newScene); + // addSeparator(); + + connect(newScene, SIGNAL(triggered()), SIGNAL(newScene())); +} + +//============================================================================= +// SceneBrowser +//----------------------------------------------------------------------------- + +SceneBrowser::SceneBrowser(QWidget *parent, Qt::WindowFlags flags, + bool noContextMenu, bool multiSelectionEnabled) + : QFrame(parent), m_folderName(0), m_itemViewer(0) { + // style sheet + setObjectName("SceneBrowser"); + setFrameStyle(QFrame::StyledPanel); + + // m_mainSplitter = new QSplitter(this); + m_folderTreeView = new DvDirTreeView(this); + QFrame *box = new QFrame(this); + QLabel *folderLabel = new QLabel(tr("Folder: "), this); + m_folderName = new QLineEdit(); + m_itemViewer = new DvItemViewer(box, noContextMenu, multiSelectionEnabled, + DvItemViewer::Browser); + DvItemViewerTitleBar *titleBar = new DvItemViewerTitleBar(m_itemViewer, box); + SceneBrowserButtonBar *buttonBar = + new SceneBrowserButtonBar(m_itemViewer, box); + DvItemViewerPanel *viewerPanel = m_itemViewer->getPanel(); + viewerPanel->setThumbnailsView(); + viewerPanel->setIconSize(QSize(192, 108)); // default 80, 60 + viewerPanel->addColumn(DvItemListModel::FileType, 50); + viewerPanel->addColumn(DvItemListModel::FrameCount, 50); + viewerPanel->addColumn(DvItemListModel::FileSize, 50); + viewerPanel->addColumn(DvItemListModel::CreationDate, 130); + viewerPanel->addColumn(DvItemListModel::ModifiedDate, 130); + if (Preferences::instance()->isSVNEnabled()) + viewerPanel->addColumn(DvItemListModel::VersionControlStatus, 120); + + viewerPanel->setSelection(new FileSelection()); + DVItemViewPlayDelegate *itemViewPlayDelegate = + new DVItemViewPlayDelegate(viewerPanel); + viewerPanel->setItemViewPlayDelegate(itemViewPlayDelegate); + + // m_mainSplitter->setObjectName("SceneBrowserSplitter"); + m_folderTreeView->hide(); + m_folderTreeView->setObjectName("DirTreeView"); + box->setObjectName("castFrame"); + box->setFrameStyle(QFrame::StyledPanel); + + m_itemViewer->setModel(this); + + // layout + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->setMargin(3); + mainLayout->setSpacing(2); + { + mainLayout->addWidget(buttonBar); + + QHBoxLayout *folderLay = new QHBoxLayout(); + folderLay->setMargin(0); + folderLay->setSpacing(0); + { + folderLay->addWidget(folderLabel, 0); + folderLay->addWidget(m_folderName, 1); + } + mainLayout->addLayout(folderLay, 0); + + // m_mainSplitter->addWidget(m_folderTreeView); + QVBoxLayout *boxLayout = new QVBoxLayout(box); + boxLayout->setMargin(0); + boxLayout->setSpacing(0); + { + boxLayout->addWidget(titleBar, 0); + boxLayout->addWidget(m_itemViewer, 1); + } + // m_mainSplitter->addWidget(box); + mainLayout->addWidget(box, 1); + } + setLayout(mainLayout); + + // m_mainSplitter->setSizes(QList() << 270 << 500); + + // signal-slot connections + bool ret = connect(m_folderTreeView, SIGNAL(currentNodeChanged()), + itemViewPlayDelegate, SLOT(resetPlayWidget())); + // if the current forder is changed in the folder tree, then update in the + // item view + ret = ret && connect(m_folderTreeView, SIGNAL(currentNodeChanged()), this, + SLOT(onTreeFolderChanged())); + + ret = ret && connect(m_itemViewer, SIGNAL(clickedItem(int)), this, + SLOT(onClickedItem(int))); + ret = ret && connect(m_itemViewer, SIGNAL(doubleClickedItem(int)), this, + SLOT(onDoubleClickedItem(int))); + ret = + ret && connect(m_itemViewer, SIGNAL(selectedItems(const std::set &)), + this, SLOT(onSelectedItems(const std::set &))); + ret = ret && connect(buttonBar, SIGNAL(newScene()), this, SLOT(newScene())); + + ret = ret && connect(&m_frameCountReader, SIGNAL(calculatedFrameCount()), + m_itemViewer->getPanel(), SLOT(update())); + + QAction *refresh = CommandManager::instance()->getAction(MI_RefreshTree); + ret = ret && connect(refresh, SIGNAL(triggered()), this, SLOT(refresh())); + addAction(refresh); + + // Version Control instance connection + if (Preferences::instance()->isSVNEnabled()) + ret = + ret && connect(VersionControl::instance(), + SIGNAL(commandDone(const QStringList &)), this, + SLOT(onVersionControlCommandDone(const QStringList &))); + + // if the folderName is edited, move the current folder accordingly + ret = ret && connect(m_folderName, SIGNAL(editingFinished()), this, + SLOT(onFolderEdited())); + + // folder history + ret = ret && connect(m_folderTreeView, SIGNAL(currentNodeChanged()), this, + SLOT(storeFolderHistory())); + + // check out the update of the current folder. + // Use MyFileSystemWatcher which is shared by all browsers. + // Adding and removing paths to the watcher is done in DvDirTreeView. + ret = ret && connect(MyFileSystemWatcher::instance(), + SIGNAL(directoryChanged(const QString &)), this, + SLOT(onFileSystemChanged(const QString &))); + + // when the scene switched, update the path of the scene location node + TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene(); + // ret = ret && connect(sceneHandle, SIGNAL(sceneSwitched()), this, + // SLOT(onSceneSwitched())); + ret = ret && connect(sceneHandle, SIGNAL(nameSceneChanged()), this, + SLOT(onSceneSwitched())); + + // onSceneSwitched(); + + // store the first item("Root") in the history + m_indexHistoryList.append(m_folderTreeView->currentIndex()); + m_currentPosition = 0; + + refreshHistoryButtons(); + + assert(ret); +} + +//----------------------------------------------------------------------------- + +SceneBrowser::~SceneBrowser() {} + +//----------------------------------------------------------------------------- +/*! when the m_folderName is edited, move the current folder accordingly + */ +void SceneBrowser::onFolderEdited() { + TFilePath inputPath(m_folderName->text().toStdWString()); + QModelIndex index = DvDirModel::instance()->getIndexByPath(inputPath); + + // If there is no node matched + if (!index.isValid()) { + QMessageBox::warning(this, tr("Open folder failed"), + tr("The input folder path was invalid.")); + return; + } + m_folderTreeView->collapseAll(); + + m_folderTreeView->setCurrentIndex(index); + + // expand the folder tree + QModelIndex tmpIndex = index; + while (tmpIndex.isValid()) { + m_folderTreeView->expand(tmpIndex); + tmpIndex = tmpIndex.parent(); + } + + m_folderTreeView->scrollTo(index); + m_folderTreeView->update(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::storeFolderHistory() { + QModelIndex currentModelIndex = m_folderTreeView->currentIndex(); + + if (!currentModelIndex.isValid()) return; + + if (m_indexHistoryList[m_currentPosition] == currentModelIndex) return; + + // If there is no next history item, then create it + if (m_currentPosition == m_indexHistoryList.size() - 1) { + m_indexHistoryList << currentModelIndex; + m_currentPosition++; + } + // If the next hitory item is the same as the current one, just move to it + else if (m_indexHistoryList[m_currentPosition + 1] == currentModelIndex) { + m_currentPosition++; + } + // If the next history item is different from the current one, then replace + // with the new one + else { + int size = m_indexHistoryList.size(); + // remove the old history items + for (int i = m_currentPosition + 1; i < size; i++) + m_indexHistoryList.removeLast(); + m_indexHistoryList << currentModelIndex; + m_currentPosition++; + } + refreshHistoryButtons(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::refreshHistoryButtons() { + emit historyChanged((m_currentPosition != 0), + (m_currentPosition != m_indexHistoryList.size() - 1)); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onBackButtonPushed() { + if (m_currentPosition == 0) return; + m_currentPosition--; + QModelIndex currentIndex = m_indexHistoryList[m_currentPosition]; + m_folderTreeView->setCurrentIndex(currentIndex); + m_folderTreeView->collapseAll(); + while (currentIndex.isValid()) { + currentIndex = currentIndex.parent(); + m_folderTreeView->expand(currentIndex); + } + m_folderTreeView->update(); + + refreshHistoryButtons(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onFwdButtonPushed() { + if (m_currentPosition >= m_indexHistoryList.size() - 1) return; + m_currentPosition++; + QModelIndex currentIndex = m_indexHistoryList[m_currentPosition]; + m_folderTreeView->setCurrentIndex(currentIndex); + m_folderTreeView->collapseAll(); + while (currentIndex.isValid()) { + currentIndex = currentIndex.parent(); + m_folderTreeView->expand(currentIndex); + } + m_folderTreeView->update(); + + refreshHistoryButtons(); +} + +//----------------------------------------------------------------------------- +/*! clear the history when the tree date is replaced + */ +void SceneBrowser::clearHistory() { + int size = m_indexHistoryList.size(); + // leave the last item + for (int i = 1; i < size; i++) m_indexHistoryList.removeLast(); + m_currentPosition = 0; + refreshHistoryButtons(); +} + +//----------------------------------------------------------------------------- +/*! update the current folder when changes detected from QFileSystemWatcher + */ +void SceneBrowser::onFileSystemChanged(const QString &folderPath) { + if (folderPath != m_folder.getQString()) return; + // changes may create/delete of folder, so update the DvDirModel + QModelIndex parentFolderIndex = m_folderTreeView->currentIndex(); + DvDirModel::instance()->refresh(parentFolderIndex); + + refreshCurrentFolderItems(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::sortByDataModel(DataType dataType, bool isDiscendent) { + struct locals { + static inline bool itemLess(int aIdx, int bIdx, SceneBrowser &fb, + DataType dataType) { + return (fb.compareData(dataType, aIdx, bIdx) > 0); + } + + static inline bool indexLess(int aIdx, int bIdx, + const std::vector &vec) { + return (vec[aIdx] < vec[bIdx]); + } + + static inline int complement(int val, int max) { + return (assert(0 <= val && val <= max), max - val); + } + }; // locals + + if (dataType != getCurrentOrderType()) { + // Build the permutation table + std::vector new2OldIdx( + boost::make_counting_iterator(0), + boost::make_counting_iterator(int(m_items.size()))); + + std::stable_sort( + new2OldIdx.begin(), new2OldIdx.end(), + boost::bind(locals::itemLess, _1, _2, boost::ref(*this), dataType)); + + // Use the renumbering table to permutate elements + std::vector( + boost::make_permutation_iterator(m_items.begin(), new2OldIdx.begin()), + boost::make_permutation_iterator(m_items.begin(), new2OldIdx.end())) + .swap(m_items); + + // Use the permutation table to update current file selection, if any + FileSelection *fs = + static_cast(m_itemViewer->getPanel()->getSelection()); + + if (!fs->isEmpty()) { + std::vector old2NewIdx( + boost::make_counting_iterator(0), + boost::make_counting_iterator(int(m_items.size()))); + + std::sort(old2NewIdx.begin(), old2NewIdx.end(), + boost::bind(locals::indexLess, _1, _2, boost::ref(new2OldIdx))); + + std::vector newSelectedIndices; + tcg::substitute( + newSelectedIndices, + tcg::permuted_range(old2NewIdx, fs->getSelectedIndices() | + ba::filtered(boost::bind( + std::less(), _1, + int(old2NewIdx.size()))))); + + fs->select(!newSelectedIndices.empty() ? &newSelectedIndices.front() : 0, + int(newSelectedIndices.size())); + } + + setIsDiscendentOrder(true); + setOrderType(dataType); + } + + // Reverse lists if necessary + if (isDiscendentOrder() != isDiscendent) { + std::reverse(m_items.begin(), m_items.end()); + + // Reverse file selection, if any + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + + if (!fs->isEmpty()) { + int iCount = int(m_items.size()), lastIdx = iCount - 1; + + std::vector newSelectedIndices; + tcg::substitute( + newSelectedIndices, + fs->getSelectedIndices() | + ba::filtered(boost::bind(std::less(), _1, iCount)) | + ba::transformed(boost::bind(locals::complement, _1, lastIdx))); + + fs->select(!newSelectedIndices.empty() ? &newSelectedIndices.front() : 0, + int(newSelectedIndices.size())); + } + + setIsDiscendentOrder(isDiscendent); + } + + m_itemViewer->getPanel()->update(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setFilterTypes(const QStringList &types) { + m_filter = types; +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::addFilterType(const QString &type) { + if (!m_filter.contains(type)) m_filter.push_back(type); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::removeFilterType(const QString &type) { + m_filter.removeAll(type); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::refreshCurrentFolderItems() { + m_items.clear(); + + // put the parent directory item + // TFilePath parentFp = m_folder.getParentDir(); + // if (parentFp != TFilePath("") && parentFp != m_folder) + // m_items.push_back(Item(parentFp, true, false)); + + // register the file items + if (m_folder != TFilePath()) { + TFilePathSet files; + TFilePathSet all_files; // for updating m_multiFileItemMap + + TFileStatus fpStatus(m_folder); + // if the item is link, then set the link target of it + if (fpStatus.isLink()) { + QFileInfo info(toQString(m_folder)); + setFolder(TFilePath(info.symLinkTarget().toStdWString())); + return; + } + if (fpStatus.doesExist() && fpStatus.isDirectory() && + fpStatus.isReadable()) { + try { + TSystem::readDirectory(files, all_files, m_folder); + } catch (...) { + } + } + TFilePathSet::iterator it; + for (it = files.begin(); it != files.end(); ++it) { + // skip the plt file (Palette file for TOONZ 4.6 and earlier) + if (it->getType() == "plt") continue; + + // filter the file + else if (m_filter.isEmpty()) { + if (it->getType() != "tnz" && it->getType() != "scr" && + it->getType() != "tnzbat" && it->getType() != "mpath" && + it->getType() != "curve" && it->getType() != "tpl" && + TFileType::getInfo(*it) == TFileType::UNKNOW_FILE) + continue; + } else if (m_filter.contains(QString::fromStdString(it->getType()))) + continue; + // store the filtered file paths + m_items.push_back(Item(*it)); + } + + // update the m_multiFileItemMap + m_multiFileItemMap.clear(); + + for (it = all_files.begin(); it != all_files.end(); it++) { + TFrameId tFrameId; + tFrameId = it->getFrame(); + TFilePath levelName(it->getLevelName()); + + if (levelName.isLevelName()) { + Item &levelItem = m_multiFileItemMap[levelName]; + + // TODO: + // とりあえず残すが、FileInfoの取得に時間がかかるようならオプション化も検討 + // 2015/12/28 shun_iwasawa + QFileInfo fileInfo(QString::fromStdWString(it->getWideString())); + // Update level infos + if (levelItem.m_creationDate.isNull() || + (fileInfo.created() < levelItem.m_creationDate)) + levelItem.m_creationDate = fileInfo.created(); + if (levelItem.m_modifiedDate.isNull() || + (fileInfo.lastModified() > levelItem.m_modifiedDate)) + levelItem.m_modifiedDate = fileInfo.lastModified(); + levelItem.m_fileSize += fileInfo.size(); + + // store frameId + levelItem.m_frameIds.push_back(tFrameId); + + levelItem.m_frameCount++; + } + } + } + + // Set Missing Version Control Items + int missingItemCount = 0; + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (node) { + QList list = node->getMissingFiles(); + missingItemCount = list.size(); + for (int i = 0; i < missingItemCount; i++) + m_items.push_back(Item(list.at(i))); + } + + // Refresh Data (fill Item field) + refreshData(); + + // If I added some missing items I need to sort items. + if (missingItemCount > 0) { + DataType currentDataType = getCurrentOrderType(); + int i; + for (i = 1; i < m_items.size(); i++) { + int index = i; + while (index > 0 && compareData(currentDataType, index - 1, index) > 0) { + std::swap(m_items[index - 1], m_items[index]); + index = index - 1; + } + } + } + // update the ordering rules + bool discendentOrder = isDiscendentOrder(); + DataType currentDataType = getCurrentOrderType(); + setOrderType(Name); + setIsDiscendentOrder(true); + sortByDataModel(currentDataType, discendentOrder); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setFolder(const TFilePath &fp, bool expandNode, + bool forceUpdate) { + if (fp == m_folder && !forceUpdate) return; + + // set the current folder path + m_folder = fp; + m_dayDateString = ""; + // set the folder name + if (fp == TFilePath()) + m_folderName->setText(""); + else + m_folderName->setText(toQString(fp)); + + refreshCurrentFolderItems(); + + if (!TFileStatus(fp).isLink()) + m_folderTreeView->setCurrentNode(fp, expandNode); +} + +//----------------------------------------------------------------------------- +/*! process when inputting the folder which is not regitered in the folder tree + (e.g. UNC path in Windows) + */ +void SceneBrowser::setUnregisteredFolder(const TFilePath &fp) { + if (fp != TFilePath()) { + TFileStatus fpStatus(fp); + // if the item is link, then set the link target of it + if (fpStatus.isLink()) { + QFileInfo info(toQString(fp)); + setFolder(TFilePath(info.symLinkTarget().toStdWString())); + return; + } + + // get both the folder & file list by readDirectory and + // readDirectory_Dir_ReadExe + TFilePathSet folders; + TFilePathSet files; + // for updating m_multiFileItemMap + TFilePathSet all_files; + + if (fpStatus.doesExist() && fpStatus.isDirectory() && + fpStatus.isReadable()) { + try { + TSystem::readDirectory(files, all_files, fp); + TSystem::readDirectory_Dir_ReadExe(folders, fp); + } catch (...) { + } + } + + TFilePathSet::iterator it; + + // register all folder items + for (it = folders.begin(); it != folders.end(); ++it) { + if (TFileStatus(*it).isLink()) + m_items.push_back(Item(*it, true, true)); + else + m_items.push_back(Item(*it, true, false)); + } + + for (it = files.begin(); it != files.end(); ++it) { + // skip the plt file (Palette file for TOONZ 4.6 and earlier) + if (it->getType() == "plt") continue; + + // filtering + else if (m_filter.isEmpty()) { + if (it->getType() != "tnz" && it->getType() != "scr" && + it->getType() != "tnzbat" && it->getType() != "mpath" && + it->getType() != "curve" && it->getType() != "tpl" && + TFileType::getInfo(*it) == TFileType::UNKNOW_FILE) + continue; + } else if (m_filter.contains(QString::fromStdString(it->getType()))) + continue; + + m_items.push_back(Item(*it)); + } + + // update the m_multiFileItemMap + m_multiFileItemMap.clear(); + for (it = all_files.begin(); it != all_files.end(); it++) { + TFilePath levelName(it->getLevelName()); + if (levelName.isLevelName()) { + Item &levelItem = m_multiFileItemMap[levelName]; + levelItem.m_frameIds.push_back(it->getFrame()); + levelItem.m_frameCount++; + } + } + } + // for all items in the folder, retrieve the file names(m_name) from the + // paths(m_path) + refreshData(); + + // update the ordering rules + bool discendentOrder = isDiscendentOrder(); + DataType currentDataType = getCurrentOrderType(); + setOrderType(Name); + setIsDiscendentOrder(true); + sortByDataModel(currentDataType, discendentOrder); + + m_itemViewer->repaint(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setHistoryDay(std::string dayDateString) { + m_folder = TFilePath(); + m_dayDateString = dayDateString; + const History::Day *day = History::instance()->getDay(dayDateString); + m_items.clear(); + if (day == 0) { + m_folderName->setText(""); + } else { + m_folderName->setText(QString::fromStdString(dayDateString)); + std::vector files; + day->getFiles(files); + std::vector::iterator it; + for (it = files.begin(); it != files.end(); ++it) + m_items.push_back(Item(*it)); + } + refreshData(); +} + +//----------------------------------------------------------------------------- +/*! for all items in the folder, retrieve the file names(m_name) from the + * paths(m_path) + */ +void SceneBrowser::refreshData() { + std::vector::iterator it; + for (it = m_items.begin(); it != m_items.end(); ++it) { + if (it->m_name == QString("")) + it->m_name = toQString(it->m_path.withoutParentDir()); + } +} + +//----------------------------------------------------------------------------- + +int SceneBrowser::getItemCount() const { return m_items.size(); } + +//----------------------------------------------------------------------------- + +void SceneBrowser::readInfo(Item &item) { + TFilePath fp = item.m_path; + QFileInfo info(toQString(fp)); + if (info.exists()) { + item.m_creationDate = info.created(); + item.m_modifiedDate = info.lastModified(); + item.m_fileType = info.suffix(); + item.m_fileSize = info.size(); + if (fp.getType() == "tnz") { + ToonzScene scene; + try { + item.m_frameCount = scene.loadFrameCount(fp); + } catch (...) { + } + } else + readFrameCount(item); + + item.m_validInfo = true; + } else if (fp.isLevelName()) // for levels johndoe..tif etc. + { + try { + // Find this level's item + std::map::iterator it = + m_multiFileItemMap.find(TFilePath(item.m_path.getLevelName())); + if (it == m_multiFileItemMap.end()) throw ""; + + item.m_creationDate = it->second.m_creationDate; + item.m_modifiedDate = it->second.m_modifiedDate; + item.m_fileType = it->second.m_fileType; + item.m_fileSize = it->second.m_fileSize; + item.m_frameCount = it->second.m_frameCount; + item.m_validInfo = true; + + // keep the list of frameIds at the first time and try to reuse it. + item.m_frameIds = it->second.m_frameIds; + + // The old way + /*TLevelReaderP lr(fp); +item.m_frameCount = lr->loadInfo()->getFrameCount(); +item.m_creationDate = QDateTime(); +item.m_modifiedDate = QDateTime(); +item.m_fileSize = -1; +item.m_validInfo = true;*/ + } catch (...) { + item.m_frameCount = 0; + } + } + + item.m_validInfo = true; +} + +//----------------------------------------------------------------------------- + +//! Frame count needs a special access function for viewable types - for they +//! are +//! calculated by using a dedicated thread and therefore cannot be simply +//! classified as *valid* or *invalid* infos... +void SceneBrowser::readFrameCount(Item &item) { + if (TFileType::isViewable(TFileType::getInfo(item.m_path))) { + if (isMultipleFrameType(item.m_path.getType())) + item.m_frameCount = m_frameCountReader.getFrameCount(item.m_path); + else + item.m_frameCount = 1; + } else + item.m_frameCount = 0; +} + +//----------------------------------------------------------------------------- + +QVariant SceneBrowser::getItemData(int index, DataType dataType, + bool isSelected) { + if (index < 0 || index >= (int)m_items.size()) return QVariant(); + Item &item = m_items[index]; + if (dataType == Name) { + // show two dots( ".." ) for the paret directory item + if (item.m_path == m_folder.getParentDir()) + return QString(".."); + else + return item.m_name; + } else if (dataType == Thumbnail) { + QSize iconSize = m_itemViewer->getPanel()->getIconSize(); + // parent folder icons + if (item.m_path == m_folder.getParentDir()) { + static QPixmap folderUpPixmap(svgToPixmap(":Resources/folderup_icon.svg", + iconSize, Qt::KeepAspectRatio)); + return folderUpPixmap; + } + // folder icons + else if (item.m_isFolder) { + if (item.m_isLink) { + static QPixmap linkIcon(svgToPixmap(":Resources/link_icon.svg", + iconSize, Qt::KeepAspectRatio)); + return linkIcon; + } else { + static QPixmap folderIcon(svgToPixmap(":Resources/folder_icon.svg", + iconSize, Qt::KeepAspectRatio)); + return folderIcon; + } + } + + QPixmap pixmap = IconGenerator::instance()->getIcon(item.m_path); + if (pixmap.isNull()) { + pixmap = QPixmap(iconSize); + pixmap.fill(Qt::white); + } + return scalePixmapKeepingAspectRatio(pixmap, iconSize, Qt::transparent); + } else if (dataType == Icon) + return QVariant(); + else if (dataType == ToolTip || dataType == FullPath) + return QString::fromStdWString(item.m_path.getWideString()); + + else if (dataType == IsFolder) { + return item.m_isFolder; + } + + if (!item.m_validInfo) { + readInfo(item); + if (!item.m_validInfo) return QVariant(); + } + + if (dataType == CreationDate) return item.m_creationDate; + if (dataType == ModifiedDate) return item.m_modifiedDate; + if (dataType == FileType) { + if (item.m_isLink) + return QString(""); + else if (item.m_isFolder) + return QString(""); + else + return QString::fromStdString(item.m_path.getType()).toUpper(); + } else if (dataType == FileSize) + return (item.m_fileSize == -1) ? QVariant() : item.m_fileSize; + else if (dataType == FrameCount) { + if (item.m_frameCount == -1) readFrameCount(item); + return item.m_frameCount; + } else if (dataType == PlayAvailable) { + std::string type = item.m_path.getType(); + if (item.m_frameCount > 1 && type != "tzp" && type != "tzu") return true; + return false; + } else if (dataType == VersionControlStatus) { + return getItemVersionControlStatus(item); + } else + return QVariant(); +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::isSceneItem(int index) const { + return 0 <= index && index < (int)m_items.size() && + m_items[index].m_path.getType() == "tnz"; +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::canRenameItem(int index) const { + // se sto guardando la history non posso rinominare nulla + if (getFolder() == TFilePath()) return false; + if (index < 0 || index >= (int)m_items.size()) return false; + // for now, disable rename for folders + if (m_items[index].m_isFolder) return false; + TFilePath fp = m_items[index].m_path; + return TFileStatus(fp).doesExist(); +} + +//----------------------------------------------------------------------------- + +int SceneBrowser::findIndexWithPath(TFilePath path) { + int i; + for (i = 0; i < m_items.size(); i++) + if (m_items[i].m_path == path) return i; + return -1; +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::renameItem(int index, const QString &newName) { + if (getFolder() == TFilePath()) return; + if (index < 0 || index >= (int)m_items.size()) return; + + TFilePath fp = m_items[index].m_path; + TFilePath newFp = fp; + if (renameFile(newFp, newName)) { + m_items[index].m_name = QString::fromStdWString(newFp.getLevelNameW()); + m_items[index].m_path = newFp; + + // ho rinominato anche la palette devo aggiornarla. + if (newFp.getType() == "tlv" || newFp.getType() == "tzp" || + newFp.getType() == "tzu") { + const char *type = (newFp.getType() == "tlv") ? "tpl" : "plt"; + int paletteIndex = findIndexWithPath(fp.withNoFrame().withType(type)); + if (paletteIndex >= 0) { + TFilePath palettePath = newFp.withNoFrame().withType(type); + m_items[paletteIndex].m_name = + QString::fromStdWString(palettePath.getLevelNameW()); + m_items[paletteIndex].m_path = palettePath; + } + } + m_itemViewer->update(); + + if (fp.getType() == "tnz") { + // ho cambiato il folder _files. Devo aggiornare il folder che lo contiene + // nel tree view + QModelIndex index = m_folderTreeView->currentIndex(); + if (index.isValid()) { + DvDirModel::instance()->refresh(index); + // m_folderTreeView->getDvDirModel()->refresh(index); + m_folderTreeView->update(); + } + } + } +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::renameFile(TFilePath &fp, QString newName) { + if (isSpaceString(newName)) return true; + + TFilePath newFp(newName.toStdWString()); + if (newFp.getType() != "" && newFp.getType() != fp.getType()) { + DVGui::error(tr("Can't change file extension")); + return false; + } + if (newFp.getType() == "") newFp = newFp.withType(fp.getType()); + if (newFp.getFrame() != TFrameId::EMPTY_FRAME && + newFp.getFrame() != TFrameId::NO_FRAME) { + DVGui::error(tr("Can't set a drawing number")); + return false; + } + if (newFp.getDots() != fp.getDots()) { + if (fp.getDots() == ".") + newFp = newFp.withNoFrame(); + else if (fp.getDots() == "..") + newFp = newFp.withFrame(TFrameId::EMPTY_FRAME); + } + newFp = newFp.withParentDir(fp.getParentDir()); + + // se sono uguali non devo rinominare nulla + if (newFp == fp) return false; + + if (TSystem::doesExistFileOrLevel(newFp)) { + DVGui::error(tr("Can't rename. File already exists: ") + toQString(newFp)); + return false; + } + + try { + TSystem::renameFileOrLevel_throw(newFp, fp, true); + IconGenerator::instance()->remove(fp); + if (fp.getType() == "tnz") { + /* TFilePath folder = fp.getParentDir() + (fp.getName() + "_files"); +TFilePath newFolder = newFp.getParentDir() + (newFp.getName() + "_files"); +TSystem::renameFile(newFolder, folder); +*/ + TFilePath sceneIconFp = ToonzScene::getIconPath(fp); + TFilePath sceneIconNewFp = ToonzScene::getIconPath(newFp); + if (TFileStatus(sceneIconFp).doesExist()) { + if (TFileStatus(sceneIconNewFp).doesExist()) + TSystem::deleteFile(sceneIconNewFp); + TSystem::renameFile(sceneIconNewFp, sceneIconFp); + } + } + + } catch (...) { + DVGui::error(tr("Couldn't rename ") + toQString(fp) + " to " + + toQString(newFp)); + return false; + } + + fp = newFp; + return true; +} + +//----------------------------------------------------------------------------- + +QMenu *SceneBrowser::getContextMenu(QWidget *parent, int index) { + auto isOldLevelType = [](TFilePath &path) -> bool { + return path.getType() == "tzp" || path.getType() == "tzu"; + }; + + bool ret = true; + + // TODO: spostare in questa classe anche la definizione delle azioni? + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return 0; + std::vector files; + fs->getSelectedFiles(files); + + QMenu *menu = new QMenu(parent); + CommandManager *cm = CommandManager::instance(); + + if (files.empty()) { + menu->addAction(cm->getAction(MI_ShowFolderContents)); + menu->addAction(cm->getAction(MI_SelectAll)); + if (!Preferences::instance()->isWatchFileSystemEnabled()) { + menu->addAction(cm->getAction(MI_RefreshTree)); + } + return menu; + } + + if (files.size() == 1 && files[0].getType() == "tnz") { + menu->addAction(cm->getAction(MI_LoadScene)); + } + + bool areResources = true; + bool areScenes = false; + int i, j; + for (i = 0; i < (int)files.size(); i++) { + TFileType::Type type = TFileType::getInfo(files[i]); + if (areResources && !TFileType::isResource(type)) areResources = false; + if (!areScenes && TFileType::isScene(type)) areScenes = true; + } + + bool areFullcolor = true; + for (i = 0; i < (int)files.size(); i++) { + TFileType::Type type = TFileType::getInfo(files[i]); + if (!TFileType::isFullColor(type)) { + areFullcolor = false; + break; + } + } + + TFilePath clickedFile; + if (0 <= index && index < (int)m_items.size()) + clickedFile = m_items[index].m_path; + + if (areResources) { + QString title; + if (clickedFile != TFilePath() && clickedFile.getType() == "tnz") + title = tr("Load As Sub-xsheet"); + else + title = tr("Load"); + QAction *action = new QAction(title, menu); + ret = ret && + connect(action, SIGNAL(triggered()), this, SLOT(loadResources())); + menu->addAction(action); + menu->addSeparator(); + } + + menu->addAction(cm->getAction(MI_DuplicateFile)); + if (!areScenes) { + menu->addAction(cm->getAction(MI_Copy)); + menu->addAction(cm->getAction(MI_Paste)); + } + menu->addAction(cm->getAction(MI_Clear)); + menu->addAction(cm->getAction(MI_ShowFolderContents)); + menu->addAction(cm->getAction(MI_SelectAll)); + menu->addAction(cm->getAction(MI_FileInfo)); + if (!clickedFile.isEmpty() && + (clickedFile.getType() == "tnz" || clickedFile.getType() == "tab")) { + menu->addSeparator(); + menu->addAction(cm->getAction(MI_AddToBatchRenderList)); + menu->addAction(cm->getAction(MI_AddToBatchCleanupList)); + } + + for (i = 0; i < files.size(); i++) + if (!TFileType::isViewable(TFileType::getInfo(files[i]))) break; + if (i == files.size()) { + std::string type = files[0].getType(); + for (j = 0; j < files.size(); j++) + if (isOldLevelType(files[j])) break; + if (j == files.size()) menu->addAction(cm->getAction(MI_ViewFile)); + + for (j = 0; j < files.size(); j++) { + if ((files[0].getType() == "pli" && files[j].getType() != "pli") || + (files[0].getType() != "pli" && files[j].getType() == "pli")) + break; + else if ((isOldLevelType(files[0]) && !isOldLevelType(files[j])) || + (!isOldLevelType(files[0]) && isOldLevelType(files[j]))) + break; + } + if (j == files.size()) { + menu->addAction(cm->getAction(MI_ConvertFiles)); + // iwsw commented out temporarily + // menu->addAction(cm->getAction(MI_ToonShadedImageToTLV)); + } + if (areFullcolor) menu->addAction(cm->getAction(MI_SeparateColors)); + + if (!areFullcolor) menu->addSeparator(); + } + if (files.size() == 1 && files[0].getType() != "tnz") { + QAction *action = new QAction(tr("Rename"), menu); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(renameAsToonzLevel())); + menu->addAction(action); + } +#ifdef LEVO + + if (files.size() == 2 && + (files[0].getType() == "tif" || files[0].getType() == "tiff" || + files[0].getType() == "png" || files[0].getType() == "TIF" || + files[0].getType() == "TIFF" || files[0].getType() == "PNG") && + (files[1].getType() == "tif" || files[1].getType() == "tiff" || + files[1].getType() == "png" || files[1].getType() == "TIF" || + files[1].getType() == "TIFF" || files[1].getType() == "PNG")) { + QAction *action = new QAction(tr("Convert to Painted TLV"), menu); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(convertToPaintedTlv())); + menu->addAction(action); + } + if (areFullcolor) { + QAction *action = new QAction(tr("Convert to Unpainted TLV"), menu); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(convertToUnpaintedTlv())); + menu->addAction(action); + menu->addSeparator(); + } +#endif + + if (!clickedFile.isEmpty() && (clickedFile.getType() == "tnz")) { + menu->addSeparator(); + menu->addAction(cm->getAction(MI_CollectAssets)); + menu->addAction(cm->getAction(MI_ImportScenes)); + menu->addAction(cm->getAction(MI_ExportScenes)); + } + + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (node) { + // Check Version Control Status + DvItemListModel::Status status = + (DvItemListModel::Status)m_itemViewer->getModel() + ->getItemData(index, DvItemListModel::VersionControlStatus) + .toInt(); + + // Remove the added actions + if (status == DvItemListModel::VC_Missing) menu->clear(); + + QMenu *vcMenu = new QMenu(tr("Version Control"), parent); + QAction *action; + + if (status == DvItemListModel::VC_ReadOnly || + (status == DvItemListModel::VC_ToUpdate && files.size() == 1)) { + if (status == DvItemListModel::VC_ReadOnly) { + action = vcMenu->addAction(tr("Edit")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(editVersionControl())); + + TFilePath path = files.at(0); + std::string fileType = path.getType(); + if (fileType == "tlv" || fileType == "pli" || path.getDots() == "..") { + action = vcMenu->addAction(tr("Edit Frame Range...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(editFrameRangeVersionControl())); + } + } else { + action = vcMenu->addAction(tr("Edit")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(updateAndEditVersionControl())); + } + } + + if (status == DvItemListModel::VC_Modified) { + action = vcMenu->addAction(tr("Put...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(putVersionControl())); + + action = vcMenu->addAction(tr("Revert")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(revertVersionControl())); + } + + if (status == DvItemListModel::VC_ReadOnly || + status == DvItemListModel::VC_ToUpdate) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + + if (status == DvItemListModel::VC_ReadOnly) { + action = vcMenu->addAction(tr("Delete")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(deleteVersionControl())); + } + + vcMenu->addSeparator(); + + if (files.size() == 1) { + action = vcMenu->addAction(tr("Get Revision...")); + TFilePath path = files.at(0); + if (path.getDots() == "..") + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionVersionControl())); + else + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionHistory())); + } else if (files.size() > 1) { + action = vcMenu->addAction("Get Revision..."); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionVersionControl())); + } + } + + if (status == DvItemListModel::VC_Edited) { + action = vcMenu->addAction(tr("Unlock")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(unlockVersionControl())); + } + + if (status == DvItemListModel::VC_Unversioned) { + action = vcMenu->addAction(tr("Put...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(putVersionControl())); + } + + if (status == DvItemListModel::VC_Locked && files.size() == 1) { + action = vcMenu->addAction(tr("Unlock")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(unlockVersionControl())); + + action = vcMenu->addAction(tr("Edit Info")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(showLockInformation())); + } + + if (status == DvItemListModel::VC_Missing) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + + if (files.size() == 1) { + vcMenu->addSeparator(); + action = vcMenu->addAction(tr("Revision History...")); + TFilePath path = files.at(0); + if (path.getDots() == "..") + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionVersionControl())); + else + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionHistory())); + } + } + + if (status == DvItemListModel::VC_PartialLocked) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + if (files.size() == 1) { + action = vcMenu->addAction(tr("Edit Frame Range...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(editFrameRangeVersionControl())); + + action = vcMenu->addAction(tr("Edit Info")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(showFrameRangeLockInfo())); + } + + } else if (status == DvItemListModel::VC_PartialEdited) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + + if (files.size() == 1) { + action = vcMenu->addAction(tr("Unlock Frame Range")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(unlockFrameRangeVersionControl())); + + action = vcMenu->addAction(tr("Edit Info")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(showFrameRangeLockInfo())); + } + } else if (status == DvItemListModel::VC_PartialModified) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + + if (files.size() == 1) { + action = vcMenu->addAction(tr("Put...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(putFrameRangeVersionControl())); + + action = vcMenu->addAction(tr("Revert")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(revertFrameRangeVersionControl())); + } + } + + if (!vcMenu->isEmpty()) { + menu->addSeparator(); + menu->addMenu(vcMenu); + } + } + + if (!Preferences::instance()->isWatchFileSystemEnabled()) { + menu->addSeparator(); + menu->addAction(cm->getAction(MI_RefreshTree)); + } + + assert(ret); + + return menu; +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::startDragDrop() { + TRepetitionGuard guard; + if (!guard.hasLock()) return; + + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + std::vector files; + fs->getSelectedFiles(files); + if (files.empty()) return; + + QList urls; + for (int i = 0; i < (int)files.size(); i++) { + if (TSystem::doesExistFileOrLevel(files[i])) + urls.append(QUrl::fromLocalFile( + QString::fromStdWString(files[i].getWideString()))); + } + if (urls.isEmpty()) return; + + QMimeData *mimeData = new QMimeData; + mimeData->setUrls(urls); + QDrag *drag = new QDrag(this); + QSize iconSize = m_itemViewer->getPanel()->getIconSize(); + QPixmap icon = IconGenerator::instance()->getIcon(files[0]); + QPixmap dropThumbnail = + scalePixmapKeepingAspectRatio(icon, iconSize, Qt::transparent); + if (!dropThumbnail.isNull()) drag->setPixmap(dropThumbnail); + drag->setMimeData(mimeData); + Qt::DropAction dropAction = drag->exec(Qt::CopyAction); +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::dropMimeData(QTreeWidgetItem *parent, int index, + const QMimeData *data, Qt::DropAction action) { + return false; +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onTreeFolderChanged() { + // Commented by KD + DvDirModelNode *node = m_folderTreeView->getCurrentNode(); + // if (node) + // node->visualizeContent(this); + // else + // setFolder(TFilePath()); + m_itemViewer->resetVerticalScrollBar(); + m_itemViewer->updateContentSize(); + m_itemViewer->getPanel()->update(); + m_frameCountReader.stopReading(); + IconGenerator::instance()->clearRequests(); + + DvDirModelFileFolderNode *fileFolderNode = + dynamic_cast(node); + if (fileFolderNode) emit treeFolderChanged(fileFolderNode->getPath()); + + // Restore scroll position + m_itemViewer->verticalScrollBar()->setValue(m_currentScroll); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::changeFolder(const QModelIndex &index) {} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onDataChanged(const QModelIndex &topLeft, + const QModelIndex &bottomRight) { + onTreeFolderChanged(); +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::acceptDrop(const QMimeData *data) const { + // se il browser non sta visualizzando un folder standard non posso accettare + // nessun drop + if (getFolder() == TFilePath()) return false; + + if (data->hasFormat("application/vnd.toonz.levels") || + data->hasFormat("application/vnd.toonz.currentscene") || + data->hasFormat("application/vnd.toonz.drawings") || + acceptResourceDrop(data->urls())) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::drop(const QMimeData *mimeData) { + // se il browser non sta visualizzando un folder standard non posso accettare + // nessun drop + TFilePath folderPath = getFolder(); + if (folderPath == TFilePath()) return false; + + if (mimeData->hasFormat(CastItems::getMimeFormat())) { + const CastItems *items = dynamic_cast(mimeData); + if (!items) return false; + + int i; + for (i = 0; i < items->getItemCount(); i++) { + CastItem *item = items->getItem(i); + if (TXshSimpleLevel *sl = item->getSimpleLevel()) { + TFilePath levelPath = sl->getPath().withParentDir(getFolder()); + IoCmd::saveLevel(levelPath, sl, false); + } else if (TXshSoundLevel *level = item->getSoundLevel()) { + TFilePath soundPath = level->getPath().withParentDir(getFolder()); + IoCmd::saveSound(soundPath, level, false); + } + } + refreshFolder(getFolder()); + return true; + } else if (mimeData->hasFormat("application/vnd.toonz.currentscene")) { + TFilePath scenePath; + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + if (scene->isUntitled()) { + bool ok; + QString sceneName = + QInputDialog::getText(this, tr("Save Scene"), tr("Scene name:"), + QLineEdit::Normal, QString(), &ok); + if (!ok || sceneName == "") return false; + scenePath = folderPath + sceneName.toStdWString(); + } else + scenePath = folderPath + scene->getSceneName(); + return IoCmd::saveScene(scenePath, false); + } else if (mimeData->hasFormat("application/vnd.toonz.drawings")) { + TFilmstripSelection *s = + dynamic_cast(TSelection::getCurrent()); + if (!s) return false; + TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel(); + if (!sl) return false; + + std::wstring levelName = sl->getName(); + folderPath += + TFilePath(levelName + ::to_wstring(sl->getPath().getDottedType())); + if (TSystem::doesExistFileOrLevel(folderPath)) { + QString question = "Level " + toQString(folderPath) + + " already exists\nDo you want to duplicate it?"; + int ret = DVGui::MsgBox(question, QObject::tr("Duplicate"), + QObject::tr("Don't Duplicate"), 0); + if (ret == 2 || ret == 0) return false; + TFilePath path = folderPath; + NameBuilder *nameBuilder = + NameBuilder::getBuilder(::to_wstring(path.getName())); + do levelName = nameBuilder->getNext(); + while (TSystem::doesExistFileOrLevel(path.withName(levelName))); + folderPath = path.withName(levelName); + } + assert(!TSystem::doesExistFileOrLevel(folderPath)); + + TXshSimpleLevel *newSl = new TXshSimpleLevel(); + newSl->setType(sl->getType()); + newSl->clonePropertiesFrom(sl); + newSl->setName(levelName); + newSl->setPalette(sl->getPalette()); + newSl->setScene(sl->getScene()); + std::set frames = s->getSelectedFids(); + for (auto const &fid : frames) { + newSl->setFrame(fid, sl->getFrame(fid, false)); + } + + IoCmd::saveLevel(folderPath, newSl, false); + refreshFolder(folderPath.getParentDir()); + return true; + } else if (mimeData->hasUrls()) { + int count = 0; + for (const QUrl &url : mimeData->urls()) { + TFilePath srcFp(url.toLocalFile().toStdWString()); + TFilePath dstFp = srcFp.withParentDir(folderPath); + if (dstFp != srcFp) { + if (!TSystem::copyFileOrLevel(dstFp, srcFp)) + DVGui::error(tr("There was an error copying %1 to %2") + .arg(toQString(srcFp)) + .arg(toQString(dstFp))); + } + } + refreshFolder(folderPath); + return true; + } else + return false; +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::loadResources() { + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + + std::vector filePaths; + fs->getSelectedFiles(filePaths); + + if (filePaths.empty()) return; + + IoCmd::LoadResourceArguments args; + args.resourceDatas.assign(filePaths.begin(), filePaths.end()); + + IoCmd::loadResources(args); +} + +//----------------------------------------------------------------------------- + +namespace { + +bool parsePathName(const QString &fullpath, QString &parentPath, QString &name, + QString &format) { + int index = fullpath.lastIndexOf('\\'); + if (index == -1) index = fullpath.lastIndexOf('/'); + + QString filename; + + if (index != -1) { + parentPath = fullpath.left(index + 1); + filename = fullpath.right(fullpath.size() - index - 1); + } else { + parentPath = ""; + filename = fullpath; + } + + index = filename.lastIndexOf('.'); + + if (index <= 0) return false; + + format = filename.right(filename.size() - index - 1); + if (format == "") return false; + + index--; + if (!filename.at(index).isDigit()) return false; + + while (index >= 0 && filename.at(index).isDigit()) index--; + + if (index < 0) return false; + + name = filename.left(index + 1); + + return true; +} + +//--------------------------------------------------------- + +void getLevelFiles(const QString &parentPath, const QString &name, + const QString &format, QStringList &pathIn) { + QString dummy, dummy1, filter = "*." + format; + QDir dir(parentPath, filter); + QStringList list = dir.entryList(); + + for (int i = 0; i < list.size(); i++) { + QString item = list.at(i); + QString itemName; + if (!parsePathName(item, dummy, itemName, dummy1) || name != itemName) + continue; + + pathIn.push_back(item); + } +} + +//--------------------------------------------------------- + +QString getFrame(const QString &filename) { + int index = filename.lastIndexOf('.'); + + if (index <= 0) return ""; + + index--; + if (!filename.at(index).isDigit()) return ""; + + int to, from; + to = from = index; + while (from >= 0 && filename.at(from).isDigit()) from--; + + if (from < 0) return ""; + + char padStr[5]; + padStr[4] = '\0'; + + int i, frame = 0; + + QString number = filename.mid(from + 1, to - from); + for (i = 0; i < 4 - number.size(); i++) padStr[i] = '0'; + for (i = 0; i < number.size(); i++) + padStr[4 - number.size() + i] = number.at(i).toLatin1(); + return QString(padStr); +} + +//------------------------------------------------------------------ + +//----------------------------------------------------------- + +void renameSingleFileOrToonzLevel(const QString &fullpath) { + TFilePath fpin(fullpath.toStdString()); + + RenameAsToonzPopup popup( + QString::fromStdWString(fpin.withoutParentDir().getWideString())); + if (popup.exec() != QDialog::Accepted) return; + + std::string name = popup.getName().toStdString(); + + if (name == fpin.getName()) { + DVGui::error(QString( + QObject::tr("The specified name is already assigned to the %1 file.") + .arg(fullpath))); + return; + } + + if (popup.doOverwrite()) + TSystem::renameFileOrLevel(fpin.withName(name), fpin, true); + else + TSystem::copyFileOrLevel(fpin.withName(name), fpin); +} + +//---------------------------------------------------------- + +void doRenameAsToonzLevel(const QString &fullpath) { + QString parentPath, name, format; + + if (!parsePathName(fullpath, parentPath, name, format)) { + renameSingleFileOrToonzLevel(fullpath); + return; + } + + QStringList pathIn; + + getLevelFiles(parentPath, name, format, pathIn); + + if (pathIn.empty()) return; + + while (name.endsWith('_') || name.endsWith('.') || name.endsWith(' ')) + name.chop(1); + + RenameAsToonzPopup popup(name, pathIn.size()); + if (popup.exec() != QDialog::Accepted) return; + + name = popup.getName(); + + QString levelOutStr = parentPath + "/" + name + ".." + format; + + TFilePath levelOut(levelOutStr.toStdWString()); + if (TSystem::doesExistFileOrLevel(levelOut)) { + QApplication::restoreOverrideCursor(); + int ret = DVGui::MsgBox( + QObject::tr("Warning: level %1 already exists; overwrite?") + .arg(toQString(levelOut)), + QObject::tr("Yes"), QObject::tr("No"), 1); + QApplication::setOverrideCursor(Qt::WaitCursor); + if (ret == 2 || ret == 0) return; + TSystem::removeFileOrLevel(levelOut); + } + + int i; + for (i = 0; i < pathIn.size(); i++) { + QString padStr = getFrame(pathIn[i]); + if (padStr == "") continue; + QString pathOut = parentPath + "/" + name + "." + padStr + "." + format; + + if (popup.doOverwrite()) { + if (!QFile::rename(parentPath + "/" + pathIn[i], pathOut)) { + QString tmp(parentPath + "/" + pathIn[i]); + DVGui::error(QString( + QObject::tr("It is not possible to rename the %1 file.").arg(tmp))); + return; + } + } else if (!QFile::copy(parentPath + "/" + pathIn[i], pathOut)) { + QString tmp(parentPath + "/" + pathIn[i]); + DVGui::error(QString( + QObject::tr("It is not possible to copy the %1 file.").arg(tmp))); + + return; + } + } +} + +} // namespace + +//------------------------------------------------------------------------------- + +void SceneBrowser::renameAsToonzLevel() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.size() != 1) return; + + doRenameAsToonzLevel(QString::fromStdWString(filePaths[0].getWideString())); + + QApplication::restoreOverrideCursor(); + + FileBrowser::refreshFolder(filePaths[0].getParentDir()); +} + +#ifdef LEVO + +void SceneBrowser::convertToUnpaintedTlv() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + + QStringList sl; + sl << "Apply Autoclose " + << "Don't Apply Autoclose "; + bool ok; + QString autoclose = QInputDialog::getItem( + this, tr("Convert To Unpainted Tlv"), "", sl, 0, false, &ok); + if (!ok) return; + + QApplication::setOverrideCursor(Qt::WaitCursor); + + int i, totFrames = 0; + std::vector converters; + for (i = 0; i < filePaths.size(); i++) { + Convert2Tlv *converter = + new Convert2Tlv(filePaths[i], TFilePath(), TFilePath(), -1, -1, + autoclose == sl.at(0), TFilePath(), 0, 0, 0); + + if (TSystem::doesExistFileOrLevel(converter->m_levelOut)) { + QApplication::restoreOverrideCursor(); + int ret = DVGui::MsgBox(tr("Warning: level %1 already exists; overwrite?") + .arg(toQString(converter->m_levelOut)), + tr("Yes"), tr("No"), 1); + QApplication::setOverrideCursor(Qt::WaitCursor); + if (ret == 2 || ret == 0) { + delete converter; + continue; + } + TSystem::removeFileOrLevel(converter->m_levelOut); + } + + totFrames += converter->getFramesToConvertCount(); + converters.push_back(converter); + } + + if (converters.empty()) { + QApplication::restoreOverrideCursor(); + return; + } + + ProgressDialog pb("", "Cancel", 0, totFrames); + int j, l, k = 0; + for (i = 0; i < converters.size(); i++) { + std::string errorMessage; + if (!converters[i]->init(errorMessage)) { + converters[i]->abort(); + DVGui::error(QString::fromStdString(errorMessage)); + delete converters[i]; + converters[i] = 0; + continue; + } + + int count = converters[i]->getFramesToConvertCount(); + + pb.setLabelText("Generating level " + toQString(converters[i]->m_levelOut)); + pb.show(); + + for (j = 0; j < count; j++) { + std::string errorMessage = ""; + if (!converters[i]->convertNext(errorMessage) || pb.wasCanceled()) { + for (l = i; l < converters.size(); l++) { + converters[l]->abort(); + delete converters[i]; + converters[i] = 0; + } + if (errorMessage != "") + DVGui::error(QString::fromStdString(errorMessage)); + QApplication::restoreOverrideCursor(); + SceneBrowser::refreshFolder(filePaths[0].getParentDir()); + return; + } + pb.setValue(++k); + } + TFilePath levelOut(converters[i]->m_levelOut); + delete converters[i]; + IconGenerator::instance()->invalidate(levelOut); + + converters[i] = 0; + } + + QApplication::restoreOverrideCursor(); + pb.hide(); + DVGui::info(tr("Done: All Levels converted to TLV Format")); + + SceneBrowser::refreshFolder(filePaths[0].getParentDir()); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::convertToPaintedTlv() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + + if (filePaths.size() != 2) return; + + QStringList sl; + sl << "Apply Autoclose " + << "Don't Apply Autoclose "; + bool ok; + QString autoclose = QInputDialog::getItem(this, tr("Convert To Painted Tlv"), + "", sl, 0, false, &ok); + if (!ok) return; + + QApplication::setOverrideCursor(Qt::WaitCursor); + + Convert2Tlv *converter = + new Convert2Tlv(filePaths[0], filePaths[1], TFilePath(), -1, -1, + autoclose == sl.at(0), TFilePath(), 0, 0, 0); + + if (TSystem::doesExistFileOrLevel(converter->m_levelOut)) { + QApplication::restoreOverrideCursor(); + int ret = DVGui::MsgBox(tr("Warning: level %1 already exists; overwrite?") + .arg(toQString(converter->m_levelOut)), + tr("Yes"), tr("No"), 1); + QApplication::setOverrideCursor(Qt::WaitCursor); + if (ret == 2 || ret == 0) { + QApplication::restoreOverrideCursor(); + return; + } + TSystem::removeFileOrLevel(converter->m_levelOut); + } + + std::string errorMessage; + if (!converter->init(errorMessage)) { + converter->abort(); + delete converter; + DVGui::error(QString::fromStdString(errorMessage)); + QApplication::restoreOverrideCursor(); + return; + } + int count = converter->getFramesToConvertCount(); + + ProgressDialog pb("Generating level " + toQString(converter->m_levelOut), + "Cancel", 0, count); + pb.show(); + + for (int i = 0; i < count; i++) { + errorMessage = ""; + if (!converter->convertNext(errorMessage) || pb.wasCanceled()) { + converter->abort(); + delete converter; + if (errorMessage != "") + DVGui::error(QString::fromStdString(errorMessage)); + QApplication::restoreOverrideCursor(); + SceneBrowser::refreshFolder(filePaths[0].getParentDir()); + return; + } + + pb.setValue(i + 1); + } + + TFilePath levelOut(converter->m_levelOut); + delete converter; + IconGenerator::instance()->invalidate(levelOut); + + QApplication::restoreOverrideCursor(); + pb.hide(); + DVGui::info(tr("Done: 2 Levels converted to TLV Format")); + + fs->selectNone(); + SceneBrowser::refreshFolder(filePaths[0].getParentDir()); +} +#endif + +//----------------------------------------------------------------------------- + +void SceneBrowser::onSceneSwitched() { + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + TFilePath scenesFolder = scene->getScenePath().getParentDir(); + // TFilePath scenesFolder = + // TProjectManager::instance()->getCurrentProject()->getScenesPath(); + setFolder(scenesFolder, true); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onSelectedItems(const std::set &indexes) { + std::set filePaths; + std::set::const_iterator it; + + // pass the frameId list for reuse + std::list> frameIDs; + + if (indexes.empty()) { // inform selection is released + emit filePathsSelected(filePaths, frameIDs); + return; + } + + for (it = indexes.begin(); it != indexes.end(); ++it) { + filePaths.insert(m_items[*it].m_path); + frameIDs.insert(frameIDs.begin(), m_items[*it].m_frameIds); + } + + // reuse the list of TFrameId in order to skip loadInfo() when loading the + // level with sequencial frames. + emit filePathsSelected(filePaths, frameIDs); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onClickedItem(int index) { + if (0 <= index && index < (int)m_items.size()) { + // if the folder is clicked, then move the current folder + TFilePath fp = m_items[index].m_path; + if (m_items[index].m_isFolder) { + setFolder(fp, true); + QModelIndex index = m_folderTreeView->currentIndex(); + if (index.isValid()) m_folderTreeView->scrollTo(index); + } else + emit filePathClicked(fp); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onDoubleClickedItem(int index) { + // TODO: Avoid duplicate code with onClickedItem(). + if (0 <= index && index < (int)m_items.size()) { + // if the folder is clicked, then move the current folder + TFilePath fp = m_items[index].m_path; + if (m_items[index].m_isFolder) { + setFolder(fp, true); + QModelIndex index = m_folderTreeView->currentIndex(); + if (index.isValid()) m_folderTreeView->scrollTo(index); + } else + emit filePathDoubleClicked(fp); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::refreshFolder(const TFilePath &folderPath) { + std::set::iterator it; + for (it = activePreproductionBoards.begin(); + it != activePreproductionBoards.end(); ++it) { + SceneBrowser *browser = *it; + DvDirModel::instance()->refreshFolder(folderPath); + if (browser->getFolder() == folderPath) { + browser->setFolder(folderPath, false, true); + } + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::updateItemViewerPanel() { + std::set::iterator it; + for (it = activePreproductionBoards.begin(); + it != activePreproductionBoards.end(); ++it) { + SceneBrowser *browser = *it; + browser->m_itemViewer->getPanel()->update(); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::getExpandedFolders(DvDirModelNode *node, + QList &expandedNodes) { + if (!node) return; + QModelIndex newIndex = DvDirModel::instance()->getIndexByNode(node); + if (!m_folderTreeView->isExpanded(newIndex)) return; + expandedNodes.push_back(node); + + int i = 0; + for (i = 0; i < node->getChildCount(); i++) + getExpandedFolders(node->getChild(i), expandedNodes); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::refresh() { + TFilePath originalFolder( + m_folder); // setFolder is invoked by Qt throughout the following... + + int dx = m_folderTreeView->verticalScrollBar()->value(); + DvDirModelNode *rootNode = DvDirModel::instance()->getNode(QModelIndex()); + + QModelIndex index = DvDirModel::instance()->getIndexByNode(rootNode); + + bool vcEnabled = m_folderTreeView->refreshVersionControlEnabled(); + + m_folderTreeView->setRefreshVersionControlEnabled(false); + DvDirModel::instance()->refreshFolderChild(index); + m_folderTreeView->setRefreshVersionControlEnabled(vcEnabled); + + QList expandedNodes; + int i; + for (i = 0; i < rootNode->getChildCount(); i++) + getExpandedFolders(rootNode->getChild(i), expandedNodes); + + for (i = 0; i < expandedNodes.size(); i++) { + DvDirModelNode *node = expandedNodes[i]; + if (!node || !node->hasChildren()) continue; + QModelIndex ind = DvDirModel::instance()->getIndexByNode(node); + if (!ind.isValid()) continue; + m_folderTreeView->expand(ind); + } + m_folderTreeView->verticalScrollBar()->setValue(dx); + + setFolder(originalFolder, false, true); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::newScene() { + m_currentScroll = m_itemViewer->verticalScrollBar()->value(); + + TFilePath parentFolder = getFolder(); + QString sceneName; + TFilePath scenePath; + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + if (scene->isUntitled()) { + bool ok; + sceneName = QInputDialog::getText(this, tr("Save Scene"), tr("Scene name:"), + QLineEdit::Normal, QString(), &ok); + if (!ok || sceneName == "") return; + } else { + sceneName = QString::fromWCharArray(scene->getSceneName().c_str()); + } + QString prefix; + QString number; + for (int j = 0; j < sceneName.length(); j++) { + QChar c; + c = sceneName.at(sceneName.length() - 1 - j); + if (c.isDigit()) { + number = QString(c) + number; + } else { + prefix = sceneName; + prefix.truncate(sceneName.length() - j); + break; + } + } + if (number.length() == 0) { + // prefix+="-"; + number = "000"; + } + int i = number.toInt(); + do { + QString number_ext = + QStringLiteral("%1").arg(++i, number.length(), 10, QLatin1Char('0')); + scenePath = parentFolder + + (prefix.toStdWString() + number_ext.toStdWString() + L".tnz"); + } while (TFileStatus(scenePath).doesExist()); + + if (!IoCmd::saveSceneIfNeeded(QObject::tr("Change project"))) return; + IoCmd::newScene(); + IoCmd::saveScene(scenePath, false); + return; + + if (parentFolder == TFilePath() || !TFileStatus(parentFolder).isDirectory()) + return; + QString tempName(tr("New Folder")); + std::wstring folderName = tempName.toStdWString(); + TFilePath folderPath = parentFolder + folderName; + // int i = 1; + while (TFileStatus(folderPath).doesExist()) + folderPath = parentFolder + (folderName + L" " + std::to_wstring(++i)); + + try { + TSystem::mkDir(folderPath); + + } catch (...) { + DVGui::error(tr("It is not possible to create the %1 folder.") + .arg(toQString(folderPath))); + return; + } + + DvDirModel *model = DvDirModel::instance(); + + QModelIndex parentFolderIndex = m_folderTreeView->currentIndex(); + model->refresh(parentFolderIndex); + m_folderTreeView->expand(parentFolderIndex); + + std::wstring newFolderName = folderPath.getWideName(); + QModelIndex newFolderIndex = + model->childByName(parentFolderIndex, newFolderName); + if (newFolderIndex.isValid()) { + m_folderTreeView->setCurrentIndex(newFolderIndex); + m_folderTreeView->scrollTo(newFolderIndex); + m_folderTreeView->QAbstractItemView::edit(newFolderIndex); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::showEvent(QShowEvent *) { + activePreproductionBoards.insert(this); + // refresh + if (getFolder() != TFilePath()) + setFolder(getFolder(), false, true); + else if (getDayDateString() != "") + setHistoryDay(getDayDateString()); + m_folderTreeView->scrollTo(m_folderTreeView->currentIndex()); + + // Refresh SVN + DvDirVersionControlNode *vcNode = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (vcNode) m_folderTreeView->refreshVersionControl(vcNode); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::hideEvent(QHideEvent *) { + activePreproductionBoards.erase(this); + m_itemViewer->getPanel()->getItemViewPlayDelegate()->resetPlayWidget(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::makeCurrentProjectVisible() {} + +//----------------------------------------------------------------------------- + +void SceneBrowser::enableGlobalSelection(bool enabled) { + m_folderTreeView->enableGlobalSelection(enabled); + m_itemViewer->enableGlobalSelection(enabled); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::selectNone() { m_itemViewer->selectNone(); } + +//----------------------------------------------------------------------------- + +void SceneBrowser::enableDoubleClickToOpenScenes() { + // perhaps this should disconnect existing signal handlers first + connect(this, SIGNAL(filePathDoubleClicked(const TFilePath &)), this, + SLOT(tryToOpenScene(const TFilePath &))); +} + +void SceneBrowser::enableSingleClickToOpenScenes() { + // perhaps this should disconnect existing signal handlers first + connect(this, SIGNAL(filePathClicked(const TFilePath &)), this, + SLOT(tryToOpenScene(const TFilePath &))); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::tryToOpenScene(const TFilePath &filePath) { + if (filePath.getType() == "tnz") { + IoCmd::loadScene(filePath); + } +} + +//============================================================================= + +OpenFloatingPanel openPreproductionBoardPane( + MI_OpenPreproductionBoard, "PreproductionBoard", + QObject::tr("Preproduction Board")); diff --git a/toonz/sources/toonz/scenebrowser.h b/toonz/sources/toonz/scenebrowser.h new file mode 100644 index 00000000..0e51a43a --- /dev/null +++ b/toonz/sources/toonz/scenebrowser.h @@ -0,0 +1,260 @@ +#pragma once + +#ifndef SCENEBROWSER_INCLUDED +#define SCENEBROWSER_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include "dvitemview.h" +#include "tfilepath.h" +#include "toonzqt/dvdialog.h" +#include "versioncontrol.h" +#include "filebrowser.h" + +#include "tthread.h" + +class QLineEdit; +class QTreeWidgetItem; +class QSplitter; +class DvDirModelNode; +class DvDirTreeView; +class QFileSystemWatcher; + +//----------------------------------------------------------------------------- +class SceneBrowserButtonBar final : public QToolBar { + Q_OBJECT + QAction *m_folderBack; + QAction *m_folderFwd; + +public: + SceneBrowserButtonBar(DvItemViewer *itemViewer, QWidget *parent = 0); + +signals: + void newScene(); +}; +//----------------------------------------------------------------------------- + +class SceneBrowser final : public QFrame, public DvItemListModel { + Q_OBJECT + +public: + SceneBrowser(QWidget *parent, Qt::WindowFlags flags = 0, + bool noContextMenu = false, bool multiSelectionEnabled = false); + ~SceneBrowser(); + + void sortByDataModel(DataType dataType, bool isDiscendent) override; + void refreshData() override; + + int getItemCount() const override; + QVariant getItemData(int index, DataType dataType, + bool isSelected = false) override; + + bool canRenameItem(int index) const override; + void renameItem(int index, const QString &newName) override; + + bool isSceneItem(int index) const override; + void startDragDrop() override; + QMenu *getContextMenu(QWidget *parent, int index) override; + + /*! +This functions adds to the types to be filtered a new type; +if this function is never called, the default filter is all image +files and scene files and palette files +*/ + void addFilterType(const QString &type); + + /*! +The setFilterTypes function directly specifies the list of file +types to be displayed in the file browser. +*/ + void setFilterTypes(const QStringList &types); + const QStringList &getFilterTypes() const { return m_filter; } + void removeFilterType(const QString &type); + + void setFolder(const TFilePath &fp, bool expandNode = false, + bool forceUpdate = false); + // process when inputting the folder which is not regitered in the folder tree + // (e.g. UNC path in Windows) + void setUnregisteredFolder(const TFilePath &fp); + + void setHistoryDay(std::string dayDateString); + + TFilePath getFolder() const { return m_folder; } + std::string getDayDateString() const { return m_dayDateString; } + + static void refreshFolder(const TFilePath &folder); + + static void updateItemViewerPanel(); + + // ritorna true se il file e' stato rinominato. dopo la chiamata fp contiene + // il nuovo path + static bool renameFile(TFilePath &fp, QString newName); + + void makeCurrentProjectVisible(); + + void enableGlobalSelection(bool enabled); + void selectNone(); + + QSplitter *getMainSplitter() const { return m_mainSplitter; } + + // Enable double-click to open a scene. + // This is not always desirable (e.g. if a user double-clicks on a file in + // a "Save As" dialog, they expect the file will be saved to, not opened). + // So it is disabled by default. + void enableDoubleClickToOpenScenes(); + + void enableSingleClickToOpenScenes(); + +protected: + int findIndexWithPath(TFilePath path); + void getExpandedFolders(DvDirModelNode *node, + QList &expandedNodes); + + bool dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, + Qt::DropAction action); + + bool acceptDrop(const QMimeData *data) const override; + bool drop(const QMimeData *data) override; + void showEvent(QShowEvent *) override; + void hideEvent(QHideEvent *) override; + + // Fill the QStringList with files selected in the browser, auxiliary files + // (palette for tlv, hooks, sceneIcons) + // retrieve also the path, and return also the sceneIconsCount + void setupVersionControlCommand(QStringList &files, QString &path, + int &sceneIconsCount); + void setupVersionControlCommand(QString &file, QString &path); + + void refreshHistoryButtons(); + +public slots: + + void onTreeFolderChanged(); + +protected slots: + + void refresh(); + + void changeFolder(const QModelIndex &index); + void onDataChanged(const QModelIndex &topLeft, + const QModelIndex &bottomRight); + void loadResources(); + void onClickedItem(int index); + void onDoubleClickedItem(int index); + void onSelectedItems(const std::set &indexes); + void onSceneSwitched(); + void newScene(); + + void onBackButtonPushed(); + void onFwdButtonPushed(); + void onFolderEdited(); + void storeFolderHistory(); + void clearHistory(); + + void renameAsToonzLevel(); + void updateAndEditVersionControl(); + void editVersionControl(); + void unlockVersionControl(); + + void editFrameRangeVersionControl(); + void unlockFrameRangeVersionControl(); + + void putFrameRangeVersionControl(); + void revertFrameRangeVersionControl(); + + void showLockInformation(); + void showFrameRangeLockInfo(); + + void putVersionControl(); + void revertVersionControl(); + void deleteVersionControl(); + void getVersionControl(); + void getRevisionVersionControl(); + void getRevisionHistory(); + + void onVersionControlCommandDone(const QStringList &files); + + void onFileSystemChanged(const QString &folderPath); + + // If filePath is a valid scene file, open it. Otherwise do nothing. + void tryToOpenScene(const TFilePath &filePath); + +signals: + + void filePathClicked(const TFilePath &); + void filePathDoubleClicked(const TFilePath &); + // reuse the list of TFrameId in order to skip loadInfo() when loading the + // level with sequencial frames. + void filePathsSelected(const std::set &, + const std::list> &); + void treeFolderChanged(const TFilePath &); + + // for activating/deactivating the folder history buttons( back button & + // forward button ) + void historyChanged(bool, bool); + +private: + struct Item { + QString m_name; + qlonglong m_fileSize; + QDateTime m_creationDate; + QDateTime m_modifiedDate; + int m_frameCount; + QString m_fileType; + TFilePath m_path; + bool m_validInfo; + + bool m_isFolder; + bool m_isLink; + // calling loadInfo to the level with sequencial frames is time consuming. + // so keep the list of frameIds at the first time and try to reuse it. + std::vector m_frameIds; + + Item() : m_frameCount(0), m_validInfo(false), m_fileSize(0) {} + Item(const TFilePath &path, bool folder = false, bool link = false, + QString name = QString("")) + : m_path(path) + , m_frameCount(0) + , m_validInfo(false) + , m_fileSize(0) + , m_isFolder(folder) + , m_isLink(link) + , m_name(name) {} + }; + +private: + DvDirTreeView *m_folderTreeView; + QSplitter *m_mainSplitter; + QLineEdit *m_folderName; + DvItemViewer *m_itemViewer; + FrameCountReader m_frameCountReader; + + // folder history + QList m_indexHistoryList; + int m_currentPosition; + int m_currentScroll; + + std::vector m_items; + TFilePath m_folder; + std::string m_dayDateString; + QStringList m_filter; + std::map m_multiFileItemMap; + +private: + void readFrameCount(Item &item); + void readInfo(Item &item); + + void refreshCurrentFolderItems(); + + DvItemListModel::Status getItemVersionControlStatus( + const SceneBrowser::Item &item); +}; + +//----------------------------------------------------------- + +#endif diff --git a/toonz/sources/toonz/scenebrowserversioncontrol.cpp b/toonz/sources/toonz/scenebrowserversioncontrol.cpp new file mode 100644 index 00000000..f21c8921 --- /dev/null +++ b/toonz/sources/toonz/scenebrowserversioncontrol.cpp @@ -0,0 +1,594 @@ + + +#include "scenebrowser.h" +#include "filebrowsermodel.h" +#include "versioncontrolgui.h" +#include "versioncontroltimeline.h" +#include "fileselection.h" +#include "dvdirtreeview.h" +#include "tsystem.h" +#include "tapp.h" + +#include "toonz/tscenehandle.h" +#include "toonz/txshsimplelevel.h" +#include "toonz/toonzscene.h" +#include "toonz/levelset.h" +#include "toonzqt/gutil.h" +#include "toonzqt/icongenerator.h" + +namespace { +//--------------------------------------------------------------------------- + +QStringList getLevelFileNames(TFilePath path) { + TFilePath dir = path.getParentDir(); + QDir qDir(QString::fromStdWString(dir.getWideString())); + QString levelName = + QRegExp::escape(QString::fromStdWString(path.getWideName())); + QString levelType = QString::fromStdString(path.getType()); + QString exp(levelName + ".[0-9]{1,4}." + levelType); + QRegExp regExp(exp); + QStringList list = qDir.entryList(QDir::Files); + return list.filter(regExp); +} +} // namespace + +//----------------------------------------------------------------------------- + +DvItemListModel::Status SceneBrowser::getItemVersionControlStatus( + const SceneBrowser::Item &item) { + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + return m_folderTreeView->getItemVersionControlStatus(node, item.m_path); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setupVersionControlCommand(QString &file, QString &path) { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.empty()) return; + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + DvDirVersionControlRootNode *rootNode = node->getVersionControlRootNode(); + + VersionControl *vc = VersionControl::instance(); + if (rootNode) { + vc->setUserName(QString::fromStdWString(rootNode->getUserName())); + vc->setPassword(QString::fromStdWString(rootNode->getPassword())); + } + + path = toQString(node->getPath()); + file = toQString(filePaths[0].withoutParentDir()); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setupVersionControlCommand(QStringList &files, QString &path, + int &sceneIconsCount) { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.empty()) return; + + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + int fileCount = filePaths.size(); + for (int i = 0; i < fileCount; i++) { + TFilePath fp = filePaths[i]; + if (fp.getDots() == "..") { + QStringList levelNames = getLevelFileNames(fp); + + if (levelNames.isEmpty()) { + QString levelName = + QRegExp::escape(QString::fromStdWString(fp.getWideName())); + QString levelType = QString::fromStdString(fp.getType()); + QString exp(levelName + ".[0-9]{1,4}." + levelType); + QRegExp regExp(exp); + levelNames = node->getMissingFiles(regExp); + } + + int count = levelNames.size(); + for (int i = 0; i < count; i++) files.append(levelNames.at(i)); + } else { + QFileInfo fi(toQString(fp)); + files.append(fi.fileName()); + } + + // Add also auxiliary files + if (fp.getDots() == ".." || fp.getType() == "tlv" || + fp.getType() == "pli") { + TFilePathSet fpset; + TXshSimpleLevel::getFiles(fp, fpset); + + TFilePathSet::iterator it; + for (it = fpset.begin(); it != fpset.end(); ++it) + files.append(QFileInfo(toQString(*it)).fileName()); + } + // Add sceneIcon (only for scene files) + else if (fp.getType() == "tnz") { + TFilePath iconPath = ToonzScene::getIconPath(fp); + if (TFileStatus(iconPath).doesExist()) { + QDir dir(toQString(fp.getParentDir())); + +#ifdef MACOSX + files.append(dir.relativeFilePath(toQString(iconPath))); +#else + files.append( + dir.relativeFilePath(toQString(iconPath)).replace("/", "\\")); +#endif + sceneIconsCount++; + } + } + } + + DvDirVersionControlRootNode *rootNode = node->getVersionControlRootNode(); + + if (rootNode) { + VersionControl *vc = VersionControl::instance(); + vc->setUserName(QString::fromStdWString(rootNode->getUserName())); + vc->setPassword(QString::fromStdWString(rootNode->getPassword())); + } + + path = toQString(node->getPath()); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::editVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + + VersionControl *vc = VersionControl::instance(); + + bool hasCurrentSceneFile = false; + + // Check the scene file + TFilePath fp = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getScenePath() + .withoutParentDir(); + if (files.contains(toQString(fp))) hasCurrentSceneFile = true; + + // Check the scene resource files + if (!hasCurrentSceneFile) { + QStringList currentSceneContents = vc->getCurrentSceneContents(); + int fileSize = files.size(); + for (int i = 0; i < fileSize; i++) { +#ifdef MACOSX + QString fp = path + "/" + files.at(i); +#else + QString fp = path + "\\" + files.at(i); +#endif + if (currentSceneContents.contains(fp)) { + hasCurrentSceneFile = true; + break; + } + } + } + + if (hasCurrentSceneFile) { + DVGui::warning( + tr("Some files that you want to edit are currently opened. Close them " + "first.")); + return; + } + + vc->lock(this, path, files, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::editFrameRangeVersionControl() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + TFilePath fp(TFilePath(path.toStdWString()) + file.toStdWString()); + if (fp.getDots() == "..") { + QStringList list = getLevelFileNames(fp); + VersionControl::instance()->lockFrameRange(this, path, list); + } else { + const std::set &indices = m_itemViewer->getSelectedIndices(); + int frameCount = + m_itemViewer->getModel() + ->getItemData(*indices.begin(), DvItemListModel::FrameCount) + .toInt(); + + VersionControl::instance()->lockFrameRange(this, path, file, frameCount); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::unlockFrameRangeVersionControl() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + TFilePath fp(TFilePath(path.toStdWString()) + file.toStdWString()); + if (fp.getDots() == "..") { + QStringList list = getLevelFileNames(fp); + VersionControl::instance()->unlockFrameRange(this, path, list); + } else + VersionControl::instance()->unlockFrameRange(this, path, file); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::putFrameRangeVersionControl() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + VersionControl::instance()->commitFrameRange(this, path, file); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::revertFrameRangeVersionControl() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + TFilePath fp = TFilePath(path.toStdWString()) + file.toStdWString(); + if (fp.getDots() != "..") { + SVNStatus s = node->getVersionControlStatus(file); + + QString from = QString::number(s.m_editFrom); + QString to = QString::number(s.m_editTo); + QString userName = VersionControl::instance()->getUserName(); + QString hostName = TSystem::getHostName(); + + TFilePath fp(s.m_path.toStdWString()); + QString tempFileName = QString::fromStdWString(fp.getWideName()) + "_" + + userName + "_" + hostName + "_" + from + "-" + to + + "." + QString::fromStdString(fp.getType()); + + TFilePath fullPath = node->getPath() + tempFileName.toStdWString(); + + if (TFileStatus(fullPath).doesExist()) + VersionControl::instance()->revertFrameRange(this, path, file, + toQString(fullPath)); + } else { + // Use the hook file, if exist, as a tempFileName + QString tempFile; + + QDir dir(path); + dir.setNameFilters(QStringList("*.xml")); + QStringList list = dir.entryList(QDir::Files | QDir::Hidden); + int listCount = list.size(); + + if (listCount > 0) { + QString prefix = QString::fromStdWString(fp.getWideName()) + "_" + + VersionControl::instance()->getUserName() + "_" + + TSystem::getHostName(); + + for (int i = 0; i < listCount; i++) { + QString str = list.at(i); + if (str.startsWith(prefix)) { + tempFile = str; + tempFile.remove("_hooks"); + tempFile = path + "/" + tempFile; + break; + } + } + } + VersionControl::instance()->revertFrameRange(this, path, file, tempFile); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::updateAndEditVersionControl() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.empty()) return; + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + QStringList files; + int sceneIconsCount = 0; + + TFilePath fp = filePaths[0]; + QFileInfo fi(toQString(fp)); + if (fp.getDots() == "..") { + QStringList levelNames = getLevelFileNames(fp); + int count = levelNames.size(); + for (int i = 0; i < count; i++) files.append(levelNames.at(i)); + } else + files.append(fi.fileName()); + + SVNStatus status = node->getVersionControlStatus(fi.fileName()); + int workingRevision = status.m_workingRevision.toInt(); + + // Add also auxiliary files + if (fp.getDots() == ".." || fp.getType() == "tlv" || fp.getType() == "pli") { + TFilePathSet fpset; + TXshSimpleLevel::getFiles(fp, fpset); + + TFilePathSet::iterator it; + for (it = fpset.begin(); it != fpset.end(); ++it) + files.append(QFileInfo(toQString(*it)).fileName()); + } + // Add sceneIcon (only for scene files) + else if (fp.getType() == "tnz") { + TFilePath iconPath = ToonzScene::getIconPath(fp); + if (TFileStatus(iconPath).doesExist()) { + QDir dir(toQString(fp.getParentDir())); + +#ifdef MACOSX + files.append(dir.relativeFilePath(toQString(iconPath))); +#else + files.append( + dir.relativeFilePath(toQString(iconPath)).replace("/", "\\")); +#endif + sceneIconsCount++; + } + } + + DvDirVersionControlRootNode *rootNode = node->getVersionControlRootNode(); + + VersionControl *vc = VersionControl::instance(); + if (rootNode) { + vc->setUserName(QString::fromStdWString(rootNode->getUserName())); + vc->setPassword(QString::fromStdWString(rootNode->getPassword())); + } + + QString path = toQString(node->getPath()); + + vc->updateAndLock(this, path, files, workingRevision, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::unlockVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + + VersionControl *vc = VersionControl::instance(); + + bool hasCurrentSceneFile = false; + + // Check the scene file + TFilePath fp = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getScenePath() + .withoutParentDir(); + if (files.contains(toQString(fp))) hasCurrentSceneFile = true; + + // Check the scene resource files + if (!hasCurrentSceneFile) { + QStringList currentSceneContents = vc->getCurrentSceneContents(); + int fileSize = files.size(); + for (int i = 0; i < fileSize; i++) { +#ifdef MACOSX + QString fp = path + "/" + files.at(i); +#else + QString fp = path + "\\" + files.at(i); +#endif + if (currentSceneContents.contains(fp)) { + hasCurrentSceneFile = true; + break; + } + } + } + + if (hasCurrentSceneFile) { + DVGui::warning( + tr("Some files that you want to unlock are currently opened. Close " + "them first.")); + return; + } + vc->unlock(this, path, files, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::putVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->commit(this, path, files, false, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::revertVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->revert(this, path, files, false, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::deleteVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->deleteFiles(this, path, files, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::getVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->update(this, path, files, sceneIconsCount, false, + false, false); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::getRevisionVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->update(this, path, files, sceneIconsCount, false, + true, false); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::getRevisionHistory() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + QStringList files; + int iconAdded; + setupVersionControlCommand(files, path, iconAdded); + + files.removeAt(files.indexOf(file)); + + SVNTimeline *timelineDialog = new SVNTimeline(this, path, file, files); + connect(timelineDialog, SIGNAL(commandDone(const QStringList &)), this, + SLOT(onVersionControlCommandDone(const QStringList &))); + + timelineDialog->show(); + timelineDialog->raise(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onVersionControlCommandDone(const QStringList &files) { + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + // Refresh the tree view and hence the version control status of each item + m_folderTreeView->refreshVersionControl(node); + + // Get the current scene + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + std::vector levels; + scene->getLevelSet()->listLevels(levels); + QMap sceneLevelPaths; + for (int i = 0; i < levels.size(); i++) + sceneLevelPaths[scene->decodeFilePath(levels[i]->getPath())] = levels[i]; + + TLevelSet *levelSetToCheck = new TLevelSet; + + for (int i = 0; i < files.size(); i++) { + TFilePath path = TFilePath(files.at(i).toStdWString()); + if (!path.isAbsolute()) { + TFilePath tempPath = TFilePath(node->getPath()) + path; + if (!TFileStatus(path).doesExist()) { + DvDirVersionControlNode *parent = + dynamic_cast(node->getParent()); + while (parent && !TFileStatus(tempPath).doesExist()) { + tempPath = TFilePath(node->getPath()) + path; + parent = dynamic_cast(parent->getParent()); + } + path = tempPath; + } + } + + if (!TFileStatus(path).doesExist()) continue; + + // Invalidate icons + IconGenerator::instance()->invalidate(path); + + // TODO: Scene checking (could be useful) + + // Level check + if (!sceneLevelPaths.isEmpty() && sceneLevelPaths.contains(path)) { + TXshLevel *level = sceneLevelPaths.value(path); + TXshSimpleLevel *sl = level->getSimpleLevel(); + if (sl) { + levelSetToCheck->insertLevel(sl); + sl->updateReadOnly(); + } + } + } + + VersionControlManager::instance()->setFrameRange(levelSetToCheck, true); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::showLockInformation() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.empty()) return; + + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + QFileInfo fi(toQString(filePaths[0])); + SVNStatus status = node->getVersionControlStatus(fi.fileName()); + + SVNLockInfoDialog *dialog = new SVNLockInfoDialog(this, status); + dialog->show(); + dialog->raise(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::showFrameRangeLockInfo() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + TFilePath fp(TFilePath(path.toStdWString()) + file.toStdWString()); + if (fp.getDots() == "..") { + QStringList list = getLevelFileNames(fp); + VersionControl::instance()->showFrameRangeLockInfo(this, path, list); + } else + VersionControl::instance()->showFrameRangeLockInfo(this, path, file); +} + +//----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/scenesettingspopup.cpp b/toonz/sources/toonz/scenesettingspopup.cpp index b0ddffe4..0095423f 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 6108497e..7ea1756e 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/sceneviewer.cpp b/toonz/sources/toonz/sceneviewer.cpp index 2048cd04..78cfeb95 100644 --- a/toonz/sources/toonz/sceneviewer.cpp +++ b/toonz/sources/toonz/sceneviewer.cpp @@ -12,6 +12,8 @@ #include "ruler.h" #include "locatorpopup.h" #include "../stopmotion/stopmotion.h" +#include "tenv.h" +#include "cellselection.h" // TnzTools includes #include "tools/cursors.h" @@ -59,6 +61,7 @@ #include "toonz/toonzimageutils.h" #include "toonz/txshleveltypes.h" #include "subcameramanager.h" +#include "toutputproperties.h" // TnzCore includes #include "tpalette.h" @@ -79,11 +82,7 @@ #include #include #include -#if QT_VERSION >= 0x050000 #include -#else -#include -#endif #include #include #include @@ -96,6 +95,11 @@ TEnv::IntVar ShowSymmetryGuide("ShowSymmetryGuide", 1); void drawSpline(const TAffine &viewMatrix, const TRect &clipRect, bool camera3d, double pixelSize); +// 0: current frame +// 1: all frames in the preview range +// 2: selected cell, auto play once & stop +TEnv::IntVar EnvViewerPreviewBehavior("ViewerPreviewBehavior", 0); + //------------------------------------------------------------------------------- namespace { @@ -824,10 +828,8 @@ SceneViewer::SceneViewer(ImageUtils::FullScreenWidget *parent) setFocusPolicy(Qt::StrongFocus); setAcceptDrops(true); this->setMouseTracking(true); -// introduced from Qt 5.9 -#if QT_VERSION >= 0x050900 + // introduced from Qt 5.9 this->setTabletTracking(true); -#endif for (int i = 0; i < m_viewAff.size(); ++i) { setViewMatrix(getNormalZoomScale(), i); @@ -968,16 +970,47 @@ void SceneViewer::enablePreview(int previewMode) { Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW) ->removeListener(this); + m_previewMode = previewMode; + // Schedule as a listener to Previewer. - if (previewMode != NO_PREVIEW) { + if (m_previewMode != NO_PREVIEW) { Previewer *previewer = - Previewer::instance(previewMode == SUBCAMERA_PREVIEW); + Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW); + previewer->addListener(this); + // 0: current frame + // 1: all frames in the preview range + // 2: selected cell, auto play once & stop + if (EnvViewerPreviewBehavior == 1) { + int r0, r1, step; + ToonzScene *scene = app->getCurrentScene()->getScene(); + scene->getProperties()->getPreviewProperties()->getRange(r0, r1, step); + if (r0 > r1) { + r0 = 0; + r1 = scene->getFrameCount() - 1; + } + int currentFrame = app->getCurrentFrame()->getFrame(); + std::vector queueFrames; + for (int f = currentFrame; f <= r1; f += step) queueFrames.push_back(f); + for (int f = r0; f < currentFrame; f += step) queueFrames.push_back(f); + + previewer->addFramesToRenderQueue(queueFrames); + } else if (EnvViewerPreviewBehavior == 2) { + TCellSelection *cellSel = + dynamic_cast(TSelection::getCurrent()); + if (cellSel && !cellSel->isEmpty()) { + int r0, c0, r1, c1; + cellSel->getSelectedCells(r0, c0, r1, c1); + if (r0 < r1) { + std::vector queueFrames; + for (int f = r0; f <= r1; f++) queueFrames.push_back(f); + previewer->addFramesToRenderQueue(queueFrames); + } + } + } previewer->update(); } - m_previewMode = previewMode; - GLInvalidateAll(); // for updating the title bar @@ -1043,7 +1076,8 @@ void SceneViewer::showEvent(QShowEvent *) { m_visualSettings.m_sceneProperties = TApp::instance()->getCurrentScene()->getScene()->getProperties(); - // If the viewer is hidden and preview is activated, remove the listener from preview + // If the viewer is hidden and preview is activated, remove the listener from + // preview if (m_previewMode != NO_PREVIEW) Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)->addListener(this); @@ -1053,7 +1087,7 @@ void SceneViewer::showEvent(QShowEvent *) { bool ret = connect(sceneHandle, SIGNAL(sceneSwitched()), this, SLOT(resetSceneViewer())); ret = ret && connect(sceneHandle, SIGNAL(sceneChanged()), this, - SLOT(onSceneChanged())); + SLOT(onSceneChanged())); TFrameHandle *frameHandle = app->getCurrentFrame(); ret = ret && connect(frameHandle, SIGNAL(frameSwitched()), this, @@ -1127,7 +1161,8 @@ void SceneViewer::showEvent(QShowEvent *) { //----------------------------------------------------------------------------- void SceneViewer::hideEvent(QHideEvent *) { - // If the viewer is hidden and preview is activated, remove the listener from preview + // If the viewer is hidden and preview is activated, remove the listener from + // preview if (m_previewMode != NO_PREVIEW) Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW) ->removeListener(this); @@ -1981,7 +2016,7 @@ static void drawFpsGraph(int t0, int t1) { //----------------------------------------------------------------------------- -//#define FPS_HISTOGRAM +// #define FPS_HISTOGRAM void SceneViewer::paintGL() { #ifdef _DEBUG @@ -2849,9 +2884,9 @@ void SceneViewer::fitToCamera() { TPointD P01 = cameraAff * cameraRect.getP01(); TPointD P11 = cameraAff * cameraRect.getP11(); TPointD p0 = TPointD(std::min({P00.x, P01.x, P10.x, P11.x}), - std::min({P00.y, P01.y, P10.y, P11.y})); + std::min({P00.y, P01.y, P10.y, P11.y})); TPointD p1 = TPointD(std::max({P00.x, P01.x, P10.x, P11.x}), - std::max({P00.y, P01.y, P10.y, P11.y})); + std::max({P00.y, P01.y, P10.y, P11.y})); cameraRect = TRectD(p0.x, p0.y, p1.x, p1.y); // Pan @@ -2894,9 +2929,9 @@ void SceneViewer::fitToCameraOutline() { TPointD P01 = cameraAff * cameraRect.getP01(); TPointD P11 = cameraAff * cameraRect.getP11(); TPointD p0 = TPointD(std::min({P00.x, P01.x, P10.x, P11.x}), - std::min({P00.y, P01.y, P10.y, P11.y})); + std::min({P00.y, P01.y, P10.y, P11.y})); TPointD p1 = TPointD(std::max({P00.x, P01.x, P10.x, P11.x}), - std::max({P00.y, P01.y, P10.y, P11.y})); + std::max({P00.y, P01.y, P10.y, P11.y})); cameraRect = TRectD(p0.x, p0.y, p1.x, p1.y); // Pan @@ -3537,11 +3572,7 @@ void drawSpline(const TAffine &viewMatrix, const TRect &clipRect, bool camera3d, //----------------------------------------------------------------------------- void SceneViewer::resetInputMethod() { -#if QT_VERSION >= 0x050000 QGuiApplication::inputMethod()->reset(); -#else - qApp->inputContext()->reset(); -#endif } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/sceneviewerevents.cpp b/toonz/sources/toonz/sceneviewerevents.cpp index 80dc7aca..b9d36062 100644 --- a/toonz/sources/toonz/sceneviewerevents.cpp +++ b/toonz/sources/toonz/sceneviewerevents.cpp @@ -76,7 +76,7 @@ namespace { void initToonzEvent(TMouseEvent &toonzEvent, QMouseEvent *event, int widgetHeight, double pressure, int devPixRatio) { toonzEvent.m_pos = TPointD(event->pos().x() * devPixRatio, - widgetHeight - 1 - event->pos().y() * devPixRatio); + widgetHeight - 1 - event->pos().y() * devPixRatio); toonzEvent.m_mousePos = event->pos(); toonzEvent.m_pressure = 1.0; @@ -204,7 +204,7 @@ void SceneViewer::onButtonPressed(FlipConsole::EGadget button) { break; case FlipConsole::eHisto: { - QAction *action = CommandManager::instance()->getAction(MI_Histogram); + QAction *action = CommandManager::instance()->getAction(MI_ViewerHistogram); action->trigger(); break; } @@ -1909,18 +1909,11 @@ void SceneViewer::onContextMenu(const QPoint &pos, const QPoint &globalPos) { menu->addLevelCommands(columnIndices); - ComboViewerPanel *cvp = - qobject_cast(parentWidget()->parentWidget()); - if (cvp) { + BaseViewerPanel *bvp = + qobject_cast(parentWidget()->parentWidget()); + if (bvp) { menu->addSeparator(); - cvp->addShowHideContextMenu(menu); - } - - SceneViewerPanel *svp = qobject_cast( - parentWidget()->parentWidget()->parentWidget()); - if (svp) { - menu->addSeparator(); - svp->addShowHideContextMenu(menu); + bvp->addShowHideContextMenu(menu); } menu->exec(globalPos); diff --git a/toonz/sources/toonz/scriptconsolepanel.cpp b/toonz/sources/toonz/scriptconsolepanel.cpp index 7c34cdcc..fe449825 100644 --- a/toonz/sources/toonz/scriptconsolepanel.cpp +++ b/toonz/sources/toonz/scriptconsolepanel.cpp @@ -123,13 +123,8 @@ static void def(ScriptEngine *teng, const QString &name, eng->globalObject().setProperty(name, evalFun); } -#if QT_VERSION >= 0x050500 ScriptConsolePanel::ScriptConsolePanel(QWidget *parent, Qt::WindowFlags flags) -#else -ScriptConsolePanel::ScriptConsolePanel(QWidget *parent, Qt::WFlags flags) -#endif : TPanel(parent) { - setPanelType("ScriptConsole"); setIsMaximizable(false); setWindowTitle(QObject::tr("Script Console")); diff --git a/toonz/sources/toonz/scriptconsolepanel.h b/toonz/sources/toonz/scriptconsolepanel.h index 98760395..38d6530a 100644 --- a/toonz/sources/toonz/scriptconsolepanel.h +++ b/toonz/sources/toonz/scriptconsolepanel.h @@ -12,11 +12,8 @@ class ScriptConsolePanel final : public TPanel { ScriptConsole *m_scriptConsole; public: -#if QT_VERSION >= 0x050500 - ScriptConsolePanel(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - ScriptConsolePanel(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif + ScriptConsolePanel(QWidget *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); ~ScriptConsolePanel(); void executeCommand(const QString &cmd); diff --git a/toonz/sources/toonz/separatecolorsswatch.cpp b/toonz/sources/toonz/separatecolorsswatch.cpp index d02e21ab..0f72e738 100644 --- a/toonz/sources/toonz/separatecolorsswatch.cpp +++ b/toonz/sources/toonz/separatecolorsswatch.cpp @@ -1,4 +1,4 @@ -#include "separatecolorsswatch.h" +#include "separatecolorsswatch.h" #include "tapp.h" @@ -240,7 +240,7 @@ void SeparateSwatchArea::paintEvent(QPaintEvent *event) { void SeparateSwatchArea::wheelEvent(QWheelEvent *event) { if (!m_sw->m_mainSwatch || m_sw->m_lx == 0 || m_sw->m_ly == 0) return; - int step = event->delta() > 0 ? 120 : -120; + int step = event->angleDelta().y() > 0 ? 120 : -120; double factor = exp(0.001 * step); if (factor == 1.0) return; double scale = m_sw->m_viewAff.det(); @@ -249,7 +249,11 @@ void SeparateSwatchArea::wheelEvent(QWheelEvent *event) { if ((factor < 1 && sqrt(scale) < minZoom) || (factor > 1 && scale > 1200.0)) return; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + TPointD delta(event->position().x(), height() - event->position().y()); +#else TPointD delta(event->pos().x(), height() - event->pos().y()); +#endif m_sw->m_viewAff = (TTranslation(delta) * TScale(factor) * TTranslation(-delta)) * m_sw->m_viewAff; @@ -411,7 +415,7 @@ void SeparateSwatch::setRaster(TRasterP orgRas, TRasterP mainRas, void SeparateSwatch::setRaster(TRasterP orgRas, TRasterP mainRas, TRasterP sub1Ras, TRasterP sub2Ras, TRasterP sub3Ras) { - //o + // o‚· m_sub3Swatch->setVisible(true); m_sub3Label->setVisible(true); diff --git a/toonz/sources/toonz/styleshortcutswitchablepanel.h b/toonz/sources/toonz/styleshortcutswitchablepanel.h index 3867fc98..261bb7e0 100644 --- a/toonz/sources/toonz/styleshortcutswitchablepanel.h +++ b/toonz/sources/toonz/styleshortcutswitchablepanel.h @@ -22,7 +22,7 @@ class StyleShortcutSwitchablePanel : public TPanel { public: StyleShortcutSwitchablePanel( - QWidget *parent = 0, Qt::WindowFlags flags = 0, + QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags(), TDockWidget::Orientation orientation = TDockWidget::vertical) : TPanel(parent, flags, orientation) {} diff --git a/toonz/sources/toonz/subcameramanager.cpp b/toonz/sources/toonz/subcameramanager.cpp index d3d941bd..519a7f5d 100644 --- a/toonz/sources/toonz/subcameramanager.cpp +++ b/toonz/sources/toonz/subcameramanager.cpp @@ -31,6 +31,11 @@ inline bool bitwiseContains(UCHAR flag, UCHAR state) { inline bool bitwiseExclude(UCHAR flag, UCHAR state) { return bitwiseContains(~state, flag); } + +inline bool areNear(double v0, double v1, double thres = 20.0) { + return std::abs(v0 - v1) < thres; +} + } // namespace //******************************************************************************** @@ -147,6 +152,27 @@ bool PreviewSubCameraManager::mouseMoveEvent(SceneViewer *viewer, TPointD worldCurPos(viewer->winToWorld(curPos)); TApp *app = TApp::instance(); + + TCamera *camera = app->getCurrentScene()->getScene()->getCurrentCamera(); + TRectD cameraStageRect(camera->getCameraToStageRef() * + convert(TRect(camera->getRes()))); + + // Snap to the current camera frame + // horizontal + if (worldCurPos.x < worldMousePressPos.x && + areNear(worldCurPos.x, cameraStageRect.x0)) + worldCurPos.x = cameraStageRect.x0; + else if (worldCurPos.x > worldMousePressPos.x && + areNear(worldCurPos.x, cameraStageRect.x1)) + worldCurPos.x = cameraStageRect.x1; + // vertical + if (worldCurPos.y < worldMousePressPos.y && + areNear(worldCurPos.y, cameraStageRect.y0)) + worldCurPos.y = cameraStageRect.y0; + else if (worldCurPos.y > worldMousePressPos.y && + areNear(worldCurPos.y, cameraStageRect.y1)) + worldCurPos.y = cameraStageRect.y1; + TAffine cameraAffInv( app->getCurrentXsheet() ->getXsheet() @@ -162,15 +188,17 @@ bool PreviewSubCameraManager::mouseMoveEvent(SceneViewer *viewer, std::max(worldMousePressPos.x, worldCurPos.x), std::max(worldMousePressPos.y, worldCurPos.y)); - TCamera *camera = app->getCurrentScene()->getScene()->getCurrentCamera(); // camera->setInterestStageRect(worldPreviewSubCameraRect); TRectD previewSubCameraD(camera->getStageToCameraRef() * worldPreviewSubCameraRect); m_editingInterestRect = TRect(previewSubCameraD.x0, previewSubCameraD.y0, - previewSubCameraD.x1 - 1, previewSubCameraD.y1 - 1) * - TRect(camera->getRes()); + previewSubCameraD.x1 - 1, previewSubCameraD.y1 - 1); + // m_editingInterestRect = + // TRect(previewSubCameraD.x0, previewSubCameraD.y0, + // previewSubCameraD.x1 - 1, previewSubCameraD.y1 - 1) * + // TRect(camera->getRes()); viewer->update(); } else { @@ -194,7 +222,8 @@ bool PreviewSubCameraManager::mouseMoveEvent(SceneViewer *viewer, subRect.y1 = subRect.y1 + dragDistance.y; } - m_editingInterestRect = subRect * TRect(camera->getRes()); + m_editingInterestRect = subRect; + // m_editingInterestRect = subRect * TRect(camera->getRes()); viewer->update(); } @@ -230,7 +259,7 @@ bool PreviewSubCameraManager::mouseMoveEvent(SceneViewer *viewer, } // In case, perform the pan - return event.buttons() == Qt::MidButton; + return event.buttons() == Qt::MiddleButton; } //---------------------------------------------------------------------- @@ -301,11 +330,32 @@ UCHAR PreviewSubCameraManager::getSubCameraDragEnum(SceneViewer *viewer, //----------------------------------------------------------------------------- -TPoint PreviewSubCameraManager::getSubCameraDragDistance( - SceneViewer *viewer, const QPointF &mousePos) { +TPoint PreviewSubCameraManager::getSubCameraDragDistance(SceneViewer *viewer, + QPointF &mousePos) { // Build the camera drag distance if (m_clickAndDrag) return TPoint(); + // Snap to the current camera frame + TCamera *camera = + TApp::instance()->getCurrentScene()->getScene()->getCurrentCamera(); + if (!bitwiseExclude(m_dragType, OUTER)) { + TPointD btmLft(cameraToWin(viewer, TPointD(0, 0))); + TPointD tpRght( + cameraToWin(viewer, TPointD(camera->getRes().lx, camera->getRes().ly))); + if (bitwiseContains(m_dragType, DRAG_LEFT) && + areNear(mousePos.x(), btmLft.x)) + mousePos.setX(btmLft.x); + else if (bitwiseContains(m_dragType, DRAG_RIGHT) && + areNear(mousePos.x(), tpRght.x)) + mousePos.setX(tpRght.x); + if (bitwiseContains(m_dragType, DRAG_BOTTOM) && + areNear(mousePos.y(), (double)viewer->height() - btmLft.y)) + mousePos.setY((double)viewer->height() - btmLft.y); + else if (bitwiseContains(m_dragType, DRAG_TOP) && + areNear(mousePos.y(), (double)viewer->height() - tpRght.y)) + mousePos.setY((double)viewer->height() - tpRght.y); + } + TPointD cameraMousePos(winToCamera(viewer, mousePos)); if (bitwiseExclude(m_dragType, OUTER)) { @@ -313,8 +363,6 @@ TPoint PreviewSubCameraManager::getSubCameraDragDistance( return TPoint(resultD.x, resultD.y); } - TCamera *camera = - TApp::instance()->getCurrentScene()->getScene()->getCurrentCamera(); TRect subCamera = camera->getInterestRect(); TRectD subCameraD(subCamera.x0, subCamera.y0, subCamera.x1 + 1, subCamera.y1 + 1); diff --git a/toonz/sources/toonz/subcameramanager.h b/toonz/sources/toonz/subcameramanager.h index 3f89a8cc..778ce0c8 100644 --- a/toonz/sources/toonz/subcameramanager.h +++ b/toonz/sources/toonz/subcameramanager.h @@ -110,7 +110,7 @@ private: TPointD cameraToWin(SceneViewer *viewer, const TPointD &cameraPos) const; UCHAR getSubCameraDragEnum(SceneViewer *viewer, const QPointF &mousePos); - TPoint getSubCameraDragDistance(SceneViewer *viewer, const QPointF &mousePos); + TPoint getSubCameraDragDistance(SceneViewer *viewer, QPointF &mousePos); }; #endif // SUBCAMERAMANAGER_INCLUDED diff --git a/toonz/sources/toonz/subscenecommand.cpp b/toonz/sources/toonz/subscenecommand.cpp index 955c1c72..e2c5a013 100644 --- a/toonz/sources/toonz/subscenecommand.cpp +++ b/toonz/sources/toonz/subscenecommand.cpp @@ -1439,8 +1439,8 @@ void collapseColumns(std::set indices, } StageObjectsData *data = new StageObjectsData(); - data->storeObjects(objIds.toVector().toStdVector(), xsh, - StageObjectsData::eDoClone); + data->storeObjects(std::vector(objIds.begin(), objIds.end()), + xsh, StageObjectsData::eDoClone); data->storeColumnFxs(indices, xsh, StageObjectsData::eDoClone); // ExpressionReferenceMonitor *monitor = xsh->getExpRefMonitor()->clone(); diff --git a/toonz/sources/toonz/tasksviewer.cpp b/toonz/sources/toonz/tasksviewer.cpp index c5f3b042..5bf78f4f 100644 --- a/toonz/sources/toonz/tasksviewer.cpp +++ b/toonz/sources/toonz/tasksviewer.cpp @@ -16,6 +16,7 @@ // TnzCore includes #include "tconvert.h" +#include "tlevel_io.h" // Qt includes #include @@ -34,15 +35,6 @@ using namespace DVGui; //============================================================================= -namespace { -bool isMovieType(std::string type) { - return (type == "avi" || type == "mp4" || - type == "webm" || type == "mov"); -} -}; // namespace - -//============================================================================= - const std::vector &TasksViewer::getActions() const { return m_actions; } @@ -51,13 +43,8 @@ const std::vector &TasksViewer::getActions() const { void TasksViewer::add(const QString &iconName, QString text, QToolBar *toolBar, const char *slot, QString iconText) { -#if QT_VERSION >= 0x050500 QAction *action = new QAction( createQIcon(iconName.toLatin1().constData(), false), text, this); -#else - QAction *action = new QAction( - createQIcon(iconName.toAscii().constData(), false), text, this); -#endif action->setIconText(iconText); bool ret = connect(action, SIGNAL(triggered(bool)), (TaskTreeModel *)m_treeView->model(), slot); @@ -86,7 +73,7 @@ QWidget *TasksViewer::createToolBar() { add("play", tr("&Start"), cmdToolbar, SLOT(start(bool)), tr("Start")); add("stop", tr("&Stop"), cmdToolbar, SLOT(stop(bool)), tr("Stop")); cmdToolbar->addSeparator(); - add("render_add", tr("&Add Render Task"), cmdToolbar, + add("new_scene", tr("&Add Render Task"), cmdToolbar, SLOT(addRenderTask(bool)), tr("Add Render")); add("cleanup_add", tr("&Add Cleanup Task"), cmdToolbar, SLOT(addCleanupTask(bool)), tr("Add Cleanup")); @@ -118,11 +105,7 @@ QWidget *TasksViewer::createToolBar() { /*! \class TasksViewer Inherits \b QSplitter. */ -#if QT_VERSION >= 0x050500 TasksViewer::TasksViewer(QWidget *parent, Qt::WindowFlags flags) -#else -TasksViewer::TasksViewer(QWidget *parent, Qt::WFlags flags) -#endif : QSplitter(parent) { QFrame *box; @@ -615,7 +598,7 @@ void TaskSheet::setShrink() { // Update children tasks, if present. TFarmTaskGroup *taskGroup = dynamic_cast(m_task); if (taskGroup) { - for (int i = 0; i < taskGroup->getTaskCount(); ++i) + for (int i = 0; i < taskGroup->getTaskCount(); ++i) taskGroup->getTask(i)->m_shrink = taskGroup->m_shrink; } } @@ -643,7 +626,7 @@ void TaskSheet::setStep() { // Update children tasks, if present. TFarmTaskGroup *taskGroup = dynamic_cast(m_task); if (taskGroup) { - for (int i = 0; i < taskGroup->getTaskCount(); ++i) + for (int i = 0; i < taskGroup->getTaskCount(); ++i) taskGroup->getTask(i)->m_step = taskGroup->m_step; } } diff --git a/toonz/sources/toonz/tasksviewer.h b/toonz/sources/toonz/tasksviewer.h index 3013cdda..827375e6 100644 --- a/toonz/sources/toonz/tasksviewer.h +++ b/toonz/sources/toonz/tasksviewer.h @@ -169,11 +169,7 @@ public: TaskTreeView *m_treeView; QTimer *m_timer; -#if QT_VERSION >= 0x050500 TasksViewer(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - TasksViewer(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif ~TasksViewer(); void update() override; diff --git a/toonz/sources/toonz/testpanel.cpp b/toonz/sources/toonz/testpanel.cpp index 2a2928f9..b195ceca 100644 --- a/toonz/sources/toonz/testpanel.cpp +++ b/toonz/sources/toonz/testpanel.cpp @@ -31,12 +31,7 @@ using namespace DVGui; margin: 1px; } */ -#if QT_VERSION >= 0x050500 -TestPanel::TestPanel(QWidget *parent, Qt::WindowFlags flags) -#else -TestPanel::TestPanel(QWidget *parent, Qt::WFlags flags) -#endif - : TPanel(parent) { +TestPanel::TestPanel(QWidget *parent, Qt::WindowFlags flags) : TPanel(parent) { setPanelType("Test"); setIsMaximizable(false); setWindowTitle("Test"); diff --git a/toonz/sources/toonz/testpanel.h b/toonz/sources/toonz/testpanel.h index 0e300f3c..1b70451c 100644 --- a/toonz/sources/toonz/testpanel.h +++ b/toonz/sources/toonz/testpanel.h @@ -13,11 +13,7 @@ class TestPanel final : public TPanel { Q_OBJECT public: -#if QT_VERSION >= 0x050500 TestPanel(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - TestPanel(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif ~TestPanel(); public slots: diff --git a/toonz/sources/toonz/tooloptionsshortcutinvoker.cpp b/toonz/sources/toonz/tooloptionsshortcutinvoker.cpp index 23b8d045..9d71f695 100644 --- a/toonz/sources/toonz/tooloptionsshortcutinvoker.cpp +++ b/toonz/sources/toonz/tooloptionsshortcutinvoker.cpp @@ -481,6 +481,16 @@ void ToolOptionsShortcutInvoker::initialize() { setCommandHandler(MI_TypeBold, this, &ToolOptionsShortcutInvoker::toggleTypeBold); + /*-- Paint Brush tool + mode switching shortcuts --*/ + setCommandHandler(MI_PaintBrushNextMode, this, + &ToolOptionsShortcutInvoker::togglePaintBrushNextMode); + setCommandHandler(MI_PaintBrushAreas, this, + &ToolOptionsShortcutInvoker::togglePaintBrushAreas); + setCommandHandler(MI_PaintBrushLines, this, + &ToolOptionsShortcutInvoker::togglePaintBrushLines); + setCommandHandler(MI_PaintBrushLinesAndAreas, this, + &ToolOptionsShortcutInvoker::togglePaintBrushLinesAndAreas); + /*-- Fill tool + type/mode switching shortcuts --*/ setCommandHandler(MI_FillNextType, this, &ToolOptionsShortcutInvoker::toggleFillNextType); @@ -492,6 +502,8 @@ void ToolOptionsShortcutInvoker::initialize() { &ToolOptionsShortcutInvoker::toggleFillFreehand); setCommandHandler(MI_FillPolyline, this, &ToolOptionsShortcutInvoker::toggleFillPolyline); + setCommandHandler(MI_FillFreepick, this, + &ToolOptionsShortcutInvoker::toggleFillFreepick); setCommandHandler(MI_FillNextMode, this, &ToolOptionsShortcutInvoker::toggleFillNextMode); setCommandHandler(MI_FillAreas, this, @@ -856,6 +868,14 @@ void ToolOptionsShortcutInvoker::toggleFillPolyline() { ->trigger(); } +void ToolOptionsShortcutInvoker::toggleFillFreepick() { + CommandManager::instance()->getAction(T_Fill)->trigger(); + CommandManager::instance()->getAction("A_ToolOption_Type:Normal")->trigger(); + CommandManager::instance() + ->getAction("A_ToolOption_Type:Freepick") + ->trigger(); +} + void ToolOptionsShortcutInvoker::toggleFillNextMode() { if (TApp::instance()->getCurrentTool()->getTool()->getName() == T_Fill) CommandManager::instance()->getAction("A_ToolOption_Mode")->trigger(); @@ -880,6 +900,32 @@ void ToolOptionsShortcutInvoker::toggleFillLinesAndAreas() { ->trigger(); } + +//--------------------------------------------------------------------------------------- +/*-- Paint Brush tool + mode switching shortcuts --*/ +void ToolOptionsShortcutInvoker::togglePaintBrushNextMode() { + if (TApp::instance()->getCurrentTool()->getTool()->getName() == T_PaintBrush) + CommandManager::instance()->getAction("A_ToolOption_Mode")->trigger(); + else + CommandManager::instance()->getAction(T_PaintBrush)->trigger(); +} +void ToolOptionsShortcutInvoker::togglePaintBrushAreas() { + CommandManager::instance()->getAction(T_PaintBrush)->trigger(); + CommandManager::instance()->getAction("A_ToolOption_Mode:Areas")->trigger(); +} + +void ToolOptionsShortcutInvoker::togglePaintBrushLines() { + CommandManager::instance()->getAction(T_PaintBrush)->trigger(); + CommandManager::instance()->getAction("A_ToolOption_Mode:Lines")->trigger(); +} + +void ToolOptionsShortcutInvoker::togglePaintBrushLinesAndAreas() { + CommandManager::instance()->getAction(T_PaintBrush)->trigger(); + CommandManager::instance() + ->getAction("A_ToolOption_Mode:Lines & Areas") + ->trigger(); +} + //--------------------------------------------------------------------------------------- /*-- Eraser tool + type switching shortcuts --*/ void ToolOptionsShortcutInvoker::toggleEraserNextType() { diff --git a/toonz/sources/toonz/tooloptionsshortcutinvoker.h b/toonz/sources/toonz/tooloptionsshortcutinvoker.h index b95e158d..5db1902d 100644 --- a/toonz/sources/toonz/tooloptionsshortcutinvoker.h +++ b/toonz/sources/toonz/tooloptionsshortcutinvoker.h @@ -191,12 +191,19 @@ protected slots: void toggleTypeBoldOblique(); void toggleTypeBold(); + /*-- Paint Brush tool + mode switching shortcuts --*/ + void togglePaintBrushNextMode(); + void togglePaintBrushAreas(); + void togglePaintBrushLines(); + void togglePaintBrushLinesAndAreas(); + /*-- Fill tool + mode switching shortcuts --*/ void toggleFillNextType(); void toggleFillNormal(); void toggleFillRectangular(); void toggleFillFreehand(); void toggleFillPolyline(); + void toggleFillFreepick(); void toggleFillNextMode(); void toggleFillAreas(); void toggleFillLines(); diff --git a/toonz/sources/toonz/toonz.qrc b/toonz/sources/toonz/toonz.qrc index 6b4306ed..5185440f 100644 --- a/toonz/sources/toonz/toonz.qrc +++ b/toonz/sources/toonz/toonz.qrc @@ -88,15 +88,15 @@ icons/dark/actions/16/redo.svg icons/dark/actions/16/delete.svg icons/dark/actions/16/new_scene.svg - icons/dark/actions/16/load_scene.svg icons/dark/actions/16/revert_scene.svg icons/dark/actions/16/save_scene.svg icons/dark/actions/16/save_scene_as.svg - icons/dark/actions/16/convert.svg + icons/dark/actions/16/load_scene.svg + icons/dark/actions/16/convert.svg icons/dark/actions/16/orientation_h.svg icons/dark/actions/16/orientation_v.svg icons/dark/actions/16/load_as_sub_xsheet.svg - icons/dark/actions/16/scene_export.svg + icons/dark/actions/16/export_scene.svg icons/dark/actions/16/stylesets.svg @@ -139,6 +139,7 @@ icons/dark/actions/16/fb_down.svg icons/dark/actions/16/viewlist.svg icons/dark/actions/16/viewicon.svg + icons/dark/actions/16/collect_assets.svg icons/dark/actions/16/projects_folder.svg icons/dark/actions/16/favorites.svg @@ -183,8 +184,6 @@ icons/dark/actions/16/render.svg - icons/dark/actions/16/render_clapboard.svg - icons/dark/actions/16/render_add.svg icons/dark/actions/16/output_settings.svg icons/dark/actions/16/fast_render_mp4.svg icons/dark/actions/16/save_previewed_frames.svg @@ -227,7 +226,8 @@ icons/dark/actions/16/nextstep.svg icons/dark/actions/16/prevstep.svg icons/dark/actions/16/nextkey.svg - icons/dark/actions/16/prevkey.svg + icons/dark/actions/16/prevkey.svg + icons/dark/actions/16/blankframes.svg icons/dark/actions/16/snapshot.svg icons/dark/actions/16/compare.svg @@ -270,6 +270,7 @@ icons/dark/actions/16/output.svg icons/dark/actions/16/fx_logo.svg icons/dark/actions/16/fx_settings.svg + icons/dark/actions/16/preset.svg icons/dark/actions/20/switchport.svg icons/dark/actions/20/switchport_on.svg icons/dark/actions/20/switchport_over.svg @@ -297,6 +298,8 @@ icons/dark/actions/16/flipvert.svg icons/dark/actions/16/actual_pixel_size.svg icons/dark/actions/16/fit_to_window.svg + icons/dark/actions/16/zoom_reset.svg + icons/dark/actions/16/rotate_reset.svg icons/dark/actions/16/reverse.svg @@ -315,7 +318,7 @@ icons/dark/actions/16/remove_empty_columns.svg icons/dark/actions/16/merge.svg - icons/dark/actions/16/new_document.svg + icons/dark/actions/16/new_level.svg icons/dark/actions/16/new_raster_level.svg icons/dark/actions/16/new_note_level.svg icons/dark/actions/16/new_vector_level.svg @@ -364,7 +367,6 @@ icons/dark/actions/16/remove_cell.svg icons/dark/actions/16/remove_cells.svg icons/dark/actions/16/duplicate_drawing.svg - icons/dark/actions/16/tracking_options.svg icons/dark/actions/16/stop_frame_hold.svg icons/dark/actions/16/camera_settings.svg @@ -386,6 +388,16 @@ icons/dark/actions/30/sound_header_on.svg icons/dark/actions/74/notelevel.svg + icons/dark/actions/16/connect.svg + icons/dark/actions/16/disconnect.svg + icons/dark/actions/16/xsheet_connect.svg + icons/dark/actions/16/xsheet_disconnect.svg + icons/dark/actions/16/cache_fx.svg + icons/dark/actions/16/uncache_fx.svg + icons/dark/actions/16/link.svg + icons/dark/actions/16/unlink.svg + icons/dark/actions/16/macro.svg + icons/dark/actions/16/toggle_nav_tag.svg icons/dark/actions/16/next_nav_tag.svg icons/dark/actions/16/prev_nav_tag.svg @@ -415,7 +427,12 @@ icons/dark/actions/20/fill_mode_areas.svg icons/dark/actions/20/fill_mode_lines.svg icons/dark/actions/20/fill_mode_lines_areas.svg - icons/dark/actions/16/fill_auto.svg + icons/dark/actions/20/fill_freepick.svg + icons/dark/actions/16/toggle_autofill.svg + + icons/dark/actions/20/stylepicker_areas.svg + icons/dark/actions/20/stylepicker_lines.svg + icons/dark/actions/20/stylepicker_lines_areas.svg icons/dark/actions/20/paintbrush_mode_areas.svg icons/dark/actions/20/paintbrush_mode_lines.svg @@ -442,9 +459,19 @@ icons/dark/actions/20/selection_freehand.svg icons/dark/actions/20/type_lasso.svg + icons/dark/actions/20/type_pickerlasso.svg icons/dark/actions/20/type_rectangular.svg icons/dark/actions/20/type_polyline.svg icons/dark/actions/20/type_normal.svg + icons/dark/actions/20/type_erase_segment.svg + + icons/dark/actions/20/tape_rectangular.svg + icons/dark/actions/20/tape_freehand.svg + icons/dark/actions/20/tape_polyline.svg + icons/dark/actions/20/tape_normal.svg + icons/dark/actions/16/tape_end_to_end.svg + icons/dark/actions/16/tape_end_to_line.svg + icons/dark/actions/16/tape_line_to_line.svg icons/dark/actions/16/shift_and_trace.svg icons/dark/actions/16/shift_and_trace_edit.svg @@ -468,12 +495,16 @@ icons/dark/actions/18/folder_preset.svg - icons/dark/actions/20/newstyle.svg - icons/dark/actions/20/newpage.svg + icons/dark/actions/16/newstyle.svg + icons/dark/actions/16/newpage.svg icons/dark/actions/16/palette.svg icons/dark/actions/16/palette_tab.svg icons/dark/actions/20/dragpalette.svg icons/dark/actions/16/palettegizmo.svg + icons/dark/actions/16/delete_unused_styles.svg + icons/dark/actions/16/paste_color.svg + icons/dark/actions/16/paste_name.svg + icons/dark/actions/16/paste_color_and_name.svg icons/dark/actions/20/pane_table.svg @@ -500,7 +531,7 @@ icons/dark/actions/16/printer.svg icons/dark/actions/16/scanner.svg icons/dark/actions/16/scanner_settings.svg - icons/dark/actions/16/define_scanner.svg + icons/dark/actions/16/scanner_define.svg icons/dark/actions/16/minus.svg @@ -533,8 +564,8 @@ icons/dark/actions/16/opentoonz.svg icons/dark/actions/16/manual.svg icons/dark/actions/16/filebrowser.svg - icons/dark/actions/16/function_editor.svg icons/dark/actions/16/scenecast.svg + icons/dark/actions/16/function_editor.svg icons/dark/actions/20/segment_linked.svg icons/dark/actions/20/segment_linked_on.svg icons/dark/actions/11/menu_toggle.svg @@ -558,6 +589,10 @@ icons/dark/actions/16/view_file.svg icons/dark/actions/16/level_strip.svg icons/dark/actions/17/syncscale.svg + icons/dark/actions/16/new_project.svg + icons/dark/actions/16/project_settings.svg + icons/dark/actions/16/save_default_settings.svg + icons/dark/actions/16/zero_thick_lines.svg icons/dark/actions/20/key_off.svg @@ -609,6 +644,7 @@ Resources/no_specialstyle.png Resources/no_vectorbrush.png Resources/no_mypaintbrush.png + Resources/no_texturestyle.png Resources/palette_header.svg Resources/paletteicon.svg Resources/pan.png diff --git a/toonz/sources/toonz/tpanels.cpp b/toonz/sources/toonz/tpanels.cpp index b0b7867a..355d525f 100644 --- a/toonz/sources/toonz/tpanels.cpp +++ b/toonz/sources/toonz/tpanels.cpp @@ -18,6 +18,7 @@ #include "flipbook.h" #include "castviewer.h" #include "filebrowser.h" +#include "scenebrowser.h" #include "filmstrip.h" #include "previewfxmanager.h" #include "comboviewerpane.h" @@ -1235,6 +1236,23 @@ public: } } browserFactory; +//============================================================================= +// PreproductionBoardFactory +//----------------------------------------------------------------------------- +class PreproductionBoardFactory final : public TPanelFactory { +public: + PreproductionBoardFactory() : TPanelFactory("PreproductionBoard") {} + void initialize(TPanel *panel) override { + SceneBrowser *browser = new SceneBrowser(panel, 0, false, true); + panel->setWidget(browser); + panel->setWindowTitle(QObject::tr("Preproduction Board")); + TFilePath scenesFolder = + TProjectManager::instance()->getCurrentProject()->getScenesPath(); + browser->setFolder(scenesFolder, true); + browser->enableSingleClickToOpenScenes(); + } +} PreproductionBoardFactory; + //============================================================================= // CastViewerFactory //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/trackerpopup.cpp b/toonz/sources/toonz/trackerpopup.cpp index a47c071f..8eee5126 100644 --- a/toonz/sources/toonz/trackerpopup.cpp +++ b/toonz/sources/toonz/trackerpopup.cpp @@ -147,11 +147,7 @@ TRaster32P loadFrame(int frame, const TAffine &affine) { // TrackerPopup //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 TrackerPopup::TrackerPopup(QWidget *parent, Qt::WindowFlags flags) -#else -TrackerPopup::TrackerPopup(QWidget *parent, Qt::WFlags flags) -#endif : Dialog(TApp::instance()->getMainWindow(), true, true, "Tracker") { // Su MAC i dialog modali non hanno bottoni di chiusura nella titleBar setModal(false); @@ -423,7 +419,7 @@ bool Tracker::setup() { return false; } - //# start frame + // # start frame std::unique_ptr m_numstart(new int[m_trackerCount]); if (!m_numstart) { m_lastErrorCode = 1; diff --git a/toonz/sources/toonz/trackerpopup.h b/toonz/sources/toonz/trackerpopup.h index 5e16be5a..052ff9f2 100644 --- a/toonz/sources/toonz/trackerpopup.h +++ b/toonz/sources/toonz/trackerpopup.h @@ -32,7 +32,7 @@ class DoubleField; class IntField; // class LineEdit; class CheckBox; -} +} // namespace DVGui //============================================================================= // TrackerPopup @@ -56,11 +56,7 @@ class TrackerPopup final : public DVGui::Dialog { DVGui::IntField *m_to; public: -#if QT_VERSION >= 0x050500 TrackerPopup(QWidget *parent = 0, Qt::WindowFlags flags = Qt::Tool); -#else - TrackerPopup(QWidget *parent = 0, Qt::WFlags flags = Qt::Tool); -#endif bool apply(); diff --git a/toonz/sources/toonz/vcrcommand.cpp b/toonz/sources/toonz/vcrcommand.cpp index 019b269e..546daf48 100644 --- a/toonz/sources/toonz/vcrcommand.cpp +++ b/toonz/sources/toonz/vcrcommand.cpp @@ -305,7 +305,9 @@ VcrCommand playCommand(MI_Play, FlipConsole::ePlay), greenChannelGComman(MI_GreenChannelGreyscale, FlipConsole::eGGreen), blueChannelGCommand(MI_BlueChannelGreyscale, FlipConsole::eGBlue), - compareCommand(MI_CompareToSnapshot, FlipConsole::eCompare); + compareCommand(MI_CompareToSnapshot, FlipConsole::eCompare), + blankFramesCommand(MI_ToggleBlankFrames, FlipConsole::eBlankFrames), + histogramCommand(MI_Histogram, FlipConsole::eHisto); NextDrawingCommand nextDrawingCommand; PrevDrawingCommand prevDrawingCommand; diff --git a/toonz/sources/toonz/vectorguideddrawingpane.cpp b/toonz/sources/toonz/vectorguideddrawingpane.cpp index 0e3e9e56..09b75c47 100644 --- a/toonz/sources/toonz/vectorguideddrawingpane.cpp +++ b/toonz/sources/toonz/vectorguideddrawingpane.cpp @@ -18,15 +18,9 @@ using namespace DVGui; //============================================================================= -#if QT_VERSION >= 0x050500 VectorGuidedDrawingPane::VectorGuidedDrawingPane(QWidget *parent, Qt::WindowFlags flags) -#else -VectorGuidedDrawingPane::VectorGuidedDrawingPane(QWidget *parent, - Qt::WindowFlags flags) -#endif : QFrame(parent) { - setObjectName("cornerWidget"); setObjectName("VectorGuidedDrawingToolbar"); diff --git a/toonz/sources/toonz/vectorguideddrawingpane.h b/toonz/sources/toonz/vectorguideddrawingpane.h index 5995fde5..9437b054 100644 --- a/toonz/sources/toonz/vectorguideddrawingpane.h +++ b/toonz/sources/toonz/vectorguideddrawingpane.h @@ -26,11 +26,7 @@ class VectorGuidedDrawingPane final : public QFrame { *m_FlipNextDirectionBtn, *m_FlipPrevDirectionBtn; public: -#if QT_VERSION >= 0x050500 VectorGuidedDrawingPane(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - VectorGuidedDrawingPane(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#endif ~VectorGuidedDrawingPane(){}; void updateStatus(); diff --git a/toonz/sources/toonz/vectorizerpopup.cpp b/toonz/sources/toonz/vectorizerpopup.cpp index 8962f47d..8e206610 100644 --- a/toonz/sources/toonz/vectorizerpopup.cpp +++ b/toonz/sources/toonz/vectorizerpopup.cpp @@ -424,11 +424,7 @@ void Vectorizer::run() { doVectorize(); } // VectorizerPopup implementation //***************************************************************************** -#if QT_VERSION >= 0x050500 VectorizerPopup::VectorizerPopup(QWidget *parent, Qt::WindowFlags flags) -#else -VectorizerPopup::VectorizerPopup(QWidget *parent, Qt::WFlags flags) -#endif : Dialog(TApp::instance()->getMainWindow(), true, false, "Vectorizer") , m_sceneHandle(TApp::instance()->getCurrentScene()) { struct Locals { diff --git a/toonz/sources/toonz/vectorizerpopup.h b/toonz/sources/toonz/vectorizerpopup.h index daf865fe..e79f8be2 100644 --- a/toonz/sources/toonz/vectorizerpopup.h +++ b/toonz/sources/toonz/vectorizerpopup.h @@ -88,11 +88,7 @@ class VectorizerPopup final : public DVGui::Dialog { Q_OBJECT public: -#if QT_VERSION >= 0x050500 VectorizerPopup(QWidget *parent = 0, Qt::WindowFlags flags = Qt::Tool); -#else - VectorizerPopup(QWidget *parent = 0, Qt::WFlags flags = Qt::Tool); -#endif bool apply(); //!< The main vectorization method - prepares input //! configuration and runs the vectorization thread. diff --git a/toonz/sources/toonz/viewerpane.cpp b/toonz/sources/toonz/viewerpane.cpp index b38474ea..2b8b670c 100644 --- a/toonz/sources/toonz/viewerpane.cpp +++ b/toonz/sources/toonz/viewerpane.cpp @@ -1,4 +1,3 @@ -#include "viewerpane.h" // TnzCore includes #include "tconvert.h" @@ -45,6 +44,8 @@ #include "xsheetdragtool.h" #include "ruler.h" #include "menubarcommandids.h" +#include "tenv.h" +#include "cellselection.h" // Qt includes #include @@ -63,76 +64,55 @@ #include #include -enum CV_Parts { - CVPARTS_None = 0, - CVPARTS_PLAYBAR = 0x1, - CVPARTS_FRAMESLIDER = 0x4, - CVPARTS_End = 0x8, - CVPARTS_ALL = CVPARTS_PLAYBAR | CVPARTS_FRAMESLIDER -}; +#include "viewerpane.h" using namespace DVGui; +extern TEnv::IntVar EnvViewerPreviewBehavior; + +// this enum is to keep comaptibility with older versions +enum OldV_Parts { + OldVPARTS_None = 0, + OldVPARTS_PLAYBAR = 0x1, + OldVPARTS_FRAMESLIDER = 0x4, + OldVPARTS_End = 0x8, + OldVPARTS_ALL = OldVPARTS_PLAYBAR | OldVPARTS_FRAMESLIDER +}; + //============================================================================= // -// SceneViewerPanel +// BaseViewerPanel // //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 -SceneViewerPanel::SceneViewerPanel(QWidget *parent, Qt::WindowFlags flags) -#else -SceneViewerPanel::SceneViewerPanel(QWidget *parent, Qt::WFlags flags) -#endif +BaseViewerPanel::BaseViewerPanel(QWidget *parent, Qt::WindowFlags flags) : QFrame(parent) { - setFrameStyle(QFrame::StyledPanel); - setObjectName("ViewerPanel"); + TApp *app = TApp::instance(); - QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); - mainLayout->setSpacing(0); + setFrameStyle(QFrame::StyledPanel); + + m_mainLayout = new QVBoxLayout(); + m_mainLayout->setMargin(0); + m_mainLayout->setSpacing(0); // Viewer - QWidget *viewer = new QWidget(this); - QGridLayout *viewerL = new QGridLayout(viewer); - - ImageUtils::FullScreenWidget *fsWidget = - new ImageUtils::FullScreenWidget(viewer); - - fsWidget->setWidget(m_sceneViewer = new SceneViewer(fsWidget)); + m_fsWidget = new ImageUtils::FullScreenWidget(this); + m_fsWidget->setWidget(m_sceneViewer = new SceneViewer(m_fsWidget)); m_sceneViewer->setIsStyleShortcutSwitchable(); - bool ret = true; - ret = ret && connect(m_sceneViewer, SIGNAL(onZoomChanged()), - SLOT(changeWindowTitle())); - - Ruler *vRuler = new Ruler(viewer, m_sceneViewer, true); - Ruler *hRuler = new Ruler(viewer, m_sceneViewer, false); - m_sceneViewer->setRulers(vRuler, hRuler); - - viewerL->setMargin(0); - viewerL->setSpacing(0); - viewerL->addWidget(vRuler, 1, 0); - viewerL->addWidget(hRuler, 0, 1); - viewerL->addWidget(fsWidget, 1, 1, 19, 13); - viewer->setMinimumHeight(200); - viewer->setLayout(viewerL); - - mainLayout->addWidget(viewer, Qt::AlignCenter); - - TApp *app = TApp::instance(); m_keyFrameButton = new ViewerKeyframeNavigator(0, app->getCurrentFrame()); 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(mainLayout, buttonMask, false, m_keyFrameButton, + new FlipConsole(m_mainLayout, buttonMask, false, m_keyFrameButton, "SceneViewerConsole", this, true); - mainLayout->addWidget(m_flipConsole); + m_mainLayout->addWidget(m_flipConsole); m_flipConsole->enableButton(FlipConsole::eMatte, false, false); m_flipConsole->enableButton(FlipConsole::eSave, false, false); @@ -144,9 +124,20 @@ SceneViewerPanel::SceneViewerPanel(QWidget *parent, Qt::WFlags flags) m_flipConsole->enableButton(FlipConsole::eBlackBg, false, false); m_flipConsole->enableButton(FlipConsole::eWhiteBg, false, false); m_flipConsole->enableButton(FlipConsole::eCheckBg, false, false); - + m_flipConsole->setChecked(FlipConsole::eSound, true); + m_playSound = m_flipConsole->isChecked(FlipConsole::eSound); + + m_flipConsole->setFrameRate(app->getCurrentScene() + ->getScene() + ->getProperties() + ->getOutputProperties() + ->getFrameRate()); m_flipConsole->setFrameHandle(TApp::instance()->getCurrentFrame()); + bool ret = true; + // When zoom changed, only if the viewer is active, change window titl + ret = ret && connect(m_sceneViewer, SIGNAL(onZoomChanged()), + SLOT(changeWindowTitle())); ret = ret && connect(m_flipConsole, SIGNAL(playStateChanged(bool)), TApp::instance()->getCurrentFrame(), SLOT(setPlaying(bool))); @@ -157,13 +148,12 @@ SceneViewerPanel::SceneViewerPanel(QWidget *parent, Qt::WFlags flags) ret = ret && connect(m_flipConsole, SIGNAL(buttonPressed(FlipConsole::EGadget)), m_sceneViewer, SLOT(onButtonPressed(FlipConsole::EGadget))); - ret = ret && connect(m_flipConsole, SIGNAL(buttonPressed(FlipConsole::EGadget)), this, SLOT(onButtonPressed(FlipConsole::EGadget))); ret = ret && connect(m_sceneViewer, SIGNAL(previewStatusChanged()), this, - SLOT(update())); + SLOT(onPreviewStatusChanged())); ret = ret && connect(m_sceneViewer, SIGNAL(onFlipHChanged(bool)), this, SLOT(setFlipHButtonChecked(bool))); ret = ret && connect(m_sceneViewer, SIGNAL(onFlipVChanged(bool)), this, @@ -172,14 +162,10 @@ SceneViewerPanel::SceneViewerPanel(QWidget *parent, Qt::WFlags flags) ret = ret && connect(app->getCurrentScene(), SIGNAL(sceneSwitched()), this, SLOT(onSceneSwitched())); + ret = ret && connect(app, SIGNAL(activeViewerChanged()), this, + SLOT(onActiveViewerChanged())); + assert(ret); - m_flipConsole->setChecked(FlipConsole::eSound, true); - m_playSound = m_flipConsole->isChecked(FlipConsole::eSound); - m_flipConsole->setFrameRate(app->getCurrentScene() - ->getScene() - ->getProperties() - ->getOutputProperties() - ->getFrameRate()); UINT mask = 0; mask = mask | eShowVcr; @@ -192,13 +178,6 @@ SceneViewerPanel::SceneViewerPanel(QWidget *parent, Qt::WFlags flags) mask = mask & ~eShowHisto; m_flipConsole->setCustomizemask(mask); - updateFrameRange(), updateFrameMarkers(); - - setLayout(mainLayout); - - m_visiblePartsFlag = CVPARTS_ALL; - updateShowHide(); - setFocusProxy(m_sceneViewer); } @@ -208,10 +187,10 @@ SceneViewerPanel::SceneViewerPanel(QWidget *parent, Qt::WFlags flags) /*! toggle show/hide of the widgets according to m_visibleFlag */ -void SceneViewerPanel::updateShowHide() { +void BaseViewerPanel::updateShowHide() { // flip console - m_flipConsole->showHidePlaybar(m_visiblePartsFlag & CVPARTS_PLAYBAR); - m_flipConsole->showHideFrameSlider(m_visiblePartsFlag & CVPARTS_FRAMESLIDER); + m_flipConsole->showHidePlaybar(m_visiblePartsFlag & VPPARTS_PLAYBAR); + m_flipConsole->showHideFrameSlider(m_visiblePartsFlag & VPPARTS_FRAMESLIDER); update(); } @@ -219,7 +198,7 @@ void SceneViewerPanel::updateShowHide() { /*! showing the show/hide commands */ -// void SceneViewerPanel::contextMenuEvent(QContextMenuEvent *event) { +// void BaseViewerPanel::contextMenuEvent(QContextMenuEvent *event) { // QMenu *menu = new QMenu(this); // addShowHideContextMenu(menu); // menu->exec(event->globalPos()); @@ -227,7 +206,7 @@ void SceneViewerPanel::updateShowHide() { //----------------------------------------------------------------------------- -void SceneViewerPanel::addShowHideContextMenu(QMenu *menu) { +void BaseViewerPanel::addShowHideContextMenu(QMenu *menu) { QMenu *showHideMenu = menu->addMenu(tr("GUI Show / Hide")); // actions @@ -235,12 +214,12 @@ void SceneViewerPanel::addShowHideContextMenu(QMenu *menu) { QAction *frameSliderSHAct = showHideMenu->addAction(tr("Frame Slider")); playbarSHAct->setCheckable(true); - playbarSHAct->setChecked(m_visiblePartsFlag & CVPARTS_PLAYBAR); - playbarSHAct->setData((UINT)CVPARTS_PLAYBAR); + playbarSHAct->setChecked(m_visiblePartsFlag & VPPARTS_PLAYBAR); + playbarSHAct->setData((UINT)VPPARTS_PLAYBAR); frameSliderSHAct->setCheckable(true); - frameSliderSHAct->setChecked(m_visiblePartsFlag & CVPARTS_FRAMESLIDER); - frameSliderSHAct->setData((UINT)CVPARTS_FRAMESLIDER); + frameSliderSHAct->setChecked(m_visiblePartsFlag & VPPARTS_FRAMESLIDER); + frameSliderSHAct->setData((UINT)VPPARTS_FRAMESLIDER); QActionGroup *showHideActGroup = new QActionGroup(this); showHideActGroup->setExclusive(false); @@ -265,18 +244,20 @@ void SceneViewerPanel::addShowHideContextMenu(QMenu *menu) { /*! slot function for show/hide the parts */ -void SceneViewerPanel::onShowHideActionTriggered(QAction *act) { - CV_Parts part = (CV_Parts)act->data().toUInt(); - assert(part < CVPARTS_End); +void BaseViewerPanel::onShowHideActionTriggered(QAction *act) { + VP_Parts part = (VP_Parts)act->data().toUInt(); + assert(part < VPPARTS_End); m_visiblePartsFlag ^= part; updateShowHide(); } -void SceneViewerPanel::onDrawFrame(int frame, - const ImagePainter::VisualSettings &settings, - QElapsedTimer *, qint64) { +//----------------------------------------------------------------------------- + +void BaseViewerPanel::onDrawFrame(int frame, + const ImagePainter::VisualSettings &settings, + QElapsedTimer *, qint64) { TApp *app = TApp::instance(); m_sceneViewer->setVisual(settings); @@ -295,13 +276,16 @@ void SceneViewerPanel::onDrawFrame(int frame, !pr->isFrameReady( frame - 1)) // stops on last rendered frame until current is ready! { - while (frame > 1 && !pr->isFrameReady(frame - 1)) frame--; + while (frame > 0 && !pr->isFrameReady(frame - 1)) frame--; + if (frame == 0) + frame = curFrame; // if no frame is ready, I stay on current...no use + // to rewind m_flipConsole->setCurrentFrame(frame); } } // assert(frame >= 0); // frame can be negative in rare cases - if (frame != frameHandle->getFrameIndex() + 1) { + if (frame != frameHandle->getFrameIndex() + 1 && !settings.m_drawBlankFrame) { int oldFrame = frameHandle->getFrame(); frameHandle->setCurrentFrame(frame); if (!frameHandle->isPlaying() && !frameHandle->isEditingLevel() && @@ -317,22 +301,22 @@ void SceneViewerPanel::onDrawFrame(int frame, //----------------------------------------------------------------------------- -SceneViewerPanel::~SceneViewerPanel() {} - -//----------------------------------------------------------------------------- - -void SceneViewerPanel::showEvent(QShowEvent *event) { +void BaseViewerPanel::showEvent(QShowEvent *event) { TApp *app = TApp::instance(); TFrameHandle *frameHandle = app->getCurrentFrame(); TSceneHandle *sceneHandle = app->getCurrentScene(); TXshLevelHandle *levelHandle = app->getCurrentLevel(); - TObjectHandle *objectHandle = app->getCurrentObject(); TXsheetHandle *xshHandle = app->getCurrentXsheet(); - onSceneChanged(); - bool ret = true; + /*! + onSceneChanged(): called when the scene changed + - set new scene's FPS + - update the range of frame slider with a new framehandle + - set the marker + - update key frames + */ ret = ret && connect(xshHandle, SIGNAL(xsheetChanged()), this, SLOT(onSceneChanged())); ret = ret && connect(sceneHandle, SIGNAL(sceneSwitched()), this, @@ -340,90 +324,66 @@ void SceneViewerPanel::showEvent(QShowEvent *event) { ret = ret && connect(sceneHandle, SIGNAL(sceneChanged()), this, SLOT(onSceneChanged())); + /*! + changeWindowTitle(): called when the scene / level / frame is changed + - chenge the title text + */ ret = ret && connect(sceneHandle, SIGNAL(nameSceneChanged()), this, SLOT(changeWindowTitle())); - ret = - ret && connect(sceneHandle, SIGNAL(preferenceChanged(const QString &)), - m_flipConsole, SLOT(onPreferenceChanged(const QString &))); - ret = ret && connect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), this, - SLOT(onXshLevelSwitched(TXshLevel *))); ret = ret && connect(levelHandle, SIGNAL(xshLevelChanged()), this, SLOT(changeWindowTitle())); ret = ret && connect(levelHandle, SIGNAL(xshLevelTitleChanged()), this, SLOT(changeWindowTitle())); + ret = ret && connect(frameHandle, SIGNAL(frameSwitched()), this, + SLOT(changeWindowTitle())); + + // updateFrameRange(): update the frame slider's range ret = ret && connect(levelHandle, SIGNAL(xshLevelChanged()), this, SLOT(updateFrameRange())); - ret = ret && connect(frameHandle, SIGNAL(frameSwitched()), this, - SLOT(changeWindowTitle())); - ret = ret && connect(frameHandle, SIGNAL(frameSwitched()), this, - SLOT(onFrameSwitched())); + // onFrameTypeChanged(): reset the marker positions in the flip console ret = ret && connect(frameHandle, SIGNAL(frameTypeChanged()), this, SLOT(onFrameTypeChanged())); + // onXshLevelSwitched(TXshLevel*)F changeWindowTitle() + updateFrameRange() + ret = ret && connect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), this, + SLOT(onXshLevelSwitched(TXshLevel *))); + + // onFrameSwitched(): update the flipconsole according to the current frame + ret = ret && connect(frameHandle, SIGNAL(frameSwitched()), this, + SLOT(onFrameSwitched())); + ret = ret && connect(app->getCurrentTool(), SIGNAL(toolSwitched()), m_sceneViewer, SLOT(onToolSwitched())); + ret = + ret && connect(sceneHandle, SIGNAL(preferenceChanged(const QString &)), + m_flipConsole, SLOT(onPreferenceChanged(const QString &))); assert(ret); - // Aggiorno FPS al valore definito nel viewer corrente. - // frameHandle->setPreviewFrameRate(m_fpsSlider->value()); m_flipConsole->setActive(true); m_flipConsole->onPreferenceChanged(""); + + // refresh + onSceneChanged(); + changeWindowTitle(); } //----------------------------------------------------------------------------- -void SceneViewerPanel::hideEvent(QHideEvent *event) { - TApp *app = TApp::instance(); - TFrameHandle *frameHandle = app->getCurrentFrame(); - TSceneHandle *sceneHandle = app->getCurrentScene(); - TXshLevelHandle *levelHandle = app->getCurrentLevel(); - TObjectHandle *objectHandle = app->getCurrentObject(); - TXsheetHandle *xshHandle = app->getCurrentXsheet(); - - disconnect(xshHandle, SIGNAL(xsheetChanged()), this, SLOT(onSceneChanged())); - - disconnect(sceneHandle, SIGNAL(sceneChanged()), this, SLOT(onSceneChanged())); - disconnect(sceneHandle, SIGNAL(nameSceneChanged()), this, - SLOT(changeWindowTitle())); - disconnect(sceneHandle, SIGNAL(sceneSwitched()), this, - SLOT(onSceneChanged())); - disconnect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), this, - SLOT(onXshLevelSwitched(TXshLevel *))); - disconnect(levelHandle, SIGNAL(xshLevelChanged()), this, - SLOT(changeWindowTitle())); - disconnect(levelHandle, SIGNAL(xshLevelTitleChanged()), this, - SLOT(changeWindowTitle())); - disconnect(levelHandle, SIGNAL(xshLevelChanged()), this, - SLOT(updateFrameRange())); - - disconnect(frameHandle, SIGNAL(frameSwitched()), this, - SLOT(changeWindowTitle())); - disconnect(frameHandle, SIGNAL(frameSwitched()), this, - SLOT(onFrameSwitched())); - disconnect(frameHandle, SIGNAL(frameTypeChanged()), this, - SLOT(onFrameTypeChanged())); - - disconnect(app->getCurrentTool(), SIGNAL(toolSwitched()), m_sceneViewer, - SLOT(onToolSwitched())); - disconnect(app->getCurrentScene(), SIGNAL(preferenceChanged(const QString &)), - m_flipConsole, SLOT(onPreferenceChanged(const QString &))); +void BaseViewerPanel::hideEvent(QHideEvent *event) { + TApp *app = TApp::instance(); + disconnect(app->getCurrentFrame(), nullptr, this, nullptr); + disconnect(app->getCurrentScene(), nullptr, this, nullptr); + disconnect(app->getCurrentLevel(), nullptr, this, nullptr); + disconnect(app->getCurrentXsheet(), nullptr, this, nullptr); m_flipConsole->setActive(false); } //----------------------------------------------------------------------------- -void SceneViewerPanel::resizeEvent(QResizeEvent *e) { - QWidget::resizeEvent(e); - repaint(); - m_sceneViewer->update(); -} - -//----------------------------------------------------------------------------- - -void SceneViewerPanel::initializeTitleBar(TPanelTitleBar *titleBar) { +void BaseViewerPanel::initializeTitleBar(TPanelTitleBar *titleBar) { bool ret = true; TPanelTitleBarButtonSet *viewModeButtonSet; @@ -483,14 +443,14 @@ void SceneViewerPanel::initializeTitleBar(TPanelTitleBar *titleBar) { button = new TPanelTitleBarButton(titleBar, getIconThemePath("actions/20/pane_3d.svg")); button->setToolTip(tr("3D View")); - x += +1 + iconWidth; + x += 1 + iconWidth; titleBar->add(QPoint(x, 0), button); button->setButtonSet(viewModeButtonSet, SceneViewer::CAMERA3D_REFERENCE); button = new TPanelTitleBarButton( titleBar, getIconThemePath("actions/20/pane_cam.svg")); button->setToolTip(tr("Camera View")); - x += +1 + iconWidth; + x += 1 + iconWidth; titleBar->add(QPoint(x, 0), button); button->setButtonSet(viewModeButtonSet, SceneViewer::CAMERA_REFERENCE); @@ -512,36 +472,57 @@ void SceneViewerPanel::initializeTitleBar(TPanelTitleBar *titleBar) { titleBar, getIconThemePath("actions/20/pane_freeze.svg")); x += 10 + iconWidth; - button->setToolTip(tr("Freeze")); // RC1 + button->setToolTip(tr("Freeze")); titleBar->add(QPoint(x, 0), button); ret = ret && connect(button, SIGNAL(toggled(bool)), m_sceneViewer, SLOT(freeze(bool))); // preview toggles - m_previewButton = new TPanelTitleBarButton( + m_previewButton = new TPanelTitleBarButtonForPreview( titleBar, getIconThemePath("actions/20/pane_preview.svg")); x += 10 + iconWidth; titleBar->add(QPoint(x, 0), m_previewButton); m_previewButton->setToolTip(tr("Preview")); - ret = ret && connect(m_previewButton, SIGNAL(toggled(bool)), - SLOT(enableFullPreview(bool))); - m_subcameraPreviewButton = new TPanelTitleBarButton( + // ret = ret && connect(m_previewButton, SIGNAL(toggled(bool)), + // SLOT(enableFullPreview(bool))); + + m_subcameraPreviewButton = new TPanelTitleBarButtonForPreview( titleBar, getIconThemePath("actions/20/pane_subpreview.svg")); - x += +1 + 24; // width of pane_preview.svg = 24px + x += 1 + 24; // width of pane_preview.svg = 24px titleBar->add(QPoint(x, 0), m_subcameraPreviewButton); m_subcameraPreviewButton->setToolTip(tr("Sub-camera Preview")); - ret = ret && connect(m_subcameraPreviewButton, SIGNAL(toggled(bool)), - SLOT(enableSubCameraPreview(bool))); + + // ret = ret && connect(m_subcameraPreviewButton, SIGNAL(toggled(bool)), + // SLOT(enableSubCameraPreview(bool))); assert(ret); } //----------------------------------------------------------------------------- -void SceneViewerPanel::enableFullPreview(bool enabled) { +void BaseViewerPanel::getPreviewButtonStates(bool &prev, bool &subCamPrev) { + prev = m_previewButton->isChecked(); + subCamPrev = m_subcameraPreviewButton->isChecked(); +} + +//----------------------------------------------------------------------------- + +void BaseViewerPanel::enableFullPreview(bool enabled) { m_subcameraPreviewButton->setPressed(false); + if (CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->isChecked()) + CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->setChecked(false); + + if (!enabled && EnvViewerPreviewBehavior == 2 && + FlipConsole::getCurrent() == m_flipConsole && + TApp::instance()->getCurrentFrame()->isPlaying()) + CommandManager::instance()->execute(MI_Pause); + m_sceneViewer->enablePreview(enabled ? SceneViewer::FULL_PREVIEW : SceneViewer::NO_PREVIEW); m_flipConsole->setProgressBarStatus( @@ -551,8 +532,20 @@ void SceneViewerPanel::enableFullPreview(bool enabled) { //----------------------------------------------------------------------------- -void SceneViewerPanel::enableSubCameraPreview(bool enabled) { +void BaseViewerPanel::enableSubCameraPreview(bool enabled) { m_previewButton->setPressed(false); + if (CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->isChecked()) + CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->setChecked(false); + + if (!enabled && EnvViewerPreviewBehavior == 2 && + FlipConsole::getCurrent() == m_flipConsole && + TApp::instance()->getCurrentFrame()->isPlaying()) + CommandManager::instance()->execute(MI_Pause); + m_sceneViewer->enablePreview(enabled ? SceneViewer::SUBCAMERA_PREVIEW : SceneViewer::NO_PREVIEW); m_flipConsole->setProgressBarStatus( @@ -562,7 +555,7 @@ void SceneViewerPanel::enableSubCameraPreview(bool enabled) { //----------------------------------------------------------------------------- -void SceneViewerPanel::enableFlipConsoleForCamerastand(bool on) { +void BaseViewerPanel::enableFlipConsoleForCamerastand(bool on) { m_flipConsole->enableButton(FlipConsole::eMatte, on, false); m_flipConsole->enableButton(FlipConsole::eSave, on, false); m_flipConsole->enableButton(FlipConsole::eCompare, on, false); @@ -575,33 +568,49 @@ void SceneViewerPanel::enableFlipConsoleForCamerastand(bool on) { m_flipConsole->enableButton(FlipConsole::eCheckBg, on, false); m_flipConsole->enableProgressBar(on); - // m_flipConsole->enableBlanks(on); // blank frames are now always enabled - // m_flipConsole->update(); update(); } //----------------------------------------------------------------------------- -void SceneViewerPanel::onXshLevelSwitched(TXshLevel *) { +void BaseViewerPanel::onXshLevelSwitched(TXshLevel *) { changeWindowTitle(); m_sceneViewer->update(); - // If the level switched by using the level choose combo box in the film - // strip, - // the current level switches without change in the frame type (level or - // scene). + // If the level is switched by using the combobox in the film strip, the + // current level switches without change in the frame type (level or scene). // For such case, update the frame range of the console here. if (TApp::instance()->getCurrentFrame()->isEditingLevel()) updateFrameRange(); } //----------------------------------------------------------------------------- -void SceneViewerPanel::onPlayingStatusChanged(bool playing) { +void BaseViewerPanel::onPlayingStatusChanged(bool playing) { if (playing) { m_playing = true; } else { m_playing = false; m_first = true; } + + // if preview behavior mode is "selected cells", release preview mode when + // stopped + if (!playing && EnvViewerPreviewBehavior == 2 && + FlipConsole::getCurrent() == m_flipConsole && + !Previewer::instance(m_sceneViewer->getPreviewMode() == + SceneViewer::SUBCAMERA_PREVIEW) + ->isBusy()) { + if (CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->isChecked()) + CommandManager::instance()->getAction(MI_ToggleViewerPreview)->trigger(); + else if (CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->isChecked()) + CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->trigger(); + } + if (Preferences::instance()->getOnionSkinDuringPlayback()) return; OnionSkinMask osm = TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask(); @@ -624,14 +633,17 @@ void SceneViewerPanel::onPlayingStatusChanged(bool playing) { //----------------------------------------------------------------------------- -void SceneViewerPanel::changeWindowTitle() { - TApp *app = TApp::instance(); - // zoom = sqrt(m_sceneViewer->getViewMatrix().det()); +void BaseViewerPanel::changeWindowTitle() { // vmF + TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); if (!scene) return; if (!parentWidget()) return; int frame = app->getCurrentFrame()->getFrame(); + + // put the titlebar texts in this string QString name; + + // if the frame type is "scene editing" if (app->getCurrentFrame()->isEditingScene()) { TProject *project = scene->getProject(); QString sceneName = QString::fromStdWString(scene->getSceneName()); @@ -671,7 +683,9 @@ void SceneViewerPanel::changeWindowTitle() { QString imageName = QString::fromStdWString(fp.withFrame(cell.m_frameId).getWideString()); name = name + tr(" :: Level: ") + imageName; - } else { + } + // if the frame type is "level editing" + else { TXshLevel *level = app->getCurrentLevel()->getLevel(); if (level) { TFilePath fp(level->getName()); @@ -696,8 +710,9 @@ void SceneViewerPanel::changeWindowTitle() { } //----------------------------------------------------------------------------- - -void SceneViewerPanel::updateFrameRange() { +/*! update the frame range according to the current frame type + */ +void BaseViewerPanel::updateFrameRange() { TFrameHandle *fh = TApp::instance()->getCurrentFrame(); int frameIndex = fh->getFrameIndex(); int maxFrameIndex = fh->getMaxFrameIndex(); @@ -707,7 +722,7 @@ void SceneViewerPanel::updateFrameRange() { //----------------------------------------------------------------------------- -void SceneViewerPanel::updateFrameMarkers() { +void BaseViewerPanel::updateFrameMarkers() { int fromIndex, toIndex, dummy; XsheetGUI::getPlayRange(fromIndex, toIndex, dummy); TFrameHandle *fh = TApp::instance()->getCurrentFrame(); @@ -720,7 +735,7 @@ void SceneViewerPanel::updateFrameMarkers() { //----------------------------------------------------------------------------- -void SceneViewerPanel::onSceneChanged() { +void BaseViewerPanel::onSceneChanged() { updateFrameRange(); updateFrameMarkers(); changeWindowTitle(); @@ -735,8 +750,6 @@ void SceneViewerPanel::onSceneChanged() { ->getOutputProperties() ->getFrameRate(), false); - // vinz: perche veniva fatto? - // m_flipConsole->updateCurrentFPS(scene->getProperties()->getOutputProperties()->getFrameRate()); int frameIndex = TApp::instance()->getCurrentFrame()->getFrameIndex(); if (m_keyFrameButton->getCurrentFrame() != frameIndex) @@ -746,7 +759,7 @@ void SceneViewerPanel::onSceneChanged() { //----------------------------------------------------------------------------- -void SceneViewerPanel::onSceneSwitched() { +void BaseViewerPanel::onSceneSwitched() { m_previewButton->setPressed(false); m_subcameraPreviewButton->setPressed(false); enableFlipConsoleForCamerastand(false); @@ -764,7 +777,7 @@ void SceneViewerPanel::onSceneSwitched() { //----------------------------------------------------------------------------- -void SceneViewerPanel::onFrameSwitched() { +void BaseViewerPanel::onFrameSwitched() { int frameIndex = TApp::instance()->getCurrentFrame()->getFrameIndex(); m_flipConsole->setCurrentFrame(frameIndex + 1); if (m_keyFrameButton->getCurrentFrame() != frameIndex) @@ -780,8 +793,9 @@ void SceneViewerPanel::onFrameSwitched() { } //----------------------------------------------------------------------------- - -void SceneViewerPanel::onFrameTypeChanged() { +/*! reset the marker positions in the flip console + */ +void BaseViewerPanel::onFrameTypeChanged() { if (TApp::instance()->getCurrentFrame()->getFrameType() == TFrameHandle::LevelFrame) { if (m_sceneViewer->isPreviewEnabled()) { @@ -803,7 +817,7 @@ void SceneViewerPanel::onFrameTypeChanged() { //----------------------------------------------------------------------------- -void SceneViewerPanel::playAudioFrame(int frame) { +void BaseViewerPanel::playAudioFrame(int frame) { if (m_first) { m_first = false; m_fps = TApp::instance() @@ -828,7 +842,7 @@ void SceneViewerPanel::playAudioFrame(int frame) { //----------------------------------------------------------------------------- -bool SceneViewerPanel::hasSoundtrack() { +bool BaseViewerPanel::hasSoundtrack() { if (m_sound != NULL) { m_sound = NULL; m_hasSoundtrack = false; @@ -855,21 +869,21 @@ bool SceneViewerPanel::hasSoundtrack() { } } -void SceneViewerPanel::onButtonPressed(FlipConsole::EGadget button) { +void BaseViewerPanel::onButtonPressed(FlipConsole::EGadget button) { if (button == FlipConsole::eSound) { m_playSound = !m_playSound; } } -void SceneViewerPanel::setFlipHButtonChecked(bool checked) { +void BaseViewerPanel::setFlipHButtonChecked(bool checked) { m_flipConsole->setChecked(FlipConsole::eFlipHorizontal, checked); } -void SceneViewerPanel::setFlipVButtonChecked(bool checked) { +void BaseViewerPanel::setFlipVButtonChecked(bool checked) { m_flipConsole->setChecked(FlipConsole::eFlipVertical, checked); } -void SceneViewerPanel::changeSceneFps(int value) { +void BaseViewerPanel::changeSceneFps(int value) { double oldFps = TApp::instance() ->getCurrentScene() ->getScene() @@ -892,7 +906,7 @@ void SceneViewerPanel::changeSceneFps(int value) { //----------------------------------------------------------------------------- -void SceneViewerPanel::setVisiblePartsFlag(UINT flag) { +void BaseViewerPanel::setVisiblePartsFlag(UINT flag) { m_visiblePartsFlag = flag; updateShowHide(); } @@ -900,15 +914,17 @@ void SceneViewerPanel::setVisiblePartsFlag(UINT flag) { //----------------------------------------------------------------------------- // SaveLoadQSettings -void SceneViewerPanel::save(QSettings &settings, bool forPopupIni) const { - settings.setValue("visibleParts", m_visiblePartsFlag); +void BaseViewerPanel::save(QSettings &settings, bool forPopupIni) const { + settings.setValue("viewerVisibleParts", m_visiblePartsFlag); settings.setValue("consoleParts", m_flipConsole->getCustomizeMask()); } //----------------------------------------------------------------------------- -void SceneViewerPanel::load(QSettings &settings) { - m_visiblePartsFlag = settings.value("visibleParts", CVPARTS_ALL).toUInt(); +void BaseViewerPanel::load(QSettings &settings) { + checkOldVersionVisblePartsFlags(settings); + m_visiblePartsFlag = + settings.value("viewerVisibleParts", m_visiblePartsFlag).toUInt(); updateShowHide(); UINT mask = 0; mask = mask | eShowVcr; @@ -922,3 +938,177 @@ void SceneViewerPanel::load(QSettings &settings) { m_flipConsole->setCustomizemask( settings.value("consoleParts", mask).toUInt()); } + +//----------------------------------------------------------------------------- + +void BaseViewerPanel::onPreviewStatusChanged() { + // if preview behavior mode is "selected cells", play once the all frames are + // completed + if (EnvViewerPreviewBehavior == 2 && + FlipConsole::getCurrent() == m_flipConsole && + !TApp::instance()->getCurrentFrame()->isPlaying() && + m_sceneViewer->isPreviewEnabled() && + !Previewer::instance(m_sceneViewer->getPreviewMode() == + SceneViewer::SUBCAMERA_PREVIEW) + ->isBusy()) { + TCellSelection *cellSel = + dynamic_cast(TSelection::getCurrent()); + if (cellSel && !cellSel->isEmpty()) { + int r0, c0, r1, c1; + cellSel->getSelectedCells(r0, c0, r1, c1); + if (r0 < r1) { + // check if all frame range is rendered. this check is needed since + // isBusy() will not be true just after the preview is triggered + for (int r = r0; r <= r1; r++) { + if (!Previewer::instance(m_sceneViewer->getPreviewMode() == + SceneViewer::SUBCAMERA_PREVIEW) + ->isFrameReady(r)) { + update(); + return; + } + } + m_flipConsole->setStopAt(r1 + 1); + m_flipConsole->setStartAt(r0 + 1); + TApp::instance()->getCurrentFrame()->setFrame(r0); + CommandManager::instance()->execute(MI_Loop); + } + } + } + + update(); +} + +//----------------------------------------------------------------------------- +// sync preview commands and buttons states when the viewer becomes active + +void BaseViewerPanel::onActiveViewerChanged() { + bool ret = true; + if (TApp::instance()->getActiveViewer() == m_sceneViewer) { + ret = ret && + connect(m_previewButton, SIGNAL(toggled(bool)), + CommandManager::instance()->getAction(MI_ToggleViewerPreview), + SLOT(trigger())); + ret = ret && + connect(CommandManager::instance()->getAction(MI_ToggleViewerPreview), + SIGNAL(triggered(bool)), m_previewButton, + SLOT(setPressed(bool))); + ret = ret && connect(m_subcameraPreviewButton, SIGNAL(toggled(bool)), + CommandManager::instance()->getAction( + MI_ToggleViewerSubCameraPreview), + SLOT(trigger())); + ret = ret && connect(CommandManager::instance()->getAction( + MI_ToggleViewerSubCameraPreview), + SIGNAL(triggered(bool)), m_subcameraPreviewButton, + SLOT(setPressed(bool))); + m_isActive = true; + } else if (m_isActive) { + ret = ret && disconnect(m_previewButton, SIGNAL(toggled(bool)), + CommandManager::instance()->getAction( + MI_ToggleViewerPreview), + SLOT(trigger())); + ret = ret && + disconnect( + CommandManager::instance()->getAction(MI_ToggleViewerPreview), + SIGNAL(triggered(bool)), m_previewButton, SLOT(setPressed(bool))); + ret = ret && disconnect(m_subcameraPreviewButton, SIGNAL(toggled(bool)), + CommandManager::instance()->getAction( + MI_ToggleViewerSubCameraPreview), + SLOT(trigger())); + ret = ret && disconnect(CommandManager::instance()->getAction( + MI_ToggleViewerSubCameraPreview), + SIGNAL(triggered(bool)), m_subcameraPreviewButton, + SLOT(setPressed(bool))); + m_isActive = false; + } + assert(ret); +} + +//============================================================================= +// +// SceneViewerPanel +// +//----------------------------------------------------------------------------- + +SceneViewerPanel::SceneViewerPanel(QWidget *parent, Qt::WindowFlags flags) + : BaseViewerPanel(parent, flags) { + setObjectName("ViewerPanel"); + setMinimumHeight(200); + + Ruler *vRuler = new Ruler(this, m_sceneViewer, true); + Ruler *hRuler = new Ruler(this, m_sceneViewer, false); + m_sceneViewer->setRulers(vRuler, hRuler); + + { + QGridLayout *viewerL = new QGridLayout(); + viewerL->setMargin(0); + viewerL->setSpacing(0); + { + viewerL->addWidget(vRuler, 1, 0); + viewerL->addWidget(hRuler, 0, 1); + viewerL->addWidget(m_fsWidget, 1, 1); + } + viewerL->setRowStretch(1, 1); + viewerL->setColumnStretch(1, 1); + m_mainLayout->insertLayout(0, viewerL, 1); + } + setLayout(m_mainLayout); + // initial state of the parts + m_visiblePartsFlag = VPPARTS_ALL; + updateShowHide(); +} + +//----------------------------------------------------------------------------- + +void SceneViewerPanel::checkOldVersionVisblePartsFlags(QSettings &settings) { + if (settings.contains("viewerVisibleParts") || + !settings.contains("visibleParts")) + return; + UINT oldVisiblePartsFlag = + settings.value("visibleParts", OldVPARTS_ALL).toUInt(); + m_visiblePartsFlag = VPPARTS_None; + if (oldVisiblePartsFlag & OldVPARTS_PLAYBAR) + m_visiblePartsFlag |= VPPARTS_PLAYBAR; + if (oldVisiblePartsFlag & OldVPARTS_FRAMESLIDER) + m_visiblePartsFlag |= VPPARTS_FRAMESLIDER; +} + +//========================================================= + +class ViewerPreviewCommands : public QObject { +public: + ViewerPreviewCommands() { + setCommandHandler("MI_ToggleViewerPreview", this, + &ViewerPreviewCommands::onPreview); + setCommandHandler("MI_ToggleViewerSubCameraPreview", this, + &ViewerPreviewCommands::onSubCameraPreview); + } + + void onPreview(); + void onSubCameraPreview(); +}; + +void ViewerPreviewCommands::onPreview() { + SceneViewer *activeViewer = TApp::instance()->getActiveViewer(); + if (!activeViewer) return; + BaseViewerPanel *bvp = qobject_cast( + activeViewer->parentWidget()->parentWidget()); + if (!bvp) return; + bool on = CommandManager::instance() + ->getAction(MI_ToggleViewerPreview) + ->isChecked(); + bvp->enableFullPreview(on); +} + +void ViewerPreviewCommands::onSubCameraPreview() { + SceneViewer *activeViewer = TApp::instance()->getActiveViewer(); + if (!activeViewer) return; + BaseViewerPanel *bvp = qobject_cast( + activeViewer->parentWidget()->parentWidget()); + if (!bvp) return; + bool on = CommandManager::instance() + ->getAction(MI_ToggleViewerSubCameraPreview) + ->isChecked(); + bvp->enableSubCameraPreview(on); +} + +ViewerPreviewCommands viewerPreviewCommands; diff --git a/toonz/sources/toonz/viewerpane.h b/toonz/sources/toonz/viewerpane.h index 9131ae86..c5af0b3c 100644 --- a/toonz/sources/toonz/viewerpane.h +++ b/toonz/sources/toonz/viewerpane.h @@ -27,19 +27,32 @@ class Ruler; class FlipConsole; class TXshLevel; -class SceneViewerPanel final : public QFrame, - public FlipConsoleOwner, - public SaveLoadQSettings { - Q_OBJECT +enum VP_Parts { + VPPARTS_None = 0, + VPPARTS_PLAYBAR = 0x1, + VPPARTS_FRAMESLIDER = 0x2, + VPPARTS_TOOLBAR = 0x4, + VPPARTS_TOOLOPTIONS = 0x8, + VPPARTS_End = 0x10, + VPPARTS_ALL = VPPARTS_PLAYBAR | VPPARTS_FRAMESLIDER, + VPPARTS_COMBO_ALL = VPPARTS_ALL | VPPARTS_TOOLBAR | VPPARTS_TOOLOPTIONS +}; + +class BaseViewerPanel : public QFrame, + public FlipConsoleOwner, + public SaveLoadQSettings { + Q_OBJECT +protected: friend class SceneViewer; + QVBoxLayout *m_mainLayout; SceneViewer *m_sceneViewer; + ImageUtils::FullScreenWidget *m_fsWidget; FlipConsole *m_flipConsole; ViewerKeyframeNavigator *m_keyFrameButton; - TPanelTitleBarButtonSet *m_referenceModeBs; - TPanelTitleBarButton *m_previewButton; - TPanelTitleBarButton *m_subcameraPreviewButton; + TPanelTitleBarButtonForPreview *m_previewButton; + TPanelTitleBarButtonForPreview *m_subcameraPreviewButton; bool m_onionSkinActive = false; UINT m_visiblePartsFlag; bool m_playSound = true; @@ -51,18 +64,17 @@ class SceneViewerPanel final : public QFrame, bool m_first = true; TSoundTrack *m_sound = NULL; + bool m_isActive = false; + public: -#if QT_VERSION >= 0x050500 - SceneViewerPanel(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - SceneViewerPanel(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif - ~SceneViewerPanel(); + BaseViewerPanel(QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~BaseViewerPanel() {} + + virtual void updateShowHide(); + virtual void addShowHideContextMenu(QMenu *); // toggle show/hide of the widgets according to m_visiblePartsFlag void setVisiblePartsFlag(UINT flag); - void updateShowHide(); - void addShowHideContextMenu(QMenu *); void onDrawFrame(int frame, const ImagePainter::VisualSettings &settings, QElapsedTimer *timer, qint64 targetInstant) override; @@ -81,30 +93,30 @@ public: void initializeTitleBar(TPanelTitleBar *titleBar); + void getPreviewButtonStates(bool &prev, bool &subCamPrev); + protected: + // void contextMenuEvent(QContextMenuEvent *event) override; void showEvent(QShowEvent *) override; void hideEvent(QHideEvent *) override; - void resizeEvent(QResizeEvent *) override; - void createFrameToolBar(); - void createPlayToolBar(); - void addColorMaskButton(QWidget *parent, const char *iconSVGName, int id); - // reimplementation of TPanel::widgetFocusOnEnter - void enableFlipConsoleForCamerastand(bool on); void playAudioFrame(int frame); bool hasSoundtrack(); - // void contextMenuEvent(QContextMenuEvent *event) override; + + virtual void checkOldVersionVisblePartsFlags(QSettings &settings) = 0; public slots: void changeWindowTitle(); + void updateFrameRange(); void onSceneChanged(); void onXshLevelSwitched(TXshLevel *); - void updateFrameRange(); void updateFrameMarkers(); void onButtonPressed(FlipConsole::EGadget button); void setFlipHButtonChecked(bool checked); void setFlipVButtonChecked(bool checked); + void enableFullPreview(bool enabled); + void enableSubCameraPreview(bool enabled); void changeSceneFps(int value); protected slots: @@ -115,8 +127,19 @@ protected slots: void onPlayingStatusChanged(bool playing); // for showing/hiding the parts void onShowHideActionTriggered(QAction *); - void enableFullPreview(bool enabled); - void enableSubCameraPreview(bool enabled); + void onPreviewStatusChanged(); + void onActiveViewerChanged(); +}; + +class SceneViewerPanel final : public BaseViewerPanel { + Q_OBJECT +public: + SceneViewerPanel(QWidget *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); + ~SceneViewerPanel() {} + +protected: + void checkOldVersionVisblePartsFlags(QSettings &settings) override; }; #endif diff --git a/toonz/sources/toonz/xshcellviewer.cpp b/toonz/sources/toonz/xshcellviewer.cpp index c671b970..7ce026c7 100644 --- a/toonz/sources/toonz/xshcellviewer.cpp +++ b/toonz/sources/toonz/xshcellviewer.cpp @@ -704,13 +704,12 @@ void RenameCellField::showInRowCol(int row, int col, bool multiColumnSelected) { // Ex. 12 -> 1B 21 -> 2A 30 -> 3 if (Preferences::instance()->isShowFrameNumberWithLettersEnabled() && cell.m_level->getType() != TXshLevelType::SND_TXT_XSHLEVEL) - setText( - (fid.isEmptyFrame() || fid.isNoFrame()) - ? QString::fromStdWString(levelName) + setText((fid.isEmptyFrame() || fid.isNoFrame()) + ? QString::fromStdWString(levelName) : (multiColumnSelected) - ? m_viewer->getFrameNumberWithLetters(fid.getNumber()) - : QString::fromStdWString(levelName) + QString(" ") + - m_viewer->getFrameNumberWithLetters(fid.getNumber())); + ? m_viewer->getFrameNumberWithLetters(fid.getNumber()) + : QString::fromStdWString(levelName) + QString(" ") + + m_viewer->getFrameNumberWithLetters(fid.getNumber())); else { QString frameNumber(""); if (fid.getNumber() > 0) frameNumber = QString::number(fid.getNumber()); @@ -727,12 +726,10 @@ void RenameCellField::showInRowCol(int row, int col, bool multiColumnSelected) { } // other level types else { - setText((frameNumber.isEmpty()) - ? QString::fromStdWString(levelName) - : (multiColumnSelected) - ? frameNumber - : QString::fromStdWString(levelName) + QString(" ") + - frameNumber); + setText((frameNumber.isEmpty()) ? QString::fromStdWString(levelName) + : (multiColumnSelected) ? frameNumber + : QString::fromStdWString(levelName) + + QString(" ") + frameNumber); } } selectAll(); @@ -867,28 +864,6 @@ void RenameCellField::renameCell() { bool animationSheetEnabled = Preferences::instance()->isAnimationSheetEnabled(); - /*bool levelDefined = - xsheet->getCell(m_row, m_col).getSimpleLevel() != 0 || - m_row > 0 && xsheet->getCell(m_row - 1, m_col).getSimpleLevel() != 0; - - if (animationSheetEnabled && levelDefined) { - TXshCell cell = xsheet->getCell(m_row, m_col); - TXshSimpleLevel *sl = cell.getSimpleLevel(); - if (sl) { - QRegExp fidRe("([0-9]+)([a-z]?)"); - if (fidRe.exactMatch(s)) { -#if QT_VERSION >= 0x050500 - fid = TFrameId(fidRe.cap(1).toInt(), - fidRe.cap(2) == "" ? 0 : fidRe.cap(2).toLatin1()[0]); -#else - fid = TFrameId(fidRe.cap(1).toInt(), - fidRe.cap(2) == "" ? 0 : fidRe.cap(2).toAscii()[0]); -#endif - FilmstripCmd::renumberDrawing(sl, cell.m_frameId, fid); - } - } - return; - }*/ TCellSelection *cellSelection = dynamic_cast( TApp::instance()->getCurrentSelection()->getSelection()); if (!cellSelection) return; @@ -1210,11 +1185,7 @@ void RenameCellField::onXsheetChanged() { // CellArea //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 CellArea::CellArea(XsheetViewer *parent, Qt::WindowFlags flags) -#else -CellArea::CellArea(XsheetViewer *parent, Qt::WFlags flags) -#endif : QWidget(parent, flags) , m_viewer(parent) , m_levelExtenderRect() @@ -1266,20 +1237,26 @@ void CellArea::drawFrameSeparator(QPainter &p, int row, int col, bool isAfterSecMarkers = secDistance > 0 && ((row - offset) % secDistance) == 0 && row != 0; - QColor color = (isAfterMarkers || isAfterSecMarkers) - ? m_viewer->getMarkerLineColor() - : m_viewer->getLightLineColor(); - double lineWidth = (isAfterSecMarkers) ? 3. : 1.; + TCellSelection *cellSelection = m_viewer->getCellSelection(); + bool isSelected = cellSelection->isCellSelected(row, col); + + QColor color = (isAfterMarkers || isAfterSecMarkers) + ? (isSelected) ? m_viewer->getSelectedMarkerLineColor() + : (isAfterSecMarkers) ? m_viewer->getSecMarkerLineColor() + : m_viewer->getMarkerLineColor() + : m_viewer->getLightLineColor(); + double lineWidth = (isAfterSecMarkers) ? 3. + : (secDistance > 0 && isAfterMarkers) ? 2. + : 1.; int frameAxis = m_viewer->rowToFrameAxis(row); int handleSize = - (emptyFrame) - ? 0 - : (o->isVerticalTimeline()) - ? (isAfterMarkers || isAfterSecMarkers) - ? 0 - : -1 // o->rect(PredefinedRect::DRAG_HANDLE_CORNER).width() - : -1; // o->rect(PredefinedRect::DRAG_HANDLE_CORNER).height(); + (emptyFrame) ? 0 + : (o->isVerticalTimeline()) + ? (isAfterMarkers || isAfterSecMarkers) + ? 0 + : -1 // o->rect(PredefinedRect::DRAG_HANDLE_CORNER).width() + : -1; // o->rect(PredefinedRect::DRAG_HANDLE_CORNER).height(); QLine horizontalLine = m_viewer->orientation()->horizontalLine( frameAxis, layerAxisRange.adjusted(handleSize - 1, 1)); @@ -1500,7 +1477,7 @@ void CellArea::drawSelectionBackground(QPainter &p) const { int newSelCol1 = std::min(selCol0, selCol1); selectionRect = m_viewer->rangeToXYRect( CellRange(CellPosition(selRow0, newSelCol0), - CellPosition(selRow1 + 1, newSelCol1 - 1))); + CellPosition(selRow1 + 1, newSelCol1 - 1))); } p.fillRect(selectionRect, QBrush(m_viewer->getSelectedEmptyCellColor())); @@ -1888,11 +1865,11 @@ void CellArea::drawFrameMarker(QPainter &p, const QPoint &xy, QColor color, QColor outlineColor = Qt::black; QPoint frameAdj = m_viewer->getFrameZoomAdjustment(); QRect dotRect = (isCamera) - ? m_viewer->orientation() + ? m_viewer->orientation() ->rect(PredefinedRect::CAMERA_FRAME_MARKER_AREA) .translated(xy) .translated(-frameAdj / 2) - : m_viewer->orientation() + : m_viewer->orientation() ->rect(PredefinedRect::FRAME_MARKER_AREA) .translated(xy) .translated(-frameAdj / 2); @@ -2232,10 +2209,12 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, cell.getFrameId().getNumber() - 1 >= cl->getFrameCount() && !Preferences::instance()->isImplicitHoldEnabled()) isRed = true; - QColor penColor = - isRed ? QColor(m_viewer->getErrorTextColor()) : m_viewer->getTextColor(); + QColor penColor = isRed ? QColor(m_viewer->getErrorTextColor()) + : isSelected ? m_viewer->getSelectedTextColor() + : m_viewer->getTextColor(); p.setPen(penColor); +/* QString fontName = Preferences::instance()->getInterfaceFont(); if (fontName == "") { #ifdef _WIN32 @@ -2245,6 +2224,8 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, #endif } static QFont font(fontName, -1, QFont::Normal); + */ + QFont font = p.font(); font.setPixelSize(XSHEET_FONT_PX_SIZE); p.setFont(font); @@ -2329,14 +2310,8 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, std::wstring levelName = cell.m_level->getName(); QString text = QString::fromStdWString(levelName); QFontMetrics fm(font); -#if QT_VERSION >= 0x050500 - // QFontMetrics fm(font); QString elidaName = elideText(text, fm, nameRect.width() - fm.width(fnum), QString("~")); -#else - QString elidaName = - elideText(text, font, nameRect.width() - fm.width(fnum)); -#endif p.drawText(nameRect, Qt::AlignLeft | Qt::AlignBottom, elidaName); } } @@ -2495,11 +2470,7 @@ void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { int charWidth = metric.width(text, 1); if ((charWidth * 2) > nameRect.width()) nameRect.adjust(-2, 0, 4, 0); -#if QT_VERSION >= 0x050500 QString elidaName = elideText(text, metric, nameRect.width(), "~"); -#else - QString elidaName = elideText(text, font, nameRect.width(), "~"); -#endif if (!sameLevel || prevCell.m_frameId != cell.m_frameId) p.drawText(nameRect, Qt::AlignLeft | Qt::AlignBottom, elidaName); @@ -3018,14 +2989,9 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, } } - QString text = QString::fromStdWString(levelName); -#if QT_VERSION >= 0x050500 + QString text = QString::fromStdWString(levelName); QString elidaName = elideText( text, fm, nameRect.width() - fm.width(numberStr) - 2, QString("~")); -#else - QString elidaName = elideText( - text, font, nameRect.width() - fm.width(numberStr) - 2, QString("~")); -#endif if (!sameLevel || isAfterMarkers || prevCell.getFrameId().isStopFrame()) p.drawText(nameRect, Qt::AlignLeft | Qt::AlignBottom, elidaName); diff --git a/toonz/sources/toonz/xshcellviewer.h b/toonz/sources/toonz/xshcellviewer.h index 924dda48..48cc55d1 100644 --- a/toonz/sources/toonz/xshcellviewer.h +++ b/toonz/sources/toonz/xshcellviewer.h @@ -151,11 +151,7 @@ class CellArea final : public QWidget { void updateKeyHighlight(int row, int col); public: -#if QT_VERSION >= 0x050500 - CellArea(XsheetViewer *parent, Qt::WindowFlags flags = 0); -#else - CellArea(XsheetViewer *parent, Qt::WFlags flags = 0); -#endif + CellArea(XsheetViewer *parent, Qt::WindowFlags flags = Qt::WindowFlags()); ~CellArea(); void mouseMoveEvent(QMouseEvent *event) override; diff --git a/toonz/sources/toonz/xshcolumnviewer.cpp b/toonz/sources/toonz/xshcolumnviewer.cpp index 0e61b2b7..d123d5d7 100644 --- a/toonz/sources/toonz/xshcolumnviewer.cpp +++ b/toonz/sources/toonz/xshcolumnviewer.cpp @@ -235,11 +235,7 @@ static void getVolumeCursorRect(QRect &out, double volume, // MotionPathMenu //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 MotionPathMenu::MotionPathMenu(QWidget *parent, Qt::WindowFlags flags) -#else -MotionPathMenu::MotionPathMenu(QWidget *parent, Qt::WFlags flags) -#endif : QWidget(parent, flags) , m_mDeleteRect(QRect(0, 0, ColumnWidth - 13, RowHeight)) , m_mNormalRect(QRect(0, RowHeight, ColumnWidth - 13, RowHeight)) @@ -482,9 +478,7 @@ void ChangeObjectParent::refresh() { : viewer->getOtherCameraColor(); } else if (id.isColumn() && (!xsh->isColumnEmpty(index))) { TXshColumn *colx = xsh->getColumn(index); - if (colx->getColumnType() == TXshColumn::eSoundTextType || - colx->getColumnType() == TXshColumn::eSoundType) - continue; + if (!colx->canBeParent()) continue; QColor unused; viewer->getColumnColor(newTextBG, unused, id.getIndex(), xsh); @@ -1272,8 +1266,7 @@ void ColumnArea::DrawHeader::drawPegbarName() const { p.setPen(m_viewer->getVerticalLineColor()); if (o->flag(PredefinedFlag::PEGBAR_NAME_BORDER)) p.drawRect(pegbarnamerect); - if (column->getSoundColumn() || column->getSoundTextColumn()) - return; + if (column->getSoundColumn() || column->getSoundTextColumn()) return; if (Preferences::instance()->isParentColorsInXsheetColumnEnabled() && column->isPreviewVisible()) { @@ -1435,11 +1428,7 @@ void ColumnArea::DrawHeader::drawVolumeControl(double volume) const { //============================================================================= // ColumnArea //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 ColumnArea::ColumnArea(XsheetViewer *parent, Qt::WindowFlags flags) -#else -ColumnArea::ColumnArea(XsheetViewer *parent, Qt::WFlags flags) -#endif : QWidget(parent, flags) , m_viewer(parent) , m_pos(-1, -1) diff --git a/toonz/sources/toonz/xshcolumnviewer.h b/toonz/sources/toonz/xshcolumnviewer.h index 904177a3..8f8b5095 100644 --- a/toonz/sources/toonz/xshcolumnviewer.h +++ b/toonz/sources/toonz/xshcolumnviewer.h @@ -50,12 +50,7 @@ class MotionPathMenu final : public QWidget { QPoint m_pos; public: -#if QT_VERSION >= 0x050500 MotionPathMenu(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - MotionPathMenu(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif - ~MotionPathMenu(); protected: @@ -353,11 +348,7 @@ class ColumnArea final : public QWidget { }; public: -#if QT_VERSION >= 0x050500 - ColumnArea(XsheetViewer *parent, Qt::WindowFlags flags = 0); -#else - ColumnArea(XsheetViewer *parent, Qt::WFlags flags = 0); -#endif + ColumnArea(XsheetViewer *parent, Qt::WindowFlags flags = Qt::WindowFlags()); ~ColumnArea(); void onControlPressed(bool pressed); diff --git a/toonz/sources/toonz/xsheetviewer.cpp b/toonz/sources/toonz/xsheetviewer.cpp index cd4d15af..605e100f 100644 --- a/toonz/sources/toonz/xsheetviewer.cpp +++ b/toonz/sources/toonz/xsheetviewer.cpp @@ -214,11 +214,7 @@ void XsheetViewer::getButton(const int &btype, QColor &bgColor, //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 XsheetViewer::XsheetViewer(QWidget *parent, Qt::WindowFlags flags) -#else -XsheetViewer::XsheetViewer(QWidget *parent, Qt::WFlags flags) -#endif : QFrame(parent) , m_timerId(0) , m_autoPanSpeed(0, 0) @@ -239,7 +235,6 @@ XsheetViewer::XsheetViewer(QWidget *parent, Qt::WFlags flags) , m_xsheetLayout("Classic") , m_frameZoomFactor(100) , m_ctrlSelectRef(CellPosition(0, 0)) { - m_xsheetLayout = Preferences::instance()->getLoadedXsheetLayout(); setFocusPolicy(Qt::StrongFocus); @@ -706,7 +701,7 @@ bool XsheetViewer::refreshContentSize(int dx, int dy) { QSize viewportSize = m_cellScrollArea->viewport()->size(); QPoint offset = m_cellArea->pos(); offset = QPoint(std::min(0, offset.x() - dx), - std::min(0, offset.y() - dy)); // what? + std::min(0, offset.y() - dy)); // what? TXsheet *xsh = getXsheet(); int frameCount = xsh ? xsh->getFrameCount() : 0; @@ -1475,7 +1470,7 @@ void XsheetViewer::onCurrentFrameSwitched() { m_isCurrentFrameSwitched = false; scrollToRow(row); - TXsheet *xsh = getXsheet(); + TXsheet *xsh = getXsheet(); NavigationTags *navTags = xsh->getNavigationTags(); int lastTag = navTags->getPrevTag(INT_MAX); int firstTag = navTags->getNextTag(-1); diff --git a/toonz/sources/toonz/xsheetviewer.h b/toonz/sources/toonz/xsheetviewer.h index 6a2b7a4d..10f3adda 100644 --- a/toonz/sources/toonz/xsheetviewer.h +++ b/toonz/sources/toonz/xsheetviewer.h @@ -163,11 +163,7 @@ class XsheetScrollArea final : public QScrollArea { Q_OBJECT public: -#if QT_VERSION >= 0x050500 XsheetScrollArea(QWidget *parent = 0, Qt::WindowFlags flags = 0) -#else - XsheetScrollArea(QWidget *parent = 0, Qt::WFlags flags = 0) -#endif : QScrollArea(parent) { setObjectName("xsheetScrollArea"); setFrameStyle(QFrame::StyledPanel); @@ -212,19 +208,27 @@ class XsheetViewer final : public QFrame, public SaveLoadQSettings { setTimelineIconLineColor) // Row - QColor m_currentRowBgColor; // current frame / column (210,210,210) - QColor m_markerLineColor; // marker lines (0, 255, 246) - QColor m_verticalLineColor; // vertical lines - QColor m_verticalLineHeadColor; // vertical lines in column head - QColor m_textColor; // text color (black) - QColor m_errorTextColor; // error text color (red, probably) - QColor m_previewFrameTextColor; // frame number in preview range (blue) + QColor m_currentRowBgColor; // current frame / column (210,210,210) + QColor m_markerLineColor; // marker lines (0, 255, 246) + QColor m_secMarkerLineColor; // second marker lines + QColor m_selectedMarkerLineColor; // marker lines in selected cells + QColor m_verticalLineColor; // vertical lines + QColor m_verticalLineHeadColor; // vertical lines in column head + QColor m_textColor; // text color (black) + QColor m_errorTextColor; // error text color (red, probably) + QColor m_selectedTextColor; // text color for the selected cells + QColor m_currentFrameTextColor; // text color for the current frame row + QColor m_previewFrameTextColor; // frame number in preview range (blue) QColor m_onionSkinAreaBgColor; QColor m_frameRangeMarkerLineColor; // timeline frame markers Q_PROPERTY(QColor CurrentRowBgColor READ getCurrentRowBgColor WRITE setCurrentRowBgColor) Q_PROPERTY( QColor MarkerLineColor READ getMarkerLineColor WRITE setMarkerLineColor) + Q_PROPERTY(QColor SecMarkerLineColor READ getSecMarkerLineColor WRITE + setSecMarkerLineColor) + Q_PROPERTY(QColor SelectedMarkerLineColor READ getSelectedMarkerLineColor + WRITE setSelectedMarkerLineColor) Q_PROPERTY(QColor VerticalLineColor READ getVerticalLineColor WRITE setVerticalLineColor) Q_PROPERTY(QColor VerticalLineHeadColor READ getVerticalLineHeadColor WRITE @@ -232,6 +236,10 @@ class XsheetViewer final : public QFrame, public SaveLoadQSettings { Q_PROPERTY(QColor TextColor READ getTextColor WRITE setTextColor) Q_PROPERTY( QColor ErrorTextColor READ getErrorTextColor WRITE setErrorTextColor) + Q_PROPERTY(QColor SelectedTextColor READ getCurrentFrameTextColor WRITE + setCurrentFrameTextColor) + Q_PROPERTY(QColor CurrentFrameTextColor READ getSelectedTextColor WRITE + setSelectedTextColor) Q_PROPERTY(QColor PreviewFrameTextColor READ getPreviewFrameTextColor WRITE setPreviewFrameTextColor) Q_PROPERTY(QColor OnionSkinAreaBgColor READ getOnionSkinAreaBgColor WRITE @@ -632,11 +640,7 @@ private: } public: -#if QT_VERSION >= 0x050500 XsheetViewer(QWidget *parent = 0, Qt::WindowFlags flags = 0); -#else - XsheetViewer(QWidget *parent = 0, Qt::WFlags flags = 0); -#endif ~XsheetViewer(); TColumnSelection *getColumnSelection() const { return m_columnSelection; } @@ -784,6 +788,16 @@ public: QColor getCurrentRowBgColor() const { return m_currentRowBgColor; } void setMarkerLineColor(const QColor &color) { m_markerLineColor = color; } QColor getMarkerLineColor() const { return m_markerLineColor; } + void setSecMarkerLineColor(const QColor &color) { + m_secMarkerLineColor = color; + } + QColor getSecMarkerLineColor() const { return m_secMarkerLineColor; } + void setSelectedMarkerLineColor(const QColor &color) { + m_selectedMarkerLineColor = color; + } + QColor getSelectedMarkerLineColor() const { + return m_selectedMarkerLineColor; + } void setVerticalLineColor(const QColor &color) { m_verticalLineColor = color; } @@ -796,6 +810,14 @@ public: QColor getTextColor() const { return m_textColor; } void setErrorTextColor(const QColor &color) { m_errorTextColor = color; } QColor getErrorTextColor() const { return m_errorTextColor; } + void setSelectedTextColor(const QColor &color) { + m_selectedTextColor = color; + } + QColor getSelectedTextColor() const { return m_selectedTextColor; } + void setCurrentFrameTextColor(const QColor &color) { + m_currentFrameTextColor = color; + } + QColor getCurrentFrameTextColor() const { return m_currentFrameTextColor; } void setPreviewFrameTextColor(const QColor &color) { m_previewFrameTextColor = color; } diff --git a/toonz/sources/toonz/xshnoteviewer.cpp b/toonz/sources/toonz/xshnoteviewer.cpp index 1cebe669..1d388c8e 100644 --- a/toonz/sources/toonz/xshnoteviewer.cpp +++ b/toonz/sources/toonz/xshnoteviewer.cpp @@ -455,11 +455,7 @@ void NoteWidget::paintEvent(QPaintEvent *event) { // NoteArea //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 NoteArea::NoteArea(XsheetViewer *parent, Qt::WindowFlags flags) -#else -NoteArea::NoteArea(XsheetViewer *parent, Qt::WFlags flags) -#endif : QFrame(parent) , m_viewer(parent) , m_flipOrientationButton(nullptr) @@ -471,7 +467,6 @@ NoteArea::NoteArea(XsheetViewer *parent, Qt::WFlags flags) , m_hamburgerButton(nullptr) , m_popup(nullptr) , m_currentLayout(nullptr) { - setFrameStyle(QFrame::StyledPanel); setObjectName("cornerWidget"); @@ -538,7 +533,7 @@ NoteArea::NoteArea(XsheetViewer *parent, Qt::WFlags flags) // signal-slot connections bool ret = true; ret = ret && connect(m_flipOrientationButton, SIGNAL(clicked()), - SLOT(flipOrientation())); + SLOT(flipOrientation())); ret = ret && connect(m_noteButton, SIGNAL(clicked()), SLOT(toggleNewNote())); ret = ret && @@ -755,18 +750,13 @@ void NoteArea::onClickHamburger() { // FooterNoteArea //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 FooterNoteArea::FooterNoteArea(QWidget *parent, XsheetViewer *viewer, Qt::WindowFlags flags) -#else -NoteArea::NoteArea(XsheetViewer *parent, Qt::WFlags flags) -#endif : QFrame(parent) , m_viewer(viewer) , m_noteButton(nullptr) , m_precNoteButton(nullptr) , m_nextNoteButton(nullptr) { - setFrameStyle(QFrame::StyledPanel); setObjectName("cornerWidget"); diff --git a/toonz/sources/toonz/xshnoteviewer.h b/toonz/sources/toonz/xshnoteviewer.h index b324f502..920ced45 100644 --- a/toonz/sources/toonz/xshnoteviewer.h +++ b/toonz/sources/toonz/xshnoteviewer.h @@ -137,11 +137,7 @@ class NoteArea final : public QFrame { QLayout *m_currentLayout; public: -#if QT_VERSION >= 0x050500 NoteArea(XsheetViewer *parent = 0, Qt::WindowFlags flags = 0); -#else - NoteArea(XsheetViewer *parent = 0, Qt::WFlags flags = 0); -#endif void updateButtons(); @@ -175,12 +171,8 @@ class FooterNoteArea final : public QFrame { QToolButton *m_precNoteButton; public: -#if QT_VERSION >= 0x050500 FooterNoteArea(QWidget *parent = 0, XsheetViewer *viewer = 0, Qt::WindowFlags flags = 0); -#else - FooterNoteArea(XsheetViewer *parent = 0, Qt::WFlags flags = 0); -#endif void updateButtons(); diff --git a/toonz/sources/toonz/xshrowviewer.cpp b/toonz/sources/toonz/xshrowviewer.cpp index 6efbe55f..e80aa2b3 100644 --- a/toonz/sources/toonz/xshrowviewer.cpp +++ b/toonz/sources/toonz/xshrowviewer.cpp @@ -41,11 +41,7 @@ namespace XsheetGUI { // RowArea //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 RowArea::RowArea(XsheetViewer *parent, Qt::WindowFlags flags) -#else -RowArea::RowArea(XsheetViewer *parent, Qt::WFlags flags) -#endif : QWidget(parent, flags) , m_viewer(parent) , m_row(-1) @@ -77,6 +73,14 @@ void RowArea::setDragTool(DragTool *dragTool) { m_viewer->setDragTool(dragTool); } +//----------------------------------------------------------------------------- +// returns true if the frame area can have extra space +bool RowArea::checkExpandFrameArea() { + return m_viewer->orientation()->isVerticalTimeline() && + !Preferences::instance()->isOnionSkinEnabled() && + !CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked(); +} + //----------------------------------------------------------------------------- void RowArea::drawRows(QPainter &p, int r0, int r1) { @@ -124,6 +128,10 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { bool simpleView = m_viewer->getFrameZoomFactor() <= o->dimension(PredefinedDimension::SCALE_THRESHOLD); + int currentRow = m_viewer->getCurrentRow(); + bool hasCurrentFrameTextColor = + m_viewer->getTextColor() != m_viewer->getCurrentFrameTextColor(); + for (int r = r0; r <= r1; r++) { int frameAxis = m_viewer->rowToFrameAxis(r); @@ -133,12 +141,14 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { bool isAfterSecMarkers = secDistance > 0 && ((r - offset) % secDistance) == 0 && r != 0; - QColor color = (isAfterSecMarkers || isAfterMarkers) - ? m_viewer->getMarkerLineColor() - : m_viewer->getLightLineColor(); + QColor color = (isAfterSecMarkers) ? m_viewer->getSecMarkerLineColor() + : (isAfterMarkers) ? m_viewer->getMarkerLineColor() + : m_viewer->getLightLineColor(); + double lineWidth = (isAfterSecMarkers) ? 3. + : (secDistance > 0 && isAfterMarkers) ? 2. + : 1.; - p.setPen( - QPen(color, (isAfterSecMarkers) ? 3. : 1., Qt::SolidLine, Qt::FlatCap)); + p.setPen(QPen(color, lineWidth, Qt::SolidLine, Qt::FlatCap)); // p.setPen(color); QLine horizontalLine = o->horizontalLine(frameAxis, layerSide); if (!o->isVerticalTimeline()) { @@ -150,10 +160,20 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { p.drawLine(horizontalLine); } + int extraSpaces = 0; + if (checkExpandFrameArea()) { + extraSpaces = + std::max(0, o->rect(PredefinedRect::FRAME_LABEL).width() / + QFontMetrics(p.font()).boundingRect("0").width() - + 6); + } + int z = 0; for (int r = r0; r <= r1; r++) { // draw frame text - if (playR0 <= r && r <= playR1) { + if (hasCurrentFrameTextColor && r == currentRow) + p.setPen(m_viewer->getCurrentFrameTextColor()); + else if (playR0 <= r && r <= playR1) { p.setPen(((r - m_r0) % step == 0) ? m_viewer->getPreviewFrameTextColor() : m_viewer->getTextColor()); } @@ -226,8 +246,9 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { int koma = (r + 1) % (frameRate * 6); if ((r + 1) % frameRate == 1) { int page = (r + 1) / (frameRate * 6) + 1; - str = QString("p%1 %2") + str = QString("p%1%2%3") .arg(QString::number(page)) + .arg(QString().leftJustified(1 + extraSpaces, ' ')) .arg(QString::number(koma).rightJustified(3, '0')); z = 0; } else { @@ -255,8 +276,9 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { int koma = (r + 1) % (frameRate * 3); if ((r + 1) % frameRate == 1) { int page = (r + 1) / (frameRate * 3) + 1; - str = QString("p%1 %2") + str = QString("p%1%2%3") .arg(QString::number(page)) + .arg(QString().leftJustified(2 + extraSpaces, ' ')) .arg(QString::number(koma).rightJustified(2, '0')); z = 0; } else { @@ -285,6 +307,12 @@ void RowArea::drawPlayRangeBackground(QPainter &p, int r0, int r1) { int playR0, playR1, step; XsheetGUI::getPlayRange(playR0, playR1, step); + int hExpansion = 0; + if (checkExpandFrameArea()) { + hExpansion = m_viewer->orientation()->dimension( + PredefinedDimension::FRAME_AREA_EXPANSION); + } + for (int r = r0; r <= r1; r++) { if (!(playR0 <= r && r <= playR1) && ((r - m_r0) % step == 0)) continue; @@ -294,9 +322,10 @@ void RowArea::drawPlayRangeBackground(QPainter &p, int r0, int r1) { else basePoint.setX(0); - QRect previewBoxRect = o->rect(PredefinedRect::PREVIEW_FRAME_AREA) - .adjusted(0, 0, -frameAdj.x(), -frameAdj.y()) - .translated(basePoint); + QRect previewBoxRect = + o->rect(PredefinedRect::PREVIEW_FRAME_AREA) + .adjusted(-hExpansion, 0, -frameAdj.x(), -frameAdj.y()) + .translated(basePoint); p.fillRect(previewBoxRect, m_viewer->getPlayRangeColor()); if (!o->isVerticalTimeline()) { @@ -333,6 +362,12 @@ void RowArea::drawPlayRange(QPainter &p, int r0, int r1) { m_r0 = 0; } + int hOffset = 0; + if (checkExpandFrameArea()) { + hOffset = m_viewer->orientation()->dimension( + PredefinedDimension::FRAME_AREA_EXPANSION); + } + QColor ArrowColor = (playRangeEnabled) ? QColor(255, 255, 255) : grey150; p.setBrush(QBrush(ArrowColor)); @@ -341,7 +376,7 @@ void RowArea::drawPlayRange(QPainter &p, int r0, int r1) { if (!m_viewer->orientation()->isVerticalTimeline()) topLeft.setY(0); else - topLeft.setX(0); + topLeft.setX(-hOffset); m_viewer->drawPredefinedPath(p, PredefinedPath::BEGIN_PLAY_RANGE, topLeft, ArrowColor, QColor(Qt::black)); } @@ -352,7 +387,7 @@ void RowArea::drawPlayRange(QPainter &p, int r0, int r1) { if (!m_viewer->orientation()->isVerticalTimeline()) topLeft.setY(0); else - topLeft.setX(0); + topLeft.setX(-hOffset); m_viewer->drawPredefinedPath(p, PredefinedPath::END_PLAY_RANGE, topLeft, ArrowColor, QColor(Qt::black)); } @@ -372,6 +407,7 @@ void RowArea::drawCurrentRowGadget(QPainter &p, int r0, int r1) { QRect header = m_viewer->orientation() ->rect(PredefinedRect::FRAME_HEADER) .translated(topLeft); + QPoint frameAdj = m_viewer->getFrameZoomAdjustment(); header.adjust(1, 1, -frameAdj.x(), -frameAdj.y()); p.fillRect(header, m_viewer->getCurrentRowBgColor()); @@ -404,6 +440,12 @@ void RowArea::drawNavigationTags(QPainter &p, int r0, int r1) { NavigationTags *tags = xsh->getNavigationTags(); + int hOffset = 0; + if (checkExpandFrameArea()) { + hOffset = m_viewer->orientation()->dimension( + PredefinedDimension::FRAME_AREA_EXPANSION); + } + for (int r = r0; r <= r1; r++) { if (!xsh->isFrameTagged(r)) continue; @@ -411,7 +453,7 @@ void RowArea::drawNavigationTags(QPainter &p, int r0, int r1) { if (!m_viewer->orientation()->isVerticalTimeline()) topLeft.setY(0); else - topLeft.setX(0); + topLeft.setX(-hOffset); QRect tagRect = m_viewer->orientation() ->rect(PredefinedRect::NAVIGATION_TAG_AREA) @@ -1047,9 +1089,16 @@ void RowArea::mousePressEvent(QMouseEvent *event) { playR0 = 0; } + int playRangeHOffset = 0; + if (checkExpandFrameArea()) { + playRangeHOffset = m_viewer->orientation()->dimension( + PredefinedDimension::FRAME_AREA_EXPANSION); + } + if (xsh->getNavigationTags()->isTagged(row) && o->rect(PredefinedRect::NAVIGATION_TAG_AREA) .adjusted(0, 0, -frameAdj.x(), -frameAdj.y()) + .translated(-playRangeHOffset, 0) .contains(mouseInCell)) { setDragTool(XsheetGUI::DragTool::makeNavigationTagDragTool(m_viewer)); frameAreaIsClicked = true; @@ -1059,6 +1108,7 @@ void RowArea::mousePressEvent(QMouseEvent *event) { frameAreaIsClicked = true; } else if (o->rect(PredefinedRect::PLAY_RANGE) .adjusted(0, 0, -frameAdj.x(), -frameAdj.y()) + .translated(-playRangeHOffset, 0) .contains(mouseInCell) && (row == playR0 || row == playR1)) { if (!playRangeEnabled) XsheetGUI::setPlayRange(playR0, playR1, step); @@ -1217,16 +1267,22 @@ void RowArea::mouseMoveEvent(QMouseEvent *event) { update(); + int hOffset = 0; + if (checkExpandFrameArea()) { + hOffset = m_viewer->orientation()->dimension( + PredefinedDimension::FRAME_AREA_EXPANSION); + } + QPoint base0 = m_viewer->positionToXY(CellPosition(m_r0, -1)); if (!m_viewer->orientation()->isVerticalTimeline()) base0.setY(0); else - base0.setX(0); + base0.setX(-hOffset); QPoint base1 = m_viewer->positionToXY(CellPosition(m_r1, -1)); if (!m_viewer->orientation()->isVerticalTimeline()) base1.setY(0); else - base1.setX(0); + base1.setX(-hOffset); QPainterPath startArrow = o->path(PredefinedPath::BEGIN_PLAY_RANGE).translated(base0); QPainterPath endArrow = diff --git a/toonz/sources/toonz/xshrowviewer.h b/toonz/sources/toonz/xshrowviewer.h index 3b877159..46399be0 100644 --- a/toonz/sources/toonz/xshrowviewer.h +++ b/toonz/sources/toonz/xshrowviewer.h @@ -46,6 +46,8 @@ class RowArea final : public QWidget { int m_contextMenuRow; + // returns true if the frame area can have extra space + bool checkExpandFrameArea(); void drawRows(QPainter &p, int r0, int r1); void drawPlayRangeBackground(QPainter &p, int r0, int r1); void drawPlayRange(QPainter &p, int r0, int r1); @@ -66,11 +68,7 @@ class RowArea final : public QWidget { bool canSetAutoMarkers(); public: -#if QT_VERSION >= 0x050500 - RowArea(XsheetViewer *parent, Qt::WindowFlags flags = 0); -#else - RowArea(XsheetViewer *parent, Qt::WFlags flags = 0); -#endif + RowArea(XsheetViewer *parent, Qt::WindowFlags flags = Qt::WindowFlags()); ~RowArea(); int getContextMenuRow() { return m_contextMenuRow; } diff --git a/toonz/sources/toonzfarm/tfarm/tfarmtask.cpp b/toonz/sources/toonzfarm/tfarm/tfarmtask.cpp index ac22b397..836266be 100644 --- a/toonz/sources/toonzfarm/tfarm/tfarmtask.cpp +++ b/toonz/sources/toonzfarm/tfarm/tfarmtask.cpp @@ -392,8 +392,7 @@ static TFilePath getFilePath(const QStringList &l, int &i) { if (outStr.startsWith('"')) { outStr = outStr.remove(0, 1); if (!outStr.endsWith('"')) { - do - outStr += " " + l.at(i); + do outStr += " " + l.at(i); while (i < l.size() && !l.at(i++).endsWith('"')); } outStr.chop(1); @@ -405,7 +404,11 @@ static TFilePath getFilePath(const QStringList &l, int &i) { //------------------------------------------------------------------------------ void TFarmTask::parseCommandLine(QString commandLine) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList l = commandLine.split(" ", Qt::SkipEmptyParts); +#else QStringList l = commandLine.split(" ", QString::SkipEmptyParts); +#endif assert(l.size() >= 2); // serve per skippare il path dell'eseguibile su mac che contiene spazi @@ -452,11 +455,10 @@ void TFarmTask::parseCommandLine(QString commandLine) { QString::number(TOutputProperties::MediumVal), QString::number(TOutputProperties::SmallVal)}; - m_maxTileSizeIndex = (str == maxTileSizeIndexes[2]) - ? 3 - : (str == maxTileSizeIndexes[1]) - ? 2 - : (str == maxTileSizeIndexes[0]) ? 1 : 0; + m_maxTileSizeIndex = (str == maxTileSizeIndexes[2]) ? 3 + : (str == maxTileSizeIndexes[1]) ? 2 + : (str == maxTileSizeIndexes[0]) ? 1 + : 0; i += 2; } @@ -479,8 +481,12 @@ void TFarmTask::parseCommandLine(QString commandLine) { //------------------------------------------------------------------------------ -QString TFarmTask::getCommandLine(bool isFarmTask) const { - QString cmdline = getExeName(m_isComposerTask); +QString TFarmTask::getCommandLinePrgName() const { + return getExeName(m_isComposerTask); +} + +QString TFarmTask::getCommandLineArguments() const { + QString cmdline = ""; if (!m_taskFilePath.isEmpty()) cmdline += " \"" + @@ -489,18 +495,10 @@ QString TFarmTask::getCommandLine(bool isFarmTask) const { "\""; if (m_callerMachineName != "") { -#if QT_VERSION >= 0x050500 struct hostent *he = gethostbyname(m_callerMachineName.toLatin1()); -#else - struct hostent *he = gethostbyname(m_callerMachineName.toAscii()); -#endif if (he) { char *ipAddress = inet_ntoa(*(struct in_addr *)*(he->h_addr_list)); -#if QT_VERSION >= 0x050500 cmdline += " -tmsg " + QString::fromUtf8(ipAddress); -#else - cmdline += " -tmsg " + QString::fromAscii(ipAddress); -#endif } } @@ -543,6 +541,10 @@ QString TFarmTask::getCommandLine(bool isFarmTask) const { return cmdline; } +QString TFarmTask::getCommandLine(bool) const { + return getCommandLinePrgName() + getCommandLineArguments(); +} + //------------------------------------------------------------------------------ namespace { diff --git a/toonz/sources/toonzfarm/tfarm/ttcpipclient.cpp b/toonz/sources/toonzfarm/tfarm/ttcpipclient.cpp index 8d096be3..f8a76450 100644 --- a/toonz/sources/toonzfarm/tfarm/ttcpipclient.cpp +++ b/toonz/sources/toonzfarm/tfarm/ttcpipclient.cpp @@ -43,17 +43,13 @@ TTcpIpClient::~TTcpIpClient() { int TTcpIpClient::connect(const QString &hostName, const QString &addrStr, int port, int &sock) { -/* - if (!addrStr.empty()) - { - unsigned long ipAddr = inet_addr(addrStr.c_str()); - } -*/ -#if QT_VERSION >= 0x050500 + /* + if (!addrStr.empty()) + { + unsigned long ipAddr = inet_addr(addrStr.c_str()); + } + */ struct hostent *he = gethostbyname(hostName.toUtf8()); -#else - struct hostent *he = gethostbyname(hostName.toAscii()); -#endif if (!he) { #ifdef _WIN32 int err = WSAGetLastError(); @@ -309,7 +305,7 @@ int readData(int sock, string &data) #endif -//#define PRIMA +// #define PRIMA #ifdef PRIMA @@ -353,7 +349,7 @@ int readData(int sock, string &data) { int TTcpIpClient::send(int sock, const QString &data, QString &reply) { if (data.size() > 0) { - int ret = send(sock, data); + int ret = send(sock, data); if (ret == 0) ret = readData(sock, reply); return ret; } diff --git a/toonz/sources/toonzfarm/tfarmserver/tfarmserver.cpp b/toonz/sources/toonzfarm/tfarmserver/tfarmserver.cpp index e948042b..1571cd2d 100644 --- a/toonz/sources/toonzfarm/tfarmserver/tfarmserver.cpp +++ b/toonz/sources/toonzfarm/tfarmserver/tfarmserver.cpp @@ -31,7 +31,7 @@ using namespace TVER; #include #endif -//#define REDIRECT_OUTPUT +// #define REDIRECT_OUTPUT #ifdef _WIN32 #define QUOTE_STR "\"" @@ -116,7 +116,8 @@ TFilePath getLocalRoot() { #else // set path to something suitable for most linux (Unix?) systems #ifdef FREEBSD - std::string unixpath = "/usr/local/etc/" + tver.getAppName() + "/tahoma.conf"; + std::string unixpath = + "/usr/local/etc/" + tver.getAppName() + "/tahoma.conf"; #else std::string unixpath = "/etc/" + tver.getAppName() + "/tahoma.conf"; #endif @@ -460,9 +461,30 @@ void Task::run() { // cout << exename << endl; // cout << cmdline << endl; - QProcess process; + // parse command line + QString prgName; + QString argsStr = ""; + int sepPos = cmdline.indexOf(" "); - process.start(cmdline); + if (sepPos == -1) { + prgName = cmdline; + } else { + prgName = cmdline.left(sepPos); + argsStr = cmdline.right(cmdline.size() - sepPos - 1); + } + + QProcess process; + process.setProgram(prgName); +#if defined(_WIN32) + process.setNativeArguments(argsStr); +#else +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + process.setArguments(argsStr.split(" ", Qt::SkipEmptyParts)); +#else + process.setArguments(argsStr.split(" ", QString::SkipEmptyParts)); +#endif +#endif + process.start(); process.waitForFinished(-1); int exitCode = process.exitCode(); @@ -612,13 +634,9 @@ int FarmServer::addTask(const QString &id, const QString &cmdline) { int FarmServer::terminateTask(const QString &taskid) { #ifdef _WIN32 - HANDLE hJob = OpenJobObject(MAXIMUM_ALLOWED, // access right - TRUE, // inheritance state -#if QT_VERSION >= 0x050500 + HANDLE hJob = OpenJobObject(MAXIMUM_ALLOWED, // access right + TRUE, // inheritance state taskid.toUtf8()); // job name -#else - taskid.toAscii()); // job name -#endif if (hJob != NULL) { BOOL res = TerminateJobObject(hJob, // handle to job @@ -661,7 +679,7 @@ void FarmServer::queryHwInfo(HwInfo &hwInfo) { #ifdef __sgi hwInfo.m_type = Irix; #else - hwInfo.m_type = Linux; + hwInfo.m_type = Linux; #endif #endif } @@ -746,12 +764,7 @@ static bool loadServerData(const QString &hostname, QString &addr, int &port) { iss >> name >> ipAddress >> port; if (name[0] == '#') continue; -#if QT_VERSION >= 0x050500 - if (STRICMP(hostname.toUtf8(), name.c_str()) == 0) -#else - if (STRICMP(hostname.toAscii(), name.c_str()) == 0) -#endif - { + if (STRICMP(hostname.toUtf8(), name.c_str()) == 0) { addr = QString(ipAddress.c_str()); return true; } @@ -1033,11 +1046,7 @@ void FarmServerService::mountDisks() { DWORD res = WNetAddConnection2(&NetResource, // connection details 0, // password -#if QT_VERSION >= 0x050500 TSystem::getUserName().toUtf8(), // user name -#else - TSystem::getUserName().toAscii(), // user name -#endif 0); // connection options if (res == NO_ERROR) m_disksMounted.push_back(drive); diff --git a/toonz/sources/toonzlib/CMakeLists.txt b/toonz/sources/toonzlib/CMakeLists.txt index c2c61816..40f2951f 100644 --- a/toonz/sources/toonzlib/CMakeLists.txt +++ b/toonz/sources/toonzlib/CMakeLists.txt @@ -65,6 +65,7 @@ set(HEADERS sandor_fxs/patternmap.h sandor_fxs/toonz4_6staff.h ../include/convert2tlv.h + ../include/thirdparty.h ../include/orientation.h ../include/toonz/Naa2TlvConverter.h ../include/toonz/autoclose.h @@ -247,6 +248,7 @@ set(SOURCES tcolumnfxset.cpp tdistort.cpp texturemanager.cpp + thirdparty.cpp tlog.cpp tnewoutlinevectorize.cpp toonzfolders.cpp diff --git a/toonz/sources/toonzlib/Naa2TlvConverter.cpp b/toonz/sources/toonzlib/Naa2TlvConverter.cpp index 45c90b1e..99840f80 100644 --- a/toonz/sources/toonzlib/Naa2TlvConverter.cpp +++ b/toonz/sources/toonzlib/Naa2TlvConverter.cpp @@ -8,7 +8,7 @@ #include "tpalette.h" #include -#include +#include #include #include @@ -357,7 +357,7 @@ void Naa2TlvConverter::findBackgroundRegions() { } for (int i = 0; i < m_regions.count(); i++) { - RegionInfo ®ion = m_regions[i]; + RegionInfo ®ion = m_regions[i]; if (region.colorIndex == bgColorIndex) region.type = RegionInfo::Background; } } @@ -407,8 +407,10 @@ void Naa2TlvConverter::findRegionBorders() { // pixels belonging to that region with m_border[pix] == k) void Naa2TlvConverter::erodeRegions() { - QTime clock; +#ifdef _DEBUG + QElapsedTimer clock; clock.start(); +#endif if (!m_regionRas || !m_borderRas) return; int lx = m_regionRas->getLx(); int ly = m_regionRas->getLy(); @@ -461,7 +463,9 @@ void Naa2TlvConverter::erodeRegions() { } } } +#ifdef _DEBUG qDebug() << "Erode regions. time = " << clock.elapsed(); +#endif } //----------------------------------------------------------------------------- @@ -630,8 +634,7 @@ void Naa2TlvConverter::findThinPaints() { if (inkBoundary * 100 > region.perimeter * 80) regions.append(i); } - for (int c : regions) - m_regions[c].type = RegionInfo::SmallPaint; + for (int c : regions) m_regions[c].type = RegionInfo::SmallPaint; } //----------------------------------------------------------------------------- @@ -683,7 +686,8 @@ void Naa2TlvConverter::findSuspectInks() { int lx = region.x1 - region.x0 + 1; int ly = region.y1 - region.y0 + 1; int d = std::max(lx, ly); - if (std::min(lx, ly) * 2 > std::max(lx, ly) && region.pixelCount > d * d / 2) { + if (std::min(lx, ly) * 2 > std::max(lx, ly) && + region.pixelCount > d * d / 2) { region.type = RegionInfo::Paint; } } @@ -695,8 +699,8 @@ void Naa2TlvConverter::findSuspectInks() { if (region.boundaries.count() == 2) isInk = true; else if (region.boundaries.count() == 3) { - int b1 = region.boundaries.at(1); - int b2 = region.boundaries.at(2); + int b1 = region.boundaries.at(1); + int b2 = region.boundaries.at(2); if (b1 * 2 < b2) isInk = true; } } @@ -806,8 +810,10 @@ void Naa2TlvConverter::addBorderInks() // add synthetic inks: lines between two //----------------------------------------------------------------------------- void Naa2TlvConverter::measureThickness() { - QTime timer; +#ifdef _DEBUG + QElapsedTimer timer; timer.start(); +#endif if (!m_regionRas || !m_borderRas) return; unsigned short *regionBuffer = m_regionRas->pixels(); unsigned char *borderBuffer = m_borderRas->pixels(); @@ -862,7 +868,9 @@ void Naa2TlvConverter::measureThickness() { region.thickness = thickness; } } +#ifdef _DEBUG qDebug() << "measure thickness. time=" << timer.elapsed(); +#endif } //----------------------------------------------------------------------------- @@ -893,9 +901,8 @@ int Naa2TlvConverter::measureThickness(int x0, int y0) { // a is a direction index; a : inside; a+1 : outside int a = 0; - while (a < 8 && - !(regionBuffer[k0 + dd[a]] == regionId && - regionBuffer[k0 + dd[(a + 1) % 8]] != regionId)) + while (a < 8 && !(regionBuffer[k0 + dd[a]] == regionId && + regionBuffer[k0 + dd[(a + 1) % 8]] != regionId)) a++; if (a == 8) { // k0 is an isolated point or (strange!) an intern point @@ -904,7 +911,7 @@ int Naa2TlvConverter::measureThickness(int x0, int y0) { } int ka = k0 + dd[a]; - int b = (a + 2) % 8; + int b = (a + 2) % 8; while (regionBuffer[k0 + dd[b]] != regionId) b = (b + 1) % 8; // a..b = boundaries int kb = k0 + dd[b]; @@ -941,9 +948,9 @@ int Naa2TlvConverter::measureThickness(int x0, int y0) { break; // just to be sure int d2 = (x - x0) * (x - x0) + (y - y0) * (y - y0); if (d2 <= lastd2) break; - lastd2 = d2; - int d1 = (d + 4) % 8; - d1 = (d1 + 1) % 8; + lastd2 = d2; + int d1 = (d + 4) % 8; + d1 = (d1 + 1) % 8; while (regionBuffer[k + dd[d1]] != regionId) d1 = (d1 + 1) % 8; Q_ASSERT(regionBuffer[k + dd[d1]] == regionId); oldk = k; @@ -967,9 +974,9 @@ int Naa2TlvConverter::measureThickness(int x0, int y0) { break; // just to be sure int d2 = (x - x0) * (x - x0) + (y - y0) * (y - y0); if (d2 <= lastd2) break; - lastd2 = d2; - int d1 = (d + 4) % 8; - d1 = (d1 + 7) % 8; + lastd2 = d2; + int d1 = (d + 4) % 8; + d1 = (d1 + 7) % 8; while (regionBuffer[k + dd[d1]] != regionId) d1 = (d1 + 7) % 8; Q_ASSERT(regionBuffer[k + dd[d1]] == regionId); oldk = k; @@ -1015,9 +1022,9 @@ TToonzImageP Naa2TlvConverter::makeTlv(bool transparentSyntheticInks, QList &usedStyleIds, double dpi) { if (!m_valid || m_colors.empty() || m_regions.empty() || !m_regionRas) return TToonzImageP(); - int lx = m_regionRas->getLx(); - int ly = m_regionRas->getLy(); - TPalette *palette = m_palette; + int lx = m_regionRas->getLx(); + int ly = m_regionRas->getLy(); + TPalette *palette = m_palette; if (!palette) palette = new TPalette(); TRasterCM32P ras(lx, ly); @@ -1145,7 +1152,7 @@ TVectorImageP Naa2TlvConverter::vectorize(const TToonzImageP &ti) { TPointD center = ti->getRaster()->getCenterD(); if (dpix != 0.0 && dpiy != 0.0) dpiAff = TScale(factor / dpix, factor / dpiy); - factor = norm(dpiAff * TPointD(1, 0)); + factor = norm(dpiAff * TPointD(1, 0)); conf.m_affine = dpiAff * TTranslation(-center); conf.m_thickScale = factor; diff --git a/toonz/sources/toonzlib/boardsettings.cpp b/toonz/sources/toonzlib/boardsettings.cpp index dc38feab..46c828a2 100644 --- a/toonz/sources/toonzlib/boardsettings.cpp +++ b/toonz/sources/toonzlib/boardsettings.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace { QMap strs = { @@ -91,10 +92,10 @@ QString BoardItem::getContentText(ToonzScene *scene) { QString::number(ff).rightJustified(2, '0'); } break; case CurrentDate: - return QDate::currentDate().toString(Qt::DefaultLocaleLongDate); + return QLocale::system().toString(QDate::currentDate()); break; case CurrentDateTime: - return QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate); + return QLocale::system().toString(QDateTime::currentDateTime()); break; case UserName: return TSystem::getUserName(); @@ -332,7 +333,11 @@ void BoardSettings::removeItem(int index) { m_items.removeAt(index); } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) +void BoardSettings::swapItems(int i, int j) { m_items.swapItemsAt(i, j); } +#else void BoardSettings::swapItems(int i, int j) { m_items.swap(i, j); } +#endif void BoardSettings::saveData(TOStream &os, bool forPreset) { if (!forPreset) os.child("active") << (int)((m_active) ? 1 : 0); diff --git a/toonz/sources/toonzlib/convert2tlv.cpp b/toonz/sources/toonzlib/convert2tlv.cpp index eb4c7864..f5174ead 100644 --- a/toonz/sources/toonzlib/convert2tlv.cpp +++ b/toonz/sources/toonzlib/convert2tlv.cpp @@ -563,7 +563,7 @@ TPalette *Convert2Tlv::buildPalette() { stylesToBeAddedToPage.push_back(it->second); } - /*- インデックス順にページに格納する -*/ + /*- Store on page in index order -*/ if (!stylesToBeAddedToPage.isEmpty()) { std::sort(stylesToBeAddedToPage.begin(), stylesToBeAddedToPage.end()); for (int s = 0; s < stylesToBeAddedToPage.size(); s++) @@ -628,7 +628,7 @@ TPalette *Convert2Tlv::buildPalette() { int addedId = m_palette->addStyle(srcPage->getStyle(srcIndexInPage)->clone()); dstPage->addStyle(addedId); - /*-- StudioPalette由来のDefaultPaletteの場合、GrobalNameを消去する --*/ + /*-- For StudioPalette-derived DefaultPalettes, clear the GrobalName --*/ m_palette->getStyle(addedId)->setGlobalName(L""); m_palette->getStyle(addedId)->setOriginalName(L""); } diff --git a/toonz/sources/toonzlib/doubleparamcmd.cpp b/toonz/sources/toonzlib/doubleparamcmd.cpp index 7a9c78ae..c4e8feb6 100644 --- a/toonz/sources/toonzlib/doubleparamcmd.cpp +++ b/toonz/sources/toonzlib/doubleparamcmd.cpp @@ -120,7 +120,7 @@ void KeyframeSetter::selectKeyframe(int kIndex) { // set key frame at frame and returns its index int KeyframeSetter::createKeyframe(double frame) { - /*--- すでにそこにキーフレームがある場合はそのIndexを返すだけ ---*/ + /*--- If there is already a keyframe there, just return its Index ---*/ int kIndex = m_param->getClosestKeyframe(frame); if (kIndex >= 0 && m_param->getKeyframe(kIndex).m_frame == frame) { selectKeyframe(kIndex); @@ -161,7 +161,8 @@ int KeyframeSetter::createKeyframe(double frame) { m_keyframe.m_step = ka.m_step; // An existing segment step should prevail // over the preference - /*---Segment内にKeyを打った場合は、Step値も元のSegmentの値を引き継ぐようにする---*/ + /*---When a Key is entered in a Segment, the Step value should also + * inherit the value of the original Segment.---*/ m_param->setKeyframe(m_kIndex, m_keyframe); if (segmentType == TDoubleKeyframe::SpeedInOut || @@ -765,7 +766,7 @@ void KeyframeSetter::setAllParams( break; } - /*--- リンクされたカーブの処理 ---*/ + /*--- Processing Linked Curves ---*/ const double eps = 0.00001; if (m_kIndex != 0 && keyframe.m_linkedHandles && keyframe.m_prevType == TDoubleKeyframe::SpeedInOut) { diff --git a/toonz/sources/toonzlib/fill.cpp b/toonz/sources/toonzlib/fill.cpp index e52a315a..abe7bf0f 100644 --- a/toonz/sources/toonzlib/fill.cpp +++ b/toonz/sources/toonzlib/fill.cpp @@ -157,7 +157,7 @@ bool calcFillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb, oldtone = tone; } if (tone == 0) { - tmp_limit = pix - 10; + tmp_limit = pix - 10; if (limit < tmp_limit) limit = tmp_limit; for (; pix >= limit; pix--) { if (pix->getPaint() == paint) break; @@ -213,7 +213,7 @@ void findSegment(const TRaster32P &r, const TPoint &p, int &xa, int &xb, oldmatte = matte; } if (matte == 0) { - tmp_limit = pix - 10; + tmp_limit = pix - 10; if (limit < tmp_limit) limit = tmp_limit; for (; pix >= limit; pix--) { if (*pix == color) break; @@ -430,7 +430,7 @@ TRasterCM32P convertRaster2CM(const TRasterP &inputRaster) { return rout; } -/*-- The return value is whether the saveBox has been updated --*/ +/*-- The return value is whether the saveBox has been updated or not. --*/ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, TTileSaverCM32 *saver, bool fillGaps, bool closeGaps, int closeStyleIndex, double autoCloseDistance, TXsheet *xsheet, @@ -466,16 +466,16 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, tempRaster = r; } - /*-- getBounds --*/ + /*-- getBounds returns the entire image --*/ TRect bbbox = tempRaster->getBounds(); - /*- Return for off-screen clicks -*/ + /*- Return if clicked outside the screen -*/ if (!bbbox.contains(p)) return false; - /*- Return if the same color is already painted -*/ + /*- If the same color has already been painted, return -*/ int paintAtClickedPos = (tempRaster->pixels(p.y) + p.x)->getPaint(); if (paintAtClickedPos == paint) return false; - /*- If the "Paint only transparent area" option is enabled and it is already - * colored, return + /*- If the "paint only transparent areas" option is enabled and the area is + * already colored, return * -*/ if (params.m_emptyOnly && (tempRaster->pixels(p.y) + p.x)->getPaint() != 0) return false; @@ -556,8 +556,8 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, default: assert(false); } - /*-- Look at the colors in the four corners and update the saveBox if even one - * changes --*/ + /*--Look at the colors in the four corners and update the saveBox if any of + * the colors change. --*/ TPixelCM32 borderIndex[4]; TPixelCM32 *borderPix[4]; pix = tempRaster->pixels(0); @@ -839,7 +839,7 @@ void fill(const TRaster32P &ras, const TRaster32P &ref, for (int i = 0; i < (int)segmentVector.size(); i++) { std::pair segment = segmentVector[i]; if (segment.second >= segment.first) { - pix = line + segment.first; + pix = line + segment.first; if (ref) refPix = refLine + segment.first; int n; for (n = 0; n < segment.second - segment.first + 1; n++, pix++) { @@ -1135,7 +1135,7 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, std::map>>::iterator it; for (it = segments.begin(); it != segments.end(); it++) { - TPixel32 *line = ras->pixels(it->first); + TPixel32 *line = ras->pixels(it->first); std::vector> segmentVector = it->second; for (int i = 0; i < (int)segmentVector.size(); i++) { std::pair segment = segmentVector[i]; diff --git a/toonz/sources/toonzlib/fillutil.cpp b/toonz/sources/toonzlib/fillutil.cpp index a2e66144..a8952989 100644 --- a/toonz/sources/toonzlib/fillutil.cpp +++ b/toonz/sources/toonzlib/fillutil.cpp @@ -188,6 +188,7 @@ bool AreaFiller::rectFill(const TRect &rect, int color, bool onlyUnfilled, // Then fills in EVERYTHING with 'color' // Then uses the fill command to fill in the edges with their original color // This makes sure only the enclosed areas not on the edge get filled. + /*- In case of FillInk only -*/ if (!fillPaints) { assert(fillInks); assert(m_ras->getBounds().contains(rect)); @@ -213,7 +214,7 @@ bool AreaFiller::rectFill(const TRect &rect, int color, bool onlyUnfilled, std::vector frameSeed(2 * (r.getLx() + r.getLy() - 2)); int x, y, count1, count2; - /*- Move ptr to the starting point of the Rect range -*/ + /*- Move ptr to the start of the Rectangular range -*/ Pixel *ptr = m_pixels + r.y0 * m_wrap + r.x0; count1 = 0; count2 = r.y1 - r.y0 + 1; @@ -227,7 +228,7 @@ bool AreaFiller::rectFill(const TRect &rect, int color, bool onlyUnfilled, // FrameSeed is filled with all the paints of the various areas of the // boundary rectangle. // It is checked if the pixels of the rectangle are all pure paint. - + /*- Store the Paint ID of the contour in the frameseed -*/ for (y = r.y0; y <= r.y1; y++, ptr += m_wrap, count1++, count2++) { if (r.x0 > 0) frameSeed[count1] = ptr->getPaint(); if (r.x1 < m_ras->getLx() - 1) frameSeed[count2] = (ptr + dx)->getPaint(); @@ -473,7 +474,7 @@ public: pix = m_buf + p.y * m_wrap + p.x; - /*-- 同じインクの場合はreturn --*/ + /*-- If the same ink is used, RETURN --*/ if (pix->getInk() == ink && !m_clearInk) return false; if (!ConnectionTable[neighboursCode(pix, p)]) { diff --git a/toonz/sources/toonzlib/ikjacobian.cpp b/toonz/sources/toonzlib/ikjacobian.cpp index cd311feb..2df9272a 100644 --- a/toonz/sources/toonzlib/ikjacobian.cpp +++ b/toonz/sources/toonzlib/ikjacobian.cpp @@ -844,9 +844,9 @@ void MatrixRmn::ConvertBidiagToDiagonal(MatrixRmn &U, MatrixRmn &V, VectorRn &w, // This gives a lambda value which will shift the Givens rotations // Last four entries of M^T * M are ( ( A, B ), ( B, C ) ). double A; - A = (firstBidiagIdx < lastBidiagIdx - 1) - ? Square(superDiag[lastBidiagIdx - 2]) - : 0.0; + A = (firstBidiagIdx < lastBidiagIdx - 1) + ? Square(superDiag[lastBidiagIdx - 2]) + : 0.0; double BSq = Square(w[lastBidiagIdx - 1]); A += BSq; // The "A" entry of M^T * M double C = Square(superDiag[lastBidiagIdx - 1]); @@ -1106,9 +1106,9 @@ void Jacobian::Reset() { // Usato nel Damped Least Squares Method DampingLambda = DefaultDampingLambda; DampingLambdaSq = Square(DampingLambda); - for (int i = 0; i < DampingLambdaSqV.GetLength(); i++) + for (int i = 0; i < DampingLambdaSqV.GetLength(); i++) DampingLambdaSqV[i] = DampingLambdaSq; - for (int i = 0; i < diagMatIdentity.GetLength(); i++) + for (int i = 0; i < diagMatIdentity.GetLength(); i++) diagMatIdentity[i] = 1.0; // DampingLambdaSDLS = 1.5*DefaultDampingLambda; @@ -1358,7 +1358,7 @@ void Jacobian::CalcDeltaThetasPseudoinverse() { Jcurrent.Multiply(dTheta, dTemp); VectorRn dTemp2(dScurrent.GetLength()); - for (int k = 0; k < dScurrent.GetLength(); k++) + for (int k = 0; k < dScurrent.GetLength(); k++) dTemp2[k] = dScurrent[k] - dTemp[k]; // Moltiplico JdstPinv per dTemp2 @@ -1366,8 +1366,8 @@ void Jacobian::CalcDeltaThetasPseudoinverse() { JdstPinv.Multiply(dTemp2, dThetaCurrent); for (int k = 0; k < dTheta.GetLength(); k++) dTheta[k] += dThetaCurrent[k]; - // Infine mi calcolo la pseudoinversa di Jcurrent e quindi la sua proiezione - // che servirà al passo successivo + // Finally, I calculate the pseudoinverse of Jcurrent and thus its + // projection, which will be used in the next step // calcolo la pseudoinversa di Jcurrent Jcurrent.ComputeSVD(U, w, V); diff --git a/toonz/sources/toonzlib/imagebuilders.cpp b/toonz/sources/toonzlib/imagebuilders.cpp index 57b972e7..163eae2a 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,11 +207,13 @@ void ImageLoader::invalidate() { ImageBuilder::invalidate(); m_subsampling = 0; m_64bitCompatible = false; + m_colorSpaceGamma = LevelOptions::DefaultColorSpaceGamma; } //------------------------------------------------------------------------- /*-- - * ImageBuilder仮想関数の実装。アイコン、画像をLoad時に全てキャッシュに格納する + * Implement ImageBuilder virtual functions. All icons and images are stored in + * the cache on Loading. * --*/ void ImageLoader::buildAllIconsAndPutInCache(TXshSimpleLevel *level, @@ -193,7 +222,7 @@ void ImageLoader::buildAllIconsAndPutInCache(TXshSimpleLevel *level, bool cacheImagesAsWell) { if (m_path.getType() != "tlv") return; if (fids.empty() || iconIds.empty()) return; - /*- fidとアイコンidの数は同じはず -*/ + /*- The number of fid and icon id should be the same -*/ if ((int)fids.size() != (int)iconIds.size()) return; try { @@ -209,7 +238,7 @@ void ImageLoader::buildAllIconsAndPutInCache(TXshSimpleLevel *level, TPalette *palette = level->getPalette(); std::string fullImgId = level->getImageId(fids[i]); - /*- 画像データも一緒にキャッシュする場合 -*/ + /*- When image data is also cached together -*/ if (cacheImagesAsWell) { ir->enable16BitRead(m_64bitCompatible); ir->setShrink(1); @@ -221,7 +250,7 @@ void ImageLoader::buildAllIconsAndPutInCache(TXshSimpleLevel *level, } } - /*- アイコンのロード -*/ + /*- load icons -*/ TImageP img = ir->loadIcon(); ir->enable16BitRead(false); if (img) { diff --git a/toonz/sources/toonzlib/imagebuilders.h b/toonz/sources/toonzlib/imagebuilders.h index 55a42717..fbd92f49 100644 --- a/toonz/sources/toonzlib/imagebuilders.h +++ b/toonz/sources/toonzlib/imagebuilders.h @@ -52,7 +52,8 @@ public: bool isImageCompatible(int imFlags, void *extData) override; /*-- - * ImageBuilder仮想関数の実装。アイコン、画像をLoad時に全てキャッシュに格納する + * Implement ImageBuilder virtual functions. All icons and images are stored + * in the cache on loading * --*/ void buildAllIconsAndPutInCache(TXshSimpleLevel *level, std::vector fids, @@ -76,8 +77,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 164dd5a7..f9c40f1b 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(); - if ((TRasterCM32P)rin) - TRop::convert(rout, (TRasterCM32P)rin, TPaletteP(palette)); - else - TRop::copy(rout, rin); + TPixelF *pix = (TPixelF *)rout->getRawData(); - TPixel32 *pix = (TPixel32 *)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()); - assert(channel & TRop::MChan); - int i; + if ((TRasterCM32P)rin) + TRop::convert(rout, (TRasterCM32P)rin, TPaletteP(palette)); + else + TRop::copy(rout, rin); - 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; + TPixel32 *pix = (TPixel32 *)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; + 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); +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); - 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); - } + 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); - // TRaster32P textureBackGround; - - // 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/imagestyles.cpp b/toonz/sources/toonzlib/imagestyles.cpp index 194bf03a..f263a415 100644 --- a/toonz/sources/toonzlib/imagestyles.cpp +++ b/toonz/sources/toonzlib/imagestyles.cpp @@ -112,10 +112,34 @@ TColorStyle *TTextureStyle::clone() const { return new TTextureStyle(*this); } //----------------------------------------------------------------------------- +TColorStyle *TTextureStyle::clone(std::string brushIdName) const { + TTextureStyle *style = new TTextureStyle(*this); + + std::string name = getBrushIdNameParam(brushIdName); + style->m_texturePath = TFilePath(name); + style->setAverageColor(); + return style; +} + +//----------------------------------------------------------------------------- + QString TTextureStyle::getDescription() const { return "TextureStyle"; } //----------------------------------------------------------------------------- +std::string TTextureStyle::getBrushIdName() const { + std::wstring ws = m_texturePath.getWideString(); + const std::string s(ws.begin(), ws.end()); + return "TextureStyle:" + s; +} + +//----------------------------------------------------------------------------- + +std::string TTextureStyle::staticBrushIdName(std::wstring texturePath) { + const std::string s(texturePath.begin(), texturePath.end()); + return "TextureStyle:" + s; +} + //----------------------------------------------------------------------------- int TTextureStyle::getTagId() const { return 4; } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/levelproperties.cpp b/toonz/sources/toonzlib/levelproperties.cpp index 3ec1db9e..14cba2f9 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/levelupdater.cpp b/toonz/sources/toonzlib/levelupdater.cpp index 4a3e5c98..b503a366 100644 --- a/toonz/sources/toonzlib/levelupdater.cpp +++ b/toonz/sources/toonzlib/levelupdater.cpp @@ -241,7 +241,7 @@ void LevelUpdater::open(const TFilePath &fp, TPropertyGroup *pg, TLevelReaderP(); // Release the reader. This is necessary since the m_lw = TLevelWriterP( fp, m_pg->clone()); // original file itself will be MODIFIED. - m_lwPath = fp; + m_lwPath = m_lw->getFilePath(); } } catch (...) { // In this case, TLevelWriterP(..) failed, that object was never diff --git a/toonz/sources/toonzlib/movierenderer.cpp b/toonz/sources/toonzlib/movierenderer.cpp index dd44345f..ff629f22 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" @@ -85,7 +87,7 @@ void getRange(ToonzScene *scene, bool isPreview, int &from, int &to) { 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); } @@ -121,8 +123,8 @@ public: std::map> m_toBeSaved; std::vector> m_framesToBeRendered; std::string m_renderCacheId; - /*--- When reusing the cache of the same raster - Gamma is applied only to the first one, and it is reused after that. + /*--- When caching the same raster, gamma only the first one and use the + result in subsequent frames ---*/ std::map m_toBeAppliedGamma; @@ -151,7 +153,7 @@ public: void onRenderRasterCompleted(const RenderData &renderData) override; void onRenderFailure(const RenderData &renderData, TException &e) override; - /*-- キャンセル時にはm_overallRenderedRegionを更新しない --*/ + /*-- Do not update m_overallRenderedRegion on cancel --*/ void onRenderFinished(bool isCanceled = false) override; void doRenderRasterCompleted(const RenderData &renderData); @@ -161,7 +163,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 @@ -254,7 +264,7 @@ void MovieRenderer::Imp::prepareForStart() { TOutputProperties *oprop = m_scene->getProperties()->getOutputProperties(); double frameRate = (double)oprop->getFrameRate(); - /*-- Frame rate の stretch --*/ + /*-- stretch of the Frame rate --*/ double stretchFactor = oprop->getRenderSettings().m_timeStretchTo / oprop->getRenderSettings().m_timeStretchFrom; frameRate *= stretchFactor; @@ -285,6 +295,7 @@ void MovieRenderer::Imp::prepareForStart() { m_fp, oprop->getFileFormatProperties(m_fp.getType()), oprop->formatTemplateFId())); m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate); + m_fp = m_levelUpdaterA->getLevelWriter()->getFilePath(); } else { TFilePath leftFp = m_fp.withName(m_fp.getName() + "_l"); TFilePath rightFp = m_fp.withName(m_fp.getName() + "_r"); @@ -296,11 +307,13 @@ void MovieRenderer::Imp::prepareForStart() { leftFp, oprop->getFileFormatProperties(leftFp.getType()), oprop->formatTemplateFId())); m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate); + leftFp = m_levelUpdaterA->getLevelWriter()->getFilePath(); m_levelUpdaterB.reset(new LevelUpdater( rightFp, oprop->getFileFormatProperties(rightFp.getType()), oprop->formatTemplateFId())); m_levelUpdaterB->getLevelWriter()->setFrameRate(frameRate); + rightFp = m_levelUpdaterB->getLevelWriter()->getFilePath(); } } catch (...) { // If we get here, it's because one of the LevelUpdaters could not be @@ -317,7 +330,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 ... @@ -371,6 +384,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); @@ -380,6 +396,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()) @@ -411,12 +444,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. } @@ -425,8 +472,8 @@ std::pair MovieRenderer::Imp::saveFrame( TRasterP rasterA = rasters.first, rasterB = rasters.second; assert(rasterA); - /*--- 同じラスタのキャッシュを使いまわすとき、 - 最初のものだけガンマをかけ、以降はそれを使いまわすようにする。 + /*--- When caching the same raster, gamma only the first one and use the +result in subsequent frames ---*/ if (m_renderSettings.m_gamma != 1.0 && m_toBeAppliedGamma[frame]) { TRop::gammaCorrect(rasterA, m_renderSettings.m_gamma); @@ -436,15 +483,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); } @@ -452,9 +503,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/mypaintbrushstyle.cpp b/toonz/sources/toonzlib/mypaintbrushstyle.cpp index 967aa14e..6d893d13 100644 --- a/toonz/sources/toonzlib/mypaintbrushstyle.cpp +++ b/toonz/sources/toonzlib/mypaintbrushstyle.cpp @@ -50,6 +50,14 @@ TMyPaintBrushStyle::~TMyPaintBrushStyle() {} //----------------------------------------------------------------------------- +TColorStyle *TMyPaintBrushStyle::clone(std::string brushIdName) const { + TMyPaintBrushStyle *style = new TMyPaintBrushStyle(*this); + style->loadBrush(TFilePath(getBrushIdNameParam(brushIdName))); + return style; +} + +//----------------------------------------------------------------------------- + TColorStyle &TMyPaintBrushStyle::copy(const TColorStyle &other) { const TMyPaintBrushStyle *otherBrushStyle = dynamic_cast(&other); @@ -73,6 +81,14 @@ QString TMyPaintBrushStyle::getDescription() const { //----------------------------------------------------------------------------- +std::string TMyPaintBrushStyle::getBrushIdName() const { + std::wstring ws = m_path.getWideString(); + const std::string s(ws.begin(), ws.end()); + return "MyPaintBrushStyle:" + s; +} + +//----------------------------------------------------------------------------- + std::string TMyPaintBrushStyle::getBrushType() { return "myb"; } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/orientation.cpp b/toonz/sources/toonzlib/orientation.cpp index 370a27dd..358e3aa1 100644 --- a/toonz/sources/toonzlib/orientation.cpp +++ b/toonz/sources/toonzlib/orientation.cpp @@ -934,6 +934,7 @@ TopToBottomOrientation::TopToBottomOrientation() { addDimension(PredefinedDimension::CENTER_ALIGN, Qt::AlignHCenter); addDimension(PredefinedDimension::CAMERA_LAYER, CAMERA_CELL_WIDTH); addDimension(PredefinedDimension::SCALE_THRESHOLD, 57); + addDimension(PredefinedDimension::FRAME_AREA_EXPANSION, PLAY_RANGE_X); // // Paths @@ -1405,6 +1406,7 @@ LeftToRightOrientation::LeftToRightOrientation() { addDimension(PredefinedDimension::CENTER_ALIGN, Qt::AlignVCenter); addDimension(PredefinedDimension::CAMERA_LAYER, CAMERA_CELL_HEIGHT); addDimension(PredefinedDimension::SCALE_THRESHOLD, 50); + addDimension(PredefinedDimension::FRAME_AREA_EXPANSION, 0); // not used // // Paths diff --git a/toonz/sources/toonzlib/outputproperties.cpp b/toonz/sources/toonzlib/outputproperties.cpp index 6568f8ff..5b718966 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 cca63a85..434213a2 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -48,7 +48,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"; //================================================================= @@ -125,6 +126,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); } //----------------------------------------------------------------- @@ -139,6 +141,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/rasterbrush.cpp b/toonz/sources/toonzlib/rasterbrush.cpp index f093ed9b..baab5428 100644 --- a/toonz/sources/toonzlib/rasterbrush.cpp +++ b/toonz/sources/toonzlib/rasterbrush.cpp @@ -381,13 +381,13 @@ double findChordalDeviation(const TQuadratic &quadratic, double t, } // Accende un pixel calcolandone l'intensita' -/*-- 筆先のアンチエイリアス部分の描画 --*/ +/*-- Drawing the anti-aliased portion of the brush tip --*/ void lightPixel(const TRasterCM32P &ras, const TPoint &pix, double distance, int styleId, bool checkAntialiasedPixel) { TPixelCM32 pixel = ras->pixels(pix.y)[pix.x]; double volumeParziale = ConeSubVolume::compute(distance); - /*- 現在のToneに乗算していく -*/ + /*- Multiply to current Tone. -*/ int newTone = tround((double)pixel.getTone() * (1.0 - volumeParziale)); assert(newTone >= 0 && newTone <= 255); ras->pixels(pix.y)[pix.x] = TPixelCM32(styleId, pixel.getPaint(), newTone); diff --git a/toonz/sources/toonzlib/rasterstrokegenerator.cpp b/toonz/sources/toonzlib/rasterstrokegenerator.cpp index 67177276..f2ee7b82 100644 --- a/toonz/sources/toonzlib/rasterstrokegenerator.cpp +++ b/toonz/sources/toonzlib/rasterstrokegenerator.cpp @@ -92,7 +92,7 @@ void RasterStrokeGenerator::generateStroke(bool isPencil, } rasterBrush(rasBuffer, partialPoints, m_styleId, !isPencil); placeOver(m_raster, rasBuffer, newOrigin); - } else if (size % 2 == 1) /*-- 奇数の場合 --*/ + } else if (size % 2 == 1) /*-- In the case of odd numbers --*/ { int strokeCount = (size - 1) / 2 - 1; std::vector partialPoints; @@ -175,7 +175,7 @@ TRect RasterStrokeGenerator::getBBox( x1 = -(std::numeric_limits::max)(), y1 = -(std::numeric_limits::max)(); for (int i = 0; i < (int)points.size(); i++) { - double radius = points[i].thick * 0.5; + double radius = points[i].thick * 0.5; if (points[i].x - radius < x0) x0 = points[i].x - radius; if (points[i].x + radius > x1) x1 = points[i].x + radius; if (points[i].y - radius < y0) y0 = points[i].y - radius; @@ -208,7 +208,7 @@ void RasterStrokeGenerator::placeOver(const TRasterCM32P &out, TRect box2 = box - p; TRasterCM32P rIn = in->extract(box2); for (int y = 0; y < rOut->getLy(); y++) { - /*-- Finger Toolの境界条件 --*/ + /*--Finger Tool Boundary Conditions --*/ if (m_task == FINGER && (y == 0 || y == rOut->getLy() - 1)) continue; TPixelCM32 *inPix = rIn->pixels(y); @@ -298,26 +298,27 @@ void RasterStrokeGenerator::placeOver(const TRasterCM32P &out, /*-- Finger tool --*/ else if (m_task == FINGER) { - /*-- 境界条件 --*/ + /*-- Boundary Conditions --*/ if (outPix == rOut->pixels(y) || outPix == outEnd - 1) continue; int inkId = inPix->getInk(); if (inkId == 0) continue; TPixelCM32 *neighbourPixels[4]; - neighbourPixels[0] = outPix - 1; /* 左 */ - neighbourPixels[1] = outPix + 1; /* 右 */ - neighbourPixels[2] = outPix - rOut->getWrap(); /* 上 */ - neighbourPixels[3] = outPix + rOut->getWrap(); /* 下 */ + neighbourPixels[0] = outPix - 1; /* left */ + neighbourPixels[1] = outPix + 1; /* right */ + neighbourPixels[2] = outPix - rOut->getWrap(); /* top */ + neighbourPixels[3] = outPix + rOut->getWrap(); /* bottom */ int count = 0; int tone = outPix->getTone(); - /*--- Invertがオフのとき:穴を埋める操作 ---*/ + /*--- When Invert is off: Fill hole operation ---*/ if (!m_selective) { - /*-- 4近傍のピクセルについて --*/ + /*-- For 4 neighborhood pixels --*/ int minTone = tone; for (int p = 0; p < 4; p++) { - /*-- 自分Pixelより線が濃い(Toneが低い)ものを集計する --*/ + /*-- Count up the items that have darker lines (lower Tone) than the + * current pixel. --*/ if (neighbourPixels[p]->getTone() < tone) { count++; if (neighbourPixels[p]->getTone() < minTone) @@ -325,19 +326,21 @@ void RasterStrokeGenerator::placeOver(const TRasterCM32P &out, } } - /*--- 周りの3つ以上のピクセルが濃ければ、最小Toneと置き換える ---*/ + /*--- If 3 or more surrounding pixels are darker, replace with the + * minimum Tone ---*/ if (count <= 2) continue; *outPix = TPixelCM32(inkId, outPix->getPaint(), minTone); } - /*--- InvertがONのとき:出っ張りを削る操作 ---*/ + /*--- When Invert is ON: Operation to trim protrusion ---*/ else { if (outPix->isPurePaint() || outPix->getInk() != inkId) continue; - /*-- 4近傍のピクセルについて --*/ + /*-- For 4 neighborhood pixels --*/ int maxTone = tone; for (int p = 0; p < 4; p++) { /*-- - * Ink#がCurrentでない、または、自分Pixelより線が薄い(Toneが高い)ものを集計する + * Count up items whose Ink# is not Current or whose line is thinner + * than your Pixel (Tone is higher). * --*/ if (neighbourPixels[p]->getInk() != inkId) { count++; @@ -349,7 +352,8 @@ void RasterStrokeGenerator::placeOver(const TRasterCM32P &out, } } - /*--- 周りの3つ以上のピクセルが薄ければ、最大Toneと置き換える ---*/ + /*--- If 3 or more surrounding pixels are thinner, replace with the + * maximum Tone ---*/ if (count <= 2) continue; *outPix = TPixelCM32((maxTone == 255) ? 0 : inkId, outPix->getPaint(), maxTone); diff --git a/toonz/sources/toonzlib/scenefx.cpp b/toonz/sources/toonzlib/scenefx.cpp index a3a5421e..b1a2f243 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() {} @@ -145,6 +147,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 &); @@ -269,15 +276,11 @@ public: , m_isPostXsheetNode(false) {} bool operator<(const PlacedFx &pf) const { - return (m_z < pf.m_z) - ? true - : (m_z > pf.m_z) - ? false - : (m_so < pf.m_so) - ? true - : (m_so > pf.m_so) - ? false - : (m_columnIndex < pf.m_columnIndex); + return (m_z < pf.m_z) ? true + : (m_z > pf.m_z) ? false + : (m_so < pf.m_so) ? true + : (m_so > pf.m_so) ? false + : (m_columnIndex < pf.m_columnIndex); } TFxP makeFx() { @@ -362,7 +365,7 @@ static bool getColumnPlacement(PlacedFx &pf, TXsheet *xsh, double row, int col, } //------------------------------------------------------------------- -/*-- Objectの位置を得る --*/ +/*-- Obtain the position of the Object --*/ static bool getStageObjectPlacement(TAffine &aff, TXsheet *xsh, double row, TStageObjectId &id, bool isPreview) { TStageObject *pegbar = xsh->getStageObjectTree()->getStageObject(id, false); @@ -387,7 +390,7 @@ static bool getStageObjectPlacement(TAffine &aff, TXsheet *xsh, double row, return isVisible; } -/*-- typeとindexからStageObjectIdを得る --*/ +/*-- Get StageObjectId from type and index --*/ namespace { TStageObjectId getMotionObjectId(MotionObjectType type, int index) { switch (type) { @@ -424,7 +427,7 @@ static TPointD getColumnSpeed(TXsheet *xsh, double row, int col, const double h = 0.001; getColumnPlacement(aff, xsh, row + h, col, isPreview); - /*-- カラムと、カメラの動きに反応 --*/ + /*-- Reacts to columns and camera movement --*/ TStageObjectId cameraId; if (isPreview) cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId(); @@ -444,15 +447,15 @@ static TPointD getColumnSpeed(TXsheet *xsh, double row, int col, } //------------------------------------------------------------------- -/*-- オブジェクトの軌跡を、基準点との差分で得る - objectId: 移動の参考にするオブジェクト。自分自身の場合はNoneId +/*-- Obtain the trajectory of an object by the difference from the reference +point objectId: The reference object for the move. NoneId for itself. --*/ static QList getColumnMotionPoints(TXsheet *xsh, double row, int col, TStageObjectId &objectId, bool isPreview, double shutterStart, double shutterEnd, int traceResolution) { - /*-- 前後フレームが共に0なら空のリストを返す --*/ + /*-- Returns an empty list if the previous and next frames are both zero. --*/ if (shutterStart == 0.0 && shutterEnd == 0.0) return QList(); /*-- 現在のカメラを得る --*/ @@ -516,6 +519,57 @@ static QList getColumnMotionPoints(TXsheet *xsh, double row, int col, return points; } +//------------------------------------------------------------------- +/*-- + フレーム前後のアフィン変換を得る + objectId: 移動の参考にするオブジェクト。自分自身の場合はNoneId +--*/ +static void getColumnMotionAffines(TAffine &aff_Before, TAffine &aff_After, + TXsheet *xsh, double row, int col, + TStageObjectId &objectId, bool isPreview, + double shutterLength) { + /*-- 現在のカメラを得る --*/ + TStageObjectId cameraId; + if (isPreview) + cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId(); + else + cameraId = xsh->getStageObjectTree()->getCurrentCameraId(); + TStageObject *camera = xsh->getStageObject(cameraId); + TAffine dpiAff = getDpiAffine(camera->getCamera()); + + /*-- objectIdが有効なものかどうかチェック --*/ + bool useOwnMotion = false; + if (objectId == TStageObjectId::NoneId || + !xsh->getStageObjectTree()->getStageObject(objectId, false)) { + useOwnMotion = true; + } + + /*-- 結果を収めるリスト --*/ + TAffine retAff[2]; + TAffine aff; + + /*-- 各点の位置を、基準点との差分で格納していく --*/ + for (int i = 0; i < 2; i++) { + /*-- 基準位置とのフレーム差 --*/ + double frameOffset = (i == 0) ? -shutterLength : shutterLength; + double targetFrame = row + frameOffset; + // Proper position cannot be obtained for frame = -1.0 + if (targetFrame == -1.0) targetFrame = -0.9999; + + /*-- 自分自身の動きを使うか、別オブジェクトの動きを使うか --*/ + if (useOwnMotion) + getColumnPlacement(aff, xsh, targetFrame, col, isPreview); + else + getStageObjectPlacement(aff, xsh, targetFrame, objectId, isPreview); + + TAffine cameraAff = camera->getPlacement(targetFrame); + retAff[i] = dpiAff.inv() * aff * cameraAff.inv(); + } + + aff_Before = retAff[0]; + aff_After = retAff[1]; +} + namespace { QString getNoteText(TXsheet *xsh, double row, int col, int noteColumnIndex, @@ -982,12 +1036,13 @@ PlacedFx FxBuilder::makePF(TZeraryColumnFx *zcfx) { PlacedFx pf; pf.m_columnIndex = zcfx->getColumn()->getIndex(); - // Add the column placement NaAffineFx - if (!getColumnPlacement(pf, m_xsh, m_frame, pf.m_columnIndex, m_isPreview)) - return PlacedFx(); - // if the cell is empty, only inherits its placement - if (cell.isEmpty() || cell.getFrameId().isStopFrame()) return pf; + if (cell.isEmpty() || cell.getFrameId().isStopFrame()) { + // Add the column placement NaAffineFx + if (!getColumnPlacement(pf, m_xsh, m_frame, pf.m_columnIndex, m_isPreview)) + return PlacedFx(); + return pf; + } // set m_fx only when the current cell is not empty pf.m_fx = @@ -1034,8 +1089,27 @@ PlacedFx FxBuilder::makePF(TZeraryColumnFx *zcfx) { noteColumnIndex, getNeighbor)); } } + if (pf.m_fx->getAttributes()->isSpeedAware()) { + MotionAwareAffineFx *maafx = + dynamic_cast(pf.m_fx.getPointer()); + if (maafx) { + double shutterLength = maafx->getShutterLength()->getValue(m_frame); + MotionObjectType type = maafx->getMotionObjectType(); + int index = maafx->getMotionObjectIndex()->getValue(); + TStageObjectId objectId = getMotionObjectId(type, index); + TAffine aff_Before, aff_After; + getColumnMotionAffines(aff_Before, aff_After, m_xsh, m_frame, + pf.m_columnIndex, objectId, m_isPreview, + shutterLength); + pf.m_fx->getAttributes()->setMotionAffines(aff_Before, aff_After); + } + } - return pf; + // Add the column placement NaAffineFx + if (getColumnPlacement(pf, m_xsh, m_frame, pf.m_columnIndex, m_isPreview)) + return pf; + else + return PlacedFx(); } //------------------------------------------------------------------- @@ -1178,8 +1252,14 @@ PlacedFx FxBuilder::makePFfromGenericFx(TFx *fx) { int m = fx->getInputPortCount(); for (int i = 0; i < m; ++i) { if (TFxP inputFx = fx->getInputPort(i)->getFx()) { - PlacedFx inputPF = makePF(inputFx.getPointer()); - inputFx = inputPF.m_fx; + PlacedFx inputPF; + if (fx->getFxType() == "STD_iwa_FlowPaintBrushFx") { + m_particleDescendentCount++; + inputPF = makePF(inputFx.getPointer()); + m_particleDescendentCount--; + } else + inputPF = makePF(inputFx.getPointer()); + inputFx = inputPF.m_fx; // check the column index instead of inputFx // so that the firstly-found input column always inherits // its placement even if the current cell is empty. diff --git a/toonz/sources/toonzlib/sceneproperties.cpp b/toonz/sources/toonzlib/sceneproperties.cpp index 8f8ebccd..8d67a047 100644 --- a/toonz/sources/toonzlib/sceneproperties.cpp +++ b/toonz/sources/toonzlib/sceneproperties.cpp @@ -226,6 +226,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(); @@ -377,7 +387,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; @@ -528,7 +540,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; @@ -775,6 +804,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); + } } //----------------------------------------------------------------------------- @@ -875,4 +911,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/scriptbinding_level.cpp b/toonz/sources/toonzlib/scriptbinding_level.cpp index 20e8bab0..f5dade6f 100644 --- a/toonz/sources/toonzlib/scriptbinding_level.cpp +++ b/toonz/sources/toonzlib/scriptbinding_level.cpp @@ -221,14 +221,10 @@ TFrameId Level::getFid(const QScriptValue &arg, QString &err) { QString c = re.cap(2); TFrameId fid; if (c.length() == 1) -#if QT_VERSION >= 0x050500 fid = TFrameId(d, c[0].unicode()); -#else - fid = TFrameId(d, c[0].toAscii()); -#endif else fid = TFrameId(d); - err = ""; + err = ""; return fid; } } diff --git a/toonz/sources/toonzlib/stylemanager.cpp b/toonz/sources/toonzlib/stylemanager.cpp index 1a6958c9..07d4167c 100644 --- a/toonz/sources/toonzlib/stylemanager.cpp +++ b/toonz/sources/toonzlib/stylemanager.cpp @@ -198,9 +198,10 @@ void CustomStyleManager::StyleLoaderTask::run() { #endif m_data.m_path = m_fp; - m_data.m_patternName = m_fp.getName(); + m_data.m_patternName = QString::fromStdString(m_fp.getName()); m_data.m_isVector = (m_fp.getType() == "pli" || m_fp.getType() == "svg"); m_data.m_image = image; + m_data.m_idName = TTextureStyle::staticBrushIdName(m_fp.getLevelNameW()); } catch (...) { } } @@ -223,7 +224,10 @@ void CustomStyleManager::StyleLoaderTask::onFinished( CustomStyleManager::CustomStyleManager(const TFilePath &stylesFolder, QString filters, QSize chipSize) - : m_stylesFolder(stylesFolder), m_filters(filters), m_chipSize(chipSize) { + : m_stylesFolder(stylesFolder) + , m_filters(filters) + , m_chipSize(chipSize) + , m_isIndexed(false) { m_executor.setMaxActiveTasks(1); } @@ -240,17 +244,41 @@ void CustomStyleManager::loadItemFinished(TFilePath file) { //----------------------------------------------------------------------------- -int CustomStyleManager::getPatternCount() { return m_patterns.size(); } +int CustomStyleManager::getPatternCount() { + return m_isIndexed ? m_indexes.count() : m_patterns.size(); +} //----------------------------------------------------------------------------- CustomStyleManager::PatternData CustomStyleManager::getPattern(int index) { + if (m_isIndexed) + return (index < 0 || index >= m_indexes.count()) + ? PatternData() + : m_patterns[m_indexes[index]]; + return (index < 0 || index >= m_patterns.size()) ? PatternData() : m_patterns[index]; } //----------------------------------------------------------------------------- +void CustomStyleManager::applyFilter() { + QList indexes; + + m_indexes.clear(); + int len = m_patterns.count(); + for (int i = 0; i < len; i++) { + auto &chip = m_patterns[i]; + if (chip.m_patternName.indexOf(m_searchText, 0, Qt::CaseInsensitive) >= 0) + m_indexes.append(i); + } + + m_indexes.append(indexes); + m_isIndexed = (m_indexes.count() != len); +} + +//----------------------------------------------------------------------------- + void CustomStyleManager::loadItems() { // Build the folder to be read if (m_stylesFolder == TFilePath()) return; @@ -323,9 +351,10 @@ void CustomStyleManager::loadGeneratedStyle(TFilePath file) { convertRaster32ToImage(style->getIcon(chipSize), image); pattern.m_path = file; - pattern.m_patternName = nameParts[0].toStdString(); + pattern.m_patternName = nameParts[0]; pattern.m_isGenerated = true; pattern.m_image = image; + pattern.m_idName = style->getBrushIdName(); m_patterns.push_back(pattern); } @@ -346,21 +375,48 @@ void CustomStyleManager::setStyleFolder(TFilePath styleFolder) { TextureStyleManager::TextureStyleManager(const TFilePath &stylesFolder, QString filters, QSize chipSize) - : m_stylesFolder(stylesFolder), m_filters(filters), m_chipSize(chipSize) {} + : m_stylesFolder(stylesFolder) + , m_filters(filters) + , m_chipSize(chipSize) + , m_isIndexed(false) {} //----------------------------------------------------------------------------- -int TextureStyleManager::getTextureCount() { return m_textures.size(); } +int TextureStyleManager::getTextureCount() { + return m_isIndexed ? m_indexes.count() : m_textures.size(); +} //----------------------------------------------------------------------------- TextureStyleManager::TextureData TextureStyleManager::getTexture(int index) { + if (m_isIndexed) + return (index < 0 || index >= m_indexes.count()) + ? TextureData() + : m_textures[m_indexes[index]]; + return (index < 0 || index >= m_textures.size()) ? TextureData() : m_textures[index]; } //----------------------------------------------------------------------------- +void TextureStyleManager::applyFilter() { + QList indexes; + + m_indexes.clear(); + int len = m_textures.count(); + for (int i = 0; i < len; i++) { + auto &chip = m_textures[i]; + if (chip.m_textureName.indexOf(m_searchText, 0, Qt::CaseInsensitive) >= 0) + m_indexes.append(i); + } + + m_indexes.append(indexes); + m_isIndexed = (m_indexes.count() != len); +} + +//----------------------------------------------------------------------------- + void TextureStyleManager::loadItems() { // Build the folder to be read @@ -418,8 +474,9 @@ void TextureStyleManager::loadTexture(TFilePath &fp) { TTextureStyle::fillCustomTextureIcon(ras); TextureData customText; customText.m_raster = ras; - customText.m_textureName = std::string(""); + customText.m_textureName = ""; customText.m_path = fp; + customText.m_idName = TTextureStyle::staticBrushIdName(fp.getLevelNameW()); m_textures.push_back(customText); return; @@ -448,8 +505,9 @@ void TextureStyleManager::loadTexture(TFilePath &fp) { TextureData text; text.m_raster = texture; - text.m_textureName = fp.getName(); + text.m_textureName = QString::fromStdString(fp.getName()); text.m_path = fp; + text.m_idName = TTextureStyle::staticBrushIdName(fp.getLevelNameW()); m_textures.push_back(text); } @@ -470,21 +528,48 @@ void TextureStyleManager::setStyleFolder(TFilePath styleFolder) { BrushStyleManager::BrushStyleManager(const TFilePath &stylesFolder, QString filters, QSize chipSize) - : m_stylesFolder(stylesFolder), m_filters(filters), m_chipSize(chipSize) {} + : m_stylesFolder(stylesFolder) + , m_filters(filters) + , m_chipSize(chipSize) + , m_isIndexed(false) {} //----------------------------------------------------------------------------- -int BrushStyleManager::getBrushCount() { return m_brushes.size(); } +int BrushStyleManager::getBrushCount() { + return m_isIndexed ? m_indexes.count() : m_brushes.size(); +} //----------------------------------------------------------------------------- BrushStyleManager::BrushData BrushStyleManager::getBrush(int index) { + if (m_isIndexed) + return (index < 0 || index >= m_indexes.count()) + ? BrushData() + : m_brushes[m_indexes[index]]; + return (index < 0 || index >= m_brushes.size()) ? BrushData() : m_brushes[index]; } //----------------------------------------------------------------------------- +void BrushStyleManager::applyFilter() { + QList indexes; + + m_indexes.clear(); + int len = m_brushes.count(); + for (int i = 0; i < len; i++) { + auto &chip = m_brushes[i]; + if (chip.m_brushName.indexOf(m_searchText, 0, Qt::CaseInsensitive) >= 0) + m_indexes.append(i); + } + + m_indexes.append(indexes); + m_isIndexed = (m_indexes.count() != len); +} + +//----------------------------------------------------------------------------- + void BrushStyleManager::loadItems() { // Build the folder to be read @@ -524,8 +609,9 @@ void BrushStyleManager::loadItems() { for (TFilePathSet::iterator it = fps.begin(); it != fps.end(); it++) { BrushData brush; brush.m_brush = TMyPaintBrushStyle(*it); - brush.m_brushName = it->getName(); + brush.m_brushName = QString::fromStdString(it->getName()); brush.m_path = *it; + brush.m_idName = brush.m_brush.getBrushIdName(); m_brushes.push_back(brush); brushesUpdated = true; @@ -736,7 +822,7 @@ void TStyleManager::changeStyleSetFolder(CustomStyleManager *styleManager, std::pair newKey(newPath, styleManager->getFilters()); std::vector>::iterator it; - int i; + int i = 0; for (it = m_customStyleFolders.begin(); it != m_customStyleFolders.end(); it++) { if (*it == oldKey) { @@ -764,7 +850,7 @@ void TStyleManager::changeStyleSetFolder(TextureStyleManager *styleManager, std::pair newKey(newPath, styleManager->getFilters()); std::vector>::iterator it; - int i; + int i = 0; for (it = m_textureStyleFolders.begin(); it != m_textureStyleFolders.end(); it++) { if (*it == oldKey) { @@ -792,7 +878,7 @@ void TStyleManager::changeStyleSetFolder(BrushStyleManager *styleManager, std::pair newKey(newPath, styleManager->getFilters()); std::vector>::iterator it; - int i; + int i = 0; for (it = m_brushStyleFolders.begin(); it != m_brushStyleFolders.end(); it++) { if (*it == oldKey) { diff --git a/toonz/sources/toonzlib/tcamera.cpp b/toonz/sources/toonzlib/tcamera.cpp index 99d68c9a..5710c5dd 100644 --- a/toonz/sources/toonzlib/tcamera.cpp +++ b/toonz/sources/toonzlib/tcamera.cpp @@ -11,9 +11,7 @@ TCamera::TCamera() //: m_size(12, 9), m_res(768, 576), m_xPrevalence(true) //: m_size(36, 20.25), - : m_size(16, 9), - m_res(1920, 1080), - m_xPrevalence(true) {} + : m_size(16, 9), m_res(1920, 1080), m_xPrevalence(true) {} //------------------------------------------------------------------- @@ -110,15 +108,18 @@ TRectD TCamera::getStageRect() const { //------------------------------------------------------------------- void TCamera::setInterestRect(const TRect &rect) { - // Not using the TRect's common intersection. Unfortunately, in case - // the rect's coordinates have lx or ly < 0, the intersection returns - // the default (empty) rect. We want to maintain the coordinates instead. - // m_interestRect = rect * TRect(m_res); - - m_interestRect.x0 = std::max(rect.x0, 0); - m_interestRect.y0 = std::max(rect.y0, 0); - m_interestRect.x1 = std::min(rect.x1, m_res.lx - 1); - m_interestRect.y1 = std::min(rect.y1, m_res.ly - 1); + // enable to preview outside of the original camera rect + m_interestRect = rect; + return; + //// Not using the TRect's common intersection. Unfortunately, in case + //// the rect's coordinates have lx or ly < 0, the intersection returns + //// the default (empty) rect. We want to maintain the coordinates instead. + //// m_interestRect = rect * TRect(m_res); + // + // m_interestRect.x0 = std::max(rect.x0, 0); + // m_interestRect.y0 = std::max(rect.y0, 0); + // m_interestRect.x1 = std::min(rect.x1, m_res.lx - 1); + // m_interestRect.y1 = std::min(rect.y1, m_res.ly - 1); } //------------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/tcolumnfx.cpp b/toonz/sources/toonzlib/tcolumnfx.cpp index 94b0a9c8..20dba525 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; } @@ -723,7 +729,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; @@ -735,23 +743,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(); @@ -802,6 +816,7 @@ public: TLevelColumnFx::TLevelColumnFx() : m_levelColumn(0), m_isCachable(true), m_mutex(), m_offlineContext(0) { setName(L"LevelColumn"); + enableComputeInFloat(true); } //-------------------------------------------------- @@ -1086,13 +1101,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()); @@ -1102,6 +1119,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); } @@ -1126,7 +1147,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 098a7016..9a77a40c 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/thirdparty.cpp b/toonz/sources/toonzlib/thirdparty.cpp new file mode 100644 index 00000000..aaefe2bb --- /dev/null +++ b/toonz/sources/toonzlib/thirdparty.cpp @@ -0,0 +1,350 @@ +#include "thirdparty.h" + +// TnzLib includes +#include "toonz/preferences.h" + +// TnzCore includes +#include "tsystem.h" +#include "tmsgcore.h" +#include "tenv.h" + +// QT includes +#include +#include +#include + +namespace ThirdParty { + +//----------------------------------------------------------------------------- + +void initialize() { + // Auto detect FFmpeg + if (!ThirdParty::checkFFmpeg()) { + QString path = ThirdParty::autodetectFFmpeg(); + if (!path.isEmpty()) ThirdParty::setFFmpegDir(path); + } + // Auto detect Rhubarb + if (!ThirdParty::checkRhubarb()) { + QString path = ThirdParty::autodetectRhubarb(); + if (!path.isEmpty()) ThirdParty::setRhubarbDir(path); + } +} + +//============================================================================= +// FFmpeg interface +//----------------------------------------------------------------------------- + +#ifdef _WIN32 +#define FFMPEG_EXE "/ffmpeg.exe" +#define FFPROBE_EXE "/ffprobe.exe" +#else +#define FFMPEG_EXE "/ffmpeg" +#define FFPROBE_EXE "/ffprobe" +#endif + +//----------------------------------------------------------------------------- + +void getFFmpegVideoSupported(QStringList &exts) { + exts.append("gif"); + exts.append("mp4"); + exts.append("webm"); +} + +//----------------------------------------------------------------------------- + +void getFFmpegAudioSupported(QStringList &exts) { + exts.append("mp3"); + exts.append("ogg"); + exts.append("flac"); +} + +//----------------------------------------------------------------------------- + +bool findFFmpeg(QString dir) { + // Relative path + if (dir.isEmpty() || dir.at(0) == ".") { + dir = QCoreApplication::applicationDirPath() + "/" + dir; + } + + // Check if both executables exist + return TSystem::doesExistFileOrLevel(TFilePath(dir + FFMPEG_EXE)) && + TSystem::doesExistFileOrLevel(TFilePath(dir + FFPROBE_EXE)); +} + +//----------------------------------------------------------------------------- + +bool checkFFmpeg() { + // Path in preferences + return findFFmpeg(Preferences::instance()->getFfmpegPath()); +} + +//----------------------------------------------------------------------------- + +QString autodetectFFmpeg() { + QString dir = Preferences::instance()->getFfmpegPath(); + if (findFFmpeg(dir)) return dir; + + // Let's try and autodetect the exe included with release + QStringList folderList; + + folderList.append("."); + folderList.append("./ffmpeg"); // ffmpeg folder + +#ifdef MACOSX + // Look inside app + folderList.append("./" + + QString::fromStdString(TEnv::getApplicationFileName()) + + ".app/ffmpeg"); // ffmpeg folder +#elif defined(LINUX) || defined(FREEBSD) + // Need to account for symbolic links + folderList.append(TEnv::getWorkingDirectory().getQString() + + "/ffmpeg"); // ffmpeg folder +#endif + +#ifndef _WIN32 + folderList.append("/usr/local/bin"); + folderList.append("/usr/bin"); + folderList.append("/bin"); +#endif + +#ifdef _WIN32 + QString exePath = TSystem::findFileLocation(folderList, "ffmpeg.exe"); +#else + QString exePath = TSystem::findFileLocation(folderList, "ffmpeg"); +#endif + + if (!exePath.isEmpty()) return exePath; + + // give up + return ""; +} + +//----------------------------------------------------------------------------- + +QString getFFmpegDir() { + return Preferences::instance()->getStringValue(ffmpegPath); +} + +//----------------------------------------------------------------------------- + +void setFFmpegDir(const QString &dir) { + QString path = Preferences::instance()->getFfmpegPath(); + if (path != dir) Preferences::instance()->setValue(ffmpegPath, dir); +} + +//----------------------------------------------------------------------------- + +int getFFmpegTimeout() { + return Preferences::instance()->getIntValue(ffmpegTimeout); +} + +//----------------------------------------------------------------------------- + +void setFFmpegTimeout(int secs) { + int timeout = Preferences::instance()->getIntValue(ffmpegTimeout); + if (secs != timeout) Preferences::instance()->setValue(ffmpegTimeout, secs); +} + +//----------------------------------------------------------------------------- + +void runFFmpeg(QProcess &process, const QStringList &arguments) { + QString dir = Preferences::instance()->getFfmpegPath(); + if (dir.at(0) == '.') // Relative path + dir = QCoreApplication::applicationDirPath() + "/" + dir; + + process.start(dir + FFMPEG_EXE, arguments); +} + +//----------------------------------------------------------------------------- + +void runFFprobe(QProcess &process, const QStringList &arguments) { + QString dir = Preferences::instance()->getFfmpegPath(); + if (dir.at(0) == '.') // Relative path + dir = QCoreApplication::applicationDirPath() + "/" + dir; + + process.start(dir + FFPROBE_EXE, arguments); +} + +//----------------------------------------------------------------------------- + +void runFFmpegAudio(QProcess &process, QString srcPath, QString dstPath, + int samplerate, int bpp, int channels) { + QStringList args; + args << "-y"; + args << "-i"; + args << srcPath; + args << "-f"; + switch (bpp) { + case 8: // unsigned + args << "u8"; + break; + case 16: + args << "s16le"; + break; + case 24: + args << "s24le"; + break; + case 32: // floating-point + args << "f32le"; + break; + default: + return; + } + args << "-ac"; + args << QString::number(channels); + args << "-ar"; + args << QString::number(samplerate); + args << dstPath; + + ThirdParty::runFFmpeg(process, args); +} + +//----------------------------------------------------------------------------- + +bool readFFmpegAudio(QProcess &process, QByteArray &rawData) { + if (!process.waitForStarted()) return false; + if (!process.waitForFinished(30000)) return false; + bool success = (process.exitCode() == 0); + + if (success) rawData = process.readAllStandardOutput(); + process.close(); + + return success; +} + +//============================================================================= +// Rhubarb interface +//----------------------------------------------------------------------------- + +#ifdef _WIN32 +#define RHUBARB_EXE "/rhubarb.exe" +#else +#define RHUBARB_EXE "/rhubarb" +#endif + +//----------------------------------------------------------------------------- + +bool findRhubarb(QString dir) { + // Rhubarb executable + + // Relative path + if (dir.isEmpty() || dir.at(0) == ".") { + dir = QCoreApplication::applicationDirPath() + "/" + dir; + } + + // Check if executable exist + return TSystem::doesExistFileOrLevel(TFilePath(dir + RHUBARB_EXE)); +} + +//----------------------------------------------------------------------------- + +bool checkRhubarb() { + // Path in preferences + return findRhubarb(Preferences::instance()->getRhubarbPath()); +} + +//----------------------------------------------------------------------------- + +QString autodetectRhubarb() { + QString dir = Preferences::instance()->getRhubarbPath(); + if (findRhubarb(dir)) return dir; + + // Let's try and autodetect the exe included with release + QStringList folderList; + + folderList.append("."); + folderList.append("./rhubarb"); // rhubarb folder + +#ifdef MACOSX + // Look inside app + folderList.append("./" + + QString::fromStdString(TEnv::getApplicationFileName()) + + ".app/rhubarb"); // rhubarb folder +#elif defined(LINUX) || defined(FREEBSD) + // Need to account for symbolic links + folderList.append(TEnv::getWorkingDirectory().getQString() + + "/rhubarb"); // rhubarb folder +#endif + +#ifndef _WIN32 + folderList.append("/usr/local/bin"); + folderList.append("/usr/bin"); + folderList.append("/bin"); +#endif + +#ifdef _WIN32 + QString exePath = TSystem::findFileLocation(folderList, "rhubarb.exe"); +#else + QString exePath = TSystem::findFileLocation(folderList, "rhubarb"); +#endif + + if (!exePath.isEmpty()) { + Preferences::instance()->setValue(rhubarbPath, exePath); + return exePath; + } + + // give up + return ""; +} + +//----------------------------------------------------------------------------- + +void runRhubarb(QProcess &process, const QStringList &arguments) { + QString dir = Preferences::instance()->getRhubarbPath(); + if (dir.at(0) == '.') // Relative path + dir = QCoreApplication::applicationDirPath() + "/" + dir; + + process.start(dir + RHUBARB_EXE, arguments); +} + +//----------------------------------------------------------------------------- + +QString getRhubarbDir() { + return Preferences::instance()->getStringValue(rhubarbPath); +} + +//----------------------------------------------------------------------------- + +void setRhubarbDir(const QString &dir) { + QString path = Preferences::instance()->getRhubarbPath(); + if (path != dir) Preferences::instance()->setValue(rhubarbPath, dir); +} + +//----------------------------------------------------------------------------- + +int getRhubarbTimeout() { + return Preferences::instance()->getIntValue(rhubarbTimeout); +} + +//----------------------------------------------------------------------------- + +void setRhubarbTimeout(int secs) { + int timeout = Preferences ::instance()->getIntValue(rhubarbTimeout); + if (secs != timeout) Preferences::instance()->setValue(rhubarbTimeout, secs); +} + +//----------------------------------------------------------------------------- + +int waitAsyncProcess(const QProcess &process, int timeout) { + QEventLoop eloop; + QTimer timer; + timer.connect(&timer, &QTimer::timeout, &eloop, [&eloop] { eloop.exit(-2); }); + QMetaObject::Connection con1 = process.connect( + &process, &QProcess::errorOccurred, &eloop, [&eloop] { eloop.exit(-1); }); + QMetaObject::Connection con2 = process.connect( + &process, + static_cast( + &QProcess::finished), + &eloop, &QEventLoop::quit); + if (timeout >= 0) timer.start(timeout); + + int result = eloop.exec(); + process.disconnect(con1); + process.disconnect(con2); + + return result; +} + +//----------------------------------------------------------------------------- + +} // namespace ThirdParty \ No newline at end of file diff --git a/toonz/sources/toonzlib/toonzimageutils.cpp b/toonz/sources/toonzlib/toonzimageutils.cpp index bee0c0a9..12775db3 100644 --- a/toonz/sources/toonzlib/toonzimageutils.cpp +++ b/toonz/sources/toonzlib/toonzimageutils.cpp @@ -101,7 +101,7 @@ TRect rasterizeStroke(TOfflineGL *&gl, TRect rasBounds, TStroke *stroke, TRect rasterizeRegion(TOfflineGL *&gl, TRect rasBounds, TRegion *region, TRectD clip) { - TRectD regionBBox = region->getBBox(); + TRectD regionBBox = region->getBBox(); if (!clip.isEmpty()) regionBBox = regionBBox * clip; TRect rect = convert(regionBBox) * rasBounds; @@ -169,7 +169,8 @@ void fastAddPaintRegion(const TToonzImageP &ti, TRegion *region, int newPaintId, std::min(maxStyleId, subregion->getStyle()), maxStyleId); } } -} +} // namespace + //------------------------------------------------------------------- @@ -455,7 +456,7 @@ TToonzImageP ToonzImageUtils::vectorToToonzImage( else { visible = false; for (int j = 0; j < style->getColorParamCount() && !visible; j++) { - TPixel32 color = style->getColorParamValue(j); + TPixel32 color = style->getColorParamValue(j); if (color.m != 0) visible = true; } } @@ -577,7 +578,7 @@ void ToonzImageUtils::scrambleStyles(const TToonzImageP &ti, assert(j >= 0); assert(j < 1000000); if (j >= (int)lut.size()) lut.resize(j + 1, -1); - lut[j] = k; + lut[j] = k; if (j != k) isIdentity = false; } if (isIdentity) return; @@ -590,9 +591,9 @@ void ToonzImageUtils::scrambleStyles(const TToonzImageP &ti, TPixelCM32 *pix = ras->pixels(y); TPixelCM32 *endPix = pix + lx; while (pix < endPix) { - int ink = pix->getInk(); - if (0 <= ink && ink < m && lut[ink] >= 0) ink = lut[ink]; - int paint = pix->getPaint(); + int ink = pix->getInk(); + if (0 <= ink && ink < m && lut[ink] >= 0) ink = lut[ink]; + int paint = pix->getPaint(); if (0 <= paint && paint < m && lut[paint] >= 0) paint = lut[paint]; if (ink != pix->getInk() || paint != pix->getPaint()) { *pix = TPixelCM32(ink, paint, pix->getTone()); @@ -735,16 +736,16 @@ void ToonzImageUtils::eraseImage(const TToonzImageP &ti, paint = inPix->m > 0 && erasePaint && canErasePaint ? 0 : outPix->getPaint(); - tone = inPix->m > 0 && eraseInk && canEraseInk - ? std::max(outPix->getTone(), (int)inPix->m) - : outPix->getTone(); + tone = inPix->m > 0 && eraseInk && canEraseInk + ? std::max(outPix->getTone(), (int)inPix->m) + : outPix->getTone(); } else { paint = inPix->m < 255 && erasePaint && canErasePaint ? 0 : outPix->getPaint(); - tone = inPix->m < 255 && eraseInk && canEraseInk - ? std::max(outPix->getTone(), 255 - (int)inPix->m) - : outPix->getTone(); + tone = inPix->m < 255 && eraseInk && canEraseInk + ? std::max(outPix->getTone(), 255 - (int)inPix->m) + : outPix->getTone(); } *outPix = TPixelCM32(outPix->getInk(), paint, tone); } @@ -800,7 +801,7 @@ else p->setValue(L"64(RGBM)"); } -bool isMovie = (format=="avi"); +bool isMovie = (format=="mov" || format=="avi" || format=="3gp"); TLevelWriterP lw; diff --git a/toonz/sources/toonzlib/toonzscene.cpp b/toonz/sources/toonzlib/toonzscene.cpp index 5d248d0c..3403024d 100644 --- a/toonz/sources/toonzlib/toonzscene.cpp +++ b/toonz/sources/toonzlib/toonzscene.cpp @@ -1184,6 +1184,8 @@ TXshLevel *ToonzScene::loadLevel(const TFilePath &actualPath, LevelProperties *lp = xl->getProperties(); assert(lp); + bool formatSpecified = false; + if (levelOptions) lp->options() = *levelOptions; else { @@ -1191,9 +1193,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()); @@ -1221,6 +1224,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/tproject.cpp b/toonz/sources/toonzlib/tproject.cpp index 96bc3bd6..cf15e2ed 100644 --- a/toonz/sources/toonzlib/tproject.cpp +++ b/toonz/sources/toonzlib/tproject.cpp @@ -836,6 +836,10 @@ TProjectManager *TProjectManager::instance() { return &_instance; } +//------------------------------------------------------------------- +// Clear all projecs roots container. +//void TProjectManager::clearProjectsRoot() { m_projectsRoots.clear(); } + //------------------------------------------------------------------- /*! Adds the specified folder \b fp in the projecs roots container.\n If \b fp is already contained in the container, the method does nothing. diff --git a/toonz/sources/toonzlib/trasterimageutils.cpp b/toonz/sources/toonzlib/trasterimageutils.cpp index b0bc575a..74b3bff4 100644 --- a/toonz/sources/toonzlib/trasterimageutils.cpp +++ b/toonz/sources/toonzlib/trasterimageutils.cpp @@ -122,7 +122,7 @@ TRect fastAddInkStroke(const TRasterImageP &ri, TStroke *stroke, TRectD clip, TRect rasterizeRegion(TOfflineGL *&gl, TRect rasBounds, TRegion *region, TPalette *palette, TRectD clip) { - TRectD regionBBox = region->getBBox(); + TRectD regionBBox = region->getBBox(); if (!clip.isEmpty()) regionBBox = regionBBox * clip; TRect rect = convert(regionBBox) * rasBounds; @@ -172,7 +172,7 @@ void fastAddPaintRegion(const TRasterImageP &ri, TRegion *region, std::min(maxStyleId, subregion->getStyle()), maxStyleId); } } -} +} // namespace //========================================================================== @@ -295,7 +295,7 @@ TRasterImageP TRasterImageUtils::vectorToFullColorImage( else { visible = false; for (int j = 0; j < style->getColorParamCount() && !visible; j++) { - TPixel32 color = style->getColorParamValue(j); + TPixel32 color = style->getColorParamValue(j); if (color.m != 0) visible = true; } } @@ -355,8 +355,8 @@ void TRasterImageUtils::addSceneNumbering(const TRasterImageP &ri, numberingFont.setPixelSize(ly * 0.04); numberingFont.setBold(true); p.setFont(numberingFont); - QMatrix matrix; - p.setMatrix(matrix.translate(0, ly).scale(1, -1), true); + QTransform transform; + p.setTransform(transform.translate(0, ly).scale(1, -1), true); QFontMetrics fm = p.fontMetrics(); int fontHeight = fm.height(); int offset = fontHeight * 0.2; @@ -367,7 +367,11 @@ void TRasterImageUtils::addSceneNumbering(const TRasterImageP &ri, QString sceneNumberingString = QString::fromStdWString(sceneName) + ": " + sceneFrame; +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int sceneNumberingWidth = fm.horizontalAdvance(sceneNumberingString); +#else int sceneNumberingWidth = fm.width(sceneNumberingString); +#endif p.setPen(Qt::NoPen); p.setBrush(QColor(255, 255, 255, 255)); p.drawRect(offset, ly - offset - fontHeight, sceneNumberingWidth + offset * 2, @@ -382,7 +386,11 @@ void TRasterImageUtils::addSceneNumbering(const TRasterImageP &ri, QString globalFrame = QString::number(globalIndex); while (globalFrame.size() < 4) globalFrame.push_front("0"); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int gloablNumberingWidth = fm.horizontalAdvance(globalFrame); +#else int gloablNumberingWidth = fm.width(globalFrame); +#endif p.setPen(Qt::NoPen); p.setBrush(QColor(255, 255, 255, 255)); p.drawRect(lx - 3 * offset - gloablNumberingWidth, ly - offset - fontHeight, @@ -411,8 +419,8 @@ void TRasterImageUtils::addGlobalNumbering(const TRasterImageP &ri, numberingFont.setPixelSize(ly * 0.04); numberingFont.setBold(true); p.setFont(numberingFont); - QMatrix matrix; - p.setMatrix(matrix.translate(0, ly).scale(1, -1), true); + QTransform transform; + p.setTransform(transform.translate(0, ly).scale(1, -1), true); QFontMetrics fm = p.fontMetrics(); int fontHeight = fm.height(); int offset = fontHeight * 0.2; @@ -421,7 +429,11 @@ void TRasterImageUtils::addGlobalNumbering(const TRasterImageP &ri, QString globalNumberingString = QString::fromStdWString(sceneName) + ": " + globalFrame; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + int globalNumberingWidth = fm.horizontalAdvance(globalNumberingString); +#else int globalNumberingWidth = fm.width(globalNumberingString); +#endif p.setPen(Qt::NoPen); p.setBrush(QColor(255, 255, 255, 255)); p.drawRect(offset, ly - offset - fontHeight, diff --git a/toonz/sources/toonzlib/txshcolumn.cpp b/toonz/sources/toonzlib/txshcolumn.cpp index 0907e1fb..c1dd18d6 100644 --- a/toonz/sources/toonzlib/txshcolumn.cpp +++ b/toonz/sources/toonzlib/txshcolumn.cpp @@ -593,6 +593,24 @@ TXshColumn::ColumnType TXshColumn::toColumnType(int levelType) { return colType; } +//----------------------------------------------------------------------------- + +bool TXshColumn::canBeParent() const { + switch (getColumnType()) { + case eLevelType: + case eZeraryFxType: + case ePaletteType: + case eMeshType: + return true; + case eSoundType: + case eSoundTextType: + return false; + default: + assert(!"Unknown level type!"); + return false; + } +} + //----------------------------------------------------------------------------- bool TXshColumn::isRendered() const { // if (!getXsheet() || !getFx()) return false; diff --git a/toonz/sources/toonzlib/txshsimplelevel.cpp b/toonz/sources/toonzlib/txshsimplelevel.cpp index 9f92bec7..82f1ed10 100644 --- a/toonz/sources/toonzlib/txshsimplelevel.cpp +++ b/toonz/sources/toonzlib/txshsimplelevel.cpp @@ -119,10 +119,7 @@ bool isAreadOnlyLevel(const TFilePath &path) { if (path.getDots() == "." || (path.getDots() == ".." && (path.getType() == "tlv" || path.getType() == "tpl"))) { - if (path.getType() == "psd" || path.getType() == "gif" || - path.getType() == "mp4" || path.getType() == "webm" || - path.getType() == "mov") - return true; + if (path.isUneditable()) return true; if (!TSystem::doesExistFileOrLevel(path)) return false; TFileStatus fs(path); return !fs.isWritable(); @@ -197,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) {} @@ -939,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; double vanishingPoint1x = 0.0; double vanishingPoint1y = 0.0; double vanishingPoint2x = 0.0; @@ -965,6 +964,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); if (is.getTagParam("vanishingPoint1x", v)) vanishingPoint1x = std::stod(v); @@ -1008,6 +1009,7 @@ void TXshSimpleLevel::loadData(TIStream &is) { m_properties->setDoAntialias(antialiasSoftness); m_properties->setWhiteTransp(whiteTransp); m_properties->setIsStopMotion(isStopMotionLevel); + m_properties->setColorSpaceGamma(colorSpaceGamma); m_properties->setVanishingPoints(vanishingPoints); if (isStopMotionLevel == 1) setIsReadOnly(true); } else @@ -1235,7 +1237,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())); @@ -1378,7 +1383,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++) { @@ -1387,7 +1395,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()) @@ -1479,6 +1490,11 @@ void TXshSimpleLevel::saveData(TOStream &os) { attr["vanishingPoint4y"] = std::to_string(vanishingPoints.at(3).y); } } + if (!areAlmostEqual(getProperties()->colorSpaceGamma(), + LevelOptions::DefaultColorSpaceGamma)) { + attr["colorSpaceGamma"] = + std::to_string(getProperties()->colorSpaceGamma()); + } if (m_type == TZI_XSHLEVEL) attr["type"] = "s"; @@ -2496,9 +2512,7 @@ bool TXshSimpleLevel::isFrameReadOnly(TFrameId fid) { if (getProperties()->isStopMotionLevel()) return true; TFilePath fullPath = getScene()->decodeFilePath(m_path); std::string fileType = fullPath.getType(); - if (fileType == "psd" || fileType == "gif" || fileType == "mp4" || - fileType == "webm" || fileType == "mov") - return true; + if (fullPath.isUneditable()) return true; TFilePath path = fullPath.getDots() == ".." ? fullPath.withFrame(fid) : fullPath; if (!TSystem::doesExistFileOrLevel(path)) return false; diff --git a/toonz/sources/toonzlib/txshsoundcolumn.cpp b/toonz/sources/toonzlib/txshsoundcolumn.cpp index 9d2d91de..63d03f47 100644 --- a/toonz/sources/toonzlib/txshsoundcolumn.cpp +++ b/toonz/sources/toonzlib/txshsoundcolumn.cpp @@ -956,10 +956,9 @@ TSoundTrackP TXshSoundColumn::getOverallSoundTrack(int fromFrame, int toFrame, sampleRate = f.m_sampleRate; format.m_channelCount = f.m_channelCount; channels = f.m_channelCount; - format.m_signedSample = f.m_signedSample; + format.m_sampleType = f.m_sampleType; format.m_bitPerSample = f.m_bitPerSample; bitsPerSample = f.m_bitPerSample; - format.m_formatType = f.m_formatType; } if (f.m_channelCount > channels) { format.m_channelCount = f.m_channelCount; @@ -969,8 +968,6 @@ TSoundTrackP TXshSoundColumn::getOverallSoundTrack(int fromFrame, int toFrame, format.m_bitPerSample = f.m_bitPerSample; bitsPerSample = f.m_bitPerSample; } - if (format.m_formatType > f.m_formatType) - format.m_formatType = f.m_formatType; } } @@ -979,8 +976,8 @@ TSoundTrackP TXshSoundColumn::getOverallSoundTrack(int fromFrame, int toFrame, format.m_sampleRate = 44100; format.m_bitPerSample = 16; format.m_channelCount = 1; - format.m_signedSample = true; - format.m_formatType = WAVE_FORMAT_PCM; + format.m_sampleType = TSound::INT; + } #ifdef _WIN32 @@ -993,8 +990,19 @@ TSoundTrackP TXshSoundColumn::getOverallSoundTrack(int fromFrame, int toFrame, if (!ssrs.contains(format.m_sampleRate)) format.m_sampleRate = 44100; QAudioFormat qFormat; qFormat.setSampleRate(format.m_sampleRate); - qFormat.setSampleType(format.m_signedSample ? QAudioFormat::SignedInt - : QAudioFormat::UnSignedInt); + switch (format.m_sampleType) { + case TSound::INT: + qFormat.setSampleType(QAudioFormat::SignedInt); + break; + case TSound::UINT: + qFormat.setSampleType(QAudioFormat::UnSignedInt); + break; + case TSound::FLOAT: + qFormat.setSampleType(QAudioFormat::Float); + break; + default: + break; + } qFormat.setSampleSize(format.m_bitPerSample); qFormat.setCodec("audio/pcm"); qFormat.setChannelCount(format.m_channelCount); @@ -1004,10 +1012,19 @@ TSoundTrackP TXshSoundColumn::getOverallSoundTrack(int fromFrame, int toFrame, format.m_bitPerSample = qFormat.sampleSize(); format.m_channelCount = qFormat.channelCount(); format.m_sampleRate = qFormat.sampleRate(); - if (qFormat.sampleType() == QAudioFormat::SignedInt) - format.m_signedSample = true; - else - format.m_signedSample = false; + switch (qFormat.sampleType()) { + case QAudioFormat::SignedInt: + format.m_sampleType = TSound::INT; + break; + case QAudioFormat::UnSignedInt: + format.m_sampleType = TSound::UINT; + break; + case QAudioFormat::Float: + format.m_sampleType = TSound::FLOAT; + break; + default: + break; + } } #endif // Create the soundTrack @@ -1065,7 +1082,7 @@ TSoundTrackP TXshSoundColumn::getOverallSoundTrack(int fromFrame, int toFrame, if (s1 > 0 && s1 >= s0) { soundTrack = soundTrack->extract(s0, s1); - if (format.m_formatType == WAVE_FORMAT_PCM) + if (format.m_sampleType != TSound::FLOAT) overallSoundTrack->copy( soundTrack, int((levelStartFrame - fromFrame) * samplePerFrame)); else @@ -1126,9 +1143,12 @@ TSoundTrackP TXshSoundColumn::mixingTogether( // Per ora perche mov vuole solo 16 bit TSoundTrackFormat fmt = mix->getFormat(); - if (fmt.m_bitPerSample != 32) fmt.m_bitPerSample = 32; - if (fmt.m_formatType != WAVE_FORMAT_PCM) fmt.m_formatType = WAVE_FORMAT_PCM; - mix = TSop::convert(mix, fmt); + if (fmt.m_bitPerSample == 8) { + fmt.m_bitPerSample = 16; + fmt.m_sampleType = TSound::INT; + // 8-bits signed don't play on windows + mix = TSop::convert(mix, fmt); + } return mix; } diff --git a/toonz/sources/toonzqt/combohistogram.cpp b/toonz/sources/toonzqt/combohistogram.cpp index 1c830f7f..507e2332 100644 --- a/toonz/sources/toonzqt/combohistogram.cpp +++ b/toonz/sources/toonzqt/combohistogram.cpp @@ -89,17 +89,24 @@ 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 - : (m_channelIndex == 1) - ? Qt::green - : (m_channelIndex == 2) ? Qt::blue : Qt::magenta; + QColor compColor = (m_channelIndex == 0) ? Qt::red + : (m_channelIndex == 1) ? Qt::green + : (m_channelIndex == 2) ? Qt::blue + : Qt::magenta; compColor.setAlpha(120); int maxValue = m_maxValue[0]; @@ -192,8 +199,9 @@ void RGBHistoGraph::setValues(int *buf, bool) { imgPainter.setCompositionMode(QPainter::CompositionMode_Plus); for (int chan = 0; chan < 3; chan++) { - imgPainter.setPen((chan == 0) ? Qt::red - : (chan == 1) ? Qt::green : Qt::blue); + imgPainter.setPen((chan == 0) ? Qt::red + : (chan == 1) ? Qt::green + : Qt::blue); for (int i = 0; i < COMBOHIST_RESOLUTION_W; i++) { int v = m_rgbValues[chan][i]; @@ -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); + } } //============================================================================= @@ -339,14 +359,18 @@ void ChannelHisto::showCurrentChannelValue(int val) { void ChannelHisto::onShowAlphaButtonToggled(bool visible) { m_histogramGraph->setVisible(visible); m_colorBar->setVisible(visible); + emit showButtonToggled(visible); } //============================================================================= // ComboHistoRGBLabel //----------------------------------------------------------------------------- ComboHistoRGBLabel::ComboHistoRGBLabel(QColor color, QWidget *parent) - : QWidget(parent), m_color(color), m_mode(DisplayMode::Display_8bit) { - setFixedSize(COMBOHIST_RESOLUTION_W, 30); + : QWidget(parent) + , m_color(color) + , m_mode(DisplayMode::Display_8bit) + , m_alphaVisible(false) { + setFixedSize(COMBOHIST_RESOLUTION_W, 35); } void ComboHistoRGBLabel::setColorAndUpdate(QColor color) { @@ -388,12 +412,14 @@ void ComboHistoRGBLabel::paintEvent(QPaintEvent *pe) { font.setPixelSize(pixelSizes[(int)m_mode]); p.setFont(font); QString colorStr; + QString colorStr2 = QString(); switch (m_mode) { case Display_8bit: { colorStr = tr("R:%1 G:%2 B:%3") .arg(m_color.red()) .arg(m_color.green()) .arg(m_color.blue()); + if (m_alphaVisible) colorStr += tr(" A:%1").arg(m_color.alpha()); break; } case Display_16bit: { @@ -402,17 +428,34 @@ void ComboHistoRGBLabel::paintEvent(QPaintEvent *pe) { .arg(rgba64.red()) .arg(rgba64.green()) .arg(rgba64.blue()); + if (m_alphaVisible) colorStr += tr(" A:%1").arg(rgba64.alpha()); break; } case Display_0_1: { - colorStr = tr("R:%1 G:%2 B:%3") - .arg(m_color.redF()) - .arg(m_color.greenF()) - .arg(m_color.blueF()); + if (!m_alphaVisible) { + colorStr = tr("R:%1 G:%2 B:%3") + .arg(m_color.redF()) + .arg(m_color.greenF()) + .arg(m_color.blueF()); + } else { + // display in 2 lines + colorStr = tr("R:%1 G:%2 B:%3") + .arg(m_color.redF()) + .arg(m_color.greenF()) + .arg(m_color.blueF()); + colorStr2 = tr("A:%1").arg(m_color.alphaF()); + } break; } } - p.drawText(rect(), Qt::AlignCenter, colorStr); + if (colorStr2.isEmpty()) + p.drawText(rect(), Qt::AlignCenter, colorStr); + else { + QRect upRect = rect().adjusted(0, 0, 0, -rect().height() / 2); + p.drawText(upRect, Qt::AlignCenter, colorStr); + QRect dnRect = upRect.translated(0, rect().height() / 2); + p.drawText(dnRect, Qt::AlignCenter, colorStr2); + } } //============================================================================= @@ -424,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); @@ -441,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( @@ -449,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); @@ -472,18 +531,28 @@ ComboHistogram::ComboHistogram(QWidget *parent) 0, Qt::AlignLeft | Qt::AlignVCenter); mainLayout->addWidget(m_rectAverageRgbLabel, 0, Qt::AlignCenter); - QGridLayout *infoParamLay = new QGridLayout(); - infoParamLay->setHorizontalSpacing(3); - infoParamLay->setVerticalSpacing(5); + QHBoxLayout *infoParamLay = new QHBoxLayout(); + infoParamLay->setMargin(5); + infoParamLay->setSpacing(3); { - infoParamLay->addWidget(new QLabel(tr("X:"), this), 0, 0, + infoParamLay->addWidget(new QLabel(tr("X:"), this), 1, Qt::AlignRight | Qt::AlignVCenter); - infoParamLay->addWidget(m_xPosLabel, 0, 1, - Qt::AlignLeft | Qt::AlignVCenter); - infoParamLay->addWidget(new QLabel(tr("Y:"), this), 1, 0, + infoParamLay->addWidget(m_xPosLabel, 1, Qt::AlignLeft | Qt::AlignVCenter); + infoParamLay->addWidget(new QLabel(tr("Y:"), this), 1, Qt::AlignRight | Qt::AlignVCenter); - infoParamLay->addWidget(m_yPosLabel, 1, 1, - Qt::AlignLeft | 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); @@ -499,6 +568,10 @@ ComboHistogram::ComboHistogram(QWidget *parent) connect(m_displayModeCombo, SIGNAL(activated(int)), this, 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())); } //----------------------------------------------------------------------------- @@ -513,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++) - m_histograms[chan]->refleshValue(&m_channelValue[chan][0]); - m_histograms[4]->refleshValue(&m_channelValue[0][0]); + float range = 1.f; + if (!!((TRasterFP)m_raster)) { + range = std::pow(2.f, (float)m_rangeStep); + } - update(); + 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]); + m_histograms[4]->setRange(range); } //----------------------------------------------------------------------------- @@ -534,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) { @@ -571,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++) { @@ -595,6 +702,7 @@ void ComboHistogram::updateInfo(const TPixel32 &pix, const TPointD &imagePos) { 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(""); @@ -603,7 +711,9 @@ void ComboHistogram::updateInfo(const TPixel32 &pix, const TPointD &imagePos) { m_histograms[0]->showCurrentChannelValue((int)pix.r); m_histograms[1]->showCurrentChannelValue((int)pix.g); m_histograms[2]->showCurrentChannelValue((int)pix.b); - m_rgbLabel->setColorAndUpdate(QColor((int)pix.r, (int)pix.g, (int)pix.b)); + m_histograms[3]->showCurrentChannelValue((int)pix.m); + m_rgbLabel->setColorAndUpdate( + QColor((int)pix.r, (int)pix.g, (int)pix.b, (int)pix.m)); m_xPosLabel->setText(QString::number(tround(imagePos.x))); m_yPosLabel->setText(QString::number(tround(imagePos.y))); } @@ -616,6 +726,7 @@ void ComboHistogram::updateInfo(const TPixel64 &pix, const TPointD &imagePos) { 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(""); @@ -625,8 +736,9 @@ void ComboHistogram::updateInfo(const TPixel64 &pix, const TPointD &imagePos) { m_histograms[0]->showCurrentChannelValue((int)pix32.r); m_histograms[1]->showCurrentChannelValue((int)pix32.g); m_histograms[2]->showCurrentChannelValue((int)pix32.b); - m_rgbLabel->setColorAndUpdate( - QColor::fromRgba64((ushort)pix.r, (ushort)pix.g, (ushort)pix.b)); + m_histograms[3]->showCurrentChannelValue((int)pix32.m); + m_rgbLabel->setColorAndUpdate(QColor::fromRgba64( + (ushort)pix.r, (ushort)pix.g, (ushort)pix.b, (ushort)pix.m)); m_xPosLabel->setText(QString::number(tround(imagePos.x))); m_yPosLabel->setText(QString::number(tround(imagePos.y))); } @@ -634,12 +746,37 @@ 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); } else { m_rectAverageRgbLabel->setColorAndUpdate( - QColor((int)pix.r, (int)pix.g, (int)pix.b)); + QColor((int)pix.r, (int)pix.g, (int)pix.b, (int)pix.m)); } } @@ -648,9 +785,20 @@ void ComboHistogram::updateAverageColor(const TPixel32 &pix) { void ComboHistogram::updateAverageColor(const TPixel64 &pix) { if (pix == TPixel64::Transparent) { m_rectAverageRgbLabel->setColorAndUpdate(Qt::transparent); + } else { + m_rectAverageRgbLabel->setColorAndUpdate(QColor::fromRgba64( + (ushort)pix.r, (ushort)pix.g, (ushort)pix.b, (ushort)pix.m)); + } +} + +//----------------------------------------------------------------------------- + +void ComboHistogram::updateAverageColor(const TPixelF &pix) { + if (pix == TPixelF::Transparent) { + m_rectAverageRgbLabel->setColorAndUpdate(Qt::transparent); } else { m_rectAverageRgbLabel->setColorAndUpdate( - QColor::fromRgba64((ushort)pix.r, (ushort)pix.g, (ushort)pix.b)); + QColor::fromRgbF(pix.r, pix.g, pix.b, pix.m)); } } @@ -696,3 +844,44 @@ void ComboHistogram::showEvent(QShowEvent *) { m_rgbLabel->setDisplayMode(mode); m_rectAverageRgbLabel->setDisplayMode(mode); } + +//----------------------------------------------------------------------------- + +void ComboHistogram::onShowAlphaButtonToggled(bool alphaVisible) { + m_rgbLabel->setAlphaVisible(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(); +} diff --git a/toonz/sources/toonzqt/docklayout.cpp b/toonz/sources/toonzqt/docklayout.cpp index dc1eabd1..1b646683 100644 --- a/toonz/sources/toonzqt/docklayout.cpp +++ b/toonz/sources/toonzqt/docklayout.cpp @@ -950,9 +950,9 @@ static void calculateNearest(std::vector target, for (i = 0; i < target.size(); ++i) { if (nearest[i] < intervals[i].first || nearest[i] > intervals[i].second) { - distance = nearest[i] < intervals[i].first - ? intervals[i].first - nearest[i] - : nearest[i] - intervals[i].second; + distance = nearest[i] < intervals[i].first + ? intervals[i].first - nearest[i] + : nearest[i] - intervals[i].second; nearest[i] = nearest[i] < intervals[i].first ? intervals[i].first : intervals[i].second; if (maxDistance < distance) { @@ -1440,7 +1440,11 @@ void DockLayout::writeRegion(Region *r, QString &hierarchy) { //! widget has ever been left unchanged or completely restored //! as it were when saved. In particular, their ordering must be preserved. bool DockLayout::restoreState(const State &state) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList vars = state.second.split(" ", Qt::SkipEmptyParts); +#else QStringList vars = state.second.split(" ", QString::SkipEmptyParts); +#endif if (vars.size() < 1) return 0; // Check number of items diff --git a/toonz/sources/toonzqt/dockwidget.cpp b/toonz/sources/toonzqt/dockwidget.cpp index c08a76be..810a79a7 100644 --- a/toonz/sources/toonzqt/dockwidget.cpp +++ b/toonz/sources/toonzqt/dockwidget.cpp @@ -9,6 +9,7 @@ #include #include #include +#include // STD includes #include @@ -82,7 +83,7 @@ inline QRect toRect(const QRectF &rect) { namespace { QDesktopWidget *desktop; void getClosestAvailableMousePosition(QPoint &globalPos); -} +} // namespace //======================================================== @@ -399,12 +400,19 @@ void DockWidget::maximizeDock() { void DockWidget::wheelEvent(QWheelEvent *we) { if (m_dragging) { if (m_selectedPlace) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) DockPlaceholder *newSelected = - (we->delta() > 0) + (we->angleDelta().y() > 0) + ? m_selectedPlace->parentPlaceholder() + : m_selectedPlace->childPlaceholder(parentWidget()->mapFromGlobal( + we->globalPosition().toPoint())); +#else + DockPlaceholder *newSelected = + (we->angleDelta().y() > 0) ? m_selectedPlace->parentPlaceholder() : m_selectedPlace->childPlaceholder( parentWidget()->mapFromGlobal(we->globalPos())); - +#endif if (newSelected != m_selectedPlace) { m_selectedPlace->hide(); newSelected->show(); @@ -715,10 +723,9 @@ DockPlaceholder *DockPlaceholder::childPlaceholder(QPoint p) { r = r->childRegion(i); // Finally, return child placeholder found. - return r->placeholders().size() - ? lastExtremity ? r->placeholders().back() - : r->placeholders().front() - : this; + return r->placeholders().size() ? lastExtremity ? r->placeholders().back() + : r->placeholders().front() + : this; } //------------------------------------------------------ @@ -963,25 +970,25 @@ namespace { // Finds the closest mouse point belonging to some available geometry. void getClosestAvailableMousePosition(QPoint &globalPos) { // Search the screen rect containing the mouse position - int i, screens = desktop->numScreens(); - for (i = 0; i < screens; ++i) - if (desktop->screenGeometry(i).contains(globalPos)) break; + for (auto screen : QGuiApplication::screens()) { + if (screen->geometry().contains(globalPos)) { + // Find the closest point to the corresponding available region + QRect rect(screen->availableGeometry()); + if (rect.contains(globalPos)) return; - // Find the closest point to the corresponding available region - QRect rect(desktop->availableGeometry(i)); - if (rect.contains(globalPos)) return; + // Return the closest point to the available geometry + QPoint result; + if (globalPos.x() < rect.left()) + globalPos.setX(rect.left()); + else if (globalPos.x() > rect.right()) + globalPos.setX(rect.right()); - // Return the closest point to the available geometry - QPoint result; - if (globalPos.x() < rect.left()) - globalPos.setX(rect.left()); - else if (globalPos.x() > rect.right()) - globalPos.setX(rect.right()); - - if (globalPos.y() < rect.top()) - globalPos.setY(rect.top()); - else if (globalPos.y() > rect.bottom()) - globalPos.setY(rect.bottom()); + if (globalPos.y() < rect.top()) + globalPos.setY(rect.top()); + else if (globalPos.y() > rect.bottom()) + globalPos.setY(rect.bottom()); + } + } } -} // Local namespace +} // namespace diff --git a/toonz/sources/toonzqt/doublepairfield.cpp b/toonz/sources/toonzqt/doublepairfield.cpp index 4488b1ef..bdaabdb9 100644 --- a/toonz/sources/toonzqt/doublepairfield.cpp +++ b/toonz/sources/toonzqt/doublepairfield.cpp @@ -70,7 +70,7 @@ DoubleValuePairField::DoubleValuePairField(QWidget *parent, bool ret = connect(m_leftLineEdit, SIGNAL(editingFinished()), SLOT(onLeftEditingFinished())); ret = ret && connect(m_rightLineEdit, SIGNAL(editingFinished()), - SLOT(onRightEditingFinished())); + SLOT(onRightEditingFinished())); assert(ret); } @@ -157,8 +157,13 @@ void DoubleValuePairField::paintEvent(QPaintEvent *) { void DoubleValuePairField::setLeftText(const QString &text) { QString oldText = m_leftLabel->text(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + int oldLabelSize = fontMetrics().horizontalAdvance(oldText); + int newLabelSize = fontMetrics().horizontalAdvance(text); +#else int oldLabelSize = fontMetrics().width(oldText); int newLabelSize = fontMetrics().width(text); +#endif int labelSize = newLabelSize - oldLabelSize; m_leftMargin += labelSize + MARGIN_OFFSET; @@ -171,8 +176,13 @@ void DoubleValuePairField::setLeftText(const QString &text) { void DoubleValuePairField::setRightText(const QString &text) { QString oldText = m_rightLabel->text(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + int oldLabelSize = fontMetrics().horizontalAdvance(oldText); + int newLabelSize = fontMetrics().horizontalAdvance(text); +#else int oldLabelSize = fontMetrics().width(oldText); int newLabelSize = fontMetrics().width(text); +#endif int labelSize = newLabelSize - oldLabelSize; m_rightMargin += labelSize + MARGIN_OFFSET; diff --git a/toonz/sources/toonzqt/dvdialog.cpp b/toonz/sources/toonzqt/dvdialog.cpp index bbe74b6d..d3f4d72b 100644 --- a/toonz/sources/toonzqt/dvdialog.cpp +++ b/toonz/sources/toonzqt/dvdialog.cpp @@ -321,7 +321,7 @@ Dialog::Dialog(QWidget *parent, bool hasButton, bool hasFixedSize, if (x > screen.right() - 50) x = screen.right() - 50; if (x < screen.left()) x = screen.left(); if (y > screen.bottom() - 90) y = screen.bottom() - 90; - if (y < screen.top()) y = screen.top(); + if (y <= screen.top()) y = screen.top() + 50; // pad for window title setGeometry(x, y, values.at(2).toInt(), values.at(3).toInt()); settings.setValue(m_name, QString::number(x) + " " + QString::number(y) + " " + QString::number(values.at(2).toInt()) + diff --git a/toonz/sources/toonzqt/expressionfield.cpp b/toonz/sources/toonzqt/expressionfield.cpp index 3a6dc128..1676533c 100644 --- a/toonz/sources/toonzqt/expressionfield.cpp +++ b/toonz/sources/toonzqt/expressionfield.cpp @@ -219,7 +219,7 @@ void ExpressionField::keyPressEvent(QKeyEvent *e) { setAutoFillBackground(true); QPalette p = palette(); p.setColor(QPalette::Base, Qt::cyan); - p.setColor(QPalette::Background, Qt::cyan); + p.setColor(QPalette::Window, Qt::cyan); setPalette(p); update(); setStyleSheet("#ExpressionField {background-color:cyan;}"); @@ -351,7 +351,8 @@ bool ExpressionField::updateCompleterPopup() { int w = m_completerPopup->sizeHintForColumn(0) + m_completerPopup->verticalScrollBar()->sizeHint().width() + 5; int h = - (m_completerPopup->sizeHintForRow(0) * std::min(7, model->rowCount()) + 3) + + (m_completerPopup->sizeHintForRow(0) * std::min(7, model->rowCount()) + + 3) + 3; QSize size(w, h); @@ -372,8 +373,8 @@ int ExpressionField::computeSuggestions() { while (start > 0) { char c = text[start - 1]; if ((isascii(c) && isalpha(c)) || c == '_' || - (c == '.' && (start - 2 < 0 || - (isascii(text[start - 2]) && isalpha(text[start - 2]))))) { + (c == '.' && (start - 2 < 0 || (isascii(text[start - 2]) && + isalpha(text[start - 2]))))) { } else break; start--; diff --git a/toonz/sources/toonzqt/filefield.cpp b/toonz/sources/toonzqt/filefield.cpp index 9e172608..ecb2c37c 100644 --- a/toonz/sources/toonzqt/filefield.cpp +++ b/toonz/sources/toonzqt/filefield.cpp @@ -22,7 +22,7 @@ FileField::FileField(QWidget *parent, QString path, bool readOnly, bool doNotBrowseInitialPath, bool codePath) : QWidget(parent) , m_filters(QStringList()) - , m_fileMode(QFileDialog::DirectoryOnly) + , m_fileMode(QFileDialog::Directory) // implies ShowDirsOnly , m_lastSelectedPath(path) , m_codePath(codePath) { setMaximumHeight(WidgetHeight); @@ -95,7 +95,7 @@ void FileField::forceOpenBrowser() { if (!m_browserPopupController) return; m_browserPopupController->openPopup( - m_filters, (m_fileMode == QFileDialog::DirectoryOnly), + m_filters, (m_fileMode == QFileDialog::Directory), (m_lastSelectedPath == m_descriptionText) ? "" : m_lastSelectedPath, this); if (m_browserPopupController->isExecute()) @@ -117,7 +117,7 @@ void FileField::browseDirectory() { if (!m_browserPopupController) return; m_browserPopupController->openPopup( - m_filters, (m_fileMode == QFileDialog::DirectoryOnly), + m_filters, (m_fileMode == QFileDialog::Directory), (m_lastSelectedPath == m_descriptionText) ? "" : m_lastSelectedPath, this); if (m_browserPopupController->isExecute()) diff --git a/toonz/sources/toonzqt/flipconsole.cpp b/toonz/sources/toonzqt/flipconsole.cpp index e599fafc..e8faed2a 100644 --- a/toonz/sources/toonzqt/flipconsole.cpp +++ b/toonz/sources/toonzqt/flipconsole.cpp @@ -12,6 +12,7 @@ // TnzLib includes #include "toonz/preferences.h" #include "toonz/tframehandle.h" +#include "toonz/toonzfolders.h" // TnzBase includes #include "tenv.h" @@ -76,6 +77,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 //----------------------------------------------------------------------------- @@ -170,7 +177,7 @@ void PlaybackExecutor::run() { qint64 nextSampleInstant = timeResolution; qint64 lastFrameCounts[4] = {0, 0, 0, - 0}; // Keep the last 4 'played frames' counts. + 0}; // Keep the last 4 'played frames' counts. qint64 lastSampleInstants[4] = {0, 0, 0, 0}; // Same for the last sampling instants @@ -190,7 +197,7 @@ void PlaybackExecutor::run() { qint64 framesCount = playedFramesCount - lastFrameCounts[currSample]; qint64 elapsedTime = emissionInstant - lastSampleInstants[currSample]; fps = troundp((long double)(1000000000 * framesCount) / - (long double)elapsedTime); + (long double)elapsedTime); targetFrameTime = 1000000000 / (qint64)abs(m_fps); // m_fps could have changed... @@ -295,11 +302,10 @@ void FlipSlider::paintEvent(QPaintEvent *ev) { PBMarkerMarginLeft; if (i == pbStatusSize - 1) nextPos += PBMarkerMarginRight; p.fillRect(currPos, PBColorMarginTop, nextPos - currPos, colorHeight, - ((*m_progressBarStatus)[i] == PBFrameStarted) - ? PBStartedColor - : ((*m_progressBarStatus)[i] == PBFrameFinished) - ? PBFinishedColor - : PBNotStartedColor); + ((*m_progressBarStatus)[i] == PBFrameStarted) ? PBStartedColor + : ((*m_progressBarStatus)[i] == PBFrameFinished) + ? PBFinishedColor + : PBNotStartedColor); currPos = nextPos; } @@ -385,7 +391,7 @@ void FlipSlider::mousePressEvent(QMouseEvent *me) { emit flipSliderPressed(); int cursorValue = sliderValueFromPosition(minimum(), maximum(), singleStep(), me->pos().x(), width()); - if (me->button() == Qt::MidButton) + if (me->button() == Qt::MiddleButton) if (cursorValue == value()) setSliderDown(true); else { @@ -447,7 +453,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) @@ -467,9 +473,14 @@ FlipConsole::FlipConsole(QVBoxLayout *mainLayout, std::vector gadgetsMask, , m_fpsLabel(0) , m_timeLabel(0) , m_consoleOwner(consoleOwner) - , m_enableBlankFrameButton(0) { + , m_enableBlankFrameButton(0) + , m_prevGainStep(0) + , m_gainSep(nullptr) + , m_resetGainBtn(nullptr) { + QSettings flipSettings(getFlipSettingsPath(), QSettings::IniFormat); + flipSettings.beginGroup("ConsoleCustomizeMasks"); if (m_customizeId != "SceneViewerConsole") { - QString s = QSettings().value(m_customizeId).toString(); + QString s = flipSettings.value(m_customizeId).toString(); if (s != "") m_customizeMask = s.toUInt(); } @@ -810,9 +821,11 @@ void FlipConsole::onNextFrame(int fps, QElapsedTimer *timer, else m_fpsField->setLineEditBackgroundColor(Qt::red); } - if (m_stopAt > 0 && m_currentFrame >= m_stopAt) { + if (m_stopAt > 0 && m_currentFrame >= m_stopAt && + (m_isPlay || m_startAt == -1)) { doButtonPressed(ePause); - m_stopAt = -1; + m_stopAt = -1; + m_startAt = -1; } } @@ -838,6 +851,10 @@ void FlipConsole::playNextFrame(QElapsedTimer *timer, qint64 targetInstant) { int from = m_from, to = m_to; if (m_markerFrom <= m_markerTo && m_stopAt == -1) from = m_markerFrom, to = m_markerTo; + else if (m_stopAt > 0 && m_startAt > 0) { + from = m_startAt; + to = m_stopAt; + } if (m_framesCount == 0 || (m_isPlay && m_currentFrame == (m_reverse ? from : to))) { @@ -1036,7 +1053,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(); } @@ -1132,6 +1151,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(); } @@ -1242,6 +1268,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); @@ -1417,6 +1446,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 *))); @@ -1537,6 +1583,7 @@ void FlipConsole::onButtonPressed(int button) { playingConsole->setChecked(ePause, true); stoppedOther = true; m_stopAt = -1; + m_startAt = -1; } } if (stoppedOther) { @@ -1692,7 +1739,8 @@ void FlipConsole::doButtonPressed(UINT button) { playingConsole->setChecked(ePause, true); } } - m_stopAt = -1; + m_stopAt = -1; + m_startAt = -1; return; } @@ -1700,6 +1748,7 @@ void FlipConsole::doButtonPressed(UINT button) { if (m_playbackExecutor.isRunning()) m_playbackExecutor.abort(); m_stopAt = -1; + m_startAt = -1; m_isPlay = false; m_blanksToDraw = 0; @@ -1818,6 +1867,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; @@ -1835,6 +1894,10 @@ void FlipConsole::setStopAt(int frame) { m_stopAt = frame; } //-------------------------------------------------------------------- +void FlipConsole::setStartAt(int frame) { m_startAt = frame; } + +//-------------------------------------------------------------------- + QFrame *FlipConsole::createFrameSlider() { QFrame *frameSliderFrame = new QFrame(this); @@ -1861,6 +1924,8 @@ QFrame *FlipConsole::createFrameSlider() { m_enableBlankFrameButton->setFixedHeight(24); m_enableBlankFrameButton->setFixedWidth(66); m_enableBlankFrameButton->setObjectName("enableBlankFrameButton"); + + m_buttons[eBlankFrames] = m_enableBlankFrameButton; } // layout @@ -1898,7 +1963,12 @@ QFrame *FlipConsole::createFpsSlider() { m_fpsField = new DVGui::IntLineEdit(fpsSliderFrame, m_fps, -60, 60); m_fpsField->setFixedWidth(40); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + m_fpsLabel->setMinimumWidth( + m_fpsLabel->fontMetrics().horizontalAdvance("_FPS_24___")); +#else m_fpsLabel->setMinimumWidth(m_fpsLabel->fontMetrics().width("_FPS_24___")); +#endif m_fpsLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); m_fpsSlider->setObjectName("ViewerFpsSlider"); m_fpsSlider->setRange(-60, 60); @@ -2146,6 +2216,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/functionpanel.cpp b/toonz/sources/toonzqt/functionpanel.cpp index dd501fb4..90429fae 100644 --- a/toonz/sources/toonzqt/functionpanel.cpp +++ b/toonz/sources/toonzqt/functionpanel.cpp @@ -52,7 +52,8 @@ void drawSquare(QPainter &painter, const QPointF &p, double r) { } void drawRoundedSquare(QPainter &painter, const QPointF &p, double r) { - painter.drawRoundRect(p.x() - r, p.y() - r, 2 * r, 2 * r, 99, 99); + painter.drawRoundedRect(p.x() - r, p.y() - r, 2 * r, 2 * r, 99, 99, + Qt::RelativeSize); } double norm2(const QPointF &p) { return p.x() * p.x() + p.y() * p.y(); } @@ -552,7 +553,11 @@ void FunctionPanel::drawFrameGrid(QPainter &painter) { Ruler ruler; ruler.setTransform(m_viewTransform.m11(), m_viewTransform.dx(), -1); ruler.setRange(m_valueAxisX, width()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + ruler.setMinLabelDistance(fm.horizontalAdvance("-8888") + 2); +#else ruler.setMinLabelDistance(fm.width("-8888") + 2); +#endif ruler.setMinDistance(5); ruler.setMinStep(1); ruler.compute(); @@ -568,7 +573,12 @@ void FunctionPanel::drawFrameGrid(QPainter &painter) { if (isLabel) { painter.setPen(m_textColor); QString labelText = QString::number(f + 1); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + painter.drawText(x - fm.horizontalAdvance(labelText) / 2, y - 6, + labelText); +#else painter.drawText(x - fm.width(labelText) / 2, y - 6, labelText); +#endif } } } @@ -598,7 +608,7 @@ void FunctionPanel::drawValueGrid(QPainter &painter) { double v = ruler.getTick(i); bool isLabel = ruler.isLabel(i); int y = tround(m_viewTransform.m22() * v + - m_viewTransform.dy()); // valueToY(curve, v); + m_viewTransform.dy()); // valueToY(curve, v); painter.setPen(m_textColor); int x = m_valueAxisX; painter.drawLine(x - (isLabel ? 5 : 2), y, x, y); @@ -609,7 +619,11 @@ void FunctionPanel::drawValueGrid(QPainter &painter) { if (isLabel) { painter.setPen(m_textColor); QString labelText = QString::number(v); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + painter.drawText(std::max(0, x - 5 - fm.horizontalAdvance(labelText)), +#else painter.drawText(std::max(0, x - 5 - fm.width(labelText)), +#endif y + fm.height() / 2, labelText); } } @@ -668,10 +682,10 @@ void FunctionPanel::drawOtherCurves(QPainter &painter) { for (int k = 0; k < kCount; k++) { double frame = curve->keyframeIndexToFrame(k); QPointF p = getWinPos(curve, frame, curve->getValue(frame)); - painter.drawRect(p.x() - 3, p.y() - 3, 7, 7); + painter.drawRect(p.x() - 2, p.y() - 2, 5, 5); QPointF p2 = getWinPos(curve, frame, curve->getValue(frame, true)); if (p2.y() != p.y()) { - painter.drawRect(p2.x() - 3, p2.y() - 3, 7, 7); + painter.drawRect(p2.x() - 2, p2.y() - 2, 5, 5); painter.setPen(solidPen); painter.drawLine(p, p2); painter.setPen(m_textColor); @@ -971,7 +985,7 @@ void FunctionPanel::drawCurrentCurve(QPainter &painter) { case Point: painter.setBrush(isSelected ? QColor(255, 126, 0) : m_subColor); painter.setPen(m_textColor); - r = isHighlighted ? 7 : 5; + r = isHighlighted ? 4 : 3; drawSquare(painter, p, r); break; @@ -979,8 +993,8 @@ void FunctionPanel::drawCurrentCurve(QPainter &painter) { case SpeedOut: painter.setBrush(m_subColor); painter.setPen(m_textColor); - r = isHighlighted ? 3 : 2; - drawRoundedSquare(painter, p, r); + r = isHighlighted ? 4 : 3; + drawCircle(painter, p, r); break; case EaseIn: @@ -1094,7 +1108,11 @@ void FunctionPanel::paintEvent(QPaintEvent *e) { QFontMetrics fm(font); // define ruler sizes +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + m_valueAxisX = fm.horizontalAdvance("-888.88") + 2; +#else m_valueAxisX = fm.width("-888.88") + 2; +#endif m_frameAxisY = fm.height() + 2; m_graphViewportY = m_frameAxisY + 12; int ox = m_valueAxisX; @@ -1142,7 +1160,12 @@ void FunctionPanel::paintEvent(QPaintEvent *e) { int x = frameToX(m_cursor.frame); painter.drawLine(x, oy0 + 1, x, oy0 + 10); QString text = QString::number(tround(m_cursor.frame) + 1); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + painter.drawText(x - fm.horizontalAdvance(text) / 2, oy0 + 10 + fm.height(), + text); +#else painter.drawText(x - fm.width(text) / 2, oy0 + 10 + fm.height(), text); +#endif TDoubleParam *currentCurve = getCurrentCurve(); if (currentCurve) { @@ -1190,7 +1213,7 @@ void FunctionPanel::mousePressEvent(QMouseEvent *e) { m_dragTool = nullptr; } - if (e->button() == Qt::MidButton || + if (e->button() == Qt::MiddleButton || (e->button() == Qt::LeftButton && m_panningArmed)) { // mid mouse click => panning bool xLocked = e->pos().x() <= m_valueAxisX; @@ -1409,7 +1432,11 @@ void FunctionPanel::mouseMoveEvent(QMouseEvent *e) { m_curveLabel.text = name.toStdString(); // in order to avoid run off the right-end of visible area +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + int textWidth = fontMetrics().horizontalAdvance(name) + 30; +#else int textWidth = fontMetrics().width(name) + 30; +#endif double frame = xToFrame(width() - textWidth); m_curveLabel.curvePos = getWinPos(curve, frame).toPoint(); @@ -1454,8 +1481,12 @@ void FunctionPanel::leaveEvent(QEvent *) { //----------------------------------------------------------------------------- void FunctionPanel::wheelEvent(QWheelEvent *e) { - double factor = exp(0.002 * (double)e->delta()); + double factor = exp(0.002 * (double)e->angleDelta().y()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + zoom(factor, factor, e->position().toPoint()); +#else zoom(factor, factor, e->pos()); +#endif } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/functionselection.cpp b/toonz/sources/toonzqt/functionselection.cpp index b53c3a73..abcb7868 100644 --- a/toonz/sources/toonzqt/functionselection.cpp +++ b/toonz/sources/toonzqt/functionselection.cpp @@ -394,7 +394,7 @@ QPair FunctionSelection::getSelectedKeyframe( if (index < count) { TDoubleParam *curve = m_selectedKeyframes[i].first; QSet::const_iterator it = m_selectedKeyframes[i].second.begin(); - it += index; + std::advance(it, index); return QPair(curve, *it); } index -= count; diff --git a/toonz/sources/toonzqt/functionsheet.cpp b/toonz/sources/toonzqt/functionsheet.cpp index 762868b7..3d4a5068 100644 --- a/toonz/sources/toonzqt/functionsheet.cpp +++ b/toonz/sources/toonzqt/functionsheet.cpp @@ -439,7 +439,7 @@ void FunctionSheetColumnHeadViewer::paintEvent(QPaintEvent *e) { /*! update tooltips */ void FunctionSheetColumnHeadViewer::mouseMoveEvent(QMouseEvent *e) { - if ((e->buttons() & Qt::MidButton) && m_draggingChannel && + if ((e->buttons() & Qt::MiddleButton) && m_draggingChannel && (e->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { QDrag *drag = new QDrag(this); @@ -499,7 +499,7 @@ void FunctionSheetColumnHeadViewer::mousePressEvent(QMouseEvent *e) { return; } - if (e->button() == Qt::MidButton) { + if (e->button() == Qt::MiddleButton) { m_draggingChannel = channel; m_dragStartPosition = e->pos(); return; @@ -645,7 +645,7 @@ FunctionSheetCellViewer::FunctionSheetCellViewer(FunctionSheet *parent) bool ret = connect(m_lineEdit, SIGNAL(editingFinished()), this, SLOT(onCellEditorEditingFinished())); ret = ret && connect(m_lineEdit, SIGNAL(mouseMoved(QMouseEvent *)), this, - SLOT(onMouseMovedInLineEdit(QMouseEvent *))); + SLOT(onMouseMovedInLineEdit(QMouseEvent *))); assert(ret); setMouseTracking(true); @@ -808,8 +808,9 @@ void FunctionSheetCellViewer::drawCells(QPainter &painter, int r0, int c0, } } - drawValue = - (curve->isKeyframe(row)) ? Key : (showIbtwn) ? Inbetween : None; + drawValue = (curve->isKeyframe(row)) ? Key + : (showIbtwn) ? Inbetween + : None; } // empty cells @@ -996,7 +997,7 @@ void FunctionSheetCellViewer::mousePressEvent(QMouseEvent *e) { if (curve) { KeyframeSetter::removeKeyframeAt(curve, row); } - } else if (e->button() == Qt::LeftButton || e->button() == Qt::MidButton) + } else if (e->button() == Qt::LeftButton || e->button() == Qt::MiddleButton) Spreadsheet::CellPanel::mousePressEvent(e); else if (e->button() == Qt::RightButton) { update(); diff --git a/toonz/sources/toonzqt/functiontreeviewer.cpp b/toonz/sources/toonzqt/functiontreeviewer.cpp index 2eb48527..b99fad0f 100644 --- a/toonz/sources/toonzqt/functiontreeviewer.cpp +++ b/toonz/sources/toonzqt/functiontreeviewer.cpp @@ -277,19 +277,9 @@ QVariant StageObjectChannelGroup::data(int role) const { return QString::fromStdString(name); } else if (role == Qt::ForegroundRole) { FunctionTreeModel *model = dynamic_cast(getModel()); - if (!model) -#if QT_VERSION >= 0x050000 - return QColor(Qt::black); -#else - return Qt::black; -#endif + if (!model) return QColor(Qt::black); FunctionTreeView *view = dynamic_cast(model->getView()); - if (!view || !model->getCurrentStageObject()) -#if QT_VERSION >= 0x050000 - return QColor(Qt::black); -#else - return Qt::black; -#endif + if (!view || !model->getCurrentStageObject()) return QColor(Qt::black); TStageObjectId currentId = model->getCurrentStageObject()->getId(); return m_stageObject->getId() == currentId ? view->getViewer()->getCurrentTextColor() @@ -398,19 +388,9 @@ QVariant FxChannelGroup::data(int role) const { return QString::fromStdWString(id + L" (" + name + L")"); } else if (role == Qt::ForegroundRole) { FunctionTreeModel *model = dynamic_cast(getModel()); - if (!model) -#if QT_VERSION >= 0x050000 - return QColor(Qt::black); -#else - return Qt::black; -#endif + if (!model) return QColor(Qt::black); FunctionTreeView *view = dynamic_cast(model->getView()); - if (!view) -#if QT_VERSION >= 0x050000 - return QColor(Qt::black); -#else - return Qt::black; -#endif + if (!view) return QColor(Qt::black); TFx *currentFx = model->getCurrentFx(); return m_fx == currentFx ? view->getViewer()->getCurrentTextColor() : view->getTextColor(); @@ -513,19 +493,9 @@ QVariant SkVDChannelGroup::data(int role) const { // it selection color // if this group refers to current vertex FunctionTreeModel *model = dynamic_cast(getModel()); - if (!model) -#if QT_VERSION >= 0x050000 - return QColor(Qt::black); -#else - return Qt::black; -#endif + if (!model) return QColor(Qt::black); FunctionTreeView *view = dynamic_cast(model->getView()); - if (!view || !model->getCurrentStageObject()) -#if QT_VERSION >= 0x050000 - return QColor(Qt::black); -#else - return Qt::black; -#endif + if (!view || !model->getCurrentStageObject()) return QColor(Qt::black); if (PlasticVertexSelection *vxSel = dynamic_cast(TSelection::getCurrent())) @@ -648,12 +618,7 @@ QVariant FunctionTreeModel::Channel::data(int role) const { } else if (role == Qt::ForegroundRole) { // 130221 iwasawa FunctionTreeView *view = dynamic_cast(m_model->m_view); - if (!view) -#if QT_VERSION >= 0x050000 - return QColor(Qt::black); -#else - return Qt::black; -#endif + if (!view) return QColor(Qt::black); return (isCurrent()) ? view->getViewer()->getCurrentTextColor() : view->getTextColor(); } else if (role == Qt::ToolTipRole) { @@ -1176,8 +1141,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)); + } } //----------------------------------------------------------------------------- @@ -1284,9 +1252,7 @@ void FunctionTreeModel::onParamChange(bool isDragging) { //----------------------------------------------------------------------------- void FunctionTreeModel::resetAll() { -#if QT_VERSION >= 0x050000 beginResetModel(); -#endif m_activeChannels.clear(); TreeModel::Item *root_item = getRootItem(); @@ -1294,9 +1260,6 @@ void FunctionTreeModel::resetAll() { m_stageObjects = 0; m_fxs = 0; -#if QT_VERSION < 0x050000 - reset(); -#endif beginRefresh(); refreshActiveChannels(); @@ -1308,9 +1271,7 @@ void FunctionTreeModel::resetAll() { m_currentChannel = 0; -#if QT_VERSION >= 0x050000 endResetModel(); -#endif } //----------------------------------------------------------------------------- @@ -1562,7 +1523,7 @@ void FunctionTreeView::onMidClick(TreeModel::Item *item, const QPoint &itemPos, QMouseEvent *e) { FunctionTreeModel::Channel *channel = dynamic_cast(item); - if (channel && e->button() == Qt::MidButton) { + if (channel && e->button() == Qt::MiddleButton) { m_draggingChannel = channel; m_dragStartPosition = e->pos(); } else @@ -1574,7 +1535,7 @@ void FunctionTreeView::onMidClick(TreeModel::Item *item, const QPoint &itemPos, void FunctionTreeView::onDrag(TreeModel::Item *item, const QPoint &itemPos, QMouseEvent *e) { // middle drag of the channel item can retrieve expression name - if ((e->buttons() & Qt::MidButton) && m_draggingChannel && + if ((e->buttons() & Qt::MiddleButton) && m_draggingChannel && (e->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { QDrag *drag = new QDrag(this); diff --git a/toonz/sources/toonzqt/functionviewer.cpp b/toonz/sources/toonzqt/functionviewer.cpp index eab93fba..e2005fef 100644 --- a/toonz/sources/toonzqt/functionviewer.cpp +++ b/toonz/sources/toonzqt/functionviewer.cpp @@ -56,11 +56,7 @@ TEnv::IntVar FunctionEditorToggleStatus("FunctionEditorToggleStatus", 1); // //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 FunctionViewer::FunctionViewer(QWidget *parent, Qt::WindowFlags flags) -#else -FunctionViewer::FunctionViewer(QWidget *parent, Qt::WFlags flags) -#endif : QSplitter(parent) , m_xshHandle(0) , m_frameHandle(0) @@ -178,13 +174,13 @@ FunctionViewer::FunctionViewer(QWidget *parent, Qt::WFlags flags) //---- signal-slot connections bool ret = true; ret = ret && connect(m_toolbar, SIGNAL(numericalColumnToggled()), this, - SLOT(toggleMode())); + SLOT(toggleMode())); ret = ret && connect(ftModel, SIGNAL(activeChannelsChanged()), - m_functionGraph, SLOT(update())); + m_functionGraph, SLOT(update())); ret = ret && connect(ftModel, SIGNAL(activeChannelsChanged()), - m_numericalColumns, SLOT(updateAll())); + m_numericalColumns, SLOT(updateAll())); ret = ret && connect(ftModel, SIGNAL(curveChanged(bool)), m_treeView, - SLOT(update())); + SLOT(update())); ret = ret && connect(ftModel, SIGNAL(curveChanged(bool)), m_functionGraph, SLOT(update())); ret = ret && connect(ftModel, SIGNAL(curveChanged(bool)), m_numericalColumns, @@ -434,7 +430,7 @@ void FunctionViewer::setObjectHandle(TObjectHandle *objectHandle) { bool ret = true; ret = connect(m_objectHandle, SIGNAL(objectSwitched()), this, - SLOT(onStageObjectSwitched())) && + SLOT(onStageObjectSwitched())) && ret; ret = connect(m_objectHandle, SIGNAL(objectChanged(bool)), this, SLOT(onStageObjectChanged(bool))) && @@ -553,7 +549,7 @@ void FunctionViewer::onCurveChanged(bool isDragging) { if (ftModel) { FunctionTreeModel::Channel *currChan = ftModel->getCurrentChannel(); if (currChan) { - //カレントチャンネルがFxChannelGroupに含まれていたらEmit + // カレントチャンネルがFxChannelGroupに含まれていたらEmit FxChannelGroup *fxChanGroup = dynamic_cast(currChan->getChannelGroup()); if (fxChanGroup) m_fxHandle->notifyFxChanged(); @@ -596,8 +592,8 @@ void FunctionViewer::onStageObjectSwitched() { const TStageObjectId &objId = m_objectHandle->getObjectId(); TStageObject *obj = (objId == TStageObjectId::NoneId) - ? (TStageObject *)0 - : xsh->getStageObject(objId); + ? (TStageObject *)0 + : xsh->getStageObject(objId); static_cast(m_treeView->model()) ->setCurrentStageObject(obj); @@ -613,8 +609,8 @@ void FunctionViewer::onStageObjectChanged(bool isDragging) { const TStageObjectId &objId = m_objectHandle->getObjectId(); TStageObject *obj = (objId == TStageObjectId::NoneId) - ? (TStageObject *)0 - : xsh->getStageObject(objId); + ? (TStageObject *)0 + : xsh->getStageObject(objId); static_cast(m_treeView->model()) ->setCurrentStageObject(obj); diff --git a/toonz/sources/toonzqt/fxhistogramrender.cpp b/toonz/sources/toonzqt/fxhistogramrender.cpp index 537a903d..00120c64 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/fxschematicnode.cpp b/toonz/sources/toonzqt/fxschematicnode.cpp index 2d6163b1..43659a35 100644 --- a/toonz/sources/toonzqt/fxschematicnode.cpp +++ b/toonz/sources/toonzqt/fxschematicnode.cpp @@ -276,21 +276,26 @@ void FxColumnPainter::contextMenuEvent(QGraphicsSceneContextMenuEvent *cme) { QMenu *addMenu = fxScene->getAddFxMenu(); QAction *disconnectFromXSheet = - new QAction(tr("&Disconnect from Scene"), &menu); + new QAction(createQIcon("xsheet_disconnect", false, true), + tr("&Disconnect from Scene"), &menu); connect(disconnectFromXSheet, SIGNAL(triggered()), fxScene, SLOT(onDisconnectFromXSheet())); - QAction *connectToXSheet = new QAction(tr("&Connect to Scene"), &menu); + QAction *connectToXSheet = + new QAction(createQIcon("xsheet_connect", false, true), + tr("&Connect to Scene"), &menu); connect(connectToXSheet, SIGNAL(triggered()), fxScene, SLOT(onConnectToXSheet())); QAction *addOutputFx = CommandManager::instance()->getAction("MI_NewOutputFx"); - QAction *addPaste = new QAction(tr("&Paste Add"), &menu); + QAction *addPaste = new QAction(createQIcon("paste_duplicate", false, true), + tr("&Paste Add"), &menu); connect(addPaste, SIGNAL(triggered()), fxScene, SLOT(onAddPaste())); - QAction *preview = new QAction(tr("&Preview"), &menu); + QAction *preview = + new QAction(createQIcon("preview", false, true), tr("&Preview"), &menu); connect(preview, SIGNAL(triggered()), fxScene, SLOT(onPreview())); bool cacheEnabled = @@ -503,15 +508,19 @@ void FxPalettePainter::contextMenuEvent(QGraphicsSceneContextMenuEvent *cme) { QMenu menu(fxScene->views()[0]); QAction *disconnectFromXSheet = - new QAction(tr("&Disconnect from Scene"), &menu); + new QAction(createQIcon("xsheet_disconnect", false, true), + tr("&Disconnect from Scene"), &menu); connect(disconnectFromXSheet, SIGNAL(triggered()), fxScene, SLOT(onDisconnectFromXSheet())); - QAction *connectToXSheet = new QAction(tr("&Connect to Scene"), &menu); + QAction *connectToXSheet = + new QAction(createQIcon("xsheet_connect", false, true), + tr("&Connect to Scene"), &menu); connect(connectToXSheet, SIGNAL(triggered()), fxScene, SLOT(onConnectToXSheet())); - QAction *preview = new QAction(tr("&Preview"), &menu); + QAction *preview = + new QAction(createQIcon("preview", false, true), tr("&Preview"), &menu); connect(preview, SIGNAL(triggered()), fxScene, SLOT(onPreview())); QAction *collapse = CommandManager::instance()->getAction("MI_Collapse"); @@ -734,56 +743,72 @@ void FxPainter::contextMenuEvent(QGraphicsSceneContextMenuEvent *cme) { QAction *group = CommandManager::instance()->getAction("MI_Group"); QAction *ungroup = CommandManager::instance()->getAction("MI_Ungroup"); - QAction *editGroup = new QAction(tr("&Open Group"), &menu); + QAction *editGroup = new QAction(createQIcon("enter_group", false, true), + tr("&Open Group"), &menu); connect(editGroup, SIGNAL(triggered()), fxScene, SLOT(onEditGroup())); - QAction *replacePaste = new QAction(tr("&Paste Replace"), &menu); + QAction *replacePaste = new QAction(createQIcon("convert", false, true), + tr("&Paste Replace"), &menu); connect(replacePaste, SIGNAL(triggered()), fxScene, SLOT(onReplacePaste())); - QAction *addPaste = new QAction(tr("&Paste Add"), &menu); + QAction *addPaste = new QAction(createQIcon("paste_duplicate", false, true), + tr("&Paste Add"), &menu); connect(addPaste, SIGNAL(triggered()), fxScene, SLOT(onAddPaste())); QAction *addOutputFx = CommandManager::instance()->getAction("MI_NewOutputFx"); - QAction *deleteFx = new QAction(tr("&Delete"), &menu); + QAction *deleteFx = + new QAction(createQIcon("delete", false, true), tr("&Delete"), &menu); connect(deleteFx, SIGNAL(triggered()), fxScene, SLOT(onDeleteFx())); QAction *disconnectFromXSheet = - new QAction(tr("&Disconnect from Scene"), &menu); + new QAction(createQIcon("xsheet_disconnect", false, true), + tr("&Disconnect from Scene"), &menu); connect(disconnectFromXSheet, SIGNAL(triggered()), fxScene, SLOT(onDisconnectFromXSheet())); - QAction *connectToXSheet = new QAction(tr("&Connect to Scene"), &menu); + QAction *connectToXSheet = + new QAction(createQIcon("xsheet_connect", false, true), + tr("&Connect to Scene"), &menu); connect(connectToXSheet, SIGNAL(triggered()), fxScene, SLOT(onConnectToXSheet())); - QAction *duplicateFx = new QAction(tr("&Create Linked FX"), &menu); + QAction *duplicateFx = new QAction(createQIcon("link", false, true), + tr("&Create Linked FX"), &menu); connect(duplicateFx, SIGNAL(triggered()), fxScene, SLOT(onDuplicateFx())); - QAction *unlinkFx = new QAction(tr("&Unlink"), &menu); + QAction *unlinkFx = + new QAction(createQIcon("unlink", false, true), tr("&Unlink"), &menu); connect(unlinkFx, SIGNAL(triggered()), fxScene, SLOT(onUnlinkFx())); - QAction *macroFx = new QAction(tr("&Make Macro FX"), &menu); + QAction *macroFx = new QAction(createQIcon("macro", false, true), + tr("&Make Macro FX"), &menu); connect(macroFx, SIGNAL(triggered()), fxScene, SLOT(onMacroFx())); - QAction *explodeMacroFx = new QAction(tr("&Explode Macro FX"), &menu); + QAction *explodeMacroFx = new QAction(createQIcon("sub_explode", false, true), + tr("&Explode Macro FX"), &menu); connect(explodeMacroFx, SIGNAL(triggered()), fxScene, SLOT(onExplodeMacroFx())); - QAction *openMacroFx = new QAction(tr("&Open Macro FX"), &menu); + QAction *openMacroFx = new QAction(createQIcon("sub_enter", false, true), + tr("&Open Macro FX"), &menu); connect(openMacroFx, SIGNAL(triggered()), fxScene, SLOT(onOpenMacroFx())); - QAction *savePresetFx = new QAction(tr("&Save As Preset..."), &menu); + QAction *savePresetFx = new QAction(createQIcon("preset", false, true), + tr("&Save As Preset..."), &menu); connect(savePresetFx, SIGNAL(triggered()), fxScene, SLOT(onSavePresetFx())); - QAction *preview = new QAction(tr("&Preview"), &menu); + QAction *preview = + new QAction(createQIcon("preview", false, true), tr("&Preview"), &menu); connect(preview, SIGNAL(triggered()), fxScene, SLOT(onPreview())); bool cacheEnabled = m_parent->isCached(); QAction *cacheFx = - new QAction(cacheEnabled ? tr("&Uncache FX") : tr("&Cache FX"), &menu); + new QAction(cacheEnabled ? createQIcon("uncache_fx", false, true) + : createQIcon("cache_fx", false, true), + cacheEnabled ? tr("&Uncache FX") : tr("&Cache FX"), &menu); if (cacheEnabled) connect(cacheFx, SIGNAL(triggered()), fxScene, SLOT(onUncacheFx())); else @@ -1017,10 +1042,12 @@ void FxXSheetPainter::contextMenuEvent(QGraphicsSceneContextMenuEvent *cme) { QAction *addOutputFx = CommandManager::instance()->getAction("MI_NewOutputFx"); - QAction *addPaste = new QAction(tr("&Paste Add"), &menu); + QAction *addPaste = new QAction(createQIcon("paste_duplicate", false, true), + tr("&Paste Add"), &menu); connect(addPaste, SIGNAL(triggered()), fxScene, SLOT(onAddPaste())); - QAction *preview = new QAction(tr("&Preview"), &menu); + QAction *preview = + new QAction(createQIcon("preview", false, true), tr("&Preview"), &menu); connect(preview, SIGNAL(triggered()), fxScene, SLOT(onPreview())); menu.addMenu(insertMenu); @@ -1073,7 +1100,7 @@ void FxOutputPainter::paint(QPainter *painter, SchematicViewer *viewer = sceneFx->getSchematicViewer(); QColor outputColor = m_isActive ? viewer->getActiveOutputColor() - : viewer->getOtherOutputColor(); + : viewer->getOtherOutputColor(); painter->setBrush(outputColor); painter->setPen(Qt::NoPen); @@ -1105,7 +1132,8 @@ void FxOutputPainter::contextMenuEvent(QGraphicsSceneContextMenuEvent *cme) { FxSchematicScene *fxScene = dynamic_cast(scene()); QMenu menu(fxScene->views()[0]); if (fxScene->getXsheet()->getFxDag()->getOutputFxCount() > 1) { - QAction *removeOutput = new QAction(tr("&Delete"), &menu); + QAction *removeOutput = + new QAction(createQIcon("delete", false, true), tr("&Delete"), &menu); connect(removeOutput, SIGNAL(triggered()), fxScene, SLOT(onRemoveOutput())); QAction *activateOutput = new QAction(tr("&Activate"), &menu); @@ -1153,10 +1181,12 @@ void FxSchematicLink::contextMenuEvent(QGraphicsSceneContextMenuEvent *cme) { } } - QAction *deleteFx = new QAction(tr("&Delete"), &menu); + QAction *deleteFx = + new QAction(createQIcon("delete", false, true), tr("&Delete"), &menu); connect(deleteFx, SIGNAL(triggered()), fxScene, SLOT(onDeleteFx())); - QAction *insertPaste = new QAction(tr("&Paste Insert"), &menu); + QAction *insertPaste = new QAction(createQIcon("paste", false, true), + tr("&Paste Insert"), &menu); connect(insertPaste, SIGNAL(triggered()), fxScene, SLOT(onInsertPaste())); menu.addMenu(fxScene->getInsertFxMenu()); @@ -1622,11 +1652,14 @@ void FxSchematicPort::contextMenuEvent(QGraphicsSceneContextMenuEvent *cme) { } QAction *disconnectFromXSheet = - new QAction(tr("&Disconnect from Scene"), &menu); + new QAction(createQIcon("xsheet_disconnect", false, true), + tr("&Disconnect from Scene"), &menu); connect(disconnectFromXSheet, SIGNAL(triggered()), fxScene, SLOT(onDisconnectFromXSheet())); - QAction *connectToXSheet = new QAction(tr("&Connect to Scene"), &menu); + QAction *connectToXSheet = + new QAction(createQIcon("xsheet_connect", false, true), + tr("&Connect to Scene"), &menu); connect(connectToXSheet, SIGNAL(triggered()), fxScene, SLOT(onConnectToXSheet())); @@ -3047,8 +3080,8 @@ FxSchematicColumnNode::FxSchematicColumnNode(FxSchematicScene *scene, m_name = QString::fromStdString(name); m_resizeItem = new SchematicThumbnailToggle( - this, fx->getAttributes()->isOpened()); //サムネイル矢印 - m_nameItem = new SchematicName(this, 74, 20); //リネーム部分 + this, fx->getAttributes()->isOpened()); // サムネイル矢印 + m_nameItem = new SchematicName(this, 74, 20); // リネーム部分 m_outDock = new FxSchematicDock(this, "", 0, eFxOutputPort); // Outポート m_renderToggle = new SchematicToggle(this, viewer->getSchematicPreviewButtonOnImage(), @@ -3876,10 +3909,12 @@ void FxPassThroughPainter::contextMenuEvent( QAction *addOutputFx = CommandManager::instance()->getAction("MI_NewOutputFx"); - QAction *addPaste = new QAction(tr("&Paste Add"), &menu); + QAction *addPaste = new QAction(createQIcon("paste_duplicate", false, true), + tr("&Paste Add"), &menu); connect(addPaste, SIGNAL(triggered()), fxScene, SLOT(onAddPaste())); - QAction *preview = new QAction(tr("&Preview"), &menu); + QAction *preview = + new QAction(createQIcon("preview", false, true), tr("&Preview"), &menu); connect(preview, SIGNAL(triggered()), fxScene, SLOT(onPreview())); menu.addMenu(insertMenu); diff --git a/toonz/sources/toonzqt/fxschematicscene.cpp b/toonz/sources/toonzqt/fxschematicscene.cpp index 6b6d0cfb..f2aad876 100644 --- a/toonz/sources/toonzqt/fxschematicscene.cpp +++ b/toonz/sources/toonzqt/fxschematicscene.cpp @@ -462,7 +462,7 @@ void FxSchematicScene::updateScene() { } // sorting fxs so that fxs with specified positions are placed first - qSort(fxsToBePlaced.begin(), fxsToBePlaced.end(), nodePosDefined); + std::sort(fxsToBePlaced.begin(), fxsToBePlaced.end(), nodePosDefined); for (auto fx : fxsToBePlaced) { SchematicNode *node = addFxSchematicNode(fx); @@ -1034,7 +1034,6 @@ QPointF FxSchematicScene::nearestPoint(const QPointF &point) { rect.adjust(-0.1, -0.1, 0.1, 0.1); itemList = items(rect); } -#if QT_VERSION >= 0x050000 /* FIXME: QTransform() のデフォルトは Qt4.8 の itemAt() と比べて equivant だろうか? @@ -1046,15 +1045,6 @@ QPointF FxSchematicScene::nearestPoint(const QPointF &point) { item = itemAt(rect.topLeft(), QTransform()); if (item) return rect.topLeft(); item = itemAt(rect.topRight(), QTransform()); -#else - QGraphicsItem *item = itemAt(rect.bottomLeft()); - if (item) return rect.bottomLeft(); - item = itemAt(rect.bottomRight()); - if (item) return rect.bottomRight(); - item = itemAt(rect.topLeft()); - if (item) return rect.topLeft(); - item = itemAt(rect.topRight()); -#endif if (item) return rect.topRight(); return QPointF(); } @@ -1386,26 +1376,28 @@ void FxSchematicScene::placeNodeAndParents(TFx *fx, double x, double &maxX, //------------------------------------------------------------------ void FxSchematicScene::onDisconnectFromXSheet() { - std::list> list = - m_selection->getFxs().toStdList(); + std::list> list(m_selection->getFxs().begin(), + m_selection->getFxs().end()); TFxCommand::disconnectNodesFromXsheet(list, m_xshHandle); } //------------------------------------------------------------------ void FxSchematicScene::onConnectToXSheet() { - std::list> list = - m_selection->getFxs().toStdList(); + std::list> list(m_selection->getFxs().begin(), + m_selection->getFxs().end()); TFxCommand::connectNodesToXsheet(list, m_xshHandle); } //------------------------------------------------------------------ void FxSchematicScene::onDeleteFx() { - std::list> fxList = - m_selection->getFxs().toStdList(); - std::list linkList = m_selection->getLinks().toStdList(); - std::list columnIndexList = m_selection->getColumnIndexes().toStdList(); + std::list> fxList(m_selection->getFxs().begin(), + m_selection->getFxs().end()); + std::list linkList(m_selection->getLinks().begin(), + m_selection->getLinks().end()); + std::list columnIndexList(m_selection->getColumnIndexes().begin(), + m_selection->getColumnIndexes().end()); TFxCommand::deleteSelection(fxList, linkList, columnIndexList, m_xshHandle, m_fxHandle); } @@ -1443,7 +1435,8 @@ void FxSchematicScene::onUnlinkFx() { //------------------------------------------------------------------ void FxSchematicScene::onMacroFx() { - TFxCommand::makeMacroFx(m_selection->getFxs().toVector().toStdVector(), + TFxCommand::makeMacroFx(std::vector(m_selection->getFxs().begin(), + m_selection->getFxs().end()), m_app); } @@ -1615,16 +1608,12 @@ TFx *FxSchematicScene::getCurrentFx() { return m_fxHandle->getFx(); } void FxSchematicScene::mousePressEvent(QGraphicsSceneMouseEvent *me) { QList items = selectedItems(); -#if QT_VERSION >= 0x050000 - QGraphicsItem *item = itemAt(me->scenePos(), QTransform()); -#else - QGraphicsItem *item = itemAt(me->scenePos()); -#endif - FxSchematicPort *port = dynamic_cast(item); - FxSchematicLink *link = dynamic_cast(item); + QGraphicsItem *item = itemAt(me->scenePos(), QTransform()); + FxSchematicPort *port = dynamic_cast(item); + FxSchematicLink *link = dynamic_cast(item); SchematicScene::mousePressEvent(me); onSelectionChanged(); - if (me->button() == Qt::MidButton) { + if (me->button() == Qt::MiddleButton) { int i; for (i = 0; i < items.size(); i++) items[i]->setSelected(true); } @@ -1634,7 +1623,7 @@ void FxSchematicScene::mousePressEvent(QGraphicsSceneMouseEvent *me) { not. */ if (selectedItems().isEmpty()) { - if (me->button() != Qt::MidButton && !item) m_fxHandle->setFx(0, false); + if (me->button() != Qt::MiddleButton && !item) m_fxHandle->setFx(0, false); return; } m_isConnected = false; @@ -1671,12 +1660,8 @@ void FxSchematicScene::mouseMoveEvent(QGraphicsSceneMouseEvent *me) { simulateDisconnectSelection(true); m_connectionLinks.showBridgeLinks(); -#if QT_VERSION >= 0x050000 SchematicLink *link = dynamic_cast(itemAt(m_lastPos, QTransform())); -#else - SchematicLink *link = dynamic_cast(itemAt(m_lastPos)); -#endif if (link && (link->getEndPort() && link->getStartPort())) { TFxCommand::Link fxLink = m_selection->getBoundingFxs(link); if (fxLink == TFxCommand::Link()) return; @@ -1743,14 +1728,16 @@ void FxSchematicScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *me) { fxLink.m_inputFx = inputNode->getFx(); if (!outputNode->isA(eXSheetFx)) fxLink.m_index = i; - TFxCommand::connectFxs(fxLink, m_selection->getFxs().toStdList(), + TFxCommand::connectFxs(fxLink, + std::list(m_selection->getFxs().begin(), + m_selection->getFxs().end()), m_xshHandle, m_selectionOldPos); } } } else if (m_disconnectionLinks.size() > 0) { - QList fxs = m_selection->getFxs(); - TFxCommand::disconnectFxs(fxs.toStdList(), m_xshHandle, - m_selectionOldPos); + TFxCommand::disconnectFxs(std::list(m_selection->getFxs().begin(), + m_selection->getFxs().end()), + m_xshHandle, m_selectionOldPos); m_selectionOldPos.clear(); } } @@ -1818,12 +1805,8 @@ void FxSchematicScene::onAltModifierChanged(bool altPressed) { if (m_disconnectionLinks.size() == 0 && m_linkUnlinkSimulation) simulateDisconnectSelection(altPressed); if (m_connectionLinks.size() == 0 && m_linkUnlinkSimulation) { -#if QT_VERSION >= 0x050000 SchematicLink *link = dynamic_cast(itemAt(m_lastPos, QTransform())); -#else - SchematicLink *link = dynamic_cast(itemAt(m_lastPos)); -#endif if (link && (!link->getEndPort() || !link->getStartPort())) return; simulateInsertSelection(link, altPressed && !!link); } @@ -2067,11 +2050,7 @@ void FxSchematicScene::updateNestedGroupEditors(FxSchematicNode *node, if (rect.isEmpty()) rect = app; else -#if QT_VERSION >= 0x050000 rect = rect.united(app); -#else - rect = rect.unite(app); -#endif } } QMap::iterator it; @@ -2081,22 +2060,14 @@ void FxSchematicScene::updateNestedGroupEditors(FxSchematicNode *node, if (rect.isEmpty()) rect = app; else -#if QT_VERSION >= 0x050000 rect = rect.united(app); -#else - rect = rect.unite(app); -#endif } } node->setPos(newPos); for (i = 0; i < groupIdStack.size(); i++) { if (!m_groupEditorTable.contains(groupIdStack[i])) continue; -#if QT_VERSION >= 0x050000 rect = rect.united(m_groupEditorTable[groupIdStack[i]]->sceneBoundingRect()); -#else - rect = rect.unite(m_groupEditorTable[groupIdStack[i]]->sceneBoundingRect()); -#endif QRectF app = m_groupEditorTable[groupIdStack[i]]->boundingSceneRect(); if (m_groupEditorTable[groupIdStack[i]]->scenePos() != app.topLeft()) m_groupEditorTable[groupIdStack[i]]->setPos(app.topLeft()); @@ -2105,12 +2076,8 @@ void FxSchematicScene::updateNestedGroupEditors(FxSchematicNode *node, FxSchematicMacroEditor *editor = it.value(); if (editor->contains(node)) { QRectF app = editor->sceneBoundingRect(); -#if QT_VERSION >= 0x050000 - rect = rect.united(app); -#else - rect = rect.unite(app); -#endif - app = editor->boundingSceneRect(); + rect = rect.united(app); + app = editor->boundingSceneRect(); if (editor->scenePos() != app.topLeft()) editor->setPos(app.topLeft()); } } @@ -2209,5 +2176,57 @@ bool FxSchematicScene::isAnEmptyZone_withParentFx(const QRectF &rect, } } } - return true; + return true; +} + +//------------------------------------------------------------------ +// update snap targets on click node +void FxSchematicScene::updateSnapTarget(QGraphicsItem *item) { + clearSnapTargets(); + FxSchematicNode *node = dynamic_cast(item); + if (!node) return; + + // find input connections + int portCount = node->getInputPortCount(); + for (int i = 0; i < portCount; i++) { + FxSchematicPort *port = node->getInputPort(i); + int j, linkCount = port->getLinkCount(); + for (j = 0; j < linkCount; j++) { + SchematicLink *link = port->getLink(j); + if (!link) continue; + if (m_disconnectionLinks.isABridgeLink(link)) continue; + SchematicNode *otherNode = link->getOtherNode(node); + if (otherNode && !otherNode->isSelected()) { + QPointF targetPos = + otherNode->scenePos() + QPointF(otherNode->boundingRect().width() + + SchematicScene::snapHSpacing, + 0); + + addSnapTarget(targetPos, node->boundingRect(), + link->getOtherPort(port)->getLinkEndPoint(), + port->getLinkEndPoint() - node->scenePos()); + } + } + } + + // find output connections + FxSchematicPort *port = node->getOutputPort(); + if (port) { + int linkCount = port->getLinkCount(); + for (int i = 0; i < linkCount; i++) { + SchematicLink *link = port->getLink(i); + if (!link) continue; + if (m_disconnectionLinks.isABridgeLink(link)) continue; + SchematicNode *otherNode = link->getOtherNode(node); + if (otherNode && !otherNode->isSelected()) { + QPointF targetPos = + otherNode->scenePos() - + QPointF(node->boundingRect().width() + SchematicScene::snapHSpacing, + 0); + addSnapTarget(targetPos, node->boundingRect(), + link->getOtherPort(port)->getLinkEndPoint(), + port->getLinkEndPoint() - node->scenePos()); + } + } + } } \ No newline at end of file diff --git a/toonz/sources/toonzqt/fxselection.cpp b/toonz/sources/toonzqt/fxselection.cpp index cd38e8f7..f459688d 100644 --- a/toonz/sources/toonzqt/fxselection.cpp +++ b/toonz/sources/toonzqt/fxselection.cpp @@ -219,9 +219,10 @@ void FxSelection::pasteSelection() { emit columnPasted(columns); } - TFxCommand::pasteFxs(fxs.toStdList(), zeraryFxColumnSize.toStdMap(), - columns.toStdList(), m_pastePosition, m_xshHandle, - m_fxHandle); + TFxCommand::pasteFxs(std::list(fxs.begin(), fxs.end()), + zeraryFxColumnSize.toStdMap(), + std::list(columns.begin(), columns.end()), + m_pastePosition, m_xshHandle, m_fxHandle); if (!columns.isEmpty()) { TUndoManager::manager()->endBlock(); @@ -281,9 +282,11 @@ bool FxSelection::insertPasteSelection() { emit columnPasted(columns); } - TFxCommand::insertPasteFxs(selectedLinks[i], fxs.toStdList(), - zeraryFxColumnSize.toStdMap(), - columns.toStdList(), m_xshHandle, m_fxHandle); + TFxCommand::insertPasteFxs( + selectedLinks[i], std::list(fxs.begin(), fxs.end()), + zeraryFxColumnSize.toStdMap(), + std::list(columns.begin(), columns.end()), m_xshHandle, + m_fxHandle); } return true; @@ -325,9 +328,11 @@ bool FxSelection::addPasteSelection() { auto_.m_destruct = true, TUndoManager::manager()->beginBlock(); TFx *inFx = selectedFxs[i].getPointer(); - TFxCommand::addPasteFxs(inFx, fxs.toStdList(), - zeraryFxColumnSize.toStdMap(), columns.toStdList(), - m_xshHandle, m_fxHandle); + TFxCommand::addPasteFxs( + inFx, std::list(fxs.begin(), fxs.end()), + zeraryFxColumnSize.toStdMap(), + std::list(columns.begin(), columns.end()), m_xshHandle, + m_fxHandle); } return true; @@ -374,9 +379,11 @@ bool FxSelection::replacePasteSelection() { } TFx *inFx = m_selectedFxs[i].getPointer(); - TFxCommand::replacePasteFxs(inFx, fxs.toStdList(), - zeraryFxColumnSize.toStdMap(), - columns.toStdList(), m_xshHandle, m_fxHandle); + TFxCommand::replacePasteFxs( + inFx, std::list(fxs.begin(), fxs.end()), + zeraryFxColumnSize.toStdMap(), + std::list(columns.begin(), columns.end()), m_xshHandle, + m_fxHandle); } return true; @@ -386,7 +393,8 @@ bool FxSelection::replacePasteSelection() { void FxSelection::groupSelection() { if (m_selectedFxs.size() <= 1) return; - TFxCommand::groupFxs(m_selectedFxs.toStdList(), m_xshHandle); + TFxCommand::groupFxs( + std::list(m_selectedFxs.begin(), m_selectedFxs.end()), m_xshHandle); selectNone(); m_xshHandle->notifyXsheetChanged(); } diff --git a/toonz/sources/toonzqt/fxsettings.cpp b/toonz/sources/toonzqt/fxsettings.cpp index 292868b0..6f311ccb 100644 --- a/toonz/sources/toonzqt/fxsettings.cpp +++ b/toonz/sources/toonzqt/fxsettings.cpp @@ -65,43 +65,35 @@ 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, + QList &ret) { + for (int i = 0; i < layout->count(); i++) { + QLayoutItem *item = layout->itemAt(i); + if (!item) continue; + if (item->widget()) { + ParamField *pf = dynamic_cast(item->widget()); + if (pf && pf->getParamName().toStdString() == name) { + ret.push_back(pf); + if (i > 0 && layout->itemAt(i - 1)->widget()) { + QLabel *label = + dynamic_cast(layout->itemAt(i - 1)->widget()); + if (label) ret.push_back(label); } -} -*/ + return true; + } + // the widget may be a container of another layout + else if (item->widget()->layout()) { + if (findItemByParamName(item->widget()->layout(), name, ret)) + return true; + } + } else if (item->layout()) { + if (findItemByParamName(item->layout(), name, ret)) return true; + } + } + return false; +}; } // namespace //============================================================================= @@ -162,9 +154,13 @@ 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); - if (param) { + bool isHidden = + (param) ? fx->getParams()->getParamVar(name)->isHidden() : true; + + if (param && !isHidden) { std::string paramName = fx->getFxType() + "." + name; QString str = QString::fromStdWString(TStringTable::translate(paramName)); @@ -268,9 +264,15 @@ void ParamsPage::setPageField(TIStream &is, const TFxP &fx, bool isVertical) { tmpWidget->setVisible(shrink == 1); } else { // modeSensitiveStr != "" QList modes; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList modeListStr = + QString::fromStdString(is.getTagAttribute("mode")) + .split(',', Qt::SkipEmptyParts); +#else QStringList modeListStr = QString::fromStdString(is.getTagAttribute("mode")) .split(',', QString::SkipEmptyParts); +#endif for (QString modeNum : modeListStr) modes.push_back(modeNum.toInt()); // find the mode combobox ModeChangerParamField *modeChanger = nullptr; @@ -285,6 +287,17 @@ void ParamsPage::setPageField(TIStream &is, const TFxP &fx, bool isVertical) { modeChanger = field; break; } + // modeChanger may be in another vbox in the page + if (!modeChanger) { + QList allModeChangers = + findChildren(); + for (auto field : allModeChangers) { + if (field->getParamName().toStdString() == modeSensitiveStr) { + modeChanger = field; + break; + } + } + } assert(modeChanger); tmpWidget = new ModeSensitiveBox(this, modeChanger, modes); } @@ -299,6 +312,7 @@ void ParamsPage::setPageField(TIStream &is, const TFxP &fx, bool isVertical) { m_mainLayout->setColumnStretch(0, 0); m_mainLayout->setColumnStretch(1, 1); setPageField(is, fx, true); + tmpWidget->setLayout(m_mainLayout); // turn back the layout m_mainLayout = keepMainLay; @@ -369,52 +383,15 @@ void ParamsPage::setPageField(TIStream &is, const TFxP &fx, bool isVertical) { std::string name; is >> name; is.matchEndTag(); - for (int r = 0; r < m_mainLayout->rowCount(); r++) { - QLayoutItem *li = m_mainLayout->itemAtPosition(r, 1); - if (!li) continue; - QWidget *w = li->widget(); - if (!w) continue; - ParamField *pf = dynamic_cast(w); - if (pf) { - if (pf->getParamName().toStdString() == name) { - if (tagName == "controller") - controller_bpf = dynamic_cast(pf); - else if (tagName == "on") { - on_items.push_back(w); - on_items.push_back( - m_mainLayout->itemAtPosition(r, 0)->widget()); - } else if (tagName == "off") { - off_items.push_back(w); - off_items.push_back( - m_mainLayout->itemAtPosition(r, 0)->widget()); - } - } - } - /*-- 入れ子のLayoutも1段階探す --*/ - else { - QGridLayout *gridLay = dynamic_cast(w->layout()); - if (!gridLay) continue; - for (int r_s = 0; r_s < gridLay->rowCount(); r_s++) { - QLayoutItem *li_s = gridLay->itemAtPosition(r_s, 1); - if (!li_s) continue; - ParamField *pf_s = dynamic_cast(li_s->widget()); - if (pf_s) { - if (pf_s->getParamName().toStdString() == name) { - if (tagName == "controller") - controller_bpf = dynamic_cast(pf_s); - else if (tagName == "on") { - on_items.push_back(pf_s); - on_items.push_back( - gridLay->itemAtPosition(r_s, 0)->widget()); - } else if (tagName == "off") { - off_items.push_back(pf_s); - off_items.push_back( - gridLay->itemAtPosition(r_s, 0)->widget()); - } - } - } - } - } + + QList widgets; + if (findItemByParamName(m_mainLayout, name, widgets)) { + if (tagName == "controller") { + controller_bpf = dynamic_cast(widgets[0]); + } else if (tagName == "on") + on_items.append(widgets); + else if (tagName == "off") + off_items.append(widgets); } } else throw TException("unexpected tag " + tagName); @@ -575,8 +552,7 @@ void ParamsPage::setFx(const TFxP ¤tFx, const TFxP &actualFx, int frame) { for (int i = 0; i < (int)m_fields.size(); i++) { ParamField *field = m_fields[i]; QString fieldName = field->getParamName(); - - TFxP fx = getCurrentFx(currentFx, actualFx->getFxId()); + TFxP fx = getCurrentFx(currentFx, actualFx->getFxId()); assert(fx.getPointer()); TParamP currentParam = currentFx->getParams()->getParam(fieldName.toStdString()); @@ -655,7 +631,11 @@ void updateMaximumPageSize(QGridLayout *layout, int &maxLabelWidth, QGroupBox *gBox = dynamic_cast(layout->itemAtPosition(r, 0)->widget()); if (label) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int tmpWidth = label->fontMetrics().horizontalAdvance(label->text()); +#else int tmpWidth = label->fontMetrics().width(label->text()); +#endif if (maxLabelWidth < tmpWidth) maxLabelWidth = tmpWidth; } /*-- PlugInFxのGroupパラメータのサイズ --*/ @@ -723,11 +703,7 @@ QSize ParamsPage::getPreferredSize() { // ParamsPageSet //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 ParamsPageSet::ParamsPageSet(QWidget *parent, Qt::WindowFlags flags) -#else -ParamsPageSet::ParamsPageSet(QWidget *parent, Qt::WFlags flags) -#endif : QWidget(parent, flags) , m_preferredSize(0, 0) , m_helpFilePath("") @@ -753,6 +729,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); @@ -763,6 +746,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); } @@ -867,7 +851,7 @@ void ParamsPageSet::addParamsPage(ParamsPage *page, const char *name) { QSize pagePreferredSize = page->getPreferredSize(); m_preferredSize = m_preferredSize.expandedTo( pagePreferredSize + QSize(m_tabBarContainer->height() + 2, - 2)); /*-- 2は上下左右のマージン --*/ + 2)); /*-- 2は上下左右のマージン --*/ QScrollArea *pane = new QScrollArea(this); pane->setWidgetResizable(true); @@ -971,7 +955,7 @@ void ParamsPageSet::createPage(TIStream &is, const TFxP &fx, int index) { QSize pagePreferredSize = paramsPage->getPreferredSize(); m_preferredSize = m_preferredSize.expandedTo( pagePreferredSize + QSize(m_tabBarContainer->height() + 2, - 2)); /*-- 2は上下左右のマージン --*/ + 2)); /*-- 2は上下左右のマージン --*/ QScrollArea *scrollAreaPage = new QScrollArea(this); scrollAreaPage->setWidgetResizable(true); @@ -996,7 +980,7 @@ void ParamsPageSet::recomputePreferredSize() { if (!page) continue; QSize pagePreferredSize = page->getPreferredSize(); newSize = newSize.expandedTo(pagePreferredSize + - QSize(m_tabBarContainer->height() + 2, 2)); + QSize(m_tabBarContainer->height() + 2, 2)); } if (!newSize.isEmpty()) { m_preferredSize = newSize; @@ -1040,15 +1024,54 @@ 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 //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 ParamViewer::ParamViewer(QWidget *parent, Qt::WindowFlags flags) -#else -ParamViewer::ParamViewer(QWidget *parent, Qt::WFlags flags) -#endif : QFrame(parent, flags), m_fx(0) { m_tablePageSet = new QStackedWidget(this); m_tablePageSet->addWidget(new QWidget()); @@ -1103,6 +1126,8 @@ void ParamViewer::setFx(const TFxP ¤tFx, const TFxP &actualFx, int frame, if (name == "macroFx") { TMacroFx *macroFx = dynamic_cast(currentFx.getPointer()); if (macroFx) name = macroFx->getMacroFxType(); + } else { + name += std::to_string(actualFx->getFxVersion()); } int currentIndex = -1; @@ -1182,6 +1207,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 //----------------------------------------------------------------------------- @@ -1235,7 +1267,7 @@ FxSettings::FxSettings(QWidget *parent, const TPixel32 &checkCol1, //---signal-slot connections bool ret = true; ret = ret && connect(m_paramViewer, SIGNAL(currentFxParamChanged()), - SLOT(updateViewer())); + SLOT(updateViewer())); ret = ret && connect(m_viewer, SIGNAL(pointPositionChanged(int, const TPointD &)), SLOT(onPointChanged(int, const TPointD &))); @@ -1387,11 +1419,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/gutil.cpp b/toonz/sources/toonzqt/gutil.cpp index d0279c4e..0bd229ca 100644 --- a/toonz/sources/toonzqt/gutil.cpp +++ b/toonz/sources/toonzqt/gutil.cpp @@ -460,6 +460,72 @@ QIcon createQIcon(const char *iconSVGName, bool useFullOpacity, //----------------------------------------------------------------------------- +void addSpecifiedSizedImageToIcon(QIcon &icon, const char *iconSVGName, + QSize newSize) { + static int devPixRatio = getHighestDevicePixelRatio(); + newSize *= devPixRatio; + QIcon themeIcon = QIcon::fromTheme(iconSVGName); + // Pseudo state name strings + QString overStr = QString(iconSVGName) + "_over"; + QString onStr = QString(iconSVGName) + "_on"; + // Control lightness of the icons + const qreal activeOpacity = 1; + const qreal baseOpacity = 0.8; + const qreal disabledOpacity = 0.15; + //---------- + + // Base pixmap + QPixmap themeIconPixmap(recolorPixmap(themeIcon.pixmap(newSize))); + if (!themeIconPixmap.isNull()) { // suppress message + themeIconPixmap.setDevicePixelRatio(devPixRatio); + themeIconPixmap = themeIconPixmap.scaled(newSize, Qt::KeepAspectRatio, + Qt::SmoothTransformation); + } + + // Over pixmap + QPixmap overPixmap(recolorPixmap(QIcon::fromTheme(overStr).pixmap(newSize))); + if (!overPixmap.isNull()) { // suppress message + overPixmap.setDevicePixelRatio(devPixRatio); + overPixmap = overPixmap.scaled(newSize, Qt::KeepAspectRatio, + Qt::SmoothTransformation); + } + + // On pixmap + QPixmap onPixmap(recolorPixmap(QIcon::fromTheme(onStr).pixmap(newSize))); + if (!onPixmap.isNull()) { // suppress message + onPixmap.setDevicePixelRatio(devPixRatio); + onPixmap = + onPixmap.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + + // Base icon + icon.addPixmap(compositePixmap(themeIconPixmap, baseOpacity), QIcon::Normal, + QIcon::Off); + icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity), + QIcon::Disabled, QIcon::Off); + + // Over icon + icon.addPixmap(!overPixmap.isNull() + ? compositePixmap(overPixmap, activeOpacity) + : compositePixmap(themeIconPixmap, activeOpacity), + QIcon::Active); + + // On icon + if (!onPixmap.isNull()) { + icon.addPixmap(compositePixmap(onPixmap, activeOpacity), QIcon::Normal, + QIcon::On); + icon.addPixmap(compositePixmap(onPixmap, disabledOpacity), QIcon::Disabled, + QIcon::On); + } else { + icon.addPixmap(compositePixmap(themeIconPixmap, activeOpacity), + QIcon::Normal, QIcon::On); + icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity), + QIcon::Disabled, QIcon::On); + } +} + +//----------------------------------------------------------------------------- + QIcon createQIconPNG(const char *iconPNGName) { QString normal = QString(":Resources/") + iconPNGName + ".png"; QString click = QString(":Resources/") + iconPNGName + "_click.png"; @@ -644,7 +710,11 @@ bool isReservedFileName_message(const QString &fileName) { QString elideText(const QString &srcText, const QFont &font, int width) { QFontMetrics metrix(font); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int srcWidth = metrix.horizontalAdvance(srcText); +#else int srcWidth = metrix.width(srcText); +#endif if (srcWidth < width) return srcText; int tilde = metrix.width("~"); int block = (width - tilde) / 2; @@ -652,13 +722,21 @@ QString elideText(const QString &srcText, const QFont &font, int width) { int i; for (i = 0; i < srcText.size(); i++) { text += srcText.at(i); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + if (metrix.horizontalAdvance(text) > block) break; +#else if (metrix.width(text) > block) break; +#endif } text[i] = '~'; QString endText(""); for (i = srcText.size() - 1; i >= 0; i--) { endText.push_front(srcText.at(i)); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + if (metrix.horizontalAdvance(endText) > block) break; +#else if (metrix.width(endText) > block) break; +#endif } endText.remove(0, 1); text += endText; @@ -671,7 +749,11 @@ QString elideText(const QString &srcText, const QFontMetrics &fm, int width, const QString &elideSymbol) { QString text(srcText); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + for (int i = text.size(); i > 1 && fm.horizontalAdvance(text) > width;) +#else for (int i = text.size(); i > 1 && fm.width(text) > width;) +#endif text = srcText.left(--i).append(elideSymbol); return text; diff --git a/toonz/sources/toonzqt/icongenerator.cpp b/toonz/sources/toonzqt/icongenerator.cpp index 961adbad..1a88b627 100644 --- a/toonz/sources/toonzqt/icongenerator.cpp +++ b/toonz/sources/toonzqt/icongenerator.cpp @@ -1086,9 +1086,9 @@ Qt::transparent) bbox = (bbox * icon->getBounds()) .enlarge(-1); // Add a 1 pixel transparent margin - this - if (bbox.getLx() > 0 && - bbox.getLy() > 0) // way the actual content doesn't look trimmed. - ::makeChessBackground(icon->extract(bbox)); + //if (bbox.getLx() > 0 && + // bbox.getLy() > 0) // way the actual content doesn't look trimmed. + // ::makeChessBackground(icon->extract(bbox)); } else icon->fill(TPixel32(255, 0, 0)); diff --git a/toonz/sources/toonzqt/infoviewer.cpp b/toonz/sources/toonzqt/infoviewer.cpp index 09eb3d84..7c182aa5 100644 --- a/toonz/sources/toonzqt/infoviewer.cpp +++ b/toonz/sources/toonzqt/infoviewer.cpp @@ -68,6 +68,7 @@ public: eChannels, eSampleRate, eSampleSize, + eSampleType, eHowMany }; @@ -222,6 +223,7 @@ InfoViewerImp::InfoViewerImp() create(eChannels, QObject::tr("Channels: ")); create(eSampleRate, QObject::tr("Sample Rate: ")); create(eSampleSize, QObject::tr("Sample Size: ")); + create(eSampleType, QObject::tr("Sample Type: ")); m_historyLabel.setStyleSheet("color: rgb(0, 0, 200);"); m_history.setStyleSheet("font-size: 12px; font-family: \"courier\";"); @@ -267,7 +269,7 @@ QString InfoViewerImp::getTypeString() { return "Smart Raster Level"; else if (ext == "pli" || ext == "svg") return "Vector Level"; - else if (ext == "avi") + else if (ext == "mov" || ext == "avi" || ext == "3gp") return "Movie File"; else if (ext == "tnz") return "Tahoma2D Scene"; @@ -275,7 +277,8 @@ QString InfoViewerImp::getTypeString() { return "Tab Scene"; else if (ext == "plt") return "Tahoma2D Palette"; - else if (ext == "wav" || ext == "aiff" || ext == "mp3") + else if (ext == "wav" || ext == "aiff" || ext == "aif" || ext == "raw" || + ext == "mp3" || ext == "ogg" || ext == "flac") return "Audio File"; else if (ext == "mesh") return "Mesh Level"; @@ -299,7 +302,11 @@ void InfoViewerImp::setGeneralFileInfo(const TFilePath &path) { setVal(eFileType, getTypeString()); if (fi.owner() != "") setVal(eOwner, fi.owner()); setVal(eSize, fileSizeString(fi.size())); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + setVal(eCreated, fi.birthTime().toString()); +#else setVal(eCreated, fi.created().toString()); +#endif setVal(eModified, fi.lastModified().toString()); setVal(eLastAccess, fi.lastRead().toString()); m_separator1.show(); @@ -454,6 +461,22 @@ void InfoViewerImp::setSoundInfo() { label = QString::number(sndTrack->getBitPerSample()) + " bit"; setVal(eSampleSize, label); + + switch (sndTrack->getSampleType()) { + case TSound::INT: + label = "Signed integer"; + break; + case TSound::UINT: + label = "Unsigned integer"; + break; + case TSound::FLOAT: + label = "Floating-point"; + break; + default: + label = "Unknown"; + break; + } + setVal(eSampleType, label); } //---------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/intfield.cpp b/toonz/sources/toonzqt/intfield.cpp index 6ab0f27a..ce446283 100644 --- a/toonz/sources/toonzqt/intfield.cpp +++ b/toonz/sources/toonzqt/intfield.cpp @@ -128,7 +128,7 @@ void RollerField::removeValue(bool isDragging) { IntLineEdit::IntLineEdit(QWidget *parent, int value, int minValue, int maxValue, int showedDigits) : LineEdit(parent), m_showedDigits(showedDigits) { - setFixedWidth(54); + setFixedWidth(40); m_validator = new QIntValidator(this); setValue(value); diff --git a/toonz/sources/toonzqt/intpairfield.cpp b/toonz/sources/toonzqt/intpairfield.cpp index 3db0a979..95e3c76d 100644 --- a/toonz/sources/toonzqt/intpairfield.cpp +++ b/toonz/sources/toonzqt/intpairfield.cpp @@ -65,7 +65,7 @@ IntPairField::IntPairField(QWidget *parent, bool isMaxRangeLimited) bool ret = connect(m_leftLineEdit, SIGNAL(editingFinished()), SLOT(onLeftEditingFinished())); ret = ret && connect(m_rightLineEdit, SIGNAL(editingFinished()), - SLOT(onRightEditingFinished())); + SLOT(onRightEditingFinished())); assert(ret); } @@ -153,8 +153,13 @@ void IntPairField::paintEvent(QPaintEvent *) { void IntPairField::setLeftText(const QString &text) { QPoint pos = m_leftLabel->pos(); QString oldText = m_leftLabel->text(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int oldLabelSize = fontMetrics().horizontalAdvance(oldText); + int newLabelSize = fontMetrics().horizontalAdvance(text); +#else int oldLabelSize = fontMetrics().width(oldText); int newLabelSize = fontMetrics().width(text); +#endif int labelSize = newLabelSize - oldLabelSize; m_leftMargin += labelSize + MARGIN_OFFSET; m_leftLabel->setText(text); @@ -165,8 +170,13 @@ void IntPairField::setLeftText(const QString &text) { void IntPairField::setRightText(const QString &text) { QString oldText = m_rightLabel->text(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int oldLabelSize = fontMetrics().horizontalAdvance(oldText); + int newLabelSize = fontMetrics().horizontalAdvance(text); +#else int oldLabelSize = fontMetrics().width(oldText); int newLabelSize = fontMetrics().width(text); +#endif int labelSize = newLabelSize - oldLabelSize; m_rightMargin += labelSize + MARGIN_OFFSET; m_rightLabel->setText(text); diff --git a/toonz/sources/toonzqt/lutcalibrator.cpp b/toonz/sources/toonzqt/lutcalibrator.cpp index 8e877229..0ef89955 100644 --- a/toonz/sources/toonzqt/lutcalibrator.cpp +++ b/toonz/sources/toonzqt/lutcalibrator.cpp @@ -514,7 +514,11 @@ bool LutManager::loadLutFile(const QString& fp) { // The third line is corrections of values at each LUT grid line = locals::readDataLine(stream); - list = line.split(" ", QString::SkipEmptyParts); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + list = line.split(" ", Qt::SkipEmptyParts); +#else + list = line.split(" ", QString::SkipEmptyParts); +#endif if (list.size() != m_lut.meshSize) { file.close(); return execWarning(QObject::tr("Failed to Load 3DLUT File.")); @@ -530,7 +534,11 @@ bool LutManager::loadLutFile(const QString& fp) { for (int i = 0; i < m_lut.meshSize; ++i) // b { line = locals::readDataLine(stream); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + list = line.split(" ", Qt::SkipEmptyParts); +#else list = line.split(" ", QString::SkipEmptyParts); +#endif if (list.size() != 3) { file.close(); delete[] m_lut.data; @@ -567,8 +575,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]; // 陬憺俣逕ィ縺ョ・代・繧ッ繧サ繝ォ縺ョ鬆らせ濶イ for (int c = 0; c < 3; c++) { float val = rawVal[c] * (float)(m_lut.meshSize - 1); @@ -616,9 +627,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/menubarcommand.cpp b/toonz/sources/toonzqt/menubarcommand.cpp index ec2bf295..1dd71d65 100644 --- a/toonz/sources/toonzqt/menubarcommand.cpp +++ b/toonz/sources/toonzqt/menubarcommand.cpp @@ -113,8 +113,8 @@ void CommandManager::setShortcut(CommandId id, QAction *action, //--------------------------------------------------------- void CommandManager::define(CommandId id, CommandType type, - std::string defaultShortcutString, - QAction *qaction) { + std::string defaultShortcutString, QAction *qaction, + const char *iconSVGName) { assert(type != UndefinedCommandType); assert(qaction != 0); assert(m_qactionTable.count(qaction) == 0); @@ -131,6 +131,7 @@ void CommandManager::define(CommandId id, CommandType type, node->m_type == MiscCommandType || node->m_type == ToolModifierCommandType || node->m_type == CellMarkCommandType); + node->m_iconSVGName = iconSVGName; m_qactionTable[qaction] = node; qaction->setShortcutContext(Qt::ApplicationShortcut); @@ -399,6 +400,35 @@ std::string CommandManager::getIdFromAction(QAction *action) { return ""; } +//--------------------------------------------------------- + +const char *CommandManager::getIconSVGName(CommandId id) { + Node *node = getNode(id, false); + if (node) return node->m_iconSVGName; + return ""; +} + +void CommandManager::enlargeIcon(CommandId id, const QSize dstSize) { + QAction *action = getAction(id, false); + if (!action) return; + + const char *iconSVGName = getIconSVGName(id); + if (iconSVGName == "") return; + + QIcon icon = action->icon(); + + for (QList sizes = icon.availableSizes(); !sizes.isEmpty(); + sizes.removeFirst()) { + if (sizes.first().width() > dstSize.width() && + sizes.first().height() > dstSize.height()) + return; + } + + addSpecifiedSizedImageToIcon(icon, iconSVGName, dstSize); + + action->setIcon(icon); +} + //--------------------------------------------------------- // load user defined shortcuts @@ -497,8 +527,10 @@ void DVAction::onTriggered() { CommandManager::instance()->execute(this); } is helpful to use \b m_triggeredActionIndex to distingue action. */ DVMenuAction::DVMenuAction(const QString &text, QWidget *parent, - QList actions) - : QMenu(text, parent), m_triggeredActionIndex(-1) { + QList actions, bool isForRecentFiles) + : QMenu(text, parent) + , m_triggeredActionIndex(-1) + , m_isForRecentFiles(isForRecentFiles) { int i; for (i = 0; i < actions.size(); i++) addAction(actions.at(i)); connect(this, SIGNAL(triggered(QAction *)), this, @@ -549,6 +581,13 @@ void DVMenuAction::onTriggered(QAction *action) { QVariant data = action->data(); if (data.isValid()) m_triggeredActionIndex = data.toInt(); CommandManager::instance()->execute(action, menuAction()); + + // simply execute the menu item and return + if (!m_isForRecentFiles) { + m_triggeredActionIndex = -1; + return; + } + int oldIndex = m_triggeredActionIndex; if (m_triggeredActionIndex != -1) m_triggeredActionIndex = -1; QString str = data.toString(); diff --git a/toonz/sources/toonzqt/paletteviewer.cpp b/toonz/sources/toonzqt/paletteviewer.cpp index cd7a4546..2d7205f7 100644 --- a/toonz/sources/toonzqt/paletteviewer.cpp +++ b/toonz/sources/toonzqt/paletteviewer.cpp @@ -10,6 +10,7 @@ #include "toonzqt/dvscrollwidget.h" #include "toonzqt/studiopaletteviewer.h" #include "toonzqt/styleselection.h" +#include "toonzqt/stylenameeditor.h" #include "palettedata.h" // TnzLib includes @@ -106,6 +107,7 @@ PaletteViewer::PaletteViewer(QWidget *parent, PaletteViewType viewType, , m_frozen(false) , m_freezePaletteToolButton(0) , m_lockPaletteToolButton(0) + , m_styleNameEditor(nullptr) , m_app(0) { setObjectName("OnePixelMarginFrame"); setFrameStyle(QFrame::StyledPanel); @@ -308,6 +310,38 @@ void PaletteViewer::updateView() { //----------------------------------------------------------------------------- +void PaletteViewer::save(QSettings &settings, bool forPopupIni) const { + int visibleParts = m_toolbarVisibleOtherParts; + if (m_visibleKeysAction->isChecked()) visibleParts |= 0x01; +// if (m_visibleNewAction->isChecked()) visibleParts |= 0x02; + if (m_visibleGizmoAction->isChecked()) visibleParts |= 0x04; + if (m_visibleNameAction->isChecked()) visibleParts |= 0x08; + settings.setValue("toolbarVisibleMsk", visibleParts); + } + + void PaletteViewer::load(QSettings &settings) { + int visibleParts; + QVariant visibleVar = settings.value("toolbarVisibleMsk"); + if (visibleVar.canConvert(QVariant::Int)) { + visibleParts = visibleVar.toInt(); + } else { + visibleParts = 3; // Show keyframes and new style/page + } + + m_visibleKeysAction->setChecked(visibleParts & 0x01); +// m_visibleNewAction->setChecked(visibleParts & 0x02); + m_visibleGizmoAction->setChecked(visibleParts & 0x04); + m_visibleNameAction->setChecked(visibleParts & 0x08); + m_toolbarVisibleOtherParts = visibleParts & ~0x0F; // Reserve + + applyToolbarPartVisibility(TBVisKeyframe, visibleParts & 0x01); +// applyToolbarPartVisibility(TBVisNewStylePage, visibleParts & 0x02); + applyToolbarPartVisibility(TBVisPaletteGizmo, visibleParts & 0x04); + applyToolbarPartVisibility(TBVisNameEditor, visibleParts & 0x08); + } + +//----------------------------------------------------------------------------- + void PaletteViewer::enableSaveAction(bool enable) { if (!m_savePaletteToolBar) return; QList actions; @@ -327,6 +361,33 @@ void PaletteViewer::enableSaveAction(bool enable) { } } +//----------------------------------------------------------------------------- + +void PaletteViewer::applyToolbarPartVisibility(int part, bool visible) { + assert(m_toolbarParts.contains(part)); + for (QAction *action : m_toolbarParts.values(part)) { + action->setVisible(visible); + } +} + +//----------------------------------------------------------------------------- + +void PaletteViewer::toggleKeyframeVisibility(bool checked) { + applyToolbarPartVisibility(TBVisKeyframe, checked); +} + +//void PaletteViewer::toggleNewStylePageVisibility(bool checked) { +// applyToolbarPartVisibility(TBVisNewStylePage, checked); +//} + +void PaletteViewer::togglePaletteGizmoVisibility(bool checked) { + applyToolbarPartVisibility(TBVisPaletteGizmo, checked); +} + +void PaletteViewer::toggleNameEditorVisibility(bool checked) { + applyToolbarPartVisibility(TBVisNameEditor, checked); +} + //----------------------------------------------------------------------------- /*! Create tab bar to select palette page. */ @@ -350,6 +411,9 @@ void PaletteViewer::createPaletteToolBar() { m_paletteToolBar->setIconSize(QSize(20, 20)); m_paletteToolBar->setLayoutDirection(Qt::LeftToRight); + m_toolbarParts.clear(); + + // m_toolbarParts.insert(TBVisNewStylePage, addPage); // QIcon newColorIcon = createQIcon("newstyle"); // QAction* addColor = // new QAction(newColorIcon, tr("&New Style"), m_paletteToolBar); @@ -358,12 +422,14 @@ void PaletteViewer::createPaletteToolBar() { // m_paletteToolBar->addAction(addColor); // m_paletteToolBar->addSeparator(); + // m_toolbarParts.insert(TBVisNewStylePage, addColor); + // m_toolbarParts.insert(TBVisNewStylePage, m_paletteToolBar->addSeparator()); // KeyFrame button if (m_viewType != CLEANUP_PALETTE) { m_keyFrameButton = new PaletteKeyframeNavigator(m_paletteToolBar); - m_paletteToolBar->addWidget(m_keyFrameButton); - m_paletteToolBar->addSeparator(); + m_toolbarParts.insert(TBVisKeyframe, m_paletteToolBar->addWidget(m_keyFrameButton)); + m_toolbarParts.insert(TBVisKeyframe, m_paletteToolBar->addSeparator()); m_keyFrameButton->setSelection(m_pageViewer->getSelection()); } @@ -393,6 +459,17 @@ void PaletteViewer::createPaletteToolBar() { m_paletteToolBar->setStyleSheet("QToolBar{spacing:3px;}"); m_paletteToolBar->addWidget(m_freezePaletteToolButton); + m_paletteToolBar->addSeparator(); + CommandManager *cmd = CommandManager::instance(); + m_sharedGizmoAction = cmd->getAction("MI_OpenPltGizmo"); + + // Clone palette gizmo action so visibility can be control + QAction *palGizmo = new DVAction(m_sharedGizmoAction->icon(), + m_sharedGizmoAction->text(), this); + connect(palGizmo, &QAction::triggered, + [&]() { m_sharedGizmoAction->trigger(); }); + m_paletteToolBar->addAction(palGizmo); + m_toolbarParts.insert(TBVisPaletteGizmo, palGizmo); } else if (m_viewType == STUDIO_PALETTE) { QToolButton *toolButton = new QToolButton(this); toolButton->setPopupMode(QToolButton::InstantPopup); @@ -416,8 +493,23 @@ void PaletteViewer::createPaletteToolBar() { SLOT(setChecked(bool))); m_paletteToolBar->addWidget(toolButton); + m_paletteToolBar->addSeparator(); } + QAction *openStyleNameEditorAct = new QAction(tr("Name Editor")); + openStyleNameEditorAct->setIcon(createQIcon("rename", false, true)); + connect(openStyleNameEditorAct, &QAction::triggered, [&]() { + if (!m_styleNameEditor) { + m_styleNameEditor = new StyleNameEditor(this); + m_styleNameEditor->setPaletteHandle(getPaletteHandle()); + } + m_styleNameEditor->show(); + m_styleNameEditor->raise(); + m_styleNameEditor->activateWindow(); + }); + m_paletteToolBar->addAction(openStyleNameEditorAct); + m_toolbarParts.insert(TBVisNameEditor, openStyleNameEditorAct); + updatePaletteToolBar(); } @@ -506,8 +598,44 @@ void PaletteViewer::createSavePaletteToolBar() { }); m_viewMode->addAction(m_showStyleIndex); + m_viewMode->addSeparator(); + + // Add ability to show or hide buttons + QMenu *visibleButtons = new QMenu(tr("Visible Toolbar Buttons")); + + m_visibleKeysAction = new QAction(tr("KeyFrame")); + m_visibleKeysAction->setCheckable(true); + m_visibleKeysAction->setChecked(true); + visibleButtons->addAction(m_visibleKeysAction); +// m_visibleNewAction = new QAction(tr("New Style/Page")); +// m_visibleNewAction->setCheckable(true); +// m_visibleNewAction->setChecked(true); +// visibleButtons->addAction(m_visibleNewAction); + m_visibleGizmoAction = new QAction(tr("Palette Gizmo")); + m_visibleGizmoAction->setCheckable(true); + m_visibleGizmoAction->setChecked(true); + visibleButtons->addAction(m_visibleGizmoAction); + m_visibleNameAction = new QAction(tr("Name Editor")); + m_visibleNameAction->setCheckable(true); + m_visibleNameAction->setChecked(true); + visibleButtons->addAction(m_visibleNameAction); + m_viewMode->addMenu(visibleButtons); + + if (m_viewType == CLEANUP_PALETTE) m_visibleKeysAction->setVisible(false); + + if (m_viewType != LEVEL_PALETTE) m_visibleGizmoAction->setVisible(false); + + connect(m_visibleKeysAction, SIGNAL(toggled(bool)), this, + SLOT(toggleKeyframeVisibility(bool))); +// connect(m_visibleNewAction, SIGNAL(toggled(bool)), this, +// SLOT(toggleNewStylePageVisibility(bool))); + connect(m_visibleGizmoAction, SIGNAL(toggled(bool)), this, + SLOT(togglePaletteGizmoVisibility(bool))); + connect(m_visibleNameAction, SIGNAL(toggled(bool)), this, + SLOT(toggleNameEditorVisibility(bool))); + if (m_viewType == STUDIO_PALETTE) { - QIcon savePaletteIcon = createQIcon("save"); + QIcon savePaletteIcon = createQIcon("save", false, true); QAction *savePalette = new QAction(savePaletteIcon, tr("&Save Palette"), m_savePaletteToolBar); savePalette->setToolTip(tr("Save the palette.")); @@ -522,12 +650,14 @@ void PaletteViewer::createSavePaletteToolBar() { // overwrite palette QAction *savePalette = - CommandManager::instance()->getAction("MI_OverwritePalette"); + new QAction(createQIcon("save", false, true), tr("&Save Palette"), + m_savePaletteToolBar); m_viewMode->addAction(savePalette); // save palette as QAction *saveAsPalette = - CommandManager::instance()->getAction("MI_SavePaletteAs"); + new QAction(createQIcon("saveas", false, true), tr("&Save Palette As"), + m_savePaletteToolBar); m_viewMode->addAction(saveAsPalette); // save as default palette @@ -740,7 +870,8 @@ void PaletteViewer::contextMenuEvent(QContextMenuEvent *event) { QMenu *menu = new QMenu(this); if (m_hasPageCommand) { - QAction *newPage = menu->addAction(tr("New Page")); + QAction *newPage = + menu->addAction(createQIcon("newpage", false, true), tr("New Page")); connect(newPage, SIGNAL(triggered()), SLOT(addNewPage())); if (m_pagesBar->geometry().contains(pos)) { @@ -752,7 +883,8 @@ void PaletteViewer::contextMenuEvent(QContextMenuEvent *event) { canRemovePage = false; if (canRemovePage) { m_indexPageToDelete = tabIndex; - QAction *deletePage = menu->addAction(tr("Delete Page")); + QAction *deletePage = menu->addAction( + createQIcon("delete", false, true), tr("Delete Page")); connect(deletePage, SIGNAL(triggered()), SLOT(deletePage())); } } diff --git a/toonz/sources/toonzqt/paletteviewergui.cpp b/toonz/sources/toonzqt/paletteviewergui.cpp index 228645cd..22475870 100644 --- a/toonz/sources/toonzqt/paletteviewergui.cpp +++ b/toonz/sources/toonzqt/paletteviewergui.cpp @@ -343,7 +343,7 @@ void PageViewer::drop(int dstIndexInPage, const QMimeData *mimeData) { int dstPageIndex = m_page->getIndex(); if ((m_page->getStyleId(0) == 0 || m_page->getStyleId(1) == 1) && dstIndexInPage < 2) - dstIndexInPage = 2; + dstIndexInPage = 2; if (dstIndexInPage < 0) dstIndexInPage = m_page->getStyleCount(); const PaletteData *paletteData = dynamic_cast(mimeData); @@ -493,7 +493,11 @@ void PageViewer::drawColorName(QPainter &p, QRect &nameRect, TColorStyle *style, QRect rect = nameRect; QPen oldPen = p.pen(); TPixel32 color = style->getMainColor(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int textWidth = QFontMetrics(p.font()).horizontalAdvance(name); +#else int textWidth = QFontMetrics(p.font()).width(name); +#endif p.setPen(Qt::black); if (textWidth < rect.width() - 2) p.drawText(rect.adjusted(1,1,1,1), Qt::AlignCenter, name); @@ -511,15 +515,15 @@ void PageViewer::drawColorName(QPainter &p, QRect &nameRect, TColorStyle *style, if (m_viewMode == LargeChips) { p.setPen(Qt::black); - QString index = QString::number(styleIndex); - QFont font = p.font(); - int fontSize = font.pointSize(); + QString index = QString::number(styleIndex); + QFont font = p.font(); + int fontSize = font.pointSize(); if (fontSize == -1) fontSize = font.pixelSize(); - int length = index.length() * fontSize; - int w = (length > 11) ? (length) : 11; - int h = 11; - int x0 = nameRect.right() - w + 1; - int y0 = nameRect.top() - h - 1; + int length = index.length() * fontSize; + int w = (length > 11) ? (length) : 11; + int h = 11; + int x0 = nameRect.right() - w + 1; + int y0 = nameRect.top() - h - 1; p.drawText(nameRect.adjusted(6, 1, -6, -1), name); QRect indexRect(x0, y0, w, h); p.fillRect(indexRect, QBrush(Qt::white)); @@ -615,10 +619,10 @@ void PageViewer::paintEvent(QPaintEvent *e) { if (!palette) return; // [i0,i1] = visible cell range - QRect visibleRect = e->rect(); - int i0 = posToIndex(visibleRect.topLeft()); - if (i0 < 0) i0 = 0; - int i1 = posToIndex(visibleRect.bottomRight()); + QRect visibleRect = e->rect(); + int i0 = posToIndex(visibleRect.topLeft()); + if (i0 < 0) i0 = 0; + int i1 = posToIndex(visibleRect.bottomRight()); if (i1 >= getChipCount()) i1 = getChipCount() - 1; QFont preFont = p.font(); @@ -726,12 +730,16 @@ void PageViewer::paintEvent(QPaintEvent *e) { } // draw frame if the style is selected or current - if (m_styleSelection->isSelected(m_page->getIndex(), i) || - currentStyleIndex == styleIndex) { + if (m_styleSelection->isSelected(m_page->getIndex(), i)) { QRect itemRect = getItemRect(i).adjusted(0, -1, 0, 1); p.setPen(Qt::NoPen); p.setBrush(getSelectedBorderColor()); - p.drawRoundRect(itemRect, 7, 25); + p.drawRoundedRect(itemRect, 7, 25, Qt::RelativeSize); + } else if (currentStyleIndex == styleIndex) { + QRect itemRect = getItemRect(i).adjusted(1, 0, -1, 0); + p.setPen(Qt::NoPen); + p.setBrush(getSelectedBorderColor()); + p.drawRoundedRect(itemRect, 7, 25, Qt::RelativeSize); } // paint style QRect chipRect = getItemRect(i).adjusted(4, 4, -5, -5); @@ -865,7 +873,12 @@ void PageViewer::paintEvent(QPaintEvent *e) { tmpFont.setItalic(false); p.setFont(tmpFont); if (ShowStyleIndex == 1) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int indexWidth = + fontMetrics().horizontalAdvance(QString().setNum(styleIndex)) + 4; +#else int indexWidth = fontMetrics().width(QString().setNum(styleIndex)) + 4; +#endif QRect indexRect(chipRect.bottomRight() + QPoint(-indexWidth, -14), chipRect.bottomRight()); p.setPen(Qt::black); @@ -1172,11 +1185,16 @@ void PageViewer::contextMenuEvent(QContextMenuEvent *event) { menu.addAction(clearAct); menu.addSeparator(); - QAction *openPltGizmoAct = cmd->getAction("MI_OpenPltGizmo"); - menu.addAction(openPltGizmoAct); + // currently palette gizmo can only change colors from the current level + // palette due to the way modifyColor works. + if (m_viewType == LEVEL_PALETTE) { + QAction *openPltGizmoAct = cmd->getAction("MI_OpenPltGizmo"); + menu.addAction(openPltGizmoAct); + } QAction *openStyleControlAct = cmd->getAction("MI_OpenStyleControl"); menu.addAction(openStyleControlAct); QAction *openStyleNameEditorAct = menu.addAction(tr("Name Editor")); + openStyleNameEditorAct->setIcon(createQIcon("rename", false, true)); connect(openStyleNameEditorAct, &QAction::triggered, [&]() { if (!m_styleNameEditor) { m_styleNameEditor = new StyleNameEditor(this); @@ -1230,9 +1248,11 @@ void PageViewer::contextMenuEvent(QContextMenuEvent *event) { if (m_page) { menu.addSeparator(); - QAction *newStyle = menu.addAction(tr("New Style")); + QIcon newStyleIco = createQIcon("newstyle", false, true); + QAction *newStyle = menu.addAction(newStyleIco, tr("New Style")); connect(newStyle, SIGNAL(triggered()), SLOT(addNewColor())); - QAction *newPage = menu.addAction(tr("New Page")); + QIcon newPageIco = createQIcon("newpage", false, true); + QAction *newPage = menu.addAction(newPageIco, tr("New Page")); connect(newPage, SIGNAL(triggered()), SLOT(addNewPage())); } @@ -1262,7 +1282,7 @@ void PageViewer::dragEnterEvent(QDragEnterEvent *event) { if (index < 0) index = 0; else if (index > m_page->getStyleCount()) - index = m_page->getStyleCount(); + index = m_page->getStyleCount(); m_dropPositionIndex = index; update(); event->acceptProposedAction(); @@ -1282,7 +1302,7 @@ void PageViewer::dragMoveEvent(QDragMoveEvent *event) { if (index < 0) index = 0; else if (index > m_page->getStyleCount()) - index = m_page->getStyleCount(); + index = m_page->getStyleCount(); m_dropPositionIndex = index; update(); event->acceptProposedAction(); @@ -1358,9 +1378,8 @@ void PageViewer::keyPressEvent(QKeyEvent *e) { if (key == cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ZoomIn))) zoomInChip(); - else if (key == - cManager->getKeyFromShortcut( - cManager->getShortcutFromId(V_ZoomOut))) + else if (key == cManager->getKeyFromShortcut( + cManager->getShortcutFromId(V_ZoomOut))) zoomOutChip(); else e->ignore(); @@ -1478,8 +1497,8 @@ void PageViewer::select(int indexInPage, QMouseEvent *event) { } bool isStyleChanged = false; - if (on) selected = true; - int styleIndex = m_page->getStyleId(indexInPage); + if (on) selected = true; + int styleIndex = m_page->getStyleId(indexInPage); if (selected) { setCurrentStyleIndex(styleIndex); @@ -1695,11 +1714,7 @@ void PaletteTabBar::updateTabName() { \brief PaletteIconWidget class provides a widget to show a palette icon. */ -#if QT_VERSION >= 0x050500 PaletteIconWidget::PaletteIconWidget(QWidget *parent, Qt::WindowFlags flags) -#else -PaletteIconWidget::PaletteIconWidget(QWidget *parent, Qt::WFlags flags) -#endif : QWidget(parent, flags), m_isOver(false) { setFixedSize(30, 20); setToolTip(QObject::tr("Click & Drag Palette into Studio Palette")); diff --git a/toonz/sources/toonzqt/paramfield.cpp b/toonz/sources/toonzqt/paramfield.cpp index 7851d675..d2c9b609 100644 --- a/toonz/sources/toonzqt/paramfield.cpp +++ b/toonz/sources/toonzqt/paramfield.cpp @@ -1321,7 +1321,7 @@ ModeSensitiveBox::ModeSensitiveBox(QWidget *parent, QCheckBox *checkBox) //----------------------------------------------------------------------------- void ModeSensitiveBox::onModeChanged(int modeValue) { - bool wasVisible = isVisible(); + bool wasVisible = isVisibleTo(parentWidget()); m_currentMode = modeValue; if (wasVisible == m_modes.contains(modeValue)) return; setVisible(!wasVisible); @@ -1387,7 +1387,6 @@ void EnumParamField::onChange(const QString &str) { emit currentParamChanged(); emit actualParamChanged(); - emit modeChanged(m_actualParam->getValue()); if (undo) TUndoManager::manager()->add(undo); @@ -1421,6 +1420,10 @@ void EnumParamField::update(int frame) { } } +//----------------------------------------------------------------------------- + +int EnumParamField::getValue() const { return m_actualParam->getValue(); } + //============================================================================= // BoolParamField //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/pickrgbutils.cpp b/toonz/sources/toonzqt/pickrgbutils.cpp index f875270f..c3563563 100644 --- a/toonz/sources/toonzqt/pickrgbutils.cpp +++ b/toonz/sources/toonzqt/pickrgbutils.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "toonzqt/pickrgbutils.h" @@ -43,7 +44,7 @@ QRgb meanColor(const QImage &img, const QRect &rect) { return r | (g << 8) | (b << 16) | (m << 24); } -} +} // namespace //============================================================================== @@ -84,9 +85,16 @@ QRgb pickScreenRGB(const QRect &rect) { #endif +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QImage img(widget->screen() + ->grabWindow(widget->winId(), theRect.x(), theRect.y(), + theRect.width(), theRect.height()) + .toImage()); +#else QImage img(QPixmap::grabWindow(widget->winId(), theRect.x(), theRect.y(), theRect.width(), theRect.height()) .toImage()); +#endif return meanColor( img, QRect(rect.left() - theRect.left(), rect.top() - theRect.top(), rect.width(), rect.height())); diff --git a/toonz/sources/toonzqt/planeviewer.cpp b/toonz/sources/toonzqt/planeviewer.cpp index 11f247e6..9fc50beb 100644 --- a/toonz/sources/toonzqt/planeviewer.cpp +++ b/toonz/sources/toonzqt/planeviewer.cpp @@ -29,7 +29,7 @@ #include #include -//#define PRINT_AFF +// #define PRINT_AFF //********************************************************************************** // Local namespace stuff @@ -50,8 +50,9 @@ private: bool PlaneViewerZoomer::zoom(bool zoomin, bool resetZoom) { PlaneViewer &planeViewer = static_cast(*getWidget()); - resetZoom ? planeViewer.resetView() : zoomin ? planeViewer.zoomIn() - : planeViewer.zoomOut(); + resetZoom ? planeViewer.resetView() + : zoomin ? planeViewer.zoomIn() + : planeViewer.zoomOut(); return true; } @@ -189,7 +190,7 @@ void PlaneViewer::mouseMoveEvent(QMouseEvent *event) { } QPoint curPos = event->pos() * getDevPixRatio(); - if (event->buttons() & Qt::MidButton) + if (event->buttons() & Qt::MiddleButton) moveView(curPos.x() - m_xpos, height() - curPos.y() - m_ypos); m_xpos = curPos.x(), m_ypos = height() - curPos.y(); @@ -255,12 +256,12 @@ void PlaneViewer::wheelEvent(QWheelEvent *event) { default: // Qt::MouseEventSynthesizedByQt, // Qt::MouseEventSynthesizedByApplication - { - std::cout << "not supported event: Qt::MouseEventSynthesizedByQt, " - "Qt::MouseEventSynthesizedByApplication" - << std::endl; - break; - } + { + std::cout << "not supported event: Qt::MouseEventSynthesizedByQt, " + "Qt::MouseEventSynthesizedByApplication" + << std::endl; + break; + } } // end switch @@ -268,9 +269,14 @@ void PlaneViewer::wheelEvent(QWheelEvent *event) { if ((m_gestureActive == true && m_touchDevice == QTouchDevice::TouchScreen) || m_gestureActive == false) { - TPointD pos(event->x() * getDevPixRatio(), - height() - event->y() * getDevPixRatio()); - double zoom_par = 1 + event->delta() * 0.001; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + TPointD pos(event->position().x() * getDevPixRatio(), + height() - event->position().y() * getDevPixRatio()); +#else + TPointD pos(event->pos().x() * getDevPixRatio(), + height() - event->pos().y() * getDevPixRatio()); +#endif + double zoom_par = 1 + event->angleDelta().y() * 0.001; zoomView(pos.x, pos.y, zoom_par); } @@ -469,10 +475,9 @@ bool PlaneViewer::event(QEvent *e) { } */ - if (e->type() == QEvent::Gesture && - CommandManager::instance() - ->getAction(MI_TouchGestureControl) - ->isChecked()) { + if (e->type() == QEvent::Gesture && CommandManager::instance() + ->getAction(MI_TouchGestureControl) + ->isChecked()) { gestureEvent(static_cast(e)); return true; } diff --git a/toonz/sources/toonzqt/pluginhost.cpp b/toonz/sources/toonzqt/pluginhost.cpp index 3e99d203..481615ce 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_; } @@ -1565,11 +1565,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 - に接続する. + (縺ゅ∪繧翫h縺上・縺ェ縺・′)listener 縺ョ譛臥┌縺ォ繧医▲縺ヲ receiver + 繧貞・縺代k繧医≧縺ォ縺励※縺・k. listener 縺後>繧句エ蜷医・蠕捺擂騾壹j context menu + 縺ョ讒狗ッ峨・縺溘a縺ォ AddFxContextMenu::fixup() 縺ォ謗・邯壹☆繧九′ 縺昴l莉・螟悶〒縺ッ + plugin_dict_ 縺ク縺ョ霑ス蜉縺ョ縺溘a PluginLoadController::finished 縺ォ謗・邯壹☆繧・ */ if (listener) { AddFxContextMenu *a = qobject_cast(listener); @@ -1624,4 +1623,4 @@ static bool copy_rendering_setting(toonz_rendering_setting_t *dst, return true; } -//#include "pluginhost.moc" +// #include "pluginhost.moc" diff --git a/toonz/sources/toonzqt/schematicgroupeditor.cpp b/toonz/sources/toonzqt/schematicgroupeditor.cpp index d2f3fffe..a2777394 100644 --- a/toonz/sources/toonzqt/schematicgroupeditor.cpp +++ b/toonz/sources/toonzqt/schematicgroupeditor.cpp @@ -24,19 +24,13 @@ SchematicWindowEditor::SchematicWindowEditor( const QList &groupedNode, SchematicScene *scene) -#if QT_VERSION >= 0x050000 : QGraphicsItem() -#else - : QGraphicsItem(0, scene) -#endif , m_groupedNode(groupedNode) , m_scene(scene) , m_lastPos() , m_button(Qt::NoButton) , m_isMacroEditor(false) { -#if QT_VERSION >= 0x050000 scene->addItem(this); -#endif m_nameItem = new SchematicName(this, 67, 14); m_nameItem->setPos(-2, -2); m_nameItem->setZValue(1); @@ -266,7 +260,8 @@ void FxSchematicGroupEditor::onNameChanged() { setFlag(QGraphicsItem::ItemIsSelectable, true); FxSchematicScene *fxScene = dynamic_cast(scene()); if (!fxScene) return; - TFxCommand::renameGroup(fxs.toStdList(), m_groupName.toStdWString(), true, + TFxCommand::renameGroup(std::list(fxs.begin(), fxs.end()), + m_groupName.toStdWString(), true, fxScene->getXsheetHandle()); update(); } @@ -297,11 +292,7 @@ QRectF FxSchematicGroupEditor::boundingSceneRect() const { int factor = k * 30; app.adjust(-factor, -factor, factor, factor); } -#if QT_VERSION >= 0x050000 rect = rect.united(app); -#else - rect = rect.unite(app); -#endif } rect.adjust(-20, -35, 0, 20); return rect; @@ -387,11 +378,7 @@ QRectF FxSchematicMacroEditor::boundingSceneRect() const { QPointF shiftAppPos(node->scenePos().x() - app.left(), node->scenePos().y() + app.top() + 10); app.moveTopLeft(shiftAppPos); -#if QT_VERSION >= 0x050000 rect = rect.united(app); -#else - rect = rect.unite(app); -#endif } rect.adjust(-20, -35, 0, 20); return rect; @@ -467,11 +454,7 @@ QRectF StageSchematicGroupEditor::boundingSceneRect() const { app.moveTopLeft(shiftAppPos); bool isASubgroupedNode = obj->getEditingGroupId() != m_groupId; if (isASubgroupedNode) app.adjust(-30, -30, 30, 30); -#if QT_VERSION >= 0x050000 rect = rect.united(app); -#else - rect = rect.unite(app); -#endif } rect.adjust(-20, -35, 0, 0); return rect; diff --git a/toonz/sources/toonzqt/schematicnode.cpp b/toonz/sources/toonzqt/schematicnode.cpp index 2e58edeb..74f4e57e 100644 --- a/toonz/sources/toonzqt/schematicnode.cpp +++ b/toonz/sources/toonzqt/schematicnode.cpp @@ -313,7 +313,7 @@ void SchematicThumbnailToggle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QRect rect(3, 3, 8, 8); - QRect sourceRect = scene()->views()[0]->matrix().mapRect(rect); + QRect sourceRect = scene()->views()[0]->transform().mapRect(rect); static QIcon onIcon(":Resources/schematic_thumbtoggle_on.svg"); static QIcon offIcon(":Resources/schematic_thumbtoggle_off.svg"); QPixmap pixmap; @@ -437,7 +437,7 @@ void SchematicToggle::paint(QPainter *painter, (m_state == 2 && !m_imageOn2.isNull()) ? m_imageOn2 : m_imageOn; painter->fillRect(boundingRect().toRect(), m_colorOn); QRect sourceRect = - scene()->views()[0]->matrix().mapRect(QRect(0, 0, 18, 17)); + scene()->views()[0]->transform().mapRect(QRect(0, 0, 18, 17)); QPixmap redPm = pix.pixmap(sourceRect.size()); painter->drawPixmap(rect, redPm); } else if (!m_imageOff.isNull()) { @@ -448,7 +448,7 @@ void SchematicToggle::paint(QPainter *painter, double d = pen.widthF() / 2.0; painter->drawRect(boundingRect().adjusted(d, d, -d, -d)); QRect sourceRect = - scene()->views()[0]->matrix().mapRect(QRect(0, 0, 18, 17)); + scene()->views()[0]->transform().mapRect(QRect(0, 0, 18, 17)); QPixmap redPm = m_imageOff.pixmap(sourceRect.size()); painter->drawPixmap(rect, redPm); } @@ -513,7 +513,7 @@ void SchematicToggle_SplineOptions::paint( if (m_state != 0) { QIcon &pix = (m_state == 2 && !m_imageOn2.isNull()) ? m_imageOn2 : m_imageOn; - QRect sourceRect = scene()->views()[0]->matrix().mapRect(rect.toRect()); + QRect sourceRect = scene()->views()[0]->transform().mapRect(rect.toRect()); QPixmap redPm = pix.pixmap(sourceRect.size()); painter->drawPixmap(rect.toRect(), redPm); } @@ -608,20 +608,14 @@ void SchematicHandleSpinBox::mouseReleaseEvent(QGraphicsSceneMouseEvent *me) { //======================================================== SchematicLink::SchematicLink(QGraphicsItem *parent, QGraphicsScene *scene) -#if QT_VERSION >= 0x050000 : QGraphicsItem(parent) -#else - : QGraphicsItem(parent, scene) -#endif , m_startPort(0) , m_endPort(0) , m_path() , m_hitPath() , m_lineShaped(false) , m_highlighted(false) { -#if QT_VERSION >= 0x050000 scene->addItem(this); -#endif setFlag(QGraphicsItem::ItemIsMovable, false); setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemIsFocusable, false); @@ -769,12 +763,8 @@ void SchematicLink::mousePressEvent(QGraphicsSceneMouseEvent *me) { } } - QMatrix matrix = scene()->views()[0]->matrix(); -#if QT_VERSION >= 0x050000 - double scaleFactor = sqrt(matrix.determinant()); -#else - double scaleFactor = sqrt(matrix.det()); -#endif + QTransform transform = scene()->views()[0]->transform(); + double scaleFactor = sqrt(transform.determinant()); QPointF startPos = getStartPort()->getLinkEndPoint(); QPointF endPos = getEndPort()->getLinkEndPoint(); @@ -832,11 +822,7 @@ SchematicPort::SchematicPort(QGraphicsItem *parent, SchematicNode *node, , m_linkingTo(0) , m_hook(0, 0) , m_type(type) { -#if QT_VERSION >= 0x050000 setAcceptHoverEvents(false); -#else - setAcceptsHoverEvents(false); -#endif setFlag(QGraphicsItem::ItemIsSelectable, false); setFlag(QGraphicsItem::ItemIsFocusable, false); } @@ -1072,18 +1058,12 @@ QPointF SchematicPort::getLinkEndPoint() const { return scenePos() + m_hook; } */ SchematicNode::SchematicNode(SchematicScene *scene) -#if QT_VERSION >= 0x050000 : QGraphicsItem(0) -#else - : QGraphicsItem(0, scene) -#endif , m_scene(scene) , m_width(0) , m_height(0) , m_buttonState(Qt::NoButton) { -#if QT_VERSION >= 0x050000 scene->addItem(this); -#endif setFlag(QGraphicsItem::ItemIsMovable, false); setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemIsFocusable, false); @@ -1141,7 +1121,25 @@ void SchematicNode::paint(QPainter *painter, void SchematicNode::mouseMoveEvent(QGraphicsSceneMouseEvent *me) { QList items = scene()->selectedItems(); if (items.empty()) return; - QPointF delta = me->scenePos() - me->lastScenePos(); + QPointF delta = me->scenePos() - m_scene->mousePos(); + // QPointF delta = me->scenePos() - me->lastScenePos(); + + if (me->modifiers() & Qt::ShiftModifier) { + QPointF deltaFromStart = me->scenePos() - m_scene->clickedPos(); + if (std::abs(deltaFromStart.x()) > std::abs(deltaFromStart.y())) + deltaFromStart.setY(0.0); + else + deltaFromStart.setX(0.0); + delta = m_scene->clickedPos() + deltaFromStart - m_scene->mousePos(); + } + + // snap to neighbor nodes + bool ctrlPressed = me->modifiers() & Qt::ControlModifier; + dynamic_cast(scene())->computeSnap(this, delta, + ctrlPressed); + + m_scene->setMousePos(m_scene->mousePos() + delta); + QGraphicsView *viewer = scene()->views()[0]; for (auto const &item : items) { SchematicNode *node = dynamic_cast(item); @@ -1169,6 +1167,11 @@ void SchematicNode::mousePressEvent(QGraphicsSceneMouseEvent *me) { setSelected(false); } onClicked(); + + m_scene->setMousePos(me->scenePos()); + m_scene->setClickedPos(me->scenePos()); + + m_scene->updateSnapTarget(this); } //-------------------------------------------------------- @@ -1176,6 +1179,8 @@ void SchematicNode::mousePressEvent(QGraphicsSceneMouseEvent *me) { void SchematicNode::mouseReleaseEvent(QGraphicsSceneMouseEvent *me) { if (me->modifiers() != Qt::ControlModifier && me->button() != Qt::RightButton) QGraphicsItem::mouseReleaseEvent(me); + + m_scene->updateSnapTarget(nullptr); } //-------------------------------------------------------- @@ -1239,3 +1244,46 @@ void SchematicNode::updateLinksGeometry() { for (it = m_ports.begin(); it != m_ports.end(); ++it) it.value()->updateLinksGeometry(); } + +//======================================================== +// +// class SnapTargetItem +// +//======================================================== + +SnapTargetItem::SnapTargetItem(const QPointF &pos, const QRectF &rect, + const QPointF &theOtherEndPos, + const QPointF &portEndOffset) + : m_rect(rect) + , m_theOtherEndPos(theOtherEndPos) + , m_portEndOffset(portEndOffset) { + setFlag(QGraphicsItem::ItemIsMovable, false); + setFlag(QGraphicsItem::ItemIsSelectable, false); + setFlag(QGraphicsItem::ItemIsFocusable, false); + setZValue(3.0); + setPos(pos); + setVisible(false); +} + +QRectF SnapTargetItem::boundingRect() const { + QRectF linkRect(m_theOtherEndPos - scenePos(), m_portEndOffset); + return m_rect.united(linkRect); +} + +void SnapTargetItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) { + painter->setPen(QPen(Qt::magenta, 1, Qt::DashDotLine)); + painter->drawRect(m_rect); + + QPointF startPos = m_theOtherEndPos - scenePos(); + QPointF endPos = m_portEndOffset; + QPointF p0((endPos.x() + startPos.x()) * 0.5, startPos.y()); + QPointF p1(p0.x(), endPos.y()); + QPointF p2(endPos); + + QPainterPath path(startPos); + path.cubicTo(p0, p1, p2); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->drawPath(path); +} \ No newline at end of file diff --git a/toonz/sources/toonzqt/schematicviewer.cpp b/toonz/sources/toonzqt/schematicviewer.cpp index 4260a83a..2c9d1da3 100644 --- a/toonz/sources/toonzqt/schematicviewer.cpp +++ b/toonz/sources/toonzqt/schematicviewer.cpp @@ -76,8 +76,12 @@ public: } }; +const int snapDistance = 15; } // namespace +int SchematicScene::snapHSpacing = 50; +int SchematicScene::snapVInterval = 75; + //================================================================== // // SchematicScene @@ -192,6 +196,96 @@ QVector SchematicScene::getPlacedNode(SchematicNode *node) { return nodes; } +//------------------------------------------------------------------ + +void SchematicScene::addSnapTarget(const QPointF &pos, const QRectF &rect, + const QPointF &theOtherEndPos, + const QPointF &endPosOffset) { + // reduce highlight margin + QRectF nodeRect = rect.adjusted(5, 5, -5, -5); + + /* + auto findSnapPos = [&](QPointF pos) { + for (auto item : m_snapTargets) { + if (item->scenePos() == pos) return true; + } + return false; + }; + + QList posList = {pos}; + + for (int i = 1; i <= 10; i++) { + QPointF tmp_pos = + pos + QPointF(0, (double)(SchematicScene::snapVInterval * i)); + posList.append(tmp_pos); + if (isAnEmptyZone(nodeRect.translated(tmp_pos))) break; + } + for (int i = -1; i >= -10; i--) { + QPointF tmp_pos = + pos + QPointF(0, (double)(SchematicScene::snapVInterval * i)); + posList.append(tmp_pos); + if (isAnEmptyZone(nodeRect.translated(tmp_pos))) break; + } + + for (auto p : posList) { + if (findSnapPos(p)) continue; + SnapTargetItem *item = new SnapTargetItem(p, nodeRect); + addItem(item); + m_snapTargets.append(item); + }*/ + + SnapTargetItem *item = + new SnapTargetItem(pos, nodeRect, theOtherEndPos, endPosOffset); + addItem(item); + m_snapTargets.append(item); +} + +//------------------------------------------------------------------ + +void SchematicScene::clearSnapTargets() { + for (auto item : m_snapTargets) { + removeItem(item); + delete item; + } + m_snapTargets.clear(); +} + +//------------------------------------------------------------------ +// snap to neighbor nodes on dragging +void SchematicScene::computeSnap(SchematicNode *node, QPointF &delta, + bool enable) { + if (m_snapTargets.isEmpty()) return; + + if (!enable) { + // hide targets + if (m_snapTargets[0]->isVisible()) { + for (auto item : m_snapTargets) item->setVisible(false); + } + return; + } + + if (!m_snapTargets[0]->isVisible()) { + for (auto item : m_snapTargets) item->setVisible(true); + } + + QPointF newScenePos = node->scenePos() + delta; + QPointF newPos = views()[0]->mapFromScene(newScenePos); + + for (auto target : m_snapTargets) { + QPointF targetPos = target->scenePos(); + int snapIndex = std::nearbyint((newScenePos.y() - targetPos.y()) / + (double)SchematicScene::snapVInterval); + targetPos.setY(targetPos.y() + + (double)(snapIndex * SchematicScene::snapVInterval)); + target->setPos(targetPos); + if ((newPos - views()[0]->mapFromScene(targetPos)).manhattanLength() < + snapDistance) { + delta = targetPos - node->scenePos(); + return; + } + } +} + //================================================================== // // SchematicSceneViewer @@ -247,14 +341,14 @@ void SchematicSceneViewer::mousePressEvent(QMouseEvent *me) { m_mousePanPoint = m_touchDevice == QTouchDevice::TouchScreen ? mapToScene(me->pos()) : me->pos() * getDevicePixelRatio(this); - m_panning = true; + m_panning = true; return; } else if (m_cursorMode == CursorMode::Zoom) { m_zoomPoint = me->pos(); m_zooming = true; return; } - } else if (m_buttonState == Qt::MidButton) { + } else if (m_buttonState == Qt::MiddleButton) { m_mousePanPoint = m_touchDevice == QTouchDevice::TouchScreen ? mapToScene(me->pos()) : me->pos() * getDevicePixelRatio(this); @@ -290,10 +384,10 @@ void SchematicSceneViewer::mouseMoveEvent(QMouseEvent *me) { QPoint currWinPos = me->pos(); QPointF currScenePos = mapToScene(currWinPos); if (((m_cursorMode == CursorMode::Hand || m_panningArmed) && m_panning) || - m_buttonState == Qt::MidButton) { - QPointF usePos = m_touchDevice == QTouchDevice::TouchScreen - ? mapToScene(me->pos()) - : me->pos() * getDevicePixelRatio(this); + m_buttonState == Qt::MiddleButton) { + QPointF usePos = m_touchDevice == QTouchDevice::TouchScreen + ? mapToScene(me->pos()) + : me->pos() * getDevicePixelRatio(this); QPointF deltaPoint = usePos - m_mousePanPoint; panQt(deltaPoint); m_mousePanPoint = m_touchDevice == QTouchDevice::TouchScreen @@ -408,7 +502,11 @@ void SchematicSceneViewer::wheelEvent(QWheelEvent *me) { m_touchDevice == QTouchDevice::TouchScreen) || m_gestureActive == false) { double factor = exp(delta * 0.001); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + changeScale(me->position().toPoint(), factor); +#else changeScale(me->pos(), factor); +#endif m_panning = false; } } @@ -419,28 +517,24 @@ void SchematicSceneViewer::wheelEvent(QWheelEvent *me) { void SchematicSceneViewer::zoomQt(bool zoomin, bool resetView) { if (resetView) { - resetMatrix(); + resetTransform(); // resetting will set view to the center of items bounding centerOn(scene()->itemsBoundingRect().center()); return; } -#if QT_VERSION >= 0x050000 - double scale2 = matrix().determinant(); -#else - double scale2 = matrix().det(); -#endif + double scale2 = transform().determinant(); if ((scale2 < 100000 || !zoomin) && (scale2 > 0.001 * 0.05 || zoomin)) { double oldZoomScale = sqrt(scale2); double zoomScale = resetView ? 1 : ImageUtils::getQuantizedZoomFactor(oldZoomScale, zoomin); - QMatrix scale = - QMatrix().scale(zoomScale / oldZoomScale, zoomScale / oldZoomScale); + QTransform scale = + QTransform().scale(zoomScale / oldZoomScale, zoomScale / oldZoomScale); // See QGraphicsView::mapToScene()'s doc for details QPointF sceneCenter(mapToScene(rect().center())); - setMatrix(scale, true); + setTransform(scale, true); centerOn(sceneCenter); } } @@ -452,8 +546,8 @@ void SchematicSceneViewer::zoomQt(bool zoomin, bool resetView) { void SchematicSceneViewer::changeScale(const QPoint &winPos, qreal scaleFactor) { QPointF startScenePos = mapToScene(winPos); - QMatrix scale = QMatrix().scale(scaleFactor, scaleFactor); - setMatrix(scale, true); + QTransform scale = QTransform().scale(scaleFactor, scaleFactor); + setTransform(scale, true); QPointF endScenePos = mapToScene(winPos); QPointF delta = endScenePos - startScenePos; translate(delta.x(), delta.y()); @@ -488,7 +582,7 @@ void SchematicSceneViewer::reorderScene() { void SchematicSceneViewer::normalizeScene() { // See QGraphicsView::mapToScene()'s doc for details QPointF sceneCenter(mapToScene(rect().center())); - resetMatrix(); + resetTransform(); #if defined(MACOSX) scale(1.32, 1.32); #endif @@ -512,7 +606,7 @@ void SchematicSceneViewer::showEvent(QShowEvent *se) { if (m_firstShowing) { m_firstShowing = false; QRectF rect = scene()->itemsBoundingRect(); - resetMatrix(); + resetTransform(); centerOn(rect.center()); } } @@ -643,13 +737,13 @@ void SchematicSceneViewer::touchEvent(QTouchEvent *e, int type) { } } if (m_panning) { - QPointF curPos = m_touchDevice == QTouchDevice::TouchScreen - ? mapToScene(panPoint.pos().toPoint()) - : mapToScene(panPoint.pos().toPoint()) * + QPointF curPos = m_touchDevice == QTouchDevice::TouchScreen + ? mapToScene(panPoint.pos().toPoint()) + : mapToScene(panPoint.pos().toPoint()) * getDevicePixelRatio(this); - QPointF lastPos = m_touchDevice == QTouchDevice::TouchScreen - ? mapToScene(panPoint.lastPos().toPoint()) - : mapToScene(panPoint.lastPos().toPoint()) * + QPointF lastPos = m_touchDevice == QTouchDevice::TouchScreen + ? mapToScene(panPoint.lastPos().toPoint()) + : mapToScene(panPoint.lastPos().toPoint()) * getDevicePixelRatio(this); QPointF centerDelta = curPos - lastPos; panQt(centerDelta); @@ -1113,7 +1207,7 @@ void SchematicViewer::setStageSchematic() { m_viewer->setScene(m_stageScene); QRectF rect = m_stageScene->itemsBoundingRect(); - m_viewer->resetMatrix(); + m_viewer->resetTransform(); m_viewer->centerOn(rect.center()); m_fxToolbar->hide(); @@ -1132,7 +1226,7 @@ void SchematicViewer::setFxSchematic() { m_viewer->setScene(m_fxScene); QRectF rect = m_fxScene->itemsBoundingRect(); - m_viewer->resetMatrix(); + m_viewer->resetTransform(); m_viewer->centerOn(rect.center()); m_stageToolbar->hide(); @@ -1174,7 +1268,7 @@ void SchematicViewer::onSceneSwitched() { m_nodeSize->setText(label); // reset schematic - m_viewer->resetMatrix(); + m_viewer->resetTransform(); m_viewer->centerOn(m_viewer->scene()->itemsBoundingRect().center()); if (m_viewer->scene() == m_fxScene && !m_fxScene->isNormalIconView()) m_fxScene->updateScene(); diff --git a/toonz/sources/toonzqt/screenboard.cpp b/toonz/sources/toonzqt/screenboard.cpp index df45844c..5565f338 100644 --- a/toonz/sources/toonzqt/screenboard.cpp +++ b/toonz/sources/toonzqt/screenboard.cpp @@ -12,6 +12,7 @@ using namespace DVGui; #include +#include //*********************************************************************************** // Local namespace @@ -25,13 +26,13 @@ public: return rect.contains(QCursor::pos()); } void paintEvent(QWidget *widget, QPaintEvent *pe) override { -// Seems that mouse masking is on by default on the drawn regions, when using -// WA_TranslucentBackground (which is weird). I think it's the Qt 2 autoMask -// feature -// that disappeared from Qt 3 on - now it must be managed internally. + // Seems that mouse masking is on by default on the drawn regions, when + // using WA_TranslucentBackground (which is weird). I think it's the Qt 2 + // autoMask feature that disappeared from Qt 3 on - now it must be managed + // internally. -// So, we have to fill the entire screen region with the most invisible color -// possible... + // So, we have to fill the entire screen region with the most invisible + // color possible... #ifdef MACOSX #define MIN_ALPHA \ @@ -179,7 +180,8 @@ void ScreenBoard::releaseMouse() { void ScreenBoard::reallocScreenWidgets() { QDesktopWidget *desktop = QApplication::desktop(); - int i, screensCount = desktop->numScreens(); + int i; + int screensCount = QGuiApplication::screens().count(); // Delete exceeding screens and resize to screensCount for (i = screensCount; i < m_screenWidgets.size(); ++i) { @@ -188,7 +190,7 @@ void ScreenBoard::reallocScreenWidgets() { // Note that updates may be invoked in event handlers. } - m_screenWidgets.resize(desktop->numScreens()); + m_screenWidgets.resize(screensCount); // Re-initialize the screen widgets list for (i = 0; i < screensCount; ++i) { @@ -200,7 +202,7 @@ void ScreenBoard::reallocScreenWidgets() { int j, drawingsCount = m_drawings.size(); for (i = 0; i < screensCount; ++i) { ScreenWidget *screenWidget = m_screenWidgets[i]; - const QRect &screenGeom = desktop->screenGeometry(i); + const QRect &screenGeom = QGuiApplication::screens().at(i)->geometry(); for (j = 0; j < drawingsCount; ++j) { Drawing *drawing = m_drawings[j]; diff --git a/toonz/sources/toonzqt/spreadsheetviewer.cpp b/toonz/sources/toonzqt/spreadsheetviewer.cpp index 6870d406..0bdd64fc 100644 --- a/toonz/sources/toonzqt/spreadsheetviewer.cpp +++ b/toonz/sources/toonzqt/spreadsheetviewer.cpp @@ -255,11 +255,7 @@ void PanTool::release(int row, int col, QMouseEvent *e) { //============================================================================= -#if QT_VERSION >= 0x050500 ScrollArea::ScrollArea(QWidget *parent, Qt::WindowFlags flags) -#else -ScrollArea::ScrollArea(QWidget *parent, Qt::WFlags flags) -#endif : QScrollArea(parent) { setFrameStyle(QFrame::Panel | QFrame::Raised); setLineWidth(6); @@ -300,7 +296,7 @@ void GenericPanel::paintEvent(QPaintEvent *e) { void GenericPanel::mousePressEvent(QMouseEvent *e) { assert(!m_dragTool); - if (e->button() == Qt::MidButton || m_viewer->getPanningArmed()) + if (e->button() == Qt::MiddleButton || m_viewer->getPanningArmed()) m_dragTool = new PanTool(this); else m_dragTool = createDragTool(e); @@ -377,16 +373,22 @@ void RowPanel::drawRows(QPainter &p, int r0, int r1) { bool simpleView = getViewer()->getFrameZoomFactor() <= Orientations::topToBottom()->dimension( PredefinedDimension::SCALE_THRESHOLD); + int currentRow = getViewer()->getCurrentRow(); int r; int y = getViewer()->rowToY(r0); for (r = r0; r <= r1; r++) { int next_y = getViewer()->rowToY(r + 1); // draw horizontal line bool isMarkSecRow = getViewer()->isMarkSecRow(r); - QColor color = (isMarkSecRow || getViewer()->isMarkRow(r)) - ? getViewer()->getMarkerLineColor() - : getViewer()->getLightLineColor(); - p.setPen(color); + bool isMarkRow = getViewer()->isMarkRow(r); + QColor color = (isMarkSecRow) ? getViewer()->getSecMarkerLineColor() + : (isMarkRow) ? getViewer()->getMarkerLineColor() + : getViewer()->getLightLineColor(); + p.setPen(QPen(color, + (isMarkSecRow) ? 3. + : (getViewer()->isSecMarkerActive() && isMarkRow) ? 2. + : 1., + Qt::SolidLine, Qt::FlatCap)); p.drawLine(x0, y, x1, y); if (simpleView && r > 0 && !getViewer()->isMarkRow(r + 1)) { @@ -395,7 +397,8 @@ void RowPanel::drawRows(QPainter &p, int r0, int r1) { } // draw numbers - p.setPen(getViewer()->getTextColor()); + p.setPen((r == currentRow) ? getViewer()->getCurrentRowTextColor() + : getViewer()->getTextColor()); QString number = QString::number(r + 1); p.drawText(QRect(x0, y + 1, width() - 4, next_y - y - 1), @@ -497,11 +500,15 @@ void CellPanel::paintEvent(QPaintEvent *e) { for (int r = r0; r <= r1; r++) { int y = getViewer()->rowToY(r); bool isMarkSecRow = getViewer()->isMarkSecRow(r); - QColor color = (isMarkSecRow || getViewer()->isMarkRow(r)) - ? getViewer()->getMarkerLineColor() - : getViewer()->getLightLineColor(); - painter.setPen( - QPen(color, (isMarkSecRow) ? 3. : 1., Qt::SolidLine, Qt::FlatCap)); + bool isMarkRow = getViewer()->isMarkRow(r); + QColor color = (isMarkSecRow) ? getViewer()->getSecMarkerLineColor() + : (isMarkRow) ? getViewer()->getMarkerLineColor() + : getViewer()->getLightLineColor(); + painter.setPen(QPen(color, + (isMarkSecRow) ? 3. + : (getViewer()->isSecMarkerActive() && isMarkRow) ? 2. + : 1., + Qt::SolidLine, Qt::FlatCap)); painter.drawLine(x0, y, x1, y); } } @@ -807,7 +814,7 @@ bool SpreadsheetViewer::refreshContentSize(int scrollDx, int scrollDy) { QSize viewportSize = m_cellScrollArea->viewport()->size(); QPoint offset = m_cellScrollArea->widget()->pos(); offset = QPoint(std::min(0, offset.x() - scrollDx), - std::min(0, offset.y() - scrollDy)); + std::min(0, offset.y() - scrollDy)); QSize contentSize(columnToX(m_columnCount + 1), rowToY(m_rowCount + 1)); diff --git a/toonz/sources/toonzqt/stageobjectselection.cpp b/toonz/sources/toonzqt/stageobjectselection.cpp index 9d665760..051fa6e4 100644 --- a/toonz/sources/toonzqt/stageobjectselection.cpp +++ b/toonz/sources/toonzqt/stageobjectselection.cpp @@ -331,7 +331,9 @@ void StageObjectSelection::copySelection() { StageObjectsData *data = new StageObjectsData(); bool pegSelected = false; data->storeObjects( - m_selectedObjects.toVector().toStdVector(), m_xshHandle->getXsheet(), + std::vector(m_selectedObjects.begin(), + m_selectedObjects.end()), + m_xshHandle->getXsheet(), StageObjectsData::eDoClone | StageObjectsData::eResetFxDagPositions); std::set columnIndexes; int i; @@ -342,7 +344,8 @@ void StageObjectSelection::copySelection() { columnIndexes, m_xshHandle->getXsheet(), StageObjectsData::eDoClone | StageObjectsData::eResetFxDagPositions); data->storeSplines( - m_selectedSplines.toStdList(), m_xshHandle->getXsheet(), + std::list(m_selectedSplines.begin(), m_selectedSplines.end()), + m_xshHandle->getXsheet(), StageObjectsData::eDoClone | StageObjectsData::eResetFxDagPositions); if (!data->isEmpty()) diff --git a/toonz/sources/toonzqt/stageschematicnode.cpp b/toonz/sources/toonzqt/stageschematicnode.cpp index 6f0377cd..f5ce2f54 100644 --- a/toonz/sources/toonzqt/stageschematicnode.cpp +++ b/toonz/sources/toonzqt/stageschematicnode.cpp @@ -487,7 +487,7 @@ void CameraPainter::paint(QPainter *painter, SchematicViewer *viewer = stageScene->getSchematicViewer(); QColor cameraColor = m_isActive ? viewer->getActiveCameraColor() - : viewer->getOtherCameraColor(); + : viewer->getOtherCameraColor(); painter->setBrush(cameraColor); painter->setPen(Qt::NoPen); @@ -1103,11 +1103,7 @@ StageSchematicNodeDock::StageSchematicNodeDock(StageSchematicNode *parent, connect(m_port, SIGNAL(xsheetChanged()), parent, SIGNAL(xsheetChanged())); setZValue(1.5); -#if QT_VERSION >= 0x050000 setAcceptHoverEvents(true); -#else - setAcceptsHoverEvents(true); -#endif } //-------------------------------------------------------- @@ -1125,11 +1121,7 @@ QRectF StageSchematicNodeDock::boundingRect() const { stageScene->isShowLetterOnPortFlagEnabled()) { QRectF handleRect = m_handleSpinBox->boundingRect(); handleRect.moveTopLeft(QPointF(portRect.width(), handleRect.topLeft().y())); -#if QT_VERSION >= 0x050000 portRect = portRect.united(handleRect); -#else - portRect = portRect.unite(handleRect); -#endif } return portRect; } @@ -1286,11 +1278,7 @@ StageSchematicSplineDock::StageSchematicSplineDock(SchematicNode *parent, setFlag(QGraphicsItem::ItemIsSelectable, false); setFlag(QGraphicsItem::ItemIsFocusable, false); m_port = new StageSchematicSplinePort(this, type); -#if QT_VERSION >= 0x050000 setAcceptHoverEvents(true); -#else - setAcceptsHoverEvents(true); -#endif connect(m_port, SIGNAL(sceneChanged()), parent, SIGNAL(sceneChanged())); connect(m_port, SIGNAL(xsheetChanged()), parent, SIGNAL(xsheetChanged())); @@ -1657,8 +1645,8 @@ void StageSchematicPegbarNode::onNameChanged() { std::string strId = id.toString(); std::string name = m_name.toStdString(); QString toolTip = name == strId - ? m_name - : m_name + " (" + QString::fromStdString(strId) + ")"; + ? m_name + : m_name + " (" + QString::fromStdString(strId) + ")"; setToolTip(toolTip); if (id.isPegbar()) TStageObjectCmd::rename(id, m_name.toStdString(), diff --git a/toonz/sources/toonzqt/stageschematicscene.cpp b/toonz/sources/toonzqt/stageschematicscene.cpp index da1f50fc..926b8e8d 100644 --- a/toonz/sources/toonzqt/stageschematicscene.cpp +++ b/toonz/sources/toonzqt/stageschematicscene.cpp @@ -531,22 +531,14 @@ void StageSchematicScene::updateNestedGroupEditors(StageSchematicNode *node, if (rect.isEmpty()) rect = app; else -#if QT_VERSION >= 0x050000 rect = rect.united(app); -#else - rect = rect.unite(app); -#endif } } node->setPos(newPos); for (i = 0; i < groupIdStack.size(); i++) { if (!m_groupEditorTable.contains(groupIdStack[i])) continue; -#if QT_VERSION >= 0x050000 rect = rect.united(m_groupEditorTable[groupIdStack[i]]->sceneBoundingRect()); -#else - rect = rect.unite(m_groupEditorTable[groupIdStack[i]]->sceneBoundingRect()); -#endif QRectF app = m_groupEditorTable[groupIdStack[i]]->boundingSceneRect(); if (m_groupEditorTable[groupIdStack[i]]->scenePos() != app.topLeft()) m_groupEditorTable[groupIdStack[i]]->setPos(app.topLeft()); @@ -1217,7 +1209,7 @@ void StageSchematicScene::contextMenuEvent( void StageSchematicScene::mousePressEvent(QGraphicsSceneMouseEvent *me) { QList items = selectedItems(); SchematicScene::mousePressEvent(me); - if (me->button() == Qt::MidButton) { + if (me->button() == Qt::MiddleButton) { int i; for (i = 0; i < items.size(); i++) items[i]->setSelected(true); } @@ -1312,3 +1304,50 @@ void StageSchematicScene::onNodeChangedSize() { if (resizingNodes) return; updateScene(); } + +//------------------------------------------------------------------ +// snap to neighbor nodes on dragging +void StageSchematicScene::updateSnapTarget(QGraphicsItem *item) { + clearSnapTargets(); + StageSchematicNode *node = dynamic_cast(item); + if (!node) return; + + int portCount = node->getChildCount(); + for (int i = 0; i < portCount; i++) { + StageSchematicNodePort *port = node->getChildPort(i); + int j, linkCount = port->getLinkCount(); + for (j = 0; j < linkCount; j++) { + SchematicLink *link = port->getLink(j); + if (!link) continue; + SchematicNode *otherNode = link->getOtherNode(node); + if (otherNode && !otherNode->isSelected()) { + QPointF targetPos = + otherNode->scenePos() - + QPointF(node->boundingRect().width() + SchematicScene::snapHSpacing, + 0); + addSnapTarget(targetPos, node->boundingRect(), + link->getOtherPort(port)->getLinkEndPoint(), + port->getLinkEndPoint() - node->scenePos()); + } + } + } + + StageSchematicNodePort *port = node->getParentPort(); + if (port) { + int linkCount = port->getLinkCount(); + for (int i = 0; i < linkCount; i++) { + SchematicLink *link = port->getLink(i); + if (!link) continue; + SchematicNode *otherNode = link->getOtherNode(node); + if (otherNode && !otherNode->isSelected()) { + QPointF targetPos = + otherNode->scenePos() + QPointF(otherNode->boundingRect().width() + + SchematicScene::snapHSpacing, + 0); + addSnapTarget(targetPos, node->boundingRect(), + link->getOtherPort(port)->getLinkEndPoint(), + port->getLinkEndPoint() - node->scenePos()); + } + } + } +} \ No newline at end of file diff --git a/toonz/sources/toonzqt/styleeditor.cpp b/toonz/sources/toonzqt/styleeditor.cpp index c891d090..f70f3268 100644 --- a/toonz/sources/toonzqt/styleeditor.cpp +++ b/toonz/sources/toonzqt/styleeditor.cpp @@ -1918,7 +1918,10 @@ StyleChooserPage::StyleChooserPage(TFilePath styleFolder, QWidget *parent) , m_allowPageDelete(true) , m_favorite(false) , m_allowFavorite(false) - , m_external(false) {} + , m_external(false) { + setObjectName("StyleChooserPage"); + setMouseTracking(true); +} //----------------------------------------------------------------------------- @@ -1944,9 +1947,14 @@ void StyleChooserPage::paintEvent(QPaintEvent *) { for (j = 0; j < nX; j++) { QRect rect(x0 + chipLx * j + 2, y0 + chipLy * i + 2, chipLx, chipLy); - drawChip(p, rect, count); - p.setPen(Qt::black); - p.drawRect(rect); + int chipType = drawChip(p, rect, count); + if (chipType == COMMONCHIP) { + p.setPen(m_commonChipBoxColor); + p.drawRect(rect); + } else { // SOLIDCHIP + p.setPen(m_solidChipBoxColor); + p.drawRect(rect.adjusted(0, 0, -1, -1)); + } // Draw selection check boxes if (std::find(m_selection.begin(), m_selection.end(), count) != @@ -1983,13 +1991,13 @@ void StyleChooserPage::paintEvent(QPaintEvent *) { if (!currentIndexRect.isEmpty()) { // Draw the curentIndex border - p.setPen(Qt::white); + p.setPen(m_selectedChipBoxColor); p.drawRect(currentIndexRect); - p.setPen(QColor(199, 202, 50)); + p.setPen(m_selectedChipBox2Color); p.drawRect(currentIndexRect.adjusted(1, 1, -1, -1)); - p.setPen(Qt::white); + p.setPen(m_selectedChipBoxColor); p.drawRect(currentIndexRect.adjusted(2, 2, -2, -2)); - p.setPen(Qt::black); + p.setPen(m_commonChipBoxColor); p.drawRect(currentIndexRect.adjusted(3, 3, -3, -3)); } } @@ -2248,6 +2256,17 @@ void StyleChooserPage::enterEvent(QEvent *event) { //----------------------------------------------------------------------------- +void StyleChooserPage::mouseMoveEvent(QMouseEvent *event) { + QPoint pos = event->pos(); + int currentIndex = posToIndex(pos); + if (currentIndex >= 0 && currentIndex < getChipCount()) + setCursor(Qt::PointingHandCursor); + else + setCursor(Qt::ArrowCursor); +} + +//----------------------------------------------------------------------------- + void StyleChooserPage::mouseReleaseEvent(QMouseEvent *event) {} //----------------------------------------------------------------------------- @@ -2564,13 +2583,16 @@ else return false; void loadItems() override { m_styleManager->loadItems(); } int getChipCount() const override { - return m_styleManager->getPatternCount() + 1; + int chipCount = m_styleManager->getPatternCount() + 1; + if (!m_styleManager->getSearchText().isEmpty() && chipCount == 1) return 0; + return chipCount; } - void drawChip(QPainter &p, QRect rect, int index) override { + int drawChip(QPainter &p, QRect rect, int index) override { if (index == 0) { static QImage noSpecialStyleImage(":Resources/no_vectorbrush.png"); p.drawImage(rect, noSpecialStyleImage); + return SOLIDCHIP; } else { index -= 1; assert(0 <= index && index <= getChipCount()); @@ -2578,8 +2600,13 @@ else return false; m_styleManager->getPattern(index); if (pattern.m_image && !pattern.m_image->isNull()) p.drawImage(rect, *pattern.m_image); + return COMMONCHIP; } } + + void applyFilter() { m_styleManager->applyFilter(); } + void applyFilter(const QString text) { m_styleManager->applyFilter(text); } + void onSelect(int index) override; void removeSelectedStylesFromSet(std::vector selection) override; @@ -2615,8 +2642,7 @@ bool CustomStyleChooserPage::event(QEvent *e) { CustomStyleManager::PatternData pattern = m_styleManager->getPattern(chipIdx); - QToolTip::showText(he->globalPos(), - QString::fromStdString(pattern.m_patternName)); + QToolTip::showText(he->globalPos(), pattern.m_patternName); } return true; } @@ -2635,7 +2661,7 @@ void CustomStyleChooserPage::onSelect(int index) { if (m_currentIndex < 0) return; - std::string name = pattern.m_patternName; + std::string name = pattern.m_patternName.toStdString(); if (pattern.m_isVector) { TVectorImagePatternStrokeStyle cs(m_stylesFolder, name); emit styleSelected(cs); @@ -2662,7 +2688,7 @@ void CustomStyleChooserPage::addSelectedStylesToPalette( CustomStyleManager::PatternData pattern = m_styleManager->getPattern(selection[i] - 1); - std::string name = pattern.m_patternName; + std::string name = pattern.m_patternName.toStdString(); if (pattern.m_isVector) { TVectorImagePatternStrokeStyle cs(m_stylesFolder, name); m_editor->addToPalette(cs); @@ -2698,13 +2724,11 @@ void CustomStyleChooserPage::removeSelectedStylesFromSet( continue; } } else { - std::string name = pattern.m_patternName; - TFilePathSet fileList; QDir patternDir(m_stylesFolder.getQString()); patternDir.setNameFilters( - QStringList(QString::fromStdString(name) + ".*" + + QStringList(pattern.m_patternName + ".*" + QString::fromStdString(pattern.m_path.getType()))); TSystem::readDirectory(fileList, patternDir, false); @@ -2725,7 +2749,6 @@ void CustomStyleChooserPage::addSelectedStylesToSet(std::vector selection, for (int i = 0; i < selection.size(); i++) { CustomStyleManager::PatternData pattern = m_styleManager->getPattern(selection[i] - 1); - std::string name = pattern.m_patternName; TFilePathSet fileList; @@ -2785,10 +2808,16 @@ public: void loadItems() override { m_styleManager->loadItems(); } int getChipCount() const override { - return m_styleManager->getPatternCount() + 1; + int chipCount = m_styleManager->getPatternCount() + 1; + if (!m_styleManager->getSearchText().isEmpty() && chipCount == 1) return 0; + return chipCount; } - void drawChip(QPainter &p, QRect rect, int index) override; + int drawChip(QPainter &p, QRect rect, int index) override; + + void applyFilter() { m_styleManager->applyFilter(); } + void applyFilter(const QString text) { m_styleManager->applyFilter(text); } + void onSelect(int index) override; void removeSelectedStylesFromSet(std::vector selection) override; @@ -2819,8 +2848,7 @@ bool VectorBrushStyleChooserPage::event(QEvent *e) { if (chipIdx > 0) { CustomStyleManager::PatternData pattern = m_styleManager->getPattern(chipIdx - 1); - QToolTip::showText(he->globalPos(), - QString::fromStdString(pattern.m_patternName)); + QToolTip::showText(he->globalPos(), pattern.m_patternName); } else QToolTip::showText( he->globalPos(), @@ -2831,15 +2859,17 @@ bool VectorBrushStyleChooserPage::event(QEvent *e) { //----------------------------------------------------------------------------- -void VectorBrushStyleChooserPage::drawChip(QPainter &p, QRect rect, int index) { +int VectorBrushStyleChooserPage::drawChip(QPainter &p, QRect rect, int index) { if (index == 0) { static QImage noSpecialStyleImage(":Resources/no_vectorbrush.png"); p.drawImage(rect, noSpecialStyleImage); + return SOLIDCHIP; } else { assert(0 <= index && index < getChipCount()); CustomStyleManager::PatternData pattern = m_styleManager->getPattern(index - 1); p.drawImage(rect, *pattern.m_image); + return COMMONCHIP; } } @@ -2855,7 +2885,7 @@ void VectorBrushStyleChooserPage::onSelect(int index) { if (m_currentIndex < 0) return; - std::string name = pattern.m_patternName; + std::string name = pattern.m_patternName.toStdString(); assert(pattern.m_isVector); if (!pattern.m_isVector) return; @@ -2877,7 +2907,7 @@ void VectorBrushStyleChooserPage::addSelectedStylesToPalette( CustomStyleManager::PatternData pattern = m_styleManager->getPattern(selection[i] - 1); - std::string name = pattern.m_patternName; + std::string name = pattern.m_patternName.toStdString(); assert(pattern.m_isVector); if (!pattern.m_isVector) return; @@ -2896,13 +2926,12 @@ void VectorBrushStyleChooserPage::removeSelectedStylesFromSet( for (int i = 0; i < selection.size(); i++) { CustomStyleManager::PatternData pattern = m_styleManager->getPattern(selection[i] - 1); - std::string name = pattern.m_patternName; TFilePathSet fileList; QDir patternDir(m_stylesFolder.getQString()); patternDir.setNameFilters( - QStringList(QString::fromStdString(name) + ".*" + + QStringList(pattern.m_patternName + ".*" + QString::fromStdString(pattern.m_path.getType()))); TSystem::readDirectory(fileList, patternDir, false); @@ -2922,13 +2951,12 @@ void VectorBrushStyleChooserPage::addSelectedStylesToSet( for (int i = 0; i < selection.size(); i++) { CustomStyleManager::PatternData pattern = m_styleManager->getPattern(selection[i] - 1); - std::string name = pattern.m_patternName; TFilePathSet fileList; QDir patternDir(m_stylesFolder.getQString()); patternDir.setNameFilters( - QStringList(QString::fromStdString(name) + ".*" + + QStringList(pattern.m_patternName + ".*" + QString::fromStdString(pattern.m_path.getType()))); TSystem::readDirectory(fileList, patternDir, false); @@ -2981,13 +3009,16 @@ public: void loadItems() override { m_styleManager->loadItems(); } int getChipCount() const override { - return m_styleManager->getTextureCount() + 1; + int chipCount = m_styleManager->getTextureCount() + 1; + if (!m_styleManager->getSearchText().isEmpty() && chipCount == 1) return 0; + return chipCount; } - void drawChip(QPainter &p, QRect rect, int index) override { + int drawChip(QPainter &p, QRect rect, int index) override { if (index == 0) { - static QImage noSpecialStyleImage(":Resources/no_vectorbrush.png"); + static QImage noSpecialStyleImage(":Resources/no_texturestyle.png"); p.drawImage(rect, noSpecialStyleImage); + return SOLIDCHIP; } else { index -= 1; assert(0 <= index && index < getChipCount()); @@ -2995,9 +3026,13 @@ public: m_styleManager->getTexture(index); if (texture.m_raster && !texture.m_raster->isEmpty()) p.drawImage(rect, rasterToQImage(texture.m_raster)); + return COMMONCHIP; } } + void applyFilter() { m_styleManager->applyFilter(); } + void applyFilter(const QString text) { m_styleManager->applyFilter(text); } + void onSelect(int index) override; void removeSelectedStylesFromSet(std::vector selection) override; @@ -3083,7 +3118,7 @@ bool TextureStyleChooserPage::event(QEvent *e) { TextureStyleManager::TextureData texture = m_styleManager->getTexture(chipIdx); - toolTip = QString::fromStdString(texture.m_textureName); + toolTip = texture.m_textureName; QToolTip::showText( helpEvent->globalPos(), toolTip != QString() @@ -3177,21 +3212,28 @@ public: void loadItems() override { m_styleManager->loadItems(); } int getChipCount() const override { - return m_styleManager->getBrushCount() + 1; + int chipCount = m_styleManager->getBrushCount() + 1; + if (!m_styleManager->getSearchText().isEmpty() && chipCount == 1) return 0; + return chipCount; } - void drawChip(QPainter &p, QRect rect, int index) override { + int drawChip(QPainter &p, QRect rect, int index) override { if (index == 0) { static QImage noStyleImage(":Resources/no_mypaintbrush.png"); p.drawImage(rect, noStyleImage); + return SOLIDCHIP; } else { index -= 1; assert(0 <= index && index < getChipCount()); BrushStyleManager::BrushData brush = m_styleManager->getBrush(index); p.drawImage(rect, rasterToQImage(brush.m_brush.getPreview())); + return COMMONCHIP; } } + void applyFilter() { m_styleManager->applyFilter(); } + void applyFilter(const QString text) { m_styleManager->applyFilter(text); } + void onSelect(int index) override; void removeSelectedStylesFromSet(std::vector selection) override; @@ -3260,7 +3302,7 @@ bool MyPaintBrushStyleChooserPage::event(QEvent *e) { if (chipIdx < 0 || chipIdx >= chipCount) return false; BrushStyleManager::BrushData brush = m_styleManager->getBrush(chipIdx); - toolTip = QString::fromStdString(brush.m_brushName); + toolTip = brush.m_brushName; QToolTip::showText(helpEvent->globalPos(), toolTip); } return true; @@ -3312,15 +3354,28 @@ void MyPaintBrushStyleChooserPage::addSelectedStylesToSet( // SpecialStyleChooser definition //***************************************************************************** +struct SpecialStyleData { + int m_tag_id; + QImage *m_image; + QString m_styleName; + std::string m_idName; // brush id name + + SpecialStyleData() : m_tag_id(), m_image(0), m_styleName(""), m_idName("") {} +}; + class SpecialStyleChooserPage final : public StyleChooserPage { - static std::vector> m_customStyles; - static bool m_loaded; - static QString m_filters; + QList m_specialStyles; + bool m_loaded; + QString m_filters; + + bool m_isIndexed; + QList m_indexes; + QString m_searchText; public: SpecialStyleChooserPage(TFilePath stylesFolder = TFilePath(), QString filters = QString(), QWidget *parent = 0) - : StyleChooserPage(stylesFolder, parent) { + : StyleChooserPage(stylesFolder, parent), m_loaded(false), m_isIndexed(false) { setPageType(StylePageType::VectorGenerated); m_filters = filters; } @@ -3337,11 +3392,25 @@ public: } else return false; } - int getChipCount() const override { return m_customStyles.size(); } + int getChipCount() const override { + int chipCount = + m_isIndexed ? m_indexes.count() + 1 : m_specialStyles.size() + 1; + if (chipCount == 1) return 0; + + return chipCount; + } void loadItems() override; - void drawChip(QPainter &p, QRect rect, int index) override; + SpecialStyleData getSpecialStyle(int index); + int drawChip(QPainter &p, QRect rect, int index) override; + + void applyFilter() override; + void applyFilter(const QString text) { + m_searchText = text; + applyFilter(); + } + void onSelect(int index) override; bool event(QEvent *e) override; @@ -3352,12 +3421,33 @@ public: //----------------------------------------------------------------------------- -QString SpecialStyleChooserPage::m_filters; +SpecialStyleData SpecialStyleChooserPage::getSpecialStyle(int index) { + if (m_isIndexed) + return (index < 0 || index >= m_indexes.count()) + ? SpecialStyleData() + : m_specialStyles[m_indexes[index]]; + + return (index < 0 || index >= m_specialStyles.size()) + ? SpecialStyleData() + : m_specialStyles[index]; +} //----------------------------------------------------------------------------- -std::vector> SpecialStyleChooserPage::m_customStyles; -bool SpecialStyleChooserPage::m_loaded(false); +void SpecialStyleChooserPage::applyFilter() { + QList indexes; + + m_indexes.clear(); + int len = m_specialStyles.count(); + for (int i = 0; i < len; i++) { + auto &chip = m_specialStyles[i]; + if (chip.m_styleName.indexOf(m_searchText, 0, Qt::CaseInsensitive) >= 0) + m_indexes.append(i); + } + + m_indexes.append(indexes); + m_isIndexed = (m_indexes.count() != len); +} //----------------------------------------------------------------------------- @@ -3386,41 +3476,51 @@ void SpecialStyleChooserPage::loadItems() { delete style; continue; } + TDimension chipSize(getChipSize().width(), getChipSize().height()); QImage *image = new QImage(rasterToQImage(style->getIcon(chipSize), false)); - m_customStyles.push_back(std::make_pair(tagId, image)); + + SpecialStyleData specialStyle; + specialStyle.m_tag_id = tagId; + specialStyle.m_image = image; + specialStyle.m_styleName = style->getDescription(); + specialStyle.m_idName = style->getBrushIdName(); + m_specialStyles.push_back(specialStyle); delete style; } } //----------------------------------------------------------------------------- -void SpecialStyleChooserPage::drawChip(QPainter &p, QRect rect, int index) { +int SpecialStyleChooserPage::drawChip(QPainter &p, QRect rect, int index) { if (index == 0) { static QImage noSpecialStyleImage(":Resources/no_specialstyle.png"); p.drawImage(rect, noSpecialStyleImage); + return SOLIDCHIP; } else { int j = index - 1; - if (0 <= j && j < (int)m_customStyles.size()) - p.drawImage(rect, *m_customStyles[j].second); - else + if (0 <= j && j < (int)m_specialStyles.size()) { + SpecialStyleData specialStyle = getSpecialStyle(j); + p.drawImage(rect, *specialStyle.m_image); + } else p.fillRect(rect, QBrush(QColor(255, 0, 0))); + return COMMONCHIP; } } //----------------------------------------------------------------------------- void SpecialStyleChooserPage::onSelect(int index) { - assert(0 <= index && index < (int)m_customStyles.size()); + assert(0 <= index && index < (int)m_specialStyles.size()); TColorStyle *cs = 0; if (m_currentIndex < 0) return; if (index == 0) cs = new TSolidColorStyle(TPixel32::Black); else { int j = index - 1; - assert(0 <= j && j < (int)m_customStyles.size()); - int tagId = m_customStyles[j].first; - cs = TColorStyle::create(tagId); + assert(0 <= j && j < (int)m_specialStyles.size()); + SpecialStyleData specialStyle = getSpecialStyle(j); + cs = TColorStyle::create(specialStyle.m_tag_id); } emit styleSelected(*cs); } @@ -3432,9 +3532,8 @@ void SpecialStyleChooserPage::addSelectedStylesToPalette( if (!selection.size()) return; for (int i = 0; i < selection.size(); i++) { - int j = selection[i] - 1; - int tagId = m_customStyles[j].first; - TColorStyle *cs = TColorStyle::create(tagId); + SpecialStyleData specialStyle = getSpecialStyle(selection[i] - 1); + TColorStyle *cs = TColorStyle::create(specialStyle.m_tag_id); m_editor->addToPalette(*cs); } } @@ -3451,9 +3550,9 @@ bool SpecialStyleChooserPage::event(QEvent *e) { toolTip = QObject::tr("Plain color", "SpecialStyleChooserPage"); else { int j = index - 1; - if (0 <= j && j < (int)m_customStyles.size()) { - int tagId = m_customStyles[j].first; - TColorStyle *cs = TColorStyle::create(tagId); + if (0 <= j && j < (int)m_specialStyles.size()) { + SpecialStyleData specialStyle = getSpecialStyle(j); + TColorStyle *cs = TColorStyle::create(specialStyle.m_tag_id); if (cs) { toolTip = cs->getDescription(); delete cs; @@ -3477,9 +3576,9 @@ void SpecialStyleChooserPage::addSelectedStylesToSet(std::vector selection, bool added = false; for (int i = 0; i < selection.size(); i++) { - int j = selection[i] - 1; - int tagId = m_customStyles[j].first; - TColorStyle *cs = TColorStyle::create(tagId); + SpecialStyleData specialStyle = getSpecialStyle(selection[i] - 1); + int tagId = specialStyle.m_tag_id; + TColorStyle *cs = TColorStyle::create(tagId); std::string name = std::to_string(tagId); @@ -4220,28 +4319,32 @@ QFrame *StyleEditor::createBottomWidget() { m_toolBar->setMaximumHeight(22); m_toolBar->addWidget(m_colorParameterSelector); - QMenu *menu = new QMenu(); - m_wheelAction = new QAction(tr("Wheel"), this); - m_hsvAction = new QAction(tr("HSV"), this); - m_alphaAction = new QAction(tr("Alpha"), this); - m_rgbAction = new QAction(tr("RGB"), this); - m_hexAction = new QAction(tr("Hex"), this); + QMenu *menu = new QMenu(); + m_wheelAction = new QAction(tr("Wheel"), this); + m_hsvAction = new QAction(tr("HSV"), this); + m_alphaAction = new QAction(tr("Alpha"), this); + m_rgbAction = new QAction(tr("RGB"), this); + m_hexAction = new QAction(tr("Hex"), this); + m_searchAction = new QAction(tr("Search"), this); m_wheelAction->setCheckable(true); m_hsvAction->setCheckable(true); m_alphaAction->setCheckable(true); m_rgbAction->setCheckable(true); m_hexAction->setCheckable(true); + m_searchAction->setCheckable(true); m_wheelAction->setChecked(true); m_hsvAction->setChecked(false); m_alphaAction->setChecked(true); m_rgbAction->setChecked(false); m_hexAction->setChecked(false); + m_searchAction->setChecked(false); menu->addAction(m_wheelAction); menu->addAction(m_alphaAction); menu->addAction(m_hsvAction); menu->addAction(m_rgbAction); menu->addAction(m_hexAction); + menu->addAction(m_searchAction); QFontMetrics fm(QApplication::font()); @@ -4351,6 +4454,8 @@ QFrame *StyleEditor::createBottomWidget() { m_plainColorPage->m_rgbFrame, SLOT(setVisible(bool))); ret = ret && connect(m_hexAction, SIGNAL(toggled(bool)), m_hexLineEdit, SLOT(setVisible(bool))); + ret = ret && connect(m_searchAction, SIGNAL(toggled(bool)), this, + SLOT(onSearchVisible(bool))); ret = ret && connect(m_hexLineEdit, SIGNAL(editingFinished()), this, SLOT(onHexChanged())); ret = ret && connect(m_hexEditorAction, SIGNAL(triggered()), this, @@ -4377,6 +4482,13 @@ QFrame *StyleEditor::createTexturePage() { QFrame *textureOutsideFrame = new QFrame(this); textureOutsideFrame->setMinimumWidth(50); + m_textureSearchFrame = new QFrame(); + m_textureSearchText = new QLineEdit(); + m_textureSearchClear = new QPushButton(tr("Clear Search")); + m_textureSearchClear->setDisabled(true); + m_textureSearchClear->setSizePolicy(QSizePolicy::Minimum, + QSizePolicy::Preferred); + /* ------ layout ------ */ QVBoxLayout *textureOutsideLayout = new QVBoxLayout(); textureOutsideLayout->setMargin(0); @@ -4411,10 +4523,29 @@ QFrame *StyleEditor::createTexturePage() { m_textureArea = makeChooserPage(textureFrame); m_textureArea->setMinimumWidth(50); textureOutsideLayout->addWidget(m_textureArea); + + QHBoxLayout *searchLayout = new QHBoxLayout(); + searchLayout->setMargin(2); + searchLayout->setSpacing(0); + searchLayout->setSizeConstraint(QLayout::SetNoConstraint); + { + searchLayout->addWidget(m_textureSearchText); + searchLayout->addWidget(m_textureSearchClear); + } + m_textureSearchFrame->setLayout(searchLayout); + textureOutsideLayout->addWidget(m_textureSearchFrame); } textureOutsideFrame->setLayout(textureOutsideLayout); - return textureOutsideFrame; + /* ------ signal-slot connections ------ */ + bool ret = true; + ret = + ret && connect(m_textureSearchText, SIGNAL(textChanged(const QString &)), + this, SLOT(onTextureSearch(const QString &))); + ret = ret && connect(m_textureSearchClear, SIGNAL(clicked()), this, + SLOT(onTextureClearSearch())); + +return textureOutsideFrame; } //----------------------------------------------------------------------------- @@ -4423,6 +4554,13 @@ QFrame *StyleEditor::createVectorPage() { QFrame *vectorOutsideFrame = new QFrame(this); vectorOutsideFrame->setMinimumWidth(50); + m_vectorsSearchFrame = new QFrame(); + m_vectorsSearchText = new QLineEdit(); + m_vectorsSearchClear = new QPushButton(tr("Clear Search")); + m_vectorsSearchClear->setDisabled(true); + m_vectorsSearchClear->setSizePolicy(QSizePolicy::Minimum, + QSizePolicy::Preferred); + /* ------ layout ------ */ QVBoxLayout *vectorOutsideLayout = new QVBoxLayout(); vectorOutsideLayout->setMargin(0); @@ -4457,9 +4595,29 @@ QFrame *StyleEditor::createVectorPage() { m_vectorArea = makeChooserPage(vectorFrame); m_vectorArea->setMinimumWidth(50); vectorOutsideLayout->addWidget(m_vectorArea); + + QHBoxLayout *searchLayout = new QHBoxLayout(); + searchLayout->setMargin(2); + searchLayout->setSpacing(0); + searchLayout->setSizeConstraint(QLayout::SetNoConstraint); + { + searchLayout->addWidget(m_vectorsSearchText); + searchLayout->addWidget(m_vectorsSearchClear); + } + m_vectorsSearchFrame->setLayout(searchLayout); + vectorOutsideLayout->addWidget(m_vectorsSearchFrame); } vectorOutsideFrame->setLayout(vectorOutsideLayout); + /* ------ signal-slot connections ------ */ + bool ret = true; + ret = + ret && connect(m_vectorsSearchText, SIGNAL(textChanged(const QString &)), + this, SLOT(onVectorsSearch(const QString &))); + ret = ret && connect(m_vectorsSearchClear, SIGNAL(clicked()), this, + SLOT(onVectorsClearSearch())); + + assert(ret); return vectorOutsideFrame; } @@ -4469,6 +4627,13 @@ QFrame *StyleEditor::createRasterPage() { QFrame *rasterOutsideFrame = new QFrame(this); rasterOutsideFrame->setMinimumWidth(50); + m_mypaintSearchFrame = new QFrame(); + m_mypaintSearchText = new QLineEdit(); + m_mypaintSearchClear = new QPushButton(tr("Clear Search")); + m_mypaintSearchClear->setDisabled(true); + m_mypaintSearchClear->setSizePolicy(QSizePolicy::Minimum, + QSizePolicy::Preferred); + /* ------ layout ------ */ QVBoxLayout *rasterOutsideLayout = new QVBoxLayout(); rasterOutsideLayout->setMargin(0); @@ -4503,14 +4668,85 @@ QFrame *StyleEditor::createRasterPage() { m_rasterArea = makeChooserPage(rasterFrame); m_rasterArea->setMinimumWidth(50); rasterOutsideLayout->addWidget(m_rasterArea); + + QHBoxLayout *searchLayout = new QHBoxLayout(); + searchLayout->setMargin(2); + searchLayout->setSpacing(0); + searchLayout->setSizeConstraint(QLayout::SetNoConstraint); + { + searchLayout->addWidget(m_mypaintSearchText); + searchLayout->addWidget(m_mypaintSearchClear); + } + m_mypaintSearchFrame->setLayout(searchLayout); + rasterOutsideLayout->addWidget(m_mypaintSearchFrame); } rasterOutsideFrame->setLayout(rasterOutsideLayout); + /* ------ signal-slot connections ------ */ + bool ret = true; + ret = + ret && connect(m_mypaintSearchText, SIGNAL(textChanged(const QString &)), + this, SLOT(onMyPaintSearch(const QString &))); + ret = ret && connect(m_mypaintSearchClear, SIGNAL(clicked()), this, + SLOT(onMyPaintClearSearch())); + + assert(ret); return rasterOutsideFrame; } //----------------------------------------------------------------------------- +void StyleEditor::onTextureSearch(const QString &search) { + m_textureSearchClear->setDisabled(search.isEmpty()); + for (int i = 0; i < m_texturePages.size(); i++) { + m_texturePages[i]->applyFilter(search); + m_texturePages[i]->computeSize(); + } +} + +//----------------------------------------------------------------------------- + +void StyleEditor::onTextureClearSearch() { + m_textureSearchText->setText(""); + m_textureSearchText->setFocus(); +} + +//----------------------------------------------------------------------------- + +void StyleEditor::onVectorsSearch(const QString &search) { + m_vectorsSearchClear->setDisabled(search.isEmpty()); + for (int i = 0; i < m_vectorPages.size(); i++) { + m_vectorPages[i]->applyFilter(search); + m_vectorPages[i]->computeSize(); + } +} + +//----------------------------------------------------------------------------- + +void StyleEditor::onVectorsClearSearch() { + m_vectorsSearchText->setText(""); + m_vectorsSearchText->setFocus(); +} + +//----------------------------------------------------------------------------- + +void StyleEditor::onMyPaintSearch(const QString &search) { + m_mypaintSearchClear->setDisabled(search.isEmpty()); + for (int i = 0; i < m_rasterPages.size(); i++) { + m_rasterPages[i]->applyFilter(search); + m_rasterPages[i]->computeSize(); + } +} + +//----------------------------------------------------------------------------- + +void StyleEditor::onMyPaintClearSearch() { + m_mypaintSearchText->setText(""); + m_mypaintSearchText->setFocus(); +} + +//----------------------------------------------------------------------------- + void StyleEditor::updateTabBar() { m_styleBar->clearTabBar(); if (m_enabled && !m_enabledOnlyFirstTab && !m_enabledFirstAndLastTab) { @@ -4562,6 +4798,7 @@ void StyleEditor::showEvent(QShowEvent *) { m_plainColorPage->m_hsvFrame->setVisible(m_hsvAction->isChecked()); m_plainColorPage->m_rgbFrame->setVisible(m_rgbAction->isChecked()); m_hexLineEdit->setVisible(m_hexAction->isChecked()); + onSearchVisible(m_searchAction->isChecked()); updateOrientationButton(); assert(ret); } @@ -5325,6 +5562,14 @@ void StyleEditor::onToggleAutoApply() { : tr("Show Auto/Apply")); } +//----------------------------------------------------------------------------- + +void StyleEditor::onSearchVisible(bool on) { + m_textureSearchFrame->setVisible(on); + m_vectorsSearchFrame->setVisible(on); + m_mypaintSearchFrame->setVisible(on); +} + //----------------------------------------------------------------------------- QStringList StyleEditor::savePageStates(StylePageType pageType) const { QStringList pageStateData; @@ -5419,6 +5664,7 @@ void StyleEditor::save(QSettings &settings, bool forPopupIni) const { if (m_alphaAction->isChecked()) visibleParts |= 0x04; if (m_rgbAction->isChecked()) visibleParts |= 0x08; if (m_hexAction->isChecked()) visibleParts |= 0x10; + if (m_searchAction->isChecked()) visibleParts |= 0x20; settings.setValue("visibleParts", visibleParts); settings.setValue("splitterState", m_plainColorPage->getSplitterState()); settings.setValue("texturePageStates", @@ -5458,6 +5704,10 @@ void StyleEditor::load(QSettings &settings) { m_hexAction->setChecked(true); else m_hexAction->setChecked(false); + if (visiblePartsInt & 0x20) + m_searchAction->setChecked(true); + else + m_searchAction->setChecked(false); } QVariant splitterState = settings.value("splitterState"); if (splitterState.canConvert(QVariant::ByteArray)) @@ -5799,10 +6049,14 @@ void StyleEditor::createStylePage(StylePageType pageType, TFilePath styleFolder, TSystem::readDirectory_DirItems(fpList, styleFolder); QStringList::iterator fpListIt; - for (fpListIt = fpList.begin(); fpListIt != fpList.end(); fpListIt++) + for (fpListIt = fpList.begin(); fpListIt != fpList.end(); fpListIt++) { + // hide Texture\Brush tips used by Flows Fx + if (pageType == StylePageType::Texture && !isFavorite && dirDepth == 0 && *fpListIt == + "brush tips") + continue; createStylePage(pageType, styleFolder + TFilePath(*fpListIt), filters, isFavorite, (dirDepth + 1)); - + } } catch (...) { return; } diff --git a/toonz/sources/toonzqt/styleindexlineedit.cpp b/toonz/sources/toonzqt/styleindexlineedit.cpp index f294d58c..021e798d 100644 --- a/toonz/sources/toonzqt/styleindexlineedit.cpp +++ b/toonz/sources/toonzqt/styleindexlineedit.cpp @@ -15,8 +15,13 @@ using namespace DVGui; StyleIndexLineEdit::StyleIndexLineEdit() : m_pltHandle(0) { // style index will not be more than 4096, but a longer text // "current" may be input instead of style id + chip width + margin +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + int currentWidth = std::max(fontMetrics().horizontalAdvance("current"), + fontMetrics().horizontalAdvance(tr("current"))); +#else int currentWidth = std::max(fontMetrics().width("current"), fontMetrics().width(tr("current"))); +#endif setMaximumWidth(currentWidth + 30); setFixedHeight(20); } diff --git a/toonz/sources/toonzqt/swatchviewer.cpp b/toonz/sources/toonzqt/swatchviewer.cpp index 5b70b35b..4f1dddc2 100644 --- a/toonz/sources/toonzqt/swatchviewer.cpp +++ b/toonz/sources/toonzqt/swatchviewer.cpp @@ -30,7 +30,7 @@ using namespace TFxUtil; -//#define USE_SQLITE_HDPOOL +// #define USE_SQLITE_HDPOOL //************************************************************************************* // Swatch cache delegate @@ -242,11 +242,7 @@ int submittedTasks = 0; // SwatchViewer //----------------------------------------------------------------------------- -#if QT_VERSION >= 0x050500 SwatchViewer::SwatchViewer(QWidget *parent, Qt::WindowFlags flags) -#else -SwatchViewer::SwatchViewer(QWidget *parent, Qt::WFlags flags) -#endif : QWidget(parent, flags) , m_fx(0) , m_actualFxClone(0) @@ -686,7 +682,7 @@ void SwatchViewer::mousePressEvent(QMouseEvent *event) { } } update(); - } else if (m_mouseButton == Qt::MidButton) { + } else if (m_mouseButton == Qt::MiddleButton) { m_pos = pos; m_firstPos = pos; m_oldContent = getContent(); @@ -724,7 +720,7 @@ void SwatchViewer::mouseMoveEvent(QMouseEvent *event) { // to avoid unnecessary recursions. QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); - } else if (m_mouseButton == Qt::MidButton) { + } else if (m_mouseButton == Qt::MiddleButton) { pan(pos - m_pos); m_pos = pos; } @@ -736,7 +732,7 @@ void SwatchViewer::mouseReleaseEvent(QMouseEvent *event) { m_mouseButton = Qt::NoButton; m_selectedPoint = -1; TPoint pos = TPoint(event->pos().x(), event->pos().y()); - if (event->button() == Qt::MidButton) { + if (event->button() == Qt::MiddleButton) { if (!m_oldContent || !m_curContent) return; TPointD p = convert(pos - m_pos); setAff(TTranslation(p.x, -p.y) * m_aff); @@ -788,9 +784,14 @@ void SwatchViewer::wheelEvent(QWheelEvent *event) { if ((m_gestureActive == true && m_touchDevice == QTouchDevice::TouchScreen) || m_gestureActive == false) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + TPoint center(event->position().x() - width() / 2, + -event->position().y() + height() / 2); +#else TPoint center(event->pos().x() - width() / 2, -event->pos().y() + height() / 2); - zoom(center, exp(0.001 * event->delta())); +#endif + zoom(center, exp(0.001 * event->angleDelta().y())); } } event->accept(); diff --git a/toonz/sources/toonzqt/treemodel.cpp b/toonz/sources/toonzqt/treemodel.cpp index 60c54f59..977cb137 100644 --- a/toonz/sources/toonzqt/treemodel.cpp +++ b/toonz/sources/toonzqt/treemodel.cpp @@ -230,7 +230,7 @@ int TreeModel::columnCount(const QModelIndex &parent) const { return 1; } //------------------------------------------------------------------------------------------------------------------ Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const { - if (!index.isValid()) return 0; + if (!index.isValid()) return Qt::ItemFlags(); return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } @@ -398,7 +398,7 @@ void TreeView::mousePressEvent(QMouseEvent *e) { onClick(item, itemPos, e); } // for drag & drop - else if (e->button() == Qt::MidButton) { + else if (e->button() == Qt::MiddleButton) { m_dragging = true; setMouseTracking(true); onMidClick(item, itemPos, e);