diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/thebes/gfxUtils.h | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h new file mode 100644 index 0000000000..a7b9f067eb --- /dev/null +++ b/gfx/thebes/gfxUtils.h @@ -0,0 +1,626 @@ +/* -*- 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 |