BitBlt convert to byte array and parse from c++ to c# - c#

I'm trying to create a Screen Capture DLL in C++ and send the resulting Byte Arrays to C#.
I'm able to get the size returned to C# but the byte array is always null.
Here's the C++ code (made up of bits i found on the internet)
__declspec(dllexport) int ScreenCap(BYTE* *data, DWORD *size)
{
try
{
//BITMAP bmpScreen;
HWND DesktopHwnd = GetDesktopWindow();
RECT DesktopParams;
HDC DevC = GetDC(DesktopHwnd);
GetWindowRect(DesktopHwnd,&DesktopParams);
DWORD Width = DesktopParams.right - DesktopParams.left;
DWORD Height = DesktopParams.bottom - DesktopParams.top;
DWORD FileSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBTRIPLE)+1*(Width*Height*4));
*size = FileSize;
char *BmpFileData = (char*)GlobalAlloc(0x0040,FileSize);
PBITMAPFILEHEADER BFileHeader = (PBITMAPFILEHEADER)BmpFileData;
PBITMAPINFOHEADER BInfoHeader = (PBITMAPINFOHEADER)&BmpFileData[sizeof(BITMAPFILEHEADER)];
BFileHeader->bfType = 0x4D42; // BM
BFileHeader->bfSize = sizeof(BITMAPFILEHEADER);
BFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
BInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
BInfoHeader->biPlanes = 1;
BInfoHeader->biBitCount = 24;
BInfoHeader->biCompression = BI_RGB;
BInfoHeader->biHeight = Height;
BInfoHeader->biWidth = Width;
RGBTRIPLE *Image = (RGBTRIPLE*)&BmpFileData[sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)];
RGBTRIPLE color;
HDC CaptureDC = CreateCompatibleDC(DevC);
HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC,Width,Height);
SelectObject(CaptureDC,CaptureBitmap);
BOOL bRet = BitBlt(CaptureDC,0,0,Width,Height,DevC,0,0,SRCCOPY|CAPTUREBLT);
//GetDIBits(CaptureDC,CaptureBitmap,0,Height,Image,(LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);
//GetObject(CaptureBitmap,sizeof(BITMAPFILEHEADER),&bmpScreen);
//BYTE* lpPixels = new BYTE[sizeof((LPBITMAPINFO)BInfoHeader)];
GetDIBits(CaptureDC, CaptureBitmap, 0, Height, *data, (LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);
//DWORD Junk;
//DIBSECTION dib;
//GetObject(CaptureBitmap, sizeof(dib), (LPVOID)&dib);
//HANDLE FH = CreateFileA(BmpName,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0);
//WriteFile(FH,BmpFileData,FileSize,&Junk,0);
//CloseHandle(FH);
GlobalFree(BmpFileData);
return 1;
}
catch(char *p)
{
return 0;
}
}
And here's the C# code i'm using with the "ref" keyword.
[DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int ScreenCap(ref byte[] data, ref int size);
public static void CapScreen()
{
try
{
int hr = 0;
byte[] gData = null;
int gSize = 0;
hr = ScreenCap(ref gData, ref gSize);
int a = 1;
}
catch(Exception ex)
{
int a = 1;
}
I'm thinking there may be an issue with my GetDIBits but i'm not sure. I hope some of you guru's can set me on the right path. Thanks.
* EDIT *
OK Big thanks to both jdweng and filip for pointing me in the right direction. i'm now receiving the screen as an IntPtr and able to render it to my C# app. here's the code that now works (kind of i'll explain at the bottom)
__declspec(dllexport) char* ScreenCap(DWORD * size)
{
try
{
// Get screen dimensions
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
*size = ((((24 * nScreenWidth + 31)&(~31)) / 8)*nScreenHeight);
BITMAPINFO MyBMInfo = {0};
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
// Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
HDC hdcScreen = GetDC(GetDesktopWindow());
HDC hdcCompatible = CreateCompatibleDC(hdcScreen);
HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nScreenWidth, nScreenHeight);
//HGDIOBJ hOldBmp = SelectObject(hdcCompatible, hBmp);
HGDIOBJ hOldBmp = (HGDIOBJ) SelectObject(hdcCompatible, hBmp);
BOOL bOK = BitBlt(hdcCompatible,0,0,nScreenWidth, nScreenHeight, hdcScreen,0,0,SRCCOPY|CAPTUREBLT);
SelectObject(hdcCompatible, hOldBmp); // always select the previously selected object once done
// Get the BITMAPINFO structure from the bitmap
GetDIBits(hdcScreen, hBmp, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS);
// create the bitmap buffer
char* lpPixels = new char[MyBMInfo.bmiHeader.biSizeImage];
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biBitCount = 24;
// get the actual bitmap buffer
GetDIBits(hdcScreen, hBmp, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS);
//Clean Up
DeleteDC(hdcCompatible);
ReleaseDC(GetDesktopWindow(), hdcScreen);
DeleteDC(hdcScreen);
DeleteObject(hBmp);
//DeleteObject(hOldBmp);
return lpPixels;
}
catch(char *p)
{
return 0;
}
}
and the C#
[DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ScreenCap(ref int size);
while(true)
{
int size = 0;
IntPtr ptr = ScreenCap(ref size);
byte[] result = new byte[size];
Marshal.Copy(ptr, result, 0, size);
Marshal.Release(ptr);
ptr = IntPtr.Zero;
MainWindow.Dispatcher.Invoke(new Action(
() =>
{
System.Windows.Media.ImageSource ThumbnailImage = System.Windows.Media.Imaging.BitmapSource.Create(1920, 1080, 96, 96, System.Windows.Media.PixelFormats.Bgr24, null, result, 1920 * 3);
MainWindow.canvasMain.Background = new System.Windows.Media.ImageBrush(ThumbnailImage);
result = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
), null);
}
Not sure if the problem i now face is due to doing it this way but i'm getting a major memory leak from the C++ dll now. If this is another issue all together, do i need to create a new thread?
** Edit **
Problem solved by creating a new function in the C++ dll to delete the returned char array. may not be elegant but it works.
i'd like to give a big thanks to Filip who's comments helped me to look in all sorts of places and see other things. Jdweng, your IntPtr was a god send. Not sure how i set this as answered by Jdweng. would love to award to you both.

Here is what worked for me if anyone is looking for the same. I'd like to send a big thanks to both Filip Kocica and jdweng who's suggestions gave me some ideas and helped very much. Thanks and appreciated very much.
C++
char* lpPixels;
__declspec(dllexport) BOOL ScreenCapClean()
{
delete[] lpPixels;
return 1;
}
__declspec(dllexport) char* ScreenCap(DWORD * size)
{
try
{
// Get screen dimensions
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
*size = ((((24 * nScreenWidth + 31)&(~31)) / 8)*nScreenHeight);
BITMAPINFO MyBMInfo = {0};
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
MyBMInfo.bmiHeader.biWidth = nScreenWidth;
MyBMInfo.bmiHeader.biHeight = -nScreenHeight;
MyBMInfo.bmiHeader.biPlanes = 1;
MyBMInfo.bmiHeader.biBitCount = 24;
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biSizeImage = 0;
MyBMInfo.bmiHeader.biXPelsPerMeter = 0;
MyBMInfo.bmiHeader.biYPelsPerMeter = 0;
MyBMInfo.bmiHeader.biClrUsed = 0;
MyBMInfo.bmiHeader.biClrImportant = 0;
// Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
HDC hdcScreen = GetDC(0);
HDC hdcCompatible = CreateCompatibleDC(hdcScreen);
HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nScreenWidth, nScreenHeight);
HGDIOBJ hOldBmp = (HGDIOBJ) SelectObject(hdcCompatible, hBmp);
BOOL bOK = BitBlt(hdcCompatible,0,0,nScreenWidth, nScreenHeight, hdcScreen,0,0,SRCCOPY|CAPTUREBLT);
SelectObject(hdcCompatible, hOldBmp); // always select the previously selected object once done
// Get the BITMAPINFO structure from the bitmap
GetDIBits(hdcScreen, hBmp, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS);
// create the bitmap buffer
lpPixels = new char[MyBMInfo.bmiHeader.biSizeImage];
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biBitCount = 24;
// get the actual bitmap buffer
GetDIBits(hdcScreen, hBmp, 0, -MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS);
//Clean Up
ReleaseDC(0, hdcScreen);
ReleaseDC(0, hdcCompatible);
DeleteDC(hdcCompatible);
DeleteDC(hdcScreen);
DeleteObject(hBmp);
DeleteObject(hOldBmp);
return lpPixels;
}
catch(char *p)
{
return 0;
}
}
C#
[DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ScreenCap(ref int size);
[DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool ScreenCapClean();
int size = 0;
IntPtr ptr = ScreenCap(ref size);
byte[] result = new byte[size];
Marshal.Copy(ptr, result, 0, size);
Marshal.Release(ptr);
ptr = IntPtr.Zero;
//After doing what's required with the byte array
bool hr = ScreenCapClean();

Related

C# Loading non-thread-safe DLL multiple times using AppDomain or Process

I have a non-thread-safe DLL that contains static methods and data. I need to call its methods from different objects and possibly threads keeping everything separated. Google says I have to create different AppDomains (in this case one for each object calling the DLL) or create a new Process. Is that true? If yes which one is the best solution and How do I practically do it? If not is there any other solution?
Also, I have a Form instantiating the class that uses the DLL and I need to be able to access the methods inside the class from the Form. Will I be able to do that?
To make it clearer I post some code.
The DLL import goes like this:
namespace ASCOM.QHYCCD
{
public class libqhyccd
{
[DllImport("import/qhyccd.dll", EntryPoint = "InitQHYCCDResource",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public unsafe static extern UInt32 InitQHYCCDResource();
[DllImport("import/qhyccd.dll", EntryPoint = "GetQHYCCDSingleFrame",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public unsafe static extern UInt32 GetQHYCCDSingleFrame(IntPtr handle, ref UInt32 w, ref UInt32 h, ref UInt32 bpp, ref UInt32 channels, byte* rawArray);
public unsafe static UInt32 C_GetQHYCCDSingleFrame(IntPtr handle, ref UInt32 w, ref UInt32 h, ref UInt32 bpp, ref UInt32 channels, byte[] rawArray)
{
UInt32 ret;
fixed(byte* prawArray = rawArray)
ret = GetQHYCCDSingleFrame(handle, ref w, ref h, ref bpp, ref channels, prawArray);
return ret;
}
// a lot more stuff like this
}
}
And this is the class:
class QHYCamera
{
public static IntPtr handler;
private bool isConnected = false;
uint width, height, bpp, channels;
double chipw, chiph, pixelw, pixelh;
byte[] rawArray;
uint arrayLength;
public QHYCamera(){}
public void Connect(int index, string strid)
{
if (!isConnected)
{
//ASCOM.QHYCCD.libqhyccd.InitQHYCCDResource();
StringBuilder id = new StringBuilder(strid);
//ASCOM.QHYCCD.libqhyccd.GetQHYCCDId(index, id);
handler = ASCOM.QHYCCD.libqhyccd.OpenQHYCCD(id);
Console.WriteLine(handler.ToString());
ASCOM.QHYCCD.libqhyccd.SetQHYCCDStreamMode(handler, 0);
ASCOM.QHYCCD.libqhyccd.InitQHYCCD(handler);
ASCOM.QHYCCD.libqhyccd.GetQHYCCDChipInfo(handler, ref chipw, ref chiph, ref width, ref height, ref pixelw, ref pixelh, ref bpp);
ASCOM.QHYCCD.libqhyccd.SetQHYCCDBinMode(handler, 1, 1);
ASCOM.QHYCCD.libqhyccd.SetQHYCCDResolution(handler, 0, 0, width, height);
arrayLength = ASCOM.QHYCCD.libqhyccd.GetQHYCCDMemLength(handler);
rawArray = new byte[arrayLength];
isConnected = true;
}
}
public void Disconnect()
{
if (isConnected)
{
ASCOM.QHYCCD.libqhyccd.CloseQHYCCD(handler);
ASCOM.QHYCCD.libqhyccd.ReleaseQHYCCDResource();
isConnected = false;
}
}
public bool IsConnected()
{
return isConnected;
}
public Dictionary<string, int> GetAvailableCameras()
{
Dictionary<string, int> index_id = new Dictionary<string, int>();
for (int i = 0; i < ASCOM.QHYCCD.libqhyccd.ScanQHYCCD(); i++)
{
StringBuilder id = new StringBuilder(200);
ASCOM.QHYCCD.libqhyccd.GetQHYCCDId(i, id);
index_id.Add(id.ToString(), i);
}
return index_id;
}
public void SetExposure(double exposure)
{
if (ASCOM.QHYCCD.libqhyccd.SetQHYCCDParam(handler, StructModel.CONTROL_ID.CONTROL_EXPOSURE, exposure) == 1)
{
MessageBox.Show("Cannot set exposure", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
public void SetGain(double gain)
{
if (ASCOM.QHYCCD.libqhyccd.SetQHYCCDParam(handler, StructModel.CONTROL_ID.CONTROL_GAIN, gain) == 1)
{
MessageBox.Show("Cannot set exposure", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
public Task<Bitmap> GetBitmapAsync()
{
return Task.Run(() =>
{
return GetBitmap();
});
}
public Bitmap GetBitmap()
{
ASCOM.QHYCCD.libqhyccd.SetQHYCCDBitsMode(handler, bpp);
ASCOM.QHYCCD.libqhyccd.InitQHYCCD(handler);
ASCOM.QHYCCD.libqhyccd.ExpQHYCCDSingleFrame(handler);
uint ret = 1;
while(ret != 0)
{
ret = ASCOM.QHYCCD.libqhyccd.C_GetQHYCCDSingleFrame(handler, ref width, ref height, ref bpp, ref channels, rawArray);
}
if (ret == 0)
{
Color[] palette = new Color[256];
for (Int32 b = 0; b < 256; b++)
palette[b] = Color.FromArgb(b, b, b);
return BuildImage(rawArray, (int)width, (int)height, (int)width, PixelFormat.Format8bppIndexed, palette, null);
}
return default(Bitmap);
}
public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat, Color[] palette, Color? defaultColor)
{
Bitmap newImage = new Bitmap(width, height, pixelFormat);
BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, newImage.PixelFormat);
Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7) / 8;
Boolean isFlipped = stride < 0;
stride = Math.Abs(stride);
Int32 targetStride = targetData.Stride;
Int64 scan0 = targetData.Scan0.ToInt64();
for (Int32 y = 0; y < height; y++)
Marshal.Copy(sourceData, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);
newImage.UnlockBits(targetData);
if (isFlipped)
newImage.RotateFlip(RotateFlipType.Rotate180FlipX);
if ((pixelFormat & PixelFormat.Indexed) != 0 && palette != null)
{
ColorPalette pal = newImage.Palette;
for (Int32 i = 0; i < pal.Entries.Length; i++)
{
if (i < palette.Length)
pal.Entries[i] = palette[i];
else if (defaultColor.HasValue)
pal.Entries[i] = defaultColor.Value;
else
break;
}
newImage.Palette = pal;
}
return newImage;
}
}
From the main form I just need to instantiate the class, spawn a new thread that calls some methods:
private void BackgroundPreviewHandler(QHYCamera camera)
{
while (camera.IsConnected())
{
Bitmap bitmap = camera.GetBitmap();
PictureBox1.Image = bitmap;
}
}
private QHYCamera visibleCam = new QHYCamera();
private QHYCamera halphaCam = new QHYCamera();
await Task.Factory.StartNew(() => BackgroundPreviewHandler(visibleCam));
await Task.Factory.StartNew(() => BackgroundPreviewHandler(halphaCam));

Convert Specific BitmapImage Colour to Transparent

I'm currently recreating my Image Explorer application, formerly written in Windows Forms to the Windows Presentation Framework.
My WinForms application was using the WindowsThumbnailProvider from #DanielPeñalba (See this link for the original version of the code)
WinForms Version - Successfully converting 0 alpha, 0 red, 0 green and 0 blue to Transparent
WPF Version - Almost working
WPF Code - Slightly modified version of the original WindowsThumbnailProvider to support System.Windows.Media.Imaging.BitmapImage instead of System.Drawing.Bitmap
MainWindow.xaml - For all the testing
<Window x:Class="WpfFileFolderThumbnails.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfFileFolderThumbnails"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="600">
<Grid>
<Image x:Name="ThumbnailImage1" HorizontalAlignment="Left" Height="256" Margin="10,20,0,0" VerticalAlignment="Top" Width="256"/>
<Image x:Name="ThumbnailImage2" HorizontalAlignment="Left" Height="256" Margin="326,20,0,0" VerticalAlignment="Top" Width="256"/>
</Grid>
</Window>
MainWindow.xaml.cs - Test code to call the GetThumbnail and CreateAlphaBitmapImage methods
using System.Windows;
namespace WpfFileFolderThumbnails
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
var thumbnail = WindowsThumbnailProviderWpf.GetThumbnail(#"D:\Pictures\Art\Anime", 256, 256,
ThumbnailOptions.ThumbnailOnly);
var alphaThumbnail = WindowsThumbnailProviderWpf.CreateAlphaBitmapImage(thumbnail);
this.ThumbnailImage1.Source = thumbnail;
this.ThumbnailImage2.Source = alphaThumbnail;
}
}
}
WindowsThumbnailProviderWpf.cs - Class to get Folder Thumbnail and make it Transparent
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Path = System.IO.Path;
namespace WpfFileFolderThumbnails
{
[Flags]
public enum ThumbnailOptions
{
None = 0x00,
BiggerSizeOk = 0x01,
InMemoryOnly = 0x02,
IconOnly = 0x04,
ThumbnailOnly = 0x08,
InCacheOnly = 0x10,
}
public static class WindowsThumbnailProviderWpf
{
private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int SHCreateItemFromParsingName(
[MarshalAs(UnmanagedType.LPWStr)] string path,
// The following parameter is not used - binding context.
IntPtr pbc,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteObject(IntPtr hObject);
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
internal interface IShellItem
{
void BindToHandler(IntPtr pbc,
[MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)]Guid riid,
out IntPtr ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
};
internal enum SIGDN : uint
{
NORMALDISPLAY = 0,
PARENTRELATIVEPARSING = 0x80018001,
PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
DESKTOPABSOLUTEPARSING = 0x80028000,
PARENTRELATIVEEDITING = 0x80031001,
DESKTOPABSOLUTEEDITING = 0x8004c000,
FILESYSPATH = 0x80058000,
URL = 0x80068000
}
internal enum HResult
{
Ok = 0x0000,
False = 0x0001,
InvalidArguments = unchecked((int)0x80070057),
OutOfMemory = unchecked((int)0x8007000E),
NoInterface = unchecked((int)0x80004002),
Fail = unchecked((int)0x80004005),
ElementNotFound = unchecked((int)0x80070490),
TypeElementNotFound = unchecked((int)0x8002802B),
NoObject = unchecked((int)0x800401E5),
Win32ErrorCanceled = 1223,
Canceled = unchecked((int)0x800704C7),
ResourceInUse = unchecked((int)0x800700AA),
AccessDenied = unchecked((int)0x80030005)
}
[ComImport()]
[Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItemImageFactory
{
[PreserveSig]
HResult GetImage(
[In, MarshalAs(UnmanagedType.Struct)] NativeSize size,
[In] ThumbnailOptions flags,
[Out] out IntPtr phbm);
}
[StructLayout(LayoutKind.Sequential)]
internal struct NativeSize
{
private int width;
private int height;
public int Width { set { this.width = value; } }
public int Height { set { this.height = value; } }
};
[StructLayout(LayoutKind.Sequential)]
public struct RGBQUAD
{
public byte rgbBlue;
public byte rgbGreen;
public byte rgbRed;
public byte rgbReserved;
}
public static BitmapImage GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
{
IntPtr hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
try
{
// return a System.Drawing.Bitmap from the hBitmap
return GetBitmapImageFromHBitmap(hBitmap);
}
finally
{
// delete HBitmap to avoid memory leaks
DeleteObject(hBitmap);
}
}
public static BitmapImage GetBitmapImageFromHBitmap(IntPtr nativeHBitmap)
{
var bmpSource = Imaging.CreateBitmapSourceFromHBitmap(nativeHBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
var bmpImage = BitmapSourceToBitmapImage(bmpSource);
return bmpImage;
}
// Conversion code
public static BitmapImage BitmapSourceToBitmapImage(BitmapSource bitmapSource)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
MemoryStream memorystream = new MemoryStream();
BitmapImage tmpImage = new BitmapImage();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(memorystream);
tmpImage.BeginInit();
tmpImage.StreamSource = new MemoryStream(memorystream.ToArray());
tmpImage.EndInit();
memorystream.Close();
return tmpImage;
}
public static BitmapImage CreateAlphaBitmapImage(BitmapImage sourceBitmapImage)
{
var bmp = sourceBitmapImage.Clone();
var pixels = new int[(int)bmp.Width * (int)bmp.Height];
var stride = (bmp.PixelWidth * bmp.Format.BitsPerPixel + 7) / 8;
bmp.CopyPixels(pixels, stride, 0);
var oldColor = pixels[0];
var red = 255;
var green = 255;
var blue = 255;
var alpha = 0;
var color = (alpha << 24) + (red << 16) + (green << 8) + blue;
for (var i = 0; i < (int)bmp.Width * (int)bmp.Height; i++)
{
if (pixels[i] == oldColor)
{
pixels[i] = color;
}
}
//remake the bitmap source with these pixels
var source = BitmapSource.Create(bmp.PixelWidth, bmp.PixelHeight, bmp.DpiX, bmp.DpiY, PixelFormats.Bgra32, bmp.Palette, pixels, stride);
//return sourceBitmapImage;
return BitmapSourceToBitmapImage(source);
}
private static IntPtr GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
{
IShellItem nativeShellItem;
Guid shellItem2Guid = new Guid(IShellItem2Guid);
int retCode = SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref shellItem2Guid, out nativeShellItem);
if (retCode != 0)
throw Marshal.GetExceptionForHR(retCode);
NativeSize nativeSize = new NativeSize();
nativeSize.Width = width;
nativeSize.Height = height;
IntPtr hBitmap;
HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(nativeSize, options, out hBitmap);
Marshal.ReleaseComObject(nativeShellItem);
if (hr == HResult.Ok) return hBitmap;
throw Marshal.GetExceptionForHR((int)hr);
}
}
}
While i understand i could just simply reference System.Drawing and use the already working solution, i'd like to know if it's possible to do the same thing in WPF.
Question - Is there a simple way to loop through each pixel of a BitmapImage (similar to a Bitmap), change a specific pixel combination and create a copy of the BitmapImage with transparency?
You may call CopyPixels on a BitmapSource to get the raw pixel buffer, then modify the buffer as you like, and create a new BitmapSource from the modified buffer.
The method below shows how this could work for a BitmapSource with a 32-bit BGRA format.
private static BitmapSource CreateTransparency(BitmapSource source)
{
if (source.Format != PixelFormats.Bgra32)
{
return source;
}
var bytesPerPixel = (source.Format.BitsPerPixel + 7) / 8;
var stride = bytesPerPixel * source.PixelWidth;
var buffer = new byte[stride * source.PixelHeight];
source.CopyPixels(buffer, stride, 0);
for (int y = 0; y < source.PixelHeight; y++)
{
for (int x = 0; x < source.PixelWidth; x++)
{
var i = stride * y + bytesPerPixel * x;
var b = buffer[i];
var g = buffer[i + 1];
var r = buffer[i + 2];
var a = buffer[i + 3];
if (...)
{
buffer[i + 3] = 0d; // set transparent
}
}
}
return BitmapSource.Create(
source.PixelWidth, source.PixelHeight,
source.DpiX, source.DpiY,
source.Format, null, buffer, stride);
}

Saving Transparent PNG as Transparent GIF

I'm trying to Resizing a Transparent png and saving it as a single frame gif image.
Let skip resizing part, while you try to save a Transparent png as gif, you will see a black background in output gif:
Bitmap n = new Bitmap(targetPngPath);
n.Save(#"C:\1.gif", ImageFormat.Gif);
yes, I can make that black background into white, but it is not what I'm looking for. Even I can remove the black color using MakeTransparent Method, But it will remove about every black color in image and we will not have a standard transparent image.
we also can do a TRICK saving gif image, we keep extension in filename but we will save it as PNG Format like this:
n.Save(#"C:\1.gif", ImageFormat.Png);
But it is not also standard.
So is there any way to safely save a transparent png as a gif image with transparency?
PNG =
GIF =
GIF Saved With Photoshop =
This is because the built-in GIF encoder cannot handle the source well, unless it is already a 8 bpp image. You must convert your PNG image to a 256 color image first, then you can save it correctly with the GIF encoder.
public static void SaveGif(string fileName, Image image)
{
int bpp = Image.GetPixelFormatSize(image.PixelFormat);
if (bpp == 8)
{
image.Save(fileName, ImageFormat.Gif);
return;
}
// 1 and 4 bpp images are need to be converted, too; otherwise, gif encoder encodes the image from 32 bpp image resulting 256 color, no transparency
if (bpp < 8)
{
using (Image image8Bpp = ConvertPixelFormat(image, PixelFormat.Format8bppIndexed, null))
{
image8Bpp.Save(fileName, ImageFormat.Gif);
return;
}
}
// high/true color bitmap: obtaining the colors
// Converting always to 8 bpp pixel format; otherwise, gif encoder would convert it to 32 bpp first.
// With 8 bpp, gif encoder will preserve transparency and will save compact palette
// Note: This works well for 256 color images in a 32bpp bitmap. Otherwise, you might try to pass null as palette so a default palette will be used.
Color[] palette = GetColors((Bitmap)image, 256);
using (Image imageIndexed = ConvertPixelFormat(image, PixelFormat.Format8bppIndexed, palette))
{
imageIndexed.Save(fileName, ImageFormat.Gif);
}
}
// TODO: Use some quantizer
private static Color[] GetColors(Bitmap bitmap, int maxColors)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (maxColors < 0)
throw new ArgumentOutOfRangeException("maxColors");
HashSet<int> colors = new HashSet<int>();
PixelFormat pixelFormat = bitmap.PixelFormat;
if (Image.GetPixelFormatSize(pixelFormat) <= 8)
return bitmap.Palette.Entries;
// 32 bpp source: the performant variant
if (pixelFormat == PixelFormat.Format32bppRgb ||
pixelFormat == PixelFormat.Format32bppArgb ||
pixelFormat == PixelFormat.Format32bppPArgb)
{
BitmapData data = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, pixelFormat);
try
{
unsafe
{
byte* line = (byte*)data.Scan0;
for (int y = 0; y < data.Height; y++)
{
for (int x = 0; x < data.Width; x++)
{
int c = ((int*)line)[x];
// if alpha is 0, adding the transparent color
if ((c >> 24) == 0)
c = 0xFFFFFF;
if (colors.Contains(c))
continue;
colors.Add(c);
if (colors.Count == maxColors)
return colors.Select(Color.FromArgb).ToArray();
}
line += data.Stride;
}
}
}
finally
{
bitmap.UnlockBits(data);
}
}
else
{
// fallback: getpixel
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
int c = bitmap.GetPixel(x, y).ToArgb();
if (colors.Contains(c))
continue;
colors.Add(c);
if (colors.Count == maxColors)
return colors.Select(Color.FromArgb).ToArray();
}
}
}
return colors.Select(Color.FromArgb).ToArray();
}
private static Image ConvertPixelFormat(Image image, PixelFormat newPixelFormat, Color[] palette)
{
if (image == null)
throw new ArgumentNullException("image");
PixelFormat sourcePixelFormat = image.PixelFormat;
int bpp = Image.GetPixelFormatSize(newPixelFormat);
if (newPixelFormat == PixelFormat.Format16bppArgb1555 || newPixelFormat == PixelFormat.Format16bppGrayScale)
throw new NotSupportedException("This pixel format is not supported by GDI+");
Bitmap result;
// non-indexed target image (transparency preserved automatically)
if (bpp > 8)
{
result = new Bitmap(image.Width, image.Height, newPixelFormat);
using (Graphics g = Graphics.FromImage(result))
{
g.DrawImage(image, 0, 0, image.Width, image.Height);
}
return result;
}
int transparentIndex;
Bitmap bmp;
// indexed colors: using GDI+ natively
RGBQUAD[] targetPalette = new RGBQUAD[256];
int colorCount = InitPalette(targetPalette, bpp, (image is Bitmap) ? image.Palette : null, palette, out transparentIndex);
BITMAPINFO bmi = new BITMAPINFO();
bmi.icHeader.biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER));
bmi.icHeader.biWidth = image.Width;
bmi.icHeader.biHeight = image.Height;
bmi.icHeader.biPlanes = 1;
bmi.icHeader.biBitCount = (ushort)bpp;
bmi.icHeader.biCompression = BI_RGB;
bmi.icHeader.biSizeImage = (uint)(((image.Width + 7) & 0xFFFFFFF8) * image.Height / (8 / bpp));
bmi.icHeader.biXPelsPerMeter = 0;
bmi.icHeader.biYPelsPerMeter = 0;
bmi.icHeader.biClrUsed = (uint)colorCount;
bmi.icHeader.biClrImportant = (uint)colorCount;
bmi.icColors = targetPalette;
bmp = (image as Bitmap) ?? new Bitmap(image);
// Creating the indexed bitmap
IntPtr bits;
IntPtr hbmResult = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits, IntPtr.Zero, 0);
// Obtaining screen DC
IntPtr dcScreen = GetDC(IntPtr.Zero);
// DC for the original hbitmap
IntPtr hbmSource = bmp.GetHbitmap();
IntPtr dcSource = CreateCompatibleDC(dcScreen);
SelectObject(dcSource, hbmSource);
// DC for the indexed hbitmap
IntPtr dcTarget = CreateCompatibleDC(dcScreen);
SelectObject(dcTarget, hbmResult);
// Copy content
BitBlt(dcTarget, 0, 0, image.Width, image.Height, dcSource, 0, 0, 0x00CC0020 /*TernaryRasterOperations.SRCCOPY*/);
// obtaining result
result = Image.FromHbitmap(hbmResult);
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
// cleanup
DeleteDC(dcSource);
DeleteDC(dcTarget);
ReleaseDC(IntPtr.Zero, dcScreen);
DeleteObject(hbmSource);
DeleteObject(hbmResult);
ColorPalette resultPalette = result.Palette;
bool resetPalette = false;
// restoring transparency
if (transparentIndex >= 0)
{
// updating palette if transparent color is not actually transparent
if (resultPalette.Entries[transparentIndex].A != 0)
{
resultPalette.Entries[transparentIndex] = Color.Transparent;
resetPalette = true;
}
ToIndexedTransparentByArgb(result, bmp, transparentIndex);
}
if (resetPalette)
result.Palette = resultPalette;
if (!ReferenceEquals(bmp, image))
bmp.Dispose();
return result;
}
private static int InitPalette(RGBQUAD[] targetPalette, int bpp, ColorPalette originalPalette, Color[] desiredPalette, out int transparentIndex)
{
int maxColors = 1 << bpp;
// using desired palette
Color[] sourcePalette = desiredPalette;
// or, using original palette if it has fewer or the same amount of colors as requested
if (sourcePalette == null && originalPalette != null && originalPalette.Entries.Length > 0 && originalPalette.Entries.Length <= maxColors)
sourcePalette = originalPalette.Entries;
// or, using default system palette
if (sourcePalette == null)
{
using (Bitmap bmpReference = new Bitmap(1, 1, GetPixelFormat(bpp)))
{
sourcePalette = bmpReference.Palette.Entries;
}
}
// it is ignored if source has too few colors (rest of the entries will be black)
transparentIndex = -1;
bool hasBlack = false;
int colorCount = Math.Min(maxColors, sourcePalette.Length);
for (int i = 0; i < colorCount; i++)
{
targetPalette[i] = new RGBQUAD(sourcePalette[i]);
if (transparentIndex == -1 && sourcePalette[i].A == 0)
transparentIndex = i;
if (!hasBlack && (sourcePalette[i].ToArgb() & 0xFFFFFF) == 0)
hasBlack = true;
}
// if transparent index is 0, relocating it and setting transparent index to 1
if (transparentIndex == 0)
{
targetPalette[0] = targetPalette[1];
transparentIndex = 1;
}
// otherwise, setting the color of transparent index the same as the previous color, so it will not be used during the conversion
else if (transparentIndex != -1)
{
targetPalette[transparentIndex] = targetPalette[transparentIndex - 1];
}
// if black color is not found in palette, counting 1 extra colors because it can be used in conversion
if (colorCount < maxColors && !hasBlack)
colorCount++;
return colorCount;
}
private unsafe static void ToIndexedTransparentByArgb(Bitmap target, Bitmap source, int transparentIndex)
{
int sourceBpp = Image.GetPixelFormatSize(source.PixelFormat);
int targetBpp = Image.GetPixelFormatSize(target.PixelFormat);
BitmapData dataTarget = target.LockBits(new Rectangle(Point.Empty, target.Size), ImageLockMode.ReadWrite, target.PixelFormat);
BitmapData dataSource = source.LockBits(new Rectangle(Point.Empty, source.Size), ImageLockMode.ReadOnly, source.PixelFormat);
try
{
byte* lineSource = (byte*)dataSource.Scan0;
byte* lineTarget = (byte*)dataTarget.Scan0;
bool is32Bpp = sourceBpp == 32;
// scanning through the lines
for (int y = 0; y < dataSource.Height; y++)
{
// scanning through the pixels within the line
for (int x = 0; x < dataSource.Width; x++)
{
// testing if pixel is transparent (applies both argb and pargb)
if (is32Bpp && ((uint*)lineSource)[x] >> 24 == 0
|| !is32Bpp && ((ulong*)lineSource)[x] >> 48 == 0UL)
{
switch (targetBpp)
{
case 8:
lineTarget[x] = (byte)transparentIndex;
break;
case 4:
// First pixel is the high nibble
int pos = x >> 1;
byte nibbles = lineTarget[pos];
if ((x & 1) == 0)
{
nibbles &= 0x0F;
nibbles |= (byte)(transparentIndex << 4);
}
else
{
nibbles &= 0xF0;
nibbles |= (byte)transparentIndex;
}
lineTarget[pos] = nibbles;
break;
case 1:
// First pixel is MSB.
pos = x >> 3;
byte mask = (byte)(128 >> (x & 7));
if (transparentIndex == 0)
lineTarget[pos] &= (byte)~mask;
else
lineTarget[pos] |= mask;
break;
}
}
}
lineSource += dataSource.Stride;
lineTarget += dataTarget.Stride;
}
}
finally
{
target.UnlockBits(dataTarget);
source.UnlockBits(dataSource);
}
}
private static PixelFormat GetPixelFormat(int bpp)
{
switch (bpp)
{
case 1:
return PixelFormat.Format1bppIndexed;
case 4:
return PixelFormat.Format4bppIndexed;
case 8:
return PixelFormat.Format8bppIndexed;
case 16:
return PixelFormat.Format16bppRgb565;
case 24:
return PixelFormat.Format24bppRgb;
case 32:
return PixelFormat.Format32bppArgb;
case 48:
return PixelFormat.Format48bppRgb;
case 64:
return PixelFormat.Format64bppArgb;
default:
throw new ArgumentOutOfRangeException("bpp");
}
}
And the native types and methods:
private const int BI_RGB = 0;
private const int DIB_RGB_COLORS = 0;
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BITMAPINFO pbmi, int iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("gdi32.dll")]
private static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("user32.dll")]
private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[StructLayout(LayoutKind.Sequential)]
private struct RGBQUAD
{
internal byte rgbBlue;
internal byte rgbGreen;
internal byte rgbRed;
internal byte rgbReserved;
internal RGBQUAD(Color color)
{
rgbRed = color.R;
rgbGreen = color.G;
rgbBlue = color.B;
rgbReserved = 0;
}
}
[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFO
{
public BITMAPINFOHEADER icHeader;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public RGBQUAD[] icColors;
}
[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFOHEADER
{
internal uint biSize;
internal int biWidth;
internal int biHeight;
internal ushort biPlanes;
internal ushort biBitCount;
internal uint biCompression;
internal uint biSizeImage;
internal int biXPelsPerMeter;
internal int biYPelsPerMeter;
internal uint biClrUsed;
internal uint biClrImportant;
}
Update:
My Drawing Libraries are now free to download. It makes a SaveAsGif extension method available on the Image type:
using KGySoft.Drawing;
/// ...
using (var stream = new FileStream(targetPngPath, FileMode.Create))
{
// You can either use an arbitrary palette,
myPngBitmap.SaveAsGif(stream, myPngBitmap.GetColors(256));
// or, you can let the built-in encoder use dithering with a fixed palette.
// Pixel format is adjusted so transparency will be preserved.
myPngBitmap.SaveAsGif(stream, allowDithering: true);
}
This may help.
The Bitmap class does not save correctly with transparency.
You need to cast Bitmap to Image.
c# Bitmap.Save transparancy doesn't save in png
There are comments on the internet about .NET not saving Bitmaps with Transparency correctly .
Here is a good link for further reading, too much code to post.
http://forums.asp.net/t/1057792.aspx?ASP+NET+C+Making+an+Image+transparent

System.AccessViolationException with double pointer?

I want to compress textures to pvrtc in managed code so I've found a source which does what I want but I get a System.AccessViolationException. This is the code I use so far:
[DllImport("x86/pvrtc.dll", CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CompressTexture(byte[] data, int height, int width, int mipLevels, bool preMultiplied, bool pvrtc4bppCompression, ref IntPtr dataSizes);
public static byte[] Compress(byte[] data, int height, int width, int mipLevels, bool preMultiplied, bool pvrtc4bppCompression)
{
IntPtr dataSizesPtr = Marshal.AllocCoTaskMem(data.Length + 1);
var texDataPtr = CompressTexture(data, height, width, mipLevels, preMultiplied, pvrtc4bppCompression, ref dataSizesPtr);
//Store the size of each mipLevel
var dataSizesArray = new int[mipLevels];
//Copies data from an unmanaged memory pointer to a managed 8-bit unsigned integer array.
Marshal.Copy(dataSizesPtr, dataSizesArray, 0, dataSizesArray.Length);
var levelSize = 0;
byte[] levelData = new byte[0];
for (int x = 0; x < mipLevels; x++)
{
levelSize = dataSizesArray[x];
levelData = new byte[levelSize];
Marshal.Copy(texDataPtr, levelData, 0, levelSize);
texDataPtr = IntPtr.Add(texDataPtr, levelSize);
}
return levelData;
}
Any help is very much appreciated.

Get image as Image Class with twain in c#

I can connect and get images from my device with twaindotnet. But I want to handle the images as Image class. When I try something like this:
...
ArrayList pics = tw.TransferPictures();
EndingScan();
tw.CloseSrc();
if(pics.Count > 0) {
IntPtr img = (IntPtr) pics[ 0 ];
PicForm newpic = new PicForm( img );
Image r = Image.FromHbitmap(img, this.Handle);
picturebox.Image = r;
}
...
I'm getting an error as "Error:Generic Error Occured in GDI+" on the line ,
Image r = Image.FromHbitmap(img, this.Handle);
So where am I wrong? How can I get as an Image the image?
I too found out that calling Image.FromHbitmap is not enough.
I had a look in the TwainDotNet library you mentioned and there I found the BitmapRenderer class.
Pulling out just the relevant bit it is easy enough to use that to create a simple static method that you can pass in the IntPtr you get from TWAIN (in your case your img variable) and convert it to a Bitmap. You, therefore, call it like so:
Image r = TwainBitmapConvertor.ToBitmap(img);
and here is the code (it only works on x86 and needs tidying but it does the job):
public static class TwainBitmapConvertor
{
[StructLayout(LayoutKind.Sequential, Pack = 2)]
private class BitmapInfoHeader
{
public int Size;
public int Width;
public int Height;
public short Planes;
public short BitCount;
public int Compression;
public int SizeImage;
public int XPelsPerMeter;
public int YPelsPerMeter;
public int ClrUsed;
public int ClrImportant;
}
[DllImport("gdi32.dll", ExactSpelling = true)]
private static extern int SetDIBitsToDevice(IntPtr hdc,
int xdst, int ydst, int width, int height, int xsrc,
int ysrc, int start, int lines, IntPtr bitsptr,
IntPtr bmiptr, int color);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalLock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern bool GlobalUnlock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalFree(IntPtr handle);
public static Bitmap ToBitmap(IntPtr dibHandle)
{
var bitmapPointer = GlobalLock(dibHandle);
var bitmapInfo = new BitmapInfoHeader();
Marshal.PtrToStructure(bitmapPointer, bitmapInfo);
var rectangle = new Rectangle();
rectangle.X = rectangle.Y = 0;
rectangle.Width = bitmapInfo.Width;
rectangle.Height = bitmapInfo.Height;
if (bitmapInfo.SizeImage == 0)
{
bitmapInfo.SizeImage =
((((bitmapInfo.Width * bitmapInfo.BitCount) + 31) & ~31) >> 3)
* bitmapInfo.Height;
}
// The following code only works on x86
Debug.Assert(Marshal.SizeOf(typeof(IntPtr)) == 4);
int pixelInfoPointer = bitmapInfo.ClrUsed;
if ((pixelInfoPointer == 0) && (bitmapInfo.BitCount <= 8))
{
pixelInfoPointer = 1 << bitmapInfo.BitCount;
}
pixelInfoPointer = (pixelInfoPointer * 4) + bitmapInfo.Size
+ bitmapPointer.ToInt32();
IntPtr pixelInfoIntPointer = new IntPtr(pixelInfoPointer);
Bitmap bitmap = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
IntPtr hdc = graphics.GetHdc();
try
{
SetDIBitsToDevice(hdc,
0, 0, rectangle.Width, rectangle.Height, 0, 0, 0,
rectangle.Height, pixelInfoIntPointer, bitmapPointer, 0);
}
finally
{
graphics.ReleaseHdc(hdc);
}
}
bitmap.SetResolution(PpmToDpi(bitmapInfo.XPelsPerMeter),
PpmToDpi(bitmapInfo.YPelsPerMeter));
GlobalUnlock(dibHandle);
GlobalFree(dibHandle);
return bitmap;
}
private static float PpmToDpi(double pixelsPerMeter)
{
double pixelsPerMillimeter = (double)pixelsPerMeter / 1000.0;
double dotsPerInch = pixelsPerMillimeter * 25.4;
return (float)Math.Round(dotsPerInch, 2);
}
}
Assuming that tw.TransferPictures() returns an array of bitmap handles, then change Image r = ... to:
Image r = Image.FromHbitmap(img);
The second argument to FromHbitmap is a handle to a GDI palette, which I doubt you have.

Categories