summaryrefslogtreecommitdiffstats
path: root/layout/painting/nsImageRenderer.h
blob: 6d5843a73f8c279e0212638832d9bf461c371829 (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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/* -*- 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 nsImageRenderer_h__
#define nsImageRenderer_h__

#include "nsStyleStruct.h"
#include "Units.h"
#include "mozilla/AspectRatio.h"
#include "mozilla/SurfaceFromElementResult.h"

class gfxDrawable;
class nsDisplayItem;
namespace mozilla {

namespace layers {
class StackingContextHelper;
class WebRenderParentCommand;
class RenderRootStateManager;
}  // namespace layers

namespace wr {
class DisplayListBuilder;
class IpcResourceUpdateQueue;
}  // namespace wr

// A CSSSizeOrRatio represents a (possibly partially specified) size for use
// in computing image sizes. Either or both of the width and height might be
// given. A ratio of width to height may also be given. If we at least two
// of these then we can compute a concrete size, that is a width and height.
struct CSSSizeOrRatio {
  CSSSizeOrRatio()
      : mWidth(0), mHeight(0), mHasWidth(false), mHasHeight(false) {}

  bool CanComputeConcreteSize() const {
    return mHasWidth + mHasHeight + HasRatio() >= 2;
  }
  bool IsConcrete() const { return mHasWidth && mHasHeight; }
  bool HasRatio() const { return !!mRatio; }
  bool IsEmpty() const {
    return (mHasWidth && mWidth <= 0) || (mHasHeight && mHeight <= 0) ||
           !mRatio;
  }

  // CanComputeConcreteSize must return true when ComputeConcreteSize is
  // called.
  nsSize ComputeConcreteSize() const;

  void SetWidth(nscoord aWidth) {
    mWidth = aWidth;
    mHasWidth = true;
    if (mHasHeight) {
      mRatio = AspectRatio::FromSize(mWidth, mHeight);
    }
  }
  void SetHeight(nscoord aHeight) {
    mHeight = aHeight;
    mHasHeight = true;
    if (mHasWidth) {
      mRatio = AspectRatio::FromSize(mWidth, mHeight);
    }
  }
  void SetSize(const nsSize& aSize) {
    mWidth = aSize.width;
    mHeight = aSize.height;
    mHasWidth = true;
    mHasHeight = true;
    mRatio = AspectRatio::FromSize(mWidth, mHeight);
  }
  void SetRatio(const AspectRatio& aRatio) {
    MOZ_ASSERT(
        !mHasWidth || !mHasHeight,
        "Probably shouldn't be setting a ratio if we have a concrete size");
    mRatio = aRatio;
  }

  AspectRatio mRatio;
  nscoord mWidth;
  nscoord mHeight;
  bool mHasWidth;
  bool mHasHeight;
};

/**
 * This is a small wrapper class to encapsulate image drawing that can draw an
 * StyleImage image, which may internally be a real image, a sub image, or a CSS
 * gradient, etc...
 *
 * @note Always call the member functions in the order of PrepareImage(),
 * SetSize(), and Draw*().
 */
class nsImageRenderer {
 public:
  typedef mozilla::image::ImgDrawResult ImgDrawResult;
  typedef mozilla::layers::LayerManager LayerManager;
  typedef mozilla::layers::ImageContainer ImageContainer;

  enum {
    FLAG_SYNC_DECODE_IMAGES = 0x01,
    FLAG_PAINTING_TO_WINDOW = 0x02,
    FLAG_HIGH_QUALITY_SCALING = 0x04
  };
  enum FitType { CONTAIN, COVER };

  nsImageRenderer(nsIFrame* aForFrame, const mozilla::StyleImage* aImage,
                  uint32_t aFlags);
  ~nsImageRenderer() = default;
  /**
   * Populates member variables to get ready for rendering.
   * @return true iff the image is ready, and there is at least a pixel to
   * draw.
   */
  bool PrepareImage();

  /**
   * The three Compute*Size functions correspond to the sizing algorthms and
   * definitions from the CSS Image Values and Replaced Content spec. See
   * http://dev.w3.org/csswg/css-images-3/#sizing .
   */

  /**
   * Compute the intrinsic size of the image as defined in the CSS Image Values
   * spec. The intrinsic size is the unscaled size which the image would ideally
   * like to be in app units.
   */
  mozilla::CSSSizeOrRatio ComputeIntrinsicSize();

  /**
   * Computes the placement for a background image, or for the image data
   * inside of a replaced element.
   *
   * @param aPos The CSS <position> value that specifies the image's position.
   * @param aOriginBounds The box to which the tiling position should be
   *          relative. For background images, this should correspond to
   *          'background-origin' for the frame, except when painting on the
   *          canvas, in which case the origin bounds should be the bounds
   *          of the root element's frame. For a replaced element, this should
   *          be the element's content-box.
   * @param aTopLeft [out] The top-left corner where an image tile should be
   *          drawn.
   * @param aAnchorPoint [out] A point which should be pixel-aligned by
   *          nsLayoutUtils::DrawImage. This is the same as aTopLeft, unless
   *          CSS specifies a percentage (including 'right' or 'bottom'), in
   *          which case it's that percentage within of aOriginBounds. So
   *          'right' would set aAnchorPoint.x to aOriginBounds.XMost().
   *
   * Points are returned relative to aOriginBounds.
   */
  static void ComputeObjectAnchorPoint(const mozilla::Position& aPos,
                                       const nsSize& aOriginBounds,
                                       const nsSize& aImageSize,
                                       nsPoint* aTopLeft,
                                       nsPoint* aAnchorPoint);

  /**
   * Compute the size of the rendered image using either the 'cover' or
   * 'contain' constraints (aFitType).
   */
  static nsSize ComputeConstrainedSize(
      const nsSize& aConstrainingSize,
      const mozilla::AspectRatio& aIntrinsicRatio, FitType aFitType);
  /**
   * Compute the size of the rendered image (the concrete size) where no cover/
   * contain constraints are given. The 'default algorithm' from the CSS Image
   * Values spec.
   */
  static nsSize ComputeConcreteSize(
      const mozilla::CSSSizeOrRatio& aSpecifiedSize,
      const mozilla::CSSSizeOrRatio& aIntrinsicSize,
      const nsSize& aDefaultSize);

  /**
   * Set this image's preferred size. This will be its intrinsic size where
   * specified and the default size where it is not. Used as the unscaled size
   * when rendering the image.
   */
  void SetPreferredSize(const mozilla::CSSSizeOrRatio& aIntrinsicSize,
                        const nsSize& aDefaultSize);

  /**
   * Draws the image to the target rendering context using
   * {background|mask}-specific arguments.
   * @see nsLayoutUtils::DrawImage() for parameters.
   */
  ImgDrawResult DrawLayer(nsPresContext* aPresContext,
                          gfxContext& aRenderingContext, const nsRect& aDest,
                          const nsRect& aFill, const nsPoint& aAnchor,
                          const nsRect& aDirty, const nsSize& aRepeatSize,
                          float aOpacity);

  /**
   * Builds WebRender DisplayItems for an image using
   * {background|mask}-specific arguments.
   * @see nsLayoutUtils::DrawImage() for parameters.
   */
  ImgDrawResult BuildWebRenderDisplayItemsForLayer(
      nsPresContext* aPresContext, mozilla::wr::DisplayListBuilder& aBuilder,
      mozilla::wr::IpcResourceUpdateQueue& aResource,
      const mozilla::layers::StackingContextHelper& aSc,
      mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
      const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor,
      const nsRect& aDirty, const nsSize& aRepeatSize, float aOpacity);

  /**
   * Draw the image to a single component of a border-image style rendering.
   * aFill The destination rect to be drawn into
   * aSrc is the part of the image to be rendered into a tile (aUnitSize in
   * aFill), if aSrc and the dest tile are different sizes, the image will be
   * scaled to map aSrc onto the dest tile.
   * aHFill and aVFill are the repeat patterns for the component -
   * NS_STYLE_BORDER_IMAGE_REPEAT_*
   * aUnitSize The scaled size of a single source rect (in destination coords)
   * aIndex identifies the component: 0 1 2
   *                                  3 4 5
   *                                  6 7 8
   * aSVGViewportSize The image size evaluated by default sizing algorithm.
   * Pass Nothing() if we can read a valid viewport size or aspect-ratio from
   * the drawing image directly, otherwise, pass Some() with viewport size
   * evaluated from default sizing algorithm.
   * aHasIntrinsicRatio is used to record if the source image has fixed
   * intrinsic ratio.
   */
  ImgDrawResult DrawBorderImageComponent(
      nsPresContext* aPresContext, gfxContext& aRenderingContext,
      const nsRect& aDirtyRect, const nsRect& aFill,
      const mozilla::CSSIntRect& aSrc, mozilla::StyleBorderImageRepeat aHFill,
      mozilla::StyleBorderImageRepeat aVFill, const nsSize& aUnitSize,
      uint8_t aIndex, const mozilla::Maybe<nsSize>& aSVGViewportSize,
      const bool aHasIntrinsicRatio);

  /**
   * Draw the image to aRenderingContext which can be used to define the
   * float area in the presence of "shape-outside: <image>".
   */
  ImgDrawResult DrawShapeImage(nsPresContext* aPresContext,
                               gfxContext& aRenderingContext);

  bool IsRasterImage();
  bool IsAnimatedImage();

  /// Retrieves the image associated with this nsImageRenderer, if there is one.
  already_AddRefed<imgIContainer> GetImage();

  bool IsImageContainerAvailable(layers::LayerManager* aManager,
                                 uint32_t aFlags);
  bool IsReady() const { return mPrepareResult == ImgDrawResult::SUCCESS; }
  ImgDrawResult PrepareResult() const { return mPrepareResult; }
  void SetExtendMode(mozilla::gfx::ExtendMode aMode) { mExtendMode = aMode; }
  void SetMaskOp(mozilla::StyleMaskMode aMaskOp) { mMaskOp = aMaskOp; }
  void PurgeCacheForViewportChange(
      const mozilla::Maybe<nsSize>& aSVGViewportSize, const bool aHasRatio);
  const nsSize& GetSize() const { return mSize; }
  mozilla::StyleImage::Tag GetType() const { return mType; }
  const mozilla::StyleGradient* GetGradientData() const {
    return mGradientData;
  }

 private:
  /**
   * Draws the image to the target rendering context.
   * aSrc is a rect on the source image which will be mapped to aDest; it's
   * currently only used for gradients.
   *
   * @see nsLayoutUtils::DrawImage() for other parameters.
   */
  ImgDrawResult Draw(nsPresContext* aPresContext, gfxContext& aRenderingContext,
                     const nsRect& aDirtyRect, const nsRect& aDest,
                     const nsRect& aFill, const nsPoint& aAnchor,
                     const nsSize& aRepeatSize, const mozilla::CSSIntRect& aSrc,
                     float aOpacity = 1.0);

  /**
   * Builds WebRender DisplayItems for the image.
   * aSrc is a rect on the source image which will be mapped to aDest; it's
   * currently only used for gradients.
   *
   * @see nsLayoutUtils::DrawImage() for other parameters.
   */
  ImgDrawResult BuildWebRenderDisplayItems(
      nsPresContext* aPresContext, mozilla::wr::DisplayListBuilder& aBuilder,
      mozilla::wr::IpcResourceUpdateQueue& aResources,
      const mozilla::layers::StackingContextHelper& aSc,
      mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
      const nsRect& aDirtyRect, const nsRect& aDest, const nsRect& aFill,
      const nsPoint& aAnchor, const nsSize& aRepeatSize,
      const mozilla::CSSIntRect& aSrc, float aOpacity = 1.0);

  /**
   * Helper method for creating a gfxDrawable from mPaintServerFrame or
   * mImageElementSurface.
   * Requires mType to be Element.
   * Returns null if we cannot create the drawable.
   */
  already_AddRefed<gfxDrawable> DrawableForElement(const nsRect& aImageRect,
                                                   gfxContext& aContext);

  nsIFrame* mForFrame;
  const mozilla::StyleImage* mImage;
  mozilla::StyleImage::Tag mType;
  nsCOMPtr<imgIContainer> mImageContainer;
  const mozilla::StyleGradient* mGradientData;
  nsIFrame* mPaintServerFrame;
  SurfaceFromElementResult mImageElementSurface;
  ImgDrawResult mPrepareResult;
  nsSize mSize;  // unscaled size of the image, in app units
  uint32_t mFlags;
  mozilla::gfx::ExtendMode mExtendMode;
  mozilla::StyleMaskMode mMaskOp;
};

}  // namespace mozilla

#endif /* nsImageRenderer_h__ */