While I was waiting for a response I did my own research on how to fix the DPI issue with BMP files. The solution was two parts. 1) Don't use GdPicture to change the DPI for BMP images - no offense intended. 2) Modify the BMP header to reflect the actual DPI I needed. As it turns out the DPI (dots-per-inch) is really pixels-per-meter in the BMP header. For 72 DPI a setting of 2835 pixels-per-meter was needed. I have listed the C# code I used to resolve my issue. We still use GdPicture, but call the methods below to fix DPI on our incoming BMP files first.
Code: Select all
#region BMP Header:
/// <summary>
/// The default image resolution in pixels-per-meter. In the BMP header
/// the horizontal and vertical resolutions are expressed in pixels-per-
/// meter. The closest value to 72 DPI converting from meters to inches
/// is 72.009. 2835 pixels-per-meter / 39.3700787 meters-per-inch = 72.009.
/// </summary>
public const int DEFAULT_RESOLUTION_PIXELS_PER_METER = 2835; //-- DPM
/// <summary>
/// Dot-per-Inch or DPI. Dots = pixels. In the BMP header the horizontal
/// and vertical resolutions are expressed in pixels-per-meter. The
/// closest value to 72 DPI converting from meters to inches is 72.009.
/// 2835 pixels-per-meter / 39.3700787 meters-per-inch = 72.009.
/// </summary>
public const double DEFAULT_RESOLUTION_PIXELS_PER_INCH = 72.009; //-- DPI
/// <summary>
/// The number of inches in a meter.
/// </summary>
public const double INCHES_PER_METER = 39.3700787;
/// <summary>
/// BMP file offset where the horizontal resolution is located within the
/// BMP header. The value is four bytes long.
/// </summary>
public const int BMP_HEADER_HRES_OFFSET = 0x26;
/// <summary>
/// BMP file offset where the vertical resolution is located within the
/// BMP header. The value is four bytes long.
/// </summary>
public const int BMP_HEADER_VRES_OFFSET = 0x2A;
/// <summary>
/// Returns the horizontal and vertical resolutions for a given BMP file
/// via parameters.
/// </summary>
/// <param name="strBMPFile">string: The path to the BMP file to examine.</param>
/// <param name="intHorizontalDPM">int: (Output) The horizontal resolution of the BMP file in pixels-per-meter.</param>
/// <param name="intVerticalDPM">int: (Output) The vertical resolution of the BMP file in pixels-per-meter.</param>
/// <returns>bool: True if successful, and false otherwise.</returns>
public static bool GetBMPResolutionDPM(string strBMPFile, ref int intHorizontalDPM, ref int intVerticalDPM)
{
//-- Locals.
bool blnResult = false;
try
{
//-- Get the resolution in pixels-per-meter.
FileStream fsBMPFile = new FileStream(strBMPFile, FileMode.Open, FileAccess.Read);
byte[] lstBytes = { 0, 0, 0, 0 };
fsBMPFile.Seek(BMP_HEADER_HRES_OFFSET, SeekOrigin.Begin);
fsBMPFile.Read(lstBytes, 0, 4);
intHorizontalDPM = BitConverter.ToInt32(lstBytes, 0);
fsBMPFile.Seek(BMP_HEADER_VRES_OFFSET, SeekOrigin.Begin);
fsBMPFile.Read(lstBytes, 0, 4);
intVerticalDPM = BitConverter.ToInt32(lstBytes, 0);
//-- Close the BMP file.
fsBMPFile.Close();
blnResult = true;
}
catch (Exception ex)
{
cUtil.WriteException(ex, "cUtil.GetBMPResolution");
blnResult = false;
}
return blnResult;
}
/// <summary>
/// Sets the horizontal and vertical resolutions for a given BMP file.
/// </summary>
/// <param name="strBMPFile">string: The path to the BMP file to update.</param>
/// <param name="intHorizontalDPM">int: The horizontal resolution of the BMP file in pixels-per-meter.</param>
/// <param name="intVerticalDPM">int: The vertical resolution of the BMP file in pixels-per-meter.</param>
/// <returns>bool: True if successful, and false otherwise.</returns>
public static bool SetBMPResolutionDPM(string strBMPFile, int intHorizontalDPM, int intVerticalDPM)
{
//-- Locals.
bool blnResult = false;
try
{
//-- Get the resolution in pixels-per-meter.
FileStream fsBMPFile = new FileStream(strBMPFile, FileMode.Open, FileAccess.ReadWrite);
byte[] lstBytes = BitConverter.GetBytes(intHorizontalDPM);
fsBMPFile.Seek(BMP_HEADER_HRES_OFFSET, SeekOrigin.Begin);
fsBMPFile.Write(lstBytes, 0, 4);
lstBytes = BitConverter.GetBytes(intVerticalDPM);
fsBMPFile.Seek(BMP_HEADER_VRES_OFFSET, SeekOrigin.Begin);
fsBMPFile.Write(lstBytes, 0, 4);
//-- Close the BMP file.
fsBMPFile.Close();
blnResult = true;
}
catch (Exception ex)
{
cUtil.WriteException(ex, "cUtil.SetBMPResolution");
blnResult = false;
}
return blnResult;
}
/// <summary>
/// This method corrects a BMP file horizontal and vertical resolution
/// to the values specified if needed.
/// </summary>
/// <param name="strBMPFile">string: The path to the BMP file to fix if needed.</param>
/// <param name="intHorizontalDPM">int: The horizontal resolution of the BMP file in pixels-per-meter.</param>
/// <param name="intVerticalDPM">int: The vertical resolution of the BMP file in pixels-per-meter.</param>
/// <returns>bool: True if successful, and false otherwise.</returns>
public static bool FixBMPResolutionDPM(string strBMPFile, int intHorizontalDPM, int intVerticalDPM)
{
//-- Locals.
bool blnResult = false;
try
{
//-- Get the resolution in pixels-per-meter.
int intHResDPM = 0;
int intVResDPM = 0;
blnResult = GetBMPResolutionDPM(strBMPFile, ref intHResDPM, ref intVResDPM);
if (blnResult)
{
//-- Make sure the resolution matches what is expected.
if (intHResDPM != intHorizontalDPM || intVResDPM != intVerticalDPM)
{
//-- Fix it.
blnResult = SetBMPResolutionDPM(strBMPFile, intHorizontalDPM, intVerticalDPM);
if (blnResult == false)
{
cUtil.WriteAsException("cUtil.FixBMPResolutionDPM: Failed to set the horizontal and vertical resolution for '" + strBMPFile + "' via a call to SetBMPResolutionDPM.");
blnResult = false;
}
}
else
{
//-- All is well.
blnResult = true;
}
}
else
{
//-- Log an alert, because this should not happen.
cUtil.WriteAsException("cUtil.FixBMPResolutionDPM: Failed to obtain the horizontal and vertical resolution for '" + strBMPFile + "' via a call to GetBMPResolutionDPM.");
blnResult = false;
}
}
catch (Exception ex)
{
cUtil.WriteException(ex, "cUtil.GetBMPResolution");
blnResult = false;
}
return blnResult;
}
#endregion