How to convert this Gdi+ 1.1 signature to C# p/invoke?:
GpStatus WINGDIPAPI GdipBitmapConvertFormat(IN GpBitmap *pInputBitmap,
PixelFormat format, DitherType dithertype, PaletteType palettetype,
ColorPalette *palette, REAL alphaThresholdPercent);
http://msdn.microsoft.com/en-us/library/windows/desktop/ms536306(v=vs.85).aspx
What I have in this moment is:
internal static class GdiPlusWrapper
{
[DllImport("gdiplus.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
public static extern Status GdipBitmapConvertFormat(HandleRef bitmap,
PixelFormat format, DitherType dithertype, PaletteType palettetype,
ColorPalette palette, float alpha);
[DllImport("gdiplus.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
public static extern Status GdipInitializePalette(out ColorPalette pallete,
PaletteType palettetype, int optimalColors, bool useTransparentColor,
HandleRef bitmap);
internal enum DitherType
{
DitherTypeNone = 0,
DitherTypeSolid = 1,
DitherTypeOrdered4x4 = 2,
DitherTypeOrdered8x8 = 3,
DitherTypeOrdered16x16 = 4,
DitherTypeOrdered91x91 = 5,
DitherTypeSpiral4x4 = 6,
DitherTypeSpiral8x8 = 7,
DitherTypeDualSpiral4x4 = 8,
DitherTypeDualSpiral8x8 = 9,
DitherTypeErrorDiffusion = 10,
}
internal enum PaletteType
{
PaletteTypeCustom = 0,
PaletteTypeOptimal = 1,
PaletteTypeFixedBW = 2,
PaletteTypeFixedHalftone8 = 3,
PaletteTypeFixedHalftone27 = 4,
PaletteTypeFixedHalftone64 = 5,
PaletteTypeFixedHalftone125 = 6,
PaletteTypeFixedHalftone216 = 7,
PaletteTypeFixedHalftone252 = 8,
PaletteTypeFixedHalftone256 = 9
}
internal enum Status
{
Ok = 0,
GenericError = 1,
InvalidParameter = 2,
OutOfMemory = 3,
ObjectBusy = 4,
InsufficientBuffer = 5,
NotImplemented = 6,
Win32Error = 7,
WrongState = 8,
Aborted = 9,
FileNotFound = 10,
ValueOverflow = 11,
AccessDenied = 12,
UnknownImageFormat = 13,
FontFamilyNotFound = 14,
FontStyleNotFound = 15,
NotTrueTypeFont = 16,
UnsupportedGdiplusVersion = 17,
GdiplusNotInitialized = 18,
PropertyNotFound = 19,
PropertyNotSupported = 20,
ProfileNotFound = 21
}
internal static T GetPrivateField<T>(this object obj, string fieldName)
{
if (obj == null) return default(T);
var ltType = obj.GetType();
var lfiFieldInfo = ltType.GetField(fieldName,
BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
if (lfiFieldInfo != null)
return (T) lfiFieldInfo.GetValue(obj);
throw new InvalidOperationException(
String.Format("Instance field '{0}' could not be located in object of type '{1}'.", fieldName,
obj.GetType().FullName));
}
}
And this is how you use this:
var bitmap = new Bitmap(@"testing\image.png");
var bmpHandle = new HandleRef(bitmap, bitmap.GetPrivateField<IntPtr>("nativeImage"));
ColorPalette palette = null;
GdiPlusWrapper.GdipInitializePalette(out palette, GdiPlusWrapper.PaletteType.PaletteTypeOptimal, 0,
true, bmpHandle);
foreach (GdiPlusWrapper.DitherType dither in Enum.GetValues(typeof(GdiPlusWrapper.DitherType)))
{
GdiPlusWrapper.GdipBitmapConvertFormat(bmpHandle, PixelFormat.Format16bppRgb555,
dither, GdiPlusWrapper.PaletteType.PaletteTypeFixedBW, ref palette, 0.0f);
bitmap.Save(@"testing\image_" + dither + ".png");
}
But the dithering isn't working :(
The pallete generation seems to be the issue. Any hint?
Found a solution in http://download.csdn.net/download/laviewpbt/4900954
// Source: http://download.csdn.net/download/laviewpbt/4900954
// Author: http://download.csdn.net/user/laviewpbt
namespace PrimeLib
{
public enum DitherType
{
DitherTypeNone = 0,
DitherTypeSolid = 1,
DitherTypeOrdered4x4 = 2,
DitherTypeOrdered8x8 = 3,
DitherTypeOrdered16x16 = 4,
DitherTypeSpiral4x4 = 5,
DitherTypeSpiral8x8 = 6,
DitherTypeDualSpiral4x4 = 7,
DitherTypeDualSpiral8x8 = 8,
DitherTypeErrorDiffusion = 9,
DitherTypeMax = 10
}
public enum PaletteType
{
PaletteTypeCustom = 0,
PaletteTypeOptimal = 1,
PaletteTypeFixedBW = 2,
PaletteTypeFixedHalftone8 = 3,
PaletteTypeFixedHalftone27 = 4,
PaletteTypeFixedHalftone64 = 5,
PaletteTypeFixedHalftone125 = 6,
PaletteTypeFixedHalftone216 = 7,
PaletteTypeFixedHalftone252 = 8,
PaletteTypeFixedHalftone256 = 9
}
public static class GdipEffect
{
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
private static extern int GdipInitializePalette(ref GdiPalette Pal, int palettetype, int optimalColors,
int useTransparentColor, IntPtr bitmap);
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
private static extern int GdipInitializePalette(int[] Pal, PaletteType palettetype, int optimalColors,
int useTransparentColor, IntPtr bitmap);
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
private static extern int GdipBitmapConvertFormat(IntPtr bitmap, int pixelFormat, DitherType dithertype,
PaletteType palettetype, ref GdiPalette Pal, float alphaThresholdPercent);
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
private static extern int GdipBitmapConvertFormat(IntPtr bitmap, int pixelFormat, DitherType dithertype,
PaletteType palettetype, int[] Pal, float alphaThresholdPercent);
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
private static extern int GdipBitmapConvertFormat(IntPtr bitmap, int pixelFormat, DitherType dithertype,
PaletteType palettetype, IntPtr Pal, float alphaThresholdPercent);
public static void ChangeTo8bppIndexed(this Bitmap Bmp, PaletteType palettetype = PaletteType.PaletteTypeOptimal,
DitherType ditherType = DitherType.DitherTypeErrorDiffusion, int optimalColors = 256)
{
int Entries;
// http://msdn.microsoft.com/en-us/library/ms534159(v=vs.85).aspx
switch (palettetype)
{
case PaletteType.PaletteTypeFixedBW:
Entries = 2;
break;
case PaletteType.PaletteTypeFixedHalftone8:
Entries = 16;
break;
case PaletteType.PaletteTypeFixedHalftone27:
Entries = 36;
break;
case PaletteType.PaletteTypeFixedHalftone64:
Entries = 73;
break;
case PaletteType.PaletteTypeFixedHalftone125:
Entries = 134;
break;
case PaletteType.PaletteTypeFixedHalftone216:
Entries = 225;
break;
case PaletteType.PaletteTypeFixedHalftone252:
Entries = 253;
break;
case PaletteType.PaletteTypeFixedHalftone256:
Entries = 256;
break;
case PaletteType.PaletteTypeOptimal:
if (optimalColors <= 0 || optimalColors > 256)
throw new ArgumentOutOfRangeException(
"Colors should be between 0 (inclusive) and 256 (exclusive)");
Entries = optimalColors;
break;
default:
throw new ArgumentException("Error");
}
var Pal = new int[2 + Entries];
Pal[0] = (int) PaletteFlags.GrayScale; // Flag
Pal[1] = Entries; // Count
if (palettetype == PaletteType.PaletteTypeOptimal)
GdipInitializePalette(Pal, palettetype, Entries, 0, Bmp.NativeHandle());
else
GdipInitializePalette(Pal, palettetype, Entries, 0, IntPtr.Zero);
if (palettetype == PaletteType.PaletteTypeOptimal)
if (ditherType != DitherType.DitherTypeNone && ditherType != DitherType.DitherTypeSolid &&
ditherType != DitherType.DitherTypeErrorDiffusion)
throw new ArgumentException("Arguments error");
GdipBitmapConvertFormat(Bmp.NativeHandle(), Convert.ToInt32(PixelFormat.Format8bppIndexed), ditherType,
palettetype, Pal, 50f);
}
public static void ChangeToSpecialIndexed(this Bitmap Bmp, PaletteType palettetype = PaletteType.PaletteTypeOptimal,
DitherType ditherType = DitherType.DitherTypeErrorDiffusion, int optimalColors = 256)
{
int Entries;
// http://msdn.microsoft.com/en-us/library/ms534159(v=vs.85).aspx
switch (palettetype)
{
case PaletteType.PaletteTypeFixedBW:
Entries = 2;
break;
case PaletteType.PaletteTypeFixedHalftone8:
Entries = 16;
break;
case PaletteType.PaletteTypeFixedHalftone27:
Entries = 36;
break;
case PaletteType.PaletteTypeFixedHalftone64:
Entries = 73;
break;
case PaletteType.PaletteTypeFixedHalftone125:
Entries = 134;
break;
case PaletteType.PaletteTypeFixedHalftone216:
Entries = 225;
break;
case PaletteType.PaletteTypeFixedHalftone252:
Entries = 253;
break;
case PaletteType.PaletteTypeFixedHalftone256:
Entries = 256;
break;
case PaletteType.PaletteTypeOptimal:
if (optimalColors <= 0 || optimalColors > 256)
throw new ArgumentOutOfRangeException(
"Colors should be between 0 (inclusive) and 256 (exclusive)");
Entries = optimalColors;
break;
default:
throw new ArgumentException("Error");
}
var Pal = new int[2 + Entries];
Pal[0] = (int)PaletteFlags.GrayScale; // Flag
Pal[1] = Entries; // Count
if (palettetype == PaletteType.PaletteTypeOptimal)
GdipInitializePalette(Pal, palettetype, Entries, 0, Bmp.NativeHandle());
else
GdipInitializePalette(Pal, palettetype, Entries, 0, IntPtr.Zero);
if (palettetype == PaletteType.PaletteTypeOptimal)
if (ditherType != DitherType.DitherTypeNone && ditherType != DitherType.DitherTypeSolid &&
ditherType != DitherType.DitherTypeErrorDiffusion)
throw new ArgumentException("Arguments error");
GdipBitmapConvertFormat(Bmp.NativeHandle(), Convert.ToInt32(PixelFormat.Format16bppArgb1555), ditherType,
palettetype, Pal, 50f);
}
public static void ChangeTo4bppIndexed(this Bitmap Bmp, PaletteType palettetype = PaletteType.PaletteTypeOptimal,
DitherType ditherType = DitherType.DitherTypeErrorDiffusion, int optimalColors = 16)
{
int Entries;
// http://msdn.microsoft.com/en-us/library/ms534159(v=vs.85).aspx
switch (palettetype)
{
case PaletteType.PaletteTypeFixedBW:
Entries = 2;
break;
case PaletteType.PaletteTypeFixedHalftone8:
Entries = 16;
break;
case PaletteType.PaletteTypeOptimal:
if (optimalColors <= 0 || optimalColors > 16)
throw new ArgumentOutOfRangeException("Colors should be between 0 (inclusive) and 16 (exclusive)");
Entries = optimalColors;
break;
default:
throw new ArgumentException("Error");
}
var Pal = new int[2 + Entries];
Pal[0] = (int) PaletteFlags.GrayScale; // Flag
Pal[1] = Entries; // Count
if (palettetype == PaletteType.PaletteTypeOptimal)
GdipInitializePalette(Pal, palettetype, Entries, 0, Bmp.NativeHandle());
else
GdipInitializePalette(Pal, palettetype, Entries, 0, IntPtr.Zero);
if (palettetype == PaletteType.PaletteTypeOptimal)
if (ditherType != DitherType.DitherTypeNone && ditherType != DitherType.DitherTypeSolid &&
ditherType != DitherType.DitherTypeErrorDiffusion)
throw new ArgumentException("Arguments error");
GdipBitmapConvertFormat(Bmp.NativeHandle(), Convert.ToInt32(PixelFormat.Format4bppIndexed), ditherType,
palettetype, Pal, 50f);
}
public static void ChangeTo1bppIndexed(this Bitmap Bmp,
DitherType ditherType = DitherType.DitherTypeErrorDiffusion)
{
if (ditherType != DitherType.DitherTypeSolid && ditherType != DitherType.DitherTypeErrorDiffusion)
throw new ArgumentException("Arguments error.");
var Pal = new int[4];
Pal[0] = (int) PaletteFlags.GrayScale; // Flag
Pal[1] = 2; // Count
GdipInitializePalette(Pal, PaletteType.PaletteTypeFixedBW, 2, 0, IntPtr.Zero);
GdipBitmapConvertFormat(Bmp.NativeHandle(), Convert.ToInt32(PixelFormat.Format1bppIndexed), ditherType,
PaletteType.PaletteTypeFixedBW, Pal, 50f);
}
public static void ChangeTo16bppRgb555(this Bitmap Bmp,
DitherType ditherType = DitherType.DitherTypeErrorDiffusion)
{
GdipBitmapConvertFormat(Bmp.NativeHandle(), Convert.ToInt32(PixelFormat.Format16bppRgb555), ditherType,
PaletteType.PaletteTypeCustom, IntPtr.Zero, 50f);
}
public static void ChangeTo24bppRgb(this Bitmap Bmp)
{
GdipBitmapConvertFormat(Bmp.NativeHandle(), Convert.ToInt32(PixelFormat.Format24bppRgb),
DitherType.DitherTypeNone, PaletteType.PaletteTypeCustom, IntPtr.Zero, 50f);
}
public static void ChangeTo32bppARGB(this Bitmap Bmp)
{
GdipBitmapConvertFormat(Bmp.NativeHandle(), Convert.ToInt32(PixelFormat.Format32bppArgb),
DitherType.DitherTypeNone, PaletteType.PaletteTypeCustom, IntPtr.Zero, 50f);
}
internal static TResult GetPrivateField<TResult>(this object obj, string fieldName)
{
if (obj == null) return default(TResult);
Type ltType = obj.GetType();
FieldInfo lfiFieldInfo = ltType.GetField(fieldName,
BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
if (lfiFieldInfo != null)
return (TResult) lfiFieldInfo.GetValue(obj);
throw new InvalidOperationException(
string.Format("Instance field '{0}' could not be located in object of type '{1}'.", fieldName,
obj.GetType().FullName));
}
public static IntPtr NativeHandle(this Bitmap Bmp)
{
return Bmp.GetPrivateField<IntPtr>("nativeImage");
}
[StructLayout(LayoutKind.Sequential)]
private struct GdiPalette
{
internal readonly PaletteFlags Flag;
internal readonly int Count;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)] internal readonly byte[] Entries;
}
}
}
Here is the cleaned file (basically just removing Chinese chars): https://github.com/eried/PrimeComm/blob/7cbce9f16a0f647c43a6b69e7a77a820a0f80df4/PrimeLib/GdipEffect.cs
I was pretty near, but it seems the palette isn't defined for non-indexed pixelformats. I am not sure if this is 100% correct, but at least it kinda works (images still present 'banding' effects).