diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp')
-rw-r--r-- | Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp new file mode 100644 index 0000000..1a00833 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp @@ -0,0 +1,541 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, Inc. + * Copyright (c) 2006, 2007, 2008, 2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Font.h" + +#include "ChromiumBridge.h" +#include "FontFallbackList.h" +#include "GlyphBuffer.h" +#include "NotImplemented.h" +#include "PlatformContextSkia.h" +#include "SimpleFontData.h" +#include "SkiaFontWin.h" +#include "SkiaUtils.h" +#include "TransparencyWin.h" +#include "UniscribeHelperTextRun.h" + +#include "skia/ext/platform_canvas_win.h" +#include "skia/ext/skia_utils_win.h" // FIXME: remove this dependency. + +#include <windows.h> + +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. +} + +class TransparencyAwareFontPainter { +public: + TransparencyAwareFontPainter(GraphicsContext*, const FloatPoint&); + ~TransparencyAwareFontPainter(); + +protected: + // Called by our subclass' constructor to initialize GDI if necessary. This + // is a separate function so it can be called after the subclass finishes + // construction (it calls virtual functions). + void init(); + + virtual IntRect estimateTextBounds() = 0; + + // Use the context from the transparency helper when drawing with GDI. It + // may point to a temporary one. + GraphicsContext* m_graphicsContext; + PlatformGraphicsContext* m_platformContext; + + 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; + TransparencyWin m_transparency; + +private: + // Call when we're using GDI mode to initialize the TransparencyWin to help + // us draw GDI text. + void initializeForGDI(); + + bool m_createdTransparencyLayer; // We created a layer to give the font some alpha. +}; + +TransparencyAwareFontPainter::TransparencyAwareFontPainter(GraphicsContext* context, + const FloatPoint& point) + : m_graphicsContext(context) + , m_platformContext(context->platformContext()) + , m_point(point) + , m_useGDI(windowsCanHandleTextDrawing(context)) + , m_hdc(0) + , m_createdTransparencyLayer(false) +{ +} + +void TransparencyAwareFontPainter::init() +{ + if (m_useGDI) + initializeForGDI(); +} + +void TransparencyAwareFontPainter::initializeForGDI() +{ + m_graphicsContext->save(); + SkColor color = m_platformContext->effectiveFillColor(); + // Used only when m_createdTransparencyLayer is true. + float layerAlpha = 0.0f; + if (SkColorGetA(color) != 0xFF) { + // When the font has some transparency, apply it by creating a new + // transparency layer with that opacity applied. We'll actually create + // a new transparency layer after we calculate the bounding box. + m_createdTransparencyLayer = true; + layerAlpha = 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_graphicsContext->clip(layerRect); + if (m_createdTransparencyLayer) + m_graphicsContext->beginTransparencyLayer(layerAlpha); + + // 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 (m_createdTransparencyLayer || 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_graphicsContext->clip(layerRect); + if (m_createdTransparencyLayer) + m_graphicsContext->beginTransparencyLayer(layerAlpha); + } 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; + } + + // Bug 26088 - init() might fail if layerRect is invalid. Given this, we + // need to be careful to check for null pointers everywhere after this call + m_transparency.init(m_graphicsContext, layerMode, + TransparencyWin::KeepTransform, layerRect); + + // Set up the DC, using the one from the transparency helper. + if (m_transparency.platformContext()) { + m_hdc = m_transparency.platformContext()->canvas()->beginPlatformPaint(); + SetTextColor(m_hdc, skia::SkColorToCOLORREF(color)); + SetBkMode(m_hdc, TRANSPARENT); + } +} + +TransparencyAwareFontPainter::~TransparencyAwareFontPainter() +{ + if (!m_useGDI || !m_graphicsContext || !m_platformContext) + return; // Nothing to do. + m_transparency.composite(); + if (m_createdTransparencyLayer) + m_graphicsContext->endTransparencyLayer(); + m_graphicsContext->restore(); + m_platformContext->canvas()->endPlatformPaint(); +} + +// Specialization for simple GlyphBuffer painting. +class TransparencyAwareGlyphPainter : public TransparencyAwareFontPainter { + public: + TransparencyAwareGlyphPainter(GraphicsContext*, + const SimpleFontData*, + const GlyphBuffer&, + int from, int numGlyphs, + const FloatPoint&); + ~TransparencyAwareGlyphPainter(); + + // 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: + virtual IntRect estimateTextBounds(); + + const SimpleFontData* m_font; + const GlyphBuffer& m_glyphBuffer; + int m_from; + int m_numGlyphs; + + // When m_useGdi is set, this stores the previous HFONT selected into the + // m_hdc so we can restore it. + HGDIOBJ m_oldFont; // For restoring the DC to its original state. +}; + +TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter( + GraphicsContext* context, + const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, + const FloatPoint& point) + : TransparencyAwareFontPainter(context, point) + , m_font(font) + , m_glyphBuffer(glyphBuffer) + , m_from(from) + , m_numGlyphs(numGlyphs) + , m_oldFont(0) +{ + init(); + + if (m_hdc) + m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont()); +} + +TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter() +{ + if (m_useGDI && m_hdc) + ::SelectObject(m_hdc, m_oldFont); +} + + +// 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 TransparencyAwareGlyphPainter::estimateTextBounds() +{ + int totalWidth = 0; + for (int i = 0; i < m_numGlyphs; i++) + totalWidth += lroundf(m_glyphBuffer.advanceAt(m_from + i)); + + return IntRect(m_point.x() - (m_font->ascent() + m_font->descent()) / 2, + m_point.y() - m_font->ascent() - m_font->lineGap(), + totalWidth + m_font->ascent() + m_font->descent(), + m_font->lineSpacing()); +} + +bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, + const WORD* glyphs, + const int* advances, + int startAdvance) const +{ + if (!m_useGDI) { + SkPoint origin = m_point; + origin.fX += startAdvance; + return paintSkiaText(m_graphicsContext, m_font->platformData().hfont(), + numGlyphs, glyphs, advances, 0, &origin); + } + + if (!m_graphicsContext || !m_hdc) + return true; + + // 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()); + + // 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 (m_graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace)) { + // If there is a shadow and this code is reached, windowsCanHandleDrawTextShadow() + // will have already returned true during the ctor initiatization of m_useGDI + ASSERT(shadowColor.alpha() == 255); + ASSERT(m_graphicsContext->fillColor().alpha() == 255); + ASSERT(shadowBlur == 0); + COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue())); + COLORREF savedTextColor = GetTextColor(m_hdc); + SetTextColor(m_hdc, textColor); + ExtTextOut(m_hdc, x + shadowOffset.width(), y + shadowOffset.height(), ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); + SetTextColor(m_hdc, savedTextColor); + } + + return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); +} + +class TransparencyAwareUniscribePainter : public TransparencyAwareFontPainter { + public: + TransparencyAwareUniscribePainter(GraphicsContext*, + const Font*, + const TextRun&, + int from, int to, + const FloatPoint&); + ~TransparencyAwareUniscribePainter(); + + // Uniscibe will draw directly into our buffer, so we need to expose our DC. + HDC hdc() const { return m_hdc; } + + private: + virtual IntRect estimateTextBounds(); + + const Font* m_font; + const TextRun& m_run; + int m_from; + int m_to; +}; + +TransparencyAwareUniscribePainter::TransparencyAwareUniscribePainter( + GraphicsContext* context, + const Font* font, + const TextRun& run, + int from, int to, + const FloatPoint& point) + : TransparencyAwareFontPainter(context, point) + , m_font(font) + , m_run(run) + , m_from(from) + , m_to(to) +{ + init(); +} + +TransparencyAwareUniscribePainter::~TransparencyAwareUniscribePainter() +{ +} + +IntRect TransparencyAwareUniscribePainter::estimateTextBounds() +{ + // This case really really sucks. There is no convenient way to estimate + // the bounding box. So we run Uniscribe twice. If we find this happens a + // lot, the way to fix it is to make the extra layer after the + // UniscribeHelper has measured the text. + IntPoint intPoint(lroundf(m_point.x()), + lroundf(m_point.y())); + + UniscribeHelperTextRun state(m_run, *m_font); + int left = lroundf(m_point.x()) + state.characterToX(m_from); + int right = lroundf(m_point.x()) + state.characterToX(m_to); + + // Adjust for RTL script since we just want to know the text bounds. + if (left > right) + std::swap(left, right); + + // This algorithm for estimating how much extra space we need (the text may + // go outside the selection rect) is based roughly on + // TransparencyAwareGlyphPainter::estimateTextBounds above. + return IntRect(left - (m_font->ascent() + m_font->descent()) / 2, + m_point.y() - m_font->ascent() - m_font->lineGap(), + (right - left) + m_font->ascent() + m_font->descent(), + m_font->lineSpacing()); +} + +} // namespace + +bool Font::canReturnFallbackFontsForComplexText() +{ + return false; +} + +void Font::drawGlyphs(GraphicsContext* graphicsContext, + const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, + int from, + int numGlyphs, + const FloatPoint& point) const +{ + graphicsContext->platformContext()->prepareForSoftwareDraw(); + + 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; + } +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, + const FloatPoint& point, + int h, + int from, + int to) const +{ + UniscribeHelperTextRun state(run, *this); + float left = static_cast<float>(point.x() + state.characterToX(from)); + float right = static_cast<float>(point.x() + state.characterToX(to)); + + // If the text is RTL, left will actually be after right. + if (left < right) + return FloatRect(left, point.y(), + right - left, static_cast<float>(h)); + + return FloatRect(right, point.y(), + left - right, static_cast<float>(h)); +} + +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; + + 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() - ascent()) + shadowOffset.height(), from, to); + SetTextColor(hdc, savedTextColor); + } + + // 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, static_cast<int>(point.x()), + static_cast<int>(point.y() - ascent()), from, to); + + context->canvas()->endPlatformPaint(); +} + +void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const +{ + notImplemented(); +} + +float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */, GlyphOverflow* /* glyphOverflow */) const +{ + UniscribeHelperTextRun state(run, *this); + return static_cast<float>(state.width()); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, + bool includePartialGlyphs) const +{ + // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers + // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. + int x = static_cast<int>(xFloat); + + // Mac code ignores includePartialGlyphs, and they don't know what it's + // supposed to do, so we just ignore it as well. + UniscribeHelperTextRun state(run, *this); + int charIndex = state.xToCharacter(x); + + // XToCharacter will return -1 if the position is before the first + // character (we get called like this sometimes). + if (charIndex < 0) + charIndex = 0; + return charIndex; +} + +} // namespace WebCore |