зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changesets 5fa68d64f749, 103b40656e24, b1ff3efb43cf, 096455736704, and 4afd6fa3da19 (bug 817700) for mochitest-1 failures.
CLOSED TREE
This commit is contained in:
Родитель
2fa7d6488c
Коммит
d2f8285bf4
|
@ -14,8 +14,8 @@
|
|||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
|
||||
{ 0x9a6a5bdf, 0x1261, 0x4057, \
|
||||
{ 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
|
||||
{ 0x8b8da863, 0xd151, 0x4014, \
|
||||
{ 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } }
|
||||
|
||||
class gfxContext;
|
||||
class gfxASurface;
|
||||
|
@ -71,9 +71,6 @@ public:
|
|||
GraphicsFilter aFilter,
|
||||
uint32_t aFlags = RenderFlagPremultAlpha) = 0;
|
||||
|
||||
// Creates an image buffer. Returns null on failure.
|
||||
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
|
||||
|
||||
// Gives you a stream containing the image represented by this context.
|
||||
// The format is given in aMimeTime, for example "image/png".
|
||||
//
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "ImageEncoder.h"
|
||||
#include "imgIEncoder.h"
|
||||
|
||||
#include "gfxContext.h"
|
||||
#include "gfxASurface.h"
|
||||
|
@ -1050,71 +1050,71 @@ CanvasRenderingContext2D::Render(gfxContext *ctx, GraphicsFilter aFilter, uint32
|
|||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
|
||||
int32_t* aFormat)
|
||||
NS_IMETHODIMP
|
||||
CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
|
||||
const PRUnichar *aEncoderOptions,
|
||||
nsIInputStream **aStream)
|
||||
{
|
||||
*aImageBuffer = nullptr;
|
||||
*aFormat = 0;
|
||||
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
nsresult rv = GetThebesSurface(getter_AddRefs(surface));
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
EnsureTarget();
|
||||
if (!IsTargetValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
|
||||
if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
|
||||
static const fallible_t fallible = fallible_t();
|
||||
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
|
||||
nsAutoArrayPtr<char> conid(new (fallible) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
|
||||
|
||||
if (!conid) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
strcpy(conid, encoderPrefix);
|
||||
strcat(conid, aMimeType);
|
||||
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
|
||||
if (!encoder) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoArrayPtr<uint8_t> imageBuffer(new (fallible) uint8_t[mWidth * mHeight * 4]);
|
||||
if (!imageBuffer) {
|
||||
return;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> imgsurf =
|
||||
new gfxImageSurface(imageBuffer,
|
||||
new gfxImageSurface(imageBuffer.get(),
|
||||
gfxIntSize(mWidth, mHeight),
|
||||
mWidth * 4,
|
||||
gfxImageFormatARGB32);
|
||||
|
||||
if (!imgsurf || imgsurf->CairoStatus()) {
|
||||
delete[] imageBuffer;
|
||||
return;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
|
||||
|
||||
if (!ctx || ctx->HasError()) {
|
||||
delete[] imageBuffer;
|
||||
return;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx->SetSource(surface, gfxPoint(0, 0));
|
||||
ctx->Paint();
|
||||
|
||||
*aImageBuffer = imageBuffer;
|
||||
*aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
}
|
||||
rv = encoder->InitFromData(imageBuffer.get(),
|
||||
mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
|
||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
nsDependentString(aEncoderOptions));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_IMETHODIMP
|
||||
CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
|
||||
const PRUnichar *aEncoderOptions,
|
||||
nsIInputStream **aStream)
|
||||
{
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(&imageBuffer, &format);
|
||||
if (!imageBuffer) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += aMimeType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
|
||||
if (!encoder) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
|
||||
encoder, aEncoderOptions, aStream);
|
||||
return CallQueryInterface(encoder, aStream);
|
||||
}
|
||||
|
||||
SurfaceFormat
|
||||
|
@ -3753,9 +3753,6 @@ NS_IMETHODIMP
|
|||
CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
|
||||
{
|
||||
EnsureTarget();
|
||||
if (!IsTargetValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxASurface> thebesSurface =
|
||||
gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "imgIEncoder.h"
|
||||
|
||||
class nsXULElement;
|
||||
|
||||
|
@ -455,8 +454,6 @@ public:
|
|||
|
||||
friend class CanvasRenderingContext2DUserData;
|
||||
|
||||
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
|
||||
|
||||
protected:
|
||||
nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
|
||||
uint32_t aWidth, uint32_t aHeight,
|
||||
|
|
|
@ -1,299 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; 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/. */
|
||||
|
||||
#include "gfxImageSurface.h"
|
||||
#include "ImageEncoder.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class EncodingCompleteEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
EncodingCompleteEvent(nsIScriptContext* aScriptContext,
|
||||
nsIThread* aEncoderThread,
|
||||
FileCallback& aCallback)
|
||||
: mImgSize(0)
|
||||
, mType()
|
||||
, mImgData(nullptr)
|
||||
, mScriptContext(aScriptContext)
|
||||
, mEncoderThread(aEncoderThread)
|
||||
, mCallback(&aCallback)
|
||||
{}
|
||||
virtual ~EncodingCompleteEvent() {}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsRefPtr<nsDOMMemoryFile> blob =
|
||||
new nsDOMMemoryFile(mImgData, mImgSize, mType);
|
||||
|
||||
if (mScriptContext) {
|
||||
JSContext* jsContext = mScriptContext->GetNativeContext();
|
||||
if (jsContext) {
|
||||
JS_updateMallocCounter(jsContext, mImgSize);
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::ErrorResult rv;
|
||||
mCallback->Call(blob, rv);
|
||||
NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
|
||||
|
||||
mEncoderThread->Shutdown();
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
|
||||
void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
|
||||
{
|
||||
mImgData = aImgData;
|
||||
mImgSize = aImgSize;
|
||||
mType = aType;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t mImgSize;
|
||||
nsAutoString mType;
|
||||
void* mImgData;
|
||||
nsCOMPtr<nsIScriptContext> mScriptContext;
|
||||
nsCOMPtr<nsIThread> mEncoderThread;
|
||||
nsRefPtr<FileCallback> mCallback;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(EncodingCompleteEvent, nsIRunnable);
|
||||
|
||||
class EncodingRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
EncodingRunnable(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
imgIEncoder* aEncoder,
|
||||
EncodingCompleteEvent* aEncodingCompleteEvent,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
bool aUsingCustomOptions)
|
||||
: mType(aType)
|
||||
, mOptions(aOptions)
|
||||
, mImageBuffer(aImageBuffer)
|
||||
, mEncoder(aEncoder)
|
||||
, mEncodingCompleteEvent(aEncodingCompleteEvent)
|
||||
, mFormat(aFormat)
|
||||
, mSize(aSize)
|
||||
, mUsingCustomOptions(aUsingCustomOptions)
|
||||
{}
|
||||
virtual ~EncodingRunnable() {}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsresult rv = ImageEncoder::ExtractDataInternal(mType,
|
||||
mOptions,
|
||||
mImageBuffer,
|
||||
mFormat,
|
||||
mSize,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
|
||||
// If there are unrecognized custom parse options, we should fall back to
|
||||
// the default values for the encoder without any options at all.
|
||||
if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
|
||||
rv = ImageEncoder::ExtractDataInternal(mType,
|
||||
EmptyString(),
|
||||
mImageBuffer,
|
||||
mFormat,
|
||||
mSize,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint64_t imgSize;
|
||||
rv = stream->Available(&imgSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
|
||||
|
||||
void* imgData = nullptr;
|
||||
rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
|
||||
rv = NS_DispatchToMainThread(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoString mType;
|
||||
nsAutoString mOptions;
|
||||
nsAutoArrayPtr<uint8_t> mImageBuffer;
|
||||
nsCOMPtr<imgIEncoder> mEncoder;
|
||||
nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
|
||||
int32_t mFormat;
|
||||
const nsIntSize mSize;
|
||||
bool mUsingCustomOptions;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(EncodingRunnable, nsIRunnable)
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
ImageEncoder::ExtractData(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIInputStream** aStream)
|
||||
{
|
||||
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
|
||||
if (!encoder) {
|
||||
return NS_IMAGELIB_ERROR_NO_ENCODER;
|
||||
}
|
||||
|
||||
return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, aContext,
|
||||
aStream, encoder);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
ImageEncoder::ExtractDataAsync(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
bool aUsingCustomOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIScriptContext* aScriptContext,
|
||||
FileCallback& aCallback)
|
||||
{
|
||||
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
|
||||
if (!encoder) {
|
||||
return NS_IMAGELIB_ERROR_NO_ENCODER;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> encoderThread;
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRefPtr<EncodingCompleteEvent> completeEvent =
|
||||
new EncodingCompleteEvent(aScriptContext, encoderThread, aCallback);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
|
||||
aOptions,
|
||||
aImageBuffer,
|
||||
encoder,
|
||||
completeEvent,
|
||||
aFormat,
|
||||
aSize,
|
||||
aUsingCustomOptions);
|
||||
return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
/*static*/ nsresult
|
||||
ImageEncoder::GetInputStream(int32_t aWidth,
|
||||
int32_t aHeight,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
imgIEncoder* aEncoder,
|
||||
const PRUnichar* aEncoderOptions,
|
||||
nsIInputStream** aStream)
|
||||
{
|
||||
nsresult rv =
|
||||
aEncoder->InitFromData(aImageBuffer,
|
||||
aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
|
||||
aFormat,
|
||||
nsDependentString(aEncoderOptions));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return CallQueryInterface(aEncoder, aStream);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
ImageEncoder::ExtractDataInternal(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> imgStream;
|
||||
|
||||
// get image bytes
|
||||
nsresult rv;
|
||||
if (aImageBuffer) {
|
||||
rv = ImageEncoder::GetInputStream(
|
||||
aSize.width,
|
||||
aSize.height,
|
||||
aImageBuffer,
|
||||
aFormat,
|
||||
aEncoder,
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else if (aContext) {
|
||||
NS_ConvertUTF16toUTF8 encoderType(aType);
|
||||
rv = aContext->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else {
|
||||
// no context, so we have to encode an empty image
|
||||
// note that if we didn't have a current context, the spec says we're
|
||||
// supposed to just return transparent black pixels of the canvas
|
||||
// dimensions.
|
||||
nsRefPtr<gfxImageSurface> emptyCanvas =
|
||||
new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
|
||||
gfxImageFormatARGB32);
|
||||
if (emptyCanvas->CairoStatus()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
rv = aEncoder->InitFromData(emptyCanvas->Data(),
|
||||
aSize.width * aSize.height * 4,
|
||||
aSize.width,
|
||||
aSize.height,
|
||||
aSize.width * 4,
|
||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
aOptions);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
imgStream = do_QueryInterface(aEncoder);
|
||||
}
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
imgStream.forget(aStream);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<imgIEncoder>
|
||||
ImageEncoder::GetImageEncoder(nsAString& aType)
|
||||
{
|
||||
// Get an image encoder for the media type.
|
||||
nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
|
||||
NS_ConvertUTF16toUTF8 encoderType(aType);
|
||||
encoderCID += encoderType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
|
||||
|
||||
if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
|
||||
// Unable to create an encoder instance of the specified type. Falling back
|
||||
// to PNG.
|
||||
aType.AssignLiteral("image/png");
|
||||
nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
|
||||
encoder = do_CreateInstance(PNGEncoderCID.get());
|
||||
}
|
||||
|
||||
return encoder.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -1,92 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; 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 ImageEncoder_h
|
||||
#define ImageEncoder_h
|
||||
|
||||
#include "imgIEncoder.h"
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsError.h"
|
||||
#include "mozilla/dom/HTMLCanvasElementBinding.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsSize.h"
|
||||
|
||||
class nsICanvasRenderingContextInternal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class EncodingRunnable;
|
||||
|
||||
class ImageEncoder
|
||||
{
|
||||
public:
|
||||
// Extracts data synchronously and gives you a stream containing the image
|
||||
// represented by aContext. aType may change to "image/png" if we had to fall
|
||||
// back to a PNG encoder. A return value of NS_OK implies successful data
|
||||
// extraction. If there are any unrecognized custom parse options in
|
||||
// aOptions, NS_ERROR_INVALID_ARG will be returned. When encountering this
|
||||
// error it is usual to call this function again without any options at all.
|
||||
static nsresult ExtractData(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIInputStream** aStream);
|
||||
|
||||
// Extracts data asynchronously. aType may change to "image/png" if we had to
|
||||
// fall back to a PNG encoder. aOptions are the options to be passed to the
|
||||
// encoder and aUsingCustomOptions specifies whether custom parse options were
|
||||
// used (i.e. by using -moz-parse-options). If there are any unrecognized
|
||||
// custom parse options, we fall back to the default values for the encoder
|
||||
// without any options at all. A return value of NS_OK only implies
|
||||
// successful dispatching of the extraction step to the encoding thread.
|
||||
static nsresult ExtractDataAsync(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
bool aUsingCustomOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIScriptContext* aScriptContext,
|
||||
FileCallback& aCallback);
|
||||
|
||||
// Gives you a stream containing the image represented by aImageBuffer.
|
||||
// The format is given in aFormat, for example
|
||||
// imgIEncoder::INPUT_FORMAT_HOSTARGB.
|
||||
static nsresult GetInputStream(int32_t aWidth,
|
||||
int32_t aHeight,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
imgIEncoder* aEncoder,
|
||||
const PRUnichar* aEncoderOptions,
|
||||
nsIInputStream** aStream);
|
||||
|
||||
private:
|
||||
// When called asynchronously, aContext is null.
|
||||
static nsresult
|
||||
ExtractDataInternal(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder);
|
||||
|
||||
// Creates and returns an encoder instance of the type specified in aType.
|
||||
// aType may change to "image/png" if no instance of the original type could
|
||||
// be created and we had to fall back to a PNG encoder. A return value of
|
||||
// NULL should be interpreted as NS_IMAGELIB_ERROR_NO_ENCODER and aType is
|
||||
// undefined in this case.
|
||||
static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
|
||||
|
||||
friend class EncodingRunnable;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ImageEncoder_h
|
|
@ -22,6 +22,5 @@ INCLUDES += \
|
|||
-I$(srcdir)/../../html/content/src \
|
||||
-I$(srcdir)/../../../js/xpconnect/src \
|
||||
-I$(srcdir)/../../../dom/base \
|
||||
-I$(srcdir)/../../../image/src \
|
||||
-I$(topsrcdir)/content/xul/content/src \
|
||||
$(NULL)
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
#include "nsIVariant.h"
|
||||
|
||||
#include "ImageEncoder.h"
|
||||
#include "imgIEncoder.h"
|
||||
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPattern.h"
|
||||
|
@ -735,54 +735,6 @@ void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
|
||||
{
|
||||
*aImageBuffer = nullptr;
|
||||
*aFormat = 0;
|
||||
|
||||
nsRefPtr<gfxImageSurface> imgsurf =
|
||||
new gfxImageSurface(gfxIntSize(mWidth, mHeight),
|
||||
gfxImageFormatARGB32);
|
||||
|
||||
if (!imgsurf || imgsurf->CairoStatus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
|
||||
if (!ctx || ctx->HasError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use Render() to make sure that appropriate y-flip gets applied
|
||||
uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
|
||||
nsresult rv = Render(ctx, GraphicsFilter::FILTER_NEAREST, flags);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
if (!mOptions.premultipliedAlpha) {
|
||||
// We need to convert to INPUT_FORMAT_RGBA, otherwise
|
||||
// we are automatically considered premult, and unpremult'd.
|
||||
// Yes, it is THAT silly.
|
||||
// Except for different lossy conversions by color,
|
||||
// we could probably just change the label, and not change the data.
|
||||
gfxUtils::ConvertBGRAtoRGBA(imgsurf);
|
||||
format = imgIEncoder::INPUT_FORMAT_RGBA;
|
||||
}
|
||||
|
||||
static const fallible_t fallible = fallible_t();
|
||||
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
|
||||
if (!imageBuffer) {
|
||||
return;
|
||||
}
|
||||
memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
|
||||
|
||||
*aImageBuffer = imageBuffer;
|
||||
*aFormat = format;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::GetInputStream(const char* aMimeType,
|
||||
const PRUnichar* aEncoderOptions,
|
||||
|
@ -792,22 +744,48 @@ WebGLContext::GetInputStream(const char* aMimeType,
|
|||
if (!gl)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(&imageBuffer, &format);
|
||||
if (!imageBuffer) {
|
||||
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
|
||||
gfxImageFormatARGB32);
|
||||
if (surf->CairoStatus() != 0)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
|
||||
// Use Render() to make sure that appropriate y-flip gets applied
|
||||
uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
|
||||
nsresult rv = Render(tmpcx, GraphicsFilter::FILTER_NEAREST, flags);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
|
||||
nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
|
||||
|
||||
strcpy(conid, encoderPrefix);
|
||||
strcat(conid, aMimeType);
|
||||
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
|
||||
if (!encoder)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
if (!mOptions.premultipliedAlpha) {
|
||||
// We need to convert to INPUT_FORMAT_RGBA, otherwise
|
||||
// we are automatically considered premult, and unpremult'd.
|
||||
// Yes, it is THAT silly.
|
||||
// Except for different lossy conversions by color,
|
||||
// we could probably just change the label, and not change the data.
|
||||
gfxUtils::ConvertBGRAtoRGBA(surf);
|
||||
format = imgIEncoder::INPUT_FORMAT_RGBA;
|
||||
}
|
||||
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += aMimeType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
|
||||
if (!encoder) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
rv = encoder->InitFromData(surf->Data(),
|
||||
mWidth * mHeight * 4,
|
||||
mWidth, mHeight,
|
||||
surf->Stride(),
|
||||
format,
|
||||
nsDependentString(aEncoderOptions));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
|
||||
encoder, aEncoderOptions, aStream);
|
||||
return CallQueryInterface(encoder, aStream);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -169,7 +169,6 @@ public:
|
|||
NS_IMETHOD Render(gfxContext *ctx,
|
||||
GraphicsFilter f,
|
||||
uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
|
||||
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
|
||||
NS_IMETHOD GetInputStream(const char* aMimeType,
|
||||
const PRUnichar* aEncoderOptions,
|
||||
nsIInputStream **aStream) MOZ_OVERRIDE;
|
||||
|
|
|
@ -22,7 +22,6 @@ CPP_SOURCES += [
|
|||
'DocumentRendererChild.cpp',
|
||||
'DocumentRendererParent.cpp',
|
||||
'ImageData.cpp',
|
||||
'ImageEncoder.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WEBGL']:
|
||||
|
|
|
@ -7,43 +7,42 @@
|
|||
<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<script>
|
||||
|
||||
function compareAsync(file, canvas, type, callback)
|
||||
var gCompares = 0;
|
||||
|
||||
function compareAsync(file, canvas, type)
|
||||
{
|
||||
++gCompares;
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload =
|
||||
function(e) {
|
||||
is(e.target.result, canvas.toDataURL(type),
|
||||
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
|
||||
callback(canvas);
|
||||
if (--gCompares == 0) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function test1(canvas)
|
||||
{
|
||||
var pngfile = canvas.mozGetAsFile("foo.png");
|
||||
is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
|
||||
compareAsync(pngfile, canvas, "image/png", test2);
|
||||
is(pngfile.name, "foo.png", "File name should be what we passed in");
|
||||
}
|
||||
|
||||
function test2(canvas)
|
||||
{
|
||||
var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
|
||||
is(jpegfile.type, "image/jpeg",
|
||||
"When a valid type is specified that should be returned");
|
||||
compareAsync(jpegfile, canvas, "image/jpeg", SimpleTest.finish);
|
||||
is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function () {
|
||||
|
||||
var canvas = document.getElementById('c');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
|
||||
|
||||
test1(canvas);
|
||||
var pngfile = canvas.mozGetAsFile("foo.png");
|
||||
is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
|
||||
compareAsync(pngfile, canvas, "image/png");
|
||||
is(pngfile.name, "foo.png", "File name should be what we passed in");
|
||||
|
||||
var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
|
||||
is(jpegfile.type, "image/jpeg",
|
||||
"When a valid type is specified that should be returned");
|
||||
compareAsync(jpegfile, canvas, "image/jpeg");
|
||||
is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE HTML>
|
||||
<title>Canvas test: toBlob</title>
|
||||
<title>Canvas test: mozGetAsFile</title>
|
||||
<script src="/MochiKit/MochiKit.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
|
@ -7,40 +7,34 @@
|
|||
<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<script>
|
||||
|
||||
function BlobListener(type, canvas, callback, file)
|
||||
var gCompares = 2;
|
||||
|
||||
function BlobListener(type, canvas, file)
|
||||
{
|
||||
is(file.type, type,
|
||||
"When a valid type is specified that should be returned");
|
||||
var reader = new FileReader();
|
||||
reader.onload =
|
||||
reader.onload =
|
||||
function(e) {
|
||||
is(e.target.result, canvas.toDataURL(type),
|
||||
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
|
||||
callback(canvas);
|
||||
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
|
||||
if (--gCompares == 0) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function test1(canvas)
|
||||
{
|
||||
canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas, test2));
|
||||
}
|
||||
|
||||
function test2(canvas)
|
||||
{
|
||||
canvas.toBlob(
|
||||
BlobListener.bind(undefined, "image/jpeg", canvas, SimpleTest.finish),
|
||||
"image/jpeg");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function () {
|
||||
|
||||
var canvas = document.getElementById('c');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
|
||||
|
||||
test1(canvas);
|
||||
canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas));
|
||||
canvas.toBlob(BlobListener.bind(undefined, "image/jpeg", canvas), "image/jpeg");
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -227,9 +227,10 @@ protected:
|
|||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aParams,
|
||||
bool* usingCustomParseOptions);
|
||||
nsresult ExtractData(nsAString& aType,
|
||||
nsresult ExtractData(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
nsIInputStream** aStream);
|
||||
nsIInputStream** aStream,
|
||||
bool& aFellBackToPNG);
|
||||
nsresult ToDataURLImpl(JSContext* aCx,
|
||||
const nsAString& aMimeType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
|
||||
#include "ImageEncoder.h"
|
||||
#include "Layers.h"
|
||||
#include "imgIEncoder.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "Layers.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
|
@ -23,7 +23,6 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsFrameManager.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsITimer.h"
|
||||
|
@ -47,6 +46,29 @@ namespace {
|
|||
typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
|
||||
HTMLImageOrCanvasOrVideoElement;
|
||||
|
||||
class ToBlobRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ToBlobRunnable(mozilla::dom::FileCallback& aCallback,
|
||||
nsIDOMBlob* aBlob)
|
||||
: mCallback(&aCallback),
|
||||
mBlob(aBlob)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
mozilla::ErrorResult rv;
|
||||
mCallback->Call(mBlob, rv);
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
private:
|
||||
nsRefPtr<mozilla::dom::FileCallback> mCallback;
|
||||
nsCOMPtr<nsIDOMBlob> mBlob;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -347,10 +369,10 @@ HTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
|
|||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsresult rv;
|
||||
bool fellBackToPNG = false;
|
||||
nsCOMPtr<nsIInputStream> inputData;
|
||||
|
||||
nsAutoString type(aType);
|
||||
rv = ExtractData(type, EmptyString(), getter_AddRefs(inputData));
|
||||
rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
|
||||
|
@ -382,15 +404,68 @@ HTMLCanvasElement::GetMozPrintCallback() const
|
|||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::ExtractData(nsAString& aType,
|
||||
HTMLCanvasElement::ExtractData(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
nsIInputStream** aStream)
|
||||
nsIInputStream** aStream,
|
||||
bool& aFellBackToPNG)
|
||||
{
|
||||
return ImageEncoder::ExtractData(aType,
|
||||
aOptions,
|
||||
GetSize(),
|
||||
mCurrentContext,
|
||||
aStream);
|
||||
// note that if we don't have a current context, the spec says we're
|
||||
// supposed to just return transparent black pixels of the canvas
|
||||
// dimensions.
|
||||
nsRefPtr<gfxImageSurface> emptyCanvas;
|
||||
nsIntSize size = GetWidthHeight();
|
||||
if (!mCurrentContext) {
|
||||
emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxImageFormatARGB32);
|
||||
if (emptyCanvas->CairoStatus()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// get image bytes
|
||||
nsCOMPtr<nsIInputStream> imgStream;
|
||||
NS_ConvertUTF16toUTF8 encoderType(aType);
|
||||
|
||||
try_again:
|
||||
if (mCurrentContext) {
|
||||
rv = mCurrentContext->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else {
|
||||
// no context, so we have to encode the empty image we created above
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += encoderType;
|
||||
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get(), &rv);
|
||||
if (NS_SUCCEEDED(rv) && encoder) {
|
||||
rv = encoder->InitFromData(emptyCanvas->Data(),
|
||||
size.width * size.height * 4,
|
||||
size.width,
|
||||
size.height,
|
||||
size.width * 4,
|
||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
aOptions);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
imgStream = do_QueryInterface(encoder);
|
||||
}
|
||||
} else {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) && !aFellBackToPNG) {
|
||||
// Try image/png instead.
|
||||
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
||||
aFellBackToPNG = true;
|
||||
encoderType.AssignLiteral("image/png");
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
imgStream.forget(aStream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -441,6 +516,8 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
|||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aDataURL)
|
||||
{
|
||||
bool fallbackToPNG = false;
|
||||
|
||||
nsIntSize size = GetWidthHeight();
|
||||
if (size.height == 0 || size.width == 0) {
|
||||
aDataURL = NS_LITERAL_STRING("data:,");
|
||||
|
@ -461,18 +538,23 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = ExtractData(type, params, getter_AddRefs(stream));
|
||||
rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
|
||||
|
||||
// If there are unrecognized custom parse options, we should fall back to
|
||||
// the default values for the encoder without any options at all.
|
||||
if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
|
||||
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
|
||||
fallbackToPNG = false;
|
||||
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// build data URL string
|
||||
aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
|
||||
if (fallbackToPNG)
|
||||
aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
|
||||
else
|
||||
aDataURL = NS_LITERAL_STRING("data:") + type +
|
||||
NS_LITERAL_STRING(";base64,");
|
||||
|
||||
uint64_t count;
|
||||
rv = stream->Available(&count);
|
||||
|
@ -482,6 +564,7 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
|||
return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
|
||||
}
|
||||
|
||||
// XXXkhuey the encoding should be off the main thread, but we're lazy.
|
||||
void
|
||||
HTMLCanvasElement::ToBlob(JSContext* aCx,
|
||||
FileCallback& aCallback,
|
||||
|
@ -520,24 +603,52 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
|
|||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIScriptContext> scriptContext =
|
||||
GetScriptContextFromJSContext(nsContentUtils::GetCurrentJSContext());
|
||||
bool fallbackToPNG = false;
|
||||
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
aRv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
|
||||
// If there are unrecognized custom parse options, we should fall back to
|
||||
// the default values for the encoder without any options at all.
|
||||
if (aRv.ErrorCode() == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
|
||||
fallbackToPNG = false;
|
||||
aRv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
|
||||
}
|
||||
|
||||
aRv = ImageEncoder::ExtractDataAsync(type,
|
||||
params,
|
||||
usingCustomParseOptions,
|
||||
imageBuffer,
|
||||
format,
|
||||
GetSize(),
|
||||
mCurrentContext,
|
||||
scriptContext,
|
||||
aCallback);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fallbackToPNG) {
|
||||
type.AssignLiteral("image/png");
|
||||
}
|
||||
|
||||
uint64_t imgSize;
|
||||
aRv = stream->Available(&imgSize);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
if (imgSize > UINT32_MAX) {
|
||||
aRv.Throw(NS_ERROR_FILE_TOO_BIG);
|
||||
return;
|
||||
}
|
||||
|
||||
void* imgData = nullptr;
|
||||
aRv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The DOMFile takes ownership of the buffer
|
||||
nsRefPtr<nsDOMMemoryFile> blob =
|
||||
new nsDOMMemoryFile(imgData, imgSize, type);
|
||||
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
if (cx) {
|
||||
JS_updateMallocCounter(cx, imgSize);
|
||||
}
|
||||
|
||||
nsRefPtr<ToBlobRunnable> runnable = new ToBlobRunnable(aCallback, blob);
|
||||
aRv = NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMFile>
|
||||
|
@ -571,11 +682,18 @@ HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
|
|||
const nsAString& aType,
|
||||
nsIDOMFile** aResult)
|
||||
{
|
||||
bool fallbackToPNG = false;
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsAutoString type(aType);
|
||||
nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
|
||||
nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
|
||||
fallbackToPNG);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString type(aType);
|
||||
if (fallbackToPNG) {
|
||||
type.AssignLiteral("image/png");
|
||||
}
|
||||
|
||||
uint64_t imgSize;
|
||||
rv = stream->Available(&imgSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
|
|
@ -22,7 +22,6 @@ INCLUDES += \
|
|||
-I$(srcdir)/../../../../editor/libeditor/text \
|
||||
-I$(srcdir)/../../../../editor/txmgr/src \
|
||||
-I$(srcdir)/../../../../netwerk/base/src \
|
||||
-I$(srcdir)/../../../../content/canvas/src \
|
||||
-I$(srcdir) \
|
||||
-I$(topsrcdir)/xpcom/ds \
|
||||
-I$(topsrcdir)/content/media/ \
|
||||
|
|
|
@ -54,7 +54,7 @@ function takeReferenceSnapshot() {
|
|||
"reference div should disappear when it becomes display:none");
|
||||
}
|
||||
|
||||
function myOnStopFrame() {
|
||||
function myOnStopFrame(aRequest) {
|
||||
gOnStopFrameCounter++;
|
||||
ok(true, "myOnStopFrame called");
|
||||
let currentSnapshot = snapshotWindow(window, false);
|
||||
|
@ -64,8 +64,7 @@ function myOnStopFrame() {
|
|||
"at call #" + gOnStopFrameCounter + " to onStopFrame");
|
||||
cleanUpAndFinish();
|
||||
}
|
||||
else
|
||||
setTimeout(myOnStopFrame, 1);
|
||||
setTimeout(function() { myOnStopFrame(0, 0); }, 1000);
|
||||
}
|
||||
|
||||
function failTest() {
|
||||
|
@ -81,6 +80,8 @@ function cleanUpAndFinish() {
|
|||
if (gIsTestFinished) {
|
||||
return;
|
||||
}
|
||||
let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
|
||||
imgLoadingContent.removeObserver(gMyDecoderObserver);
|
||||
SimpleTest.finish();
|
||||
gIsTestFinished = true;
|
||||
}
|
||||
|
@ -88,12 +89,19 @@ function cleanUpAndFinish() {
|
|||
function main() {
|
||||
takeReferenceSnapshot();
|
||||
|
||||
// Create, customize & attach decoder observer
|
||||
observer = new ImageDecoderObserverStub();
|
||||
observer.frameComplete = myOnStopFrame;
|
||||
gMyDecoderObserver =
|
||||
Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(observer);
|
||||
let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
|
||||
imgLoadingContent.addObserver(gMyDecoderObserver);
|
||||
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
|
||||
setTimeout(myOnStopFrame, 1);
|
||||
|
||||
// kick off image-loading! myOnStopFrame handles the rest.
|
||||
gImg.setAttribute("src", "lime-anim-100x100.svg");
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче