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

cc / paint / draw_looper.h [blame]

// Copyright 2024 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_DRAW_LOOPER_H_
#define CC_PAINT_DRAW_LOOPER_H_

#include <memory>
#include <vector>

#include "base/containers/adapters.h"
#include "base/stl_util.h"
#include "cc/paint/paint_export.h"
#include "third_party/skia/include/core/SkBlendMode.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkPoint.h"
#include "third_party/skia/include/core/SkRefCnt.h"

class SkPaint;

namespace cc {

// Optional collection of modifiers to paint/canvas to facilitate drawing
// a given primitive multiple times. Often this is used for shadows.
class CC_PAINT_EXPORT DrawLooper : public SkRefCnt {
 public:
  enum Flags {
    // If set, apply layer's offset after the canvas' transformation matrix.
    // If clear, pre-translate with layer's offset
    kPostTransformFlag = 1 << 0,

    // If set, set the paint's alpha to opaque
    kOverrideAlphaFlag = 1 << 1,

    // If set, ignore all modifiers but still translate using layer's offset
    kDontModifyPaintFlag = 1 << 2,
  };

  ~DrawLooper() override;

  //  The callback will be invoked for each "layer" in the looper, each time
  //  with a modified canvas and paint (depending on what the looper wanted
  //  to change). These can each be drawn directly, as the looper will callback
  //  logically bottom to top visually.
  template <typename DrawProc>
  void Apply(SkCanvas* canvas, const SkPaint& paint, DrawProc proc) const {
    // Our array is stored top to bottom
    //  e.g. layers_[0] is the top (will be drawn last)
    //       layers_[N-1] is on bottom, and will be drawn first
    //
    //  Hence, since we must draw in painter's order (bottom to top), we
    //  walk the array in reverse.
    //
    //  Each time through the loop, we make a copy of the draw's paint, modify
    //  it as indicated by the layer info, modify the canvas' translate, and
    //  then call back to issue the actual draw.
    for (const Layer& layer : base::Reversed(layers_)) {
      SkAutoCanvasRestore acr(canvas, true);
      SkPaint p(paint);
      layer.Apply(canvas, &p);
      proc(canvas, p);
    }
  }

  bool EqualsForTesting(const DrawLooper& other) const;

 private:
  // Keep this in sync with the fields in Flags
  // Used to mask out illegal bits when constructing Layer
  enum {
    kAllFlagsMask =
        kPostTransformFlag | kOverrideAlphaFlag | kDontModifyPaintFlag,
  };

  struct Layer {
    SkPoint offset;
    float blur_sigma;
    SkColor4f color;
    uint32_t flags;

    bool operator==(const Layer& other) const = default;

    void Apply(SkCanvas* canvas, SkPaint* paint) const;
  };
  std::vector<Layer> layers_;

  explicit DrawLooper(std::vector<Layer> l);

  void UpdateForLayer(const Layer& layer,
                      SkCanvas* canvas,
                      SkPaint* paint) const;

  friend class DrawLooperBuilder;
  friend class PaintOpReader;
  friend class PaintOpWriter;
};

class CC_PAINT_EXPORT DrawLooperBuilder {
 public:
  DrawLooperBuilder();
  ~DrawLooperBuilder();

  void AddUnmodifiedContent(bool add_on_top = false);
  void AddShadow(SkPoint offset,
                 float blur_sigma,
                 SkColor4f color,
                 uint32_t flags,
                 bool add_on_top = false);
  sk_sp<DrawLooper> Detach();

 private:
  std::vector<DrawLooper::Layer> layers_;
};

}  // namespace cc

#endif  // CC_PAINT_DRAW_LOOPER_H_