Using BitBlt to Scroll Text over a Picture Background - winapi

My user-control is intended to operate as a smooth vertical text scroller.
It renders the text (to be scrolled) to control's surface only once, using TextRenderer.DrawText.
It then starts a timer, which upon each of its ticks, Bit-Blits (BitBlt) the entire client-rectangle one pixel up. control's device-context is both the source and the destination of the BitBlt operation, as follows:
Protected Sub handleTimerTick(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim oG As Graphics = Me.CreateGraphics()
Dim sHdc As IntPtr = oG.GetHdc()
Dim iRes As Integer = BitBlt(sHdc, 0, 0, Me.ClientRectangle.Size.Width, Me.ClientRectangle.Size.Height, sHdc, 0, 1, SRCCOPY)
oG.ReleaseHdc(sHdc)
oG.Dispose()
End Sub
This successfully accomplishes the desired scroll effect, BUT only if my control's background is a solid-color (e.g. Me.BackColor = Color.Gray).
If I set a picture as control's background, BitBlt scrolls the background along with the text displayed over it. Ofcourse, I'd like only the text to be scrolled, and the background image to remain static.
I have found the following thread, which suggests using TransparentBlt instead of BitBlt where the background is a solid uniform color to be ignored : How to copy with BitBlt?
The solution suggested there would not be suitable to the case on hand, in which a colorful background is used.
Please note that the text itself is of a solid uniform color.
Your advice would be much appreciated. If it is of any matter, I'm using VB.NET 2005.

If the background is supposed to stay fixed while the text moves around on it, I would suggest that you maintain two separate bitmaps: one that contains the background image, and a second one that contain the text on a transparent background.
Then, on each timer tick, you draw the background bitmap first and then draw the text bitmap at the desired offset position.
I don't understand why you need to P/Invoke the BitBlt function for this. What's wrong with the equivalent GDI+ function, wrapped by the .NET Framework already as Graphics.DrawImage?

Related

Semi-transparent pixel support in GDI+ Graphics.DrawImage() methods

I need to draw 32-bit PNG images with semi-transparent pixels loaded into an ImageList control on a Graphics when clip rectangles may be applied to this drawing. It turned out that I can't do this with the Graphics.DrawImage() method. The translucent pixels simply became gray when I tried to call some overloaded versions of Graphics.DrawImage.
As an example, look at the following picture:
The picture above was created with the following redefined OnPaint method of the form:
protected override void OnPaint(PaintEventArgs e)
{
imageList1.Draw(e.Graphics, 10, 30, 0);
e.Graphics.DrawImage(imageList1.Images[0], 10, 90);
base.OnPaint(e);
}
As you can see, the first ImageList.Draw() method based on the Win32 API ImageList_DrawEx function renders the translucent pixels correctly, while the GDI+ Graphics methods don't. I would be glad to use ImageList.Draw as it does the work correctly, but GDI+ clip rectangles do not work with it (because of its GDI nature).
Is there a way to draw 32-bit PNG images with semi-transparent pixels correctly with pure GDI+ methods if they can be limited by GDI+ clip rectangles?
If it may help, I uploaded the image into the ImageList control at design time this way:
Dropped the ImageList control onto the designer's surface.
Set the ColorDepth property to Depth32Bit.
Set the ImageSize property (48, 48 for that test icon).
Clicked the ellipsis button in the editor of the Images property and added the icon in the Images Collection Editor with its Add button.
The test Mobile Phone Icon of the size 48x48 I used can be downloaded from this source.
You might want to check the Graphics Property CompositingMode.
It should be set to
e.Graphics.CompositingMode = CompositingMode.SourceOver;.
If not, the graphics object is not using transparency. I assume that this is done implicitly by the ImageList control if you use its draw method.

How do I force a repaint of only the background so a control doesn't repaint over it again?

This question was generated by a response to another question: Common Controls on a Transparent Window?.
Apparently, there is a way to only paint the background without a control painting itself again This would solve the problem of having common control buttons on a transparent background.
So my question is, how do I paint only the background around a common control after the common control has painted itself?
About how to redraw visible borders.
The rounded rectangle drawn by RoundRect is used as representative
The FrameRgn function draws a border around the specified region by using the specified brush.
Simple code demonstration:
HRGN hRegion = ::CreateRoundRectRgn (0, 0, ClientWidth, ClientHeight,12,12);
Canvas->Brush->Style = bsSolid;
Canvas->Brush->Color = RGB(96, 96, 96);
::FrameRgn(Canvas->Handle, hRegion, Canvas->Brush->Handle, 2, 2);
::DeleteObject(hRegion); // Don't leak a GDI object
Link you need: Redraw Border

Common Controls on a Transparent Window?

While there are lots of variations of the question, there doesn't seem to be a specific answer to a simple case of wanting to use built-in common controls on a transparent window using Win32. I don't want the controls to be transparent, I just want the border around it to be transparent. I can't believe MS didn't update the .dll's to handle transparency when they added it, but I guess they forgot? Is there a specific method that works. A button can get close with WS_EX_TRANSPARENT, but flaky where it works most of the time but at times part of the border shows up. Edit controls, change depending on when get focus or not.
So the question is simply:
Is there a way to make common controls on transparent window so there is no white border around them?
If not, is there a good replacement library that does it via owner draw?
If some, which ones and what is the method?
Seems silly to reinvent the wheel just because of the area around the control.
TIA!!
If I am not mistaken, you can take the following steps to achieve this effect.
1.Create a GDI+ Bitmap object with the PixelFormat32bppPARGB pixel format.
2.Create a Graphics object to draw in this Bitmap object.
3.Do all your drawing into this object using GDI+.
4.Destroy the Graphics object created in step 2.
5.Call the GetHBITMAP method on the Bitmap object to get a Windows HBITMAP.
6.Destroy the Bitmap object.
7.Create a memory DC using CreateCompatibleDC and select the HBITMAP from step 5 into it.
8.Call UpdateLayeredWindow using the memory DC as a source.
9.Select previous bitmap and delete the memory DC.
10.Destroy the HBITMAP created in step 5.
This method should allow you to control the alpha channel of everything that is drawn: transparent for the background, opaque for the button.
A similar discussion: Transparent window containing opaque text and buttons

Resizing Layered Windows

I'm hoping I can get away with communicating the issue I'm having clearly without posting a ton of source code. The source is all ASM and while most would understand it, it's still not their primary "thing."
I have a layered window which is working perfectly - up to the point of resizing it. The window is all per-pixel alpha. There are no transparent pixels. The borders and caption are opaque (255 alpha); the client area is something like 128 alpha or whatever I put it at. So no transparent pixels.
I have a function that handles redraws - it's actually geared toward rebuilding the frame for a new window size. GetWindowDC is called on the layered window. CreateCompatibleDC creates off of that DC. The bitmap is a DIB section. The bitmap is properly sent to UpdateLayeredWindow. Again, everything works perfectly as long as the window is not in active resize (border drag).
I have tried using DefWindowProc to handle border drags, as well as completely handling all aspects of that functionality myself. The results are the same: massive flicker but ONLY beyond the window boundaries that existed before border drag began, and then the window size does not ultimately change - sort of. The original window area remains displayed without issue. No flicker there. However if (for example) I'm dragging the right border to the right, everything right of the original window right edge flickers rapidly then vanishes. When I release the left mouse button, the display remains at the original size (the size the window was before dragging the border). I've verified the bitmap is correctly rebuilt to the new size. It seems like the DC for the layered window doesn't want to resize because the entire area of original window size is completely stable, but any new area appearing post-resize flickers then hides.
I've even tried removing the ws_ex_layered style before resizing the window in my manual handling (I've tried both MoveWindow and SetWindowPos) then resetting the ex style after SetWindowPos / MoveWindow but before dragging.
When I let DefWindowProc handle the updates, I got the exact same result. So indications are that something is wrong with my repainting. But I even whittled that process down (commenting out oodles of code for test) and just filled the bitmap with 0xFF0000FF (arbitrary test value, blue, full opacity) then jumped straight to UpdateLayeredWindow. Same result.
The bitmap used for UpdateLayeredWindow is deleted before calling the "rebuild" function and within the function that bitmap is recreated to the new size. I have verified the new size extensively - I can see the new bitmap as it high-speed flickers and it's all good. The DC is GetWindowDC from the layered window. The compatible DC creates off that DC. The bitmap is selected into the compatible DC, and that compatible DC is passed to UpdateLayeredWindow. If any of these things were off, I would never see the new bitmap displayed. It's there - it just flickers at high speed ONLY beyond the window bounds that existed before resize began. DefWindowProc yields the same results. Within that original area, my caption buttons slide off to the right as I drag the right border to the right, further verifying everything is good.
I'm at a loss. Any input would be welcome.

Draw inside a win32 HRGN object

I am implementing a border control in win32, and I have implemented various styles for that border. I am using simple MoveTo() and LineTo() commands for drawing the border.
My problem is, when i select some new style for my border, it starts painting over the currently drawn border. I understand i need to refresh/repaint my window inorder to have a fresh canvas. I am using InvalidateRect() for now to achieve this purpose. But I am concerned, If i have other windows associated with my border control window (as child windows), how will this effect the child windows?i will need to repaint everything on this window, correct?
Secondly, Is there anyway I can draw lines inside a GDI Region (HRGN)? So far, all i have come across is how to fill up that HRGN with some fill color. Is there anyway i can retrieve HDC associated with that particular HRGN object??
This is not a concern, anything you draw is automatically clipped by the child window rectangles. No extra code is required. The underlying window style flag is WS_CLIPCHILDREN.
You cannot draw lines in a region nor are they associated with a device context. Other than by drawing the region and then drawing the lines in your paint message handler. You are probably interested in paths. The MSDN docs start here.

Resources