Graphics FillRectangle except Specified Area (.NET, GDI+) - c#

I would like to use Graphics.FillRectangle or Graphics.Clear with semi-transparent colour (e.g. ARGB=128,0,0,0) to darken entire area of the Graphics object except some specified (rectangular) area:
I know this can be achieved with four FillRectangle calls, but I wonder if it cannot be done easier.
I am aware of simple clipping (Graphics.SetClip), but this allows me to clip everything outside the specified area and I would like to achieve the opposite.

Dim img = Bitmap.FromFile("C:\Users\Public\Pictures\Sample Pictures\Desert.jpg")
Using gfx = Graphics.FromImage(img)
Dim r = New Rectangle(100, 150, 50, 50)
gfx.SetClip(r, Drawing2D.CombineMode.Exclude)
Using b = New SolidBrush(Color.FromArgb(128, 0, 0, 0))
gfx.FillRectangle(b, New Rectangle(0, 0, img.Width, img.Height))
End Using
Me.PictureBox1.Image = img
End Using

Related

C# WinForms - Exclude edge from this.Bounds

I'm using this code to grab an image of my form:
Rectangle bounds = this.Bounds;
bitmapScreen = new Bitmap(bounds.Width, bounds.Height);
using (Graphics gImage = Graphics.FromImage(bitmapScreen))
{
gImage.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
}
However, in Windows 10, I'm getting the desktop or other windows showing through as the bounds are wide due to the transparent/shaded edges outside of my actual content. How can I limit this bitmap to the actual window/form bounds?
It seems as though these extra edges occur on the left, bottom and right only. (See picture)Extra Edges
This seems to work:
bitmapScreen = new Bitmap(this.Width, this.Height);
this.DrawToBitmap(bitmapScreen, new Rectangle(Point.Empty, bitmapScreen.Size));

Invert Crop from (Cut hole into) Image

Everywhere I look online, I see people posting on how to successfully crop an image. However, I want to 'crop'/ clear a hole out of an image. I want to keep the original image, but crop out a rectangle
As you can see in the image above, I have "cropped" out the kittens face. I maintained the original image, but removed only part of it. I cannot figure out how to do that.
Assuming you want to replace the original pixel colors with transparency you run into a small problem: You can't draw or fill with transparency in GDI+.
But you can use Graphics.Clear(Color.Transparent).
To do that you restrict the region where the Graphics object will draw. Here we can use the simple cropping rectangle but you can clear more complex shapes using a GraphicsPath..
Example using a bitmap bmp:
using (Graphics g = Graphics.FromImage(bmp))
{
Rectangle crop = new Rectangle(222,222,55,55);
g.SetClip(crop);
g.Clear(Color.Transparent);
}
bmp.Save(somefilename, ImageFormat.Png);
Setting your Graphics object's CompositingMode property to CompositingMode.SourceCopy will allow your drawing operations to replace the alpha value instead of proportionally opacifying it:
public static void TestDrawTransparent()
{
//This code will, successfully, draw something transparent overwriting an opaque area.
//More precisely, it creates a 100*100 fully-opaque red square with a 50*50 semi-transparent center.
using(Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb))
{
using(Graphics g = Graphics.FromImage(bmp))
using(Brush opaqueRedBrush = new SolidBrush(Color.FromArgb(255, 255, 0, 0)))
using(Brush semiRedBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 0)))
{
g.Clear(Color.Transparent);
Rectangle bigRect = new Rectangle(0, 0, 100, 100);
Rectangle smallRect = new Rectangle(25, 25, 50, 50);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
g.FillRectangle(opaqueRedBrush, bigRect);
g.FillRectangle(semiRedBrush, smallRect);
}
bmp.Save(#"C:\FilePath\TestDrawTransparent.png", ImageFormat.Png);
}
}
In this code, I first draw a fully-opaque red square, then a semi-transparent red square "over" it. The result is a semi-transparent "hole" in the square:
And on a black background:
A zero-opacity brush works just as well, leaving a clear hole through the image (I checked).
With that in mind, you should be able to crop any shapes you want, simply by filling them with a zero-opacity brush.

Graphics.DrawImage produces alpha-channel gradient in C# WinForms 2.0

I'm facing a really perplexing problem..
I have a .Net 2.0 C# WinForms project.
I'm trying to stretch a bitmap onto a drawing area, but for some reason it is not stretched properly - I get alpha channel gradient on the right and bottom margins of my drawing area.
It took me quite a while to isolate this problem. I create a few lines of code that reproduce the problem (see code snippet and screenshot below).
Can anyone please shed some light over this matter?
Thanks in advance.
--
private void Form1_Paint( object sender, PaintEventArgs e )
{
// Create a black bitmap resource sized 10x10
Image resourceImg = new Bitmap( 10, 10 );
Graphics g = Graphics.FromImage( resourceImg );
g.FillRectangle( Brushes.Black, 0, 0, resourceImg.Width, resourceImg.Height );
Rectangle drawingArea = new Rectangle( 0, 0, 200, 200 ); // Set the size of the drawing area
e.Graphics.FillRectangle( Brushes.Aqua, drawingArea ); // Fill an aqua colored rectangle
e.Graphics.DrawImage( resourceImg, drawingArea ); // Stretch the resource image
// Expected result: The resource image should completely cover the aqua rectangle.
// Actual Result: The right and bottom edges become gradiently transparent (revealing the aqua rectangle under it)
}
The behavior has to do with how GDI+ handles edges. In this case, you're scaling a very small image over a large area, and you haven't told GDI+ how to handle the edge. If you use the ImageAttributes class and set the WrapMode appropriately, you can get around this issue.
For example:
private void Form1_Paint(object sender, PaintEventArgs e)
{
using (var resourceImg = new Bitmap(10, 10))
{
using (var g = Graphics.FromImage(resourceImg))
{
g.FillRectangle(Brushes.Black, 0, 0,
resourceImg.Width, resourceImg.Height);
}
var drawingArea = new Rectangle(0, 0, 200, 200);
e.Graphics.FillRectangle(Brushes.Aqua, drawingArea);
using (var attribs = new ImageAttributes())
{
attribs.SetWrapMode(WrapMode.TileFlipXY);
e.Graphics.DrawImage(resourceImg, drawingArea,
0, 0, resourceImg.Width, resourceImg.Height,
GraphicsUnit.Pixel, attribs);
}
}
}
The above code should produce an all black image. If you comment out the attribs.SetWrapMode(WrapMode.TileFlipXY); statement, you should see the blue gradient. With the wrap mode set, you're telling GDI+ to flip the image at the edges, so it will pick up more black and not fade things out at the edge when it scales the image.

How to create a simple glass effect

I am currently painting a light blue, partly transparent overlay over owner-drawn objects to indicate certain state. It's OK but I thought that it would be even nicer if I could at some sort of glass effect to further establish the idea that the particular object has "something" overlaid over the top of it.
I thought that some glass streaks, for example, in addition to the blue transparency would lend a nice effect.
I've Googled around for GDI+ (and others) algorithms to do simple things painting like this but have come up empty. Links to any (fairly simple) algorithms in any language would be appreciated. I prefer .NET but can figure out the painting from pseudo-code on up.
Sorry, shoul've also specified that I need to target WinXP and using .NET version 2.0 - So unable to use WPF or Vista/Win7 goodies.
I've not done this myself but, have used codeproject source to render a sample...Try this:
http://www.codeproject.com/KB/GDI-plus/Image-Glass-Reflection.aspx
public static Image DrawReflection(Image _Image, Color _BackgroundColor, int _Reflectivity)
{
// Calculate the size of the new image
int height = (int)(_Image.Height + (_Image.Height * ((float)_Reflectivity / 255)));
Bitmap newImage = new Bitmap(_Image.Width, height, PixelFormat.Format24bppRgb);
newImage.SetResolution(_Image.HorizontalResolution, _Image.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(newImage))
{
// Initialize main graphics buffer
graphics.Clear(_BackgroundColor);
graphics.DrawImage(_Image, new Point(0, 0));
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle destinationRectangle = new Rectangle(0, _Image.Size.Height,
_Image.Size.Width, _Image.Size.Height);
// Prepare the reflected image
int reflectionHeight = (_Image.Height * _Reflectivity) / 255;
Image reflectedImage = new Bitmap(_Image.Width, reflectionHeight);
// Draw just the reflection on a second graphics buffer
using (Graphics gReflection = Graphics.FromImage(reflectedImage))
{
gReflection.DrawImage(_Image,
new Rectangle(0, 0, reflectedImage.Width, reflectedImage.Height),
0, _Image.Height - reflectedImage.Height, reflectedImage.Width,
reflectedImage.Height, GraphicsUnit.Pixel);
}
reflectedImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
Rectangle imageRectangle =
new Rectangle(destinationRectangle.X, destinationRectangle.Y,
destinationRectangle.Width,
(destinationRectangle.Height * _Reflectivity) / 255);
// Draw the image on the original graphics
graphics.DrawImage(reflectedImage, imageRectangle);
// Finish the reflection using a gradiend brush
LinearGradientBrush brush = new LinearGradientBrush(imageRectangle,
Color.FromArgb(255 - _Reflectivity, _BackgroundColor),
_BackgroundColor, 90, false);
graphics.FillRectangle(brush, imageRectangle);
}
return newImage;
}
I was actually able to achieve a basic glass effect by overlaying my image with a rectangle about one third the size of the image below that contains a gradient fill of white that starts at 25% opacity and goes to 75% opacity. This is single bit of painting produces a glassy "streak" that I was happy with. The same idea could be repeated a number of times with a variety of rect widths to produce several "streaks" that will give the illusion of a glass overlay.
You could try the Aero Glass function, if you are using Vista or Windows 7.
These might be helpful:
http://msdn.microsoft.com/en-us/library/aa969537%28VS.85%29.aspx#blurbehind
http://msdn.microsoft.com/en-us/library/ms748975.aspx

Best options for image resizing

I have to resize images exceeding a max size. Different methods I tried so far are not good enough:
System.Drawing.Image.GetThumbnailImage generates very poor quality images in general.
Playing with options like this one I can generate better images in quality but heavier than the original one.
Probably the second option (or something similar) is the best option and I would need to resize using the proper options.
Any advice?
EDIT
My option 2 was generating heavier images for some specific pictures. In general is working as expected so I'd say this is solved.
Try something like this:
public Bitmap Resize(Bitmap originalImage, int newWidth)
{
int newHeight = (int)Math.Round(originalImage.Height * (decimal)newWidth / originalImage.Width, 0);
var destination = new Bitmap(newWidth, newHeight);
using (Graphics g = Graphics.FromImage(destination))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(originalImage, 0, 0, newWidth, newHeight);
}
return destination;
}
I would go with using the WPF libs instead of the GDI+ libs. The WPF libs perform faster and I think they yield better results compared to the GDI+ libs.
Check out these excellent posts from Bertrand Le Roy.
Resizing images from the server using WPF/WIC instead of GDI+
Server-side resizing with WPF: now with JPG
The fastest way to resize images from ASP.NET. And it’s (more) supported-ish
Create a new Bitmap object, then using the Graphics object, re-draw the old image into the new image's buffer at the increased/decreased size based on the resize engine you want.
// inImage is your original
Bitmap outImage = new Bitmap(newWid, newHei);
Graphics gfx = Graphics.FromImage(outImage);
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.DrawImage(inImage,
new Rectangle(0, 0, newWid, new Hei),
new Rectangle(0, 0, inImage.Width, inImage.Height),
GraphicsUnit.Pixel);

Categories

Resources