1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
12 #include "ImageTypes.h"
13 #include "imgIContainer.h"
14 #include "mozilla/gfx/2D.h"
15 #include "mozilla/Maybe.h"
16 #include "mozilla/RefPtr.h"
17 #include "mozilla/UniquePtr.h"
19 #include "nsContentUtils.h"
20 #include "nsPrintfCString.h"
21 #include "nsRegionFwd.h"
22 #include "mozilla/gfx/Rect.h"
23 #include "mozilla/CheckedInt.h"
24 #include "mozilla/webrender/WebRenderTypes.h"
31 class nsICookieJarSettings
;
40 class WebRenderBridgeChild
;
42 struct PlanarYCbCrData
;
43 class WebRenderCommand
;
49 class DisplayListBuilder
;
51 } // namespace mozilla
53 enum class ImageType
{
62 typedef mozilla::gfx::DataSourceSurface DataSourceSurface
;
63 typedef mozilla::gfx::DrawTarget DrawTarget
;
64 typedef mozilla::gfx::IntPoint IntPoint
;
65 typedef mozilla::gfx::Matrix Matrix
;
66 typedef mozilla::gfx::Matrix4x4 Matrix4x4
;
67 typedef mozilla::gfx::SourceSurface SourceSurface
;
68 typedef mozilla::gfx::SurfaceFormat SurfaceFormat
;
69 typedef mozilla::image::ImageRegion ImageRegion
;
72 * Premultiply or Unpremultiply aSourceSurface, writing the result
73 * to aDestSurface or back into aSourceSurface if aDestSurface is null.
75 * If aDestSurface is given, it must have identical format, dimensions, and
76 * stride as the source.
78 * If the source is not SurfaceFormat::A8R8G8B8_UINT32, no operation is
79 * performed. If aDestSurface is given, the data is copied over.
81 static bool PremultiplyDataSurface(DataSourceSurface
* srcSurf
,
82 DataSourceSurface
* destSurf
);
83 static bool UnpremultiplyDataSurface(DataSourceSurface
* srcSurf
,
84 DataSourceSurface
* destSurf
);
86 static already_AddRefed
<DataSourceSurface
> CreatePremultipliedDataSurface(
87 DataSourceSurface
* srcSurf
);
88 static already_AddRefed
<DataSourceSurface
> CreateUnpremultipliedDataSurface(
89 DataSourceSurface
* srcSurf
);
91 static void ConvertBGRAtoRGBA(uint8_t* aData
, uint32_t aLength
);
94 * Draw something drawable while working around limitations like bad support
95 * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
96 * extreme user-space-to-image-space transforms.
98 * The input parameters here usually come from the output of our image
99 * snapping algorithm in nsLayoutUtils.cpp.
100 * This method is split from nsLayoutUtils::DrawPixelSnapped to allow for
101 * adjusting the parameters. For example, certain images with transparent
102 * margins only have a drawable subimage. For those images, imgFrame::Draw
103 * will tweak the rects and transforms that it gets from the pixel snapping
104 * algorithm before passing them on to this method.
106 static void DrawPixelSnapped(gfxContext
* aContext
, gfxDrawable
* aDrawable
,
107 const gfxSize
& aImageSize
,
108 const ImageRegion
& aRegion
,
109 const mozilla::gfx::SurfaceFormat aFormat
,
110 mozilla::gfx::SamplingFilter aSamplingFilter
,
111 uint32_t aImageFlags
= imgIContainer::FLAG_NONE
,
112 gfxFloat aOpacity
= 1.0,
113 bool aUseOptimalFillOp
= true);
116 * Clip aContext to the region aRegion.
118 static void ClipToRegion(gfxContext
* aContext
, const nsIntRegion
& aRegion
);
121 * Clip aTarget to the region aRegion.
123 static void ClipToRegion(mozilla::gfx::DrawTarget
* aTarget
,
124 const nsIntRegion
& aRegion
);
127 * Convert image format to depth value
129 static int ImageFormatToDepth(gfxImageFormat aFormat
);
132 * Return the transform matrix that maps aFrom to the rectangle defined by
133 * aToTopLeft/aToTopRight/aToBottomRight. aFrom must be
134 * nonempty and the destination rectangle must be axis-aligned.
136 static gfxMatrix
TransformRectToRect(const gfxRect
& aFrom
,
137 const gfxPoint
& aToTopLeft
,
138 const gfxPoint
& aToTopRight
,
139 const gfxPoint
& aToBottomRight
);
141 static Matrix
TransformRectToRect(const gfxRect
& aFrom
,
142 const IntPoint
& aToTopLeft
,
143 const IntPoint
& aToTopRight
,
144 const IntPoint
& aToBottomRight
);
147 * If aIn can be represented exactly using an gfx::IntRect (i.e.
148 * integer-aligned edges and coordinates in the int32_t range) then we
149 * set aOut to that rectangle, otherwise return failure.
151 static bool GfxRectToIntRect(const gfxRect
& aIn
, mozilla::gfx::IntRect
* aOut
);
153 /* Conditions this border to Cairo's max coordinate space.
154 * The caller can check IsEmpty() after Condition() -- if it's TRUE,
155 * the caller can possibly avoid doing any extra rendering.
157 static void ConditionRect(gfxRect
& aRect
);
160 * Transform this rectangle with aMatrix, resulting in a gfxQuad.
162 static gfxQuad
TransformToQuad(const gfxRect
& aRect
,
163 const mozilla::gfx::Matrix4x4
& aMatrix
);
166 * Return the smallest power of kScaleResolution (2) greater than or equal to
167 * aVal. If aRoundDown is specified, the power of 2 will rather be less than
170 static float ClampToScaleFactor(float aVal
, bool aRoundDown
= false);
173 * We can snap layer transforms for two reasons:
174 * 1) To avoid unnecessary resampling when a transform is a translation
175 * by a non-integer number of pixels.
176 * Snapping the translation to an integer number of pixels avoids
177 * blurring the layer and can be faster to composite.
178 * 2) When a layer is used to render a rectangular object, we need to
179 * emulate the rendering of rectangular inactive content and snap the
180 * edges of the rectangle to pixel boundaries. This is both to ensure
181 * layer rendering is consistent with inactive content rendering, and to
183 * This function implements type 1 snapping. If aTransform is a 2D
184 * translation, and this layer's layer manager has enabled snapping
185 * (which is the default), return aTransform with the translation snapped
186 * to nearest pixels. Otherwise just return aTransform. Call this when the
187 * layer does not correspond to a single rectangular content object.
188 * This function does not try to snap if aTransform has a scale, because in
189 * that case resampling is inevitable and there's no point in trying to
190 * avoid it. In fact snapping can cause problems because pixel edges in the
191 * layer's content can be rendered unpredictably (jiggling) as the scale
192 * interacts with the snapping of the translation, especially with animated
194 * @param aResidualTransform a transform to apply before the result transform
195 * in order to get the results to completely match aTransform.
197 static Matrix4x4
SnapTransformTranslation(const Matrix4x4
& aTransform
,
198 Matrix
* aResidualTransform
);
199 static Matrix
SnapTransformTranslation(const Matrix
& aTransform
,
200 Matrix
* aResidualTransform
);
201 static Matrix4x4
SnapTransformTranslation3D(const Matrix4x4
& aTransform
,
202 Matrix
* aResidualTransform
);
204 * See comment for SnapTransformTranslation.
205 * This function implements type 2 snapping. If aTransform is a translation
206 * and/or scale, transform aSnapRect by aTransform, snap to pixel boundaries,
207 * and return the transform that maps aSnapRect to that rect. Otherwise
208 * just return aTransform.
209 * @param aSnapRect a rectangle whose edges should be snapped to pixel
210 * boundaries in the destination surface.
211 * @param aResidualTransform a transform to apply before the result transform
212 * in order to get the results to completely match aTransform.
214 static Matrix4x4
SnapTransform(const Matrix4x4
& aTransform
,
215 const gfxRect
& aSnapRect
,
216 Matrix
* aResidualTransform
);
217 static Matrix
SnapTransform(const Matrix
& aTransform
,
218 const gfxRect
& aSnapRect
,
219 Matrix
* aResidualTransform
);
222 * Clears surface to aColor (which defaults to transparent black).
224 static void ClearThebesSurface(gfxASurface
* aSurface
);
226 static const float* YuvToRgbMatrix4x3RowMajor(
227 mozilla::gfx::YUVColorSpace aYUVColorSpace
);
228 static const float* YuvToRgbMatrix3x3ColumnMajor(
229 mozilla::gfx::YUVColorSpace aYUVColorSpace
);
230 static const float* YuvToRgbMatrix4x4ColumnMajor(
231 mozilla::gfx::YUVColorSpace aYUVColorSpace
);
233 static mozilla::Maybe
<mozilla::gfx::YUVColorSpace
> CicpToColorSpace(
234 const mozilla::gfx::CICP::MatrixCoefficients
,
235 const mozilla::gfx::CICP::ColourPrimaries
,
236 mozilla::LazyLogModule
& aLogger
);
238 static mozilla::Maybe
<mozilla::gfx::ColorSpace2
> CicpToColorPrimaries(
239 const mozilla::gfx::CICP::ColourPrimaries
,
240 mozilla::LazyLogModule
& aLogger
);
242 static mozilla::Maybe
<mozilla::gfx::TransferFunction
> CicpToTransferFunction(
243 const mozilla::gfx::CICP::TransferCharacteristics
);
246 * Creates a copy of aSurface, but having the SurfaceFormat aFormat.
248 * This function always creates a new surface. Do not call it if aSurface's
249 * format is the same as aFormat. Such a non-conversion would just be an
250 * unnecessary and wasteful copy (this function asserts to prevent that).
252 * This function is intended to be called by code that needs to access the
253 * pixel data of the surface, but doesn't want to have lots of branches
254 * to handle different pixel data formats (code which would become out of
255 * date if and when new formats are added). Callers can use this function
256 * to copy the surface to a specified format so that they only have to
257 * handle pixel data in that one format.
259 * WARNING: There are format conversions that will not be supported by this
260 * function. It very much depends on what the Moz2D backends support. If
261 * the temporary B8G8R8A8 DrawTarget that this function creates has a
262 * backend that supports DrawSurface() calls passing a surface with
263 * aSurface's format it will work. Otherwise it will not.
265 * *** IMPORTANT PERF NOTE ***
267 * This function exists partly because format conversion is fraught with
268 * non-obvious performance hazards, so we don't want Moz2D consumers to be
269 * doing their own format conversion. Do not try to do so, or at least read
270 * the comments in this functions implemtation. That said, the copy that
271 * this function carries out has a cost and, although this function tries
272 * to avoid perf hazards such as expensive uploads to/readbacks from the
273 * GPU, it can't guarantee that it always successfully does so. Perf
274 * critical code that can directly handle the common formats that it
275 * encounters in a way that is cheaper than a copy-with-format-conversion
276 * should consider doing so, and only use this function as a fallback to
277 * handle other formats.
279 * XXXjwatt it would be nice if SourceSurface::GetDataSurface took a
280 * SurfaceFormat argument (with a default argument meaning "use the
281 * existing surface's format") and returned a DataSourceSurface in that
282 * format. (There would then be an issue of callers maybe failing to
283 * realize format conversion may involve expensive copying/uploading/
286 static already_AddRefed
<DataSourceSurface
>
287 CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface
* aSurface
,
288 SurfaceFormat aFormat
);
291 * Return a color that can be used to identify a frame with a given frame
292 * number. The colors will cycle after sNumFrameColors. You can query colors
293 * 0 .. sNumFrameColors-1 to get all the colors back.
295 static const mozilla::gfx::DeviceColor
& GetColorForFrameNumber(
296 uint64_t aFrameNumber
);
297 static const uint32_t sNumFrameColors
;
299 enum BinaryOrData
{ eBinaryEncode
, eDataURIEncode
};
302 * Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder.
303 * If both aFile and aString are null, the encoded data is copied to the
306 * @param aImageType The image type that the surface is to be encoded to.
307 * Used to create an appropriate imgIEncoder instance to do the encoding.
309 * @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
310 * the value of the |outputOptions| parameter. Callers are responsible
311 * for making sure that this is a reasonable value for the passed MIME-type
312 * (i.e. for the type of encoder that will be created).
314 * @aBinaryOrData Flag used to determine if the surface is simply encoded
315 * to the requested binary image format, or if the binary image is
316 * further converted to base-64 and written out as a 'data:' URI.
318 * @aFile If specified, the encoded data is written out to aFile.
320 * @aString If specified, the encoded data is written out to aString.
322 * TODO: Copying to the clipboard as a binary file is not currently
325 static nsresult
EncodeSourceSurface(SourceSurface
* aSurface
,
326 const ImageType aImageType
,
327 const nsAString
& aOutputOptions
,
328 BinaryOrData aBinaryOrData
, FILE* aFile
,
329 nsACString
* aString
= nullptr);
332 * Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder
333 * and returns the result as an nsIInputStream.
335 * @param aSurface The source surface to encode
337 * @param aImageType The image type that the surface is to be encoded to.
338 * Used to create an appropriate imgIEncoder instance to do the encoding.
340 * @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
341 * the value of the |outputOptions| parameter. Callers are responsible
342 * for making sure that this is a reasonable value for the passed MIME-type
343 * (i.e. for the type of encoder that will be created).
345 * @param aOutStream pointer to the output stream
348 static nsresult
EncodeSourceSurfaceAsStream(SourceSurface
* aSurface
,
349 const ImageType aImageType
,
350 const nsAString
& aOutputOptions
,
351 nsIInputStream
** aOutStream
);
354 * Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder
355 * and returns the result as a vector of bytes
357 * @param aImageType The image type that the surface is to be encoded to.
358 * Used to create an appropriate imgIEncoder instance to do the encoding.
360 * @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
361 * the value of the |outputOptions| parameter. Callers are responsible
362 * for making sure that this is a reasonable value for the passed MIME-type
363 * (i.e. for the type of encoder that will be created).
366 static mozilla::Maybe
<nsTArray
<uint8_t>> EncodeSourceSurfaceAsBytes(
367 SourceSurface
* aSurface
, const ImageType aImageType
,
368 const nsAString
& aOutputOptions
);
371 * Write as a PNG file to the path aFile.
373 static void WriteAsPNG(SourceSurface
* aSurface
, const nsAString
& aFile
);
374 static void WriteAsPNG(SourceSurface
* aSurface
, const char* aFile
);
375 static void WriteAsPNG(DrawTarget
* aDT
, const nsAString
& aFile
);
376 static void WriteAsPNG(DrawTarget
* aDT
, const char* aFile
);
379 * Dump as a PNG encoded Data URL to a FILE stream (using stdout by
382 * Rather than giving aFile a default argument we have separate functions
383 * to make them easier to use from a debugger.
385 static void DumpAsDataURI(SourceSurface
* aSourceSurface
, FILE* aFile
);
386 static inline void DumpAsDataURI(SourceSurface
* aSourceSurface
) {
387 DumpAsDataURI(aSourceSurface
, stdout
);
389 static void DumpAsDataURI(DrawTarget
* aDT
, FILE* aFile
);
390 static inline void DumpAsDataURI(DrawTarget
* aDT
) {
391 DumpAsDataURI(aDT
, stdout
);
393 static nsCString
GetAsDataURI(SourceSurface
* aSourceSurface
);
394 static nsCString
GetAsDataURI(DrawTarget
* aDT
);
395 static nsCString
GetAsLZ4Base64Str(DataSourceSurface
* aSourceSurface
);
397 static mozilla::UniquePtr
<uint8_t[]> GetImageBuffer(
398 DataSourceSurface
* aSurface
, bool aIsAlphaPremultiplied
,
401 static mozilla::UniquePtr
<uint8_t[]> GetImageBufferWithRandomNoise(
402 DataSourceSurface
* aSurface
, bool aIsAlphaPremultiplied
,
403 nsICookieJarSettings
* aCookieJarSettings
, int32_t* outFormat
);
405 static nsresult
GetInputStream(DataSourceSurface
* aSurface
,
406 bool aIsAlphaPremultiplied
,
407 const char* aMimeType
,
408 const nsAString
& aEncoderOptions
,
409 nsIInputStream
** outStream
);
411 static nsresult
GetInputStreamWithRandomNoise(
412 DataSourceSurface
* aSurface
, bool aIsAlphaPremultiplied
,
413 const char* aMimeType
, const nsAString
& aEncoderOptions
,
414 nsICookieJarSettings
* aCookieJarSettings
, nsIInputStream
** outStream
);
416 static void RemoveShaderCacheFromDiskIfNecessary();
419 * Copy to the clipboard as a PNG encoded Data URL.
421 static void CopyAsDataURI(SourceSurface
* aSourceSurface
);
422 static void CopyAsDataURI(DrawTarget
* aDT
);
424 static bool DumpDisplayList();
426 static FILE* sDumpPaintFile
;
431 // Container for either a single element of type T, or an nsTArray<T>.
432 // Provides a minimal subset of nsTArray's API, just enough to support use
433 // by ContextState for the clipsAndTransforms list, and by gfxTextRun for
435 // Using this instead of a simple nsTArray avoids an extra allocation in the
436 // common case where no more than one element is ever added to the list.
437 // Unlike an AutoTArray<..., 1>, this class is memmovable and therefore can
438 // be used in ContextState without breaking its movability.
439 template <typename T
>
440 class ElementOrArray
{
445 enum class Tag
: uint8_t {
450 // gfxTextRun::SortGlyphRuns and SanitizeGlyphRuns directly access the array.
451 friend class ::gfxTextRun
;
452 nsTArray
<T
>& Array() {
453 MOZ_DIAGNOSTIC_ASSERT(mTag
== Tag::Array
);
458 // Construct as an empty array.
459 ElementOrArray() : mTag(Tag::Array
) { new (&mArray
) nsTArray
<T
>(); }
461 // For now, don't support copy/move.
462 ElementOrArray(const ElementOrArray
&) = delete;
463 ElementOrArray(ElementOrArray
&&) = delete;
465 ElementOrArray
& operator=(const ElementOrArray
&) = delete;
466 ElementOrArray
& operator=(ElementOrArray
&&) = delete;
468 // Destroy the appropriate variant.
480 size_t Length() const { return mTag
== Tag::Element
? 1 : mArray
.Length(); }
482 T
* AppendElement(const T
& aElement
) {
485 // Move the existing element into an array, then append the new one.
486 T temp
= std::move(mElement
);
489 new (&mArray
) nsTArray
<T
>();
490 mArray
.AppendElement(std::move(temp
));
491 return mArray
.AppendElement(aElement
);
494 // If currently empty, just store the element directly.
495 if (mArray
.IsEmpty()) {
498 new (&mElement
) T(aElement
);
501 // Otherwise, append it to the array.
502 return mArray
.AppendElement(aElement
);
505 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("invalid tag");
509 const T
& LastElement() const {
510 return mTag
== Tag::Element
? mElement
: mArray
.LastElement();
514 return mTag
== Tag::Element
? mElement
: mArray
.LastElement();
517 bool IsEmpty() const {
518 return mTag
== Tag::Element
? false : mArray
.IsEmpty();
521 void TruncateLength(uint32_t aLength
= 0) {
522 MOZ_DIAGNOSTIC_ASSERT(aLength
<= Length());
526 // Destroy the single element, and convert to an empty array.
529 new (&mArray
) nsTArray
<T
>();
533 mArray
.TruncateLength(aLength
);
543 new (&mArray
) nsTArray
<T
>();
551 // Convert from Array to Element storage. Only to be used when the current
552 // state is a single-element array!
553 void ConvertToElement() {
554 MOZ_DIAGNOSTIC_ASSERT(mTag
== Tag::Array
&& mArray
.Length() == 1);
555 T temp
= std::move(mArray
[0]);
558 new (&mElement
) T(std::move(temp
));
561 const T
& operator[](uint32_t aIndex
) const {
562 MOZ_DIAGNOSTIC_ASSERT(aIndex
< Length());
563 return mTag
== Tag::Element
? mElement
: mArray
[aIndex
];
565 T
& operator[](uint32_t aIndex
) {
566 MOZ_DIAGNOSTIC_ASSERT(aIndex
< Length());
567 return mTag
== Tag::Element
? mElement
: mArray
[aIndex
];
570 // Simple iterators to support range-for loops.
571 const T
* begin() const {
572 return mTag
== Tag::Array
? mArray
.IsEmpty() ? nullptr : &*mArray
.begin()
576 return mTag
== Tag::Array
? mArray
.IsEmpty() ? nullptr : &*mArray
.begin()
580 const T
* end() const {
581 return mTag
== Tag::Array
? begin() + mArray
.Length() : &mElement
+ 1;
584 return mTag
== Tag::Array
? begin() + mArray
.Length() : &mElement
+ 1;
587 size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf
) {
588 return mTag
== Tag::Array
? mArray
.ShallowSizeOfExcludingThis(aMallocSizeOf
)
593 struct StyleAbsoluteColor
;
598 * If the CMS mode is CMSMode::All, these functions transform the passed
599 * color to a device color using the transform returned by
600 * gfxPlatform::GetCMSRGBTransform(). If the CMS mode is some other value, the
601 * color is returned unchanged (other than a type change to Moz2D Color, if
604 DeviceColor
ToDeviceColor(const sRGBColor
&);
605 DeviceColor
ToDeviceColor(const StyleAbsoluteColor
&);
606 DeviceColor
ToDeviceColor(nscolor
);
608 sRGBColor
ToSRGBColor(const StyleAbsoluteColor
&);
611 * Performs a checked multiply of the given width, height, and bytes-per-pixel
614 static inline CheckedInt
<uint32_t> SafeBytesForBitmap(uint32_t aWidth
,
616 unsigned aBytesPerPixel
) {
617 MOZ_ASSERT(aBytesPerPixel
> 0);
618 CheckedInt
<uint32_t> width
= uint32_t(aWidth
);
619 CheckedInt
<uint32_t> height
= uint32_t(aHeight
);
620 return width
* height
* aBytesPerPixel
;
624 } // namespace mozilla