C# convert RGB value to CMYK using an ICC profile? - c#

this question seems posted at many places over the interwebs and SO, but I could not find a satisfactory answer :(
How can I convert a RGB value to a CMYK value using an ICC profile?
The closest answer I have is there, where it explains how to convert from CMYK to RGB but not the other way around, which is what I need.
(http://stackoverflow.com/questions/4920482/cmyk-to-rgb-formula-of-photoshop/5076731#5076731)
float[] colorValues = new float[4];
colorValues[0] = c / 255f;
colorValues[1] = m / 255f;
colorValues[2] = y / 255f;
colorValues[3] = k / 255f;
System.Windows.Media.Color color = Color.FromValues(colorValues,
new Uri(#"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc"));
System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(color.R, color.G, color.B);
I guess I should be using some classes/structures/methods from the System.Windows.Media namespace.
The System.Windows.Media.Color structure contains a method FromRgb, but I can't get CMYK values after, out of that System.Windows.Media.Color!
Many thanks

I don't know of any C# API or library that can achieve this. However, if you have enough C/C++ knowledge to build a wrapper for C#, I see two options:
Windows Color System (WCS) (part of Windows)
LittleCMS
The System.Windows.Media namespace is very limited. There's probably a powerful engine (WCS?) behind it, but just a small part is made available.
Update:
Here's some C# code to do the conversion using WCS. It certainly could use a wrapper that would make it easier to use:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ICM
{
public class WindowsColorSystem
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class ProfileFilename
{
public uint type;
[MarshalAs(UnmanagedType.LPTStr)]
public string profileData;
public uint dataSize;
public ProfileFilename(string filename)
{
type = ProfileFilenameType;
profileData = filename;
dataSize = (uint)filename.Length * 2 + 2;
}
};
public const uint ProfileFilenameType = 1;
public const uint ProfileMembufferType = 2;
public const uint ProfileRead = 1;
public const uint ProfileReadWrite = 2;
public enum FileShare : uint
{
Read = 1,
Write = 2,
Delete = 4
};
public enum CreateDisposition : uint
{
CreateNew = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5
};
public enum LogicalColorSpace : uint
{
CalibratedRGB = 0x00000000,
sRGB = 0x73524742,
WindowsColorSpace = 0x57696E20
};
public enum ColorTransformMode : uint
{
ProofMode = 0x00000001,
NormalMode = 0x00000002,
BestMode = 0x00000003,
EnableGamutChecking = 0x00010000,
UseRelativeColorimetric = 0x00020000,
FastTranslate = 0x00040000,
PreserveBlack = 0x00100000,
WCSAlways = 0x00200000
};
enum ColorType : int
{
Gray = 1,
RGB = 2,
XYZ = 3,
Yxy = 4,
Lab = 5,
_3_Channel = 6,
CMYK = 7,
_5_Channel = 8,
_6_Channel = 9,
_7_Channel = 10,
_8_Channel = 11,
Named = 12
};
public const uint IntentPerceptual = 0;
public const uint IntentRelativeColorimetric = 1;
public const uint IntentSaturation = 2;
public const uint IntentAbsoluteColorimetric = 3;
public const uint IndexDontCare = 0;
[StructLayout(LayoutKind.Sequential)]
public struct RGBColor
{
public ushort red;
public ushort green;
public ushort blue;
public ushort pad;
};
[StructLayout(LayoutKind.Sequential)]
public struct CMYKColor
{
public ushort cyan;
public ushort magenta;
public ushort yellow;
public ushort black;
};
[DllImport("mscms.dll", SetLastError = true, EntryPoint = "OpenColorProfileW", CallingConvention = CallingConvention.Winapi)]
static extern IntPtr OpenColorProfile(
[MarshalAs(UnmanagedType.LPStruct)] ProfileFilename profile,
uint desiredAccess,
FileShare shareMode,
CreateDisposition creationMode);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool CloseColorProfile(IntPtr hProfile);
[DllImport("mscms.dll", SetLastError = true, EntryPoint = "GetStandardColorSpaceProfileW", CallingConvention = CallingConvention.Winapi)]
static extern bool GetStandardColorSpaceProfile(
uint machineName,
LogicalColorSpace profileID,
[MarshalAs(UnmanagedType.LPTStr), In, Out] StringBuilder profileName,
ref uint size);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern IntPtr CreateMultiProfileTransform(
[In] IntPtr[] profiles,
uint nProfiles,
[In] uint[] intents,
uint nIntents,
ColorTransformMode flags,
uint indexPreferredCMM);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool DeleteColorTransform(IntPtr hTransform);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool TranslateColors(
IntPtr hColorTransform,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), In] RGBColor[] inputColors,
uint nColors,
ColorType ctInput,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] CMYKColor[] outputColors,
ColorType ctOutput);
public static void Test()
{
bool success;
StringBuilder profileName = new StringBuilder(256);
uint size = (uint)profileName.Capacity * 2;
success = GetStandardColorSpaceProfile(0, LogicalColorSpace.sRGB, profileName, ref size);
ProfileFilename sRGBFilename = new ProfileFilename(profileName.ToString());
IntPtr hSRGBProfile = OpenColorProfile(sRGBFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);
ProfileFilename isoCoatedFilename = new ProfileFilename(#"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc");
IntPtr hIsoCoatedProfile = OpenColorProfile(isoCoatedFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);
IntPtr[] profiles = new IntPtr[] { hSRGBProfile, hIsoCoatedProfile };
uint[] intents = new uint[] { IntentPerceptual };
IntPtr transform = CreateMultiProfileTransform(profiles, 2, intents, 1, ColorTransformMode.BestMode, IndexDontCare);
RGBColor[] rgbColors = new RGBColor[1];
rgbColors[0] = new RGBColor();
CMYKColor[] cmykColors = new CMYKColor[1];
cmykColors[0] = new CMYKColor();
rgbColors[0].red = 30204;
rgbColors[0].green = 4420;
rgbColors[0].blue = 60300;
success = TranslateColors(transform, rgbColors, 1, ColorType.RGB, cmykColors, ColorType.CMYK);
success = DeleteColorTransform(transform);
success = CloseColorProfile(hSRGBProfile);
success = CloseColorProfile(hIsoCoatedProfile);
}
}
}

None of the answers here seem to satisfactorily address the need to use an ICC profile.
I found an MS Connect issue page that has some sample code using Windows Imaging Components to convert an RBG JPEG to CMYK using an ICC profile.
If you have your ICC file and a sample JPEG file, you can set-up a console app to use this code very quickly.
I have saved the ICC profile in a folder called "Profiles" and have set the "Copy to Output Directory" value to "Always".
The JPEG is saved to a folder called "Images", and I have set its "Build Action" value to "Embedded Resource".
The console app project needs references to the following modules:
PresentationCore
System.Xaml
WindowsBase
The console app in full (named CMYKConversion) :
Program.cs:
using System;
namespace CMYKConversion
{
class Program
{
static void Main(string[] args)
{
Converter c = new Converter();
c.Convert();
Console.ReadKey();
}
}
}
Converter.cs:
using System;
using System.IO;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace CMYKConversion
{
public class Converter
{
public void Convert()
{
var rgbJpeg = BitmapFrame.Create(GetStreamFromResource("CMYKConversion.Images.Desert.jpg"));
var iccCmykJpeg = new ColorConvertedBitmap(
rgbJpeg,
new ColorContext(PixelFormats.Default),
new ColorContext(GetProfilePath("Profiles/1010_ISO_Coated_39L.icc")),
PixelFormats.Cmyk32
);
var jpegBitmapEncoder = new JpegBitmapEncoder();
jpegBitmapEncoder.Frames.Add(BitmapFrame.Create(iccCmykJpeg));
var iccCmykJpegStream = new MemoryStream();
jpegBitmapEncoder.Save(iccCmykJpegStream);
iccCmykJpegStream.Flush();
SaveMemoryStream(iccCmykJpegStream, "C:\\desertCMYK.jpg");
iccCmykJpegStream.Close();
}
private Stream GetStreamFromResource(string name)
{
return typeof(Program).Assembly.GetManifestResourceStream(name);
}
private Uri GetProfilePath(string name)
{
string folder = Path.GetDirectoryName(Assembly.GetAssembly(typeof(Program)).CodeBase);
return new Uri(Path.Combine(folder, name));
}
private void SaveMemoryStream(MemoryStream ms, string fileName)
{
FileStream outStream = File.OpenWrite(fileName);
ms.WriteTo(outStream);
outStream.Flush();
outStream.Close();
}
}
}

According to an MVP GDI+ can read CMYK but can't encode it (Source: http://www.pcreview.co.uk/forums/convert-rgb-image-cmyk-t1419911.html). They go on to say that using TIF as an intermediation format may be the way to go.
Other than that, you might try Graphics Mill imaging SDK for .NET at http://imaging.aurigma.com/ (I'm not affiliated with this company).
I know this isn't much of an answer, but hopefully it sheds some light and points you in the right direction.

You could have a look at this: Convert RGB color to CMYK?
Although this conversion is fairly subjective, hence the need for the ICC profile, it may be that you can extract that "factor" from the ICC and adjust the formula?
What is the context is which you need to convert the RGB values to CMYK?

Related

Error in lineDevSpecific (login in the phone)

I am trying to do login in the phone. I am developing in c# and the library is in C++. The function "lineDevSpecific" returns the value "-2147483595", but it must to be positive.
[DllImport("Tapi32.dll", SetLastError = true)]
unsafe private static extern int lineDevSpecific(IntPtr hLine, IntPtr lpParams);
[StructLayout(LayoutKind.Sequential)]
public struct UserRec
{
//[MarshalAs(UnmanagedType.I4)]
public int dwMode=8;
public int dwParam1=201;
public int dwParam2=0;
}
unsafe static void Iniciar()
{
string vline = "Ip Office Phone: 201";
IntPtr hline = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(vline);
var sizeUserRec = Marshal.SizeOf(typeof(UserRec));
var userRec = Marshal.AllocHGlobal(sizeUserRec);
int result=lineDevSpecific(hline, userRec);
var x = (UserRec)Marshal.PtrToStructure(userRec, typeof(UserRec));
Marshal.FreeHGlobal(userRec);
}
Version 2:
I have modified the initial post adding the lineInitializeEx method and lineOpen.
These methods returns a positive value, after this lineDevSpecific continues returning the same value.
[DllImport("Tapi32.dll", CharSet = CharSet.Auto)]
unsafe private static extern long lineInitializeEx(ref uint lphLineApp, uint hInstance, uint lpfnCallback, uint lpszFriendlyAppName, ref uint lpdwNumDevs, ref uint lpdwAPIVersion, ref uint lpLineInitializeExParams);
[DllImport("Tapi32.dll", SetLastError = true)]
internal static extern long lineOpen(ref uint hLineApp, uint dwDeviceID, uint lphLine, uint dwAPIVersion, uint dwExtVersion, uint dwCallbackInstance, uint dwPrivileges, uint dwMediaModes, ref uint lpCallParams);
[DllImport("Tapi32.dll", CharSet = CharSet.Auto)]
unsafe private static extern int lineDevSpecific(uint hLine, IntPtr lpParams);
uint lineApp = 0;
uint numdevs = 0;
uint apiversion = 0;
uint exparams = 0;
uint lpcallparams = 0;
string sParams = "";
long lSize = 0;
long inicio = lineInitializeEx(ref lineApp, 0, 0, 0, ref numdevs, ref apiversion, ref exparams);
if (inicio > 0)
{
long open = lineOpen(ref lineApp, 0, 0, 0, 0, 0, 0, 0, ref lpcallparams);
//string vline = "Ip Office Phone: 201";
//IntPtr hline = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(vline);
var sizeUserRec = Marshal.SizeOf(typeof(UserRec));
var userRec = Marshal.AllocHGlobal(sizeUserRec);
int result = lineDevSpecific(lineApp, userRec);
var x = (UserRec)Marshal.PtrToStructure(userRec, typeof(UserRec));
Marshal.FreeHGlobal(userRec);
}
Solution: I have called to Julmar Atapi.
string extension = "201";
char[] c = new char[extension.Length + 2];
c[0] = (char)0x08; //login character
int i = 1;
foreach (char s in extension)
{
c[i] = s;
i++;
}
c[i] = (char)0x00; //null terminator
CurrentAddress.DeviceSpecific(Encoding.ASCII.GetBytes(c));
That is a LINEERR_ constant, see MSDN LINEERR_ Constants page
These use a hexadecimal unsigned "0x8000 00xx" style, turning them negative if you look at them as a signed int.
So this one is 0x8000 0035 LINEERR_INVALPOINTER
Your hline is wrong, this is a handle to a line not a text in a pointer. You need to get it from a lineOpen
Update for version 2
You are mixing up hLineApp and hLine. From lineOpen MSDN:
hLineApp: Handle to the application's registration with TAPI.
lphLine: Pointer to an HLINE handle that is then loaded with the handle representing the opened line device. Use this handle to identify the device when invoking other functions on the open line device.

P/Invoke throw System.ExecutionEngineException when upgrading .NET (3.5 -> 4.5)

I am using nsis7z.dll to extract 7z files. The code works just fine on .NET 3.5, but when I compile using .NET 4.5, it does extract the 7z file, but crashes afterwards with a System.ExecutionEngineException exception. From what I've searched it seems like there's something problematic with the parameters sent using the P/Invoke, mostly the one passed as ref.
The code:
public static bool ExtractNsis7z(string i_FileName, string i_ToDirectory)
{
IntPtr ptr = GlobalAlloc((uint)GlobalMemoryFlags.GPTR, (UIntPtr)Marshal.SizeOf(typeof(stack)));
stack st = (stack)Marshal.PtrToStructure(ptr, typeof(stack));
st.next = IntPtr.Zero;
for (int i = 0; i < i_FileName.Length && (i < st.text.Length - 1); i++)
{
st.text[i] = i_FileName[i];
}
return Extract7z(IntPtr.Zero, 0, i_ToDirectory, ref st, IntPtr.Zero);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class stack
{
public IntPtr next;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public char[] text;
};
[DllImport("nsis7z.dll", EntryPoint = "Extract", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
static extern bool Extract7z(IntPtr hWnd, Int32 stringSize, String outputDirectory, ref stack theStack, IntPtr extra);
enum GlobalMemoryFlags : uint
{
GMEM_FIXED = 0x0000,
GMEM_MOVEABLE = 0x0002,
GMEM_ZEROINIT = 0x0040,
GMEM_MODIFY = 0x0080,
GMEM_VALID_FLAGS = 0x7F72,
GMEM_INVALID_HANDLE = 0x8000,
GHND = (GMEM_MOVEABLE | GMEM_ZEROINIT),
GPTR = (GMEM_FIXED | GMEM_ZEROINIT),
/*The following values are obsolete, but are provided for compatibility with 16-bit Windows. They are ignored.*/
GMEM_DDESHARE = 0x2000,
GMEM_DISCARDABLE = 0x0100,
GMEM_LOWER = GMEM_NOT_BANKED,
GMEM_NOCOMPACT = 0x0010,
GMEM_NODISCARD = 0x0020,
GMEM_NOT_BANKED = 0x1000,
GMEM_NOTIFY = 0x4000,
GMEM_SHARE = 0x2000
}
[DllImport("kernel32.dll")]
static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);

Convert RGB value to CMYK using an ICC profile? c# [duplicate]

this question seems posted at many places over the interwebs and SO, but I could not find a satisfactory answer :(
How can I convert a RGB value to a CMYK value using an ICC profile?
The closest answer I have is there, where it explains how to convert from CMYK to RGB but not the other way around, which is what I need.
(http://stackoverflow.com/questions/4920482/cmyk-to-rgb-formula-of-photoshop/5076731#5076731)
float[] colorValues = new float[4];
colorValues[0] = c / 255f;
colorValues[1] = m / 255f;
colorValues[2] = y / 255f;
colorValues[3] = k / 255f;
System.Windows.Media.Color color = Color.FromValues(colorValues,
new Uri(#"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc"));
System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(color.R, color.G, color.B);
I guess I should be using some classes/structures/methods from the System.Windows.Media namespace.
The System.Windows.Media.Color structure contains a method FromRgb, but I can't get CMYK values after, out of that System.Windows.Media.Color!
Many thanks
I don't know of any C# API or library that can achieve this. However, if you have enough C/C++ knowledge to build a wrapper for C#, I see two options:
Windows Color System (WCS) (part of Windows)
LittleCMS
The System.Windows.Media namespace is very limited. There's probably a powerful engine (WCS?) behind it, but just a small part is made available.
Update:
Here's some C# code to do the conversion using WCS. It certainly could use a wrapper that would make it easier to use:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ICM
{
public class WindowsColorSystem
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class ProfileFilename
{
public uint type;
[MarshalAs(UnmanagedType.LPTStr)]
public string profileData;
public uint dataSize;
public ProfileFilename(string filename)
{
type = ProfileFilenameType;
profileData = filename;
dataSize = (uint)filename.Length * 2 + 2;
}
};
public const uint ProfileFilenameType = 1;
public const uint ProfileMembufferType = 2;
public const uint ProfileRead = 1;
public const uint ProfileReadWrite = 2;
public enum FileShare : uint
{
Read = 1,
Write = 2,
Delete = 4
};
public enum CreateDisposition : uint
{
CreateNew = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5
};
public enum LogicalColorSpace : uint
{
CalibratedRGB = 0x00000000,
sRGB = 0x73524742,
WindowsColorSpace = 0x57696E20
};
public enum ColorTransformMode : uint
{
ProofMode = 0x00000001,
NormalMode = 0x00000002,
BestMode = 0x00000003,
EnableGamutChecking = 0x00010000,
UseRelativeColorimetric = 0x00020000,
FastTranslate = 0x00040000,
PreserveBlack = 0x00100000,
WCSAlways = 0x00200000
};
enum ColorType : int
{
Gray = 1,
RGB = 2,
XYZ = 3,
Yxy = 4,
Lab = 5,
_3_Channel = 6,
CMYK = 7,
_5_Channel = 8,
_6_Channel = 9,
_7_Channel = 10,
_8_Channel = 11,
Named = 12
};
public const uint IntentPerceptual = 0;
public const uint IntentRelativeColorimetric = 1;
public const uint IntentSaturation = 2;
public const uint IntentAbsoluteColorimetric = 3;
public const uint IndexDontCare = 0;
[StructLayout(LayoutKind.Sequential)]
public struct RGBColor
{
public ushort red;
public ushort green;
public ushort blue;
public ushort pad;
};
[StructLayout(LayoutKind.Sequential)]
public struct CMYKColor
{
public ushort cyan;
public ushort magenta;
public ushort yellow;
public ushort black;
};
[DllImport("mscms.dll", SetLastError = true, EntryPoint = "OpenColorProfileW", CallingConvention = CallingConvention.Winapi)]
static extern IntPtr OpenColorProfile(
[MarshalAs(UnmanagedType.LPStruct)] ProfileFilename profile,
uint desiredAccess,
FileShare shareMode,
CreateDisposition creationMode);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool CloseColorProfile(IntPtr hProfile);
[DllImport("mscms.dll", SetLastError = true, EntryPoint = "GetStandardColorSpaceProfileW", CallingConvention = CallingConvention.Winapi)]
static extern bool GetStandardColorSpaceProfile(
uint machineName,
LogicalColorSpace profileID,
[MarshalAs(UnmanagedType.LPTStr), In, Out] StringBuilder profileName,
ref uint size);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern IntPtr CreateMultiProfileTransform(
[In] IntPtr[] profiles,
uint nProfiles,
[In] uint[] intents,
uint nIntents,
ColorTransformMode flags,
uint indexPreferredCMM);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool DeleteColorTransform(IntPtr hTransform);
[DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool TranslateColors(
IntPtr hColorTransform,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), In] RGBColor[] inputColors,
uint nColors,
ColorType ctInput,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] CMYKColor[] outputColors,
ColorType ctOutput);
public static void Test()
{
bool success;
StringBuilder profileName = new StringBuilder(256);
uint size = (uint)profileName.Capacity * 2;
success = GetStandardColorSpaceProfile(0, LogicalColorSpace.sRGB, profileName, ref size);
ProfileFilename sRGBFilename = new ProfileFilename(profileName.ToString());
IntPtr hSRGBProfile = OpenColorProfile(sRGBFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);
ProfileFilename isoCoatedFilename = new ProfileFilename(#"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc");
IntPtr hIsoCoatedProfile = OpenColorProfile(isoCoatedFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);
IntPtr[] profiles = new IntPtr[] { hSRGBProfile, hIsoCoatedProfile };
uint[] intents = new uint[] { IntentPerceptual };
IntPtr transform = CreateMultiProfileTransform(profiles, 2, intents, 1, ColorTransformMode.BestMode, IndexDontCare);
RGBColor[] rgbColors = new RGBColor[1];
rgbColors[0] = new RGBColor();
CMYKColor[] cmykColors = new CMYKColor[1];
cmykColors[0] = new CMYKColor();
rgbColors[0].red = 30204;
rgbColors[0].green = 4420;
rgbColors[0].blue = 60300;
success = TranslateColors(transform, rgbColors, 1, ColorType.RGB, cmykColors, ColorType.CMYK);
success = DeleteColorTransform(transform);
success = CloseColorProfile(hSRGBProfile);
success = CloseColorProfile(hIsoCoatedProfile);
}
}
}
None of the answers here seem to satisfactorily address the need to use an ICC profile.
I found an MS Connect issue page that has some sample code using Windows Imaging Components to convert an RBG JPEG to CMYK using an ICC profile.
If you have your ICC file and a sample JPEG file, you can set-up a console app to use this code very quickly.
I have saved the ICC profile in a folder called "Profiles" and have set the "Copy to Output Directory" value to "Always".
The JPEG is saved to a folder called "Images", and I have set its "Build Action" value to "Embedded Resource".
The console app project needs references to the following modules:
PresentationCore
System.Xaml
WindowsBase
The console app in full (named CMYKConversion) :
Program.cs:
using System;
namespace CMYKConversion
{
class Program
{
static void Main(string[] args)
{
Converter c = new Converter();
c.Convert();
Console.ReadKey();
}
}
}
Converter.cs:
using System;
using System.IO;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace CMYKConversion
{
public class Converter
{
public void Convert()
{
var rgbJpeg = BitmapFrame.Create(GetStreamFromResource("CMYKConversion.Images.Desert.jpg"));
var iccCmykJpeg = new ColorConvertedBitmap(
rgbJpeg,
new ColorContext(PixelFormats.Default),
new ColorContext(GetProfilePath("Profiles/1010_ISO_Coated_39L.icc")),
PixelFormats.Cmyk32
);
var jpegBitmapEncoder = new JpegBitmapEncoder();
jpegBitmapEncoder.Frames.Add(BitmapFrame.Create(iccCmykJpeg));
var iccCmykJpegStream = new MemoryStream();
jpegBitmapEncoder.Save(iccCmykJpegStream);
iccCmykJpegStream.Flush();
SaveMemoryStream(iccCmykJpegStream, "C:\\desertCMYK.jpg");
iccCmykJpegStream.Close();
}
private Stream GetStreamFromResource(string name)
{
return typeof(Program).Assembly.GetManifestResourceStream(name);
}
private Uri GetProfilePath(string name)
{
string folder = Path.GetDirectoryName(Assembly.GetAssembly(typeof(Program)).CodeBase);
return new Uri(Path.Combine(folder, name));
}
private void SaveMemoryStream(MemoryStream ms, string fileName)
{
FileStream outStream = File.OpenWrite(fileName);
ms.WriteTo(outStream);
outStream.Flush();
outStream.Close();
}
}
}
According to an MVP GDI+ can read CMYK but can't encode it (Source: http://www.pcreview.co.uk/forums/convert-rgb-image-cmyk-t1419911.html). They go on to say that using TIF as an intermediation format may be the way to go.
Other than that, you might try Graphics Mill imaging SDK for .NET at http://imaging.aurigma.com/ (I'm not affiliated with this company).
I know this isn't much of an answer, but hopefully it sheds some light and points you in the right direction.
You could have a look at this: Convert RGB color to CMYK?
Although this conversion is fairly subjective, hence the need for the ICC profile, it may be that you can extract that "factor" from the ICC and adjust the formula?
What is the context is which you need to convert the RGB values to CMYK?

C# Newbie: How do I fix this code to do a DNS lookup?

This is my first time using C#, so I'm very much out of my element. But I have faith that the wonderful people here at Stack Overflow can help me out! I've come up with the following code (below) based on some other pieces of code I found floating around on the internet. What I am trying to do is to look up all the "text" DNS records (TXT) of a given domain. I've started out simple, with a Console Application in Visual C# 2008 Express looking up the records for google.com. I'll worry about customizing the domain based on command line parameters later; for now I'd just like to get this working. Any DNS lookup tools can tell you that google.com has this DNS text record in their DNS:
v=spf1 include:_netblocks.google.com ip4:216.73.93.70/31 ip4:216.73.93.72/31 ~all
But unfortunately that's not what I'm getting. Here's my code:
namespace DnsUtils
{
using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
public class DnsTxt
{
[DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)]ref string pszName, QueryTypes wType, QueryOptions options, int aipServers, ref IntPtr ppQueryResults, int pReserved);
[DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);
public static string[] GetTXTRecords(string domain)
{
IntPtr ptr1 = IntPtr.Zero;
IntPtr ptr2 = IntPtr.Zero;
TXTRecord recTxt;
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
{
throw new NotSupportedException();
}
ArrayList list1 = new ArrayList();
UnicodeEncoding encoding = new UnicodeEncoding();
int num1 = DnsTxt.DnsQuery(ref domain, QueryTypes.DNS_TYPE_TEXT, QueryOptions.DNS_QUERY_BYPASS_CACHE, 0, ref ptr1, 0);
if (num1 != 0)
{
throw new Win32Exception(num1);
}
for (ptr2 = ptr1; !ptr2.Equals(IntPtr.Zero); ptr2 = recTxt.pNext)
{
recTxt = (TXTRecord)Marshal.PtrToStructure(ptr2, typeof(TXTRecord));
if (recTxt.wType == 16)
{
IntPtr pointerToAddressStringArray = Marshal.AllocHGlobal(IntPtr.Size);
IntPtr addressStringArray = Marshal.ReadIntPtr(pointerToAddressStringArray);
for (int i = 0; i < recTxt.dwStringCount; i++)
{
IntPtr addressCharArray = Marshal.ReadIntPtr(recTxt.pStringArray, i * 4);
int offset = 0;
ArrayList bytesList = new ArrayList();
byte newByte = Marshal.ReadByte(addressCharArray, offset++);
while (newByte != 0)
{
bytesList.Add(newByte);
newByte = Marshal.ReadByte(addressCharArray, offset++);
}
byte[] bytesArray = new byte[offset];
bytesList.CopyTo(bytesArray);
string textValue = encoding.GetString(bytesArray);
list1.Add(textValue);
}
Marshal.FreeHGlobal(pointerToAddressStringArray);
}
}
DnsTxt.DnsRecordListFree(ptr2, 0);
return (string[])list1.ToArray(typeof(string));
}
private enum QueryOptions
{
DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,
DNS_QUERY_BYPASS_CACHE = 8,
DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
DNS_QUERY_NO_HOSTS_FILE = 0x40,
DNS_QUERY_NO_LOCAL_NAME = 0x20,
DNS_QUERY_NO_NETBT = 0x80,
DNS_QUERY_NO_RECURSION = 4,
DNS_QUERY_NO_WIRE_QUERY = 0x10,
DNS_QUERY_RESERVED = -16777216,
DNS_QUERY_RETURN_MESSAGE = 0x200,
DNS_QUERY_STANDARD = 0,
DNS_QUERY_TREAT_AS_FQDN = 0x1000,
DNS_QUERY_USE_TCP_ONLY = 2,
DNS_QUERY_WIRE_ONLY = 0x100
}
private enum QueryTypes
{
DNS_TYPE_TEXT = 16
}
[StructLayout(LayoutKind.Sequential)]
private struct TXTRecord
{
public IntPtr pNext;
public string pName;
public short wType;
public short wDataLength;
public int flags;
public int dwTtl;
public int dwReserved;
public int dwStringCount;
public IntPtr pStringArray;
}
static void Main(string[] args)
{
try
{
string[] s = DnsUtils.DnsTxt.GetTXTRecords("google.com");
foreach (string st in s)
{
Console.WriteLine("Value: {0}", st);
}
}
catch (Win32Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
}
}
When I run this code, it prints Value: ?. Now I'm hoping that this means it actually did the DNS query and got the results as expected, but I just screwed something up in the part where it tries to convert an IntPtr to an array of strings (because I'm guessing it's harder to debug the former). Anyways, have any suggestions? Can anyone see where I went awry? Thanks in advance.
This line looks a bit off to me:
IntPtr addressCharArray = Marshal.ReadIntPtr(recTxt.pStringArray, i * 4);
It looks like you'd be reading the first 4 bytes of the first string entry of the TXT record as an IntPtr. I think something like:
string s = Marshal.PtrToStringAuto(recTxt.pStringArray);
would get you the first entry. After that, I think something like:
IntPtr p = new IntPtr(recTxt.pStringArray.ToInt32() + sizeof(uint) * i);
string s = Marshal.PtrToStringAuto(p);
would get the remainders.
As a simpler alternative you could avoid dealing with all the COM interop by creating a new nslookup process and parsing the StandardOuput to grab what you need.
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var startInfo = new ProcessStartInfo("nslookup");
startInfo.Arguments = "-type=TXT google.com";
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
using (var cmd = Process.Start(startInfo))
{
// This is where you grab the output from nslookup.
Console.WriteLine(cmd.StandardOutput.ReadToEnd());
}
Console.Read();
}
}
}

wglCreateContext in C# failing but not in managed C++

I'm trying to use opengl in C#. I have following code which fails with error 2000 ERROR_INVALID_PIXEL_FORMAT
First definitions:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
public struct PIXELFORMATDESCRIPTOR
{
public void Init()
{
nSize = (ushort) Marshal.SizeOf(typeof (PIXELFORMATDESCRIPTOR));
nVersion = 1;
dwFlags = PFD_FLAGS.PFD_DRAW_TO_WINDOW | PFD_FLAGS.PFD_SUPPORT_OPENGL | PFD_FLAGS.PFD_DOUBLEBUFFER | PFD_FLAGS.PFD_SUPPORT_COMPOSITION;
iPixelType = PFD_PIXEL_TYPE.PFD_TYPE_RGBA;
cColorBits = 24;
cRedBits = cRedShift = cGreenBits = cGreenShift = cBlueBits = cBlueShift = 0;
cAlphaBits = cAlphaShift = 0;
cAccumBits = cAccumRedBits = cAccumGreenBits = cAccumBlueBits = cAccumAlphaBits = 0;
cDepthBits = 32;
cStencilBits = cAuxBuffers = 0;
iLayerType = PFD_LAYER_TYPES.PFD_MAIN_PLANE;
bReserved = 0;
dwLayerMask = dwVisibleMask = dwDamageMask = 0;
}
ushort nSize;
ushort nVersion;
PFD_FLAGS dwFlags;
PFD_PIXEL_TYPE iPixelType;
byte cColorBits;
byte cRedBits;
byte cRedShift;
byte cGreenBits;
byte cGreenShift;
byte cBlueBits;
byte cBlueShift;
byte cAlphaBits;
byte cAlphaShift;
byte cAccumBits;
byte cAccumRedBits;
byte cAccumGreenBits;
byte cAccumBlueBits;
byte cAccumAlphaBits;
byte cDepthBits;
byte cStencilBits;
byte cAuxBuffers;
PFD_LAYER_TYPES iLayerType;
byte bReserved;
uint dwLayerMask;
uint dwVisibleMask;
uint dwDamageMask;
}
[Flags]
public enum PFD_FLAGS : uint
{
PFD_DOUBLEBUFFER = 0x00000001,
PFD_STEREO = 0x00000002,
PFD_DRAW_TO_WINDOW = 0x00000004,
PFD_DRAW_TO_BITMAP = 0x00000008,
PFD_SUPPORT_GDI = 0x00000010,
PFD_SUPPORT_OPENGL = 0x00000020,
PFD_GENERIC_FORMAT = 0x00000040,
PFD_NEED_PALETTE = 0x00000080,
PFD_NEED_SYSTEM_PALETTE = 0x00000100,
PFD_SWAP_EXCHANGE = 0x00000200,
PFD_SWAP_COPY = 0x00000400,
PFD_SWAP_LAYER_BUFFERS = 0x00000800,
PFD_GENERIC_ACCELERATED = 0x00001000,
PFD_SUPPORT_DIRECTDRAW = 0x00002000,
PFD_DIRECT3D_ACCELERATED = 0x00004000,
PFD_SUPPORT_COMPOSITION = 0x00008000,
PFD_DEPTH_DONTCARE = 0x20000000,
PFD_DOUBLEBUFFER_DONTCARE = 0x40000000,
PFD_STEREO_DONTCARE = 0x80000000
}
public enum PFD_LAYER_TYPES : byte
{
PFD_MAIN_PLANE = 0,
PFD_OVERLAY_PLANE = 1,
PFD_UNDERLAY_PLANE = 255
}
public enum PFD_PIXEL_TYPE : byte
{
PFD_TYPE_RGBA = 0,
PFD_TYPE_COLORINDEX = 1
}
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int ChoosePixelFormat(IntPtr hdc, [In] ref PIXELFORMATDESCRIPTOR ppfd);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern bool SetPixelFormat(IntPtr hdc, int iPixelFormat, ref PIXELFORMATDESCRIPTOR ppfd);
[DllImport("opengl32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern IntPtr wglCreateContext(IntPtr hDC);
And now the code that fails:
IntPtr dc = Win.GetDC(hwnd);
var pixelformatdescriptor = new GL.PIXELFORMATDESCRIPTOR();
pixelformatdescriptor.Init();
var pixelFormat = GL.ChoosePixelFormat(dc, ref pixelformatdescriptor);
if(!GL.SetPixelFormat(dc, pixelFormat, ref pixelformatdescriptor))
throw new Win32Exception(Marshal.GetLastWin32Error());
IntPtr hglrc;
if((hglrc = GL.wglCreateContext(dc)) == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error()); //<----- here I have exception
the same code in managed C++ is working
HDC dc = GetDC(hWnd);
PIXELFORMATDESCRIPTOR pf;
pf.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pf.nVersion = 1;
pf.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SUPPORT_COMPOSITION;
pf.cColorBits = 24;
pf.cRedBits = pf.cRedShift = pf.cGreenBits = pf.cGreenShift = pf.cBlueBits = pf.cBlueShift = 0;
pf.cAlphaBits = pf.cAlphaShift = 0;
pf.cAccumBits = pf.cAccumRedBits = pf.cAccumGreenBits = pf.cAccumBlueBits = pf.cAccumAlphaBits = 0;
pf.cDepthBits = 32;
pf.cStencilBits = pf.cAuxBuffers = 0;
pf.iLayerType = PFD_MAIN_PLANE;
pf.bReserved = 0;
pf.dwLayerMask = pf.dwVisibleMask = pf.dwDamageMask = 0;
int ipf = ChoosePixelFormat(dc, &pf);
SetPixelFormat(dc, ipf, &pf);
HGLRC hglrc = wglCreateContext(dc);
I've tried it on VIsta 64-bit with ATI graphic card and on Windows XP 32-bit with Nvidia with the same result in both cases.
Also I want to mention that I don't want to use any already written framework for it.
Can anyone show me where is the bug in C# code that is causing the exception?
Found solution.
Problem is very strange ugly and really hard to find. Somwhere on the internet I found that when you are linking opengl32.lib while compiling c++ application it must be placed before gdi32.lib. The reason for this is that (supposedly) opengl32.dll is overwriting ChoosePixelFormat and SetPixelFormat functions (and probably more :-). As I found in my c++ version, accidentally it was the case.
Heh, but how to do it in C#
After few days of searching I found that in tao framework they solved it using kernel32.dll LoadLibrary() function and loading opengl32.dll before calling SetPixelFormat
public static bool SetPixelFormat(IntPtr deviceContext, int pixelFormat, ref PIXELFORMATDESCRIPTOR pixelFormatDescriptor) {
Kernel.LoadLibrary("opengl32.dll");
return _SetPixelFormat(deviceContext, pixelFormat, ref pixelFormatDescriptor);
}
So we know that opengl32.dll must be loaded before gdi32.dll, is there any other way of doing this. After while I thought that we can call some NOP function from opengl32.dll to load it. For example:
[DllImport("opengl32.dll", EntryPoint = "glGetString", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
static extern IntPtr _glGetString(StringName name);
public static string glGetString(StringName name)
{
return Marshal.PtrToStringAnsi(_glGetString(name));
}
public enum StringName : uint
{
GL_VENDOR = 0x1F00,
GL_RENDERER = 0x1F01,
GL_VERSION = 0x1F02,
GL_EXTENSIONS = 0x1F03
}
and on the start of application, before any call to gdi32.dll I use this:
GL.glGetString(0);
Both ways solves the problem.
I cannot test this right now, but my first suspicion would be the structure packing. Have you tried setting the packing to 1 in the StructLayout attribute? For example:
[StructLayout(LayoutKind.Sequential, Pack=1)]
Cheers,
Brian
Calling wglCreateContext twice helps too.
if (SetPixelFormat(DC, iPixelformat, ref pfd) == false)
throw new Win32Exception(Marshal.GetLastWin32Error());
RC = wglCreateContext(DC);
if (RC == HGLRC.Zero)
{
if (SetPixelFormat(DC, iPixelformat, ref pfd) == false)
throw new Win32Exception(Marshal.GetLastWin32Error());
RC = wglCreateContext(DC);
if (RC == HGLRC.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}

Categories