Bug 986526 - Create helper functions to encode DrawTargets/SourceSurfaces to PNG/JPEG/BMP/ICO/etc. and save them, dump them, or copy them to the clipboard, either as binary or as data: URIs. r=mattwoodrow

This commit is contained in:
Jonathan Watt 2014-06-27 12:19:40 +01:00
parent ce87b3d1fc
commit fa20dc97b8
4 changed files with 355 additions and 211 deletions

View File

@ -10,9 +10,19 @@
#include "gfxImageSurface.h"
#include "gfxPlatform.h"
#include "gfxDrawable.h"
#include "imgIEncoder.h"
#include "mozilla/Base64.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Vector.h"
#include "nsComponentManagerUtils.h"
#include "nsIClipboardHelper.h"
#include "nsIFile.h"
#include "nsIPresShell.h"
#include "nsPresContext.h"
#include "nsRegion.h"
#include "nsServiceManagerUtils.h"
#include "yuv_convert.h"
#include "ycbcr_to_rgb565.h"
#include "GeckoProfiler.h"
@ -1105,83 +1115,258 @@ gfxUtils::GetColorForFrameNumber(uint64_t aFrameNumber)
return colors[aFrameNumber % sNumFrameColors];
}
#ifdef MOZ_DUMP_PAINTING
/* static */ nsresult
gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
const nsACString& aMimeType,
const nsAString& aOutputOptions,
BinaryOrData aBinaryOrData,
FILE* aFile)
{
MOZ_ASSERT(aBinaryOrData == eDataURIEncode || aFile,
"Copying binary encoding to clipboard not currently supported");
const IntSize size = aSurface->GetSize();
if (size.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
}
const Size floatSize(size.width, size.height);
RefPtr<DataSourceSurface> dataSurface;
if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
// FIXME bug 995807 (B8G8R8X8), bug 831898 (R5G6B5)
dataSurface =
CopySurfaceToDataSourceSurfaceWithFormat(aSurface,
SurfaceFormat::B8G8R8A8);
} else {
dataSurface = aSurface->GetDataSurface();
}
if (!dataSurface) {
return NS_ERROR_FAILURE;
}
DataSourceSurface::MappedSurface map;
if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
return NS_ERROR_FAILURE;
}
nsAutoCString encoderCID(
NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
if (!encoder) {
#ifdef DEBUG
int32_t w = std::min(size.width, 8);
int32_t h = std::min(size.height, 8);
printf("Could not create encoder. Top-left %dx%d pixels contain:\n", w, h);
for (int32_t y = 0; y < h; ++y) {
for (int32_t x = 0; x < w; ++x) {
printf("%x ", reinterpret_cast<uint32_t*>(map.mData)[y*map.mStride+x]);
}
}
#endif
dataSurface->Unmap();
return NS_ERROR_FAILURE;
}
nsresult rv = encoder->InitFromData(map.mData,
BufferSizeFromStrideAndHeight(map.mStride, size.height),
size.width,
size.height,
map.mStride,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
aOutputOptions);
dataSurface->Unmap();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> imgStream;
CallQueryInterface(encoder.get(), getter_AddRefs(imgStream));
if (!imgStream) {
return NS_ERROR_FAILURE;
}
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;
}
uint32_t numReadThisTime = 0;
while ((rv = imgStream->Read(imgData.begin() + imgSize,
bufSize - imgSize,
&numReadThisTime)) == NS_OK && numReadThisTime > 0)
{
imgSize += numReadThisTime;
if (imgSize == bufSize) {
// need a bigger buffer, just double
bufSize *= 2;
if (!imgData.resizeUninitialized(bufSize)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
}
if (aBinaryOrData == eBinaryEncode) {
if (aFile) {
fwrite(imgData.begin(), 1, imgSize, aFile);
}
return NS_OK;
}
// base 64, result will be null-terminated
nsCString encodedImg;
rv = Base64Encode(Substring(imgData.begin(), imgSize), encodedImg);
NS_ENSURE_SUCCESS(rv, rv);
nsCString string("data:");
string.Append(aMimeType);
string.Append(";base64,");
string.Append(encodedImg);
if (aFile) {
#ifdef ANDROID
if (aFile == stdout || aFile == stderr) {
// ADB logcat cuts off long strings so we will break it down
const char* cStr = string.BeginReading();
size_t len = strlen(cStr);
while (true) {
printf_stderr("IMG: %.140s\n", cStr);
if (len <= 140)
break;
len -= 140;
cStr += 140;
}
}
#endif
fprintf(aFile, "%s", string.BeginReading());
} else {
nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
if (clipboard) {
clipboard->CopyString(NS_ConvertASCIItoUTF16(string), nullptr);
}
}
return NS_OK;
}
/* static */ void
gfxUtils::WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile)
{
WriteAsPNG(aSurface, NS_ConvertUTF16toUTF8(aFile).get());
}
/* static */ void
gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile)
{
FILE* file = fopen(aFile, "wb");
if (!file) {
// Maybe the directory doesn't exist; try creating it, then fopen again.
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
if (comFile) {
NS_ConvertUTF8toUTF16 utf16path((nsDependentCString(aFile)));
rv = comFile->InitWithPath(utf16path);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFile> dirPath;
comFile->GetParent(getter_AddRefs(dirPath));
if (dirPath) {
rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
file = fopen(aFile, "wb");
}
}
}
}
if (!file) {
NS_WARNING("Failed to open file to create PNG!\n");
return;
}
}
EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
EmptyString(), eBinaryEncode, file);
fclose(file);
}
/* static */ void
gfxUtils::WriteAsPNG(DrawTarget* aDT, const nsAString& aFile)
{
WriteAsPNG(aDT, NS_ConvertUTF16toUTF8(aFile).get());
}
/* static */ void
gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile)
{
aDT->Flush();
nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
if (surf) {
surf->WriteAsPNG(aFile);
RefPtr<SourceSurface> surface = aDT->Snapshot();
if (surface) {
WriteAsPNG(surface, aFile);
} else {
NS_WARNING("Failed to get Thebes surface!");
NS_WARNING("Failed to get surface!");
}
}
/* static */ void
gfxUtils::DumpAsDataURL(DrawTarget* aDT)
gfxUtils::WriteAsPNG(nsIPresShell* aShell, const char* aFile)
{
aDT->Flush();
nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
if (surf) {
surf->DumpAsDataURL();
int32_t width = 1000, height = 1000;
nsRect r(0, 0, aShell->GetPresContext()->DevPixelsToAppUnits(width),
aShell->GetPresContext()->DevPixelsToAppUnits(height));
RefPtr<mozilla::gfx::DrawTarget> dt = gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(IntSize(width, height),
SurfaceFormat::B8G8R8A8);
NS_ENSURE_TRUE(dt, /*void*/);
nsRefPtr<gfxContext> context = new gfxContext(dt);
aShell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
WriteAsPNG(dt.get(), aFile);
}
/* static */ void
gfxUtils::DumpAsDataURI(SourceSurface* aSurface, FILE* aFile)
{
EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
EmptyString(), eDataURIEncode, aFile);
}
/* static */ void
gfxUtils::DumpAsDataURI(DrawTarget* aDT, FILE* aFile)
{
RefPtr<SourceSurface> surface = aDT->Snapshot();
if (surface) {
DumpAsDataURI(surface, aFile);
} else {
NS_WARNING("Failed to get Thebes surface!");
NS_WARNING("Failed to get surface!");
}
}
/* static */ void
gfxUtils::CopyAsDataURL(DrawTarget* aDT)
gfxUtils::CopyAsDataURI(SourceSurface* aSurface)
{
aDT->Flush();
nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
if (surf) {
surf->CopyAsDataURL();
EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
EmptyString(), eDataURIEncode, nullptr);
}
/* static */ void
gfxUtils::CopyAsDataURI(DrawTarget* aDT)
{
RefPtr<SourceSurface> surface = aDT->Snapshot();
if (surface) {
CopyAsDataURI(surface);
} else {
NS_WARNING("Failed to get Thebes surface!");
NS_WARNING("Failed to get surface!");
}
}
/* static */ void
gfxUtils::WriteAsPNG(gfx::SourceSurface* aSourceSurface, const char* aFile)
{
RefPtr<gfx::DataSourceSurface> dataSurface = aSourceSurface->GetDataSurface();
RefPtr<gfx::DrawTarget> dt
= gfxPlatform::GetPlatform()
->CreateDrawTargetForData(dataSurface->GetData(),
dataSurface->GetSize(),
dataSurface->Stride(),
aSourceSurface->GetFormat());
gfxUtils::WriteAsPNG(dt.get(), aFile);
}
/* static */ void
gfxUtils::DumpAsDataURL(gfx::SourceSurface* aSourceSurface)
{
RefPtr<gfx::DataSourceSurface> dataSurface = aSourceSurface->GetDataSurface();
RefPtr<gfx::DrawTarget> dt
= gfxPlatform::GetPlatform()
->CreateDrawTargetForData(dataSurface->GetData(),
dataSurface->GetSize(),
dataSurface->Stride(),
aSourceSurface->GetFormat());
gfxUtils::DumpAsDataURL(dt.get());
}
/* static */ void
gfxUtils::CopyAsDataURL(gfx::SourceSurface* aSourceSurface)
{
RefPtr<gfx::DataSourceSurface> dataSurface = aSourceSurface->GetDataSurface();
RefPtr<gfx::DrawTarget> dt
= gfxPlatform::GetPlatform()
->CreateDrawTargetForData(dataSurface->GetData(),
dataSurface->GetSize(),
dataSurface->Stride(),
aSourceSurface->GetFormat());
gfxUtils::CopyAsDataURL(dt.get());
}
#ifdef MOZ_DUMP_PAINTING
static bool sDumpPaintList = getenv("MOZ_DUMP_PAINT_LIST") != 0;
/* static */ bool

View File

@ -16,6 +16,8 @@
class gfxASurface;
class gfxDrawable;
class nsIntRegion;
class nsIPresShell;
struct nsIntRect;
namespace mozilla {
@ -27,6 +29,7 @@ struct PlanarYCbCrData;
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::SourceSurface SourceSurface;
@ -228,45 +231,78 @@ public:
static const mozilla::gfx::Color& GetColorForFrameNumber(uint64_t aFrameNumber);
static const uint32_t sNumFrameColors;
enum BinaryOrData {
eBinaryEncode,
eDataURIEncode
};
/**
* Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder.
*
* @param aMimeType The MIME-type of 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).
*
* @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, otherwise
* it is copied to the clipboard.
*
* TODO: Copying to the clipboard as a binary file is not currently
* supported.
*/
static nsresult
EncodeSourceSurface(SourceSurface* aSurface,
const nsACString& aMimeType,
const nsAString& aOutputOptions,
BinaryOrData aBinaryOrData,
FILE* aFile);
/**
* 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);
static void WriteAsPNG(nsIPresShell* aShell, 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);
}
/**
* Copy to the clipboard as a PNG encoded Data URL.
*/
static void CopyAsDataURI(SourceSurface* aSourceSurface);
static void CopyAsDataURI(DrawTarget* aDT);
#ifdef MOZ_DUMP_PAINTING
/**
* Writes a binary PNG file.
*/
static void WriteAsPNG(mozilla::gfx::DrawTarget* aDT, const char* aFile);
/**
* Write as a PNG encoded Data URL to stdout.
*/
static void DumpAsDataURL(mozilla::gfx::DrawTarget* aDT);
/**
* Copy a PNG encoded Data URL to the clipboard.
*/
static void CopyAsDataURL(mozilla::gfx::DrawTarget* aDT);
static bool DumpPaintList();
static bool sDumpPainting;
static bool sDumpPaintingToFile;
static FILE* sDumpPaintFile;
/**
* Writes a binary PNG file.
* Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads
*/
static void WriteAsPNG(mozilla::gfx::SourceSurface* aSourceSurface, const char* aFile);
/**
* Write as a PNG encoded Data URL to stdout.
* Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads
*/
static void DumpAsDataURL(mozilla::gfx::SourceSurface* aSourceSurface);
/**
* Copy a PNG encoded Data URL to the clipboard.
* Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads
*/
static void CopyAsDataURL(mozilla::gfx::SourceSurface* aSourceSurface);
#endif
};

View File

@ -114,6 +114,7 @@
#include "nsStyleSheetService.h"
#include "gfxImageSurface.h"
#include "gfxContext.h"
#include "gfxUtils.h"
#include "nsSMILAnimationController.h"
#include "SVGContentUtils.h"
#include "nsSVGEffects.h"
@ -9522,68 +9523,6 @@ PresShell::CloneStyleSet(nsStyleSet* aSet)
return clone;
}
#ifdef DEBUG_Eli
static nsresult
DumpToPNG(nsIPresShell* shell, nsAString& name) {
int32_t width=1000, height=1000;
nsRect r(0, 0, shell->GetPresContext()->DevPixelsToAppUnits(width),
shell->GetPresContext()->DevPixelsToAppUnits(height));
nsRefPtr<gfxImageSurface> imgSurface =
new gfxImageSurface(gfxIntSize(width, height),
gfxImageFormat::ARGB32);
nsRefPtr<gfxContext> imgContext = new gfxContext(imgSurface);
nsRefPtr<gfxASurface> surface =
gfxPlatform::GetPlatform()->
CreateOffscreenSurface(IntSize(width, height),
gfxASurface::ContentFromFormat(gfxImageFormat::ARGB32));
NS_ENSURE_TRUE(surface, NS_ERROR_OUT_OF_MEMORY);
nsRefPtr<gfxContext> context = new gfxContext(surface);
shell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
imgContext->DrawSurface(surface, gfxSize(width, height));
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE);
encoder->InitFromData(imgSurface->Data(), imgSurface->Stride() * height,
width, height, imgSurface->Stride(),
imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString());
// XXX not sure if this is the right way to write to a file
nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
rv = file->InitWithPath(name);
NS_ENSURE_SUCCESS(rv, rv);
uint64_t length64;
rv = encoder->Available(&length64);
NS_ENSURE_SUCCESS(rv, rv);
if (length64 > UINT32_MAX)
return NS_ERROR_FILE_TOO_BIG;
uint32_t length = (uint32_t)length64;
nsCOMPtr<nsIOutputStream> outputStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
outputStream, length);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t numWritten;
rv = bufferedOutputStream->WriteFrom(encoder, length, &numWritten);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
#endif
// After an incremental reflow, we verify the correctness by doing a
// full reflow into a fresh frame tree.
bool
@ -9668,7 +9607,7 @@ PresShell::VerifyIncrementalReflow()
root2->List(stdout, 0);
}
#ifdef DEBUG_Eli
#if 0
// Sample code for dumping page to png
// XXX Needs to be made more flexible
if (!ok) {
@ -9677,12 +9616,12 @@ PresShell::VerifyIncrementalReflow()
stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
stra.AppendInt(num);
stra.AppendLiteral(".png");
DumpToPNG(sh, stra);
gfxUtils::WriteAsPNG(sh, stra);
nsString strb;
strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
strb.AppendInt(num);
strb.AppendLiteral(".png");
DumpToPNG(this, strb);
gfxUtils::WriteAsPNG(sh, strb);
++num;
}
#endif

View File

@ -7,6 +7,7 @@
#include "WinUtils.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "nsWindow.h"
#include "nsWindowDefs.h"
#include "KeyboardLayout.h"
@ -851,65 +852,48 @@ NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run()
{
NS_PRECONDITION(!NS_IsMainThread(), "Should not be called on the main thread.");
nsCOMPtr<nsIInputStream> iconStream;
nsRefPtr<imgIEncoder> encoder =
do_CreateInstance("@mozilla.org/image/encoder;2?"
"type=image/vnd.microsoft.icon");
NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE);
nsresult rv = encoder->InitFromData(mBuffer, mBufferLength,
mWidth, mHeight,
mStride,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
EmptyString());
NS_ENSURE_SUCCESS(rv, rv);
CallQueryInterface(encoder.get(), getter_AddRefs(iconStream));
if (!iconStream) {
return NS_ERROR_FAILURE;
RefPtr<DrawTarget> dt =
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
RefPtr<SourceSurface> surface =
dt->CreateSourceSurfaceFromData(mBuffer, IntSize(mWidth, mHeight), mStride,
SurfaceFormat::B8G8R8A8);
FILE* file = fopen(NS_ConvertUTF16toUTF8(mIconPath).get(), "wb");
if (!file) {
// Maybe the directory doesn't exist; try creating it, then fopen again.
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
if (comFile) {
//NS_ConvertUTF8toUTF16 utf16path(mIconPath);
rv = comFile->InitWithPath(mIconPath);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFile> dirPath;
comFile->GetParent(getter_AddRefs(dirPath));
if (dirPath) {
rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
file = fopen(NS_ConvertUTF16toUTF8(mIconPath).get(), "wb");
if (!file) {
rv = NS_ERROR_FAILURE;
}
}
}
}
}
if (!file) {
return rv;
}
}
nsresult rv =
gfxUtils::EncodeSourceSurface(surface,
NS_LITERAL_CSTRING("image/vnd.microsoft.icon"),
EmptyString(),
gfxUtils::eBinaryEncode,
file);
fclose(file);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> icoFile
= do_CreateInstance("@mozilla.org/file/local;1");
NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE);
rv = icoFile->InitWithPath(mIconPath);
// Try to create the directory if it's not there yet
nsCOMPtr<nsIFile> dirPath;
icoFile->GetParent(getter_AddRefs(dirPath));
rv = (dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777));
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
return rv;
}
// Setup the output stream for the ICO file on disk
nsCOMPtr<nsIOutputStream> outputStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), icoFile);
NS_ENSURE_SUCCESS(rv, rv);
// Obtain the ICO buffer size from the re-encoded ICO stream
uint64_t bufSize64;
rv = iconStream->Available(&bufSize64);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(bufSize64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
uint32_t bufSize = (uint32_t)bufSize64;
// Setup a buffered output stream from the stream object
// so that we can simply use WriteFrom with the stream object
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
outputStream, bufSize);
NS_ENSURE_SUCCESS(rv, rv);
// Write out the icon stream to disk and make sure we wrote everything
uint32_t wrote;
rv = bufferedOutputStream->WriteFrom(iconStream, bufSize, &wrote);
NS_ASSERTION(bufSize == wrote,
"Icon wrote size should be equal to requested write size");
// Cleanup
bufferedOutputStream->Close();
outputStream->Close();
if (mURLShortcut) {
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0);
}