TPNGObject - Create a new blank image and draw translucent images on it
Asked Answered
L

4

5

I am building an application that has "virtual windows". The output is TImage object.

1) The application loads window skin files into TPNGObject's:

skin

2) Then application has to create a new blank TPNGObject, and resize the skin files to needed sizes and draw them on that blank image. Should look something like this:

new form

3) And the final output on TImage:

output

The problem is that I do know how to create a completely blank off screen image. Of course I could simply render the skin files on to TImage each time, but it's easier and better to resize skin files and create the window once, instead.
I'm using the PNG Library by Gustavo Daud, version 1.564 (31st July, 2006).

Lotetgaronne answered 18/12, 2011 at 19:13 Comment(29)
Some possible explanations for the poor response are that you didn't, at least to my mind, state clearly enough what is failing. Also, D7 is rather ancient and there were quite a lot of competing PNG classes. And it's Sunday! Be patient. Also, a BMP won't help because D7 BMP support was pretty lame.Drunkard
@Roberts Can you please let us know exactly which PNG library you are using? There are many out there, and we can't help without knowing which one.Synchrotron
@Jerry, Roberts is using pngimage by Gustavo Daud, version at least 1.56. Roberts, your comment seems reasonable enough, but open the produced image in an image editor and all channels are empty.Haerle
I'm investigating this and I'm just as stumped as you. I'm explicitly drawing to the canvas, but no image is coming out...Synchrotron
Roberts, do you have Martijn Saly's (Thany) extensions, specifically 'pngfunctions.pas'?Haerle
@SertacAkyuz I have only PNGFunctions.pas.Lotetgaronne
Before the people trying to answer you question get out of hand, it looks as if you're meaning to ask something else. The scope of the question has changed. This question should remain about the specific PNG constructor giving bad results, and you should ask another question about layering transparent images. As I thought I made clear in both of my answers, all the primary drawing should be done on bitmaps, and copied to a PNG as needed. Transparency can be accomplished in bitmaps, and the only time you should worry about PNG is if you're working with PNG files.Synchrotron
And to elaborate on my last comment: You should take a step back and take a look at the bigger picture of whatever project you're trying to do. The PNG library (although possible) isn't necessarily intended just for transparent drawing. It's meant for working with PNG files.Synchrotron
@Roberts I'm sorry, but the question is in no way related to the originally asked question. My answers below are now pointless and will get downvotes from here on out. You should never change your question like you did above. Adding information, yes. Re-wording, yes. But deleting the content of the question and writing another question is a nightmare for us.Synchrotron
PS Great detail to explain what you are asking, just replacing your question with another question is the last thing you want to do.Synchrotron
@JerryDodge It's same question, I didn't mention some things. That's allLotetgaronne
I think the word "Translucent" will make your question much more clear, instead of "Almost Transparent". Almost Transparent made me confused, but if it's Translucency which you mean, then that's a whole different ballgame. Is this what you mean?Synchrotron
@JerryDodge Again sorry, my english is bad, Im from different country and yes, "Translucent", that's what I meant.Lotetgaronne
@JerryDodge Big thanks for your help, also google for PNGFunctions.pas - there are a lot of good functions inside them ;), im learning PNG a little bit better right now.Lotetgaronne
@Roberts I myself barely know PNG and am learning it, but I would still recommend to only use PNG if you intend to work with PNG files. Otherwise, Bitmaps are fully capable of the same things you want.Synchrotron
@Roberts Also part of the reason I want to help you out with this is that I in fact am ironically doing something similar - building customized windows with a glass effect.Synchrotron
@JerryDodge Yes, I see your questions ^^. Since you can't just paint one PNG over second PNG I downloaded source from russian blog and now I can make one PNG from 2 PNG'sLotetgaronne
@JerryDodge But I think I will give up, because I don't know how to make blank PNG. I think my own answer is the only one...Lotetgaronne
@JerryDodge Actually I had already such a program, I have builded it one month ago, but due of backup I forgot to copy it. But now I want to make it from scratch with better window creation process.Lotetgaronne
The problem could be something as simple as forgetting to set the width/height of this PNG object. I've made this mistake plenty times (with bitmaps). Also, by the sounds of it, you're taking on a project way over your head and there will be many little pieces working together - which is always madness.Synchrotron
> "How to make blank PNG" - (1) Create a bitmap, set its size, pixel format 24 bit, (2) Create png object, then png.Assign(bmp) (3) png.CreateAlpha.Haerle
@SertacAkyuz But I don't need a white image!!! Blank, empty - get it?Lotetgaronne
@Roberts - Then use the CreatePNG procedure I've mentioned in the deleted answer. Somehow you don't seem to have got it though..Haerle
@SertacAkyuz That didn't work.Lotetgaronne
It's a completely transparent png that I showed how to write some visible text on it and leave the rest transparent. Isn't that what you mean by a blank png? What can't you do with it? What didn't work?Haerle
@SertacAkyuz Oh, sorry. I don't have that code anymore, you deleted it. I didn't know how to use it. Now after these days I learned how to use PNG. Sorry again. Can you restore it or send to [email protected] ??Lotetgaronne
Undeleted, see if it is a blank png.Haerle
Have you seen FireMonkey and its ability to skin your application for any OS (and actually compile it for those as well).Excipient
@Excipient No, no, no. That isn't what I am doing. I am not skinning my application. I am makking skinnable forms on TImageLotetgaronne
L
2

I apologize to people that I messed their heads up.

It turns out CreateBlank works as wanted. The problem was that I was drawing PNG on PNG canvas (PNG.Canvas.Draw). Canvas doesn't really support transparency. To draw a translucent PNG on another PNG you will need a procedure/function that merges those both layers together. With some googling I ended up with this procedure:

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;


Usage:

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

And again, I am really sorry to users that wanted to help, but couldn't due to not understanding me.

Lotetgaronne answered 2/1, 2012 at 21:11 Comment(0)
H
5

The below uses CreatePNG procedure of 'pngfunctions.pas' of Martijn Sally, from an extension library (pngcomponents) to 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...


Here's the output (black, white squares are TShapes):

enter image description here

Haerle answered 19/12, 2011 at 21:6 Comment(8)
I don't know how to use this, I need to draw some PNG's on this new blank image.Lotetgaronne
@Roberts, so you're drawing into different 'png's, then combining them into a different blank png?Haerle
I am drawing few PNG's over this blank PNG. Any help?Lotetgaronne
@Roberts - Who knows? Evidently I haven't understood the question at all.. I've modified the question so that you can take the vote back. Please do so, since other answers are at least relevant as this one :).Haerle
It's not so hard. 1) Create blank PNG 2) Paint few PNG's on the blank PNG. :) You are doing quite well!Lotetgaronne
@Roberts Well you never mentioned "paint a few PNG's on the blank PNG" before, you only mentioned "drawing" to the PNG's canvas. Specific details are very helpful to people who are answering your questions.Synchrotron
@JerryDodge I know, Im a bad person.Lotetgaronne
No one is a bad person here, just lack of details leads to a lack of help.Synchrotron
S
3

My other answer is another alternative which I suggest. However your question still poses an issue: The PNG library must either have a bug which is preventing any canvas drawing from being visible (after using CreateBlank constructor with COLOR_RGBALPHA as color type) or we're all missing something.

It looks like the only workaround that I can see is (as you mention in your edit) use a Bitmap to do your drawing instead. Use the transparent properties of this bitmap (Transparent: Bool and TransparentColor: TColor) to set up the transparent area of your image, then when you need a transparent PNG, just copy that bitmap over to the new PNG object...

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);

And the white area of the image should be transparent. There are other ways of accomplishing the transparent area, but that's another subject.

Image:

Is this what you're trying to do?

enter image description here

Synchrotron answered 19/12, 2011 at 15:29 Comment(6)
It works. But If I draw something not fully transparent, then those pixels turns white. So it's not an aswer. :(Lotetgaronne
@Roberts I have no idea what you mean by pixels turning white, but it sounds like a subject for a different question.Synchrotron
I mean, you need to put examples about what you mean. "Pixels turn white" could mean thousands of different things.Synchrotron
I deleted the project, but I don't know how to say. You used TransparentColor = clWhite and I am painting my almost transparent pixels on the blank image. The pixels look like it's white painted under them. I can draw It by my self and send you the photo. As I said, I have deleted the project and I have no time to make a new one, so I will paint by my self.Lotetgaronne
Open this image in any PNG editor and you see the problem. i.sstatic.net/e0pxY.png The borders aren't opaque and not fully transparent but you see that they are in white color when using your code. :(Lotetgaronne
I want to make blank PNG, then draw some borders on it. This PNG I am drawing on already loaded with background Image.Canvas. Since I am drawing PNG on Image Canvas, the transparancy stays. And it looks cool. I can just simply draw these borders on Image canvas, but instead wasting CPU I want to make one PNG and paint it on Image when needed.Lotetgaronne
L
2

I apologize to people that I messed their heads up.

It turns out CreateBlank works as wanted. The problem was that I was drawing PNG on PNG canvas (PNG.Canvas.Draw). Canvas doesn't really support transparency. To draw a translucent PNG on another PNG you will need a procedure/function that merges those both layers together. With some googling I ended up with this procedure:

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;


Usage:

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

And again, I am really sorry to users that wanted to help, but couldn't due to not understanding me.

Lotetgaronne answered 2/1, 2012 at 21:11 Comment(0)
L
0

I don't have answer to this question but I figured out how to get same result with any PNG editor. I have created blank 1000x1000 PNG image and saved it in my application directory. Then I open this image in my program and resize image to needed sizes (smaller of course) and that's the trick.

Lotetgaronne answered 19/12, 2011 at 11:43 Comment(2)
I presume that may work, but that seems like a dirty alternative. There must be a better solution for this problem, they didn't add this CreateBlank constructor for nothing.Synchrotron
@JerryDodge Yes, they didn't add CreateBlank for nothing. But I spended 5 hours yesterday on that. I figured that CreateBlank just changes all pixel transparency to hundred and when you paint something, you wont see it because its 100% transparent. (Im not sure about this theory)Lotetgaronne

© 2022 - 2024 — McMap. All rights reserved.