// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BaseRenderingContext2D_h #define BaseRenderingContext2D_h #include "bindings/core/v8/UnionTypesCore.h" #include "bindings/modules/v8/UnionTypesModules.h" #include "modules/ModulesExport.h" #include "modules/canvas2d/CanvasGradient.h" #include "modules/canvas2d/CanvasPathMethods.h" #include "modules/canvas2d/CanvasRenderingContext2DState.h" #include "modules/canvas2d/CanvasStyle.h" #include "third_party/skia/include/core/SkCanvas.h" namespace blink { class CanvasImageSource; class Color; class Image; class ImageBuffer; class Path2D; class SVGMatrixTearOff; typedef HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrImageBitmap CanvasImageSourceUnion; class MODULES_EXPORT BaseRenderingContext2D : public WillBeGarbageCollectedMixin, public CanvasPathMethods { WTF_MAKE_NONCOPYABLE(BaseRenderingContext2D); public: ~BaseRenderingContext2D() override; void strokeStyle(StringOrCanvasGradientOrCanvasPattern&) const; void setStrokeStyle(const StringOrCanvasGradientOrCanvasPattern&); void fillStyle(StringOrCanvasGradientOrCanvasPattern&) const; void setFillStyle(const StringOrCanvasGradientOrCanvasPattern&); double lineWidth() const; void setLineWidth(double); String lineCap() const; void setLineCap(const String&); String lineJoin() const; void setLineJoin(const String&); double miterLimit() const; void setMiterLimit(double); const Vector& getLineDash() const; void setLineDash(const Vector&); double lineDashOffset() const; void setLineDashOffset(double); double shadowOffsetX() const; void setShadowOffsetX(double); double shadowOffsetY() const; void setShadowOffsetY(double); double shadowBlur() const; void setShadowBlur(double); String shadowColor() const; void setShadowColor(const String&); double globalAlpha() const; void setGlobalAlpha(double); String globalCompositeOperation() const; void setGlobalCompositeOperation(const String&); String filter() const; void setFilter(const String&); void save(); void restore(); PassRefPtrWillBeRawPtr currentTransform() const; void setCurrentTransform(PassRefPtrWillBeRawPtr); void scale(double sx, double sy); void rotate(double angleInRadians); void translate(double tx, double ty); void transform(double m11, double m12, double m21, double m22, double dx, double dy); void setTransform(double m11, double m12, double m21, double m22, double dx, double dy); void resetTransform(); void beginPath(); void fill(const String& winding = "nonzero"); void fill(Path2D*, const String& winding = "nonzero"); void stroke(); void stroke(Path2D*); void clip(const String& winding = "nonzero"); void clip(Path2D*, const String& winding = "nonzero"); bool isPointInPath(const double x, const double y, const String& winding = "nonzero"); bool isPointInPath(Path2D*, const double x, const double y, const String& winding = "nonzero"); bool isPointInStroke(const double x, const double y); bool isPointInStroke(Path2D*, const double x, const double y); void clearRect(double x, double y, double width, double height); void fillRect(double x, double y, double width, double height); void strokeRect(double x, double y, double width, double height); void drawImage(const CanvasImageSourceUnion&, double x, double y, ExceptionState&); void drawImage(const CanvasImageSourceUnion&, double x, double y, double width, double height, ExceptionState&); void drawImage(const CanvasImageSourceUnion&, double sx, double sy, double sw, double sh, double dx, double dy, double dw, double dh, ExceptionState&); void drawImage(CanvasImageSource*, double sx, double sy, double sw, double sh, double dx, double dy, double dw, double dh, ExceptionState&); CanvasGradient* createLinearGradient(double x0, double y0, double x1, double y1); CanvasGradient* createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1, ExceptionState&); CanvasPattern* createPattern(const CanvasImageSourceUnion&, const String& repetitionType, ExceptionState&); ImageData* createImageData(ImageData*, ExceptionState&) const; ImageData* createImageData(double width, double height, ExceptionState&) const; ImageData* getImageData(double sx, double sy, double sw, double sh, ExceptionState&) const; void putImageData(ImageData*, double dx, double dy, ExceptionState&); void putImageData(ImageData*, double dx, double dy, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight, ExceptionState&); bool imageSmoothingEnabled() const; void setImageSmoothingEnabled(bool); String imageSmoothingQuality() const; void setImageSmoothingQuality(const String&); virtual bool originClean() const = 0; virtual void setOriginTainted() = 0; virtual bool wouldTaintOrigin(CanvasImageSource*) = 0; virtual int width() const = 0; virtual int height() const = 0; virtual bool hasImageBuffer() const = 0; virtual ImageBuffer* imageBuffer() const = 0; virtual bool parseColorOrCurrentColor(Color&, const String& colorString) const = 0; virtual SkCanvas* drawingCanvas() const = 0; virtual SkCanvas* existingDrawingCanvas() const = 0; virtual void disableDeferral(DisableDeferralReason) = 0; virtual AffineTransform baseTransform() const = 0; virtual void didDraw(const SkIRect& dirtyRect) = 0; virtual bool stateHasFilter() = 0; virtual SkImageFilter* stateGetFilter() = 0; virtual void validateStateStack() = 0; virtual bool hasAlpha() const = 0; virtual bool isContextLost() const = 0; DECLARE_VIRTUAL_TRACE(); protected: BaseRenderingContext2D(); CanvasRenderingContext2DState& modifiableState(); const CanvasRenderingContext2DState& state() const { return *m_stateStack.last(); } bool computeDirtyRect(const FloatRect& localBounds, SkIRect*); bool computeDirtyRect(const FloatRect& localBounds, const SkIRect& transformedClipBounds, SkIRect*); template bool draw(const DrawFunc&, const ContainsFunc&, const SkRect& bounds, CanvasRenderingContext2DState::PaintType, CanvasRenderingContext2DState::ImageType = CanvasRenderingContext2DState::NoImage); void inflateStrokeRect(FloatRect&) const; enum DrawType { ClipFill, // Fill that is already known to cover the current clip UntransformedUnclippedFill }; void checkOverdraw(const SkRect&, const SkPaint*, CanvasRenderingContext2DState::ImageType, DrawType); WillBeHeapVector> m_stateStack; AntiAliasingMode m_clipAntialiasing; private: void realizeSaves(); bool shouldDrawImageAntialiased(const FloatRect& destRect) const; void drawPathInternal(const Path&, CanvasRenderingContext2DState::PaintType, SkPath::FillType = SkPath::kWinding_FillType); void drawImageInternal(SkCanvas*, CanvasImageSource*, Image*, const FloatRect& srcRect, const FloatRect& dstRect, const SkPaint*); void clipInternal(const Path&, const String& windingRuleString); bool isPointInPathInternal(const Path&, const double x, const double y, const String& windingRuleString); bool isPointInStrokeInternal(const Path&, const double x, const double y); static bool isFullCanvasCompositeMode(SkXfermode::Mode); template void compositedDraw(const DrawFunc&, SkCanvas*, CanvasRenderingContext2DState::PaintType, CanvasRenderingContext2DState::ImageType); void clearCanvas(); bool rectContainsTransformedRect(const FloatRect&, const SkIRect&) const; }; template bool BaseRenderingContext2D::draw(const DrawFunc& drawFunc, const ContainsFunc& drawCoversClipBounds, const SkRect& bounds, CanvasRenderingContext2DState::PaintType paintType, CanvasRenderingContext2DState::ImageType imageType) { if (!state().isTransformInvertible()) return false; SkIRect clipBounds; if (!drawingCanvas() || !drawingCanvas()->getClipDeviceBounds(&clipBounds)) return false; // If gradient size is zero, then paint nothing. CanvasStyle* style = state().style(paintType); if (style) { CanvasGradient* gradient = style->getCanvasGradient(); if (gradient && gradient->getGradient()->isZeroSize()) return false; } if (isFullCanvasCompositeMode(state().globalComposite()) || stateHasFilter()) { compositedDraw(drawFunc, drawingCanvas(), paintType, imageType); didDraw(clipBounds); } else if (state().globalComposite() == SkXfermode::kSrc_Mode) { clearCanvas(); // takes care of checkOverdraw() const SkPaint* paint = state().getPaint(paintType, DrawForegroundOnly, imageType); drawFunc(drawingCanvas(), paint); didDraw(clipBounds); } else { SkIRect dirtyRect; if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) { const SkPaint* paint = state().getPaint(paintType, DrawShadowAndForeground, imageType); if (paintType != CanvasRenderingContext2DState::StrokePaintType && drawCoversClipBounds(clipBounds)) checkOverdraw(bounds, paint, imageType, ClipFill); drawFunc(drawingCanvas(), paint); didDraw(dirtyRect); } } return true; } template void BaseRenderingContext2D::compositedDraw(const DrawFunc& drawFunc, SkCanvas* c, CanvasRenderingContext2DState::PaintType paintType, CanvasRenderingContext2DState::ImageType imageType) { SkImageFilter* filter = stateGetFilter(); ASSERT(isFullCanvasCompositeMode(state().globalComposite()) || filter); SkMatrix ctm = c->getTotalMatrix(); c->resetMatrix(); SkPaint compositePaint; compositePaint.setXfermodeMode(state().globalComposite()); if (state().shouldDrawShadows()) { // unroll into two independently composited passes if drawing shadows SkPaint shadowPaint = *state().getPaint(paintType, DrawShadowOnly, imageType); int saveCount = c->getSaveCount(); if (filter) { SkPaint filterPaint; filterPaint.setImageFilter(filter); // TODO(junov): crbug.com/502921 We could use primitive bounds if we knew that the filter // does not affect transparent black regions. c->saveLayer(nullptr, &shadowPaint); c->saveLayer(nullptr, &filterPaint); SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroundOnly, imageType); c->setMatrix(ctm); drawFunc(c, &foregroundPaint); } else { ASSERT(isFullCanvasCompositeMode(state().globalComposite())); c->saveLayer(nullptr, &compositePaint); shadowPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); c->setMatrix(ctm); drawFunc(c, &shadowPaint); } c->restoreToCount(saveCount); } compositePaint.setImageFilter(filter); // TODO(junov): crbug.com/502921 We could use primitive bounds if we knew that the filter // does not affect transparent black regions *and* !isFullCanvasCompositeMode c->saveLayer(nullptr, &compositePaint); SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroundOnly, imageType); foregroundPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); c->setMatrix(ctm); drawFunc(c, &foregroundPaint); c->restore(); c->setMatrix(ctm); } } // namespace blink #endif // BaseRenderingContext2D_h