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, |