Adobe-Runtime-Support
Adobe-Runtime-Support copied to clipboard
[Windows] Non-embedded fonts displayed without anti-aliasing (smoothing)
Problem Description
Non-embedded fonts displayed without anti-aliasing (smoothing) for Windows devices. Embedded fonts looks better better. I tried multiple approaches - nothing helps:
-
TextRenderer.setAdvancedAntiAliasingTable()
withCSMSettings
-
TextRenderer.displayMode
andTextRenderer.maxLevel
- Rasterize (draw to
BitmapData
) -
cacheAsBitmapData
-
TextField::antiAliasType
-
TextLine
Tested with multiple AIR versions, even with latest AIR 50.2.5.1 with multiple Windows devices, with multiple applications, different fonts and OS versions. Same issue in all cases. Most noticeable with FullHD screens (or lower resolution) with screen scaling 100% and low text sizes (15-30). There is no such issue with macOS (tested with the same monitors with the same screen scaling). Didn't test with Linux and other platforms.
Related issues: https://github.com/airsdk/Adobe-Runtime-Support/issues/2744 https://github.com/airsdk/Adobe-Runtime-Support/issues/2201 https://github.com/airsdk/Adobe-Runtime-Support/issues/1957 https://github.com/airsdk/Adobe-Runtime-Support/issues/867 https://github.com/airsdk/Adobe-Runtime-Support/issues/150
Steps to Reproduce
Launch application with code below on any Windows device. Better to use devices with FullHD resolution screen (non-HiDPI) with 100% screen scaling. There is 12 rows with different approaches for comparison:
-
TextField
with embedded font. -
TextField
with embedded font,AntiAliasType.ADVANCED
andGridFitType.NONE
. -
TextField
with non-embedded font. -
TextField
rasterized toBitmapData
. -
TextField
rasterized toBitmapData
with x2width
/height
. -
TextField
with x2 text size rasterized toBitmapData
. -
TextField
with non-embedded font andcacheAsBitmapData
. -
StageText
. -
TextLine
with non-embedded font. -
TextLine
with non-embedded font andRenderingMode.CFF
. -
TextLine
with embedded font. -
TextField
rasterized toBitmapData
with x4width
/height
.
Application example with sources and comparison screenshot attached. windows_textfield_antialiasing_bug.zip
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.text.Font;
import flash.text.AntiAliasType;
import flash.text.GridFitType;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.PixelSnapping;
import flash.text.StageText;
import flash.geom.Rectangle;
import flash.text.engine.FontWeight;
import flash.text.engine.ElementFormat;
import flash.text.engine.FontDescription;
import flash.text.engine.FontPosture;
import flash.text.engine.FontLookup;
import flash.text.engine.RenderingMode;
import flash.text.engine.CFFHinting;
import flash.text.engine.TextElement;
import flash.text.engine.TextBlock;
import flash.text.engine.TextLine;
import flash.geom.Matrix;
public class WindowsTextFieldAntialiasingBug extends Sprite {
[Embed(source="Montserrat-Bold.ttf", fontFamily="MontserratBold", embedAsCFF="false")]
private var MyFont:Class;
[Embed(source="Montserrat-Bold.ttf", fontFamily="MontserratBoldCFF", embedAsCFF="true")]
private var MyFontCFF:Class;
private const TEXT:String = "Test Test";
private const FONT_NAME:String = "Montserrat";
private const EMBEDDED_FONT_NAME:String = "MontserratBold";
private const EMBEDDED_FONT_NAME_CFF:String = "MontserratBoldCFF";
private const TEXT_COLOR:uint = 0xffffff;
private const TEXT_SIZE:uint = 20;
public function WindowsTextFieldAntialiasingBug() {
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
//Row 1, TextField with embedded font
var embeddedBoldTextField:TextField = new TextField();
embeddedBoldTextField.x = 10;
embeddedBoldTextField.autoSize = TextFieldAutoSize.LEFT;
embeddedBoldTextField.embedFonts = true;
var embeddedBoldTextFormat:TextFormat = new TextFormat(EMBEDDED_FONT_NAME);
embeddedBoldTextFormat.size = TEXT_SIZE;
embeddedBoldTextFormat.bold = true;
embeddedBoldTextFormat.color = TEXT_COLOR;
embeddedBoldTextField.defaultTextFormat = embeddedBoldTextFormat;
embeddedBoldTextField.text = TEXT;
addChild(embeddedBoldTextField);
//Row 2, TextField with embedded font, advanced
var embeddedBoldAdvancedTextField:TextField = new TextField();
embeddedBoldAdvancedTextField.x = 10;
embeddedBoldAdvancedTextField.y = 50;
embeddedBoldAdvancedTextField.autoSize = TextFieldAutoSize.LEFT;
embeddedBoldAdvancedTextField.embedFonts = true;
var embeddedBoldAdvancedTextFormat:TextFormat = new TextFormat(EMBEDDED_FONT_NAME);
embeddedBoldAdvancedTextFormat.size = TEXT_SIZE;
embeddedBoldAdvancedTextFormat.bold = true;
embeddedBoldAdvancedTextFormat.color = TEXT_COLOR;
embeddedBoldAdvancedTextField.antiAliasType = AntiAliasType.ADVANCED;
embeddedBoldAdvancedTextField.gridFitType = GridFitType.NONE;
embeddedBoldAdvancedTextField.defaultTextFormat = embeddedBoldAdvancedTextFormat;
embeddedBoldAdvancedTextField.text = TEXT;
addChild(embeddedBoldAdvancedTextField);
//Row 3, TextField with non-embedded font
//This one looks ugly for Windows
var nonEmbeddedBoldTextField:TextField = new TextField();
nonEmbeddedBoldTextField.x = 10;
nonEmbeddedBoldTextField.y = 100;
nonEmbeddedBoldTextField.autoSize = TextFieldAutoSize.LEFT;
var nonEmbeddedBoldTextFormat:TextFormat = new TextFormat(FONT_NAME);
nonEmbeddedBoldTextFormat.size = TEXT_SIZE;
nonEmbeddedBoldTextFormat.bold = true;
nonEmbeddedBoldTextFormat.color = TEXT_COLOR;
nonEmbeddedBoldTextField.defaultTextFormat = nonEmbeddedBoldTextFormat;
nonEmbeddedBoldTextField.text = TEXT;
addChild(nonEmbeddedBoldTextField);
//Row 4, BitmapData
var nonEmbeddedBoldBitmapData:BitmapData = new BitmapData(nonEmbeddedBoldTextField.textWidth, nonEmbeddedBoldTextField.textHeight, true, 0x00000000);
nonEmbeddedBoldBitmapData.draw(nonEmbeddedBoldTextField, null, null, null, null, true);
var nonEmbeddedBoldBitmap:Bitmap = new Bitmap(nonEmbeddedBoldBitmapData, PixelSnapping.AUTO, true);
nonEmbeddedBoldBitmap.x = 10;
nonEmbeddedBoldBitmap.y = 150;
addChild(nonEmbeddedBoldBitmap);
//Row 5, BitmapData double BD
var nonEmbeddedBoldDoubleBitmapData:BitmapData = new BitmapData(nonEmbeddedBoldTextField.textWidth * 2, nonEmbeddedBoldTextField.textHeight * 2, true, 0x00000000);
var nonEmbeddedBoldDoubleMatrix:Matrix = new Matrix();
nonEmbeddedBoldDoubleMatrix.identity();
nonEmbeddedBoldDoubleMatrix.scale(2, 2);
nonEmbeddedBoldDoubleBitmapData.draw(nonEmbeddedBoldTextField, nonEmbeddedBoldDoubleMatrix, null, null, null, true);
var nonEmbeddedBoldDoubleBitmap:Bitmap = new Bitmap(nonEmbeddedBoldDoubleBitmapData, PixelSnapping.AUTO, true);
nonEmbeddedBoldDoubleBitmap.width = nonEmbeddedBoldTextField.textWidth;
nonEmbeddedBoldDoubleBitmap.height = nonEmbeddedBoldTextField.textHeight;
nonEmbeddedBoldDoubleBitmap.x = 10;
nonEmbeddedBoldDoubleBitmap.y = 200;
addChild(nonEmbeddedBoldDoubleBitmap);
//Row 6, BitmapData double size
var nonEmbeddedBoldDouble2BitmapData:BitmapData = new BitmapData(nonEmbeddedBoldTextField.textWidth, nonEmbeddedBoldTextField.textHeight, true, 0x00000000);
var tf:TextFormat = nonEmbeddedBoldTextField.defaultTextFormat;
tf.size = TEXT_SIZE * 2;
nonEmbeddedBoldTextField.defaultTextFormat = tf;
nonEmbeddedBoldTextField.setTextFormat(tf);
var nonEmbeddedBoldDouble2Matrix:Matrix = new Matrix();
nonEmbeddedBoldDouble2Matrix.identity();
nonEmbeddedBoldDouble2Matrix.scale(0.5, 0.5);
nonEmbeddedBoldDouble2BitmapData.draw(nonEmbeddedBoldTextField, nonEmbeddedBoldDouble2Matrix, null, null, null, true);
var nonEmbeddedBoldDouble2Bitmap:Bitmap = new Bitmap(nonEmbeddedBoldDouble2BitmapData, PixelSnapping.AUTO, true);
nonEmbeddedBoldDouble2Bitmap.x = 10;
nonEmbeddedBoldDouble2Bitmap.y = 250;
addChild(nonEmbeddedBoldDouble2Bitmap);
//reset TextFormat back
tf = nonEmbeddedBoldTextField.defaultTextFormat;
tf.size = TEXT_SIZE;
nonEmbeddedBoldTextField.defaultTextFormat = tf;
nonEmbeddedBoldTextField.setTextFormat(tf);
//Row 7, TextField with non-embedded font, cached
var nonEmbeddedBoldCacheTextField:TextField = new TextField();
nonEmbeddedBoldCacheTextField.x = 10;
nonEmbeddedBoldCacheTextField.y = 300;
nonEmbeddedBoldCacheTextField.autoSize = TextFieldAutoSize.LEFT;
nonEmbeddedBoldCacheTextField.cacheAsBitmap = true;
var nonEmbeddedBoldCacheTextFormat:TextFormat = new TextFormat(FONT_NAME);
nonEmbeddedBoldCacheTextFormat.size = TEXT_SIZE;
nonEmbeddedBoldCacheTextFormat.bold = true;
nonEmbeddedBoldCacheTextFormat.color = TEXT_COLOR;
nonEmbeddedBoldCacheTextField.defaultTextFormat = nonEmbeddedBoldCacheTextFormat;
nonEmbeddedBoldCacheTextField.text = TEXT;
addChild(nonEmbeddedBoldCacheTextField);
//Row 8, StageText
var stageTextBold:StageText = new StageText();
stageTextBold.fontSize = TEXT_SIZE;
stageTextBold.fontFamily = FONT_NAME;
stageTextBold.fontWeight = FontWeight.BOLD;
stageTextBold.color = TEXT_COLOR;
stageTextBold.text = TEXT;
stageTextBold.viewPort = new Rectangle(10, 350, 155, 50);
stageTextBold.stage = stage;
stageTextBold.visible = true;
//Row 9, TextLine
var boldFontDescription:FontDescription = new FontDescription(FONT_NAME, FontWeight.BOLD, FontPosture.NORMAL, FontLookup.DEVICE, RenderingMode.NORMAL, CFFHinting.HORIZONTAL_STEM);
var boldElementFormat:ElementFormat = new ElementFormat(boldFontDescription);
boldElementFormat.fontSize = TEXT_SIZE;
boldElementFormat.fontDescription = boldFontDescription;
boldElementFormat.color = TEXT_COLOR;
var boldTextElement:TextElement = new TextElement(TEXT, boldElementFormat);
var boldTextBlock:TextBlock = new TextBlock();
boldTextBlock.content = boldTextElement;
var boldTextLine:TextLine = boldTextBlock.createTextLine(null, 150);
boldTextLine.x = 10;
boldTextLine.y = 400 + boldTextLine.ascent + boldTextLine.descent;
addChild(boldTextLine);
//Column 10, TextLine CFF
var boldCFFFontDescription:FontDescription = new FontDescription(FONT_NAME, FontWeight.BOLD, FontPosture.NORMAL, FontLookup.DEVICE, RenderingMode.CFF, CFFHinting.HORIZONTAL_STEM);
var boldCFFElementFormat:ElementFormat = new ElementFormat(boldCFFFontDescription);
boldCFFElementFormat.fontSize = TEXT_SIZE;
boldCFFElementFormat.fontDescription = boldCFFFontDescription;
boldCFFElementFormat.color = TEXT_COLOR;
var boldCFFTextElement:TextElement = new TextElement(TEXT, boldCFFElementFormat);
var boldCFFTextBlock:TextBlock = new TextBlock();
boldCFFTextBlock.content = boldCFFTextElement;
var boldCFFTextLine:TextLine = boldCFFTextBlock.createTextLine(null, 150);
boldCFFTextLine.x = 10;
boldCFFTextLine.y = 450 + boldCFFTextLine.ascent + boldCFFTextLine.descent;
addChild(boldCFFTextLine);
//Column 11, TextLine CFF
var boldEmbeddedCFFFontDescription:FontDescription = new FontDescription(EMBEDDED_FONT_NAME_CFF, FontWeight.BOLD, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF, RenderingMode.CFF, CFFHinting.HORIZONTAL_STEM);
var boldEmbeddedCFFElementFormat:ElementFormat = new ElementFormat(boldEmbeddedCFFFontDescription);
boldEmbeddedCFFElementFormat.fontSize = TEXT_SIZE;
boldEmbeddedCFFElementFormat.fontDescription = boldEmbeddedCFFFontDescription;
boldEmbeddedCFFElementFormat.color = TEXT_COLOR;
var boldEmbeddedCFFTextElement:TextElement = new TextElement(TEXT, boldEmbeddedCFFElementFormat);
var boldEmbeddedCFFTextBlock:TextBlock = new TextBlock();
boldEmbeddedCFFTextBlock.content = boldEmbeddedCFFTextElement;
var boldEmbeddedCFFTextLine:TextLine = boldEmbeddedCFFTextBlock.createTextLine(null, 150);
boldEmbeddedCFFTextLine.x = 10;
boldEmbeddedCFFTextLine.y = 500 + boldEmbeddedCFFTextLine.ascent + boldEmbeddedCFFTextLine.descent;
addChild(boldEmbeddedCFFTextLine);
//Row 12, BitmapData x4
var nonEmbeddedBold4BitmapData:BitmapData = new BitmapData(nonEmbeddedBoldTextField.textWidth * 4, nonEmbeddedBoldTextField.textHeight * 4, true, 0x00000000);
var nonEmbeddedBold4Matrix:Matrix = new Matrix();
nonEmbeddedBold4Matrix.identity();
nonEmbeddedBold4Matrix.scale(4, 4);
nonEmbeddedBold4BitmapData.draw(nonEmbeddedBoldTextField, nonEmbeddedBold4Matrix, null, null, null, true);
var nonEmbeddedBold4Bitmap:Bitmap = new Bitmap(nonEmbeddedBold4BitmapData, PixelSnapping.AUTO, true);
nonEmbeddedBold4Bitmap.width = nonEmbeddedBoldTextField.textWidth;
nonEmbeddedBold4Bitmap.height = nonEmbeddedBoldTextField.textHeight;
nonEmbeddedBold4Bitmap.x = 10;
nonEmbeddedBold4Bitmap.y = 550;
addChild(nonEmbeddedBold4Bitmap);
}
}
}
Actual Result:
All variants with non-embedded fonts looks ugly, without anti-aliasing.
Here comparison screenshot. At the left - Windows at the right - macOS.
Most noticible at row 3:
-
Fine.
TextField
with embedded font. -
Best.
TextField
with embedded font,AntiAliasType.ADVANCED
andGridFitType.NONE
. -
Bad.
TextField
with non-embedded font. -
Bad.
TextField
rasterized toBitmapData
. -
Medium.
TextField
rasterized toBitmapData
with x2width
/height
. -
Bad.
TextField
with x2 text size rasterized toBitmapData
. -
Bad.
TextField
with non-embedded font andcacheAsBitmapData
. -
Bad.
StageText
. -
Bad.
TextLine
with non-embedded font. -
Bad.
TextLine
with non-embedded font andRenderingMode.CFF
. -
Best.
TextLine
with embedded font. -
Fine/Best.
TextField
rasterized toBitmapData
with x4width
/height
.
Expected Result: Non-embedded fonts displayed with anti-aliasing like with macOS.
Known Workarounds
Embed fonts if it possible or rasterize with x4 width
/height
but it consume x16 RAM.