1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef nsImageRenderer_h__
8 #define nsImageRenderer_h__
9 
10 #include "nsStyleStruct.h"
11 #include "Units.h"
12 #include "mozilla/AspectRatio.h"
13 #include "mozilla/SurfaceFromElementResult.h"
14 
15 class gfxDrawable;
16 class nsDisplayItem;
17 namespace mozilla {
18 
19 namespace layers {
20 class StackingContextHelper;
21 class WebRenderParentCommand;
22 class RenderRootStateManager;
23 }  // namespace layers
24 
25 namespace wr {
26 class DisplayListBuilder;
27 class IpcResourceUpdateQueue;
28 }  // namespace wr
29 
30 // A CSSSizeOrRatio represents a (possibly partially specified) size for use
31 // in computing image sizes. Either or both of the width and height might be
32 // given. A ratio of width to height may also be given. If we at least two
33 // of these then we can compute a concrete size, that is a width and height.
34 struct CSSSizeOrRatio {
CSSSizeOrRatioCSSSizeOrRatio35   CSSSizeOrRatio()
36       : mWidth(0), mHeight(0), mHasWidth(false), mHasHeight(false) {}
37 
CanComputeConcreteSizeCSSSizeOrRatio38   bool CanComputeConcreteSize() const {
39     return mHasWidth + mHasHeight + HasRatio() >= 2;
40   }
IsConcreteCSSSizeOrRatio41   bool IsConcrete() const { return mHasWidth && mHasHeight; }
HasRatioCSSSizeOrRatio42   bool HasRatio() const { return !!mRatio; }
IsEmptyCSSSizeOrRatio43   bool IsEmpty() const {
44     return (mHasWidth && mWidth <= 0) || (mHasHeight && mHeight <= 0) ||
45            !mRatio;
46   }
47 
48   // CanComputeConcreteSize must return true when ComputeConcreteSize is
49   // called.
50   nsSize ComputeConcreteSize() const;
51 
SetWidthCSSSizeOrRatio52   void SetWidth(nscoord aWidth) {
53     mWidth = aWidth;
54     mHasWidth = true;
55     if (mHasHeight) {
56       mRatio = AspectRatio::FromSize(mWidth, mHeight);
57     }
58   }
SetHeightCSSSizeOrRatio59   void SetHeight(nscoord aHeight) {
60     mHeight = aHeight;
61     mHasHeight = true;
62     if (mHasWidth) {
63       mRatio = AspectRatio::FromSize(mWidth, mHeight);
64     }
65   }
SetSizeCSSSizeOrRatio66   void SetSize(const nsSize& aSize) {
67     mWidth = aSize.width;
68     mHeight = aSize.height;
69     mHasWidth = true;
70     mHasHeight = true;
71     mRatio = AspectRatio::FromSize(mWidth, mHeight);
72   }
SetRatioCSSSizeOrRatio73   void SetRatio(const AspectRatio& aRatio) {
74     MOZ_ASSERT(
75         !mHasWidth || !mHasHeight,
76         "Probably shouldn't be setting a ratio if we have a concrete size");
77     mRatio = aRatio;
78   }
79 
80   AspectRatio mRatio;
81   nscoord mWidth;
82   nscoord mHeight;
83   bool mHasWidth;
84   bool mHasHeight;
85 };
86 
87 /**
88  * This is a small wrapper class to encapsulate image drawing that can draw an
89  * StyleImage image, which may internally be a real image, a sub image, or a CSS
90  * gradient, etc...
91  *
92  * @note Always call the member functions in the order of PrepareImage(),
93  * SetSize(), and Draw*().
94  */
95 class nsImageRenderer {
96  public:
97   typedef mozilla::image::ImgDrawResult ImgDrawResult;
98   typedef mozilla::layers::LayerManager LayerManager;
99   typedef mozilla::layers::ImageContainer ImageContainer;
100 
101   enum {
102     FLAG_SYNC_DECODE_IMAGES = 0x01,
103     FLAG_PAINTING_TO_WINDOW = 0x02,
104     FLAG_HIGH_QUALITY_SCALING = 0x04
105   };
106   enum FitType { CONTAIN, COVER };
107 
108   nsImageRenderer(nsIFrame* aForFrame, const mozilla::StyleImage* aImage,
109                   uint32_t aFlags);
110   ~nsImageRenderer() = default;
111   /**
112    * Populates member variables to get ready for rendering.
113    * @return true iff the image is ready, and there is at least a pixel to
114    * draw.
115    */
116   bool PrepareImage();
117 
118   /**
119    * The three Compute*Size functions correspond to the sizing algorthms and
120    * definitions from the CSS Image Values and Replaced Content spec. See
121    * http://dev.w3.org/csswg/css-images-3/#sizing .
122    */
123 
124   /**
125    * Compute the intrinsic size of the image as defined in the CSS Image Values
126    * spec. The intrinsic size is the unscaled size which the image would ideally
127    * like to be in app units.
128    */
129   mozilla::CSSSizeOrRatio ComputeIntrinsicSize();
130 
131   /**
132    * Computes the placement for a background image, or for the image data
133    * inside of a replaced element.
134    *
135    * @param aPos The CSS <position> value that specifies the image's position.
136    * @param aOriginBounds The box to which the tiling position should be
137    *          relative. For background images, this should correspond to
138    *          'background-origin' for the frame, except when painting on the
139    *          canvas, in which case the origin bounds should be the bounds
140    *          of the root element's frame. For a replaced element, this should
141    *          be the element's content-box.
142    * @param aTopLeft [out] The top-left corner where an image tile should be
143    *          drawn.
144    * @param aAnchorPoint [out] A point which should be pixel-aligned by
145    *          nsLayoutUtils::DrawImage. This is the same as aTopLeft, unless
146    *          CSS specifies a percentage (including 'right' or 'bottom'), in
147    *          which case it's that percentage within of aOriginBounds. So
148    *          'right' would set aAnchorPoint.x to aOriginBounds.XMost().
149    *
150    * Points are returned relative to aOriginBounds.
151    */
152   static void ComputeObjectAnchorPoint(const mozilla::Position& aPos,
153                                        const nsSize& aOriginBounds,
154                                        const nsSize& aImageSize,
155                                        nsPoint* aTopLeft,
156                                        nsPoint* aAnchorPoint);
157 
158   /**
159    * Compute the size of the rendered image using either the 'cover' or
160    * 'contain' constraints (aFitType).
161    */
162   static nsSize ComputeConstrainedSize(
163       const nsSize& aConstrainingSize,
164       const mozilla::AspectRatio& aIntrinsicRatio, FitType aFitType);
165   /**
166    * Compute the size of the rendered image (the concrete size) where no cover/
167    * contain constraints are given. The 'default algorithm' from the CSS Image
168    * Values spec.
169    */
170   static nsSize ComputeConcreteSize(
171       const mozilla::CSSSizeOrRatio& aSpecifiedSize,
172       const mozilla::CSSSizeOrRatio& aIntrinsicSize,
173       const nsSize& aDefaultSize);
174 
175   /**
176    * Set this image's preferred size. This will be its intrinsic size where
177    * specified and the default size where it is not. Used as the unscaled size
178    * when rendering the image.
179    */
180   void SetPreferredSize(const mozilla::CSSSizeOrRatio& aIntrinsicSize,
181                         const nsSize& aDefaultSize);
182 
183   /**
184    * Draws the image to the target rendering context using
185    * {background|mask}-specific arguments.
186    * @see nsLayoutUtils::DrawImage() for parameters.
187    */
188   ImgDrawResult DrawLayer(nsPresContext* aPresContext,
189                           gfxContext& aRenderingContext, const nsRect& aDest,
190                           const nsRect& aFill, const nsPoint& aAnchor,
191                           const nsRect& aDirty, const nsSize& aRepeatSize,
192                           float aOpacity);
193 
194   /**
195    * Builds WebRender DisplayItems for an image using
196    * {background|mask}-specific arguments.
197    * @see nsLayoutUtils::DrawImage() for parameters.
198    */
199   ImgDrawResult BuildWebRenderDisplayItemsForLayer(
200       nsPresContext* aPresContext, mozilla::wr::DisplayListBuilder& aBuilder,
201       mozilla::wr::IpcResourceUpdateQueue& aResource,
202       const mozilla::layers::StackingContextHelper& aSc,
203       mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
204       const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor,
205       const nsRect& aDirty, const nsSize& aRepeatSize, float aOpacity);
206 
207   /**
208    * Draw the image to a single component of a border-image style rendering.
209    * aFill The destination rect to be drawn into
210    * aSrc is the part of the image to be rendered into a tile (aUnitSize in
211    * aFill), if aSrc and the dest tile are different sizes, the image will be
212    * scaled to map aSrc onto the dest tile.
213    * aHFill and aVFill are the repeat patterns for the component -
214    * NS_STYLE_BORDER_IMAGE_REPEAT_*
215    * aUnitSize The scaled size of a single source rect (in destination coords)
216    * aIndex identifies the component: 0 1 2
217    *                                  3 4 5
218    *                                  6 7 8
219    * aSVGViewportSize The image size evaluated by default sizing algorithm.
220    * Pass Nothing() if we can read a valid viewport size or aspect-ratio from
221    * the drawing image directly, otherwise, pass Some() with viewport size
222    * evaluated from default sizing algorithm.
223    * aHasIntrinsicRatio is used to record if the source image has fixed
224    * intrinsic ratio.
225    */
226   ImgDrawResult DrawBorderImageComponent(
227       nsPresContext* aPresContext, gfxContext& aRenderingContext,
228       const nsRect& aDirtyRect, const nsRect& aFill,
229       const mozilla::CSSIntRect& aSrc, mozilla::StyleBorderImageRepeat aHFill,
230       mozilla::StyleBorderImageRepeat aVFill, const nsSize& aUnitSize,
231       uint8_t aIndex, const mozilla::Maybe<nsSize>& aSVGViewportSize,
232       const bool aHasIntrinsicRatio);
233 
234   /**
235    * Draw the image to aRenderingContext which can be used to define the
236    * float area in the presence of "shape-outside: <image>".
237    */
238   ImgDrawResult DrawShapeImage(nsPresContext* aPresContext,
239                                gfxContext& aRenderingContext);
240 
241   bool IsRasterImage();
242   bool IsAnimatedImage();
243 
244   /// Retrieves the image associated with this nsImageRenderer, if there is one.
245   already_AddRefed<imgIContainer> GetImage();
246 
247   bool IsImageContainerAvailable(layers::LayerManager* aManager,
248                                  uint32_t aFlags);
IsReady()249   bool IsReady() const { return mPrepareResult == ImgDrawResult::SUCCESS; }
PrepareResult()250   ImgDrawResult PrepareResult() const { return mPrepareResult; }
SetExtendMode(mozilla::gfx::ExtendMode aMode)251   void SetExtendMode(mozilla::gfx::ExtendMode aMode) { mExtendMode = aMode; }
SetMaskOp(mozilla::StyleMaskMode aMaskOp)252   void SetMaskOp(mozilla::StyleMaskMode aMaskOp) { mMaskOp = aMaskOp; }
253   void PurgeCacheForViewportChange(
254       const mozilla::Maybe<nsSize>& aSVGViewportSize, const bool aHasRatio);
GetSize()255   const nsSize& GetSize() const { return mSize; }
GetType()256   mozilla::StyleImage::Tag GetType() const { return mType; }
GetGradientData()257   const mozilla::StyleGradient* GetGradientData() const {
258     return mGradientData;
259   }
260 
261  private:
262   /**
263    * Draws the image to the target rendering context.
264    * aSrc is a rect on the source image which will be mapped to aDest; it's
265    * currently only used for gradients.
266    *
267    * @see nsLayoutUtils::DrawImage() for other parameters.
268    */
269   ImgDrawResult Draw(nsPresContext* aPresContext, gfxContext& aRenderingContext,
270                      const nsRect& aDirtyRect, const nsRect& aDest,
271                      const nsRect& aFill, const nsPoint& aAnchor,
272                      const nsSize& aRepeatSize, const mozilla::CSSIntRect& aSrc,
273                      float aOpacity = 1.0);
274 
275   /**
276    * Builds WebRender DisplayItems for the image.
277    * aSrc is a rect on the source image which will be mapped to aDest; it's
278    * currently only used for gradients.
279    *
280    * @see nsLayoutUtils::DrawImage() for other parameters.
281    */
282   ImgDrawResult BuildWebRenderDisplayItems(
283       nsPresContext* aPresContext, mozilla::wr::DisplayListBuilder& aBuilder,
284       mozilla::wr::IpcResourceUpdateQueue& aResources,
285       const mozilla::layers::StackingContextHelper& aSc,
286       mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
287       const nsRect& aDirtyRect, const nsRect& aDest, const nsRect& aFill,
288       const nsPoint& aAnchor, const nsSize& aRepeatSize,
289       const mozilla::CSSIntRect& aSrc, float aOpacity = 1.0);
290 
291   /**
292    * Helper method for creating a gfxDrawable from mPaintServerFrame or
293    * mImageElementSurface.
294    * Requires mType to be Element.
295    * Returns null if we cannot create the drawable.
296    */
297   already_AddRefed<gfxDrawable> DrawableForElement(const nsRect& aImageRect,
298                                                    gfxContext& aContext);
299 
300   nsIFrame* mForFrame;
301   const mozilla::StyleImage* mImage;
302   ImageResolution mImageResolution;
303   mozilla::StyleImage::Tag mType;
304   nsCOMPtr<imgIContainer> mImageContainer;
305   const mozilla::StyleGradient* mGradientData;
306   nsIFrame* mPaintServerFrame;
307   SurfaceFromElementResult mImageElementSurface;
308   ImgDrawResult mPrepareResult;
309   nsSize mSize;  // unscaled size of the image, in app units
310   uint32_t mFlags;
311   mozilla::gfx::ExtendMode mExtendMode;
312   mozilla::StyleMaskMode mMaskOp;
313 };
314 
315 }  // namespace mozilla
316 
317 #endif /* nsImageRenderer_h__ */
318