|  | /* | 
|  | * Copyright 2010 The Android Open Source Project | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #ifndef SkDevice_DEFINED | 
|  | #define SkDevice_DEFINED | 
|  |  | 
|  | #include "include/core/SkCanvas.h" | 
|  | #include "include/core/SkColor.h" | 
|  | #include "include/core/SkRefCnt.h" | 
|  | #include "include/core/SkRegion.h" | 
|  | #include "include/core/SkShader.h" | 
|  | #include "include/core/SkSurfaceProps.h" | 
|  | #include "include/private/SkNoncopyable.h" | 
|  | #include "src/core/SkMatrixPriv.h" | 
|  | #include "src/core/SkMatrixProvider.h" | 
|  | #include "src/core/SkRasterClip.h" | 
|  | #include "src/shaders/SkShaderBase.h" | 
|  |  | 
|  | class SkBitmap; | 
|  | struct SkDrawShadowRec; | 
|  | class SkGlyphRun; | 
|  | class SkGlyphRunList; | 
|  | class SkImageFilter; | 
|  | class SkImageFilterCache; | 
|  | struct SkIRect; | 
|  | class SkMarkerStack; | 
|  | class SkRasterHandleAllocator; | 
|  | class SkSpecialImage; | 
|  |  | 
|  | namespace skif { class Mapping; } | 
|  | namespace skgpu { class BaseDevice; } | 
|  |  | 
|  | class SkBaseDevice : public SkRefCnt, public SkMatrixProvider { | 
|  | public: | 
|  | SkBaseDevice(const SkImageInfo&, const SkSurfaceProps&); | 
|  |  | 
|  | /** | 
|  | *  Return ImageInfo for this device. If the canvas is not backed by pixels | 
|  | *  (cpu or gpu), then the info's ColorType will be kUnknown_SkColorType. | 
|  | */ | 
|  | const SkImageInfo& imageInfo() const { return fInfo; } | 
|  |  | 
|  | /** | 
|  | *  Return SurfaceProps for this device. | 
|  | */ | 
|  | const SkSurfaceProps& surfaceProps() const { | 
|  | return fSurfaceProps; | 
|  | } | 
|  |  | 
|  | SkIRect bounds() const { return SkIRect::MakeWH(this->width(), this->height()); } | 
|  |  | 
|  | /** | 
|  | *  Return the bounds of the device in the coordinate space of the root | 
|  | *  canvas. The root device will have its top-left at 0,0, but other devices | 
|  | *  such as those associated with saveLayer may have a non-zero origin. | 
|  | */ | 
|  | void getGlobalBounds(SkIRect* bounds) const { | 
|  | SkASSERT(bounds); | 
|  | *bounds = SkMatrixPriv::MapRect(fDeviceToGlobal, SkRect::Make(this->bounds())).roundOut(); | 
|  | } | 
|  |  | 
|  | SkIRect getGlobalBounds() const { | 
|  | SkIRect bounds; | 
|  | this->getGlobalBounds(&bounds); | 
|  | return bounds; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *  Returns the bounding box of the current clip, in this device's | 
|  | *  coordinate space. No pixels outside of these bounds will be touched by | 
|  | *  draws unless the clip is further modified (at which point this will | 
|  | *  return the updated bounds). | 
|  | */ | 
|  | SkIRect devClipBounds() const { return this->onDevClipBounds(); } | 
|  |  | 
|  | int width() const { | 
|  | return this->imageInfo().width(); | 
|  | } | 
|  |  | 
|  | int height() const { | 
|  | return this->imageInfo().height(); | 
|  | } | 
|  |  | 
|  | bool isOpaque() const { | 
|  | return this->imageInfo().isOpaque(); | 
|  | } | 
|  |  | 
|  | bool writePixels(const SkPixmap&, int x, int y); | 
|  |  | 
|  | /** | 
|  | *  Try to get write-access to the pixels behind the device. If successful, this returns true | 
|  | *  and fills-out the pixmap parameter. On success it also bumps the genID of the underlying | 
|  | *  bitmap. | 
|  | * | 
|  | *  On failure, returns false and ignores the pixmap parameter. | 
|  | */ | 
|  | bool accessPixels(SkPixmap* pmap); | 
|  |  | 
|  | /** | 
|  | *  Try to get read-only-access to the pixels behind the device. If successful, this returns | 
|  | *  true and fills-out the pixmap parameter. | 
|  | * | 
|  | *  On failure, returns false and ignores the pixmap parameter. | 
|  | */ | 
|  | bool peekPixels(SkPixmap*); | 
|  |  | 
|  | /** | 
|  | *  Return the device's coordinate space transform: this maps from the device's coordinate space | 
|  | *  into the global canvas' space (or root device space). This includes the translation | 
|  | *  necessary to account for the device's origin. | 
|  | */ | 
|  | const SkM44& deviceToGlobal() const { return fDeviceToGlobal; } | 
|  | /** | 
|  | *  Return the inverse of getDeviceToGlobal(), mapping from the global canvas' space (or root | 
|  | *  device space) into this device's coordinate space. | 
|  | */ | 
|  | const SkM44& globalToDevice() const { return fGlobalToDevice; } | 
|  | /** | 
|  | *  DEPRECATED: This asserts that 'getDeviceToGlobal' is a translation matrix with integer | 
|  | *  components. In the future some SkDevices will have more complex device-to-global transforms, | 
|  | *  so getDeviceToGlobal() or getRelativeTransform() should be used instead. | 
|  | */ | 
|  | SkIPoint getOrigin() const; | 
|  | /** | 
|  | * Returns true when this device's pixel grid is axis aligned with the global coordinate space, | 
|  | * and any relative translation between the two spaces is in integer pixel units. | 
|  | */ | 
|  | bool isPixelAlignedToGlobal() const; | 
|  | /** | 
|  | * Get the transformation from this device's coordinate system to the provided device space. | 
|  | * This transform can be used to draw this device into the provided device, such that once | 
|  | * that device is drawn to the root device, the net effect will be that this device's contents | 
|  | * have been transformed by the global CTM. | 
|  | */ | 
|  | SkMatrix getRelativeTransform(const SkBaseDevice&) const; | 
|  |  | 
|  | virtual void* getRasterHandle() const { return nullptr; } | 
|  |  | 
|  | SkMarkerStack* markerStack() const { return fMarkerStack; } | 
|  | void setMarkerStack(SkMarkerStack* ms) { fMarkerStack = ms; } | 
|  |  | 
|  | // SkMatrixProvider interface: | 
|  | bool getLocalToMarker(uint32_t, SkM44* localToMarker) const override; | 
|  | bool localToDeviceHitsPixelCenters() const override { return true; } | 
|  |  | 
|  | const SkMatrixProvider& asMatrixProvider() const { return *this; } | 
|  |  | 
|  | void save() { this->onSave(); } | 
|  | void restore(const SkM44& ctm) { | 
|  | this->onRestore(); | 
|  | this->setGlobalCTM(ctm); | 
|  | } | 
|  | void restoreLocal(const SkM44& localToDevice) { | 
|  | this->onRestore(); | 
|  | this->setLocalToDevice(localToDevice); | 
|  | } | 
|  | void clipRect(const SkRect& rect, SkClipOp op, bool aa) { | 
|  | this->onClipRect(rect, op, aa); | 
|  | } | 
|  | void clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) { | 
|  | this->onClipRRect(rrect, op, aa); | 
|  | } | 
|  | void clipPath(const SkPath& path, SkClipOp op, bool aa) { | 
|  | this->onClipPath(path, op, aa); | 
|  | } | 
|  | void clipShader(sk_sp<SkShader> sh, SkClipOp op) { | 
|  | sh = as_SB(sh)->makeWithCTM(this->localToDevice()); | 
|  | if (op == SkClipOp::kDifference) { | 
|  | sh = as_SB(sh)->makeInvertAlpha(); | 
|  | } | 
|  | this->onClipShader(std::move(sh)); | 
|  | } | 
|  | void clipRegion(const SkRegion& region, SkClipOp op) { | 
|  | this->onClipRegion(region, op); | 
|  | } | 
|  | void replaceClip(const SkIRect& rect) { | 
|  | this->onReplaceClip(rect); | 
|  | } | 
|  |  | 
|  | bool clipIsWideOpen() const { | 
|  | return this->onClipIsWideOpen(); | 
|  | } | 
|  |  | 
|  | void setLocalToDevice(const SkM44& localToDevice) { | 
|  | fLocalToDevice = localToDevice; | 
|  | fLocalToDevice33 = fLocalToDevice.asM33(); | 
|  | } | 
|  | void setGlobalCTM(const SkM44& ctm); | 
|  | virtual void validateDevBounds(const SkIRect&) {} | 
|  |  | 
|  | virtual bool android_utils_clipWithStencil() { return false; } | 
|  |  | 
|  | virtual skgpu::BaseDevice* asGpuDevice() { return nullptr; } | 
|  |  | 
|  | // Ensure that non-RSXForm runs are passed to onDrawGlyphRunList. | 
|  | void drawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint); | 
|  |  | 
|  | protected: | 
|  | enum TileUsage { | 
|  | kPossible_TileUsage,    //!< the created device may be drawn tiled | 
|  | kNever_TileUsage,       //!< the created device will never be drawn tiled | 
|  | }; | 
|  |  | 
|  | struct TextFlags { | 
|  | uint32_t    fFlags;     // SkPaint::getFlags() | 
|  | }; | 
|  |  | 
|  | virtual void onSave() {} | 
|  | virtual void onRestore() {} | 
|  | virtual void onClipRect(const SkRect& rect, SkClipOp, bool aa) {} | 
|  | virtual void onClipRRect(const SkRRect& rrect, SkClipOp, bool aa) {} | 
|  | virtual void onClipPath(const SkPath& path, SkClipOp, bool aa) {} | 
|  | virtual void onClipShader(sk_sp<SkShader>) {} | 
|  | virtual void onClipRegion(const SkRegion& deviceRgn, SkClipOp) {} | 
|  | virtual void onReplaceClip(const SkIRect& rect) {} | 
|  | virtual bool onClipIsAA() const = 0; | 
|  | virtual bool onClipIsWideOpen() const = 0; | 
|  | virtual void onAsRgnClip(SkRegion*) const = 0; | 
|  | enum class ClipType { | 
|  | kEmpty, | 
|  | kRect, | 
|  | kComplex | 
|  | }; | 
|  | virtual ClipType onGetClipType() const = 0; | 
|  |  | 
|  | // This should strive to be as tight as possible, ideally not just mapping | 
|  | // the global clip bounds by fToGlobal^-1. | 
|  | virtual SkIRect onDevClipBounds() const = 0; | 
|  |  | 
|  | /** These are called inside the per-device-layer loop for each draw call. | 
|  | When these are called, we have already applied any saveLayer operations, | 
|  | and are handling any looping from the paint. | 
|  | */ | 
|  | virtual void drawPaint(const SkPaint& paint) = 0; | 
|  | virtual void drawPoints(SkCanvas::PointMode mode, size_t count, | 
|  | const SkPoint[], const SkPaint& paint) = 0; | 
|  | virtual void drawRect(const SkRect& r, | 
|  | const SkPaint& paint) = 0; | 
|  | virtual void drawRegion(const SkRegion& r, | 
|  | const SkPaint& paint); | 
|  | virtual void drawOval(const SkRect& oval, | 
|  | const SkPaint& paint) = 0; | 
|  | /** By the time this is called we know that abs(sweepAngle) is in the range [0, 360). */ | 
|  | virtual void drawArc(const SkRect& oval, SkScalar startAngle, | 
|  | SkScalar sweepAngle, bool useCenter, const SkPaint& paint); | 
|  | virtual void drawRRect(const SkRRect& rr, | 
|  | const SkPaint& paint) = 0; | 
|  |  | 
|  | // Default impl calls drawPath() | 
|  | virtual void drawDRRect(const SkRRect& outer, | 
|  | const SkRRect& inner, const SkPaint&); | 
|  |  | 
|  | /** | 
|  | *  If pathIsMutable, then the implementation is allowed to cast path to a | 
|  | *  non-const pointer and modify it in place (as an optimization). Canvas | 
|  | *  may do this to implement helpers such as drawOval, by placing a temp | 
|  | *  path on the stack to hold the representation of the oval. | 
|  | */ | 
|  | virtual void drawPath(const SkPath& path, | 
|  | const SkPaint& paint, | 
|  | bool pathIsMutable = false) = 0; | 
|  |  | 
|  | virtual void drawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, | 
|  | const SkSamplingOptions&, const SkPaint&, | 
|  | SkCanvas::SrcRectConstraint) = 0; | 
|  | virtual void drawImageLattice(const SkImage*, const SkCanvas::Lattice&, | 
|  | const SkRect& dst, SkFilterMode, const SkPaint&); | 
|  |  | 
|  | virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) = 0; | 
|  | virtual void drawShadow(const SkPath&, const SkDrawShadowRec&); | 
|  |  | 
|  | // default implementation calls drawVertices | 
|  | virtual void drawPatch(const SkPoint cubics[12], const SkColor colors[4], | 
|  | const SkPoint texCoords[4], SkBlendMode, const SkPaint& paint); | 
|  |  | 
|  | // default implementation calls drawPath | 
|  | virtual void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[], | 
|  | const SkColor[], int count, SkBlendMode, const SkSamplingOptions&, | 
|  | const SkPaint&); | 
|  |  | 
|  | virtual void drawAnnotation(const SkRect&, const char[], SkData*) {} | 
|  |  | 
|  | // Default impl always calls drawRect() with a solid-color paint, setting it to anti-aliased | 
|  | // only when all edge flags are set. If there's a clip region, it draws that using drawPath, | 
|  | // or uses clipPath(). | 
|  | virtual void drawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], | 
|  | SkCanvas::QuadAAFlags aaFlags, const SkColor4f& color, | 
|  | SkBlendMode mode); | 
|  | // Default impl uses drawImageRect per entry, being anti-aliased only when an entry's edge flags | 
|  | // are all set. If there's a clip region, it will be applied using clipPath(). | 
|  | virtual void drawEdgeAAImageSet(const SkCanvas::ImageSetEntry[], int count, | 
|  | const SkPoint dstClips[], const SkMatrix preViewMatrices[], | 
|  | const SkSamplingOptions&, const SkPaint&, | 
|  | SkCanvas::SrcRectConstraint); | 
|  |  | 
|  | virtual void drawDrawable(SkDrawable*, const SkMatrix*, SkCanvas*); | 
|  |  | 
|  | // Only called with glyphRunLists that do not contain RSXForm. | 
|  | virtual void onDrawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint) = 0; | 
|  |  | 
|  | /** | 
|  | * The SkDevice passed will be an SkDevice which was returned by a call to | 
|  | * onCreateDevice on this device with kNeverTile_TileExpectation. | 
|  | * | 
|  | * The default implementation calls snapSpecial() and drawSpecial() with the relative transform | 
|  | * from the input device to this device. The provided SkPaint cannot have a mask filter or | 
|  | * image filter, and any shader is ignored. | 
|  | */ | 
|  | virtual void drawDevice(SkBaseDevice*, const SkSamplingOptions&, const SkPaint&); | 
|  |  | 
|  | /** | 
|  | * Draw the special image's subset to this device, subject to the given matrix transform instead | 
|  | * of the device's current local to device matrix. | 
|  | */ | 
|  | virtual void drawSpecial(SkSpecialImage*, const SkMatrix& localToDevice, | 
|  | const SkSamplingOptions&, const SkPaint&); | 
|  |  | 
|  | /** | 
|  | * Evaluate 'filter' and draw the final output into this device using 'paint'. The 'mapping' | 
|  | * defines the parameter-to-layer space transform used to evaluate the image filter on 'src', | 
|  | * and the layer-to-device space transform that is used to draw the result into this device. | 
|  | * Since 'mapping' fully specifies the transform, this draw function ignores the current | 
|  | * local-to-device matrix (i.e. just like drawSpecial and drawDevice). | 
|  | * | 
|  | * The final paint must not have an image filter or mask filter set on it; a shader is ignored. | 
|  | */ | 
|  | virtual void drawFilteredImage(const skif::Mapping& mapping, SkSpecialImage* src, | 
|  | const SkImageFilter*, const SkSamplingOptions&, const SkPaint&); | 
|  |  | 
|  | virtual sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&); | 
|  | virtual sk_sp<SkSpecialImage> makeSpecial(const SkImage*); | 
|  |  | 
|  | // Get a view of the entire device's current contents as an image. | 
|  | sk_sp<SkSpecialImage> snapSpecial(); | 
|  | // Snap the 'subset' contents from this device, possibly as a read-only view. If 'forceCopy' | 
|  | // is true then the returned image's pixels must not be affected by subsequent draws into the | 
|  | // device. When 'forceCopy' is false, the image can be a view into the device's pixels | 
|  | // (avoiding a copy for performance, at the expense of safety). Default returns null. | 
|  | virtual sk_sp<SkSpecialImage> snapSpecial(const SkIRect& subset, bool forceCopy = false); | 
|  |  | 
|  | virtual void setImmutable() {} | 
|  |  | 
|  | bool readPixels(const SkPixmap&, int x, int y); | 
|  |  | 
|  | virtual sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&); | 
|  | virtual bool onPeekPixels(SkPixmap*) { return false; } | 
|  |  | 
|  | /** | 
|  | *  The caller is responsible for "pre-clipping" the dst. The impl can assume that the dst | 
|  | *  image at the specified x,y offset will fit within the device's bounds. | 
|  | * | 
|  | *  This is explicitly asserted in readPixels(), the public way to call this. | 
|  | */ | 
|  | virtual bool onReadPixels(const SkPixmap&, int x, int y); | 
|  |  | 
|  | /** | 
|  | *  The caller is responsible for "pre-clipping" the src. The impl can assume that the src | 
|  | *  image at the specified x,y offset will fit within the device's bounds. | 
|  | * | 
|  | *  This is explicitly asserted in writePixelsDirect(), the public way to call this. | 
|  | */ | 
|  | virtual bool onWritePixels(const SkPixmap&, int x, int y); | 
|  |  | 
|  | virtual bool onAccessPixels(SkPixmap*) { return false; } | 
|  |  | 
|  | struct CreateInfo { | 
|  | CreateInfo(const SkImageInfo& info, | 
|  | SkPixelGeometry geo, | 
|  | TileUsage tileUsage, | 
|  | bool trackCoverage, | 
|  | SkRasterHandleAllocator* allocator) | 
|  | : fInfo(info) | 
|  | , fTileUsage(tileUsage) | 
|  | , fPixelGeometry(geo) | 
|  | , fTrackCoverage(trackCoverage) | 
|  | , fAllocator(allocator) | 
|  | {} | 
|  |  | 
|  | const SkImageInfo       fInfo; | 
|  | const TileUsage         fTileUsage; | 
|  | const SkPixelGeometry   fPixelGeometry; | 
|  | const bool              fTrackCoverage = false; | 
|  | SkRasterHandleAllocator* fAllocator = nullptr; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | *  Create a new device based on CreateInfo. If the paint is not null, then it represents a | 
|  | *  preview of how the new device will be composed with its creator device (this). | 
|  | * | 
|  | *  The subclass may be handed this device in drawDevice(), so it must always return | 
|  | *  a device that it knows how to draw, and that it knows how to identify if it is not of the | 
|  | *  same subclass (since drawDevice is passed a SkBaseDevice*). If the subclass cannot fulfill | 
|  | *  that contract (e.g. PDF cannot support some settings on the paint) it should return NULL, | 
|  | *  and the caller may then decide to explicitly create a bitmapdevice, knowing that later | 
|  | *  it could not call drawDevice with it (but it could call drawSprite or drawBitmap). | 
|  | */ | 
|  | virtual SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // SkCanvas uses NoPixelsDevice when onCreateDevice fails; but then it needs to be able to | 
|  | // inspect a layer's device to know if calling drawDevice() later is allowed. | 
|  | virtual bool isNoPixelsDevice() const { return false; } | 
|  |  | 
|  | private: | 
|  | friend class SkAndroidFrameworkUtils; | 
|  | friend class SkCanvas; | 
|  | friend class SkDraw; | 
|  | friend class SkSurface_Raster; | 
|  | friend class DeviceTestingAccess; | 
|  |  | 
|  | void simplifyGlyphRunRSXFormAndRedraw(const SkGlyphRunList& glyphRunList, const SkPaint& paint); | 
|  |  | 
|  | // used to change the backend's pixels (and possibly config/rowbytes) | 
|  | // but cannot change the width/height, so there should be no change to | 
|  | // any clip information. | 
|  | // TODO: move to SkBitmapDevice | 
|  | virtual void replaceBitmapBackendForRasterSurface(const SkBitmap&) {} | 
|  |  | 
|  | virtual bool forceConservativeRasterClip() const { return false; } | 
|  |  | 
|  | // Configure the device's coordinate spaces, specifying both how its device image maps back to | 
|  | // the global space (via 'deviceToGlobal') and the initial CTM of the device (via | 
|  | // 'localToDevice', i.e. what geometry drawn into this device will be transformed with). | 
|  | // | 
|  | // (bufferOriginX, bufferOriginY) defines where the (0,0) pixel the device's backing buffer | 
|  | // is anchored in the device space. The final device-to-global matrix stored by the SkDevice | 
|  | // will include a pre-translation by T(deviceOriginX, deviceOriginY), and the final | 
|  | // local-to-device matrix will have a post-translation of T(-deviceOriginX, -deviceOriginY). | 
|  | // | 
|  | // Returns false if the final device coordinate space is invalid, in which case the canvas | 
|  | // should discard the device | 
|  | bool SK_WARN_UNUSED_RESULT setDeviceCoordinateSystem(const SkM44& deviceToGlobal, | 
|  | const SkM44& localToDevice, | 
|  | int bufferOriginX, int bufferOriginY); | 
|  | // Convenience to configure the device to be axis-aligned with the root canvas, but with a | 
|  | // unique origin. | 
|  | void setOrigin(const SkM44& globalCTM, int x, int y) { | 
|  | SkAssertResult(this->setDeviceCoordinateSystem(SkM44(), globalCTM, x, y)); | 
|  | } | 
|  |  | 
|  | virtual SkImageFilterCache* getImageFilterCache() { return nullptr; } | 
|  |  | 
|  | friend class SkNoPixelsDevice; | 
|  | friend class SkBitmapDevice; | 
|  | void privateResize(int w, int h) { | 
|  | *const_cast<SkImageInfo*>(&fInfo) = fInfo.makeWH(w, h); | 
|  | } | 
|  |  | 
|  | SkMarkerStack* fMarkerStack = nullptr;  // does not own this, set in setMarkerStack() | 
|  |  | 
|  | const SkImageInfo    fInfo; | 
|  | const SkSurfaceProps fSurfaceProps; | 
|  | // fDeviceToGlobal and fGlobalToDevice are inverses of each other; there are never that many | 
|  | // SkDevices, so pay the memory cost to avoid recalculating the inverse. | 
|  | SkM44 fDeviceToGlobal; | 
|  | SkM44 fGlobalToDevice; | 
|  |  | 
|  | // fLocalToDevice (inherited from SkMatrixProvider) is the device CTM, not the global CTM | 
|  | // It maps from local space to the device's coordinate space. | 
|  | // fDeviceToGlobal * fLocalToDevice will match the canvas' CTM. | 
|  |  | 
|  | using INHERITED = SkRefCnt; | 
|  | }; | 
|  |  | 
|  | class SkNoPixelsDevice : public SkBaseDevice { | 
|  | public: | 
|  | SkNoPixelsDevice(const SkIRect& bounds, const SkSurfaceProps& props, | 
|  | sk_sp<SkColorSpace> colorSpace = nullptr) | 
|  | : SkBaseDevice(SkImageInfo::Make(bounds.size(), kUnknown_SkColorType, | 
|  | kUnknown_SkAlphaType, std::move(colorSpace)), | 
|  | props) { | 
|  | // this fails if we enable this assert: DiscardableImageMapTest.GetDiscardableImagesInRectMaxImage | 
|  | //SkASSERT(bounds.width() >= 0 && bounds.height() >= 0); | 
|  |  | 
|  | this->setOrigin(SkM44(), bounds.left(), bounds.top()); | 
|  | this->resetClipStack(); | 
|  | } | 
|  |  | 
|  | void resetForNextPicture(const SkIRect& bounds) { | 
|  | //SkASSERT(bounds.width() >= 0 && bounds.height() >= 0); | 
|  | this->privateResize(bounds.width(), bounds.height()); | 
|  | this->setOrigin(SkM44(), bounds.left(), bounds.top()); | 
|  | this->resetClipStack(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // SkNoPixelsDevice tracks the clip conservatively in order to respond to some queries as | 
|  | // accurately as possible while emphasizing performance | 
|  | void onSave() override; | 
|  | void onRestore() override; | 
|  | void onClipRect(const SkRect& rect, SkClipOp op, bool aa) override; | 
|  | void onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) override; | 
|  | void onClipPath(const SkPath& path, SkClipOp op, bool aa) override; | 
|  | void onClipRegion(const SkRegion& globalRgn, SkClipOp op) override; | 
|  | void onClipShader(sk_sp<SkShader> shader) override; | 
|  | void onReplaceClip(const SkIRect& rect) override; | 
|  | bool onClipIsAA() const override { return this->clip().fIsAA; } | 
|  | bool onClipIsWideOpen() const override { | 
|  | return this->clip().fIsRect && | 
|  | this->onDevClipBounds() == this->bounds(); | 
|  | } | 
|  | void onAsRgnClip(SkRegion* rgn) const override { | 
|  | rgn->setRect(this->onDevClipBounds()); | 
|  | } | 
|  | ClipType onGetClipType() const override; | 
|  | SkIRect onDevClipBounds() const override { return this->clip().fClipBounds; } | 
|  |  | 
|  | void drawPaint(const SkPaint& paint) override {} | 
|  | void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&) override {} | 
|  | void drawImageRect(const SkImage*, const SkRect*, const SkRect&, | 
|  | const SkSamplingOptions&, const SkPaint&, | 
|  | SkCanvas::SrcRectConstraint) override {} | 
|  | void drawRect(const SkRect&, const SkPaint&) override {} | 
|  | void drawOval(const SkRect&, const SkPaint&) override {} | 
|  | void drawRRect(const SkRRect&, const SkPaint&) override {} | 
|  | void drawPath(const SkPath&, const SkPaint&, bool) override {} | 
|  | void drawDevice(SkBaseDevice*, const SkSamplingOptions&, const SkPaint&) override {} | 
|  | void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override {} | 
|  |  | 
|  | void drawFilteredImage(const skif::Mapping&, SkSpecialImage* src, const SkImageFilter*, | 
|  | const SkSamplingOptions&, const SkPaint&) override {} | 
|  |  | 
|  | void onDrawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint) override {} | 
|  |  | 
|  |  | 
|  | bool isNoPixelsDevice() const override { return true; } | 
|  |  | 
|  | private: | 
|  | struct ClipState { | 
|  | SkIRect fClipBounds; | 
|  | int fDeferredSaveCount; | 
|  | bool fIsAA; | 
|  | bool fIsRect; | 
|  |  | 
|  | ClipState(const SkIRect& bounds, bool isAA, bool isRect) | 
|  | : fClipBounds(bounds) | 
|  | , fDeferredSaveCount(0) | 
|  | , fIsAA(isAA) | 
|  | , fIsRect(isRect) {} | 
|  |  | 
|  | void op(SkClipOp op, const SkM44& transform, const SkRect& bounds, | 
|  | bool isAA, bool fillsBounds); | 
|  | }; | 
|  |  | 
|  | const ClipState& clip() const { return fClipStack.back(); } | 
|  | ClipState& writableClip(); | 
|  |  | 
|  | void resetClipStack() { | 
|  | fClipStack.reset(); | 
|  | fClipStack.emplace_back(this->bounds(), /*isAA=*/false, /*isRect=*/true); | 
|  | } | 
|  |  | 
|  | SkSTArray<4, ClipState> fClipStack; | 
|  |  | 
|  | using INHERITED = SkBaseDevice; | 
|  | }; | 
|  |  | 
|  | class SkAutoDeviceTransformRestore : SkNoncopyable { | 
|  | public: | 
|  | SkAutoDeviceTransformRestore(SkBaseDevice* device, const SkMatrix& localToDevice) | 
|  | : fDevice(device) | 
|  | , fPrevLocalToDevice(device->localToDevice()) | 
|  | { | 
|  | fDevice->setLocalToDevice(SkM44(localToDevice)); | 
|  | } | 
|  | ~SkAutoDeviceTransformRestore() { | 
|  | fDevice->setLocalToDevice(fPrevLocalToDevice); | 
|  | } | 
|  |  | 
|  | private: | 
|  | SkBaseDevice* fDevice; | 
|  | const SkM44   fPrevLocalToDevice; | 
|  | }; | 
|  |  | 
|  | #endif |