Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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 GFX_UTILS_H
#define GFX_UTILS_H
#include "gfxMatrix.h"
#include "gfxRect.h"
#include "gfxTypes.h"
#include "ImageTypes.h"
#include "imgIContainer.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsColor.h"
#include "nsContentUtils.h"
#include "nsPrintfCString.h"
#include "nsRegionFwd.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/webrender/WebRenderTypes.h"
#include "qcms.h"
class gfxASurface;
class gfxDrawable;
class gfxTextRun;
struct gfxQuad;
class nsICookieJarSettings;
class nsIInputStream;
class nsIGfxInfo;
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
namespace layers {
class WebRenderBridgeChild;
class GlyphArray;
struct PlanarYCbCrData;
class WebRenderCommand;
} // namespace layers
namespace image {
class ImageRegion;
} // namespace image
namespace wr {
class DisplayListBuilder;
} // namespace wr
} // namespace mozilla
enum class ImageType {
BMP,
ICO,
JPEG,
PNG,
};
class gfxUtils {
public:
typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::IntPoint IntPoint;
typedef mozilla::gfx::Matrix Matrix;
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
typedef mozilla::gfx::SourceSurface SourceSurface;
typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
typedef mozilla::image::ImageRegion ImageRegion;
/*
* Premultiply or Unpremultiply aSourceSurface, writing the result
* to aDestSurface or back into aSourceSurface if aDestSurface is null.
*
* If aDestSurface is given, it must have identical format, dimensions, and
* stride as the source.
*
* If the source is not SurfaceFormat::A8R8G8B8_UINT32, no operation is
* performed. If aDestSurface is given, the data is copied over.
*/
static bool PremultiplyDataSurface(DataSourceSurface* srcSurf,
DataSourceSurface* destSurf);
static bool UnpremultiplyDataSurface(DataSourceSurface* srcSurf,
DataSourceSurface* destSurf);
static already_AddRefed<DataSourceSurface> CreatePremultipliedDataSurface(
DataSourceSurface* srcSurf);
static already_AddRefed<DataSourceSurface> CreateUnpremultipliedDataSurface(
DataSourceSurface* srcSurf);
static void ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength);
/**
* Draw something drawable while working around limitations like bad support
* for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
* extreme user-space-to-image-space transforms.
*
* The input parameters here usually come from the output of our image
* snapping algorithm in nsLayoutUtils.cpp.
* This method is split from nsLayoutUtils::DrawPixelSnapped to allow for
* adjusting the parameters. For example, certain images with transparent
* margins only have a drawable subimage. For those images, imgFrame::Draw
* will tweak the rects and transforms that it gets from the pixel snapping
* algorithm before passing them on to this method.
*/
static void DrawPixelSnapped(gfxContext* aContext, gfxDrawable* aDrawable,
const gfxSize& aImageSize,
const ImageRegion& aRegion,
const mozilla::gfx::SurfaceFormat aFormat,
mozilla::gfx::SamplingFilter aSamplingFilter,
uint32_t aImageFlags = imgIContainer::FLAG_NONE,
gfxFloat aOpacity = 1.0,
bool aUseOptimalFillOp = true);
/**
* Clip aContext to the region aRegion.
*/
static void ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion);
/**
* Clip aTarget to the region aRegion.
*/
static void ClipToRegion(mozilla::gfx::DrawTarget* aTarget,
const nsIntRegion& aRegion);
/*
* Convert image format to depth value
*/
static int ImageFormatToDepth(gfxImageFormat aFormat);
/**
* Return the transform matrix that maps aFrom to the rectangle defined by
* aToTopLeft/aToTopRight/aToBottomRight. aFrom must be
* nonempty and the destination rectangle must be axis-aligned.
*/
static gfxMatrix TransformRectToRect(const gfxRect& aFrom,
const gfxPoint& aToTopLeft,
const gfxPoint& aToTopRight,
const gfxPoint& aToBottomRight);
static Matrix TransformRectToRect(const gfxRect& aFrom,
const IntPoint& aToTopLeft,
const IntPoint& aToTopRight,
const IntPoint& aToBottomRight);
/**
* If aIn can be represented exactly using an gfx::IntRect (i.e.
* integer-aligned edges and coordinates in the int32_t range) then we
* set aOut to that rectangle, otherwise return failure.
*/
static bool GfxRectToIntRect(const gfxRect& aIn, mozilla::gfx::IntRect* aOut);
/* Conditions this border to Cairo's max coordinate space.
* The caller can check IsEmpty() after Condition() -- if it's TRUE,
* the caller can possibly avoid doing any extra rendering.
*/
static void ConditionRect(gfxRect& aRect);
/*
* Transform this rectangle with aMatrix, resulting in a gfxQuad.
*/
static gfxQuad TransformToQuad(const gfxRect& aRect,
const mozilla::gfx::Matrix4x4& aMatrix);
/**
* Return the smallest power of kScaleResolution (2) greater than or equal to
* aVal. If aRoundDown is specified, the power of 2 will rather be less than
* or equal to aVal.
*/
static float ClampToScaleFactor(float aVal, bool aRoundDown = false);
/**
* We can snap layer transforms for two reasons:
* 1) To avoid unnecessary resampling when a transform is a translation
* by a non-integer number of pixels.
* Snapping the translation to an integer number of pixels avoids
* blurring the layer and can be faster to composite.
* 2) When a layer is used to render a rectangular object, we need to
* emulate the rendering of rectangular inactive content and snap the
* edges of the rectangle to pixel boundaries. This is both to ensure
* layer rendering is consistent with inactive content rendering, and to
* avoid seams.
* This function implements type 1 snapping. If aTransform is a 2D
* translation, and this layer's layer manager has enabled snapping
* (which is the default), return aTransform with the translation snapped
* to nearest pixels. Otherwise just return aTransform. Call this when the
* layer does not correspond to a single rectangular content object.
* This function does not try to snap if aTransform has a scale, because in
* that case resampling is inevitable and there's no point in trying to
* avoid it. In fact snapping can cause problems because pixel edges in the
* layer's content can be rendered unpredictably (jiggling) as the scale
* interacts with the snapping of the translation, especially with animated
* transforms.
* @param aResidualTransform a transform to apply before the result transform
* in order to get the results to completely match aTransform.
*/
static Matrix4x4 SnapTransformTranslation(const Matrix4x4& aTransform,
Matrix* aResidualTransform);
static Matrix SnapTransformTranslation(const Matrix& aTransform,
Matrix* aResidualTransform);
static Matrix4x4 SnapTransformTranslation3D(const Matrix4x4& aTransform,
Matrix* aResidualTransform);
/**
* See comment for SnapTransformTranslation.
* This function implements type 2 snapping. If aTransform is a translation
* and/or scale, transform aSnapRect by aTransform, snap to pixel boundaries,
* and return the transform that maps aSnapRect to that rect. Otherwise
* just return aTransform.
* @param aSnapRect a rectangle whose edges should be snapped to pixel
* boundaries in the destination surface.
* @param aResidualTransform a transform to apply before the result transform
* in order to get the results to completely match aTransform.
*/
static Matrix4x4 SnapTransform(const Matrix4x4& aTransform,
const gfxRect& aSnapRect,
Matrix* aResidualTransform);
static Matrix SnapTransform(const Matrix& aTransform,
const gfxRect& aSnapRect,
Matrix* aResidualTransform);
/**
* Clears surface to aColor (which defaults to transparent black).
*/
static void ClearThebesSurface(gfxASurface* aSurface);
static const float* YuvToRgbMatrix4x3RowMajor(
mozilla::gfx::YUVColorSpace aYUVColorSpace);
static const float* YuvToRgbMatrix3x3ColumnMajor(
mozilla::gfx::YUVColorSpace aYUVColorSpace);
static const float* YuvToRgbMatrix4x4ColumnMajor(
mozilla::gfx::YUVColorSpace aYUVColorSpace);
static mozilla::Maybe<mozilla::gfx::YUVColorSpace> CicpToColorSpace(
const mozilla::gfx::CICP::MatrixCoefficients,
const mozilla::gfx::CICP::ColourPrimaries,
mozilla::LazyLogModule& aLogger);
static mozilla::Maybe<mozilla::gfx::ColorSpace2> CicpToColorPrimaries(
const mozilla::gfx::CICP::ColourPrimaries,
mozilla::LazyLogModule& aLogger);
static mozilla::Maybe<mozilla::gfx::TransferFunction> CicpToTransferFunction(
const mozilla::gfx::CICP::TransferCharacteristics);
/**
* Creates a copy of aSurface, but having the SurfaceFormat aFormat.
*
* This function always creates a new surface. Do not call it if aSurface's
* format is the same as aFormat. Such a non-conversion would just be an
* unnecessary and wasteful copy (this function asserts to prevent that).
*
* This function is intended to be called by code that needs to access the
* pixel data of the surface, but doesn't want to have lots of branches
* to handle different pixel data formats (code which would become out of
* date if and when new formats are added). Callers can use this function
* to copy the surface to a specified format so that they only have to
* handle pixel data in that one format.
*
* WARNING: There are format conversions that will not be supported by this
* function. It very much depends on what the Moz2D backends support. If
* the temporary B8G8R8A8 DrawTarget that this function creates has a
* backend that supports DrawSurface() calls passing a surface with
* aSurface's format it will work. Otherwise it will not.
*
* *** IMPORTANT PERF NOTE ***
*
* This function exists partly because format conversion is fraught with
* non-obvious performance hazards, so we don't want Moz2D consumers to be
* doing their own format conversion. Do not try to do so, or at least read
* the comments in this functions implemtation. That said, the copy that
* this function carries out has a cost and, although this function tries
* to avoid perf hazards such as expensive uploads to/readbacks from the
* GPU, it can't guarantee that it always successfully does so. Perf
* critical code that can directly handle the common formats that it
* encounters in a way that is cheaper than a copy-with-format-conversion
* should consider doing so, and only use this function as a fallback to
* handle other formats.
*
* XXXjwatt it would be nice if SourceSurface::GetDataSurface took a
* SurfaceFormat argument (with a default argument meaning "use the
* existing surface's format") and returned a DataSourceSurface in that
* format. (There would then be an issue of callers maybe failing to
* realize format conversion may involve expensive copying/uploading/
* readback.)
*/
static already_AddRefed<DataSourceSurface>
CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
SurfaceFormat aFormat);
/**
* Return a color that can be used to identify a frame with a given frame
* number. The colors will cycle after sNumFrameColors. You can query colors
* 0 .. sNumFrameColors-1 to get all the colors back.
*/
static const mozilla::gfx::DeviceColor& GetColorForFrameNumber(
uint64_t aFrameNumber);
static const uint32_t sNumFrameColors;
enum BinaryOrData { eBinaryEncode, eDataURIEncode };
/**
* Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder.
* If both aFile and aString are null, the encoded data is copied to the
* clipboard.
*
* @param aImageType The image type that the surface is to be encoded to.
* Used to create an appropriate imgIEncoder instance to do the encoding.
*
* @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
* the value of the |outputOptions| parameter. Callers are responsible
* for making sure that this is a reasonable value for the passed MIME-type
* (i.e. for the type of encoder that will be created).
*
* @aBinaryOrData Flag used to determine if the surface is simply encoded
* to the requested binary image format, or if the binary image is
* further converted to base-64 and written out as a 'data:' URI.
*
* @aFile If specified, the encoded data is written out to aFile.
*
* @aString If specified, the encoded data is written out to aString.
*
* TODO: Copying to the clipboard as a binary file is not currently
* supported.
*/
static nsresult EncodeSourceSurface(SourceSurface* aSurface,
const ImageType aImageType,
const nsAString& aOutputOptions,
BinaryOrData aBinaryOrData, FILE* aFile,
nsACString* aString = nullptr);
/**
* Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder
* and returns the result as an nsIInputStream.
*
* @param aSurface The source surface to encode
*
* @param aImageType The image type that the surface is to be encoded to.
* Used to create an appropriate imgIEncoder instance to do the encoding.
*
* @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
* the value of the |outputOptions| parameter. Callers are responsible
* for making sure that this is a reasonable value for the passed MIME-type
* (i.e. for the type of encoder that will be created).
*
* @param aOutStream pointer to the output stream
*
*/
static nsresult EncodeSourceSurfaceAsStream(SourceSurface* aSurface,
const ImageType aImageType,
const nsAString& aOutputOptions,
nsIInputStream** aOutStream);
/**
* Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder
* and returns the result as a vector of bytes
*
* @param aImageType The image type that the surface is to be encoded to.
* Used to create an appropriate imgIEncoder instance to do the encoding.
*
* @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
* the value of the |outputOptions| parameter. Callers are responsible
* for making sure that this is a reasonable value for the passed MIME-type
* (i.e. for the type of encoder that will be created).
*
*/
static mozilla::Maybe<nsTArray<uint8_t>> EncodeSourceSurfaceAsBytes(
SourceSurface* aSurface, const ImageType aImageType,
const nsAString& aOutputOptions);
/**
* Write as a PNG file to the path aFile.
*/
static void WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile);
static void WriteAsPNG(SourceSurface* aSurface, const char* aFile);
static void WriteAsPNG(DrawTarget* aDT, const nsAString& aFile);
static void WriteAsPNG(DrawTarget* aDT, const char* aFile);
/**
* Dump as a PNG encoded Data URL to a FILE stream (using stdout by
* default).
*
* Rather than giving aFile a default argument we have separate functions
* to make them easier to use from a debugger.
*/
static void DumpAsDataURI(SourceSurface* aSourceSurface, FILE* aFile);
static inline void DumpAsDataURI(SourceSurface* aSourceSurface) {
DumpAsDataURI(aSourceSurface, stdout);
}
static void DumpAsDataURI(DrawTarget* aDT, FILE* aFile);
static inline void DumpAsDataURI(DrawTarget* aDT) {
DumpAsDataURI(aDT, stdout);
}
static nsCString GetAsDataURI(SourceSurface* aSourceSurface);
static nsCString GetAsDataURI(DrawTarget* aDT);
static nsCString GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface);
static mozilla::UniquePtr<uint8_t[]> GetImageBuffer(
DataSourceSurface* aSurface, bool aIsAlphaPremultiplied,
int32_t* outFormat);
static mozilla::UniquePtr<uint8_t[]> GetImageBufferWithRandomNoise(
DataSourceSurface* aSurface, bool aIsAlphaPremultiplied,
nsICookieJarSettings* aCookieJarSettings, int32_t* outFormat);
static nsresult GetInputStream(DataSourceSurface* aSurface,
bool aIsAlphaPremultiplied,
const char* aMimeType,
const nsAString& aEncoderOptions,
nsIInputStream** outStream);
static nsresult GetInputStreamWithRandomNoise(
DataSourceSurface* aSurface, bool aIsAlphaPremultiplied,
const char* aMimeType, const nsAString& aEncoderOptions,
nsICookieJarSettings* aCookieJarSettings, nsIInputStream** outStream);
static void RemoveShaderCacheFromDiskIfNecessary();
/**
* Copy to the clipboard as a PNG encoded Data URL.
*/
static void CopyAsDataURI(SourceSurface* aSourceSurface);
static void CopyAsDataURI(DrawTarget* aDT);
static bool DumpDisplayList();
static FILE* sDumpPaintFile;
};
namespace mozilla {
// Container for either a single element of type T, or an nsTArray<T>.
// Provides a minimal subset of nsTArray's API, just enough to support use
// by ContextState for the clipsAndTransforms list, and by gfxTextRun for
// its mGlyphRuns.
// Using this instead of a simple nsTArray avoids an extra allocation in the
// common case where no more than one element is ever added to the list.
// Unlike an AutoTArray<..., 1>, this class is memmovable and therefore can
// be used in ContextState without breaking its movability.
template <typename T>
class ElementOrArray {
union {
T mElement;
nsTArray<T> mArray;
};
enum class Tag : uint8_t {
Element,
Array,
} mTag;
// gfxTextRun::SortGlyphRuns and SanitizeGlyphRuns directly access the array.
friend class ::gfxTextRun;
nsTArray<T>& Array() {
MOZ_DIAGNOSTIC_ASSERT(mTag == Tag::Array);
return mArray;
}
public:
// Construct as an empty array.
ElementOrArray() : mTag(Tag::Array) { new (&mArray) nsTArray<T>(); }
// For now, don't support copy/move.
ElementOrArray(const ElementOrArray&) = delete;
ElementOrArray(ElementOrArray&&) = delete;
ElementOrArray& operator=(const ElementOrArray&) = delete;
ElementOrArray& operator=(ElementOrArray&&) = delete;
// Destroy the appropriate variant.
~ElementOrArray() {
switch (mTag) {
case Tag::Element:
mElement.~T();
break;
case Tag::Array:
mArray.~nsTArray();
break;
}
}
size_t Length() const { return mTag == Tag::Element ? 1 : mArray.Length(); }
T* AppendElement(const T& aElement) {
switch (mTag) {
case Tag::Element: {
// Move the existing element into an array, then append the new one.
T temp = std::move(mElement);
mElement.~T();
mTag = Tag::Array;
new (&mArray) nsTArray<T>();
mArray.AppendElement(std::move(temp));
return mArray.AppendElement(aElement);
}
case Tag::Array: {
// If currently empty, just store the element directly.
if (mArray.IsEmpty()) {
mArray.~nsTArray();
mTag = Tag::Element;
new (&mElement) T(aElement);
return &mElement;
}
// Otherwise, append it to the array.
return mArray.AppendElement(aElement);
}
default:
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("invalid tag");
}
}
const T& LastElement() const {
return mTag == Tag::Element ? mElement : mArray.LastElement();
}
T& LastElement() {
return mTag == Tag::Element ? mElement : mArray.LastElement();
}
bool IsEmpty() const {
return mTag == Tag::Element ? false : mArray.IsEmpty();
}
void TruncateLength(uint32_t aLength = 0) {
MOZ_DIAGNOSTIC_ASSERT(aLength <= Length());
switch (mTag) {
case Tag::Element:
if (aLength == 0) {
// Destroy the single element, and convert to an empty array.
mElement.~T();
mTag = Tag::Array;
new (&mArray) nsTArray<T>();
}
break;
case Tag::Array:
mArray.TruncateLength(aLength);
break;
}
}
void Clear() {
switch (mTag) {
case Tag::Element:
mElement.~T();
mTag = Tag::Array;
new (&mArray) nsTArray<T>();
break;
case Tag::Array:
mArray.Clear();
break;
}
}
// Convert from Array to Element storage. Only to be used when the current
// state is a single-element array!
void ConvertToElement() {
MOZ_DIAGNOSTIC_ASSERT(mTag == Tag::Array && mArray.Length() == 1);
T temp = std::move(mArray[0]);
mArray.~nsTArray();
mTag = Tag::Element;
new (&mElement) T(std::move(temp));
}
const T& operator[](uint32_t aIndex) const {
MOZ_DIAGNOSTIC_ASSERT(aIndex < Length());
return mTag == Tag::Element ? mElement : mArray[aIndex];
}
T& operator[](uint32_t aIndex) {
MOZ_DIAGNOSTIC_ASSERT(aIndex < Length());
return mTag == Tag::Element ? mElement : mArray[aIndex];
}
// Simple iterators to support range-for loops.
const T* begin() const {
return mTag == Tag::Array ? mArray.IsEmpty() ? nullptr : &*mArray.begin()
: &mElement;
}
T* begin() {
return mTag == Tag::Array ? mArray.IsEmpty() ? nullptr : &*mArray.begin()
: &mElement;
}
const T* end() const {
return mTag == Tag::Array ? begin() + mArray.Length() : &mElement + 1;
}
T* end() {
return mTag == Tag::Array ? begin() + mArray.Length() : &mElement + 1;
}
size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
return mTag == Tag::Array ? mArray.ShallowSizeOfExcludingThis(aMallocSizeOf)
: 0;
}
};
struct StyleAbsoluteColor;
namespace gfx {
/**
* If the CMS mode is CMSMode::All, these functions transform the passed
* color to a device color using the transform returned by
* gfxPlatform::GetCMSRGBTransform(). If the CMS mode is some other value, the
* color is returned unchanged (other than a type change to Moz2D Color, if
* applicable).
*/
DeviceColor ToDeviceColor(const sRGBColor&);
DeviceColor ToDeviceColor(const StyleAbsoluteColor&);
DeviceColor ToDeviceColor(nscolor);
sRGBColor ToSRGBColor(const StyleAbsoluteColor&);
/**
* Performs a checked multiply of the given width, height, and bytes-per-pixel
* values.
*/
static inline CheckedInt<uint32_t> SafeBytesForBitmap(uint32_t aWidth,
uint32_t aHeight,
unsigned aBytesPerPixel) {
MOZ_ASSERT(aBytesPerPixel > 0);
CheckedInt<uint32_t> width = uint32_t(aWidth);
CheckedInt<uint32_t> height = uint32_t(aHeight);
return width * height * aBytesPerPixel;
}
} // namespace gfx
} // namespace mozilla
#endif