Need Help Setting an Image with Transparent Background to Clipboard - winforms

I need help setting a transparent image to the clipboard. I keep getting "handle is invalid". Basically, I need a "second set of eyes" to look over the following code. (The complete working project at ftp://missico.net/ImageVisualizer.zip.)
This is an image Debug Visualizer class library, but I made the included project to run as an executable for testing. (Note that window is a toolbox window and show in taskbar is set to false.) I was tired of having to perform a screen capture on the toolbox window, open the screen capture with an image editor, and then deleting the background added because it was a screen capture. So I thought I would quickly put the transparent image onto the clipboard. Well, the problem is...no transparency support for Clipboard.SetImage. Google to the rescue...not quite.
This is what I have so far. I pulled from a number of sources. See the code for the main reference. My problem is the "invalid handle" when using CF_DIBV5. Do I need to use BITMAPV5HEADER and CreateDIBitmap?
Any help from you GDI/GDI+ Wizards would be greatly appreciated.
public static void SetClipboardData(Bitmap bitmap, IntPtr hDC) {
const uint SRCCOPY = 0x00CC0020;
const int CF_DIBV5 = 17;
const int CF_BITMAP = 2;
//'reference
//'http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/816a35f6-9530-442b-9647-e856602cc0e2
IntPtr memDC = CreateCompatibleDC(hDC);
IntPtr memBM = CreateCompatibleBitmap(hDC, bitmap.Width, bitmap.Height);
SelectObject(memDC, memBM);
using (Graphics g = Graphics.FromImage(bitmap)) {
IntPtr hBitmapDC = g.GetHdc();
IntPtr hBitmap = bitmap.GetHbitmap();
SelectObject(hBitmapDC, hBitmap);
BitBlt(memDC, 0, 0, bitmap.Width, bitmap.Height, hBitmapDC, 0, 0, SRCCOPY);
if (!OpenClipboard(IntPtr.Zero)) {
throw new System.Runtime.InteropServices.ExternalException("Could not open Clipboard", new Win32Exception());
}
if (!EmptyClipboard()) {
throw new System.Runtime.InteropServices.ExternalException("Unable to empty Clipboard", new Win32Exception());
}
//IntPtr hClipboard = SetClipboardData(CF_BITMAP, memBM); //works but image is not transparent
//all my attempts result in SetClipboardData returning hClipboard = IntPtr.Zero
IntPtr hClipboard = SetClipboardData(CF_DIBV5, memBM);
//because
if (hClipboard == IntPtr.Zero) {
// InnerException: System.ComponentModel.Win32Exception
// Message="The handle is invalid"
// ErrorCode=-2147467259
// NativeErrorCode=6
// InnerException:
throw new System.Runtime.InteropServices.ExternalException("Could not put data on Clipboard", new Win32Exception());
}
if (!CloseClipboard()) {
throw new System.Runtime.InteropServices.ExternalException("Could not close Clipboard", new Win32Exception());
}
g.ReleaseHdc(hBitmapDC);
}
}
private void __copyMenuItem_Click(object sender, EventArgs e) {
using (Graphics g = __pictureBox.CreateGraphics()) {
IntPtr hDC = g.GetHdc();
MemoryStream ms = new MemoryStream();
__pictureBox.Image.Save(ms, ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
Image imag = Image.FromStream(ms);
// Derive BitMap object using Image instance, so that you can avoid the issue
//"a graphics object cannot be created from an image that has an indexed pixel format"
Bitmap img = new Bitmap(new Bitmap(imag));
SetClipboardData(img, hDC);
g.ReleaseHdc();
}
}

There are a few things you can do to tighten up the codebase a bit, and I've done some homework on CF_DIBV5 that you may find helpful.
First off, in __copyMenuItem_Click(), we have four complete copies of your image, which is much more than necessary.
__pictureBox.Image
Image imag = Image.FromStream(ms);
new Bitmap(imag)
Bitmap img = new Bitmap(new Bitmap(imag)); (the outer bitmap)
Furthermore, your MemoryStream, imag, new Bitmap(imag), and img do not get disposed, which could result in problems.
Without changing the intent of the code (and probably without solving the handle issue), you could rewrite the method like this:
private void __copyMenuItem_Click(object sender, EventArgs e)
{
var image = __pictureBox.Image;
using (var g = __pictureBox.CreateGraphics())
using (var bmp = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb))
using (var bmpg = Graphics.FromImage(bmp))
{
IntPtr hDC = g.GetHdc();
bmpg.DrawImage(image, 0, 0, image.Width, image.Height);
SetClipboardData(bmp, hDC);
g.ReleaseHdc();
}
}
The next thing that looked like it would require attention was the line:
IntPtr hClipboard = SetClipboardData(CF_DIBV5, memBM);
I am fairly certain that you must marshal out the BITMAPV5HEADER structure to pass bits to the clipboard when using CF_DIBV5. I've been wrong before, but I would verify that memBM actually contains the header. A good indicator is whether the first DWORD (UInt32) has the value 124 (the size of the header in bytes).
My final remarks are more recommendation than a second pair of eyes. I found that photo applications like GIMP, Paint.NET, Fireworks, and PhotoScape appear to have poor or non-existant support for CF_DIBV5 (Format17) pasting. you might consider copying to the clipboard the PNG format, with an opaque bitmap as backup just in case the target application does not support PNG. I use an extension method to facilitate this:
public static void CopyMultiFormatBitmapToClipboard(this Image image)
{
using (var opaque = image.CreateOpaqueBitmap(Color.White))
using (var stream = new MemoryStream())
{
image.Save(stream, ImageFormat.Png);
Clipboard.Clear();
var data = new DataObject();
data.SetData(DataFormats.Bitmap, true, opaque);
data.SetData("PNG", true, stream);
Clipboard.SetDataObject(data, true);
}
}
With the extension method in hand, your __copyMenuItem_Click() method could be reduced to the following, and the SetClipboardData() method could be removed altogether:
private void __copyMenuItem_Click(object sender, EventArgs e)
{
__pictureBox.Image.CopyMultiFormatBitmapToClipboard();
}
Now, as we already discussed on another thread, PNG support may not cut it for you. I've tested this approach on a few applications; however, it will be up to you to determine whether this is sufficient transparency support for your requirements.
GIMP: transparency supported
Fireworks (3.0): transparency supported
PhotoScape: white background
Paint.NET: white background
MS PowerPoint 2007: transparency supported
MS Word 2007: white background
MS Paint (Win7): white background
Discussion of everything I looked into would be too lengthy for Stack Overflow. I have additional sample code and discussion available at my blog: http://www.notesoncode.com/articles/2010/08/16/HandlingTransparentImagesOnTheClipboardIsForSomeReasonHard.aspx
Good luck!

I see three problems:
The invalid handle error might come from leaving memBM selected into memDC. You should always select the bitmap out of a DC before passing it anywhere else.
BitBlt is a GDI call (not GDI+). GDI doesn't preserve the alpha channel. On newer versions of Windows, you can use AlphaBlend to composite a bitmap with alpha onto a background, but the composite won't have an alpha channel.
You've created a compatible bitmap, which means the bitmap has the same color format as the DC you passed in (which I'm assuming is the same as the screen). So your bitmap could end up as 16-bit, 24-bit, 32-bit, or even 8-bit, depending on how the screen is set. If BitBlt had preserved the alpha channel of the original, you'd likely lose it when converting to the screen format.

Bitmap.GetHbitmap() will actually composite the bitmap to an opaque background, losing the alpha channel. This question addressed how to get an HBITMAP with the alpha channel intact.

Related

Create a FrameworkElement out of an BitmapImage file, and then destroy?

I have a WPF project, which we using some WindowsForm control(PivotTable from OWC, Office Web Component). However, since the PivotTable doesn't support print very well, the only way we can think of is to print the image file PivotTable exports(another way is to print from exported Excel file, but we want to avoid it since it is not guaranteed that Excel is installed on the machine).
We already have one print project, which will print WPF ElementFramework nicely. So I want to use that piece of code. Now my question is: how I can generate a FrameworkElement out of a BitmapImage from code. Since this FrameworkElement is purely for printing, so I guess I must create it from the code, probably assign a parent to it, not show it on the screen, and after the printing destroy the FrameworkElement so that ultimately I can delete the temp BitmapImage file.
So this is beyond my knowledge. I don't even know whether that's a proper way: create a UI element for some non-UI related work? Any advice on that? Thanks!
You should load the temp image file's bytes into memory and create a Image from those bytes.
Your Image will not use the temp file directly and you can delete it immediately after reading the bytes, the rest is GarbageCollector's job. Here is a function to create your BitmapImage from bytes:
public static BitmapImage GetImageFromByteArray(byte[] rawImageBytes)
{
BitmapImage imageSource = null;
try
{
using (MemoryStream stream = new MemoryStream(rawImageBytes))
{
stream.Seek(0, SeekOrigin.Begin);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.StreamSource = stream;
bi.EndInit();
imageSource = bi;
}
}
catch (System.Exception)
{
}
return imageSource;
}
And use it like this:
testImage.Source = GetImageFromByteArray(rawImageBytes);

SurfaceDragDrop never seems to end, object seems stuck in eternal (non)manipulation

I'm developing a WPF application for the Surface, and have run into a bug which neither I nor my colleagues know the answer to.
I have a situation when the user can drag things from a box. The box is just an image, and when touching it, another image is spawned and is the object that gets the interaction, i.e. gets dragged and dropped. The whole dragging is performed with SurfaceDragDrop.BeginDragDrop(...). Upon drop, the object simply disappears.
Now the problem is that occasionally (seems to be when input comes rapidly, just hammering the screen and not actually dragging the objects) the spawned item never despawns, i.e. it remains on screen even when input has been removed. It is possible to intereact with it by touching it to continue dragging it, but it's movement and rotation is off because it appears to think there already is another finger interacting with it.
However, through debugging I have certified that there are no touches registered to the object. It's AreAnyTouchesCaptured property is false. Manipulation is however active. So what seems to happen is that it goes into some sort of manipulation which never ends.
In order to get rid of it, I've implemented a temporary solution which simply does SurfaceDragDrop.CancelDragDrop(..) on such objects that have been on screen a certain period of time without any touches captured.
I hope my description of the problem is somewhat clear, if not please ask. Below is the code used to spawn the objects. I have however not written it (an ex-colleague did), I'm just stuck with the debugging:
private void item_PreviewTouchDown(object sender, TouchEventArgs e)
{
var box = sender as Canvas;
var imgpath = String.Format(#"[someimgpath].png");
var bmp = new BitmapImage(new Uri(imgpath, UriKind.RelativeOrAbsolute));
Image draggedItem = new Image { Source = bmp, Stretch = Stretch.None, Width = bmp.Width, Height = bmp.Height };
draggedItem.UpdateLayout();
var touchPoint = e.TouchDevice.GetTouchPoint(box);
var itemX = touchPoint.Position.X - draggedItem.Width / 2.0;
var itemY = touchPoint.Position.Y - draggedItem.Height / 2.0;
box.Children.Add(draggedItem);
draggedItem.SetValue(Canvas.LeftProperty, itemX);
draggedItem.SetValue(Canvas.TopProperty, itemY);
draggedItem.Visibility = System.Windows.Visibility.Hidden;
//We should perfom drag-and-drop when the size of the draggedItem is updated, since
//BeginDragDrop uses ActualWidth and ActualHeight property
draggedItem.SizeChanged += (o, ev) =>
{
List<InputDevice> devices = new List<InputDevice>();
devices.Add(e.TouchDevice);
foreach (TouchDevice touch in box.TouchesCapturedWithin)
{
if (touch != e.TouchDevice)
{
devices.Add(touch);
}
}
var img = new Image { Source = draggedItem.Source, Stretch = Stretch.None, Width = bmp.Width, Height = bmp.Height };
img.SetValue(Canvas.LeftProperty, itemX);
img.SetValue(Canvas.TopProperty, itemY);
var cursor = SurfaceDragDrop.BeginDragDrop(box, draggedItem, img, draggedItem, devices, DragDropEffects.Move);
};
e.Handled = true;
}

Update Image container when local image source file changes

I am buliding a GUI around an algorithm. For this purpose I have a Window component which contains a Image component inside it.
The content inside the Image component (imgHolder) is given by a BitmapImage object (_image), which for the purpose of this example is initialized as such:
_image = new BitmapImage();
_image.BeginInit();
_image.CacheOption = BitmapCacheOption.OnLoad;
_image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
_image.UriSource = new Uri("c:\\a1.bmp");
_image.EndInit();
imgHolder.Source = _image;
The main program containing the Window will perform a call to one of the algorithm's functions from within the Matlab DLL file. This will run one iteration of the algorithm, by the end of which it will write the results to the local bmp file from above ("c:\a1.bmp"). Consequently the underlying data for the image has been updated and I would like for this to be reflected in the imgHolder component. For this purpose I am simply duplicating the code above whenever I return from the function.
The problem with this approach is that the algorithm will have around 100 iterations per run so this would have to happen 100 times. It just doesn't seem right. I would be creating 100 different bitmap images all sourced from the same file.
Researching this I found out that one could use memorystream instead but I wonder whether this would be a drastical improvement or not as it seems that I would still have to create 100 images as the algorithm executes. Is it not possible to directly modify the underlying buffer somehow? I hear that GetPixel and SetPixel are not very efficient as they set a lock each time they are called. Is it possible perhaps to do this using the same _image object? If this was not a local file, I could've used a no cache option and it should've worked "by itself". Having a no cache option with the file being local did not work because of the lock.
Does anyone have a better idea?
Thanks!
I haven't quite understood if you do have access to the bitmap's raw data or not, or if it is always written to file. Since you mention SetPixel i guess you have.
So you could perhaps use a WritableBitmap as _image.Source and cyclically update its content by one of its WritePixels methods.
I just tried the following example which updates the image on every mouse move with a simple pattern.
XAML:
<Window ...>
<Grid>
<Image Name="image" />
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
private WriteableBitmap bitmap = new WriteableBitmap(100, 100, 96d, 96d, PixelFormats.Rgb24, null);
private byte[] buffer = new byte[30000];
public MainWindow()
{
InitializeComponent();
image.Source = bitmap;
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
Point pos = e.GetPosition(image);
int offset = (int)pos.X + (int)pos.Y;
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = (byte)(i + offset);
}
bitmap.WritePixels(new Int32Rect(0, 0, 100, 100), buffer, 300, 0);
}
}

Converting a BitmapSource to Image keeping all frames

I have a method here that is supposed to produce a System.Drawing.Image instance. Consider the following prerequesites:
I get a BitmapSource as a method parameter
Below you find the code that does
the transformation from BitmapSource to Image.
Conversion:
public Image ConvertBitmapSourceToImage(BitmapSource input)
{
MemoryStream transportStream = new MemoryStream();
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(input));
enc.Save(transportStream);
transportStream.Seek(0, SeekOrigin.Begin);
return Image.FromStream(transportStream);
}
Now imagine the BitmapSource has been created from a Multipage Tif file. What I need to do is make any nth page available in code. The BitmapSource class offers no support for this, so do you know a way how the get any one but the first frame out of my input? Or does BitmapSource read in the whole Tif as one frame, losing the framing information?
If it were possible, I could add another parameter to my method signature, like so:
public Image ConvertBitmapSourceToImage(BitmapSource input, int frame)
{
///[..]
}
Any Ideas?
Thanks in advance!
As you've said already, a BitmapSource does not support multiple frames. Perhaps it would be an option to step in at the point where the TIFF is decoded and convert an Image from every frame:
TiffBitmapDecoder decoder = new TiffBitmapDecoder(...) // use stream or uri here
System.Drawing.Image[] images = new System.Drawing.Image[decoder.Frames.Count];
for (int i = 0; i < decoder.Frames.Count; i++)
{
// use your converter function here
images[i] = ConvertBitmapSourceToImage(decoder.Frames[i]));
}
I didn't test the above code, so sorry for any flaws.

Is there a way to display a image in WPF stored in memory?

What I got is something like a screenshot making application. (managed to serialize, thank god!!!) When a button is clicked a screenshot is taken by accessing a handling classe's method. now the tricky part is that the class has another method for operating with the above said result, in such a manner than when the respective handling method is called, a window is created(shown) and the bitmap image should go into a display container in that window. The problem is that so far, I've noticed that in WPF the image control's source cannot be attribuited to a variable which stores the image. How can i display the image stored in that variable without having to save it first,get uri,etc. ?
You need to create an image from a memory stream, this has been well documented by many people. Here are two links that may get you started:
http://forums.silverlight.net/forums/p/44637/166282.aspx
http://www.wpftutorial.net/Images.html
thanks for the links slugster. Here's how I did it:
MemoryStream ms = new MemoryStream();
sBmp = gBmp; //note: gBmp is a variable that stores the captured image and passes it on to the method that uses sBMP as a distribuitor of the variable holding the captured image data
//variable that holds image
sBmp.Save(ms,ImageFormat.Bmp);
//my buffer byte
byte[] buffer = ms.GetBuffer();
//Create new MemoryStream that has the contents of buffer
MemoryStream bufferPasser = new MemoryStream(buffer);
//Creates a window with parents classthatholdsthismethod and null
Edit childEdit = new Edit(this, null);
childEdit.Show();
//I create a new BitmapImage to work with
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = bufferPasser;
bitmap.EndInit();
//I set the source of the image control type as the new BitmapImage created earlier.
childEdit.imgImageCanvas.Source = bitmap;
childEdit.Activate();
I've basically combined what I had found on those pages with some info I found on how to pass on a bmp to a memstream. I got this to work 100% :)

Resources