libgdiplus
libgdiplus copied to clipboard
System.Drawing - Font is not scaled on Bitmap with scaled Resolution
On the Windows platform the Font size (with GraphicsUnit.Point) will increase proportionally depending on the resolution of the Bitmap. but not on the Linux platform. sample code:
const string outputFile = "output";
const int max = 5;
const int originalBitmapSize = 128;
for(int i = 1; i < max + 1; i++) {
float resolution = 96 * i * 0.75f;
int size = (int)(originalBitmapSize * resolution / 96);
using(var bitmap = new Bitmap(size, size))
using(var font = new Font("Arial", 20f, FontStyle.Regular, GraphicsUnit.Point)) {
bitmap.SetResolution(resolution, resolution);
using(var graphics = Graphics.FromImage(bitmap)) {
graphics.FillRectangle(Brushes.LightBlue, 0, 0, bitmap.Width, bitmap.Height);
graphics.DrawString("test", font, Brushes.Black, 0, 0);
bitmap.Save(outputFile + i + ".png", ImageFormat.Png);
}
}
}
the output "test" string on the Linux platforms will not be enlarged.
(copied from https://github.com/dotnet/corefx/issues/34479 )
Why didn't you reply?
It seems like this problem is in libgdiplus.DrawString
that is not getting the right resolution as in System.Drawing we're passing down the same resolution and size in points that in Windows.
People can workaround this issue with: https://github.com/dotnet/runtime/issues/28361#issuecomment-654390769
@tarekgh suggested that this would be fixed by something like, changing: https://github.com/mono/libgdiplus/blob/aff49ac7b568b5e12b244ebb304bc068fecf51d1/src/text-cairo.c#L234
To:
if (graphics->type == gtPostScript)
{
FontSize = font->emSize;
}
else if (font->unit == UnitPoint && graphics->type == gtMemoryBitmap && graphics->dpi_x != 96) // These conditions exist only to scope the fix
{
FontSize = font->sizeInPixels * (graphics->dpi_x / gdip_get_display_dpi());
}
else
{
FontSize = font->sizeInPixels;
}
The workaround would be to make all font methrics to use the DPI of the graphics object. gdip_get_display_dpi() should be called only as a fallback when nothing else works. The problem is that cairo calculates glyph metrics from this DPI, and rounds the result to whole pixels. pango ends up using the same approach as cairo is it's backend.
Windows implementation on the other hand doesn't round anything, so when font metrics are recalculated in the DPI of the graphics object, everything is perfect. Also, Windows implementation renders the fonts themselves with float pixel coordinates, and not an integer ones, as cairo, so there's no need of rounding at all. This fundamental difference is the root of many-many problems, and as long as cairo (with pango or not) is used as backend for libgdiplus, it will never be resolved to a full extent. The only thing that can be done is to make font metrics DPI aware. This way, when a user wants a greater convergence to windows implementation, he/she can just set a higher resolution of graphics object and all the methrics should be calculated according to this resolution.
For the moment, at least for pango implementation of MeasureString, this is easily achievable by scaling both the font size and layout dimensions by (graphics object dpi / gdip_get_display_dpi()) and dividing the result by the same coefficient. It is ugly, but it works. I am in a process of investigating a better approach.