Как смешать цвета (раскрасить указанным альфа-значением) область холста, используя чистый GDI?

Я хотел бы смешать цвета (раскрасить по заданному альфа-значению) области холста, используя чистыйWindows GDI (то есть без GDI +, DirectX или подобного, без OpenGL, без ассемблера или сторонних библиотек).

Я создал следующую функцию, и я хотел бы знать, есть ли более эффективный или более простой способ сделать это:

<code>procedure ColorBlend(const ACanvas: HDC; const ARect: TRect;
  const ABlendColor: TColor; const ABlendValue: Integer);
var
  DC: HDC;
  Brush: HBRUSH;
  Bitmap: HBITMAP;
  BlendFunction: TBlendFunction;
begin
  DC := CreateCompatibleDC(ACanvas);
  Bitmap := CreateCompatibleBitmap(ACanvas, ARect.Right - ARect.Left,
    ARect.Bottom - ARect.Top);
  Brush := CreateSolidBrush(ColorToRGB(ABlendColor));
  try
    SelectObject(DC, Bitmap);
    Windows.FillRect(DC, Rect(0, 0, ARect.Right - ARect.Left,
      ARect.Bottom - ARect.Top), Brush);
    BlendFunction.BlendOp := AC_SRC_OVER;
    BlendFunction.BlendFlags := 0;
    BlendFunction.AlphaFormat := 0;
    BlendFunction.SourceConstantAlpha := ABlendValue;
    Windows.AlphaBlend(ACanvas, ARect.Left, ARect.Top,
      ARect.Right - ARect.Left, ARect.Bottom - ARect.Top, DC, 0, 0,
      ARect.Right - ARect.Left, ARect.Bottom - ARect.Top, BlendFunction);
  finally
    DeleteObject(Brush);
    DeleteObject(Bitmap);
    DeleteDC(DC);
  end;
end;
</code>

Чтобы понять, что должна делать эта функция, смотрите следующие (различающие :-) изображения:

И код, который может отрисовыватьthis image в верхней левой части формы, как показано выше:

<code>uses
  PNGImage;

procedure TForm1.Button1Click(Sender: TObject);
var
  Image: TPNGImage;
begin
  Image := TPNGImage.Create;
  try
    Image.LoadFromFile('d:\6G3Eg.png');
    ColorBlend(Image.Canvas.Handle, Image.Canvas.ClipRect, $0000FF80, 175);
    Canvas.Draw(0, 0, Image);
  finally
    Image.Free;
  end;
end;
</code>

Есть ли более эффективный способ сделать это, используя чистый GDI или Delphi VCL?

 Remy Lebeau10 мая 2012 г., 23:36
AlphaBlend() являетс простой способ сделать это - пусть ОС сделает всю работу. В противном случае вам придется напрямую управлять пикселями PNG. Единственная оптимизация, которую вы могли бы сделать дальше, - это создать сплошное растровое изображение один раз и использовать его снова и снова.
 johnathan11 мая 2012 г., 01:46
Почувствуйте, что вы используете com-интерфейс Windows Api, почему бы не использовать Direct 2D? это быстрее (если ваша система Windows Vista / 7). Есть на что посмотреть;)
 Remy Lebeau10 мая 2012 г., 22:43
Почему ты думаешь, что то, что у тебя уже есть, неэффективно?
 Uli Gerhardt11 мая 2012 г., 08:28
@ TLama: Эти статьи может быть интересно.
 TLama10 мая 2012 г., 22:45
Вот почему я спросил. Я знаю, что это эффективно, но я хочу больше (если возможно: -)

Ответы на вопрос(1)

Решение Вопроса

что-то тип

Canvas.Draw(Arect.Left, ARect.Top, ABitmap, AAlphaBlendValue);

комбинируется с FillRect для смешанного цвета

Обновит: А вот код, максимально приближенный к вашему интерфейсу, но чистый VCL.
Может быть не так эффективно, но гораздо проще (и несколько портативно).
Как сказал Реми, чтобы рисовать на форме псевдопостоянным способом, тебе придется использовать OnPaint ...

procedure ColorBlend(const ACanvas: TCanvas; const ARect: TRect;
  const ABlendColor: TColor; const ABlendValue: Integer);
var
  bmp: TBitmap;
begin
  bmp := TBitmap.Create;
  try
    bmp.Canvas.Brush.Color := ABlendColor;
    bmp.Width := ARect.Right - ARect.Left;
    bmp.Height := ARect.Bottom - ARect.Top;
    bmp.Canvas.FillRect(Rect(0,0,bmp.Width, bmp.Height));
    ACanvas.Draw(ARect.Left, ARect.Top, bmp, ABlendValue);
  finally
    bmp.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Image: TPNGImage;
begin
  Image := TPNGImage.Create;
  try
    Image.LoadFromFile('d:\6G3Eg.png');
    ColorBlend(Image.Canvas, Image.Canvas.ClipRect, $0000FF80, 175);
    Canvas.Draw(0, 0, Image);
    // then for fun do it to the Form itself
    ColorBlend(Canvas, ClientRect, clYellow, 15);
  finally
    Image.Free;
  end;
end;
 Sertac Akyuz16 сент. 2013 г., 00:08
TheFillRect звонок не нужен. При изменении размера растрового изображения в качестве фона будет использоваться цвет кисти.
 François11 мая 2012 г., 23:13
@ TLama, конечно, но Canvas.ClipRect включает в себя немного больше, чем просто построение Rect. Так что вместо этого я пошел набирать тексты ...; -)
 TLama10 мая 2012 г., 22:59
+ 1, спасибо! Я никогда не видел эту перегрузку раньше (у меня Delphi 2009 вручную). Приятно узнавать что-то новое, но мне нужно применить эффект colorize.
 TLama11 мая 2012 г., 23:01
Франсуа, принято, спасибо! Btw.bmp.Canvas.FillRect(bmp.Canvas.ClipRect); может также пригодиться ;-) @Remy, спасибо за примечание о постоянстве рисования, но здесь это не нужно, так как эта функция была предназначена для использования в VirtualTreeView'sOnBeforeCellPaint где фон может быть анимирован, например (я забыл упомянуть об этом, мой плохой).
 Remy Lebeau10 мая 2012 г., 23:40
Вы можете создать сплошное растровое изображение какTBitmap объект иDraw() это с альфа указанным на вашем изображенииCanvas, тогдаDraw() готовое изображение на вашей форме. Кстати, если вы хотите, чтобы окончательный рисунок был постоянным, вы должны нарисовать его на форме из ееOnPaint событие, а не в кнопкеOnClick мероприятие. Вы можете подготовить изображение вOnClick событие, а затемInvalidate() форму, чтобы вызвать перерисовку, а затем рисовать текущее изображение всякий раз, когдаOnPaint событие запущено. Или просто поместите окончательное изображение вTImage составная часть

Ваш ответ на вопрос