void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const { PlatformGraphicsContext* context = graphicsContext->platformContext(); UniscribeHelperTextRun state(run, *this); 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; #if USE(SKIA_TEXT) HDC hdc = 0; #else TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point); HDC hdc = painter.hdc(); if (windowsCanHandleTextDrawing(graphicsContext) && !hdc) return; // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. // Enforce non-transparent color. color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); if (hdc) { SetTextColor(hdc, skia::SkColorToCOLORREF(color)); SetBkMode(hdc, TRANSPARENT); } // If there is a non-blur shadow and both the fill color and shadow color // are opaque, handle without skia. FloatSize shadowOffset; float shadowBlur; Color shadowColor; ColorSpace shadowColorSpace; if (graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace) && windowsCanHandleDrawTextShadow(graphicsContext)) { COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue())); COLORREF savedTextColor = GetTextColor(hdc); SetTextColor(hdc, textColor); state.draw(graphicsContext, hdc, static_cast<int>(point.x()) + shadowOffset.width(), static_cast<int>(point.y() - fontMetrics().ascent()) + shadowOffset.height(), from, to); SetTextColor(hdc, savedTextColor); } #endif // Uniscribe counts the coordinates from the upper left, while WebKit uses // the baseline, so we have to subtract off the ascent. state.draw(graphicsContext, hdc, lroundf(point.x()), lroundf(point.y() - fontMetrics().ascent()), from, to); }
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); }