Нарисуйте непрозрачные линии на прозрачном TBitmap - PullRequest
1 голос
/ 27 апреля 2011

У меня есть пустое растровое изображение, и у меня есть процедура рисования, которая получает TCanvas.Процедура рисования является частью большой библиотеки, и поэтому фактически вне моего контроля.

Проще говоря: я хочу, чтобы пиксели были непрозрачными, если процедуры рисования влияют на них;каждый пиксель, который не был затронут, должен оставаться прозрачным.Поскольку я не контролирую цвета, которые будут использоваться в процедурах рисования, я бы предпочел не использовать свойство TransparentColor.

Есть ли способ добиться этого?Какие-то настройки я могу использовать, чтобы указать, что холст должен влиять на альфа-слой пикселей, на которые он рисует?


Обновление : я использую Delphi 2010, и здесьследует коду, который я пробовал:

Bmp := TBitmap.Create;
try
  Bmp.PixelFormat := pf32bit;
  Bmp.Transparent := False;
  Bmp.SetSize(Width, Height);

  // Ensure all pixels are black with opacity 0 (= fully transparent)
  ScanlineWidth := Width * SizeOf(TRGBQuad);
  for y := 0 to Bmp.Height - 1 do begin
    ZeroMemory(Bmp.ScanLine[y], ScanlineWidth);
  end;

  // call drawing routines here
  DrawContours(Bmp.Canvas, Width, Height);

  {$IFDEF DEBUG}
  Bmp.SaveToFile(OwnPath + 'Contours-' + IntToStr(GetTickCount) + '.bmp');
  {$ENDIF}

  Result := Bmp.ReleaseHandle;
finally
  Bmp.Free;
end;

Ответы [ 3 ]

1 голос
/ 27 апреля 2011

Следующим подходом может быть вариант, если рисование не дорого.Нарисуйте три раза, один раз на прозрачном растровом изображении, один раз на 1-битное растровое изображение с белым фоном и один раз на 1-битное растровое изображение с черным фоном, чтобы получить изменения, которые необходимо внести в альфа-канал (поскольку Тим прав -изначально растровое изображение полностью прозрачно, нам нужно найти непрозрачные пиксели).

Редактировать: В первой версии использовался только один 1-разрядный растровый рисунок с белым фоном.Это, к сожалению, не обнаружило более легких картин.Итак, давайте сделаем еще один шаг и создадим два 1-битных растровых изображения.Хотя все больше и больше растровых изображений становится все менее элегантным.

var
  Bmp: TBitmap;
  ScanlineWidth: Integer;
  x: Integer;
  y: Integer;
  // this one will track dark paintings
  BmpBitMaskWhite:TBitmap;
  // this one will track light paintings
  BmpBitMaskBlack:TBitmap;
  Row32bit, Row1bitWhite, Row1bitBlack:PByteArray;
  ByteAccess:Byte;

  // Init bitmaps needed for tracking pixels we need to change the alpha channel for
  procedure InitAlphaMaskBitmaps;
  begin
    BmpBitMaskWhite.PixelFormat:=pf1bit;          // <= one bit is enough
    BmpBitMaskWhite.Transparent:=False;
    BmpBitMaskWhite.SetSize(Width, Height);
    BmpBitMaskWhite.Canvas.Brush.Color:=clWhite;  // <= fill white; changes can then be seen as black pixels
    BmpBitMaskWhite.Canvas.FillRect(Rect(0,0,Width, Height));

    BmpBitMaskBlack.PixelFormat:=pf1bit;          // <= one bit is enough
    BmpBitMaskBlack.Transparent:=False;
    BmpBitMaskBlack.SetSize(Width, Height);
    BmpBitMaskBlack.Canvas.Brush.Color:=clBlack;  // <= fill black; changes can then be seen as white pixels
    BmpBitMaskBlack.Canvas.FillRect(Rect(0,0,Width, Height));
  end;

begin
  Bmp := TBitmap.Create;
  BmpBitMaskWhite:=TBitmap.Create;
  BmpBitMaskBlack:=TBitmap.Create;
  try
    Bmp.PixelFormat := pf32bit;
    Bmp.Transparent := False;
    Bmp.SetSize(Width, Height);

    InitAlphaMaskBitmaps;

    // ensure all pixels are black with opacity 0 (= fully transparent)
    ScanlineWidth := Width * SizeOf(TRGBQuad);
    for y := 0 to Bmp.Height - 1 do
    begin
      ZeroMemory(Bmp.ScanLine[y], ScanlineWidth);
    end;

    // call drawing routines here
    DrawContours(Bmp.Canvas, Width, Height);
    // call again to get areas where we need to un-transparent the Bmp (this is for dark paintings)
    DrawContours(BmpBitMaskWhite.Canvas, Width, Height);
    // call again to get areas where we need to un-transparent the Bmp  (this is for light paintings)
    DrawContours(BmpBitMaskBlack.Canvas, Width, Height);

    // modify alpha channel of Bmp by checking changed pixels of BmpBitMaskWhite and BmpBitMaskBlack
    // iterate all lines
    for y := 0 to Bmp.Height - 1 do
    begin
      // iterate all pixels
      for x := 0 to Bmp.Width - 1 do
      begin
        Row32bit:=PByteArray(Bmp.ScanLine[y]);
        Row1bitWhite:=PByteArray(BmpBitMaskWhite.ScanLine[y]);
        Row1bitBlack:=PByteArray(BmpBitMaskBlack.ScanLine[y]);

        // Now we need to find the changed bits in BmpBitMaskWhite and BmpBitMaskBlack to modify the corresponding
        // alpha-byte in Bmp. Black areas (Bit=0) in BmpBitMaskWhite are the ones that
        // have been drawn to, as well as white areas (Bit=1) in BmpBitMaskBlack.
        // Not pretty, but works.
        ByteAccess:=1 shl (7-x mod 8);
        if ((Row1bitWhite[x div 8] and ByteAccess)=0) or
           ((Row1bitBlack[x div 8] and ByteAccess)<>0) then
        begin
          Row32bit[x*4+3]:=255;
        end;
      end;
    end;

    {$IFDEF DEBUG}
    Bmp.SaveToFile('C:\Temp\Contours-' + IntToStr(GetTickCount) + '.bmp');
    BmpBitMaskWhite.SaveToFile('C:\Temp\Contours-' + IntToStr(GetTickCount) + '_BitMaskWhite.bmp');
    BmpBitMaskBlack.SaveToFile('C:\Temp\Contours-' + IntToStr(GetTickCount) + '_BitMaskBlack.bmp');
    {$ENDIF}

//    Result := Bmp.ReleaseHandle;
  finally
    Bmp.Free;
    BmpBitMaskWhite.Free;
    BmpBitMaskBlack.Free;
  end;

Кстати: единственная программа, которая показала мне прозрачность в этих растровых изображениях правильно, была PixelFormer .Другие (Gimp, IrfanView, Windows Fax Fessy, FastStone Image Viewer, MS Paint) все окрашивали прозрачную область в черный цвет.

1 голос
/ 27 апреля 2011

Если я правильно понимаю :
Вы получаете полностью прозрачное растровое изображение каждый раз.

Предложения
Вы можете переписать функцию DrawContours, чтобы установить альфа-значение для каждой области холста, на котором он рисует. Я бы предложил этот маршрут, но я думаю, что вы где-то упоминали, что не хотите переписывать эти функции.

Единственные другие варианты, которые я могу придумать, - это (поскольку ваш начальный bmp полностью черный и прозрачный) после того, как вы передадите его в функцию DrawContrours, проверьте каждый пиксель и, если он больше не черный, измените альфа на 255. Но это имеет тот же результат, что и простое использование прозрачного цвета и свойства, которое вы уже сказали, что не хотите использовать.

Мысли
Я не знаю способа (и скептически отношусь к тому, что он вообще есть) изменить растровое изображение так, чтобы любая процедура рисования, которой вы ее передаете, обрабатывала бы ее иначе, чем была спроектирована.

Окончательный ответ :
Перепишите процедуру рисования DrawContrours или используйте Transparent color

0 голосов
/ 27 апреля 2011

После работы с несколькими опциями, я получил следующее:

  • Установите 32-битный формат пикселей;
  • Установите AlphaFormat на afIgnored;
  • Установите Transparent в True;(это важно)
  • Очистить альфа-значения;
  • Вызвать процедуру рисования;
  • Затем назначить растровое изображение PNG и сохранить его.

В коде:

Bmp := TBitmap.Create;
try
  Bmp.PixelFormat := pf32bit;
  Bmp.AlphaFormat := afIgnored;
  Bmp.Transparent := True;

  Bmp.SetSize(Width, Height);

  // Make bitmap fully transparent
  ScanlineWidth := Width * SizeOf(TRGBQuad);
  for y := 0 to Bmp.Height - 1 do begin
    ZeroMemory(Bmp.ScanLine[y], ScanlineWidth);
  end;

  DrawContours(Bmp.Canvas);

  PNG := TPNGImage.Create;
  try
    PNG.Assign(Bmp);
    PNG.SaveToFile('contours.png'); // => this one has the wanted transparency!
  finally
    PNG.Free;
  end;
finally
  Bmp.Free;
end;
...