|
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/. */ |
|
5 |
|
6 #include "gfxFontMissingGlyphs.h" |
|
7 #include "nsDeviceContext.h" |
|
8 #include "gfxContext.h" |
|
9 #include "gfxColor.h" |
|
10 |
|
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)) |
|
15 |
|
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 }; |
|
98 |
|
99 /* Parameters that control the rendering of hexboxes. They look like this: |
|
100 |
|
101 BMP codepoints non-BMP codepoints |
|
102 (U+0000 - U+FFFF) (U+10000 - U+10FFFF) |
|
103 |
|
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 */ |
|
120 |
|
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 |
|
168 |
|
169 void |
|
170 gfxFontMissingGlyphs::DrawMissingGlyph(gfxContext *aContext, |
|
171 const gfxRect& aRect, |
|
172 uint32_t aChar, |
|
173 uint32_t aAppUnitsPerDevPixel) |
|
174 { |
|
175 aContext->Save(); |
|
176 |
|
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 } |
|
183 |
|
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); |
|
203 |
|
204 #ifdef MOZ_GFX_OPTIMIZE_MOBILE |
|
205 aContext->Fill(); |
|
206 #else |
|
207 aContext->Stroke(); |
|
208 #endif |
|
209 } |
|
210 |
|
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 |
|
260 |
|
261 aContext->Restore(); |
|
262 } |
|
263 |
|
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 } |