| #Topic Paint |
| #Alias Paint_Reference ## |
| |
| #PhraseDef paint_font_metrics |
| Typeface, Paint_Text_Size, Paint_Text_Scale_X, |
| Paint_Text_Skew_X, Paint_Text_Align, 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 # kNormal_Hinting ## |
| # 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 ## |
| ## |
| |
| #Enum Hinting |
| #Line # level of glyph outline adjustment ## |
| |
| #Code |
| #Populate |
| ## |
| |
| Hinting adjusts the glyph outlines so that the shape provides a uniform |
| look at a given point size on font engines that support it. Hinting may have a |
| muted effect or no effect at all depending on the platform. |
| |
| The four levels roughly control corresponding features on platforms that use FreeType |
| as the Font_Engine. |
| |
| #Const kNo_Hinting 0 |
| #Line # glyph outlines unchanged ## |
| Leaves glyph outlines unchanged from their native representation. |
| With FreeType, this is equivalent to the FT_LOAD_NO_HINTING |
| bit-field constant supplied to FT_Load_Glyph, which indicates that the vector |
| outline being loaded should not be fitted to the pixel grid but simply scaled |
| to 26.6 fractional pixels. |
| ## |
| #Const kSlight_Hinting 1 |
| #Line # minimal modification to improve constrast ## |
| Modifies glyph outlines minimally to improve constrast. |
| With FreeType, this is equivalent in spirit to the |
| FT_LOAD_TARGET_LIGHT value supplied to FT_Load_Glyph. It chooses a |
| lighter hinting algorithm for non-monochrome modes. |
| Generated Glyphs may be fuzzy but better resemble their original shape. |
| ## |
| #Const kNormal_Hinting 2 |
| #Line # glyph outlines modified to improve constrast ## |
| Modifies glyph outlines to improve constrast. This is the default. |
| With FreeType, this supplies FT_LOAD_TARGET_NORMAL to FT_Load_Glyph, |
| choosing the default hinting algorithm, which is optimized for standard |
| gray-level rendering. |
| ## |
| #Const kFull_Hinting 3 |
| #Line # modifies glyph outlines for maximum constrast ## |
| Modifies glyph outlines for maximum constrast. With FreeType, this selects |
| FT_LOAD_TARGET_LCD or FT_LOAD_TARGET_LCD_V if kLCDRenderText_Flag is set. |
| FT_LOAD_TARGET_LCD is a variant of FT_LOAD_TARGET_NORMAL optimized for |
| horizontally decimated LCD displays; FT_LOAD_TARGET_LCD_V is a |
| variant of FT_LOAD_TARGET_NORMAL optimized for vertically decimated LCD displays. |
| ## |
| |
| #Bug 915 |
| On OS_X and iOS, hinting controls whether Core_Graphics dilates the font outlines |
| to account for LCD text. No hinting uses Core_Text grayscale output. |
| Normal hinting uses Core_Text LCD output. If kLCDRenderText_Flag is clear, |
| the LCD output is reduced to a single grayscale channel. |
| |
| On Windows with DirectWrite, Hinting has no effect. |
| |
| Hinting defaults to kNormal_Hinting. |
| Set SkPaintDefaults_Hinting at compile time to change the default setting. |
| |
| #ToDo add an illustration? linux running GM:typefacerendering is best for this |
| the hinting variations are every other character horizontally |
| #ToDo ## |
| |
| #Enum ## |
| |
| #Method Hinting getHinting() const |
| #In Hinting |
| #Line # returns Hinting, glyph outline adjustment level ## |
| #Populate |
| |
| #Example |
| SkPaint paint; |
| SkDebugf("SkPaint::kNormal_Hinting %c= paint.getHinting()\n", |
| SkPaint::kNormal_Hinting == paint.getHinting() ? '=' : ':'); |
| |
| #StdOut |
| SkPaint::kNormal_Hinting == paint.getHinting() |
| ## |
| ## |
| ## |
| |
| #Method void setHinting(Hinting hintingLevel) |
| |
| #In Hinting |
| #Line # sets Hinting, glyph outline adjustment level ## |
| Sets level of glyph outline adjustment. |
| Does not check for valid values of hintingLevel. |
| |
| #Table |
| #Legend |
| # Hinting # value # effect on generated glyph outlines ## |
| ## |
| # kNo_Hinting # 0 # leaves glyph outlines unchanged from their native representation ## |
| # kSlight_Hinting # 1 # modifies glyph outlines minimally to improve contrast ## |
| # kNormal_Hinting # 2 # modifies glyph outlines to improve contrast ## |
| # kFull_Hinting # 3 # modifies glyph outlines for maximum contrast ## |
| ## |
| |
| #Param hintingLevel one of: kNo_Hinting, kSlight_Hinting, kNormal_Hinting, kFull_Hinting ## |
| |
| #Example |
| SkPaint paint1, paint2; |
| paint2.setHinting(SkPaint::kNormal_Hinting); |
| SkDebugf("paint1 %c= paint2\n", paint1 == paint2 ? '=' : ':'); |
| |
| #StdOut |
| paint1 == paint2 |
| ## |
| ## |
| ## |
| |
| # ------------------------------------------------------------------------------ |
| #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 ## |
| |
| #Enum ReserveFlags |
| #Deprecated soon |
| |
| Only valid for Android framework. |
| |
| #Code |
| #Populate |
| ## |
| |
| #Const kUnderlineText_ReserveFlag 0x0008 |
| #Deprecated soon |
| ## |
| #Const kStrikeThruText_ReserveFlag 0x0010 |
| #Deprecated soon |
| ## |
| ## |
| |
| #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 kNormal_Hinting. |
| If kLinearText_Flag is clear, it is the same as setting Hinting to kNo_Hinting. |
| #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 kNormal_Hinting or kFull_Hinting, Automatic_Hinting |
| instructs the Font_Manager to always hint Glyphs. |
| Automatic_Hinting has no effect if Hinting is set to kNo_Hinting or |
| kSlight_Hinting. |
| |
| 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 kFull_Hinting, 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 ## |
| |
| #Method bool isDevKernText() const |
| #Deprecated |
| ## |
| |
| #Method void setDevKernText(bool) |
| #Deprecated |
| ## |
| |
| # ------------------------------------------------------------------------------ |
| #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 SkDrawLooper* getLooper() const |
| #Bug 6259 |
| #Deprecated |
| ## |
| |
| #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); |
| } |
| ## |
| |
| ## |
| |
| #Method void setLooper(sk_sp<SkDrawLooper> drawLooper) |
| #Bug 6259 |
| #Deprecated |
| ## |
| |
| #Subtopic Draw_Looper_Methods ## |
| |
| # ------------------------------------------------------------------------------ |
| #Subtopic Text_Align |
| #Line # text placement relative to position ## |
| |
| #Enum Align |
| #Code |
| #Populate |
| ## |
| #Const kLeft_Align 1 |
| #Deprecated |
| ## |
| #Const kCenter_Align 2 |
| #Deprecated |
| ## |
| #Const kRight_Align 3 |
| #Deprecated |
| ## |
| #Deprecated |
| #Enum ## |
| |
| #Const kAlignCount 0 |
| #Deprecated |
| ## |
| |
| #Method Align getTextAlign() const |
| #Deprecated |
| ## |
| |
| #Method void setTextAlign(Align align) |
| #Deprecated |
| ## |
| |
| #Subtopic Text_Align ## |
| # ------------------------------------------------------------------------------ |
| #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 |
| ## |
| ## |
| |
| ## |
| |
| #Subtopic Text_Encoding ## |
| # ------------------------------------------------------------------------------ |
| #Subtopic Font_Metrics |
| #Line # common glyph dimensions ## |
| |
| Font_Metrics describe dimensions common to the Glyphs in Typeface. |
| The dimensions are computed by Font_Manager from font data and do not take |
| Paint settings other than Text_Size into account. |
| |
| Font dimensions specify the anchor to the left of the glyph at baseline as the origin. |
| X-axis values to the left of the glyph are negative, and to the right of the left glyph edge |
| are positive. |
| Y-axis values above the baseline are negative, and below the baseline are positive. |
| |
| #Example |
| #Width 512 |
| void draw(SkCanvas* canvas) { |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| paint.setTextSize(120); |
| SkPaint::FontMetrics fm; |
| SkScalar lineHeight = paint.getFontMetrics(&fm); |
| SkPoint pt = { 70, 180 }; |
| canvas->drawString("M", pt.fX, pt.fY, paint); |
| canvas->drawLine(pt.fX, pt.fY, pt.fX, pt.fY + fm.fTop, paint); |
| SkScalar ascent = pt.fY + fm.fAscent; |
| canvas->drawLine(pt.fX - 25, ascent, pt.fX - 25, ascent + lineHeight, paint); |
| canvas->drawLine(pt.fX - 50, pt.fY, pt.fX - 50, pt.fY + fm.fDescent, paint); |
| canvas->drawLine(pt.fX + 100, pt.fY, pt.fX + 100, pt.fY + fm.fAscent, paint); |
| canvas->drawLine(pt.fX + 125, pt.fY, pt.fX + 125, pt.fY - fm.fXHeight, paint); |
| canvas->drawLine(pt.fX + 150, pt.fY, pt.fX + 150, pt.fY - fm.fCapHeight, paint); |
| canvas->drawLine(pt.fX + 5, pt.fY, pt.fX + 5, pt.fY + fm.fBottom, paint); |
| SkScalar xmin = pt.fX + fm.fXMin; |
| canvas->drawLine(xmin, pt.fY + 60, xmin + fm.fMaxCharWidth, pt.fY + 60, paint); |
| canvas->drawLine(xmin, pt.fY - 145, pt.fX, pt.fY - 145, paint); |
| canvas->drawLine(pt.fX + fm.fXMax, pt.fY - 160, pt.fX, pt.fY - 160, paint); |
| SkScalar upos = pt.fY + fm.fUnderlinePosition; |
| canvas->drawLine(pt.fX + 25, upos, pt.fX + 160, upos, paint); |
| SkScalar ut = fm.fUnderlineThickness; |
| canvas->drawLine(pt.fX + 130, upos + ut, pt.fX + 160, upos + ut, paint); |
| paint.setTextSize(12); |
| canvas->drawString("x-min", pt.fX - 50, pt.fY - 148, paint); |
| canvas->drawString("x-max", pt.fX + 140, pt.fY - 150, paint); |
| canvas->drawString("max char width", pt.fX + 120, pt.fY + 57, paint); |
| canvas->drawString("underline position", pt.fX + 30, pt.fY + 22, paint); |
| canvas->drawString("underline thickness", pt.fX + 162, pt.fY + 13, paint); |
| canvas->rotate(-90); |
| canvas->drawString("descent", -pt.fY - 30, pt.fX - 54, paint); |
| canvas->drawString("line height", -pt.fY, pt.fX - 29, paint); |
| canvas->drawString("top", -pt.fY + 30, pt.fX - 4, paint); |
| canvas->drawString("ascent", -pt.fY, pt.fX + 110, paint); |
| canvas->drawString("x-height", -pt.fY, pt.fX + 135, paint); |
| canvas->drawString("cap-height", -pt.fY, pt.fX + 160, paint); |
| canvas->drawString("bottom", -pt.fY - 50, pt.fX + 15, paint); |
| } |
| ## |
| |
| #Struct FontMetrics |
| #Line # values computed by Font_Manager using Typeface ## |
| |
| #Code |
| #Populate |
| ## |
| |
| FontMetrics is filled out by getFontMetrics. FontMetrics contents reflect the values |
| computed by Font_Manager using Typeface. Values are set to zero if they are |
| not available. |
| |
| All vertical values are relative to the baseline, on a y-axis pointing down. |
| Zero is on the baseline, negative values are above the baseline, and positive |
| values are below the baseline. |
| |
| fUnderlineThickness and fUnderlinePosition have a bit set in fFlags if their values |
| are valid, since their value may be zero. |
| |
| fStrikeoutThickness and fStrikeoutPosition have a bit set in fFlags if their values |
| are valid, since their value may be zero. |
| |
| #Enum FontMetricsFlags |
| #Line # valid Font_Metrics ## |
| |
| #Code |
| #Populate |
| ## |
| |
| FontMetricsFlags are set in fFlags when underline and strikeout metrics are valid; |
| the underline or strikeout metric may be valid and zero. |
| Fonts with embedded bitmaps may not have valid underline or strikeout metrics. |
| |
| #Const kUnderlineThicknessIsValid_Flag 0x0001 |
| #Line # set if fUnderlineThickness is valid ## |
| ## |
| #Const kUnderlinePositionIsValid_Flag 0x0002 |
| #Line # set if fUnderlinePosition is valid ## |
| ## |
| #Const kStrikeoutThicknessIsValid_Flag 0x0004 |
| #Line # set if fStrikeoutThickness is valid ## |
| ## |
| #Const kStrikeoutPositionIsValid_Flag 0x0008 |
| #Line # set if fStrikeoutPosition is valid ## |
| ## |
| |
| #Enum ## |
| |
| #Member uint32_t fFlags |
| #Line # is set to FontMetricsFlags when metrics are valid ## |
| ## |
| |
| #Member SkScalar fTop |
| #Line # extent above baseline ## |
| Greatest extent above the baseline for any glyph. |
| Typically less than zero. |
| ## |
| |
| #Member SkScalar fAscent |
| #Line # distance to reserve above baseline ## |
| Recommended distance above the baseline to reserve for a line of text. |
| Typically less than zero. |
| ## |
| |
| #Member SkScalar fDescent |
| #Line # distance to reserve below baseline ## |
| Recommended distance below the baseline to reserve for a line of text. |
| Typically greater than zero. |
| ## |
| |
| #Member SkScalar fBottom |
| #Line # extent below baseline ## |
| Greatest extent below the baseline for any glyph. |
| Typically greater than zero. |
| ## |
| |
| #Member SkScalar fLeading |
| #Line # distance to add between lines ## |
| Recommended distance to add between lines of text. |
| Typically greater than or equal to zero. |
| ## |
| |
| #Member SkScalar fAvgCharWidth |
| #Line # average character width ## |
| Average character width, if it is available. |
| Zero if no average width is stored in the font. |
| ## |
| |
| #Member SkScalar fMaxCharWidth |
| #Line # maximum character width ## |
| ## |
| |
| #Member SkScalar fXMin |
| #Line # minimum x ## |
| Minimum bounding box x-axis value for all Glyphs. |
| Typically less than zero. |
| ## |
| |
| #Member SkScalar fXMax |
| #Line # maximum x ## |
| Maximum bounding box x-axis value for all Glyphs. |
| Typically greater than zero. |
| ## |
| |
| #Member SkScalar fXHeight |
| #Line # height of lower-case 'x' ## |
| May be zero if no lower-case height is stored in the font. |
| ## |
| |
| #Member SkScalar fCapHeight |
| #Line # height of an upper-case letter ## |
| May be zero if no upper-case height is stored in the font. |
| ## |
| |
| #Member SkScalar fUnderlineThickness |
| #Line # underline thickness ## |
| If the metric is valid, the kUnderlineThicknessIsValid_Flag is set in fFlags. |
| If kUnderlineThicknessIsValid_Flag is clear, fUnderlineThickness is zero. |
| ## |
| |
| #Member SkScalar fUnderlinePosition |
| #Line # underline position relative to baseline ## |
| Position of the top of the underline stroke relative to the baseline. |
| Typically positive when valid. |
| |
| If the metric is valid, the kUnderlinePositionIsValid_Flag is set in fFlags. |
| If kUnderlinePositionIsValid_Flag is clear, fUnderlinePosition is zero. |
| ## |
| |
| #Member SkScalar fStrikeoutThickness |
| #Line # strikeout thickness ## |
| |
| If the metric is valid, the kStrikeoutThicknessIsValid_Flag is set in fFlags. |
| If kStrikeoutThicknessIsValid_Flag is clear, fStrikeoutThickness is zero. |
| ## |
| |
| #Member SkScalar fStrikeoutPosition |
| #Line # strikeout position relative to baseline ## |
| Position of the bottom of the strikeout stroke relative to the baseline. |
| Typically negative when valid. |
| |
| If the metric is valid, the kStrikeoutPositionIsValid_Flag is set in fFlags. |
| If kStrikeoutPositionIsValid_Flag is clear, fStrikeoutPosition is zero. |
| ## |
| |
| #Method bool hasUnderlineThickness(SkScalar* thickness) const |
| #Line # returns underline thickness if set ## |
| #Populate |
| |
| #NoExample |
| ## |
| ## |
| |
| #Method bool hasUnderlinePosition(SkScalar* position) const |
| #Line # returns underline position if set ## |
| #Populate |
| |
| #NoExample |
| ## |
| ## |
| |
| #Method bool hasStrikeoutThickness(SkScalar* thickness) const |
| #Line # returns strikeout thickness if set ## |
| #Populate |
| |
| #NoExample |
| ## |
| ## |
| |
| #Method bool hasStrikeoutPosition(SkScalar* position) const |
| #Line # returns strikeout position if set ## |
| #Populate |
| |
| #NoExample |
| ## |
| ## |
| |
| #Struct ## |
| |
| #Method SkScalar getFontMetrics(FontMetrics* 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); |
| paint.setStyle(SkPaint::kStroke_Style); |
| paint.setStrokeWidth(10); |
| lineHeight = paint.getFontMetrics(nullptr, 1.10f); // account for stroke height |
| canvas->drawString("line 3", 120, 40, paint); |
| canvas->drawString("line 4", 120, 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 |
| ## |
| ## |
| |
| ## |
| |
| |
| #Method SkRect getFontBounds() const |
| |
| #In Font_Metrics |
| #Line # returns union all glyph bounds ## |
| #Populate |
| |
| #Example |
| SkPaint paint; |
| SkPaint::FontMetrics 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 } |
| ## |
| ## |
| |
| ## |
| |
| #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 |
| |
| #Example |
| #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 |
| |
| #Example |
| #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 |
| |
| #Example |
| #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) { |
| SkPaint paint; |
| paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); |
| paint.setTextSize(120); |
| SkPoint textPos = { 20, 110 }; |
| int len = 3; |
| SkTextBlobBuilder textBlobBuilder; |
| const SkTextBlobBuilder::RunBuffer& run = |
| textBlobBuilder.allocRun(paint, len, textPos.fX, textPos.fY); |
| run.glyphs[0] = 10; |
| run.glyphs[1] = 20; |
| run.glyphs[2] = 30; |
| sk_sp<const SkTextBlob> blob = textBlobBuilder.make(); |
| canvas->drawTextBlob(blob.get(), textPos.fX, textPos.fY, 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 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 Fast_Bounds |
| #Line # approximate area required by Paint ## |
| #Private |
| To be made private. |
| ## |
| |
| Fast_Bounds functions conservatively outset a drawing bounds by additional area |
| Paint may draw to. |
| |
| #Method bool canComputeFastBounds() const |
| |
| #In Fast_Bounds |
| #Line # returns true if settings allow for fast bounds computation ## |
| #Private |
| (to be made private) |
| ## |
| |
| Returns true if Paint does not include elements requiring extensive computation |
| to compute Device bounds of drawn geometry. For instance, Paint with Path_Effect |
| always returns false. |
| |
| #Return true if Paint allows for fast computation of bounds ## |
| ## |
| |
| #Method const SkRect& computeFastBounds(const SkRect& orig, SkRect* storage) const |
| |
| #In Fast_Bounds |
| #Line # returns fill bounds for quick reject tests ## |
| #Private |
| (to be made private) |
| ## |
| |
| Only call this if canComputeFastBounds returned true. This takes a |
| raw rectangle (the raw bounds of a shape), and adjusts it for stylistic |
| effects in the paint (e.g. stroking). If needed, it uses the storage |
| parameter. It returns the adjusted bounds that can then be used |
| for SkCanvas::quickReject tests. |
| |
| The returned Rect will either be orig or storage, thus the caller |
| should not rely on storage being set to the result, but should always |
| use the returned value. It is legal for orig and storage to be the same |
| Rect. |
| |
| #Private |
| For example: |
| if (!path.isInverseFillType() && paint.canComputeFastBounds()) { |
| SkRect storage; |
| if (canvas->quickReject(paint.computeFastBounds(path.getBounds(), &storage))) { |
| return; // do not draw the path |
| } |
| } |
| // draw the path |
| ## |
| |
| #Param orig geometry modified by Paint when drawn ## |
| #Param storage computed bounds of geometry; may not be nullptr ## |
| |
| #Return fast computed bounds ## |
| ## |
| |
| #Method const SkRect& computeFastStrokeBounds(const SkRect& orig, |
| SkRect* storage) const |
| #In Fast_Bounds |
| #Line # returns stroke bounds for quick reject tests ## |
| #Private |
| (to be made private) |
| ## |
| |
| #Param orig geometry modified by Paint when drawn ## |
| #Param storage computed bounds of geometry ## |
| |
| #Return fast computed bounds ## |
| ## |
| |
| #Method const SkRect& doComputeFastBounds(const SkRect& orig, SkRect* storage, |
| Style style) const |
| #In Fast_Bounds |
| #Line # returns bounds for quick reject tests ## |
| #Private |
| (to be made private) |
| ## |
| |
| Computes the bounds, overriding the Paint Style. This can be used to |
| account for additional width required by stroking orig, without |
| altering Style set to fill. |
| |
| #Param orig geometry modified by Paint when drawn ## |
| #Param storage computed bounds of geometry ## |
| #Param style overrides Style ## |
| |
| #Return fast computed bounds ## |
| ## |
| |
| #Subtopic Fast_Bounds ## |
| |
| # ------------------------------------------------------------------------------ |
| #Subtopic Utility |
| #Line # rarely called management functions ## |
| ## |
| |
| # ------------------------------------------------------------------------------ |
| |
| #Class SkPaint ## |
| |
| #Topic Paint ## |