I have that function to convert BitmapImage to Bitmap:
private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
{
using(MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapImage));
enc.Save(outStream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
return new Bitmap(bitmap);
}
}
My images have black background (as .PNG they've transparent background).
How can I make them always have a transparent background?
If your display technology is WinForms, the answer is: You can not really. WinForms is a few decades to old to support proper transparency on the Display side.
Windows Forms is pretty good at feining Transparency, but not that good: https://www.codeproject.com/Articles/25048/How-to-Use-Transparent-Images-and-Labels-in-Window
Related
There is a Windows program that generates PNG from HTML via WebBrowser.
This PNG must then be inserted into a PDF that is opened in Adobe Acrobat Reader DC as a stamp.
Below is a code that draws a PNG and sends it to the clipboard.
static void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var webBrowser = (WebBrowser)sender;
Bitmap bitmap = new Bitmap(webBrowser.Width, webBrowser.Height, PixelFormat.Format32bppArgb);
webBrowser.DrawToBitmap(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
bitmap.MakeTransparent(Color.White);
Clipboard.SetImage(bitmap);
}
The image is clipboard with a gray background, although when saved via bitmap.Save("img.png", ImageFormat.Png); the background is indeed transparent.
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png);
var data = new DataObject("PNG", stream);
Clipboard.Clear();
Clipboard.SetDataObject(data, true);
}
Tried saving the image to a MemoryStream and sending it to the clipboard as a DataObject. In this case, the clipboard is left empty.
Thanks to Nyerguds, I know that by default the Windows clipboard does not preserve transparency. But due to lack of experience, I can't figure out how to apply it myself.
How can I keep the transparency of the image for later pasting?
I am trying to Crop a BitmapImage in my WPF application StageWindow.Stage using the CroppedBitmap class.
When saving the new cropped image to file JpegBitmapEncoder I can see the correctly cropped image. CroppedBitmap Crop
However I actually want to save the cropped image into a List\<BitmapImage\> for later use in another WPF image control. That list contains BitmapImages already so changing it to CroppedBitmaps would not suffice.
to be able to store the new croppedBitmap I use something like this:
BitmapImage xx = CroppedBitmap1 as BitmapImage;
when setting an WPF image control to the new BitmapImage (ExpectedCroppedImage) it still displays the CroppedBitmaps original source image without the crop.
I suspect the code above to delete the crop attribute from the new BitmapImage because the BitmapImage itself has no attributes for the cropped Area. But how can I save only the cropped part into a new BitmapImage?
I have been searching around but it seems CroppedBitmap should do the trick I just dont know how to convert it back in the correct manner.
Here is the Code for clarification:
//Stage is the WPF image element that im reading the source image from.
ImageSource StageWindowImageSource = StageWindow.Stage.Source;
CroppedBitmap Crop = new CroppedBitmap((BitmapSource)StageWindowImageSource, new Int32Rect(0,0,50,50));
ExpectedCroppedImage = Crop.Source as BitmapImage;
JpegBitmapEncoder jpg = new JpegBitmapEncoder();
jpg.Frames.Add(BitmapFrame.Create(Crop));
FileStream fp = new FileStream("F:/Crop.jpg", FileMode.Create, FileAccess.Write);
jpg.Save(fp);
fp.Close();
The line
ExpectedCroppedImage = Crop.Source as BitmapImage;
does not return the cropped bitmap, but the original one from which the crop was taken, i.e. the Source bitmap.
Besides that, you don't need any conversion from CroppedBitmap to BitmapImage.
Instead, use a base class of BitmapImage and CroppedBitmap as element type of the List, i.e. use a List<BitmapSource> or List<ImageSource> for your collection.
Now you can assign instances of any class derived from the element type.
List<BitmapSource> images = ...
CroppedBitmap croppedImage = new CroppedBitmap(...);
images.Add(croppedImage);
BitmapImage someOtherImage = new BitmapImage(...);
images.Add(someOtherImage);
private void MyMethod()
{
ImageSource StageWindowImageSource = img.Source;
CroppedBitmap Crop = new CroppedBitmap((BitmapSource) StageWindowImageSource, new Int32Rect(20, 20, 150, 110));
img2.Source = GetImage(Crop, new JpegBitmapEncoder());
}
private BitmapImage GetImage(BitmapSource source, BitmapEncoder encoder)
{
var image = new BitmapImage();
using (var sourceMs = new MemoryStream())
{
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(sourceMs);
sourceMs.Position = 0;
using (var destMs = new MemoryStream(sourceMs.ToArray()))
{
image.BeginInit();
image.StreamSource = destMs;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
image.Freeze();
}
}
return image;
}
Here's my problem :
I need to create a TIFF and a PNG with a Palette containing specific colors and alpha.
I am actually able to deal with the PNG but not with the TIFF.
I've search on Internet and found out that TIFF should handle transparency, but that not all software can.
I've tried many way to load the TIFF, but they all lead to a Palette with reseted Alpha values.
So I think that the problem comes from how I save the TIFF, but I can't find any other way.
Just in case : The palette is set with #00FFFFFF, and then with #FFXXXXXX, #FEXXXXXX, etc. Whenever I load the TIFF, the colors are back to : #FFFFFFFF, #FFXXXXXX, #FFXXXXXX, etc
CreateImage
public static BitmapSource CreateImage()
{
int width = ImageGenerator.sizeW;
int height = ImageGenerator.sizeH;
int bytesPerPixel = (format.BitsPerPixel + 7) / 8;
int stride = width * bytesPerPixel;
byte[] pixels = new byte[height * stride];
List<Color> colors = new List<Color>();
PopuplateColors(ref colors);
BitmapPalette myPalette = new BitmapPalette(colors);
PopulatePixels(height, stride, ref pixels);
BitmapSource image = BitmapSource.Create(width, height, 96, 96, format, myPalette, pixels, stride);
if (imageStreamSource != null)
imageStreamSource.Dispose();
return image;
}
CreateTIFF
public static void CreateTIFF()
{
BitmapSource image = CreateImage();
FileStream stream = new FileStream("test.tif", FileMode.Create);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);
stream.Dispose();
}
GetTIFF
public static ImageBrush GetTIFF()
{
/* Another try
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri("test.tif",UriKind.Relative);
bitmap.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
bitmap.EndInit();
imagePalette = bitmap.Palette;
CreateData(bitmap);
return new ImageBrush(bitmap);*/
//The last try i've done, i've looked at the palette through debug
Bitmap bitmap = new Bitmap("test.tif");
System.Drawing.Imaging.ColorPalette cp = bitmap.Palette;
bitmap.MakeTransparent(cp.Entries[0]);
return new ImageBrush();
/* How I normally read the TIFF
TiffBitmapDecoder decoder = new TiffBitmapDecoder(GetImage("test.tif"), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bitmapSource = decoder.Frames[0];
imagePalette = bitmapSource.Palette;
CreateData(bitmapSource);
return new ImageBrush(bitmapSource);*/
}
Palette or indexed color TIFF does not support alpha values in the color map. The entries are 16 bit per color component, but only R, G, B (and no Alpha component) are stored. If you try to store a TIFF with alpha in the color map, the alpha will be lost.
You could probably store the TIFF with a separate "Transparency mask" (single bit mask) though. This will be a separate IFD, meaning some software will probably not merge it with the RGB values to display a transparent image. But if you control the software that reads and writes the TIFF file, I think this will be your best option for transparency.
Another option, if you need the transparency information in the color map (and you control the reading and writing), is to either make the first color in the color map transparent always, or add a custom tag with the transparent index that you can set to transparent. Your image should still display correctly in other software, but without the transparency.
See TIFF tags ColorMap and PhotometricInterpretation (there is also an Indexed extension tag, but it doesn't change the number of components stored in the color map for an RGB image).
I am writing a new WPF application that creates some visual elements and then trying to print them on a Poloroid printer. I am successfully printing using the System.Printing classes in WPF as follows:
Dim Pd As PrintDialog = New PrintDialog()
Dim Ps = New LocalPrintServer()
Dim PrintQueue = New PrintQueue(Ps, "Poloroid Printer")
Pd.PrintQueue = PrintQueue
Pd.PrintVisual(Me.Grid1, "Print Job 1") 'this prints out perfectly
The problem is that the poloroid printer has an SDK that you can use to write to the Magstrip on the back of the card. I have a working example using the PrintPageEventArgs in System.Drawing.Printing but I cannot find any close matches for the WPF world. Here is that code:
Private Sub PrintPage(ByVal sender as Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs)
'...
' Obtain the device context for this printer
deviceContext = e.Graphics.GetHdc().ToInt32()
'... device context is used in SDK to write to magstrip
e.Graphics.ReleaseHdc(New IntPtr(deviceContext))
End Sub
So, my question is:
How can I print my existing markup (XAML) using System.Drawing.Printing
OR
Are there events in the System.Printing to talk to the SDK and getting the Int32 deviceContext?
and I tried to RenderTargetBitmap class to render the visual in WPF to bitmap and convert the bitmap to System.Drawing.Bitmap
RenderTargetBitmap bitmapSource;
...
bitmapSource.Render(visual);
...
using(MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSource));
enc.Save(outStream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
...
}
But printing was not clear and perfect.
Try this
Rect bounds = VisualTreeHelper.GetDescendantBounds(FrontCanvas);
double dpiX =e.Graphics.DpiX , dpiY=e.Graphics.DpiY ;
RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
(int)(bounds.Height * dpiY / 96.0),
dpiX,
dpiY,
PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(FrontCanvas);
ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);
//System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(rtb);
//e.Graphics.DrawImage(rtb, 0, 0);
using (MemoryStream outStream = new MemoryStream())
{
// Use png encoder for data
BitmapEncoder encoder = new PngBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(outStream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
e.Graphics.DrawImage(bitmap, 0, 0);
}
I recently answered a similar question that can be found here: https://stackoverflow.com/a/43373398/3393832. That question and answer relate more to magnetic encoding (on ID cards) than image printing, but I will try to give some insight about that here, as well.
For the card images, I actually set up two Grids within my XAML: an outer grid that houses everything (buttons, labels, combo-boxes, the inner grid) and an inner grid that is essentially the image I want to apply, in full, on the card itself. In code, I take a "snapshot" of the inner grid after all elements have been added/updated and simply send that image into the Print Job.
To continue with my previous post (the link above), I add the image print AFTER the magnetic encoding step for the first side of the card (PageCount == 0).
Below is some code that I use to get the Bitmap "snapshot" of the Grid/Full Image without any loss of clarity/resolution or any pixelation:
RenderTargetBitmap rtb = new RenderTargetBitmap(gridWidth, gridHeight, 210, 210, PixelFormats.Pbgra32);
rtb.Render(YourGrid);
// If you need to Crop Anything Out
CroppedBitmap crop = new CroppedBitmap();
crop = new CroppedBitmap(rtb, new Int32Rect(0, 0, gridWidth, gridHeight));
Bitmap bmpToPrint = BitmapSourceToBitmap(crop);
// Helper Method
public Bitmap BitmapSourceToBitmap(BitmapSource bs)
{
Bitmap bitmap;
using (MemoryStream ms = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bs));
enc.Save(ms);
bitmap = new Bitmap(ms);
}
return bitmap;
}
For the Magnetic Encoding, I use e.Graphics.DrawString and for the Image (you guessed it) e.Graphics.DrawImage. Simply call DrawString prior to DrawImage and your Printer Driver should pick up the encode prior to the image within the PrintPage method (again, please refer to the link above for more details into the Print method I am using. This method is also not tied to a printer make or model and is not reliant on any SDK).
Though generic and in need of your particular implementation details, I hope this post helps guide you to a solution that fits your particular needs. Please don't hesitate to ask if you have any follow-up questions or need more detail into the particular elements of the print job itself.
I wrote a little utility class that saves BitmapSource objects to image files. The image files can be either bmp, jpeg, or png. Here is the code:
public class BitmapProcessor
{
public void SaveAsBmp(BitmapSource bitmapSource, string path)
{
Save(bitmapSource, path, new BmpBitmapEncoder());
}
public void SaveAsJpg(BitmapSource bitmapSource, string path)
{
Save(bitmapSource, path, new JpegBitmapEncoder());
}
public void SaveAsPng(BitmapSource bitmapSource, string path)
{
Save(bitmapSource, path, new PngBitmapEncoder());
}
private void Save(BitmapSource bitmapSource, string path, BitmapEncoder encoder)
{
using (var stream = new FileStream(path, FileMode.Create))
{
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(stream);
}
}
}
Each of the three Save methods work, but I get unexpected results with bmp and jpeg. Png is the only format that produces an exact reproduction of what I see if I show the BitmapSource on screen using a WPF Image control.
Here are the results:
BMP - too dark
too dark http://img822.imageshack.us/img822/7403/terrainbmp.png
JPEG - too saturated
too saturated http://img816.imageshack.us/img816/8127/terrainjpeg.jpg
PNG - correct
correct http://img810.imageshack.us/img810/6243/terrainpng.png
Why am I getting completely different results for different file types?
I should note that the BitmapSource in my example uses an alpha value of 0.1 (which is why it appears very desaturated), but it should be possible to show the resulting colors in any image format. I know if I take a screen capture using something like HyperSnap, it will look correct regardless of what file type I save to.
Here's a HyperSnap screen capture saved as a bmp:
correct http://img815.imageshack.us/img815/9966/terrainbmphypersnap.png
As you can see, this isn't a problem, so there's definitely something strange about WPF's image encoders.
Do I have a setting wrong? Am I missing something?
I don't personally think it too surprising to see what you're seeing. BMP and JPG don't support opacity and PNG does.
Take this code, which creates a partially transparent blue rectangle in an image.
WriteableBitmap bm = new WriteableBitmap( 100, 100, 96, 96, PixelFormats.Pbgra32, null );
bm.Lock();
Bitmap bmp = new Bitmap( bm.PixelWidth, bm.PixelHeight, bm.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format32bppArgb, bm.BackBuffer );
using( Graphics g = Graphics.FromImage( bmp ) ) {
var color = System.Drawing.Color.FromArgb( 20, System.Drawing.Color.Blue);
g.FillRectangle(
new System.Drawing.SolidBrush( color ),
new RectangleF( 0, 0, bmp.Width, bmp.Height ) );
}
bmp.Save( #".\000_foo.bmp", System.Drawing.Imaging.ImageFormat.Bmp );
bmp.Save( #".\000_foo.jpg", System.Drawing.Imaging.ImageFormat.Jpeg );
bmp.Save( #".\000_foo.png", System.Drawing.Imaging.ImageFormat.Png );
bmp.Dispose();
bm.AddDirtyRect( new Int32Rect( 0, 0, bm.PixelWidth, bm.PixelHeight ) );
bm.Unlock();
new BitmapProcessor().SaveAsBmp( bm, #".\foo.bmp" );
new BitmapProcessor().SaveAsJpg( bm, #".\foo.jpg" );
new BitmapProcessor().SaveAsPng( bm, #".\foo.png" );
The PNG formats always work, whether it's System.Drawing or the WPF encoders. The JPG and BMP encoders do not work. They show a solid blue rectangle.
The key here is I failed to specify a background color in my image. Without a background color, the image won't render correctly in formats that don't support an alpha channel (BMP/JPG). With one extra line of code:
g.Clear( System.Drawing.Color.White );
g.FillRectangle(
new System.Drawing.SolidBrush( color ),
new RectangleF( 0, 0, bmp.Width, bmp.Height ) );
My image has a background color, so the encoders that do not support an alpha channel can determine what the output color should be per pixel. Now all my images look correct.
In your case, you should either RenderTargetBitmap a control with a background color specified, or paint a background color when you're rendering your image.
And FYI, the reason your 3rd party print screen works is that ultimately the transparent colors have a background color at that point (being on a window which has a background color). But inside WPF, you're dealing with elements that don't have one set; using RTB on an element does not inherit its various parent element's properties like background color.