summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/skia
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/graphics/skia
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/graphics/skia')
-rw-r--r--Source/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h87
-rw-r--r--Source/WebCore/platform/graphics/skia/FloatPointSkia.cpp51
-rw-r--r--Source/WebCore/platform/graphics/skia/FloatRectSkia.cpp50
-rw-r--r--Source/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp211
-rw-r--r--Source/WebCore/platform/graphics/skia/FontCustomPlatformData.h82
-rw-r--r--Source/WebCore/platform/graphics/skia/GlyphPageTreeNodeSkia.cpp115
-rw-r--r--Source/WebCore/platform/graphics/skia/GradientSkia.cpp196
-rw-r--r--Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp90
-rw-r--r--Source/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h55
-rw-r--r--Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp1262
-rw-r--r--Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp339
-rw-r--r--Source/WebCore/platform/graphics/skia/ImageSkia.cpp540
-rw-r--r--Source/WebCore/platform/graphics/skia/IntPointSkia.cpp56
-rw-r--r--Source/WebCore/platform/graphics/skia/IntRectSkia.cpp57
-rw-r--r--Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp140
-rw-r--r--Source/WebCore/platform/graphics/skia/NativeImageSkia.h113
-rw-r--r--Source/WebCore/platform/graphics/skia/PathSkia.cpp274
-rw-r--r--Source/WebCore/platform/graphics/skia/PatternSkia.cpp103
-rw-r--r--Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp890
-rw-r--r--Source/WebCore/platform/graphics/skia/PlatformContextSkia.h240
-rw-r--r--Source/WebCore/platform/graphics/skia/PlatformGraphics.h37
-rw-r--r--Source/WebCore/platform/graphics/skia/SkiaFontWin.cpp358
-rw-r--r--Source/WebCore/platform/graphics/skia/SkiaFontWin.h94
-rw-r--r--Source/WebCore/platform/graphics/skia/SkiaUtils.cpp257
-rw-r--r--Source/WebCore/platform/graphics/skia/SkiaUtils.h82
-rw-r--r--Source/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp78
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