xref: /third_party/skia/src/core/SkDevice.h (revision cb93a386)
1/*
2 * Copyright 2010 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SkDevice_DEFINED
9#define SkDevice_DEFINED
10
11#include "include/core/SkBlurTypes.h"
12#include "include/core/SkCanvas.h"
13#include "include/core/SkColor.h"
14#include "include/core/SkRefCnt.h"
15#include "include/core/SkRegion.h"
16#include "include/core/SkShader.h"
17#include "include/core/SkSurfaceProps.h"
18#include "include/private/SkNoncopyable.h"
19#include "src/core/SkMatrixPriv.h"
20#include "src/core/SkMatrixProvider.h"
21#include "src/core/SkRasterClip.h"
22#include "src/shaders/SkShaderBase.h"
23
24class SkBitmap;
25struct SkDrawShadowRec;
26class SkGlyphRun;
27class SkGlyphRunList;
28class SkImageFilter;
29class SkImageFilterCache;
30struct SkIRect;
31class SkMarkerStack;
32class SkRasterHandleAllocator;
33class SkSpecialImage;
34
35namespace skif { class Mapping; }
36namespace skgpu { class BaseDevice; }
37
38class SkBaseDevice : public SkRefCnt, public SkMatrixProvider {
39public:
40    SkBaseDevice(const SkImageInfo&, const SkSurfaceProps&);
41
42    /**
43     *  Return ImageInfo for this device. If the canvas is not backed by pixels
44     *  (cpu or gpu), then the info's ColorType will be kUnknown_SkColorType.
45     */
46    const SkImageInfo& imageInfo() const { return fInfo; }
47
48    /**
49     *  Return SurfaceProps for this device.
50     */
51    const SkSurfaceProps& surfaceProps() const {
52        return fSurfaceProps;
53    }
54
55    SkIRect bounds() const { return SkIRect::MakeWH(this->width(), this->height()); }
56
57    /**
58     *  Return the bounds of the device in the coordinate space of the root
59     *  canvas. The root device will have its top-left at 0,0, but other devices
60     *  such as those associated with saveLayer may have a non-zero origin.
61     */
62    void getGlobalBounds(SkIRect* bounds) const {
63        SkASSERT(bounds);
64        *bounds = SkMatrixPriv::MapRect(fDeviceToGlobal, SkRect::Make(this->bounds())).roundOut();
65    }
66
67    SkIRect getGlobalBounds() const {
68        SkIRect bounds;
69        this->getGlobalBounds(&bounds);
70        return bounds;
71    }
72
73    /**
74     *  Returns the bounding box of the current clip, in this device's
75     *  coordinate space. No pixels outside of these bounds will be touched by
76     *  draws unless the clip is further modified (at which point this will
77     *  return the updated bounds).
78     */
79    SkIRect devClipBounds() const { return this->onDevClipBounds(); }
80
81    int width() const {
82        return this->imageInfo().width();
83    }
84
85    int height() const {
86        return this->imageInfo().height();
87    }
88
89    bool isOpaque() const {
90        return this->imageInfo().isOpaque();
91    }
92
93    bool writePixels(const SkPixmap&, int x, int y);
94
95    /**
96     *  Try to get write-access to the pixels behind the device. If successful, this returns true
97     *  and fills-out the pixmap parameter. On success it also bumps the genID of the underlying
98     *  bitmap.
99     *
100     *  On failure, returns false and ignores the pixmap parameter.
101     */
102    bool accessPixels(SkPixmap* pmap);
103
104    /**
105     *  Try to get read-only-access to the pixels behind the device. If successful, this returns
106     *  true and fills-out the pixmap parameter.
107     *
108     *  On failure, returns false and ignores the pixmap parameter.
109     */
110    bool peekPixels(SkPixmap*);
111
112    /**
113     *  Return the device's coordinate space transform: this maps from the device's coordinate space
114     *  into the global canvas' space (or root device space). This includes the translation
115     *  necessary to account for the device's origin.
116     */
117    const SkM44& deviceToGlobal() const { return fDeviceToGlobal; }
118    /**
119     *  Return the inverse of getDeviceToGlobal(), mapping from the global canvas' space (or root
120     *  device space) into this device's coordinate space.
121     */
122    const SkM44& globalToDevice() const { return fGlobalToDevice; }
123    /**
124     *  DEPRECATED: This asserts that 'getDeviceToGlobal' is a translation matrix with integer
125     *  components. In the future some SkDevices will have more complex device-to-global transforms,
126     *  so getDeviceToGlobal() or getRelativeTransform() should be used instead.
127     */
128    SkIPoint getOrigin() const;
129    /**
130     * Returns true when this device's pixel grid is axis aligned with the global coordinate space,
131     * and any relative translation between the two spaces is in integer pixel units.
132     */
133    bool isPixelAlignedToGlobal() const;
134    /**
135     * Get the transformation from this device's coordinate system to the provided device space.
136     * This transform can be used to draw this device into the provided device, such that once
137     * that device is drawn to the root device, the net effect will be that this device's contents
138     * have been transformed by the global CTM.
139     */
140    SkMatrix getRelativeTransform(const SkBaseDevice&) const;
141
142    virtual void* getRasterHandle() const { return nullptr; }
143
144    SkMarkerStack* markerStack() const { return fMarkerStack; }
145    void setMarkerStack(SkMarkerStack* ms) { fMarkerStack = ms; }
146
147    // SkMatrixProvider interface:
148    bool getLocalToMarker(uint32_t, SkM44* localToMarker) const override;
149    bool localToDeviceHitsPixelCenters() const override { return true; }
150
151    const SkMatrixProvider& asMatrixProvider() const { return *this; }
152
153    void save() { this->onSave(); }
154    void restore(const SkM44& ctm) {
155        this->onRestore();
156        this->setGlobalCTM(ctm);
157    }
158    void restoreLocal(const SkM44& localToDevice) {
159        this->onRestore();
160        this->setLocalToDevice(localToDevice);
161    }
162    void clipRect(const SkRect& rect, SkClipOp op, bool aa) {
163        this->onClipRect(rect, op, aa);
164    }
165    void clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
166        this->onClipRRect(rrect, op, aa);
167    }
168    void clipPath(const SkPath& path, SkClipOp op, bool aa) {
169        this->onClipPath(path, op, aa);
170    }
171    void clipShader(sk_sp<SkShader> sh, SkClipOp op) {
172        sh = as_SB(sh)->makeWithCTM(this->localToDevice());
173        if (op == SkClipOp::kDifference) {
174            sh = as_SB(sh)->makeInvertAlpha();
175        }
176        this->onClipShader(std::move(sh));
177    }
178    void clipRegion(const SkRegion& region, SkClipOp op) {
179        this->onClipRegion(region, op);
180    }
181    void replaceClip(const SkIRect& rect) {
182        this->onReplaceClip(rect);
183    }
184
185    bool clipIsWideOpen() const {
186        return this->onClipIsWideOpen();
187    }
188
189    void setLocalToDevice(const SkM44& localToDevice) {
190        fLocalToDevice = localToDevice;
191        fLocalToDevice33 = fLocalToDevice.asM33();
192    }
193    void setGlobalCTM(const SkM44& ctm);
194    virtual void validateDevBounds(const SkIRect&) {}
195
196    virtual bool android_utils_clipWithStencil() { return false; }
197
198    virtual skgpu::BaseDevice* asGpuDevice() { return nullptr; }
199
200    // Ensure that non-RSXForm runs are passed to onDrawGlyphRunList.
201    void drawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint);
202
203protected:
204    enum TileUsage {
205        kPossible_TileUsage,    //!< the created device may be drawn tiled
206        kNever_TileUsage,       //!< the created device will never be drawn tiled
207    };
208
209    struct TextFlags {
210        uint32_t    fFlags;     // SkPaint::getFlags()
211    };
212
213    virtual void onSave() {}
214    virtual void onRestore() {}
215    virtual void onClipRect(const SkRect& rect, SkClipOp, bool aa) {}
216    virtual void onClipRRect(const SkRRect& rrect, SkClipOp, bool aa) {}
217    virtual void onClipPath(const SkPath& path, SkClipOp, bool aa) {}
218    virtual void onClipShader(sk_sp<SkShader>) {}
219    virtual void onClipRegion(const SkRegion& deviceRgn, SkClipOp) {}
220    virtual void onReplaceClip(const SkIRect& rect) {}
221    virtual bool onClipIsAA() const = 0;
222    virtual bool onClipIsWideOpen() const = 0;
223    virtual void onAsRgnClip(SkRegion*) const = 0;
224    enum class ClipType {
225        kEmpty,
226        kRect,
227        kComplex
228    };
229    virtual ClipType onGetClipType() const = 0;
230
231    // This should strive to be as tight as possible, ideally not just mapping
232    // the global clip bounds by fToGlobal^-1.
233    virtual SkIRect onDevClipBounds() const = 0;
234
235    /** These are called inside the per-device-layer loop for each draw call.
236     When these are called, we have already applied any saveLayer operations,
237     and are handling any looping from the paint.
238     */
239    virtual void drawPaint(const SkPaint& paint) = 0;
240    virtual void drawPoints(SkCanvas::PointMode mode, size_t count,
241                            const SkPoint[], const SkPaint& paint) = 0;
242    virtual void drawRect(const SkRect& r,
243                          const SkPaint& paint) = 0;
244    virtual void drawRegion(const SkRegion& r,
245                            const SkPaint& paint);
246    virtual void drawOval(const SkRect& oval,
247                          const SkPaint& paint) = 0;
248    /** By the time this is called we know that abs(sweepAngle) is in the range [0, 360). */
249    virtual void drawArc(const SkRect& oval, SkScalar startAngle,
250                         SkScalar sweepAngle, bool useCenter, const SkPaint& paint);
251    virtual void drawRRect(const SkRRect& rr,
252                           const SkPaint& paint) = 0;
253
254    // Default impl calls drawPath()
255    virtual void drawDRRect(const SkRRect& outer,
256                            const SkRRect& inner, const SkPaint&);
257
258    /**
259     *  If pathIsMutable, then the implementation is allowed to cast path to a
260     *  non-const pointer and modify it in place (as an optimization). Canvas
261     *  may do this to implement helpers such as drawOval, by placing a temp
262     *  path on the stack to hold the representation of the oval.
263     */
264    virtual void drawPath(const SkPath& path,
265                          const SkPaint& paint,
266                          bool pathIsMutable = false) = 0;
267
268    virtual void drawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
269                               const SkSamplingOptions&, const SkPaint&,
270                               SkCanvas::SrcRectConstraint) = 0;
271    virtual void drawImageLattice(const SkImage*, const SkCanvas::Lattice&,
272                                  const SkRect& dst, SkFilterMode, const SkPaint&);
273
274    virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) = 0;
275    virtual void drawShadow(const SkPath&, const SkDrawShadowRec&);
276
277    // default implementation calls drawVertices
278    virtual void drawPatch(const SkPoint cubics[12], const SkColor colors[4],
279                           const SkPoint texCoords[4], SkBlendMode, const SkPaint& paint);
280
281    // default implementation calls drawVertices
282    virtual void drawAtlas(const SkRSXform[], const SkRect[], const SkColor[], int count,
283                           SkBlendMode, const SkPaint&);
284
285    virtual void drawAnnotation(const SkRect&, const char[], SkData*) {}
286
287    // Default impl always calls drawRect() with a solid-color paint, setting it to anti-aliased
288    // only when all edge flags are set. If there's a clip region, it draws that using drawPath,
289    // or uses clipPath().
290    virtual void drawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
291                                SkCanvas::QuadAAFlags aaFlags, const SkColor4f& color,
292                                SkBlendMode mode);
293    // Default impl uses drawImageRect per entry, being anti-aliased only when an entry's edge flags
294    // are all set. If there's a clip region, it will be applied using clipPath().
295    virtual void drawEdgeAAImageSet(const SkCanvas::ImageSetEntry[], int count,
296                                    const SkPoint dstClips[], const SkMatrix preViewMatrices[],
297                                    const SkSamplingOptions&, const SkPaint&,
298                                    SkCanvas::SrcRectConstraint);
299
300    virtual void drawDrawable(SkDrawable*, const SkMatrix*, SkCanvas*);
301
302    // Only called with glyphRunLists that do not contain RSXForm.
303    virtual void onDrawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint) = 0;
304
305    /**
306     * The SkDevice passed will be an SkDevice which was returned by a call to
307     * onCreateDevice on this device with kNeverTile_TileExpectation.
308     *
309     * The default implementation calls snapSpecial() and drawSpecial() with the relative transform
310     * from the input device to this device. The provided SkPaint cannot have a mask filter or
311     * image filter, and any shader is ignored.
312     */
313    virtual void drawDevice(SkBaseDevice*, const SkSamplingOptions&, const SkPaint&);
314
315    /**
316     * Draw the special image's subset to this device, subject to the given matrix transform instead
317     * of the device's current local to device matrix.
318     */
319    virtual void drawSpecial(SkSpecialImage*, const SkMatrix& localToDevice,
320                             const SkSamplingOptions&, const SkPaint&);
321
322    /**
323     * Evaluate 'filter' and draw the final output into this device using 'paint'. The 'mapping'
324     * defines the parameter-to-layer space transform used to evaluate the image filter on 'src',
325     * and the layer-to-device space transform that is used to draw the result into this device.
326     * Since 'mapping' fully specifies the transform, this draw function ignores the current
327     * local-to-device matrix (i.e. just like drawSpecial and drawDevice).
328     *
329     * The final paint must not have an image filter or mask filter set on it; a shader is ignored.
330     */
331    virtual void drawFilteredImage(const skif::Mapping& mapping, SkSpecialImage* src,
332                                   const SkImageFilter*, const SkSamplingOptions&, const SkPaint&);
333
334    virtual bool drawBlurImage(const SkImage* image, const SkBlurArg& blurArg) = 0;
335
336    virtual sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&);
337    virtual sk_sp<SkSpecialImage> makeSpecial(const SkImage*);
338
339    // Get a view of the entire device's current contents as an image.
340    sk_sp<SkSpecialImage> snapSpecial();
341    // Snap the 'subset' contents from this device, possibly as a read-only view. If 'forceCopy'
342    // is true then the returned image's pixels must not be affected by subsequent draws into the
343    // device. When 'forceCopy' is false, the image can be a view into the device's pixels
344    // (avoiding a copy for performance, at the expense of safety). Default returns null.
345    virtual sk_sp<SkSpecialImage> snapSpecial(const SkIRect& subset, bool forceCopy = false);
346
347    virtual void setImmutable() {}
348
349    bool readPixels(const SkPixmap&, int x, int y);
350
351    virtual sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&);
352    virtual bool onPeekPixels(SkPixmap*) { return false; }
353
354    /**
355     *  The caller is responsible for "pre-clipping" the dst. The impl can assume that the dst
356     *  image at the specified x,y offset will fit within the device's bounds.
357     *
358     *  This is explicitly asserted in readPixels(), the public way to call this.
359     */
360    virtual bool onReadPixels(const SkPixmap&, int x, int y);
361
362    /**
363     *  The caller is responsible for "pre-clipping" the src. The impl can assume that the src
364     *  image at the specified x,y offset will fit within the device's bounds.
365     *
366     *  This is explicitly asserted in writePixelsDirect(), the public way to call this.
367     */
368    virtual bool onWritePixels(const SkPixmap&, int x, int y);
369
370    virtual bool onAccessPixels(SkPixmap*) { return false; }
371
372    struct CreateInfo {
373        CreateInfo(const SkImageInfo& info,
374                   SkPixelGeometry geo,
375                   TileUsage tileUsage,
376                   bool trackCoverage,
377                   SkRasterHandleAllocator* allocator)
378            : fInfo(info)
379            , fTileUsage(tileUsage)
380            , fPixelGeometry(geo)
381            , fTrackCoverage(trackCoverage)
382            , fAllocator(allocator)
383        {}
384
385        const SkImageInfo       fInfo;
386        const TileUsage         fTileUsage;
387        const SkPixelGeometry   fPixelGeometry;
388        const bool              fTrackCoverage = false;
389        SkRasterHandleAllocator* fAllocator = nullptr;
390    };
391
392    /**
393     *  Create a new device based on CreateInfo. If the paint is not null, then it represents a
394     *  preview of how the new device will be composed with its creator device (this).
395     *
396     *  The subclass may be handed this device in drawDevice(), so it must always return
397     *  a device that it knows how to draw, and that it knows how to identify if it is not of the
398     *  same subclass (since drawDevice is passed a SkBaseDevice*). If the subclass cannot fulfill
399     *  that contract (e.g. PDF cannot support some settings on the paint) it should return NULL,
400     *  and the caller may then decide to explicitly create a bitmapdevice, knowing that later
401     *  it could not call drawDevice with it (but it could call drawSprite or drawBitmap).
402     */
403    virtual SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) {
404        return nullptr;
405    }
406
407    // SkCanvas uses NoPixelsDevice when onCreateDevice fails; but then it needs to be able to
408    // inspect a layer's device to know if calling drawDevice() later is allowed.
409    virtual bool isNoPixelsDevice() const { return false; }
410
411private:
412    friend class SkAndroidFrameworkUtils;
413    friend class SkCanvas;
414    friend class SkDraw;
415    friend class SkSurface_Raster;
416    friend class DeviceTestingAccess;
417
418    void simplifyGlyphRunRSXFormAndRedraw(const SkGlyphRunList& glyphRunList, const SkPaint& paint);
419
420    // used to change the backend's pixels (and possibly config/rowbytes)
421    // but cannot change the width/height, so there should be no change to
422    // any clip information.
423    // TODO: move to SkBitmapDevice
424    virtual void replaceBitmapBackendForRasterSurface(const SkBitmap&) {}
425
426    virtual bool forceConservativeRasterClip() const { return false; }
427
428    // Configure the device's coordinate spaces, specifying both how its device image maps back to
429    // the global space (via 'deviceToGlobal') and the initial CTM of the device (via
430    // 'localToDevice', i.e. what geometry drawn into this device will be transformed with).
431    //
432    // (bufferOriginX, bufferOriginY) defines where the (0,0) pixel the device's backing buffer
433    // is anchored in the device space. The final device-to-global matrix stored by the SkDevice
434    // will include a pre-translation by T(deviceOriginX, deviceOriginY), and the final
435    // local-to-device matrix will have a post-translation of T(-deviceOriginX, -deviceOriginY).
436    //
437    // Returns false if the final device coordinate space is invalid, in which case the canvas
438    // should discard the device
439    bool SK_WARN_UNUSED_RESULT setDeviceCoordinateSystem(const SkM44& deviceToGlobal,
440                                                         const SkM44& localToDevice,
441                                                         int bufferOriginX, int bufferOriginY);
442    // Convenience to configure the device to be axis-aligned with the root canvas, but with a
443    // unique origin.
444    void setOrigin(const SkM44& globalCTM, int x, int y) {
445        SkAssertResult(this->setDeviceCoordinateSystem(SkM44(), globalCTM, x, y));
446    }
447
448    virtual SkImageFilterCache* getImageFilterCache() { return nullptr; }
449
450    friend class SkNoPixelsDevice;
451    friend class SkBitmapDevice;
452    void privateResize(int w, int h) {
453        *const_cast<SkImageInfo*>(&fInfo) = fInfo.makeWH(w, h);
454    }
455
456    SkMarkerStack* fMarkerStack = nullptr;  // does not own this, set in setMarkerStack()
457
458    const SkImageInfo    fInfo;
459    const SkSurfaceProps fSurfaceProps;
460    // fDeviceToGlobal and fGlobalToDevice are inverses of each other; there are never that many
461    // SkDevices, so pay the memory cost to avoid recalculating the inverse.
462    SkM44 fDeviceToGlobal;
463    SkM44 fGlobalToDevice;
464
465    // fLocalToDevice (inherited from SkMatrixProvider) is the device CTM, not the global CTM
466    // It maps from local space to the device's coordinate space.
467    // fDeviceToGlobal * fLocalToDevice will match the canvas' CTM.
468
469    using INHERITED = SkRefCnt;
470};
471
472class SkNoPixelsDevice : public SkBaseDevice {
473public:
474    SkNoPixelsDevice(const SkIRect& bounds, const SkSurfaceProps& props,
475                     sk_sp<SkColorSpace> colorSpace = nullptr)
476            : SkBaseDevice(SkImageInfo::Make(bounds.size(), kUnknown_SkColorType,
477                                             kUnknown_SkAlphaType, std::move(colorSpace)),
478                           props) {
479        // this fails if we enable this assert: DiscardableImageMapTest.GetDiscardableImagesInRectMaxImage
480        //SkASSERT(bounds.width() >= 0 && bounds.height() >= 0);
481
482        this->setOrigin(SkM44(), bounds.left(), bounds.top());
483        this->resetClipStack();
484    }
485
486    void resetForNextPicture(const SkIRect& bounds) {
487        //SkASSERT(bounds.width() >= 0 && bounds.height() >= 0);
488        this->privateResize(bounds.width(), bounds.height());
489        this->setOrigin(SkM44(), bounds.left(), bounds.top());
490        this->resetClipStack();
491    }
492
493protected:
494    // SkNoPixelsDevice tracks the clip conservatively in order to respond to some queries as
495    // accurately as possible while emphasizing performance
496    void onSave() override;
497    void onRestore() override;
498    void onClipRect(const SkRect& rect, SkClipOp op, bool aa) override;
499    void onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) override;
500    void onClipPath(const SkPath& path, SkClipOp op, bool aa) override;
501    void onClipRegion(const SkRegion& globalRgn, SkClipOp op) override;
502    void onClipShader(sk_sp<SkShader> shader) override;
503    void onReplaceClip(const SkIRect& rect) override;
504    bool onClipIsAA() const override { return this->clip().fIsAA; }
505    bool onClipIsWideOpen() const override {
506        return this->clip().fIsRect &&
507               this->onDevClipBounds() == this->bounds();
508    }
509    void onAsRgnClip(SkRegion* rgn) const override {
510        rgn->setRect(this->onDevClipBounds());
511    }
512    ClipType onGetClipType() const override;
513    SkIRect onDevClipBounds() const override { return this->clip().fClipBounds; }
514
515    void drawPaint(const SkPaint& paint) override {}
516    void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&) override {}
517    void drawImageRect(const SkImage*, const SkRect*, const SkRect&,
518                       const SkSamplingOptions&, const SkPaint&,
519                       SkCanvas::SrcRectConstraint) override {}
520    void drawRect(const SkRect&, const SkPaint&) override {}
521    void drawOval(const SkRect&, const SkPaint&) override {}
522    void drawRRect(const SkRRect&, const SkPaint&) override {}
523    void drawPath(const SkPath&, const SkPaint&, bool) override {}
524    void drawDevice(SkBaseDevice*, const SkSamplingOptions&, const SkPaint&) override {}
525    void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override {}
526
527    void drawFilteredImage(const skif::Mapping&, SkSpecialImage* src, const SkImageFilter*,
528                           const SkSamplingOptions&, const SkPaint&) override {}
529
530    bool drawBlurImage(const SkImage* image, const SkBlurArg& blurArg) override { return false; }
531
532    void onDrawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint) override {}
533
534
535    bool isNoPixelsDevice() const override { return true; }
536
537private:
538    struct ClipState {
539        SkIRect fClipBounds;
540        int fDeferredSaveCount;
541        bool fIsAA;
542        bool fIsRect;
543
544        ClipState(const SkIRect& bounds, bool isAA, bool isRect)
545                : fClipBounds(bounds)
546                , fDeferredSaveCount(0)
547                , fIsAA(isAA)
548                , fIsRect(isRect) {}
549
550        void op(SkClipOp op, const SkM44& transform, const SkRect& bounds,
551                bool isAA, bool fillsBounds);
552    };
553
554    const ClipState& clip() const { return fClipStack.back(); }
555    ClipState& writableClip();
556
557    void resetClipStack() {
558        fClipStack.reset();
559        fClipStack.emplace_back(this->bounds(), /*isAA=*/false, /*isRect=*/true);
560    }
561
562    SkSTArray<4, ClipState> fClipStack;
563
564    using INHERITED = SkBaseDevice;
565};
566
567class SkAutoDeviceTransformRestore : SkNoncopyable {
568public:
569    SkAutoDeviceTransformRestore(SkBaseDevice* device, const SkMatrix& localToDevice)
570        : fDevice(device)
571        , fPrevLocalToDevice(device->localToDevice())
572    {
573        fDevice->setLocalToDevice(SkM44(localToDevice));
574    }
575    ~SkAutoDeviceTransformRestore() {
576        fDevice->setLocalToDevice(fPrevLocalToDevice);
577    }
578
579private:
580    SkBaseDevice* fDevice;
581    const SkM44   fPrevLocalToDevice;
582};
583
584#endif
585