| /* |
| * Copyright 2006, The Android Open Source Project |
| * |
| * 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #include "config.h" |
| #include "GraphicsContext.h" |
| |
| #include "AffineTransform.h" |
| #include "Font.h" |
| #include "Gradient.h" |
| #include "NotImplemented.h" |
| #include "Path.h" |
| #include "Pattern.h" |
| #include "PlatformGraphicsContext.h" |
| #include "PlatformGraphicsContextSkia.h" |
| #include "SkBitmapRef.h" |
| #include "SkBlurDrawLooper.h" |
| #include "SkBlurMaskFilter.h" |
| #include "SkCanvas.h" |
| #include "SkColorPriv.h" |
| #include "SkCornerPathEffect.h" |
| #include "SkDashPathEffect.h" |
| #include "SkDevice.h" |
| #include "SkGradientShader.h" |
| #include "SkPaint.h" |
| #include "SkString.h" |
| #include "SkiaUtils.h" |
| #include "TransformationMatrix.h" |
| |
| using namespace std; |
| |
| namespace WebCore { |
| |
| // This class just holds onto a PlatformContextSkia for GraphicsContext. |
| class GraphicsContextPlatformPrivate { |
| WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); |
| public: |
| GraphicsContextPlatformPrivate(PlatformGraphicsContext* platformContext) |
| : m_context(platformContext) { } |
| ~GraphicsContextPlatformPrivate() |
| { |
| if (m_context && m_context->deleteUs()) |
| delete m_context; |
| } |
| |
| |
| PlatformGraphicsContext* context() { return m_context; } |
| |
| private: |
| // Non-owning pointer to the PlatformContext. |
| PlatformGraphicsContext* m_context; |
| }; |
| |
| static void syncPlatformContext(GraphicsContext* gc) |
| { |
| // Stroke and fill sometimes reference each other, so always |
| // sync them both to make sure our state is consistent. |
| |
| PlatformGraphicsContext* pgc = gc->platformContext(); |
| Gradient* grad = gc->state().fillGradient.get(); |
| Pattern* pat = gc->state().fillPattern.get(); |
| |
| if (grad) |
| pgc->setFillShader(grad->platformGradient()); |
| else if (pat) |
| pgc->setFillShader(pat->platformPattern(AffineTransform())); |
| else |
| pgc->setFillColor(gc->state().fillColor); |
| |
| grad = gc->state().strokeGradient.get(); |
| pat = gc->state().strokePattern.get(); |
| |
| if (grad) |
| pgc->setStrokeShader(grad->platformGradient()); |
| else if (pat) |
| pgc->setStrokeShader(pat->platformPattern(AffineTransform())); |
| else |
| pgc->setStrokeColor(gc->state().strokeColor); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height) |
| { |
| SkBitmap bitmap; |
| bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); |
| bitmap.allocPixels(); |
| bitmap.eraseColor(0); |
| |
| PlatformGraphicsContextSkia* pgc = |
| new PlatformGraphicsContextSkia(new SkCanvas(bitmap), true); |
| GraphicsContext* ctx = new GraphicsContext(pgc); |
| return ctx; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| void GraphicsContext::platformInit(PlatformGraphicsContext* gc) |
| { |
| if (gc) |
| gc->setGraphicsContext(this); |
| m_data = new GraphicsContextPlatformPrivate(gc); |
| setPaintingDisabled(!gc || gc->isPaintingDisabled()); |
| } |
| |
| void GraphicsContext::platformDestroy() |
| { |
| delete m_data; |
| } |
| |
| void GraphicsContext::savePlatformState() |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->save(); |
| } |
| |
| void GraphicsContext::restorePlatformState() |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->restore(); |
| } |
| |
| bool GraphicsContext::willFill() const |
| { |
| return m_state.fillColor.rgb(); |
| } |
| |
| bool GraphicsContext::willStroke() const |
| { |
| return m_state.strokeColor.rgb(); |
| } |
| |
| // Draws a filled rectangle with a stroked border. |
| void GraphicsContext::drawRect(const IntRect& rect) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->drawRect(rect); |
| } |
| |
| // This is only used to draw borders. |
| void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->drawLine(point1, point2); |
| } |
| |
| void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool /* printing */) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->drawLineForText(pt, width); |
| } |
| |
| void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, |
| TextCheckingLineStyle style) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->drawLineForTextChecking(pt, width, style); |
| } |
| |
| // This method is only used to draw the little circles used in lists. |
| void GraphicsContext::drawEllipse(const IntRect& rect) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->drawEllipse(rect); |
| } |
| |
| void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->strokeArc(r, startAngle, angleSpan); |
| } |
| |
| void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, |
| bool shouldAntialias) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->drawConvexPolygon(numPoints, points, shouldAntialias); |
| } |
| |
| void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, |
| const IntSize& bottomLeft, const IntSize& bottomRight, |
| const Color& color, ColorSpace colorSpace) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->fillRoundedRect(rect, topLeft, topRight, |
| bottomLeft, bottomRight, color, colorSpace); |
| } |
| |
| void GraphicsContext::fillRect(const FloatRect& rect) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->fillRect(rect); |
| } |
| |
| void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->fillRect(rect, color, colorSpace); |
| } |
| |
| void GraphicsContext::clip(const FloatRect& rect) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->clip(rect); |
| } |
| |
| void GraphicsContext::clip(const Path& path) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->clip(path); |
| } |
| |
| void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->addInnerRoundedRectClip(rect, thickness); |
| } |
| |
| void GraphicsContext::canvasClip(const Path& path) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->canvasClip(path); |
| } |
| |
| void GraphicsContext::clipOut(const IntRect& r) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->clipOut(r); |
| } |
| |
| #if ENABLE(SVG) |
| void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->clipPath(pathToClip, clipRule); |
| } |
| #endif |
| |
| void GraphicsContext::clipOut(const Path& p) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->clipOut(p); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| #if SVG_SUPPORT |
| KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext() |
| { |
| return new KRenderingDeviceContextQuartz(platformContext()); |
| } |
| #endif |
| |
| void GraphicsContext::beginTransparencyLayer(float opacity) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->beginTransparencyLayer(opacity); |
| } |
| |
| void GraphicsContext::endTransparencyLayer() |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->endTransparencyLayer(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| void GraphicsContext::setupFillPaint(SkPaint* paint) |
| { |
| if (paintingDisabled()) |
| return; |
| syncPlatformContext(this); |
| platformContext()->setupPaintFill(paint); |
| } |
| |
| void GraphicsContext::setupStrokePaint(SkPaint* paint) |
| { |
| if (paintingDisabled()) |
| return; |
| syncPlatformContext(this); |
| platformContext()->setupPaintStroke(paint, 0); |
| } |
| |
| bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset) |
| { |
| if (paintingDisabled()) |
| return false; |
| syncPlatformContext(this); |
| return platformContext()->setupPaintShadow(paint, offset); |
| } |
| |
| void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace) |
| { |
| } |
| |
| void GraphicsContext::setPlatformStrokeThickness(float f) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->setStrokeThickness(f); |
| } |
| |
| void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->setStrokeStyle(style); |
| } |
| |
| void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace) |
| { |
| } |
| |
| void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| if (blur <= 0) |
| this->clearPlatformShadow(); |
| |
| SkColor c; |
| if (color.isValid()) |
| c = color.rgb(); |
| else |
| c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color |
| platformContext()->setShadow(blur, size.width(), size.height(), c); |
| } |
| |
| void GraphicsContext::clearPlatformShadow() |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->setShadow(0, 0, 0, 0); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->drawFocusRing(rects, width, offset, color); |
| } |
| |
| void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&) |
| { |
| // Do nothing, since we draw the focus ring independently. |
| } |
| |
| PlatformGraphicsContext* GraphicsContext::platformContext() const |
| { |
| ASSERT(!paintingDisabled()); |
| return m_data->context(); |
| } |
| |
| void GraphicsContext::setMiterLimit(float limit) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->setMiterLimit(limit); |
| } |
| |
| void GraphicsContext::setAlpha(float alpha) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->setAlpha(alpha); |
| } |
| |
| void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->setCompositeOperation(op); |
| } |
| |
| void GraphicsContext::clearRect(const FloatRect& rect) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->clearRect(rect); |
| } |
| |
| void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->strokeRect(rect, lineWidth); |
| } |
| |
| void GraphicsContext::setLineCap(LineCap cap) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->setLineCap(cap); |
| } |
| |
| #if ENABLE(SVG) |
| void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| platformContext()->setLineDash(dashes, dashOffset); |
| } |
| #endif |
| |
| void GraphicsContext::setLineJoin(LineJoin join) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->setLineJoin(join); |
| } |
| |
| void GraphicsContext::scale(const FloatSize& size) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->scale(size); |
| } |
| |
| void GraphicsContext::rotate(float angleInRadians) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->rotate(angleInRadians); |
| } |
| |
| void GraphicsContext::translate(float x, float y) |
| { |
| if (paintingDisabled()) |
| return; |
| if (!x && !y) |
| return; |
| platformContext()->translate(x, y); |
| } |
| |
| void GraphicsContext::concatCTM(const AffineTransform& affine) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->concatCTM(affine); |
| } |
| |
| // This is intended to round the rect to device pixels (through the CTM) |
| // and then invert the result back into source space, with the hope that when |
| // it is drawn (through the matrix), it will land in the "right" place (i.e. |
| // on pixel boundaries). |
| |
| // For android, we record this geometry once and then draw it though various |
| // scale factors as the user zooms, without re-recording. Thus this routine |
| // should just leave the original geometry alone. |
| |
| // If we instead draw into bitmap tiles, we should then perform this |
| // transform -> round -> inverse step. |
| |
| FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode) |
| { |
| return rect; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) |
| { |
| // Appears to be PDF specific, so we ignore it |
| } |
| |
| void GraphicsContext::setPlatformShouldAntialias(bool useAA) |
| { |
| if (paintingDisabled()) |
| return; |
| platformContext()->setShouldAntialias(useAA); |
| } |
| |
| void GraphicsContext::setPlatformFillGradient(Gradient* fillGradient) |
| { |
| } |
| |
| void GraphicsContext::setPlatformFillPattern(Pattern* fillPattern) |
| { |
| } |
| |
| void GraphicsContext::setPlatformStrokeGradient(Gradient* strokeGradient) |
| { |
| } |
| |
| void GraphicsContext::setPlatformStrokePattern(Pattern* strokePattern) |
| { |
| } |
| |
| AffineTransform GraphicsContext::getCTM() const |
| { |
| if (paintingDisabled()) |
| return AffineTransform(); |
| const SkMatrix& m = platformContext()->getTotalMatrix(); |
| return AffineTransform(SkScalarToDouble(m.getScaleX()), // a |
| SkScalarToDouble(m.getSkewY()), // b |
| SkScalarToDouble(m.getSkewX()), // c |
| SkScalarToDouble(m.getScaleY()), // d |
| SkScalarToDouble(m.getTranslateX()), // e |
| SkScalarToDouble(m.getTranslateY())); // f |
| } |
| |
| void GraphicsContext::setCTM(const AffineTransform& transform) |
| { |
| // The SkPicture mode of Skia does not support SkCanvas::setMatrix(), so we |
| // can not simply use that method here. We could calculate the transform |
| // required to achieve the desired matrix and use SkCanvas::concat(), but |
| // there's currently no need for this. |
| ASSERT_NOT_REACHED(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void GraphicsContext::fillPath(const Path& pathToFill) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->fillPath(pathToFill, fillRule()); |
| } |
| |
| void GraphicsContext::strokePath(const Path& pathToStroke) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->strokePath(pathToStroke); |
| } |
| |
| InterpolationQuality GraphicsContext::imageInterpolationQuality() const |
| { |
| notImplemented(); |
| return InterpolationDefault; |
| } |
| |
| void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode) |
| { |
| #if 0 |
| enum InterpolationQuality { |
| InterpolationDefault, |
| InterpolationNone, |
| InterpolationLow, |
| InterpolationMedium, |
| InterpolationHigh |
| }; |
| #endif |
| // TODO: record this, so we can know when to use bitmap-filtering when we draw |
| // ... not sure how meaningful this will be given our playback model. |
| |
| // Certainly safe to do nothing for the present. |
| } |
| |
| void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, |
| bool antialias) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| if (numPoints <= 1) |
| return; |
| |
| // FIXME: IMPLEMENT! |
| } |
| |
| void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, |
| const FloatPoint& point, int h, |
| const Color& backgroundColor, |
| ColorSpace colorSpace, int from, |
| int to, bool isActive) |
| { |
| if (paintingDisabled()) |
| return; |
| |
| syncPlatformContext(this); |
| platformContext()->drawHighlightForText(font, run, point, h, backgroundColor, |
| colorSpace, from, to, isActive); |
| } |
| |
| } // namespace WebCore |