📄 skiafontwin.cpp
字号:
/* * Copyright (c) 2008, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "config.h"#include "SkiaFontWin.h"#include "PlatformContextSkia.h"#include "Gradient.h"#include "Pattern.h"#include "SkCanvas.h"#include "SkPaint.h"#include "SkShader.h"#include "TransformationMatrix.h"#include <wtf/ListHashSet.h>#include <wtf/Vector.h>namespace WebCore {struct CachedOutlineKey { CachedOutlineKey() : font(0), glyph(0), path(0) {} CachedOutlineKey(HFONT f, WORD g) : font(f), glyph(g), path(0) {} HFONT font; WORD glyph; // The lifetime of this pointer is managed externally to this class. Be sure // to call DeleteOutline to remove items. SkPath* path;};const bool operator==(const CachedOutlineKey& a, const CachedOutlineKey& b){ return a.font == b.font && a.glyph == b.glyph;}struct CachedOutlineKeyHash { static unsigned hash(const CachedOutlineKey& key) { unsigned keyBytes; memcpy(&keyBytes, &key.font, sizeof(unsigned)); return keyBytes + key.glyph; } static unsigned equal(const CachedOutlineKey& a, const CachedOutlineKey& b) { return a.font == b.font && a.glyph == b.glyph; } static const bool safeToCompareToEmptyOrDeleted = true;};typedef ListHashSet<CachedOutlineKey, CachedOutlineKeyHash> OutlineCache;// FIXME: Convert from static constructor to accessor function. WebCore tries to// avoid global constructors to save on start-up time.static OutlineCache outlineCache;// The global number of glyph outlines we'll cache.static const int outlineCacheSize = 256;static SkScalar FIXEDToSkScalar(FIXED fixed){ SkFixed skFixed; memcpy(&skFixed, &fixed, sizeof(SkFixed)); return SkFixedToScalar(skFixed);}// Removes the given key from the cached outlines, also deleting the path.static void deleteOutline(OutlineCache::iterator deleteMe){ delete deleteMe->path; outlineCache.remove(deleteMe);}static void addPolyCurveToPath(const TTPOLYCURVE* polyCurve, SkPath* path){ switch (polyCurve->wType) { case TT_PRIM_LINE: for (WORD i = 0; i < polyCurve->cpfx; i++) { path->lineTo(FIXEDToSkScalar(polyCurve->apfx[i].x), -FIXEDToSkScalar(polyCurve->apfx[i].y)); } break; case TT_PRIM_QSPLINE: // FIXME: doesn't this duplicate points if we do the loop > once? for (WORD i = 0; i < polyCurve->cpfx - 1; i++) { SkScalar bx = FIXEDToSkScalar(polyCurve->apfx[i].x); SkScalar by = FIXEDToSkScalar(polyCurve->apfx[i].y); SkScalar cx = FIXEDToSkScalar(polyCurve->apfx[i + 1].x); SkScalar cy = FIXEDToSkScalar(polyCurve->apfx[i + 1].y); if (i < polyCurve->cpfx - 2) { // We're not the last point, compute C. cx = SkScalarAve(bx, cx); cy = SkScalarAve(by, cy); } // Need to flip the y coordinates since the font's coordinate system is // flipped from ours vertically. path->quadTo(bx, -by, cx, -cy); } break; case TT_PRIM_CSPLINE: // FIXME break; }}// The size of the glyph path buffer.static const int glyphPathBufferSize = 4096;// Fills the given SkPath with the outline for the given glyph index. The font// currently selected into the given DC is used. Returns true on success.static bool getPathForGlyph(HDC dc, WORD glyph, SkPath* path){ char buffer[glyphPathBufferSize]; GLYPHMETRICS gm; MAT2 mat = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; // Each one is (fract,value). DWORD totalSize = GetGlyphOutlineW(dc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &gm, glyphPathBufferSize, buffer, &mat); if (totalSize == GDI_ERROR) return false; const char* curGlyph = buffer; const char* endGlyph = &buffer[totalSize]; while (curGlyph < endGlyph) { const TTPOLYGONHEADER* polyHeader = reinterpret_cast<const TTPOLYGONHEADER*>(curGlyph); path->moveTo(FIXEDToSkScalar(polyHeader->pfxStart.x), -FIXEDToSkScalar(polyHeader->pfxStart.y)); const char* curPoly = curGlyph + sizeof(TTPOLYGONHEADER); const char* endPoly = curGlyph + polyHeader->cb; while (curPoly < endPoly) { const TTPOLYCURVE* polyCurve = reinterpret_cast<const TTPOLYCURVE*>(curPoly); addPolyCurveToPath(polyCurve, path); curPoly += sizeof(WORD) * 2 + sizeof(POINTFX) * polyCurve->cpfx; } curGlyph += polyHeader->cb; } path->close(); return true;}// Returns a SkPath corresponding to the give glyph in the given font. The font// should be selected into the given DC. The returned path is owned by the// hashtable. Returns 0 on error.const SkPath* SkiaWinOutlineCache::lookupOrCreatePathForGlyph(HDC hdc, HFONT font, WORD glyph){ CachedOutlineKey key(font, glyph); OutlineCache::iterator found = outlineCache.find(key); if (found != outlineCache.end()) { // Keep in MRU order by removing & reinserting the value. key = *found; outlineCache.remove(found); outlineCache.add(key); return key.path; } key.path = new SkPath; if (!getPathForGlyph(hdc, glyph, key.path)) return 0; if (outlineCache.size() > outlineCacheSize) // The cache is too big, find the oldest value (first in the list). deleteOutline(outlineCache.begin()); outlineCache.add(key); return key.path;}void SkiaWinOutlineCache::removePathsForFont(HFONT hfont){ // ListHashSet isn't the greatest structure for deleting stuff out of, but // removing entries will be relatively rare (we don't remove fonts much, nor // do we draw out own glyphs using these routines much either). // // We keep a list of all glyphs we're removing which we do in a separate // pass. Vector<CachedOutlineKey> outlinesToDelete; for (OutlineCache::iterator i = outlineCache.begin(); i != outlineCache.end(); ++i) outlinesToDelete.append(*i); for (Vector<CachedOutlineKey>::iterator i = outlinesToDelete.begin(); i != outlinesToDelete.end(); ++i) deleteOutline(outlineCache.find(*i));}bool windowsCanHandleTextDrawing(GraphicsContext* context){ // Check for non-translation transforms. Sometimes zooms will look better in // Skia, and sometimes better in Windows. The main problem is that zooming // in using Skia will show you the hinted outlines for the smaller size, // which look weird. All else being equal, it's better to use Windows' text // drawing, so we don't check for zooms. const TransformationMatrix& matrix = context->getCTM(); if (matrix.b() != 0 || matrix.c() != 0) // Check for skew. return false; // Check for stroke effects. if (context->platformContext()->getTextDrawingMode() != cTextFill) return false; // Check for gradients. if (context->fillGradient() || context->strokeGradient()) return false; // Check for patterns. if (context->fillPattern() || context->strokePattern()) return false; // Check for shadow effects. if (context->platformContext()->getDrawLooper()) return false; return true;}// Draws the given text string using skia. Note that gradient or// pattern may be NULL, in which case a solid colour is used.static bool skiaDrawText(HFONT hfont, HDC dc, SkCanvas* canvas, const SkPoint& point, SkPaint* paint, const TransformationMatrix& transformationMatrix, Gradient* gradient, Pattern* pattern, const WORD* glyphs, const int* advances, const GOFFSET* offsets, int numGlyphs){ SkShader* shader = NULL; SkMatrix oldShaderMatrix; if (gradient) { shader = gradient->platformGradient(); // Get the length of the string in pixels. int width = 0; for (int i = 0; i < numGlyphs; i++) width += advances[i]; // Save the current shader matrix. shader->getLocalMatrix(&oldShaderMatrix); // Scale up the gradient matrix by the width of the text string. SkMatrix shaderMatrix(oldShaderMatrix); shaderMatrix.postScale(static_cast<float>(width), 1.0f); shaderMatrix.postTranslate(point.fX, point.fY); shader->setLocalMatrix(shaderMatrix); } else if (pattern) shader = pattern->createPlatformPattern(transformationMatrix); paint->setShader(shader); float x = point.fX, y = point.fY; for (int i = 0; i < numGlyphs; i++) { const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]); if (!path) return false; float offsetX = 0.0f, offsetY = 0.0f; if (offsets && (offsets[i].du != 0 || offsets[i].dv != 0)) { offsetX = offsets[i].du; offsetY = offsets[i].dv; } SkPath newPath; newPath.addPath(*path, x + offsetX, y + offsetY); canvas->drawPath(newPath, *paint); x += advances[i]; } // Restore the previous shader matrix. if (gradient) shader->setLocalMatrix(oldShaderMatrix); return true;}bool paintSkiaText(GraphicsContext* context, HFONT hfont, int numGlyphs, const WORD* glyphs, const int* advances, const GOFFSET* offsets, const SkPoint* origin){ HDC dc = GetDC(0); HGDIOBJ oldFont = SelectObject(dc, hfont); PlatformContextSkia* platformContext = context->platformContext(); int textMode = platformContext->getTextDrawingMode(); // Filling (if necessary). This is the common case. SkPaint paint; platformContext->setupPaintForFilling(&paint); paint.setFlags(SkPaint::kAntiAlias_Flag); bool didFill = false; if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) { if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, context->getCTM(), context->fillGradient(), context->fillPattern(), &glyphs[0], &advances[0], &offsets[0], numGlyphs)) return false; didFill = true; } // Stroking on top (if necessary). if ((textMode & WebCore::cTextStroke) && platformContext->getStrokeStyle() != NoStroke && platformContext->getStrokeThickness() > 0) { paint.reset(); platformContext->setupPaintForStroking(&paint, 0, 0); paint.setFlags(SkPaint::kAntiAlias_Flag); if (didFill) { // If there is a shadow and we filled above, there will already be // a shadow. We don't want to draw it again or it will be too dark // and it will go on top of the fill. // // Note that this isn't strictly correct, since the stroke could be // very thick and the shadow wouldn't account for this. The "right" // thing would be to draw to a new layer and then draw that layer // with a shadow. But this is a lot of extra work for something // that isn't normally an issue. paint.setLooper(0)->safeUnref(); } if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, context->getCTM(), context->strokeGradient(), context->strokePattern(), &glyphs[0], &advances[0], &offsets[0], numGlyphs)) return false; } SelectObject(dc, oldFont); ReleaseDC(0, dc); return true;}} // namespace WebCore
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -