Rank: Advanced Member
Groups: Guest
Joined: 5/10/2006(UTC) Posts: 32
Thanks: 6 times
|
I looked at the BitBlt function, which by default ignores color management, but then I found SetICMMode , which says Quote:"Specifically, if a device independent bitmap (DIB) is used as the source for a blit, and the blit is performed into a DC that has WCS enabled, color matching will be performed." Based on that, I added a call to SetICMMode to query the mode (updated code below) of the printer device context, and on the Cute PDF printer ICM mode is ON and on the Microsoft PDF printer it is OFF. This is causing the output between these two printers to be different. In affect, if ICM mode is ON, then the color profile is being applied by Windows Color Mangament (WCM) and it should not be applied to the image before DrawOn (BitBlt) otherwise, the profile has been applied twice. To test this hypothesis, I updated the code so that it uses whatever the printer ICM mode is and calls DrawOn without and with the specificed profile applied. I then switch the ICM mode of the printer context, and DrawOn the image again without and with the profile. For the Microsoft PDF printer the results looked like this: So, the upper left has no profile applied, the upper right has the profile applied using Aurigma, the lower left has the profile applied by WCM, and the lower right has the profile applied once by Aurigma and again by WCM. Using a sRGB color profile image, the upper right and lower left are the same (virtually, there are differences in the jpg artifacts). For the CutePDF printer the results looked like this: Since CutePDF ICM is ON by default, the results are switched compared to the Microsoft PDF. The upper left has the profile applied by WCM, the upper right has the profile applied once by Aurigma and again by WCM, the lower left has no profile applied, and the lower right has the profile applied using Aurigma. Using a sRGB color profile image, the upper left and lower right are the same. Note there are some slight color differences between WCM and Aurigma applying the same profile when the source image is not sRGB (AdobeRGB in this case). This example is the CutePDF with WCM applying the left side of each swatch (via ICM ON) and Aurigma applying the right side (via ICM OFF and applying the color transform). Which application of the printer's profile is correct? Or do I have to put in support to either have Aurigma apply the output profile, or WCM apply the output profile? Code:
//string inputFile = @"C:\temp\ColorTestChart_SRGB_900.tif";
string inputFile = @"C:\temp\ColorTestChart_ARGB_900.tif";
string inputProfile = @"C:\Windows\System32\spool\drivers\color\sRGB Color Space Profile.icm";
//https://www.inkowl.com/page/performance-d-icc-profiles/
string outputProfile = @"C:\Windows\System32\spool\drivers\color\Performance-D - 4 colors - Sublimation Transfer Paper - InkOwl Profile.icm";
void Main()
{
PrintImage();
}
private void PrintImage()
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler(this.pd_PrintPage);
pd.Print();
}
private void pd_PrintPage(object sender, PrintPageEventArgs ev)
{
ColorProfile printerSurfaceProfile;
ICM_MODE icmMode = ICM_MODE.ICM_QUERY;
try
{
IntPtr hdc = ev.Graphics.GetHdc();
//Get HDC color profile
printerSurfaceProfile = ColorProfile.FromHdc(hdc);
icmMode = (ICM_MODE)SetICMMode(hdc, (int)ICM_MODE.ICM_QUERY);
Debug.WriteLine($"ICM Mode of the printer device context {icmMode}");
}
finally
{
ev.Graphics.ReleaseHdc();
}
int leftMargin = 50;
int xPos = leftMargin;
int yPos = 0;
System.Drawing.Font f = new System.Drawing.Font("Ariel", 20, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel);
ev.Graphics.PageUnit = System.Drawing.GraphicsUnit.Pixel;
int textHeight = (int)ev.Graphics.MeasureString("Testing 123", f).Height + 5;
ev.Graphics.DrawString($"{((PrintDocument)sender).PrinterSettings.PrinterName} - Default ICM Mode: {icmMode} - Input Image: {inputFile}", f, System.Drawing.Brushes.Black, xPos, yPos);
yPos += textHeight;
int imageHeight = 10;
using (Bitmap input = new Bitmap(inputFile))
{
imageHeight = input.Height;
string profileName = input.ColorProfile != null ? input.ColorProfile.Description : "sRGB(NotSet)";
ev.Graphics.DrawString($"Org w/SP ({profileName}) -No PP", f, System.Drawing.Brushes.Black, xPos, yPos);
//draw original image without printer surface profile
input.DrawOn(ev.Graphics, xPos, yPos + textHeight, CombineMode.Alpha, 1);
xPos += input.Width + 20;
ConvertInternalImageToColorProfile(input, inputProfile, printerSurfaceProfile, ColorTransformationIntent.Perceptual);
ev.Graphics.DrawString($"Org w/SP ({profileName}) w/PP(Perceptual)", f, System.Drawing.Brushes.Black, xPos, yPos);
//draw original image with printer driver surface profile applied
input.DrawOn(ev.Graphics, xPos, yPos + textHeight, CombineMode.Alpha, 1);
xPos += input.Width + 20;
}
//New line
xPos = leftMargin;
yPos += imageHeight + textHeight + 10;
//swap ICM Mode
try
{
IntPtr hdc = ev.Graphics.GetHdc();
icmMode = (ICM_MODE)SetICMMode(hdc, icmMode == ICM_MODE.ICM_ON ? (int)ICM_MODE.ICM_OFF : (int)ICM_MODE.ICM_ON);
icmMode = (ICM_MODE)SetICMMode(hdc, (int)ICM_MODE.ICM_QUERY);
Debug.WriteLine($"ICM Mode of the printer device context, after switching is: {icmMode}");
}
finally
{
ev.Graphics.ReleaseHdc();
}
//Draw the images the same way, again, with the new mode
using (Bitmap input = new Bitmap(inputFile))
{
string profileName = input.ColorProfile != null ? input.ColorProfile.Description : "sRGB(NotSet)";
ev.Graphics.DrawString($"Org w/SP ({profileName}) -No PP -ICM Mode: {icmMode}", f, System.Drawing.Brushes.Black, xPos, yPos);
//draw original image without printer surface profile
input.DrawOn(ev.Graphics, xPos, yPos + textHeight, CombineMode.Alpha, 1);
xPos += input.Width + 20;
ConvertInternalImageToColorProfile(input, inputProfile, printerSurfaceProfile, ColorTransformationIntent.Perceptual);
ev.Graphics.DrawString($"Org w/SP ({profileName}) w/PP(Perceptual) -ICM Mode: {icmMode}", f, System.Drawing.Brushes.Black, xPos, yPos);
//draw original image with printer driver surface profile applied
input.DrawOn(ev.Graphics, xPos, yPos + textHeight, CombineMode.Alpha, 1);
xPos += input.Width + 20;
}
//New line
xPos = leftMargin;
yPos += imageHeight + textHeight + 10;
using (var gdiBmp = new System.Drawing.Bitmap(inputFile))
{
ev.Graphics.DrawString("Org GDI w/SP (ARGB) -No PP", f, System.Drawing.Brushes.Black, xPos, yPos);
//draw original image without printer surface profile
ev.Graphics.DrawImage(gdiBmp, xPos, yPos + textHeight);
xPos = leftMargin;
yPos += gdiBmp.Height + textHeight + 10;
}
}
private static void ConvertInternalImageToColorProfile(Aurigma.GraphicsMill.Bitmap imgToConvert, string sourceProfile, Aurigma.GraphicsMill.ColorProfile outputPF, ColorTransformationIntent renderIntent)
{
Aurigma.GraphicsMill.ColorProfile sourcePF = imgToConvert.ColorProfile;
if (sourcePF == null)
{
sourcePF = new ColorProfile(sourceProfile);
}
if (sourcePF.Description != outputPF.Description ||
sourcePF.Model != outputPF.Model ||
sourcePF.Manufacturer != outputPF.Manufacturer ||
sourcePF.ColorSpace != outputPF.ColorSpace)
{
imgToConvert.ColorManagement.ColorManagementEngine = ColorManagementEngine.LittleCms;
imgToConvert.ColorProfile = sourcePF;
imgToConvert.ColorManagement.TransformationIntent = renderIntent;
imgToConvert.ColorManagement.DestinationProfile = outputPF;
if (outputPF.ColorSpace == ColorSpace.Cmyk)
{
imgToConvert.ColorManagement.Convert(PixelFormat.Format32bppCmyk);
}
else if (outputPF.ColorSpace == ColorSpace.Rgb)
{
imgToConvert.ColorManagement.Convert(PixelFormat.Format24bppRgb);
}
}
}
enum ICM_MODE
{
ICM_OFF = 1,
ICM_ON = 2,
ICM_QUERY = 3,
ICM_DONE_OUTSIDEDC = 4
}
[DllImport("gdi32.dll", EntryPoint = "SetICMMode", SetLastError = true)]
static extern int SetICMMode([In] IntPtr hdc, int mode);
I have included the pdfs, sRGB version of the test image, and the linqPad script SamplePDF_Linq_ICM_Modes.zip (2,580kb) downloaded 5 time(s).
|