DirectWrite + Direct2D custom text rendering is hairy - direct2d

I'm evaluating Direct2D for research purposes, and while I was at it, I decided to render my usual help text with a DirectWrite custom text renderer, which converts the text to a path geometry in order to add outline (as demonstrated in the DWriteHelloWorld sample on MSDN).
However, some letters have weird "hairs" or "horns" on them (picture: stroke width of 3 and 5).
Also tried with other fonts (f.e. Consolas), the effect is the same.
Source code (VS 2015):
https://www.dropbox.com/s/v3204h0ww2cp0yk/FOR_STACKOVERFLOW_12.zip?dl=0

The solution is as easy as I hoped. The "hairs" are actually caused by the line joints which D2D generates. Therefore the solution is to create an ID2D1StrokeStyle object as the following:
ID2D1StrokeStyle* strokestyle = nullptr;
D2D1_STROKE_STYLE_PROPERTIES strokeprops = D2D1::StrokeStyleProperties();
strokeprops.lineJoin = D2D1_LINE_JOIN_ROUND;
d2dfactory->CreateStrokeStyle(strokeprops, NULL, 0, &strokestyle);
// draw
rendertarget->DrawGeometry(transformedgeometry, blackbrush, 3.0f, strokestyle);
With this solution, the text is rendered as expected (perhaps a little more roundish at the joints).

I would suspect the reason for that is that default flattening tolerance in D2D does not work well for the purpose of rendering glyph outlines, at small enough sizes. Normally you'd use bitmap rendering for small sizes and outlines for larger ones, according to GetRecommendedRenderingMode(). Do you have same artifacts if you increase font size let's say 10 times?

Related

How to calculate size of Windows bitmap font using FreeType

The Problem
I am loading the classic serife.fon file from Microsoft Windows using FreeType.
Here is how I set the size:
FT_Set_Pixel_Sizes(face, 0, fontHeight);
I use 0 for the fontWidth so that it will be auto-calculated based on the height.
How do I find the correct value for fontHeight such that the resulting font will be exactly 9 pixels tall?
Notes
Using trial and error, I know that the correct value is 32 - but I don't understand why.
I am not sure how relevant this is for bitmap fonts, but according to the docs:
pixel_size = point_size * resolution / 72
Substituting in the values:
point_size = 32
resolution = 96 (from FT_Get_WinFNT_Header)
gives:
pixel_size = 42.6666666
This is a long way from our target height of 9!
The docs do go on to say:
pixel_size computed in the above formula does not directly relate to the size of characters on the screen. It simply is the size of the EM square if it was to be displayed. Each font designer is free to place its glyphs as it pleases him within the square.
But again, I am not sure if this is relevant for bitmap fonts.
fon files are exe files with a fnt payload, where the fnt payload can be a vector or raster font. If this is a raster font (which is most likely) then the dfPixHeight value in the fnt header will tell you what size it's meant to be, which is exposed by FreeType2 as the pixel_height field of the FT_WinFNT_Header.
(And of course, note that using any size other than "the actual raster-size of the FNT" is going to lead to hilarious headaches because bitmap scaling is the kind of madness that's so bad, OpenType instead went with "just embed as many bitmaps as you need, at however many sizes you need, because that's the only way your bitmaps are going to look good")
The FNT-specific FT2 documentation can be found over on https://www.freetype.org/freetype2/docs/reference/ft2-winfnt_fonts.html but you may need to read it in conjunction with https://jeffpar.github.io/kbarchive/kb/065/Q65123 (or https://web.archive.org/web/20120215123301/http://support.microsoft.com/kb/65123) to find any further mappings that you might need between names/fields as defined in the FNT spec and FT2's naming conventions.

How to optimize A-Frame html shader text to VR visualization?

Hello everyone so I've been trying to render a book page that is attached to your hand in VR (testing on oculus-go) using A-FRAME. Initially I tried using a plane and applying the text to it using the text attribute and then defining its value, alignment, font etc... Everything worked fine enough however the text gets "jagged edges" that seem to get worse the more you move your hand (which is basically impossible to not do) making it extremely bad for a long text-form such as a book page.
Then I tried exploring an alternative by using the aframe-html-shader by mayognaise. By creating an html div and using css it's the perfect solution in terms of customization, alignment and etc and when I render it, the text doesn't get any "jagged edges" anymore (since it's basically a texture).
However, it gets blurry enough that it becomes tiresome for long reads.
I've tried everything I could to increase its sharpness however it keeps being blurry which makes absolutely no sense to me.
What I've tried:
Increasing the size of the object the texture applies to and then scaling it back after the render - result: same thing...
Increasing the size of the canvas or the texture inside the aframe-html-shader.js - result: the same thing... however some of the tinkering attempts seem to trigger a "image too big (...) scaling down to 4000" (4000 something I don't recall the exact value) which seem to indicate the canvas is already being rendered at full resolution
Switching from Mayognaise aframe-html-shader.js to wildlifela fork (which already has an option of "scale" on the shader) and applying "canvasScale: 2" - result: same thing...
Using a 4000px width html element as the object to render from, increasing the font accordingly- result: same thing...
I'm out of ideas and really don't understand why I can't get good enough text out of the html shader since if the text is within an image and I use that same image as a texture, the text comes out perfectly readable.
Need some help from all the A-Frame experts and developers over here!
Thank you all in advance!
Increasing the size of the canvas or the texture inside the aframe-html-shader.js - result: the same thing... however some of the tinkering attempts seem to trigger a "image too big (...) scaling down to 4000" (4000 something I don't recall the exact value) which seem to indicate the canvas is already being rendered at full resolution
It wasn't too big. This is because textures needs to be a power of two size (e.g., 4096x4096).
The standard text component should be clearest though. A-Frame master branch has a fix to make text look clearer, might help. https://github.com/aframevr/aframe/commit/8d3f32b93633e82025b4061deb148059757a4a0f

Why might the scaling of SetWindowExtEx be just wrong?

I am trying to scale images/text etc using MM_ANISTROPIC and what I've done is the following (by the way if the syntax is a little weird, it's originally from delphi so treat the following as pseudocode)
I would expect the following code to produce a rectangle that is 70% of the width of the PaintBox and 30% of the height, yet it doesn't, instead it it noticeably too small.
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,100,100,0);
SetViewportExtEx(hdc,70,30,0);
Rectangle(hdc, 0,0,PaintBox.width-1,PaintBox.Height-1);
if, on the other hand I change the code so that the SetWindowExtEx has 91 instead of 100 as its parameters (as shown below) then it works, which makes no sense to me at all...
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,91,91,0);
SetViewportExtEx(hdc,70,30,0);
Rectangle(hdc, 0,0,PaintBox.width-1,PaintBox.Height-1);
My sanity test case was to add the following pseudocode
SetMapMode(hdc,MM_TEXT);
DrawLine(hdc,Round(PaintBox.width*0.7),0,Round(PaintBox.width*0.7),PaintBox.Height-1);
DrawLine(hdc,0,Round(PaintBox.height*0.3),PaintBox.width-1,Round(PaintBox.height*0.3));
I would have expected this to overwrite the lower / bottom edges of my original Rectangle but it does not unless I uses that 91,91 SetWindowExtEx.
Can anyone duplicate this?
FURTHER EDIT: Here is my exact original code I had given pseudo code before to make the question more accessible to non-delphi users but one of my commenters wanted full code to see if my contention that it was a delphi quirk was true or not.
The entire project consisted of a VCL form with a rectangular paintbox dropped on it centered so there was space all around it, and its onPaint event was set to the code below resulting in this image::
procedure TForm11.PaintBox2Paint(Sender: TObject);
var
hdc:THandle;
res:TPoint;
procedure SetupMapMode;
begin
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,100,100,0);
SetViewportExtEx(hdc,70,30,0);
// These lines are required when we're painting to a TPaintBox but can't be used
// if we're paiting to a TPanel and they were NOT in my original question but only
// got added as part of the answer
// SetViewportOrgEx(hdc,PaintBox2.Left,PaintBox2.Top,#ZeroPoint);
// SetWindowOrgEx(hdc,0,0,#ZeroPoint);
end;
begin
//draw a rectangle to frame the Paintbox Surface
PaintBox2.Canvas.Pen.Style:=psSolid;
PaintBox2.Canvas.Pen.width:=2;
PaintBox2.Canvas.Pen.Color:=clGreen;
PaintBox2.Canvas.Brush.Style:=bsClear;
PaintBox2.Canvas.Rectangle(0,0,PaintBox2.Width-1,PaintBox2.Height-1);
PaintBox2.Canvas.Brush.Style:=bsSolid;
//initialize convenience variable
hdc:=PaintBox2.Canvas.Handle;
SetTextAlign(hdc,TA_LEFT);
//as doing things to the PaintBox2.Canvas via Delphi's interface tends to reset
//everything, I'm ensuring the map mode gets set **immediately** before
//each drawing call
SetupMapMode;
/// Draw Text at the bottom of the PaintBox2.Canvas (though as it's mapped it
/// SHOULD be 1/3 of the way down and much smaller instead)
TextOut(hdc,200,PaintBox2.Height-PaintBox2.Canvas.TextHeight('Ap'),'Hello, World!',13);
PaintBox2.Canvas.Pen.Color:=clblue;
PaintBox2.Canvas.Brush.Style:=bsClear;
//ensure it's set before doing the rectangle
SetupMapMode;
// Redraw the same rectangle as before but in the mapped mode
Rectangle(hdc, 0,0,PaintBox2.Width-1,PaintBox2.Height-1);
PaintBox2.Canvas.Brush.Style:=bsSolid;
//reset the map mode to normal
SetMapMode(hdc,MM_Text);
//draw text at the "same" position as before but unmapped...
TextOut(hdc,200,PaintBox2.Height-PaintBox2.Canvas.TextHeight('Ap'),'Goodbye, World!',15);
//Draw lines exactly at 70% of the way across and 30% of the way down
//if this works as expected they should overwrite the right and bottom
//borders of the rectangle drawn in the mapped mode
PaintBox2.Canvas.Pen.Color:=RGB(0,255,255);
PaintBox2.Canvas.MoveTo(Round(PaintBox2.Width*0.7),0);
PaintBox2.Canvas.LineTo(Round(PaintBox2.Width*0.7),PaintBox2.Height);
PaintBox2.Canvas.MoveTo(0,Round(PaintBox2.Height*0.3));
PaintBox2.Canvas.LineTo(PaintBox2.Width,Round(PaintBox2.Height*0.3));
end;
Okay, I don't know WHY the following is necessary -- it may be a Delphi quirk, the fact that I'm using a TPaintBox with is a TGraphicControl rather than a Component, or if I'm missing out on some fundamental concept on how this whole mapping mode works, BUT if I add the following code:
ZeroPoint:=TPoint.Zero;
SetViewportOrgEx(hdc,PaintBox1.Left,PaintBox1.Top,#ZeroPoint);
SetWindowOrgEx(hdc,0,0,#ZeroPoint);
Then it all displays as expected. Anyone have any explanations as to why this is necessary?
EDIT: Okay, I've got a PARTIAL explanation. It has to do with the control I was painting on being a TPaintBox, which is a TGraphic control rather than a TWinControl. To wit:
TGraphicControl is the base class for all lightweight controls.
TGraphicControl supports simple lightweight controls that do not need the ability to accept keyboard input or contain other controls. Since lightweight controls do not wrap Windows screen objects, they are faster and user fewer resources than controls based on TWinControl.
As such, although they APPEAR to have a separate canvas, I have this sneaking feeling that they are really sharing the form's canvas which is why, when I switched to a TWinControl descendant, which DOES own its own Windows DC, then the display worked as expected without setting the ViewpointOrg.
So it was a Delphi quirk after all...!

WP7 XNA: How to change size or style of SpriteFont fonts dynamically in code?

There seems no way to change font size or style in code, right?? It seems the only way is to duplicate the font files and load them all when program starts??
Thanks
SpriteFonts convert a font, with style, size, and other parameters, to a pixel-based format for use as a texture within XNA. Those pixels are static, so yes, there is no way to change them, short of looping through per pixel.
However there is scaling (though it won't look so great scaling larger) to help with size adjustments needed, plus you could, like you said, create multiple SpriteFont files from the same base font for different styles, and dynamically choose one of those sprite font "textures" within your code.
Beyond that, for true fully dynamic runtime usage, you'd need to essentially create these sprite font textures on the fly, in memory. This means you'd have to do what the SpriteFont Content Pipeline project does but at runtime instead. This is possible in WinForms, but as far as I know not really an option for WP7 as you apparently are using.

Why do my Windows Forms strings look so ugly when anti-aliased?

I'm rendering some strings manually on top of a GraphicsBox, because you can't have a Label with a treansparent backdrop.
No matter which rendering mode I try though, I can't get the strings to look any good (ie. as they would appear in Word or in a graphics program.
Here's a picture of the interface mockup compared to what renders onscreen:
Unfortunately StackOverflow seems to shrink the picture so here's a direct link too: http://i.stack.imgur.com/vYFaF.png
And here's the code used to render:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics labelDrawing = e.Graphics;
labelDrawing.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
labelDrawing.DrawString("Setup Progress", new Font("Calibri", 10, FontStyle.Bold, GraphicsUnit.Point, 0), new SolidBrush(Color.Black), new Point(12, 9));
labelDrawing.DrawString("The following components are being configured.", new Font("Calibri", 10, FontStyle.Regular, GraphicsUnit.Point, 0), new SolidBrush(Color.Black), new Point(24, 27));
}
I've tried changing the TextRenderingHint to every option in turn, but no matter what I try if there's any antialiasing then it comes out in a blurry, smeared mess like in the screenshot. Any idea?
You can have transparent labels in .NET.
Check out this article on CodeProject on How to Use Transparent Images and Labels in Windows Forms
As for you drawing problem Calibri doesn't have a native font size of 10. You can verify this in Control Panel->Fonts. The smallest native font size is 12 (on my machine at least). Change you from size to 12 and you will see it's much better.
When you don't use native font sizes somewhere under the hood Windows/.NET/GDI+ will attempt to scale the font for you. This scaling is most likely causing your problem.

Resources