1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294

cc / paint / record_paint_canvas.h [blame]

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CC_PAINT_RECORD_PAINT_CANVAS_H_
#define CC_PAINT_RECORD_PAINT_CANVAS_H_

#include <optional>

#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_op_buffer.h"
#include "cc/paint/paint_record.h"
#include "cc/paint/skottie_color_map.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/utils/SkNoDrawCanvas.h"

namespace cc {

class PaintFilter;

// This implementation of PaintCanvas records paint operations into the given
// PaintOpBuffer. The methods that inspect the current clip or CTM are not
// implemented (DCHECK will fail if called). Use InspectableRecordPaintCanvas
// instead if the client needs to call those methods.
class CC_PAINT_EXPORT RecordPaintCanvas : public PaintCanvas {
 public:
  RecordPaintCanvas();
  ~RecordPaintCanvas() override;

  RecordPaintCanvas(const RecordPaintCanvas&) = delete;
  RecordPaintCanvas& operator=(const RecordPaintCanvas&) = delete;

  // Returns the set of paint ops recorded so far and clears it from the
  // internal buffer maintained by the canvas.
  virtual PaintRecord ReleaseAsRecord();
  // Returns the set of paint ops recorded so far without clearing it from the
  // internal buffer.
  virtual PaintRecord CopyAsRecord();

  // See comments around `maybe_draw_lines_as_paths_` for details.
  void DisableLineDrawingAsPaths();

  bool HasRecordedDrawOps() const { return buffer_.has_draw_ops(); }
  size_t TotalOpCount() const { return buffer_.total_op_count(); }
  size_t OpBytesUsed() const { return buffer_.paint_ops_size(); }

  void* accessTopLayerPixels(SkImageInfo* info,
                             size_t* rowBytes,
                             SkIPoint* origin = nullptr) override;

  void flush() override;
  bool NeedsFlush() const override;

  int save() override;
  int saveLayer(const PaintFlags& flags) override;
  int saveLayer(const SkRect& bounds, const PaintFlags& flags) override;
  int saveLayerAlphaf(float alpha) override;
  int saveLayerAlphaf(const SkRect& bounds, float alpha) override;
  int saveLayerFilters(base::span<const sk_sp<PaintFilter>> filters,
                       const PaintFlags& flags) override;
  void restore() override;
  int getSaveCount() const final;
  void restoreToCount(int save_count) override;

  void translate(SkScalar dx, SkScalar dy) override;
  void scale(SkScalar sx, SkScalar sy) override;
  void rotate(SkScalar degrees) override;
  void concat(const SkM44& matrix) override;
  void setMatrix(const SkM44& matrix) override;

  void clipRect(const SkRect& rect, SkClipOp op, bool antialias) override;
  void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) final;
  void clipPath(const SkPath& path,
                SkClipOp op,
                bool antialias,
                UsePaintCache use_paint_cache) final;

  // These state-query functions can be called only if `size` is not empty in
  // the constructor. With this restriction, we don't need to create
  // SkNoDrawCanvas for clients that only need recording.
  SkImageInfo imageInfo() const override;
  bool getLocalClipBounds(SkRect* bounds) const override;
  bool getDeviceClipBounds(SkIRect* bounds) const override;
  SkM44 getLocalToDevice() const override;

  void drawColor(SkColor4f color, SkBlendMode mode) override;
  void clear(SkColor4f color) override;
  void drawLine(SkScalar x0,
                SkScalar y0,
                SkScalar x1,
                SkScalar y1,
                const PaintFlags& flags) override;
  void drawArc(const SkRect& oval,
               SkScalar start_angle_degrees,
               SkScalar sweep_angle_degrees,
               const PaintFlags& flags) override;
  void drawRect(const SkRect& rect, const PaintFlags& flags) override;
  void drawIRect(const SkIRect& rect, const PaintFlags& flags) override;
  void drawOval(const SkRect& oval, const PaintFlags& flags) override;
  void drawRRect(const SkRRect& rrect, const PaintFlags& flags) override;
  void drawDRRect(const SkRRect& outer,
                  const SkRRect& inner,
                  const PaintFlags& flags) override;
  void drawRoundRect(const SkRect& rect,
                     SkScalar rx,
                     SkScalar ry,
                     const PaintFlags& flags) override;
  void drawPath(const SkPath& path,
                const PaintFlags& flags,
                UsePaintCache use_paint_cache) override;
  void drawImage(const PaintImage& image,
                 SkScalar left,
                 SkScalar top,
                 const SkSamplingOptions&,
                 const PaintFlags* flags) override;
  void drawImageRect(const PaintImage& image,
                     const SkRect& src,
                     const SkRect& dst,
                     const SkSamplingOptions&,
                     const PaintFlags* flags,
                     SkCanvas::SrcRectConstraint constraint) override;
  void drawVertices(scoped_refptr<RefCountedBuffer<SkPoint>> vertices,
                    scoped_refptr<RefCountedBuffer<SkPoint>> uvs,
                    scoped_refptr<RefCountedBuffer<uint16_t>> indices,
                    const PaintFlags& flags) override;
  void drawSkottie(scoped_refptr<SkottieWrapper> skottie,
                   const SkRect& dst,
                   float t,
                   SkottieFrameDataMap images,
                   const SkottieColorMap& color_map,
                   SkottieTextPropertyValueMap text_map) override;
  void drawTextBlob(sk_sp<SkTextBlob> blob,
                    SkScalar x,
                    SkScalar y,
                    const PaintFlags& flags) override;
  void drawTextBlob(sk_sp<SkTextBlob> blob,
                    SkScalar x,
                    SkScalar y,
                    NodeId node_id,
                    const PaintFlags& flags) override;
  void drawPicture(PaintRecord record) override;
  void drawPicture(PaintRecord record, bool local_ctm) override;

  void Annotate(AnnotationType type,
                const SkRect& rect,
                sk_sp<SkData> data) override;
  void recordCustomData(uint32_t id) override;
  void setNodeId(int) override;

  // Don't shadow non-virtual helper functions.
  using PaintCanvas::clipPath;
  using PaintCanvas::clipRect;
  using PaintCanvas::clipRRect;
  using PaintCanvas::drawColor;
  using PaintCanvas::drawImage;
  using PaintCanvas::drawPath;
  using PaintCanvas::drawPicture;

#if DCHECK_IS_ON()
  void EnterDisableFlushCheckScope() { ++disable_flush_check_scope_; }
  void LeaveDisableFlushCheckScope() { DCHECK(disable_flush_check_scope_--); }
  bool IsInDisableFlushCheckScope() { return disable_flush_check_scope_; }
#endif

  class DisableFlushCheckScope {
    // Create an object of this type to temporarily allow draw commands to be
    // recorded while the recording is marked as needing to be flushed.  This is
    // meant to be used to allow client code to issue the commands necessary to
    // reach a state where the recording can be safely flushed before beginning
    // to enforce a check that forbids recording additional draw commands after
    // a flush was requested.
   public:
    explicit DisableFlushCheckScope(RecordPaintCanvas* canvas) {
#if DCHECK_IS_ON()
      // We require that NeedsFlush be false upon entering a top-level scope
      // to prevent consecutive scopes from evading evading flush checks
      // indefinitely.
      DCHECK(!canvas->NeedsFlush() || canvas->IsInDisableFlushCheckScope());
      canvas->EnterDisableFlushCheckScope();
      canvas_ = canvas;
#endif
    }
    ~DisableFlushCheckScope() {
#if DCHECK_IS_ON()
      canvas_->LeaveDisableFlushCheckScope();
#endif
    }

   private:
#if DCHECK_IS_ON()
    raw_ptr<RecordPaintCanvas> canvas_;
#endif
  };

 protected:
  virtual void clipRRectInternal(const SkRRect& rrect,
                                 SkClipOp op,
                                 bool antialias);
  virtual void clipPathInternal(const SkPath& path,
                                SkClipOp op,
                                bool antialias,
                                UsePaintCache use_paint_cache);

  bool IsDrawLinesAsPathsEnabled() const { return maybe_draw_lines_as_paths_; }

 private:
  template <typename T, typename... Args>
  void push(Args&&... args);

  PaintOpBuffer buffer_;
  int save_count_ = 1;

  bool needs_flush_ = false;
#if DCHECK_IS_ON()
  unsigned disable_flush_check_scope_ = 0;
#endif
  // These fields are used to determine if lines should be rastered as paths.
  // Rasterization may batch operations, and that batching may be disabled if
  // drawLine() is used instead of drawPath(). These members are used to
  // determine is a drawLine() should be rastered as a drawPath().
  // TODO(crbug.com/40045234): figure out better heurstics.
  bool maybe_draw_lines_as_paths_ = true;
  uint32_t draw_path_count_ = 0;
  uint32_t draw_line_count_ = 0;
};

// Besides the recording functions, this implementation of PaintCanvas allows
// inspection of the current clip and CTM during recording.
class CC_PAINT_EXPORT InspectableRecordPaintCanvas : public RecordPaintCanvas {
 public:
  explicit InspectableRecordPaintCanvas(const gfx::Size& size);
  ~InspectableRecordPaintCanvas() override;

  int save() override;
  int saveLayer(const PaintFlags& flags) override;
  int saveLayer(const SkRect& bounds, const PaintFlags& flags) override;
  int saveLayerAlphaf(float alpha) override;
  int saveLayerAlphaf(const SkRect& bounds, float alpha) override;
  int saveLayerFilters(base::span<const sk_sp<PaintFilter>> filters,
                       const PaintFlags& flags) override;
  void restore() override;

  void translate(SkScalar dx, SkScalar dy) override;
  void scale(SkScalar sx, SkScalar sy) override;
  void rotate(SkScalar degrees) override;
  void concat(const SkM44& matrix) override;
  void setMatrix(const SkM44& matrix) override;

  void clipRect(const SkRect& rect, SkClipOp op, bool antialias) override;

  SkImageInfo imageInfo() const override;
  bool getLocalClipBounds(SkRect* bounds) const override;
  bool getDeviceClipBounds(SkIRect* bounds) const override;
  SkM44 getLocalToDevice() const override;

  // Don't shadow non-virtual helper functions.
  using RecordPaintCanvas::clipRect;

 protected:
  // Creates a child canvas that has the same transform matrix and size as
  // `parent`. `CreateChildCanvasTag` is used to differentiate this from a copy
  // constructor.
  struct CreateChildCanvasTag {};
  InspectableRecordPaintCanvas(CreateChildCanvasTag,
                               const InspectableRecordPaintCanvas& parent);

 private:
  void clipRRectInternal(const SkRRect& rrect,
                         SkClipOp op,
                         bool antialias) override;
  void clipPathInternal(const SkPath& path,
                        SkClipOp op,
                        bool antialias,
                        UsePaintCache use_paint_cache) override;

  int CheckSaveCount(int super_prev_save_count, int canvas_prev_save_count);

  SkNoDrawCanvas canvas_;

  // Cached value of `canvas.getDeviceClipBounds()`. Cached as this value is
  // used in every fill/stroke operation and calculating is on the expensive
  // side.
  mutable std::optional<SkIRect> device_clip_bounds_;
};

}  // namespace cc

#endif  // CC_PAINT_RECORD_PAINT_CANVAS_H_