TEST(GlyphBufferTest, ReverseForSimpleRTL) { RefPtr<SimpleFontData> font1 = TestSimpleFontData::create(); RefPtr<SimpleFontData> font2 = TestSimpleFontData::create(); GlyphBuffer glyphBuffer; glyphBuffer.add(42, font1.get(), 10); glyphBuffer.add(43, font1.get(), 15); glyphBuffer.add(44, font2.get(), 25); EXPECT_FALSE(glyphBuffer.isEmpty()); EXPECT_EQ(3u, glyphBuffer.size()); glyphBuffer.reverseForSimpleRTL(30, 100); EXPECT_FALSE(glyphBuffer.isEmpty()); EXPECT_EQ(3u, glyphBuffer.size()); EXPECT_EQ(44, glyphBuffer.glyphAt(0)); EXPECT_EQ(43, glyphBuffer.glyphAt(1)); EXPECT_EQ(42, glyphBuffer.glyphAt(2)); EXPECT_EQ(font2.get(), glyphBuffer.fontDataAt(0)); EXPECT_EQ(font1.get(), glyphBuffer.fontDataAt(1)); EXPECT_EQ(font1.get(), glyphBuffer.fontDataAt(2)); EXPECT_EQ(70, glyphBuffer.xOffsetAt(0)); EXPECT_EQ(75, glyphBuffer.xOffsetAt(1)); EXPECT_EQ(85, glyphBuffer.xOffsetAt(2)); }
void Font::drawEmphasisMarks(GraphicsContext* context, const TextRun& run, const GlyphBuffer& glyphBuffer, const AtomicString& mark, const FloatPoint& point) const { FontCachePurgePreventer purgePreventer; GlyphData markGlyphData; if (!getEmphasisMarkGlyphData(mark, markGlyphData)) return; const SimpleFontData* markFontData = markGlyphData.fontData; ASSERT(markFontData); if (!markFontData) return; Glyph markGlyph = markGlyphData.glyph; Glyph spaceGlyph = markFontData->spaceGlyph(); float middleOfLastGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, 0); FloatPoint startPoint(point.x() + middleOfLastGlyph - offsetToMiddleOfGlyph(markFontData, markGlyph), point.y()); GlyphBuffer markBuffer; for (int i = 0; i + 1 < glyphBuffer.size(); ++i) { float middleOfNextGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, i + 1); float advance = glyphBuffer.advanceAt(i).width() - middleOfLastGlyph + middleOfNextGlyph; markBuffer.add(glyphBuffer.glyphAt(i) ? markGlyph : spaceGlyph, markFontData, advance); middleOfLastGlyph = middleOfNextGlyph; } markBuffer.add(glyphBuffer.glyphAt(glyphBuffer.size() - 1) ? markGlyph : spaceGlyph, markFontData, 0); drawGlyphBuffer(context, run, markBuffer, startPoint); }
float SVGTextRunRenderingContext::floatWidthUsingSVGFont(const Font& font, const TextRun& run, int& charsConsumed, Glyph& glyphId) const { WidthIterator it(&font, run); GlyphBuffer glyphBuffer; charsConsumed += it.advance(run.length(), &glyphBuffer); glyphId = !glyphBuffer.isEmpty() ? glyphBuffer.glyphAt(0) : 0; return it.runWidthSoFar(); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SkColor color = graphicsContext->platformContext()->effectiveFillColor(); unsigned char alpha = SkColorGetA(color); // Skip 100% transparent text; no need to draw anything. if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke && !graphicsContext->hasShadow()) return; // We draw the glyphs in chunks to avoid having to do a heap allocation for // the arrays of characters and advances. const int kMaxBufferLength = 256; Vector<WORD, kMaxBufferLength> glyphs; Vector<int, kMaxBufferLength> advances; int glyphIndex = 0; // The starting glyph of the current chunk. // In order to round all offsets to the correct pixel boundary, this code keeps track of the absolute position // of each glyph in floating point units and rounds to integer advances at the last possible moment. float horizontalOffset = point.x(); // The floating point offset of the left side of the current glyph. int lastHorizontalOffsetRounded = lroundf(horizontalOffset); // The rounded offset of the left side of the last glyph rendered. while (glyphIndex < numGlyphs) { // How many chars will be in this chunk? int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); glyphs.resize(curLen); advances.resize(curLen); float currentWidth = 0; for (int i = 0; i < curLen; ++i, ++glyphIndex) { glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex); horizontalOffset += glyphBuffer.advanceAt(from + glyphIndex); advances[i] = lroundf(horizontalOffset) - lastHorizontalOffsetRounded; lastHorizontalOffsetRounded += advances[i]; currentWidth += glyphBuffer.advanceAt(from + glyphIndex); // Bug 26088 - very large positive or negative runs can fail to // render so we clamp the size here. In the specs, negative // letter-spacing is implementation-defined, so this should be // fine, and it matches Safari's implementation. The call actually // seems to crash if kMaxNegativeRun is set to somewhere around // -32830, so we give ourselves a little breathing room. const int maxNegativeRun = -32768; const int maxPositiveRun = 32768; if ((currentWidth + advances[i] < maxNegativeRun) || (currentWidth + advances[i] > maxPositiveRun)) advances[i] = 0; } SkPoint origin = point; origin.fX += SkFloatToScalar(horizontalOffset - point.x() - currentWidth); paintSkiaText(graphicsContext, font->platformData(), curLen, &glyphs[0], &advances[0], 0, &origin); } }
// Tests that filling a glyph buffer for a specific range returns the same // results when shaping word by word as when shaping the full run in one go. TEST_F(CachingWordShaperTest, CommonAccentLeftToRightFillGlyphBuffer) { // "/. ." with an accent mark over the first dot. const UChar str[] = {0x2F, 0x301, 0x2E, 0x20, 0x2E, 0x0}; TextRun textRun(str, 5); CachingWordShaper shaper(cache.get()); GlyphBuffer glyphBuffer; shaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &glyphBuffer, 0, 3); std::unique_ptr<ShapeCache> referenceCache = wrapUnique(new ShapeCache()); CachingWordShaper referenceShaper(referenceCache.get()); GlyphBuffer referenceGlyphBuffer; font.setCanShapeWordByWordForTesting(false); referenceShaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &referenceGlyphBuffer, 0, 3); ASSERT_EQ(referenceGlyphBuffer.glyphAt(0), glyphBuffer.glyphAt(0)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(1), glyphBuffer.glyphAt(1)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(2), glyphBuffer.glyphAt(2)); }
// Tests that filling a glyph buffer for a specific range returns the same // results when shaping word by word as when shaping the full run in one go. TEST_F(CachingWordShaperTest, CommonAccentRightToLeftFillGlyphBuffer) { // "[] []" with an accent mark over the last square bracket. const UChar str[] = { 0x5B, 0x5D, 0x20, 0x5B, 0x301, 0x5D, 0x0 }; TextRun textRun(str, 6); textRun.setDirection(RTL); CachingWordShaper shaper(cache.get()); GlyphBuffer glyphBuffer; shaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &glyphBuffer, 1, 6); OwnPtr<ShapeCache> referenceCache = adoptPtr(new ShapeCache()); CachingWordShaper referenceShaper(referenceCache.get()); GlyphBuffer referenceGlyphBuffer; font.setCanShapeWordByWordForTesting(false); referenceShaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &referenceGlyphBuffer, 1, 6); ASSERT_EQ(5u, referenceGlyphBuffer.size()); ASSERT_EQ(referenceGlyphBuffer.size(), glyphBuffer.size()); ASSERT_EQ(referenceGlyphBuffer.glyphAt(0), glyphBuffer.glyphAt(0)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(1), glyphBuffer.glyphAt(1)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(2), glyphBuffer.glyphAt(2)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(3), glyphBuffer.glyphAt(3)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(4), glyphBuffer.glyphAt(4)); }
TEST(GlyphBufferTest, StoresGlyphs) { RefPtr<SimpleFontData> font1 = TestSimpleFontData::create(); RefPtr<SimpleFontData> font2 = TestSimpleFontData::create(); GlyphBuffer glyphBuffer; glyphBuffer.add(42, font1.get(), 10); glyphBuffer.add(43, font1.get(), 15); glyphBuffer.add(44, font2.get(), 22); EXPECT_FALSE(glyphBuffer.isEmpty()); EXPECT_FALSE(glyphBuffer.hasVerticalOffsets()); EXPECT_EQ(3u, glyphBuffer.size()); EXPECT_EQ(42, glyphBuffer.glyphAt(0)); EXPECT_EQ(43, glyphBuffer.glyphAt(1)); EXPECT_EQ(44, glyphBuffer.glyphAt(2)); const Glyph* glyphs = glyphBuffer.glyphs(0); EXPECT_EQ(42, glyphs[0]); EXPECT_EQ(43, glyphs[1]); EXPECT_EQ(44, glyphs[2]); }
void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { if (context->paintingDisabled()) return; bool shouldFill = context->textDrawingMode() & TextModeFill; bool shouldStroke = context->textDrawingMode() & TextModeStroke; // Stroking text should always take the complex path. ASSERT(!shouldStroke); // Shadowed text should always take the complex path. ASSERT(context->contextShadow()->m_type == ContextShadow::NoShadow); if (!shouldFill && !shouldStroke) return; QVector<quint32> glyphIndexes; QVector<QPointF> positions; glyphIndexes.reserve(numGlyphs); positions.reserve(numGlyphs); float x = 0; for (int i = 0; i < numGlyphs; ++i) { Glyph glyph = glyphBuffer.glyphAt(from + i); float advance = glyphBuffer.advanceAt(from + i); if (!glyph) continue; glyphIndexes.append(glyph); positions.append(QPointF(x, 0)); x += advance; } QGlyphs qtGlyphs; qtGlyphs.setGlyphIndexes(glyphIndexes); qtGlyphs.setPositions(positions); qtGlyphs.setFont(fontData->platformData().rawFont()); QPainter* painter = context->platformContext(); QPen previousPen = painter->pen(); painter->setPen(fillPenForContext(context)); painter->drawGlyphs(point, qtGlyphs); painter->setPen(previousPen); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* sfont, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { int i = 0; FloatPoint pos(point); float w=0, h=0; UChar c=0; void* dc = (void *)graphicsContext->platformContext(); WKCPeerFont pf; WKCFloatRect clip; WKCFloatPoint pp; if (!dc) return; pf.fFont = sfont->platformData().Font(); if (!pf.fFont) return; const FontPlatformData& pd(sfont->platformData()); pf.fRequestedSize = pd.requestSize(); pf.fCreatedSize = pd.createdSize(); pf.fWeight = pd.weight(); pf.fItalic = pd.isItalic(); pf.fScale = pd.scale(); pf.fiScale = pd.iscale(); pf.fCanScale = pd.canScale(); pf.fFontId = (void *)pd.hash(); pos.setY(pos.y() - (float)pd.ascent()*pd.scale()); h = (pd.lineSpacing()) * pd.scale(); for (i=0; i<numGlyphs; i++) { float advance = 0.f; c = fixedChar(glyphBuffer.glyphAt(from + i)); if (!c) continue; advance = glyphBuffer.advanceAt(from + i); w = wkcFontGetClipWidthPeer(pf.fFont, c) * pd.scale(); clip.fX = pos.x(); clip.fY = pos.y(); clip.fWidth = w; clip.fHeight = h; pp.fX = pos.x(); pp.fY = pos.y(); wkcDrawContextDrawCharPeer(dc, (unsigned int)c, &pp, &clip, &pf); pos.move(advance, 0); } }
SVGGlyphToPathTranslator::SVGGlyphToPathTranslator(const GlyphBuffer& glyphBuffer, const FloatPoint& point, const SVGFontData& svgFontData, SVGFontElement& fontElement, const int from, const int numGlyphs, float scale, bool isVerticalText) : m_glyphBuffer(glyphBuffer) , m_svgFontData(svgFontData) , m_currentPoint(point) , m_glyphOrigin(m_svgFontData.horizontalOriginX() * scale, m_svgFontData.horizontalOriginY() * scale) , m_index(from) , m_glyph(glyphBuffer.glyphAt(m_index)) , m_fontElement(fontElement) , m_stoppingPoint(numGlyphs + from) , m_scale(scale) , m_isVerticalText(isVerticalText) { ASSERT(glyphBuffer.size() > m_index); if (m_glyph) { m_svgGlyph = m_fontElement.svgGlyphForGlyph(m_glyph); ASSERT(!m_svgGlyph.isPartOfLigature); ASSERT(m_svgGlyph.tableEntry == m_glyph); SVGGlyphElement::inheritUnspecifiedAttributes(m_svgGlyph, &m_svgFontData); } moveToNextValidGlyph(); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SkColor color = graphicsContext->platformContext()->effectiveFillColor(); unsigned char alpha = SkColorGetA(color); // Skip 100% transparent text; no need to draw anything. if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke) return; TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point); // We draw the glyphs in chunks to avoid having to do a heap allocation for // the arrays of characters and advances. Since ExtTextOut is the // lowest-level text output function on Windows, there should be little // penalty for splitting up the text. On the other hand, the buffer cannot // be bigger than 4094 or the function will fail. const int kMaxBufferLength = 256; Vector<WORD, kMaxBufferLength> glyphs; Vector<int, kMaxBufferLength> advances; int glyphIndex = 0; // The starting glyph of the current chunk. int curAdvance = 0; // How far from the left the current chunk is. while (glyphIndex < numGlyphs) { // How many chars will be in this chunk? int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); glyphs.resize(curLen); advances.resize(curLen); int curWidth = 0; for (int i = 0; i < curLen; ++i, ++glyphIndex) { glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex); advances[i] = static_cast<int>(glyphBuffer.advanceAt(from + glyphIndex)); // Bug 26088 - very large positive or negative runs can fail to // render so we clamp the size here. In the specs, negative // letter-spacing is implementation-defined, so this should be // fine, and it matches Safari's implementation. The call actually // seems to crash if kMaxNegativeRun is set to somewhere around // -32830, so we give ourselves a little breathing room. const int maxNegativeRun = -32768; const int maxPositiveRun = 32768; if ((curWidth + advances[i] < maxNegativeRun) || (curWidth + advances[i] > maxPositiveRun)) advances[i] = 0; curWidth += advances[i]; } // Actually draw the glyphs (with retry on failure). bool success = false; for (int executions = 0; executions < 2; ++executions) { success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], curAdvance); if (!success && executions == 0) { // Ask the browser to load the font for us and retry. ChromiumBridge::ensureFontLoaded(font->platformData().hfont()); continue; } break; } if (!success) LOG_ERROR("Unable to draw the glyphs after second attempt"); curAdvance += curWidth; } }
static void drawGlyphsWin(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point); // We draw the glyphs in chunks to avoid having to do a heap allocation for // the arrays of characters and advances. Since ExtTextOut is the // lowest-level text output function on Windows, there should be little // penalty for splitting up the text. On the other hand, the buffer cannot // be bigger than 4094 or the function will fail. const int kMaxBufferLength = 256; Vector<WORD, kMaxBufferLength> glyphs; Vector<int, kMaxBufferLength> advances; int glyphIndex = 0; // The starting glyph of the current chunk. // In order to round all offsets to the correct pixel boundary, this code keeps track of the absolute position // of each glyph in floating point units and rounds to integer advances at the last possible moment. float horizontalOffset = point.x(); // The floating point offset of the left side of the current glyph. int lastHorizontalOffsetRounded = lroundf(horizontalOffset); // The rounded offset of the left side of the last glyph rendered. while (glyphIndex < numGlyphs) { // How many chars will be in this chunk? int curLen = min(kMaxBufferLength, numGlyphs - glyphIndex); glyphs.resize(curLen); advances.resize(curLen); float currentWidth = 0; for (int i = 0; i < curLen; ++i, ++glyphIndex) { glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex); horizontalOffset += glyphBuffer.advanceAt(from + glyphIndex); advances[i] = lroundf(horizontalOffset) - lastHorizontalOffsetRounded; lastHorizontalOffsetRounded += advances[i]; currentWidth += glyphBuffer.advanceAt(from + glyphIndex); // Bug 26088 - very large positive or negative runs can fail to // render so we clamp the size here. In the specs, negative // letter-spacing is implementation-defined, so this should be // fine, and it matches Safari's implementation. The call actually // seems to crash if kMaxNegativeRun is set to somewhere around // -32830, so we give ourselves a little breathing room. const int maxNegativeRun = -32768; const int maxPositiveRun = 32768; if ((currentWidth + advances[i] < maxNegativeRun) || (currentWidth + advances[i] > maxPositiveRun)) advances[i] = 0; } // Actually draw the glyphs (with retry on failure). bool success = false; for (int executions = 0; executions < 2; ++executions) { success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], horizontalOffset - point.x() - currentWidth); if (!success && !executions) { // Ask the browser to load the font for us and retry. PlatformSupport::ensureFontLoaded(font->platformData().hfont()); continue; } break; } if (!success) LOG_ERROR("Unable to draw the glyphs after second attempt"); } }
static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { Color fillColor = graphicsContext->fillColor(); bool drawIntoBitmap = false; TextDrawingModeFlags drawingMode = graphicsContext->textDrawingMode(); if (drawingMode == TextModeFill) { if (!fillColor.alpha()) return; drawIntoBitmap = fillColor.alpha() != 255 || graphicsContext->inTransparencyLayer(); if (!drawIntoBitmap) { FloatSize offset; float blur; Color color; ColorSpace shadowColorSpace; graphicsContext->getShadow(offset, blur, color, shadowColorSpace); drawIntoBitmap = offset.width() || offset.height() || blur; } } // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances. Vector<int, 2048> gdiAdvances; int totalWidth = 0; for (int i = 0; i < numGlyphs; i++) { gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i))); totalWidth += gdiAdvances[i]; } HDC hdc = 0; OwnPtr<GraphicsContext::WindowsBitmap> bitmap; IntRect textRect; if (!drawIntoBitmap) hdc = graphicsContext->getWindowsContext(textRect, true, false); if (!hdc) { drawIntoBitmap = true; // We put slop into this rect, since glyphs can overflow the ascent/descent bounds and the left/right edges. // FIXME: Can get glyphs' optical bounds (even from CG) to get this right. const FontMetrics& fontMetrics = font->fontMetrics(); int lineGap = fontMetrics.lineGap(); textRect = IntRect(point.x() - (fontMetrics.ascent() + fontMetrics.descent()) / 2, point.y() - fontMetrics.ascent() - lineGap, totalWidth + fontMetrics.ascent() + fontMetrics.descent(), fontMetrics.lineSpacing()); bitmap = graphicsContext->createWindowsBitmap(textRect.size()); memset(bitmap->buffer(), 255, bitmap->bufferLength()); hdc = bitmap->hdc(); XFORM xform; xform.eM11 = 1.0f; xform.eM12 = 0.0f; xform.eM21 = 0.0f; xform.eM22 = 1.0f; xform.eDx = -textRect.x(); xform.eDy = -textRect.y(); SetWorldTransform(hdc, &xform); } SelectObject(hdc, font->platformData().hfont()); // Set the correct color. if (drawIntoBitmap) SetTextColor(hdc, RGB(0, 0, 0)); else SetTextColor(hdc, RGB(fillColor.red(), fillColor.green(), fillColor.blue())); SetBkMode(hdc, TRANSPARENT); SetTextAlign(hdc, TA_LEFT | TA_BASELINE); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); if (translation.width() || translation.height()) { XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = 0; xform.eM22 = 1.0; xform.eDx = translation.width(); xform.eDy = translation.height(); ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); } if (drawingMode == TextModeFill) { XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = font->platformData().syntheticOblique() ? -tanf(syntheticObliqueAngle * piFloat / 180.0f) : 0; xform.eM22 = 1.0; xform.eDx = point.x(); xform.eDy = point.y(); ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); if (font->syntheticBoldOffset()) { xform.eM21 = 0; xform.eDx = font->syntheticBoldOffset(); xform.eDy = 0; ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); } } else { XFORM xform; GetWorldTransform(hdc, &xform); AffineTransform hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy); CGAffineTransform initialGlyphTransform = hdcTransform.isInvertible() ? hdcTransform.inverse() : CGAffineTransformIdentity; if (font->platformData().syntheticOblique()) initialGlyphTransform = CGAffineTransformConcat(initialGlyphTransform, CGAffineTransformMake(1, 0, tanf(syntheticObliqueAngle * piFloat / 180.0f), 1, 0, 0)); initialGlyphTransform.tx = 0; initialGlyphTransform.ty = 0; CGContextRef cgContext = graphicsContext->platformContext(); CGContextSaveGState(cgContext); BOOL fontSmoothingEnabled = false; SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0); CGContextSetShouldAntialias(cgContext, fontSmoothingEnabled); CGContextScaleCTM(cgContext, 1.0, -1.0); CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height())); for (unsigned i = 0; i < numGlyphs; ++i) { RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); CGContextSaveGState(cgContext); CGContextConcatCTM(cgContext, initialGlyphTransform); if (drawingMode & TextModeFill) { CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); if (font->syntheticBoldOffset()) { CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); } } if (drawingMode & TextModeStroke) { CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); if (font->syntheticBoldOffset()) { CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); } } CGContextRestoreGState(cgContext); CGContextTranslateCTM(cgContext, gdiAdvances[i], 0); } CGContextRestoreGState(cgContext); } if (drawIntoBitmap) { UInt8* buffer = bitmap->buffer(); unsigned bufferLength = bitmap->bufferLength(); for (unsigned i = 0; i < bufferLength; i += 4) { // Use green, which is always in the middle. UInt8 alpha = (255 - buffer[i + 1]) * fillColor.alpha() / 255; buffer[i] = fillColor.blue(); buffer[i + 1] = fillColor.green(); buffer[i + 2] = fillColor.red(); buffer[i + 3] = alpha; } graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.location()); } else graphicsContext->releaseWindowsContext(hdc, textRect, true, false); }
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return; // We can only paint SVGFonts if a context is available. RenderObject* renderObject = renderObjectFromRun(run); ASSERT(renderObject); bool isVerticalText = false; if (RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject)) { RenderStyle* parentRenderObjectStyle = parentRenderObject->style(); ASSERT(parentRenderObjectStyle); isVerticalText = parentRenderObjectStyle->svgStyle().isVerticalWritingMode(); } float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm()); FloatPoint glyphOrigin; glyphOrigin.setX(svgFontData->horizontalOriginX() * scale); glyphOrigin.setY(svgFontData->horizontalOriginY() * scale); unsigned short resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode; FloatPoint currentPoint = point; for (int i = 0; i < numGlyphs; ++i) { Glyph glyph = glyphBuffer.glyphAt(from + i); if (!glyph) continue; float advance = glyphBuffer.advanceAt(from + i); SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); ASSERT(!svgGlyph.isPartOfLigature); ASSERT(svgGlyph.tableEntry == glyph); SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData); // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (svgGlyph.pathData.isEmpty()) { if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); continue; } if (isVerticalText) { glyphOrigin.setX(svgGlyph.verticalOriginX * scale); glyphOrigin.setY(svgGlyph.verticalOriginY * scale); } AffineTransform glyphPathTransform; glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); glyphPathTransform.scale(scale, -scale); Path glyphPath = svgGlyph.pathData; glyphPath.transform(glyphPathTransform); SVGRenderSupport::fillOrStrokePath(context, resourceMode, glyphPath); if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); } }
inline static float offsetToMiddleOfGlyphAtIndex(const GlyphBuffer& glyphBuffer, size_t i) { return offsetToMiddleOfGlyph(glyphBuffer.fontDataAt(i), glyphBuffer.glyphAt(i)); }
void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { // compile-time assert SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); if (numGlyphs == 1 && glyphBuffer.glyphAt(from) == 0x3) { // Webkit likes to draw end text control command for some reason // Just ignore it return; } SkPaint paint; if (!setupForText(&paint, gc, font)) { return; } SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); const GlyphBufferAdvance* adv = glyphBuffer.advances(from); SkAutoSTMalloc<32, SkPoint> storage(numGlyphs), storage2(numGlyphs), storage3(numGlyphs); SkPoint* pos = storage.get(); SkCanvas* canvas = gc->platformContext()->recordingCanvas(); /* We need an array of [x,y,x,y,x,y,...], but webkit is giving us point.xy + [width, height, width, height, ...], so we have to convert */ if (font->platformData().orientation() == Vertical) { float yOffset = SkFloatToScalar(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent()); gc->platformContext()->setTextOffset(FloatSize(0.0f, -yOffset)); // compensate for offset in bounds calculation y += yOffset; } if (EmojiFont::IsAvailable()) { // set filtering, to make scaled images look nice(r) paint.setFilterBitmap(true); SkMatrix rotator; rotator.reset(); if (font->platformData().orientation() == Vertical) { canvas->save(); canvas->rotate(-90); rotator.setRotate(90); } int localIndex = 0; int localCount = 0; for (int i = 0; i < numGlyphs; i++) { if (EmojiFont::IsEmojiGlyph(glyphs[i])) { if (localCount) { rotator.mapPoints(&pos[localIndex], localCount); canvas->drawPosText(&glyphs[localIndex], localCount * sizeof(uint16_t), &pos[localIndex], paint); } EmojiFont::Draw(canvas, glyphs[i], x, y, paint); // reset local index/count track for "real" glyphs localCount = 0; localIndex = i + 1; } else { pos[i].set(x, y); localCount += 1; } x += SkFloatToScalar(adv[i].width()); y += SkFloatToScalar(adv[i].height()); } // draw the last run of glyphs (if any) if (localCount) { rotator.mapPoints(&pos[localIndex], localCount); canvas->drawPosText(&glyphs[localIndex], localCount * sizeof(uint16_t), &pos[localIndex], paint); } if (font->platformData().orientation() == Vertical) canvas->restore(); } else { for (int i = 0; i < numGlyphs; i++) { pos[i].set(x, y); y += SkFloatToScalar(adv[i].height()); x += SkFloatToScalar(adv[i].width()); } if (font->platformData().orientation() == Vertical) { canvas->save(); canvas->rotate(-90); SkMatrix rotator; rotator.reset(); rotator.setRotate(90); rotator.mapPoints(pos, numGlyphs); } canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); if (font->platformData().orientation() == Vertical) canvas->restore(); } if (font->platformData().orientation() == Vertical) gc->platformContext()->setTextOffset(FloatSize()); // reset to undo above }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SkColor color = graphicsContext->effectiveFillColor(); unsigned char alpha = SkColorGetA(color); // Skip 100% transparent text; no need to draw anything. if (!alpha && graphicsContext->strokeStyle() == NoStroke && !graphicsContext->hasShadow()) return; // We draw the glyphs in chunks to avoid having to do a heap allocation for // the arrays of characters and advances. const int kMaxBufferLength = 256; Vector<int, kMaxBufferLength> advances; int glyphIndex = 0; // The starting glyph of the current chunk. float horizontalOffset = point.x(); // The floating point offset of the left side of the current glyph. #if ENABLE(OPENTYPE_VERTICAL) const OpenTypeVerticalData* verticalData = font->verticalData(); if (verticalData) { Vector<FloatPoint, kMaxBufferLength> translations; Vector<GOFFSET, kMaxBufferLength> offsets; // Skia doesn't have matrix for glyph coordinate space, so we rotate back the CTM. AffineTransform savedMatrix = graphicsContext->getCTM(); graphicsContext->concatCTM(AffineTransform(0, -1, 1, 0, point.x(), point.y())); graphicsContext->concatCTM(AffineTransform(1, 0, 0, 1, -point.x(), -point.y())); const FontMetrics& metrics = font->fontMetrics(); SkScalar verticalOriginX = SkFloatToScalar(point.x() + metrics.floatAscent() - metrics.floatAscent(IdeographicBaseline)); while (glyphIndex < numGlyphs) { // How many chars will be in this chunk? int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); const Glyph* glyphs = glyphBuffer.glyphs(from + glyphIndex); translations.resize(curLen); verticalData->getVerticalTranslationsForGlyphs(font, &glyphs[0], curLen, reinterpret_cast<float*>(&translations[0])); // To position glyphs vertically, we use offsets instead of advances. advances.resize(curLen); advances.fill(0); offsets.resize(curLen); float currentWidth = 0; for (int i = 0; i < curLen; ++i, ++glyphIndex) { offsets[i].du = lroundf(translations[i].x()); offsets[i].dv = -lroundf(currentWidth - translations[i].y()); currentWidth += glyphBuffer.advanceAt(from + glyphIndex); } SkPoint origin; origin.set(verticalOriginX, SkFloatToScalar(point.y() + horizontalOffset - point.x())); horizontalOffset += currentWidth; paintSkiaText(graphicsContext, font->platformData(), curLen, &glyphs[0], &advances[0], &offsets[0], &origin); } graphicsContext->setCTM(savedMatrix); return; } #endif // In order to round all offsets to the correct pixel boundary, this code keeps track of the absolute position // of each glyph in floating point units and rounds to integer advances at the last possible moment. int lastHorizontalOffsetRounded = lroundf(horizontalOffset); // The rounded offset of the left side of the last glyph rendered. Vector<WORD, kMaxBufferLength> glyphs; while (glyphIndex < numGlyphs) { // How many chars will be in this chunk? int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); glyphs.resize(curLen); advances.resize(curLen); float currentWidth = 0; for (int i = 0; i < curLen; ++i, ++glyphIndex) { glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex); horizontalOffset += glyphBuffer.advanceAt(from + glyphIndex); advances[i] = lroundf(horizontalOffset) - lastHorizontalOffsetRounded; lastHorizontalOffsetRounded += advances[i]; currentWidth += glyphBuffer.advanceAt(from + glyphIndex); // Bug 26088 - very large positive or negative runs can fail to // render so we clamp the size here. In the specs, negative // letter-spacing is implementation-defined, so this should be // fine, and it matches Safari's implementation. The call actually // seems to crash if kMaxNegativeRun is set to somewhere around // -32830, so we give ourselves a little breathing room. const int maxNegativeRun = -32768; const int maxPositiveRun = 32768; if ((currentWidth + advances[i] < maxNegativeRun) || (currentWidth + advances[i] > maxPositiveRun)) advances[i] = 0; } SkPoint origin = point; origin.fX += SkFloatToScalar(horizontalOffset - point.x() - currentWidth); paintSkiaText(graphicsContext, font->platformData(), curLen, &glyphs[0], &advances[0], 0, &origin); } }
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return; // We can only paint SVGFonts if a context is available. RenderSVGResource* activePaintingResource = activePaintingResourceFromRun(run); RenderObject* renderObject = renderObjectFromRun(run); RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject); RenderStyle* parentRenderObjectStyle = 0; ASSERT(renderObject); if (!activePaintingResource) { // TODO: We're only supporting simple filled HTML text so far. RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource(); solidPaintingResource->setColor(context->fillColor()); activePaintingResource = solidPaintingResource; } bool isVerticalText = false; if (parentRenderObject) { parentRenderObjectStyle = parentRenderObject->style(); ASSERT(parentRenderObjectStyle); isVerticalText = parentRenderObjectStyle->svgStyle()->isVerticalWritingMode(); } float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm()); ASSERT(activePaintingResource); FloatPoint glyphOrigin; glyphOrigin.setX(svgFontData->horizontalOriginX() * scale); glyphOrigin.setY(svgFontData->horizontalOriginY() * scale); FloatPoint currentPoint = point; RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode; for (int i = 0; i < numGlyphs; ++i) { Glyph glyph = glyphBuffer.glyphAt(from + i); if (!glyph) continue; float advance = glyphBuffer.advanceAt(from + i); SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); ASSERT(!svgGlyph.isPartOfLigature); ASSERT(svgGlyph.tableEntry == glyph); SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData); // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (svgGlyph.pathData.isEmpty()) { if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); continue; } context->save(); if (isVerticalText) { glyphOrigin.setX(svgGlyph.verticalOriginX * scale); glyphOrigin.setY(svgGlyph.verticalOriginY * scale); } AffineTransform glyphPathTransform; glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); glyphPathTransform.scale(scale, -scale); Path glyphPath = svgGlyph.pathData; glyphPath.transform(glyphPathTransform); if (activePaintingResource->applyResource(parentRenderObject, parentRenderObjectStyle, context, resourceMode)) { if (renderObject && renderObject->isSVGInlineText()) { const RenderSVGInlineText* textRenderer = toRenderSVGInlineText(renderObject); context->setStrokeThickness(context->strokeThickness() * textRenderer->scalingFactor()); } activePaintingResource->postApplyResource(parentRenderObject, context, resourceMode, &glyphPath, 0); } context->restore(); if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); } }
void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { #if USE(WXGC) wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); wxGraphicsContext* gc = dc->GetGraphicsContext(); gc->PushState(); cairo_t* cr = (cairo_t*)gc->GetNativeContext(); wxFont* wxfont = font->getWxFont(); cairo_scaled_font_t* scaled_font = 0; #if wxUSE_PANGO PangoFont* pangoFont = createPangoFontForFont(wxfont); PangoFontMap* fontMap = pangoFontMap(); PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(fontMap)); scaled_font = createScaledFontForFont(wxfont); #elif __WXMSW__ cairo_matrix_t sizeMatrix, ctm; cairo_matrix_init_identity(&ctm); int size = font->platformData().size(); cairo_matrix_init_scale(&sizeMatrix, size, size); cairo_font_options_t* fontOptions = cairo_font_options_create(); cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_SUBPIXEL); cairo_font_face_t* win_face = cairo_win32_font_face_create_for_hfont((HFONT)wxfont->GetHFONT()); scaled_font = cairo_scaled_font_create(win_face, &sizeMatrix, &ctm, fontOptions); #endif ASSERT(scaled_font); GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); float offset = point.x(); for (int i = 0; i < numGlyphs; i++) { #if wxUSE_PANGO glyphs[i].index = pango_font_get_glyph(pangoFont, pangoContext, glyphBuffer.glyphAt(from + i)); #endif glyphs[i].x = offset; glyphs[i].y = point.y(); offset += glyphBuffer.advanceAt(from + i); } cairo_set_source_rgba(cr, color.Red()/255.0, color.Green()/255.0, color.Blue()/255.0, color.Alpha()/255.0); cairo_set_scaled_font(cr, scaled_font); cairo_show_glyphs(cr, glyphs, numGlyphs); cairo_scaled_font_destroy(scaled_font); gc->PopState(); #else wxDC* dc = graphicsContext->platformContext(); wxFont* wxfont = font->getWxFont(); if (wxfont && wxfont->IsOk()) dc->SetFont(*wxfont); dc->SetTextForeground(color); // convert glyphs to wxString GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); int offset = point.x(); wxString text = wxEmptyString; for (unsigned i = 0; i < numGlyphs; i++) { text = text.Append((wxChar)glyphs[i]); offset += glyphBuffer.advanceAt(from + i); } // the y point is actually the bottom point of the text, turn it into the top float height = font->ascent() - font->descent(); wxCoord ypoint = (wxCoord) (point.y() - height); dc->DrawText(text, (wxCoord)point.x(), ypoint); #endif }