How do I create nodes with transparent backgrounds in a TVirtualDrawTree? - delphi

I am deriving a custom control from TVirtualDrawTree and I am overriding the DoPaintBackground event to draw a background gradient effect for the tree view.
I am also overriding the DoBeforeItemPaint function so I can custom draw the tree view items. However, I can't quite manage to get the items to paint with a transparent background.
Looking into the source for TVirtualDrawTree, it looks as though the item is copied to a TBitmap image and then copied onto the canvas, however, I have tried editing the source and setting the transparency options on the bitmap itself and it still doesn't seem to be working.
I have also tried clearing the canvas before drawing e.g. Canvas.Brush.Style := bsClear and filling the item rect but no joy.

Don't do transparency, cheat!
Drawing the gradient sounds like a lot of work: draw it to a temporary bitmap so you don't need to re-generate it every time DoPaintBackgrdound() is called. Once you have the background in a bitmap you can BitBlt the relevant portion into the Canvas when you handle DoBeforeItemPaint, and you can BitBlt the bitmap to the whole virtual tree when you need the whole background.
This way you don't need to deal with expensive transparency yet for the end user it looks like your items had been painted using transparency. It's a win:win situation.

Related

How to draw over a fresh copy of Timage with Canvas.CopyRect()

I have a code to draw a moving line over timage, and I want the previous old line always to disappear, but this code makes all lines to appears there for ever... When I enable DoubleBuffering to form, the code below works OK but the background bitmap located in myimg does not show at all.
Fbitmap.Canvas.Pen.Color:=clRed;
Fbitmap.Canvas.Pen.Width:=2;
Fbitmap.Canvas.MoveTo(Xo,Yo);
Fbitmap.Canvas.LineTo(Xs,Ys);
myimg.Canvas.CopyRect(Rect(0, 0, Width, Height), FBitmap.Canvas, Rect(0, 0, Width, Height));
It's not surprising that this happens. TImage is intended for static images. When you draw on its canvas, what you draw stays there. That is by design.
It seems to me that you have chosen the wrong control. Obvious candidates for this are:
A TPaintBox. In the OnPaint handler draw the background, and then the lines.
A custom control that draws the background in response to WM_ERASEBKGND, and the foreground in response to WM_PAINT.
The latter option may be overkill for you but in my experience is the best defence against flickering.
There are several ways you can accomplish this. The easiest one is to leave the image as is, and to add another component (TShape) which you will move over the image.
If you have to write the line on the image, you need to keet the original image in the memory and use CopyRect over the one with the line before drawing a new one. Faster variation of this method is to keep in memory only the part of the image where the line will be drawn, so you can copy it over the line later, thus deleting it.

Multiple Transparency Layers

Please be kind, I'm new to this....
I have an application that I'm developing where I need to take a PNG image, which has a transparency layer, and treat another color (I'm thinking of using RGB( 1, 1, 1 ), since it's so close to pure black that I can hard-code it) as a separate transparency layer. The reason for this is that I have a background image sitting behind the PNG image that I would like to still display as my sprite gets filled (by adding a progress bar to the sprite), and I only want the portions of the sprite that aren't of the given color to reflect the color fill of the progress bar. In this way, I can avoid having to deal with vector computations for the outline of the image within the sprite, flood the area outside of the discernable image with my new "transparent" color, and be on my merry way.
I've tried using shaders, but they seem to be less than helpful.
There is no way that I know of due to Open GL not allowing you to do it. You will either have to modify the pixel data manually or write a shader (Which you hav already done ).

Fill background by tiling with bitmap

In my FMX Application I have to fill the entire background area of my form using a Bitmap pattern.
The reason why I'm thinking to use this pattern is because the form is freely resizable by the user and whether I set a fixed image the stretch causes the loss of quality of the background.
Is there a way to use a small Bitmap pattern I can repeat (X and Y) to fill the whole form area according to the resizing?
The effect you are looking for is a Tile.
See TTilerEffect and FireMonkey_Image_Effects for documentation.
The properties HorizontalTileCount and VerticalTileCount controls how many times a bitmap will be replicated. Since these values are floats, you need to adapt their values when the form is rescaled.
Add a TImage to the form
Make all the other controls children of it (so they appear on top).
Set the image's Align to alClient.
Load the Bitmap.
Set the WrapMode to imTile.

Display secondary underlying image on a Imagelist?

I have a Image list assigned to a listview to display transparent images.
There is a slight issue with this regarding some transparent images that are added, and that is they are sometimes hard to see/find in the listview.
See this example image:
You will notice that the images (noticeably the mouse) is barely viewable, infact if a empty image was added you would not even see it, the number captions come to the rescue here to show there is something actually there.
But I would like to make the images visually easier to see. I thought maybe having another image underneath the transparent images would work - of course though it could not affect the actual image.
So with that in mind, I made a bitmap of a chessboard grid:
I feel this would be the most suitable way of representing transparent areas of the images just like Paint.NET etc does.
To further illustrate this example I have modified the original image to show how it would look, if we had the chessboard bitmap as the underlay image:
Having the chessboard there would indicate there is a list item there in the first place, and the bitmap of the chessboard grid could be darker or an altogether different kind of image. As I said earlier if there was no image you would see nothing at all, so better to show an empty chess grid or other bitmap than nothing.
So, how can I display a second image underneath the original images using a imagelist to give a result similar to the example above? The underneath image could be anything - just another loaded bitmap for example.
If you store the chessboard as the first image (with index 0) and make the overlay image from the current image in the OnGetImageIndex event handler, it will do what you want to. However I'm not sure how efficient is to make the overlay image every time the event is fired.
procedure TForm1.ListView1GetImageIndex(Sender: TObject; Item: TListItem);
begin
// make the overlay (with overlay index 1) from the
// image with index Item.Index + 1
ImageList1.Overlay(Item.Index + 1, 1);
// use the first image from the list as a background
Item.ImageIndex := 0;
// and assign just created overlay index for overlay
Item.OverlayIndex := 1;
end;
I seems that what you need is a TImageList with extra capabilities.
As a starting point, I suggests you to consider TImageListEx described in the book Inside Delphi 2006
Excerpt:
The TImageListEx component is a TImageList descendant that can use the
images from another image list to generate disabled images, which can
be used on toolbars and other user interface elements.
The TImageListEx component is a TImageList descendant that can use the
images from another image list to generate disabled images, which can
be used on toolbars and other user interface elements.
There are several benefits of the TImageListEx component:
It eliminates the need for creating disabled glyphs.
It eliminates the need for adding the disabled glyphs to an additional TImageList component at design time.
It can drastically reduce the size of the .dfm file and of the entire application, especially in large applications that use a lot of
glyphs.
It's extremely fast, taking only milliseconds to disable all images in an image list, even when there are number of images.
It's extremely lightweight. (If you add it to an application that already uses the standard TImageList component, it won't increase the
size of the executable at all, and if you add it to an application
that doesn't use the standard TImageList component, the overhead is
only 2 KB.)
It's far from your requirements but yet detailed enough to show how to extend TImageList.

Transparency in TImage

When a new TImage is created it is transparent. After drawing objects to this image I would like to clear them. Note that I need to keep the image transparent as the TImage is being used as an overlay to another image.
Some sort of "clear" function for the TImage would be best. I think I'm missing something simple here, I just could not find any clear function within the TImage menu.
You're not really meant to draw things on a TImage control. You're meant to assign its Picture property and leave it alone. In fact, when you draw on a TImage, you're either drawing transiently by drawing on its Canvas property, or you're modifying the underlying Picture object by drawing on its canvas.
To clear a TImage, simply unassign the Picture property.
Image.Picture := nil;
To draw transient images — something you'll need to repaint whenever the window gets obscured and revealed — use a TPaintBox.

Resources