Bug 1816559 - Add ability to encode gfx surface as stream or bytes r=mstange

Differential Revision: https://phabricator.services.mozilla.com/D170513
This commit is contained in:
Chris Martin 2023-03-14 13:31:37 +00:00
parent 222aa43a42
commit b23a9763fb
2 changed files with 118 additions and 45 deletions

View File

@ -40,7 +40,6 @@
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/Unused.h"
#include "mozilla/Vector.h"
#include "mozilla/webrender/webrender_ffi.h"
#include "nsAppRunner.h"
#include "nsComponentManagerUtils.h"
@ -1070,20 +1069,15 @@ const gfx::DeviceColor& gfxUtils::GetColorForFrameNumber(
return colors[aFrameNumber % sNumFrameColors];
}
/* static */
nsresult gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
const ImageType aImageType,
const nsAString& aOutputOptions,
BinaryOrData aBinaryOrData, FILE* aFile,
nsACString* aStrOut) {
MOZ_ASSERT(aBinaryOrData == gfxUtils::eDataURIEncode || aFile || aStrOut,
"Copying binary encoding to clipboard not currently supported");
// static
nsresult gfxUtils::EncodeSourceSurfaceAsStream(SourceSurface* aSurface,
const ImageType aImageType,
const nsAString& aOutputOptions,
nsIInputStream** aOutStream) {
const IntSize size = aSurface->GetSize();
if (size.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
return NS_ERROR_FAILURE;
}
const Size floatSize(size.width, size.height);
RefPtr<DataSourceSurface> dataSurface;
if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
@ -1132,53 +1126,91 @@ nsresult gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
size.width, size.height, map.mStride, imgIEncoder::INPUT_FORMAT_HOSTARGB,
aOutputOptions);
dataSurface->Unmap();
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIInputStream> imgStream(encoder);
if (!imgStream) {
return NS_ERROR_FAILURE;
}
imgStream.forget(aOutStream);
return NS_OK;
}
// static
Maybe<nsTArray<uint8_t>> gfxUtils::EncodeSourceSurfaceAsBytes(
SourceSurface* aSurface, const ImageType aImageType,
const nsAString& aOutputOptions) {
nsCOMPtr<nsIInputStream> imgStream;
nsresult rv = EncodeSourceSurfaceAsStream(
aSurface, aImageType, aOutputOptions, getter_AddRefs(imgStream));
if (NS_FAILED(rv)) {
return Nothing();
}
uint64_t bufSize64;
rv = imgStream->Available(&bufSize64);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(bufSize64 < UINT32_MAX - 16, NS_ERROR_FAILURE);
uint32_t bufSize = (uint32_t)bufSize64;
// ...leave a little extra room so we can call read again and make sure we
// got everything. 16 bytes for better padding (maybe)
bufSize += 16;
uint32_t imgSize = 0;
Vector<char> imgData;
if (!imgData.initCapacity(bufSize)) {
return NS_ERROR_OUT_OF_MEMORY;
if (NS_FAILED(rv) || bufSize64 > UINT32_MAX) {
return Nothing();
}
uint32_t numReadThisTime = 0;
while ((rv = imgStream->Read(imgData.begin() + imgSize, bufSize - imgSize,
&numReadThisTime)) == NS_OK &&
numReadThisTime > 0) {
// Update the length of the vector without overwriting the new data.
if (!imgData.growByUninitialized(numReadThisTime)) {
return NS_ERROR_OUT_OF_MEMORY;
uint32_t bytesLeft = static_cast<uint32_t>(bufSize64);
nsTArray<uint8_t> imgData;
imgData.SetLength(bytesLeft);
uint8_t* bytePtr = imgData.Elements();
while (bytesLeft > 0) {
uint32_t bytesRead = 0;
rv = imgStream->Read(reinterpret_cast<char*>(bytePtr), bytesLeft,
&bytesRead);
if (NS_FAILED(rv) || bytesRead == 0) {
return Nothing();
}
imgSize += numReadThisTime;
if (imgSize == bufSize) {
// need a bigger buffer, just double
bufSize *= 2;
if (!imgData.resizeUninitialized(bufSize)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
bytePtr += bytesRead;
bytesLeft -= bytesRead;
}
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(!imgData.empty(), NS_ERROR_FAILURE);
#ifdef DEBUG
// Currently, all implementers of imgIEncoder report their exact size through
// nsIInputStream::Available(), but let's explicitly state that we rely on
// that behavior for the algorithm above.
char dummy = 0;
uint32_t bytesRead = 0;
rv = imgStream->Read(&dummy, 1, &bytesRead);
MOZ_ASSERT(NS_SUCCEEDED(rv) && bytesRead == 0);
#endif
return Some(std::move(imgData));
}
/* static */
nsresult gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
const ImageType aImageType,
const nsAString& aOutputOptions,
BinaryOrData aBinaryOrData, FILE* aFile,
nsACString* aStrOut) {
MOZ_ASSERT(aBinaryOrData == gfxUtils::eDataURIEncode || aFile || aStrOut,
"Copying binary encoding to clipboard not currently supported");
auto maybeImgData =
EncodeSourceSurfaceAsBytes(aSurface, aImageType, aOutputOptions);
if (!maybeImgData) {
return NS_ERROR_FAILURE;
}
nsTArray<uint8_t>& imgData = *maybeImgData;
if (aBinaryOrData == gfxUtils::eBinaryEncode) {
if (aFile) {
Unused << fwrite(imgData.begin(), 1, imgSize, aFile);
Unused << fwrite(imgData.Elements(), 1, imgData.Length(), aFile);
}
return NS_OK;
}
@ -1208,7 +1240,8 @@ nsresult gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
}
dataURI.AppendLiteral(";base64,");
rv = Base64EncodeAppend(imgData.begin(), imgSize, dataURI);
nsresult rv = Base64EncodeAppend(reinterpret_cast<char*>(imgData.Elements()),
imgData.Length(), dataURI);
NS_ENSURE_SUCCESS(rv, rv);
if (aFile) {

View File

@ -12,6 +12,7 @@
#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"
@ -325,6 +326,45 @@ class gfxUtils {
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 sane 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 sane 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.
*/