Save TIFF with transparency/alpha channel - c#

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).

Related

C# Image GPS location property disappears after resizing image [duplicate]

Yes yes... I've seen other posts related to this issue, and yes... I've googled about it.
But so far, I was not able to get to the result I need.
I'm loading a large image taken in 300 dpi, and I need to resize it.
I know... I know... dpi is relative and doesn't really matter... what matters are the dimensions in pixels:
DPI is essentially the number of pixels that correspond to an inch when the image is printed not when it is viewed on a screen. Therefore by increasing the DPI of the image, you do not increase the size of the image on the screen. You only increase the quality of print.
Even though the DPI information stored in the EXIF of an image is somewhat useless, it is causing me problems.
The image I'm resizing is losing the original exif information, including the horizontal and vertical resolution (dpi), and thus it is saving with a default of 96 dpi. Possible reason to this is that only JPEG and another format can hold metadata information.
The end image result is should look like this: 275x375 at 300dpi
Instead is looking like this: 275x375 at 96dpi
You can argue that they are they same, and I agree, but we have a corel draw script that used to load these images, and since this dpi information is different, it places it in different sizes on the document.
Here's what I'm using for resizing:
public System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
{
Bitmap result = new Bitmap(width, height);
// set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//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.High;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
That does the work very well, but loses the EXIF information.
Setting the SetResolution to SetResolution(300, 300) does not work!
I looked at reading and changing the EXIF information of an image, and I've tried:
public void setImageDpi(string Filename, string NewRes)
{
Image Pic;
PropertyItem[] PropertyItems;
byte[] bDescription = new Byte[NewRes.Length];
int i;
string FilenameTemp;
System.Drawing.Imaging.Encoder Enc = System.Drawing.Imaging.Encoder.Transformation;
EncoderParameters EncParms = new EncoderParameters(1);
EncoderParameter EncParm;
ImageCodecInfo CodecInfo = GetEncoderInfo("image/jpeg");
// copy description into byte array
for (i = 0; i < NewRes.Length; i++) bDescription[i] = (byte)NewRes[i];
// load the image to change
Pic = Image.FromFile(Filename);
foreach (PropertyItem item in Pic.PropertyItems)
{
if (item.Id == 282 || item.Id == 283)
{
PropertyItem myProperty = item;
myProperty.Value = bDescription;
myProperty.Type = 2;
myProperty.Len = NewRes.Length;
Pic.SetPropertyItem(item);
Console.WriteLine(item.Type);
}
}
// we cannot store in the same image, so use a temporary image instead
FilenameTemp = Filename + ".temp";
// for lossless rewriting must rotate the image by 90 degrees!
EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate90);
EncParms.Param[0] = EncParm;
// now write the rotated image with new description
Pic.Save(FilenameTemp, CodecInfo, EncParms);
// for computers with low memory and large pictures: release memory now
Pic.Dispose();
Pic = null;
GC.Collect();
// delete the original file, will be replaced later
System.IO.File.Delete(Filename);
// now must rotate back the written picture
Pic = Image.FromFile(FilenameTemp);
EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate270);
EncParms.Param[0] = EncParm;
Pic.Save(Filename, CodecInfo, EncParms);
// release memory now
Pic.Dispose();
Pic = null;
GC.Collect();
// delete the temporary picture
System.IO.File.Delete(FilenameTemp);
}
That didn't work either.
I tried looking and changing the EXIF information for DPI (282 and 283) later in the process as such:
Encoding _Encoding = Encoding.UTF8;
Image theImage = Image.FromFile("somepath");
PropertyItem propItem282 = theImage.GetPropertyItem(282);
propItem282.Value = _Encoding.GetBytes("300" + '\0');
theImage.SetPropertyItem(propItem282);
PropertyItem propItem283 = theImage.GetPropertyItem(283);
propItem283.Value = _Encoding.GetBytes("300" + '\0');
theImage.SetPropertyItem(propItem283);
theImage.Save("somepath");
But the program crashes saying that Property Cannot be Found.
If the property doesn't exist, apparently I can't add it:
A PropertyItem is not intended to be used as a stand-alone object. A PropertyItem object is intended to be used by classes that are derived from Image. A PropertyItem object is used to retrieve and to change the metadata of existing image files, not to create the metadata. Therefore, the PropertyItem class does not have a defined Public constructor, and you cannot create an instance of a PropertyItem object.
I'm stuck... all I need is a resized image with a dpi set to 300, it shouldn't be so hard.
Any help much appreciated. Thanks
The following code worked for me:
const string InputFileName = "test_input.jpg";
const string OutputFileName = "test_output.jpg";
var newSize = new Size(640, 480);
using (var bmpInput = Image.FromFile(InputFileName))
{
using (var bmpOutput = new Bitmap(bmpInput, newSize))
{
foreach (var id in bmpInput.PropertyIdList)
bmpOutput.SetPropertyItem(bmpInput.GetPropertyItem(id));
bmpOutput.SetResolution(300.0f, 300.0f);
bmpOutput.Save(OutputFileName, ImageFormat.Jpeg);
}
}
When I inspect the output file I can see EXIF data and the DPI has been changed to 300.

C# Image Resizing - Losing EXIF

Yes yes... I've seen other posts related to this issue, and yes... I've googled about it.
But so far, I was not able to get to the result I need.
I'm loading a large image taken in 300 dpi, and I need to resize it.
I know... I know... dpi is relative and doesn't really matter... what matters are the dimensions in pixels:
DPI is essentially the number of pixels that correspond to an inch when the image is printed not when it is viewed on a screen. Therefore by increasing the DPI of the image, you do not increase the size of the image on the screen. You only increase the quality of print.
Even though the DPI information stored in the EXIF of an image is somewhat useless, it is causing me problems.
The image I'm resizing is losing the original exif information, including the horizontal and vertical resolution (dpi), and thus it is saving with a default of 96 dpi. Possible reason to this is that only JPEG and another format can hold metadata information.
The end image result is should look like this: 275x375 at 300dpi
Instead is looking like this: 275x375 at 96dpi
You can argue that they are they same, and I agree, but we have a corel draw script that used to load these images, and since this dpi information is different, it places it in different sizes on the document.
Here's what I'm using for resizing:
public System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
{
Bitmap result = new Bitmap(width, height);
// set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//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.High;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
That does the work very well, but loses the EXIF information.
Setting the SetResolution to SetResolution(300, 300) does not work!
I looked at reading and changing the EXIF information of an image, and I've tried:
public void setImageDpi(string Filename, string NewRes)
{
Image Pic;
PropertyItem[] PropertyItems;
byte[] bDescription = new Byte[NewRes.Length];
int i;
string FilenameTemp;
System.Drawing.Imaging.Encoder Enc = System.Drawing.Imaging.Encoder.Transformation;
EncoderParameters EncParms = new EncoderParameters(1);
EncoderParameter EncParm;
ImageCodecInfo CodecInfo = GetEncoderInfo("image/jpeg");
// copy description into byte array
for (i = 0; i < NewRes.Length; i++) bDescription[i] = (byte)NewRes[i];
// load the image to change
Pic = Image.FromFile(Filename);
foreach (PropertyItem item in Pic.PropertyItems)
{
if (item.Id == 282 || item.Id == 283)
{
PropertyItem myProperty = item;
myProperty.Value = bDescription;
myProperty.Type = 2;
myProperty.Len = NewRes.Length;
Pic.SetPropertyItem(item);
Console.WriteLine(item.Type);
}
}
// we cannot store in the same image, so use a temporary image instead
FilenameTemp = Filename + ".temp";
// for lossless rewriting must rotate the image by 90 degrees!
EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate90);
EncParms.Param[0] = EncParm;
// now write the rotated image with new description
Pic.Save(FilenameTemp, CodecInfo, EncParms);
// for computers with low memory and large pictures: release memory now
Pic.Dispose();
Pic = null;
GC.Collect();
// delete the original file, will be replaced later
System.IO.File.Delete(Filename);
// now must rotate back the written picture
Pic = Image.FromFile(FilenameTemp);
EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate270);
EncParms.Param[0] = EncParm;
Pic.Save(Filename, CodecInfo, EncParms);
// release memory now
Pic.Dispose();
Pic = null;
GC.Collect();
// delete the temporary picture
System.IO.File.Delete(FilenameTemp);
}
That didn't work either.
I tried looking and changing the EXIF information for DPI (282 and 283) later in the process as such:
Encoding _Encoding = Encoding.UTF8;
Image theImage = Image.FromFile("somepath");
PropertyItem propItem282 = theImage.GetPropertyItem(282);
propItem282.Value = _Encoding.GetBytes("300" + '\0');
theImage.SetPropertyItem(propItem282);
PropertyItem propItem283 = theImage.GetPropertyItem(283);
propItem283.Value = _Encoding.GetBytes("300" + '\0');
theImage.SetPropertyItem(propItem283);
theImage.Save("somepath");
But the program crashes saying that Property Cannot be Found.
If the property doesn't exist, apparently I can't add it:
A PropertyItem is not intended to be used as a stand-alone object. A PropertyItem object is intended to be used by classes that are derived from Image. A PropertyItem object is used to retrieve and to change the metadata of existing image files, not to create the metadata. Therefore, the PropertyItem class does not have a defined Public constructor, and you cannot create an instance of a PropertyItem object.
I'm stuck... all I need is a resized image with a dpi set to 300, it shouldn't be so hard.
Any help much appreciated. Thanks
The following code worked for me:
const string InputFileName = "test_input.jpg";
const string OutputFileName = "test_output.jpg";
var newSize = new Size(640, 480);
using (var bmpInput = Image.FromFile(InputFileName))
{
using (var bmpOutput = new Bitmap(bmpInput, newSize))
{
foreach (var id in bmpInput.PropertyIdList)
bmpOutput.SetPropertyItem(bmpInput.GetPropertyItem(id));
bmpOutput.SetResolution(300.0f, 300.0f);
bmpOutput.Save(OutputFileName, ImageFormat.Jpeg);
}
}
When I inspect the output file I can see EXIF data and the DPI has been changed to 300.

Display captured Jpeg File

I'm able to save the captured image from a barcode scanner using this code:
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.DefaultExt = ".jpg";
dlg.Filter = "JPEG Images (.jpg)|*.jpg|All files (*.*)|*.*";
if (dlg.ShowDialog() == true)
{
using (FileStream file = File.OpenWrite(dlg.FileName))
{
file.Write(e.ImageBuffer, 0, e.ImageSize);
}
}
However, I would like to display the captured image using WPF but I get a distorted image.
private void _barcodeScannerInstance_SavePhotoEvent(object sender, ImageEventArgs e)
{
SetBitmap(e.ImageBuffer, 350, 263, 96);
}
private void SetBitmap(byte[] image, int width, int height, int dpi)
{
MainWindow.Instance.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate()
{
BitmapSource bitmapSource = BitmapSource.Create(
width, height, (double)dpi, (double)dpi, PixelFormats.Bgr24, null, image, ((width * 24 + 31) & ~31) >> 3);
HwModeScreen.BarcodeImageCanvas.Children.Clear();
Image myImage = new Image();
myImage.Width = HwModeScreen.BarcodeImageCanvas.ActualWidth;
myImage.Height = HwModeScreen.BarcodeImageCanvas.ActualHeight;
myImage.Stretch = Stretch.Fill;
myImage.Source = bitmapSource;
HwModeScreen.BarcodeImageCanvas.Children.Add(myImage);
});
Here is the image I see. It should be a black and white picture of a kleenex box.
Here is the saved jpg file:
did you mix up width and height? are you sure your dpi value is correct?
I suspect the whole problem is this line:
BitmapSource bitmapSource = BitmapSource.Create(
width, height, (double)dpi, (double)dpi, PixelFormats.Bgr24, null, image, ((width * 24 + 31) & ~31) >> 3)
What I would do to debug the issue is to write out the image to file and confirm all the inputs. Use photoshop, paint.net, file properties...
Are you sure you are working with bitmap format?
Are you sure you are working with 24bits per pixel?
Are you sure you have height and width correct, and you are feeding the values into the correct argument
What is this line all about, and why are you doing it? I am slightly suspicious.
((width * 24 + 31) & ~31) >> 3)
Basically, the way I look at this is that you are feeding the bitmap library a stream of bits... it doesn't know what the bits are but it will attempt to create the image from the information you give it: bits per pixel, size, etc. If you give it incorrect information, it will create a corrupted image as you have shown.
I am slightly suspicious that the problem is not with width and height; even if you mix those two values up-- I think you would get at least part of the first row of pixels to be rendered correctly. I see static / noise / snow, which tells me that there is something about the way the stream of bits was interpreted-- it is rendered as random blacks and whites.
Another thing: in your screen cap, I see color. this is another hint that there is something incorrect about your assumptions about the image. The values should probably 1 to 256 ( 8 bits per pixel I think? ) I would try creating a 8 bit per pixel black and white bitmap. Somehow the library thinks this is a color image.
I just noticed that you are assuming jpeg. jpeg is a lossy format-- I would have assumed that you would end up with a bitmap or tiff image. double check that you are indeed getting back a jpeg image (check the barcode api documentation)
The JPEG compression algorithm is quite unsuitable for the kind of image you are capturing. It works well for photos, it behaves poorly on images containing fine lines. The slight artifacts the compression produces makes it a lot harder to properly scan the barcode.
You don't see the Kleenex box because you are writing the raw image bytes. You need to use an image encoder. I recommend you use the PngBitmapEncoder class. GifBitmapEncoder should work too since you don't need a lot of colors, it makes smaller files. A code snippet that shows how to use an encoder is available here.
this is likely distorting it
myImage.Stretch = Stretch.Fill;
I used a jpeg decoder to fix the problem.
private void SetBitmap(byte[] image, int width, int height, int dpi)
{
MainWindow.Instance.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate()
{
BMemoryStream ms = new MemoryStream(image);
JpegBitmapDecoder decoder = new JpegBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bitmapSource = decoder.Frames[0];
HwModeScreen.BarcodeImageCanvas.Children.Clear();
Image myImage = new Image();
myImage.Width = HwModeScreen.BarcodeImageCanvas.ActualWidth;
myImage.Height = HwModeScreen.BarcodeImageCanvas.ActualHeight;
myImage.Stretch = Stretch.Fill;
myImage.Source = bitmapSource;
HwModeScreen.BarcodeImageCanvas.Children.Add(myImage);
});

Why do I get completely different results when saving a BitmapSource to bmp, jpeg, and png in WPF

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.

Converting from a Format8bppIndexed to a Format24bppRgb in C#/GDI+

Alright, I have an image coming through from an external application in an 8-bit indexed format. I need this image converted to a 24-bit format of the exact same size.
I've tried creating a new Bitmap of the same size and of type Format24bppRgb and then using a Graphics object to draw the 8-bit image over it before saving it as a Bmp. This approach doesn't error out but when I open the resulting image the BMP header has all kinds of funky values. The height and width are HUGE and, in addition, there are funny (and large) values for the compression flags and a few others. Unfortunately my particular requirements are to pass this file off to a specific printer driver that demands a 24-bit image with specific header values (which I'm trying to achieve through GDI+)
Anyone know of an example on "up-converting" an indexed file to a not-indexed 24-bit file? If not an example, which path should I start down to write my own?
-Kevin Grossnicklaus
kvgros#sseinc.com
I used the code below to "up-convert" an image from 8bpp to 24bpp. Inspecting the generated 24bpp file with a hex editor and comparing against the 8bpp file shows no difference in height and width in the two files. That is, the 8bpp image was 1600x1200, and the 24bpp image has the same values.
private static void ConvertTo24(string inputFileName, string outputFileName)
{
Bitmap bmpIn = (Bitmap)Bitmap.FromFile(inputFileName);
Bitmap converted = new Bitmap(bmpIn.Width, bmpIn.Height, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(converted))
{
// Prevent DPI conversion
g.PageUnit = GraphicsUnit.Pixel
// Draw the image
g.DrawImageUnscaled(bmpIn, 0, 0);
}
converted.Save(outputFileName, ImageFormat.Bmp);
}
Everything else in the headers looks reasonable, and the images display identical on my system. What "funky values" are you seeing?
This is my conversion code. Notice the matching of resolution between source image and resulting image.
private void ConvertTo24bppPNG(Stream imageDataAsStream, out byte[] data)
{
using ( Image img = Image.FromStream(imageDataAsStream) )
{
using ( Bitmap bmp = new Bitmap(img.Width, img.Height, PixelFormat.Format24bppRgb) )
{
// ensure resulting image has same resolution as source image
// otherwise resulting image will appear scaled
bmp.SetResolution(img.HorizontalResolution, img.VerticalResolution);
using ( Graphics gfx = Graphics.FromImage(bmp) )
{
gfx.DrawImage(img, 0, 0);
}
using ( MemoryStream ms = new MemoryStream() )
{
bmp.Save(ms, ImageFormat.Png);
data = new byte[ms.Length];
ms.Position = 0;
ms.Read(data, 0, (int) ms.Length);
}
}
}
}
It seems odd that you're creating a Bitmap of the same width and height as your input, yet the generated BMP is much larger. Can you post some code?
The problem is probably the difference between the Vertical- and HorizontalResolution of your source image and your output image. If you load a 8bpp indexed bitmap with a resolution of 72 DPI, and then create a new 24bpp bitmap (default resolution will be 96 DPI... at least it is on my system) and then use Graphics.DrawImage to blit to the new bitmap, your image will appear slightly zoomed in and cropped.
Having said that, I don't know off the top of my head how to properly create the output Bitmap and/or Graphics object to scale properly when saved. I suspect it will have something to do with creating the images using a common scale like inches instead of pixels.

Categories