diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/skia')
26 files changed, 5857 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h b/Source/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h new file mode 100644 index 0000000..553f203 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2006,2007,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: + * + * * 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 BitmapImageSingleFrameSkia_h +#define BitmapImageSingleFrameSkia_h + +#include "Image.h" +#include "NativeImageSkia.h" + +namespace WebCore { + +// This image class can be used in places which need an Image, but have +// raw pixel data rather than undecoded image data. +// The Image is simpler than a BitmapImage, as it does not have image +// observers, animation, multiple frames, or non-decoded data. +// Therefore trimming the decoded data (destroyDecodedData()) has no effect. +// +// The difficulty with putting this in BitmapImage::create(NativeImagePtr) +// is that NativeImagePtr = NativeImageSkia, yet callers have SkBitmap. +class BitmapImageSingleFrameSkia : public Image { +public: + // Creates a new Image from the given SkBitmap. If "copyPixels" is true, a + // deep copy is done. Otherwise, a shallow copy is done (pixel data is + // ref'ed). + static PassRefPtr<BitmapImageSingleFrameSkia> create(const SkBitmap&, bool copyPixels); + + virtual bool isBitmapImage() const { return true; } + + virtual IntSize size() const + { + return IntSize(m_nativeImage.width(), m_nativeImage.height()); + } + + // Do nothing, as we only have the one representation of data (decoded). + virtual void destroyDecodedData(bool destroyAll = true) { } + + virtual unsigned decodedSize() const + { + return m_nativeImage.decodedSize(); + } + + // We only have a single frame. + virtual NativeImagePtr nativeImageForCurrentFrame() + { + return &m_nativeImage; + } + +protected: + virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator); + +private: + NativeImageSkia m_nativeImage; + + // Creates a new Image from the given SkBitmap, using a shallow copy. + explicit BitmapImageSingleFrameSkia(const SkBitmap&); +}; + +} // namespace WebCore + +#endif // BitmapImageSingleFrameSkia_h diff --git a/Source/WebCore/platform/graphics/skia/FloatPointSkia.cpp b/Source/WebCore/platform/graphics/skia/FloatPointSkia.cpp new file mode 100644 index 0000000..054a772 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/FloatPointSkia.cpp @@ -0,0 +1,51 @@ +/* + * 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: + * + * * 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 "FloatPoint.h" + +#include "SkPoint.h" +#include "SkiaUtils.h" + +namespace WebCore { + +FloatPoint::FloatPoint(const SkPoint& p) + : m_x(p.fX) + , m_y(p.fY) +{ +} + +FloatPoint::operator SkPoint() const +{ + SkPoint p = { WebCoreFloatToSkScalar(m_x), WebCoreFloatToSkScalar(m_y) }; + return p; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/FloatRectSkia.cpp b/Source/WebCore/platform/graphics/skia/FloatRectSkia.cpp new file mode 100644 index 0000000..a10371f --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/FloatRectSkia.cpp @@ -0,0 +1,50 @@ +/* + * 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: + * + * * 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 "FloatRect.h" + +#include "SkRect.h" + +namespace WebCore { + +FloatRect::FloatRect(const SkRect& r) + : m_location(r.fLeft, r.fTop) + , m_size(r.width(), r.height()) +{ +} + +FloatRect::operator SkRect() const +{ + SkRect rect = { x(), y(), right(), bottom() }; + return rect; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp new file mode 100644 index 0000000..e94c417 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2007 Apple Computer, Inc. + * Copyright (c) 2007, 2008, 2009, Google Inc. All rights reserved. + * Copyright (C) 2010 Company 100, Inc. + * + * 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 "FontCustomPlatformData.h" + +#if OS(WINDOWS) +#include "Base64.h" +#include "ChromiumBridge.h" +#include "OpenTypeUtilities.h" +#elif OS(LINUX) || OS(FREEBSD) || PLATFORM(BREWMP) +#include "SkStream.h" +#endif + +#include "FontPlatformData.h" +#include "NotImplemented.h" +#include "OpenTypeSanitizer.h" +#include "SharedBuffer.h" + +#if OS(WINDOWS) +#include <objbase.h> +#elif OS(LINUX) || OS(FREEBSD) || PLATFORM(BREWMP) +#include <cstring> +#endif + +namespace WebCore { + +FontCustomPlatformData::~FontCustomPlatformData() +{ +#if OS(WINDOWS) + if (m_fontReference) + RemoveFontMemResourceEx(m_fontReference); +#elif OS(LINUX) || OS(FREEBSD) || PLATFORM(BREWMP) + if (m_fontReference) + m_fontReference->unref(); +#endif +} + +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation orientation, FontRenderingMode mode) +{ +#if OS(WINDOWS) + ASSERT(m_fontReference); + + LOGFONT logFont; + // m_name comes from createUniqueFontName, which, in turn, gets + // it from base64-encoded uuid (128-bit). So, m_name + // can never be longer than LF_FACESIZE (32). + if (m_name.length() + 1 >= LF_FACESIZE) { + ASSERT_NOT_REACHED(); + return FontPlatformData(); + } + memcpy(logFont.lfFaceName, m_name.charactersWithNullTermination(), + sizeof(logFont.lfFaceName[0]) * (1 + m_name.length())); + + // FIXME: almost identical to FillLogFont in FontCacheWin.cpp. + // Need to refactor. + logFont.lfHeight = -size; + logFont.lfWidth = 0; + logFont.lfEscapement = 0; + logFont.lfOrientation = 0; + logFont.lfUnderline = false; + logFont.lfStrikeOut = false; + logFont.lfCharSet = DEFAULT_CHARSET; + logFont.lfOutPrecision = OUT_TT_ONLY_PRECIS; + logFont.lfQuality = ChromiumBridge::layoutTestMode() ? + NONANTIALIASED_QUALITY : + DEFAULT_QUALITY; // Honor user's desktop settings. + logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + logFont.lfItalic = italic; + logFont.lfWeight = bold ? 700 : 400; + + HFONT hfont = CreateFontIndirect(&logFont); + return FontPlatformData(hfont, size); +#elif OS(LINUX) || OS(FREEBSD) || PLATFORM(BREWMP) + ASSERT(m_fontReference); + return FontPlatformData(m_fontReference, "", size, bold && !m_fontReference->isBold(), italic && !m_fontReference->isItalic(), orientation); +#else + notImplemented(); + return FontPlatformData(); +#endif +} + +#if OS(WINDOWS) +// Creates a unique and unpredictable font name, in order to avoid collisions and to +// not allow access from CSS. +static String createUniqueFontName() +{ + Vector<char> fontUuid(sizeof(GUID)); + CoCreateGuid(reinterpret_cast<GUID*>(fontUuid.data())); + + Vector<char> fontNameVector; + base64Encode(fontUuid, fontNameVector); + ASSERT(fontNameVector.size() < LF_FACESIZE); + return String(fontNameVector.data(), fontNameVector.size()); +} +#endif + +#if OS(LINUX) || OS(FREEBSD) || PLATFORM(BREWMP) +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(); + } + // This is a request to read bytes or skip bytes (when buffer is 0). + if (!m_buffer->data() || !m_buffer->size()) + return 0; + size_t left = m_buffer->size() - m_offset; + size_t bytesToConsume = std::min(left, size); + if (buffer) + std::memcpy(buffer, m_buffer->data() + m_offset, bytesToConsume); + m_offset += bytesToConsume; + return bytesToConsume; + } + +private: + RefPtr<SharedBuffer> m_buffer; + size_t m_offset; +}; +#endif + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) +{ + ASSERT_ARG(buffer, buffer); + +#if ENABLE(OPENTYPE_SANITIZER) + OpenTypeSanitizer sanitizer(buffer); + RefPtr<SharedBuffer> transcodeBuffer = sanitizer.sanitize(); + if (!transcodeBuffer) + return 0; // validation failed. + buffer = transcodeBuffer.get(); +#endif + +#if OS(WINDOWS) + // Introduce the font to GDI. AddFontMemResourceEx should be used with care, because it will pollute the process's + // font namespace (Windows has no API for creating an HFONT from data without exposing the font to the + // entire process first). + String fontName = createUniqueFontName(); + HANDLE fontReference = renameAndActivateFont(buffer, fontName); + if (!fontReference) + return 0; + return new FontCustomPlatformData(fontReference, fontName); +#elif OS(LINUX) || OS(FREEBSD) || PLATFORM(BREWMP) + RemoteFontStream* stream = new RemoteFontStream(buffer); + SkTypeface* typeface = SkTypeface::CreateFromStream(stream); + if (!typeface) + return 0; + return new FontCustomPlatformData(typeface); +#else + notImplemented(); + return 0; +#endif +} + +bool FontCustomPlatformData::supportsFormat(const String& format) +{ + return equalIgnoringCase(format, "truetype") || equalIgnoringCase(format, "opentype") +#if ENABLE(OPENTYPE_SANITIZER) + || equalIgnoringCase(format, "woff") +#endif + ; +} + +} diff --git a/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.h new file mode 100644 index 0000000..e51b6b6 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2007 Apple Computer, Inc. + * Copyright (c) 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. + */ + +#ifndef FontCustomPlatformData_h +#define FontCustomPlatformData_h + +#include "FontOrientation.h" +#include "FontRenderingMode.h" +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> + +#if OS(WINDOWS) +#include "PlatformString.h" +#include <windows.h> +#elif OS(LINUX) || OS(FREEBSD) || PLATFORM(BREWMP) +#include "SkTypeface.h" +#endif + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformData : Noncopyable { +#if OS(WINDOWS) + FontCustomPlatformData(HANDLE fontReference, const String& name) + : m_fontReference(fontReference) + , m_name(name) + {} +#elif OS(LINUX) || OS(FREEBSD) || PLATFORM(BREWMP) + explicit FontCustomPlatformData(SkTypeface* typeface) + : m_fontReference(typeface) + {} +#endif + + ~FontCustomPlatformData(); + + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, + FontRenderingMode = NormalRenderingMode); + + static bool supportsFormat(const String&); + +#if OS(WINDOWS) + HANDLE m_fontReference; + String m_name; +#elif OS(LINUX) || OS(FREEBSD) || PLATFORM(BREWMP) + SkTypeface* m_fontReference; +#endif +}; + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); +} + +#endif // FontCustomPlatformData_h diff --git a/Source/WebCore/platform/graphics/skia/GlyphPageTreeNodeSkia.cpp b/Source/WebCore/platform/graphics/skia/GlyphPageTreeNodeSkia.cpp new file mode 100644 index 0000000..66e6839 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/GlyphPageTreeNodeSkia.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 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 "GlyphPageTreeNode.h" + +#include "Font.h" +#include "HarfbuzzSkia.h" +#include "SimpleFontData.h" + +#include "SkTemplates.h" +#include "SkPaint.h" +#include "SkUtils.h" + +namespace WebCore { + +static int substituteWithVerticalGlyphs(const SimpleFontData* fontData, uint16_t* glyphs, unsigned bufferLength) +{ + HB_FaceRec_* hbFace = fontData->platformData().harfbuzzFace(); + if (!hbFace->gsub) { + // if there is no GSUB table, treat it as not covered + return 0Xffff; + } + + HB_Buffer buffer; + hb_buffer_new(&buffer); + for (unsigned i = 0; i < bufferLength; ++i) + hb_buffer_add_glyph(buffer, glyphs[i], 0, i); + + HB_UShort scriptIndex; + HB_UShort featureIndex; + + HB_GSUB_Select_Script(hbFace->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &scriptIndex); + HB_GSUB_Select_Feature(hbFace->gsub, HB_MAKE_TAG('v', 'e', 'r', 't'), scriptIndex, 0xffff, &featureIndex); + HB_GSUB_Add_Feature(hbFace->gsub, featureIndex, 1); + HB_GSUB_Select_Feature(hbFace->gsub, HB_MAKE_TAG('v', 'r', 't', '2'), scriptIndex, 0xffff, &featureIndex); + HB_GSUB_Add_Feature(hbFace->gsub, featureIndex, 1); + + int error = HB_GSUB_Apply_String(hbFace->gsub, buffer); + if (!error) { + for (unsigned i = 0; i < bufferLength; ++i) + glyphs[i] = static_cast<Glyph>(buffer->out_string[i].gindex); + } + return error; +} + +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +{ + if (SkUTF16_IsHighSurrogate(buffer[bufferLength-1])) { + SkDebugf("%s last char is high-surrogate", __FUNCTION__); + return false; + } + + SkPaint paint; + fontData->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + SkAutoSTMalloc <GlyphPage::size, uint16_t> glyphStorage(length); + uint16_t* glyphs = glyphStorage.get(); + // textToGlyphs takes a byte count, not a glyph count so we multiply by two. + unsigned count = paint.textToGlyphs(buffer, bufferLength * 2, glyphs); + if (count != length) { + SkDebugf("%s count != length\n", __FUNCTION__); + return false; + } + + if ((fontData->orientation() == Vertical) && (!fontData->isBrokenIdeographFont())) { + bool lookVariants = false; + for (unsigned i = 0; i < bufferLength; ++i) { + if (!Font::isCJKIdeograph(buffer[i])) { + lookVariants = true; + continue; + } + } + if (lookVariants) + substituteWithVerticalGlyphs(fontData, glyphs, bufferLength); + } + + unsigned allGlyphs = 0; // track if any of the glyphIDs are non-zero + for (unsigned i = 0; i < length; i++) { + setGlyphDataForIndex(offset + i, glyphs[i], glyphs[i] ? fontData : NULL); + allGlyphs |= glyphs[i]; + } + + return allGlyphs != 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/GradientSkia.cpp b/Source/WebCore/platform/graphics/skia/GradientSkia.cpp new file mode 100644 index 0000000..a636d10 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/GradientSkia.cpp @@ -0,0 +1,196 @@ +/* + * 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: + * + * * 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 "Gradient.h" + +#include "CSSParser.h" +#include "GraphicsContext.h" + +#include "SkGradientShader.h" +#include "SkiaUtils.h" + +namespace WebCore { + +void Gradient::platformDestroy() +{ + if (m_gradient) + SkSafeUnref(m_gradient); + m_gradient = 0; +} + +static inline U8CPU F2B(float x) +{ + return static_cast<int>(x * 255); +} + +static SkColor makeSkColor(float a, float r, float g, float b) +{ + return SkColorSetARGB(F2B(a), F2B(r), F2B(g), F2B(b)); +} + +// Determine the total number of stops needed, including pseudo-stops at the +// ends as necessary. +static size_t totalStopsNeeded(const Gradient::ColorStop* stopData, size_t count) +{ + // N.B.: The tests in this function should kept in sync with the ones in + // fillStops(), or badness happens. + const Gradient::ColorStop* stop = stopData; + size_t countUsed = count; + if (count < 1 || stop->stop > 0.0) + countUsed++; + stop += count - 1; + if (count < 1 || stop->stop < 1.0) + countUsed++; + return countUsed; +} + +// Collect sorted stop position and color information into the pos and colors +// buffers, ensuring stops at both 0.0 and 1.0. The buffers must be large +// enough to hold information for all stops, including the new endpoints if +// stops at 0.0 and 1.0 aren't already included. +static void fillStops(const Gradient::ColorStop* stopData, + size_t count, SkScalar* pos, SkColor* colors) +{ + const Gradient::ColorStop* stop = stopData; + size_t start = 0; + if (count < 1) { + // A gradient with no stops must be transparent black. + pos[0] = WebCoreFloatToSkScalar(0.0); + colors[0] = makeSkColor(0.0, 0.0, 0.0, 0.0); + start = 1; + } else if (stop->stop > 0.0) { + // Copy the first stop to 0.0. The first stop position may have a slight + // rounding error, but we don't care in this float comparison, since + // 0.0 comes through cleanly and people aren't likely to want a gradient + // with a stop at (0 + epsilon). + pos[0] = WebCoreFloatToSkScalar(0.0); + colors[0] = makeSkColor(stop->alpha, stop->red, stop->green, stop->blue); + start = 1; + } + + for (size_t i = start; i < start + count; i++) { + pos[i] = WebCoreFloatToSkScalar(stop->stop); + colors[i] = makeSkColor(stop->alpha, stop->red, stop->green, stop->blue); + ++stop; + } + + // Copy the last stop to 1.0 if needed. See comment above about this float + // comparison. + if (count < 1 || (--stop)->stop < 1.0) { + pos[start + count] = WebCoreFloatToSkScalar(1.0); + colors[start + count] = colors[start + count - 1]; + } +} + +static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::ColorStop& b) +{ + return a.stop < b.stop; +} + +SkShader* Gradient::platformGradient() +{ + if (m_gradient) + return m_gradient; + + // FIXME: This and compareStops() are also in Gradient.cpp and + // CSSGradientValue.cpp; probably should refactor in WebKit. + if (!m_stopsSorted) { + if (m_stops.size()) + std::stable_sort(m_stops.begin(), m_stops.end(), compareStops); + m_stopsSorted = true; + } + size_t countUsed = totalStopsNeeded(m_stops.data(), m_stops.size()); + ASSERT(countUsed >= 2); + ASSERT(countUsed >= m_stops.size()); + + // FIXME: Why is all this manual pointer math needed?! + SkAutoMalloc storage(countUsed * (sizeof(SkColor) + sizeof(SkScalar))); + SkColor* colors = (SkColor*)storage.get(); + SkScalar* pos = (SkScalar*)(colors + countUsed); + + fillStops(m_stops.data(), m_stops.size(), pos, colors); + + SkShader::TileMode tile = SkShader::kClamp_TileMode; + switch (m_spreadMethod) { + case SpreadMethodReflect: + tile = SkShader::kMirror_TileMode; + break; + case SpreadMethodRepeat: + tile = SkShader::kRepeat_TileMode; + break; + case SpreadMethodPad: + tile = SkShader::kClamp_TileMode; + break; + } + + if (m_radial) { + // Since the two-point radial gradient is slower than the plain radial, + // only use it if we have to. + if (m_p0 == m_p1 && m_r0 <= 0.0f) { + // The radius we give to Skia must be positive (and non-zero). If + // we're given a zero radius, just ask for a very small radius so + // Skia will still return an object. + SkScalar radius = m_r1 > 0 ? WebCoreFloatToSkScalar(m_r1) : SK_ScalarMin; + m_gradient = SkGradientShader::CreateRadial(m_p1, radius, colors, pos, static_cast<int>(countUsed), tile); + } else { + // The radii we give to Skia must be positive. If we're given a + // negative radius, ask for zero instead. + SkScalar radius0 = m_r0 >= 0.0f ? WebCoreFloatToSkScalar(m_r0) : 0; + SkScalar radius1 = m_r1 >= 0.0f ? WebCoreFloatToSkScalar(m_r1) : 0; + m_gradient = SkGradientShader::CreateTwoPointRadial(m_p0, radius0, m_p1, radius1, colors, pos, static_cast<int>(countUsed), tile); + } + } else { + SkPoint pts[2] = { m_p0, m_p1 }; + m_gradient = SkGradientShader::CreateLinear(pts, colors, pos, + static_cast<int>(countUsed), tile); + } + + ASSERT(m_gradient); + + SkMatrix matrix = m_gradientSpaceTransformation; + m_gradient->setLocalMatrix(matrix); + + return m_gradient; +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + context->setFillGradient(this); + context->fillRect(rect); +} + +void Gradient::setPlatformGradientSpaceTransform(const AffineTransform& matrix) +{ + if (m_gradient) + m_gradient->setLocalMatrix(m_gradientSpaceTransformation); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp b/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp new file mode 100644 index 0000000..c4b753b --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 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" + +#if ENABLE(3D_CANVAS) + +#include "GraphicsContext3D.h" + +#include "Image.h" +#include "ImageSource.h" +#include "NativeImageSkia.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +#include <algorithm> + +namespace WebCore { + +bool GraphicsContext3D::getImageData(Image* image, + GC3Denum format, + GC3Denum type, + bool premultiplyAlpha, + bool ignoreGammaAndColorProfile, + Vector<uint8_t>& outputVector) +{ + if (!image) + return false; + OwnPtr<NativeImageSkia> pixels; + NativeImageSkia* skiaImage = 0; + AlphaOp neededAlphaOp = AlphaDoNothing; + if (image->data()) { + ImageSource decoder(ImageSource::AlphaNotPremultiplied, + ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied); + decoder.setData(image->data(), true); + if (!decoder.frameCount() || !decoder.frameIsCompleteAtIndex(0)) + return false; + bool hasAlpha = decoder.frameHasAlphaAtIndex(0); + pixels = adoptPtr(decoder.createFrameAtIndex(0)); + if (!pixels.get() || !pixels->isDataComplete() || !pixels->width() || !pixels->height()) + return false; + SkBitmap::Config skiaConfig = pixels->config(); + if (skiaConfig != SkBitmap::kARGB_8888_Config) + return false; + skiaImage = pixels.get(); + if (hasAlpha && premultiplyAlpha) + neededAlphaOp = AlphaDoPremultiply; + } else { + // This is a special case for texImage2D with HTMLCanvasElement input. + skiaImage = image->nativeImageForCurrentFrame(); + if (!premultiplyAlpha) + neededAlphaOp = AlphaDoUnmultiply; + } + if (!skiaImage) + return false; + SkBitmap& skiaImageRef = *skiaImage; + SkAutoLockPixels lock(skiaImageRef); + ASSERT(skiaImage->rowBytes() == skiaImage->width() * 4); + outputVector.resize(skiaImage->rowBytes() * skiaImage->height()); + return packPixels(reinterpret_cast<const uint8_t*>(skiaImage->getPixels()), + SourceFormatBGRA8, skiaImage->width(), skiaImage->height(), 0, + format, type, neededAlphaOp, outputVector.data()); +} + +} // namespace WebCore + +#endif // ENABLE(3D_CANVAS) diff --git a/Source/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h b/Source/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h new file mode 100644 index 0000000..5e12ad6 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h @@ -0,0 +1,55 @@ +/* + * 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: + * + * * 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 GraphicsContextPlatformPrivate_h +#define GraphicsContextPlatformPrivate_h + +#include <wtf/Noncopyable.h> + +class PlatformContextSkia; + +namespace WebCore { + +// This class just holds onto a PlatformContextSkia for GraphicsContext. +class GraphicsContextPlatformPrivate : public Noncopyable { +public: + GraphicsContextPlatformPrivate(PlatformContextSkia* platformContext) + : m_context(platformContext) { } + + PlatformContextSkia* context() { return m_context; } + +private: + // Non-owning pointer to the PlatformContext. + PlatformContextSkia* m_context; +}; + +} // namespace WebCore + +#endif // GraphicsContextPlatformPrivate_h diff --git a/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp new file mode 100644 index 0000000..51e2477 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -0,0 +1,1262 @@ +/* + * Copyright (c) 2006, 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 "GraphicsContext.h" + +#include "AffineTransform.h" +#include "Color.h" +#include "FloatRect.h" +#include "GLES2Canvas.h" +#include "Gradient.h" +#include "GraphicsContextPlatformPrivate.h" +#include "ImageBuffer.h" +#include "IntRect.h" +#include "NativeImageSkia.h" +#include "NotImplemented.h" +#include "PlatformContextSkia.h" + +#include "SkBitmap.h" +#include "SkBlurDrawLooper.h" +#include "SkCornerPathEffect.h" +#include "SkShader.h" +#include "SkiaUtils.h" +#include "skia/ext/platform_canvas.h" + +#include <math.h> +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/UnusedParam.h> + +using namespace std; + +namespace WebCore { + +namespace { + +inline int fastMod(int value, int max) +{ + int sign = SkExtractSign(value); + + value = SkApplySign(value, sign); + if (value >= max) + value %= max; + return SkApplySign(value, sign); +} + +inline float square(float n) +{ + return n * n; +} + +} // namespace + +// "Seatbelt" functions ------------------------------------------------------ +// +// These functions check certain graphics primitives for being "safe". +// Skia has historically crashed when sent crazy data. These functions do +// additional checking to prevent crashes. +// +// Ideally, all of these would be fixed in the graphics layer and we would not +// have to do any checking. You can uncomment the ENSURE_VALUE_SAFETY_FOR_SKIA +// flag to check the graphics layer. + +// Disabling these checks (20/01/2010), since we think we've fixed all the Skia +// bugs. Leaving the code in for now, so we can revert easily if necessary. +// #define ENSURE_VALUE_SAFETY_FOR_SKIA + +#ifdef ENSURE_VALUE_SAFETY_FOR_SKIA +static bool isCoordinateSkiaSafe(float coord) +{ + // First check for valid floats. +#if defined(_MSC_VER) + if (!_finite(coord)) +#else + if (!finite(coord)) +#endif + return false; + + // Skia uses 16.16 fixed point and 26.6 fixed point in various places. If + // the transformed point exceeds 15 bits, we just declare that it's + // unreasonable to catch both of these cases. + static const int maxPointMagnitude = 32767; + if (coord > maxPointMagnitude || coord < -maxPointMagnitude) + return false; + + return true; +} +#endif + +static bool isPointSkiaSafe(const SkMatrix& transform, const SkPoint& pt) +{ +#ifdef ENSURE_VALUE_SAFETY_FOR_SKIA + // Now check for points that will overflow. We check the *transformed* + // points since this is what will be rasterized. + SkPoint xPt; + transform.mapPoints(&xPt, &pt, 1); + return isCoordinateSkiaSafe(xPt.fX) && isCoordinateSkiaSafe(xPt.fY); +#else + return true; +#endif +} + +static bool isRectSkiaSafe(const SkMatrix& transform, const SkRect& rc) +{ +#ifdef ENSURE_VALUE_SAFETY_FOR_SKIA + SkPoint topleft = {rc.fLeft, rc.fTop}; + SkPoint bottomright = {rc.fRight, rc.fBottom}; + return isPointSkiaSafe(transform, topleft) && isPointSkiaSafe(transform, bottomright); +#else + return true; +#endif +} + +bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path) +{ +#ifdef ENSURE_VALUE_SAFETY_FOR_SKIA + SkPoint current_points[4]; + SkPath::Iter iter(path, false); + for (SkPath::Verb verb = iter.next(current_points); + verb != SkPath::kDone_Verb; + verb = iter.next(current_points)) { + switch (verb) { + case SkPath::kMove_Verb: + // This move will be duplicated in the next verb, so we can ignore. + break; + case SkPath::kLine_Verb: + // iter.next returns 2 points. + if (!isPointSkiaSafe(transform, current_points[0]) + || !isPointSkiaSafe(transform, current_points[1])) + return false; + break; + case SkPath::kQuad_Verb: + // iter.next returns 3 points. + if (!isPointSkiaSafe(transform, current_points[0]) + || !isPointSkiaSafe(transform, current_points[1]) + || !isPointSkiaSafe(transform, current_points[2])) + return false; + break; + case SkPath::kCubic_Verb: + // iter.next returns 4 points. + if (!isPointSkiaSafe(transform, current_points[0]) + || !isPointSkiaSafe(transform, current_points[1]) + || !isPointSkiaSafe(transform, current_points[2]) + || !isPointSkiaSafe(transform, current_points[3])) + return false; + break; + case SkPath::kClose_Verb: + case SkPath::kDone_Verb: + default: + break; + } + } + return true; +#else + return true; +#endif +} + +// Local helper functions ------------------------------------------------------ + +void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int startAngle) +{ + SkIRect ir; + int rx = SkMin32(SkScalarRound(rect.width()), size.width()); + int ry = SkMin32(SkScalarRound(rect.height()), size.height()); + + ir.set(-rx, -ry, rx, ry); + switch (startAngle) { + case 0: + ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom); + break; + case 90: + ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom); + break; + case 180: + ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop); + break; + case 270: + ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop); + break; + default: + ASSERT(0); + } + + SkRect r; + r.set(ir); + path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false); +} + +// ----------------------------------------------------------------------------- + +// This may be called with a NULL pointer to create a graphics context that has +// no painting. +void GraphicsContext::platformInit(PlatformGraphicsContext* gc) +{ + m_data = new GraphicsContextPlatformPrivate(gc); + setPaintingDisabled(!gc || !platformContext()->canvas()); +} + +void GraphicsContext::platformDestroy() +{ + delete m_data; +} + +PlatformGraphicsContext* GraphicsContext::platformContext() const +{ + ASSERT(!paintingDisabled()); + return m_data->context(); +} + +// State saving ---------------------------------------------------------------- + +void GraphicsContext::savePlatformState() +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU()) + platformContext()->gpuCanvas()->save(); + + // Save our private State. + platformContext()->save(); +} + +void GraphicsContext::restorePlatformState() +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU()) + platformContext()->gpuCanvas()->restore(); + + // Restore our private State. + platformContext()->restore(); +} + +void GraphicsContext::beginTransparencyLayer(float opacity) +{ + if (paintingDisabled()) + return; + + // We need the "alpha" layer flag here because the base layer is opaque + // (the surface of the page) but layers on top may have transparent parts. + // Without explicitly setting the alpha flag, the layer will inherit the + // opaque setting of the base and some things won't work properly. + platformContext()->canvas()->saveLayerAlpha( + 0, + static_cast<unsigned char>(opacity * 255), + static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | + SkCanvas::kFullColorLayer_SaveFlag)); +} + +void GraphicsContext::endTransparencyLayer() +{ + if (paintingDisabled()) + return; + platformContext()->canvas()->restore(); +} + +// Graphics primitives --------------------------------------------------------- + +void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) +{ + if (paintingDisabled()) + return; + + SkRect r(rect); + if (!isRectSkiaSafe(getCTM(), r)) + return; + + platformContext()->prepareForSoftwareDraw(); + SkPath path; + path.addOval(r, SkPath::kCW_Direction); + // only perform the inset if we won't invert r + if (2 * thickness < rect.width() && 2 * thickness < rect.height()) { + // Adding one to the thickness doesn't make the border too thick as + // it's painted over afterwards. But without this adjustment the + // border appears a little anemic after anti-aliasing. + r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1)); + path.addOval(r, SkPath::kCCW_Direction); + } + platformContext()->clipPathAntiAliased(path); +} + +void GraphicsContext::addPath(const Path& path) +{ + if (paintingDisabled()) + return; + platformContext()->addPath(*path.platformPath()); +} + +void GraphicsContext::beginPath() +{ + if (paintingDisabled()) + return; + platformContext()->beginPath(); +} + +void GraphicsContext::clearPlatformShadow() +{ + if (paintingDisabled()) + return; + platformContext()->setDrawLooper(0); +} + +void GraphicsContext::clearRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU() && !platformContext()->canvasClipApplied()) { + platformContext()->prepareForHardwareDraw(); + platformContext()->gpuCanvas()->clearRect(rect); + return; + } + + // Force a readback here (if we're using the GPU), since clearRect() is + // incompatible with mixed-mode rendering. + platformContext()->syncSoftwareCanvas(); + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + paint.setXfermodeMode(SkXfermode::kClear_Mode); + platformContext()->canvas()->drawRect(r, paint); +} + +void GraphicsContext::clip(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r(rect); + if (!isRectSkiaSafe(getCTM(), r)) + return; + + platformContext()->prepareForSoftwareDraw(); + platformContext()->canvas()->clipRect(r); +} + +void GraphicsContext::clip(const Path& path) +{ + if (paintingDisabled()) + return; + + const SkPath& p = *path.platformPath(); + if (!isPathSkiaSafe(getCTM(), p)) + return; + + platformContext()->prepareForSoftwareDraw(); + platformContext()->clipPathAntiAliased(p); +} + +void GraphicsContext::canvasClip(const Path& path) +{ + if (paintingDisabled()) + return; + + const SkPath& p = *path.platformPath(); + if (!isPathSkiaSafe(getCTM(), p)) + return; + + platformContext()->canvasClipPath(p); +} + +void GraphicsContext::clipOut(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r(rect); + if (!isRectSkiaSafe(getCTM(), r)) + return; + + platformContext()->canvas()->clipRect(r, SkRegion::kDifference_Op); +} + +void GraphicsContext::clipOut(const Path& p) +{ + if (paintingDisabled()) + return; + + const SkPath& path = *p.platformPath(); + if (!isPathSkiaSafe(getCTM(), path)) + return; + + platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op); +} + +void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) +{ + if (paintingDisabled()) + return; + + // FIXME: Be smarter about this. + beginPath(); + addPath(pathToClip); + + SkPath path = platformContext()->currentPathInLocalCoordinates(); + if (!isPathSkiaSafe(getCTM(), path)) + return; + + path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); + platformContext()->clipPathAntiAliased(path); +} + +void GraphicsContext::concatCTM(const AffineTransform& affine) +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU()) + platformContext()->gpuCanvas()->concatCTM(affine); + + platformContext()->canvas()->concat(affine); +} + +void GraphicsContext::drawConvexPolygon(size_t numPoints, + const FloatPoint* points, + bool shouldAntialias) +{ + if (paintingDisabled()) + return; + + if (numPoints <= 1) + return; + + platformContext()->prepareForSoftwareDraw(); + + SkPath path; + + path.incReserve(numPoints); + path.moveTo(WebCoreFloatToSkScalar(points[0].x()), + WebCoreFloatToSkScalar(points[0].y())); + for (size_t i = 1; i < numPoints; i++) { + path.lineTo(WebCoreFloatToSkScalar(points[i].x()), + WebCoreFloatToSkScalar(points[i].y())); + } + + if (!isPathSkiaSafe(getCTM(), path)) + return; + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + platformContext()->canvas()->drawPath(path, paint); + + if (strokeStyle() != NoStroke) { + paint.reset(); + platformContext()->setupPaintForStroking(&paint, 0, 0); + platformContext()->canvas()->drawPath(path, paint); + } +} + +void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) +{ + if (paintingDisabled()) + return; + + if (numPoints <= 1) + return; + + // FIXME: IMPLEMENT!! +} + +// This method is only used to draw the little circles used in lists. +void GraphicsContext::drawEllipse(const IntRect& elipseRect) +{ + if (paintingDisabled()) + return; + + SkRect rect = elipseRect; + if (!isRectSkiaSafe(getCTM(), rect)) + return; + + platformContext()->prepareForSoftwareDraw(); + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + platformContext()->canvas()->drawOval(rect, paint); + + if (strokeStyle() != NoStroke) { + paint.reset(); + platformContext()->setupPaintForStroking(&paint, &rect, 0); + platformContext()->canvas()->drawOval(rect, paint); + } +} + +void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color) +{ + // FIXME: implement +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color) +{ + if (paintingDisabled()) + return; + + unsigned rectCount = rects.size(); + if (!rectCount) + return; + + platformContext()->prepareForSoftwareDraw(); + SkRegion focusRingRegion; + const SkScalar focusRingOutset = WebCoreFloatToSkScalar(0.5); + for (unsigned i = 0; i < rectCount; i++) { + SkIRect r = rects[i]; + r.inset(-focusRingOutset, -focusRingOutset); + focusRingRegion.op(r, SkRegion::kUnion_Op); + } + + SkPath path; + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + + paint.setColor(color.rgb()); + paint.setStrokeWidth(focusRingOutset * 2); + paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref(); + focusRingRegion.getBoundaryPath(&path); + platformContext()->canvas()->drawPath(path, paint); +} + +// This is only used to draw borders. +void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) +{ + if (paintingDisabled()) + return; + + StrokeStyle penStyle = strokeStyle(); + if (penStyle == NoStroke) + return; + + SkPaint paint; + if (!isPointSkiaSafe(getCTM(), point1) || !isPointSkiaSafe(getCTM(), point2)) + return; + + platformContext()->prepareForSoftwareDraw(); + + FloatPoint p1 = point1; + FloatPoint p2 = point2; + bool isVerticalLine = (p1.x() == p2.x()); + int width = roundf(strokeThickness()); + + // We know these are vertical or horizontal lines, so the length will just + // be the sum of the displacement component vectors give or take 1 - + // probably worth the speed up of no square root, which also won't be exact. + FloatSize disp = p2 - p1; + int length = SkScalarRound(disp.width() + disp.height()); + platformContext()->setupPaintForStroking(&paint, 0, length); + + if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { + // Do a rect fill of our endpoints. This ensures we always have the + // appearance of being a border. We then draw the actual dotted/dashed line. + + SkRect r1, r2; + r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width); + r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width); + + if (isVerticalLine) { + r1.offset(-width / 2, 0); + r2.offset(-width / 2, -width); + } else { + r1.offset(0, -width / 2); + r2.offset(-width, -width / 2); + } + SkPaint fillPaint; + fillPaint.setColor(paint.getColor()); + platformContext()->canvas()->drawRect(r1, fillPaint); + platformContext()->canvas()->drawRect(r2, fillPaint); + } + + adjustLineToPixelBoundaries(p1, p2, width, penStyle); + SkPoint pts[2] = { (SkPoint)p1, (SkPoint)p2 }; + + platformContext()->canvas()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint); +} + +void GraphicsContext::drawLineForTextChecking(const IntPoint& pt, int width, TextCheckingLineStyle style) +{ + if (paintingDisabled()) + return; + + platformContext()->prepareForSoftwareDraw(); + + // Create the pattern we'll use to draw the underline. + static SkBitmap* misspellBitmap = 0; + if (!misspellBitmap) { + // We use a 2-pixel-high misspelling indicator because that seems to be + // what WebKit is designed for, and how much room there is in a typical + // page for it. + const int rowPixels = 32; // Must be multiple of 4 for pattern below. + const int colPixels = 2; + misspellBitmap = new SkBitmap; + misspellBitmap->setConfig(SkBitmap::kARGB_8888_Config, + rowPixels, colPixels); + misspellBitmap->allocPixels(); + + misspellBitmap->eraseARGB(0, 0, 0, 0); + const uint32_t lineColor = 0xFFFF0000; // Opaque red. + const uint32_t antiColor = 0x60600000; // Semitransparent red. + + // Pattern: X o o X o o X + // o X o o X o + uint32_t* row1 = misspellBitmap->getAddr32(0, 0); + uint32_t* row2 = misspellBitmap->getAddr32(0, 1); + for (int x = 0; x < rowPixels; x++) { + switch (x % 4) { + case 0: + row1[x] = lineColor; + break; + case 1: + row1[x] = antiColor; + row2[x] = antiColor; + break; + case 2: + row2[x] = lineColor; + break; + case 3: + row1[x] = antiColor; + row2[x] = antiColor; + break; + } + } + } + + // Offset it vertically by 1 so that there's some space under the text. + SkScalar originX = SkIntToScalar(pt.x()); + SkScalar originY = SkIntToScalar(pt.y()) + 1; + + // Make a shader for the bitmap with an origin of the box we'll draw. This + // shader is refcounted and will have an initial refcount of 1. + SkShader* shader = SkShader::CreateBitmapShader( + *misspellBitmap, SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + SkMatrix matrix; + matrix.reset(); + matrix.postTranslate(originX, originY); + shader->setLocalMatrix(matrix); + + // Assign the shader to the paint & release our reference. The paint will + // now own the shader and the shader will be destroyed when the paint goes + // out of scope. + SkPaint paint; + paint.setShader(shader); + shader->unref(); + + SkRect rect; + rect.set(originX, + originY, + originX + SkIntToScalar(width), + originY + SkIntToScalar(misspellBitmap->height())); + platformContext()->canvas()->drawRect(rect, paint); +} + +void GraphicsContext::drawLineForText(const IntPoint& pt, + int width, + bool printing) +{ + if (paintingDisabled()) + return; + + if (width <= 0) + return; + + platformContext()->prepareForSoftwareDraw(); + + int thickness = SkMax32(static_cast<int>(strokeThickness()), 1); + SkRect r; + r.fLeft = SkIntToScalar(pt.x()); + r.fTop = SkIntToScalar(pt.y()); + r.fRight = r.fLeft + SkIntToScalar(width); + r.fBottom = r.fTop + SkIntToScalar(thickness); + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + // Text lines are drawn using the stroke color. + paint.setColor(platformContext()->effectiveStrokeColor()); + platformContext()->canvas()->drawRect(r, paint); +} + +// Draws a filled rectangle with a stroked border. +void GraphicsContext::drawRect(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + platformContext()->prepareForSoftwareDraw(); + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) { + // See the fillRect below. + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } + + platformContext()->drawRect(r); +} + +void GraphicsContext::fillPath(const Path& pathToFill) +{ + if (paintingDisabled()) + return; + + // FIXME: Be smarter about this. + beginPath(); + addPath(pathToFill); + + SkPath path = platformContext()->currentPathInLocalCoordinates(); + if (!isPathSkiaSafe(getCTM(), path)) + return; + + platformContext()->prepareForSoftwareDraw(); + + const GraphicsContextState& state = m_state; + path.setFillType(state.fillRule == RULE_EVENODD ? + SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + + platformContext()->canvas()->drawPath(path, paint); +} + +void GraphicsContext::fillRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) { + // See the other version of fillRect below. + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } + + if (platformContext()->useGPU() && platformContext()->canAccelerate()) { + platformContext()->prepareForHardwareDraw(); + platformContext()->gpuCanvas()->fillRect(rect); + return; + } + + platformContext()->save(); + + platformContext()->prepareForSoftwareDraw(); + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + platformContext()->canvas()->drawRect(r, paint); + + platformContext()->restore(); +} + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU() && platformContext()->canAccelerate()) { + platformContext()->prepareForHardwareDraw(); + platformContext()->gpuCanvas()->fillRect(rect, color, colorSpace); + return; + } + + platformContext()->prepareForSoftwareDraw(); + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) { + // Special case when the rectangle overflows fixed point. This is a + // workaround to fix bug 1212844. When the input rectangle is very + // large, it can overflow Skia's internal fixed point rect. This + // should be fixable in Skia (since the output bitmap isn't that + // large), but until that is fixed, we try to handle it ourselves. + // + // We manually clip the rectangle to the current clip rect. This + // will prevent overflow. The rectangle will be transformed to the + // canvas' coordinate space before it is converted to fixed point + // so we are guaranteed not to overflow after doing this. + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } + + SkPaint paint; + platformContext()->setupPaintCommon(&paint); + paint.setColor(color.rgb()); + platformContext()->canvas()->drawRect(r, paint); +} + +void GraphicsContext::fillRoundedRect(const IntRect& rect, + const IntSize& topLeft, + const IntSize& topRight, + const IntSize& bottomLeft, + const IntSize& bottomRight, + const Color& color, + ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + platformContext()->prepareForSoftwareDraw(); + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) + // See fillRect(). + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + + if (topLeft.width() + topRight.width() > rect.width() + || bottomLeft.width() + bottomRight.width() > rect.width() + || topLeft.height() + bottomLeft.height() > rect.height() + || topRight.height() + bottomRight.height() > rect.height()) { + // Not all the radii fit, return a rect. This matches the behavior of + // Path::createRoundedRectangle. Without this we attempt to draw a round + // shadow for a square box. + fillRect(rect, color, colorSpace); + return; + } + + SkPath path; + addCornerArc(&path, r, topRight, 270); + addCornerArc(&path, r, bottomRight, 0); + addCornerArc(&path, r, bottomLeft, 90); + addCornerArc(&path, r, topLeft, 180); + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + platformContext()->canvas()->drawPath(path, paint); +} + +AffineTransform GraphicsContext::getCTM() const +{ + const SkMatrix& m = platformContext()->canvas()->getTotalMatrix(); + return AffineTransform(SkScalarToDouble(m.getScaleX()), + SkScalarToDouble(m.getSkewY()), + SkScalarToDouble(m.getSkewX()), + SkScalarToDouble(m.getScaleY()), + SkScalarToDouble(m.getTranslateX()), + SkScalarToDouble(m.getTranslateY())); +} + +FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) +{ + // This logic is copied from GraphicsContextCG, eseidel 5/05/08 + + // It is not enough just to round to pixels in device space. The rotation + // part of the affine transform matrix to device space can mess with this + // conversion if we have a rotating image like the hands of the world clock + // widget. We just need the scale, so we get the affine transform matrix and + // extract the scale. + + const SkMatrix& deviceMatrix = platformContext()->canvas()->getTotalMatrix(); + if (deviceMatrix.isIdentity()) + return rect; + + float deviceScaleX = sqrtf(square(deviceMatrix.getScaleX()) + + square(deviceMatrix.getSkewY())); + float deviceScaleY = sqrtf(square(deviceMatrix.getSkewX()) + + square(deviceMatrix.getScaleY())); + + FloatPoint deviceOrigin(rect.x() * deviceScaleX, rect.y() * deviceScaleY); + FloatPoint deviceLowerRight((rect.x() + rect.width()) * deviceScaleX, + (rect.y() + rect.height()) * deviceScaleY); + + deviceOrigin.setX(roundf(deviceOrigin.x())); + deviceOrigin.setY(roundf(deviceOrigin.y())); + deviceLowerRight.setX(roundf(deviceLowerRight.x())); + deviceLowerRight.setY(roundf(deviceLowerRight.y())); + + // Don't let the height or width round to 0 unless either was originally 0 + if (deviceOrigin.y() == deviceLowerRight.y() && rect.height()) + deviceLowerRight.move(0, 1); + if (deviceOrigin.x() == deviceLowerRight.x() && rect.width()) + deviceLowerRight.move(1, 0); + + FloatPoint roundedOrigin(deviceOrigin.x() / deviceScaleX, + deviceOrigin.y() / deviceScaleY); + FloatPoint roundedLowerRight(deviceLowerRight.x() / deviceScaleX, + deviceLowerRight.y() / deviceScaleY); + return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin); +} + +void GraphicsContext::scale(const FloatSize& size) +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU()) + platformContext()->gpuCanvas()->scale(size); + + platformContext()->canvas()->scale(WebCoreFloatToSkScalar(size.width()), + WebCoreFloatToSkScalar(size.height())); +} + +void GraphicsContext::setAlpha(float alpha) +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU()) + platformContext()->gpuCanvas()->setAlpha(alpha); + + platformContext()->setAlpha(alpha); +} + +void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU()) + platformContext()->gpuCanvas()->setCompositeOperation(op); + + platformContext()->setXfermodeMode(WebCoreCompositeToSkiaComposite(op)); +} + +InterpolationQuality GraphicsContext::imageInterpolationQuality() const +{ + return platformContext()->interpolationQuality(); +} + +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality q) +{ + platformContext()->setInterpolationQuality(q); +} + +void GraphicsContext::setLineCap(LineCap cap) +{ + if (paintingDisabled()) + return; + switch (cap) { + case ButtCap: + platformContext()->setLineCap(SkPaint::kButt_Cap); + break; + case RoundCap: + platformContext()->setLineCap(SkPaint::kRound_Cap); + break; + case SquareCap: + platformContext()->setLineCap(SkPaint::kSquare_Cap); + break; + default: + ASSERT(0); + break; + } +} + +void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) +{ + if (paintingDisabled()) + return; + + // FIXME: This is lifted directly off SkiaSupport, lines 49-74 + // so it is not guaranteed to work correctly. + size_t dashLength = dashes.size(); + if (!dashLength) { + // If no dash is set, revert to solid stroke + // FIXME: do we need to set NoStroke in some cases? + platformContext()->setStrokeStyle(SolidStroke); + platformContext()->setDashPathEffect(0); + return; + } + + size_t count = !(dashLength % 2) ? dashLength : dashLength * 2; + SkScalar* intervals = new SkScalar[count]; + + for (unsigned int i = 0; i < count; i++) + intervals[i] = dashes[i % dashLength]; + + platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset)); + + delete[] intervals; +} + +void GraphicsContext::setLineJoin(LineJoin join) +{ + if (paintingDisabled()) + return; + switch (join) { + case MiterJoin: + platformContext()->setLineJoin(SkPaint::kMiter_Join); + break; + case RoundJoin: + platformContext()->setLineJoin(SkPaint::kRound_Join); + break; + case BevelJoin: + platformContext()->setLineJoin(SkPaint::kBevel_Join); + break; + default: + ASSERT(0); + break; + } +} + +void GraphicsContext::setMiterLimit(float limit) +{ + if (paintingDisabled()) + return; + platformContext()->setMiterLimit(limit); +} + +void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU()) + platformContext()->gpuCanvas()->setFillColor(color, colorSpace); + + platformContext()->setFillColor(color.rgb()); +} + +void GraphicsContext::setPlatformFillGradient(Gradient* gradient) +{ + if (paintingDisabled()) + return; + + platformContext()->setFillShader(gradient->platformGradient()); +} + +void GraphicsContext::setPlatformFillPattern(Pattern* pattern) +{ + if (paintingDisabled()) + return; + + platformContext()->setFillShader(pattern->platformPattern(getCTM())); +} + +void GraphicsContext::setPlatformShadow(const FloatSize& size, + float blurFloat, + const Color& color, + ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + // Detect when there's no effective shadow and clear the looper. + if (!size.width() && !size.height() && !blurFloat) { + platformContext()->setDrawLooper(0); + return; + } + + double width = size.width(); + double height = size.height(); + double blur = blurFloat; + + SkBlurDrawLooper::BlurFlags blurFlags = SkBlurDrawLooper::kNone_BlurFlag; + + if (m_state.shadowsIgnoreTransforms) { + // Currently only the GraphicsContext associated with the + // CanvasRenderingContext for HTMLCanvasElement have shadows ignore + // Transforms. So with this flag set, we know this state is associated + // with a CanvasRenderingContext. + blurFlags = SkBlurDrawLooper::kIgnoreTransform_BlurFlag; + + // CG uses natural orientation for Y axis, but the HTML5 canvas spec + // does not. + // So we now flip the height since it was flipped in + // CanvasRenderingContext in order to work with CG. + height = -height; + } + + SkColor c; + if (color.isValid()) + c = color.rgb(); + else + c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" apple shadow color. + + // TODO(tc): Should we have a max value for the blur? CG clamps at 1000.0 + // for perf reasons. + SkDrawLooper* dl = new SkBlurDrawLooper(blur / 2, width, height, c, blurFlags); + platformContext()->setDrawLooper(dl); + dl->unref(); +} + +void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + platformContext()->setStrokeColor(strokecolor.rgb()); +} + +void GraphicsContext::setPlatformStrokeStyle(StrokeStyle stroke) +{ + if (paintingDisabled()) + return; + + platformContext()->setStrokeStyle(stroke); +} + +void GraphicsContext::setPlatformStrokeThickness(float thickness) +{ + if (paintingDisabled()) + return; + + platformContext()->setStrokeThickness(thickness); +} + +void GraphicsContext::setPlatformStrokeGradient(Gradient* gradient) +{ + if (paintingDisabled()) + return; + + platformContext()->setStrokeShader(gradient->platformGradient()); +} + +void GraphicsContext::setPlatformStrokePattern(Pattern* pattern) +{ + if (paintingDisabled()) + return; + + platformContext()->setStrokeShader(pattern->platformPattern(getCTM())); +} + +void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode) +{ + if (paintingDisabled()) + return; + + platformContext()->setTextDrawingMode(mode); +} + +void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) +{ +} + +void GraphicsContext::setPlatformShouldAntialias(bool enable) +{ + if (paintingDisabled()) + return; + + platformContext()->setUseAntialiasing(enable); +} + +void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) +{ + if (paintingDisabled()) + return; + + platformContext()->prepareForSoftwareDraw(); + + SkPaint paint; + SkRect oval = r; + if (strokeStyle() == NoStroke) { + // Stroke using the fill color. + // TODO(brettw) is this really correct? It seems unreasonable. + platformContext()->setupPaintForFilling(&paint); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(WebCoreFloatToSkScalar(strokeThickness())); + } else + platformContext()->setupPaintForStroking(&paint, 0, 0); + + // We do this before converting to scalar, so we don't overflow SkFixed. + startAngle = fastMod(startAngle, 360); + angleSpan = fastMod(angleSpan, 360); + + SkPath path; + path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan)); + if (!isPathSkiaSafe(getCTM(), path)) + return; + platformContext()->canvas()->drawPath(path, paint); +} + +void GraphicsContext::strokePath(const Path& pathToStroke) +{ + if (paintingDisabled()) + return; + + // FIXME: Be smarter about this. + beginPath(); + addPath(pathToStroke); + + SkPath path = platformContext()->currentPathInLocalCoordinates(); + if (!isPathSkiaSafe(getCTM(), path)) + return; + + platformContext()->prepareForSoftwareDraw(); + + SkPaint paint; + platformContext()->setupPaintForStroking(&paint, 0, 0); + platformContext()->canvas()->drawPath(path, paint); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) +{ + if (paintingDisabled()) + return; + + if (!isRectSkiaSafe(getCTM(), rect)) + return; + + platformContext()->prepareForSoftwareDraw(); + + SkPaint paint; + platformContext()->setupPaintForStroking(&paint, 0, 0); + paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth)); + platformContext()->canvas()->drawRect(rect, paint); +} + +void GraphicsContext::rotate(float angleInRadians) +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU()) + platformContext()->gpuCanvas()->rotate(angleInRadians); + + platformContext()->canvas()->rotate(WebCoreFloatToSkScalar( + angleInRadians * (180.0f / 3.14159265f))); +} + +void GraphicsContext::translate(float w, float h) +{ + if (paintingDisabled()) + return; + + if (platformContext()->useGPU()) + platformContext()->gpuCanvas()->translate(w, h); + + platformContext()->canvas()->translate(WebCoreFloatToSkScalar(w), + WebCoreFloatToSkScalar(h)); +} + +void GraphicsContext::syncSoftwareCanvas() +{ + platformContext()->syncSoftwareCanvas(); +} + +void GraphicsContext::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* framebuffer, const IntSize& size) +{ + platformContext()->setSharedGraphicsContext3D(context, framebuffer, size); +} + +void GraphicsContext::markDirtyRect(const IntRect& rect) +{ + platformContext()->markDirtyRect(rect); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp new file mode 100644 index 0000000..a9f6d3c --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2008, Google Inc. All rights reserved. + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. 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 "ImageBuffer.h" + +#include "Base64.h" +#include "BitmapImage.h" +#include "BitmapImageSingleFrameSkia.h" +#include "DrawingBuffer.h" +#include "GLES2Canvas.h" +#include "GraphicsContext.h" +#include "ImageData.h" +#include "JPEGImageEncoder.h" +#include "MIMETypeRegistry.h" +#include "PNGImageEncoder.h" +#include "PlatformContextSkia.h" +#include "SkColorPriv.h" +#include "SkiaUtils.h" + +#include <wtf/text/StringConcatenate.h> + +using namespace std; + +namespace WebCore { + +// We pass a technically-uninitialized canvas to the platform context here since +// the canvas initialization completes in ImageBuffer::ImageBuffer. But +// PlatformContext doesn't actually need to use the object, and this makes all +// the ownership easier to manage. +ImageBufferData::ImageBufferData(const IntSize& size) + : m_platformContext(0) // Canvas is set in ImageBuffer constructor. +{ +} + +ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success) + : m_data(size) + , m_size(size) +{ + if (!m_data.m_canvas.initialize(size.width(), size.height(), false)) { + success = false; + return; + } + + m_data.m_platformContext.setCanvas(&m_data.m_canvas); + m_context.set(new GraphicsContext(&m_data.m_platformContext)); + m_context->platformContext()->setDrawingToImageBuffer(true); + + // Make the background transparent. It would be nice if this wasn't + // required, but the canvas is currently filled with the magic transparency + // color. Can we have another way to manage this? + m_data.m_canvas.drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); + success = true; +} + +ImageBuffer::~ImageBuffer() +{ +} + +GraphicsContext* ImageBuffer::context() const +{ + return m_context.get(); +} + +bool ImageBuffer::drawsUsingCopy() const +{ + return false; +} + +PassRefPtr<Image> ImageBuffer::copyImage() const +{ + m_context->platformContext()->syncSoftwareCanvas(); + return BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), true); +} + +void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const +{ + context->platformContext()->beginLayerClippedToImage(rect, this); +} + +void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, + CompositeOperator op, bool useLowQualityScale) +{ + if (m_data.m_platformContext.useGPU() && context->platformContext()->useGPU()) { + if (context->platformContext()->canAccelerate()) { + DrawingBuffer* sourceDrawingBuffer = m_data.m_platformContext.gpuCanvas()->drawingBuffer(); + unsigned sourceTexture = static_cast<unsigned>(sourceDrawingBuffer->platformColorBuffer()); + FloatRect destRectFlipped(destRect); + destRectFlipped.setY(destRect.y() + destRect.height()); + destRectFlipped.setHeight(-destRect.height()); + context->platformContext()->prepareForHardwareDraw(); + context->platformContext()->gpuCanvas()->drawTexturedRect(sourceTexture, m_size, srcRect, destRectFlipped, styleColorSpace, op); + return; + } + m_data.m_platformContext.syncSoftwareCanvas(); + } + + RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context); + context->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); +} + +void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) +{ + RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context); + image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect); +} + +void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) +{ + const SkBitmap& bitmap = *context()->platformContext()->bitmap(); + if (bitmap.isNull()) + return; + + ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); + SkAutoLockPixels bitmapLock(bitmap); + for (int y = 0; y < m_size.height(); ++y) { + uint32_t* srcRow = bitmap.getAddr32(0, y); + for (int x = 0; x < m_size.width(); ++x) { + SkColor color = SkPMColorToColor(srcRow[x]); + srcRow[x] = SkPreMultiplyARGB(SkColorGetA(color), + lookUpTable[SkColorGetR(color)], + lookUpTable[SkColorGetG(color)], + lookUpTable[SkColorGetB(color)]); + } + } +} + +template <Multiply multiplied> +PassRefPtr<ByteArray> getImageData(const IntRect& rect, const SkBitmap& bitmap, + const IntSize& size) +{ + RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); + + if (bitmap.config() == SkBitmap::kNo_Config) { + // This is an empty SkBitmap that could not be configured. + ASSERT(!size.width() || !size.height()); + return result.release(); + } + + unsigned char* data = result->data(); + + if (rect.x() < 0 + || rect.y() < 0 + || rect.right() > size.width() + || rect.bottom() > size.height()) + memset(data, 0, result->length()); + + int originX = rect.x(); + int destX = 0; + if (originX < 0) { + destX = -originX; + originX = 0; + } + int endX = rect.right(); + if (endX > size.width()) + endX = size.width(); + int numColumns = endX - originX; + + if (numColumns <= 0) + return result.release(); + + int originY = rect.y(); + int destY = 0; + if (originY < 0) { + destY = -originY; + originY = 0; + } + int endY = rect.bottom(); + if (endY > size.height()) + endY = size.height(); + int numRows = endY - originY; + + if (numRows <= 0) + return result.release(); + + ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); + SkAutoLockPixels bitmapLock(bitmap); + + unsigned destBytesPerRow = 4 * rect.width(); + unsigned char* destRow = data + destY * destBytesPerRow + destX * 4; + + for (int y = 0; y < numRows; ++y) { + uint32_t* srcRow = bitmap.getAddr32(originX, originY + y); + for (int x = 0; x < numColumns; ++x) { + unsigned char* destPixel = &destRow[x * 4]; + if (multiplied == Unmultiplied) { + SkColor color = srcRow[x]; + unsigned a = SkColorGetA(color); + destPixel[0] = a ? SkColorGetR(color) * 255 / a : 0; + destPixel[1] = a ? SkColorGetG(color) * 255 / a : 0; + destPixel[2] = a ? SkColorGetB(color) * 255 / a : 0; + destPixel[3] = a; + } else { + // Input and output are both pre-multiplied, we just need to re-arrange the + // bytes from the bitmap format to RGBA. + destPixel[0] = SkGetPackedR32(srcRow[x]); + destPixel[1] = SkGetPackedG32(srcRow[x]); + destPixel[2] = SkGetPackedB32(srcRow[x]); + destPixel[3] = SkGetPackedA32(srcRow[x]); + } + } + destRow += destBytesPerRow; + } + + return result.release(); +} + +PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const +{ + context()->platformContext()->syncSoftwareCanvas(); + return getImageData<Unmultiplied>(rect, *context()->platformContext()->bitmap(), m_size); +} + +PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const +{ + context()->platformContext()->syncSoftwareCanvas(); + return getImageData<Premultiplied>(rect, *context()->platformContext()->bitmap(), m_size); +} + +template <Multiply multiplied> +void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, + const SkBitmap& bitmap, const IntSize& size) +{ + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originX = sourceRect.x(); + int destX = destPoint.x() + sourceRect.x(); + ASSERT(destX >= 0); + ASSERT(destX < size.width()); + ASSERT(originX >= 0); + ASSERT(originX < sourceRect.right()); + + int endX = destPoint.x() + sourceRect.right(); + ASSERT(endX <= size.width()); + + int numColumns = endX - destX; + + int originY = sourceRect.y(); + int destY = destPoint.y() + sourceRect.y(); + ASSERT(destY >= 0); + ASSERT(destY < size.height()); + ASSERT(originY >= 0); + ASSERT(originY < sourceRect.bottom()); + + int endY = destPoint.y() + sourceRect.bottom(); + ASSERT(endY <= size.height()); + int numRows = endY - destY; + + ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); + SkAutoLockPixels bitmapLock(bitmap); + + unsigned srcBytesPerRow = 4 * sourceSize.width(); + + const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4; + + for (int y = 0; y < numRows; ++y) { + uint32_t* destRow = bitmap.getAddr32(destX, destY + y); + for (int x = 0; x < numColumns; ++x) { + const unsigned char* srcPixel = &srcRow[x * 4]; + if (multiplied == Unmultiplied) { + unsigned char alpha = srcPixel[3]; + unsigned char r = SkMulDiv255Ceiling(srcPixel[0], alpha); + unsigned char g = SkMulDiv255Ceiling(srcPixel[1], alpha); + unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha); + destRow[x] = SkPackARGB32(alpha, r, g, b); + } else + destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], + srcPixel[1], srcPixel[2]); + } + srcRow += srcBytesPerRow; + } +} + +void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) +{ + context()->platformContext()->prepareForSoftwareDraw(); + putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, *context()->platformContext()->bitmap(), m_size); +} + +void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) +{ + putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, *context()->platformContext()->bitmap(), m_size); +} + +String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const +{ + ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); + + Vector<unsigned char> encodedImage; + if (mimeType == "image/jpeg") { + int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality; + if (quality && *quality >= 0.0 && *quality <= 1.0) + compressionQuality = static_cast<int>(*quality * 100 + 0.5); + if (!JPEGImageEncoder::encode(*context()->platformContext()->bitmap(), compressionQuality, &encodedImage)) + return "data:,"; + } else { + if (!PNGImageEncoder::encode(*context()->platformContext()->bitmap(), &encodedImage)) + return "data:,"; + ASSERT(mimeType == "image/png"); + } + + Vector<char> base64Data; + base64Encode(*reinterpret_cast<Vector<char>*>(&encodedImage), base64Data); + + return makeString("data:", mimeType, ";base64,", base64Data); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/ImageSkia.cpp b/Source/WebCore/platform/graphics/skia/ImageSkia.cpp new file mode 100644 index 0000000..c7fa6f4 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/ImageSkia.cpp @@ -0,0 +1,540 @@ +/* + * 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: + * + * * 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 "AffineTransform.h" +#include "BitmapImage.h" +#include "BitmapImageSingleFrameSkia.h" +#include "FloatConversion.h" +#include "FloatRect.h" +#include "GLES2Canvas.h" +#include "GraphicsContext.h" +#include "Logging.h" +#include "NativeImageSkia.h" +#include "PlatformContextSkia.h" +#include "PlatformString.h" +#include "SkPixelRef.h" +#include "SkRect.h" +#include "SkShader.h" +#include "SkiaUtils.h" +#include "Texture.h" + +#include "skia/ext/image_operations.h" +#include "skia/ext/platform_canvas.h" + +namespace WebCore { + +// Used by computeResamplingMode to tell how bitmaps should be resampled. +enum ResamplingMode { + // Nearest neighbor resampling. Used when we detect that the page is + // trying to make a pattern by stretching a small bitmap very large. + RESAMPLE_NONE, + + // Default skia resampling. Used for large growing of images where high + // quality resampling doesn't get us very much except a slowdown. + RESAMPLE_LINEAR, + + // High quality resampling. + RESAMPLE_AWESOME, +}; + +static ResamplingMode computeResamplingMode(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, int srcWidth, int srcHeight, float destWidth, float destHeight) +{ + if (platformContext->hasImageResamplingHint()) { + IntSize srcSize; + FloatSize dstSize; + platformContext->getImageResamplingHint(&srcSize, &dstSize); + srcWidth = srcSize.width(); + srcHeight = srcSize.height(); + destWidth = dstSize.width(); + destHeight = dstSize.height(); + } + + int destIWidth = static_cast<int>(destWidth); + int destIHeight = static_cast<int>(destHeight); + + // The percent change below which we will not resample. This usually means + // an off-by-one error on the web page, and just doing nearest neighbor + // sampling is usually good enough. + const float kFractionalChangeThreshold = 0.025f; + + // Images smaller than this in either direction are considered "small" and + // are not resampled ever (see below). + const int kSmallImageSizeThreshold = 8; + + // The amount an image can be stretched in a single direction before we + // say that it is being stretched so much that it must be a line or + // background that doesn't need resampling. + const float kLargeStretch = 3.0f; + + // Figure out if we should resample this image. We try to prune out some + // common cases where resampling won't give us anything, since it is much + // slower than drawing stretched. + if (srcWidth == destIWidth && srcHeight == destIHeight) { + // We don't need to resample if the source and destination are the same. + return RESAMPLE_NONE; + } + + if (srcWidth <= kSmallImageSizeThreshold + || srcHeight <= kSmallImageSizeThreshold + || destWidth <= kSmallImageSizeThreshold + || destHeight <= kSmallImageSizeThreshold) { + // Never resample small images. These are often used for borders and + // rules (think 1x1 images used to make lines). + return RESAMPLE_NONE; + } + + if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) { + // Large image detected. + + // Don't resample if it is being stretched a lot in only one direction. + // This is trying to catch cases where somebody has created a border + // (which might be large) and then is stretching it to fill some part + // of the page. + if (srcWidth == destWidth || srcHeight == destHeight) + return RESAMPLE_NONE; + + // The image is growing a lot and in more than one direction. Resampling + // is slow and doesn't give us very much when growing a lot. + return RESAMPLE_LINEAR; + } + + if ((fabs(destWidth - srcWidth) / srcWidth < kFractionalChangeThreshold) + && (fabs(destHeight - srcHeight) / srcHeight < kFractionalChangeThreshold)) { + // It is disappointingly common on the web for image sizes to be off by + // one or two pixels. We don't bother resampling if the size difference + // is a small fraction of the original size. + return RESAMPLE_NONE; + } + + // When the image is not yet done loading, use linear. We don't cache the + // partially resampled images, and as they come in incrementally, it causes + // us to have to resample the whole thing every time. + if (!bitmap.isDataComplete()) + return RESAMPLE_LINEAR; + + // Everything else gets resampled. + // If the platform context permits high quality interpolation, use it. + // High quality interpolation only enabled for scaling and translation. + if (platformContext->interpolationQuality() == InterpolationHigh + && !(platformContext->canvas()->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) + return RESAMPLE_AWESOME; + + return RESAMPLE_LINEAR; +} + +// Draws the given bitmap to the given canvas. The subset of the source bitmap +// identified by src_rect is drawn to the given destination rect. The bitmap +// will be resampled to resample_width * resample_height (this is the size of +// the whole image, not the subset). See shouldResampleBitmap for more. +// +// This does a lot of computation to resample only the portion of the bitmap +// that will only be drawn. This is critical for performance since when we are +// scrolling, for example, we are only drawing a small strip of the image. +// Resampling the whole image every time is very slow, so this speeds up things +// dramatically. +static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) +{ + // First get the subset we need. This is efficient and does not copy pixels. + SkBitmap subset; + bitmap.extractSubset(&subset, srcIRect); + SkRect srcRect; + srcRect.set(srcIRect); + + // Whether we're doing a subset or using the full source image. + bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0 + && srcIRect.width() == bitmap.width() + && srcIRect.height() == bitmap.height(); + + // We will always draw in integer sizes, so round the destination rect. + SkIRect destRectRounded; + destRect.round(&destRectRounded); + SkIRect resizedImageRect = // Represents the size of the resized image. + { 0, 0, destRectRounded.width(), destRectRounded.height() }; + + // Apply forward transform to destRect to estimate required size of + // re-sampled bitmap, and use only in calls required to resize, or that + // check for the required size. + SkRect destRectTransformed; + canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect); + SkIRect destRectTransformedRounded; + destRectTransformed.round(&destRectTransformedRounded); + + if (srcIsFull && bitmap.hasResizedBitmap(destRectTransformedRounded.width(), destRectTransformedRounded.height())) { + // Yay, this bitmap frame already has a resized version. + SkBitmap resampled = bitmap.resizedBitmap(destRectTransformedRounded.width(), destRectTransformedRounded.height()); + canvas.drawBitmapRect(resampled, 0, destRect, &paint); + return; + } + + // Compute the visible portion of our rect. + // We also need to compute the transformed portion of the + // visible portion for use below. + SkRect destBitmapSubsetSk; + ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk); + SkRect destBitmapSubsetTransformed; + canvas.getTotalMatrix().mapRect(&destBitmapSubsetTransformed, destBitmapSubsetSk); + destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop); + SkIRect destBitmapSubsetTransformedRounded; + destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded); + destBitmapSubsetTransformedRounded.offset(-destRectTransformedRounded.fLeft, -destRectTransformedRounded.fTop); + + // The matrix inverting, etc. could have introduced rounding error which + // causes the bounds to be outside of the resized bitmap. We round outward + // so we always lean toward it being larger rather than smaller than we + // need, and then clamp to the bitmap bounds so we don't get any invalid + // data. + SkIRect destBitmapSubsetSkI; + destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI); + if (!destBitmapSubsetSkI.intersect(resizedImageRect)) + return; // Resized image does not intersect. + + if (srcIsFull && bitmap.shouldCacheResampling( + resizedImageRect.width(), + resizedImageRect.height(), + destBitmapSubsetSkI.width(), + destBitmapSubsetSkI.height())) { + // We're supposed to resize the entire image and cache it, even though + // we don't need all of it. + SkBitmap resampled = bitmap.resizedBitmap(destRectTransformedRounded.width(), + destRectTransformedRounded.height()); + canvas.drawBitmapRect(resampled, 0, destRect, &paint); + } else { + // We should only resize the exposed part of the bitmap to do the + // minimal possible work. + + // Resample the needed part of the image. + // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded + // to go outside the image, so need to clip to avoid problems. + if (destBitmapSubsetTransformedRounded.intersect(0, 0, + destRectTransformedRounded.width(), destRectTransformedRounded.height())) { + + SkBitmap resampled = skia::ImageOperations::Resize(subset, + skia::ImageOperations::RESIZE_LANCZOS3, + destRectTransformedRounded.width(), destRectTransformedRounded.height(), + destBitmapSubsetTransformedRounded); + + // Compute where the new bitmap should be drawn. Since our new bitmap + // may be smaller than the original, we have to shift it over by the + // same amount that we cut off the top and left. + destBitmapSubsetSkI.offset(destRect.fLeft, destRect.fTop); + SkRect offsetDestRect; + offsetDestRect.set(destBitmapSubsetSkI); + + canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint); + } + } +} + +static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp) +{ + SkPaint paint; + paint.setXfermodeMode(compOp); + paint.setFilterBitmap(true); + paint.setAlpha(platformContext->getNormalizedAlpha()); + + skia::PlatformCanvas* canvas = platformContext->canvas(); + + ResamplingMode resampling = platformContext->isPrinting() ? RESAMPLE_NONE : + computeResamplingMode(platformContext, bitmap, srcRect.width(), srcRect.height(), + SkScalarToFloat(destRect.width()), + SkScalarToFloat(destRect.height())); + if (resampling == RESAMPLE_AWESOME) { + drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); + } else { + // No resampling necessary, we can just draw the bitmap. We want to + // filter it if we decided to do linear interpolation above, or if there + // is something interesting going on with the matrix (like a rotation). + // Note: for serialization, we will want to subset the bitmap first so + // we don't send extra pixels. + canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint); + } +} + +// Transforms the given dimensions with the given matrix. Used to see how big +// images will be once transformed. +static void TransformDimensions(const SkMatrix& matrix, float srcWidth, float srcHeight, float* destWidth, float* destHeight) { + // Transform 3 points to see how long each side of the bitmap will be. + SkPoint src_points[3]; // (0, 0), (width, 0), (0, height). + src_points[0].set(0, 0); + src_points[1].set(SkFloatToScalar(srcWidth), 0); + src_points[2].set(0, SkFloatToScalar(srcHeight)); + + // Now measure the length of the two transformed vectors relative to the + // transformed origin to see how big the bitmap will be. Note: for skews, + // this isn't the best thing, but we don't have skews. + SkPoint dest_points[3]; + matrix.mapPoints(dest_points, src_points, 3); + *destWidth = SkScalarToFloat((dest_points[1] - dest_points[0]).length()); + *destHeight = SkScalarToFloat((dest_points[2] - dest_points[0]).length()); +} + +// A helper method for translating negative width and height values. +static FloatRect normalizeRect(const FloatRect& rect) +{ + FloatRect norm = rect; + if (norm.width() < 0) { + norm.setX(norm.x() + norm.width()); + norm.setWidth(-norm.width()); + } + if (norm.height() < 0) { + norm.setY(norm.y() + norm.height()); + norm.setHeight(-norm.height()); + } + return norm; +} + +bool FrameData::clear(bool clearMetadata) +{ + if (clearMetadata) + m_haveMetadata = false; + + if (m_frame) { + // ImageSource::createFrameAtIndex() allocated |m_frame| and passed + // ownership to BitmapImage; we must delete it here. + delete m_frame; + m_frame = 0; + return true; + } + return false; +} + +void Image::drawPattern(GraphicsContext* context, + const FloatRect& floatSrcRect, + const AffineTransform& patternTransform, + const FloatPoint& phase, + ColorSpace styleColorSpace, + CompositeOperator compositeOp, + const FloatRect& destRect) +{ + FloatRect normSrcRect = normalizeRect(floatSrcRect); + if (destRect.isEmpty() || normSrcRect.isEmpty()) + return; // nothing to draw + + NativeImageSkia* bitmap = nativeImageForCurrentFrame(); + if (!bitmap) + return; + + // This is a very inexpensive operation. It will generate a new bitmap but + // it will internally reference the old bitmap's pixels, adjusting the row + // stride so the extra pixels appear as padding to the subsetted bitmap. + SkBitmap srcSubset; + SkIRect srcRect = enclosingIntRect(normSrcRect); + bitmap->extractSubset(&srcSubset, srcRect); + + SkBitmap resampled; + SkShader* shader; + + // Figure out what size the bitmap will be in the destination. The + // destination rect is the bounds of the pattern, we need to use the + // matrix to see how bit it will be. + float destBitmapWidth, destBitmapHeight; + TransformDimensions(patternTransform, srcRect.width(), srcRect.height(), + &destBitmapWidth, &destBitmapHeight); + + // Compute the resampling mode. + ResamplingMode resampling; + if (context->platformContext()->isPrinting()) + resampling = RESAMPLE_LINEAR; + else { + resampling = computeResamplingMode(context->platformContext(), *bitmap, + srcRect.width(), srcRect.height(), + destBitmapWidth, destBitmapHeight); + } + + // Load the transform WebKit requested. + SkMatrix matrix(patternTransform); + + if (resampling == RESAMPLE_AWESOME) { + // Do nice resampling. + SkBitmap resampled; + int width = static_cast<int>(destBitmapWidth); + int height = static_cast<int>(destBitmapHeight); + if (!srcRect.fLeft && !srcRect.fTop + && srcRect.fRight == bitmap->width() && srcRect.fBottom == bitmap->height() + && (bitmap->hasResizedBitmap(width, height) + || bitmap->shouldCacheResampling(width, height, width, height))) { + // resizedBitmap() caches resized image. + resampled = bitmap->resizedBitmap(width, height); + } else { + resampled = skia::ImageOperations::Resize(srcSubset, + skia::ImageOperations::RESIZE_LANCZOS3, width, height); + } + shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); + + // Since we just resized the bitmap, we need to undo the scale set in + // the image transform. + matrix.setScaleX(SkIntToScalar(1)); + matrix.setScaleY(SkIntToScalar(1)); + } else { + // No need to do nice resampling. + shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); + } + + // We also need to translate it such that the origin of the pattern is the + // origin of the destination rect, which is what WebKit expects. Skia uses + // the coordinate system origin as the base for the patter. If WebKit wants + // a shifted image, it will shift it from there using the patternTransform. + float adjustedX = phase.x() + normSrcRect.x() * + narrowPrecisionToFloat(patternTransform.a()); + float adjustedY = phase.y() + normSrcRect.y() * + narrowPrecisionToFloat(patternTransform.d()); + matrix.postTranslate(SkFloatToScalar(adjustedX), + SkFloatToScalar(adjustedY)); + shader->setLocalMatrix(matrix); + + SkPaint paint; + paint.setShader(shader)->unref(); + paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); + paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); + + context->platformContext()->paintSkPaint(destRect, paint); +} + +static void drawBitmapGLES2(GraphicsContext* ctxt, NativeImageSkia* bitmap, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace styleColorSpace, CompositeOperator compositeOp) +{ + ctxt->platformContext()->prepareForHardwareDraw(); + GLES2Canvas* gpuCanvas = ctxt->platformContext()->gpuCanvas(); + Texture* texture = gpuCanvas->getTexture(bitmap); + if (!texture) { + ASSERT(bitmap->config() == SkBitmap::kARGB_8888_Config); + ASSERT(bitmap->rowBytes() == bitmap->width() * 4); + texture = gpuCanvas->createTexture(bitmap, Texture::BGRA8, bitmap->width(), bitmap->height()); + SkAutoLockPixels lock(*bitmap); + ASSERT(bitmap->getPixels()); + texture->load(bitmap->getPixels()); + } + gpuCanvas->drawTexturedRect(texture, srcRect, dstRect, styleColorSpace, compositeOp); +} + +// ================================================ +// BitmapImage Class +// ================================================ + +// FIXME: These should go to BitmapImageSkia.cpp + +void BitmapImage::initPlatformData() +{ + // This is not used. On Mac, the "platform" data is a cache of some OS + // specific versions of the image that are created is some cases. These + // aren't normally used, it is equivalent to getHBITMAP on Windows, and + // the platform data is the cache. +} + +void BitmapImage::invalidatePlatformData() +{ + // See initPlatformData above. +} + +void BitmapImage::checkForSolidColor() +{ + m_checkedForSolidColor = true; +} + +void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, + const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp) +{ + if (!m_source.initialized()) + return; + + // Spin the animation to the correct frame before we try to draw it, so we + // don't draw an old frame and then immediately need to draw a newer one, + // causing flicker and wasting CPU. + startAnimation(); + + NativeImageSkia* bm = nativeImageForCurrentFrame(); + if (!bm) + return; // It's too early and we don't have an image yet. + + FloatRect normDstRect = normalizeRect(dstRect); + FloatRect normSrcRect = normalizeRect(srcRect); + + if (normSrcRect.isEmpty() || normDstRect.isEmpty()) + return; // Nothing to draw. + + if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) { + drawBitmapGLES2(ctxt, bm, normSrcRect, normDstRect, colorSpace, compositeOp); + return; + } + + ctxt->platformContext()->prepareForSoftwareDraw(); + + paintSkBitmap(ctxt->platformContext(), + *bm, + enclosingIntRect(normSrcRect), + normDstRect, + WebCoreCompositeToSkiaComposite(compositeOp)); +} + +// FIXME: These should go into BitmapImageSingleFrameSkia.cpp + +void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt, + const FloatRect& dstRect, + const FloatRect& srcRect, + ColorSpace styleColorSpace, + CompositeOperator compositeOp) +{ + FloatRect normDstRect = normalizeRect(dstRect); + FloatRect normSrcRect = normalizeRect(srcRect); + + if (normSrcRect.isEmpty() || normDstRect.isEmpty()) + return; // Nothing to draw. + + if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) { + drawBitmapGLES2(ctxt, &m_nativeImage, srcRect, dstRect, styleColorSpace, compositeOp); + return; + } + + ctxt->platformContext()->prepareForSoftwareDraw(); + + paintSkBitmap(ctxt->platformContext(), + m_nativeImage, + enclosingIntRect(normSrcRect), + normDstRect, + WebCoreCompositeToSkiaComposite(compositeOp)); +} + +BitmapImageSingleFrameSkia::BitmapImageSingleFrameSkia(const SkBitmap& bitmap) + : m_nativeImage(bitmap) +{ +} + +PassRefPtr<BitmapImageSingleFrameSkia> BitmapImageSingleFrameSkia::create(const SkBitmap& bitmap, bool copyPixels) +{ + if (copyPixels) { + SkBitmap temp; + bitmap.copyTo(&temp, bitmap.config()); + return adoptRef(new BitmapImageSingleFrameSkia(temp)); + } + return adoptRef(new BitmapImageSingleFrameSkia(bitmap)); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/IntPointSkia.cpp b/Source/WebCore/platform/graphics/skia/IntPointSkia.cpp new file mode 100644 index 0000000..fd9a6fd --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/IntPointSkia.cpp @@ -0,0 +1,56 @@ +/* + * 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: + * + * * 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 "IntPoint.h" + +#include "SkPoint.h" + +namespace WebCore { + +IntPoint::IntPoint(const SkIPoint& p) + : m_x(p.fX) + , m_y(p.fY) +{ +} + +IntPoint::operator SkIPoint() const +{ + SkIPoint p = { m_x, m_y }; + return p; +} + +IntPoint::operator SkPoint() const +{ + SkPoint p = { SkIntToScalar(m_x), SkIntToScalar(m_y) }; + return p; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/IntRectSkia.cpp b/Source/WebCore/platform/graphics/skia/IntRectSkia.cpp new file mode 100644 index 0000000..ea138ee --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/IntRectSkia.cpp @@ -0,0 +1,57 @@ +/* + * 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: + * + * * 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 "IntRect.h" + +#include "SkRect.h" + +namespace WebCore { + +IntRect::operator SkIRect() const +{ + SkIRect rect = { x(), y(), right(), bottom() }; + return rect; +} + +IntRect::operator SkRect() const +{ + SkRect rect; + rect.set(SkIntToScalar(x()), SkIntToScalar(y()), SkIntToScalar(right()), SkIntToScalar(bottom())); + return rect; +} + +IntRect::IntRect(const SkIRect& r) + : m_location(r.fLeft, r.fTop) + , m_size(r.width(), r.height()) +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp b/Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp new file mode 100644 index 0000000..94f9b75 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp @@ -0,0 +1,140 @@ +/* + * 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: + * + * * 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" + +#if !PLATFORM(ANDROID) +#include "skia/ext/image_operations.h" +#endif + +#include "NativeImageSkia.h" +#include "SharedGraphicsContext3D.h" +#include "SkiaUtils.h" + +namespace WebCore { + +NativeImageSkia::NativeImageSkia() + : m_isDataComplete(false), + m_lastRequestSize(0, 0), + m_resizeRequests(0) +{ +} + +NativeImageSkia::NativeImageSkia(const SkBitmap& other) + : SkBitmap(other), + m_isDataComplete(false), + m_lastRequestSize(0, 0), + m_resizeRequests(0) +{ +} + + +NativeImageSkia::~NativeImageSkia() +{ +#if PLATFORM(ANDROID) + // SharedGraphicsContext3D::removeTexturesFor() takes a NativeImagePtr. On + // Chromium, this is NativeImageSkia, which inherits from SkBitmap. On + // Android, NativeImagePtr is a SkBitmapRef, which is a wrapper around + // SkBitmap. Failing to call removeTexturesFor() probably causes a leak. + // TODO: Fix this. See http://b/3047425 +#else + SharedGraphicsContext3D::removeTexturesFor(this); +#endif +} + +int NativeImageSkia::decodedSize() const +{ + return getSize() + m_resizedImage.getSize(); +} + +bool NativeImageSkia::hasResizedBitmap(int w, int h) const +{ + if (m_lastRequestSize.width() == w && m_lastRequestSize.height() == h) + m_resizeRequests++; + else { + m_lastRequestSize = IntSize(w, h); + m_resizeRequests = 0; + } + + return m_resizedImage.width() == w && m_resizedImage.height() == h; +} + +// FIXME: don't cache when image is in-progress. + +SkBitmap NativeImageSkia::resizedBitmap(int w, int h) const +{ +#if !PLATFORM(ANDROID) + if (m_resizedImage.width() != w || m_resizedImage.height() != h) + m_resizedImage = skia::ImageOperations::Resize(*this, skia::ImageOperations::RESIZE_LANCZOS3, w, h); +#endif + + return m_resizedImage; +} + +bool NativeImageSkia::shouldCacheResampling(int destWidth, + int destHeight, + int destSubsetWidth, + int destSubsetHeight) const +{ + // We can not cache incomplete frames. This might be a good optimization in + // the future, were we know how much of the frame has been decoded, so when + // we incrementally draw more of the image, we only have to resample the + // parts that are changed. + if (!m_isDataComplete) + return false; + + // If the destination bitmap is small, we'll always allow caching, since + // there is not very much penalty for computing it and it may come in handy. + static const int kSmallBitmapSize = 4096; + if (destWidth * destHeight <= kSmallBitmapSize) + return true; + + // If "too many" requests have been made for this bitmap, we assume that + // many more will be made as well, and we'll go ahead and cache it. + static const int kManyRequestThreshold = 4; + if (m_lastRequestSize.width() == destWidth && + m_lastRequestSize.height() == destHeight) { + if (m_resizeRequests >= kManyRequestThreshold) + return true; + } else { + // When a different size is being requested, count this as a query + // (hasResizedBitmap) and reset the counter. + m_lastRequestSize = IntSize(destWidth, destHeight); + m_resizeRequests = 0; + } + + // Otherwise, use the heuristic that if more than 1/4 of the image is + // requested, it's worth caching. + int destSize = destWidth * destHeight; + int destSubsetSize = destSubsetWidth * destSubsetHeight; + return destSize / 4 < destSubsetSize; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/NativeImageSkia.h b/Source/WebCore/platform/graphics/skia/NativeImageSkia.h new file mode 100644 index 0000000..00b0a68 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/NativeImageSkia.h @@ -0,0 +1,113 @@ +/* + * 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: + * + * * 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 NativeImageSkia_h +#define NativeImageSkia_h + +#include "SkBitmap.h" +#include "IntSize.h" + +namespace WebCore { + +// This object is used as the "native image" in our port. When WebKit uses +// "NativeImagePtr", it is a pointer to this type. It is an SkBitmap, but also +// stores a cached resized image. +class NativeImageSkia : public SkBitmap { +public: + NativeImageSkia(); + ~NativeImageSkia(); + + // This constructor does a shallow copy of the passed-in SkBitmap (ie., it + // references the same pixel data and bumps the refcount). Use only when + // you want sharing semantics. + explicit NativeImageSkia(const SkBitmap&); + + // Returns the number of bytes of image data. This includes the cached + // resized version if there is one. + int decodedSize() const; + + // Sets the data complete flag. This is called by the image decoder when + // all data is complete, and used by us to know whether we can cache + // resized images. + void setDataComplete() { m_isDataComplete = true; } + + // Returns true if the entire image has been decoded. + bool isDataComplete() const { return m_isDataComplete; } + + // We can keep a resized version of the bitmap cached on this object. + // This function will return true if there is a cached version of the + // given image subset with the given dimensions. + bool hasResizedBitmap(int width, int height) const; + + // This will return an existing resized image, or generate a new one of + // the specified size and store it in the cache. Subsetted images can not + // be cached unless the subset is the entire bitmap. + SkBitmap resizedBitmap(int width, int height) const; + + // Returns true if the given resize operation should either resize the whole + // image and cache it, or resize just the part it needs and throw the result + // away. + // + // On the one hand, if only a small subset is desired, then we will waste a + // lot of time resampling the entire thing, so we only want to do exactly + // what's required. On the other hand, resampling the entire bitmap is + // better if we're going to be using it more than once (like a bitmap + // scrolling on and off the screen. Since we only cache when doing the + // entire thing, it's best to just do it up front. + bool shouldCacheResampling(int destWidth, + int destHeight, + int destSubsetWidth, + int destSubsetHeight) const; + +private: + // Set to true when the data is complete. Before the entire image has + // loaded, we do not want to cache a resize. + bool m_isDataComplete; + + // The cached bitmap. This will be empty() if there is no cached image. + mutable SkBitmap m_resizedImage; + + // References how many times that the image size has been requested for + // the last size. + // + // Every time we get a request, if it matches the m_lastRequestSize, we'll + // increment the counter, and if not, we'll reset the counter and save the + // size. + // + // This allows us to see if many requests have been made for the same + // resized image, we know that we should probably cache it, even if all of + // those requests individually are small and would not otherwise be cached. + mutable IntSize m_lastRequestSize; + mutable int m_resizeRequests; +}; + +} +#endif // NativeImageSkia_h + diff --git a/Source/WebCore/platform/graphics/skia/PathSkia.cpp b/Source/WebCore/platform/graphics/skia/PathSkia.cpp new file mode 100644 index 0000000..89323c4 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/PathSkia.cpp @@ -0,0 +1,274 @@ +// 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: +// +// * 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 "Path.h" + +#include "AffineTransform.h" +#include "FloatRect.h" +#include "ImageBuffer.h" +#include "StrokeStyleApplier.h" + +#include "SkPath.h" +#include "SkRegion.h" +#include "SkiaUtils.h" + +#include <wtf/MathExtras.h> + +namespace WebCore { + +Path::Path() +{ + m_path = new SkPath; +} + +Path::Path(const Path& other) +{ + m_path = new SkPath(*other.m_path); +} + +Path::~Path() +{ + delete m_path; +} + +Path& Path::operator=(const Path& other) +{ + *m_path = *other.m_path; + return *this; +} + +bool Path::isEmpty() const +{ + return m_path->isEmpty(); +} + +bool Path::hasCurrentPoint() const +{ + return m_path->getPoints(NULL, 0) != 0; +} + +FloatPoint Path::currentPoint() const +{ + // FIXME: return current point of subpath. + float quietNaN = std::numeric_limits<float>::quiet_NaN(); + return FloatPoint(quietNaN, quietNaN); +} + +bool Path::contains(const FloatPoint& point, WindRule rule) const +{ + return SkPathContainsPoint(m_path, point, + rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType); +} + +void Path::translate(const FloatSize& size) +{ + m_path->offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height())); +} + +FloatRect Path::boundingRect() const +{ + return m_path->getBounds(); +} + +void Path::moveTo(const FloatPoint& point) +{ + m_path->moveTo(point); +} + +void Path::addLineTo(const FloatPoint& point) +{ + m_path->lineTo(point); +} + +void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep) +{ + m_path->quadTo(cp, ep); +} + +void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep) +{ + m_path->cubicTo(p1, p2, ep); +} + +void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) +{ + m_path->arcTo(p1, p2, WebCoreFloatToSkScalar(radius)); +} + +void Path::closeSubpath() +{ + m_path->close(); +} + +void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlockwise) { + SkScalar cx = WebCoreFloatToSkScalar(p.x()); + SkScalar cy = WebCoreFloatToSkScalar(p.y()); + SkScalar radius = WebCoreFloatToSkScalar(r); + SkScalar s360 = SkIntToScalar(360); + + SkRect oval; + oval.set(cx - radius, cy - radius, cx + radius, cy + radius); + + float sweep = ea - sa; + SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat); + SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat); + // Check for a circle. + if (sweepDegrees >= s360 || sweepDegrees <= -s360) { + // Move to the start position (0 sweep means we add a single point). + m_path->arcTo(oval, startDegrees, 0, false); + // Draw the circle. + m_path->addOval(oval); + // Force a moveTo the end position. + m_path->arcTo(oval, startDegrees + sweepDegrees, 0, true); + } else { + // Counterclockwise arcs should be drawn with negative sweeps, while + // clockwise arcs should be drawn with positive sweeps. Check to see + // if the situation is reversed and correct it by adding or subtracting + // a full circle + if (anticlockwise && sweepDegrees > 0) { + sweepDegrees -= s360; + } else if (!anticlockwise && sweepDegrees < 0) { + sweepDegrees += s360; + } + + m_path->arcTo(oval, startDegrees, sweepDegrees, false); + } +} + +void Path::addRect(const FloatRect& rect) +{ + m_path->addRect(rect); +} + +void Path::addEllipse(const FloatRect& rect) +{ + m_path->addOval(rect); +} + +void Path::clear() +{ + m_path->reset(); +} + +static FloatPoint* convertPathPoints(FloatPoint dst[], const SkPoint src[], int count) +{ + for (int i = 0; i < count; i++) { + dst[i].setX(SkScalarToFloat(src[i].fX)); + dst[i].setY(SkScalarToFloat(src[i].fY)); + } + return dst; +} + +void Path::apply(void* info, PathApplierFunction function) const +{ + SkPath::Iter iter(*m_path, false); + SkPoint pts[4]; + PathElement pathElement; + FloatPoint pathPoints[3]; + + for (;;) { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + pathElement.type = PathElementMoveToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[0], 1); + break; + case SkPath::kLine_Verb: + pathElement.type = PathElementAddLineToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[1], 1); + break; + case SkPath::kQuad_Verb: + pathElement.type = PathElementAddQuadCurveToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[1], 2); + break; + case SkPath::kCubic_Verb: + pathElement.type = PathElementAddCurveToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[1], 3); + break; + case SkPath::kClose_Verb: + pathElement.type = PathElementCloseSubpath; + pathElement.points = convertPathPoints(pathPoints, 0, 0); + break; + case SkPath::kDone_Verb: + return; + } + function(info, &pathElement); + } +} + +void Path::transform(const AffineTransform& xform) +{ + m_path->transform(xform); +} + +// Computes the bounding box for the stroke and style currently selected into +// the given bounding box. This also takes into account the stroke width. +static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context) +{ + SkPaint paint; + context->platformContext()->setupPaintForStroking(&paint, 0, 0); + SkPath boundingPath; + paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath); + return boundingPath.getBounds(); +} + +FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) +{ + GraphicsContext* scratch = scratchContext(); + scratch->save(); + scratch->beginPath(); + scratch->addPath(*this); + + if (applier) + applier->strokeStyle(scratch); + + FloatRect r = boundingBoxForCurrentStroke(scratch); + scratch->restore(); + return r; +} + +bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const +{ + ASSERT(applier); + GraphicsContext* scratch = scratchContext(); + scratch->save(); + + applier->strokeStyle(scratch); + + SkPaint paint; + scratch->platformContext()->setupPaintForStroking(&paint, 0, 0); + SkPath strokePath; + paint.getFillPath(*platformPath(), &strokePath); + bool contains = SkPathContainsPoint(&strokePath, point, + SkPath::kWinding_FillType); + + scratch->restore(); + return contains; +} +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/PatternSkia.cpp b/Source/WebCore/platform/graphics/skia/PatternSkia.cpp new file mode 100644 index 0000000..72fac77 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/PatternSkia.cpp @@ -0,0 +1,103 @@ +/* + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "Pattern.h" + +#include "AffineTransform.h" +#include "Image.h" +#include "NativeImageSkia.h" + +#include "SkCanvas.h" +#include "SkColor.h" +#include "SkColorShader.h" +#include "SkShader.h" + +namespace WebCore { + +void Pattern::platformDestroy() +{ + SkSafeUnref(m_pattern); + m_pattern = 0; +} + +PlatformPatternPtr Pattern::platformPattern(const AffineTransform& patternTransform) +{ + if (m_pattern) + return m_pattern; + + // Note: patternTransform is ignored since it seems to be applied elsewhere + // (when the pattern is used?). Applying it to the pattern (i.e. + // shader->setLocalMatrix) results in a double transformation. This can be + // seen, for instance, as an extra offset in: + // LayoutTests/fast/canvas/patternfill-repeat.html + // and expanded scale and skew in: + // LayoutTests/svg/W3C-SVG-1.1/pservers-grad-06-b.svg + + SkBitmap* bm = m_tileImage->nativeImageForCurrentFrame(); + // If we don't have a bitmap, return a transparent shader. + if (!bm) + m_pattern = new SkColorShader(SkColorSetARGB(0, 0, 0, 0)); + + else if (m_repeatX && m_repeatY) + m_pattern = SkShader::CreateBitmapShader(*bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); + + else { + + // Skia does not have a "draw the tile only once" option. Clamp_TileMode + // repeats the last line of the image after drawing one tile. To avoid + // filling the space with arbitrary pixels, this workaround forces the + // image to have a line of transparent pixels on the "repeated" edge(s), + // thus causing extra space to be transparent filled. + SkShader::TileMode tileModeX = m_repeatX ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; + SkShader::TileMode tileModeY = m_repeatY ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; + int expandW = m_repeatX ? 0 : 1; + int expandH = m_repeatY ? 0 : 1; + + // Create a transparent bitmap 1 pixel wider and/or taller than the + // original, then copy the orignal into it. + // FIXME: Is there a better way to pad (not scale) an image in skia? + SkBitmap bm2; + bm2.setConfig(bm->config(), bm->width() + expandW, bm->height() + expandH); + bm2.allocPixels(); + bm2.eraseARGB(0x00, 0x00, 0x00, 0x00); + SkCanvas canvas(bm2); + canvas.drawBitmap(*bm, 0, 0); + m_pattern = SkShader::CreateBitmapShader(bm2, tileModeX, tileModeY); + } + m_pattern->setLocalMatrix(m_patternSpaceTransformation); + return m_pattern; +} + +void Pattern::setPlatformPatternSpaceTransform() +{ + if (m_pattern) + m_pattern->setLocalMatrix(m_patternSpaceTransformation); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp new file mode 100644 index 0000000..d3c0e00 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -0,0 +1,890 @@ +/* + * 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: + * + * * 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 "PlatformContextSkia.h" + +#include "AffineTransform.h" +#include "DrawingBuffer.h" +#include "Extensions3D.h" +#include "GraphicsContext.h" +#include "GraphicsContext3D.h" +#include "ImageBuffer.h" +#include "NativeImageSkia.h" +#include "SkiaUtils.h" +#include "Texture.h" +#include "TilingData.h" + +#include "skia/ext/image_operations.h" +#include "skia/ext/platform_canvas.h" + +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkDashPathEffect.h" +#include "SkShader.h" + +#include <wtf/MathExtras.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/Vector.h> + +#if ENABLE(ACCELERATED_2D_CANVAS) +#include "GLES2Canvas.h" +#include "SharedGraphicsContext3D.h" +#endif + +namespace WebCore { + +extern bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path); + +// State ----------------------------------------------------------------------- + +// Encapsulates the additional painting state information we store for each +// pushed graphics state. +struct PlatformContextSkia::State { + State(); + State(const State&); + ~State(); + + // Common shader state. + float m_alpha; + SkXfermode::Mode m_xferMode; + bool m_useAntialiasing; + SkDrawLooper* m_looper; + + // Fill. + SkColor m_fillColor; + SkShader* m_fillShader; + + // Stroke. + StrokeStyle m_strokeStyle; + SkColor m_strokeColor; + SkShader* m_strokeShader; + float m_strokeThickness; + int m_dashRatio; // Ratio of the length of a dash to its width. + float m_miterLimit; + SkPaint::Cap m_lineCap; + SkPaint::Join m_lineJoin; + SkDashPathEffect* m_dash; + + // Text. (See TextModeFill & friends in GraphicsContext.h.) + TextDrawingModeFlags m_textDrawingMode; + + // Helper function for applying the state's alpha value to the given input + // color to produce a new output color. + SkColor applyAlpha(SkColor) const; + + // If non-empty, the current State is clipped to this image. + SkBitmap m_imageBufferClip; + // If m_imageBufferClip is non-empty, this is the region the image is clipped to. + FloatRect m_clip; + + // This is a list of clipping paths which are currently active, in the + // order in which they were pushed. + WTF::Vector<SkPath> m_antiAliasClipPaths; + InterpolationQuality m_interpolationQuality; + + // If we currently have a canvas (non-antialiased path) clip applied. + bool m_canvasClipApplied; + + PlatformContextSkia::State cloneInheritedProperties(); +private: + // Not supported. + void operator=(const State&); +}; + +// Note: Keep theses default values in sync with GraphicsContextState. +PlatformContextSkia::State::State() + : m_alpha(1) + , m_xferMode(SkXfermode::kSrcOver_Mode) + , m_useAntialiasing(true) + , m_looper(0) + , m_fillColor(0xFF000000) + , m_fillShader(0) + , m_strokeStyle(SolidStroke) + , m_strokeColor(Color::black) + , m_strokeShader(0) + , m_strokeThickness(0) + , m_dashRatio(3) + , m_miterLimit(4) + , m_lineCap(SkPaint::kDefault_Cap) + , m_lineJoin(SkPaint::kDefault_Join) + , m_dash(0) + , m_textDrawingMode(TextModeFill) + , m_interpolationQuality(InterpolationHigh) + , m_canvasClipApplied(false) +{ +} + +PlatformContextSkia::State::State(const State& other) + : m_alpha(other.m_alpha) + , m_xferMode(other.m_xferMode) + , m_useAntialiasing(other.m_useAntialiasing) + , m_looper(other.m_looper) + , m_fillColor(other.m_fillColor) + , m_fillShader(other.m_fillShader) + , m_strokeStyle(other.m_strokeStyle) + , m_strokeColor(other.m_strokeColor) + , m_strokeShader(other.m_strokeShader) + , m_strokeThickness(other.m_strokeThickness) + , m_dashRatio(other.m_dashRatio) + , m_miterLimit(other.m_miterLimit) + , m_lineCap(other.m_lineCap) + , m_lineJoin(other.m_lineJoin) + , m_dash(other.m_dash) + , m_textDrawingMode(other.m_textDrawingMode) + , m_imageBufferClip(other.m_imageBufferClip) + , m_clip(other.m_clip) + , m_antiAliasClipPaths(other.m_antiAliasClipPaths) + , m_interpolationQuality(other.m_interpolationQuality) + , m_canvasClipApplied(other.m_canvasClipApplied) +{ + // Up the ref count of these. SkSafeRef does nothing if its argument is 0. + SkSafeRef(m_looper); + SkSafeRef(m_dash); + SkSafeRef(m_fillShader); + SkSafeRef(m_strokeShader); +} + +PlatformContextSkia::State::~State() +{ + SkSafeUnref(m_looper); + SkSafeUnref(m_dash); + SkSafeUnref(m_fillShader); + SkSafeUnref(m_strokeShader); +} + +// Returns a new State with all of this object's inherited properties copied. +PlatformContextSkia::State PlatformContextSkia::State::cloneInheritedProperties() +{ + PlatformContextSkia::State state(*this); + + // Everything is inherited except for the clip paths. + state.m_antiAliasClipPaths.clear(); + + return state; +} + +SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const +{ + int s = roundf(m_alpha * 256); + if (s >= 256) + return c; + if (s < 0) + return 0; + + int a = SkAlphaMul(SkColorGetA(c), s); + return (c & 0x00FFFFFF) | (a << 24); +} + +// PlatformContextSkia --------------------------------------------------------- + +// Danger: canvas can be NULL. +PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) + : m_canvas(canvas) + , m_drawingToImageBuffer(false) + , m_useGPU(false) +#if ENABLE(ACCELERATED_2D_CANVAS) + , m_gpuCanvas(0) +#endif + , m_backingStoreState(None) +{ + m_stateStack.append(State()); + m_state = &m_stateStack.last(); +} + +PlatformContextSkia::~PlatformContextSkia() +{ +#if ENABLE(ACCELERATED_2D_CANVAS) + if (m_gpuCanvas) + m_gpuCanvas->drawingBuffer()->setWillPublishCallback(0); +#endif +} + +void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) +{ + m_canvas = canvas; +} + +void PlatformContextSkia::setDrawingToImageBuffer(bool value) +{ + m_drawingToImageBuffer = value; +} + +bool PlatformContextSkia::isDrawingToImageBuffer() const +{ + return m_drawingToImageBuffer; +} + +void PlatformContextSkia::save() +{ + ASSERT(!hasImageResamplingHint()); + + m_stateStack.append(m_state->cloneInheritedProperties()); + m_state = &m_stateStack.last(); + + // The clip image only needs to be applied once. Reset the image so that we + // don't attempt to clip multiple times. + m_state->m_imageBufferClip.reset(); + + // Save our native canvas. + canvas()->save(); +} + +void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect, + const ImageBuffer* imageBuffer) +{ + // Skia doesn't support clipping to an image, so we create a layer. The next + // time restore is invoked the layer and |imageBuffer| are combined to + // create the resulting image. + m_state->m_clip = rect; + SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), + SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) }; + + canvas()->clipRect(bounds); + canvas()->saveLayerAlpha(&bounds, 255, + static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag)); + // Copy off the image as |imageBuffer| may be deleted before restore is invoked. + const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap(); + if (!bitmap->pixelRef()) { + // The bitmap owns it's pixels. This happens when we've allocated the + // pixels in some way and assigned them directly to the bitmap (as + // happens when we allocate a DIB). In this case the assignment operator + // does not copy the pixels, rather the copied bitmap ends up + // referencing the same pixels. As the pixels may not live as long as we + // need it to, we copy the image. + bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config); + } else { + // If there is a pixel ref, we can safely use the assignment operator. + m_state->m_imageBufferClip = *bitmap; + } +} + +void PlatformContextSkia::clipPathAntiAliased(const SkPath& clipPath) +{ + // If we are currently tracking any anti-alias clip paths, then we already + // have a layer in place and don't need to add another. + bool haveLayerOutstanding = m_state->m_antiAliasClipPaths.size(); + + // See comments in applyAntiAliasedClipPaths about how this works. + m_state->m_antiAliasClipPaths.append(clipPath); + + if (!haveLayerOutstanding) { + SkRect bounds = clipPath.getBounds(); + canvas()->saveLayerAlpha(&bounds, 255, static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag)); + // Guards state modification during clipped operations. + // The state is popped in applyAntiAliasedClipPaths(). + canvas()->save(); + } +} + +void PlatformContextSkia::restore() +{ + if (!m_state->m_imageBufferClip.empty()) { + applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip); + canvas()->restore(); + } + + if (!m_state->m_antiAliasClipPaths.isEmpty()) + applyAntiAliasedClipPaths(m_state->m_antiAliasClipPaths); + + m_stateStack.removeLast(); + m_state = &m_stateStack.last(); + + // Restore our native canvas. + canvas()->restore(); +} + +void PlatformContextSkia::drawRect(SkRect rect) +{ + SkPaint paint; + int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000; + if (fillcolorNotTransparent) { + setupPaintForFilling(&paint); + canvas()->drawRect(rect, paint); + } + + if (m_state->m_strokeStyle != NoStroke + && (m_state->m_strokeColor & 0xFF000000)) { + // We do a fill of four rects to simulate the stroke of a border. + SkColor oldFillColor = m_state->m_fillColor; + + // setFillColor() will set the shader to NULL, so save a ref to it now. + SkShader* oldFillShader = m_state->m_fillShader; + SkSafeRef(oldFillShader); + setFillColor(m_state->m_strokeColor); + paint.reset(); + setupPaintForFilling(&paint); + SkRect topBorder = { rect.fLeft, rect.fTop, rect.fRight, rect.fTop + 1 }; + canvas()->drawRect(topBorder, paint); + SkRect bottomBorder = { rect.fLeft, rect.fBottom - 1, rect.fRight, rect.fBottom }; + canvas()->drawRect(bottomBorder, paint); + SkRect leftBorder = { rect.fLeft, rect.fTop + 1, rect.fLeft + 1, rect.fBottom - 1 }; + canvas()->drawRect(leftBorder, paint); + SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, rect.fRight, rect.fBottom - 1 }; + canvas()->drawRect(rightBorder, paint); + setFillColor(oldFillColor); + setFillShader(oldFillShader); + SkSafeUnref(oldFillShader); + } +} + +void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const +{ +#if defined(SK_DEBUG) + { + SkPaint defaultPaint; + SkASSERT(*paint == defaultPaint); + } +#endif + + paint->setAntiAlias(m_state->m_useAntialiasing); + paint->setXfermodeMode(m_state->m_xferMode); + paint->setLooper(m_state->m_looper); +} + +void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const +{ + setupPaintCommon(paint); + paint->setColor(m_state->applyAlpha(m_state->m_fillColor)); + paint->setShader(m_state->m_fillShader); +} + +float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const +{ + setupPaintCommon(paint); + float width = m_state->m_strokeThickness; + + paint->setColor(m_state->applyAlpha(m_state->m_strokeColor)); + paint->setShader(m_state->m_strokeShader); + paint->setStyle(SkPaint::kStroke_Style); + paint->setStrokeWidth(SkFloatToScalar(width)); + paint->setStrokeCap(m_state->m_lineCap); + paint->setStrokeJoin(m_state->m_lineJoin); + paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit)); + + if (m_state->m_dash) + paint->setPathEffect(m_state->m_dash); + else { + switch (m_state->m_strokeStyle) { + case NoStroke: + case SolidStroke: + break; + case DashedStroke: + width = m_state->m_dashRatio * width; + // Fall through. + case DottedStroke: + // Truncate the width, since we don't want fuzzy dots or dashes. + int dashLength = static_cast<int>(width); + // Subtract off the endcaps, since they're rendered separately. + int distance = length - 2 * static_cast<int>(m_state->m_strokeThickness); + int phase = 1; + if (dashLength > 1) { + // Determine how many dashes or dots we should have. + int numDashes = distance / dashLength; + int remainder = distance % dashLength; + // Adjust the phase to center the dashes within the line. + if (numDashes % 2 == 0) { + // Even: shift right half a dash, minus half the remainder + phase = (dashLength - remainder) / 2; + } else { + // Odd: shift right a full dash, minus half the remainder + phase = dashLength - remainder / 2; + } + } + SkScalar dashLengthSk = SkIntToScalar(dashLength); + SkScalar intervals[2] = { dashLengthSk, dashLengthSk }; + paint->setPathEffect(new SkDashPathEffect(intervals, 2, SkIntToScalar(phase)))->unref(); + } + } + + return width; +} + +void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl) +{ + SkRefCnt_SafeAssign(m_state->m_looper, dl); +} + +void PlatformContextSkia::setMiterLimit(float ml) +{ + m_state->m_miterLimit = ml; +} + +void PlatformContextSkia::setAlpha(float alpha) +{ + m_state->m_alpha = alpha; +} + +void PlatformContextSkia::setLineCap(SkPaint::Cap lc) +{ + m_state->m_lineCap = lc; +} + +void PlatformContextSkia::setLineJoin(SkPaint::Join lj) +{ + m_state->m_lineJoin = lj; +} + +void PlatformContextSkia::setXfermodeMode(SkXfermode::Mode pdm) +{ + m_state->m_xferMode = pdm; +} + +void PlatformContextSkia::setFillColor(SkColor color) +{ + m_state->m_fillColor = color; + setFillShader(0); +} + +SkDrawLooper* PlatformContextSkia::getDrawLooper() const +{ + return m_state->m_looper; +} + +StrokeStyle PlatformContextSkia::getStrokeStyle() const +{ + return m_state->m_strokeStyle; +} + +void PlatformContextSkia::setStrokeStyle(StrokeStyle strokeStyle) +{ + m_state->m_strokeStyle = strokeStyle; +} + +void PlatformContextSkia::setStrokeColor(SkColor strokeColor) +{ + m_state->m_strokeColor = strokeColor; + setStrokeShader(0); +} + +float PlatformContextSkia::getStrokeThickness() const +{ + return m_state->m_strokeThickness; +} + +void PlatformContextSkia::setStrokeThickness(float thickness) +{ + m_state->m_strokeThickness = thickness; +} + +void PlatformContextSkia::setStrokeShader(SkShader* strokeShader) +{ + if (strokeShader) + m_state->m_strokeColor = Color::black; + + if (strokeShader != m_state->m_strokeShader) { + SkSafeUnref(m_state->m_strokeShader); + m_state->m_strokeShader = strokeShader; + SkSafeRef(m_state->m_strokeShader); + } +} + +TextDrawingModeFlags PlatformContextSkia::getTextDrawingMode() const +{ + return m_state->m_textDrawingMode; +} + +float PlatformContextSkia::getAlpha() const +{ + return m_state->m_alpha; +} + +int PlatformContextSkia::getNormalizedAlpha() const +{ + int alpha = roundf(m_state->m_alpha * 256); + if (alpha > 255) + alpha = 255; + else if (alpha < 0) + alpha = 0; + return alpha; +} + +void PlatformContextSkia::setTextDrawingMode(TextDrawingModeFlags mode) +{ + // TextModeClip is never used, so we assert that it isn't set: + // https://bugs.webkit.org/show_bug.cgi?id=21898 + ASSERT(!(mode & TextModeClip)); + m_state->m_textDrawingMode = mode; +} + +void PlatformContextSkia::setUseAntialiasing(bool enable) +{ + m_state->m_useAntialiasing = enable; +} + +SkColor PlatformContextSkia::effectiveFillColor() const +{ + return m_state->applyAlpha(m_state->m_fillColor); +} + +SkColor PlatformContextSkia::effectiveStrokeColor() const +{ + return m_state->applyAlpha(m_state->m_strokeColor); +} + +void PlatformContextSkia::beginPath() +{ + m_path.reset(); +} + +void PlatformContextSkia::addPath(const SkPath& path) +{ + m_path.addPath(path, m_canvas->getTotalMatrix()); +} + +SkPath PlatformContextSkia::currentPathInLocalCoordinates() const +{ + SkPath localPath = m_path; + const SkMatrix& matrix = m_canvas->getTotalMatrix(); + SkMatrix inverseMatrix; + if (!matrix.invert(&inverseMatrix)) + return SkPath(); + localPath.transform(inverseMatrix); + return localPath; +} + +void PlatformContextSkia::canvasClipPath(const SkPath& path) +{ + m_state->m_canvasClipApplied = true; + m_canvas->clipPath(path); +} + +void PlatformContextSkia::setFillRule(SkPath::FillType fr) +{ + m_path.setFillType(fr); +} + +void PlatformContextSkia::setFillShader(SkShader* fillShader) +{ + if (fillShader) + m_state->m_fillColor = Color::black; + + if (fillShader != m_state->m_fillShader) { + SkSafeUnref(m_state->m_fillShader); + m_state->m_fillShader = fillShader; + SkSafeRef(m_state->m_fillShader); + } +} + +InterpolationQuality PlatformContextSkia::interpolationQuality() const +{ + return m_state->m_interpolationQuality; +} + +void PlatformContextSkia::setInterpolationQuality(InterpolationQuality interpolationQuality) +{ + m_state->m_interpolationQuality = interpolationQuality; +} + +void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash) +{ + if (dash != m_state->m_dash) { + SkSafeUnref(m_state->m_dash); + m_state->m_dash = dash; + } +} + +void PlatformContextSkia::paintSkPaint(const SkRect& rect, + const SkPaint& paint) +{ + m_canvas->drawRect(rect, paint); +} + +const SkBitmap* PlatformContextSkia::bitmap() const +{ + return &m_canvas->getDevice()->accessBitmap(false); +} + +bool PlatformContextSkia::isPrinting() +{ + return m_canvas->getTopPlatformDevice().IsVectorial(); +} + +void PlatformContextSkia::getImageResamplingHint(IntSize* srcSize, FloatSize* dstSize) const +{ + *srcSize = m_imageResamplingHintSrcSize; + *dstSize = m_imageResamplingHintDstSize; +} + +void PlatformContextSkia::setImageResamplingHint(const IntSize& srcSize, const FloatSize& dstSize) +{ + m_imageResamplingHintSrcSize = srcSize; + m_imageResamplingHintDstSize = dstSize; +} + +void PlatformContextSkia::clearImageResamplingHint() +{ + m_imageResamplingHintSrcSize = IntSize(); + m_imageResamplingHintDstSize = FloatSize(); +} + +bool PlatformContextSkia::hasImageResamplingHint() const +{ + return !m_imageResamplingHintSrcSize.isEmpty() && !m_imageResamplingHintDstSize.isEmpty(); +} + +void PlatformContextSkia::applyClipFromImage(const FloatRect& rect, const SkBitmap& imageBuffer) +{ + // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we + // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping. + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kDstIn_Mode); + m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint); +} + +void PlatformContextSkia::applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths) +{ + // Anti-aliased clipping: + // + // Skia's clipping is 1-bit only. Consider what would happen if it were 8-bit: + // We have a square canvas, filled with white and we declare a circular + // clipping path. Then we fill twice with a black rectangle. The fractional + // pixels would first get the correct color (white * alpha + black * (1 - + // alpha)), but the second fill would apply the alpha to the already + // modified color and the result would be too dark. + // + // This, anti-aliased clipping needs to be performed after the drawing has + // been done. In order to do this, we create a new layer of the canvas in + // clipPathAntiAliased and store the clipping path. All drawing is done to + // the layer's bitmap while it's in effect. When WebKit calls restore() to + // undo the clipping, this function is called. + // + // Here, we walk the list of clipping paths backwards and, for each, we + // clear outside of the clipping path. We only need a single extra layer + // for any number of clipping paths. + // + // When we call restore on the SkCanvas, the layer's bitmap is composed + // into the layer below and we end up with correct, anti-aliased clipping. + + m_canvas->restore(); + + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kClear_Mode); + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kFill_Style); + + for (size_t i = paths.size() - 1; i < paths.size(); --i) { + paths[i].setFillType(SkPath::kInverseWinding_FillType); + m_canvas->drawPath(paths[i], paint); + } + + m_canvas->restore(); +} + +bool PlatformContextSkia::canAccelerate() const +{ + return !m_state->m_fillShader // Can't accelerate with a fill gradient or pattern. + && !m_state->m_looper // Can't accelerate with a shadow. + && !m_state->m_canvasClipApplied; // Can't accelerate with a clip to path applied. +} + +bool PlatformContextSkia::canvasClipApplied() const +{ + return m_state->m_canvasClipApplied; +} + +class WillPublishCallbackImpl : public DrawingBuffer::WillPublishCallback { +public: + static PassOwnPtr<WillPublishCallback> create(PlatformContextSkia* pcs) + { + return adoptPtr(new WillPublishCallbackImpl(pcs)); + } + + virtual void willPublish() + { + m_pcs->prepareForHardwareDraw(); + } + +private: + explicit WillPublishCallbackImpl(PlatformContextSkia* pcs) + : m_pcs(pcs) + { + } + + PlatformContextSkia* m_pcs; +}; + +void PlatformContextSkia::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* drawingBuffer, const WebCore::IntSize& size) +{ +#if ENABLE(ACCELERATED_2D_CANVAS) + if (context && drawingBuffer) { + m_useGPU = true; + m_gpuCanvas = new GLES2Canvas(context, drawingBuffer, size); + m_uploadTexture.clear(); + drawingBuffer->setWillPublishCallback(WillPublishCallbackImpl::create(this)); + } else { + syncSoftwareCanvas(); + m_uploadTexture.clear(); + m_gpuCanvas.clear(); + m_useGPU = false; + } +#endif +} + +void PlatformContextSkia::prepareForSoftwareDraw() const +{ + if (!m_useGPU) + return; + + if (m_backingStoreState == Hardware) { + // Depending on the blend mode we need to do one of a few things: + + // * For associative blend modes, we can draw into an initially empty + // canvas and then composite the results on top of the hardware drawn + // results before the next hardware draw or swapBuffers(). + + // * For non-associative blend modes we have to do a readback and then + // software draw. When we re-upload in this mode we have to blow + // away whatever is in the hardware backing store (do a copy instead + // of a compositing operation). + + if (m_state->m_xferMode == SkXfermode::kSrcOver_Mode) { + // Note that we have rendering results in both the hardware and software backing stores. + m_backingStoreState = Mixed; + } else { + readbackHardwareToSoftware(); + // When we switch back to hardware copy the results, don't composite. + m_backingStoreState = Software; + } + } else if (m_backingStoreState == Mixed) { + if (m_state->m_xferMode != SkXfermode::kSrcOver_Mode) { + // Have to composite our currently software drawn data... + uploadSoftwareToHardware(CompositeSourceOver); + // then do a readback so we can hardware draw stuff. + readbackHardwareToSoftware(); + m_backingStoreState = Software; + } + } else if (m_backingStoreState == None) { + m_backingStoreState = Software; + } +} + +void PlatformContextSkia::prepareForHardwareDraw() const +{ + if (!m_useGPU) + return; + + if (m_backingStoreState == Software) { + // Last drawn in software; upload everything we've drawn. + uploadSoftwareToHardware(CompositeCopy); + } else if (m_backingStoreState == Mixed) { + // Stuff in software/hardware, composite the software stuff on top of + // the hardware stuff. + uploadSoftwareToHardware(CompositeSourceOver); + } + m_backingStoreState = Hardware; +} + +void PlatformContextSkia::syncSoftwareCanvas() const +{ + if (!m_useGPU) + return; + + if (m_backingStoreState == Hardware) + readbackHardwareToSoftware(); + else if (m_backingStoreState == Mixed) { + // Have to composite our currently software drawn data.. + uploadSoftwareToHardware(CompositeSourceOver); + // then do a readback. + readbackHardwareToSoftware(); + m_backingStoreState = Software; + } + m_backingStoreState = Software; +} + +void PlatformContextSkia::markDirtyRect(const IntRect& rect) +{ + if (!m_useGPU) + return; + + switch (m_backingStoreState) { + case Software: + case Mixed: + m_softwareDirtyRect.unite(rect); + return; + case Hardware: + return; + default: + ASSERT_NOT_REACHED(); + } +} + +void PlatformContextSkia::uploadSoftwareToHardware(CompositeOperator op) const +{ +#if ENABLE(ACCELERATED_2D_CANVAS) + const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(false); + SkAutoLockPixels lock(bitmap); + SharedGraphicsContext3D* context = m_gpuCanvas->context(); + if (!m_uploadTexture || m_uploadTexture->tiles().totalSizeX() < bitmap.width() || m_uploadTexture->tiles().totalSizeY() < bitmap.height()) + m_uploadTexture = context->createTexture(Texture::BGRA8, bitmap.width(), bitmap.height()); + + m_uploadTexture->updateSubRect(bitmap.getPixels(), m_softwareDirtyRect); + AffineTransform identity; + gpuCanvas()->drawTexturedRect(m_uploadTexture.get(), m_softwareDirtyRect, m_softwareDirtyRect, identity, 1.0, ColorSpaceDeviceRGB, op); + // Clear out the region of the software canvas we just uploaded. + m_canvas->save(); + m_canvas->resetMatrix(); + SkRect bounds = m_softwareDirtyRect; + m_canvas->clipRect(bounds, SkRegion::kReplace_Op); + m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); + m_canvas->restore(); + m_softwareDirtyRect.setWidth(0); // Clear dirty rect. +#endif +} + +void PlatformContextSkia::readbackHardwareToSoftware() const +{ +#if ENABLE(ACCELERATED_2D_CANVAS) + const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(true); + SkAutoLockPixels lock(bitmap); + int width = bitmap.width(), height = bitmap.height(); + OwnArrayPtr<uint32_t> buf(new uint32_t[width]); + SharedGraphicsContext3D* context = m_gpuCanvas->context(); + m_gpuCanvas->bindFramebuffer(); + // Flips the image vertically. + for (int y = 0; y < height; ++y) { + uint32_t* pixels = bitmap.getAddr32(0, y); + if (context->supportsBGRA()) + context->readPixels(0, height - 1 - y, width, 1, Extensions3D::BGRA_EXT, GraphicsContext3D::UNSIGNED_BYTE, pixels); + else { + context->readPixels(0, height - 1 - y, width, 1, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixels); + for (int i = 0; i < width; ++i) { + uint32_t pixel = pixels[i]; + // Swizzles from RGBA -> BGRA. + pixels[i] = (pixel & 0xFF00FF00) | ((pixel & 0x00FF0000) >> 16) | ((pixel & 0x000000FF) << 16); + } + } + } + m_softwareDirtyRect.unite(IntRect(0, 0, width, height)); // Mark everything as dirty. +#endif +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h new file mode 100644 index 0000000..11b311a --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -0,0 +1,240 @@ +/* + * 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: + * + * * 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 PlatformContextSkia_h +#define PlatformContextSkia_h + +#include "GraphicsContext.h" +#include "Noncopyable.h" + +#include "SkDashPathEffect.h" +#include "SkDrawLooper.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "skia/ext/platform_canvas.h" + +#include <wtf/Vector.h> + +namespace WebCore { + +enum CompositeOperator; +class DrawingBuffer; +class GLES2Canvas; +class GraphicsContext3D; +class Texture; + +// This class holds the platform-specific state for GraphicsContext. We put +// most of our Skia wrappers on this class. In theory, a lot of this stuff could +// be moved to GraphicsContext directly, except that some code external to this +// would like to poke at our graphics layer as well (like the Image and Font +// stuff, which needs some amount of our wrappers and state around SkCanvas). +// +// So in general, this class uses just Skia types except when there's no easy +// conversion. GraphicsContext is responsible for converting the WebKit types to +// Skia types and setting up the eventual call to the Skia functions. +// +// This class then keeps track of all the current Skia state. WebKit expects +// that the graphics state that is pushed and popped by save() and restore() +// includes things like colors and pen styles. Skia does this differently, where +// push and pop only includes transforms and bitmaps, and the application is +// responsible for managing the painting state which is store in separate +// SkPaint objects. This class provides the adaptor that allows the painting +// state to be pushed and popped along with the bitmap. +class PlatformContextSkia : public Noncopyable { +public: + // For printing, there shouldn't be any canvas. canvas can be NULL. If you + // supply a NULL canvas, you can also call setCanvas later. + PlatformContextSkia(skia::PlatformCanvas*); + ~PlatformContextSkia(); + + // Sets the canvas associated with this context. Use when supplying NULL + // to the constructor. + void setCanvas(skia::PlatformCanvas*); + + // If false we're rendering to a GraphicsContext for a web page, if false + // we're not (as is the case when rendering to a canvas object). + // If this is true the contents have not been marked up with the magic + // color and all text drawing needs to go to a layer so that the alpha is + // correctly updated. + void setDrawingToImageBuffer(bool); + bool isDrawingToImageBuffer() const; + + void save(); + void restore(); + + // Begins a layer that is clipped to the image |imageBuffer| at the location + // |rect|. This layer is implicitly restored when the next restore is + // invoked. + // NOTE: |imageBuffer| may be deleted before the |restore| is invoked. + void beginLayerClippedToImage(const FloatRect&, const ImageBuffer*); + void clipPathAntiAliased(const SkPath&); + + // Sets up the common flags on a paint for antialiasing, effects, etc. + // This is implicitly called by setupPaintFill and setupPaintStroke, but + // you may wish to call it directly sometimes if you don't want that other + // behavior. + void setupPaintCommon(SkPaint*) const; + + // Sets up the paint for the current fill style. + void setupPaintForFilling(SkPaint*) const; + + // Sets up the paint for stroking. Returns an int representing the width of + // the pen, or 1 if the pen's width is 0 if a non-zero length is provided, + // the number of dashes/dots on a dashed/dotted line will be adjusted to + // start and end that length with a dash/dot. + float setupPaintForStroking(SkPaint*, SkRect*, int length) const; + + // State setting functions. + void setDrawLooper(SkDrawLooper*); // Note: takes an additional ref. + void setMiterLimit(float); + void setAlpha(float); + void setLineCap(SkPaint::Cap); + void setLineJoin(SkPaint::Join); + void setFillRule(SkPath::FillType); + void setXfermodeMode(SkXfermode::Mode); + void setFillColor(SkColor); + void setFillShader(SkShader*); + void setStrokeStyle(StrokeStyle); + void setStrokeColor(SkColor); + void setStrokeThickness(float thickness); + void setStrokeShader(SkShader*); + void setTextDrawingMode(TextDrawingModeFlags mode); + void setUseAntialiasing(bool enable); + void setDashPathEffect(SkDashPathEffect*); + + SkDrawLooper* getDrawLooper() const; + StrokeStyle getStrokeStyle() const; + float getStrokeThickness() const; + TextDrawingModeFlags getTextDrawingMode() const; + float getAlpha() const; + int getNormalizedAlpha() const; + + void beginPath(); + void addPath(const SkPath&); + SkPath currentPathInLocalCoordinates() const; + + void canvasClipPath(const SkPath&); + + // Returns the fill color. The returned color has it's alpha adjusted + // by the current alpha. + SkColor effectiveFillColor() const; + + // Returns the stroke color. The returned color has it's alpha adjusted + // by the current alpha. + SkColor effectiveStrokeColor() const; + + skia::PlatformCanvas* canvas() { return m_canvas; } + + InterpolationQuality interpolationQuality() const; + void setInterpolationQuality(InterpolationQuality interpolationQuality); + + // FIXME: This should be pushed down to GraphicsContext. + void drawRect(SkRect rect); + + // FIXME: I'm still unsure how I will serialize this call. + void paintSkPaint(const SkRect&, const SkPaint&); + + const SkBitmap* bitmap() const; + + // Returns the canvas used for painting, NOT guaranteed to be non-NULL. + // + // Warning: This function is deprecated so the users are reminded that they + // should use this layer of indirection instead of using the canvas + // directly. This is to help with the eventual serialization. + skia::PlatformCanvas* canvas() const; + + // Returns if the context is a printing context instead of a display + // context. Bitmap shouldn't be resampled when printing to keep the best + // possible quality. + bool isPrinting(); + + void getImageResamplingHint(IntSize* srcSize, FloatSize* dstSize) const; + void setImageResamplingHint(const IntSize& srcSize, const FloatSize& dstSize); + void clearImageResamplingHint(); + bool hasImageResamplingHint() const; + + bool canAccelerate() const; + bool canvasClipApplied() const; + bool useGPU() { return m_useGPU; } + void setSharedGraphicsContext3D(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&); +#if ENABLE(ACCELERATED_2D_CANVAS) + GLES2Canvas* gpuCanvas() const { return m_gpuCanvas.get(); } +#else + GLES2Canvas* gpuCanvas() const { return 0; } +#endif + // Call these before making a call that manipulates the underlying + // skia::PlatformCanvas or WebCore::GLES2Canvas + void prepareForSoftwareDraw() const; + void prepareForHardwareDraw() const; + // Call to force the skia::PlatformCanvas to contain all rendering results. + void syncSoftwareCanvas() const; + void markDirtyRect(const IntRect& rect); + +private: + // Used when restoring and the state has an image clip. Only shows the pixels in + // m_canvas that are also in imageBuffer. + void applyClipFromImage(const FloatRect&, const SkBitmap&); + void applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths); + + void uploadSoftwareToHardware(CompositeOperator) const; + void readbackHardwareToSoftware() const; + + // Defines drawing style. + struct State; + + // NULL indicates painting is disabled. Never delete this object. + skia::PlatformCanvas* m_canvas; + + // States stack. Enables local drawing state change with save()/restore() + // calls. + WTF::Vector<State> m_stateStack; + // Pointer to the current drawing state. This is a cached value of + // mStateStack.back(). + State* m_state; + + // Current path in global coordinates. + SkPath m_path; + + // Stores image sizes for a hint to compute image resampling modes. + // Values are used in ImageSkia.cpp + IntSize m_imageResamplingHintSrcSize; + FloatSize m_imageResamplingHintDstSize; + bool m_drawingToImageBuffer; + bool m_useGPU; +#if ENABLE(ACCELERATED_2D_CANVAS) + OwnPtr<GLES2Canvas> m_gpuCanvas; + mutable RefPtr<Texture> m_uploadTexture; +#endif + mutable enum { None, Software, Mixed, Hardware } m_backingStoreState; + mutable IntRect m_softwareDirtyRect; +}; + +} +#endif // PlatformContextSkia_h diff --git a/Source/WebCore/platform/graphics/skia/PlatformGraphics.h b/Source/WebCore/platform/graphics/skia/PlatformGraphics.h new file mode 100644 index 0000000..4ae8835 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/PlatformGraphics.h @@ -0,0 +1,37 @@ +/* + * 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: + * + * * 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 PlatformGraphics_h +#define PlatformGraphics_h + +typedef class SkShader* PlatformGradient; +typedef class SkShader* PlatformPattern; + +#endif // PlatformGraphics_h diff --git a/Source/WebCore/platform/graphics/skia/SkiaFontWin.cpp b/Source/WebCore/platform/graphics/skia/SkiaFontWin.cpp new file mode 100644 index 0000000..5046c50 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/SkiaFontWin.cpp @@ -0,0 +1,358 @@ +/* + * 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: + * + * * 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 "SkiaFontWin.h" + +#include "AffineTransform.h" +#include "PlatformContextSkia.h" +#include "Gradient.h" +#include "Pattern.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkShader.h" + +#include <wtf/ListHashSet.h> +#include <wtf/Vector.h> + +namespace WebCore { + +struct CachedOutlineKey { + CachedOutlineKey() : font(0), glyph(0), path(0) {} + CachedOutlineKey(HFONT f, WORD g) : font(f), glyph(g), path(0) {} + + HFONT font; + WORD glyph; + + // The lifetime of this pointer is managed externally to this class. Be sure + // to call DeleteOutline to remove items. + SkPath* path; +}; + +const bool operator==(const CachedOutlineKey& a, const CachedOutlineKey& b) +{ + return a.font == b.font && a.glyph == b.glyph; +} + +struct CachedOutlineKeyHash { + static unsigned hash(const CachedOutlineKey& key) + { + unsigned keyBytes; + memcpy(&keyBytes, &key.font, sizeof(unsigned)); + return keyBytes + key.glyph; + } + + static unsigned equal(const CachedOutlineKey& a, const CachedOutlineKey& b) + { + return a.font == b.font && a.glyph == b.glyph; + } + + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +// The global number of glyph outlines we'll cache. +static const int outlineCacheSize = 256; + +typedef ListHashSet<CachedOutlineKey, outlineCacheSize+1, CachedOutlineKeyHash> OutlineCache; + +// FIXME: Convert from static constructor to accessor function. WebCore tries to +// avoid global constructors to save on start-up time. +static OutlineCache outlineCache; + +static SkScalar FIXEDToSkScalar(FIXED fixed) +{ + SkFixed skFixed; + memcpy(&skFixed, &fixed, sizeof(SkFixed)); + return SkFixedToScalar(skFixed); +} + +// Removes the given key from the cached outlines, also deleting the path. +static void deleteOutline(OutlineCache::iterator deleteMe) +{ + delete deleteMe->path; + outlineCache.remove(deleteMe); +} + +static void addPolyCurveToPath(const TTPOLYCURVE* polyCurve, SkPath* path) +{ + switch (polyCurve->wType) { + case TT_PRIM_LINE: + for (WORD i = 0; i < polyCurve->cpfx; i++) { + path->lineTo(FIXEDToSkScalar(polyCurve->apfx[i].x), -FIXEDToSkScalar(polyCurve->apfx[i].y)); + } + break; + + case TT_PRIM_QSPLINE: + // FIXME: doesn't this duplicate points if we do the loop > once? + for (WORD i = 0; i < polyCurve->cpfx - 1; i++) { + SkScalar bx = FIXEDToSkScalar(polyCurve->apfx[i].x); + SkScalar by = FIXEDToSkScalar(polyCurve->apfx[i].y); + + SkScalar cx = FIXEDToSkScalar(polyCurve->apfx[i + 1].x); + SkScalar cy = FIXEDToSkScalar(polyCurve->apfx[i + 1].y); + if (i < polyCurve->cpfx - 2) { + // We're not the last point, compute C. + cx = SkScalarAve(bx, cx); + cy = SkScalarAve(by, cy); + } + + // Need to flip the y coordinates since the font's coordinate system is + // flipped from ours vertically. + path->quadTo(bx, -by, cx, -cy); + } + break; + + case TT_PRIM_CSPLINE: + // FIXME + break; + } +} + +// The size of the glyph path buffer. +static const int glyphPathBufferSize = 4096; + +// Fills the given SkPath with the outline for the given glyph index. The font +// currently selected into the given DC is used. Returns true on success. +static bool getPathForGlyph(HDC dc, WORD glyph, SkPath* path) +{ + char buffer[glyphPathBufferSize]; + GLYPHMETRICS gm; + MAT2 mat = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; // Each one is (fract,value). + + DWORD totalSize = GetGlyphOutlineW(dc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, + &gm, glyphPathBufferSize, buffer, &mat); + if (totalSize == GDI_ERROR) + return false; + + const char* curGlyph = buffer; + const char* endGlyph = &buffer[totalSize]; + while (curGlyph < endGlyph) { + const TTPOLYGONHEADER* polyHeader = + reinterpret_cast<const TTPOLYGONHEADER*>(curGlyph); + path->moveTo(FIXEDToSkScalar(polyHeader->pfxStart.x), + -FIXEDToSkScalar(polyHeader->pfxStart.y)); + + const char* curPoly = curGlyph + sizeof(TTPOLYGONHEADER); + const char* endPoly = curGlyph + polyHeader->cb; + while (curPoly < endPoly) { + const TTPOLYCURVE* polyCurve = + reinterpret_cast<const TTPOLYCURVE*>(curPoly); + addPolyCurveToPath(polyCurve, path); + curPoly += sizeof(WORD) * 2 + sizeof(POINTFX) * polyCurve->cpfx; + } + path->close(); + curGlyph += polyHeader->cb; + } + + return true; +} + +// Returns a SkPath corresponding to the give glyph in the given font. The font +// should be selected into the given DC. The returned path is owned by the +// hashtable. Returns 0 on error. +const SkPath* SkiaWinOutlineCache::lookupOrCreatePathForGlyph(HDC hdc, HFONT font, WORD glyph) +{ + CachedOutlineKey key(font, glyph); + OutlineCache::iterator found = outlineCache.find(key); + if (found != outlineCache.end()) { + // Keep in MRU order by removing & reinserting the value. + key = *found; + outlineCache.remove(found); + outlineCache.add(key); + return key.path; + } + + key.path = new SkPath; + if (!getPathForGlyph(hdc, glyph, key.path)) + return 0; + + if (outlineCache.size() > outlineCacheSize) + // The cache is too big, find the oldest value (first in the list). + deleteOutline(outlineCache.begin()); + + outlineCache.add(key); + return key.path; +} + + +void SkiaWinOutlineCache::removePathsForFont(HFONT hfont) +{ + // ListHashSet isn't the greatest structure for deleting stuff out of, but + // removing entries will be relatively rare (we don't remove fonts much, nor + // do we draw out own glyphs using these routines much either). + // + // We keep a list of all glyphs we're removing which we do in a separate + // pass. + Vector<CachedOutlineKey> outlinesToDelete; + for (OutlineCache::iterator i = outlineCache.begin(); + i != outlineCache.end(); ++i) + outlinesToDelete.append(*i); + + for (Vector<CachedOutlineKey>::iterator i = outlinesToDelete.begin(); + i != outlinesToDelete.end(); ++i) + deleteOutline(outlineCache.find(*i)); +} + +bool windowsCanHandleDrawTextShadow(GraphicsContext *context) +{ + FloatSize shadowOffset; + float shadowBlur; + Color shadowColor; + ColorSpace shadowColorSpace; + + bool hasShadow = context->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); + return (hasShadow && (shadowBlur == 0) && (shadowColor.alpha() == 255) && (context->fillColor().alpha() == 255)); +} + +bool windowsCanHandleTextDrawing(GraphicsContext* context) +{ + // Check for non-translation transforms. Sometimes zooms will look better in + // Skia, and sometimes better in Windows. The main problem is that zooming + // in using Skia will show you the hinted outlines for the smaller size, + // which look weird. All else being equal, it's better to use Windows' text + // drawing, so we don't check for zooms. + const AffineTransform& matrix = context->getCTM(); + if (matrix.b() != 0 || matrix.c() != 0) // Check for skew. + return false; + + // Check for stroke effects. + if (context->platformContext()->getTextDrawingMode() != TextModeFill) + return false; + + // Check for gradients. + if (context->fillGradient() || context->strokeGradient()) + return false; + + // Check for patterns. + if (context->fillPattern() || context->strokePattern()) + return false; + + // Check for shadow effects. + if (context->platformContext()->getDrawLooper() && (!windowsCanHandleDrawTextShadow(context))) + return false; + + return true; +} + +// Draws the given text string using skia. Note that gradient or +// pattern may be NULL, in which case a solid colour is used. +static bool skiaDrawText(HFONT hfont, + HDC dc, + SkCanvas* canvas, + const SkPoint& point, + SkPaint* paint, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + int numGlyphs) +{ + float x = point.fX, y = point.fY; + + for (int i = 0; i < numGlyphs; i++) { + const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]); + if (!path) + return false; + + float offsetX = 0.0f, offsetY = 0.0f; + if (offsets && (offsets[i].du != 0 || offsets[i].dv != 0)) { + offsetX = offsets[i].du; + offsetY = offsets[i].dv; + } + + SkPath newPath; + newPath.addPath(*path, x + offsetX, y + offsetY); + canvas->drawPath(newPath, *paint); + + x += advances[i]; + } + + return true; +} + +bool paintSkiaText(GraphicsContext* context, + HFONT hfont, + int numGlyphs, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + const SkPoint* origin) +{ + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, hfont); + + PlatformContextSkia* platformContext = context->platformContext(); + TextDrawingModeFlags textMode = platformContext->getTextDrawingMode(); + + // Filling (if necessary). This is the common case. + SkPaint paint; + platformContext->setupPaintForFilling(&paint); + paint.setFlags(SkPaint::kAntiAlias_Flag); + bool didFill = false; + + if ((textMode & TextModeFill) && SkColorGetA(paint.getColor())) { + if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, + &glyphs[0], &advances[0], &offsets[0], numGlyphs)) + return false; + didFill = true; + } + + // Stroking on top (if necessary). + if ((textMode & TextModeStroke) + && platformContext->getStrokeStyle() != NoStroke + && platformContext->getStrokeThickness() > 0) { + + paint.reset(); + platformContext->setupPaintForStroking(&paint, 0, 0); + paint.setFlags(SkPaint::kAntiAlias_Flag); + if (didFill) { + // If there is a shadow and we filled above, there will already be + // a shadow. We don't want to draw it again or it will be too dark + // and it will go on top of the fill. + // + // Note that this isn't strictly correct, since the stroke could be + // very thick and the shadow wouldn't account for this. The "right" + // thing would be to draw to a new layer and then draw that layer + // with a shadow. But this is a lot of extra work for something + // that isn't normally an issue. + SkSafeUnref(paint.setLooper(0)); + } + + if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, + &glyphs[0], &advances[0], &offsets[0], numGlyphs)) + return false; + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/SkiaFontWin.h b/Source/WebCore/platform/graphics/skia/SkiaFontWin.h new file mode 100644 index 0000000..40bee62 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/SkiaFontWin.h @@ -0,0 +1,94 @@ +/* + * 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: + * + * * 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 SkiaFontWin_h +#define SkiaFontWin_h + +#include <windows.h> +#include <usp10.h> + +class SkPath; +class SkPoint; + +namespace WebCore { + +class GraphicsContext; +class PlatformContextSkia; + +// FIXME: Rename file to SkiaWinOutlineCache +class SkiaWinOutlineCache { +public: + static const SkPath* lookupOrCreatePathForGlyph(HDC, HFONT, WORD); + // Removes any cached glyphs from the outline cache corresponding to the + // given font handle. + static void removePathsForFont(HFONT); + +private: + SkiaWinOutlineCache(); +}; + +// The functions below are used for more complex font drawing (effects such as +// stroking and more complex transforms) than Windows supports directly. Since +// Windows drawing is faster you should use windowsCanHandleTextDrawing first to +// check if using Skia is required at all. +// Note that the text will look different (no ClearType) so this should only be +// used when necessary. +// +// When you call a Skia* text drawing function, various glyph outlines will be +// cached. As a result, you should call SkiaWinOutlineCache::removePathsForFont +// when the font is destroyed so that the cache does not outlive the font (since +// the HFONTs are recycled). +// +// Remember that Skia's text drawing origin is the baseline, like WebKit, not +// the top, like Windows. + +// Returns true if the fillColor and shadowColor are opaque and the text-shadow +// is not blurred. +bool windowsCanHandleDrawTextShadow(GraphicsContext*); + +// Returns true if advanced font rendering is recommended. +bool windowsCanHandleTextDrawing(GraphicsContext*); + +// Note that the offsets parameter is optional. If not NULL it represents a +// per glyph offset (such as returned by ScriptPlace Windows API function). +// +// Returns true of the text was drawn successfully. False indicates an error +// from Windows. +bool paintSkiaText(GraphicsContext* graphicsContext, + HFONT hfont, + int numGlyphs, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + const SkPoint* origin); + +} // namespace WebCore + +#endif // SkiaFontWin_h diff --git a/Source/WebCore/platform/graphics/skia/SkiaUtils.cpp b/Source/WebCore/platform/graphics/skia/SkiaUtils.cpp new file mode 100644 index 0000000..da83793 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/SkiaUtils.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2006,2007,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: + * + * * 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 "SkiaUtils.h" + +#include "ImageBuffer.h" +#include "SharedBuffer.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkMatrix.h" +#include "SkRegion.h" +#include "SkUnPreMultiply.h" + +namespace WebCore { + +#if PLATFORM(ANDROID) +static const struct CompositOpToSkiaMode { + uint8_t mCompositOp; + uint8_t mMode; +} gMapCompositOpsToSkiaModes[] = { + { CompositeClear, SkXfermode::kClear_Mode }, + { CompositeCopy, SkXfermode::kSrc_Mode }, + { CompositeSourceOver, SkXfermode::kSrcOver_Mode }, + { CompositeSourceIn, SkXfermode::kSrcIn_Mode }, + { CompositeSourceOut, SkXfermode::kSrcOut_Mode }, + { CompositeSourceAtop, SkXfermode::kSrcATop_Mode }, + { CompositeDestinationOver, SkXfermode::kDstOver_Mode }, + { CompositeDestinationIn, SkXfermode::kDstIn_Mode }, + { CompositeDestinationOut, SkXfermode::kDstOut_Mode }, + { CompositeDestinationAtop, SkXfermode::kDstATop_Mode }, + { CompositeXOR, SkXfermode::kXor_Mode }, + // need more details on the composite modes to be sure these are right + { CompositePlusDarker, SkXfermode::kDarken_Mode }, + { CompositeHighlight, SkXfermode::kSrcOver_Mode }, // TODO + { CompositePlusLighter, SkXfermode::kPlus_Mode } +}; + +SkXfermode::Mode WebCoreCompositeToSkiaCOmposite(CompositeOperator op) +{ + const CompositOpToSkiaMode* table = gMapCompositOpsToSkiaModes; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToSkiaModes); i++) { + if (table[i].mCompositOp == op) + return (SkXfermode::Mode)table[i].mMode; + } + + SkDEBUGF(("GraphicsContext::setCompositeOperation uknown CompositeOperator %d\n", op)); + return SkXfermode::kSrcOver_Mode; // fall-back +} + +#endif + +static const struct CompositOpToXfermodeMode { + uint8_t mCompositOp; + uint8_t m_xfermodeMode; +} gMapCompositOpsToXfermodeModes[] = { + { CompositeClear, SkXfermode::kClear_Mode }, + { CompositeCopy, SkXfermode::kSrc_Mode }, + { CompositeSourceOver, SkXfermode::kSrcOver_Mode }, + { CompositeSourceIn, SkXfermode::kSrcIn_Mode }, + { CompositeSourceOut, SkXfermode::kSrcOut_Mode }, + { CompositeSourceAtop, SkXfermode::kSrcATop_Mode }, + { CompositeDestinationOver, SkXfermode::kDstOver_Mode }, + { CompositeDestinationIn, SkXfermode::kDstIn_Mode }, + { CompositeDestinationOut, SkXfermode::kDstOut_Mode }, + { CompositeDestinationAtop, SkXfermode::kDstATop_Mode }, + { CompositeXOR, SkXfermode::kXor_Mode }, + { CompositePlusDarker, SkXfermode::kDarken_Mode }, + { CompositeHighlight, SkXfermode::kSrcOver_Mode }, // TODO + { CompositePlusLighter, SkXfermode::kPlus_Mode } +}; + +SkXfermode::Mode WebCoreCompositeToSkiaComposite(CompositeOperator op) +{ + const CompositOpToXfermodeMode* table = gMapCompositOpsToXfermodeModes; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToXfermodeModes); i++) { + if (table[i].mCompositOp == op) + return (SkXfermode::Mode)table[i].m_xfermodeMode; + } + + SkDEBUGF(("GraphicsContext::setPlatformCompositeOperation unknown CompositeOperator %d\n", op)); + return SkXfermode::kSrcOver_Mode; // fall-back +} + +#if PLATFORM(ANDROID) +Color SkPMColorToWebCoreColor(SkPMColor pm) +{ + SkColor c = SkUnPreMultiply::PMColorToColor(pm); + // need the cast to find the right constructor + return WebCore::Color((int)SkColorGetR(c), (int)SkColorGetG(c), + (int)SkColorGetB(c), (int)SkColorGetA(c)); +} +#else +static U8CPU InvScaleByte(U8CPU component, uint32_t scale) +{ + SkASSERT(component == (uint8_t)component); + return (component * scale + 0x8000) >> 16; +} + +SkColor SkPMColorToColor(SkPMColor pm) +{ + if (!pm) + return 0; + unsigned a = SkGetPackedA32(pm); + if (!a) { + // A zero alpha value when there are non-zero R, G, or B channels is an + // invalid premultiplied color (since all channels should have been + // multiplied by 0 if a=0). + SkASSERT(false); + // In production, return 0 to protect against division by zero. + return 0; + } + + uint32_t scale = (255 << 16) / a; + + return SkColorSetARGB(a, + InvScaleByte(SkGetPackedR32(pm), scale), + InvScaleByte(SkGetPackedG32(pm), scale), + InvScaleByte(SkGetPackedB32(pm), scale)); +} + +Color SkPMColorToWebCoreColor(SkPMColor pm) +{ + return SkPMColorToColor(pm); +} +#endif + +void IntersectRectAndRegion(const SkRegion& region, const SkRect& srcRect, SkRect* destRect) { + // The cliperator requires an int rect, so we round out. + SkIRect srcRectRounded; + srcRect.roundOut(&srcRectRounded); + + // The Cliperator will iterate over a bunch of rects where our transformed + // rect and the clipping region (which may be non-square) overlap. + SkRegion::Cliperator cliperator(region, srcRectRounded); + if (cliperator.done()) { + destRect->setEmpty(); + return; + } + + // Get the union of all visible rects in the clip that overlap our bitmap. + SkIRect currentVisibleRect = cliperator.rect(); + cliperator.next(); + while (!cliperator.done()) { + currentVisibleRect.join(cliperator.rect()); + cliperator.next(); + } + + destRect->set(currentVisibleRect); +} + +void ClipRectToCanvas(const SkCanvas& canvas, const SkRect& srcRect, SkRect* destRect) { + // Translate into the canvas' coordinate space. This is where the clipping + // region applies. + SkRect transformedSrc; + canvas.getTotalMatrix().mapRect(&transformedSrc, srcRect); + + // Do the intersection. + SkRect transformedDest; + IntersectRectAndRegion(canvas.getTotalClip(), transformedSrc, &transformedDest); + + // Now transform it back into world space. + SkMatrix inverseTransform; + canvas.getTotalMatrix().invert(&inverseTransform); + inverseTransform.mapRect(destRect, transformedDest); +} + +bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath::FillType ft) +{ + SkRegion rgn; + SkRegion clip; + + SkPath::FillType originalFillType = originalPath->getFillType(); + + const SkPath* path = originalPath; + SkPath scaledPath; + int scale = 1; + + SkRect bounds = originalPath->getBounds(); + + // We can immediately return false if the point is outside the bounding + // rect. We don't use bounds.contains() here, since it would exclude + // points on the right and bottom edges of the bounding rect, and we want + // to include them. + SkScalar fX = SkFloatToScalar(point.x()); + SkScalar fY = SkFloatToScalar(point.y()); + if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop || fY > bounds.fBottom) + return false; + + originalPath->setFillType(ft); + + // Skia has trouble with coordinates close to the max signed 16-bit values + // If we have those, we need to scale. + // + // TODO: remove this code once Skia is patched to work properly with large + // values + const SkScalar kMaxCoordinate = SkIntToScalar(1<<15); + SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); + + if (biggestCoord > kMaxCoordinate) { + scale = SkScalarCeil(SkScalarDiv(biggestCoord, kMaxCoordinate)); + + SkMatrix m; + m.setScale(SkScalarInvert(SkIntToScalar(scale)), SkScalarInvert(SkIntToScalar(scale))); + originalPath->transform(m, &scaledPath); + path = &scaledPath; + } + + int x = static_cast<int>(floorf(point.x() / scale)); + int y = static_cast<int>(floorf(point.y() / scale)); + clip.setRect(x - 1, y - 1, x + 1, y + 1); + + bool contains = rgn.setPath(*path, clip); + + originalPath->setFillType(originalFillType); + return contains; +} + +GraphicsContext* scratchContext() +{ + static ImageBuffer* scratch = ImageBuffer::create(IntSize(1, 1)).leakPtr(); + // We don't bother checking for failure creating the ImageBuffer, since our + // ImageBuffer initializer won't fail. + return scratch->context(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/skia/SkiaUtils.h b/Source/WebCore/platform/graphics/skia/SkiaUtils.h new file mode 100644 index 0000000..681dcd5 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/SkiaUtils.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006,2007,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: + * + * * 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. + */ + +// All of the functions in this file should move to new homes and this file should be deleted. + +#ifndef SkiaUtils_h +#define SkiaUtils_h + +#include <wtf/MathExtras.h> +#include "GraphicsContext.h" +#include "SkPath.h" +#include "SkXfermode.h" + +class SkCanvas; +class SkRegion; + +namespace WebCore { + +SkXfermode::Mode WebCoreCompositeToSkiaComposite(CompositeOperator); + +// move this guy into SkColor.h +SkColor SkPMColorToColor(SkPMColor); + +// This should be an operator on Color +Color SkPMColorToWebCoreColor(SkPMColor); + +// Skia has problems when passed infinite, etc floats, filter them to 0. +inline SkScalar WebCoreFloatToSkScalar(float f) +{ + return SkFloatToScalar(isfinite(f) ? f : 0); +} + +inline SkScalar WebCoreDoubleToSkScalar(double d) +{ + return SkDoubleToScalar(isfinite(d) ? d : 0); +} + +// Computes the smallest rectangle that, which when drawn to the given canvas, +// will cover the same area as the source rectangle. It will clip to the canvas' +// clip, doing the necessary coordinate transforms. +// +// srcRect and destRect can be the same. +void ClipRectToCanvas(const SkCanvas&, const SkRect& srcRect, SkRect* destRect); + +// Determine if a given WebKit point is contained in a path +bool SkPathContainsPoint(SkPath*, const FloatPoint&, SkPath::FillType); + +// Returns a statically allocated 1x1 GraphicsContext intended for temporary +// operations. Please save() the state and restore() it when you're done with +// the context. +GraphicsContext* scratchContext(); + +} // namespace WebCore + +#endif // SkiaUtils_h diff --git a/Source/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp b/Source/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp new file mode 100644 index 0000000..dc610d7 --- /dev/null +++ b/Source/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp @@ -0,0 +1,78 @@ +// 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: +// +// * 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 "AffineTransform.h" +#include "TransformationMatrix.h" + +#include "SkiaUtils.h" + +namespace WebCore { + +TransformationMatrix::operator SkMatrix() const +{ + SkMatrix result; + + result.setScaleX(WebCoreDoubleToSkScalar(a())); + result.setSkewX(WebCoreDoubleToSkScalar(c())); + result.setTranslateX(WebCoreDoubleToSkScalar(e())); + + result.setScaleY(WebCoreDoubleToSkScalar(d())); + result.setSkewY(WebCoreDoubleToSkScalar(b())); + result.setTranslateY(WebCoreDoubleToSkScalar(f())); + + // FIXME: Set perspective properly. + result.setPerspX(0); + result.setPerspY(0); + result.set(SkMatrix::kMPersp2, SK_Scalar1); + + return result; +} + +AffineTransform::operator SkMatrix() const +{ + SkMatrix result; + + result.setScaleX(WebCoreDoubleToSkScalar(a())); + result.setSkewX(WebCoreDoubleToSkScalar(c())); + result.setTranslateX(WebCoreDoubleToSkScalar(e())); + + result.setScaleY(WebCoreDoubleToSkScalar(d())); + result.setSkewY(WebCoreDoubleToSkScalar(b())); + result.setTranslateY(WebCoreDoubleToSkScalar(f())); + + // FIXME: Set perspective properly. + result.setPerspX(0); + result.setPerspY(0); + result.set(SkMatrix::kMPersp2, SK_Scalar1); + + return result; +} + +} // namespace WebCore |
