summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/skia
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-05 14:34:32 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-05 14:34:32 -0800
commit635860845790a19bf50bbc51ba8fb66a96dde068 (patch)
treeef6ad9ff73a5b57f65249d4232a202fa77e6a140 /WebCore/platform/graphics/skia
parent8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (diff)
downloadexternal_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.zip
external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.gz
external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.bz2
auto import from //depot/cupcake/@136594
Diffstat (limited to 'WebCore/platform/graphics/skia')
-rw-r--r--WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h86
-rw-r--r--WebCore/platform/graphics/skia/FloatPointSkia.cpp51
-rw-r--r--WebCore/platform/graphics/skia/FloatRectSkia.cpp50
-rw-r--r--WebCore/platform/graphics/skia/GradientSkia.cpp164
-rw-r--r--WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h55
-rw-r--r--WebCore/platform/graphics/skia/GraphicsContextSkia.cpp1122
-rw-r--r--WebCore/platform/graphics/skia/ImageBufferSkia.cpp210
-rw-r--r--WebCore/platform/graphics/skia/ImageSkia.cpp462
-rw-r--r--WebCore/platform/graphics/skia/ImageSourceSkia.cpp247
-rw-r--r--WebCore/platform/graphics/skia/ImageSourceSkia.h60
-rw-r--r--WebCore/platform/graphics/skia/IntPointSkia.cpp56
-rw-r--r--WebCore/platform/graphics/skia/IntRectSkia.cpp57
-rw-r--r--WebCore/platform/graphics/skia/NativeImageSkia.cpp109
-rw-r--r--WebCore/platform/graphics/skia/NativeImageSkia.h104
-rw-r--r--WebCore/platform/graphics/skia/PathSkia.cpp316
-rw-r--r--WebCore/platform/graphics/skia/PatternSkia.cpp77
-rw-r--r--WebCore/platform/graphics/skia/PlatformContextSkia.cpp427
-rw-r--r--WebCore/platform/graphics/skia/PlatformContextSkia.h173
-rw-r--r--WebCore/platform/graphics/skia/PlatformGraphics.h37
-rw-r--r--WebCore/platform/graphics/skia/SkiaFontWin.cpp218
-rw-r--r--WebCore/platform/graphics/skia/SkiaFontWin.h54
-rw-r--r--WebCore/platform/graphics/skia/SkiaUtils.cpp199
-rw-r--r--WebCore/platform/graphics/skia/SkiaUtils.h82
-rw-r--r--WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp222
24 files changed, 4638 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h b/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h
new file mode 100644
index 0000000..5d85652
--- /dev/null
+++ b/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h
@@ -0,0 +1,86 @@
+/*
+ * 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, by copying the pixel values out of |bitmap|.
+ // If creation failed, returns null.
+ static PassRefPtr<BitmapImageSingleFrameSkia> create(const SkBitmap&);
+
+ 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, CompositeOperator);
+
+private:
+ NativeImageSkia m_nativeImage;
+
+ // Use create().
+ BitmapImageSingleFrameSkia() { }
+};
+
+} // namespace WebCore
+
+#endif // BitmapImageSingleFrameSkia_h
diff --git a/WebCore/platform/graphics/skia/FloatPointSkia.cpp b/WebCore/platform/graphics/skia/FloatPointSkia.cpp
new file mode 100644
index 0000000..054a772
--- /dev/null
+++ b/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/WebCore/platform/graphics/skia/FloatRectSkia.cpp b/WebCore/platform/graphics/skia/FloatRectSkia.cpp
new file mode 100644
index 0000000..a10371f
--- /dev/null
+++ b/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/WebCore/platform/graphics/skia/GradientSkia.cpp b/WebCore/platform/graphics/skia/GradientSkia.cpp
new file mode 100644
index 0000000..eff7c66
--- /dev/null
+++ b/WebCore/platform/graphics/skia/GradientSkia.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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)
+ m_gradient->safeUnref();
+ 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)
+{
+ const Gradient::ColorStop* stop = stopData;
+ size_t countUsed = count;
+ if (count < 1 || stop->stop > 0.0)
+ countUsed++;
+ stop += count - 1;
+ if (count < 2 || 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);
+
+ if (m_radial) {
+ // FIXME: CSS radial Gradients allow an offset focal point (the
+ // "start circle"), but skia doesn't seem to support that, so this just
+ // ignores m_p0/m_r0 and draws the gradient centered in the "end
+ // circle" (m_p1/m_r1).
+ // See http://webkit.org/blog/175/introducing-css-gradients/ for a
+ // description of the expected behavior.
+ m_gradient = SkGradientShader::CreateRadial(m_p1,
+ WebCoreFloatToSkScalar(m_r1), colors, pos,
+ static_cast<int>(countUsed), SkShader::kClamp_TileMode);
+ } else {
+ SkPoint pts[2] = { m_p0, m_p1 };
+ m_gradient = SkGradientShader::CreateLinear(pts, colors, pos,
+ static_cast<int>(countUsed), SkShader::kClamp_TileMode);
+ }
+
+ return m_gradient;
+}
+
+void Gradient::fill(GraphicsContext* context, const FloatRect& rect)
+{
+ context->setFillGradient(this);
+ context->fillRect(rect);
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h b/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h
new file mode 100644
index 0000000..29738f4
--- /dev/null
+++ b/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 : 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/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp
new file mode 100644
index 0000000..e6c7783
--- /dev/null
+++ b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp
@@ -0,0 +1,1122 @@
+/*
+ * 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 "GraphicsContextPlatformPrivate.h"
+#include "GraphicsContextPrivate.h"
+#include "Color.h"
+#include "FloatRect.h"
+#include "Gradient.h"
+#include "IntRect.h"
+#include "NativeImageSkia.h"
+#include "NotImplemented.h"
+#include "PlatformContextSkia.h"
+#include "TransformationMatrix.h"
+
+#include "SkBitmap.h"
+#include "SkBlurDrawLooper.h"
+#include "SkCornerPathEffect.h"
+#include "skia/ext/platform_canvas.h"
+#include "SkiaUtils.h"
+#include "SkShader.h"
+
+#include <math.h>
+#include <wtf/Assertions.h>
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+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.
+#define ENSURE_VALUE_SAFETY_FOR_SKIA
+
+static bool isCoordinateSkiaSafe(float coord)
+{
+#ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
+ // 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;
+#else
+ 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);
+}
+
+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
+
+// -----------------------------------------------------------------------------
+
+// This may be called with a NULL pointer to create a graphics context that has
+// no painting.
+GraphicsContext::GraphicsContext(PlatformGraphicsContext* gc)
+ : m_common(createGraphicsContextPrivate())
+ , m_data(new GraphicsContextPlatformPrivate(gc))
+{
+ setPaintingDisabled(!gc || !platformContext()->canvas());
+}
+
+GraphicsContext::~GraphicsContext()
+{
+ delete m_data;
+ this->destroyGraphicsContextPrivate(m_common);
+}
+
+PlatformGraphicsContext* GraphicsContext::platformContext() const
+{
+ ASSERT(!paintingDisabled());
+ return m_data->context();
+}
+
+// State saving ----------------------------------------------------------------
+
+void GraphicsContext::savePlatformState()
+{
+ if (paintingDisabled())
+ return;
+
+ // Save our private State.
+ platformContext()->save();
+}
+
+void GraphicsContext::restorePlatformState()
+{
+ if (paintingDisabled())
+ return;
+
+ // 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;
+
+#if PLATFORM(WIN_OS)
+ platformContext()->canvas()->getTopPlatformDevice().
+ fixupAlphaBeforeCompositing();
+#endif
+ platformContext()->canvas()->restore();
+}
+
+// Graphics primitives ---------------------------------------------------------
+
+void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect r(rect);
+ if (!isRectSkiaSafe(getCTM(), r))
+ return;
+
+ 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()) {
+ r.inset(SkIntToScalar(thickness) ,SkIntToScalar(thickness));
+ path.addOval(r, SkPath::kCCW_Direction);
+ }
+ platformContext()->canvas()->clipPath(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;
+
+ SkRect r = rect;
+ if (!isRectSkiaSafe(getCTM(), r))
+ ClipRectToCanvas(*platformContext()->canvas(), r, &r);
+
+ SkPaint paint;
+ platformContext()->setupPaintForFilling(&paint);
+ paint.setPorterDuffXfermode(SkPorterDuff::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()->canvas()->clipRect(r);
+}
+
+void GraphicsContext::clip(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+ const SkPath& p = *path.platformPath();
+ if (!isPathSkiaSafe(getCTM(), p))
+ return;
+
+ platformContext()->canvas()->clipPath(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::clipOutEllipseInRect(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect oval(rect);
+ if (!isRectSkiaSafe(getCTM(), oval))
+ return;
+
+ SkPath path;
+ path.addOval(oval, SkPath::kCCW_Direction);
+ platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op);
+}
+
+void GraphicsContext::clipPath(WindRule clipRule)
+{
+ if (paintingDisabled())
+ return;
+
+ const SkPath* oldPath = platformContext()->currentPath();
+ SkPath path(*oldPath);
+ path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
+ platformContext()->canvas()->clipPath(path);
+}
+
+void GraphicsContext::clipToImageBuffer(const FloatRect& rect,
+ const ImageBuffer* imageBuffer)
+{
+ if (paintingDisabled())
+ return;
+
+ // FIXME: This is needed for image masking and complex text fills.
+ notImplemented();
+}
+
+void GraphicsContext::concatCTM(const TransformationMatrix& xform)
+{
+ if (paintingDisabled())
+ return;
+ platformContext()->canvas()->concat(xform);
+}
+
+void GraphicsContext::drawConvexPolygon(size_t numPoints,
+ const FloatPoint* points,
+ bool shouldAntialias)
+{
+ if (paintingDisabled())
+ return;
+
+ if (numPoints <= 1)
+ return;
+
+ 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;
+ if (fillColor().alpha() > 0) {
+ platformContext()->setupPaintForFilling(&paint);
+ platformContext()->canvas()->drawPath(path, paint);
+ }
+
+ if (strokeStyle() != NoStroke) {
+ paint.reset();
+ platformContext()->setupPaintForStroking(&paint, 0, 0);
+ platformContext()->canvas()->drawPath(path, paint);
+ }
+}
+
+// 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;
+
+ SkPaint paint;
+ if (fillColor().alpha() > 0) {
+ 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 Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ const Vector<IntRect>& rects = focusRingRects();
+ unsigned rectCount = rects.size();
+ if (0 == rectCount)
+ return;
+
+ 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(focusRingColor().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;
+ SkPoint pts[2] = { (SkPoint)point1, (SkPoint)point2 };
+ if (!isPointSkiaSafe(getCTM(), pts[0]) || !isPointSkiaSafe(getCTM(), pts[1]))
+ return;
+
+ // 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.
+ SkPoint disp = pts[1] - pts[0];
+ int length = SkScalarRound(disp.fX + disp.fY);
+ int width = roundf(
+ platformContext()->setupPaintForStroking(&paint, 0, length));
+
+ // "Borrowed" this comment and idea from GraphicsContextCG.cpp
+ // For odd widths, we add in 0.5 to the appropriate x/y so that the float
+ // arithmetic works out. For example, with a border width of 3, KHTML will
+ // pass us (y1+y2)/2, e.g., (50+53)/2 = 103/2 = 51 when we want 51.5. It is
+ // always true that an even width gave us a perfect position, but an odd
+ // width gave us a position that is off by exactly 0.5.
+ bool isVerticalLine = pts[0].fX == pts[1].fX;
+
+ if (width & 1) { // Odd.
+ if (isVerticalLine) {
+ pts[0].fX = pts[0].fX + SK_ScalarHalf;
+ pts[1].fX = pts[0].fX;
+ } else { // Horizontal line
+ pts[0].fY = pts[0].fY + SK_ScalarHalf;
+ pts[1].fY = pts[0].fY;
+ }
+ }
+ platformContext()->canvas()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
+}
+
+void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& pt,
+ int width,
+ bool grammar)
+{
+ if (paintingDisabled())
+ return;
+
+ // 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;
+
+ 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;
+ paint.setColor(strokeColor().rgb());
+ platformContext()->canvas()->drawRect(r, paint);
+}
+
+// Draws a filled rectangle with a stroked border.
+void GraphicsContext::drawRect(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect r = rect;
+ if (!isRectSkiaSafe(getCTM(), r))
+ // See the fillRect below.
+ ClipRectToCanvas(*platformContext()->canvas(), r, &r);
+
+ platformContext()->drawRect(r);
+}
+
+void GraphicsContext::fillPath()
+{
+ if (paintingDisabled())
+ return;
+
+ const SkPath& path = *platformContext()->currentPath();
+ if (!isPathSkiaSafe(getCTM(), path))
+ return;
+
+ const GraphicsContextState& state = m_common->state;
+ ColorSpace colorSpace = state.fillColorSpace;
+
+ if (colorSpace == SolidColorSpace && !fillColor().alpha())
+ return;
+
+ platformContext()->setFillRule(state.fillRule == RULE_EVENODD ?
+ SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
+
+ SkPaint paint;
+ platformContext()->setupPaintForFilling(&paint);
+
+ if (colorSpace == PatternColorSpace) {
+ SkShader* pat = state.fillPattern->createPlatformPattern(getCTM());
+ paint.setShader(pat);
+ pat->unref();
+ } else if (colorSpace == GradientColorSpace)
+ paint.setShader(state.fillGradient->platformGradient());
+
+ 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);
+
+ const GraphicsContextState& state = m_common->state;
+ ColorSpace colorSpace = state.fillColorSpace;
+
+ if (colorSpace == SolidColorSpace && !fillColor().alpha())
+ return;
+
+ SkPaint paint;
+ platformContext()->setupPaintForFilling(&paint);
+
+ if (colorSpace == PatternColorSpace) {
+ SkShader* pat = state.fillPattern->createPlatformPattern(getCTM());
+ paint.setShader(pat);
+ pat->unref();
+ } else if (colorSpace == GradientColorSpace)
+ paint.setShader(state.fillGradient->platformGradient());
+
+ platformContext()->canvas()->drawRect(r, paint);
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ if (!color.alpha())
+ return;
+
+ 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)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect r = rect;
+ if (!isRectSkiaSafe(getCTM(), r))
+ // See fillRect().
+ ClipRectToCanvas(*platformContext()->canvas(), r, &r);
+
+ 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);
+ return fillRect(rect, color);
+}
+
+TransformationMatrix GraphicsContext::getCTM() const
+{
+ return platformContext()->canvas()->getTotalMatrix();
+}
+
+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() != 0)
+ deviceLowerRight.move(0, 1);
+ if (deviceOrigin.x() == deviceLowerRight.x() && rect.width() != 0)
+ 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;
+ platformContext()->canvas()->scale(WebCoreFloatToSkScalar(size.width()),
+ WebCoreFloatToSkScalar(size.height()));
+}
+
+void GraphicsContext::setAlpha(float alpha)
+{
+ if (paintingDisabled())
+ return;
+ platformContext()->setAlpha(alpha);
+}
+
+void GraphicsContext::setCompositeOperation(CompositeOperator op)
+{
+ if (paintingDisabled())
+ return;
+ platformContext()->setPorterDuffMode(WebCoreCompositeToSkiaComposite(op));
+}
+
+void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
+{
+ notImplemented();
+}
+
+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)
+ return;
+
+ size_t count = (dashLength % 2) == 0 ? 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)
+{
+ if (paintingDisabled())
+ return;
+ platformContext()->setFillColor(color.rgb());
+}
+
+void GraphicsContext::setPlatformShadow(const IntSize& size,
+ int blurInt,
+ const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ double width = size.width();
+ double height = size.height();
+ double blur = blurInt;
+
+ // TODO(tc): This still does not address the issue that shadows
+ // within canvas elements should ignore transforms.
+ if (m_common->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.
+ // 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);
+ platformContext()->setDrawLooper(dl);
+ dl->unref();
+}
+
+void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor)
+{
+ if (paintingDisabled())
+ return;
+
+ platformContext()->setStrokeColor(strokecolor.rgb());
+}
+
+void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& stroke)
+{
+ if (paintingDisabled())
+ return;
+
+ platformContext()->setStrokeStyle(stroke);
+}
+
+void GraphicsContext::setPlatformStrokeThickness(float thickness)
+{
+ if (paintingDisabled())
+ return;
+
+ platformContext()->setStrokeThickness(thickness);
+}
+
+void GraphicsContext::setPlatformTextDrawingMode(int 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;
+
+ 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()
+{
+ if (paintingDisabled())
+ return;
+
+ const SkPath& path = *platformContext()->currentPath();
+ if (!isPathSkiaSafe(getCTM(), path))
+ return;
+
+ const GraphicsContextState& state = m_common->state;
+ ColorSpace colorSpace = state.strokeColorSpace;
+
+ if (colorSpace == SolidColorSpace && !strokeColor().alpha())
+ return;
+
+ SkPaint paint;
+ platformContext()->setupPaintForStroking(&paint, 0, 0);
+
+ if (colorSpace == PatternColorSpace) {
+ SkShader* pat = state.strokePattern->createPlatformPattern(getCTM());
+ paint.setShader(pat);
+ pat->unref();
+ } else if (colorSpace == GradientColorSpace)
+ paint.setShader(state.strokeGradient->platformGradient());
+
+ platformContext()->canvas()->drawPath(path, paint);
+}
+
+void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
+{
+ if (paintingDisabled())
+ return;
+
+ if (!isRectSkiaSafe(getCTM(), rect))
+ return;
+
+ const GraphicsContextState& state = m_common->state;
+ ColorSpace colorSpace = state.strokeColorSpace;
+
+ if (colorSpace == SolidColorSpace && !strokeColor().alpha())
+ return;
+
+ SkPaint paint;
+ platformContext()->setupPaintForStroking(&paint, 0, 0);
+ paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
+
+ if (colorSpace == PatternColorSpace) {
+ SkShader* pat = state.strokePattern->createPlatformPattern(getCTM());
+ paint.setShader(pat);
+ pat->unref();
+ } else if (colorSpace == GradientColorSpace)
+ paint.setShader(state.strokeGradient->platformGradient());
+
+ platformContext()->canvas()->drawRect(rect, paint);
+}
+
+void GraphicsContext::rotate(float angleInRadians)
+{
+ if (paintingDisabled())
+ return;
+
+ platformContext()->canvas()->rotate(WebCoreFloatToSkScalar(
+ angleInRadians * (180.0f / 3.14159265f)));
+}
+
+void GraphicsContext::translate(float w, float h)
+{
+ if (paintingDisabled())
+ return;
+
+ platformContext()->canvas()->translate(WebCoreFloatToSkScalar(w),
+ WebCoreFloatToSkScalar(h));
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp
new file mode 100644
index 0000000..fdfcb85
--- /dev/null
+++ b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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 "ImageBuffer.h"
+
+#include "BitmapImage.h"
+#include "BitmapImageSingleFrameSkia.h"
+#include "GraphicsContext.h"
+#include "ImageData.h"
+#include "NotImplemented.h"
+#include "PlatformContextSkia.h"
+#include "SkiaUtils.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, bool grayScale, 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));
+
+ // 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, SkPorterDuff::kClear_Mode);
+ success = true;
+}
+
+ImageBuffer::~ImageBuffer()
+{
+}
+
+GraphicsContext* ImageBuffer::context() const
+{
+ return m_context.get();
+}
+
+Image* ImageBuffer::image() const
+{
+ if (!m_image) {
+ // This creates a COPY of the image and will cache that copy. This means
+ // that if subsequent operations take place on the context, neither the
+ // currently-returned image, nor the results of future image() calls,
+ // will contain that operation.
+ //
+ // This seems silly, but is the way the CG port works: image() is
+ // intended to be used only when rendering is "complete."
+ m_image = BitmapImageSingleFrameSkia::create(
+ *m_data.m_platformContext.bitmap());
+ }
+ return m_image.get();
+}
+
+PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const
+{
+ ASSERT(context());
+
+ RefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
+ unsigned char* data = result->data()->data();
+
+ if (rect.x() < 0 || rect.y() < 0 ||
+ (rect.x() + rect.width()) > m_size.width() ||
+ (rect.y() + rect.height()) > m_size.height())
+ memset(data, 0, result->data()->length());
+
+ int originX = rect.x();
+ int destX = 0;
+ if (originX < 0) {
+ destX = -originX;
+ originX = 0;
+ }
+ int endX = rect.x() + rect.width();
+ if (endX > m_size.width())
+ endX = m_size.width();
+ int numColumns = endX - originX;
+
+ int originY = rect.y();
+ int destY = 0;
+ if (originY < 0) {
+ destY = -originY;
+ originY = 0;
+ }
+ int endY = rect.y() + rect.height();
+ if (endY > m_size.height())
+ endY = m_size.height();
+ int numRows = endY - originY;
+
+ const SkBitmap& bitmap = *context()->platformContext()->bitmap();
+ 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) {
+ SkColor color = SkPMColorToColor(srcRow[x]);
+ unsigned char* destPixel = &destRow[x * 4];
+ destPixel[0] = SkColorGetR(color);
+ destPixel[1] = SkColorGetG(color);
+ destPixel[2] = SkColorGetB(color);
+ destPixel[3] = SkColorGetA(color);
+ }
+ destRow += destBytesPerRow;
+ }
+
+ return result;
+}
+
+void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect,
+ const IntPoint& destPoint)
+{
+ ASSERT(sourceRect.width() > 0);
+ ASSERT(sourceRect.height() > 0);
+
+ int originX = sourceRect.x();
+ int destX = destPoint.x() + sourceRect.x();
+ ASSERT(destX >= 0);
+ ASSERT(destX < m_size.width());
+ ASSERT(originX >= 0);
+ ASSERT(originX < sourceRect.right());
+
+ int endX = destPoint.x() + sourceRect.right();
+ ASSERT(endX <= m_size.width());
+
+ int numColumns = endX - destX;
+
+ int originY = sourceRect.y();
+ int destY = destPoint.y() + sourceRect.y();
+ ASSERT(destY >= 0);
+ ASSERT(destY < m_size.height());
+ ASSERT(originY >= 0);
+ ASSERT(originY < sourceRect.bottom());
+
+ int endY = destPoint.y() + sourceRect.bottom();
+ ASSERT(endY <= m_size.height());
+ int numRows = endY - destY;
+
+ const SkBitmap& bitmap = *context()->platformContext()->bitmap();
+ ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
+ SkAutoLockPixels bitmapLock(bitmap);
+
+ unsigned srcBytesPerRow = 4 * source->width();
+
+ const unsigned char* srcRow = source->data()->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];
+ destRow[x] = SkPreMultiplyARGB(srcPixel[3], srcPixel[0],
+ srcPixel[1], srcPixel[2]);
+ }
+ srcRow += srcBytesPerRow;
+ }
+}
+
+String ImageBuffer::toDataURL(const String&) const
+{
+ notImplemented();
+ return String();
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/skia/ImageSkia.cpp b/WebCore/platform/graphics/skia/ImageSkia.cpp
new file mode 100644
index 0000000..1123fe9
--- /dev/null
+++ b/WebCore/platform/graphics/skia/ImageSkia.cpp
@@ -0,0 +1,462 @@
+/*
+ * 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 "BitmapImage.h"
+#include "BitmapImageSingleFrameSkia.h"
+#include "ChromiumBridge.h"
+#include "FloatConversion.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "Logging.h"
+#include "NativeImageSkia.h"
+#include "NotImplemented.h"
+#include "PlatformContextSkia.h"
+#include "PlatformString.h"
+#include "SkiaUtils.h"
+#include "SkShader.h"
+#include "TransformationMatrix.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(const NativeImageSkia& bitmap, int srcWidth, int srcHeight, float destWidth, float destHeight)
+{
+ 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.
+ return RESAMPLE_AWESOME;
+}
+
+// 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.
+ resizedImageRect.set(0, 0, destRectRounded.width(), destRectRounded.height());
+
+ if (srcIsFull && bitmap.hasResizedBitmap(destRectRounded.width(), destRectRounded.height())) {
+ // Yay, this bitmap frame already has a resized version.
+ SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), destRectRounded.height());
+ canvas.drawBitmapRect(resampled, 0, destRect, &paint);
+ return;
+ }
+
+ // Compute the visible portion of our rect.
+ SkRect destBitmapSubsetSk;
+ ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk);
+ destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.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(destRectRounded.width(),
+ destRectRounded.height());
+ canvas.drawBitmapRect(resampled, 0, destRect, &paint);
+ } else {
+ // We should only resize the exposed part of the bitmap to do the
+ // minimal possible work.
+ gfx::Rect destBitmapSubset(destBitmapSubsetSkI.fLeft,
+ destBitmapSubsetSkI.fTop,
+ destBitmapSubsetSkI.width(),
+ destBitmapSubsetSkI.height());
+
+ // Resample the needed part of the image.
+ SkBitmap resampled = skia::ImageOperations::Resize(subset,
+ skia::ImageOperations::RESIZE_LANCZOS3,
+ destRectRounded.width(), destRectRounded.height(),
+ destBitmapSubset);
+
+ // 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.
+ SkRect offsetDestRect = {
+ destBitmapSubset.x() + destRect.fLeft,
+ destBitmapSubset.y() + destRect.fTop,
+ destBitmapSubset.right() + destRect.fLeft,
+ destBitmapSubset.bottom() + destRect.fTop };
+
+ canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint);
+ }
+}
+
+static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkPorterDuff::Mode& compOp)
+{
+ SkPaint paint;
+ paint.setPorterDuffXfermode(compOp);
+
+ skia::PlatformCanvas* canvas = platformContext->canvas();
+
+ ResamplingMode resampling = platformContext->isPrinting() ? RESAMPLE_NONE :
+ computeResamplingMode(bitmap, srcRect.width(), srcRect.height(),
+ SkScalarToFloat(destRect.width()),
+ SkScalarToFloat(destRect.height()));
+ if (resampling == RESAMPLE_AWESOME) {
+ paint.setFilterBitmap(false);
+ 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.
+ paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
+ 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;
+}
+
+PassRefPtr<Image> Image::loadPlatformResource(const char *name)
+{
+ return ChromiumBridge::loadPlatformImageResource(name);
+}
+
+void Image::drawPattern(GraphicsContext* context,
+ const FloatRect& floatSrcRect,
+ const TransformationMatrix& patternTransform,
+ const FloatPoint& phase,
+ CompositeOperator compositeOp,
+ const FloatRect& destRect)
+{
+ if (destRect.isEmpty() || floatSrcRect.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(floatSrcRect);
+ 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(*bitmap,
+ srcRect.width(), srcRect.height(),
+ destBitmapWidth, destBitmapHeight);
+ }
+
+ // Load the transform WebKit requested.
+ SkMatrix matrix(patternTransform);
+
+ if (resampling == RESAMPLE_AWESOME) {
+ // Do nice resampling.
+ SkBitmap resampled = skia::ImageOperations::Resize(srcSubset,
+ skia::ImageOperations::RESIZE_LANCZOS3,
+ static_cast<int>(destBitmapWidth),
+ static_cast<int>(destBitmapHeight));
+ 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() + floatSrcRect.x() *
+ narrowPrecisionToFloat(patternTransform.a());
+ float adjustedY = phase.y() + floatSrcRect.y() *
+ narrowPrecisionToFloat(patternTransform.d());
+ matrix.postTranslate(SkFloatToScalar(adjustedX),
+ SkFloatToScalar(adjustedY));
+ shader->setLocalMatrix(matrix);
+
+ SkPaint paint;
+ paint.setShader(shader)->unref();
+ paint.setPorterDuffXfermode(WebCoreCompositeToSkiaComposite(compositeOp));
+ paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
+
+ context->platformContext()->paintSkPaint(destRect, paint);
+}
+
+// ================================================
+// 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()
+{
+}
+
+void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
+ const FloatRect& srcRect, 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();
+
+ const 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.
+
+ paintSkBitmap(ctxt->platformContext(),
+ *bm,
+ enclosingIntRect(normSrcRect),
+ enclosingIntRect(normDstRect),
+ WebCoreCompositeToSkiaComposite(compositeOp));
+}
+
+// FIXME: These should go into BitmapImageSingleFrameSkia.cpp
+
+void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt,
+ const FloatRect& dstRect,
+ const FloatRect& srcRect,
+ CompositeOperator compositeOp)
+{
+ FloatRect normDstRect = normalizeRect(dstRect);
+ FloatRect normSrcRect = normalizeRect(srcRect);
+
+ if (normSrcRect.isEmpty() || normDstRect.isEmpty())
+ return; // Nothing to draw.
+
+ paintSkBitmap(ctxt->platformContext(),
+ m_nativeImage,
+ enclosingIntRect(normSrcRect),
+ enclosingIntRect(normDstRect),
+ WebCoreCompositeToSkiaComposite(compositeOp));
+}
+
+PassRefPtr<BitmapImageSingleFrameSkia> BitmapImageSingleFrameSkia::create(const SkBitmap& bitmap)
+{
+ RefPtr<BitmapImageSingleFrameSkia> image(adoptRef(new BitmapImageSingleFrameSkia()));
+ if (!bitmap.copyTo(&image->m_nativeImage, bitmap.config()))
+ return 0;
+ return image.release();
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp
new file mode 100644
index 0000000..f77620b
--- /dev/null
+++ b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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 "ImageSourceSkia.h"
+#include "SharedBuffer.h"
+
+#include "GIFImageDecoder.h"
+#include "JPEGImageDecoder.h"
+#include "PNGImageDecoder.h"
+#include "BMPImageDecoder.h"
+#include "XBMImageDecoder.h"
+#include "ICOImageDecoder.h"
+
+#include "SkBitmap.h"
+
+namespace WebCore {
+
+ImageDecoder* createDecoder(const Vector<char>& data, const IntSize& preferredIconSize)
+{
+ // We need at least 4 bytes to figure out what kind of image we're dealing with.
+ int length = data.size();
+ if (length < 4)
+ return 0;
+
+ const unsigned char* uContents = (const unsigned char*)data.data();
+ const char* contents = data.data();
+
+ // GIFs begin with GIF8(7 or 9).
+ if (strncmp(contents, "GIF8", 4) == 0)
+ return new GIFImageDecoder();
+
+ // Test for PNG.
+ if (uContents[0]==0x89 &&
+ uContents[1]==0x50 &&
+ uContents[2]==0x4E &&
+ uContents[3]==0x47)
+ return new PNGImageDecoder();
+
+ // JPEG
+ if (uContents[0]==0xFF &&
+ uContents[1]==0xD8 &&
+ uContents[2]==0xFF)
+ return new JPEGImageDecoder();
+
+ // BMP
+ if (strncmp(contents, "BM", 2) == 0)
+ return new BMPImageDecoder();
+
+ // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
+ // CURs begin with 2-byte 0 followed by 2-byte 2.
+ if (!memcmp(contents, "\000\000\001\000", 4) ||
+ !memcmp(contents, "\000\000\002\000", 4))
+ return new ICOImageDecoder(preferredIconSize);
+
+ // XBMs require 8 bytes of info.
+ if (length >= 8 && strncmp(contents, "#define ", 8) == 0)
+ return new XBMImageDecoder();
+
+ // Give up. We don't know what the heck this is.
+ return 0;
+}
+
+ImageSource::ImageSource()
+ : m_decoder(0)
+{}
+
+ImageSource::~ImageSource()
+{
+ clear(true);
+}
+
+void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived)
+{
+ // TODO(darin): Figure out what to do with the |data| and |allDataReceived| params.
+
+ if (destroyAll) {
+ delete m_decoder;
+ m_decoder = 0;
+ return;
+ }
+
+ if (m_decoder)
+ m_decoder->clearFrameBufferCache(clearBeforeFrame);
+}
+
+bool ImageSource::initialized() const
+{
+ return m_decoder;
+}
+
+void ImageSource::setData(SharedBuffer* data, bool allDataReceived)
+{
+ // Make the decoder by sniffing the bytes.
+ // This method will examine the data and instantiate an instance of the appropriate decoder plugin.
+ // If insufficient bytes are available to determine the image type, no decoder plugin will be
+ // made.
+ if (!m_decoder)
+ m_decoder = createDecoder(data->buffer(), IntSize());
+
+ // CreateDecoder will return NULL if the decoder could not be created. Plus,
+ // we should not send more data to a decoder which has already decided it
+ // has failed.
+ if (!m_decoder || m_decoder->failed())
+ return;
+ m_decoder->setData(data, allDataReceived);
+}
+
+bool ImageSource::isSizeAvailable()
+{
+ if (!m_decoder)
+ return false;
+
+ return m_decoder->isSizeAvailable();
+}
+
+IntSize ImageSource::size() const
+{
+ if (!m_decoder)
+ return IntSize();
+
+ return m_decoder->size();
+}
+
+IntSize ImageSource::frameSizeAtIndex(size_t) const
+{
+ // TODO(brettw) do we need anything here?
+ return size();
+}
+
+int ImageSource::repetitionCount()
+{
+ if (!m_decoder)
+ return cAnimationNone;
+
+ return m_decoder->repetitionCount();
+}
+
+size_t ImageSource::frameCount() const
+{
+ if (!m_decoder)
+ return 0;
+ return m_decoder->failed() ? 0 : m_decoder->frameCount();
+}
+
+NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
+{
+ if (!m_decoder)
+ return 0;
+
+ // Note that the buffer can have NULL bytes even when it is marked as
+ // non-empty. It seems "FrameEmpty" is only set before the frame has been
+ // initialized. If it is decoded and it happens to be empty, it will be
+ // marked as "FrameComplete" but will still have NULL bytes.
+ RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
+ if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
+ return 0;
+
+ // Copy the bitmap. The pixel data is refcounted internally by SkBitmap, so
+ // this doesn't cost much. This pointer will be owned by the BitmapImage
+ // and freed in FrameData::clear().
+ return new NativeImageSkia(buffer->bitmap());
+}
+
+bool ImageSource::frameIsCompleteAtIndex(size_t index)
+{
+ if (!m_decoder)
+ return false;
+
+ RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
+ return buffer && buffer->status() == RGBA32Buffer::FrameComplete;
+}
+
+float ImageSource::frameDurationAtIndex(size_t index)
+{
+ if (!m_decoder)
+ return 0;
+
+ RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
+ if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
+ return 0;
+
+ // Many annoying ads specify a 0 duration to make an image flash as quickly
+ // as possible. We follow WinIE's behavior and use a duration of 100 ms
+ // for any frames that specify a duration of <= 50 ms. See
+ // <http://bugs.webkit.org/show_bug.cgi?id=14413> or Radar 4051389 for
+ // more.
+ const float duration = buffer->duration() / 1000.0f;
+ return (duration < 0.051f) ? 0.100f : duration;
+}
+
+bool ImageSource::frameHasAlphaAtIndex(size_t index)
+{
+ if (!m_decoder || !m_decoder->supportsAlpha())
+ return false;
+
+ RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
+ if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
+ return false;
+
+ return buffer->hasAlpha();
+}
+
+void ImageSourceSkia::setData(SharedBuffer* data,
+ bool allDataReceived,
+ const IntSize& preferredIconSize)
+{
+ if (!m_decoder)
+ m_decoder = createDecoder(data->buffer(), preferredIconSize);
+
+ ImageSource::setData(data, allDataReceived);
+}
+
+String ImageSource::filenameExtension() const
+{
+ return m_decoder ? m_decoder->filenameExtension() : String();
+}
+
+}
diff --git a/WebCore/platform/graphics/skia/ImageSourceSkia.h b/WebCore/platform/graphics/skia/ImageSourceSkia.h
new file mode 100644
index 0000000..9cb4a95
--- /dev/null
+++ b/WebCore/platform/graphics/skia/ImageSourceSkia.h
@@ -0,0 +1,60 @@
+/*
+ * 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 "ImageSource.h"
+
+namespace WebCore {
+
+class ImageSourceSkia : public ImageSource {
+public:
+ // FIXME: This class is a hack to support Chromium's ICO decoder
+ // Currently our ICO decoder decodes all data during setData() instead of
+ // being lazy. In addition, it only decodes one frame (closest to the size
+ // passed to the decoder during createDecoder, called from setData) and
+ // discards all other data in the file.
+ //
+ // To fix this will require fixing the ICO decoder to be lazy, or to decode
+ // all frames. Apple's decoders (ImageIO) decode all frames, and return
+ // them all sorted in decreasing size. WebCore always draws the first frame.
+ //
+ // This is a special-purpose routine for the favicon decoder, which is used
+ // to specify a particular icon size for the ICOImageDecoder to prefer
+ // decoding. Note that not all favicons are ICOs, so this won't
+ // necessarily do anything differently than ImageSource::setData().
+ //
+ // Passing an empty IntSize for |preferredIconSize| here is exactly
+ // equivalent to just calling ImageSource::setData(). See also comments in
+ // ICOImageDecoder.cpp.
+ void setData(SharedBuffer* data,
+ bool allDataReceived,
+ const IntSize& preferredIconSize);
+};
+
+}
diff --git a/WebCore/platform/graphics/skia/IntPointSkia.cpp b/WebCore/platform/graphics/skia/IntPointSkia.cpp
new file mode 100644
index 0000000..fd9a6fd
--- /dev/null
+++ b/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/WebCore/platform/graphics/skia/IntRectSkia.cpp b/WebCore/platform/graphics/skia/IntRectSkia.cpp
new file mode 100644
index 0000000..ea138ee
--- /dev/null
+++ b/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/WebCore/platform/graphics/skia/NativeImageSkia.cpp b/WebCore/platform/graphics/skia/NativeImageSkia.cpp
new file mode 100644
index 0000000..e59d1e2
--- /dev/null
+++ b/WebCore/platform/graphics/skia/NativeImageSkia.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 "skia/ext/image_operations.h"
+
+#include "NativeImageSkia.h"
+#include "SkiaUtils.h"
+
+NativeImageSkia::NativeImageSkia()
+ : m_isDataComplete(false),
+ m_lastRequestSize(0, 0),
+ m_resizeRequests(0)
+{
+}
+
+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 = WebCore::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 (m_resizedImage.width() != w || m_resizedImage.height() != h)
+ m_resizedImage = skia::ImageOperations::Resize(*this, skia::ImageOperations::RESIZE_LANCZOS3, w, h);
+
+ 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 = WebCore::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;
+}
diff --git a/WebCore/platform/graphics/skia/NativeImageSkia.h b/WebCore/platform/graphics/skia/NativeImageSkia.h
new file mode 100644
index 0000000..5947238
--- /dev/null
+++ b/WebCore/platform/graphics/skia/NativeImageSkia.h
@@ -0,0 +1,104 @@
+/*
+ * 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"
+
+// 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();
+
+ // 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 WebCore::IntSize m_lastRequestSize;
+ mutable int m_resizeRequests;
+};
+
+#endif // NativeImageSkia_h
+
diff --git a/WebCore/platform/graphics/skia/PathSkia.cpp b/WebCore/platform/graphics/skia/PathSkia.cpp
new file mode 100644
index 0000000..ca99322
--- /dev/null
+++ b/WebCore/platform/graphics/skia/PathSkia.cpp
@@ -0,0 +1,316 @@
+// 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 "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::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
+{
+ SkRect rect;
+ m_path->computeBounds(&rect, SkPath::kExact_BoundsType);
+ return rect;
+}
+
+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);
+
+ SkRect oval;
+ oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
+
+ float sweep = ea - sa;
+ // check for a circle
+ if (sweep >= 2 * piFloat || sweep <= -2 * piFloat)
+ m_path->addOval(oval);
+ else {
+ SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat);
+ SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat);
+
+ // 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 -= SkIntToScalar(360);
+ } else if (!anticlockwise && sweepDegrees < 0) {
+ sweepDegrees += SkIntToScalar(360);
+ }
+
+ 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 TransformationMatrix& xform)
+{
+ m_path->transform(xform);
+}
+
+String Path::debugString() const
+{
+ String result;
+
+ SkPath::Iter iter(*m_path, false);
+ SkPoint pts[4];
+
+ int numPoints = m_path->getPoints(0, 0);
+ SkPath::Verb verb;
+
+ do {
+ verb = iter.next(pts);
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
+ numPoints -= 1;
+ break;
+ case SkPath::kLine_Verb:
+ if (!iter.isCloseLine()) {
+ result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY);
+ numPoints -= 1;
+ }
+ break;
+ case SkPath::kQuad_Verb:
+ result += String::format("Q%.2f,%.2f,%.2f,%.2f ",
+ pts[1].fX, pts[1].fY,
+ pts[2].fX, pts[2].fY);
+ numPoints -= 2;
+ break;
+ case SkPath::kCubic_Verb:
+ result += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ",
+ pts[1].fX, pts[1].fY,
+ pts[2].fX, pts[2].fY,
+ pts[3].fX, pts[3].fY);
+ numPoints -= 3;
+ break;
+ case SkPath::kClose_Verb:
+ result += "Z ";
+ break;
+ case SkPath::kDone_Verb:
+ break;
+ }
+ } while (verb != SkPath::kDone_Verb);
+
+ // If you have a path that ends with an M, Skia will not iterate the
+ // trailing M. That's nice of it, but Apple's paths output the trailing M
+ // and we want out layout dumps to look like theirs
+ if (numPoints) {
+ ASSERT(numPoints==1);
+ m_path->getLastPt(pts);
+ result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
+ }
+
+ return result.stripWhiteSpace();
+}
+
+// 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()->currentPath(), &boundingPath);
+ SkRect r;
+ boundingPath.computeBounds(&r, SkPath::kExact_BoundsType);
+ return r;
+}
+
+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/WebCore/platform/graphics/skia/PatternSkia.cpp b/WebCore/platform/graphics/skia/PatternSkia.cpp
new file mode 100644
index 0000000..be8eb8a
--- /dev/null
+++ b/WebCore/platform/graphics/skia/PatternSkia.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "Image.h"
+#include "NativeImageSkia.h"
+#include "TransformationMatrix.h"
+
+#include "SkShader.h"
+#include "SkCanvas.h"
+
+namespace WebCore {
+
+PlatformPatternPtr Pattern::createPlatformPattern(const TransformationMatrix& patternTransform) const
+{
+ // 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 (m_repeatX && m_repeatY)
+ return SkShader::CreateBitmapShader(*bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
+
+ // 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);
+ return SkShader::CreateBitmapShader(bm2, tileModeX, tileModeY);
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp
new file mode 100644
index 0000000..60dbbe0
--- /dev/null
+++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp
@@ -0,0 +1,427 @@
+/*
+ * 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 "GraphicsContext.h"
+#include "NativeImageSkia.h"
+#include "PlatformContextSkia.h"
+#include "SkiaUtils.h"
+
+#include "skia/ext/image_operations.h"
+#include "skia/ext/platform_canvas.h"
+
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkDashPathEffect.h"
+
+#include <wtf/MathExtras.h>
+
+#if defined(__linux__)
+#include "GdkSkia.h"
+#endif
+
+// 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;
+ SkPorterDuff::Mode m_porterDuffMode;
+ SkShader* m_gradient;
+ SkShader* m_pattern;
+ bool m_useAntialiasing;
+ SkDrawLooper* m_looper;
+
+ // Fill.
+ SkColor m_fillColor;
+
+ // Stroke.
+ WebCore::StrokeStyle m_strokeStyle;
+ SkColor m_strokeColor;
+ 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 cTextFill & friends in GraphicsContext.h.)
+ int 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;
+
+private:
+ // Not supported.
+ void operator=(const State&);
+};
+
+// Note: Keep theses default values in sync with GraphicsContextState.
+PlatformContextSkia::State::State()
+ : m_alpha(1)
+ , m_porterDuffMode(SkPorterDuff::kSrcOver_Mode)
+ , m_gradient(0)
+ , m_pattern(0)
+ , m_useAntialiasing(true)
+ , m_looper(0)
+ , m_fillColor(0xFF000000)
+ , m_strokeStyle(WebCore::SolidStroke)
+ , m_strokeColor(WebCore::Color::black)
+ , m_strokeThickness(0)
+ , m_dashRatio(3)
+ , m_miterLimit(4)
+ , m_lineCap(SkPaint::kDefault_Cap)
+ , m_lineJoin(SkPaint::kDefault_Join)
+ , m_dash(0)
+ , m_textDrawingMode(WebCore::cTextFill)
+{
+}
+
+PlatformContextSkia::State::State(const State& other)
+{
+ memcpy(this, &other, sizeof(State));
+
+ m_looper->safeRef();
+ m_dash->safeRef();
+ m_gradient->safeRef();
+ m_pattern->safeRef();
+}
+
+PlatformContextSkia::State::~State()
+{
+ m_looper->safeUnref();
+ m_dash->safeUnref();
+ m_gradient->safeUnref();
+ m_pattern->safeUnref();
+}
+
+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_stateStack(sizeof(State))
+{
+ m_stateStack.append(State());
+ m_state = &m_stateStack.last();
+#if defined(OS_LINUX)
+ m_gdkskia = m_canvas ? gdk_skia_new(m_canvas) : 0;
+#endif
+}
+
+PlatformContextSkia::~PlatformContextSkia()
+{
+#if defined(OS_LINUX)
+ if (m_gdkskia) {
+ g_object_unref(m_gdkskia);
+ m_gdkskia = 0;
+ }
+#endif
+}
+
+void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas)
+{
+ m_canvas = canvas;
+}
+
+void PlatformContextSkia::save()
+{
+ m_stateStack.append(*m_state);
+ m_state = &m_stateStack.last();
+
+ // Save our native canvas.
+ canvas()->save();
+}
+
+void PlatformContextSkia::restore()
+{
+ 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 != WebCore::NoStroke &&
+ (m_state->m_strokeColor & 0xFF000000)) {
+ if (fillcolorNotTransparent) {
+ // This call is expensive so don't call it unnecessarily.
+ paint.reset();
+ }
+ setupPaintForStroking(&paint, &rect, 0);
+ canvas()->drawRect(rect, paint);
+ }
+}
+
+void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
+{
+#ifdef SK_DEBUGx
+ {
+ SkPaint defaultPaint;
+ SkASSERT(*paint == defaultPaint);
+ }
+#endif
+
+ paint->setAntiAlias(m_state->m_useAntialiasing);
+ paint->setPorterDuffXfermode(m_state->m_porterDuffMode);
+ paint->setLooper(m_state->m_looper);
+
+ if (m_state->m_gradient)
+ paint->setShader(m_state->m_gradient);
+ else if (m_state->m_pattern)
+ paint->setShader(m_state->m_pattern);
+}
+
+void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
+{
+ setupPaintCommon(paint);
+ paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
+}
+
+float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
+{
+ setupPaintCommon(paint);
+ float width = m_state->m_strokeThickness;
+
+ // This allows dashing and dotting to work properly for hairline strokes.
+ if (width == 0)
+ width = 1;
+
+ paint->setColor(m_state->applyAlpha(m_state->m_strokeColor));
+ 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 (rect != 0 && (static_cast<int>(roundf(width)) & 1))
+ rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
+
+ if (m_state->m_dash)
+ paint->setPathEffect(m_state->m_dash);
+ else {
+ switch (m_state->m_strokeStyle) {
+ case WebCore::NoStroke:
+ case WebCore::SolidStroke:
+ break;
+ case WebCore::DashedStroke:
+ width = m_state->m_dashRatio * width;
+ // Fall through.
+ case WebCore::DottedStroke:
+ SkScalar dashLength;
+ if (length) {
+ // Determine about how many dashes or dots we should have.
+ int numDashes = length / roundf(width);
+ if (!(numDashes & 1))
+ numDashes++; // Make it odd so we end on a dash/dot.
+ // Use the number of dashes to determine the length of a
+ // dash/dot, which will be approximately width
+ dashLength = SkScalarDiv(SkIntToScalar(length), SkIntToScalar(numDashes));
+ } else
+ dashLength = SkFloatToScalar(width);
+ SkScalar intervals[2] = { dashLength, dashLength };
+ paint->setPathEffect(new SkDashPathEffect(intervals, 2, 0))->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::setPorterDuffMode(SkPorterDuff::Mode pdm)
+{
+ m_state->m_porterDuffMode = pdm;
+}
+
+void PlatformContextSkia::setFillColor(SkColor color)
+{
+ m_state->m_fillColor = color;
+}
+
+SkDrawLooper* PlatformContextSkia::getDrawLooper() const
+{
+ return m_state->m_looper;
+}
+
+WebCore::StrokeStyle PlatformContextSkia::getStrokeStyle() const
+{
+ return m_state->m_strokeStyle;
+}
+
+void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle)
+{
+ m_state->m_strokeStyle = strokeStyle;
+}
+
+void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
+{
+ m_state->m_strokeColor = strokeColor;
+}
+
+float PlatformContextSkia::getStrokeThickness() const
+{
+ return m_state->m_strokeThickness;
+}
+
+void PlatformContextSkia::setStrokeThickness(float thickness)
+{
+ m_state->m_strokeThickness = thickness;
+}
+
+int PlatformContextSkia::getTextDrawingMode() const
+{
+ return m_state->m_textDrawingMode;
+}
+
+void PlatformContextSkia::setTextDrawingMode(int mode)
+{
+ // cTextClip is never used, so we assert that it isn't set:
+ // https://bugs.webkit.org/show_bug.cgi?id=21898
+ ASSERT((mode & WebCore::cTextClip) == 0);
+ m_state->m_textDrawingMode = mode;
+}
+
+void PlatformContextSkia::setUseAntialiasing(bool enable)
+{
+ m_state->m_useAntialiasing = enable;
+}
+
+SkColor PlatformContextSkia::fillColor() const
+{
+ return m_state->m_fillColor;
+}
+
+void PlatformContextSkia::beginPath()
+{
+ m_path.reset();
+}
+
+void PlatformContextSkia::addPath(const SkPath& path)
+{
+ m_path.addPath(path);
+}
+
+void PlatformContextSkia::setFillRule(SkPath::FillType fr)
+{
+ m_path.setFillType(fr);
+}
+
+void PlatformContextSkia::setGradient(SkShader* gradient)
+{
+ if (gradient != m_state->m_gradient) {
+ m_state->m_gradient->safeUnref();
+ m_state->m_gradient = gradient;
+ }
+}
+
+void PlatformContextSkia::setPattern(SkShader* pattern)
+{
+ if (pattern != m_state->m_pattern) {
+ m_state->m_pattern->safeUnref();
+ m_state->m_pattern = pattern;
+ }
+}
+
+void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
+{
+ if (dash != m_state->m_dash) {
+ m_state->m_dash->safeUnref();
+ 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();
+}
diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h
new file mode 100644
index 0000000..78e9a19
--- /dev/null
+++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h
@@ -0,0 +1,173 @@
+/*
+ * 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 "SkDeque.h"
+#include "skia/ext/platform_canvas.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+
+#include <wtf/Vector.h>
+
+typedef struct _GdkDrawable GdkSkia;
+
+// 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 : 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*);
+
+ void save();
+ void restore();
+
+ // 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 setPorterDuffMode(SkPorterDuff::Mode);
+ void setFillColor(SkColor);
+ void setStrokeStyle(WebCore::StrokeStyle);
+ void setStrokeColor(SkColor);
+ void setStrokeThickness(float thickness);
+ void setTextDrawingMode(int mode);
+ void setUseAntialiasing(bool enable);
+ void setGradient(SkShader*);
+ void setPattern(SkShader*);
+ void setDashPathEffect(SkDashPathEffect*);
+
+ SkDrawLooper* getDrawLooper() const;
+ WebCore::StrokeStyle getStrokeStyle() const;
+ float getStrokeThickness() const;
+ int getTextDrawingMode() const;
+
+ void beginPath();
+ void addPath(const SkPath&);
+ const SkPath* currentPath() const { return &m_path; }
+
+ SkColor fillColor() const;
+
+ skia::PlatformCanvas* canvas() { return m_canvas; }
+
+ // 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();
+
+#if defined(__linux__)
+ // FIXME: should be camelCase.
+ GdkSkia* gdk_skia() const { return m_gdkskia; }
+#endif
+
+private:
+ // 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.
+ SkPath m_path;
+
+#if defined(__linux__)
+ // A pointer to a GDK Drawable wrapping of this Skia canvas
+ GdkSkia* m_gdkskia;
+#endif
+};
+
+#endif // PlatformContextSkia_h
diff --git a/WebCore/platform/graphics/skia/PlatformGraphics.h b/WebCore/platform/graphics/skia/PlatformGraphics.h
new file mode 100644
index 0000000..4ae8835
--- /dev/null
+++ b/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/WebCore/platform/graphics/skia/SkiaFontWin.cpp b/WebCore/platform/graphics/skia/SkiaFontWin.cpp
new file mode 100644
index 0000000..6e79a7e
--- /dev/null
+++ b/WebCore/platform/graphics/skia/SkiaFontWin.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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 "SkCanvas.h"
+#include "SkPaint.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;
+};
+
+typedef ListHashSet<CachedOutlineKey, 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;
+
+// The global number of glyph outlines we'll cache.
+static const int outlineCacheSize = 256;
+
+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;
+ }
+ curGlyph += polyHeader->cb;
+ }
+
+ path->close();
+ 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));
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.h b/WebCore/platform/graphics/skia/SkiaFontWin.h
new file mode 100644
index 0000000..2adab39
--- /dev/null
+++ b/WebCore/platform/graphics/skia/SkiaFontWin.h
@@ -0,0 +1,54 @@
+/*
+ * 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 SkiaWinOutlineCache_h
+#define SkiaWinOutlineCache_h
+
+#include <windows.h>
+
+class SkPath;
+
+namespace WebCore {
+
+// 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();
+};
+
+} // namespace WebCore
+
+#endif // SkiaWinOutlineCache_h
diff --git a/WebCore/platform/graphics/skia/SkiaUtils.cpp b/WebCore/platform/graphics/skia/SkiaUtils.cpp
new file mode 100644
index 0000000..6d9ffe2
--- /dev/null
+++ b/WebCore/platform/graphics/skia/SkiaUtils.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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"
+
+namespace WebCore {
+
+static const struct CompositOpToPorterDuffMode {
+ uint8_t mCompositOp;
+ uint8_t mPorterDuffMode;
+} gMapCompositOpsToPorterDuffModes[] = {
+ { CompositeClear, SkPorterDuff::kClear_Mode },
+ { CompositeCopy, SkPorterDuff::kSrcOver_Mode }, // TODO
+ { CompositeSourceOver, SkPorterDuff::kSrcOver_Mode },
+ { CompositeSourceIn, SkPorterDuff::kSrcIn_Mode },
+ { CompositeSourceOut, SkPorterDuff::kSrcOut_Mode },
+ { CompositeSourceAtop, SkPorterDuff::kSrcATop_Mode },
+ { CompositeDestinationOver, SkPorterDuff::kDstOver_Mode },
+ { CompositeDestinationIn, SkPorterDuff::kDstIn_Mode },
+ { CompositeDestinationOut, SkPorterDuff::kDstOut_Mode },
+ { CompositeDestinationAtop, SkPorterDuff::kDstATop_Mode },
+ { CompositeXOR, SkPorterDuff::kXor_Mode },
+ { CompositePlusDarker, SkPorterDuff::kDarken_Mode },
+ { CompositeHighlight, SkPorterDuff::kSrcOver_Mode }, // TODO
+ { CompositePlusLighter, SkPorterDuff::kLighten_Mode }
+};
+
+SkPorterDuff::Mode WebCoreCompositeToSkiaComposite(CompositeOperator op)
+{
+ const CompositOpToPorterDuffMode* table = gMapCompositOpsToPorterDuffModes;
+
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToPorterDuffModes); i++) {
+ if (table[i].mCompositOp == op)
+ return (SkPorterDuff::Mode)table[i].mPorterDuffMode;
+ }
+
+ SkDEBUGF(("GraphicsContext::setCompositeOperation uknown CompositeOperator %d\n", op));
+ return SkPorterDuff::kSrcOver_Mode; // fall-back
+}
+
+static U8CPU InvScaleByte(U8CPU component, uint32_t scale)
+{
+ SkASSERT(component == (uint8_t)component);
+ return (component * scale + 0x8000) >> 16;
+}
+
+SkColor SkPMColorToColor(SkPMColor pm)
+{
+ if (0 == pm)
+ return 0;
+
+ unsigned a = SkGetPackedA32(pm);
+ 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);
+}
+
+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->computeBounds(&bounds, SkPath::kFast_BoundsType);
+
+ // We can immediately return false if the point is outside the bounding rect
+ if (!bounds.contains(SkFloatToScalar(point.x()), SkFloatToScalar(point.y())))
+ 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, y, x + 1, y + 1);
+
+ bool contains = rgn.setPath(*path, clip);
+
+ originalPath->setFillType(originalFillType);
+ return contains;
+}
+
+GraphicsContext* scratchContext()
+{
+ static ImageBuffer* scratch = 0;
+ if (!scratch)
+ scratch = ImageBuffer::create(IntSize(1, 1), false).release();
+ // 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/WebCore/platform/graphics/skia/SkiaUtils.h b/WebCore/platform/graphics/skia/SkiaUtils.h
new file mode 100644
index 0000000..0e68574
--- /dev/null
+++ b/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 "SkPorterDuff.h"
+
+class SkCanvas;
+class SkRegion;
+
+namespace WebCore {
+
+SkPorterDuff::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/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp
new file mode 100644
index 0000000..1e2a194
--- /dev/null
+++ b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp
@@ -0,0 +1,222 @@
+// 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 "TransformationMatrix.h"
+
+#include "FloatRect.h"
+#include "IntRect.h"
+
+#include "SkiaUtils.h"
+
+namespace WebCore {
+
+TransformationMatrix::TransformationMatrix()
+{
+ m_transform.reset();
+}
+
+TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double e, double f)
+{
+ setMatrix(a, b, c, d, e, f);
+}
+
+TransformationMatrix::TransformationMatrix(const SkMatrix& matrix)
+ : m_transform(matrix)
+{
+}
+
+void TransformationMatrix::setMatrix(double a, double b, double c, double d, double e, double f)
+{
+ m_transform.reset();
+
+ m_transform.setScaleX(WebCoreDoubleToSkScalar(a));
+ m_transform.setSkewX(WebCoreDoubleToSkScalar(c));
+ m_transform.setTranslateX(WebCoreDoubleToSkScalar(e));
+
+ m_transform.setScaleY(WebCoreDoubleToSkScalar(d));
+ m_transform.setSkewY(WebCoreDoubleToSkScalar(b));
+ m_transform.setTranslateY(WebCoreDoubleToSkScalar(f));
+}
+
+void TransformationMatrix::map(double x, double y, double* x2, double* y2) const
+{
+ SkPoint src, dst;
+ src.set(WebCoreDoubleToSkScalar(x), WebCoreDoubleToSkScalar(y));
+ m_transform.mapPoints(&dst, &src, 1);
+
+ *x2 = SkScalarToDouble(dst.fX);
+ *y2 = SkScalarToDouble(dst.fY);
+}
+
+IntRect TransformationMatrix::mapRect(const IntRect& src) const
+{
+ SkRect dst;
+ m_transform.mapRect(&dst, src);
+ return enclosingIntRect(dst);
+}
+
+FloatRect TransformationMatrix::mapRect(const FloatRect& src) const
+{
+ SkRect dst;
+ m_transform.mapRect(&dst, src);
+ return dst;
+}
+
+bool TransformationMatrix::isIdentity() const
+{
+ return m_transform.isIdentity();
+}
+
+void TransformationMatrix::reset()
+{
+ m_transform.reset();
+}
+
+TransformationMatrix &TransformationMatrix::scale(double sx, double sy)
+{
+ m_transform.preScale(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0);
+ return *this;
+}
+
+TransformationMatrix &TransformationMatrix::rotate(double d)
+{
+ m_transform.preRotate(WebCoreDoubleToSkScalar(d), 0, 0);
+ return *this;
+}
+
+TransformationMatrix &TransformationMatrix::translate(double tx, double ty)
+{
+ m_transform.preTranslate(WebCoreDoubleToSkScalar(tx), WebCoreDoubleToSkScalar(ty));
+ return *this;
+}
+
+TransformationMatrix &TransformationMatrix::shear(double sx, double sy)
+{
+ m_transform.preSkew(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0);
+ return *this;
+}
+
+double TransformationMatrix::det() const
+{
+ return SkScalarToDouble(m_transform.getScaleX()) * SkScalarToDouble(m_transform.getScaleY()) -
+ SkScalarToDouble(m_transform.getSkewY()) * SkScalarToDouble(m_transform.getSkewX());
+}
+
+TransformationMatrix TransformationMatrix::inverse() const
+{
+ TransformationMatrix inverse;
+ m_transform.invert(&inverse.m_transform);
+ return inverse;
+}
+
+TransformationMatrix::operator SkMatrix() const
+{
+ return m_transform;
+}
+
+bool TransformationMatrix::operator==(const TransformationMatrix& m2) const
+{
+ return m_transform == m2.m_transform;
+}
+
+TransformationMatrix &TransformationMatrix::operator*=(const TransformationMatrix& m2)
+{
+ m_transform.setConcat(m2.m_transform, m_transform);
+ return *this;
+}
+
+TransformationMatrix TransformationMatrix::operator*(const TransformationMatrix& m2)
+{
+ TransformationMatrix cat;
+ cat.m_transform.setConcat(m2.m_transform, m_transform);
+ return cat;
+}
+
+double TransformationMatrix::a() const
+{
+ return SkScalarToDouble(m_transform.getScaleX());
+}
+
+void TransformationMatrix::setA(double a)
+{
+ m_transform.setScaleX(WebCoreDoubleToSkScalar(a));
+}
+
+double TransformationMatrix::b() const
+{
+ return SkScalarToDouble(m_transform.getSkewY());
+}
+
+void TransformationMatrix::setB(double b)
+{
+ m_transform.setSkewY(WebCoreDoubleToSkScalar(b));
+}
+
+double TransformationMatrix::c() const
+{
+ return SkScalarToDouble(m_transform.getSkewX());
+}
+
+void TransformationMatrix::setC(double c)
+{
+ m_transform.setSkewX(WebCoreDoubleToSkScalar(c));
+}
+
+double TransformationMatrix::d() const
+{
+ return SkScalarToDouble(m_transform.getScaleY());
+}
+
+void TransformationMatrix::setD(double d)
+{
+ m_transform.setScaleY(WebCoreDoubleToSkScalar(d));
+}
+
+double TransformationMatrix::e() const
+{
+ return SkScalarToDouble(m_transform.getTranslateX());
+}
+
+void TransformationMatrix::setE(double e)
+{
+ m_transform.setTranslateX(WebCoreDoubleToSkScalar(e));
+}
+
+double TransformationMatrix::f() const
+{
+ return SkScalarToDouble(m_transform.getTranslateY());
+}
+
+void TransformationMatrix::setF(double f)
+{
+ m_transform.setTranslateY(WebCoreDoubleToSkScalar(f));
+}
+
+} // namespace WebCore