|  | #Topic Paint | 
|  | #Alias Paint_Reference ## | 
|  |  | 
|  | #PhraseDef paint_font_metrics | 
|  | Typeface, Paint_Text_Size, Paint_Text_Scale_X, | 
|  | Paint_Text_Skew_X, Paint_Hinting, Anti_Alias, Paint_Fake_Bold, | 
|  | Font_Embedded_Bitmaps, Full_Hinting_Spacing, LCD_Text, Linear_Text, | 
|  | and Subpixel_Text | 
|  | ## | 
|  |  | 
|  | #Class SkPaint | 
|  |  | 
|  | #Code | 
|  | #Populate | 
|  | ## | 
|  |  | 
|  | Paint controls options applied when drawing and measuring. Paint collects all | 
|  | options outside of the Canvas_Clip and Canvas_Matrix. | 
|  |  | 
|  | 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 Canvas | 
|  | functionality; for instance, text size is identical to matrix scale. | 
|  |  | 
|  | Paint options are rarely exclusive; each option modifies a stage of the drawing | 
|  | pipeline and multiple pipeline stages may be affected by a single Paint. | 
|  |  | 
|  | Paint collects effects and filters that describe single-pass and multiple-pass | 
|  | algorithms that alter the drawing geometry, color, and transparency. For instance, | 
|  | Paint does not directly implement dashing or blur, but contains the objects that do so. | 
|  |  | 
|  | The objects contained by Paint are opaque, and cannot be edited outside of the Paint | 
|  | to affect it. The implementation is free to defer computations associated with the | 
|  | Paint, or ignore them altogether. For instance, some GPU implementations draw all | 
|  | Path geometries with Anti_Aliasing, regardless of how SkPaint::kAntiAlias_Flag | 
|  | is set in Paint. | 
|  |  | 
|  | Paint 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 | 
|  | Shader attached to Paint. | 
|  |  | 
|  | #Method SkPaint() | 
|  | #Line # constructs with default values ## | 
|  | Constructs Paint with default values. | 
|  |  | 
|  | #Table | 
|  | #Legend | 
|  | # attribute              # default value            ## | 
|  | #Legend ## | 
|  | # Anti_Alias             # false                    ## | 
|  | # Blend_Mode             # SkBlendMode::kSrcOver    ## | 
|  | # Color                  # SK_ColorBLACK            ## | 
|  | # Color_Alpha            # 255                      ## | 
|  | # Color_Filter           # nullptr                  ## | 
|  | # Dither                 # false                    ## | 
|  | # Draw_Looper            # nullptr                  ## | 
|  | # Fake_Bold              # false                    ## | 
|  | # Filter_Quality         # kNone_SkFilterQuality    ## | 
|  | # Font_Embedded_Bitmaps  # false                    ## | 
|  | # Automatic_Hinting      # false                    ## | 
|  | # Full_Hinting_Spacing   # false                    ## | 
|  | # Hinting                # SkFontHinting::kNormal   ## | 
|  | # Image_Filter           # nullptr                  ## | 
|  | # LCD_Text               # false                    ## | 
|  | # Linear_Text            # false                    ## | 
|  | # Miter_Limit            # 4                        ## | 
|  | # Mask_Filter            # nullptr                  ## | 
|  | # Path_Effect            # nullptr                  ## | 
|  | # Shader                 # nullptr                  ## | 
|  | # Style                  # kFill_Style              ## | 
|  | # Text_Encoding          # kUTF8_TextEncoding       ## | 
|  | # Text_Scale_X           # 1                        ## | 
|  | # Text_Size              # 12                       ## | 
|  | # Text_Skew_X            # 0                        ## | 
|  | # Typeface               # nullptr                  ## | 
|  | # Stroke_Cap             # kButt_Cap                ## | 
|  | # Stroke_Join            # kMiter_Join              ## | 
|  | # Stroke_Width           # 0                        ## | 
|  | # Subpixel_Text          # false                    ## | 
|  | #Table ## | 
|  |  | 
|  | 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. | 
|  |  | 
|  | #Return  default initialized Paint ## | 
|  |  | 
|  | #Example | 
|  | #ToDo mark this as no output ## | 
|  | #Height 1 | 
|  | ###$  $ redefine markup character so preprocessor commands appear normally | 
|  | #ifndef SkUserConfig_DEFINED | 
|  | #define SkUserConfig_DEFINED | 
|  |  | 
|  | #define SkPaintDefaults_Flags      0x01   // always enable antialiasing | 
|  | #define SkPaintDefaults_TextSize   24.f   // double default font size | 
|  | #define SkPaintDefaults_Hinting    3      // use full hinting | 
|  | #define SkPaintDefaults_MiterLimit 10.f   // use HTML Canvas miter limit setting | 
|  |  | 
|  | #endif | 
|  | $$$#  # restore original markup character | 
|  | ## | 
|  |  | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method SkPaint(const SkPaint& paint) | 
|  | #Line # makes a shallow copy ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #ToDo why is this double-spaced on Fiddle? ## | 
|  | SkPaint paint1; | 
|  | paint1.setColor(SK_ColorRED); | 
|  | SkPaint paint2(paint1); | 
|  | paint2.setColor(SK_ColorBLUE); | 
|  | SkDebugf("SK_ColorRED %c= paint1.getColor()\n", SK_ColorRED == paint1.getColor() ? '=' : '!'); | 
|  | SkDebugf("SK_ColorBLUE %c= paint2.getColor()\n", SK_ColorBLUE == paint2.getColor() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | SK_ColorRED == paint1.getColor() | 
|  | SK_ColorBLUE == paint2.getColor() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method SkPaint(SkPaint&& paint) | 
|  | #Line # moves paint without copying it ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | float intervals[] = { 5, 5 }; | 
|  | paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 2.5f)); | 
|  | SkPaint dashed(std::move(paint)); | 
|  | SkDebugf("path effect unique: %s\n", dashed.getPathEffect()->unique() ? "true" : "false"); | 
|  |  | 
|  | #StdOut | 
|  | path effect unique: true | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method void reset() | 
|  | #In Constructors | 
|  | #Line # sets to default values ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setColor(SK_ColorRED); | 
|  | paint1.reset(); | 
|  | SkDebugf("paint1 %c= paint2", paint1 == paint2 ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | paint1 == paint2 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method ~SkPaint() | 
|  | #Line # decreases Reference_Count of owned objects ## | 
|  | #Populate | 
|  |  | 
|  | #NoExample | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Subtopic Management | 
|  | #Line # paint copying, moving, comparing ## | 
|  | ## | 
|  |  | 
|  | #Method SkPaint& operator=(const SkPaint& paint) | 
|  | #In Management | 
|  | #Line # makes a shallow copy ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setColor(SK_ColorRED); | 
|  | paint2 = paint1; | 
|  | SkDebugf("SK_ColorRED %c= paint1.getColor()\n", SK_ColorRED == paint1.getColor() ? '=' : '!'); | 
|  | SkDebugf("SK_ColorRED %c= paint2.getColor()\n", SK_ColorRED == paint2.getColor() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | SK_ColorRED == paint1.getColor() | 
|  | SK_ColorRED == paint2.getColor() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method SkPaint& operator=(SkPaint&& paint) | 
|  | #In Management | 
|  | #Line # moves paint without copying it ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setColor(SK_ColorRED); | 
|  | paint2 = std::move(paint1); | 
|  | SkDebugf("SK_ColorRED == paint2.getColor()\n", SK_ColorRED == paint2.getColor() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | SK_ColorRED == paint2.getColor() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method bool operator==(const SkPaint& a, const SkPaint& b) | 
|  | #Line # compares paints for equality ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setColor(SK_ColorRED); | 
|  | paint2.setColor(0xFFFF0000); | 
|  | SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); | 
|  | float intervals[] = { 5, 5 }; | 
|  | paint1.setPathEffect(SkDashPathEffect::Make(intervals, 2, 2.5f)); | 
|  | paint2.setPathEffect(SkDashPathEffect::Make(intervals, 2, 2.5f)); | 
|  | SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | paint1 == paint2 | 
|  | paint1 != paint2 | 
|  | ## | 
|  | ## | 
|  | #SeeAlso operator!=(const SkPaint& a, const SkPaint& b) | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method bool operator!=(const SkPaint& a, const SkPaint& b) | 
|  | #Line # compares paints for inequality ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setColor(SK_ColorRED); | 
|  | paint2.setColor(0xFFFF0000); | 
|  | SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); | 
|  | SkDebugf("paint1 %c= paint2\n", paint1 != paint2 ? '!' : '='); | 
|  |  | 
|  | #StdOut | 
|  | paint1 == paint2 | 
|  | paint1 == paint2 | 
|  | ## | 
|  | ## | 
|  | #SeeAlso operator==(const SkPaint& a, const SkPaint& b) | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method uint32_t getHash() const | 
|  | #In Management | 
|  | #Line # returns a shallow hash for equality checks ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setColor(SK_ColorRED); | 
|  | paint2.setColor(0xFFFF0000); | 
|  | SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); | 
|  | SkDebugf("paint1.getHash() %c= paint2.getHash()\n", | 
|  | paint1.getHash() == paint2.getHash() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | paint1 == paint2 | 
|  | paint1.getHash() == paint2.getHash() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #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 Fake_Bold ## | 
|  | ## | 
|  | #Const kLinearText_Flag         0x0040 | 
|  | #Line # mask for setting Linear_Text ## | 
|  | ## | 
|  | #Const kSubpixelText_Flag       0x0080 | 
|  | #Line # mask for setting Subpixel_Text ## | 
|  | ## | 
|  | #Const kLCDRenderText_Flag      0x0200 | 
|  | #Line # mask for setting LCD_Text ## | 
|  | ## | 
|  | #Const kEmbeddedBitmapText_Flag 0x0400 | 
|  | #Line # mask for setting Font_Embedded_Bitmaps ## | 
|  | ## | 
|  | #Const kAutoHinting_Flag        0x0800 | 
|  | #Line # mask for setting Automatic_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 | 
|  | #Substitute anti-alias | 
|  | ## | 
|  | #Alias Anti_Aliased | 
|  | #Substitute anti-aliased | 
|  | ## | 
|  | #Alias Anti_Aliasing | 
|  | #Substitute anti-aliasing | 
|  | ## | 
|  | #In Related_Function | 
|  | #Line # approximating coverage with transparency ## | 
|  |  | 
|  | Anti_Alias drawing approximates partial pixel coverage with transparency. | 
|  | If kAntiAlias_Flag is clear, pixel centers contained by the shape edge are drawn opaque. | 
|  | If kAntiAlias_Flag is set, pixels are drawn with Color_Alpha equal to their coverage. | 
|  |  | 
|  | The rule for Aliased pixels is inconsistent across platforms. A shape edge | 
|  | passing through the pixel center may, but is not required to, draw the pixel. | 
|  |  | 
|  | Raster_Engine draws Aliased pixels whose centers are on or to the right of the start of an | 
|  | active Path edge, and whose center is to the left of the end of the active Path edge. | 
|  |  | 
|  | #ToDo  add illustration of raster pixels ## | 
|  |  | 
|  | A platform may only support Anti_Aliased drawing. Some GPU-backed platforms use | 
|  | Supersampling to Anti_Alias all drawing, and have no mechanism to selectively | 
|  | Alias. | 
|  |  | 
|  | The amount of coverage computed for Anti_Aliased pixels also varies across platforms. | 
|  |  | 
|  | Anti_Alias is disabled by default. | 
|  | Anti_Alias can be enabled by default by setting SkPaintDefaults_Flags to kAntiAlias_Flag | 
|  | at compile time. | 
|  |  | 
|  | #Example | 
|  | #Width 512 | 
|  | #Description | 
|  | A red line is drawn with transparency on the edges to make it look smoother. | 
|  | A blue line draws only where the pixel centers are contained. | 
|  | The lines are drawn into Bitmap, then drawn magnified to make the | 
|  | Aliasing easier to see. | 
|  | ## | 
|  |  | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkBitmap bitmap; | 
|  | bitmap.allocN32Pixels(50, 50); | 
|  | SkCanvas offscreen(bitmap); | 
|  | SkPaint paint; | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(10); | 
|  | for (bool antialias : { false, true }) { | 
|  | paint.setColor(antialias ? SK_ColorRED : SK_ColorBLUE); | 
|  | paint.setAntiAlias(antialias); | 
|  | bitmap.eraseColor(0); | 
|  | offscreen.drawLine(5, 5, 15, 30, paint); | 
|  | canvas->drawLine(5, 5, 15, 30, paint); | 
|  | canvas->save(); | 
|  | canvas->scale(10, 10); | 
|  | canvas->drawBitmap(bitmap, antialias ? 12 : 0, 0); | 
|  | canvas->restore(); | 
|  | canvas->translate(15, 0); | 
|  | } | 
|  | } | 
|  | ## | 
|  | #Subtopic Anti_Alias ## | 
|  |  | 
|  | #Method bool isAntiAlias() const | 
|  | #In Anti_alias | 
|  | #Line # returns true if Anti_Alias is set ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("paint.isAntiAlias() %c= !!(paint.getFlags() & SkPaint::kAntiAlias_Flag)\n", | 
|  | paint.isAntiAlias() == !!(paint.getFlags() & SkPaint::kAntiAlias_Flag) ? '=' : '!'); | 
|  | paint.setAntiAlias(true); | 
|  | SkDebugf("paint.isAntiAlias() %c= !!(paint.getFlags() & SkPaint::kAntiAlias_Flag)\n", | 
|  | paint.isAntiAlias() == !!(paint.getFlags() & SkPaint::kAntiAlias_Flag) ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | paint.isAntiAlias() == !!(paint.getFlags() & SkPaint::kAntiAlias_Flag) | 
|  | paint.isAntiAlias() == !!(paint.getFlags() & SkPaint::kAntiAlias_Flag) | 
|  | ## | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #Method void setAntiAlias(bool aa) | 
|  | #In Anti_alias | 
|  | #Line # sets or clears Anti_Alias ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setAntiAlias(true); | 
|  | paint2.setFlags(paint2.getFlags() | SkPaint::kAntiAlias_Flag); | 
|  | SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | paint1 == paint2 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Dither | 
|  | #Line # distributing color error ## | 
|  |  | 
|  | Dither increases fidelity by adjusting the color of adjacent pixels. | 
|  | This can help to smooth color transitions and reducing banding in gradients. | 
|  | Dithering lessens visible banding from kRGB_565_SkColorType | 
|  | and kRGBA_8888_SkColorType gradients, | 
|  | and improves rendering into a kRGB_565_SkColorType Surface. | 
|  |  | 
|  | Dithering is always enabled for linear gradients drawing into | 
|  | kRGB_565_SkColorType Surface and kRGBA_8888_SkColorType Surface. | 
|  | Dither cannot be enabled for kAlpha_8_SkColorType Surface and | 
|  | kRGBA_F16_SkColorType Surface. | 
|  |  | 
|  | Dither is disabled by default. | 
|  | Dither can be enabled by default by setting SkPaintDefaults_Flags to kDither_Flag | 
|  | at compile time. | 
|  |  | 
|  | Some platform implementations may ignore dithering. Set #Formula # SK_IGNORE_GPU_DITHER ## | 
|  | to ignore Dither on GPU_Surface. | 
|  |  | 
|  | #Example | 
|  | #Description | 
|  | Dithering in the bottom half more closely approximates the requested color by | 
|  | alternating nearby colors from pixel to pixel. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkBitmap bm16; | 
|  | bm16.allocPixels(SkImageInfo::Make(32, 32, kRGB_565_SkColorType, kOpaque_SkAlphaType)); | 
|  | SkCanvas c16(bm16); | 
|  | SkPaint colorPaint; | 
|  | for (auto dither : { false, true } ) { | 
|  | colorPaint.setDither(dither); | 
|  | for (auto colors : { 0xFF333333, 0xFF666666, 0xFF999999, 0xFFCCCCCC } ) { | 
|  | for (auto mask : { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFFFF } ) { | 
|  | colorPaint.setColor(colors & mask); | 
|  | c16.drawRect({0, 0, 8, 4}, colorPaint); | 
|  | c16.translate(8, 0); | 
|  | } | 
|  | c16.translate(-32, 4); | 
|  | } | 
|  | } | 
|  | canvas->scale(8, 8); | 
|  | canvas->drawBitmap(bm16, 0, 0); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Example | 
|  | #Description | 
|  | Dithering introduces subtle adjustments to color to smooth gradients. | 
|  | Drawing the gradient repeatedly with SkBlendMode::kPlus exaggerates the | 
|  | dither, making it easier to see. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | canvas->clear(0); | 
|  | SkBitmap bm32; | 
|  | bm32.allocPixels(SkImageInfo::Make(20, 10, kN32_SkColorType, kPremul_SkAlphaType)); | 
|  | SkCanvas c32(bm32); | 
|  | SkPoint points[] = {{0, 0}, {20, 0}}; | 
|  | SkColor colors[] = {0xFF334455, 0xFF662211 }; | 
|  | SkPaint paint; | 
|  | paint.setShader(SkGradientShader::MakeLinear( | 
|  | points, colors, nullptr, SK_ARRAY_COUNT(colors), | 
|  | SkShader::kClamp_TileMode, 0, nullptr)); | 
|  | paint.setDither(true); | 
|  | c32.drawPaint(paint); | 
|  | canvas->scale(12, 12); | 
|  | canvas->drawBitmap(bm32, 0, 0); | 
|  | paint.setBlendMode(SkBlendMode::kPlus); | 
|  | canvas->drawBitmap(bm32, 0, 11, &paint); | 
|  | canvas->drawBitmap(bm32, 0, 11, &paint); | 
|  | canvas->drawBitmap(bm32, 0, 11, &paint); | 
|  | } | 
|  | ## | 
|  | #SeeAlso Gradient kRGB_565_SkColorType | 
|  | #Subtopic Dither ## | 
|  |  | 
|  | #Method bool isDither() const | 
|  | #In Dither | 
|  | #Line # returns true if Dither is set ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("paint.isDither() %c= !!(paint.getFlags() & SkPaint::kDither_Flag)\n", | 
|  | paint.isDither() == !!(paint.getFlags() & SkPaint::kDither_Flag) ? '=' : '!'); | 
|  | paint.setDither(true); | 
|  | SkDebugf("paint.isDither() %c= !!(paint.getFlags() & SkPaint::kDither_Flag)\n", | 
|  | paint.isDither() == !!(paint.getFlags() & SkPaint::kDither_Flag) ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | paint.isDither() == !!(paint.getFlags() & SkPaint::kDither_Flag) | 
|  | paint.isDither() == !!(paint.getFlags() & SkPaint::kDither_Flag) | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setDither(bool dither) | 
|  | #In Dither | 
|  | #Line # sets or clears Dither ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setDither(true); | 
|  | paint2.setFlags(paint2.getFlags() | SkPaint::kDither_Flag); | 
|  | SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | paint1 == paint2 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso kRGB_565_SkColorType | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Device_Text | 
|  | #Line # increase precision of glyph position ## | 
|  |  | 
|  | LCD_Text and Subpixel_Text increase the precision of glyph position. | 
|  |  | 
|  | When set, Flags kLCDRenderText_Flag takes advantage of the organization of RGB stripes that | 
|  | create a color, and relies | 
|  | on the small size of the stripe and visual perception to make the color fringing imperceptible. | 
|  | LCD_Text can be enabled on devices that orient stripes horizontally or vertically, and that order | 
|  | the color components as RGB or BGR. | 
|  |  | 
|  | Flags kSubpixelText_Flag uses the pixel transparency to represent a fractional offset. | 
|  | As the opaqueness | 
|  | of the color increases, the edge of the glyph appears to move towards the outside of the pixel. | 
|  |  | 
|  | Either or both techniques can be enabled. | 
|  | kLCDRenderText_Flag and kSubpixelText_Flag are clear by default. | 
|  | LCD_Text or Subpixel_Text can be enabled by default by setting SkPaintDefaults_Flags to | 
|  | kLCDRenderText_Flag or kSubpixelText_Flag (or both) at compile time. | 
|  |  | 
|  | #Example | 
|  | #Description | 
|  | Four commas are drawn normally and with combinations of LCD_Text and Subpixel_Text. | 
|  | When Subpixel_Text is disabled, the comma Glyphs are identical, but not evenly spaced. | 
|  | When Subpixel_Text is enabled, the comma Glyphs are unique, but appear evenly spaced. | 
|  | ## | 
|  |  | 
|  | SkBitmap bitmap; | 
|  | bitmap.allocN32Pixels(24, 33); | 
|  | SkCanvas offscreen(bitmap); | 
|  | offscreen.clear(SK_ColorWHITE); | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(20); | 
|  | for (bool lcd : { false, true }) { | 
|  | paint.setLCDRenderText(lcd); | 
|  | for (bool subpixel : { false, true }) { | 
|  | paint.setSubpixelText(subpixel); | 
|  | offscreen.drawString(",,,,", 0, 4, paint); | 
|  | offscreen.translate(0, 7); | 
|  | } | 
|  | } | 
|  | canvas->drawBitmap(bitmap, 4, 12); | 
|  | canvas->scale(9, 9); | 
|  | canvas->drawBitmap(bitmap, 4, -1); | 
|  | ## | 
|  | #Subtopic Device_Text ## | 
|  |  | 
|  | #Subtopic Linear_Text | 
|  | #Alias Linear_Text ## | 
|  | #Line # selects text rendering as Glyph or Path ## | 
|  |  | 
|  | Linear_Text selects whether text is rendered as a Glyph or as a Path. | 
|  | If kLinearText_Flag is set, it has the same effect as setting Hinting to SkFontHinting::kNormal. | 
|  | If kLinearText_Flag 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"; | 
|  | for (int textSize : { 12, 24 } ) { | 
|  | paint.setTextSize(textSize); | 
|  | for (auto linearText : { false, true } ) { | 
|  | paint.setLinearText(linearText); | 
|  | SkString width; | 
|  | width.appendScalar(paint.measureText(testStr, SK_ARRAY_COUNT(testStr), nullptr)); | 
|  | canvas->translate(0, textSize + 4); | 
|  | canvas->drawString(testStr, 10, 0, paint); | 
|  | canvas->drawString(width, 128, 0, paint); | 
|  | } | 
|  | } | 
|  | } | 
|  | ## | 
|  |  | 
|  | #SeeAlso isLinearText Hinting | 
|  | ## | 
|  |  | 
|  | #Subtopic Subpixel_Text | 
|  | #Alias Subpixel_Text ## | 
|  | #Line # uses pixel transparency to represent fractional offset ## | 
|  |  | 
|  | Flags kSubpixelText_Flag uses the pixel transparency to represent a fractional offset. | 
|  | 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 Subpixel_Text 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 Subpixel_Text ## | 
|  | #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 ## | 
|  |  | 
|  | # make this a top level name, since it is under subtopic Device_Text | 
|  | #Alias LCD_Text | 
|  | #Substitute LCD text | 
|  | ## | 
|  |  | 
|  | When set, Flags kLCDRenderText_Flag takes advantage of the organization of RGB stripes that | 
|  | create a color, and relies | 
|  | on the small size of the stripe and visual perception to make the color fringing imperceptible. | 
|  | LCD_Text can be enabled on devices that orient stripes horizontally or vertically, and that order | 
|  | the color components as RGB or BGR. | 
|  | #Subtopic LCD_Text ## | 
|  |  | 
|  | #Method bool isLCDRenderText() const | 
|  | #In LCD_Text | 
|  | #Line # returns true if LCD_Text 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 LCD_Text ## | 
|  | #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 Font_Embedded_Bitmaps | 
|  | #Line # custom sized bitmap Glyphs ## | 
|  | #Alias Font_Embedded_Bitmaps ## # long-winded enough, alias so I don't type Paint_Font_... | 
|  |  | 
|  | Font_Embedded_Bitmaps allows selecting custom sized bitmap Glyphs. | 
|  | Flags kEmbeddedBitmapText_Flag when set chooses an embedded bitmap glyph over an outline contained | 
|  | in a font if the platform supports this option. | 
|  |  | 
|  | FreeType selects the bitmap glyph if available when kEmbeddedBitmapText_Flag is set, and selects | 
|  | the outline glyph if kEmbeddedBitmapText_Flag is clear. | 
|  | Windows may select the bitmap glyph but is not required to do so. | 
|  | OS_X and iOS do not support this option. | 
|  |  | 
|  | Font_Embedded_Bitmaps is disabled by default. | 
|  | Font_Embedded_Bitmaps can be enabled by default by setting SkPaintDefaults_Flags to | 
|  | kEmbeddedBitmapText_Flag at compile time. | 
|  |  | 
|  | #Example | 
|  | #ToDo image will only output on Ubuntu ... how to handle that in fiddle? ## | 
|  | #Platform !fiddle | 
|  | #Description | 
|  | The "hintgasp" TrueType font in the Skia resources/fonts directory | 
|  | includes an embedded bitmap Glyph at odd font sizes. This example works | 
|  | on platforms that use FreeType as their Font_Engine. | 
|  | Windows may, but is not required to, return a bitmap glyph if | 
|  | kEmbeddedBitmapText_Flag is set. | 
|  | ## | 
|  | #Image  embeddedbitmap.png | 
|  |  | 
|  | SkBitmap bitmap; | 
|  | bitmap.allocN32Pixels(30, 15); | 
|  | bitmap.eraseColor(0); | 
|  | SkCanvas offscreen(bitmap); | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(13); | 
|  | paint.setTypeface(MakeResourceAsTypeface("fonts/hintgasp.ttf")); | 
|  | for (bool embedded : { false, true}) { | 
|  | paint.setEmbeddedBitmapText(embedded); | 
|  | offscreen.drawString("A", embedded ? 5 : 15, 15, paint); | 
|  | } | 
|  | canvas->drawBitmap(bitmap, 0, 0); | 
|  | canvas->scale(10, 10); | 
|  | canvas->drawBitmap(bitmap, -2, 1); | 
|  | ## | 
|  | #Subtopic Font_Embedded_Bitmaps ## | 
|  |  | 
|  | #Method bool isEmbeddedBitmapText() const | 
|  | #In Font_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 Font_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 ## | 
|  | #Substitute auto-hinting | 
|  |  | 
|  | If Hinting is set to SkFontHinting::kNormal or SkFontHinting::kFull, Automatic_Hinting | 
|  | instructs the Font_Manager to always hint Glyphs. | 
|  | Automatic_Hinting has no effect if Hinting is set to SkFontHinting::kNone or | 
|  | SkFontHinting::kSlight. | 
|  |  | 
|  | Automatic_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 | 
|  | #Line # approximate font styles ## | 
|  |  | 
|  | Fake_Bold approximates the bold font style accompanying a normal font when a bold font face | 
|  | is not available. Skia does not provide font substitution; it is up to the client to find the | 
|  | bold font face using the platform Font_Manager. | 
|  |  | 
|  | Use Text_Skew_X to approximate an italic font style when the italic font face | 
|  | is not available. | 
|  |  | 
|  | A FreeType based port may define SK_USE_FREETYPE_EMBOLDEN at compile time to direct | 
|  | the font engine to create the bold Glyphs. Otherwise, the extra bold is computed | 
|  | by increasing the stroke width and setting the Style to kStrokeAndFill_Style as needed. | 
|  |  | 
|  | Fake_Bold is disabled by default. | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(40); | 
|  | canvas->drawString("OjYy_-", 10, 35, paint); | 
|  | paint.setFakeBoldText(true); | 
|  | canvas->drawString("OjYy_-", 10, 75, paint); | 
|  | // create a custom fake bold by varying the stroke width | 
|  | paint.setFakeBoldText(false); | 
|  | paint.setStyle(SkPaint::kStrokeAndFill_Style); | 
|  | paint.setStrokeWidth(40.f / 48); | 
|  | canvas->drawString("OjYy_-", 10, 115, paint); | 
|  | } | 
|  | ## | 
|  | #Subtopic Fake_Bold ## | 
|  |  | 
|  | #Method bool isFakeBoldText() const | 
|  | #In Fake_Bold | 
|  | #Line # returns true if Fake_Bold 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 Fake_Bold ## | 
|  | #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 Full_Hinting_Spacing | 
|  | #Line # glyph spacing affected by hinting ## | 
|  | #Alias Full_Hinting_Spacing ## # long winded enough -- maybe things with two underscores auto-aliased? | 
|  |  | 
|  | if Hinting is set to SkFontHinting::kFull, Full_Hinting_Spacing adjusts the character | 
|  | spacing by the difference of the hinted and unhinted Left_Side_Bearing and | 
|  | Right_Side_Bearing. Full_Hinting_Spacing only applies to platforms that use | 
|  | FreeType as their Font_Engine. | 
|  |  | 
|  | Full_Hinting_Spacing is not related to text kerning, where the space between | 
|  | a specific pair of characters is adjusted using data in the font kerning tables. | 
|  | #Subtopic Full_Hinting_Spacing ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Filter_Quality_Methods | 
|  | #Line # get and set Filter_Quality ## | 
|  |  | 
|  | Filter_Quality trades speed for image filtering when the image is scaled. | 
|  | A lower Filter_Quality draws faster, but has less fidelity. | 
|  | A higher Filter_Quality draws slower, but looks better. | 
|  | If the image is drawn without scaling, the Filter_Quality choice will not result | 
|  | in a noticeable difference. | 
|  |  | 
|  | Filter_Quality is used in Paint passed as a parameter to | 
|  | #List | 
|  | # SkCanvas::drawBitmap ## | 
|  | # SkCanvas::drawBitmapRect ## | 
|  | # SkCanvas::drawImage ## | 
|  | # SkCanvas::drawImageRect ## | 
|  | #ToDo probably more... ## | 
|  | #List ## | 
|  | and when Paint has a Shader specialization that uses Image or Bitmap. | 
|  |  | 
|  | Filter_Quality is kNone_SkFilterQuality by default. | 
|  |  | 
|  | #Example | 
|  | #Image 3 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | canvas->scale(.2f, .2f); | 
|  | for (SkFilterQuality q : { kNone_SkFilterQuality, kLow_SkFilterQuality, | 
|  | kMedium_SkFilterQuality, kHigh_SkFilterQuality } ) { | 
|  | paint.setFilterQuality(q); | 
|  | canvas->drawImage(image.get(), 0, 0, &paint); | 
|  | canvas->translate(550, 0); | 
|  | if (kLow_SkFilterQuality == q) canvas->translate(-1100, 550); | 
|  | } | 
|  | } | 
|  | ## | 
|  | #Subtopic Filter_Quality_Methods ## | 
|  |  | 
|  | #Method SkFilterQuality getFilterQuality() const | 
|  | #In Filter_Quality_Methods | 
|  | #Line # returns Filter_Quality, image filtering level ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("kNone_SkFilterQuality %c= paint.getFilterQuality()\n", | 
|  | kNone_SkFilterQuality == paint.getFilterQuality() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | kNone_SkFilterQuality == paint.getFilterQuality() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  |  | 
|  | #Method void setFilterQuality(SkFilterQuality quality) | 
|  | #In Filter_Quality_Methods | 
|  | #Line # sets Filter_Quality, the image filtering level ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | paint.setFilterQuality(kHigh_SkFilterQuality); | 
|  | SkDebugf("kHigh_SkFilterQuality %c= paint.getFilterQuality()\n", | 
|  | kHigh_SkFilterQuality == paint.getFilterQuality() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | kHigh_SkFilterQuality == paint.getFilterQuality() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso SkFilterQuality Image_Scaling | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Color_Methods | 
|  | #Line # get and set Color ## | 
|  |  | 
|  | #Table | 
|  | #Legend | 
|  | # name                  # description                                          ## | 
|  | #Legend ## | 
|  | # getColor              # returns Color_Alpha and RGB, one drawing color ## | 
|  | # setColor              # sets Color_Alpha and RGB, one drawing color    ## | 
|  | #Table ## | 
|  |  | 
|  | Color specifies the red, blue, green, and Color_Alpha | 
|  | values used to draw a filled or stroked shape in a 32-bit value. Each component | 
|  | occupies 8-bits, ranging from zero: no contribution; to 255: full intensity. | 
|  | All values in any combination are valid. | 
|  |  | 
|  | Color is not Premultiplied; Color_Alpha sets the transparency independent of | 
|  | RGB: red, blue, and green. | 
|  |  | 
|  | The bit positions of Color_Alpha and RGB are independent of the bit | 
|  | positions on the output device, which may have more or fewer bits, and may have | 
|  | a different arrangement. | 
|  |  | 
|  | #Table | 
|  | #Legend | 
|  | # bit positions # Color_Alpha # red # blue # green ## | 
|  | #Legend ## | 
|  | #               # 31 - 24     # 23 - 16       # 15 - 8         # 7 - 0           ## | 
|  | #Table ## | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setColor(0x8000FF00);  // transparent green | 
|  | canvas->drawCircle(50, 50, 40, paint); | 
|  | paint.setARGB(128, 255, 0, 0); // transparent red | 
|  | canvas->drawCircle(80, 50, 40, paint); | 
|  | paint.setColor(SK_ColorBLUE); | 
|  | paint.setAlpha(0x80); | 
|  | canvas->drawCircle(65, 65, 40, paint); | 
|  | } | 
|  | ## | 
|  | #Subtopic Color_Methods ## | 
|  |  | 
|  | #Method SkColor getColor() const | 
|  | #In Color_Methods | 
|  | #Line # returns Color_Alpha and RGB, one drawing color ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | paint.setColor(SK_ColorYELLOW); | 
|  | SkColor y = paint.getColor(); | 
|  | SkDebugf("Yellow is %d%% red, %d%% green, and %d%% blue.\n", (int) (SkColorGetR(y) / 2.55f), | 
|  | (int) (SkColorGetG(y) / 2.55f), (int) (SkColorGetB(y) / 2.55f)); | 
|  |  | 
|  | #StdOut | 
|  | Yellow is 100% red, 100% green, and 0% blue. | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso getColor4f SkColor | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method SkColor4f getColor4f() const | 
|  | #In Color_Methods | 
|  | #Line # returns Color_Alpha and RGB, one drawing color ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | paint.setColor(SK_ColorYELLOW); | 
|  | SkColor4f y = paint.getColor4f(); | 
|  | SkDebugf("Yellow is %d%% red, %d%% green, and %d%% blue.\n", (int) (y.fR * 100), | 
|  | (int) (y.fG * 100), (int) (y.fB * 100)); | 
|  |  | 
|  | #StdOut | 
|  | Yellow is 100% red, 100% green, and 0% blue. | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso getColor SkColor | 
|  | ## | 
|  |  | 
|  |  | 
|  | #Method void setColor(SkColor color) | 
|  | #In Color_Methods | 
|  | #Line # sets Color_Alpha and RGB, one drawing color ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint green1, green2; | 
|  | unsigned a = 255; | 
|  | unsigned r = 0; | 
|  | unsigned g = 255; | 
|  | unsigned b = 0; | 
|  | green1.setColor((a << 24) + (r << 16) + (g << 8) + (b << 0)); | 
|  | green2.setColor(0xFF00FF00); | 
|  | SkDebugf("green1 %c= green2\n", green1 == green2 ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | green1 == green2 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso SkColor setColor4f setARGB SkColorSetARGB | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setColor4f(const SkColor4f& color, SkColorSpace* colorSpace) | 
|  | #In Color_Methods | 
|  | #Line # sets Color_Alpha and RGB, one drawing color ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint green1, green2; | 
|  | green1.setColor4f({0, 1, 0, 1}, nullptr);  // R=0 G=1 B=0 A=1 | 
|  | green2.setColor(0xFF00FF00); // A=255 R=0 G=255 B=0 | 
|  | SkDebugf("green1 %c= green2\n", green1 == green2 ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | green1 == green2 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso SkColor setColor setARGB SkColorSetARGB | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Alpha_Methods | 
|  | #Line # get and set Alpha ## | 
|  |  | 
|  | Color_Alpha sets the transparency independent of RGB: red, blue, and green. | 
|  | #Subtopic Alpha_Methods ## | 
|  |  | 
|  | #Method uint8_t getAlpha() const | 
|  | #In Alpha_Methods | 
|  | #Line # returns Color_Alpha, color opacity ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("255 %c= paint.getAlpha()\n", 255 == paint.getAlpha() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | 255 == paint.getAlpha() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setAlpha(U8CPU a) | 
|  | #In Alpha_Methods | 
|  | #Line # sets Color_Alpha, color opacity ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | paint.setColor(0x00112233); | 
|  | paint.setAlpha(0x44); | 
|  | SkDebugf("0x44112233 %c= paint.getColor()\n", 0x44112233 == paint.getColor() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | 0x44112233 == paint.getColor() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) | 
|  | #In Color_Methods | 
|  | #Line # sets color by component ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint transRed1, transRed2; | 
|  | transRed1.setARGB(255 / 2, 255, 0, 0); | 
|  | transRed2.setColor(SkColorSetARGB(255 / 2, 255, 0, 0)); | 
|  | SkDebugf("transRed1 %c= transRed2", transRed1 == transRed2 ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | transRed1 == transRed2 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso setColor SkColorSetARGB | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Style | 
|  | #Line # geometry filling, stroking ## | 
|  |  | 
|  | Style specifies if the geometry is filled, stroked, or both filled and stroked. | 
|  | Some shapes ignore Style and are always drawn filled or stroked. | 
|  |  | 
|  | #Subtopic Fill | 
|  | Set Style to kFill_Style to fill the shape. | 
|  | The fill covers the area inside the geometry for most shapes. | 
|  | #Subtopic Fill ## | 
|  |  | 
|  | #Subtopic Stroke | 
|  | Set Style to kStroke_Style to stroke the shape. | 
|  |  | 
|  | The stroke covers the area described by following the shape edge with a pen or brush of | 
|  | Stroke_Width. The area covered where the shape starts and stops is described by Stroke_Cap. | 
|  | The area covered where the shape turns a corner is described by Stroke_Join. | 
|  | The stroke is centered on the shape; it extends equally on either side of the shape edge. | 
|  | #Subtopic Stroke ## | 
|  |  | 
|  | As Stroke_Width gets smaller, the drawn path frame is thinner. Stroke_Width less than one | 
|  | may have gaps, and if kAntiAlias_Flag is set, Color_Alpha will increase to visually decrease coverage. | 
|  |  | 
|  | #SeeAlso Path_Fill_Type Path_Effect Style_Fill Style_Stroke | 
|  | #Subtopic Style ## | 
|  |  | 
|  | #Subtopic Hairline | 
|  | Stroke_Width of zero has a special meaning and switches drawing to use Hairline. | 
|  | Hairline draws the thinnest continuous frame. If kAntiAlias_Flag is clear, adjacent pixels | 
|  | flow horizontally, vertically,or diagonally. | 
|  |  | 
|  | #ToDo  what is the description of Anti_Aliased hairlines? ## | 
|  |  | 
|  | Path drawing with Hairline may hit the same pixel more than once. For instance, Path containing | 
|  | two lines in one Path_Contour will draw the corner point once, but may both lines may draw the adjacent | 
|  | pixel. If kAntiAlias_Flag is set, transparency is applied twice, resulting in a darker pixel. Some | 
|  | GPU-backed implementations apply transparency at a later drawing stage, avoiding double hit pixels | 
|  | while stroking. | 
|  |  | 
|  | #SeeAlso Path_Fill_Type Path_Effect Style_Fill Style_Stroke | 
|  | #Subtopic Hairline ## | 
|  |  | 
|  | #Enum Style | 
|  | #Line # stroke, fill, or both ## | 
|  |  | 
|  | #Code | 
|  | #Populate | 
|  | ## | 
|  |  | 
|  | #Code | 
|  | #In Constant | 
|  | #Filter kStyle | 
|  | #Populate | 
|  | ## | 
|  |  | 
|  | Set Style to fill, stroke, or both fill and stroke geometry. | 
|  | The stroke and fill | 
|  | share all paint attributes; for instance, they are drawn with the same color. | 
|  |  | 
|  | Use kStrokeAndFill_Style to avoid hitting the same pixels twice with a stroke draw and | 
|  | a fill draw. | 
|  |  | 
|  | #Const  kFill_Style 0 | 
|  | #Line # set to fill geometry ## | 
|  | Applies to Rect, Region, Round_Rect, Circles, Ovals, Path, and Text. | 
|  | Bitmap, Image, Patches, Region, Sprites, and Vertices are painted as if | 
|  | kFill_Style is set, and ignore the set Style. | 
|  | The Path_Fill_Type specifies additional rules to fill the area outside the path edge, | 
|  | and to create an unfilled hole inside the shape. | 
|  | Style is set to kFill_Style by default. | 
|  | ## | 
|  |  | 
|  | #Const kStroke_Style 1 | 
|  | #Line # set to stroke geometry ## | 
|  | Applies to Rect, Region, Round_Rect, Arcs, Circles, Ovals, Path, and Text. | 
|  | Arcs, Lines, and points, are always drawn as if kStroke_Style is set, | 
|  | and ignore the set Style. | 
|  | The stroke construction is unaffected by the Path_Fill_Type. | 
|  | ## | 
|  |  | 
|  | #Const kStrokeAndFill_Style 2 | 
|  | #Line # sets to stroke and fill geometry ## | 
|  | Applies to Rect, Region, Round_Rect, Circles, Ovals, Path, and Text. | 
|  | Path is treated as if it is set to SkPath::kWinding_FillType, | 
|  | and the set Path_Fill_Type is ignored. | 
|  | ## | 
|  |  | 
|  | #Const kStyleCount 3 | 
|  | #Line # number of different Style values defined ## | 
|  | May be used to verify that Style is a legal value. | 
|  | ## | 
|  |  | 
|  | #Enum Style ## | 
|  |  | 
|  | #Method Style getStyle() const | 
|  | #In Style | 
|  | #Line # returns Style: stroke, fill, or both ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("SkPaint::kFill_Style %c= paint.getStyle()\n", | 
|  | SkPaint::kFill_Style == paint.getStyle() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | SkPaint::kFill_Style == paint.getStyle() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso Style setStyle | 
|  | ## | 
|  |  | 
|  | #Method void setStyle(Style style) | 
|  | #In Style | 
|  | #Line # sets Style: stroke, fill, or both ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setStrokeWidth(5); | 
|  | SkRegion region; | 
|  | region.op(140, 10, 160, 30, SkRegion::kUnion_Op); | 
|  | region.op(170, 40, 190, 60, SkRegion::kUnion_Op); | 
|  | SkBitmap bitmap; | 
|  | bitmap.setInfo(SkImageInfo::MakeA8(50, 50), 50); | 
|  | uint8_t pixels[50][50]; | 
|  | for (int x = 0; x < 50; ++x) { | 
|  | for (int y = 0; y < 50; ++y) { | 
|  | pixels[y][x] = (x + y) % 5 ? 0xFF : 0x00; | 
|  | } | 
|  | } | 
|  | bitmap.setPixels(pixels); | 
|  | for (auto style : { SkPaint::kFill_Style, | 
|  | SkPaint::kStroke_Style, | 
|  | SkPaint::kStrokeAndFill_Style }) { | 
|  | paint.setStyle(style); | 
|  | canvas->drawLine(10, 10, 60, 60, paint); | 
|  | canvas->drawRect({80, 10, 130, 60}, paint); | 
|  | canvas->drawRegion(region, paint); | 
|  | canvas->drawBitmap(bitmap, 200, 10, &paint); | 
|  | canvas->translate(0, 80); | 
|  | } | 
|  | } | 
|  | ## | 
|  |  | 
|  | #SeeAlso Style getStyle | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Stroke_Width | 
|  | #Line # thickness perpendicular to geometry ## | 
|  |  | 
|  | Stroke_Width sets the width for stroking. The width is the thickness | 
|  | of the stroke perpendicular to the path direction when the paint style is | 
|  | set to kStroke_Style or kStrokeAndFill_Style. | 
|  |  | 
|  | When width is greater than zero, the stroke encompasses as many pixels partially | 
|  | or fully as needed. When the width equals zero, the paint enables hairlines; | 
|  | the stroke is always one pixel wide. | 
|  |  | 
|  | The stroke dimensions are scaled by the canvas matrix, but Hairline stroke | 
|  | remains one pixel wide regardless of scaling. | 
|  |  | 
|  | The default width for the paint is zero. | 
|  |  | 
|  | #Example | 
|  | #Height 170 | 
|  | #Platform raster gpu | 
|  | #Description | 
|  | The pixels hit to represent thin lines vary with the angle of the | 
|  | line and the platform implementation. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | for (bool antialias : { false, true }) { | 
|  | paint.setAntiAlias(antialias); | 
|  | for (int width = 0; width <= 4; ++width) { | 
|  | SkScalar offset = antialias * 100 + width * 20; | 
|  | paint.setStrokeWidth(width * 0.25f); | 
|  | canvas->drawLine(10 + offset,  10, 20 + offset,  60, paint); | 
|  | canvas->drawLine(10 + offset, 110, 60 + offset, 160, paint); | 
|  | } | 
|  | } | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Method SkScalar getStrokeWidth() const | 
|  |  | 
|  | #In Stroke_Width | 
|  | #Line # returns thickness of the stroke ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("0 %c= paint.getStrokeWidth()\n", 0 == paint.getStrokeWidth() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | 0 == paint.getStrokeWidth() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setStrokeWidth(SkScalar width) | 
|  |  | 
|  | #In Stroke_Width | 
|  | #Line # sets thickness of the stroke ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | paint.setStrokeWidth(5); | 
|  | paint.setStrokeWidth(-1); | 
|  | SkDebugf("5 %c= paint.getStrokeWidth()\n", 5 == paint.getStrokeWidth() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | 5 == paint.getStrokeWidth() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Stroke_Width ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Miter_Limit | 
|  | #Line # maximum length of stroked corners ## | 
|  |  | 
|  | Miter_Limit specifies the maximum miter length, | 
|  | relative to the stroke width. | 
|  |  | 
|  | Miter_Limit is used when the Stroke_Join | 
|  | is set to kMiter_Join, and the Style is either kStroke_Style | 
|  | or kStrokeAndFill_Style. | 
|  |  | 
|  | If the miter at a corner exceeds this limit, kMiter_Join | 
|  | is replaced with kBevel_Join. | 
|  |  | 
|  | Miter_Limit can be computed from the corner angle using: | 
|  | #Formula # miter limit = 1 / sin ( angle / 2 ) ##. | 
|  |  | 
|  | Miter_Limit default value is 4. | 
|  | The default may be changed at compile time by setting SkPaintDefaults_MiterLimit | 
|  | in "SkUserConfig.h" or as a define supplied by the build environment. | 
|  |  | 
|  | Here are some miter limits and the angles that triggers them. | 
|  | #Table | 
|  | #Legend | 
|  | # miter limit    # angle in degrees ## | 
|  | #Legend ## | 
|  | # 10             # 11.48            ## | 
|  | # 9              # 12.76            ## | 
|  | # 8              # 14.36            ## | 
|  | # 7              # 16.43            ## | 
|  | # 6              # 19.19            ## | 
|  | # 5              # 23.07            ## | 
|  | # 4              # 28.96            ## | 
|  | # 3              # 38.94            ## | 
|  | # 2              # 60               ## | 
|  | # 1              # 180              ## | 
|  | #Table ## | 
|  |  | 
|  | #Example | 
|  | #Height 170 | 
|  | #Width 384 | 
|  | #Description | 
|  | This example draws a stroked corner and the miter length beneath. | 
|  | When the miter limit is decreased slightly, the miter join is replaced | 
|  | by a bevel join. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPoint pts[] = {{ 10, 50 }, { 110, 80 }, { 10, 110 }}; | 
|  | SkVector v[] = { pts[0] - pts[1], pts[2] - pts[1] }; | 
|  | SkScalar angle1 = SkScalarATan2(v[0].fY, v[0].fX); | 
|  | SkScalar angle2 = SkScalarATan2(v[1].fY, v[1].fX); | 
|  | const SkScalar strokeWidth = 20; | 
|  | SkScalar miterLimit = 1 / SkScalarSin((angle2 - angle1) / 2); | 
|  | SkScalar miterLength = strokeWidth * miterLimit; | 
|  | SkPath path; | 
|  | path.moveTo(pts[0]); | 
|  | path.lineTo(pts[1]); | 
|  | path.lineTo(pts[2]); | 
|  | SkPaint paint;  // set to default kMiter_Join | 
|  | paint.setAntiAlias(true); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeMiter(miterLimit); | 
|  | paint.setStrokeWidth(strokeWidth); | 
|  | canvas->drawPath(path, paint); | 
|  | paint.setStrokeWidth(1); | 
|  | canvas->drawLine(pts[1].fX - miterLength / 2, pts[1].fY + 50, | 
|  | pts[1].fX + miterLength / 2, pts[1].fY + 50, paint); | 
|  | canvas->translate(200, 0); | 
|  | miterLimit *= 0.99f; | 
|  | paint.setStrokeMiter(miterLimit); | 
|  | paint.setStrokeWidth(strokeWidth); | 
|  | canvas->drawPath(path, paint); | 
|  | paint.setStrokeWidth(1); | 
|  | canvas->drawLine(pts[1].fX - miterLength / 2, pts[1].fY + 50, | 
|  | pts[1].fX + miterLength / 2, pts[1].fY + 50, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Method SkScalar getStrokeMiter() const | 
|  |  | 
|  | #In Miter_Limit | 
|  | #Line # returns Miter_Limit, angles with sharp corners ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("default miter limit == %g\n", paint.getStrokeMiter()); | 
|  |  | 
|  | #StdOut | 
|  | default miter limit == 4 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso Miter_Limit setStrokeMiter Join | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setStrokeMiter(SkScalar miter) | 
|  |  | 
|  | #In Miter_Limit | 
|  | #Line # sets Miter_Limit, angles with sharp corners ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | paint.setStrokeMiter(8); | 
|  | paint.setStrokeMiter(-1); | 
|  | SkDebugf("default miter limit == %g\n", paint.getStrokeMiter()); | 
|  |  | 
|  | #StdOut | 
|  | default miter limit == 8 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso Miter_Limit getStrokeMiter Join | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Miter_Limit ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Stroke_Cap | 
|  | #Line # decorations at ends of open strokes ## | 
|  | #Subtopic Stroke_Cap ## | 
|  |  | 
|  | #Enum Cap | 
|  | #Line # start and end geometry on stroked shapes ## | 
|  |  | 
|  | #Code | 
|  | #Populate | 
|  | ## | 
|  |  | 
|  | #Code | 
|  | #In Constant | 
|  | #Filter kCap | 
|  | #Populate | 
|  | ## | 
|  |  | 
|  | Stroke_Cap draws at the beginning and end of an open Path_Contour. | 
|  |  | 
|  | #Const kButt_Cap 0 | 
|  | #Line # no stroke extension ## | 
|  | Does not extend the stroke past the beginning or the end. | 
|  | ## | 
|  | #Const kRound_Cap 1 | 
|  | #Line # adds circle ## | 
|  | Adds a circle with a diameter equal to Stroke_Width at the beginning | 
|  | and end. | 
|  | ## | 
|  | #Const kSquare_Cap 2 | 
|  | #Line # adds square ## | 
|  | Adds a square with sides equal to Stroke_Width at the beginning | 
|  | and end. The square sides are parallel to the initial and final direction | 
|  | of the stroke. | 
|  | ## | 
|  | #Const kLast_Cap 2 | 
|  | #Line # largest Stroke_Cap value ## | 
|  | Equivalent to the largest value for Stroke_Cap. | 
|  | ## | 
|  | #Const kDefault_Cap 0 | 
|  | #Line # equivalent to kButt_Cap ## | 
|  | Stroke_Cap is set to kButt_Cap by default. | 
|  | ## | 
|  |  | 
|  | #Const kCapCount 3 | 
|  | #Line # number of different Stroke_Cap values defined ## | 
|  | May be used to verify that Stroke_Cap is a legal value. | 
|  | ## | 
|  | #Enum ## | 
|  |  | 
|  | Stroke describes the area covered by a pen of Stroke_Width as it | 
|  | follows the Path_Contour, moving parallel to the contour direction. | 
|  |  | 
|  | If the Path_Contour is not terminated by SkPath::kClose_Verb, the contour has a | 
|  | visible beginning and end. | 
|  |  | 
|  | Path_Contour may start and end at the same point; defining Zero_Length_Contour. | 
|  |  | 
|  | kButt_Cap and Zero_Length_Contour is not drawn. | 
|  | kRound_Cap and Zero_Length_Contour draws a circle of diameter Stroke_Width | 
|  | at the contour point. | 
|  | kSquare_Cap and Zero_Length_Contour draws an upright square with a side of | 
|  | Stroke_Width at the contour point. | 
|  |  | 
|  | Stroke_Cap is kButt_Cap by default. | 
|  |  | 
|  | #Example | 
|  | #Height 200 | 
|  | SkPaint paint; | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(20); | 
|  | SkPath path; | 
|  | path.moveTo(30, 30); | 
|  | path.lineTo(30, 30); | 
|  | path.moveTo(70, 30); | 
|  | path.lineTo(90, 40); | 
|  | for (SkPaint::Cap c : { SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap } ) { | 
|  | paint.setStrokeCap(c); | 
|  | canvas->drawPath(path, paint); | 
|  | canvas->translate(0, 70); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Method Cap getStrokeCap() const | 
|  |  | 
|  | #In Stroke_Cap | 
|  | #Line # returns Cap, the area drawn at path ends ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("kButt_Cap %c= default stroke cap\n", | 
|  | SkPaint::kButt_Cap == paint.getStrokeCap() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | kButt_Cap == default stroke cap | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso Stroke_Cap setStrokeCap | 
|  | ## | 
|  |  | 
|  | #Method void setStrokeCap(Cap cap) | 
|  |  | 
|  | #In Stroke_Cap | 
|  | #Line # sets Cap, the area drawn at path ends ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | paint.setStrokeCap(SkPaint::kRound_Cap); | 
|  | paint.setStrokeCap((SkPaint::Cap) SkPaint::kCapCount); | 
|  | SkDebugf("kRound_Cap %c= paint.getStrokeCap()\n", | 
|  | SkPaint::kRound_Cap == paint.getStrokeCap() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | kRound_Cap == paint.getStrokeCap() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso Stroke_Cap getStrokeCap | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Stroke_Join | 
|  | #Line # decoration at corners of strokes ## | 
|  | #Subtopic Stroke_Join ## | 
|  |  | 
|  | Stroke_Join draws at the sharp corners of an open or closed Path_Contour. | 
|  |  | 
|  | Stroke describes the area covered by a pen of Stroke_Width as it | 
|  | follows the Path_Contour, moving parallel to the contour direction. | 
|  |  | 
|  | If the contour direction changes abruptly, because the tangent direction leading | 
|  | to the end of a curve within the contour does not match the tangent direction of | 
|  | the following curve, the pair of curves meet at Stroke_Join. | 
|  |  | 
|  | #Example | 
|  | #Height 200 | 
|  | SkPaint paint; | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(20); | 
|  | SkPath path; | 
|  | path.moveTo(30, 20); | 
|  | path.lineTo(40, 40); | 
|  | path.conicTo(70, 20, 100, 20, .707f); | 
|  | for (SkPaint::Join j : { SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join } ) { | 
|  | paint.setStrokeJoin(j); | 
|  | canvas->drawPath(path, paint); | 
|  | canvas->translate(0, 70); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Enum Join | 
|  | #Line # corner geometry on stroked shapes ## | 
|  | #Code | 
|  | #Populate | 
|  | ## | 
|  |  | 
|  | #Code | 
|  | #In Constant | 
|  | #Filter kJoin | 
|  | #Populate | 
|  | ## | 
|  |  | 
|  | Join specifies how corners are drawn when a shape is stroked. Join | 
|  | affects the four corners of a stroked rectangle, and the connected segments in a | 
|  | stroked path. | 
|  |  | 
|  | Choose miter join to draw sharp corners. Choose round join to draw a circle with a | 
|  | radius equal to the stroke width on top of the corner. Choose bevel join to minimally | 
|  | connect the thick strokes. | 
|  |  | 
|  | The fill path constructed to describe the stroked path respects the join setting but may | 
|  | not contain the actual join. For instance, a fill path constructed with round joins does | 
|  | not necessarily include circles at each connected segment. | 
|  |  | 
|  | #Const kMiter_Join 0 | 
|  | #Line # extends to Miter_Limit ## | 
|  | Extends the outside corner to the extent allowed by Miter_Limit. | 
|  | If the extension exceeds Miter_Limit, kBevel_Join is used instead. | 
|  | ## | 
|  |  | 
|  | #Const kRound_Join 1 | 
|  | #Line # adds circle ## | 
|  | Adds a circle with a diameter of Stroke_Width at the sharp corner. | 
|  | ## | 
|  |  | 
|  | #Const kBevel_Join 2 | 
|  | #Line # connects outside edges ## | 
|  | Connects the outside edges of the sharp corner. | 
|  | ## | 
|  |  | 
|  | #Const kLast_Join 2 | 
|  | #Line # equivalent to the largest value for Stroke_Join ## | 
|  | ## | 
|  |  | 
|  | #Const kDefault_Join 1 | 
|  | #Line # equivalent to kMiter_Join ## | 
|  | Stroke_Join is set to kMiter_Join by default. | 
|  | ## | 
|  |  | 
|  | #Const kJoinCount 3 | 
|  | #Line # number of different Stroke_Join values defined ## | 
|  | May be used to verify that Stroke_Join is a legal value. | 
|  | ## | 
|  |  | 
|  | #Example | 
|  | #Width 462 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPath path; | 
|  | path.moveTo(10, 50); | 
|  | path.quadTo(35, 110, 60, 210); | 
|  | path.quadTo(105, 110, 130, 10); | 
|  | SkPaint paint;  // set to default kMiter_Join | 
|  | paint.setAntiAlias(true); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(20); | 
|  | canvas->drawPath(path, paint); | 
|  | canvas->translate(150, 0); | 
|  | paint.setStrokeJoin(SkPaint::kBevel_Join); | 
|  | canvas->drawPath(path, paint); | 
|  | canvas->translate(150, 0); | 
|  | paint.setStrokeJoin(SkPaint::kRound_Join); | 
|  | canvas->drawPath(path, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #SeeAlso setStrokeJoin getStrokeJoin setStrokeMiter getStrokeMiter | 
|  |  | 
|  | #Enum ## | 
|  |  | 
|  | #Method Join getStrokeJoin() const | 
|  |  | 
|  | #In Stroke_Join | 
|  | #Line # returns Join, geometry on path corners ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("kMiter_Join %c= default stroke join\n", | 
|  | SkPaint::kMiter_Join == paint.getStrokeJoin() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | kMiter_Join == default stroke join | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso Stroke_Join setStrokeJoin | 
|  | ## | 
|  |  | 
|  | #Method void setStrokeJoin(Join join) | 
|  |  | 
|  | #In Stroke_Join | 
|  | #Line # sets Join, geometry on path corners ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | paint.setStrokeJoin(SkPaint::kMiter_Join); | 
|  | paint.setStrokeJoin((SkPaint::Join) SkPaint::kJoinCount); | 
|  | SkDebugf("kMiter_Join %c= paint.getStrokeJoin()\n", | 
|  | SkPaint::kMiter_Join == paint.getStrokeJoin() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | kMiter_Join == paint.getStrokeJoin() | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso Stroke_Join getStrokeJoin | 
|  | ## | 
|  |  | 
|  | #SeeAlso Miter_Limit | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Fill_Path | 
|  | #Line # make Path from Path_Effect, stroking ## | 
|  |  | 
|  | Fill_Path creates a Path by applying the Path_Effect, followed by the Style_Stroke. | 
|  |  | 
|  | If Paint contains Path_Effect, Path_Effect operates on the source Path; the result | 
|  | replaces the destination Path. Otherwise, the source Path is replaces the | 
|  | destination Path. | 
|  |  | 
|  | Fill Path can request the Path_Effect to restrict to a culling rectangle, but | 
|  | the Path_Effect is not required to do so. | 
|  |  | 
|  | If Style is kStroke_Style or kStrokeAndFill_Style, | 
|  | and Stroke_Width is greater than zero, the Stroke_Width, Stroke_Cap, Stroke_Join, | 
|  | and Miter_Limit operate on the destination Path, replacing it. | 
|  |  | 
|  | Fill Path can specify the precision used by Stroke_Width to approximate the stroke geometry. | 
|  |  | 
|  | If the Style is kStroke_Style and the Stroke_Width is zero, getFillPath | 
|  | returns false since Hairline has no filled equivalent. | 
|  |  | 
|  | #SeeAlso Style_Stroke Stroke_Width Path_Effect | 
|  |  | 
|  | #Subtopic Fill_Path ## | 
|  |  | 
|  | #Method bool getFillPath(const SkPath& src, SkPath* dst, const SkRect* cullRect, | 
|  | SkScalar resScale = 1) const | 
|  | #In Fill_Path | 
|  | #Line # returns fill path equivalent to stroke ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 192 | 
|  | #Description | 
|  | A very small Quad stroke is turned into a filled path with increasing levels of precision. | 
|  | At the lowest precision, the Quad stroke is approximated by a rectangle. | 
|  | At the highest precision, the filled path has high fidelity compared to the original stroke. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint strokePaint; | 
|  | strokePaint.setAntiAlias(true); | 
|  | strokePaint.setStyle(SkPaint::kStroke_Style); | 
|  | strokePaint.setStrokeWidth(.1f); | 
|  | SkPath strokePath; | 
|  | strokePath.moveTo(.08f, .08f); | 
|  | strokePath.quadTo(.09f, .08f, .17f, .17f); | 
|  | SkPath fillPath; | 
|  | SkPaint outlinePaint(strokePaint); | 
|  | outlinePaint.setStrokeWidth(2); | 
|  | SkMatrix scale = SkMatrix::MakeScale(300, 300); | 
|  | for (SkScalar precision : { 0.01f, .1f, 1.f, 10.f, 100.f } ) { | 
|  | strokePaint.getFillPath(strokePath, &fillPath, nullptr, precision); | 
|  | fillPath.transform(scale); | 
|  | canvas->drawPath(fillPath, outlinePaint); | 
|  | canvas->translate(60, 0); | 
|  | if (1.f == precision) canvas->translate(-180, 100); | 
|  | } | 
|  | strokePath.transform(scale); | 
|  | strokePaint.setStrokeWidth(30); | 
|  | canvas->drawPath(strokePath, strokePaint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method bool getFillPath(const SkPath& src, SkPath* dst) const | 
|  |  | 
|  | #In Fill_Path | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(10); | 
|  | SkPath strokePath; | 
|  | strokePath.moveTo(20, 20); | 
|  | strokePath.lineTo(100, 100); | 
|  | canvas->drawPath(strokePath, paint); | 
|  | SkPath fillPath; | 
|  | paint.getFillPath(strokePath, &fillPath); | 
|  | paint.setStrokeWidth(2); | 
|  | canvas->translate(40, 0); | 
|  | canvas->drawPath(fillPath, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Shader_Methods | 
|  | #Line # get and set Shader ## | 
|  |  | 
|  | Shader defines the colors used when drawing a shape. | 
|  | Shader may be an image, a gradient, or a computed fill. | 
|  | If Paint has no Shader, then Color fills the shape. | 
|  |  | 
|  | Shader is modulated by Color_Alpha component of Color. | 
|  | If Shader object defines only Color_Alpha, then Color modulated by Color_Alpha describes | 
|  | the fill. | 
|  |  | 
|  | The drawn transparency can be modified without altering Shader, by changing Color_Alpha. | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkPoint center = { 50, 50 }; | 
|  | SkScalar radius = 50; | 
|  | const SkColor colors[] = { 0xFFFFFFFF, 0xFF000000 }; | 
|  | paint.setShader(SkGradientShader::MakeRadial(center, radius, colors, | 
|  | nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode)); | 
|  | for (SkScalar a : { 0.3f, 0.6f, 1.0f } ) { | 
|  | paint.setAlpha((int) (a * 255)); | 
|  | canvas->drawCircle(center.fX, center.fY, radius, paint); | 
|  | canvas->translate(70, 70); | 
|  | } | 
|  | } | 
|  | ## | 
|  |  | 
|  | If Shader generates only Color_Alpha then all components of Color modulate the output. | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkBitmap bitmap; | 
|  | bitmap.setInfo(SkImageInfo::MakeA8(5, 1), 5);  // bitmap only contains alpha | 
|  | uint8_t pixels[5] = { 0x22, 0x55, 0x88, 0xBB, 0xFF }; | 
|  | bitmap.setPixels(pixels); | 
|  | paint.setShader(SkShader::MakeBitmapShader(bitmap, | 
|  | SkShader::kMirror_TileMode, SkShader::kMirror_TileMode)); | 
|  | for (SkColor c : { SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN } ) { | 
|  | paint.setColor(c);  // all components in color affect shader | 
|  | canvas->drawCircle(50, 50, 50, paint); | 
|  | canvas->translate(70, 70); | 
|  | } | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Method SkShader* getShader() const | 
|  |  | 
|  | #In Shader_Methods | 
|  | #Line # returns Shader, multiple drawing colors; gradients ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkDebugf("nullptr %c= shader\n", paint.getShader() ? '!' : '='); | 
|  | paint.setShader(SkShader::MakeEmptyShader()); | 
|  | SkDebugf("nullptr %c= shader\n", paint.getShader() ? '!' : '='); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | nullptr == shader | 
|  | nullptr != shader | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method sk_sp<SkShader> refShader() const | 
|  |  | 
|  | #In Shader_Methods | 
|  | #Line # references Shader, multiple drawing colors; gradients ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setShader(SkShader::MakeEmptyShader()); | 
|  | SkDebugf("shader unique: %s\n", paint1.getShader()->unique() ? "true" : "false"); | 
|  | paint2.setShader(paint1.refShader()); | 
|  | SkDebugf("shader unique: %s\n", paint1.getShader()->unique() ? "true" : "false"); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | shader unique: true | 
|  | shader unique: false | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setShader(sk_sp<SkShader> shader) | 
|  |  | 
|  | #In Shader_Methods | 
|  | #Line # sets Shader, multiple drawing colors; gradients ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 64 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setColor(SK_ColorBLUE); | 
|  | paint.setShader(SkShader::MakeColorShader(SK_ColorRED)); | 
|  | canvas->drawRect(SkRect::MakeWH(40, 40), paint); | 
|  | paint.setShader(nullptr); | 
|  | canvas->translate(50, 0); | 
|  | canvas->drawRect(SkRect::MakeWH(40, 40), paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Shader_Methods ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Color_Filter_Methods | 
|  | #Line # get and set Color_Filter ## | 
|  |  | 
|  | Color_Filter alters the color used when drawing a shape. | 
|  | Color_Filter may apply Blend_Mode, transform the color through a matrix, or composite multiple filters. | 
|  | If Paint has no Color_Filter, the color is unaltered. | 
|  |  | 
|  | The drawn transparency can be modified without altering Color_Filter, by changing Color_Alpha. | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setColorFilter(SkColorMatrixFilter::MakeLightingFilter(0xFFFFFF, 0xFF0000)); | 
|  | for (SkColor c : { SK_ColorBLACK, SK_ColorGREEN } ) { | 
|  | paint.setColor(c); | 
|  | canvas->drawRect(SkRect::MakeXYWH(10, 10, 50, 50), paint); | 
|  | paint.setAlpha(0x80); | 
|  | canvas->drawRect(SkRect::MakeXYWH(60, 60, 50, 50), paint); | 
|  | canvas->translate(100, 0); | 
|  | } | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Method SkColorFilter* getColorFilter() const | 
|  |  | 
|  | #In Color_Filter_Methods | 
|  | #Line # returns Color_Filter, how colors are altered ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkDebugf("nullptr %c= color filter\n", paint.getColorFilter() ? '!' : '='); | 
|  | paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorLTGRAY, SkBlendMode::kSrcIn)); | 
|  | SkDebugf("nullptr %c= color filter\n", paint.getColorFilter() ? '!' : '='); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | nullptr == color filter | 
|  | nullptr != color filter | 
|  | ## | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #Method sk_sp<SkColorFilter> refColorFilter() const | 
|  |  | 
|  | #In Color_Filter_Methods | 
|  | #Line # references Color_Filter, how colors are altered ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setColorFilter(SkColorFilter::MakeModeFilter(0xFFFF0000, SkBlendMode::kSrcATop)); | 
|  | SkDebugf("color filter unique: %s\n", paint1.getColorFilter()->unique() ? "true" : "false"); | 
|  | paint2.setColorFilter(paint1.refColorFilter()); | 
|  | SkDebugf("color filter unique: %s\n", paint1.getColorFilter()->unique() ? "true" : "false"); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | color filter unique: true | 
|  | color filter unique: false | 
|  | ## | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #Method void setColorFilter(sk_sp<SkColorFilter> colorFilter) | 
|  |  | 
|  | #In Color_Filter_Methods | 
|  | #Line # sets Color_Filter, alters color ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 64 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorLTGRAY, SkBlendMode::kSrcIn)); | 
|  | canvas->drawRect(SkRect::MakeWH(50, 50), paint); | 
|  | paint.setColorFilter(nullptr); | 
|  | canvas->translate(70, 0); | 
|  | canvas->drawRect(SkRect::MakeWH(50, 50), paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Color_Filter_Methods ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Blend_Mode_Methods | 
|  | #Line # get and set Blend_Mode ## | 
|  |  | 
|  | Blend_Mode describes how Color combines with the destination color. | 
|  | The default setting, SkBlendMode::kSrcOver, draws the source color | 
|  | over the destination color. | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint normal, blender; | 
|  | normal.setColor(0xFF58a889); | 
|  | blender.setColor(0xFF8958a8); | 
|  | canvas->clear(0); | 
|  | for (SkBlendMode m : { SkBlendMode::kSrcOver, SkBlendMode::kSrcIn, SkBlendMode::kSrcOut } ) { | 
|  | normal.setBlendMode(SkBlendMode::kSrcOver); | 
|  | canvas->drawOval(SkRect::MakeXYWH(30, 30, 30, 80), normal); | 
|  | blender.setBlendMode(m); | 
|  | canvas->drawOval(SkRect::MakeXYWH(10, 50, 80, 30), blender); | 
|  | canvas->translate(70, 70); | 
|  | } | 
|  | } | 
|  | ## | 
|  |  | 
|  | #SeeAlso Blend_Mode | 
|  |  | 
|  | #Method SkBlendMode getBlendMode() const | 
|  |  | 
|  | #In Blend_Mode_Methods | 
|  | #Line # returns Blend_Mode, how colors combine with Device ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkDebugf("kSrcOver %c= getBlendMode\n", | 
|  | SkBlendMode::kSrcOver == paint.getBlendMode() ? '=' : '!'); | 
|  | paint.setBlendMode(SkBlendMode::kSrc); | 
|  | SkDebugf("kSrcOver %c= getBlendMode\n", | 
|  | SkBlendMode::kSrcOver == paint.getBlendMode() ? '=' : '!'); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | kSrcOver == getBlendMode | 
|  | kSrcOver != getBlendMode | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method bool isSrcOver() const | 
|  |  | 
|  | #In Blend_Mode_Methods | 
|  | #Line # returns true if Blend_Mode is SkBlendMode::kSrcOver ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkDebugf("isSrcOver %c= true\n", paint.isSrcOver() ? '=' : '!'); | 
|  | paint.setBlendMode(SkBlendMode::kSrc); | 
|  | SkDebugf("isSrcOver %c= true\n", paint.isSrcOver() ? '=' : '!'); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | isSrcOver == true | 
|  | isSrcOver != true | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setBlendMode(SkBlendMode mode) | 
|  |  | 
|  | #In Blend_Mode_Methods | 
|  | #Line # sets Blend_Mode, how colors combine with destination ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkDebugf("isSrcOver %c= true\n", paint.isSrcOver() ? '=' : '!'); | 
|  | paint.setBlendMode(SkBlendMode::kSrc); | 
|  | SkDebugf("isSrcOver %c= true\n", paint.isSrcOver() ? '=' : '!'); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | isSrcOver == true | 
|  | isSrcOver != true | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Blend_Mode_Methods ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Path_Effect_Methods | 
|  | #Line # get and set Path_Effect ## | 
|  |  | 
|  | Path_Effect modifies the path geometry before drawing it. | 
|  | Path_Effect may implement dashing, custom fill effects and custom stroke effects. | 
|  | If Paint has no Path_Effect, the path geometry is unaltered when filled or stroked. | 
|  |  | 
|  | #Example | 
|  | #Height 160 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(16); | 
|  | SkScalar intervals[] = {30, 10}; | 
|  | paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 1)); | 
|  | canvas->drawRoundRect({20, 20, 120, 120}, 20, 20, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #SeeAlso Path_Effect | 
|  |  | 
|  | #Method SkPathEffect* getPathEffect() const | 
|  |  | 
|  | #In Path_Effect_Methods | 
|  | #Line # returns Path_Effect, modifications to path geometry; dashing ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkDebugf("nullptr %c= path effect\n", paint.getPathEffect() ? '!' : '='); | 
|  | paint.setPathEffect(SkCornerPathEffect::Make(10)); | 
|  | SkDebugf("nullptr %c= path effect\n", paint.getPathEffect() ? '!' : '='); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | nullptr == path effect | 
|  | nullptr != path effect | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  |  | 
|  | #Method sk_sp<SkPathEffect> refPathEffect() const | 
|  |  | 
|  | #In Path_Effect_Methods | 
|  | #Line # references Path_Effect, modifications to path geometry; dashing ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint1, paint2; | 
|  | SkScalar intervals[] = {1, 2}; | 
|  | paint1.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 10)); | 
|  | SkDebugf("path effect unique: %s\n", paint1.getPathEffect()->unique() ? "true" : "false"); | 
|  | paint2.setPathEffect(paint1.refPathEffect()); | 
|  | SkDebugf("path effect unique: %s\n", paint1.getPathEffect()->unique() ? "true" : "false"); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | path effect unique: true | 
|  | path effect unique: false | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  |  | 
|  | #Method void setPathEffect(sk_sp<SkPathEffect> pathEffect) | 
|  |  | 
|  | #In Path_Effect_Methods | 
|  | #Line # sets Path_Effect, modifications to path geometry; dashing ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setPathEffect(SkDiscretePathEffect::Make(3, 5)); | 
|  | canvas->drawRect(SkRect::MakeXYWH(40, 40, 175, 175), paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Path_Effect_Methods ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Mask_Filter_Methods | 
|  | #Line # get and set Mask_Filter ## | 
|  |  | 
|  | Mask_Filter uses coverage of the shape drawn to create Mask_Alpha. | 
|  | Mask_Filter takes a Mask, and returns a Mask. | 
|  |  | 
|  | Mask_Filter may change the geometry and transparency of the shape, such as | 
|  | creating a blur effect. Set Mask_Filter to nullptr to prevent Mask_Filter from | 
|  | modifying the draw. | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setMaskFilter(SkMaskFilter::MakeBlur(kSolid_SkBlurStyle, 3)); | 
|  | canvas->drawRect(SkRect::MakeXYWH(40, 40, 175, 175), paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Method SkMaskFilter* getMaskFilter() const | 
|  |  | 
|  | #In Mask_Filter_Methods | 
|  | #Line # returns Mask_Filter, alterations to Mask_Alpha ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkDebugf("nullptr %c= mask filter\n", paint.getMaskFilter() ? '!' : '='); | 
|  | paint.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, 3)); | 
|  | SkDebugf("nullptr %c= mask filter\n", paint.getMaskFilter() ? '!' : '='); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | nullptr == mask filter | 
|  | nullptr != mask filter | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method sk_sp<SkMaskFilter> refMaskFilter() const | 
|  |  | 
|  | #In Mask_Filter_Methods | 
|  | #Line # references Mask_Filter, alterations to Mask_Alpha ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 1)); | 
|  | SkDebugf("mask filter unique: %s\n", paint1.getMaskFilter()->unique() ? "true" : "false"); | 
|  | paint2.setMaskFilter(paint1.refMaskFilter()); | 
|  | SkDebugf("mask filter unique: %s\n", paint1.getMaskFilter()->unique() ? "true" : "false"); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | mask filter unique: true | 
|  | mask filter unique: false | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setMaskFilter(sk_sp<SkMaskFilter> maskFilter) | 
|  |  | 
|  | #In Mask_Filter_Methods | 
|  | #Line # sets Mask_Filter, alterations to Mask_Alpha ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(10); | 
|  | paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 10)); | 
|  | canvas->drawRect(SkRect::MakeXYWH(40, 40, 175, 175), paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Mask_Filter_Methods ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Typeface_Methods | 
|  | #Line # get and set Typeface ## | 
|  |  | 
|  | Typeface identifies the font used when drawing and measuring text. | 
|  | Typeface may be specified by name, from a file, or from a data stream. | 
|  | The default Typeface defers to the platform-specific default font | 
|  | implementation. | 
|  |  | 
|  | #Example | 
|  | #Height 100 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setTypeface(SkTypeface::MakeFromName(nullptr, SkFontStyle())); | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(36); | 
|  | canvas->drawString("A Big Hello!", 10, 40, paint); | 
|  | paint.setTypeface(nullptr); | 
|  | paint.setFakeBoldText(true); | 
|  | canvas->drawString("A Big Hello!", 10, 80, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #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<SkTypeface> 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<SkTypeface> 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 | 
|  | #Line # get and set Image_Filter ## | 
|  |  | 
|  | Image_Filter operates on the pixel representation of the shape, as modified by Paint | 
|  | with Blend_Mode set to SkBlendMode::kSrcOver. Image_Filter creates a new bitmap, | 
|  | which is drawn to the device using the set Blend_Mode. | 
|  |  | 
|  | Image_Filter is higher level than Mask_Filter; for instance, an Image_Filter | 
|  | can operate on all channels of Color, while Mask_Filter generates Alpha only. | 
|  | Image_Filter operates independently of and can be used in combination with | 
|  | Mask_Filter. | 
|  |  | 
|  | #Example | 
|  | #ToDo explain why the two draws are so different ## | 
|  | #Function | 
|  | ###$ | 
|  | #include "SkBlurImageFilter.h" | 
|  | $$$# | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(2); | 
|  | SkRegion region; | 
|  | region.op( 10, 10, 50, 50, SkRegion::kUnion_Op); | 
|  | region.op( 10, 50, 90, 90, SkRegion::kUnion_Op); | 
|  | paint.setImageFilter(SkBlurImageFilter::Make(5.0f, 5.0f, nullptr)); | 
|  | canvas->drawRegion(region, paint); | 
|  | paint.setImageFilter(nullptr); | 
|  | paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5)); | 
|  | canvas->translate(100, 100); | 
|  | canvas->drawRegion(region, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Method SkImageFilter* getImageFilter() const | 
|  |  | 
|  | #In Image_Filter_Methods | 
|  | #Line # returns Image_Filter, alter pixels; blur ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Function | 
|  | ###$ | 
|  | #include "SkBlurImageFilter.h" | 
|  | $$$# | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkDebugf("nullptr %c= image filter\n", paint.getImageFilter() ? '!' : '='); | 
|  | paint.setImageFilter(SkBlurImageFilter::Make(kOuter_SkBlurStyle, 3, nullptr, nullptr)); | 
|  | SkDebugf("nullptr %c= image filter\n", paint.getImageFilter() ? '!' : '='); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | nullptr == image filter | 
|  | nullptr != image filter | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method sk_sp<SkImageFilter> refImageFilter() const | 
|  |  | 
|  | #In Image_Filter_Methods | 
|  | #Line # references Image_Filter, alter pixels; blur ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint1, paint2; | 
|  | paint1.setImageFilter(SkOffsetImageFilter::Make(25, 25, nullptr)); | 
|  | SkDebugf("image filter unique: %s\n", paint1.getImageFilter()->unique() ? "true" : "false"); | 
|  | paint2.setImageFilter(paint1.refImageFilter()); | 
|  | SkDebugf("image filter unique: %s\n", paint1.getImageFilter()->unique() ? "true" : "false"); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | image filter unique: true | 
|  | image filter unique: false | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setImageFilter(sk_sp<SkImageFilter> imageFilter) | 
|  |  | 
|  | #In Image_Filter_Methods | 
|  | #Line # sets Image_Filter, alter pixels; blur ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 160 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkBitmap bitmap; | 
|  | bitmap.allocN32Pixels(100, 100); | 
|  | SkCanvas offscreen(bitmap); | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setColor(SK_ColorWHITE); | 
|  | paint.setTextSize(96); | 
|  | offscreen.clear(0); | 
|  | offscreen.drawString("e", 20, 70, paint); | 
|  | paint.setImageFilter( | 
|  | SkLightingImageFilter::MakePointLitDiffuse(SkPoint3::Make(80, 100, 10), | 
|  | SK_ColorWHITE, 1, 2, nullptr, nullptr)); | 
|  | canvas->drawBitmap(bitmap, 0, 0, &paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Image_Filter_Methods ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Draw_Looper_Methods | 
|  | #Line # get and set Draw_Looper ## | 
|  |  | 
|  | Draw_Looper sets a modifier that communicates state from one Draw_Layer | 
|  | to another to construct the draw. | 
|  |  | 
|  | Draw_Looper draws one or more times, modifying the canvas and paint each time. | 
|  | Draw_Looper may be used to draw multiple colors or create a colored shadow. | 
|  | Set Draw_Looper to nullptr to prevent Draw_Looper from modifying the draw. | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkLayerDrawLooper::LayerInfo info; | 
|  | info.fPaintBits = (SkLayerDrawLooper::BitFlags) SkLayerDrawLooper::kColorFilter_Bit; | 
|  | info.fColorMode = SkBlendMode::kSrc; | 
|  | SkLayerDrawLooper::Builder looperBuilder; | 
|  | SkPaint* loopPaint = looperBuilder.addLayer(info); | 
|  | loopPaint->setColor(SK_ColorRED); | 
|  | info.fOffset.set(20, 20); | 
|  | loopPaint = looperBuilder.addLayer(info); | 
|  | loopPaint->setColor(SK_ColorBLUE); | 
|  | SkPaint paint; | 
|  | paint.setDrawLooper(looperBuilder.detach()); | 
|  | canvas->drawCircle(50, 50, 50, paint); | 
|  | } | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method SkDrawLooper* getDrawLooper() const | 
|  |  | 
|  | #In Draw_Looper_Methods | 
|  | #Line # returns Draw_Looper, multiple layers ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | SkDebugf("nullptr %c= draw looper\n", paint.getDrawLooper() ? '!' : '='); | 
|  | SkLayerDrawLooper::Builder looperBuilder; | 
|  | paint.setDrawLooper(looperBuilder.detach()); | 
|  | SkDebugf("nullptr %c= draw looper\n", paint.getDrawLooper() ? '!' : '='); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | nullptr == draw looper | 
|  | nullptr != draw looper | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method sk_sp<SkDrawLooper> refDrawLooper() const | 
|  |  | 
|  | #In Draw_Looper_Methods | 
|  | #Line # references Draw_Looper, multiple layers ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint1, paint2; | 
|  | SkLayerDrawLooper::Builder looperBuilder; | 
|  | paint1.setDrawLooper(looperBuilder.detach()); | 
|  | SkDebugf("draw looper unique: %s\n", paint1.getDrawLooper()->unique() ? "true" : "false"); | 
|  | paint2.setDrawLooper(paint1.refDrawLooper()); | 
|  | SkDebugf("draw looper unique: %s\n", paint1.getDrawLooper()->unique() ? "true" : "false"); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | draw looper unique: true | 
|  | draw looper unique: false | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void setDrawLooper(sk_sp<SkDrawLooper> drawLooper) | 
|  | #In Draw_Looper_Methods | 
|  | #Line # sets Draw_Looper, multiple layers ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setDrawLooper(SkBlurDrawLooper::Make(0x7FFF0000, 4, -5, -10)); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(10); | 
|  | paint.setAntiAlias(true); | 
|  | paint.setColor(0x7f0000ff); | 
|  | canvas->drawCircle(70, 70, 50, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Draw_Looper_Methods ## | 
|  |  | 
|  | #Subtopic Text_Size | 
|  | #Line # overall height in points ## | 
|  |  | 
|  | Text_Size adjusts the overall text size in points. | 
|  | Text_Size can be set to any positive value or zero. | 
|  | Text_Size defaults to 12. | 
|  | Set SkPaintDefaults_TextSize at compile time to change the default setting. | 
|  |  | 
|  | #Example | 
|  | #Height 135 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | canvas->drawString("12 point", 10, 20, paint); | 
|  | paint.setTextSize(24); | 
|  | canvas->drawString("24 point", 10, 60, paint); | 
|  | paint.setTextSize(48); | 
|  | canvas->drawString("48 point", 10, 120, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #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 | 
|  | #Line # text horizontal scale ## | 
|  |  | 
|  | Text_Scale_X adjusts the text horizontal scale. | 
|  | Text scaling approximates condensed and expanded type faces when the actual face | 
|  | is not available. | 
|  | Text_Scale_X can be set to any value. | 
|  | Text_Scale_X defaults to 1. | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(24); | 
|  | paint.setTextScaleX(.8f); | 
|  | canvas->drawString("narrow", 10, 20, paint); | 
|  | paint.setTextScaleX(1); | 
|  | canvas->drawString("normal", 10, 60, paint); | 
|  | paint.setTextScaleX(1.2f); | 
|  | canvas->drawString("wide", 10, 100, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #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 | 
|  | #Line # text horizontal slant ## | 
|  |  | 
|  |  | 
|  | Text_Skew_X adjusts the text horizontal slant. | 
|  | Text skewing approximates italic and oblique type faces when the actual face | 
|  | is not available. | 
|  | Text_Skew_X can be set to any value. | 
|  | Text_Skew_X defaults to 0. | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(24); | 
|  | paint.setTextSkewX(-.25f); | 
|  | canvas->drawString("right-leaning", 10, 100, paint); | 
|  | paint.setTextSkewX(0); | 
|  | canvas->drawString("normal", 10, 60, paint); | 
|  | paint.setTextSkewX(.25f); | 
|  | canvas->drawString("left-leaning", 10, 20, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #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 ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Text_Encoding | 
|  | #Line # text encoded as characters or Glyphs ## | 
|  |  | 
|  | #Enum TextEncoding | 
|  | #Line # character or glyph encoded size ## | 
|  |  | 
|  | #Code | 
|  | #Populate | 
|  | ## | 
|  |  | 
|  | TextEncoding determines whether text specifies character codes and their encoded | 
|  | size, or glyph indices. Characters are encoded as specified by the | 
|  | #A Unicode standard # https://unicode.org/standard/standard.html ## | 
|  | . | 
|  |  | 
|  | Character codes encoded size are specified by UTF-8, UTF-16, or UTF-32. | 
|  | All character code formats are able to represent all of Unicode, differing only | 
|  | in the total storage required. | 
|  |  | 
|  | #A UTF-8 (RFC 3629) # https://tools.ietf.org/html/rfc3629 ## | 
|  | encodes each character as one or more 8-bit bytes. | 
|  |  | 
|  | #A UTF-16 (RFC 2781) # https://tools.ietf.org/html/rfc2781 ## | 
|  | encodes each character as one or two 16-bit words. | 
|  |  | 
|  | #A UTF-32 # https://www.unicode.org/versions/Unicode5.0.0/ch03.pdf ## | 
|  | encodes each character as one 32-bit word. | 
|  |  | 
|  | Font_Manager uses font data to convert character code points into glyph indices. | 
|  | A glyph index is a 16-bit word. | 
|  |  | 
|  | TextEncoding is set to kUTF8_TextEncoding by default. | 
|  |  | 
|  | #Const kUTF8_TextEncoding 0 | 
|  | #Line # uses bytes to represent UTF-8 or ASCII ## | 
|  | ## | 
|  | #Const kUTF16_TextEncoding 1 | 
|  | #Line # uses two byte words to represent most of Unicode ## | 
|  | ## | 
|  | #Const kUTF32_TextEncoding 2 | 
|  | #Line # uses four byte words to represent all of Unicode ## | 
|  | ## | 
|  | #Const kGlyphID_TextEncoding 3 | 
|  | #Line # uses two byte words to represent glyph indices ## | 
|  | ## | 
|  |  | 
|  | #Enum ## | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | #Description | 
|  | First line is encoded in UTF-8. | 
|  | Second line is encoded in UTF-16. | 
|  | Third line is encoded in UTF-32. | 
|  | Fourth line has 16-bit glyph indices. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | const char hello8[] = "Hello" "\xE2" "\x98" "\xBA"; | 
|  | const uint16_t hello16[] = { 'H', 'e', 'l', 'l', 'o', 0x263A }; | 
|  | const uint32_t hello32[] = { 'H', 'e', 'l', 'l', 'o', 0x263A }; | 
|  | paint.setTextSize(24); | 
|  | canvas->drawText(hello8, sizeof(hello8) - 1, 10, 30, paint); | 
|  | paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); | 
|  | canvas->drawText(hello16, sizeof(hello16), 10, 60, paint); | 
|  | paint.setTextEncoding(SkPaint::kUTF32_TextEncoding); | 
|  | canvas->drawText(hello32, sizeof(hello32), 10, 90, paint); | 
|  | uint16_t glyphs[SK_ARRAY_COUNT(hello32)]; | 
|  | paint.textToGlyphs(hello32, sizeof(hello32), glyphs); | 
|  | paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 
|  | canvas->drawText(glyphs, sizeof(glyphs), 10, 120, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #Method TextEncoding getTextEncoding() const | 
|  |  | 
|  | #In Text_Encoding | 
|  | #Line # returns character or glyph encoded size ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("kUTF8_TextEncoding %c= text encoding\n", | 
|  | SkPaint::kUTF8_TextEncoding == paint.getTextEncoding() ? '=' : '!'); | 
|  | paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 
|  | SkDebugf("kGlyphID_TextEncoding %c= text encoding\n", | 
|  | SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | kUTF8_TextEncoding == text encoding | 
|  | kGlyphID_TextEncoding == text encoding | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  |  | 
|  | #Method void setTextEncoding(TextEncoding encoding) | 
|  |  | 
|  | #In Text_Encoding | 
|  | #Line # sets character or glyph encoded size ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | paint.setTextEncoding((SkPaint::TextEncoding) 4); | 
|  | SkDebugf("4 %c= text encoding\n", (SkPaint::TextEncoding) 4 == paint.getTextEncoding() ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | 4 != text encoding | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Typedef typedef SkFontMetrics FontMetrics | 
|  | ## | 
|  |  | 
|  | #Subtopic Text_Encoding ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Font_Metrics | 
|  |  | 
|  | #Method SkScalar getFontMetrics(SkFontMetrics* metrics) const | 
|  |  | 
|  | #In Font_Metrics | 
|  | #Line # returns Typeface metrics scaled by text size ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setTextSize(32); | 
|  | SkScalar lineHeight = paint.getFontMetrics(nullptr); | 
|  | canvas->drawString("line 1", 10, 40, paint); | 
|  | canvas->drawString("line 2", 10, 40 + lineHeight, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | #SeeAlso Text_Size Typeface Typeface_Methods | 
|  |  | 
|  | ## | 
|  |  | 
|  |  | 
|  | #Method SkScalar getFontSpacing() const | 
|  |  | 
|  | #In Font_Metrics | 
|  | #Line # returns recommended spacing between lines ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | for (SkScalar textSize : { 12, 18, 24, 32 } ) { | 
|  | paint.setTextSize(textSize); | 
|  | SkDebugf("textSize: %g fontSpacing: %g\n", textSize, paint.getFontSpacing()); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | textSize: 12 fontSpacing: 13.9688 | 
|  | textSize: 18 fontSpacing: 20.9531 | 
|  | textSize: 24 fontSpacing: 27.9375 | 
|  | textSize: 32 fontSpacing: 37.25 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Font_Metrics ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method int textToGlyphs(const void* text, size_t byteLength, | 
|  | SkGlyphID glyphs[]) const | 
|  | #In Utility | 
|  | #Line # converts text into glyph indices ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 64 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | const uint8_t utf8[] = { 0x24, 0xC2, 0xA2, 0xE2, 0x82, 0xAC, 0xC2, 0xA5, 0xC2, 0xA3 }; | 
|  | std::vector<SkGlyphID> glyphs; | 
|  | int count = paint.textToGlyphs(utf8, sizeof(utf8), nullptr); | 
|  | glyphs.resize(count); | 
|  | (void) paint.textToGlyphs(utf8, sizeof(utf8), &glyphs.front()); | 
|  | paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 
|  | paint.setTextSize(32); | 
|  | canvas->drawText(&glyphs.front(), glyphs.size() * sizeof(SkGlyphID), 10, 40, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method int countText(const void* text, size_t byteLength) const | 
|  | #In Utility | 
|  | #Line # returns number of Glyphs in text ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | const uint8_t utf8[] = { 0x24, 0xC2, 0xA2, 0xE2, 0x82, 0xAC, 0xC2, 0xA5, 0xC2, 0xA3 }; | 
|  | SkDebugf("count = %d\n", paint.countText(utf8, sizeof(utf8))); | 
|  |  | 
|  | #StdOut | 
|  | count = 5 | 
|  | ## | 
|  | ## | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method bool containsText(const void* text, size_t byteLength) const | 
|  | #In Utility | 
|  | #Line # returns if all text corresponds to Glyphs ## | 
|  | #Populate | 
|  |  | 
|  | #NoExample | 
|  | #Description | 
|  | containsText succeeds for degree symbol, but cannot find a glyph index | 
|  | corresponding to the Unicode surrogate code point. | 
|  | ## | 
|  | SkPaint paint; | 
|  | const uint16_t goodChar = 0x00B0;  // degree symbol | 
|  | const uint16_t badChar = 0xD800;   // Unicode surrogate | 
|  | paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); | 
|  | SkDebugf("0x%04x %c= has char\n", goodChar, | 
|  | paint.containsText(&goodChar, 2) ? '=' : '!'); | 
|  | SkDebugf("0x%04x %c= has char\n", badChar, | 
|  | paint.containsText(&badChar, 2) ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | 0x00b0 == has char | 
|  | 0xd800 != has char | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #Example | 
|  | #Description | 
|  | containsText returns true that glyph index is greater than zero, not | 
|  | that it corresponds to an entry in Typeface. | 
|  | ## | 
|  | SkPaint paint; | 
|  | const uint16_t goodGlyph = 511; | 
|  | const uint16_t zeroGlyph = 0; | 
|  | const uint16_t badGlyph = 65535; // larger than glyph count in font | 
|  | paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 
|  | SkDebugf("0x%04x %c= has glyph\n", goodGlyph, | 
|  | paint.containsText(&goodGlyph, 2) ? '=' : '!'); | 
|  | SkDebugf("0x%04x %c= has glyph\n", zeroGlyph, | 
|  | paint.containsText(&zeroGlyph, 2) ? '=' : '!'); | 
|  | SkDebugf("0x%04x %c= has glyph\n", badGlyph, | 
|  | paint.containsText(&badGlyph, 2) ? '=' : '!'); | 
|  |  | 
|  | #StdOut | 
|  | 0x01ff == has glyph | 
|  | 0x0000 != has glyph | 
|  | 0xffff == has glyph | 
|  | ## | 
|  | ## | 
|  |  | 
|  | #SeeAlso setTextEncoding Typeface | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method void glyphsToUnichars(const SkGlyphID glyphs[], | 
|  | int count, SkUnichar text[]) const | 
|  | #In Utility | 
|  | #Line # converts Glyphs into text ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 64 | 
|  | #Description | 
|  | Convert UTF-8 text to glyphs; then convert glyphs to Unichar code points. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | const char hello[] = "Hello!"; | 
|  | const int count = sizeof(hello) - 1; | 
|  | SkGlyphID glyphs[count]; | 
|  | if (count != paint.textToGlyphs(hello, count, glyphs)) { | 
|  | return; | 
|  | } | 
|  | SkUnichar unichars[count]; | 
|  | paint.glyphsToUnichars(glyphs, count, unichars); | 
|  | paint.setTextEncoding(SkPaint::kUTF32_TextEncoding); | 
|  | canvas->drawText(unichars, sizeof(unichars), 10, 30, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Measure_Text | 
|  | #Line # width, height, bounds of text ## | 
|  |  | 
|  | #Method SkScalar measureText(const void* text, size_t length, SkRect* bounds) const | 
|  |  | 
|  | #In Measure_Text | 
|  | #Line # returns advance width and bounds of text ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 64 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(50); | 
|  | const char str[] = "ay^jZ"; | 
|  | const int count = sizeof(str) - 1; | 
|  | canvas->drawText(str, count, 25, 50, paint); | 
|  | SkRect bounds; | 
|  | paint.measureText(str, count, &bounds); | 
|  | canvas->translate(25, 50); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | canvas->drawRect(bounds, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method SkScalar measureText(const void* text, size_t length) const | 
|  |  | 
|  | #In Measure_Text | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkDebugf("default width = %g\n", paint.measureText("!", 1)); | 
|  | paint.setTextSize(paint.getTextSize() * 2); | 
|  | SkDebugf("double width = %g\n", paint.measureText("!", 1)); | 
|  |  | 
|  | #StdOut | 
|  | default width = 5 | 
|  | double width = 10 | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method size_t breakText(const void* text, size_t length, SkScalar maxWidth, | 
|  | SkScalar* measuredWidth = nullptr) const | 
|  | #In Measure_Text | 
|  | #Line # returns text that fits in a width ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Description | 
|  | Line under "Breakfast" shows desired width, shorter than available characters. | 
|  | Line under "Bre" shows measured width after breaking text. | 
|  | ## | 
|  | #Height 128 | 
|  | #Width 280 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(50); | 
|  | const char str[] = "Breakfast"; | 
|  | const int count = sizeof(str) - 1; | 
|  | canvas->drawText(str, count, 25, 50, paint); | 
|  | SkScalar measuredWidth; | 
|  | int partialBytes = paint.breakText(str, count, 100, &measuredWidth); | 
|  | canvas->drawText(str, partialBytes, 25, 100, paint); | 
|  | canvas->drawLine(25, 60, 25 + 100, 60, paint); | 
|  | canvas->drawLine(25, 110, 25 + measuredWidth, 110, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method int getTextWidths(const void* text, size_t byteLength, SkScalar widths[], | 
|  | SkRect bounds[] = nullptr) const | 
|  | #In Measure_Text | 
|  | #Line # returns advance and bounds for each glyph in text ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 160 | 
|  | #Description | 
|  | Bounds of Glyphs increase for stroked text, but text advance remains the same. | 
|  | The underlines show the text advance, spaced to keep them distinct. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(50); | 
|  | const char str[] = "abc"; | 
|  | const int bytes = sizeof(str) - 1; | 
|  | int count = paint.getTextWidths(str, bytes, nullptr); | 
|  | std::vector<SkScalar> widths; | 
|  | std::vector<SkRect> bounds; | 
|  | widths.resize(count); | 
|  | bounds.resize(count); | 
|  | for (int loop = 0; loop < 2; ++loop) { | 
|  | (void) paint.getTextWidths(str, count, &widths.front(), &bounds.front()); | 
|  | SkPoint loc = { 25, 50 }; | 
|  | canvas->drawText(str, bytes, loc.fX, loc.fY, paint); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(0); | 
|  | SkScalar advanceY = loc.fY + 10; | 
|  | for (int index = 0; index < count; ++index) { | 
|  | bounds[index].offset(loc.fX, loc.fY); | 
|  | canvas->drawRect(bounds[index], paint); | 
|  | canvas->drawLine(loc.fX, advanceY, loc.fX + widths[index], advanceY, paint); | 
|  | loc.fX += widths[index]; | 
|  | advanceY += 5; | 
|  | } | 
|  | canvas->translate(0, 80); | 
|  | paint.setStrokeWidth(3); | 
|  | } | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Measure_Text ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Text_Path | 
|  | #Line # geometry of Glyphs ## | 
|  |  | 
|  | Text_Path describes the geometry of Glyphs used to draw text. | 
|  |  | 
|  | #Method void getTextPath(const void* text, size_t length, SkScalar x, SkScalar y, | 
|  | SkPath* path) const | 
|  | #In Text_Path | 
|  | #Line # returns Path equivalent to text ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Description | 
|  | Text is added to Path, offset, and subtracted from Path, then added at | 
|  | the offset location. The result is rendered with one draw call. | 
|  | ## | 
|  | #Height 128 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setTextSize(80); | 
|  | SkPath path, path2; | 
|  | paint.getTextPath("ABC", 3, 20, 80, &path); | 
|  | path.offset(20, 20, &path2); | 
|  | Op(path, path2, SkPathOp::kDifference_SkPathOp, &path); | 
|  | path.addPath(path2); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | canvas->drawPath(path, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method void getPosTextPath(const void* text, size_t length, | 
|  | const SkPoint pos[], SkPath* path) const | 
|  | #In Text_Path | 
|  | #Line # returns Path equivalent to positioned text ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 85 | 
|  | #Description | 
|  | Simplifies three Glyphs to eliminate overlaps, and strokes the result. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setTextSize(80); | 
|  | SkPath path, path2; | 
|  | SkPoint pos[] = {{20, 60}, {30, 70}, {40, 80}}; | 
|  | paint.getPosTextPath("ABC", 3, pos, &path); | 
|  | Simplify(path, &path); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | canvas->drawPath(path, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Text_Path ## | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Text_Intercepts | 
|  | #Line # advanced underline, strike through ## | 
|  |  | 
|  | Text_Intercepts describe the intersection of drawn text Glyphs with a pair | 
|  | of lines parallel to the text advance. Text_Intercepts permits creating a | 
|  | underline that skips Descenders. | 
|  |  | 
|  | #Method int getTextIntercepts(const void* text, size_t length, SkScalar x, SkScalar y, | 
|  | const SkScalar bounds[2], SkScalar* intervals) const | 
|  | #In Text_Intercepts | 
|  | #Line # returns where lines intersect text; underlines ## | 
|  | #Populate | 
|  |  | 
|  | #NoExample | 
|  | #Height 128 | 
|  | #Description | 
|  | Underline uses intercepts to draw on either side of the glyph Descender. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setTextSize(120); | 
|  | SkPoint textOrigin = { 20, 100 }; | 
|  | SkScalar bounds[] = { 100, 108 }; | 
|  | int count = paint.getTextIntercepts("y", 1, textOrigin.fX, textOrigin.fY, bounds, nullptr); | 
|  | std::vector<SkScalar> intervals; | 
|  | intervals.resize(count); | 
|  | (void) paint.getTextIntercepts("y", 1, textOrigin.fX, textOrigin.fY, bounds, | 
|  | &intervals.front()); | 
|  | canvas->drawString("y", textOrigin.fX, textOrigin.fY, paint); | 
|  | paint.setColor(SK_ColorRED); | 
|  | SkScalar x = textOrigin.fX; | 
|  | for (int i = 0; i < count; i += 2) { | 
|  | canvas->drawRect({x, bounds[0], intervals[i], bounds[1]}, paint); | 
|  | x = intervals[i + 1]; | 
|  | } | 
|  | canvas->drawRect({intervals[count - 1], bounds[0], | 
|  | textOrigin.fX + paint.measureText("y", 1), bounds[1]}, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method int getPosTextIntercepts(const void* text, size_t length, const SkPoint pos[], | 
|  | const SkScalar bounds[2], SkScalar* intervals) const | 
|  | #In Text_Intercepts | 
|  | #Line # returns where lines intersect positioned text; underlines ## | 
|  | #Populate | 
|  |  | 
|  | #NoExample | 
|  | #Description | 
|  | Text intercepts draw on either side of, but not inside, Glyphs in a run. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setTextSize(120); | 
|  | SkPoint textPos[] = {{ 60, 90 }, { 120, 90 }}; | 
|  | SkScalar bounds[] = { 40, 70 }; | 
|  | const char str[] = "A+"; | 
|  | int len = sizeof(str) - 1; | 
|  | int count = paint.getPosTextIntercepts(str, len, textPos, bounds, nullptr); | 
|  | std::vector<SkScalar> intervals; | 
|  | intervals.resize(count); | 
|  | (void) paint.getPosTextIntercepts(str, len, textPos, bounds, &intervals.front()); | 
|  | canvas->drawPosText(str, len, textPos, paint); | 
|  | paint.setColor(SK_ColorRED); | 
|  | SkScalar x = textPos[0].fX; | 
|  | for (int i = 0; i < count; i+= 2) { | 
|  | canvas->drawRect({x, bounds[0], intervals[i], bounds[1]}, paint); | 
|  | x = intervals[i + 1]; | 
|  | } | 
|  | if (count) { | 
|  | canvas->drawRect({intervals[count - 1], bounds[0], 180, bounds[1]}, paint); | 
|  | } | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method int getPosTextHIntercepts(const void* text, size_t length, const SkScalar xpos[], | 
|  | SkScalar constY, const SkScalar bounds[2], | 
|  | SkScalar* intervals) const | 
|  | #In Text_Intercepts | 
|  | #Line # returns where lines intersect horizontally positioned text; underlines ## | 
|  | #Populate | 
|  |  | 
|  | #NoExample | 
|  | #Height 128 | 
|  | #Description | 
|  | Text intercepts do not take stroke thickness into consideration. | 
|  | ## | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkPaint paint; | 
|  | paint.setTextSize(120); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(4); | 
|  | SkScalar textPosH[] = { 20, 80, 140 }; | 
|  | SkScalar y = 100; | 
|  | SkScalar bounds[] = { 56, 78 }; | 
|  | const char str[] = "\\-/"; | 
|  | int len = sizeof(str) - 1; | 
|  | int count = paint.getPosTextHIntercepts(str, len, textPosH, y, bounds, nullptr); | 
|  | std::vector<SkScalar> intervals; | 
|  | intervals.resize(count); | 
|  | (void) paint.getPosTextHIntercepts(str, len, textPosH, y, bounds, &intervals.front()); | 
|  | canvas->drawPosTextH(str, len, textPosH, y, paint); | 
|  | paint.setColor(0xFFFF7777); | 
|  | paint.setStyle(SkPaint::kFill_Style); | 
|  | SkScalar x = textPosH[0]; | 
|  | for (int i = 0; i < count; i+= 2) { | 
|  | canvas->drawRect({x, bounds[0], intervals[i], bounds[1]}, paint); | 
|  | x = intervals[i + 1]; | 
|  | } | 
|  | canvas->drawRect({intervals[count - 1], bounds[0], 180, bounds[1]}, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  |  | 
|  | #Method int getTextBlobIntercepts(const SkTextBlob* blob, const SkScalar bounds[2], | 
|  | SkScalar* intervals) const | 
|  | #In Text_Intercepts | 
|  | #Line # returns where lines intersect Text_Blob; underlines ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | #Height 143 | 
|  | void draw(SkCanvas* canvas) { | 
|  | SkFont font; | 
|  | font.setSize(120); | 
|  | SkPoint textPos = { 20, 110 }; | 
|  | int len = 3; | 
|  | SkTextBlobBuilder textBlobBuilder; | 
|  | const SkTextBlobBuilder::RunBuffer& run = | 
|  | textBlobBuilder.allocRun(font, len, textPos.fX, textPos.fY); | 
|  | run.glyphs[0] = 10; | 
|  | run.glyphs[1] = 20; | 
|  | run.glyphs[2] = 30; | 
|  | sk_sp<const SkTextBlob> blob = textBlobBuilder.make(); | 
|  | SkPaint paint; | 
|  | SkScalar bounds[] = { 116, 134 }; | 
|  | int count = paint.getTextBlobIntercepts(blob.get(), bounds, nullptr); | 
|  | std::vector<SkScalar> intervals; | 
|  | intervals.resize(count); | 
|  | (void) paint.getTextBlobIntercepts(blob.get(), bounds, &intervals.front()); | 
|  | canvas->drawTextBlob(blob.get(), 0, 0, paint); | 
|  | paint.setColor(0xFFFF7777); | 
|  | SkScalar x = textPos.fX; | 
|  | for (int i = 0; i < count; i+= 2) { | 
|  | canvas->drawRect({x, bounds[0], intervals[i], bounds[1]}, paint); | 
|  | x = intervals[i + 1]; | 
|  | } | 
|  | canvas->drawRect({intervals[count - 1], bounds[0], 180, bounds[1]}, paint); | 
|  | } | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Subtopic Text_Intercepts ## | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Method SkRect getFontBounds() const | 
|  |  | 
|  | #In Font_Metrics | 
|  | #Line # returns union all glyph bounds ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | SkPaint paint; | 
|  | SkFontMetrics fm; | 
|  | paint.getFontMetrics(&fm); | 
|  | SkRect fb = paint.getFontBounds(); | 
|  | SkDebugf("metrics bounds = { %g, %g, %g, %g }\n", fm.fXMin, fm.fTop, fm.fXMax, fm.fBottom ); | 
|  | SkDebugf("font bounds    = { %g, %g, %g, %g }\n", fb.fLeft, fb.fTop, fb.fRight, fm.fBottom ); | 
|  |  | 
|  | #StdOut | 
|  | metrics bounds = { -12.2461, -14.7891, 21.5215, 5.55469 } | 
|  | font bounds    = { -12.2461, -14.7891, 21.5215, 5.55469 } | 
|  | ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | #Method bool nothingToDraw() const | 
|  | #In Utility | 
|  | #Line # returns true if Paint prevents all drawing ## | 
|  | #Populate | 
|  |  | 
|  | #Example | 
|  | void draw(SkCanvas* canvas) { | 
|  | auto debugster = [](const char* prefix, const SkPaint& p) -> void { | 
|  | SkDebugf("%s nothing to draw: %s\n", prefix, | 
|  | p.nothingToDraw() ? "true" : "false"); | 
|  | }; | 
|  | SkPaint paint; | 
|  | debugster("initial", paint); | 
|  | paint.setBlendMode(SkBlendMode::kDst); | 
|  | debugster("blend dst", paint); | 
|  | paint.setBlendMode(SkBlendMode::kSrcOver); | 
|  | debugster("blend src over", paint); | 
|  | paint.setAlpha(0); | 
|  | debugster("alpha 0", paint); | 
|  | } | 
|  |  | 
|  | #StdOut | 
|  | initial nothing to draw: false | 
|  | blend dst nothing to draw: true | 
|  | blend src over nothing to draw: false | 
|  | alpha 0 nothing to draw: true | 
|  | #StdOut  ## | 
|  | ## | 
|  |  | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  | #Subtopic Utility | 
|  | #Line # rarely called management functions ## | 
|  | ## | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  | #Class SkPaint ## | 
|  |  | 
|  | #Topic Paint ## |