1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2007
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Robert O'Callahan <robert@ocallahan.org>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "gfxFontMissingGlyphs.h"
39 :
40 : #define CHAR_BITS(b00, b01, b02, b10, b11, b12, b20, b21, b22, b30, b31, b32, b40, b41, b42) \
41 : ((b00 << 0) | (b01 << 1) | (b02 << 2) | (b10 << 3) | (b11 << 4) | (b12 << 5) | \
42 : (b20 << 6) | (b21 << 7) | (b22 << 8) | (b30 << 9) | (b31 << 10) | (b32 << 11) | \
43 : (b40 << 12) | (b41 << 13) | (b42 << 14))
44 :
45 : static const PRUint16 glyphMicroFont[16] = {
46 : CHAR_BITS(0, 1, 0,
47 : 1, 0, 1,
48 : 1, 0, 1,
49 : 1, 0, 1,
50 : 0, 1, 0),
51 : CHAR_BITS(0, 1, 0,
52 : 0, 1, 0,
53 : 0, 1, 0,
54 : 0, 1, 0,
55 : 0, 1, 0),
56 : CHAR_BITS(1, 1, 1,
57 : 0, 0, 1,
58 : 1, 1, 1,
59 : 1, 0, 0,
60 : 1, 1, 1),
61 : CHAR_BITS(1, 1, 1,
62 : 0, 0, 1,
63 : 1, 1, 1,
64 : 0, 0, 1,
65 : 1, 1, 1),
66 : CHAR_BITS(1, 0, 1,
67 : 1, 0, 1,
68 : 1, 1, 1,
69 : 0, 0, 1,
70 : 0, 0, 1),
71 : CHAR_BITS(1, 1, 1,
72 : 1, 0, 0,
73 : 1, 1, 1,
74 : 0, 0, 1,
75 : 1, 1, 1),
76 : CHAR_BITS(1, 1, 1,
77 : 1, 0, 0,
78 : 1, 1, 1,
79 : 1, 0, 1,
80 : 1, 1, 1),
81 : CHAR_BITS(1, 1, 1,
82 : 0, 0, 1,
83 : 0, 0, 1,
84 : 0, 0, 1,
85 : 0, 0, 1),
86 : CHAR_BITS(0, 1, 0,
87 : 1, 0, 1,
88 : 0, 1, 0,
89 : 1, 0, 1,
90 : 0, 1, 0),
91 : CHAR_BITS(1, 1, 1,
92 : 1, 0, 1,
93 : 1, 1, 1,
94 : 0, 0, 1,
95 : 0, 0, 1),
96 : CHAR_BITS(1, 1, 1,
97 : 1, 0, 1,
98 : 1, 1, 1,
99 : 1, 0, 1,
100 : 1, 0, 1),
101 : CHAR_BITS(1, 1, 0,
102 : 1, 0, 1,
103 : 1, 1, 0,
104 : 1, 0, 1,
105 : 1, 1, 0),
106 : CHAR_BITS(0, 1, 1,
107 : 1, 0, 0,
108 : 1, 0, 0,
109 : 1, 0, 0,
110 : 0, 1, 1),
111 : CHAR_BITS(1, 1, 0,
112 : 1, 0, 1,
113 : 1, 0, 1,
114 : 1, 0, 1,
115 : 1, 1, 0),
116 : CHAR_BITS(1, 1, 1,
117 : 1, 0, 0,
118 : 1, 1, 1,
119 : 1, 0, 0,
120 : 1, 1, 1),
121 : CHAR_BITS(1, 1, 1,
122 : 1, 0, 0,
123 : 1, 1, 1,
124 : 1, 0, 0,
125 : 1, 0, 0)
126 : };
127 :
128 : /* Parameters that control the rendering of hexboxes. They look like this:
129 :
130 : BMP codepoints non-BMP codepoints
131 : (U+0000 - U+FFFF) (U+10000 - U+10FFFF)
132 :
133 : +---------+ +-------------+
134 : | | | |
135 : | HHH HHH | | HHH HHH HHH |
136 : | HHH HHH | | HHH HHH HHH |
137 : | HHH HHH | | HHH HHH HHH |
138 : | HHH HHH | | HHH HHH HHH |
139 : | HHH HHH | | HHH HHH HHH |
140 : | | | |
141 : | HHH HHH | | HHH HHH HHH |
142 : | HHH HHH | | HHH HHH HHH |
143 : | HHH HHH | | HHH HHH HHH |
144 : | HHH HHH | | HHH HHH HHH |
145 : | HHH HHH | | HHH HHH HHH |
146 : | | | |
147 : +---------+ +-------------+
148 : */
149 :
150 : /** Width of a minifont glyph (see above) */
151 : static const int MINIFONT_WIDTH = 3;
152 : /** Height of a minifont glyph (see above) */
153 : static const int MINIFONT_HEIGHT = 5;
154 : /**
155 : * Gap between minifont glyphs (both horizontal and vertical) and also
156 : * the minimum desired gap between the box border and the glyphs
157 : */
158 : static const int HEX_CHAR_GAP = 1;
159 : /**
160 : * The amount of space between the vertical edge of the glyphbox and the
161 : * box border. We make this nonzero so that when multiple missing glyphs
162 : * occur consecutively there's a gap between their rendered boxes.
163 : */
164 : static const int BOX_HORIZONTAL_INSET = 1;
165 : /** The width of the border */
166 : static const int BOX_BORDER_WIDTH = 1;
167 : /**
168 : * The scaling factor for the border opacity; this is multiplied by the current
169 : * opacity being used to draw the text.
170 : */
171 : static const gfxFloat BOX_BORDER_OPACITY = 0.5;
172 : /**
173 : * Draw a single hex character using the current color. A nice way to do this
174 : * would be to fill in an A8 image surface and then use it as a mask
175 : * to paint the current color. Tragically this doesn't currently work with the
176 : * Quartz cairo backend which doesn't generally support masking with surfaces.
177 : * So for now we just paint a bunch of rectangles...
178 : */
179 : static void
180 0 : DrawHexChar(gfxContext *aContext, const gfxPoint& aPt, PRUint32 aDigit)
181 : {
182 0 : aContext->NewPath();
183 0 : PRUint32 glyphBits = glyphMicroFont[aDigit];
184 : int x, y;
185 0 : for (y = 0; y < MINIFONT_HEIGHT; ++y) {
186 0 : for (x = 0; x < MINIFONT_WIDTH; ++x) {
187 0 : if (glyphBits & 1) {
188 0 : aContext->Rectangle(gfxRect(x, y, 1, 1) + aPt, true);
189 : }
190 0 : glyphBits >>= 1;
191 : }
192 : }
193 0 : aContext->Fill();
194 0 : }
195 :
196 : void
197 0 : gfxFontMissingGlyphs::DrawMissingGlyph(gfxContext *aContext, const gfxRect& aRect,
198 : PRUint32 aChar)
199 : {
200 0 : aContext->Save();
201 :
202 0 : gfxRGBA currentColor;
203 0 : if (!aContext->GetDeviceColor(currentColor)) {
204 : // We're currently drawing with some kind of pattern... Just draw
205 : // the missing-glyph data in black.
206 0 : currentColor = gfxRGBA(0,0,0,1);
207 : }
208 :
209 : // Stroke a rectangle so that the stroke's left edge is inset one pixel
210 : // from the left edge of the glyph box and the stroke's right edge
211 : // is inset one pixel from the right edge of the glyph box.
212 0 : gfxFloat halfBorderWidth = BOX_BORDER_WIDTH/2.0;
213 0 : gfxFloat borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
214 0 : gfxFloat borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
215 0 : gfxRect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth,
216 0 : borderRight - borderLeft, aRect.Height() - 2*halfBorderWidth);
217 0 : if (!borderStrokeRect.IsEmpty()) {
218 0 : aContext->SetLineWidth(BOX_BORDER_WIDTH);
219 0 : aContext->SetDash(gfxContext::gfxLineSolid);
220 0 : aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
221 0 : aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
222 0 : gfxRGBA color = currentColor;
223 0 : color.a *= BOX_BORDER_OPACITY;
224 0 : aContext->SetDeviceColor(color);
225 0 : aContext->NewPath();
226 0 : aContext->Rectangle(borderStrokeRect);
227 :
228 : #ifdef MOZ_GFX_OPTIMIZE_MOBILE
229 : aContext->Fill();
230 : #else
231 0 : aContext->Stroke();
232 : #endif
233 : }
234 :
235 : #ifndef MOZ_GFX_OPTIMIZE_MOBILE
236 0 : gfxPoint center(aRect.X() + aRect.Width()/2,
237 0 : aRect.Y() + aRect.Height()/2);
238 0 : gfxFloat halfGap = HEX_CHAR_GAP/2.0;
239 0 : gfxFloat top = -(MINIFONT_HEIGHT + halfGap);
240 0 : if (aChar < 0x10000) {
241 0 : if (aRect.Width() >= 2*MINIFONT_WIDTH + HEX_CHAR_GAP &&
242 0 : aRect.Height() >= 2*MINIFONT_HEIGHT + HEX_CHAR_GAP) {
243 : // Draw 4 digits for BMP
244 0 : aContext->SetDeviceColor(currentColor);
245 0 : gfxFloat left = -(MINIFONT_WIDTH + halfGap);
246 : DrawHexChar(aContext,
247 0 : center + gfxPoint(left, top), (aChar >> 12) & 0xF);
248 : DrawHexChar(aContext,
249 0 : center + gfxPoint(halfGap, top), (aChar >> 8) & 0xF);
250 : DrawHexChar(aContext,
251 0 : center + gfxPoint(left, halfGap), (aChar >> 4) & 0xF);
252 : DrawHexChar(aContext,
253 0 : center + gfxPoint(halfGap, halfGap), aChar & 0xF);
254 : }
255 : } else {
256 0 : if (aRect.Width() >= 3*MINIFONT_WIDTH + 2*HEX_CHAR_GAP &&
257 0 : aRect.Height() >= 2*MINIFONT_HEIGHT + HEX_CHAR_GAP) {
258 : // Draw 6 digits for non-BMP
259 0 : aContext->SetDeviceColor(currentColor);
260 0 : gfxFloat first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
261 0 : gfxFloat second = -(MINIFONT_WIDTH / 2.0);
262 0 : gfxFloat third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
263 : DrawHexChar(aContext,
264 0 : center + gfxPoint(first, top), (aChar >> 20) & 0xF);
265 : DrawHexChar(aContext,
266 0 : center + gfxPoint(second, top), (aChar >> 16) & 0xF);
267 : DrawHexChar(aContext,
268 0 : center + gfxPoint(third, top), (aChar >> 12) & 0xF);
269 : DrawHexChar(aContext,
270 0 : center + gfxPoint(first, halfGap), (aChar >> 8) & 0xF);
271 : DrawHexChar(aContext,
272 0 : center + gfxPoint(second, halfGap), (aChar >> 4) & 0xF);
273 : DrawHexChar(aContext,
274 0 : center + gfxPoint(third, halfGap), aChar & 0xF);
275 : }
276 : }
277 : #endif
278 :
279 0 : aContext->Restore();
280 0 : }
281 :
282 : gfxFloat
283 0 : gfxFontMissingGlyphs::GetDesiredMinWidth(PRUint32 aChar)
284 : {
285 : /**
286 : * The minimum desired width for a missing-glyph glyph box. I've laid it out
287 : * like this so you can see what goes where.
288 : */
289 : return BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP +
290 : MINIFONT_WIDTH + HEX_CHAR_GAP + MINIFONT_WIDTH +
291 : ((aChar < 0x10000) ? 0 : HEX_CHAR_GAP + MINIFONT_WIDTH) +
292 0 : HEX_CHAR_GAP + BOX_BORDER_WIDTH + BOX_HORIZONTAL_INSET;
293 : }
|