зеркало из https://github.com/mozilla/gecko-dev.git
Bug 567426: Make image encoders implement nsIAsyncInputStream. r=joedrew sr=bz
This commit is contained in:
Родитель
4256887d4f
Коммит
2ac461c8a4
|
@ -45,10 +45,7 @@
|
|||
#include <setjmp.h>
|
||||
#include "jerror.h"
|
||||
|
||||
// Input streams that do not implement nsIAsyncInputStream should be threadsafe
|
||||
// so that they may be used with nsIInputStreamPump and nsIInputStreamChannel,
|
||||
// which read such a stream on a background thread.
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsJPEGEncoder, imgIEncoder, nsIInputStream)
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS3(nsJPEGEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
|
||||
|
||||
// used to pass error info through the JPEG library
|
||||
struct encoder_error_mgr {
|
||||
|
@ -57,7 +54,10 @@ struct encoder_error_mgr {
|
|||
};
|
||||
|
||||
nsJPEGEncoder::nsJPEGEncoder() : mImageBuffer(nsnull), mImageBufferSize(0),
|
||||
mImageBufferUsed(0), mImageBufferReadPoint(0)
|
||||
mImageBufferUsed(0), mImageBufferReadPoint(0),
|
||||
mFinished(PR_FALSE), mCallback(nsnull),
|
||||
mCallbackTarget(nsnull), mNotifyThreshold(0),
|
||||
mMonitor("JPEG Encoder Monitor")
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -197,6 +197,9 @@ NS_IMETHODIMP nsJPEGEncoder::InitFromData(const PRUint8* aData,
|
|||
jpeg_finish_compress(&cinfo);
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
mFinished = PR_TRUE;
|
||||
NotifyListener();
|
||||
|
||||
// if output callback can't get enough memory, it will free our buffer
|
||||
if (!mImageBuffer)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -263,10 +266,13 @@ NS_IMETHODIMP nsJPEGEncoder::Read(char * aBuf, PRUint32 aCount,
|
|||
/* [noscript] unsigned long readSegments (in nsWriteSegmentFun aWriter, in voidPtr aClosure, in unsigned long aCount); */
|
||||
NS_IMETHODIMP nsJPEGEncoder::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, PRUint32 aCount, PRUint32 *_retval)
|
||||
{
|
||||
// Avoid another thread reallocing the buffer underneath us
|
||||
mozilla::MonitorAutoEnter autoEnter(mMonitor);
|
||||
|
||||
PRUint32 maxCount = mImageBufferUsed - mImageBufferReadPoint;
|
||||
if (maxCount == 0) {
|
||||
*_retval = 0;
|
||||
return NS_OK;
|
||||
return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
|
||||
if (aCount > maxCount)
|
||||
|
@ -286,10 +292,42 @@ NS_IMETHODIMP nsJPEGEncoder::ReadSegments(nsWriteSegmentFun aWriter, void *aClos
|
|||
/* boolean isNonBlocking (); */
|
||||
NS_IMETHODIMP nsJPEGEncoder::IsNonBlocking(PRBool *_retval)
|
||||
{
|
||||
*_retval = PR_FALSE; // We don't implement nsIAsyncInputStream
|
||||
*_retval = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsJPEGEncoder::AsyncWait(nsIInputStreamCallback *aCallback,
|
||||
PRUint32 aFlags,
|
||||
PRUint32 aRequestedCount,
|
||||
nsIEventTarget *aTarget)
|
||||
{
|
||||
if (aFlags != 0)
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
if (mCallback || mCallbackTarget)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
mCallbackTarget = aTarget;
|
||||
// 0 means "any number of bytes except 0"
|
||||
mNotifyThreshold = aRequestedCount;
|
||||
if (!aRequestedCount)
|
||||
mNotifyThreshold = 1024; // 1 KB seems good. We don't want to notify incessantly
|
||||
|
||||
// We set the callback absolutely last, because NotifyListener uses it to
|
||||
// determine if someone needs to be notified. If we don't set it last,
|
||||
// NotifyListener might try to fire off a notification to a null target
|
||||
// which will generally cause non-threadsafe objects to be used off the main thread
|
||||
mCallback = aCallback;
|
||||
|
||||
// What we are being asked for may be present already
|
||||
NotifyListener();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsJPEGEncoder::CloseWithStatus(nsresult aStatus)
|
||||
{
|
||||
return Close();
|
||||
}
|
||||
|
||||
// nsJPEGEncoder::ConvertHostARGBRow
|
||||
//
|
||||
|
@ -374,6 +412,10 @@ nsJPEGEncoder::emptyOutputBuffer(jpeg_compress_struct* cinfo)
|
|||
nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data);
|
||||
NS_ASSERTION(that->mImageBuffer, "No buffer to empty!");
|
||||
|
||||
// When we're reallocing the buffer we need to take the lock to ensure
|
||||
// that nobody is trying to read from the buffer we are destroying
|
||||
mozilla::MonitorAutoEnter autoEnter(that->mMonitor);
|
||||
|
||||
that->mImageBufferUsed = that->mImageBufferSize;
|
||||
|
||||
// expand buffer, just double size each time
|
||||
|
@ -416,6 +458,7 @@ nsJPEGEncoder::termDestination(jpeg_compress_struct* cinfo)
|
|||
that->mImageBufferUsed = cinfo->dest->next_output_byte - that->mImageBuffer;
|
||||
NS_ASSERTION(that->mImageBufferUsed < that->mImageBufferSize,
|
||||
"JPEG library busted, got a bad image buffer size");
|
||||
that->NotifyListener();
|
||||
}
|
||||
|
||||
|
||||
|
@ -442,3 +485,35 @@ nsJPEGEncoder::errorExit(jpeg_common_struct* cinfo)
|
|||
// Return control to the setjmp point.
|
||||
longjmp(err->setjmp_buffer, error_code);
|
||||
}
|
||||
|
||||
void
|
||||
nsJPEGEncoder::NotifyListener()
|
||||
{
|
||||
// We might call this function on multiple threads (any threads that call
|
||||
// AsyncWait and any that do encoding) so we lock to avoid notifying the
|
||||
// listener twice about the same data (which generally leads to a truncated
|
||||
// image).
|
||||
mozilla::MonitorAutoEnter autoEnter(mMonitor);
|
||||
|
||||
if (mCallback &&
|
||||
(mImageBufferUsed - mImageBufferReadPoint >= mNotifyThreshold ||
|
||||
mFinished)) {
|
||||
nsCOMPtr<nsIInputStreamCallback> callback;
|
||||
if (mCallbackTarget) {
|
||||
NS_NewInputStreamReadyEvent(getter_AddRefs(callback),
|
||||
mCallback,
|
||||
mCallbackTarget);
|
||||
} else {
|
||||
callback = mCallback;
|
||||
}
|
||||
|
||||
NS_ASSERTION(callback, "Shouldn't fail to make the callback");
|
||||
// Null the callback first because OnInputStreamReady could reenter
|
||||
// AsyncWait
|
||||
mCallback = nsnull;
|
||||
mCallbackTarget = nsnull;
|
||||
mNotifyThreshold = 0;
|
||||
|
||||
callback->OnInputStreamReady(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,10 @@
|
|||
|
||||
#include "imgIEncoder.h"
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
// needed for JPEG library
|
||||
#include <stdio.h>
|
||||
|
||||
|
@ -58,10 +62,12 @@ extern "C" {
|
|||
|
||||
class nsJPEGEncoder : public imgIEncoder
|
||||
{
|
||||
typedef mozilla::Monitor Monitor;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_IMGIENCODER
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSIASYNCINPUTSTREAM
|
||||
|
||||
nsJPEGEncoder();
|
||||
|
||||
|
@ -80,10 +86,26 @@ protected:
|
|||
|
||||
static void errorExit(jpeg_common_struct* cinfo);
|
||||
|
||||
void NotifyListener();
|
||||
|
||||
PRPackedBool mFinished;
|
||||
|
||||
// image buffer
|
||||
PRUint8* mImageBuffer;
|
||||
PRUint32 mImageBufferSize;
|
||||
PRUint32 mImageBufferUsed;
|
||||
|
||||
PRUint32 mImageBufferReadPoint;
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> mCallbackTarget;
|
||||
PRUint32 mNotifyThreshold;
|
||||
|
||||
/*
|
||||
nsJPEGEncoder is designed to allow one thread to pump data into it while another
|
||||
reads from it. We lock to ensure that the buffer remains append-only while
|
||||
we read from it (that it is not realloced) and to ensure that only one thread
|
||||
dispatches a callback for each call to AsyncWait.
|
||||
*/
|
||||
Monitor mMonitor;
|
||||
};
|
||||
|
|
|
@ -46,15 +46,15 @@
|
|||
#include "nsString.h"
|
||||
#include "nsStreamUtils.h"
|
||||
|
||||
// Input streams that do not implement nsIAsyncInputStream should be threadsafe
|
||||
// so that they may be used with nsIInputStreamPump and nsIInputStreamChannel,
|
||||
// which read such a stream on a background thread.
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsPNGEncoder, imgIEncoder, nsIInputStream)
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS3(nsPNGEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
|
||||
|
||||
nsPNGEncoder::nsPNGEncoder() : mPNG(nsnull), mPNGinfo(nsnull),
|
||||
mIsAnimation(PR_FALSE),
|
||||
mImageBuffer(nsnull), mImageBufferSize(0),
|
||||
mImageBufferUsed(0), mImageBufferReadPoint(0)
|
||||
mImageBufferUsed(0), mImageBufferReadPoint(0),
|
||||
mFinished(PR_FALSE), mCallback(nsnull),
|
||||
mCallbackTarget(nsnull), mNotifyThreshold(0),
|
||||
mMonitor("PNG Encoder Monitor")
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -334,6 +334,9 @@ NS_IMETHODIMP nsPNGEncoder::EndImageEncode()
|
|||
png_write_end(mPNG, mPNGinfo);
|
||||
png_destroy_write_struct(&mPNG, &mPNGinfo);
|
||||
|
||||
mFinished = PR_TRUE;
|
||||
NotifyListener();
|
||||
|
||||
// if output callback can't get enough memory, it will free our buffer
|
||||
if (!mImageBuffer)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -526,10 +529,13 @@ NS_IMETHODIMP nsPNGEncoder::ReadSegments(nsWriteSegmentFun aWriter,
|
|||
void *aClosure, PRUint32 aCount,
|
||||
PRUint32 *_retval)
|
||||
{
|
||||
// Avoid another thread reallocing the buffer underneath us
|
||||
mozilla::MonitorAutoEnter autoEnter(mMonitor);
|
||||
|
||||
PRUint32 maxCount = mImageBufferUsed - mImageBufferReadPoint;
|
||||
if (maxCount == 0) {
|
||||
*_retval = 0;
|
||||
return NS_OK;
|
||||
return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
|
||||
if (aCount > maxCount)
|
||||
|
@ -550,10 +556,42 @@ NS_IMETHODIMP nsPNGEncoder::ReadSegments(nsWriteSegmentFun aWriter,
|
|||
/* boolean isNonBlocking (); */
|
||||
NS_IMETHODIMP nsPNGEncoder::IsNonBlocking(PRBool *_retval)
|
||||
{
|
||||
*_retval = PR_FALSE; // We don't implement nsIAsyncInputStream
|
||||
*_retval = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsPNGEncoder::AsyncWait(nsIInputStreamCallback *aCallback,
|
||||
PRUint32 aFlags,
|
||||
PRUint32 aRequestedCount,
|
||||
nsIEventTarget *aTarget)
|
||||
{
|
||||
if (aFlags != 0)
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
if (mCallback || mCallbackTarget)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
mCallbackTarget = aTarget;
|
||||
// 0 means "any number of bytes except 0"
|
||||
mNotifyThreshold = aRequestedCount;
|
||||
if (!aRequestedCount)
|
||||
mNotifyThreshold = 1024; // We don't want to notify incessantly
|
||||
|
||||
// We set the callback absolutely last, because NotifyListener uses it to
|
||||
// determine if someone needs to be notified. If we don't set it last,
|
||||
// NotifyListener might try to fire off a notification to a null target
|
||||
// which will generally cause non-threadsafe objects to be used off the main thread
|
||||
mCallback = aCallback;
|
||||
|
||||
// What we are being asked for may be present already
|
||||
NotifyListener();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsPNGEncoder::CloseWithStatus(nsresult aStatus)
|
||||
{
|
||||
return Close();
|
||||
}
|
||||
|
||||
// nsPNGEncoder::ConvertHostARGBRow
|
||||
//
|
||||
|
@ -629,6 +667,10 @@ nsPNGEncoder::WriteCallback(png_structp png, png_bytep data,
|
|||
return;
|
||||
|
||||
if (that->mImageBufferUsed + size > that->mImageBufferSize) {
|
||||
// When we're reallocing the buffer we need to take the lock to ensure
|
||||
// that nobody is trying to read from the buffer we are destroying
|
||||
mozilla::MonitorAutoEnter autoEnter(that->mMonitor);
|
||||
|
||||
// expand buffer, just double each time
|
||||
that->mImageBufferSize *= 2;
|
||||
PRUint8* newBuf = (PRUint8*)PR_Realloc(that->mImageBuffer,
|
||||
|
@ -644,4 +686,37 @@ nsPNGEncoder::WriteCallback(png_structp png, png_bytep data,
|
|||
}
|
||||
memcpy(&that->mImageBuffer[that->mImageBufferUsed], data, size);
|
||||
that->mImageBufferUsed += size;
|
||||
that->NotifyListener();
|
||||
}
|
||||
|
||||
void
|
||||
nsPNGEncoder::NotifyListener()
|
||||
{
|
||||
// We might call this function on multiple threads (any threads that call
|
||||
// AsyncWait and any that do encoding) so we lock to avoid notifying the
|
||||
// listener twice about the same data (which generally leads to a truncated
|
||||
// image).
|
||||
mozilla::MonitorAutoEnter autoEnter(mMonitor);
|
||||
|
||||
if (mCallback &&
|
||||
(mImageBufferUsed - mImageBufferReadPoint >= mNotifyThreshold ||
|
||||
mFinished)) {
|
||||
nsCOMPtr<nsIInputStreamCallback> callback;
|
||||
if (mCallbackTarget) {
|
||||
NS_NewInputStreamReadyEvent(getter_AddRefs(callback),
|
||||
mCallback,
|
||||
mCallbackTarget);
|
||||
} else {
|
||||
callback = mCallback;
|
||||
}
|
||||
|
||||
NS_ASSERTION(callback, "Shouldn't fail to make the callback");
|
||||
// Null the callback first because OnInputStreamReady could reenter
|
||||
// AsyncWait
|
||||
mCallback = nsnull;
|
||||
mCallbackTarget = nsnull;
|
||||
mNotifyThreshold = 0;
|
||||
|
||||
callback->OnInputStreamReady(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
|
||||
#include "imgIEncoder.h"
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#define NS_PNGENCODER_CID \
|
||||
|
@ -52,10 +56,12 @@
|
|||
|
||||
class nsPNGEncoder : public imgIEncoder
|
||||
{
|
||||
typedef mozilla::Monitor Monitor;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_IMGIENCODER
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSIASYNCINPUTSTREAM
|
||||
|
||||
nsPNGEncoder();
|
||||
|
||||
|
@ -79,11 +85,13 @@ protected:
|
|||
PRUint32 aPixelWidth);
|
||||
static void ErrorCallback(png_structp png_ptr, png_const_charp warning_msg);
|
||||
static void WriteCallback(png_structp png, png_bytep data, png_size_t size);
|
||||
void NotifyListener();
|
||||
|
||||
png_struct* mPNG;
|
||||
png_info* mPNGinfo;
|
||||
|
||||
PRBool mIsAnimation;
|
||||
PRPackedBool mIsAnimation;
|
||||
PRPackedBool mFinished;
|
||||
|
||||
// image buffer
|
||||
PRUint8* mImageBuffer;
|
||||
|
@ -91,4 +99,16 @@ protected:
|
|||
PRUint32 mImageBufferUsed;
|
||||
|
||||
PRUint32 mImageBufferReadPoint;
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> mCallbackTarget;
|
||||
PRUint32 mNotifyThreshold;
|
||||
|
||||
/*
|
||||
nsPNGEncoder is designed to allow one thread to pump data into it while another
|
||||
reads from it. We lock to ensure that the buffer remains append-only while
|
||||
we read from it (that it is not realloced) and to ensure that only one thread
|
||||
dispatches a callback for each call to AsyncWait.
|
||||
*/
|
||||
Monitor mMonitor;
|
||||
};
|
||||
|
|
|
@ -38,13 +38,14 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIInputStream.idl"
|
||||
#include "nsIAsyncInputStream.idl"
|
||||
#include "nsIEventTarget.idl"
|
||||
|
||||
/**
|
||||
* imgIEncoder interface
|
||||
*/
|
||||
[scriptable, uuid(ba3a854b-fb8d-4881-8af9-5849df10e5e5)]
|
||||
interface imgIEncoder : nsIInputStream
|
||||
[scriptable, uuid(4bcba4ec-7a44-43f9-a70f-1e92cdc625b9)]
|
||||
interface imgIEncoder : nsIAsyncInputStream
|
||||
{
|
||||
// Possible values for outputOptions. Multiple values are semicolon-separated.
|
||||
//
|
||||
|
|
|
@ -7,149 +7,217 @@ const Ci = Components.interfaces;
|
|||
const Cc = Components.classes;
|
||||
|
||||
var png1A = {
|
||||
// A 3x3 image, rows are red, green, blue.
|
||||
// RGB format, transparency defaults.
|
||||
// A 3x3 image, rows are red, green, blue.
|
||||
// RGB format, transparency defaults.
|
||||
|
||||
transparency : null,
|
||||
transparency : null,
|
||||
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
|
||||
|
||||
pixels : [
|
||||
255,0,0, 255,0,0, 255,0,0,
|
||||
0,255,0, 0,255,0, 0,255,0,
|
||||
0,0,255, 0,0,255, 0,0,255,
|
||||
]
|
||||
}
|
||||
pixels : [
|
||||
255,0,0, 255,0,0, 255,0,0,
|
||||
0,255,0, 0,255,0, 0,255,0,
|
||||
0,0,255, 0,0,255, 0,0,255,
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
|
||||
};
|
||||
|
||||
var png1B = {
|
||||
// A 3x3 image, rows are red, green, blue.
|
||||
// RGB format, transparency=none.
|
||||
// A 3x3 image, rows are red, green, blue.
|
||||
// RGB format, transparency=none.
|
||||
|
||||
transparency : "none",
|
||||
transparency : "none",
|
||||
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
|
||||
|
||||
pixels : [
|
||||
255,0,0, 255,0,0, 255,0,0,
|
||||
0,255,0, 0,255,0, 0,255,0,
|
||||
0,0,255, 0,0,255, 0,0,255,
|
||||
]
|
||||
}
|
||||
pixels : [
|
||||
255,0,0, 255,0,0, 255,0,0,
|
||||
0,255,0, 0,255,0, 0,255,0,
|
||||
0,0,255, 0,0,255, 0,0,255,
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
|
||||
};
|
||||
|
||||
var png2A = {
|
||||
// A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent.
|
||||
// A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent.
|
||||
|
||||
transparency : null,
|
||||
transparency : null,
|
||||
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,170, 255,0,0,85,
|
||||
0,255,0,255, 0,255,0,170, 0,255,0,85,
|
||||
0,0,255,255, 0,0,255,170, 0,0,255,85
|
||||
]
|
||||
}
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,170, 255,0,0,85,
|
||||
0,255,0,255, 0,255,0,170, 0,255,0,85,
|
||||
0,0,255,255, 0,0,255,170, 0,0,255,85
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAIElEQVQImSXJMQEAMAwEIUy+yZi8DmVFFBcjycn86GgPJw4O8v9DkkEAAAAASUVORK5CYII="
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAIElEQVQImSXJMQEAMAwEIUy+yZi8DmVFFBcjycn86GgPJw4O8v9DkkEAAAAASUVORK5CYII="
|
||||
};
|
||||
|
||||
var png2B = {
|
||||
// A 3x3 image, rows are: red, green, blue. Coulmns are: 0%, 33%, 66% transparent,
|
||||
// but transparency will be ignored.
|
||||
// A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent,
|
||||
// but transparency will be ignored.
|
||||
|
||||
transparency : "none",
|
||||
transparency : "none",
|
||||
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,170, 255,0,0,85,
|
||||
0,255,0,255, 0,255,0,170, 0,255,0,85,
|
||||
0,0,255,255, 0,0,255,170, 0,0,255,85
|
||||
]
|
||||
}
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,170, 255,0,0,85,
|
||||
0,255,0,255, 0,255,0,170, 0,255,0,85,
|
||||
0,0,255,255, 0,0,255,170, 0,0,255,85
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
|
||||
};
|
||||
|
||||
|
||||
// Main test entry point.
|
||||
function run_test() {
|
||||
dump("Checking png1A...\n")
|
||||
run_test_for(png1A);
|
||||
dump("Checking png1B...\n")
|
||||
run_test_for(png1B);
|
||||
dump("Checking png2A...\n")
|
||||
run_test_for(png2A);
|
||||
dump("Checking png2B...\n")
|
||||
run_test_for(png2B);
|
||||
dump("Checking png1A...\n")
|
||||
run_test_for(png1A);
|
||||
dump("Checking png1B...\n")
|
||||
run_test_for(png1B);
|
||||
dump("Checking png2A...\n")
|
||||
run_test_for(png2A);
|
||||
dump("Checking png2B...\n")
|
||||
run_test_for(png2B);
|
||||
};
|
||||
|
||||
|
||||
function run_test_for(input) {
|
||||
var encoder, dataURL;
|
||||
var encoder, dataURL;
|
||||
|
||||
encoder = encodeImage(input);
|
||||
dataURL = makeDataURL(encoder, "image/png");
|
||||
do_check_eq(dataURL, input.expected);
|
||||
encoder = encodeImage(input);
|
||||
dataURL = makeDataURL(encoder, "image/png");
|
||||
do_check_eq(dataURL, input.expected);
|
||||
|
||||
encoder = encodeImageAsync(input);
|
||||
dataURL = makeDataURLFromAsync(encoder, "image/png", input.expected);
|
||||
};
|
||||
|
||||
|
||||
function encodeImage(input) {
|
||||
var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
|
||||
encoder.QueryInterface(Ci.imgIEncoder);
|
||||
var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
|
||||
encoder.QueryInterface(Ci.imgIEncoder);
|
||||
|
||||
var options = "";
|
||||
if (input.transparency) {
|
||||
options += "transparency=" + input.transparency;
|
||||
}
|
||||
var options = "";
|
||||
if (input.transparency) {
|
||||
options += "transparency=" + input.transparency;
|
||||
}
|
||||
|
||||
var frame = input.frames[0];
|
||||
encoder.initFromData(frame.pixels, frame.pixels.length,
|
||||
frame.width, frame.height, frame.stride,
|
||||
frame.format, options);
|
||||
return encoder;
|
||||
var frame = input.frames[0];
|
||||
encoder.initFromData(frame.pixels, frame.pixels.length,
|
||||
frame.width, frame.height, frame.stride,
|
||||
frame.format, options);
|
||||
return encoder;
|
||||
}
|
||||
|
||||
function _encodeImageAsyncFactory(frame, options, encoder)
|
||||
{
|
||||
function finishEncode() {
|
||||
encoder.addImageFrame(frame.pixels, frame.pixels.length,
|
||||
frame.width, frame.height, frame.stride,
|
||||
frame.format, options);
|
||||
encoder.endImageEncode();
|
||||
}
|
||||
return finishEncode;
|
||||
}
|
||||
|
||||
function encodeImageAsync(input)
|
||||
{
|
||||
var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
|
||||
encoder.QueryInterface(Ci.imgIEncoder);
|
||||
|
||||
var options = "";
|
||||
if (input.transparency) {
|
||||
options += "transparency=" + input.transparency;
|
||||
}
|
||||
|
||||
var frame = input.frames[0];
|
||||
encoder.startImageEncode(frame.width, frame.height,
|
||||
frame.format, options);
|
||||
|
||||
do_timeout(50, _encodeImageAsyncFactory(frame, options, encoder));
|
||||
return encoder;
|
||||
}
|
||||
|
||||
|
||||
function makeDataURL(encoder, mimetype) {
|
||||
var rawStream = encoder.QueryInterface(Ci.nsIInputStream);
|
||||
var rawStream = encoder.QueryInterface(Ci.nsIInputStream);
|
||||
|
||||
var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
|
||||
stream.QueryInterface(Ci.nsIBinaryInputStream);
|
||||
var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
|
||||
stream.QueryInterface(Ci.nsIBinaryInputStream);
|
||||
|
||||
stream.setInputStream(rawStream);
|
||||
stream.setInputStream(rawStream);
|
||||
|
||||
var bytes = stream.readByteArray(stream.available()); // returns int[]
|
||||
var bytes = stream.readByteArray(stream.available()); // returns int[]
|
||||
|
||||
var base64String = toBase64(bytes);
|
||||
var base64String = toBase64(bytes);
|
||||
|
||||
return "data:" + mimetype + ";base64," + base64String;
|
||||
return "data:" + mimetype + ";base64," + base64String;
|
||||
}
|
||||
|
||||
function makeDataURLFromAsync(encoder, mimetype, expected) {
|
||||
do_test_pending();
|
||||
var rawStream = encoder.QueryInterface(Ci.nsIAsyncInputStream);
|
||||
|
||||
var currentThread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
|
||||
|
||||
var bytes = [];
|
||||
|
||||
var binarystream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
|
||||
binarystream.QueryInterface(Ci.nsIBinaryInputStream);
|
||||
|
||||
var asyncReader =
|
||||
{
|
||||
onInputStreamReady: function(stream)
|
||||
{
|
||||
binarystream.setInputStream(stream);
|
||||
var available = 0;
|
||||
try {
|
||||
available = stream.available();
|
||||
} catch(e) { }
|
||||
|
||||
if (available > 0)
|
||||
{
|
||||
bytes = bytes.concat(binarystream.readByteArray(available));
|
||||
stream.asyncWait(this, 0, 0, currentThread);
|
||||
} else {
|
||||
var base64String = toBase64(bytes);
|
||||
var dataURL = "data:" + mimetype + ";base64," + base64String;
|
||||
do_check_eq(dataURL, expected);
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
rawStream.asyncWait(asyncReader, 0, 0, currentThread);
|
||||
}
|
||||
|
||||
/* toBase64 copied from extensions/xml-rpc/src/nsXmlRpcClient.js */
|
||||
|
|
Загрузка…
Ссылка в новой задаче