| Index: third_party/WebKit/WebCore/platform/graphics/chromium/FontChromiumWin.cpp
|
| ===================================================================
|
| --- third_party/WebKit/WebCore/platform/graphics/chromium/FontChromiumWin.cpp (revision 10194)
|
| +++ third_party/WebKit/WebCore/platform/graphics/chromium/FontChromiumWin.cpp (working copy)
|
| @@ -39,6 +39,7 @@
|
| #include "SimpleFontData.h"
|
| #include "SkiaFontWin.h"
|
| #include "SkiaUtils.h"
|
| +#include "TransparencyWin.h"
|
| #include "UniscribeHelperTextRun.h"
|
|
|
| #include "skia/ext/platform_canvas_win.h"
|
| @@ -48,6 +49,163 @@
|
|
|
| namespace WebCore {
|
|
|
| +namespace {
|
| +
|
| +bool canvasHasMultipleLayers(const SkCanvas* canvas)
|
| +{
|
| + SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false);
|
| + iter.next(); // There is always at least one layer.
|
| + return !iter.done(); // There is > 1 layer if the the iterator can stil advance.
|
| +}
|
| +
|
| +// Estimates the bounding box of the given text. This is copied from
|
| +// FontCGWin.cpp, it is possible, but a lot more work, to get the precide
|
| +// bounds.
|
| +IntRect estimateTextBounds(const SimpleFontData* font,
|
| + const GlyphBuffer& glyphBuffer,
|
| + int from, int numGlyphs,
|
| + const FloatPoint& point)
|
| +{
|
| + int totalWidth = 0;
|
| + for (int i = 0; i < numGlyphs; i++)
|
| + totalWidth += lroundf(glyphBuffer.advanceAt(from + i));
|
| +
|
| + return IntRect(point.x() - (font->ascent() + font->descent()) / 2,
|
| + point.y() - font->ascent() - font->lineGap(),
|
| + totalWidth + font->ascent() + font->descent(),
|
| + font->lineSpacing());
|
| +}
|
| +
|
| +class TransparencyAwareFontPainter {
|
| +public:
|
| + TransparencyAwareFontPainter(GraphicsContext* context,
|
| + const SimpleFontData* font,
|
| + const GlyphBuffer& glyphBuffer,
|
| + int from, int numGlyphs,
|
| + const FloatPoint& point);
|
| + TransparencyAwareFontPainter(GraphicsContext* context,
|
| + const SimpleFontData* font);
|
| + ~TransparencyAwareFontPainter();
|
| +
|
| + // Draws the partial string of glyphs, starting at |startAdvance| to the
|
| + // left of m_point. We express it this way so that if we're using the Skia
|
| + // drawing path we can use floating-point positioning, even though we have
|
| + // to use integer positioning in the GDI path.
|
| + bool drawGlyphs(int numGlyphs, const WORD* glyphs, const int* advances,
|
| + int startAdvance) const;
|
| +
|
| +private:
|
| + // Use the context from the transparency helper when drawing with GDI. It
|
| + // may point to a temporary one.
|
| + GraphicsContext* m_graphicsContext;
|
| + PlatformGraphicsContext* m_platformContext;
|
| +
|
| + const SimpleFontData* m_font;
|
| + FloatPoint m_point;
|
| +
|
| + // Set when Windows can handle the type of drawing we're doing.
|
| + bool m_useGDI;
|
| +
|
| + // These members are valid only when m_useGDI is set.
|
| + HDC m_hdc;
|
| + HGDIOBJ m_oldFont; // For restoring the DC to its original state.
|
| + TransparencyWin m_transparency;
|
| + bool m_createdLayer; // We created a layer to give the font some alpha.
|
| +};
|
| +
|
| +TransparencyAwareFontPainter::TransparencyAwareFontPainter(
|
| + GraphicsContext* context,
|
| + const SimpleFontData* font,
|
| + const GlyphBuffer& glyphBuffer,
|
| + int from, int numGlyphs,
|
| + const FloatPoint& point)
|
| + : m_graphicsContext(context)
|
| + , m_platformContext(context->platformContext())
|
| + , m_font(font)
|
| + , m_point(point)
|
| + , m_useGDI(windowsCanHandleTextDrawing(context))
|
| + , m_hdc(0)
|
| + , m_oldFont(0)
|
| + , m_createdLayer(false)
|
| +{
|
| + if (!m_useGDI)
|
| + return; // Nothing to do.
|
| +
|
| + SkColor color = m_platformContext->effectiveFillColor();
|
| + if (SkColorGetA(color) != 0xFF) {
|
| + // When the font has some transparency, apply it by creating a new
|
| + // transparency layer with that opacity applied.
|
| + m_createdLayer = true;
|
| + m_graphicsContext->beginTransparencyLayer(SkColorGetA(color) / 255.0f);
|
| + // The color should be opaque now.
|
| + color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
|
| + }
|
| +
|
| + TransparencyWin::LayerMode layerMode;
|
| + IntRect layerRect;
|
| + if (m_platformContext->isDrawingToImageBuffer()) {
|
| + // Assume if we're drawing to an image buffer that the background
|
| + // is not opaque and we have to undo ClearType. We may want to
|
| + // enhance this to actually check, since it will often be opaque
|
| + // and we could do ClearType in that case.
|
| + layerMode = TransparencyWin::TextComposite;
|
| + layerRect = estimateTextBounds(m_font, glyphBuffer, from, numGlyphs, point);
|
| +
|
| + // The transparency helper requires that we draw text in black in
|
| + // this mode and it will apply the color.
|
| + m_transparency.setTextCompositeColor(color);
|
| + color = SkColorSetRGB(0, 0, 0);
|
| + } else if (canvasHasMultipleLayers(m_platformContext->canvas())) {
|
| + // When we're drawing a web page, we know the background is opaque,
|
| + // but if we're drawing to a layer, we still need extra work.
|
| + layerMode = TransparencyWin::OpaqueCompositeLayer;
|
| + layerRect = estimateTextBounds(m_font, glyphBuffer, from, numGlyphs, point);
|
| + } else {
|
| + // Common case of drawing onto the bottom layer of a web page: we
|
| + // know everything is opaque so don't need to do anything special.
|
| + layerMode = TransparencyWin::NoLayer;
|
| + }
|
| + m_transparency.init(m_graphicsContext, layerMode, TransparencyWin::KeepTransform, layerRect);
|
| +
|
| + // Set up the DC, using the one from the transparency helper.
|
| + m_hdc = m_transparency.platformContext()->canvas()->beginPlatformPaint();
|
| + m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont());
|
| + SetTextColor(m_hdc, skia::SkColorToCOLORREF(color));
|
| + SetBkMode(m_hdc, TRANSPARENT);
|
| +}
|
| +
|
| +TransparencyAwareFontPainter::~TransparencyAwareFontPainter()
|
| +{
|
| + if (!m_useGDI)
|
| + return; // Nothing to do.
|
| + if (m_createdLayer)
|
| + m_graphicsContext->endTransparencyLayer();
|
| +
|
| + ::SelectObject(m_hdc, m_oldFont);
|
| + m_platformContext->canvas()->endPlatformPaint();
|
| +}
|
| +
|
| +bool TransparencyAwareFontPainter::drawGlyphs(int numGlyphs,
|
| + const WORD* glyphs,
|
| + const int* advances,
|
| + int startAdvance) const
|
| +{
|
| + if (!m_useGDI) {
|
| + SkPoint origin = { SkFloatToScalar(m_point.x() + startAdvance),
|
| + SkFloatToScalar(m_point.y()) };
|
| + return paintSkiaText(m_platformContext, m_font->platformData().hfont(),
|
| + numGlyphs, glyphs, advances, 0, &origin);
|
| + }
|
| +
|
| + // Windows' origin is the top-left of the bounding box, so we have
|
| + // to subtract off the font ascent to get it.
|
| + int x = lroundf(m_point.x() + startAdvance);
|
| + int y = lroundf(m_point.y() - m_font->ascent());
|
| + return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| void Font::drawGlyphs(GraphicsContext* graphicsContext,
|
| const SimpleFontData* font,
|
| const GlyphBuffer& glyphBuffer,
|
| @@ -55,62 +213,27 @@
|
| int numGlyphs,
|
| const FloatPoint& point) const
|
| {
|
| - PlatformGraphicsContext* context = graphicsContext->platformContext();
|
| -
|
| - bool canUseGDI = windowsCanHandleTextDrawing(graphicsContext);
|
| - bool createdLayer = false;
|
| -
|
| - if (canUseGDI && context->isDrawingToImageBuffer()) {
|
| - // We're drawing to an image buffer and about to render text with GDI.
|
| - // We need to start a layer here, otherwise the alpha values rendered
|
| - // by GDI are never correctly updated.
|
| - // NOTE: this doesn't handle clear type well and should be removed when
|
| - // we have better text handling code.
|
| - graphicsContext->beginTransparencyLayer(1.0);
|
| - createdLayer = true;
|
| - }
|
| -
|
| - // Max buffer length passed to the underlying windows API.
|
| - const int kMaxBufferLength = 1024;
|
| - // Default size for the buffer. It should be enough for most of cases.
|
| - const int kDefaultBufferLength = 256;
|
| -
|
| - SkColor color = context->effectiveFillColor();
|
| + SkColor color = graphicsContext->platformContext()->effectiveFillColor();
|
| unsigned char alpha = SkColorGetA(color);
|
| // Skip 100% transparent text; no need to draw anything.
|
| - if (!alpha && context->getStrokeStyle() == NoStroke)
|
| + if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke)
|
| return;
|
|
|
| - // Set up our graphics context.
|
| - HDC hdc = context->canvas()->beginPlatformPaint();
|
| - HGDIOBJ oldFont = SelectObject(hdc, font->platformData().hfont());
|
| + TransparencyAwareFontPainter helper(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
|
|
|
| - // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency.
|
| - // Enforce non-transparent color.
|
| - color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
|
| - SetTextColor(hdc, skia::SkColorToCOLORREF(color));
|
| - SetBkMode(hdc, TRANSPARENT);
|
| -
|
| - // Windows needs the characters and the advances in nice contiguous
|
| - // buffers, which we build here.
|
| - Vector<WORD, kDefaultBufferLength> glyphs;
|
| - Vector<int, kDefaultBufferLength> advances;
|
| -
|
| - // Compute the coordinate. The 'origin' represents the baseline, so we need
|
| - // to move it up to the top of the bounding square.
|
| - int x = static_cast<int>(point.x());
|
| - int lineTop = static_cast<int>(point.y()) - font->ascent();
|
| -
|
| // 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.
|
| - int glyphIndex = 0;
|
| + 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?
|
| + // How many chars will be in this chunk?
|
| int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex);
|
| -
|
| glyphs.resize(curLen);
|
| advances.resize(curLen);
|
|
|
| @@ -121,21 +244,10 @@
|
| curWidth += advances[i];
|
| }
|
|
|
| + // Actually draw the glyphs (with retry on failure).
|
| bool success = false;
|
| for (int executions = 0; executions < 2; ++executions) {
|
| - if (canUseGDI)
|
| - success = !!ExtTextOut(hdc, x, lineTop, ETO_GLYPH_INDEX, 0,
|
| - reinterpret_cast<const wchar_t*>(&glyphs[0]),
|
| - curLen, &advances[0]);
|
| - else {
|
| - // Skia's text drawing origin is the baseline, like WebKit, not
|
| - // the top, like Windows.
|
| - SkPoint origin = { x, point.y() };
|
| - success = paintSkiaText(context, font->platformData().hfont(),
|
| - numGlyphs, reinterpret_cast<const WORD*>(&glyphs[0]),
|
| - &advances[0], 0, &origin);
|
| - }
|
| -
|
| + success = helper.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());
|
| @@ -145,14 +257,8 @@
|
| }
|
|
|
| ASSERT(success);
|
| -
|
| - x += curWidth;
|
| + curAdvance += curWidth;
|
| }
|
| -
|
| - SelectObject(hdc, oldFont);
|
| - if (createdLayer)
|
| - graphicsContext->endTransparencyLayer();
|
| - context->canvas()->endPlatformPaint();
|
| }
|
|
|
| FloatRect Font::selectionRectForComplexText(const TextRun& run,
|
|
|