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); } }
void paintSkiaText(GraphicsContext* context, const FontPlatformData& data, int numGlyphs, const WORD* glyphs, const int* advances, const GOFFSET* offsets, const SkPoint* origin) { paintSkiaText(context, data.hfont(), data.typeface(), data.size(), data.lfQuality(), numGlyphs, glyphs, advances, offsets, origin); }
void paintSkiaText(GraphicsContext* context, const FontPlatformData& data, int numGlyphs, const WORD* glyphs, const int* advances, const GOFFSET* offsets, const SkPoint& origin, const SkRect& textRect) { paintSkiaText(context, data.hfont(), data.typeface(), data.size(), data.paintTextFlags(), numGlyphs, glyphs, advances, offsets, origin, textRect); }
void paintSkiaText(GraphicsContext* context, HFONT hfont, int numGlyphs, const WORD* glyphs, const int* advances, const GOFFSET* offsets, const SkPoint& origin, const SkRect& textRect) { int size; int paintTextFlags; RefPtr<SkTypeface> face = CreateTypefaceFromHFont(hfont, &size, &paintTextFlags); paintSkiaText(context, hfont, face.get(), size, paintTextFlags, numGlyphs, glyphs, advances, offsets, origin, textRect); }
void paintSkiaText(GraphicsContext* context, HFONT hfont, int numGlyphs, const WORD* glyphs, const int* advances, const GOFFSET* offsets, const SkPoint* origin) { int size; int quality; SkTypeface* face = CreateTypefaceFromHFont(hfont, &size, &quality); SkAutoUnref aur(face); paintSkiaText(context, hfont, face, size, quality, numGlyphs, glyphs, advances, offsets, origin); }
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 UniscribeHelper::draw(GraphicsContext* graphicsContext, HDC dc, int x, int y, int from, int to) { HGDIOBJ oldFont = 0; int curX = x; bool firstRun = true; bool useWindowsDrawing = windowsCanHandleTextDrawing(graphicsContext); for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { int itemIndex = m_screenOrder[screenIndex]; const SCRIPT_ITEM& item = m_runs[itemIndex]; const Shaping& shaping = m_shapes[itemIndex]; // Character offsets within this run. THESE MAY NOT BE IN RANGE and may // be negative, etc. The code below handles this. int fromChar = from - item.iCharPos; int toChar = to - item.iCharPos; // See if we need to draw any characters in this item. if (shaping.charLength() == 0 || fromChar >= shaping.charLength() || toChar <= 0) { // No chars in this item to display. curX += advanceForItem(itemIndex); continue; } // Compute the starting glyph within this span. |from| and |to| are // global offsets that may intersect arbitrarily with our local run. int fromGlyph, afterGlyph; if (item.a.fRTL) { // To compute the first glyph when going RTL, we use |to|. if (toChar >= shaping.charLength()) // The end of the text is after (to the left) of us. fromGlyph = 0; else { // Since |to| is exclusive, the first character we draw on the // left is actually the one right before (to the right) of // |to|. fromGlyph = shaping.m_logs[toChar - 1]; } // The last glyph is actually the first character in the range. if (fromChar <= 0) { // The first character to draw is before (to the right) of this // span, so draw all the way to the end. afterGlyph = shaping.glyphLength(); } else { // We want to draw everything up until the character to the // right of |from|. To the right is - 1, so we look that up // (remember our character could be more than one glyph, so we // can't look up our glyph and add one). afterGlyph = shaping.m_logs[fromChar - 1]; } } else { // Easy case, everybody agrees about directions. We only need to // handle boundary conditions to get a range inclusive at the // beginning, and exclusive at the ending. We have to do some // computation to see the glyph one past the end. fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar]; if (toChar >= shaping.charLength()) afterGlyph = shaping.glyphLength(); else afterGlyph = shaping.m_logs[toChar]; } // Account for the characters that were skipped in this run. When // WebKit asks us to draw a subset of the run, it actually tells us // to draw at the X offset of the beginning of the run, since it // doesn't know the internal position of any of our characters. const int* effectiveAdvances = shaping.effectiveAdvances(); int innerOffset = 0; for (int i = 0; i < fromGlyph; i++) innerOffset += effectiveAdvances[i]; // Actually draw the glyphs we found. int glyphCount = afterGlyph - fromGlyph; if (fromGlyph >= 0 && glyphCount > 0) { // Account for the preceding space we need to add to this run. We // don't need to count for the following space because that will be // counted in advanceForItem below when we move to the next run. innerOffset += shaping.m_prePadding; // Pass 0 in when there is no justification. const int* justify = shaping.m_justify.size() == 0 ? 0 : &shaping.m_justify[fromGlyph]; if (useWindowsDrawing) { if (firstRun) { oldFont = SelectObject(dc, shaping.m_hfont); firstRun = false; } else SelectObject(dc, shaping.m_hfont); } // Fonts with different ascents can be used to render different // runs. 'Across-runs' y-coordinate correction needs to be // adjusted for each font. bool textOutOk = false; for (int executions = 0; executions < 2; ++executions) { if (useWindowsDrawing) { HRESULT hr = ScriptTextOut(dc, shaping.m_scriptCache, curX + innerOffset, y - shaping.m_ascentOffset, 0, 0, &item.a, 0, 0, &shaping.m_glyphs[fromGlyph], glyphCount, &shaping.m_advance[fromGlyph], justify, &shaping.m_offsets[fromGlyph]); ASSERT(S_OK == hr); textOutOk = (hr == S_OK); } else { SkPoint origin; origin.fX = curX + + innerOffset; origin.fY = y + m_ascent; textOutOk = paintSkiaText(graphicsContext, shaping.m_hfont, glyphCount, &shaping.m_glyphs[fromGlyph], &shaping.m_advance[fromGlyph], &shaping.m_offsets[fromGlyph], &origin); } if (!textOutOk && 0 == executions) { // If TextOut is called from the renderer it might fail // because the sandbox is preventing it from opening the // font files. If we are running in the renderer, // TryToPreloadFont is overridden to ask the browser to // preload the font for us so we can access it. tryToPreloadFont(shaping.m_hfont); continue; } break; } } curX += advanceForItem(itemIndex); } if (oldFont) SelectObject(dc, oldFont); }