summaryrefslogtreecommitdiffstats
path: root/layout/svg/SVGContextPaint.h
blob: 1264bf706fc9a25412ab5694e4f00923da89d878 (plain)
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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef LAYOUT_SVG_SVGCONTEXTPAINT_H_
#define LAYOUT_SVG_SVGCONTEXTPAINT_H_

#include "DrawMode.h"
#include "gfxMatrix.h"
#include "gfxPattern.h"
#include "gfxTypes.h"
#include "gfxUtils.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/gfx/2D.h"
#include "nsColor.h"
#include "nsStyleStruct.h"
#include "nsTArray.h"
#include "ImgDrawResult.h"
#include "nsRefPtrHashtable.h"

class gfxContext;

namespace mozilla {
class SVGPaintServerFrame;

namespace dom {
class Document;
}

/**
 * This class is used to pass information about a context element through to
 * SVG painting code in order to resolve the 'context-fill' and related
 * keywords. See:
 *
 *   https://www.w3.org/TR/SVG2/painting.html#context-paint
 *
 * This feature allows the color in an SVG-in-OpenType glyph to come from the
 * computed style for the text that is being drawn, for example, or for color
 * in an SVG embedded by an <img> element to come from the embedding <img>
 * element.
 *
 * This class is reference counted so that it can be shared among many similar
 * SVGImageContext objects. (SVGImageContext objects are frequently
 * copy-constructed with small modifications, and we'd like for those copies to
 * be able to share their context-paint data cheaply.)  However, in most cases,
 * SVGContextPaint instances are stored in a local RefPtr and only last for the
 * duration of a function call.
 * XXX Note: SVGImageContext doesn't actually have a SVGContextPaint member yet,
 * but it will in a later patch in the patch series that added this comment.
 */
class SVGContextPaint : public RefCounted<SVGContextPaint> {
 protected:
  using DrawTarget = mozilla::gfx::DrawTarget;
  using Float = mozilla::gfx::Float;
  using imgDrawingParams = mozilla::image::imgDrawingParams;

  SVGContextPaint() : mDashOffset(0.0f), mStrokeWidth(0.0f) {}

 public:
  MOZ_DECLARE_REFCOUNTED_TYPENAME(SVGContextPaint)

  virtual ~SVGContextPaint() = default;

  virtual already_AddRefed<gfxPattern> GetFillPattern(
      const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
      imgDrawingParams& aImgParams) = 0;
  virtual already_AddRefed<gfxPattern> GetStrokePattern(
      const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
      imgDrawingParams& aImgParams) = 0;
  virtual float GetFillOpacity() const = 0;
  virtual float GetStrokeOpacity() const = 0;

  already_AddRefed<gfxPattern> GetFillPattern(const DrawTarget* aDrawTarget,
                                              const gfxMatrix& aCTM,
                                              imgDrawingParams& aImgParams) {
    return GetFillPattern(aDrawTarget, GetFillOpacity(), aCTM, aImgParams);
  }

  already_AddRefed<gfxPattern> GetStrokePattern(const DrawTarget* aDrawTarget,
                                                const gfxMatrix& aCTM,
                                                imgDrawingParams& aImgParams) {
    return GetStrokePattern(aDrawTarget, GetStrokeOpacity(), aCTM, aImgParams);
  }

  static SVGContextPaint* GetContextPaint(nsIContent* aContent);

  // XXX This gets the geometry params from the gfxContext.  We should get that
  // information from the actual paint context!
  void InitStrokeGeometry(gfxContext* aContext, float devUnitsPerSVGUnit);

  const FallibleTArray<Float>& GetStrokeDashArray() const { return mDashes; }

  Float GetStrokeDashOffset() const { return mDashOffset; }

  Float GetStrokeWidth() const { return mStrokeWidth; }

  virtual uint32_t Hash() const {
    MOZ_ASSERT_UNREACHABLE(
        "Only VectorImage needs to hash, and that should "
        "only be operating on our SVGEmbeddingContextPaint "
        "subclass");
    return 0;
  }

  /**
   * Returns true if image context paint is allowed to be used in an image that
   * has the given URI, else returns false.
   */
  static bool IsAllowedForImageFromURI(nsIURI* aURI);

 private:
  // Member-vars are initialized in InitStrokeGeometry.
  FallibleTArray<Float> mDashes;
  MOZ_INIT_OUTSIDE_CTOR Float mDashOffset;
  MOZ_INIT_OUTSIDE_CTOR Float mStrokeWidth;
};

/**
 * RAII class used to temporarily set and remove an SVGContextPaint while a
 * piece of SVG is being painted.  The context paint is set on the SVG's owner
 * document, as expected by SVGContextPaint::GetContextPaint.  Any pre-existing
 * context paint is restored after this class removes the context paint that it
 * set.
 */
class MOZ_RAII AutoSetRestoreSVGContextPaint {
 public:
  AutoSetRestoreSVGContextPaint(const SVGContextPaint* aContextPaint,
                                dom::Document* aDocument);
  ~AutoSetRestoreSVGContextPaint();

 private:
  dom::Document* mDocument;
  // The context paint that needs to be restored by our dtor after it removes
  // aContextPaint:
  const SVGContextPaint* mOuterContextPaint;
};

/**
 * This class should be flattened into SVGContextPaint once we get rid of the
 * other sub-class (SimpleTextContextPaint).
 */
struct SVGContextPaintImpl : public SVGContextPaint {
 protected:
  using DrawTarget = mozilla::gfx::DrawTarget;

 public:
  DrawMode Init(const DrawTarget* aDrawTarget, const gfxMatrix& aContextMatrix,
                nsIFrame* aFrame, SVGContextPaint* aOuterContextPaint,
                imgDrawingParams& aImgParams);

  already_AddRefed<gfxPattern> GetFillPattern(
      const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
      imgDrawingParams& aImgParams) override;
  already_AddRefed<gfxPattern> GetStrokePattern(
      const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
      imgDrawingParams& aImgParams) override;

  void SetFillOpacity(float aOpacity) { mFillOpacity = aOpacity; }
  float GetFillOpacity() const override { return mFillOpacity; }

  void SetStrokeOpacity(float aOpacity) { mStrokeOpacity = aOpacity; }
  float GetStrokeOpacity() const override { return mStrokeOpacity; }

  struct Paint {
    enum class Tag : uint8_t {
      None,
      Color,
      PaintServer,
      ContextFill,
      ContextStroke,
    };

    Paint() : mPaintDefinition{}, mPaintType(Tag::None) {}

    void SetPaintServer(nsIFrame* aFrame, const gfxMatrix& aContextMatrix,
                        SVGPaintServerFrame* aPaintServerFrame) {
      mPaintType = Tag::PaintServer;
      mPaintDefinition.mPaintServerFrame = aPaintServerFrame;
      mFrame = aFrame;
      mContextMatrix = aContextMatrix;
    }

    void SetColor(const nscolor& aColor) {
      mPaintType = Tag::Color;
      mPaintDefinition.mColor = aColor;
    }

    void SetContextPaint(SVGContextPaint* aContextPaint, Tag aTag) {
      MOZ_ASSERT(aTag == Tag::ContextFill || aTag == Tag::ContextStroke);
      mPaintType = aTag;
      mPaintDefinition.mContextPaint = aContextPaint;
    }

    union {
      SVGPaintServerFrame* mPaintServerFrame;
      SVGContextPaint* mContextPaint;
      nscolor mColor;
    } mPaintDefinition;

    // Initialized (if needed) in SetPaintServer():
    MOZ_INIT_OUTSIDE_CTOR nsIFrame* mFrame;
    // CTM defining the user space for the pattern we will use.
    gfxMatrix mContextMatrix;
    Tag mPaintType;

    // Device-space-to-pattern-space
    gfxMatrix mPatternMatrix;
    nsRefPtrHashtable<nsFloatHashKey, gfxPattern> mPatternCache;

    already_AddRefed<gfxPattern> GetPattern(
        const DrawTarget* aDrawTarget, float aOpacity,
        StyleSVGPaint nsStyleSVG::*aFillOrStroke, const gfxMatrix& aCTM,
        imgDrawingParams& aImgParams);
  };

  Paint mFillPaint;
  Paint mStrokePaint;

  float mFillOpacity;
  float mStrokeOpacity;
};

/**
 * This class is used to pass context paint to an SVG image when an element
 * references that image (e.g. via HTML <img> or SVG <image>, or by referencing
 * it from a CSS property such as 'background-image').  In this case we only
 * support context colors and not paint servers.
 */
class SVGEmbeddingContextPaint : public SVGContextPaint {
  using DeviceColor = gfx::DeviceColor;

 public:
  SVGEmbeddingContextPaint() : mFillOpacity(1.0f), mStrokeOpacity(1.0f) {}

  bool operator==(const SVGEmbeddingContextPaint& aOther) const {
    MOZ_ASSERT(GetStrokeWidth() == aOther.GetStrokeWidth() &&
                   GetStrokeDashOffset() == aOther.GetStrokeDashOffset() &&
                   GetStrokeDashArray() == aOther.GetStrokeDashArray(),
               "We don't currently include these in the context information "
               "from an embedding element");
    return mFill == aOther.mFill && mStroke == aOther.mStroke &&
           mFillOpacity == aOther.mFillOpacity &&
           mStrokeOpacity == aOther.mStrokeOpacity;
  }

  void SetFill(nscolor aFill) { mFill.emplace(gfx::ToDeviceColor(aFill)); }
  const Maybe<DeviceColor>& GetFill() const { return mFill; }
  void SetStroke(nscolor aStroke) {
    mStroke.emplace(gfx::ToDeviceColor(aStroke));
  }
  const Maybe<DeviceColor>& GetStroke() const { return mStroke; }

  /**
   * Returns a pattern of type PatternType::COLOR, or else nullptr.
   */
  already_AddRefed<gfxPattern> GetFillPattern(
      const DrawTarget* aDrawTarget, float aFillOpacity, const gfxMatrix& aCTM,
      imgDrawingParams& aImgParams) override;

  /**
   * Returns a pattern of type PatternType::COLOR, or else nullptr.
   */
  already_AddRefed<gfxPattern> GetStrokePattern(
      const DrawTarget* aDrawTarget, float aStrokeOpacity,
      const gfxMatrix& aCTM, imgDrawingParams& aImgParams) override;

  void SetFillOpacity(float aOpacity) { mFillOpacity = aOpacity; }
  float GetFillOpacity() const override { return mFillOpacity; };

  void SetStrokeOpacity(float aOpacity) { mStrokeOpacity = aOpacity; }
  float GetStrokeOpacity() const override { return mStrokeOpacity; };

  uint32_t Hash() const override;

 private:
  Maybe<DeviceColor> mFill;
  Maybe<DeviceColor> mStroke;
  float mFillOpacity;
  float mStrokeOpacity;
};

}  // namespace mozilla

#endif  // LAYOUT_SVG_SVGCONTEXTPAINT_H_