michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef GFX_UTILS_H michael@0: #define GFX_UTILS_H michael@0: michael@0: #include "gfxTypes.h" michael@0: #include "GraphicsFilter.h" michael@0: #include "imgIContainer.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "mozilla/RefPtr.h" michael@0: michael@0: class gfxDrawable; michael@0: class nsIntRegion; michael@0: struct nsIntRect; michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: class PlanarYCbCrData; michael@0: } michael@0: } michael@0: michael@0: class gfxUtils { michael@0: public: michael@0: typedef mozilla::gfx::DataSourceSurface DataSourceSurface; michael@0: typedef mozilla::gfx::IntPoint IntPoint; michael@0: typedef mozilla::gfx::Matrix Matrix; michael@0: typedef mozilla::gfx::SourceSurface SourceSurface; michael@0: typedef mozilla::gfx::SurfaceFormat SurfaceFormat; michael@0: michael@0: /* michael@0: * Premultiply or Unpremultiply aSourceSurface, writing the result michael@0: * to aDestSurface or back into aSourceSurface if aDestSurface is null. michael@0: * michael@0: * If aDestSurface is given, it must have identical format, dimensions, and michael@0: * stride as the source. michael@0: * michael@0: * If the source is not gfxImageFormat::ARGB32, no operation is performed. If michael@0: * aDestSurface is given, the data is copied over. michael@0: */ michael@0: static void PremultiplyImageSurface(gfxImageSurface *aSourceSurface, michael@0: gfxImageSurface *aDestSurface = nullptr); michael@0: static void UnpremultiplyImageSurface(gfxImageSurface *aSurface, michael@0: gfxImageSurface *aDestSurface = nullptr); michael@0: static mozilla::TemporaryRef UnpremultiplyDataSurface(DataSourceSurface* aSurface); michael@0: michael@0: static void ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface, michael@0: gfxImageSurface *aDestSurface = nullptr); michael@0: static void ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength); michael@0: michael@0: /** michael@0: * Draw something drawable while working around limitations like bad support michael@0: * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with michael@0: * extreme user-space-to-image-space transforms. michael@0: * michael@0: * The input parameters here usually come from the output of our image michael@0: * snapping algorithm in nsLayoutUtils.cpp. michael@0: * This method is split from nsLayoutUtils::DrawPixelSnapped to allow for michael@0: * adjusting the parameters. For example, certain images with transparent michael@0: * margins only have a drawable subimage. For those images, imgFrame::Draw michael@0: * will tweak the rects and transforms that it gets from the pixel snapping michael@0: * algorithm before passing them on to this method. michael@0: */ michael@0: static void DrawPixelSnapped(gfxContext* aContext, michael@0: gfxDrawable* aDrawable, michael@0: const gfxMatrix& aUserSpaceToImageSpace, michael@0: const gfxRect& aSubimage, michael@0: const gfxRect& aSourceRect, michael@0: const gfxRect& aImageRect, michael@0: const gfxRect& aFill, michael@0: const gfxImageFormat aFormat, michael@0: GraphicsFilter aFilter, michael@0: uint32_t aImageFlags = imgIContainer::FLAG_NONE); michael@0: michael@0: /** michael@0: * Clip aContext to the region aRegion. michael@0: */ michael@0: static void ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion); michael@0: michael@0: /** michael@0: * Clip aTarget to the region aRegion. michael@0: */ michael@0: static void ClipToRegion(mozilla::gfx::DrawTarget* aTarget, const nsIntRegion& aRegion); michael@0: michael@0: /** michael@0: * Clip aContext to the region aRegion, snapping the rectangles. michael@0: */ michael@0: static void ClipToRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion); michael@0: michael@0: /** michael@0: * Clip aTarget to the region aRegion, snapping the rectangles. michael@0: */ michael@0: static void ClipToRegionSnapped(mozilla::gfx::DrawTarget* aTarget, const nsIntRegion& aRegion); michael@0: michael@0: /** michael@0: * Create a path consisting of rectangles in |aRegion|. michael@0: */ michael@0: static void PathFromRegion(gfxContext* aContext, const nsIntRegion& aRegion); michael@0: michael@0: /** michael@0: * Create a path consisting of rectangles in |aRegion|, snapping the rectangles. michael@0: */ michael@0: static void PathFromRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion); michael@0: michael@0: /* michael@0: * Convert image format to depth value michael@0: */ michael@0: static int ImageFormatToDepth(gfxImageFormat aFormat); michael@0: michael@0: /** michael@0: * Return the transform matrix that maps aFrom to the rectangle defined by michael@0: * aToTopLeft/aToTopRight/aToBottomRight. aFrom must be michael@0: * nonempty and the destination rectangle must be axis-aligned. michael@0: */ michael@0: static gfxMatrix TransformRectToRect(const gfxRect& aFrom, michael@0: const gfxPoint& aToTopLeft, michael@0: const gfxPoint& aToTopRight, michael@0: const gfxPoint& aToBottomRight); michael@0: michael@0: static Matrix TransformRectToRect(const gfxRect& aFrom, michael@0: const IntPoint& aToTopLeft, michael@0: const IntPoint& aToTopRight, michael@0: const IntPoint& aToBottomRight); michael@0: michael@0: /** michael@0: * If aIn can be represented exactly using an nsIntRect (i.e. michael@0: * integer-aligned edges and coordinates in the int32_t range) then we michael@0: * set aOut to that rectangle, otherwise return failure. michael@0: */ michael@0: static bool GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut); michael@0: michael@0: /** michael@0: * Return the smallest power of kScaleResolution (2) greater than or equal to michael@0: * aVal. michael@0: */ michael@0: static gfxFloat ClampToScaleFactor(gfxFloat aVal); michael@0: michael@0: /** michael@0: * Helper function for ConvertYCbCrToRGB that finds the michael@0: * RGB buffer size and format for given YCbCrImage. michael@0: * @param aSuggestedFormat will be set to gfxImageFormat::RGB24 michael@0: * if the desired format is not supported. michael@0: * @param aSuggestedSize will be set to the picture size from aData michael@0: * if either the suggested size was {0,0} michael@0: * or simultaneous scaling and conversion is not supported. michael@0: */ michael@0: static void michael@0: GetYCbCrToRGBDestFormatAndSize(const mozilla::layers::PlanarYCbCrData& aData, michael@0: gfxImageFormat& aSuggestedFormat, michael@0: gfxIntSize& aSuggestedSize); michael@0: michael@0: /** michael@0: * Convert YCbCrImage into RGB aDestBuffer michael@0: * Format and Size parameters must have michael@0: * been passed to GetYCbCrToRGBDestFormatAndSize michael@0: */ michael@0: static void michael@0: ConvertYCbCrToRGB(const mozilla::layers::PlanarYCbCrData& aData, michael@0: const gfxImageFormat& aDestFormat, michael@0: const gfxIntSize& aDestSize, michael@0: unsigned char* aDestBuffer, michael@0: int32_t aStride); michael@0: michael@0: /** michael@0: * Creates a copy of aSurface, but having the SurfaceFormat aFormat. michael@0: * michael@0: * This function always creates a new surface. Do not call it if aSurface's michael@0: * format is the same as aFormat. Such a non-conversion would just be an michael@0: * unnecessary and wasteful copy (this function asserts to prevent that). michael@0: * michael@0: * This function is intended to be called by code that needs to access the michael@0: * pixel data of the surface, but doesn't want to have lots of branches michael@0: * to handle different pixel data formats (code which would become out of michael@0: * date if and when new formats are added). Callers can use this function michael@0: * to copy the surface to a specified format so that they only have to michael@0: * handle pixel data in that one format. michael@0: * michael@0: * WARNING: There are format conversions that will not be supported by this michael@0: * function. It very much depends on what the Moz2D backends support. If michael@0: * the temporary B8G8R8A8 DrawTarget that this function creates has a michael@0: * backend that supports DrawSurface() calls passing a surface with michael@0: * aSurface's format it will work. Otherwise it will not. michael@0: * michael@0: * *** IMPORTANT PERF NOTE *** michael@0: * michael@0: * This function exists partly because format conversion is fraught with michael@0: * non-obvious performance hazards, so we don't want Moz2D consumers to be michael@0: * doing their own format conversion. Do not try to do so, or at least read michael@0: * the comments in this functions implemtation. That said, the copy that michael@0: * this function carries out has a cost and, although this function tries michael@0: * to avoid perf hazards such as expensive uploads to/readbacks from the michael@0: * GPU, it can't guarantee that it always successfully does so. Perf michael@0: * critical code that can directly handle the common formats that it michael@0: * encounters in a way that is cheaper than a copy-with-format-conversion michael@0: * should consider doing so, and only use this function as a fallback to michael@0: * handle other formats. michael@0: * michael@0: * XXXjwatt it would be nice if SourceSurface::GetDataSurface took a michael@0: * SurfaceFormat argument (with a default argument meaning "use the michael@0: * existing surface's format") and returned a DataSourceSurface in that michael@0: * format. (There would then be an issue of callers maybe failing to michael@0: * realize format conversion may involve expensive copying/uploading/ michael@0: * readback.) michael@0: */ michael@0: static mozilla::TemporaryRef michael@0: CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface, michael@0: SurfaceFormat aFormat); michael@0: michael@0: static const uint8_t sUnpremultiplyTable[256*256]; michael@0: static const uint8_t sPremultiplyTable[256*256]; michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: /** michael@0: * Writes a binary PNG file. michael@0: */ michael@0: static void WriteAsPNG(mozilla::gfx::DrawTarget* aDT, const char* aFile); michael@0: michael@0: /** michael@0: * Write as a PNG encoded Data URL to stdout. michael@0: */ michael@0: static void DumpAsDataURL(mozilla::gfx::DrawTarget* aDT); michael@0: michael@0: /** michael@0: * Copy a PNG encoded Data URL to the clipboard. michael@0: */ michael@0: static void CopyAsDataURL(mozilla::gfx::DrawTarget* aDT); michael@0: michael@0: static bool sDumpPaintList; michael@0: static bool sDumpPainting; michael@0: static bool sDumpPaintingToFile; michael@0: static FILE* sDumpPaintFile; michael@0: michael@0: /** michael@0: * Writes a binary PNG file. michael@0: * Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads michael@0: */ michael@0: static void WriteAsPNG(mozilla::RefPtr aSourceSurface, const char* aFile); michael@0: michael@0: /** michael@0: * Write as a PNG encoded Data URL to stdout. michael@0: * Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads michael@0: */ michael@0: static void DumpAsDataURL(mozilla::RefPtr aSourceSurface); michael@0: michael@0: /** michael@0: * Copy a PNG encoded Data URL to the clipboard. michael@0: * Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads michael@0: */ michael@0: static void CopyAsDataURL(mozilla::RefPtr aSourceSurface); michael@0: #endif michael@0: }; michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: michael@0: /* These techniques are suggested by "Bit Twiddling Hacks" michael@0: */ michael@0: michael@0: /** michael@0: * Returns true if |aNumber| is a power of two michael@0: * 0 is incorreclty considered a power of two michael@0: */ michael@0: static inline bool michael@0: IsPowerOfTwo(int aNumber) michael@0: { michael@0: return (aNumber & (aNumber - 1)) == 0; michael@0: } michael@0: michael@0: /** michael@0: * Returns the first integer greater than |aNumber| which is a power of two michael@0: * Undefined for |aNumber| < 0 michael@0: */ michael@0: static inline int michael@0: NextPowerOfTwo(int aNumber) michael@0: { michael@0: #if defined(__arm__) michael@0: return 1 << (32 - __builtin_clz(aNumber - 1)); michael@0: #else michael@0: --aNumber; michael@0: aNumber |= aNumber >> 1; michael@0: aNumber |= aNumber >> 2; michael@0: aNumber |= aNumber >> 4; michael@0: aNumber |= aNumber >> 8; michael@0: aNumber |= aNumber >> 16; michael@0: return ++aNumber; michael@0: #endif michael@0: } michael@0: michael@0: } // namespace gfx michael@0: } // namespace mozilla michael@0: michael@0: #endif