如何在FireMonkey中组合图像滤镜并扩展TFilterHueAdjust实现HSL调整?
HSL全参数调整的扩展方案及多滤镜叠加问题解决思路
一、直接自定义HSL全量调整逻辑
既然现有TFilterHueAdjust仅支持色相调整,且多滤镜叠加易出现效果冲突或损耗,最稳妥的方式是直接实现完整的HSL转换与调整逻辑,无需依赖现有滤镜组件。这种方式能一次性完成H(色相)、S(饱和度)、L(亮度)三个参数的调整,避免多次滤镜转换带来的色彩偏差。
核心思路:遍历位图每个像素,将RGB颜色值转换为HSL空间,调整对应参数后再转回RGB空间写入目标位图。示例代码如下:
procedure AdjustHSL(Source, Dest: TBitmap; HDelta, SScale, LScale: Single); var X, Y: Integer; SR, SG, SB: Byte; H, S, L: Double; begin Dest.SetSize(Source.Width, Source.Height); Dest.PixelFormat := pf24bit; // 确保像素格式统一 for Y := 0 to Source.Height - 1 do begin for X := 0 to Source.Width - 1 do begin // 获取源像素RGB分量 SR := Source.Canvas.Pixels[X, Y]; SG := Source.Canvas.Pixels[X, Y] shr 8; SB := Source.Canvas.Pixels[X, Y] shr 16; // RGB转HSL RGBToHSL(SR, SG, SB, H, S, L); // 调整色相(偏移量范围0-360) H := H + HDelta; if H > 360 then H := H - 360; if H < 0 then H := H + 360; // 调整饱和度(缩放系数:0=灰度,1=原饱和度,大于1增强) S := S * SScale; S := EnsureRange(S, 0, 1); // 调整亮度(缩放系数:0=全黑,1=原亮度,大于1增亮) L := L * LScale; L := EnsureRange(L, 0, 1); // HSL转回RGB并写入目标位图 Dest.Canvas.Pixels[X, Y] := HSLToRGB(H, S, L); end; end; end; // RGB转HSL辅助函数 procedure RGBToHSL(R, G, B: Byte; out H, S, L: Double); var Min, Max, Delta: Double; begin Min := Min(Min(R, G), B) / 255; Max := Max(Max(R, G), B) / 255; L := (Max + Min) / 2; if Max = Min then begin H := 0; S := 0; end else begin Delta := Max - Min; S := IfThen(L < 0.5, Delta/(Max+Min), Delta/(2-Max-Min)); if R = Max then H := (G - B)/Delta else if G = Max then H := 2 + (B - R)/Delta else H := 4 + (R - G)/Delta; H := H * 60; if H < 0 then H := H + 360; end; end; // HSL转RGB辅助函数 function HSLToRGB(H, S, L: Double): TColor; var R, G, B, Temp1, Temp2: Double; function HueToRGB(T1, T2, Hue: Double): Double; begin if Hue < 0 then Hue := Hue + 1; if Hue > 1 then Hue := Hue - 1; if Hue < 1/6 then Result := T1 + (T2 - T1) * 6 * Hue else if Hue < 0.5 then Result := T2 else if Hue < 2/3 then Result := T1 + (T2 - T1) * (2/3 - Hue) * 6 else Result := T1; end; begin if S = 0 then begin R := L; G := L; B := L; end else begin Temp2 := IfThen(L < 0.5, L*(1+S), L+S-L*S); Temp1 := 2*L - Temp2; H := H / 360; R := HueToRGB(Temp1, Temp2, H + 1/3); G := HueToRGB(Temp1, Temp2, H); B := HueToRGB(Temp1, Temp2, H - 1/3); end; Result := RGB(Round(R*255), Round(G*255), Round(B*255)); end;
二、扩展TFilterHueAdjust类实现全参数调整
如果希望沿用现有滤镜框架,可以通过继承TFilterHueAdjust类,添加饱和度和亮度参数,并修改核心处理方法,将HSL三个参数的调整合并到一次滤镜处理中。示例代码如下:
type TFilterHSLAdjust = class(TFilterHueAdjust) private FSaturation: Single; FLightness: Single; procedure SetSaturation(Value: Single); procedure SetLightness(Value: Single); protected procedure DoFilter; override; public constructor Create(AOwner: TComponent); override; published property Saturation: Single read FSaturation write SetSaturation default 1.0; property Lightness: Single read FLightness write SetLightness default 1.0; end; constructor TFilterHSLAdjust.Create(AOwner: TComponent); begin inherited; FSaturation := 1.0; FLightness := 1.0; end; procedure TFilterHSLAdjust.SetSaturation(Value: Single); begin if FSaturation <> Value then begin FSaturation := Value; Changed; end; end; procedure TFilterHSLAdjust.SetLightness(Value: Single); begin if FLightness <> Value then begin FLightness := Value; Changed; end; end; procedure TFilterHSLAdjust.DoFilter; var X, Y: Integer; SR, SG, SB: Byte; H, S, L: Double; begin // 完全重写处理逻辑,避免依赖父类的单一色相调整 Output.SetSize(Input.Width, Input.Height); Output.PixelFormat := Input.PixelFormat; for Y := 0 to Input.Height - 1 do begin for X := 0 to Input.Width - 1 do begin SR := Input.Canvas.Pixels[X, Y]; SG := Input.Canvas.Pixels[X, Y] shr 8; SB := Input.Canvas.Pixels[X, Y] shr 16; RGBToHSL(SR, SG, SB, H, S, L); // 应用色相偏移 H := H + Hue; if H > 360 then H := H - 360; if H < 0 then H := H + 360; // 应用饱和度缩放 S := S * FSaturation; S := EnsureRange(S, 0, 1); // 应用亮度缩放 L := L * FLightness; L := EnsureRange(L, 0, 1); Output.Canvas.Pixels[X, Y] := HSLToRGB(H, S, L); end; end; end;
使用时替换原TFilterHueAdjust即可:
procedure AdjustHSL(Source, Dest: TBitmap; H, S, L: Single); var Filter: TFilterHSLAdjust; begin Filter := TFilterHSLAdjust.Create(nil); try Filter.Input := Source; Filter.Hue := H; Filter.Saturation := S; Filter.Lightness := L; Dest.Assign(Filter.Output); finally Filter.Free; end; end;
三、多滤镜叠加问题的解决
如果坚持使用现有多个滤镜组件,需确保前一个滤镜的输出作为后一个滤镜的输入,按顺序叠加调整,而非每次从原始位图重新处理。正确的叠加方式示例:
procedure AdjustHSLWithFilters(Source, Dest: TBitmap; H, S, L: Single); var TempBmp: TBitmap; HueFilter: TFilterHueAdjust; SatFilter: TFilterSaturationAdjust; // 假设存在饱和度滤镜 LightFilter: TFilterBrightnessAdjust; // 假设存在亮度滤镜 begin TempBmp := TBitmap.Create; HueFilter := TFilterHueAdjust.Create(nil); SatFilter := TFilterSaturationAdjust.Create(nil); LightFilter := TFilterBrightnessAdjust.Create(nil); try // 第一步:调整色相 HueFilter.Input := Source; HueFilter.Hue := H; TempBmp.Assign(HueFilter.Output); // 第二步:基于色相调整后的结果调整饱和度 SatFilter.Input := TempBmp; SatFilter.Saturation := S; TempBmp.Assign(SatFilter.Output); // 第三步:基于饱和度调整后的结果调整亮度 LightFilter.Input := TempBmp; LightFilter.Brightness := L; Dest.Assign(LightFilter.Output); finally LightFilter.Free; SatFilter.Free; HueFilter.Free; TempBmp.Free; end; end;
内容的提问来源于stack exchange,提问作者Craig
相关产品推荐
相关产品推荐

