1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxFontMissingGlyphs.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,280 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "gfxFontMissingGlyphs.h" 1.10 +#include "nsDeviceContext.h" 1.11 +#include "gfxContext.h" 1.12 +#include "gfxColor.h" 1.13 + 1.14 +#define CHAR_BITS(b00, b01, b02, b10, b11, b12, b20, b21, b22, b30, b31, b32, b40, b41, b42) \ 1.15 + ((b00 << 0) | (b01 << 1) | (b02 << 2) | (b10 << 3) | (b11 << 4) | (b12 << 5) | \ 1.16 + (b20 << 6) | (b21 << 7) | (b22 << 8) | (b30 << 9) | (b31 << 10) | (b32 << 11) | \ 1.17 + (b40 << 12) | (b41 << 13) | (b42 << 14)) 1.18 + 1.19 +static const uint16_t glyphMicroFont[16] = { 1.20 + CHAR_BITS(0, 1, 0, 1.21 + 1, 0, 1, 1.22 + 1, 0, 1, 1.23 + 1, 0, 1, 1.24 + 0, 1, 0), 1.25 + CHAR_BITS(0, 1, 0, 1.26 + 0, 1, 0, 1.27 + 0, 1, 0, 1.28 + 0, 1, 0, 1.29 + 0, 1, 0), 1.30 + CHAR_BITS(1, 1, 1, 1.31 + 0, 0, 1, 1.32 + 1, 1, 1, 1.33 + 1, 0, 0, 1.34 + 1, 1, 1), 1.35 + CHAR_BITS(1, 1, 1, 1.36 + 0, 0, 1, 1.37 + 1, 1, 1, 1.38 + 0, 0, 1, 1.39 + 1, 1, 1), 1.40 + CHAR_BITS(1, 0, 1, 1.41 + 1, 0, 1, 1.42 + 1, 1, 1, 1.43 + 0, 0, 1, 1.44 + 0, 0, 1), 1.45 + CHAR_BITS(1, 1, 1, 1.46 + 1, 0, 0, 1.47 + 1, 1, 1, 1.48 + 0, 0, 1, 1.49 + 1, 1, 1), 1.50 + CHAR_BITS(1, 1, 1, 1.51 + 1, 0, 0, 1.52 + 1, 1, 1, 1.53 + 1, 0, 1, 1.54 + 1, 1, 1), 1.55 + CHAR_BITS(1, 1, 1, 1.56 + 0, 0, 1, 1.57 + 0, 0, 1, 1.58 + 0, 0, 1, 1.59 + 0, 0, 1), 1.60 + CHAR_BITS(0, 1, 0, 1.61 + 1, 0, 1, 1.62 + 0, 1, 0, 1.63 + 1, 0, 1, 1.64 + 0, 1, 0), 1.65 + CHAR_BITS(1, 1, 1, 1.66 + 1, 0, 1, 1.67 + 1, 1, 1, 1.68 + 0, 0, 1, 1.69 + 0, 0, 1), 1.70 + CHAR_BITS(1, 1, 1, 1.71 + 1, 0, 1, 1.72 + 1, 1, 1, 1.73 + 1, 0, 1, 1.74 + 1, 0, 1), 1.75 + CHAR_BITS(1, 1, 0, 1.76 + 1, 0, 1, 1.77 + 1, 1, 0, 1.78 + 1, 0, 1, 1.79 + 1, 1, 0), 1.80 + CHAR_BITS(0, 1, 1, 1.81 + 1, 0, 0, 1.82 + 1, 0, 0, 1.83 + 1, 0, 0, 1.84 + 0, 1, 1), 1.85 + CHAR_BITS(1, 1, 0, 1.86 + 1, 0, 1, 1.87 + 1, 0, 1, 1.88 + 1, 0, 1, 1.89 + 1, 1, 0), 1.90 + CHAR_BITS(1, 1, 1, 1.91 + 1, 0, 0, 1.92 + 1, 1, 1, 1.93 + 1, 0, 0, 1.94 + 1, 1, 1), 1.95 + CHAR_BITS(1, 1, 1, 1.96 + 1, 0, 0, 1.97 + 1, 1, 1, 1.98 + 1, 0, 0, 1.99 + 1, 0, 0) 1.100 +}; 1.101 + 1.102 +/* Parameters that control the rendering of hexboxes. They look like this: 1.103 + 1.104 + BMP codepoints non-BMP codepoints 1.105 + (U+0000 - U+FFFF) (U+10000 - U+10FFFF) 1.106 + 1.107 + +---------+ +-------------+ 1.108 + | | | | 1.109 + | HHH HHH | | HHH HHH HHH | 1.110 + | HHH HHH | | HHH HHH HHH | 1.111 + | HHH HHH | | HHH HHH HHH | 1.112 + | HHH HHH | | HHH HHH HHH | 1.113 + | HHH HHH | | HHH HHH HHH | 1.114 + | | | | 1.115 + | HHH HHH | | HHH HHH HHH | 1.116 + | HHH HHH | | HHH HHH HHH | 1.117 + | HHH HHH | | HHH HHH HHH | 1.118 + | HHH HHH | | HHH HHH HHH | 1.119 + | HHH HHH | | HHH HHH HHH | 1.120 + | | | | 1.121 + +---------+ +-------------+ 1.122 +*/ 1.123 + 1.124 +/** Width of a minifont glyph (see above) */ 1.125 +static const int MINIFONT_WIDTH = 3; 1.126 +/** Height of a minifont glyph (see above) */ 1.127 +static const int MINIFONT_HEIGHT = 5; 1.128 +/** 1.129 + * Gap between minifont glyphs (both horizontal and vertical) and also 1.130 + * the minimum desired gap between the box border and the glyphs 1.131 + */ 1.132 +static const int HEX_CHAR_GAP = 1; 1.133 +/** 1.134 + * The amount of space between the vertical edge of the glyphbox and the 1.135 + * box border. We make this nonzero so that when multiple missing glyphs 1.136 + * occur consecutively there's a gap between their rendered boxes. 1.137 + */ 1.138 +static const int BOX_HORIZONTAL_INSET = 1; 1.139 +/** The width of the border */ 1.140 +static const int BOX_BORDER_WIDTH = 1; 1.141 +/** 1.142 + * The scaling factor for the border opacity; this is multiplied by the current 1.143 + * opacity being used to draw the text. 1.144 + */ 1.145 +static const gfxFloat BOX_BORDER_OPACITY = 0.5; 1.146 +/** 1.147 + * Draw a single hex character using the current color. A nice way to do this 1.148 + * would be to fill in an A8 image surface and then use it as a mask 1.149 + * to paint the current color. Tragically this doesn't currently work with the 1.150 + * Quartz cairo backend which doesn't generally support masking with surfaces. 1.151 + * So for now we just paint a bunch of rectangles... 1.152 + */ 1.153 +#ifndef MOZ_GFX_OPTIMIZE_MOBILE 1.154 +static void 1.155 +DrawHexChar(gfxContext *aContext, const gfxPoint& aPt, uint32_t aDigit) 1.156 +{ 1.157 + aContext->NewPath(); 1.158 + uint32_t glyphBits = glyphMicroFont[aDigit]; 1.159 + int x, y; 1.160 + for (y = 0; y < MINIFONT_HEIGHT; ++y) { 1.161 + for (x = 0; x < MINIFONT_WIDTH; ++x) { 1.162 + if (glyphBits & 1) { 1.163 + aContext->Rectangle(gfxRect(x, y, 1, 1) + aPt, true); 1.164 + } 1.165 + glyphBits >>= 1; 1.166 + } 1.167 + } 1.168 + aContext->Fill(); 1.169 +} 1.170 +#endif // MOZ_GFX_OPTIMIZE_MOBILE 1.171 + 1.172 +void 1.173 +gfxFontMissingGlyphs::DrawMissingGlyph(gfxContext *aContext, 1.174 + const gfxRect& aRect, 1.175 + uint32_t aChar, 1.176 + uint32_t aAppUnitsPerDevPixel) 1.177 +{ 1.178 + aContext->Save(); 1.179 + 1.180 + gfxRGBA currentColor; 1.181 + if (!aContext->GetDeviceColor(currentColor)) { 1.182 + // We're currently drawing with some kind of pattern... Just draw 1.183 + // the missing-glyph data in black. 1.184 + currentColor = gfxRGBA(0,0,0,1); 1.185 + } 1.186 + 1.187 + // Stroke a rectangle so that the stroke's left edge is inset one pixel 1.188 + // from the left edge of the glyph box and the stroke's right edge 1.189 + // is inset one pixel from the right edge of the glyph box. 1.190 + gfxFloat halfBorderWidth = BOX_BORDER_WIDTH / 2.0; 1.191 + gfxFloat borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth; 1.192 + gfxFloat borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth; 1.193 + gfxRect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth, 1.194 + borderRight - borderLeft, 1.195 + aRect.Height() - 2.0 * halfBorderWidth); 1.196 + if (!borderStrokeRect.IsEmpty()) { 1.197 + aContext->SetLineWidth(BOX_BORDER_WIDTH); 1.198 + aContext->SetDash(gfxContext::gfxLineSolid); 1.199 + aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE); 1.200 + aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER); 1.201 + gfxRGBA color = currentColor; 1.202 + color.a *= BOX_BORDER_OPACITY; 1.203 + aContext->SetDeviceColor(color); 1.204 + aContext->NewPath(); 1.205 + aContext->Rectangle(borderStrokeRect); 1.206 + 1.207 +#ifdef MOZ_GFX_OPTIMIZE_MOBILE 1.208 + aContext->Fill(); 1.209 +#else 1.210 + aContext->Stroke(); 1.211 +#endif 1.212 + } 1.213 + 1.214 +#ifndef MOZ_GFX_OPTIMIZE_MOBILE 1.215 + gfxPoint center(aRect.X() + aRect.Width() / 2, 1.216 + aRect.Y() + aRect.Height() / 2); 1.217 + gfxFloat halfGap = HEX_CHAR_GAP / 2.0; 1.218 + gfxFloat top = -(MINIFONT_HEIGHT + halfGap); 1.219 + aContext->SetDeviceColor(currentColor); 1.220 + aContext->Translate(center); 1.221 + // We always want integer scaling, otherwise the "bitmap" glyphs will look 1.222 + // even uglier than usual when zoomed 1.223 + int32_t scale = 1.224 + std::max<int32_t>(1, nsDeviceContext::AppUnitsPerCSSPixel() / 1.225 + aAppUnitsPerDevPixel); 1.226 + aContext->Scale(gfxFloat(scale), gfxFloat(scale)); 1.227 + if (aChar < 0x10000) { 1.228 + if (aRect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) && 1.229 + aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) { 1.230 + // Draw 4 digits for BMP 1.231 + gfxFloat left = -(MINIFONT_WIDTH + halfGap); 1.232 + DrawHexChar(aContext, 1.233 + gfxPoint(left, top), (aChar >> 12) & 0xF); 1.234 + DrawHexChar(aContext, 1.235 + gfxPoint(halfGap, top), (aChar >> 8) & 0xF); 1.236 + DrawHexChar(aContext, 1.237 + gfxPoint(left, halfGap), (aChar >> 4) & 0xF); 1.238 + DrawHexChar(aContext, 1.239 + gfxPoint(halfGap, halfGap), aChar & 0xF); 1.240 + } 1.241 + } else { 1.242 + if (aRect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) && 1.243 + aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) { 1.244 + // Draw 6 digits for non-BMP 1.245 + gfxFloat first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP); 1.246 + gfxFloat second = -(MINIFONT_WIDTH / 2.0); 1.247 + gfxFloat third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP); 1.248 + DrawHexChar(aContext, 1.249 + gfxPoint(first, top), (aChar >> 20) & 0xF); 1.250 + DrawHexChar(aContext, 1.251 + gfxPoint(second, top), (aChar >> 16) & 0xF); 1.252 + DrawHexChar(aContext, 1.253 + gfxPoint(third, top), (aChar >> 12) & 0xF); 1.254 + DrawHexChar(aContext, 1.255 + gfxPoint(first, halfGap), (aChar >> 8) & 0xF); 1.256 + DrawHexChar(aContext, 1.257 + gfxPoint(second, halfGap), (aChar >> 4) & 0xF); 1.258 + DrawHexChar(aContext, 1.259 + gfxPoint(third, halfGap), aChar & 0xF); 1.260 + } 1.261 + } 1.262 +#endif 1.263 + 1.264 + aContext->Restore(); 1.265 +} 1.266 + 1.267 +gfxFloat 1.268 +gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar, 1.269 + uint32_t aAppUnitsPerDevPixel) 1.270 +{ 1.271 +/** 1.272 + * The minimum desired width for a missing-glyph glyph box. I've laid it out 1.273 + * like this so you can see what goes where. 1.274 + */ 1.275 + gfxFloat width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP + 1.276 + MINIFONT_WIDTH + HEX_CHAR_GAP + MINIFONT_WIDTH + 1.277 + ((aChar < 0x10000) ? 0 : HEX_CHAR_GAP + MINIFONT_WIDTH) + 1.278 + HEX_CHAR_GAP + BOX_BORDER_WIDTH + BOX_HORIZONTAL_INSET; 1.279 + // Note that this will give us floating-point division, so the width will 1.280 + // -not- be snapped to integer multiples of its basic pixel value 1.281 + width *= gfxFloat(nsDeviceContext::AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel; 1.282 + return width; 1.283 +}