C# Win8: copy .PNG to the Windows clipboard; paste with transparency preserved - c#

I have been tasked with capturing an image, copy it to the clipboard, and paste it to the application below. I must be able to support pretty much any rich text field, and it must preserve transparency. My current solution first renders a white background. Here is my code:
The RenderTargetBitmap contains the image that I wish to copy as a .PNG
public static void CopyImageToClipboard(RenderTargetBitmap b)
{
MemoryStream stream = new MemoryStream();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(b));
encoder.Save(stream);
Bitmap bmp = new Bitmap(stream);
Bitmap blank = new Bitmap(Convert.ToInt32(b.Width), Convert.ToInt32(b.Height));
Graphics g = Graphics.FromImage(blank);
g.Clear(System.Drawing.Color.White);
System.Drawing.Image img = System.Drawing.Image.FromStream(stream);
g.DrawImage(img, 0, 0, Convert.ToInt32(b.Width), Convert.ToInt32(b.Height));
Bitmap tempImage = new Bitmap(blank);
blank.Dispose();
img.Dispose();
bmp = new Bitmap(tempImage);
tempImage.Dispose();
System.Windows.Forms.Clipboard.SetImage(bmp);
stream.Dispose();
}

Just pick a random color to use it as background, let's say
var background = Color.FromArgb(1, 255, 1, 255);
Erase background to it:
g.Clear(background); // instead of System.Drawing.Color.White
Then make that color transparent:
Bitmap tempImage = new Bitmap(blank);
tempImage.MakeTransparent(background);
Note that also default transparent color works pretty well, no need to pick a magic color (check if you need to Clear() background, it may be - I didn't check - default bitmap background):
g.Clear(Color.Transparent);
// ...
tempImage.MakeTransparent();
EDIT Some older RTF control versions won't handle transparency and there is nothing (AFAIK) you can do for it. A half decent workaround (if you can't detect control class of paste target and read its background color) is to use Color.FromArgb(254, 255, 255, 255). White where transparency isn't supported and...completely transparent (because of MakeTransparent()) where it is.

The Windows clipboard, by default, does not support transparency, but you can put content on the clipboard in many types together to make sure most applications find some type in it that they can use. Generally, if, in addition to the normal nontransparent clipboard Bitmap format, you put the image on the clipboard in both PNG and DIB formats, most applications will be able to use at least one of them to get the image in a format they support as being transparent.
These formats are put on the clipboard in a specific way, though. They need to have their data (for png, that's not the loaded image object but the actual png file's bytes) put in a MemoryStream that is then put on the clipboard.
PNG put on the clipboard like that will be accepted by a multitude of applications, including Gimp and the newer MS Office. And of course, if you implement reading for it too, you can use it in your own application. The (rather dirty) DIB format should take care of most other applications, if they indeed support transparent image pasting at all.
I detailed how to do both the copying and the retrieving in this answer:
https://stackoverflow.com/a/46424800/395685

Related

Is there a workaround for overlaying transparent images from resources?

Right now I have a form with a PictureBox on the form. I'm using two partially transparent images and trying to put one on top of the other.
sideMenuWide = rounded rectangle with transparent background (.png) [Bottom image]
labelPointer = triangle with transparent background (.png) [Top image]
Here is my approach:
// METHOD #1 //
Image img1 = Image.FromFile(#"C:\sideMenuWide.png");
Image img2 = Image.FromFile(#"C:\labelPointer.png");
picBox.Image = CombineImages(img1, img2);
// METHOD #2 //
Image imgA = RBS.Properties.Resources.sideMenuWide;
Image imgB = RBS.Properties.Resources.labelPointer;
picBox.Image = CombineImages(imgA, imgB);
And the CombineImage function: (I did not write this function, only modified)
public static Bitmap CombineImages(Image imgA, Image imgB)
{
//a holder for the result (By default, use the first image as the main size)
Bitmap result = new Bitmap(imgA.Size.Width, imgA.Size.Height);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the images into the target bitmap
graphics.DrawImage(imgA, 0, 0, imgA.Width, imgA.Height);
graphics.DrawImage(imgB, 100, 70, imgB.Width, imgB.Height);
}
return result;
}
Method #1 DOES work how I want it to, displays imgB perfectly on top of imgA.
Method #2 however shows imgA just fine but imgB is really faint.
Any ideas on how to overcome this? I would like to be able to do this using Resources instead of having to pull from a file.
If the files work when loaded, but the resources don't, then it sounds like the resx build process or the ResourceManager is doing something undesirable. I'd try embedding the files and reading them directly from a stream rather than relying on the ResourceManager to do it for you.
In your Solution Explorer, Add Existing File to add the file to your project. Get properties on the added file and set it to Embedded Resource=. (Don't add it to a resx file)
In your code, you can now get a stream containing the file's data:
Stream instream = Assembly.GetExecutingAssembly().
GetManifestResourceStream("RBS.labelPointer.png"));
(Hint: The compiler generates an obtuse name for the embedded resource, so after adding the files you can add temporary code to call Assembly.GetExecutingAssembly().GetManifestResourceNames() to get a list of them all and find the file you're interested in)
Load your bitmap/image from the stream:
Image img2 = new Bitmap(inStream);

adding a clipart from png file

I have a problem adding cliparts (from png files) to bmp object. I add some cliparts to a jpeg image and after I save it, I get a mistery: some cliparts are present in saved image, but some not.
Here is code how I add cliparts to image:
using (System.Drawing.Graphics gfx = System.Drawing.Graphics.FromImage(this._image))
{
gfx.CompositingMode = CompositingMode.SourceOver;
gfx.CompositingQuality = CompositingQuality.HighQuality;
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.DrawImage(image, posX, posY, newWidth, newHeight);
gfx.Save();
}
Can anybode explain what am I doing wrong or why this situation appears?
edited:
it happens when I use this code inside a ASP.NET Application, when I use it in windows application everything is fine
I'm not sure if this is the cause of Your issue, but line gfx.Save() does not save the image you painted back to the image, but saves state of the Graphics object. In order to save the changes back to the image, make sure to call Image.Save() method and double check to make sure You are not restoring the previous image somewhere in the code. More information about it Graphics.Save method can be found here: http://msdn.microsoft.com/en-us/library/system.drawing.graphics.save(v=vs.100).aspx.
Update
You wrote in the comments that it's a transparency issue. If this is the cause, take a look at the code loading the clipart images from files. This also may be caused by various pixel formats used in your image files. Changing You images loading algorithm to something like this may solve the issue:
load your background image, let's say it's done like this: Image backgroundImg = new Bitmap(backgroundImgPath);
create a new empty image as a base for all the images: this._image = new Bimap(backgroundImg.Width, backgroundImg.Height, PixelFormat.Format32bppArgb);
draw the contents of backgroundImg onto this._image with gfx.CompositingMode = CompositingMode.SourceCopy;
then, you can draw the cliparts as You're doing it now (just comment out gfx.Save(); method)
Let me know if this helped.

initializing a 48bpp bitmap from a file in c#

I write a 48bppRgb to a file but when I create a bitmap from this file it is 32bppArgb(object img2 has a property PixelFormat.32bppArgb).
Minimized example:
Bitmap img1 = new Bitmap(100, 100, PixelFormat.Format48bppRgb);
img1.Save("/img1.bmp");
Bitmap img2 = new Bitmap("/img1.bmp");
Why?
More than one problem. You didn't save the image in the BMP format. The default format for Image.Save(string) is PNG. The PNG encoder built into GDI+ doesn't support 48bpp images. Saving as a BMP requires specifying the image format:
Bitmap img1 = new Bitmap(100, 100, PixelFormat.Format48bppRgb);
img1.Save("c:/temp/img1.bmp", ImageFormat.Bmp);
You'll however find that the BMP encoder doesn't support 48bpp images either, you'll get a 24bpp image when you load it back. None of the codecs supports 48bpp.
There's lots of missing functionality in GDI+. ImageFormat.Icon doesn't work for example, it actually saves a PNG. And support for any of the Indexed pixels formats is quite poor. If you need this kind of support then you'll need a professional imaging library. LeadTools or ImageMagick are the usual choices.

C# resize, resave .tif as .jpg gives a blue tint to the new image

I'm building a batch processor to save a directory of .tif images as .jpgs. The processing is working fine. However, the rendered jpgs have a blue-ish tint to them. They aren't "blue", as much as they have a cooler hue, a blue hue. The originals are much brighter and warmer in color. This is how I am creating the resized jpeg:
Bitmap bitmap = new Bitmap(image.Image, size);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
graphics.DrawImage(image.Image, 0, 0, size.Width, size.Height);
// Get EncoderInfo("image/jpeg") gets the jpeg Codec by mime type
bitmap.Save(path, GetEncoderInfo("image/jpeg"), EncoderParameters);
The original tif images are 7MB is size - large in comparison to the rendered jpegs. Perhaps that has something to do with it. Not sure.
I've come up empty on the Googles. Does anyone have any experience with this or any advice on what to try next? Thanks!
It could be that the original files have a color profile. In that case, you need to copy that information into the new file as well. I don't know how to do that with .NET's imaging classes.
I would first suggest a test:
Create a plain image with a single colour.
Convert it to jpeg.
Then check in a photo package if the hue has actually changed or if it is your perception.
From the MSDN documentation, it looks like you can be doing this somewhat simpler (assuming image.Image is an actual System.Drawing.Image instance):
image.Image.Save(path, System.Drawing.ImageFormat.Jpeg)
That might help - it looks like you're taking your TIF, converting to a bitmap, and only then converting to a JPG.

How to render images in real-time

I am looking for a solution\software that I can use in order to render buttons in real time.
That is, I have an image and text, and I want them to be an image altogether in realtime.
Thank you
You can dynamically create an image based on text using the System.Drawing namespace.
private Bitmap CreateBitmapImage(string imageText, string imageUrl)
{
Bitmap myBitmap = new Bitmap(imageUrl);
Graphics g = Graphics.FromImage(myBitmap);
g.DrawString(imageText, new Font("Tahoma", 40), Brushes.White, new PointF(0, 0));
return (objBmpImage);
}
Once you have the Bitmap object in memory you can save it to the disk and call it's location from the web.
Bitmap bmp = CreateBitmapImage("my picture", #"C:\myBasePic.bmp");
bmp.Save(#"C:\bla.png", System.Drawing.Imaging.ImageFormat.png);
It would be good if you can specify why such requirement is there. One of such scenario that I had encountered was need of image buttons with different text (round corners with shaded background) - this can easily be achieved using CSS. If you really want to combine an image and text together then you can do that at server side (using say System.Drawing types) and serve the combined image over a handler/web-service.

Categories