Нарисуйте картинку в формате PNG на холсте без использования GDI +

Мне нужно нарисовать PNG (в TPicture) на холсте со следующими требованиями:

  1. Он должен быть очень быстрым (из-за того, что на целевом ПК работает медленный процессор).
  2. Он не требует дополнительных библиотек, которые увеличивают размер exe (из-за автоматического обновления целевого ПК через мобильное соединение 2G).

Приведенный ниже код выполняет свою работу, но использует GDI + и:

  1. Это намного медленнее, чем рисование простого непрозрачного растрового изображения с использованием BitBlt. На быстром процессоре время прорисовки увеличивается с 1 мс до 16 мс. На медленном процессоре оно увеличивается со 100 мс до 900 мс.
  2. Увеличивает размер exe примерно на 0,5 МБ.

Вот код GDI +. Он предназначен для возврата к стандартному BitBlt, если:

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  ComCtrls, ExtCtrls,

  GDIPObj, GDIPAPI;
...

procedure DrawPictureToBitmap(Bitmap: TBitmap; X, Y: Integer; Picture: TPicture);

  function PictureToGPBitmap(Picture: TPicture): TGPBitmap;
  var
    MemStream: TMemoryStream;
  begin
    MemStream := TMemoryStream.Create;
    try
      Picture.Graphic.SaveToStream(MemStream);

      MemStream.Position := 0;

      Result := TGPBitmap.Create(TStreamAdapter.Create(MemStream));
    finally
      FreeAndNil(MemStream);
    end;
  end;

var
  GDICanvas: TGPGraphics;
  GPImage: TGPImage;
begin
  GDICanvas := TGPGraphics.Create(Bitmap.Canvas.Handle);
  try
    GPImage := PictureToGPBitmap(Picture);
    try
      GDICanvas.DrawImage(GPImage, X, Y);

      // Did the draw succeed?
      if GDICanvas.GetLastStatus <> Ok then
      begin
        // No, try a BitBlt!
        BitBlt(Bitmap.Canvas.Handle, X, Y, Bitmap.Height, Bitmap.Width, Picture.Bitmap.Canvas.Handle, 0, 0, SRCCOPY);
      end;
    finally
      FreeAndNil(GPImage);
    end;
  finally
    FreeAndNil(GDICanvas);
  end;
end;

Обновление 1

Используя предложение Дэвида, мне удалось избавиться от GDI +, используя встроенную в Delphi поддержку PNG.

procedure DrawPictureToBitmap(Bitmap: TBitmap; X, Y: Integer; Picture: TPicture);
var
  PNG: TPngImage;
  MemStream: TMemoryStream;
begin
  PNG := TPngImage.Create;
  try
    MemStream := TMemoryStream.Create;
    try
      Picture.Graphic.SaveToStream(MemStream);

      MemStream.Position := 0;

      PNG.LoadFromStream(MemStream);
    finally
      FreeAndNil(MemStream);
    end;

    PNG.Draw(Bitmap.Canvas, Rect(X, Y, X + Picture.Width, Y + Picture.Height));
  finally
    FreeAndNil(PNG);
  end;
end;

К сожалению, время прорисовки точно такое же, как и у метода GDI +. Есть ли способ, которым это можно оптимизировать?


person norgepaul    schedule 02.08.2013    source источник
comment
@DavidHeffernan - Хорошая идея. Это избавляет от зависимости GDI +, но время отрисовки не улучшается. Смотрите мое обновление выше.   -  person norgepaul    schedule 02.08.2013
comment
Почему не просто DestinationCanvas.Draw (X, Y, Image1.Picture.Graphic);   -  person bummi    schedule 02.08.2013


Ответы (1)


Мне кажется, что вы напрасно берете графику в памяти, сжимаете в PNG, а затем распаковываете. Вы можете нарисовать графику напрямую.

Просто вызовите Draw на холсте растрового изображения, передав Picture.Graphic:

procedure DrawPictureToBitmap(Bitmap: TBitmap; X, Y: Integer; Picture: TPicture);
begin
  Bitmap.Canvas.Draw(X, Y, Picture.Graphic);
end;

В этот момент вы, вероятно, решите, что DrawPictureToBitmap бесполезен, удалите его и вызовите Bitmap.Canvas.Draw() напрямую.

Это также будет иметь то преимущество, что ваше изображение не ограничивается изображением PNG, согласно коду в вопросе.

person David Heffernan    schedule 02.08.2013
comment
Отлично, исправлены обе проблемы. - person norgepaul; 02.08.2013