I am trying to use DrawThemeBackground to draw to a bitmap in C#, but it always comes out as black.
Bitmap bmp = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Green);
IntPtr hdc = g.GetHdc();
DrawThemeBackground(hTheme, hdc, 0, 1, ref rect, IntPtr.Zero);
g.ReleaseHdc(hdc);
}
It appears that DrawThemeBackground isn't looking at the background colour of the bitmap (green in this case) and is always blending with black. Am I missing something here? Is this even possible to do?
Related
I am drawing lines by using win32 gdi native apis. Now, I want to draw the line as transparent. I have set the alpha channel property in the color. However, setting the alpha channel in color is not drawing the line as transparent. I read about Alpha Blend Api but could not figure out the solution.
var hdc = g.GdiDeviceContext;
var srcHdc = CreateCompatibleDC(hdc);
var clipRegion = CreateRectRgn(x, y, x + width, y + height);
SelectClipRgn(hdc, clipRegion);
var pen = CreatePen(PenStyle.Solid, LineWidth, (uint)ColorTranslator.ToWin32(colour));
if (pen != IntPtr.Zero)
{
var oldPen = SelectObject(hdc, pen);
Polyline(hdc, points, points.Length);
SelectObject(hdc, oldPen);
DeleteObject(pen);
}
SelectClipRgn(hdc, IntPtr.Zero);
AlphaBlend(hdc, x, y, width, height, srcHdc, x, y, width, height, new BlendFunction(0x00, 0, 0x7f, 0x00));
DeleteObject(clipRegion);
I am trying to draw the line as transparent.
var srcHdc = CreateCompatibleDC(hdc);
This creates a memory device context. This is the right first step. But the memory dc is not ready yet. It requires a memory bitmap as well.
SelectObject(hdc, pen);
Polyline(hdc, points, points.Length);
This will draw on windows device context. But we want to draw on memory device context, and then draw the memory on to HDC using AlphaBlend
See example below:
int w = 100;
int h = 100;
//create memory device context
var memdc = CreateCompatibleDC(hdc);
//create bitmap
var hbitmap = CreateCompatibleBitmap(hdc, w, h);
//select bitmap in to memory device context
var holdbmp = SelectObject(memdc, hbitmap);
//begine drawing:
var hpen = CreatePen(0, 4, 255);
var holdpen = SelectObject(memdc, hpen);
Rectangle(memdc, 10, 10, 90, 90);
//draw memory device (memdc) context on to windows device context (hdc)
AlphaBlend(hdc, 0, 0, w, h, memdc, 0, 0, w, h, new BLENDFUNCTION(0, 0, 128, 0));
//clean up:
SelectObject(memdc, holdbmp);
SelectObject(memdc, holdpen);
DeleteObject(hbitmap);
DeleteObject(hpen);
DeleteDC(memdc);
I cannot seem to programmatically create a colored bitmap to display in a PictureBox. The bitmap saves normally as a file, but is faded at the edges when displayed in a PictureBox. Here is simplified code used to create and display the Bitmap (in actual code, the bitmap generation is completely separate from the form, so forcing the bitmap size to match the picturebox size isn't possible):
Bitmap Bmp = new Bitmap(4, 4, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics gfx = Graphics.FromImage(Bmp))
using (SolidBrush brush = new SolidBrush(Color.BlueViolet))
{
gfx.FillRectangle(brush, 0, 0, 4, 4);
}
Then set the Image value on a PictureBox to the generated Bitmap:
pictureBox1.Image = Bmp;
Here is the resulting bitmap displayed in a 300x300 picturebox:
How do I set the Image on the PictureBox so that it displays the colored bitmap properly (full solid)?
EDIT: I am restricted to generating smaller source bitmaps, so upscaling into a PictureBox is unavoidable. The problem appears when the generated source bitmap is 4px or 100px square, so I believe these are relevant cases.
EDIT: The PictureBox scaling should be set to stretch or zoom for the issue to manifest. In this example case the 4x4 source bitmap is stretched to 300x300.
EDIT: The basic problem is PictureBox's inability to upscale small bitmaps into large controls. This is confusing because the Bitmap upscales nicely into a PictureBox.Background image. Unless you have a magic bullet that will fix the image upscaling problem, I think it might be best to go for clear and simple workarounds in your answer.
You are generating a 4x4 bitmap and it's being stretched. Specify the size to match the picture box instead:
int width = pictureBox1.Width;
int height = pictureBox1.Height;
var Bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics gfx = Graphics.FromImage(Bmp))
using (var brush = new SolidBrush(Color.BlueViolet))
{
gfx.FillRectangle(brush, 0, 0, width, height);
}
pictureBox1.Image = Bmp;
You will need to turn anti-aliasing off. Also, since you are using one color for the whole picturebox, why not make the bitmap 1x1? If you need it 4x4, change the int the top of the example from 1 to 4.
int hw = 1;
Bitmap Bmp = new Bitmap(hw, hw,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics gfx = Graphics.FromImage(Bmp))
{
// Turn off anti-aliasing and draw an exact copy
gfx.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
gfx.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
using (SolidBrush brush = new SolidBrush(Color.BlueViolet))
{
gfx.FillRectangle(brush, 0, 0, hw, hw);
}
}
pictureBox1.Image = Bmp;
UPDATE
Since you are still having the same issue by setting the picturebox to the image, you will have to get the graphics object from the picturebox and draw directly on it.
The code is very similar.
using (Graphics gfx = Graphics.FromImage(pictureBox1.Image))
{
// Turn off anti-aliasing and draw an exact copy
gfx.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
gfx.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
using (SolidBrush brush = new SolidBrush(Color.BlueViolet))
{
gfx.FillRectangle(brush, 0, 0,
pictureBox11.Width - 1,
pictureBox11.Height - 1);
}
}
// Force the picturebox to redraw with the new image.
// You could also use pictureBox11.Refresh() to do the redraw.
pictureBox11.Invalidate();
I tried to test your code and the image were properly displayed.
But when i used this code:
Rectangle srcRect = New Rectangle(0, 0, Bmp.Width, Bmp.Height);
Rectangle dstRect = New Rectangle(0, 0, PictureBox1.Width, PictureBox1.Height);
g = PictureBox1.CreateGraphics;
g.DrawImage(Bmp, dstRect, srcRect, GraphicsUnit.Pixel);
g.Dispose();
I did get exactly your result. In order to fix it:
Rectangle srcRect = New Rectangle(0, 0, Bmp.Width - 1, Bmp.Height - 1);
Rectangle dstRect = New Rectangle(0, 0, PictureBox1.Width, PictureBox1.Height);
g = PictureBox1.CreateGraphics;
g.DrawImage(Bmp, dstRect, srcRect, GraphicsUnit.Pixel);
g.Dispose();
Edit:
So you have the bitmap and you want to stretch it. And the bitmap has ane solid color. Do this insted:
Color pixelColor = Bmp.GetPixel(0, 0);
PictureBox1.BackColor = pixelColor;
valter
I have a thread that continuously generates bitmaps and takes a screenshot of another program's window. Now, I have a pictureBox on my form, and that's constantly being updated with the bitmaps generated. Here's the code I have in the thread:
Bitmap bitmap = null;
while (true)
{
if (listBoxIndex != -1)
{
Rectangle rect = windowRects[listBoxIndex];
bitmap = new Bitmap(rect.Width, rect.Height);
Graphics g = Graphics.FromImage(bitmap);
IntPtr hdc = g.GetHdc();
PrintWindow(windows[listBoxIndex], hdc, 0);
pictureBox1.Image = bitmap;
g.ReleaseHdc(hdc);
}
}
As you can see, this leads to a memory leak, because of the continuous call to new Bitmap(rect.Width, rect.Height). I've tried adding "bitmap.Dispose()" to the bottom of the while loop, but that leads to the pictureBox's image also being disposed, which makes a giant red X in place of the actual image. Is there any way I can dispose of "bitmap" without disposing of the pictureBox image?
You're also "leaking" the Graphics object. Try this:
while (true)
{
if (listBoxIndex != -1)
{
Rectangle rect = windowRects[listBoxIndex];
Bitmap bitmap = new Bitmap(rect.Width, rect.Height);
using (Graphics g = Graphics.FromImage(bitmap))
{
IntPtr hdc = g.GetHdc();
try
{
PrintWindow(windows[listBoxIndex], hdc, 0);
}
finally
{
g.ReleaseHdc(hdc);
}
}
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = bitmap;
}
}
The answered example has a leak with Graphics g
after g.ReleaseHdc(..);
Remember to dipose the graphics variable
as for example:
g.Dispose();
I have a PictureBox with lots of transparent png's drawn into...
Now I'd like to save the content of this PictureBox to a file, but without the transparency.
How can I do this?
I have already tried to remove transparency from the Image like this, but it didn't work, I still got a transparent image after the save.
...
removeTransparency(pictureBox.Image).Save(saveFileDialog.FileName);
private Bitmap removeTransparency(Image transparentImage)
{
Bitmap src = new Bitmap(transparentImage);
Bitmap newImage = new Bitmap(src.Size.Width, src.Size.Height);
Graphics g = Graphics.FromImage(newImage);
g.DrawRectangle(new Pen(new SolidBrush(Color.White)), 0, 0, newImage.Width, newImage.Height);
g.DrawImage(src, 0, 0);
return newImage;
}
You may cycle through all pixel (please not using GetPixel/SetPixel) to change the color or you may create another bitmap with the same size, create a graphics context from the image, clear the image with your favorite background color and then draw your image on it (so transparent pixels simply will be replaced by the background color).
Example
Did you do something like this?
public static Bitmap Repaint(Bitmap source, Color backgroundColor)
{
Bitmap result = new Bitmap(source.Width, source.Height, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(result))
{
g.Clear(backgroundColor);
g.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height));
}
return result;
}
I'm trying to resize my image after copying it from the screen, and can't figure out how to do it. The tutorials I've been reading recommend using Graphics.DrawImage to resize the image, but when I run this code, it doesn't resize.
Bitmap b = new Bitmap(control.Width, control.Height);
Graphics g = Graphics.FromImage(b);
g.CopyFromScreen(control.Parent.RectangleToScreen(control.Bounds).X, control.Parent.RectangleToScreen(control.Bounds).Y, 0, 0, new Size(control.Bounds.Width, control.Bounds.Height), CopyPixelOperation.SourceCopy);
g.DrawImage(b, 0,0,newWidth, newHeight);
Any help would be appreciated, thanks!
Try this. Graphics won't "replace" the image when you use DrawImage - it draws the input image on its source, which is the same as the the image you're trying to draw to it.
Probably a more concise way to do this but.....
Bitmap b = new Bitmap(control.Width, control.Height);
using (Graphics g = Graphics.FromImage(b)) {
g.CopyFromScreen(control.Parent.RectangleToScreen(control.Bounds).X,
control.Parent.RectangleToScreen(control.Bounds).Y, 0, 0,
new Size(control.Bounds.Width, control.Bounds.Height),
CopyPixelOperation.SourceCopy);
}
Bitmap output = new Bitmap(newWidth, newHeight);
using (Graphics g = Graphics.FromImage(output)) {
g.DrawImage(b, 0,0,newWidth, newHeight);
}
Is there any reason why you can't just use a PictureBox control? That control takes care of stretching for you.