summarylogtreecommitdiffstats
path: root/clip-ft-glyph.diff
blob: 4ca7035043640488ceba35e9d5e6209ef9f165f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# HG changeset patch
# User Lee Salzman <lsalzman@mozilla.com>
# Date 1504120456 14400
#      Wed Aug 30 15:14:16 2017 -0400
# Node ID 708d52f954b6d7ca2497fcb5b5084c6483300e89
# Parent  33224536ce20d942576cd4b9ffb350d6dce397bc
clip FreeType glyph bitmap to mask in Skia

MozReview-Commit-ID: 9NqLj9SkHFo

diff --git a/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp b/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp
--- a/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp
@@ -390,65 +390,131 @@ void SkScalerContext_FreeType_Base::gene
     const SkMatrix& bitmapTransform)
 {
     const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
     const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
 
     switch ( face->glyph->format ) {
         case FT_GLYPH_FORMAT_OUTLINE: {
             FT_Outline* outline = &face->glyph->outline;
-            FT_BBox     bbox;
-            FT_Bitmap   target;
 
             int dx = 0, dy = 0;
             if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
                 dx = SkFixedToFDot6(glyph.getSubXFixed());
                 dy = SkFixedToFDot6(glyph.getSubYFixed());
                 // negate dy since freetype-y-goes-up and skia-y-goes-down
                 dy = -dy;
             }
-            FT_Outline_Get_CBox(outline, &bbox);
-            /*
-                what we really want to do for subpixel is
-                    offset(dx, dy)
-                    compute_bounds
-                    offset(bbox & !63)
-                but that is two calls to offset, so we do the following, which
-                achieves the same thing with only one offset call.
-            */
-            FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
-                                          dy - ((bbox.yMin + dy) & ~63));
+
+            memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
 
             if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+                FT_Outline_Translate(outline, dx, dy);
                 FT_Error err = FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V :
                                                                      FT_RENDER_MODE_LCD);
                 if (err) {
                     SK_TRACEFTR(err, "Could not render glyph.");
-                    sk_bzero(glyph.fImage, glyph.computeImageSize());
                     return;
                 }
+
                 SkMask mask;
                 glyph.toMask(&mask);
+#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
+                memset(mask.fImage, 0x80, mask.fBounds.height() * mask.fRowBytes);
+#endif
+                FT_GlyphSlotRec& ftGlyph = *face->glyph;
+
+                if (!SkIRect::Intersects(mask.fBounds,
+                                         SkIRect::MakeXYWH( ftGlyph.bitmap_left,
+                                                           -ftGlyph.bitmap_top,
+                                                            ftGlyph.bitmap.width,
+                                                            ftGlyph.bitmap.rows)))
+                {
+                    return;
+                }
+
+                // If the FT_Bitmap extent is larger, discard bits of the bitmap outside the mask.
+                // If the SkMask extent is larger, shrink mask to fit bitmap (clearing discarded).
+                unsigned char* origBuffer = ftGlyph.bitmap.buffer;
+                // First align the top left (origin).
+                if (-ftGlyph.bitmap_top < mask.fBounds.fTop) {
+                    int32_t topDiff = mask.fBounds.fTop - (-ftGlyph.bitmap_top);
+                    ftGlyph.bitmap.buffer += ftGlyph.bitmap.pitch * topDiff;
+                    ftGlyph.bitmap.rows -= topDiff;
+                    ftGlyph.bitmap_top = -mask.fBounds.fTop;
+                }
+                if (ftGlyph.bitmap_left < mask.fBounds.fLeft) {
+                    int32_t leftDiff = mask.fBounds.fLeft - ftGlyph.bitmap_left;
+                    ftGlyph.bitmap.buffer += leftDiff;
+                    ftGlyph.bitmap.width -= leftDiff;
+                    ftGlyph.bitmap_left = mask.fBounds.fLeft;
+                }
+                if (mask.fBounds.fTop < -ftGlyph.bitmap_top) {
+                    mask.fImage += mask.fRowBytes * (-ftGlyph.bitmap_top - mask.fBounds.fTop);
+                    mask.fBounds.fTop = -ftGlyph.bitmap_top;
+                }
+                if (mask.fBounds.fLeft < ftGlyph.bitmap_left) {
+                    mask.fImage += sizeof(uint16_t) * (ftGlyph.bitmap_left - mask.fBounds.fLeft);
+                    mask.fBounds.fLeft = ftGlyph.bitmap_left;
+                }
+                // Origins aligned, clean up the width and height.
+                int ftVertScale = (doVert ? 3 : 1);
+                int ftHoriScale = (doVert ? 1 : 3);
+                if (mask.fBounds.height() * ftVertScale < SkToInt(ftGlyph.bitmap.rows)) {
+                    ftGlyph.bitmap.rows = mask.fBounds.height() * ftVertScale;
+                }
+                if (mask.fBounds.width() * ftHoriScale < SkToInt(ftGlyph.bitmap.width)) {
+                    ftGlyph.bitmap.width = mask.fBounds.width() * ftHoriScale;
+                }
+                if (SkToInt(ftGlyph.bitmap.rows) < mask.fBounds.height() * ftVertScale) {
+                    mask.fBounds.fBottom = mask.fBounds.fTop + ftGlyph.bitmap.rows / ftVertScale;
+                }
+                if (SkToInt(ftGlyph.bitmap.width) < mask.fBounds.width() * ftHoriScale) {
+                    mask.fBounds.fRight = mask.fBounds.fLeft + ftGlyph.bitmap.width / ftHoriScale;
+                }
                 if (fPreBlend.isApplicable()) {
-                    copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR,
+                    copyFT2LCD16<true>(ftGlyph.bitmap, mask, doBGR,
                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
                 } else {
-                    copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR,
+                    copyFT2LCD16<false>(ftGlyph.bitmap, mask, doBGR,
                                         fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
                 }
+                // Restore the buffer pointer so FreeType can properly free it.
+                ftGlyph.bitmap.buffer = origBuffer;
             } else {
+                FT_BBox     bbox;
+                FT_Bitmap   target;
+                FT_Outline_Get_CBox(outline, &bbox);
+                /*
+                    what we really want to do for subpixel is
+                        offset(dx, dy)
+                        compute_bounds
+                        offset(bbox & !63)
+                    but that is two calls to offset, so we do the following, which
+                    achieves the same thing with only one offset call.
+                */
+                FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
+                                              dy - ((bbox.yMin + dy) & ~63));
+
                 target.width = glyph.fWidth;
                 target.rows = glyph.fHeight;
                 target.pitch = glyph.rowBytes();
                 target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
                 target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat);
                 target.num_grays = 256;
 
-                memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
                 FT_Outline_Get_Bitmap(face->glyph->library, outline, &target);
+#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
+                for (int y = 0; y < glyph.fHeight; ++y) {
+                    for (int x = 0; x < glyph.fWidth; ++x) {
+                        uint8_t& a = ((uint8_t*)glyph.fImage)[(glyph.rowBytes() * y) + x];
+                        a = SkTMax<uint8_t>(a, 0x20);
+                    }
+                }
+#endif
             }
         } break;
 
         case FT_GLYPH_FORMAT_BITMAP: {
             FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode);
             SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
 
             // Assume that the other formats do not exist.