/src/mozilla-central/gfx/thebes/gfxFontMissingGlyphs.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
8 | | #include "gfxUtils.h" |
9 | | #include "mozilla/gfx/2D.h" |
10 | | #include "mozilla/gfx/Helpers.h" |
11 | | #include "mozilla/gfx/PathHelpers.h" |
12 | | #include "mozilla/LinkedList.h" |
13 | | #include "mozilla/RefPtr.h" |
14 | | #include "nsDeviceContext.h" |
15 | | #include "nsLayoutUtils.h" |
16 | | #include "TextDrawTarget.h" |
17 | | |
18 | | using namespace mozilla; |
19 | | using namespace mozilla::gfx; |
20 | | |
21 | | #define X 255 |
22 | | static const uint8_t gMiniFontData[] = { |
23 | | 0,X,0, 0,X,0, X,X,X, X,X,X, X,0,X, X,X,X, X,X,X, X,X,X, X,X,X, X,X,X, X,X,X, X,X,0, 0,X,X, X,X,0, X,X,X, X,X,X, |
24 | | X,0,X, 0,X,0, 0,0,X, 0,0,X, X,0,X, X,0,0, X,0,0, 0,0,X, X,0,X, X,0,X, X,0,X, X,0,X, X,0,0, X,0,X, X,0,0, X,0,0, |
25 | | X,0,X, 0,X,0, X,X,X, X,X,X, X,X,X, X,X,X, X,X,X, 0,0,X, X,X,X, X,X,X, X,X,X, X,X,0, X,0,0, X,0,X, X,X,X, X,X,X, |
26 | | X,0,X, 0,X,0, X,0,0, 0,0,X, 0,0,X, 0,0,X, X,0,X, 0,0,X, X,0,X, 0,0,X, X,0,X, X,0,X, X,0,0, X,0,X, X,0,0, X,0,0, |
27 | | 0,X,0, 0,X,0, X,X,X, X,X,X, 0,0,X, X,X,X, X,X,X, 0,0,X, X,X,X, 0,0,X, X,0,X, X,X,0, 0,X,X, X,X,0, X,X,X, X,0,0, |
28 | | }; |
29 | | #undef X |
30 | | |
31 | | /* Parameters that control the rendering of hexboxes. They look like this: |
32 | | |
33 | | BMP codepoints non-BMP codepoints |
34 | | (U+0000 - U+FFFF) (U+10000 - U+10FFFF) |
35 | | |
36 | | +---------+ +-------------+ |
37 | | | | | | |
38 | | | HHH HHH | | HHH HHH HHH | |
39 | | | HHH HHH | | HHH HHH HHH | |
40 | | | HHH HHH | | HHH HHH HHH | |
41 | | | HHH HHH | | HHH HHH HHH | |
42 | | | HHH HHH | | HHH HHH HHH | |
43 | | | | | | |
44 | | | HHH HHH | | HHH HHH HHH | |
45 | | | HHH HHH | | HHH HHH HHH | |
46 | | | HHH HHH | | HHH HHH HHH | |
47 | | | HHH HHH | | HHH HHH HHH | |
48 | | | HHH HHH | | HHH HHH HHH | |
49 | | | | | | |
50 | | +---------+ +-------------+ |
51 | | */ |
52 | | |
53 | | /** Width of a minifont glyph (see above) */ |
54 | | static const int MINIFONT_WIDTH = 3; |
55 | | /** Height of a minifont glyph (see above) */ |
56 | | static const int MINIFONT_HEIGHT = 5; |
57 | | /** |
58 | | * Gap between minifont glyphs (both horizontal and vertical) and also |
59 | | * the minimum desired gap between the box border and the glyphs |
60 | | */ |
61 | | static const int HEX_CHAR_GAP = 1; |
62 | | /** |
63 | | * The amount of space between the vertical edge of the glyphbox and the |
64 | | * box border. We make this nonzero so that when multiple missing glyphs |
65 | | * occur consecutively there's a gap between their rendered boxes. |
66 | | */ |
67 | | static const int BOX_HORIZONTAL_INSET = 1; |
68 | | /** The width of the border */ |
69 | | static const int BOX_BORDER_WIDTH = 1; |
70 | | /** |
71 | | * The scaling factor for the border opacity; this is multiplied by the current |
72 | | * opacity being used to draw the text. |
73 | | */ |
74 | | static const Float BOX_BORDER_OPACITY = 0.5; |
75 | | |
76 | | #ifndef MOZ_GFX_OPTIMIZE_MOBILE |
77 | | |
78 | | static RefPtr<DrawTarget> gGlyphDrawTarget; |
79 | | static RefPtr<SourceSurface> gGlyphMask; |
80 | | static RefPtr<SourceSurface> gGlyphAtlas; |
81 | | static Color gGlyphColor; |
82 | | |
83 | | /** |
84 | | * Generates a new colored mini-font atlas from the mini-font mask. |
85 | | */ |
86 | | static bool |
87 | | MakeGlyphAtlas(const Color& aColor) |
88 | 0 | { |
89 | 0 | gGlyphAtlas = nullptr; |
90 | 0 | if (!gGlyphDrawTarget) { |
91 | 0 | gGlyphDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( |
92 | 0 | IntSize(MINIFONT_WIDTH * 16, MINIFONT_HEIGHT), SurfaceFormat::B8G8R8A8); |
93 | 0 | if (!gGlyphDrawTarget) { |
94 | 0 | return false; |
95 | 0 | } |
96 | 0 | } |
97 | 0 | if (!gGlyphMask) { |
98 | 0 | gGlyphMask = gGlyphDrawTarget->CreateSourceSurfaceFromData( |
99 | 0 | const_cast<uint8_t*>(gMiniFontData), IntSize(MINIFONT_WIDTH * 16, MINIFONT_HEIGHT), |
100 | 0 | MINIFONT_WIDTH * 16, SurfaceFormat::A8); |
101 | 0 | if (!gGlyphMask) { |
102 | 0 | return false; |
103 | 0 | } |
104 | 0 | } |
105 | 0 | gGlyphDrawTarget->MaskSurface(ColorPattern(aColor), gGlyphMask, Point(0, 0), |
106 | 0 | DrawOptions(1.0f, CompositionOp::OP_SOURCE)); |
107 | 0 | gGlyphAtlas = gGlyphDrawTarget->Snapshot(); |
108 | 0 | if (!gGlyphAtlas) { |
109 | 0 | return false; |
110 | 0 | } |
111 | 0 | gGlyphColor = aColor; |
112 | 0 | return true; |
113 | 0 | } |
114 | | |
115 | | /** |
116 | | * Reuse the current mini-font atlas if the color matches, otherwise regenerate it. |
117 | | */ |
118 | | static inline already_AddRefed<SourceSurface> |
119 | | GetGlyphAtlas(const Color& aColor) |
120 | 0 | { |
121 | 0 | // Get the opaque color, ignoring any transparency which will be handled later. |
122 | 0 | Color color(aColor.r, aColor.g, aColor.b); |
123 | 0 | if ((gGlyphAtlas && gGlyphColor == color) || MakeGlyphAtlas(color)) { |
124 | 0 | return do_AddRef(gGlyphAtlas); |
125 | 0 | } |
126 | 0 | return nullptr; |
127 | 0 | } |
128 | | |
129 | | /** |
130 | | * Clear any cached glyph atlas resources. |
131 | | */ |
132 | | static void |
133 | | PurgeGlyphAtlas() |
134 | 0 | { |
135 | 0 | gGlyphAtlas = nullptr; |
136 | 0 | gGlyphDrawTarget = nullptr; |
137 | 0 | gGlyphMask = nullptr; |
138 | 0 | } |
139 | | |
140 | | // WebRender layer manager user data that will get signaled when the layer |
141 | | // manager is destroyed. |
142 | | class WRUserData : public layers::LayerUserData, public LinkedListElement<WRUserData> |
143 | | { |
144 | | public: |
145 | | explicit WRUserData(layers::WebRenderLayerManager* aManager); |
146 | | |
147 | | ~WRUserData(); |
148 | | |
149 | | static void |
150 | | Assign(layers::WebRenderLayerManager* aManager) |
151 | 0 | { |
152 | 0 | if (!aManager->HasUserData(&sWRUserDataKey)) { |
153 | 0 | aManager->SetUserData(&sWRUserDataKey, new WRUserData(aManager)); |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | void |
158 | | Remove() |
159 | 0 | { |
160 | 0 | mManager->RemoveUserData(&sWRUserDataKey); |
161 | 0 | } |
162 | | |
163 | | layers::WebRenderLayerManager* mManager; |
164 | | |
165 | | static UserDataKey sWRUserDataKey; |
166 | | }; |
167 | | |
168 | | static RefPtr<SourceSurface> gWRGlyphAtlas[8]; |
169 | | static LinkedList<WRUserData> gWRUsers; |
170 | | UserDataKey WRUserData::sWRUserDataKey; |
171 | | |
172 | | /** |
173 | | * Generates a transformed WebRender mini-font atlas for a given orientation. |
174 | | */ |
175 | | static already_AddRefed<SourceSurface> |
176 | | MakeWRGlyphAtlas(const Matrix* aMat) |
177 | 0 | { |
178 | 0 | IntSize size(MINIFONT_WIDTH * 16, MINIFONT_HEIGHT); |
179 | 0 | // If the orientation is transposed, width/height are swapped. |
180 | 0 | if (aMat && aMat->_11 == 0) { |
181 | 0 | std::swap(size.width, size.height); |
182 | 0 | } |
183 | 0 | RefPtr<DrawTarget> ref = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); |
184 | 0 | RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateSimilarSoftwareDrawTarget( |
185 | 0 | ref, size, SurfaceFormat::B8G8R8A8); |
186 | 0 | if (!dt) { |
187 | 0 | return nullptr; |
188 | 0 | } |
189 | 0 | if (aMat) { |
190 | 0 | // Select appropriate transform matrix based on whether the |
191 | 0 | // orientation is transposed. |
192 | 0 | dt->SetTransform(aMat->_11 == 0 ? |
193 | 0 | Matrix(0.0f, copysign(1.0f, aMat->_12), |
194 | 0 | copysign(1.0f, aMat->_21), 0.0f, |
195 | 0 | aMat->_21 < 0 ? MINIFONT_HEIGHT : 0.0f, |
196 | 0 | aMat->_12 < 0 ? MINIFONT_WIDTH * 16 : 0.0f) : |
197 | 0 | Matrix(copysign(1.0f, aMat->_11), 0.0f, |
198 | 0 | 0.0f, copysign(1.0f, aMat->_22), |
199 | 0 | aMat->_11 < 0 ? MINIFONT_WIDTH * 16 : 0.0f, |
200 | 0 | aMat->_22 < 0 ? MINIFONT_HEIGHT : 0.0f)); |
201 | 0 | } |
202 | 0 | RefPtr<SourceSurface> mask = dt->CreateSourceSurfaceFromData( |
203 | 0 | const_cast<uint8_t*>(gMiniFontData), IntSize(MINIFONT_WIDTH * 16, MINIFONT_HEIGHT), |
204 | 0 | MINIFONT_WIDTH * 16, SurfaceFormat::A8); |
205 | 0 | if (!mask) { |
206 | 0 | return nullptr; |
207 | 0 | } |
208 | 0 | dt->MaskSurface(ColorPattern(Color(1.0f, 1.0f, 1.0f)), mask, Point(0, 0)); |
209 | 0 | return dt->Snapshot(); |
210 | 0 | } |
211 | | |
212 | | /** |
213 | | * Clear any cached WebRender glyph atlas resources. |
214 | | */ |
215 | | static void |
216 | | PurgeWRGlyphAtlas() |
217 | 0 | { |
218 | 0 | // For each WR layer manager, we need go through each atlas orientation |
219 | 0 | // and see if it has a stashed image key. If it does, remove the image |
220 | 0 | // from the layer manager. |
221 | 0 | for (WRUserData* user : gWRUsers) { |
222 | 0 | auto* manager = user->mManager; |
223 | 0 | for (size_t i = 0; i < 8; i++) { |
224 | 0 | if (gWRGlyphAtlas[i]) { |
225 | 0 | uint32_t handle = |
226 | 0 | (uint32_t)(uintptr_t)gWRGlyphAtlas[i]->GetUserData( |
227 | 0 | reinterpret_cast<UserDataKey*>(manager)); |
228 | 0 | if (handle) { |
229 | 0 | manager->AddImageKeyForDiscard( |
230 | 0 | wr::ImageKey{manager->WrBridge()->GetNamespace(), handle}); |
231 | 0 | } |
232 | 0 | } |
233 | 0 | } |
234 | 0 | } |
235 | 0 | // Remove the layer managers' destroy notifications only after processing |
236 | 0 | // so as not to mess up gWRUsers iteration. |
237 | 0 | while (!gWRUsers.isEmpty()) { |
238 | 0 | gWRUsers.popFirst()->Remove(); |
239 | 0 | } |
240 | 0 | // Finally, clear out the atlases. |
241 | 0 | for (size_t i = 0; i < 8; i++) { |
242 | 0 | gWRGlyphAtlas[i] = nullptr; |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | WRUserData::WRUserData(layers::WebRenderLayerManager* aManager) |
247 | | : mManager(aManager) |
248 | 0 | { |
249 | 0 | gWRUsers.insertFront(this); |
250 | 0 | } |
251 | | |
252 | | WRUserData::~WRUserData() |
253 | 0 | { |
254 | 0 | // When the layer manager is destroyed, we need go through each |
255 | 0 | // atlas and remove any assigned image keys. |
256 | 0 | if (isInList()) { |
257 | 0 | for (size_t i = 0; i < 8; i++) { |
258 | 0 | if (gWRGlyphAtlas[i]) { |
259 | 0 | gWRGlyphAtlas[i]->RemoveUserData(reinterpret_cast<UserDataKey*>(mManager)); |
260 | 0 | } |
261 | 0 | } |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | | static already_AddRefed<SourceSurface> |
266 | | GetWRGlyphAtlas(DrawTarget& aDrawTarget, const Matrix* aMat) |
267 | 0 | { |
268 | 0 | uint32_t key = 0; |
269 | 0 | // Encode orientation in the key. |
270 | 0 | if (aMat) { |
271 | 0 | if (aMat->_11 == 0) { |
272 | 0 | key |= 4 | (aMat->_12 < 0 ? 1 : 0) | (aMat->_21 < 0 ? 2 : 0); |
273 | 0 | } else { |
274 | 0 | key |= (aMat->_11 < 0 ? 1 : 0) | (aMat->_22 < 0 ? 2 : 0); |
275 | 0 | } |
276 | 0 | } |
277 | 0 | // Check if an atlas was already created, or create one if necessary. |
278 | 0 | RefPtr<SourceSurface> atlas = gWRGlyphAtlas[key]; |
279 | 0 | if (!atlas) { |
280 | 0 | atlas = MakeWRGlyphAtlas(aMat); |
281 | 0 | gWRGlyphAtlas[key] = atlas; |
282 | 0 | } |
283 | 0 | // The atlas may exist, but an image key may not be assigned for it to |
284 | 0 | // the given layer manager. |
285 | 0 | auto* tdt = static_cast<layout::TextDrawTarget*>(&aDrawTarget); |
286 | 0 | auto* manager = tdt->WrLayerManager(); |
287 | 0 | if (!atlas->GetUserData(reinterpret_cast<UserDataKey*>(manager))) { |
288 | 0 | // No image key, so we need to map the atlas' data for transfer to WR. |
289 | 0 | RefPtr<DataSourceSurface> dataSurface = atlas->GetDataSurface(); |
290 | 0 | if (!dataSurface) { |
291 | 0 | return nullptr; |
292 | 0 | } |
293 | 0 | DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ); |
294 | 0 | if (!map.IsMapped()) { |
295 | 0 | return nullptr; |
296 | 0 | } |
297 | 0 | // Transfer the data and get an image key for it. |
298 | 0 | Maybe<wr::ImageKey> result = |
299 | 0 | tdt->DefineImage(atlas->GetSize(), |
300 | 0 | map.GetStride(), |
301 | 0 | atlas->GetFormat(), |
302 | 0 | map.GetData()); |
303 | 0 | if (!result.isSome()) { |
304 | 0 | return nullptr; |
305 | 0 | } |
306 | 0 | // Assign the image key to the atlas. |
307 | 0 | atlas->AddUserData(reinterpret_cast<UserDataKey*>(manager), |
308 | 0 | (void*)(uintptr_t)result.value().mHandle, |
309 | 0 | nullptr); |
310 | 0 | // Create a user data notification for when the layer manager is |
311 | 0 | // destroyed so we can clean up any assigned image keys. |
312 | 0 | WRUserData::Assign(manager); |
313 | 0 | } |
314 | 0 | return atlas.forget(); |
315 | 0 | } |
316 | | |
317 | | static void |
318 | | DrawHexChar(uint32_t aDigit, Float aLeft, Float aTop, DrawTarget& aDrawTarget, |
319 | | SourceSurface* aAtlas, const Color& aColor, |
320 | | const Matrix* aMat = nullptr) |
321 | 0 | { |
322 | 0 | Rect dest(aLeft, aTop, MINIFONT_WIDTH, MINIFONT_HEIGHT); |
323 | 0 | if (aDrawTarget.GetBackendType() == BackendType::WEBRENDER_TEXT) { |
324 | 0 | // For WR, we need to get the image key assigned to the given WR layer manager |
325 | 0 | // for referencing the image. |
326 | 0 | auto* tdt = static_cast<layout::TextDrawTarget*>(&aDrawTarget); |
327 | 0 | auto* manager = tdt->WrLayerManager(); |
328 | 0 | wr::ImageKey key = { |
329 | 0 | manager->WrBridge()->GetNamespace(), |
330 | 0 | (uint32_t)(uintptr_t)aAtlas->GetUserData(reinterpret_cast<UserDataKey*>(manager)) |
331 | 0 | }; |
332 | 0 | // Transform the bounds of the atlas into the given orientation, and then also transform |
333 | 0 | // a small clip rect which will be used to select the given digit from the atlas. |
334 | 0 | Rect bounds(aLeft - aDigit * MINIFONT_WIDTH, aTop, MINIFONT_WIDTH * 16, MINIFONT_HEIGHT); |
335 | 0 | if (aMat) { |
336 | 0 | // Width and height may be negative after the transform, so move the rect |
337 | 0 | // if necessary and fix size. |
338 | 0 | bounds = aMat->TransformRect(bounds); |
339 | 0 | bounds.x += std::min(bounds.width, 0.0f); |
340 | 0 | bounds.y += std::min(bounds.height, 0.0f); |
341 | 0 | bounds.width = fabs(bounds.width); |
342 | 0 | bounds.height = fabs(bounds.height); |
343 | 0 | dest = aMat->TransformRect(dest); |
344 | 0 | dest.x += std::min(dest.width, 0.0f); |
345 | 0 | dest.y += std::min(dest.height, 0.0f); |
346 | 0 | dest.width = fabs(dest.width); |
347 | 0 | dest.height = fabs(dest.height); |
348 | 0 | } |
349 | 0 | // Finally, push the colored image with point filtering. |
350 | 0 | tdt->PushImage(key, |
351 | 0 | wr::ToLayoutRect(bounds), |
352 | 0 | wr::ToLayoutRect(dest), |
353 | 0 | wr::ImageRendering::Pixelated, |
354 | 0 | wr::ToColorF(aColor)); |
355 | 0 | } else { |
356 | 0 | // For the normal case, just draw the given digit from the atlas. Point filtering is used |
357 | 0 | // to ensure the mini-font rectangles stay sharp with any scaling. Handle any transparency |
358 | 0 | // here as well. |
359 | 0 | aDrawTarget.DrawSurface(aAtlas, |
360 | 0 | dest, |
361 | 0 | Rect(aDigit * MINIFONT_WIDTH, 0, MINIFONT_WIDTH, MINIFONT_HEIGHT), |
362 | 0 | DrawSurfaceOptions(SamplingFilter::POINT), |
363 | 0 | DrawOptions(aColor.a, CompositionOp::OP_OVER, AntialiasMode::NONE)); |
364 | 0 | } |
365 | 0 | } |
366 | | |
367 | | void |
368 | | gfxFontMissingGlyphs::Purge() |
369 | 0 | { |
370 | 0 | PurgeGlyphAtlas(); |
371 | 0 | PurgeWRGlyphAtlas(); |
372 | 0 | } |
373 | | |
374 | | #else // MOZ_GFX_OPTIMIZE_MOBILE |
375 | | |
376 | | void |
377 | | gfxFontMissingGlyphs::Purge() |
378 | | { |
379 | | } |
380 | | |
381 | | #endif |
382 | | |
383 | | void |
384 | | gfxFontMissingGlyphs::Shutdown() |
385 | 0 | { |
386 | 0 | Purge(); |
387 | 0 | } |
388 | | |
389 | | void |
390 | | gfxFontMissingGlyphs::DrawMissingGlyph(uint32_t aChar, |
391 | | const Rect& aRect, |
392 | | DrawTarget& aDrawTarget, |
393 | | const Pattern& aPattern, |
394 | | uint32_t aAppUnitsPerDevPixel, |
395 | | const Matrix* aMat) |
396 | 0 | { |
397 | 0 | Rect rect(aRect); |
398 | 0 | // If there is an orientation transform, reorient the bounding rect. |
399 | 0 | if (aMat) { |
400 | 0 | rect.MoveBy(-aRect.BottomLeft()); |
401 | 0 | rect = aMat->TransformBounds(rect); |
402 | 0 | rect.MoveBy(aRect.BottomLeft()); |
403 | 0 | } |
404 | 0 |
|
405 | 0 | // If we're currently drawing with some kind of pattern, we just draw the |
406 | 0 | // missing-glyph data in black. |
407 | 0 | Color color = aPattern.GetType() == PatternType::COLOR ? |
408 | 0 | static_cast<const ColorPattern&>(aPattern).mColor : |
409 | 0 | ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)); |
410 | 0 |
|
411 | 0 | // Stroke a rectangle so that the stroke's left edge is inset one pixel |
412 | 0 | // from the left edge of the glyph box and the stroke's right edge |
413 | 0 | // is inset one pixel from the right edge of the glyph box. |
414 | 0 | Float halfBorderWidth = BOX_BORDER_WIDTH / 2.0; |
415 | 0 | Float borderLeft = rect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth; |
416 | 0 | Float borderRight = rect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth; |
417 | 0 | Rect borderStrokeRect(borderLeft, rect.Y() + halfBorderWidth, |
418 | 0 | borderRight - borderLeft, |
419 | 0 | rect.Height() - 2.0 * halfBorderWidth); |
420 | 0 | if (!borderStrokeRect.IsEmpty()) { |
421 | 0 | ColorPattern adjustedColor(color); |
422 | 0 | adjustedColor.mColor.a *= BOX_BORDER_OPACITY; |
423 | | #ifdef MOZ_GFX_OPTIMIZE_MOBILE |
424 | | aDrawTarget.FillRect(borderStrokeRect, adjustedColor); |
425 | | #else |
426 | | StrokeOptions strokeOptions(BOX_BORDER_WIDTH); |
427 | 0 | aDrawTarget.StrokeRect(borderStrokeRect, adjustedColor, strokeOptions); |
428 | 0 | #endif |
429 | 0 | } |
430 | 0 |
|
431 | 0 | #ifndef MOZ_GFX_OPTIMIZE_MOBILE |
432 | 0 | RefPtr<SourceSurface> atlas = |
433 | 0 | aDrawTarget.GetBackendType() == BackendType::WEBRENDER_TEXT ? |
434 | 0 | GetWRGlyphAtlas(aDrawTarget, aMat) : |
435 | 0 | GetGlyphAtlas(color); |
436 | 0 | if (!atlas) { |
437 | 0 | return; |
438 | 0 | } |
439 | 0 | |
440 | 0 | Point center = rect.Center(); |
441 | 0 | Float halfGap = HEX_CHAR_GAP / 2.f; |
442 | 0 | Float top = -(MINIFONT_HEIGHT + halfGap); |
443 | 0 | // We always want integer scaling, otherwise the "bitmap" glyphs will look |
444 | 0 | // even uglier than usual when zoomed |
445 | 0 | int32_t devPixelsPerCSSPx = |
446 | 0 | std::max<int32_t>(1, AppUnitsPerCSSPixel() / |
447 | 0 | aAppUnitsPerDevPixel); |
448 | 0 |
|
449 | 0 | Matrix tempMat; |
450 | 0 | if (aMat) { |
451 | 0 | // If there is an orientation transform, since draw target transforms may |
452 | 0 | // not be supported, scale and translate it so that it can be directly used |
453 | 0 | // for rendering the mini font without changing the draw target transform. |
454 | 0 | tempMat = Matrix(*aMat).PostScale(devPixelsPerCSSPx, devPixelsPerCSSPx) |
455 | 0 | .PostTranslate(center); |
456 | 0 | aMat = &tempMat; |
457 | 0 | } else { |
458 | 0 | // Otherwise, scale and translate the draw target transform assuming it |
459 | 0 | // supports that. |
460 | 0 | tempMat = aDrawTarget.GetTransform(); |
461 | 0 | aDrawTarget.SetTransform( |
462 | 0 | Matrix(tempMat).PreTranslate(center) |
463 | 0 | .PreScale(devPixelsPerCSSPx, devPixelsPerCSSPx)); |
464 | 0 | } |
465 | 0 |
|
466 | 0 | if (aChar < 0x10000) { |
467 | 0 | if (rect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) && |
468 | 0 | rect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) { |
469 | 0 | // Draw 4 digits for BMP |
470 | 0 | Float left = -(MINIFONT_WIDTH + halfGap); |
471 | 0 | DrawHexChar((aChar >> 12) & 0xF, left, top, aDrawTarget, atlas, color, aMat); |
472 | 0 | DrawHexChar((aChar >> 8) & 0xF, halfGap, top, aDrawTarget, atlas, color, aMat); |
473 | 0 | DrawHexChar((aChar >> 4) & 0xF, left, halfGap, aDrawTarget, atlas, color, aMat); |
474 | 0 | DrawHexChar(aChar & 0xF, halfGap, halfGap, aDrawTarget, atlas, color, aMat); |
475 | 0 | } |
476 | 0 | } else { |
477 | 0 | if (rect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) && |
478 | 0 | rect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) { |
479 | 0 | // Draw 6 digits for non-BMP |
480 | 0 | Float first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP); |
481 | 0 | Float second = -(MINIFONT_WIDTH / 2.0); |
482 | 0 | Float third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP); |
483 | 0 | DrawHexChar((aChar >> 20) & 0xF, first, top, aDrawTarget, atlas, color, aMat); |
484 | 0 | DrawHexChar((aChar >> 16) & 0xF, second, top, aDrawTarget, atlas, color, aMat); |
485 | 0 | DrawHexChar((aChar >> 12) & 0xF, third, top, aDrawTarget, atlas, color, aMat); |
486 | 0 | DrawHexChar((aChar >> 8) & 0xF, first, halfGap, aDrawTarget, atlas, color, aMat); |
487 | 0 | DrawHexChar((aChar >> 4) & 0xF, second, halfGap, aDrawTarget, atlas, color, aMat); |
488 | 0 | DrawHexChar(aChar & 0xF, third, halfGap, aDrawTarget, atlas, color, aMat); |
489 | 0 | } |
490 | 0 | } |
491 | 0 |
|
492 | 0 | if (!aMat) { |
493 | 0 | // The draw target transform was changed, so it must be restored to |
494 | 0 | // the original value. |
495 | 0 | aDrawTarget.SetTransform(tempMat); |
496 | 0 | } |
497 | 0 | #endif |
498 | 0 | } |
499 | | |
500 | | Float |
501 | | gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar, |
502 | | uint32_t aAppUnitsPerDevPixel) |
503 | 0 | { |
504 | 0 | /** |
505 | 0 | * The minimum desired width for a missing-glyph glyph box. I've laid it out |
506 | 0 | * like this so you can see what goes where. |
507 | 0 | */ |
508 | 0 | Float width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP + |
509 | 0 | MINIFONT_WIDTH + HEX_CHAR_GAP + MINIFONT_WIDTH + |
510 | 0 | ((aChar < 0x10000) ? 0 : HEX_CHAR_GAP + MINIFONT_WIDTH) + |
511 | 0 | HEX_CHAR_GAP + BOX_BORDER_WIDTH + BOX_HORIZONTAL_INSET; |
512 | 0 | // Note that this will give us floating-point division, so the width will |
513 | 0 | // -not- be snapped to integer multiples of its basic pixel value |
514 | 0 | width *= Float(AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel; |
515 | 0 | return width; |
516 | 0 | } |