diff options
Diffstat (limited to 'WebCore/platform/graphics/chromium')
18 files changed, 1034 insertions, 253 deletions
diff --git a/WebCore/platform/graphics/chromium/ColorChromium.cpp b/WebCore/platform/graphics/chromium/ColorChromium.cpp deleted file mode 100644 index 647169c..0000000 --- a/WebCore/platform/graphics/chromium/ColorChromium.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "Color.h" - -namespace WebCore { - -#if !PLATFORM(DARWIN) -// On OS X, there's code to monitor changes in the focus color system setting. -// On Windows/Linux there is no equivalent system setting and therefore a static -// color is all we need. -Color focusRingColor() -{ - static Color focusRingColor(229, 151, 0, 255); - return focusRingColor; -} -#endif - -} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/ColorChromiumMac.mm b/WebCore/platform/graphics/chromium/ColorChromiumMac.mm deleted file mode 100644 index 01dff7e..0000000 --- a/WebCore/platform/graphics/chromium/ColorChromiumMac.mm +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "Color.h" - -#import <AppKit/NSColor.h> -#import <wtf/Assertions.h> -#import <wtf/StdLibExtras.h> -#import <wtf/RetainPtr.h> - -namespace WebCore { - -Color focusRingColor() -{ - // To avoid the Mac Chromium build having to rebasline 500+ layout tests and - // continue to do this w/ new tests that get landed in WebKit, we want to - // run the layout tests w/ the same color that stock WebKit uses. - // - // FIXME: For now we've hard coded the color that WebKit uses for layout - // tests. We need to revisit this and do either of the following: - // A. Fully honor the color from the UI, which means collecting the color - // (and change notifications) in the browser process, and messaging the - // color to the render process. - // B. Adding a "layout tests" flag, to control the orage vs. blue colors - // depending if we're running layout tests. - // To see the WebKit implementation of using the UI color and/or a flag for - // layout tests see WebKit/WebCore/platform/graphics/mac/ColorMac.mm. - // (Reality is we just need an api to override the focus color and both - // of the above are covered for what this file needs to provide, the - // two options would be details that happen in other places.) - - // From WebKit: - // static RGBA32 oldAquaFocusRingColorRGBA = 0xFF7DADD9; - static Color oldAquaFocusRingColor(0x7D, 0xAD, 0xD9, 0xFF); - return oldAquaFocusRingColor; -} - -// createCGColor() and the functions it calls are verbatum copies of -// graphics/mac/ColorMac.mm. These are copied here so that we don't need to -// include ColorMac.mm in the Chromium build. -// FIXME: Check feasibility of using pure CG calls and unifying this copy with -// ColorMac.mm's under graphics/cg. - -NSColor* nsColor(const Color& color) -{ - unsigned c = color.rgb(); - switch (c) { - case 0: { - // Need this to avoid returning nil because cachedRGBAValues will default to 0. - DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, clearColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:0.0f])); - return clearColor.get(); - } - case Color::black: { - DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, blackColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:1.0f])); - return blackColor.get(); - } - case Color::white: { - DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, whiteColor, ([NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f])); - return whiteColor.get(); - } - default: { - const int cacheSize = 32; - static unsigned cachedRGBAValues[cacheSize]; - static RetainPtr<NSColor>* cachedColors = new RetainPtr<NSColor>[cacheSize]; - - for (int i = 0; i != cacheSize; ++i) - if (cachedRGBAValues[i] == c) - return cachedColors[i].get(); - - NSColor* result = [NSColor colorWithDeviceRed:color.red() / 255.0f - green:color.green() / 255.0f - blue:color.blue() / 255.0f - alpha:color.alpha() /255.0f]; - - static int cursor; - cachedRGBAValues[cursor] = c; - cachedColors[cursor] = result; - if (++cursor == cacheSize) - cursor = 0; - return result; - } - } -} - -static CGColorRef CGColorFromNSColor(NSColor* color) -{ - // This needs to always use device colorspace so it can de-calibrate the color for - // CGColor to possibly recalibrate it. - NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - CGFloat red = [deviceColor redComponent]; - CGFloat green = [deviceColor greenComponent]; - CGFloat blue = [deviceColor blueComponent]; - CGFloat alpha = [deviceColor alphaComponent]; - const CGFloat components[4] = { red, green, blue, alpha }; - static CGColorSpaceRef deviceRGBColorSpace = CGColorSpaceCreateDeviceRGB(); - CGColorRef cgColor = CGColorCreate(deviceRGBColorSpace, components); - return cgColor; -} - -CGColorRef createCGColor(const Color& c) -{ - // We could directly create a CGColor here, but that would - // skip any RGB caching the nsColor method does. A direct - // creation could be investigated for a possible performance win. - return CGColorFromNSColor(nsColor(c)); -} - -} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp index bf1cd2e..9252ae0 100644 --- a/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp @@ -288,7 +288,7 @@ static bool fontContainsCharacter(const FontPlatformData* fontData, if (count == 0 && ChromiumBridge::ensureFontLoaded(hfont)) count = GetFontUnicodeRanges(hdc, 0); if (count == 0) { - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the font unicode range after second attempt"); SelectObject(hdc, oldFont); ReleaseDC(0, hdc); return true; diff --git a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp index 797825e..3fe1561 100644 --- a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp @@ -31,9 +31,8 @@ #include "config.h" #include "FontCache.h" -#include <fontconfig/fontconfig.h> - #include "AtomicString.h" +#include "ChromiumBridge.h" #include "CString.h" #include "Font.h" #include "FontDescription.h" @@ -46,6 +45,7 @@ #include "SkTypeface.h" #include "SkUtils.h" +#include <unicode/utf16.h> #include <wtf/Assertions.h> namespace WebCore { @@ -58,38 +58,12 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) { - FcCharSet* cset = FcCharSetCreate(); - for (int i = 0; i < length; ++i) - FcCharSetAddChar(cset, characters[i]); - - FcPattern* pattern = FcPatternCreate(); - - FcValue fcvalue; - fcvalue.type = FcTypeCharSet; - fcvalue.u.c = cset; - FcPatternAdd(pattern, FC_CHARSET, fcvalue, 0); - - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - FcResult result; - FcPattern* match = FcFontMatch(0, pattern, &result); - FcPatternDestroy(pattern); - - SimpleFontData* ret = 0; - - if (match) { - FcChar8* family; - if (FcPatternGetString(match, FC_FAMILY, 0, &family) == FcResultMatch) { - AtomicString fontFamily(reinterpret_cast<char*>(family)); - ret = getCachedFontData(getCachedFontPlatformData(font.fontDescription(), fontFamily, false)); - } - FcPatternDestroy(match); - } - - FcCharSetDestroy(cset); + String family = ChromiumBridge::getFontFamilyForCharacters(characters, length); + if (family.isEmpty()) + return 0; - return ret; + AtomicString atomicFamily(family); + return getCachedFontData(getCachedFontPlatformData(font.fontDescription(), atomicFamily, false)); } FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) @@ -165,13 +139,7 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD if (fontDescription.italic()) style |= SkTypeface::kItalic; - // FIXME: This #ifdef can go away once we're firmly using the new Skia. - // During the transition, this makes the code compatible with both versions. -#ifdef SK_USE_OLD_255_TO_256 SkTypeface* tf = SkTypeface::CreateFromName(name, static_cast<SkTypeface::Style>(style)); -#else - SkTypeface* tf = SkTypeface::Create(name, static_cast<SkTypeface::Style>(style)); -#endif if (!tf) return 0; diff --git a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp index 4710245..3d67992 100644 --- a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp @@ -146,17 +146,23 @@ void TransparencyAwareFontPainter::initializeForGDI() // know everything is opaque so don't need to do anything special. layerMode = TransparencyWin::NoLayer; } - m_transparency.init(m_graphicsContext, layerMode, TransparencyWin::KeepTransform, layerRect); + + // 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. - m_hdc = m_transparency.platformContext()->canvas()->beginPlatformPaint(); - SetTextColor(m_hdc, skia::SkColorToCOLORREF(color)); - SetBkMode(m_hdc, TRANSPARENT); + 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) + if (!m_useGDI || !m_graphicsContext || !m_platformContext) return; // Nothing to do. m_transparency.composite(); if (m_createdTransparencyLayer) @@ -208,12 +214,13 @@ TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter( { init(); - m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont()); + if (m_hdc) + m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont()); } TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter() { - if (m_useGDI) + if (m_useGDI && m_hdc) ::SelectObject(m_hdc, m_oldFont); } @@ -245,6 +252,9 @@ bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, 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); @@ -265,7 +275,7 @@ bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, COLORREF savedTextColor = GetTextColor(m_hdc); SetTextColor(m_hdc, textColor); ExtTextOut(m_hdc, x + shadowSize.width(), y + shadowSize.height(), ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); - SetTextColor(m_hdc, savedTextColor); + SetTextColor(m_hdc, savedTextColor); } return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); @@ -379,6 +389,17 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, 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]; } @@ -394,7 +415,9 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, break; } - ASSERT(success); + if (!success) + LOG_ERROR("Unable to draw the glyphs after second attempt"); + curAdvance += curWidth; } } @@ -436,6 +459,8 @@ void Font::drawComplexText(GraphicsContext* graphicsContext, TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point); HDC hdc = painter.hdc(); + if (!hdc) + return; // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. // Enforce non-transparent color. diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp index e99c12a..88035d5 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp @@ -36,6 +36,8 @@ #include "Base64.h" #include "ChromiumBridge.h" #include "OpenTypeUtilities.h" +#elif PLATFORM(LINUX) +#include "SkStream.h" #endif #include "FontPlatformData.h" @@ -46,6 +48,8 @@ #include <objbase.h> #include <t2embapi.h> #pragma comment(lib, "t2embed") +#elif PLATFORM(LINUX) +#include <cstring> #endif namespace WebCore { @@ -60,6 +64,9 @@ FontCustomPlatformData::~FontCustomPlatformData() } else RemoveFontMemResourceEx(m_fontReference); } +#elif PLATFORM(LINUX) + if (m_fontReference) + m_fontReference->unref(); #endif } @@ -102,6 +109,9 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b HFONT hfont = CreateFontIndirect(&logFont); return FontPlatformData(hfont, size); +#elif PLATFORM(LINUX) + ASSERT(m_fontReference); + return FontPlatformData(m_fontReference, size, bold && !m_fontReference->isBold(), italic && !m_fontReference->isItalic()); #else notImplemented(); return FontPlatformData(); @@ -186,6 +196,51 @@ static String createUniqueFontName() } #endif +#if PLATFORM(LINUX) +class RemoteFontStream : public SkStream { +public: + explicit RemoteFontStream(PassRefPtr<SharedBuffer> buffer) + : m_buffer(buffer) + , m_offset(0) + { + } + + virtual ~RemoteFontStream() + { + } + + virtual bool rewind() + { + m_offset = 0; + return true; + } + + virtual size_t read(void* buffer, size_t size) + { + if (!buffer && !size) { + // This is request for the length of the stream. + return m_buffer->size(); + } + if (!buffer) { + // This is a request to skip bytes. This operation is not supported. + return 0; + } + // This is a request to read bytes. + if (!m_buffer->data() || !m_buffer->size()) + return 0; + size_t left = m_buffer->size() - m_offset; + size_t toRead = (left > size) ? size : left; + std::memcpy(buffer, m_buffer->data() + m_offset, toRead); + m_offset += toRead; + return toRead; + } + +private: + RefPtr<SharedBuffer> m_buffer; + size_t m_offset; +}; +#endif + FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) { ASSERT_ARG(buffer, buffer); @@ -223,8 +278,14 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) } return new FontCustomPlatformData(fontReference, fontName); +#elif PLATFORM(LINUX) + RemoteFontStream stream(buffer); + SkTypeface* typeface = SkTypeface::CreateFromStream(&stream); + if (!typeface) + return 0; + return new FontCustomPlatformData(typeface); #else - notImplemented();; + notImplemented(); return 0; #endif } diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.h b/WebCore/platform/graphics/chromium/FontCustomPlatformData.h index 2f1a597..a42f1ec 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.h @@ -32,12 +32,14 @@ #ifndef FontCustomPlatformData_h #define FontCustomPlatformData_h +#include "FontRenderingMode.h" #include <wtf/Noncopyable.h> #if PLATFORM(WIN_OS) -#include "FontRenderingMode.h" #include "PlatformString.h" #include <windows.h> +#elif PLATFORM(LINUX) +#include "SkTypeface.h" #endif namespace WebCore { @@ -51,6 +53,10 @@ struct FontCustomPlatformData : Noncopyable { : m_fontReference(fontReference) , m_name(name) {} +#elif PLATFORM(LINUX) + explicit FontCustomPlatformData(SkTypeface* typeface) + : m_fontReference(typeface) + {} #endif ~FontCustomPlatformData(); @@ -61,6 +67,8 @@ struct FontCustomPlatformData : Noncopyable { #if PLATFORM(WIN_OS) HANDLE m_fontReference; String m_name; +#elif PLATFORM(LINUX) + SkTypeface* m_fontReference; #endif }; diff --git a/WebCore/platform/graphics/chromium/FontLinux.cpp b/WebCore/platform/graphics/chromium/FontLinux.cpp index a952685..d4e45fb 100644 --- a/WebCore/platform/graphics/chromium/FontLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -34,6 +34,7 @@ #include "FloatRect.h" #include "GlyphBuffer.h" #include "GraphicsContext.h" +#include "HarfbuzzSkia.h" #include "NotImplemented.h" #include "PlatformContextSkia.h" #include "SimpleFontData.h" @@ -54,7 +55,7 @@ bool Font::canReturnFallbackFontsForComplexText() void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { - SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert + SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); SkScalar x = SkFloatToScalar(point.x()); @@ -96,7 +97,6 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, SkPaint paint; gc->platformContext()->setupPaintForStroking(&paint, 0, 0); font->platformData().setupPaint(&paint); - paint.setFlags(SkPaint::kAntiAlias_Flag); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setColor(gc->strokeColor().rgb()); @@ -110,31 +110,503 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, } } -void Font::drawComplexText(GraphicsContext* context, const TextRun& run, +// Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't +// handle subpixel positioning so this function is used to truncate Harfbuzz +// values to a number of pixels. +static int truncateFixedPointToInteger(HB_Fixed value) +{ + return value >> 6; +} + +// TextRunWalker walks a TextRun and presents each script run in sequence. A +// TextRun is a sequence of code-points with the same embedding level (i.e. they +// are all left-to-right or right-to-left). A script run is a subsequence where +// all the characters have the same script (e.g. Arabic, Thai etc). Shaping is +// only ever done with script runs since the shapers only know how to deal with +// a single script. +// +// After creating it, the script runs are either iterated backwards or forwards. +// It defaults to backwards for RTL and forwards otherwise (which matches the +// presentation order), however you can set it with |setBackwardsIteration|. +// +// Once you have setup the object, call |nextScriptRun| to get the first script +// run. This will return false when the iteration is complete. At any time you +// can call |reset| to start over again. +class TextRunWalker { +public: + TextRunWalker(const TextRun& run, unsigned startingX, const Font* font) + : m_font(font) + , m_run(run) + , m_startingX(startingX) + , m_offsetX(m_startingX) + , m_iterateBackwards(run.rtl()) + { + memset(&m_item, 0, sizeof(m_item)); + // We cannot know, ahead of time, how many glyphs a given script run + // will produce. We take a guess that script runs will not produce more + // than twice as many glyphs as there are code points and fallback if + // we find that we are wrong. + m_maxGlyphs = run.length() * 2; + createGlyphArrays(); + + m_item.log_clusters = new unsigned short[run.length()]; + + m_item.face = 0; + m_item.font = allocHarfbuzzFont(); + + m_item.string = run.characters(); + m_item.stringLength = run.length(); + m_item.item.bidiLevel = run.rtl(); + + reset(); + } + + ~TextRunWalker() + { + fastFree(m_item.font); + deleteGlyphArrays(); + delete[] m_item.log_clusters; + } + + void reset() + { + if (m_iterateBackwards) + m_indexOfNextScriptRun = m_run.length() - 1; + else + m_indexOfNextScriptRun = 0; + m_offsetX = m_startingX; + } + + // Set the x offset for the next script run. This affects the values in + // |xPositions| + void setXOffsetToZero() + { + m_offsetX = 0; + } + + bool rtl() const + { + return m_run.rtl(); + } + + void setBackwardsIteration(bool isBackwards) + { + m_iterateBackwards = isBackwards; + reset(); + } + + // Advance to the next script run, returning false when the end of the + // TextRun has been reached. + bool nextScriptRun() + { + if (m_iterateBackwards) { + // In right-to-left mode we need to render the shaped glyph backwards and + // also render the script runs themselves backwards. So given a TextRun: + // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai) + // we render: + // TTTTTTCAAAAAAA + // (and the glyphs in each A, C and T section are backwards too) + if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) + return false; + } else { + if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) + return false; + } + + setupFontForScriptRun(); + + if (!shapeGlyphs()) + return false; + setGlyphXPositions(rtl()); + return true; + } + + const uint16_t* glyphs() const + { + return m_glyphs16; + } + + // Return the length of the array returned by |glyphs| + const unsigned length() const + { + return m_item.num_glyphs; + } + + // Return the x offset for each of the glyphs. Note that this is translated + // by the current x offset and that the x offset is updated for each script + // run. + const SkScalar* xPositions() const + { + return m_xPositions; + } + + // Get the advances (widths) for each glyph. + const HB_Fixed* advances() const + { + return m_item.advances; + } + + // Return the width (in px) of the current script run. + const unsigned width() const + { + return m_pixelWidth; + } + + // Return the cluster log for the current script run. For example: + // script run: f i a n c é (fi gets ligatured) + // log clutrs: 0 0 1 2 3 4 + // So, for each input code point, the log tells you which output glyph was + // generated for it. + const unsigned short* logClusters() const + { + return m_item.log_clusters; + } + + // return the number of code points in the current script run + const unsigned numCodePoints() const + { + return m_numCodePoints; + } + + const FontPlatformData* fontPlatformDataForScriptRun() + { + return reinterpret_cast<FontPlatformData*>(m_item.font->userData); + } + + float widthOfFullRun() + { + float widthSum = 0; + while (nextScriptRun()) + widthSum += width(); + + return widthSum; + } + +private: + void setupFontForScriptRun() + { + const FontData* fontData = m_font->fontDataAt(0); + if (!fontData->containsCharacters(m_item.string + m_item.item.pos, m_item.item.length)) + fontData = m_font->fontDataForCharacters(m_item.string + m_item.item.pos, m_item.item.length); + const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData(); + m_item.face = platformData.harfbuzzFace(); + void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); + m_item.font->userData = opaquePlatformData; + } + + HB_FontRec* allocHarfbuzzFont() + { + HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec))); + memset(font, 0, sizeof(HB_FontRec)); + font->klass = &harfbuzzSkiaClass; + font->userData = 0; + // The values which harfbuzzSkiaClass returns are already scaled to + // pixel units, so we just set all these to one to disable further + // scaling. + font->x_ppem = 1; + font->y_ppem = 1; + font->x_scale = 1; + font->y_scale = 1; + + return font; + } + + void deleteGlyphArrays() + { + delete[] m_item.glyphs; + delete[] m_item.attributes; + delete[] m_item.advances; + delete[] m_item.offsets; + delete[] m_glyphs16; + delete[] m_xPositions; + } + + bool createGlyphArrays() + { + m_item.glyphs = new HB_Glyph[m_maxGlyphs]; + m_item.attributes = new HB_GlyphAttributes[m_maxGlyphs]; + m_item.advances = new HB_Fixed[m_maxGlyphs]; + m_item.offsets = new HB_FixedPoint[m_maxGlyphs]; + m_glyphs16 = new uint16_t[m_maxGlyphs]; + m_xPositions = new SkScalar[m_maxGlyphs]; + + return m_item.glyphs + && m_item.attributes + && m_item.advances + && m_item.offsets + && m_glyphs16 + && m_xPositions; + } + + bool expandGlyphArrays() + { + deleteGlyphArrays(); + m_maxGlyphs <<= 1; + return createGlyphArrays(); + } + + bool shapeGlyphs() + { + for (;;) { + m_item.num_glyphs = m_maxGlyphs; + HB_ShapeItem(&m_item); + if (m_item.num_glyphs < m_maxGlyphs) + break; + + // We overflowed our arrays. Resize and retry. + if (!expandGlyphArrays()) + return false; + } + + return true; + } + + void setGlyphXPositions(bool isRTL) + { + m_pixelWidth = 0; + for (unsigned i = 0; i < m_item.num_glyphs; ++i) { + int index; + if (isRTL) + index = m_item.num_glyphs - (i + 1); + else + index = i; + + m_glyphs16[i] = m_item.glyphs[i]; + m_xPositions[index] = m_offsetX + m_pixelWidth; + m_pixelWidth += truncateFixedPointToInteger(m_item.advances[index]); + } + m_offsetX += m_pixelWidth; + } + + const Font* const m_font; + const TextRun& m_run; + HB_ShaperItem m_item; + uint16_t* m_glyphs16; // A vector of 16-bit glyph ids. + SkScalar* m_xPositions; // A vector of x positions for each glyph. + ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|. + const unsigned m_startingX; // Offset in pixels of the first script run. + unsigned m_offsetX; // Offset in pixels to the start of the next script run. + unsigned m_pixelWidth; // Width (in px) of the current script run. + unsigned m_numCodePoints; // Code points in current script run. + unsigned m_maxGlyphs; // Current size of all the Harfbuzz arrays. + bool m_iterateBackwards; +}; + +static void setupForTextPainting(SkPaint* paint, SkColor color) +{ + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint->setColor(color); +} + +void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, const FloatPoint& point, int from, int to) const { - notImplemented(); + if (!run.length()) + return; + + SkCanvas* canvas = gc->platformContext()->canvas(); + int textMode = gc->platformContext()->getTextDrawingMode(); + bool fill = textMode & cTextFill; + bool stroke = (textMode & cTextStroke) + && gc->platformContext()->getStrokeStyle() != NoStroke + && gc->platformContext()->getStrokeThickness() > 0; + + if (!fill && !stroke) + return; + + SkPaint strokePaint, fillPaint; + if (fill) { + gc->platformContext()->setupPaintForFilling(&fillPaint); + setupForTextPainting(&fillPaint, gc->fillColor().rgb()); + } + if (stroke) { + gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0); + setupForTextPainting(&strokePaint, gc->strokeColor().rgb()); + } + + TextRunWalker walker(run, point.x(), this); + + while (walker.nextScriptRun()) { + if (fill) { + walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); + canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint); + } + + if (stroke) { + walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); + canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint); + } + } } float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */) const { - notImplemented(); - return 0; + TextRunWalker walker(run, 0, this); + return walker.widthOfFullRun(); } +static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x) +{ + const HB_Fixed* advances = walker.advances(); + int glyphIndex; + if (walker.rtl()) { + for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) { + if (x < truncateFixedPointToInteger(advances[glyphIndex])) + break; + x -= truncateFixedPointToInteger(advances[glyphIndex]); + } + } else { + for (glyphIndex = 0; glyphIndex < walker.length(); ++glyphIndex) { + if (x < truncateFixedPointToInteger(advances[glyphIndex])) + break; + x -= truncateFixedPointToInteger(advances[glyphIndex]); + } + } + + return glyphIndex; +} + +// Return the code point index for the given |x| offset into the text run. int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const { - notImplemented(); - return 0; + // (Mac code ignores includePartialGlyphs, and they don't know what it's + // supposed to do, so we just ignore it as well.) + TextRunWalker walker(run, 0, this); + + // If this is RTL text, the first glyph from the left is actually the last + // code point. So we need to know how many code points there are total in + // order to subtract. This is different from the length of the TextRun + // because UTF-16 surrogate pairs are a single code point, but 32-bits long. + // In LTR we leave this as 0 so that we get the correct value for + // |basePosition|, below. + unsigned totalCodePoints = 0; + if (walker.rtl()) { + ssize_t offset = 0; + while (offset < run.length()) { + utf16_to_code_point(run.characters(), run.length(), &offset); + totalCodePoints++; + } + } + + unsigned basePosition = totalCodePoints; + + // For RTL: + // code-point order: abcd efg hijkl + // on screen: lkjih gfe dcba + // ^ ^ + // | | + // basePosition--| | + // totalCodePoints----| + // Since basePosition is currently the total number of code-points, the + // first thing we do is decrement it so that it's pointing to the start of + // the current script-run. + // + // For LTR, basePosition is zero so it already points to the start of the + // first script run. + while (walker.nextScriptRun()) { + if (walker.rtl()) + basePosition -= walker.numCodePoints(); + + if (x < walker.width()) { + // The x value in question is within this script run. We consider + // each glyph in presentation order and stop when we find the one + // covering this position. + const int glyphIndex = glyphIndexForXPositionInScriptRun(walker, x); + + // Now that we have a glyph index, we have to turn that into a + // code-point index. Because of ligatures, several code-points may + // have gone into a single glyph. We iterate over the clusters log + // and find the first code-point which contributed to the glyph. + + // Some shapers (i.e. Khmer) will produce cluster logs which report + // that /no/ code points contributed to certain glyphs. Because of + // this, we take any code point which contributed to the glyph in + // question, or any subsequent glyph. If we run off the end, then + // we take the last code point. + const unsigned short* log = walker.logClusters(); + for (unsigned j = 0; j < walker.numCodePoints(); ++j) { + if (log[j] >= glyphIndex) + return basePosition + j; + } + + return basePosition + walker.numCodePoints() - 1; + } + + x -= walker.width(); + + if (!walker.rtl()) + basePosition += walker.numCodePoints(); + } + + return basePosition; } +// Return the rectangle for selecting the given range of code-points in the TextRun. FloatRect Font::selectionRectForComplexText(const TextRun& run, - const IntPoint& point, int h, + const IntPoint& point, int height, int from, int to) const { - notImplemented(); - return FloatRect(); + int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1; + TextRunWalker walker(run, 0, this); + + // Base will point to the x offset for the current script run. Note that, in + // the LTR case, width will be 0. + int base = walker.rtl() ? walker.widthOfFullRun() : 0; + const int leftEdge = base; + + // We want to enumerate the script runs in code point order in the following + // code. This call also resets |walker|. + walker.setBackwardsIteration(false); + + while (walker.nextScriptRun() && (fromX == -1 || toX == -1)) { + // TextRunWalker will helpfully accululate the x offsets for different + // script runs for us. For this code, however, we always want the x offsets + // to start from zero so we call this before each script run. + walker.setXOffsetToZero(); + + if (walker.rtl()) + base -= walker.width(); + + if (fromX == -1 && from < walker.numCodePoints()) { + // |from| is within this script run. So we index the clusters log to + // find which glyph this code-point contributed to and find its x + // position. + int glyph = walker.logClusters()[from]; + fromX = base + walker.xPositions()[glyph]; + fromAdvance = walker.advances()[glyph]; + } else + from -= walker.numCodePoints(); + + if (toX == -1 && to < walker.numCodePoints()) { + int glyph = walker.logClusters()[to]; + toX = base + walker.xPositions()[glyph]; + toAdvance = walker.advances()[glyph]; + } else + to -= walker.numCodePoints(); + + if (!walker.rtl()) + base += walker.width(); + } + + // The position in question might be just after the text. + const int rightEdge = base; + if (fromX == -1 && !from) + fromX = leftEdge; + else if (walker.rtl()) + fromX += truncateFixedPointToInteger(fromAdvance); + + if (toX == -1 && !to) + toX = rightEdge; + else if (!walker.rtl()) + toX += truncateFixedPointToInteger(toAdvance); + + ASSERT(fromX != -1 && toX != -1); + + if (fromX < toX) + return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); + + return FloatRect(point.x() + toX, point.y(), fromX - toX, height); } -} // namespace WebCore +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp index 767fe76..d6c83ec 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp @@ -141,7 +141,7 @@ SCRIPT_FONTPROPERTIES* FontPlatformData::scriptFontProperties() const hr = ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties); if (S_OK != hr) { - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the font properties after second attempt"); } } } @@ -153,4 +153,11 @@ SCRIPT_FONTPROPERTIES* FontPlatformData::scriptFontProperties() const return m_scriptFontProperties; } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + } diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h index ce15a93..25c9cf8 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h +++ b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h @@ -45,6 +45,7 @@ typedef struct HFONT__ *HFONT; namespace WebCore { class FontDescription; +class String; class FontPlatformData { public: @@ -78,6 +79,10 @@ public: return m_font == other.m_font && m_size == other.m_size; } +#ifndef NDEBUG + String description() const; +#endif + SCRIPT_FONTPROPERTIES* scriptFontProperties() const; SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; } diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp index e6a61f6..bf4697f 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp @@ -31,19 +31,45 @@ #include "config.h" #include "FontPlatformData.h" -#include "StringImpl.h" +#include "HarfbuzzSkia.h" #include "NotImplemented.h" +#include "PlatformString.h" +#include "StringImpl.h" #include "SkPaint.h" #include "SkTypeface.h" namespace WebCore { +static SkPaint::Hinting skiaHinting = SkPaint::kNormal_Hinting; +static bool isSkiaAntiAlias = true, isSkiaSubpixelGlyphs; + +void FontPlatformData::setHinting(SkPaint::Hinting hinting) +{ + skiaHinting = hinting; +} + +void FontPlatformData::setAntiAlias(bool isAntiAlias) +{ + isSkiaAntiAlias = isAntiAlias; +} + +void FontPlatformData::setSubpixelGlyphs(bool isSubpixelGlyphs) +{ + isSkiaSubpixelGlyphs = isSubpixelGlyphs; +} + +FontPlatformData::RefCountedHarfbuzzFace::~RefCountedHarfbuzzFace() +{ + HB_FreeFace(m_harfbuzzFace); +} + FontPlatformData::FontPlatformData(const FontPlatformData& src) : m_typeface(src.m_typeface) , m_textSize(src.m_textSize) , m_fakeBold(src.m_fakeBold) , m_fakeItalic(src.m_fakeItalic) + , m_harfbuzzFace(src.m_harfbuzzFace) { m_typeface->safeRef(); } @@ -62,6 +88,7 @@ FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) , m_textSize(textSize) , m_fakeBold(src.m_fakeBold) , m_fakeItalic(src.m_fakeItalic) + , m_harfbuzzFace(src.m_harfbuzzFace) { m_typeface->safeRef(); } @@ -78,21 +105,29 @@ FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) m_textSize = src.m_textSize; m_fakeBold = src.m_fakeBold; m_fakeItalic = src.m_fakeItalic; + m_harfbuzzFace = src.m_harfbuzzFace; return *this; } +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + void FontPlatformData::setupPaint(SkPaint* paint) const { const float ts = m_textSize > 0 ? m_textSize : 12; - paint->setAntiAlias(true); - paint->setSubpixelText(false); + paint->setAntiAlias(isSkiaAntiAlias); + paint->setHinting(skiaHinting); + paint->setLCDRenderText(isSkiaSubpixelGlyphs); paint->setTextSize(SkFloatToScalar(ts)); paint->setTypeface(m_typeface); paint->setFakeBoldText(m_fakeBold); paint->setTextSkewX(m_fakeItalic ? -SK_Scalar1 / 4 : 0); - paint->setTextEncoding(SkPaint::kUTF16_TextEncoding); } SkFontID FontPlatformData::uniqueID() const @@ -141,4 +176,12 @@ bool FontPlatformData::isFixedPitch() const return false; } +HB_FaceRec_* FontPlatformData::harfbuzzFace() const +{ + if (!m_harfbuzzFace) + m_harfbuzzFace = RefCountedHarfbuzzFace::create(HB_NewFace(const_cast<FontPlatformData*>(this), harfbuzzSkiaGetTable)); + + return m_harfbuzzFace->face(); +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h index c63a860..29ce8e7 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h +++ b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h @@ -33,14 +33,17 @@ #include "StringImpl.h" #include <wtf/RefPtr.h> +#include <SkPaint.h> -class SkPaint; class SkTypeface; typedef uint32_t SkFontID; +struct HB_FaceRec_; + namespace WebCore { class FontDescription; +class String; // ----------------------------------------------------------------------------- // FontPlatformData is the handle which WebKit has on a specific face. A face @@ -104,12 +107,45 @@ public: FontPlatformData& operator=(const FontPlatformData&); bool isHashTableDeletedValue() const { return m_typeface == hashTableDeletedFontValue(); } +#ifndef NDEBUG + String description() const; +#endif + + HB_FaceRec_* harfbuzzFace() const; + + // ------------------------------------------------------------------------- + // Global font preferences... + + static void setHinting(SkPaint::Hinting); + static void setAntiAlias(bool on); + static void setSubpixelGlyphs(bool on); + private: + class RefCountedHarfbuzzFace : public RefCounted<RefCountedHarfbuzzFace> { + public: + static PassRefPtr<RefCountedHarfbuzzFace> create(HB_FaceRec_* harfbuzzFace) + { + return adoptRef(new RefCountedHarfbuzzFace(harfbuzzFace)); + } + + ~RefCountedHarfbuzzFace(); + + HB_FaceRec_* face() const { return m_harfbuzzFace; } + + private: + RefCountedHarfbuzzFace(HB_FaceRec_* harfbuzzFace) : m_harfbuzzFace(harfbuzzFace) + { + } + + HB_FaceRec_* m_harfbuzzFace; + }; + // FIXME: Could SkAutoUnref be used here? SkTypeface* m_typeface; float m_textSize; bool m_fakeBold; bool m_fakeItalic; + mutable RefPtr<RefCountedHarfbuzzFace> m_harfbuzzFace; SkTypeface* hashTableDeletedFontValue() const { return reinterpret_cast<SkTypeface*>(-1); } }; diff --git a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp index 2cb1cc5..e2c47c1 100644 --- a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp @@ -87,10 +87,9 @@ static bool fillBMPGlyphs(unsigned offset, return false; } } else { - // FIXME: This should never happen. We want to crash the - // process and receive a crash dump. We should revisit this code later. + // FIXME: Handle gracefully the error if this call also fails. // See http://crbug.com/6401 - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the text metrics after second attempt"); fillEmptyGlyphs(page); return false; } diff --git a/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp b/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp new file mode 100644 index 0000000..621d674 --- /dev/null +++ b/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 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 "FontPlatformData.h" +#include "wtf/OwnArrayPtr.h" + +#include "SkFontHost.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPoint.h" +#include "SkRect.h" + +extern "C" { +#include "harfbuzz-shaper.h" +#include "harfbuzz-unicode.h" +} + +// This file implements the callbacks which Harfbuzz requires by using Skia +// calls. See the Harfbuzz source for references about what these callbacks do. + +namespace WebCore { + +static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value) +{ + // HB_Fixed is a 26.6 fixed point format. + return value * 64; +} + +static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length, HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), reinterpret_cast<uint16_t*>(glyphs)); + + // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our + // |glyphs| array needs to be converted. + // Additionally, if the CSS white-space property is inhibiting line + // breaking, we might find end-of-line charactors rendered via the complex + // text path. Fonts don't provide glyphs for these code points so, if we + // find one, we simulate the space glyph being interposed in this case. + // Because the input is variable-length per code point, we walk the input + // in step with the output. + // FIXME: it seems that this logic is duplicated in CoreTextController and UniscribeController + ssize_t indexOfNextCodePoint = 0; + uint16_t spaceGlyphNumber = 0; + for (int i = numGlyphs - 1; i >= 0; --i) { + const uint32_t currentCodePoint = utf16_to_code_point(characters, length, &indexOfNextCodePoint); + + uint16_t value; + // We use a memcpy to avoid breaking strict aliasing rules. + memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(uint16_t)); + + if (!value && Font::treatAsSpace(currentCodePoint)) { + static const uint16_t spaceUTF16 = ' '; + if (!spaceGlyphNumber) + paint.textToGlyphs(&spaceUTF16, sizeof(spaceUTF16), &spaceGlyphNumber); + value = spaceGlyphNumber; + } + + glyphs[i] = value; + } + + *glyphsSize = numGlyphs; + return 1; +} + +static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs, HB_Fixed* advances, int flags) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + OwnArrayPtr<uint16_t> glyphs16(new uint16_t[numGlyphs]); + if (!glyphs16.get()) + return; + for (unsigned i = 0; i < numGlyphs; ++i) + glyphs16[i] = glyphs[i]; + paint.getTextWidths(glyphs16.get(), numGlyphs * sizeof(uint16_t), reinterpret_cast<SkScalar*>(advances)); + + // The |advances| values which Skia outputs are SkScalars, which are floats + // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format. + // These two formats are both 32-bits long. + for (unsigned i = 0; i < numGlyphs; ++i) { + float value; + // We use a memcpy to avoid breaking strict aliasing rules. + memcpy(&value, reinterpret_cast<char*>(advances) + sizeof(float) * i, sizeof(float)); + advances[i] = SkiaScalarToHarfbuzzFixed(value); + } +} + +static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + OwnArrayPtr<uint16_t> glyphs16(new uint16_t[length]); + if (!glyphs16.get()) + return 0; + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16.get()); + + bool canRender = true; + for (int i = 0; i < numGlyphs; ++i) { + if (!glyphs16[i]) { + canRender = false; + break; + } + } + + return canRender; +} + +static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + if (flags & HB_ShaperFlag_UseDesignMetrics) + return HB_Err_Invalid_Argument; // This is requesting pre-hinted positions. We can't support this. + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + uint16_t glyph16 = glyph; + SkPath path; + paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path); + int numPoints = path.getPoints(NULL, 0); + if (point >= numPoints) + return HB_Err_Invalid_SubTable; + SkPoint* points = reinterpret_cast<SkPoint*>(fastMalloc(sizeof(SkPoint) * (point + 1))); + if (!points) + return HB_Err_Invalid_SubTable; + // Skia does let us get a single point from the path. + path.getPoints(points, point + 1); + *xPos = SkiaScalarToHarfbuzzFixed(points[point].fX); + *yPos = SkiaScalarToHarfbuzzFixed(points[point].fY); + *resultingNumPoints = numPoints; + fastFree(points); + + return HB_Err_Ok; +} + +static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + uint16_t glyph16 = glyph; + SkScalar width; + SkRect bounds; + paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); + + metrics->x = SkiaScalarToHarfbuzzFixed(width); + // We can't actually get the |y| correct because Skia doesn't export + // the vertical advance. However, nor we do ever render vertical text at + // the moment so it's unimportant. + metrics->y = 0; + metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width()); + metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height()); + metrics->xOffset = SkiaScalarToHarfbuzzFixed(bounds.fLeft); + metrics->yOffset = SkiaScalarToHarfbuzzFixed(bounds.fTop); +} + +static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + SkPaint::FontMetrics skiaMetrics; + paint.getFontMetrics(&skiaMetrics); + + switch (metric) { + case HB_FontAscent: + return SkiaScalarToHarfbuzzFixed(-skiaMetrics.fAscent); + // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them. + default: + return 0; + } +} + +HB_FontClass harfbuzzSkiaClass = { + stringToGlyphs, + glyphsToAdvances, + canRender, + getOutlinePoint, + getGlyphMetrics, + getFontMetric, +}; + +HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(voidface); + + const size_t tableSize = SkFontHost::GetTableSize(font->uniqueID(), tag); + if (!tableSize) + return HB_Err_Invalid_Argument; + // If Harfbuzz specified a NULL buffer then it's asking for the size of the table. + if (!buffer) { + *len = tableSize; + return HB_Err_Ok; + } + + if (*len < tableSize) + return HB_Err_Invalid_Argument; + SkFontHost::GetTableData(font->uniqueID(), tag, 0, tableSize, buffer); + return HB_Err_Ok; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/HarfbuzzSkia.h b/WebCore/platform/graphics/chromium/HarfbuzzSkia.h new file mode 100644 index 0000000..f7e0496 --- /dev/null +++ b/WebCore/platform/graphics/chromium/HarfbuzzSkia.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + */ + +#ifndef HarfbuzzSkia_h +#define HarfbuzzSkia_h + +extern "C" { +#include "harfbuzz-shaper.h" +#include "harfbuzz-unicode.h" +} + +namespace WebCore { + HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len); + extern const HB_FontClass harfbuzzSkiaClass; +} // namespace WebCore + +#endif diff --git a/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp b/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp index 6f5ce90..3d68ea8 100644 --- a/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp @@ -63,7 +63,7 @@ void SimpleFontData::platformInit() // FIXME: Handle gracefully the error if this call also fails. // See http://crbug.com/6401. if (!GetTextMetrics(dc, &textMetric)) - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the text metrics after second attempt"); } } @@ -141,7 +141,7 @@ void SimpleFontData::determinePitch() // FIXME: Handle gracefully the error if this call also fails. // See http://crbug.com/6401. if (!GetTextMetrics(dc, &textMetric)) - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the text metrics after second attempt"); } } @@ -163,7 +163,7 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const // FIXME: Handle gracefully the error if this call also fails. // See http://crbug.com/6401. if (!GetCharWidthI(dc, glyph, 1, 0, &width)) - ASSERT_NOT_REACHED(); + LOG_ERROR("Unable to get the char width after second attempt"); } } diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.cpp b/WebCore/platform/graphics/chromium/TransparencyWin.cpp index d3ced81..7957d5a 100644 --- a/WebCore/platform/graphics/chromium/TransparencyWin.cpp +++ b/WebCore/platform/graphics/chromium/TransparencyWin.cpp @@ -109,7 +109,7 @@ class TransparencyWin::OwnedBuffers { public: OwnedBuffers(const IntSize& size, bool needReferenceBuffer) { - m_destBitmap = ImageBuffer::create(size, false); + m_destBitmap = ImageBuffer::create(size); if (needReferenceBuffer) { m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); @@ -152,6 +152,7 @@ TransparencyWin::TransparencyWin() , m_savedOnDrawContext(false) , m_layerBuffer(0) , m_referenceBitmap(0) + , m_validLayer(false) { } @@ -237,11 +238,14 @@ void TransparencyWin::setupLayer() void TransparencyWin::setupLayerForNoLayer() { m_drawContext = m_destContext; // Draw to the source context. + m_validLayer = true; } void TransparencyWin::setupLayerForOpaqueCompositeLayer() { initializeNewContext(); + if (!m_validLayer) + return; TransformationMatrix mapping; mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); @@ -268,6 +272,9 @@ void TransparencyWin::setupLayerForTextComposite() void TransparencyWin::setupLayerForWhiteLayer() { initializeNewContext(); + if (!m_validLayer) + return; + m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white); // Layer rect represents the part of the original layer. } @@ -289,6 +296,9 @@ void TransparencyWin::setupTransform(const IntRect& region) void TransparencyWin::setupTransformForKeepTransform(const IntRect& region) { + if (!m_validLayer) + return; + if (m_layerMode != NoLayer) { // Need to save things since we're modifying the transform. m_drawContext->save(); @@ -319,6 +329,9 @@ void TransparencyWin::setupTransformForUntransform() void TransparencyWin::setupTransformForScaleTransform() { + if (!m_validLayer) + return; + if (m_layerMode == NoLayer) { // Need to save things since we're modifying the layer. m_drawContext->save(); @@ -345,16 +358,22 @@ void TransparencyWin::setTextCompositeColor(Color color) void TransparencyWin::initializeNewContext() { int pixelSize = m_layerSize.width() * m_layerSize.height(); + if (pixelSize <= 0) + return; + if (pixelSize > maxCachedBufferPixelSize) { // Create a 1-off buffer for drawing into. We only need the reference // buffer if we're making an OpaqueCompositeLayer. bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer; m_ownedBuffers.set(new OwnedBuffers(m_layerSize, needReferenceBitmap)); - m_layerBuffer = m_ownedBuffers->destBitmap(); + if (!m_layerBuffer) + return; + m_drawContext = m_layerBuffer->context(); if (needReferenceBitmap) m_referenceBitmap = m_ownedBuffers->referenceBitmap(); + m_validLayer = true; return; } @@ -366,6 +385,7 @@ void TransparencyWin::initializeNewContext() bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0); m_referenceBitmap = m_cachedBuffers->referenceBitmap(); m_referenceBitmap->eraseARGB(0, 0, 0, 0); + m_validLayer = true; return; } @@ -377,10 +397,14 @@ void TransparencyWin::initializeNewContext() m_layerBuffer = m_cachedBuffers->destBitmap(); m_drawContext = m_cachedBuffers->destBitmap()->context(); m_referenceBitmap = m_cachedBuffers->referenceBitmap(); + m_validLayer = true; } void TransparencyWin::compositeOpaqueComposite() { + if (!m_validLayer) + return; + SkCanvas* destCanvas = canvasForContext(*m_destContext); destCanvas->save(); @@ -436,6 +460,9 @@ void TransparencyWin::compositeOpaqueComposite() void TransparencyWin::compositeTextComposite() { + if (!m_validLayer) + return; + const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()->canvas()->getTopPlatformDevice().accessBitmap(true); SkColor textColor = m_textCompositeColor.rgb(); for (int y = 0; y < m_layerSize.height(); y++) { @@ -451,6 +478,7 @@ void TransparencyWin::compositeTextComposite() // Now the layer has text with the proper color and opacity. SkCanvas* destCanvas = canvasForContext(*m_destContext); + destCanvas->save(); // We want to use Untransformed space (see above) SkMatrix identity; @@ -467,6 +495,9 @@ void TransparencyWin::compositeTextComposite() void TransparencyWin::makeLayerOpaque() { + if (!m_validLayer) + return; + SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()-> canvas()->getTopPlatformDevice().accessBitmap(true)); for (int y = 0; y < m_layerSize.height(); y++) { @@ -477,4 +508,3 @@ void TransparencyWin::makeLayerOpaque() } } // namespace WebCore - diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.h b/WebCore/platform/graphics/chromium/TransparencyWin.h index e1963b3..ab75375 100644 --- a/WebCore/platform/graphics/chromium/TransparencyWin.h +++ b/WebCore/platform/graphics/chromium/TransparencyWin.h @@ -135,13 +135,16 @@ public: const IntRect& region); // Combines the source and destination bitmaps using the given mode. + // Calling this function before the destructor runs is mandatory in most + // cases, and harmless otherwise. The mandatory cases are: + // (m_layerMode != NoLayer) || (m_transformMode == ScaleTransform) void composite(); // Returns the context for drawing into, which may be the destination // context, or a temporary one. GraphicsContext* context() const { return m_drawContext; } - PlatformGraphicsContext* platformContext() const { return m_drawContext->platformContext(); } + PlatformGraphicsContext* platformContext() const { return m_drawContext ? m_drawContext->platformContext() : 0; } // When the mode is TextComposite, this sets the color that the text will // get. See the enum above for more. @@ -245,6 +248,12 @@ private: // m_layerBuffer, which will either point to this object, or the statically // cached one. Don't access directly. OwnPtr<OwnedBuffers> m_ownedBuffers; + + // Sometimes we're asked to create layers that have negative dimensions. + // This API is not designed to fail to initialize, so we hide the fact + // that they are illegal and can't be rendered (failing silently, drawing + // nothing). + bool m_validLayer; }; } // namespace WebCore |