diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
commit | 635860845790a19bf50bbc51ba8fb66a96dde068 (patch) | |
tree | ef6ad9ff73a5b57f65249d4232a202fa77e6a140 /WebCore/platform/graphics/skia | |
parent | 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (diff) | |
download | external_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')
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 |