From fb0e2aa8fdefb30d15372a16ddb0ecb919efd28e Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Mon, 28 Jan 2019 00:17:56 +0000 Subject: [PATCH] Revert "remove legacy code for text attributes on paint" This reverts commit 80f4adf98fefbb3665e09186affefbb26e9b8fa1. Reason for revert: breaking PaintTest Original change's description: > remove legacy code for text attributes on paint > > Bug: skia:2664 > Change-Id: I09f9d1401410d7ca338d7acde5a9660921c74da9 > Reviewed-on: https://skia-review.googlesource.com/c/185460 > Commit-Queue: Mike Reed > Reviewed-by: Florin Malita TBR=bungeman@google.com,rmistry@google.com,herb@google.com,fmalita@chromium.org,reed@google.com Change-Id: If9bbb92d6eb064aba30cfa9069051ff60585a269 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: skia:2664 Reviewed-on: https://skia-review.googlesource.com/c/187260 Reviewed-by: Mike Reed Commit-Queue: Mike Reed --- docs/SkPaint_Reference.bmh | 603 ++++++++++++++++++++++++++++++++- gn/tests.gni | 1 + include/core/SkFont.h | 12 + include/core/SkPaint.h | 478 ++++++++++++++++++++++++-- src/core/SkFont.cpp | 68 ++++ src/core/SkPaint.cpp | 141 +++++++- src/core/SkPaintPriv.h | 5 +- src/core/SkPaint_text.cpp | 211 ++++++++++++ src/core/SkPictureFlat.h | 12 +- src/core/SkPicturePlayback.cpp | 166 +++++++++ src/utils/SkLua.cpp | 7 +- tests/FontObjTest.cpp | 93 +++++ tests/PaintTest.cpp | 55 ++- 13 files changed, 1801 insertions(+), 51 deletions(-) create mode 100644 tests/FontObjTest.cpp diff --git a/docs/SkPaint_Reference.bmh b/docs/SkPaint_Reference.bmh index 93f3faf6a4..96b263ba67 100644 --- a/docs/SkPaint_Reference.bmh +++ b/docs/SkPaint_Reference.bmh @@ -49,18 +49,31 @@ Constructs Paint with default values. # Dither # false ## # Draw_Looper # nullptr ## # Filter_Quality # kNone_SkFilterQuality ## +# Font_Force_Hinting # false ## +# Font_Embedded_Bitmaps # false ## +# Font_Embolden # false ## +# Font_Hinting # SkFontHinting::kNormal ## +# Font_Hinting_Spacing # false ## +# Font_Anti_Alias # false ## +# Font_Linear # false ## +# Font_Scale_X # 1 ## +# Font_Size # 12 ## +# Font_Skew_X # 0 ## +# Font_Subpixel # false ## # Image_Filter # nullptr ## # Miter_Limit # 4 ## # Mask_Filter # nullptr ## # Path_Effect # nullptr ## # Shader # nullptr ## # Style # kFill_Style ## +# Text_Encoding # kUTF8_SkTextEncoding ## +# Typeface # nullptr ## # Stroke_Cap # kButt_Cap ## # Stroke_Join # kMiter_Join ## # Stroke_Width # 0 ## #Table ## -The miter limit may be overridden at compile time by defining +The flags, text size, hinting, and miter limit may be overridden at compile time by defining paint default values. The overrides may be included in "SkUserConfig.h" or predefined by the build system. @@ -270,6 +283,132 @@ $$$# # restore original markup character ## +# ------------------------------------------------------------------------------ + +#Subtopic Hinting +#Line # glyph outline adjustment ## +## + +#Method void setHinting(SkFontHinting hintingLevel) +#In Hinting +#Line # sets Hinting, glyph outline adjustment level ## +#Populate + + #Example + SkPaint paint1, paint2; + paint2.setHinting(SkFontHinting::kNormal); + SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : ':'); + + #StdOut + paint1 == paint2 + ## + ## +## + +#Method SkFontHinting getHinting() const +#In Hinting +#Line # returns Hinting, glyph outline adjustment level ## +#Populate + + #Example + SkPaint paint; + SkDebugf("SkFontHinting::kNormal %c= paint.getHinting()\n", + SkFontHinting::kNormal == paint.getHinting() ? '=' : ':'); + + #StdOut + SkFontHinting::kNormal == paint.getHinting() + ## + ## +## + +# ------------------------------------------------------------------------------ +#Subtopic Flags +#Line # attributes represented by single bits ## +## + +#Enum Flags +#Line # values described by bits and masks ## + +#Code +#Populate +## + +The bit values stored in Flags. +The default value for Flags, normally zero, can be changed at compile time +with a custom definition of SkPaintDefaults_Flags. +All flags can be read and written explicitly; Flags allows manipulating +multiple settings at once. + + #Const kAntiAlias_Flag 0x0001 + #Line # mask for setting Anti_Alias ## + ## + #Const kDither_Flag 0x0004 + #Line # mask for setting Dither ## + ## + #Const kFakeBoldText_Flag 0x0020 + #Line # mask for setting Font_Embolden ## + ## + #Const kLinearText_Flag 0x0040 + #Line # mask for setting Font_Linear ## + ## + #Const kSubpixelText_Flag 0x0080 + #Line # mask for setting Font_Subpixel ## + ## + #Const kLCDRenderText_Flag 0x0200 + #Line # mask for setting Font_Anti_Alias ## + ## + #Const kEmbeddedBitmapText_Flag 0x0400 + #Line # mask for setting Font_Embedded_Bitmaps ## + ## + #Const kAutoHinting_Flag 0x0800 + #Line # mask for setting Font_Force_Hinting ## + ## + #Const kAllFlags 0xFFFF + #Line # mask of all Flags ## + mask of all Flags, including private flags and flags reserved for future use + ## + +Flags default to all flags clear, disabling the associated feature. + +#Enum ## + +#Method uint32_t getFlags() const +#In Flags +#Line # returns Flags stored in a bit field ## +#Populate + +#Example + SkPaint paint; + paint.setAntiAlias(true); + SkDebugf("(SkPaint::kAntiAlias_Flag & paint.getFlags()) %c= 0\n", + SkPaint::kAntiAlias_Flag & paint.getFlags() ? '!' : '='); + + #StdOut + (SkPaint::kAntiAlias_Flag & paint.getFlags()) != 0 + ## +## + +## + +#Method void setFlags(uint32_t flags) +#In Flags +#Line # sets multiple Flags in a bit field ## +#Populate + +#Example + SkPaint paint; + paint.setFlags((uint32_t) (SkPaint::kAntiAlias_Flag | SkPaint::kDither_Flag)); + SkDebugf("paint.isAntiAlias()\n", paint.isAntiAlias() ? '!' : '='); + SkDebugf("paint.isDither()\n", paint.isDither() ? '!' : '='); + + #StdOut + paint.isAntiAlias() + paint.isDither() + ## +## + +## + # ------------------------------------------------------------------------------ #Subtopic Anti_Alias #Alias Anti_Alias @@ -552,6 +691,64 @@ If Font_Linear is set, it has the same effect as setting Hinting to SkFontHintin If Font_Linear is clear, it is the same as setting Hinting to SkFontHinting::kNone. #Subtopic Linear_Text ## +#Method bool isLinearText() const +#In Linear_Text +#Line # returns true if text is converted to Path ## +#Populate + +#Example + #Height 128 + void draw(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + const char testStr[] = "xxxx xxxx"; + for (auto linearText : { false, true } ) { + paint.setLinearText(linearText); + paint.setTextSize(24); + canvas->drawString(paint.isLinearText() ? "linear" : "hinted", 128, 30, paint); + for (SkScalar textSize = 8; textSize < 30; textSize *= 1.22f) { + paint.setTextSize(textSize); + canvas->translate(0, textSize); + canvas->drawString(testStr, 10, 0, paint); + } + } + } + ## + + #SeeAlso setLinearText Hinting +## + +#Method void setLinearText(bool linearText) +#In Linear_Text +#Line # converts to Path before draw or measure ## +#Populate + +#Example + #Height 128 + void draw(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + const char testStr[] = "abcd efgh"; + size_t count = 9; + for (int textSize : { 12, 24 } ) { + SkFont font(nullptr, textSize); + for (auto linearMetrics : { false, true } ) { + font.setLinearMetrics(linearMetrics); + SkString width; + width.appendScalar(font.measureText(testStr, count, SkTextEncoding::kUTF8)); + canvas->translate(0, textSize + 4); + canvas->drawSimpleText(testStr, count, SkTextEncoding::kUTF8, + 10, 0, font, paint); + canvas->drawSimpleText(width.c_str(), width.size(), SkTextEncoding::kUTF8, + 128, 0, font, paint); + } + } + } + ## + + #SeeAlso isLinearText Hinting +## + #Subtopic Subpixel_Text #Line # uses pixel transparency to represent fractional offset ## @@ -560,6 +757,45 @@ As the opaqueness of the color increases, the edge of the glyph appears to move towards the outside of the pixel. #Subtopic Subpixel_Text ## +#Method bool isSubpixelText() const +#In Subpixel_Text +#Line # returns true if Font_Subpixel is set ## +#Populate + +#Example +SkPaint paint; +SkDebugf("paint.isSubpixelText() %c= !!(paint.getFlags() & SkPaint::kSubpixelText_Flag)\n", + paint.isSubpixelText() == !!(paint.getFlags() & SkPaint::kSubpixelText_Flag) ? '=' : '!'); +paint.setSubpixelText(true); +SkDebugf("paint.isSubpixelText() %c= !!(paint.getFlags() & SkPaint::kSubpixelText_Flag)\n", + paint.isSubpixelText() == !!(paint.getFlags() & SkPaint::kSubpixelText_Flag) ? '=' : '!'); + + #StdOut + paint.isSubpixelText() == !!(paint.getFlags() & SkPaint::kSubpixelText_Flag) + paint.isSubpixelText() == !!(paint.getFlags() & SkPaint::kSubpixelText_Flag) + ## + ## + +## + +#Method void setSubpixelText(bool subpixelText) +#In Subpixel_Text +#Line # sets or clears Font_Subpixel ## +#Populate + +#Example + SkPaint paint1, paint2; + paint1.setSubpixelText(true); + paint2.setFlags(paint2.getFlags() | SkPaint::kSubpixelText_Flag); + SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); + + #StdOut + paint1 == paint2 + ## + ## + +## + #Subtopic LCD_Text #Line # text relying on the order of RGB stripes ## @@ -570,6 +806,46 @@ Font_Anti_Alias can be enabled on devices that orient stripes horizontally or ve the color components as RGB or BGR. #Subtopic LCD_Text ## +#Method bool isLCDRenderText() const +#In LCD_Text +#Line # returns true if Font_Anti_Alias is set ## +#Populate + +#Example +SkPaint paint; +SkDebugf("paint.isLCDRenderText() %c= !!(paint.getFlags() & SkPaint::kLCDRenderText_Flag)\n", + paint.isLCDRenderText() == !!(paint.getFlags() & SkPaint::kLCDRenderText_Flag) ? '=' : '!'); +paint.setLCDRenderText(true); +SkDebugf("paint.isLCDRenderText() %c= !!(paint.getFlags() & SkPaint::kLCDRenderText_Flag)\n", + paint.isLCDRenderText() == !!(paint.getFlags() & SkPaint::kLCDRenderText_Flag) ? '=' : '!'); + + #StdOut + paint.isLCDRenderText() == !!(paint.getFlags() & SkPaint::kLCDRenderText_Flag) + paint.isLCDRenderText() == !!(paint.getFlags() & SkPaint::kLCDRenderText_Flag) + ## + ## + +## + +#Method void setLCDRenderText(bool lcdText) +#In LCD_Text +#Line # sets or clears Font_Anti_Alias ## +#Populate + +#Example + SkPaint paint1, paint2; + paint1.setLCDRenderText(true); + paint2.setFlags(paint2.getFlags() | SkPaint::kLCDRenderText_Flag); + SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); + + #StdOut + paint1 == paint2 + ## + ## + + +## + # ------------------------------------------------------------------------------ #Subtopic Embedded_Bitmaps #Line # custom sized bitmap Glyphs ## @@ -617,6 +893,49 @@ kEmbeddedBitmapText_Flag at compile time. ## #Subtopic Embedded_Bitmaps ## +#Method bool isEmbeddedBitmapText() const +#In Embedded_Bitmaps +#Line # returns true if Font_Embedded_Bitmaps is set ## +#Populate + +#Example + SkPaint paint; + SkDebugf("paint.isEmbeddedBitmapText() %c=" + " !!(paint.getFlags() & SkPaint::kEmbeddedBitmapText_Flag)\n", + paint.isEmbeddedBitmapText() == + !!(paint.getFlags() & SkPaint::kEmbeddedBitmapText_Flag) ? '=' : '!'); + paint.setEmbeddedBitmapText(true); + SkDebugf("paint.isEmbeddedBitmapText() %c=" + " !!(paint.getFlags() & SkPaint::kEmbeddedBitmapText_Flag)\n", + paint.isEmbeddedBitmapText() == + !!(paint.getFlags() & SkPaint::kEmbeddedBitmapText_Flag) ? '=' : '!'); + + #StdOut + paint.isEmbeddedBitmapText() == !!(paint.getFlags() & SkPaint::kEmbeddedBitmapText_Flag) + paint.isEmbeddedBitmapText() == !!(paint.getFlags() & SkPaint::kEmbeddedBitmapText_Flag) + ## + ## + +## + +#Method void setEmbeddedBitmapText(bool useEmbeddedBitmapText) +#In Embedded_Bitmaps +#Line # sets or clears Font_Embedded_Bitmaps ## +#Populate + +#Example + SkPaint paint1, paint2; + paint1.setEmbeddedBitmapText(true); + paint2.setFlags(paint2.getFlags() | SkPaint::kEmbeddedBitmapText_Flag); + SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); + + #StdOut + paint1 == paint2 + ## + ## + +## + # ------------------------------------------------------------------------------ #Subtopic Automatic_Hinting #Line # always adjust glyph paths ## @@ -629,6 +948,57 @@ SkFontHinting::kSlight. Font_Force_Hinting only affects platforms that use FreeType as the Font_Manager. #Subtopic Automatic_Hinting ## +#Method bool isAutohinted() const +#In Automatic_Hinting +#Line # returns true if Glyphs are always hinted ## +#Populate + +#Example + SkPaint paint; + for (auto forceAutoHinting : { false, true} ) { + paint.setAutohinted(forceAutoHinting); + SkDebugf("paint.isAutohinted() %c=" + " !!(paint.getFlags() & SkPaint::kAutoHinting_Flag)\n", + paint.isAutohinted() == + !!(paint.getFlags() & SkPaint::kAutoHinting_Flag) ? '=' : '!'); + } + #StdOut + paint.isAutohinted() == !!(paint.getFlags() & SkPaint::kAutoHinting_Flag) + paint.isAutohinted() == !!(paint.getFlags() & SkPaint::kAutoHinting_Flag) + ## + ## + + #SeeAlso setAutohinted Hinting + +## + +#Method void setAutohinted(bool useAutohinter) +#In Automatic_Hinting +#Line # sets Glyphs to always be hinted ## +#Populate + +#Example + void draw(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + const char testStr[] = "xxxx xxxx"; + for (auto forceAutoHinting : { false, true} ) { + paint.setAutohinted(forceAutoHinting); + paint.setTextSize(24); + canvas->drawString(paint.isAutohinted() ? "auto-hinted" : "default", 108, 30, paint); + for (SkScalar textSize = 8; textSize < 30; textSize *= 1.22f) { + paint.setTextSize(textSize); + canvas->translate(0, textSize); + canvas->drawString(testStr, 10, 0, paint); + } + } + } + ## + + #SeeAlso isAutohinted Hinting + +## + # ------------------------------------------------------------------------------ #Subtopic Fake_Bold @@ -665,6 +1035,45 @@ void draw(SkCanvas* canvas) { ## #Subtopic Fake_Bold ## +#Method bool isFakeBoldText() const +#In Fake_Bold +#Line # returns true if Font_Embolden is set ## +#Populate + +#Example + SkPaint paint; + SkDebugf("paint.isFakeBoldText() %c= !!(paint.getFlags() & SkPaint::kFakeBoldText_Flag)\n", + paint.isFakeBoldText() == !!(paint.getFlags() & SkPaint::kFakeBoldText_Flag) ? '=' : '!'); + paint.setFakeBoldText(true); + SkDebugf("paint.isFakeBoldText() %c= !!(paint.getFlags() & SkPaint::kFakeBoldText_Flag)\n", + paint.isFakeBoldText() == !!(paint.getFlags() & SkPaint::kFakeBoldText_Flag) ? '=' : '!'); + + #StdOut + paint.isFakeBoldText() == !!(paint.getFlags() & SkPaint::kFakeBoldText_Flag) + paint.isFakeBoldText() == !!(paint.getFlags() & SkPaint::kFakeBoldText_Flag) + ## + ## + +## + +#Method void setFakeBoldText(bool fakeBoldText) +#In Fake_Bold +#Line # sets or clears Font_Embolden ## +#Populate + +#Example + SkPaint paint1, paint2; + paint1.setFakeBoldText(true); + paint2.setFlags(paint2.getFlags() | SkPaint::kFakeBoldText_Flag); + SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); + + #StdOut + paint1 == paint2 + ## + ## + +## + # ------------------------------------------------------------------------------ #Subtopic Filter_Quality_Methods #Line # get and set Filter_Quality ## @@ -2135,6 +2544,74 @@ implementation. } ## +#Method SkTypeface* getTypeface() const + +#In Typeface_Methods +#Line # returns Typeface, font description ## +#Populate + +#Example + void draw(SkCanvas* canvas) { + SkPaint paint; + SkDebugf("nullptr %c= typeface\n", paint.getTypeface() ? '!' : '='); + paint.setTypeface(SkTypeface::MakeFromName("monospace", SkFontStyle())); + SkDebugf("nullptr %c= typeface\n", paint.getTypeface() ? '!' : '='); + } + + #StdOut + nullptr == typeface + nullptr != typeface + ## + ## + +## + +#Method sk_sp refTypeface() const + +#In Typeface_Methods +#Line # references Typeface, font description ## +#Populate + +#Example + void draw(SkCanvas* canvas) { + SkPaint paint1, paint2; + paint1.setTypeface(SkTypeface::MakeFromName("monospace", + SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width, + SkFontStyle::kItalic_Slant))); + SkDebugf("typeface1 %c= typeface2\n", + paint1.getTypeface() == paint2.getTypeface() ? '=' : '!'); + paint2.setTypeface(paint1.refTypeface()); + SkDebugf("typeface1 %c= typeface2\n", + paint1.getTypeface() == paint2.getTypeface() ? '=' : '!'); + } + + #StdOut + typeface1 != typeface2 + typeface1 == typeface2 + ## + ## + +## + +#Method void setTypeface(sk_sp typeface) + +#In Typeface_Methods +#Line # sets Typeface, font description ## +#Populate + +#Example + #Height 64 + void draw(SkCanvas* canvas) { + SkPaint paint; + paint.setTypeface(SkTypeface::MakeFromName("monospace", SkFontStyle())); + canvas->drawString("hamburgerfons", 10, 30, paint); + paint.setTypeface(nullptr); + canvas->drawString("hamburgerfons", 10, 50, paint); + } + ## + +## + #Subtopic Typeface_Methods ## # ------------------------------------------------------------------------------ #Subtopic Image_Filter_Methods @@ -2369,6 +2846,34 @@ Set SkPaintDefaults_TextSize at compile time to change the default setting. } ## +#Method SkScalar getTextSize() const + +#In Text_Size +#Line # returns text size in points ## +#Populate + +#Example + SkPaint paint; + SkDebugf("12 %c= default text size\n", 12 == paint.getTextSize() ? '=' : '!'); + ## + +## + +#Method void setTextSize(SkScalar textSize) + +#In Text_Size +#Line # sets text size in points ## +#Populate + +#Example + SkPaint paint; + SkDebugf("12 %c= text size\n", 12 == paint.getTextSize() ? '=' : '!'); + paint.setTextSize(-20); + SkDebugf("12 %c= text size\n", 12 == paint.getTextSize() ? '=' : '!'); + ## + +## + #Subtopic Text_Size ## # ------------------------------------------------------------------------------ #Subtopic Text_Scale_X @@ -2395,6 +2900,34 @@ Text_Scale_X defaults to 1. } ## +#Method SkScalar getTextScaleX() const + +#In Text_Scale_X +#Line # returns the text horizontal scale; condensed text ## +#Populate + +#Example + SkPaint paint; + SkDebugf("1 %c= default text scale x\n", 1 == paint.getTextScaleX() ? '=' : '!'); + ## + +## + + +#Method void setTextScaleX(SkScalar scaleX) + +#In Text_Scale_X +#Line # sets the text horizontal scale; condensed text ## +#Populate + +#Example + SkPaint paint; + paint.setTextScaleX(0.f / 0.f); + SkDebugf("text scale %s-a-number\n", SkScalarIsNaN(paint.getTextScaleX()) ? "not" : "is"); + ## + +## + #Subtopic Text_Scale_X ## #Subtopic Text_Skew_X @@ -2422,6 +2955,33 @@ Text_Skew_X defaults to 0. } ## +#Method SkScalar getTextSkewX() const + +#In Text_Skew_X +#Line # returns the text horizontal skew; oblique text ## +#Populate + +#Example + SkPaint paint; + SkDebugf("0 %c= default text skew x\n", 0 == paint.getTextSkewX() ? '=' : '!'); + ## + +## + +#Method void setTextSkewX(SkScalar skewX) + +#In Text_Skew_X +#Line # sets the text horizontal skew; oblique text ## +#Populate + +#Example + SkPaint paint; + paint.setTextScaleX(1.f / 0.f); + SkDebugf("text scale %s-finite\n", SkScalarIsFinite(paint.getTextScaleX()) ? "is" : "not"); + ## + +## + #Subtopic Text_Skew_X ## # ------------------------------------------------------------------------------ @@ -2456,6 +3016,47 @@ void draw(SkCanvas* canvas) { } ## +#Method SkTextEncoding getTextEncoding() const + +#In Text_Encoding +#Line # returns character or glyph encoded size ## +#Populate + +#Example + SkPaint paint; + SkDebugf("kUTF8_SkTextEncoding %c= text encoding\n", + kUTF8_SkTextEncoding == paint.getTextEncoding() ? '=' : '!'); + paint.setTextEncoding(kGlyphID_SkTextEncoding); + SkDebugf("kGlyphID_SkTextEncoding %c= text encoding\n", + kGlyphID_SkTextEncoding == paint.getTextEncoding() ? '=' : '!'); + + #StdOut + kUTF8_SkTextEncoding == text encoding + kGlyphID_SkTextEncoding == text encoding + ## + ## + +## + + +#Method void setTextEncoding(SkTextEncoding encoding) + +#In Text_Encoding +#Line # sets character or glyph encoded size ## +#Populate + +#Example + SkPaint paint; + paint.setTextEncoding((SkTextEncoding) 4); + SkDebugf("4 %c= text encoding\n", (SkTextEncoding) 4 == paint.getTextEncoding() ? '=' : '!'); + + #StdOut + 4 != text encoding + ## + ## + +## + #Subtopic Text_Encoding ## # ------------------------------------------------------------------------------ diff --git a/gn/tests.gni b/gn/tests.gni index 31bbb30992..1965251ff8 100644 --- a/gn/tests.gni +++ b/gn/tests.gni @@ -80,6 +80,7 @@ tests_sources = [ "$_tests/FontMgrFontConfigTest.cpp", "$_tests/FontMgrTest.cpp", "$_tests/FontNamesTest.cpp", + "$_tests/FontObjTest.cpp", "$_tests/FrontBufferedStreamTest.cpp", "$_tests/GeometryTest.cpp", "$_tests/GifTest.cpp", diff --git a/include/core/SkFont.h b/include/core/SkFont.h index cada4b5bdf..61f7463f0a 100644 --- a/include/core/SkFont.h +++ b/include/core/SkFont.h @@ -514,6 +514,18 @@ public: */ SkScalar getSpacing() const { return this->getMetrics(nullptr); } +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + /** Deprecated. + */ + void LEGACY_applyToPaint(SkPaint* paint) const; + /** Deprecated. + */ + void LEGACY_applyPaintFlags(uint32_t paintFlags); + /** Deprecated. + */ + static SkFont LEGACY_ExtractFromPaint(const SkPaint& paint); +#endif + /** Experimental. * Dumps fields of the font to SkDebugf. May change its output over time, so clients should * not rely on this for anything specific. Used to aid in debugging. diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 02ef629827..057f145b1c 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -22,36 +22,62 @@ #include "SkBlendMode.h" #include "SkColor.h" #include "SkFilterQuality.h" +#include "SkFontMetrics.h" +#include "SkFontTypes.h" #include "SkMatrix.h" #include "SkRefCnt.h" -// TODO: remove after updating android sites to IWYU -#include "SkFontMetrics.h" - class GrTextBlob; +class SkAutoDescriptor; class SkColorFilter; class SkColorSpace; class SkData; +class SkDescriptor; class SkDrawLooper; +class SkGlyph; +class SkGlyphRunBuilder; +class SkGlyphRun; +class SkGlyphRunListPainter; struct SkRect; +class SkStrike; class SkImageFilter; class SkMaskFilter; class SkPath; class SkPathEffect; struct SkPoint; +class SkFont; class SkShader; class SkSurfaceProps; class SkTextBlob; +class SkTextBlobRunIterator; +class SkTypeface; /** \class SkPaint - SkPaint controls options applied when drawing. SkPaint collects all + SkPaint controls options applied when drawing and measuring. SkPaint collects all options outside of the SkCanvas clip and SkCanvas matrix. - Various options apply to strokes and fills, and images. + Various options apply to text, strokes and fills, and images. + + Some options may not be implemented on all platforms; in these cases, setting + the option has no effect. Some options are conveniences that duplicate SkCanvas + functionality; for instance, text size is identical to matrix scale. + + SkPaint options are rarely exclusive; each option modifies a stage of the drawing + pipeline and multiple pipeline stages may be affected by a single SkPaint. SkPaint collects effects and filters that describe single-pass and multiple-pass algorithms that alter the drawing geometry, color, and transparency. For instance, SkPaint does not directly implement dashing or blur, but contains the objects that do so. + + The objects contained by SkPaint are opaque, and cannot be edited outside of the SkPaint + to affect it. The implementation is free to defer computations associated with the + SkPaint, or ignore them altogether. For instance, some GPU implementations draw all + SkPath geometries with anti-aliasing, regardless of how SkPaint::kAntiAlias_Flag + is set in SkPaint. + + SkPaint describes a single color, a single font, a single image quality, and so on. + Multiple colors are drawn either by using multiple paints or with objects like + SkShader attached to SkPaint. */ class SK_API SkPaint { public: @@ -62,7 +88,7 @@ public: */ SkPaint(); - /** Makes a shallow copy of SkPaint. SkPathEffect, SkShader, + /** Makes a shallow copy of SkPaint. SkTypeface, SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, SkDrawLooper, and SkImageFilter are shared between the original paint and the copy. Objects containing SkRefCnt increment their references by one. @@ -86,13 +112,13 @@ public: */ SkPaint(SkPaint&& paint); - /** Decreases SkPaint SkRefCnt of owned objects: SkPathEffect, SkShader, + /** Decreases SkPaint SkRefCnt of owned objects: SkTypeface, SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, SkDrawLooper, and SkImageFilter. If the objects containing SkRefCnt go to zero, they are deleted. */ ~SkPaint(); - /** Makes a shallow copy of SkPaint. SkPathEffect, SkShader, + /** Makes a shallow copy of SkPaint. SkTypeface, SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, SkDrawLooper, and SkImageFilter are shared between the original paint and the copy. Objects containing SkRefCnt in the prior destination are decreased by one, and the referenced objects are deleted if the @@ -117,7 +143,7 @@ public: SkPaint& operator=(SkPaint&& paint); /** Compares a and b, and returns true if a and b are equivalent. May return false - if SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, + if SkTypeface, SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, SkDrawLooper, or SkImageFilter have identical contents but different pointers. @param a SkPaint to compare @@ -127,7 +153,7 @@ public: SK_API friend bool operator==(const SkPaint& a, const SkPaint& b); /** Compares a and b, and returns true if a and b are not equivalent. May return true - if SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, + if SkTypeface, SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, SkDrawLooper, or SkImageFilter have identical contents but different pointers. @param a SkPaint to compare @@ -157,30 +183,231 @@ public: */ void reset(); + /** Sets level of glyph outline adjustment. + Does not check for valid values of hintingLevel. + + @param hintingLevel one of: SkFontHinting::kNone, SkFontHinting::kSlight, + SkFontHinting::kNormal, SkFontHinting::kFull + */ + void setHinting(SkFontHinting hintingLevel); + + /** Returns level of glyph outline adjustment. + + @return one of: SkFontHinting::kNone, SkFontHinting::kSlight, SkFontHinting::kNormal, + SkFontHinting::kFull + */ + SkFontHinting getHinting() const { return (SkFontHinting)fBitfields.fHinting; } + + /** \enum SkPaint::Flags + The bit values stored in Flags. + The default value for Flags, normally zero, can be changed at compile time + with a custom definition of SkPaintDefaults_Flags. + All flags can be read and written explicitly; Flags allows manipulating + multiple settings at once. + */ + enum Flags { + kAntiAlias_Flag = 0x01, //!< mask for setting anti-alias + kDither_Flag = 0x04, //!< mask for setting dither + kFakeBoldText_Flag = 0x20, //!< mask for setting fake bold + kLinearText_Flag = 0x40, //!< mask for setting linear text + kSubpixelText_Flag = 0x80, //!< mask for setting subpixel text + kLCDRenderText_Flag = 0x200, //!< mask for setting LCD text + kEmbeddedBitmapText_Flag = 0x400, //!< mask for setting font embedded bitmaps + kAutoHinting_Flag = 0x800, //!< mask for setting force hinting + // 0x1000 used to be kVertical + kAllFlags = 0xFFFF, //!< mask of all Flags + }; + + #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + /** Private. + */ + enum ReserveFlags { + kUnderlineText_ReserveFlag = 0x08, //!< to be deprecated soon + kStrikeThruText_ReserveFlag = 0x10, //!< to be deprecated soon + }; + #endif + +#ifdef SK_SUPPORT_LEGACY_PAINT_FLAGS + /** Returns paint settings described by SkPaint::Flags. Each setting uses one + bit, and can be tested with SkPaint::Flags members. + + @return zero, one, or more bits described by SkPaint::Flags + */ + uint32_t getFlags() const { return fBitfields.fFlags; } + + /** Replaces SkPaint::Flags with flags, the union of the SkPaint::Flags members. + All SkPaint::Flags members may be cleared, or one or more may be set. + + @param flags union of SkPaint::Flags for SkPaint + */ + void setFlags(uint32_t flags); +#endif + /** Returns true if pixels on the active edges of SkPath may be drawn with partial transparency. - @return antialiasing state + + Equivalent to getFlags() masked with kAntiAlias_Flag. + + @return kAntiAlias_Flag state */ bool isAntiAlias() const { - return SkToBool(fBitfields.fAntiAlias); + return SkToBool(this->internal_getFlags() & kAntiAlias_Flag); } /** Requests, but does not require, that edge pixels draw opaque or with partial transparency. - @param aa setting for antialiasing + + Sets kAntiAlias_Flag if aa is true. + Clears kAntiAlias_Flag if aa is false. + + @param aa setting for kAntiAlias_Flag */ - void setAntiAlias(bool aa) { fBitfields.fAntiAlias = static_cast(aa); } + void setAntiAlias(bool aa); /** Returns true if color error may be distributed to smooth color transition. - @return dithering state + + Equivalent to getFlags() masked with kDither_Flag. + + @return kDither_Flag state */ bool isDither() const { - return SkToBool(fBitfields.fDither); + return SkToBool(this->internal_getFlags() & kDither_Flag); } /** Requests, but does not require, to distribute color error. - @param dither setting for ditering + + Sets kDither_Flag if dither is true. + Clears kDither_Flag if dither is false. + + @param dither setting for kDither_Flag */ - void setDither(bool dither) { fBitfields.fDither = static_cast(dither); } + void setDither(bool dither); + +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + /** Returns true if text is converted to SkPath before drawing and measuring. + + Equivalent to getFlags() masked with kLinearText_Flag. + + @return kLinearText_Flag state + */ + bool isLinearText() const { + return SkToBool(this->internal_getFlags() & kLinearText_Flag); + } + + /** Requests, but does not require, that glyphs are converted to SkPath + before drawing and measuring. + By default, kLinearText_Flag is clear. + + Sets kLinearText_Flag if linearText is true. + Clears kLinearText_Flag if linearText is false. + + @param linearText setting for kLinearText_Flag + */ + void setLinearText(bool linearText); + + /** Returns true if glyphs at different sub-pixel positions may differ on pixel edge coverage. + + Equivalent to getFlags() masked with kSubpixelText_Flag. + + @return kSubpixelText_Flag state + */ + bool isSubpixelText() const { + return SkToBool(this->internal_getFlags() & kSubpixelText_Flag); + } + + /** Requests, but does not require, that glyphs respect sub-pixel positioning. + + Sets kSubpixelText_Flag if subpixelText is true. + Clears kSubpixelText_Flag if subpixelText is false. + + @param subpixelText setting for kSubpixelText_Flag + */ + void setSubpixelText(bool subpixelText); + + /** Returns true if glyphs may use LCD striping to improve glyph edges. + + Returns true if SkPaint::Flags kLCDRenderText_Flag is set. + + @return kLCDRenderText_Flag state + */ + bool isLCDRenderText() const { + return SkToBool(this->internal_getFlags() & kLCDRenderText_Flag); + } + + /** Requests, but does not require, that glyphs use LCD striping for glyph edges. + + Sets kLCDRenderText_Flag if lcdText is true. + Clears kLCDRenderText_Flag if lcdText is false. + + @param lcdText setting for kLCDRenderText_Flag + */ + void setLCDRenderText(bool lcdText); + + /** Returns true if font engine may return glyphs from font bitmaps instead of from outlines. + + Equivalent to getFlags() masked with kEmbeddedBitmapText_Flag. + + @return kEmbeddedBitmapText_Flag state + */ + bool isEmbeddedBitmapText() const { + return SkToBool(this->internal_getFlags() & kEmbeddedBitmapText_Flag); + } + + /** Requests, but does not require, to use bitmaps in fonts instead of outlines. + + Sets kEmbeddedBitmapText_Flag if useEmbeddedBitmapText is true. + Clears kEmbeddedBitmapText_Flag if useEmbeddedBitmapText is false. + + @param useEmbeddedBitmapText setting for kEmbeddedBitmapText_Flag + */ + void setEmbeddedBitmapText(bool useEmbeddedBitmapText); + + /** Returns true if SkPaint::Hinting is set to SkFontHinting::kNormal or + SkFontHinting::kFull, and if platform uses FreeType as the font manager. + If true, instructs the font manager to always hint glyphs. + + Equivalent to getFlags() masked with kAutoHinting_Flag. + + @return kAutoHinting_Flag state + */ + bool isAutohinted() const { + return SkToBool(this->internal_getFlags() & kAutoHinting_Flag); + } + + /** Sets whether to always hint glyphs. + If SkPaint::Hinting is set to SkFontHinting::kNormal or SkFontHinting::kFull + and useAutohinter is set, instructs the font manager to always hint glyphs. + useAutohinter has no effect if SkPaint::Hinting is set to SkFontHinting::kNone or + SkFontHinting::kSlight. + + Only affects platforms that use FreeType as the font manager. + + Sets kAutoHinting_Flag if useAutohinter is true. + Clears kAutoHinting_Flag if useAutohinter is false. + + @param useAutohinter setting for kAutoHinting_Flag + */ + void setAutohinted(bool useAutohinter); + + /** Returns true if approximate bold by increasing the stroke width when creating glyph bitmaps + from outlines. + + Equivalent to getFlags() masked with kFakeBoldText_Flag. + + @return kFakeBoldText_Flag state + */ + bool isFakeBoldText() const { + return SkToBool(this->internal_getFlags() & kFakeBoldText_Flag); + } + + /** Increases stroke width when creating glyph bitmaps to approximate a bold typeface. + + Sets kFakeBoldText_Flag if fakeBoldText is true. + Clears kFakeBoldText_Flag if fakeBoldText is false. + + @param fakeBoldText setting for kFakeBoldText_Flag + */ + void setFakeBoldText(bool fakeBoldText); +#endif /** Returns SkFilterQuality, the image filtering level. A lower setting draws faster; a higher setting looks better when the image is scaled. @@ -463,20 +690,20 @@ public: @return mode used to combine source color with destination color */ - SkBlendMode getBlendMode() const { return (SkBlendMode)fBitfields.fBlendMode; } + SkBlendMode getBlendMode() const { return (SkBlendMode)fBlendMode; } /** Returns true if SkBlendMode is SkBlendMode::kSrcOver, the default. @return true if SkBlendMode is SkBlendMode::kSrcOver */ - bool isSrcOver() const { return (SkBlendMode)fBitfields.fBlendMode == SkBlendMode::kSrcOver; } + bool isSrcOver() const { return (SkBlendMode)fBlendMode == SkBlendMode::kSrcOver; } /** Sets SkBlendMode to mode. Does not check for valid input. @param mode SkBlendMode used to combine source color and destination */ - void setBlendMode(SkBlendMode mode) { fBitfields.fBlendMode = (unsigned)mode; } + void setBlendMode(SkBlendMode mode) { fBlendMode = (unsigned)mode; } /** Returns SkPathEffect if set, or nullptr. Does not alter SkPathEffect SkRefCnt. @@ -526,6 +753,33 @@ public: */ void setMaskFilter(sk_sp maskFilter); +#ifndef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS +private: +#endif + /** Returns SkTypeface if set, or nullptr. + Does not alter SkTypeface SkRefCnt. + + @return SkTypeface if previously set, nullptr otherwise + */ + SkTypeface* getTypeface() const { return fTypeface.get(); } + + /** Increases SkTypeface SkRefCnt by one. + + @return SkTypeface if previously set, nullptr otherwise + */ + sk_sp refTypeface() const; + + /** Sets SkTypeface to typeface, decreasing SkRefCnt of the previous SkTypeface. + Pass nullptr to clear SkTypeface and use the default typeface. Increments + typeface SkRefCnt by one. + + @param typeface font and style used to draw text + */ + void setTypeface(sk_sp typeface); +#ifndef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS +public: +#endif + /** Returns SkImageFilter if set, or nullptr. Does not alter SkImageFilter SkRefCnt. @@ -584,11 +838,130 @@ public: */ void setLooper(sk_sp drawLooper); +#ifndef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS +private: +#endif + /** Returns text size in points. + + @return typographic height of text + */ + SkScalar getTextSize() const { return fTextSize; } + + /** Sets text size in points. + Has no effect if textSize is not greater than or equal to zero. + + @param textSize typographic height of text + */ + void setTextSize(SkScalar textSize); + + /** Returns text scale on x-axis. + Default value is 1. + + @return text horizontal scale + */ + SkScalar getTextScaleX() const { return fTextScaleX; } + + /** Sets text scale on x-axis. + Default value is 1. + + @param scaleX text horizontal scale + */ + void setTextScaleX(SkScalar scaleX); + + /** Returns text skew on x-axis. + Default value is zero. + + @return additional shear on x-axis relative to y-axis + */ + SkScalar getTextSkewX() const { return fTextSkewX; } + + /** Sets text skew on x-axis. + Default value is zero. + + @param skewX additional shear on x-axis relative to y-axis + */ + void setTextSkewX(SkScalar skewX); + +#ifdef SK_SUPPORT_LEGACY_PAINTTEXTENCODING + /** + * Returns the text encoding. Text encoding describes how to interpret the text bytes pass + * to methods like SkFont::measureText() and SkCanvas::drawText(). + * @return the text encoding + */ + SkTextEncoding getTextEncoding() const { + return (SkTextEncoding)fBitfields.fTextEncoding; + } + + /** + * Sets the text encoding. Text encoding describes how to interpret the text bytes pass + * to methods like SkFont::measureText() and SkCanvas::drawText(). + * @param encoding the new text encoding + */ + void setTextEncoding(SkTextEncoding encoding); +#endif +#ifndef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS +public: +#endif + +#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTMEASURE + + /** Deprecated; use SkFont::getMetrics instead + */ + SkScalar getFontMetrics(SkFontMetrics* metrics) const; + + /** Deprecated; use SkFont::getSpacing instead + */ + SkScalar getFontSpacing() const { return this->getFontMetrics(nullptr); } + + /** Deprecated; use SkFont::textToGlyphs instead + */ + int textToGlyphs(const void* text, size_t byteLength, + SkGlyphID glyphs[]) const; + + /** Deprecated; use SkFont::containsText instead + */ + bool containsText(const void* text, size_t byteLength) const; +#endif + +#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTMEASURE + /** Deprecated; use SkFont::countText instead + */ + int countText(const void* text, size_t byteLength) const; + + /** Deprecated; use SkFont::measureText instead + */ + SkScalar measureText(const void* text, size_t length, SkRect* bounds) const; + + /** Deprecated; use SkFont::measureText instead + */ + SkScalar measureText(const void* text, size_t length) const { + return this->measureText(text, length, nullptr); + } + + /** Deprecated; use SkFont::getWidthsBounds instead + */ + int getTextWidths(const void* text, size_t byteLength, SkScalar widths[], + SkRect bounds[] = nullptr) const; + + /** Deprecated; use SkFont::getPath instead + */ + void getTextPath(const void* text, size_t length, SkScalar x, SkScalar y, + SkPath* path) const; + + /** Deprecated; use SkFont::getPath instead + */ + void getPosTextPath(const void* text, size_t length, + const SkPoint pos[], SkPath* path) const; +#endif + /** DEPRECATED -- call method on SkTextBlob Returns the number of intervals that intersect bounds. bounds describes a pair of lines parallel to the text advance. The return count is zero or a multiple of two, and is at most twice the number of glyphs in the string. + Uses SkTypeface to get the glyph paths, + and text size, fake bold, and SkPathEffect to scale and modify the glyph paths. + Uses run array to position intervals. SkTextEncoding must be set to kGlyphID_SkTextEncoding. @@ -690,6 +1063,7 @@ public: Style style) const; private: + sk_sp fTypeface; sk_sp fPathEffect; sk_sp fShader; sk_sp fMaskFilter; @@ -697,22 +1071,44 @@ private: sk_sp fDrawLooper; sk_sp fImageFilter; + SkScalar fTextSize; + SkScalar fTextScaleX; + SkScalar fTextSkewX; SkColor4f fColor4f; SkScalar fWidth; SkScalar fMiterLimit; + uint32_t fBlendMode; // just need 5-6 bits union { struct { - unsigned fAntiAlias : 1; - unsigned fDither : 1; - unsigned fCapType : 2; - unsigned fJoinType : 2; - unsigned fStyle : 2; - unsigned fFilterQuality : 2; - unsigned fBlendMode : 8; // only need 5-6? + // all of these bitfields should add up to 32 + unsigned fFlags : 16; + unsigned fCapType : 2; + unsigned fJoinType : 2; + unsigned fStyle : 2; + unsigned fTextEncoding : 2; // 3 values + unsigned fHinting : 2; + unsigned fFilterQuality : 2; + //unsigned fFreeBits : 4; } fBitfields; uint32_t fBitfieldsUInt; }; + uint32_t internal_getFlags() const { return fBitfields.fFlags; } + + void internal_setFlags(uint32_t flags) { + fBitfields.fFlags = flags; + } + + SkTextEncoding private_internal_getTextEncoding() const { + return (SkTextEncoding)fBitfields.fTextEncoding; + } + void private_internal_setTextEncoding(SkTextEncoding e) { + fBitfields.fTextEncoding = (unsigned)e; + } + + SkScalar measure_text(SkStrike*, const char* text, size_t length, + int* count, SkRect* bounds) const; + /* * The luminance color is used to determine which Gamma Canonical color to map to. This is * really only used by backends which want to cache glyph masks, and need some way to know if @@ -720,6 +1116,31 @@ private: */ SkColor computeLuminanceColor() const; + /* This is the size we use when we ask for a glyph's path. We then + * post-transform it as we draw to match the request. + * This is done to try to re-use cache entries for the path. + * + * This value is somewhat arbitrary. In theory, it could be 1, since + * we store paths as floats. However, we get the path from the font + * scaler, and it may represent its paths as fixed-point (or 26.6), + * so we shouldn't ask for something too big (might overflow 16.16) + * or too small (underflow 26.6). + * + * This value could track kMaxSizeForGlyphCache, assuming the above + * constraints, but since we ask for unhinted paths, the two values + * need not match per-se. + */ + static constexpr int kCanonicalTextSizeForPaths = 64; + + static bool TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM, SkScalar maxLimit); + + // Set flags/hinting/textSize up to use for drawing text as paths. + // Returns scale factor to restore the original textSize, since will will + // have change it to kCanonicalTextSizeForPaths. + SkScalar setupForAsPaths(); + + static SkScalar MaxCacheSize2(SkScalar maxLimit); + friend class GrTextBlob; friend class GrTextContext; friend class GrGLPathRendering; @@ -728,6 +1149,7 @@ private: friend class SkCanonicalizePaint; friend class SkCanvas; friend class SkDraw; + friend class SkFont; friend class SkGlyphRunListPainter; friend class SkPaintPriv; friend class SkPicturePlayback; diff --git a/src/core/SkFont.cpp b/src/core/SkFont.cpp index 68c0867c98..ec1a405082 100644 --- a/src/core/SkFont.cpp +++ b/src/core/SkFont.cpp @@ -494,6 +494,74 @@ sk_sp SkFont::refTypefaceOrDefault() const { return fTypeface ? fTypeface : SkTypeface::MakeDefault(); } +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPaint.h" + +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS +void SkFont::LEGACY_applyToPaint(SkPaint* paint) const { + paint->setTypeface(fTypeface); + paint->setTextSize(fSize); + paint->setTextScaleX(fScaleX); + paint->setTextSkewX(fSkewX); + + paint->setEmbeddedBitmapText(SkToBool(fFlags & kEmbeddedBitmaps_PrivFlag)); + paint->setFakeBoldText(SkToBool(fFlags & kEmbolden_PrivFlag)); + paint->setAutohinted(SkToBool(fFlags & kForceAutoHinting_PrivFlag)); + paint->setSubpixelText(SkToBool(fFlags & kSubpixel_PrivFlag)); + paint->setLinearText(SkToBool(fFlags & kLinearMetrics_PrivFlag)); + + bool doAA = false, + doLCD = false; + switch (this->getEdging()) { + case Edging::kAlias: break; + case Edging::kAntiAlias: doAA = true; break; + case Edging::kSubpixelAntiAlias: doAA = true; doLCD = true; break; + } + paint->setAntiAlias(doAA); + paint->setLCDRenderText(doLCD); + + paint->setHinting((SkFontHinting)this->getHinting()); +} + +SkFont SkFont::LEGACY_ExtractFromPaint(const SkPaint& paint) { + SkFont font(sk_ref_sp(paint.getTypeface()), paint.getTextSize(), paint.getTextScaleX(), + paint.getTextSkewX()); + font.LEGACY_applyPaintFlags(paint.getFlags()); + font.setHinting((SkFontHinting)paint.getHinting()); + return font; +} + +void SkFont::LEGACY_applyPaintFlags(uint32_t paintFlags) { + uint32_t flags = 0; + if (paintFlags & SkPaint::kEmbeddedBitmapText_Flag) { + flags |= kEmbeddedBitmaps_PrivFlag; + } + if (paintFlags & SkPaint::kFakeBoldText_Flag) { + flags |= kEmbolden_PrivFlag; + } + if (paintFlags & SkPaint::kAutoHinting_Flag) { + flags |= kForceAutoHinting_PrivFlag; + } + if (paintFlags & SkPaint::kSubpixelText_Flag) { + flags |= kSubpixel_PrivFlag; + } + if (paintFlags & SkPaint::kLinearText_Flag) { + flags |= kLinearMetrics_PrivFlag; + } + fFlags = flags; + + Edging edging = Edging::kAlias; + if (paintFlags & SkPaint::kAntiAlias_Flag) { + edging = Edging::kAntiAlias; + if (paintFlags & SkPaint::kLCDRenderText_Flag) { + edging = Edging::kSubpixelAntiAlias; + } + } + this->setEdging(edging); +} +#endif + ////////////////////////////////////////////////////////////////////////////////////////////////// int SkFontPriv::ValidCountText(const void* text, size_t length, SkTextEncoding encoding) { diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index e2c22f4449..ad05a2ef5c 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -35,49 +35,69 @@ #include "SkTypeface.h" #include "SkWriteBuffer.h" +static inline uint32_t set_clear_mask(uint32_t bits, bool cond, uint32_t mask) { + return cond ? bits | mask : bits & ~mask; +} + // define this to get a printf for out-of-range parameter in setters // e.g. setTextSize(-1) //#define SK_REPORT_API_RANGE_CHECK SkPaint::SkPaint() { + fTextSize = SkPaintDefaults_TextSize; + fTextScaleX = SK_Scalar1; + fTextSkewX = 0; fColor4f = { 0, 0, 0, 1 }; // opaque black fWidth = 0; fMiterLimit = SkPaintDefaults_MiterLimit; + fBlendMode = (unsigned)SkBlendMode::kSrcOver; // Zero all bitfields, then set some non-zero defaults. fBitfieldsUInt = 0; + fBitfields.fFlags = SkPaintDefaults_Flags; fBitfields.fCapType = kDefault_Cap; fBitfields.fJoinType = kDefault_Join; fBitfields.fStyle = kFill_Style; - fBitfields.fBlendMode = (unsigned)SkBlendMode::kSrcOver; + fBitfields.fTextEncoding = static_cast(kUTF8_SkTextEncoding); + fBitfields.fHinting = static_cast(SkPaintDefaults_Hinting); } SkPaint::SkPaint(const SkPaint& src) #define COPY(field) field(src.field) - : COPY(fPathEffect) + : COPY(fTypeface) + , COPY(fPathEffect) , COPY(fShader) , COPY(fMaskFilter) , COPY(fColorFilter) , COPY(fDrawLooper) , COPY(fImageFilter) + , COPY(fTextSize) + , COPY(fTextScaleX) + , COPY(fTextSkewX) , COPY(fColor4f) , COPY(fWidth) , COPY(fMiterLimit) + , COPY(fBlendMode) , COPY(fBitfields) #undef COPY {} SkPaint::SkPaint(SkPaint&& src) { #define MOVE(field) field = std::move(src.field) + MOVE(fTypeface); MOVE(fPathEffect); MOVE(fShader); MOVE(fMaskFilter); MOVE(fColorFilter); MOVE(fDrawLooper); MOVE(fImageFilter); + MOVE(fTextSize); + MOVE(fTextScaleX); + MOVE(fTextSkewX); MOVE(fColor4f); MOVE(fWidth); MOVE(fMiterLimit); + MOVE(fBlendMode); MOVE(fBitfields); #undef MOVE } @@ -90,15 +110,20 @@ SkPaint& SkPaint::operator=(const SkPaint& src) { } #define ASSIGN(field) field = src.field + ASSIGN(fTypeface); ASSIGN(fPathEffect); ASSIGN(fShader); ASSIGN(fMaskFilter); ASSIGN(fColorFilter); ASSIGN(fDrawLooper); ASSIGN(fImageFilter); + ASSIGN(fTextSize); + ASSIGN(fTextScaleX); + ASSIGN(fTextSkewX); ASSIGN(fColor4f); ASSIGN(fWidth); ASSIGN(fMiterLimit); + ASSIGN(fBlendMode); ASSIGN(fBitfields); #undef ASSIGN @@ -111,15 +136,20 @@ SkPaint& SkPaint::operator=(SkPaint&& src) { } #define MOVE(field) field = std::move(src.field) + MOVE(fTypeface); MOVE(fPathEffect); MOVE(fShader); MOVE(fMaskFilter); MOVE(fColorFilter); MOVE(fDrawLooper); MOVE(fImageFilter); + MOVE(fTextSize); + MOVE(fTextScaleX); + MOVE(fTextSkewX); MOVE(fColor4f); MOVE(fWidth); MOVE(fMiterLimit); + MOVE(fBlendMode); MOVE(fBitfields); #undef MOVE @@ -128,15 +158,20 @@ SkPaint& SkPaint::operator=(SkPaint&& src) { bool operator==(const SkPaint& a, const SkPaint& b) { #define EQUAL(field) (a.field == b.field) - return EQUAL(fPathEffect) + return EQUAL(fTypeface) + && EQUAL(fPathEffect) && EQUAL(fShader) && EQUAL(fMaskFilter) && EQUAL(fColorFilter) && EQUAL(fDrawLooper) && EQUAL(fImageFilter) + && EQUAL(fTextSize) + && EQUAL(fTextScaleX) + && EQUAL(fTextSkewX) && EQUAL(fColor4f) && EQUAL(fWidth) && EQUAL(fMiterLimit) + && EQUAL(fBlendMode) && EQUAL(fBitfieldsUInt) ; #undef EQUAL @@ -149,6 +184,7 @@ DEFINE_REF_FOO(ImageFilter) DEFINE_REF_FOO(MaskFilter) DEFINE_REF_FOO(PathEffect) DEFINE_REF_FOO(Shader) +DEFINE_REF_FOO(Typeface) #undef DEFINE_REF_FOO void SkPaint::reset() { @@ -160,6 +196,50 @@ void SkPaint::setFilterQuality(SkFilterQuality quality) { fBitfields.fFilterQuality = quality; } +void SkPaint::setHinting(SkFontHinting hintingLevel) { + fBitfields.fHinting = static_cast(hintingLevel); +} + +#ifdef SK_SUPPORT_LEGACY_PAINT_FLAGS +void SkPaint::setFlags(uint32_t flags) { + fBitfields.fFlags = flags; +} +#endif + +void SkPaint::setAntiAlias(bool doAA) { + this->internal_setFlags(set_clear_mask(fBitfields.fFlags, doAA, kAntiAlias_Flag)); +} + +void SkPaint::setDither(bool doDither) { + this->internal_setFlags(set_clear_mask(fBitfields.fFlags, doDither, kDither_Flag)); +} + +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS +void SkPaint::setSubpixelText(bool doSubpixel) { + this->internal_setFlags(set_clear_mask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag)); +} + +void SkPaint::setLCDRenderText(bool doLCDRender) { + this->internal_setFlags(set_clear_mask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag)); +} + +void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) { + this->internal_setFlags(set_clear_mask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag)); +} + +void SkPaint::setAutohinted(bool useAutohinter) { + this->internal_setFlags(set_clear_mask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag)); +} + +void SkPaint::setLinearText(bool doLinearText) { + this->internal_setFlags(set_clear_mask(fBitfields.fFlags, doLinearText, kLinearText_Flag)); +} + +void SkPaint::setFakeBoldText(bool doFakeBold) { + this->internal_setFlags(set_clear_mask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag)); +} +#endif + void SkPaint::setStyle(Style style) { if ((unsigned)style < kStyleCount) { fBitfields.fStyle = style; @@ -232,7 +312,40 @@ void SkPaint::setStrokeJoin(Join jt) { /////////////////////////////////////////////////////////////////////////////// +void SkPaint::setTextSize(SkScalar ts) { + if (ts >= 0) { + fTextSize = ts; + } else { +#ifdef SK_REPORT_API_RANGE_CHECK + SkDebugf("SkPaint::setTextSize() called with negative value\n"); +#endif + } +} + +void SkPaint::setTextScaleX(SkScalar scaleX) { + fTextScaleX = scaleX; +} + +void SkPaint::setTextSkewX(SkScalar skewX) { + fTextSkewX = skewX; +} + +#ifdef SK_SUPPORT_LEGACY_PAINTTEXTENCODING +void SkPaint::setTextEncoding(SkTextEncoding encoding) { + if ((unsigned)encoding <= (unsigned)kGlyphID_SkTextEncoding) { + fBitfields.fTextEncoding = (unsigned)encoding; + } else { +#ifdef SK_REPORT_API_RANGE_CHECK + SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding); +#endif + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + #define MOVE_FIELD(Field) void SkPaint::set##Field(sk_sp f) { f##Field = std::move(f); } +MOVE_FIELD(Typeface) MOVE_FIELD(ImageFilter) MOVE_FIELD(Shader) MOVE_FIELD(ColorFilter) @@ -294,8 +407,12 @@ enum PaintFlagsForFont { static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed, SkFont* font) { uint32_t f = packed >> 16; +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + paint->setFlags(f); +#else paint->setAntiAlias((f & kAA_PaintFlagForPaint) != 0); paint->setDither((f & kDither_PaintFlagForPaint) != 0); +#endif if (font) { font->setEmbolden((f & kFakeBold_PaintFlagForFont) != 0); font->setLinearMetrics((f & kLinear_PaintFlagForFont) != 0); @@ -316,6 +433,7 @@ static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed, SkFont* fon } } + paint->setHinting((SkFontHinting)((packed >> 14) & BPF_Mask(kHint_BPF))); paint->setFilterQuality((SkFilterQuality)((packed >> 10) & BPF_Mask(kFilter_BPF))); return (FlatFlags)(packed & kFlatFlagMask); } @@ -405,6 +523,11 @@ SkReadPaintResult SkPaintPriv::Unflatten_PreV68(SkPaint* paint, SkReadBuffer& bu SkScalar sz = buffer.readScalar(); SkScalar sx = buffer.readScalar(); SkScalar kx = buffer.readScalar(); +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + paint->setTextSize(sz); + paint->setTextScaleX(sx); + paint->setTextSkewX(kx); +#endif if (font) { font->setSize(sz); font->setScaleX(sx); @@ -428,12 +551,16 @@ SkReadPaintResult SkPaintPriv::Unflatten_PreV68(SkPaint* paint, SkReadBuffer& bu paint->setStrokeCap(safe.checkLE((tmp >> 24) & 0xFF, SkPaint::kLast_Cap)); paint->setStrokeJoin(safe.checkLE((tmp >> 16) & 0xFF, SkPaint::kLast_Join)); paint->setStyle(safe.checkLE((tmp >> 12) & 0xF, SkPaint::kStrokeAndFill_Style)); + paint->private_internal_setTextEncoding(safe.checkLE((tmp >> 8) & 0xF, kGlyphID_SkTextEncoding)); paint->setBlendMode(safe.checkLE(tmp & 0xFF, SkBlendMode::kLastMode)); sk_sp tf; if (flatFlags & kHasTypeface_FlatFlag) { tf = buffer.readTypeface(); } +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + paint->setTypeface(tf); +#endif if (font) { font->setTypeface(tf); } @@ -600,7 +727,7 @@ bool SkPaint::nothingToDraw() const { if (fDrawLooper) { return false; } - switch (this->getBlendMode()) { + switch ((SkBlendMode)fBlendMode) { case SkBlendMode::kSrcOver: case SkBlendMode::kSrcATop: case SkBlendMode::kDstOut: @@ -619,9 +746,9 @@ bool SkPaint::nothingToDraw() const { } uint32_t SkPaint::getHash() const { - // We're going to hash 6 pointers and 6 32-bit values, finishing up with fBitfields, - // so fBitfields should be 6 pointers and 6 32-bit values from the start. - static_assert(offsetof(SkPaint, fBitfields) == 6 * sizeof(void*) + 6 * sizeof(uint32_t), + // We're going to hash 7 pointers and 11 32-bit values, finishing up with fBitfields, + // so fBitfields should be 7 pointers and 10 32-bit values from the start. + static_assert(offsetof(SkPaint, fBitfields) == 7 * sizeof(void*) + 10 * sizeof(uint32_t), "SkPaint_notPackedTightly"); return SkOpts::hash(reinterpret_cast(this), offsetof(SkPaint, fBitfields) + sizeof(fBitfields)); diff --git a/src/core/SkPaintPriv.h b/src/core/SkPaintPriv.h index 4d3750573c..462ad99d9c 100644 --- a/src/core/SkPaintPriv.h +++ b/src/core/SkPaintPriv.h @@ -14,7 +14,6 @@ #include "SkTypeface.h" class SkBitmap; -class SkFont; class SkImage; class SkReadBuffer; class SkWriteBuffer; @@ -59,6 +58,10 @@ public: static bool ShouldDither(const SkPaint&, SkColorType); + static SkTextEncoding GetEncoding(const SkPaint& paint) { + return paint.private_internal_getTextEncoding(); + } + /** Serializes SkPaint into a buffer. A companion unflatten() call can reconstitute the paint at a later time. diff --git a/src/core/SkPaint_text.cpp b/src/core/SkPaint_text.cpp index 9903ff8032..afc7146db5 100644 --- a/src/core/SkPaint_text.cpp +++ b/src/core/SkPaint_text.cpp @@ -60,6 +60,25 @@ SkScalar SkFontPriv::MaxCacheSize2(SkScalar maxLimit) { #include "SkUtils.h" +#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTMEASURE +int SkPaint::countText(const void* text, size_t length) const { + return SkFont::LEGACY_ExtractFromPaint(*this).countText(text, length, this->getTextEncoding()); +} + +int SkPaint::textToGlyphs(const void* text, size_t length, uint16_t glyphs[]) const { + return SkFont::LEGACY_ExtractFromPaint(*this).textToGlyphs(text, length, + this->getTextEncoding(), + glyphs, length); +} + +bool SkPaint::containsText(const void* text, size_t length) const { + return SkFont::LEGACY_ExtractFromPaint(*this).containsText(text, length, + this->getTextEncoding()); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + static const SkGlyph& sk_getMetrics_utf8_next(SkStrike* cache, const char** text, const char* stop) { @@ -168,13 +187,205 @@ SkFontPriv::GlyphCacheProc SkFontPriv::GetGlyphCacheProc(SkTextEncoding encoding /////////////////////////////////////////////////////////////////////////////// +#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTMEASURE +SkScalar SkPaint::setupForAsPaths() { + + constexpr uint32_t flagsToIgnore = SkPaint::kLinearText_Flag | + SkPaint::kLCDRenderText_Flag | + SkPaint::kEmbeddedBitmapText_Flag | + SkPaint::kAutoHinting_Flag; + + uint32_t flags = this->getFlags(); + + // clear the flags we don't care about + flags &= ~flagsToIgnore; + + // set the flags we do care about + flags |= SkPaint::kSubpixelText_Flag; + + this->setFlags(flags); + this->setHinting(kNo_SkFontHinting); + this->setStyle(SkPaint::kFill_Style); + this->setPathEffect(nullptr); + + SkScalar textSize = fTextSize; + this->setTextSize(kCanonicalTextSizeForPaths); + return textSize / kCanonicalTextSizeForPaths; +} + +class SkCanonicalizePaint { +public: + SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) { + const SkFont font = SkFont::LEGACY_ExtractFromPaint(paint); + if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(font, paint, SkMatrix::I())) { + SkPaint* p = fLazy.set(paint); + fScale = p->setupForAsPaths(); + fPaint = p; + } + } + + const SkPaint& getPaint() const { return *fPaint; } + + /** + * Returns 0 if the paint was unmodified, or the scale factor need to + * the original textSize + */ + SkScalar getScale() const { return fScale; } + +private: + const SkPaint* fPaint; + SkScalar fScale; + SkTLazy fLazy; +}; +#endif + +static void set_bounds(const SkGlyph& g, SkRect* bounds) { + bounds->set(SkIntToScalar(g.fLeft), + SkIntToScalar(g.fTop), + SkIntToScalar(g.fLeft + g.fWidth), + SkIntToScalar(g.fTop + g.fHeight)); +} + +static void join_bounds_x(const SkGlyph& g, SkRect* bounds, SkScalar dx) { + bounds->join(SkIntToScalar(g.fLeft) + dx, + SkIntToScalar(g.fTop), + SkIntToScalar(g.fLeft + g.fWidth) + dx, + SkIntToScalar(g.fTop + g.fHeight)); +} + // xyIndex is 0 for fAdvanceX or 1 for fAdvanceY static SkScalar advance(const SkGlyph& glyph) { return SkFloatToScalar(glyph.fAdvanceX); } +SkScalar SkPaint::measure_text(SkStrike* cache, + const char* text, size_t byteLength, + int* count, SkRect* bounds) const { + SkASSERT(count); + if (byteLength == 0) { + *count = 0; + if (bounds) { + bounds->setEmpty(); + } + return 0; + } + + SkFontPriv::GlyphCacheProc glyphCacheProc = SkFontPriv::GetGlyphCacheProc( + this->private_internal_getTextEncoding(), nullptr != bounds); + + int n = 1; + const char* stop = (const char*)text + byteLength; + const SkGlyph* g = &glyphCacheProc(cache, &text, stop); + SkScalar x = advance(*g); + + if (nullptr == bounds) { + for (; text < stop; n++) { + x += advance(glyphCacheProc(cache, &text, stop)); + } + } else { + set_bounds(*g, bounds); + + for (; text < stop; n++) { + g = &glyphCacheProc(cache, &text, stop); + join_bounds_x(*g, bounds, x); + x += advance(*g); + } + } + SkASSERT(text == stop); + + *count = n; + return x; +} + +#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTMEASURE +SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const { + const char* text = (const char*)textData; + SkASSERT(text != nullptr || length == 0); + + SkCanonicalizePaint canon(*this); + const SkPaint& paint = canon.getPaint(); + SkScalar scale = canon.getScale(); + + const SkFont font = SkFont::LEGACY_ExtractFromPaint(paint); + auto cache = SkStrikeCache::FindOrCreateStrikeWithNoDeviceExclusive(font, paint); + + SkScalar width = 0; + + if (length > 0) { + int tempCount; + + width = paint.measure_text(cache.get(), text, length, &tempCount, bounds); + if (scale) { + width *= scale; + if (bounds) { + bounds->fLeft *= scale; + bounds->fTop *= scale; + bounds->fRight *= scale; + bounds->fBottom *= scale; + } + } + } else if (bounds) { + // ensure that even if we don't measure_text we still update the bounds + bounds->setEmpty(); + } + return width; +} + +SkScalar SkPaint::getFontMetrics(SkFontMetrics* metrics) const { + return SkFont::LEGACY_ExtractFromPaint(*this).getMetrics(metrics); +} + +int SkPaint::getTextWidths(const void* text, size_t len, SkScalar widths[], SkRect bounds[]) const { + const SkFont font = SkFont::LEGACY_ExtractFromPaint(*this); + SkAutoToGlyphs gly(font, text, len, this->getTextEncoding()); + font.getWidthsBounds(gly.glyphs(), gly.count(), widths, bounds, this); + return gly.count(); +} + +#endif + /////////////////////////////////////////////////////////////////////////////// +#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTMEASURE +#include "SkDraw.h" + +struct PathPosRec { + SkPath* fDst; + const SkPoint* fPos; +}; +static void PathPosProc(const SkPath* src, const SkMatrix& mx, void* ctx) { + PathPosRec* rec = static_cast(ctx); + if (src) { + SkMatrix m(mx); + m.postTranslate(rec->fPos->fX, rec->fPos->fY); + rec->fDst->addPath(*src, m); + } + rec->fPos += 1; +} + +void SkPaint::getTextPath(const void* text, size_t length, + SkScalar x, SkScalar y, SkPath* path) const { + SkFont font = SkFont::LEGACY_ExtractFromPaint(*this); + SkAutoToGlyphs gly(font, text, length, this->getTextEncoding()); + SkAutoSTArray<32, SkPoint> fPos(gly.count()); + font.getPos(gly.glyphs(), gly.count(), fPos.get(), {x, y}); + + path->reset(); + PathPosRec rec = { path, fPos.get() }; + font.getPaths(gly.glyphs(), gly.count(), PathPosProc, &rec); +} + +void SkPaint::getPosTextPath(const void* text, size_t length, + const SkPoint pos[], SkPath* path) const { + SkFont font = SkFont::LEGACY_ExtractFromPaint(*this); + SkAutoToGlyphs gly(font, text, length, this->getTextEncoding()); + + path->reset(); + PathPosRec rec = { path, pos }; + font.getPaths(gly.glyphs(), gly.count(), PathPosProc, &rec); +} +#endif + int SkPaint::getTextBlobIntercepts(const SkTextBlob* blob, const SkScalar bounds[2], SkScalar* intervals) const { return blob->getIntercepts(bounds, intervals, this); diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h index de1c269485..e4bb6e4399 100644 --- a/src/core/SkPictureFlat.h +++ b/src/core/SkPictureFlat.h @@ -41,16 +41,16 @@ enum DrawType { DRAW_PATH, DRAW_PICTURE, DRAW_POINTS, - DRAW_POS_TEXT_REMOVED_1_2019, - DRAW_POS_TEXT_TOP_BOTTOM_REMOVED_1_2019, - DRAW_POS_TEXT_H_REMOVED_1_2019, - DRAW_POS_TEXT_H_TOP_BOTTOM_REMOVED_1_2019, + DRAW_POS_TEXT, + DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT + DRAW_POS_TEXT_H, + DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H DRAW_RECT, DRAW_RRECT, DRAW_SPRITE_RETIRED_2015_REMOVED_2018, - DRAW_TEXT_REMOVED_1_2019, + DRAW_TEXT, DRAW_TEXT_ON_PATH_RETIRED_08_2018_REMOVED_10_2018, - DRAW_TEXT_TOP_BOTTOM_REMOVED_1_2019, + DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT DRAW_VERTICES_RETIRED_03_2017_REMOVED_01_2018, RESTORE, ROTATE, diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index 86bb2af8ab..499e2a576d 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -68,6 +68,36 @@ static const SkRect* get_rect_ptr(SkReadBuffer* reader, SkRect* storage) { } } +class TextContainer { +public: + TextContainer(SkReadBuffer* reader, const SkPaint* paint) { + if (reader->validate(paint != nullptr)) { + fByteLength = reader->readInt(); + fText = (const char*)reader->skip(fByteLength); + if (reader->isValid()) { + if (fByteLength == 0) { + fCount = 0; + } else { + fCount = SkFontPriv::ValidCountText(fText, fByteLength, + SkPaintPriv::GetEncoding(*paint)); + reader->validate(fCount > 0); + } + } + } + } + + operator bool() const { return fCount >= 0; } + + size_t length() const { return fByteLength; } + const void* text() const { return (const void*)fText; } + unsigned count() const { return fCount; } + +private: + size_t fByteLength = 0; + const char* fText = nullptr; + int fCount = -1; +}; + void SkPicturePlayback::draw(SkCanvas* canvas, SkPicture::AbortCallback* callback, SkReadBuffer* buffer) { @@ -431,6 +461,77 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader, canvas->drawPoints(mode, count, pts, *paint); } } break; +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + case DRAW_POS_TEXT: { + const SkPaint* paint = fPictureData->getPaint(reader); + TextContainer text(reader, paint); + size_t points = reader->readInt(); + reader->validate(points == text.count()); + const SkPoint* pos = (const SkPoint*)reader->skip(points, sizeof(SkPoint)); + BREAK_ON_READ_ERROR(reader); + + if (paint && text.text()) { + SkFont font = SkFont::LEGACY_ExtractFromPaint(*paint); + auto blob = SkTextBlob::MakeFromPosText(text.text(), text.length(), pos, font, + SkPaintPriv::GetEncoding(*paint)); + canvas->drawTextBlob(blob, 0, 0, *paint); + } + } break; + case DRAW_POS_TEXT_TOP_BOTTOM: { + const SkPaint* paint = fPictureData->getPaint(reader); + TextContainer text(reader, paint); + size_t points = reader->readInt(); + reader->validate(points == text.count()); + const SkPoint* pos = (const SkPoint*)reader->skip(points, sizeof(SkPoint)); + const SkScalar top = reader->readScalar(); + const SkScalar bottom = reader->readScalar(); + BREAK_ON_READ_ERROR(reader); + + SkRect clip = canvas->getLocalClipBounds(); + if (top < clip.fBottom && bottom > clip.fTop && paint && text.text()) { + SkFont font = SkFont::LEGACY_ExtractFromPaint(*paint); + auto blob = SkTextBlob::MakeFromPosText(text.text(), text.length(), pos, font, + SkPaintPriv::GetEncoding(*paint)); + canvas->drawTextBlob(blob, 0, 0, *paint); + } + } break; + case DRAW_POS_TEXT_H: { + const SkPaint* paint = fPictureData->getPaint(reader); + TextContainer text(reader, paint); + size_t xCount = reader->readInt(); + reader->validate(xCount == text.count()); + const SkScalar constY = reader->readScalar(); + const SkScalar* xpos = (const SkScalar*)reader->skip(xCount, sizeof(SkScalar)); + BREAK_ON_READ_ERROR(reader); + + if (paint && text.text()) { + SkFont font = SkFont::LEGACY_ExtractFromPaint(*paint); + auto blob = SkTextBlob::MakeFromPosTextH(text.text(), text.length(), xpos, constY, + font, SkPaintPriv::GetEncoding(*paint)); + canvas->drawTextBlob(blob, 0, 0, *paint); + } + } break; + case DRAW_POS_TEXT_H_TOP_BOTTOM: { + const SkPaint* paint = fPictureData->getPaint(reader); + TextContainer text(reader, paint); + size_t xCount = reader->readInt(); + reader->validate(xCount == text.count()); + const SkScalar* xpos = (const SkScalar*)reader->skip(SkSafeMath::Add(3, xCount), + sizeof(SkScalar)); + BREAK_ON_READ_ERROR(reader); + + const SkScalar top = *xpos++; + const SkScalar bottom = *xpos++; + const SkScalar constY = *xpos++; + SkRect clip = canvas->getLocalClipBounds(); + if (top < clip.fBottom && bottom > clip.fTop && paint && text.text()) { + SkFont font = SkFont::LEGACY_ExtractFromPaint(*paint); + auto blob = SkTextBlob::MakeFromPosTextH(text.text(), text.length(), xpos, constY, + font, SkPaintPriv::GetEncoding(*paint)); + canvas->drawTextBlob(blob, 0, 0, *paint); + } + } break; +#endif case DRAW_RECT: { const SkPaint* paint = fPictureData->getPaint(reader); SkRect rect; @@ -482,6 +583,21 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader, canvas->private_draw_shadow_rec(path, rec); } break; +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + case DRAW_TEXT: { + const SkPaint* paint = fPictureData->getPaint(reader); + TextContainer text(reader, paint); + SkScalar x = reader->readScalar(); + SkScalar y = reader->readScalar(); + BREAK_ON_READ_ERROR(reader); + + if (paint && text.text()) { + canvas->drawSimpleText(text.text(), text.length(), + paint->private_internal_getTextEncoding(), + x, y, SkFont::LEGACY_ExtractFromPaint(*paint), *paint); + } + } break; +#endif case DRAW_TEXT_BLOB: { const SkPaint* paint = fPictureData->getPaint(reader); const SkTextBlob* blob = fPictureData->getTextBlob(reader); @@ -493,6 +609,56 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader, canvas->drawTextBlob(blob, x, y, *paint); } } break; +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + case DRAW_TEXT_TOP_BOTTOM: { + const SkPaint* paint = fPictureData->getPaint(reader); + TextContainer text(reader, paint); + const SkScalar* ptr = (const SkScalar*)reader->skip(4 * sizeof(SkScalar)); + BREAK_ON_READ_ERROR(reader); + + // ptr[0] == x + // ptr[1] == y + // ptr[2] == top + // ptr[3] == bottom + SkRect clip = canvas->getLocalClipBounds(); + float top = ptr[2]; + float bottom = ptr[3]; + if (top < clip.fBottom && bottom > clip.fTop && paint && text.text()) { + canvas->drawSimpleText(text.text(), text.length(), + paint->private_internal_getTextEncoding(), ptr[0], ptr[1], + SkFont::LEGACY_ExtractFromPaint(*paint), *paint); + } + } break; + case DRAW_TEXT_ON_PATH_RETIRED_08_2018_REMOVED_10_2018: { + const SkPaint* paint = fPictureData->getPaint(reader); + TextContainer text(reader, paint); + /* ignored */ fPictureData->getPath(reader); + SkMatrix matrix; + reader->readMatrix(&matrix); + BREAK_ON_READ_ERROR(reader); + // no longer supported, so we draw nothing + } break; + case DRAW_TEXT_RSXFORM_DEPRECATED_DEC_2018: { + const SkPaint* paint = fPictureData->getPaint(reader); + uint32_t count = reader->readUInt(); + uint32_t flags = reader->readUInt(); + TextContainer text(reader, paint); + const SkRSXform* xform = (const SkRSXform*)reader->skip(count, sizeof(SkRSXform)); + if (flags & DRAW_TEXT_RSXFORM_HAS_CULL) { + // skip past cull rect + (void)reader->skip(sizeof(SkRect)); + } + reader->validate(count == text.count()); + BREAK_ON_READ_ERROR(reader); + + if (text.text()) { + SkFont font = SkFont::LEGACY_ExtractFromPaint(*paint); + auto blob = SkTextBlob::MakeFromRSXform(text.text(), text.length(), xform, font, + SkPaintPriv::GetEncoding(*paint)); + canvas->drawTextBlob(blob, 0, 0, *paint); + } + } break; +#endif case DRAW_VERTICES_OBJECT: { const SkPaint* paint = fPictureData->getPaint(reader); const SkVertices* vertices = fPictureData->getVertices(reader); diff --git a/src/utils/SkLua.cpp b/src/utils/SkLua.cpp index a7e978a644..8083e1eac1 100644 --- a/src/utils/SkLua.cpp +++ b/src/utils/SkLua.cpp @@ -16,7 +16,6 @@ #include "SkColorFilter.h" #include "SkData.h" #include "SkFont.h" -#include "SkFontMetrics.h" #include "SkFontStyle.h" #include "SkGradientShader.h" #include "SkImage.h" @@ -557,8 +556,7 @@ static int lcanvas_drawText(lua_State* L) { return 0; } - // TODO: restore this logic based on SkFont instead of SkPaint -#if 0 +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS if (lua_isstring(L, 2) && lua_isnumber(L, 3) && lua_isnumber(L, 4)) { size_t len; const char* text = lua_tolstring(L, 2, &len); @@ -1875,8 +1873,7 @@ static int lsk_newTextBlob(lua_State* L) { SkShaper shaper(nullptr); - // TODO: restore this logic based on SkFont instead of SkPaint -#if 0 +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS const SkPaint& paint = *get_obj(L, 3); SkFont font = SkFont::LEGACY_ExtractFromPaint(paint); #else diff --git a/tests/FontObjTest.cpp b/tests/FontObjTest.cpp new file mode 100644 index 0000000000..6ddb9f3a64 --- /dev/null +++ b/tests/FontObjTest.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkFont.h" +#include "SkPaint.h" +#include "SkTypeface.h" +#include "Test.h" + +#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTMEASURE +static void test_fontmetrics(skiatest::Reporter* reporter, + const SkPaint& paint, const SkFont& font) { + SkFontMetrics fm0, fm1; + SkScalar h0 = paint.getFontMetrics(&fm0); + SkScalar h1 = font.getMetrics(&fm1); + + REPORTER_ASSERT(reporter, h0 == h1); +#define CMP(field) REPORTER_ASSERT(reporter, fm0.field == fm1.field) + CMP(fFlags); + CMP(fTop); + CMP(fAscent); + CMP(fDescent); + CMP(fBottom); + CMP(fLeading); +#undef CMP +} + +DEF_TEST(FontObj_test_cachedfont, reporter) { + SkPaint paint; + char txt[] = "long .text .with .lots .of.dots."; + unsigned mask = SkPaint::kAntiAlias_Flag | + SkPaint::kFakeBoldText_Flag | + SkPaint::kLinearText_Flag | + SkPaint::kSubpixelText_Flag | + SkPaint::kLCDRenderText_Flag | + SkPaint::kEmbeddedBitmapText_Flag | + SkPaint::kAutoHinting_Flag; + + paint.setStrokeWidth(2); + { + for (unsigned flags = 0; flags <= 0xFFF; ++flags) { + if (flags & ~mask) { + continue; + } + paint.setFlags(flags); + for (int hint = 0; hint <= 3; ++hint) { + paint.setHinting((SkFontHinting)hint); + { + for (auto style : { SkPaint::kFill_Style, SkPaint::kStroke_Style}) { + paint.setStyle(style); + + const SkFont font(SkFont::LEGACY_ExtractFromPaint(paint)); + test_fontmetrics(reporter, paint, font); + + SkRect pbounds, fbounds; + + // Requesting the bounds forces a generateMetrics call. + SkScalar pwidth = paint.measureText(txt, strlen(txt), &pbounds); + SkScalar fwidth = font.measureText(txt, strlen(txt), kUTF8_SkTextEncoding, + &fbounds, &paint); + REPORTER_ASSERT(reporter, pwidth == fwidth); + REPORTER_ASSERT(reporter, pbounds == fbounds); + } + } + } + } + } +} +#endif // SK_SUPPORT_LEGACY_PAINT_TEXTMEASURE + +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS +DEF_TEST(FontObj_test_aa_hinting, reporter) { + SkPaint paint; + + for (bool aa : {false, true}) { + paint.setAntiAlias(aa); + for (int hint = 0; hint <= 3; ++hint) { + paint.setHinting((SkFontHinting)hint); + SkFont font = SkFont::LEGACY_ExtractFromPaint(paint); + + SkPaint p2; + font.LEGACY_applyToPaint(&p2); + REPORTER_ASSERT(reporter, paint.isAntiAlias() == p2.isAntiAlias()); + REPORTER_ASSERT(reporter, paint.getHinting() == p2.getHinting()); + } + } +} +#endif + +// need tests for SkStrSearch diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp index ae9d3d5475..c34dc765f9 100644 --- a/tests/PaintTest.cpp +++ b/tests/PaintTest.cpp @@ -267,6 +267,10 @@ DEF_TEST(Paint_regression_measureText, reporter) { DEF_TEST(Paint_MoreFlattening, r) { SkPaint paint; paint.setColor(0x00AABBCC); +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + paint.setTextScaleX(1.0f); // Default value, ignored. + paint.setTextSize(19); +#endif paint.setBlendMode(SkBlendMode::kModulate); paint.setLooper(nullptr); // Default value, ignored. @@ -300,10 +304,18 @@ DEF_TEST(Paint_getHash, r) { paint.setColor(SK_ColorBLACK); // Reset to default value. REPORTER_ASSERT(r, paint.getHash() == defaultHash); - // This is part of fBitfields, the last field we hash. - paint.setBlendMode(SkBlendMode::kSrc); +#ifdef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS + // SkTypeface is the first field we hash, so test it specially. + paint.setTypeface(SkTypeface::MakeDefault()); REPORTER_ASSERT(r, paint.getHash() != defaultHash); - paint.setBlendMode(SkBlendMode::kSrcOver); + paint.setTypeface(nullptr); + REPORTER_ASSERT(r, paint.getHash() == defaultHash); +#endif + + // This is part of fBitfields, the last field we hash. + paint.setHinting(kSlight_SkFontHinting); + REPORTER_ASSERT(r, paint.getHash() != defaultHash); + paint.setHinting(kNormal_SkFontHinting); REPORTER_ASSERT(r, paint.getHash() == defaultHash); } @@ -333,6 +345,43 @@ DEF_TEST(Paint_nothingToDraw, r) { REPORTER_ASSERT(r, !paint.nothingToDraw()); } +#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTMEASURE +DEF_TEST(Paint_getwidths, r) { + SkPaint paint; + const char text[] = "Hamburgefons!@#!#23425,./;'[]"; + int count = paint.countText(text, strlen(text)); + SkAutoTArray glyphStorage(count * 2); + uint16_t* glyphs = glyphStorage.get(); + + (void)paint.textToGlyphs(text, strlen(text), glyphs); + paint.setTextEncoding(kGlyphID_SkTextEncoding); + + SkAutoTArray widthStorage(count * 2); + SkScalar* widths = widthStorage.get(); + SkAutoTArray rectStorage(count * 2); + SkRect* bounds = rectStorage.get(); + + for (bool subpix : { false, true }) { + paint.setSubpixelText(subpix); + for (auto hint : { kNo_SkFontHinting, kSlight_SkFontHinting, kNormal_SkFontHinting, kFull_SkFontHinting}) { + paint.setHinting(hint); + for (auto size : { 1.0f, 12.0f, 100.0f }) { + paint.setTextSize(size); + paint.getTextWidths(glyphs, count * 2, widths, bounds); + + SkFont font = SkFont::LEGACY_ExtractFromPaint(paint); + font.getWidths(glyphs, count, widths + count, bounds + count); + + for (int i = 0; i < count; ++i) { + REPORTER_ASSERT(r, widths[i] == widths[i + count]); + REPORTER_ASSERT(r, bounds[i] == bounds[i + count]); + } + } + } + } +} +#endif + DEF_TEST(Font_getpos, r) { SkFont font; const char text[] = "Hamburgefons!@#!#23425,./;'[]";