Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "gfxFontMissingGlyphs.h"
7 #include "nsDeviceContext.h"
8 #include "gfxContext.h"
9 #include "gfxColor.h"
11 #define CHAR_BITS(b00, b01, b02, b10, b11, b12, b20, b21, b22, b30, b31, b32, b40, b41, b42) \
12 ((b00 << 0) | (b01 << 1) | (b02 << 2) | (b10 << 3) | (b11 << 4) | (b12 << 5) | \
13 (b20 << 6) | (b21 << 7) | (b22 << 8) | (b30 << 9) | (b31 << 10) | (b32 << 11) | \
14 (b40 << 12) | (b41 << 13) | (b42 << 14))
16 static const uint16_t glyphMicroFont[16] = {
17 CHAR_BITS(0, 1, 0,
18 1, 0, 1,
19 1, 0, 1,
20 1, 0, 1,
21 0, 1, 0),
22 CHAR_BITS(0, 1, 0,
23 0, 1, 0,
24 0, 1, 0,
25 0, 1, 0,
26 0, 1, 0),
27 CHAR_BITS(1, 1, 1,
28 0, 0, 1,
29 1, 1, 1,
30 1, 0, 0,
31 1, 1, 1),
32 CHAR_BITS(1, 1, 1,
33 0, 0, 1,
34 1, 1, 1,
35 0, 0, 1,
36 1, 1, 1),
37 CHAR_BITS(1, 0, 1,
38 1, 0, 1,
39 1, 1, 1,
40 0, 0, 1,
41 0, 0, 1),
42 CHAR_BITS(1, 1, 1,
43 1, 0, 0,
44 1, 1, 1,
45 0, 0, 1,
46 1, 1, 1),
47 CHAR_BITS(1, 1, 1,
48 1, 0, 0,
49 1, 1, 1,
50 1, 0, 1,
51 1, 1, 1),
52 CHAR_BITS(1, 1, 1,
53 0, 0, 1,
54 0, 0, 1,
55 0, 0, 1,
56 0, 0, 1),
57 CHAR_BITS(0, 1, 0,
58 1, 0, 1,
59 0, 1, 0,
60 1, 0, 1,
61 0, 1, 0),
62 CHAR_BITS(1, 1, 1,
63 1, 0, 1,
64 1, 1, 1,
65 0, 0, 1,
66 0, 0, 1),
67 CHAR_BITS(1, 1, 1,
68 1, 0, 1,
69 1, 1, 1,
70 1, 0, 1,
71 1, 0, 1),
72 CHAR_BITS(1, 1, 0,
73 1, 0, 1,
74 1, 1, 0,
75 1, 0, 1,
76 1, 1, 0),
77 CHAR_BITS(0, 1, 1,
78 1, 0, 0,
79 1, 0, 0,
80 1, 0, 0,
81 0, 1, 1),
82 CHAR_BITS(1, 1, 0,
83 1, 0, 1,
84 1, 0, 1,
85 1, 0, 1,
86 1, 1, 0),
87 CHAR_BITS(1, 1, 1,
88 1, 0, 0,
89 1, 1, 1,
90 1, 0, 0,
91 1, 1, 1),
92 CHAR_BITS(1, 1, 1,
93 1, 0, 0,
94 1, 1, 1,
95 1, 0, 0,
96 1, 0, 0)
97 };
99 /* Parameters that control the rendering of hexboxes. They look like this:
101 BMP codepoints non-BMP codepoints
102 (U+0000 - U+FFFF) (U+10000 - U+10FFFF)
104 +---------+ +-------------+
105 | | | |
106 | HHH HHH | | HHH HHH HHH |
107 | HHH HHH | | HHH HHH HHH |
108 | HHH HHH | | HHH HHH HHH |
109 | HHH HHH | | HHH HHH HHH |
110 | HHH HHH | | HHH HHH HHH |
111 | | | |
112 | HHH HHH | | HHH HHH HHH |
113 | HHH HHH | | HHH HHH HHH |
114 | HHH HHH | | HHH HHH HHH |
115 | HHH HHH | | HHH HHH HHH |
116 | HHH HHH | | HHH HHH HHH |
117 | | | |
118 +---------+ +-------------+
119 */
121 /** Width of a minifont glyph (see above) */
122 static const int MINIFONT_WIDTH = 3;
123 /** Height of a minifont glyph (see above) */
124 static const int MINIFONT_HEIGHT = 5;
125 /**
126 * Gap between minifont glyphs (both horizontal and vertical) and also
127 * the minimum desired gap between the box border and the glyphs
128 */
129 static const int HEX_CHAR_GAP = 1;
130 /**
131 * The amount of space between the vertical edge of the glyphbox and the
132 * box border. We make this nonzero so that when multiple missing glyphs
133 * occur consecutively there's a gap between their rendered boxes.
134 */
135 static const int BOX_HORIZONTAL_INSET = 1;
136 /** The width of the border */
137 static const int BOX_BORDER_WIDTH = 1;
138 /**
139 * The scaling factor for the border opacity; this is multiplied by the current
140 * opacity being used to draw the text.
141 */
142 static const gfxFloat BOX_BORDER_OPACITY = 0.5;
143 /**
144 * Draw a single hex character using the current color. A nice way to do this
145 * would be to fill in an A8 image surface and then use it as a mask
146 * to paint the current color. Tragically this doesn't currently work with the
147 * Quartz cairo backend which doesn't generally support masking with surfaces.
148 * So for now we just paint a bunch of rectangles...
149 */
150 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
151 static void
152 DrawHexChar(gfxContext *aContext, const gfxPoint& aPt, uint32_t aDigit)
153 {
154 aContext->NewPath();
155 uint32_t glyphBits = glyphMicroFont[aDigit];
156 int x, y;
157 for (y = 0; y < MINIFONT_HEIGHT; ++y) {
158 for (x = 0; x < MINIFONT_WIDTH; ++x) {
159 if (glyphBits & 1) {
160 aContext->Rectangle(gfxRect(x, y, 1, 1) + aPt, true);
161 }
162 glyphBits >>= 1;
163 }
164 }
165 aContext->Fill();
166 }
167 #endif // MOZ_GFX_OPTIMIZE_MOBILE
169 void
170 gfxFontMissingGlyphs::DrawMissingGlyph(gfxContext *aContext,
171 const gfxRect& aRect,
172 uint32_t aChar,
173 uint32_t aAppUnitsPerDevPixel)
174 {
175 aContext->Save();
177 gfxRGBA currentColor;
178 if (!aContext->GetDeviceColor(currentColor)) {
179 // We're currently drawing with some kind of pattern... Just draw
180 // the missing-glyph data in black.
181 currentColor = gfxRGBA(0,0,0,1);
182 }
184 // Stroke a rectangle so that the stroke's left edge is inset one pixel
185 // from the left edge of the glyph box and the stroke's right edge
186 // is inset one pixel from the right edge of the glyph box.
187 gfxFloat halfBorderWidth = BOX_BORDER_WIDTH / 2.0;
188 gfxFloat borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
189 gfxFloat borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
190 gfxRect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth,
191 borderRight - borderLeft,
192 aRect.Height() - 2.0 * halfBorderWidth);
193 if (!borderStrokeRect.IsEmpty()) {
194 aContext->SetLineWidth(BOX_BORDER_WIDTH);
195 aContext->SetDash(gfxContext::gfxLineSolid);
196 aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
197 aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
198 gfxRGBA color = currentColor;
199 color.a *= BOX_BORDER_OPACITY;
200 aContext->SetDeviceColor(color);
201 aContext->NewPath();
202 aContext->Rectangle(borderStrokeRect);
204 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
205 aContext->Fill();
206 #else
207 aContext->Stroke();
208 #endif
209 }
211 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
212 gfxPoint center(aRect.X() + aRect.Width() / 2,
213 aRect.Y() + aRect.Height() / 2);
214 gfxFloat halfGap = HEX_CHAR_GAP / 2.0;
215 gfxFloat top = -(MINIFONT_HEIGHT + halfGap);
216 aContext->SetDeviceColor(currentColor);
217 aContext->Translate(center);
218 // We always want integer scaling, otherwise the "bitmap" glyphs will look
219 // even uglier than usual when zoomed
220 int32_t scale =
221 std::max<int32_t>(1, nsDeviceContext::AppUnitsPerCSSPixel() /
222 aAppUnitsPerDevPixel);
223 aContext->Scale(gfxFloat(scale), gfxFloat(scale));
224 if (aChar < 0x10000) {
225 if (aRect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
226 aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
227 // Draw 4 digits for BMP
228 gfxFloat left = -(MINIFONT_WIDTH + halfGap);
229 DrawHexChar(aContext,
230 gfxPoint(left, top), (aChar >> 12) & 0xF);
231 DrawHexChar(aContext,
232 gfxPoint(halfGap, top), (aChar >> 8) & 0xF);
233 DrawHexChar(aContext,
234 gfxPoint(left, halfGap), (aChar >> 4) & 0xF);
235 DrawHexChar(aContext,
236 gfxPoint(halfGap, halfGap), aChar & 0xF);
237 }
238 } else {
239 if (aRect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
240 aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
241 // Draw 6 digits for non-BMP
242 gfxFloat first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
243 gfxFloat second = -(MINIFONT_WIDTH / 2.0);
244 gfxFloat third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
245 DrawHexChar(aContext,
246 gfxPoint(first, top), (aChar >> 20) & 0xF);
247 DrawHexChar(aContext,
248 gfxPoint(second, top), (aChar >> 16) & 0xF);
249 DrawHexChar(aContext,
250 gfxPoint(third, top), (aChar >> 12) & 0xF);
251 DrawHexChar(aContext,
252 gfxPoint(first, halfGap), (aChar >> 8) & 0xF);
253 DrawHexChar(aContext,
254 gfxPoint(second, halfGap), (aChar >> 4) & 0xF);
255 DrawHexChar(aContext,
256 gfxPoint(third, halfGap), aChar & 0xF);
257 }
258 }
259 #endif
261 aContext->Restore();
262 }
264 gfxFloat
265 gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar,
266 uint32_t aAppUnitsPerDevPixel)
267 {
268 /**
269 * The minimum desired width for a missing-glyph glyph box. I've laid it out
270 * like this so you can see what goes where.
271 */
272 gfxFloat width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP +
273 MINIFONT_WIDTH + HEX_CHAR_GAP + MINIFONT_WIDTH +
274 ((aChar < 0x10000) ? 0 : HEX_CHAR_GAP + MINIFONT_WIDTH) +
275 HEX_CHAR_GAP + BOX_BORDER_WIDTH + BOX_HORIZONTAL_INSET;
276 // Note that this will give us floating-point division, so the width will
277 // -not- be snapped to integer multiples of its basic pixel value
278 width *= gfxFloat(nsDeviceContext::AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel;
279 return width;
280 }