TPNGObject - 创建一个新的空白图片并在其上绘制半透明图像

5
我正在构建一个具有“虚拟窗口”的应用程序,输出是TImage对象。
1)该应用程序将窗口皮肤文件加载到TPNGObject中:

skin

2)然后应用程序必须创建一个新的空白TPNGObject,调整所需大小并在该空白图像上绘制皮肤文件。应该看起来像这样:

new form

3)输出最终结果为TImage

output

问题在于我不知道如何创建完全空白的屏幕外图像。当然,我可以每次将皮肤文件渲染到TImage上,但是调整皮肤文件的大小并创建窗口一次更加简单和更好。
我正在使用Gustavo Daud的PNG库,版本为1.564(2006年7月31日)。

4
对于反应不佳的一些可能原因是,你没有清晰地表达出什么失败了,至少在我看来是这样。此外,D7相当古老,并且存在着相当多竞争的PNG类。而且今天是星期天!要有耐心。还有,BMP也无济于事,因为D7对BMP的支持非常差。 - David Heffernan
@Roberts,您能否告诉我们您正在使用的确切PNG库是哪一个? 有许多库可供选择,如果不知道具体使用哪个,我们无法提供帮助。 - Jerry Dodge
在回答你的问题之前,似乎有些人已经误解了你的意思。问题的范围已经改变了。这个问题应该仍然关于特定的PNG构造器产生错误的情况,并且你应该提出另一个关于图层透明图像的问题。正如我在我的两个答案中所表明的那样,所有主要的绘图都应该在位图上完成,并根据需要复制到PNG中。透明度可以在位图中实现,只有当你使用PNG文件时才需要担心PNG。 - Jerry Dodge
非常详细地解释你的问题是很重要的,仅仅用另一个问题来代替你的问题是最不想做的事情。 - Jerry Dodge
3
如何制作空白的PNG图片:(1)创建一个位图,设置其大小和像素格式为24位;(2) 创建PNG对象,然后使用png.Assign(bmp);(3) 使用png.CreateAlpha创建透明度。 - Sertac Akyuz
显示剩余24条评论
4个回答

5
以下使用Martijn Sally的扩展库(pngcomponents)中的“pngfunctions.pas”文件中的CreatePNG程序来处理pngimage。
var
  Bmp, Mask: TBitmap;
  PNG: TPNGObject;
begin
  Bmp := TBitmap.Create;
  Bmp.PixelFormat := pf24bit;
  Bmp.SetSize(64, 64);

  Bmp.Canvas.Brush.Color := clBtnFace;
  Bmp.Canvas.Font.Color := clRed;
  Bmp.Canvas.Font.Size := 24;
  Bmp.Canvas.TextOut(4, 10, 'text');

  Mask := TBitmap.Create;
  Mask.PixelFormat := pf24bit;
  Mask.Canvas.Brush.Color := clBlack;
  Mask.SetSize(64, 64);
  Mask.Canvas.Font.Color := clWhite;
  Mask.Canvas.Font.Size := 24;
  Mask.Canvas.TextOut(4, 10, 'text');

  PNG := TPNGObject.Create;
  CreatePNG(Bmp, Mask, PNG, False);
  PNG.Draw(Canvas, Rect(10, 10, 74, 74));

  // finally, free etc...

这是输出结果(黑白方块是 TShapes):

enter image description here

我不知道如何使用这个,我需要在这张新的空白图像上绘制一些PNG。 - Little Helper
@Roberts,所以你是在不同的“png”中绘制,然后将它们合并到另一个空白的png中? - Sertac Akyuz
1
@Roberts - 谁知道呢?显然我完全没有理解这个问题...我修改了问题,让你可以取消投票。请这么做,因为其他答案至少和这个相关 :)。 - Sertac Akyuz
@Roberts嗯,您之前从未提到过“在空白PNG上绘制几个PNG”,您只提到了“向PNG画布绘制”。对于回答您问题的人来说,具体细节非常有帮助。 - Jerry Dodge
3
这里没有坏人,只是缺乏细节导致帮助不足。 - Jerry Dodge
显示剩余3条评论

3
我的另一个答案是我建议的另一种选择。然而,你的问题仍然存在一个问题:PNG库必须要么有一个bug防止任何画布绘制可见(使用CreateBlank构造函数与COLOR_RGBALPHA作为颜色类型),要么我们都错过了什么。
看起来我唯一能看到的解决方法是(正如你在编辑中提到的)使用位图来进行绘制。使用这个位图的透明属性(Transparent: Bool和TransparentColor: TColor)来设置你的图像的透明区域,然后当你需要一个透明的PNG时,只需将该位图复制到新的PNG对象中...
BMP.Width:= 100;
BMP.Height:= 100;
BMP.Transparent:= True;
BMP.TransparentColor:= clWhite;
BMP.Canvas.Brush.Style:= bsSolid;
BMP.Canvas.Brush.Color:= clWhite;
BMP.Canvas.FillRect(BMP.Canvas.ClipRect);
BMP.Canvas.Brush.Color:= clBlue;
BMP.Canvas.Ellipse(10, 10, 90, 90);
PNG.Assign(BMP);

图片中的白色区域应该是透明的。还有其他实现透明区域的方法,但那是另一个话题。

图片:

您是否试图做到这一点?

enter image description here


它能工作。但是如果我画的东西不是完全透明的,那么这些像素就会变成白色。所以这不是一个答案。:( - Little Helper
@Roberts,我不知道你所说的像素变白是什么意思,但这似乎是另一个问题的主题。 - Jerry Dodge
我的意思是,你需要提供一些关于你的意思的例子。“像素变白”可能有成千上万种不同的含义。 - Jerry Dodge
我已经删除了这个项目,但是我不知道该怎么说。你使用了 TransparentColor = clWhite,而我正在将我的几乎透明的像素绘制在空白图像上。这些像素看起来像是在它们下面涂了白色。我可以自己画出来并发送给你照片。正如我所说,我已经删除了这个项目,也没有时间再做一个新的,所以我会自己画。 - Little Helper
在任何PNG编辑器中打开这张图片,你就会看到问题了。边框不是不透明的,也不是完全透明的,但当使用你的代码时,你会发现它们是白色的。 :( - Little Helper
我想制作一个空白的PNG,然后在上面画一些边框。我正在绘制PNG时已经加载了背景图像画布。由于我正在Image Canvas上绘制PNG,透明度保持不变。看起来很酷。我可以简单地在Image canvas上绘制这些边框,但是为了避免浪费CPU,我想制作一个PNG,并在需要时将其绘制在Image上。 - Little Helper

2

我向那些被我搞糊涂的人道歉。

事实证明,CreateBlank 的工作效果很好。问题是我在一个 PNG 画布上绘制另一个 PNG (PNG.Canvas.Draw)。 Canvas 并不真正支持透明度。要在另一个 PNG 上绘制半透明 PNG,您需要一个将两个层合并在一起的过程/函数。通过一些谷歌搜索,我最终找到了这个过程:

procedure MergePNGLayer(Layer1, Layer2: TPNGObject; Const aLeft, aTop: Integer);
var
  x, y: Integer;
  SL1,  SL2,  SLBlended: pRGBLine;
  aSL1, aSL2, aSLBlended: PByteArray;
  blendCoeff: single;
  blendedPNG, Lay2buff: TPNGObject;
begin
  blendedPNG := TPNGObject.Create;
  blendedPNG.Assign(Layer1);
  Lay2buff:=TPNGObject.Create;
  Lay2buff.Assign(Layer2);
  SetPNGCanvasSize(Layer2, Layer1.Width, Layer1.Height, aLeft, aTop);
  for y := 0 to Layer1.Height - 1 do
  begin
    SL1 := Layer1.Scanline[y];
    SL2 := Layer2.Scanline[y];
    aSL1 := Layer1.AlphaScanline[y];
    aSL2 := Layer2.AlphaScanline[y];
    SLBlended := blendedPNG.Scanline[y];
    aSLBlended := blendedPNG.AlphaScanline[y];
    for x := 0 to Layer1.Width - 1 do
    begin
      blendCoeff:=aSL1[x] * 100/255/100;
      aSLBlended[x] := round(aSL2[x] + (aSL1[x]-aSL2[x]) * blendCoeff);
      SLBlended[x].rgbtRed   := round(SL2[x].rgbtRed + (SL1[x].rgbtRed-SL2[x].rgbtRed) * blendCoeff);
      SLBlended[x].rgbtGreen := round(SL2[x].rgbtGreen + (SL1[x].rgbtGreen-SL2[x].rgbtGreen) * blendCoeff);
      SLBlended[x].rgbtBlue  := round(SL2[x].rgbtBlue + (SL1[x].rgbtBlue-SL2[x].rgbtBlue) * blendCoeff);
    end;
  end;
  Layer1.Assign(blendedPNG);
  Layer2.Assign(Lay2buff);
  blendedPNG.Free;
  Lay2buff.Free;
end;


用法:

var
  PNG1, PNG2: TPNGObject;
begin
  PNG1 := TPNGObject.CreateBlank(COLOR_RGBALPHA, 16, 500, 500);
  PNG2 := TPNGObject.Create;
  PNG2.LoadFromFile('...*.png');
  MergePNGLayer(PNG1, PNG2, 0, 0);
  // PNG1 is the output

我再次向那些想要帮忙但由于不理解我的人道歉。


0

我对这个问题没有答案,但我找到了如何使用任何PNG编辑器获得相同结果的方法。我创建了一个空的1000x1000 PNG图像并将其保存在我的应用程序目录中。然后我在我的程序中打开这个图像并将图像调整为所需大小(当然是更小的),这就是诀窍。


我猜那可能会起作用,但那似乎是一个不太好的替代方案。对于这个问题肯定有更好的解决方案,他们不会无缘无故地添加这个“CreateBlank”构造函数的。 - Jerry Dodge
@JerryDodge 是的,他们不是无缘无故地添加了CreateBlank。但是昨天我花了5个小时在这上面。我发现CreateBlank只是将所有像素的透明度更改为100,当你绘制一些东西时,你看不到它,因为它是100%透明的。(我对这个理论不太确定) - Little Helper

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