Сохранение прозрачных слоев ImgView32 в PNG
У меня проблемы с сохранением слоя ImgView32 в качестве прозрачного PNG. Я использую код из этого вопроса, чтобы сделать сохранение. Однако изображение сохраняется с белым фоном.
Вот как я инициализирую свой ImgView32, создаю на нем слой и затем рисую на нем линию:
procedure TputLine.FormCreate(Sender: TObject);
var
P: TPoint;
W, H: Single;
imwidth: integer;
imheight: integer;
begin
imwidth := Iv1.Width;
imheight := Iv1.Height;
with iv1 do
begin
Selection := nil;
Layers.Clear;
Scale := 1;
Scaled := True;
Bitmap.DrawMode := dmTransparent;
Bitmap.SetSize(imwidth, imheight);
Bitmap.Canvas.Pen.Width := 4;
end;
BL := TBitmapLayer.Create(iv1.Layers);
try
BL.Bitmap.DrawMode := dmTransparent;
BL.Bitmap.SetSize(imwidth,imheight);
BL.Bitmap.Canvas.Pen.Width := penwidth;
BL.Bitmap.Canvas.Pen.Color := pencolor;
BL.Location := GR32.FloatRect(0, 0, imwidth, imheight);
BL.Scaled := False;
except
BL.Free;
raise;
end;
end;
Итак, iv1 - это имя моего ImgView32. Затем я рисую линию, используя этот код:
var
bm32:TBitmapLayer;
...
begin
bm32:=(iv1.Layers[0] as TBitmapLayer).Bitmap;
bm32.canvas.pen.color:=clwhite;
bm32.canvas.brush.color:=clwhite;
bm32.canvas.rectangle(0,0,bm32.width-1, bm32.height-1);
bm32.canvas.Pen.Color:=WinColor(ColorPickerGTK1.SelectedColor);
bm32.canvas.brush.color:=clWhite;
bm32.Canvas.Pen.Width:=3;
bm32.Canvas.MoveTo(0,bm32.Height);
bm32.Canvas.LineTo(0+150,bm32.Height-250);
end;
Если я использую clWhite32 для вышеуказанного кода при рисовании прямоугольника, то при сохранении PNG фон imgView становится черным... Так что я действительно не понимаю проблему.
Я делаю экономию так:
procedure TputLine.Button2Click(Sender: TObject);
var
myLay:TBitmapLayer;
begin
mylay := iv1.Layers.Items[0] as TBitmapLayer;
SavePNGTransparentX(mylay.Bitmap);
end;
и актуальный код сохранения (по ссылке, описанной выше)
procedure TPutLine.SavePNGTransparentX(bm32:TBitmap32);
var
Y: Integer;
X: Integer;
Png: TPortableNetworkGraphic32;
function IsWhite(Color32: TColor32): Boolean;
begin
Result:= (TColor32Entry(Color32).B = 255) and
(TColor32Entry(Color32).G = 255) and
(TColor32Entry(Color32).R = 255);
end;
begin
bm32.ResetAlpha;
for Y := 0 to bm32.Height-1 do
for X := 0 to bm32.Width-1 do
begin
if IsWhite(bm32.Pixel[X, Y]) then
bm32.Pixel[X,Y]:=Color32(255,255,255,0);
end;
Png:= TPortableNetworkGraphic32.Create;
Png.Assign(bm32);
Png.SaveToFile('C:\ThisShouldBeTransparent.png');
Png.Free;
end;
Я не понимаю, почему это не сохраняет слой как прозрачный PNG. Как я могу это исправить? Любая идея приветствуется.
Вы можете повторить мою проблему, используя приведенный выше код. Он использует GR32_PNG и GR32_PortableNetworkGraphic. Вам нужно только добавить элемент управления TImgView32 в форму и добавить код, указанный выше.
1 ответ
Причина проблемы, кажется, двоякая.
- Во-первых, когда вы звоните
Png.Assign(bm32);
в блокеGR32_PNG
он пытается выяснить, в каком наименьшем формате будет храниться изображение. Если изображение имеет менее 256 различных цветов, оно создает формат палитры, и в зависимости от того, сколько цветов оно находит, глубина в битах может стать 1, 2, 4 или 8. Насколько мне известно, только изображения с TrueColor и Alpha могут быть сохранены в качестве переменной прозрачностиpng
изображений. - Во-вторых, вы рисуете только одним цветом, что вызывает вышеуказанную проблему. Это, конечно, не ваша вина, IMO вышеупомянутый анализ должен быть в состоянии обойти.
TPortableNetworkGraphic32
класс имеет два свойства, BitDepth
а также ColorType
который контролирует формат png
изображение, которое было бы полезно, если бы они были настраиваемыми! Попытка установить их в коде как:
Png.BitDepth := 8;
Png.ColorType := ctTrueColorAlpha;
приводит к исключениям
EPngError с сообщением "Битовая глубина не может быть указана напрямую!
EPngError с сообщением 'Color Type может быть еще не указан напрямую!
Из формулировки можно предположить дальнейшее развитие в будущем.
Лечение
Чтобы обойти описанный выше анализ изображения, вы можете изменить строку 459 в GR32_PNG.pas
,
procedure TPortableNetworkGraphic32.AssignPropertiesFromBitmap32()
var
...
begin
...
IsPalette := True; // <--- change to False
...
Это позаботится об анализе глубины цвета и предотвратит создание палитры, если менее 256 цветов. После этого взлома вы можете использовать SavePNGTransparentX()
процедура для сохранения TBitmap32
к .png
файл и сохранение прозрачности.
Затем есть еще одно изменение, которое вас может заинтересовать в отношении SavePNGTransparentX()
процедура. Как вы уже видели, он требует, чтобы фон вашей поверхности рисования был белым, потому что он специально устанавливает Alpha
канал на ноль для всех белых пикселей. TBitmapLayer
однако инициализируется со всеми пикселями (RGBA) как всеми нулями, поэтому цветовые компоненты каждого пикселя составляют черный цвет (который не виден, потому что альфа-канал равен нулю). Поэтому вам необходимо заполнить слой для рисования белым, что делает его непрозрачным, что снова покрывает все нижние слои (под слоем для рисования).
Чтобы исправить это, вы можете
- удалить инициализацию слоя рисования для всего белого
- изменить
IsWhite
функция вIsBlack
функция - изменить назначение прозрачных пикселей
Код станет
procedure TForm8.SavePNGTransparentX(bm32:TBitmap32);
var
Y: Integer;
X: Integer;
Png: TPortableNetworkGraphic32;
function IsBlack(Color32: TColor32): Boolean;
begin
Result:= (TColor32Entry(Color32).B = 0) and
(TColor32Entry(Color32).G = 0) and
(TColor32Entry(Color32).R = 0);
end;
function IsWhite(Color32: TColor32): Boolean;
begin
Result:= (TColor32Entry(Color32).B = 255) and
(TColor32Entry(Color32).G = 255) and
(TColor32Entry(Color32).R = 255);
end;
begin
bm32.ResetAlpha;
for Y := 0 to bm32.Height-1 do
for X := 0 to bm32.Width-1 do
begin
// if IsWhite(bm32.Pixel[X, Y]) then
// bm32.Pixel[X,Y]:=Color32(255,255,255, 0);
if IsBlack(bm32.Pixel[X, Y]) then
bm32.Pixel[X,Y]:=Color32( 0, 0, 0, 0);
end;
Png:= TPortableNetworkGraphic32.Create;
Png.Assign(bm32);
Png.SaveToFile('C:\tmp\imgs\ThisShouldBeTransparent3.png');
Png.Free;
end;
С этим изменением вы можете видеть слои под вашим слоем для рисования, и полученный файл вашего слоя для рисования будет иметь все неотрисованные пиксели прозрачными. Однако в целом вышеупомянутая процедура все еще имеет одну проблему, заключающуюся в том, что частично прозрачные пиксели теряют прозрачность, но это останется в будущем.
Вот несколько изображений. Сначала ImageView с битовой картой, загруженной на нижний слой:
Затем я рисую синий крест на BL
слой (см. ваш код), используя в основном код в вашем Button1Click()
но без белого прямоугольника.
Тогда я сохраняю BL
слой к .png
и просмотрите его с помощью Windows Explorer "Preview":