如何使用混合模式(如乘法)而不是简单透明度来填充矩形?

4
我正在使用Delphi库Graphics32在屏幕上绘制音频图形,然后表示音频中一系列的时间段。该图形相当简单:黑色背景,音频波形填充浅灰色。目前的时间段是使用半透明颜色用FillRectTS绘制的矩形。我正在寻找(希望是简单的)一种使用类似于Multiply的混合模式来绘制它的方法,这样音频波形就会变成彩色,而背景仍旧是黑色。我没有找到任何在TBitmap32类(例如g32_Interface单元)中允许我执行此操作的函数。需要在绘制音频波形之后完成此操作,因为按需重新绘制非常费时,而时间段在运行时由用户定义,因此无法直接使用颜色绘制音频波形。下面是绘制周期的当前代码,我需要更改的部分。
TColor32 ColorDibujado = Color == 0 ? GetColorDeTipoEvento(PeriodoADibujar->Evento->TipoEvento) : Color;

if (PeriodoADibujar->Habilitado == false)
  ColorDibujado = (ColorDibujado & 0x00ffffff) | 0xAf000000;

Gr32::TRect area;

area.Left   = (PeriodoADibujar->PosicionInicioRelativa * (double)LimitesDestino.Width()) / Proporcion + PosX;
area.Right  = (PeriodoADibujar->PosicionFinRelativa * (double)LimitesDestino.Width()) / Proporcion + PosX;
area.Top    = LimitesDestino.Top;
area.Bottom = LimitesDestino.Bottom;

MapaDestino->FillRectTS(area, ColorDibujado);
ColorDibujado 根据不同的条件(事件是否被选中以及事件类型)获取所需的透明度,但最终的想法是相同的。该代码绘制的图形类似于上面图形的前半部分。
Delphi或C++Builder中的解决方案(或想法)对我来说都可以。 更新 我直接在Graphics32代码上实现了这个功能,使用它已经提供的任何颜色混合例程,并且它满足了我的需求。这是一组额外的FillRect函数,它需要另一个参数来使用混合模式。如果官方项目仍然存在,我将尝试将其加入其中。

你的代码目前进展如何? - EProgrammerNotFound
@EProgrammerNotFound 我会添加它,尽管我需要更改的部分非常简单:只是调用FillRectTS函数而已。 - Rodrigo Gómez
我将要移除 Delphi 标签。 - EProgrammerNotFound
@EProgrammerNotFound 为什么?我不在乎有人用 Delphi 发布解决方案或者一些想法,我可以很容易地进行翻译。最后,该库是基于 Delphi 的(在 Delphi 中的用户比 C++Builder 中的用户多得多)-从某种程度上说,我最初没有发布代码,因为这是与编程语言无关的。重要的是 FillRectTS 函数调用,在 Delphi 和 C++Builder 中都是相同的。 - Rodrigo Gómez
2个回答

2
我在Graphics32源代码中添加了以下代码,这样我就可以使用库提供的多种混合模式之一来填充所需的矩形:
TBlendMode = (bmAdd, bmSub, bmDiv, bmMod, bmMax, bmMin, bmDif, bmAvg, bmExc, bmSca);

procedure TCustomBitmap32.FillRectTB(X1, Y1, X2, Y2: Integer; Value: TColor32; BlendMode: TBlendMode);
var
  i, j: Integer;
  P: PColor32;
  blend: TBlendReg;
begin

  case BlendMode of
    bmAdd: blend := ColorAdd;
    bmSub: blend := ColorSub;
    bmDiv: blend := ColorDiv;
    bmMod: blend := ColorModulate;
    bmMax: blend := ColorMax;
    bmMin: blend := ColorMin;
    bmDif: blend := ColorDifference;
    bmAvg: blend := ColorAverage;
    bmExc: blend := ColorExclusion;
    bmSca: blend := ColorScale;
    else blend := MergeReg;
  end;

  try
    Dec(Y2);
    Dec(X2);
    for j := Y1 to Y2 do
    begin
      P := GetPixelPtr(X1, j);
      for i := X1 to X2 do
      begin
        P^ := blend(Value, P^);
        Inc(P);
      end;
    end;
  finally
    EMMS;
    Changed(MakeRect(X1, Y1, X2 + 1, Y2 + 1));
  end;
end;

同样还有一些其他的。
procedure FillRectTSB(X1, Y1, X2, Y2: Integer; Value: TColor32; BlendMode: TBlendMode); overload;
procedure FillRectSB(const ARect: TRect; Value: TColor32; BlendMode: TBlendMode); overload;
procedure FillRectTSB(const ARect: TRect; Value: TColor32; BlendMode: TBlendMode); overload;

这些代码是库提供的副本,但内部调用新的FillRectTB过程。如果对于库维护者有意义,我将把这个代码提交给官方源以便合并。
看起来要把代码合并到官方库不太容易,所以我在这里附上完整的补丁,以防其他人也会发现它有用。
我还在Graphics32.general新闻组中发布了这个内容,以免它被遗忘。 补丁文件链接

1
我建议你将波形显示分成两个不同的层。一个用于背景,另一个用于前景。这样做的好处是可以重复使用现有的代码,只需分为两个步骤:一个处理背景,另一个处理前景。
但更好的方法可能是像Cool Editor Pro一样,使用不同的颜色来渲染所选内容,这样就可以完全摆脱alpha渲染,从而大大提高渲染性能。

Waveform selecion in Cool Editor Pro


谢谢您的回复。实际上我有类似于您提到的东西,但没有使用图层。我有一个包含整个音频波形(通常是24小时的音频)的缓冲区,当需要重新绘制时,我在显示器上绘制所需的部分,然后应用选择和事件。这就是我想要做一些混合的地方,而不仅仅是alpha渲染。我不能按需重绘音频,因为这是非常耗时的任务,每次需要重新绘制时都这样做是不可行的。我将研究图层选项,也许那里有一些可以工作的东西。 - Rodrigo Gómez
生成位掩码,然后使用该位掩码替换所选颜色,这种方法怎么样?虽然我不是很擅长 Graphics32,但在标准画布上,当使用 flod-fill 无法达到效果(非连通的图像部分)时,您可以采用这种方法。 - SilverWarior
我想我要修改Graphics32的代码,以提供一种使用其中一个[已包含]函数来混合颜色的方法。我在想是否有一种开箱即用的方式来实现这个功能,因为他们确实提供了Multiply/Modulate颜色函数,但到目前为止我还没有找到一种方法来实现这个功能,不过实现它应该不难。我已经在我的代码中有一个差不多可以工作的版本,只需要将其移植到Delphi并将其移到Graphics32单元中。 - Rodrigo Gómez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接