b=539771; Add support for context attribs to canvas; r=jmuizelaar

This commit is contained in:
Vladimir Vukicevic 2010-11-16 20:33:03 -08:00
Родитель 9bb6cd9bc5
Коммит 5ce28c1a33
12 изменённых файлов: 430 добавлений и 183 удалений

Просмотреть файл

@ -50,6 +50,7 @@
class nsHTMLCanvasElement;
class gfxContext;
class gfxASurface;
class nsIPropertyBag;
namespace mozilla {
namespace layers {
@ -115,6 +116,14 @@ public:
// Redraw the dirty rectangle of this canvas.
NS_IMETHOD Redraw(const gfxRect &dirty) = 0;
// Passes a generic nsIPropertyBag options argument, along with the
// previous one, if any. Optional.
NS_IMETHOD SetContextOptions(nsIPropertyBag *aNewOptions) { return NS_OK; }
//
// shmem support
//
// If this context can be set to use Mozilla's Shmem segments as its backing
// store, this will set it to that state. Note that if you have drawn
// anything into this canvas before changing the shmem state, it will be

Просмотреть файл

@ -48,6 +48,9 @@
#include "nsDOMError.h"
#include "nsIGfxInfo.h"
#include "nsIPropertyBag.h"
#include "nsIVariant.h"
#include "gfxContext.h"
#include "gfxPattern.h"
#include "gfxUtils.h"
@ -88,6 +91,7 @@ WebGLContext::WebGLContext()
mInvalidated = PR_FALSE;
mResetLayer = PR_TRUE;
mVerbose = PR_FALSE;
mOptionsFrozen = PR_FALSE;
mActiveTexture = 0;
mSynthesizedGLError = LOCAL_GL_NO_ERROR;
@ -266,6 +270,67 @@ WebGLContext::SetCanvasElement(nsHTMLCanvasElement* aParentCanvas)
return NS_OK;
}
static bool
GetBoolFromPropertyBag(nsIPropertyBag *bag, const char *propName, bool *boolResult)
{
nsCOMPtr<nsIVariant> vv;
PRBool bv;
nsresult rv = bag->GetProperty(NS_ConvertASCIItoUTF16(propName), getter_AddRefs(vv));
if (NS_FAILED(rv) || !vv)
return false;
rv = vv->GetAsBool(&bv);
if (NS_FAILED(rv))
return false;
*boolResult = bv ? true : false;
return true;
}
NS_IMETHODIMP
WebGLContext::SetContextOptions(nsIPropertyBag *aOptions)
{
if (!aOptions)
return NS_OK;
WebGLContextOptions newOpts;
// defaults are: yes: depth, alpha, premultipliedAlpha; no: stencil
if (!GetBoolFromPropertyBag(aOptions, "stencil", &newOpts.stencil))
newOpts.stencil = false;
if (!GetBoolFromPropertyBag(aOptions, "depth", &newOpts.depth))
newOpts.depth = true;
if (!GetBoolFromPropertyBag(aOptions, "alpha", &newOpts.alpha))
newOpts.alpha = true;
if (!GetBoolFromPropertyBag(aOptions, "premultipliedAlpha", &newOpts.premultipliedAlpha))
newOpts.premultipliedAlpha = true;
GetBoolFromPropertyBag(aOptions, "antialiasHint", &newOpts.antialiasHint);
// enforce that if stencil is specified, we also give back depth
newOpts.depth |= newOpts.stencil;
LogMessage("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d\n",
newOpts.antialiasHint ? 1 : 0,
newOpts.stencil ? 1 : 0,
newOpts.depth ? 1 : 0,
newOpts.alpha ? 1 : 0,
newOpts.premultipliedAlpha ? 1 : 0);
if (mOptionsFrozen && newOpts != mOptions) {
// Error if the options are already frozen, and the ones that were asked for
// aren't the same as what they were originally.
return NS_ERROR_FAILURE;
}
mOptions = newOpts;
return NS_OK;
}
NS_IMETHODIMP
WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
{
@ -300,8 +365,26 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
DestroyResourcesAndContext();
gl::ContextFormat format(gl::ContextFormat::BasicRGBA32);
format.depth = 16;
format.minDepth = 1;
if (mOptions.depth) {
format.depth = 24;
format.minDepth = 16;
}
if (mOptions.stencil) {
format.stencil = 8;
format.minStencil = 8;
}
if (!mOptions.alpha) {
// Select 565; we won't/shouldn't hit this on the desktop,
// but let mobile know we're ok with it.
format.red = 5;
format.green = 6;
format.blue = 5;
format.alpha = 0;
format.minAlpha = 0;
}
nsCOMPtr<nsIPrefBranch> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
NS_ENSURE_TRUE(prefService != nsnull, NS_ERROR_FAILURE);
@ -421,6 +504,7 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
mWidth = width;
mHeight = height;
mResetLayer = PR_TRUE;
mOptionsFrozen = PR_TRUE;
// increment the generation number
++mGeneration;
@ -589,7 +673,7 @@ WebGLContext::GetCanvasLayer(CanvasLayer *aOldLayer,
}
data.mSize = nsIntSize(mWidth, mHeight);
data.mGLBufferIsPremultiplied = PR_FALSE;
data.mGLBufferIsPremultiplied = mOptions.premultipliedAlpha ? PR_TRUE : PR_FALSE;
canvasLayer->Initialize(data);
PRUint32 flags = gl->CreationFormat().alpha == 0 ? Layer::CONTENT_OPAQUE : 0;
@ -602,6 +686,40 @@ WebGLContext::GetCanvasLayer(CanvasLayer *aOldLayer,
return canvasLayer.forget().get();
}
NS_IMETHODIMP
WebGLContext::GetContextAttributes(jsval *aResult)
{
JSContext *cx = nsContentUtils::GetCurrentJSContext();
if (!cx)
return NS_ERROR_FAILURE;
JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
if (!obj)
return NS_ERROR_FAILURE;
*aResult = OBJECT_TO_JSVAL(obj);
gl::ContextFormat cf = gl->ActualFormat();
if (!JS_DefineProperty(cx, obj, "alpha", cf.alpha > 0 ? JSVAL_TRUE : JSVAL_FALSE,
NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "depth", cf.depth > 0 ? JSVAL_TRUE : JSVAL_FALSE,
NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "stencil", cf.stencil > 0 ? JSVAL_TRUE : JSVAL_FALSE,
NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "antialias", JSVAL_FALSE,
NULL, NULL, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, obj, "premultipliedAlpha",
mOptions.premultipliedAlpha ? JSVAL_TRUE : JSVAL_FALSE,
NULL, NULL, JSPROP_ENUMERATE))
{
*aResult = JSVAL_VOID;
return NS_ERROR_FAILURE;
}
return NS_OK;
}
//
// XPCOM goop
//

Просмотреть файл

@ -67,6 +67,7 @@
#define CONTEXT_LOST_WEBGL 0x9242
class nsIDocShell;
class nsIPropertyBag;
namespace mozilla {
@ -277,6 +278,39 @@ struct WebGLVertexAttribData {
}
};
struct WebGLContextOptions {
// these are defaults
WebGLContextOptions()
: alpha(true), depth(true), stencil(false),
premultipliedAlpha(true), antialiasHint(false)
{ }
bool operator==(const WebGLContextOptions& other) const {
return
alpha == other.alpha &&
depth == other.depth &&
stencil == other.stencil &&
premultipliedAlpha == other.premultipliedAlpha &&
antialiasHint == other.antialiasHint;
}
bool operator!=(const WebGLContextOptions& other) const {
return
alpha != other.alpha ||
depth != other.depth ||
stencil != other.stencil ||
premultipliedAlpha != other.premultipliedAlpha ||
antialiasHint != other.antialiasHint;
}
bool alpha;
bool depth;
bool stencil;
bool premultipliedAlpha;
bool antialiasHint;
};
class WebGLContext :
public nsICanvasRenderingContextWebGL,
public nsICanvasRenderingContextInternal,
@ -305,6 +339,8 @@ public:
nsIInputStream **aStream);
NS_IMETHOD GetThebesSurface(gfxASurface **surface);
NS_IMETHOD SetIsOpaque(PRBool b) { return NS_OK; };
NS_IMETHOD SetContextOptions(nsIPropertyBag *aOptions);
NS_IMETHOD SetIsIPC(PRBool b) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD Redraw(const gfxRect&) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD Swap(mozilla::ipc::Shmem& aBack,
@ -360,9 +396,12 @@ protected:
PRInt32 mWidth, mHeight;
CheckedUint32 mGeneration;
WebGLContextOptions mOptions;
PRPackedBool mInvalidated;
PRPackedBool mResetLayer;
PRPackedBool mVerbose;
PRPackedBool mOptionsFrozen;
WebGLuint mActiveTexture;
WebGLenum mSynthesizedGLError;
@ -729,13 +768,14 @@ public:
NS_DECL_NSIWEBGLTEXTURE
protected:
friend class WebGLContext;
friend class WebGLFramebuffer;
PRBool mDeleted;
WebGLuint mName;
////////////////////////////////////////////////////////////////////////////////////////////////////
/////// everything below that point is only used for the texture completeness/npot business
/////// (sections 3.7.10 and 3.8.2 in GL ES 2.0.24 spec)
////////////////////////////////////////////////////////////////////////////////////////////////////
// we store information about the various images that are part of
// this texture (cubemap faces, mipmap levels)
struct ImageInfo {
ImageInfo() : mWidth(0), mHeight(0), mFormat(0), mType(0), mIsDefined(PR_FALSE) {}
@ -1328,6 +1368,7 @@ public:
WebGLFramebuffer(WebGLContext *context, WebGLuint name) :
WebGLContextBoundObject(context),
mName(name), mDeleted(PR_FALSE),
mColorAttachment0HasAlpha(PR_FALSE),
mHasDepthAttachment(PR_FALSE),
mHasStencilAttachment(PR_FALSE),
mHasDepthStencilAttachment(PR_FALSE)
@ -1342,6 +1383,8 @@ public:
PRBool Deleted() { return mDeleted; }
WebGLuint GLName() { return mName; }
PRBool ColorAttachment0HasAlpha() { return mColorAttachment0HasAlpha; }
nsresult FramebufferRenderbuffer(WebGLenum target,
WebGLenum attachment,
WebGLenum rbtarget,
@ -1405,15 +1448,20 @@ public:
{
return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
}
// ReadPixels needs alpha and size information, but only
// for COLOR_ATTACHMENT0
if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) {
setDimensions(wrb);
mColorAttachment0HasAlpha = InternalFormatHasAlpha(wrb->mInternalFormat);
} else {
mColorAttachment0HasAlpha = PR_FALSE;
}
}
mColorRenderbufferAttachment = wrb;
break;
}
// dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0
if (attachment == LOCAL_GL_COLOR_ATTACHMENT0)
setDimensions(wrb);
mContext->MakeContextCurrent();
mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername);
@ -1460,14 +1508,26 @@ public:
{
return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment);
}
// nothing to do for color buffers. all textures have a color-renderable format.
// keep data for readPixels, function only uses COLOR_ATTACHMENT0
if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) {
setDimensions(wtex);
if (wtex) {
const WebGLTexture::ImageInfo& ia = wtex->ImageInfoAt
(level, textarget == LOCAL_GL_TEXTURE_2D
? 0
: textarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X);
mColorAttachment0HasAlpha = InternalFormatHasAlpha(ia.mFormat);
} else {
mColorAttachment0HasAlpha = PR_FALSE;
}
}
// nothing else to do for color buffers. all textures have a color-renderable format.
break;
}
// dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0
if (attachment == LOCAL_GL_COLOR_ATTACHMENT0)
setDimensions(wtex);
mContext->MakeContextCurrent();
mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level);
@ -1501,6 +1561,14 @@ public:
int(mHasDepthStencilAttachment) > 1;
}
static PRBool InternalFormatHasAlpha(WebGLenum aInternalFormat) {
return
aInternalFormat == LOCAL_GL_RGBA ||
aInternalFormat == LOCAL_GL_ALPHA ||
aInternalFormat == LOCAL_GL_RGBA4 ||
aInternalFormat == LOCAL_GL_RGB5_A1;
}
protected:
// protected because WebGLContext should only call InitializeRenderbuffers
@ -1605,7 +1673,8 @@ protected:
}
WebGLuint mName;
PRBool mDeleted;
PRPackedBool mDeleted;
PRPackedBool mColorAttachment0HasAlpha;
// we only store pointers to attached renderbuffers, not to attached textures, because
// we will only need to initialize renderbuffers. Textures are already initialized.

Просмотреть файл

@ -123,18 +123,17 @@ WebGLProgram::GetUniformLocationObject(GLint glLocation)
return loc.forget();
}
static PRBool
InternalFormatHasAlpha(WebGLenum aInternalFormat) {
return aInternalFormat == LOCAL_GL_RGBA4 ||
aInternalFormat == LOCAL_GL_RGB5_A1;
}
//
// WebGL API
//
/* void present (); */
NS_IMETHODIMP
WebGLContext::Present()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void GlActiveTexture (in GLenum texture); */
NS_IMETHODIMP
WebGLContext::ActiveTexture(WebGLenum texture)
@ -2485,9 +2484,9 @@ WebGLContext::ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsiz
}
switch (type) {
// case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
// case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
// case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
// XXX we need to support 565 with GL_RGB, but the code
// below needs to be taught about unsigned short
//case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
case LOCAL_GL_UNSIGNED_BYTE:
break;
default:
@ -2579,6 +2578,51 @@ WebGLContext::ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsiz
}
delete [] subrect_data;
}
// if we're reading alpha, we may need to do fixup
if (format == LOCAL_GL_ALPHA ||
format == LOCAL_GL_RGBA)
{
PRBool needAlphaFixup;
if (mBoundFramebuffer) {
needAlphaFixup = !mBoundFramebuffer->ColorAttachment0HasAlpha();
} else {
needAlphaFixup = gl->ActualFormat().alpha == 0;
}
if (needAlphaFixup) {
if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
// this is easy; it's an 0xff memset per row
PRUint8 *row = (PRUint8*)data;
for (GLint j = 0; j < height; ++j) {
memset(row, 0xff, checked_plainRowSize.value());
row += checked_alignedRowSize.value();
}
} else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
// this is harder, we need to just set the alpha byte here
PRUint8 *row = (PRUint8*)data;
for (GLint j = 0; j < height; ++j) {
PRUint8 *rowp = row;
#ifdef IS_LITTLE_ENDIAN
// offset to get the alpha byte; we're always going to
// move by 4 bytes
rowp += 3;
#endif
PRUint8 *endrowp = rowp + 4 * width;
while (rowp != endrowp) {
*rowp = 0xff;
rowp += 4;
}
row += checked_alignedRowSize.value();
}
} else {
NS_WARNING("Unhandled case, how'd we get here?");
return NS_ERROR_FAILURE;
}
}
}
return NS_OK;
}
@ -3880,129 +3924,6 @@ WebGLContext::TexSubImage2D_dom(WebGLenum target, WebGLint level,
srcFormat, PR_TRUE);
}
#if 0
// ImageData getImageData (in float x, in float y, in float width, in float height);
NS_IMETHODIMP
WebGLContext::GetImageData(PRUint32 x, PRUint32 y, PRUint32 w, PRUint32 h)
{
// disabled due to win32 linkage issues with thebes symbols and NS_RELEASE
return NS_ERROR_FAILURE;
#if 0
NativeJSContext js;
if (NS_FAILED(js.error))
return js.error;
if (js.argc != 4) return NS_ERROR_INVALID_ARG;
if (!mGLPbuffer ||
!mGLPbuffer->ThebesSurface())
return NS_ERROR_FAILURE;
if (!mCanvasElement)
return NS_ERROR_FAILURE;
if (HTMLCanvasElement()->IsWriteOnly() && !IsCallerTrustedForRead()) {
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
return NS_ERROR_DOM_SECURITY_ERR;
}
JSContext *ctx = js.ctx;
if (!CanvasUtils::CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight))
return NS_ERROR_DOM_SYNTAX_ERR;
nsAutoArrayPtr<PRUint8> surfaceData (new (std::nothrow) PRUint8[w * h * 4]);
int surfaceDataStride = w*4;
int surfaceDataOffset = 0;
if (!surfaceData)
return NS_ERROR_OUT_OF_MEMORY;
nsRefPtr<gfxImageSurface> tmpsurf = new gfxImageSurface(surfaceData,
gfxIntSize(w, h),
w * 4,
gfxASurface::ImageFormatARGB32);
if (!tmpsurf || tmpsurf->CairoStatus())
return NS_ERROR_FAILURE;
nsRefPtr<gfxContext> tmpctx = new gfxContext(tmpsurf);
if (!tmpctx || tmpctx->HasError())
return NS_ERROR_FAILURE;
nsRefPtr<gfxASurface> surf = mGLPbuffer->ThebesSurface();
nsRefPtr<gfxPattern> pat = CanvasGLThebes::CreatePattern(surf);
gfxMatrix m;
m.Translate(gfxPoint(x, mGLPbuffer->Height()-y));
m.Scale(1.0, -1.0);
pat->SetMatrix(m);
// XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
// pixel alignment for this stuff!
tmpctx->NewPath();
tmpctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, w, h), pat);
tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE);
tmpctx->Fill();
tmpctx = nsnull;
tmpsurf = nsnull;
PRUint32 len = w * h * 4;
if (len > (((PRUint32)0xfff00000)/sizeof(jsval)))
return NS_ERROR_INVALID_ARG;
nsAutoArrayPtr<jsval> jsvector(new (std::nothrow) jsval[w * h * 4]);
if (!jsvector)
return NS_ERROR_OUT_OF_MEMORY;
jsval *dest = jsvector.get();
PRUint8 *row;
for (PRUint32 j = 0; j < h; j++) {
row = surfaceData + surfaceDataOffset + (surfaceDataStride * j);
for (PRUint32 i = 0; i < w; i++) {
// XXX Is there some useful swizzle MMX we can use here?
// I guess we have to INT_TO_JSVAL still
#ifdef IS_LITTLE_ENDIAN
PRUint8 b = *row++;
PRUint8 g = *row++;
PRUint8 r = *row++;
PRUint8 a = *row++;
#else
PRUint8 a = *row++;
PRUint8 r = *row++;
PRUint8 g = *row++;
PRUint8 b = *row++;
#endif
// Convert to non-premultiplied color
if (a != 0) {
r = (r * 255) / a;
g = (g * 255) / a;
b = (b * 255) / a;
}
*dest++ = INT_TO_JSVAL(r);
*dest++ = INT_TO_JSVAL(g);
*dest++ = INT_TO_JSVAL(b);
*dest++ = INT_TO_JSVAL(a);
}
}
JSObject *dataArray = JS_NewArrayObject(ctx, w*h*4, jsvector);
if (!dataArray)
return NS_ERROR_OUT_OF_MEMORY;
JSObjectHelper retobj(&js);
retobj.DefineProperty("width", w);
retobj.DefineProperty("height", h);
retobj.DefineProperty("data", dataArray);
js.SetRetVal(retobj);
return NS_OK;
#endif
}
#endif
PRBool
BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize)
{

Просмотреть файл

@ -60,7 +60,6 @@ function getWebGL(canvasName, contextAttribs, clearColor, clearDepth, clearStenc
alert("No WebGL context found");
return null;
}
var actualContextAttribs = gl.getContextAttributes();
// Add a console
gl.console = ("console" in window) ? window.console : { log: function() { } };
@ -146,6 +145,13 @@ function testAlpha(alpha)
else
shouldBeNonNull("webGL = getWebGL('alphaOff', { alpha: false, depth: false, stencil: false, antialias: false }, [ 0, 0, 0, 0 ], 1, 0)");
shouldBeNonNull("contextAttribs = webGL.getContextAttributes()");
shouldBe("'depth' in contextAttribs", "true");
shouldBe("'stencil' in contextAttribs", "true");
shouldBe("'alpha' in contextAttribs", "true");
shouldBe("'antialias' in contextAttribs", "true");
shouldBe("'premultipliedAlpha' in contextAttribs", "true");
shouldBe("contextAttribs.alpha", (alpha ? "true" : "false"));
shouldBe("contextAttribs.depth", "false");
shouldBe("contextAttribs.stencil", "false");

Просмотреть файл

@ -169,7 +169,7 @@ public:
protected:
nsIntSize GetWidthHeight();
nsresult UpdateContext();
nsresult UpdateContext(nsIPropertyBag *aNewContextOptions = nsnull);
nsresult ExtractData(const nsAString& aType,
const nsAString& aOptions,
char*& aData,

Просмотреть файл

@ -51,6 +51,9 @@
#include "nsDisplayList.h"
#include "ImageLayers.h"
#include "BasicLayers.h"
#include "imgIEncoder.h"
#include "nsIWritablePropertyBag2.h"
#define DEFAULT_CANVAS_WIDTH 300
#define DEFAULT_CANVAS_HEIGHT 150
@ -151,7 +154,7 @@ nsHTMLCanvasElement::CopyInnerTo(nsGenericElement* aDest) const
if (aDest->GetOwnerDoc()->IsStaticDocument()) {
nsHTMLCanvasElement* dest = static_cast<nsHTMLCanvasElement*>(aDest);
nsCOMPtr<nsISupports> cxt;
dest->GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(cxt));
dest->GetContext(NS_LITERAL_STRING("2d"), JSVAL_VOID, getter_AddRefs(cxt));
nsCOMPtr<nsIDOMCanvasRenderingContext2D> context2d = do_QueryInterface(cxt);
if (context2d) {
context2d->DrawImage(const_cast<nsHTMLCanvasElement*>(this),
@ -230,33 +233,60 @@ nsHTMLCanvasElement::ExtractData(const nsAString& aType,
PRUint32& aSize,
bool& aFellBackToPNG)
{
// We get an input stream from the context. If more than one context type
// is supported in the future, this will have to be changed to do the right
// thing. For now, just assume that the 2D context has all the goods.
nsCOMPtr<nsICanvasRenderingContextInternal> context;
nsresult rv = GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(context));
NS_ENSURE_SUCCESS(rv, rv);
if (!context) {
// XXX bug 578349
return NS_ERROR_NOT_IMPLEMENTED;
// 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), gfxASurface::ImageFormatARGB32);
}
nsresult rv;
// get image bytes
nsCOMPtr<nsIInputStream> imgStream;
NS_ConvertUTF16toUTF8 aMimeType8(aType);
rv = context->GetInputStream(nsPromiseFlatCString(aMimeType8).get(),
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
if (NS_FAILED(rv)) {
// Use image/png instead.
nsCAutoString encoderType;
encoderType.Assign(NS_ConvertUTF16toUTF8(aType));
try_again:
if (mCurrentContext) {
rv = mCurrentContext->GetInputStream(nsPromiseFlatCString(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(nsPromiseFlatCString(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;
rv = context->GetInputStream("image/png",
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
NS_ENSURE_SUCCESS(rv, rv);
encoderType.AssignLiteral("image/png");
goto try_again;
}
// at this point, we either need to succeed or bail.
NS_ENSURE_SUCCESS(rv, rv);
// Generally, there will be only one chunk of data, and it will be available
// for us to read right away, so optimize this case.
PRUint32 bufSize;
@ -301,6 +331,7 @@ nsHTMLCanvasElement::ToDataURLImpl(const nsAString& aMimeType,
nsAString& aDataURL)
{
bool fallbackToPNG = false;
PRUint32 imgSize = 0;
char* imgData;
@ -417,6 +448,7 @@ nsHTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
NS_IMETHODIMP
nsHTMLCanvasElement::GetContext(const nsAString& aContextId,
const jsval& aContextOptions,
nsISupports **aContext)
{
nsresult rv;
@ -443,7 +475,52 @@ nsHTMLCanvasElement::GetContext(const nsAString& aContextId,
return rv;
}
rv = UpdateContext();
nsCOMPtr<nsIPropertyBag> contextProps;
if (!JSVAL_IS_NULL(aContextOptions) &&
!JSVAL_IS_VOID(aContextOptions))
{
JSContext *cx = nsContentUtils::GetCurrentJSContext();
nsCOMPtr<nsIWritablePropertyBag2> newProps;
// note: if any contexts end up supporting something other
// than objects, e.g. plain strings, then we'll need to expand
// this to know how to create nsISupportsStrings etc.
if (JSVAL_IS_OBJECT(aContextOptions)) {
newProps = do_CreateInstance("@mozilla.org/hash-property-bag;1");
JSObject *opts = JSVAL_TO_OBJECT(aContextOptions);
JSIdArray *props = JS_Enumerate(cx, opts);
for (int i = 0; props && i < props->length; ++i) {
jsid propid = props->vector[i];
jsval propname, propval;
if (!JS_IdToValue(cx, propid, &propname) ||
!JS_GetPropertyById(cx, opts, propid, &propval))
{
continue;
}
JSString *propnameString = JS_ValueToString(cx, propname);
nsDependentString pstr(JS_GetStringChars(propnameString), JS_GetStringLength(propnameString));
if (JSVAL_IS_BOOLEAN(propval)) {
newProps->SetPropertyAsBool(pstr, propval == JSVAL_TRUE ? PR_TRUE : PR_FALSE);
} else if (JSVAL_IS_INT(propval)) {
newProps->SetPropertyAsInt32(pstr, JSVAL_TO_INT(propval));
} else if (JSVAL_IS_DOUBLE(propval)) {
newProps->SetPropertyAsDouble(pstr, JSVAL_TO_DOUBLE(propval));
} else if (JSVAL_IS_STRING(propval)) {
newProps->SetPropertyAsAString(pstr, nsDependentString(JS_GetStringChars(JS_ValueToString(cx, propval)),
JS_GetStringLength(JS_ValueToString(cx, propval))));
}
}
}
contextProps = newProps;
}
rv = UpdateContext(contextProps);
if (NS_FAILED(rv)) {
mCurrentContext = nsnull;
return rv;
@ -504,14 +581,25 @@ nsHTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
}
nsresult
nsHTMLCanvasElement::UpdateContext()
nsHTMLCanvasElement::UpdateContext(nsIPropertyBag *aNewContextOptions)
{
if (!mCurrentContext)
return NS_OK;
nsresult rv = NS_OK;
if (mCurrentContext) {
nsIntSize sz = GetWidthHeight();
rv = mCurrentContext->SetIsOpaque(GetIsOpaque());
rv = mCurrentContext->SetDimensions(sz.width, sz.height);
}
rv = mCurrentContext->SetIsOpaque(GetIsOpaque());
if (NS_FAILED(rv))
return rv;
rv = mCurrentContext->SetContextOptions(aNewContextOptions);
if (NS_FAILED(rv))
return rv;
nsIntSize sz = GetWidthHeight();
rv = mCurrentContext->SetDimensions(sz.width, sz.height);
if (NS_FAILED(rv))
return rv;
return rv;
}

Просмотреть файл

@ -41,6 +41,7 @@
interface nsIDOMElement;
interface nsIDOMHTMLCanvasElement;
interface nsIPropertyBag;
// XXX should we comment out these typedefs in the C++ header?
@ -55,6 +56,9 @@ typedef float WebGLfloat;
typedef float WebGLclampf;
%{C++
// for jsval
#include "jsapi.h"
namespace js {
struct ArrayBuffer;
struct TypedArray;
@ -558,7 +562,7 @@ interface nsICanvasRenderingContextWebGL : nsISupports
//
// METHODS
//
void present();
jsval getContextAttributes();
void activeTexture(in WebGLenum texture);
void attachShader([optional] in nsIWebGLProgram program, [optional] in nsIWebGLShader shader);

Просмотреть файл

@ -37,6 +37,11 @@
#include "nsIDOMHTMLElement.idl"
%{C++
// for jsval
#include "jsapi.h"
%}
/**
* The nsIDOMHTMLCanvasElement interface is the interface to a HTML
* <canvas> element.
@ -49,14 +54,15 @@
interface nsIDOMFile;
[scriptable, uuid(28945fd6-c4a0-44e3-8629-98358eab5d7b)]
[scriptable, uuid(53ad994a-3cd0-48fa-8ffb-7f3d8cd19c50)]
interface nsIDOMHTMLCanvasElement : nsIDOMHTMLElement
{
attribute long width;
attribute long height;
attribute boolean mozOpaque;
nsISupports getContext(in DOMString contextId);
nsISupports getContext(in DOMString contextId,
[optional] in jsval contextOptions);
// Valid calls are:

Просмотреть файл

@ -364,6 +364,8 @@ GLContext::InitWithPrefix(const char *prefix, PRBool trygl)
}
}
#endif
UpdateActualFormat();
}
#ifdef DEBUG
@ -709,6 +711,10 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, (GLint*) &curBoundRenderbuffer);
fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
// the context format of what we're defining;
// for some reason, UpdateActualFormat isn't working with a bound FBO.
ContextFormat cf;
// If this is the first time we're going through this, we need
// to create the objects we'll use. Otherwise, just bind them.
if (firstTime) {
@ -746,6 +752,8 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
LOCAL_GL_RGBA,
LOCAL_GL_UNSIGNED_BYTE,
NULL);
cf.red = cf.green = cf.blue = cf.alpha = 8;
} else {
fTexImage2D(LOCAL_GL_TEXTURE_2D,
0,
@ -760,6 +768,15 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
: LOCAL_GL_UNSIGNED_BYTE,
#endif
NULL);
#ifdef XP_WIN
cf.red = cf.green = cf.blue = 8;
#else
cf.red = 5;
cf.green = 6;
cf.blue = 5;
#endif
cf.alpha = 0;
}
if (depth && stencil && useDepthStencil) {
@ -767,6 +784,8 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
LOCAL_GL_DEPTH24_STENCIL8,
aSize.width, aSize.height);
cf.depth = 24;
cf.stencil = 8;
} else {
if (depth) {
GLenum depthType;
@ -787,6 +806,7 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
mIsGLES2 ? LOCAL_GL_DEPTH_COMPONENT16
: LOCAL_GL_DEPTH_COMPONENT24,
aSize.width, aSize.height);
cf.depth = mIsGLES2 ? 16 : 24;
}
if (stencil) {
@ -794,6 +814,7 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
LOCAL_GL_STENCIL_INDEX8,
aSize.width, aSize.height);
cf.stencil = 8;
}
}
@ -843,7 +864,10 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
mOffscreenActualSize = aSize;
if (firstTime) {
UpdateActualFormat();
// UpdateActualFormat() doesn't work for some reason, with a
// FBO bound, even though it should.
//UpdateActualFormat();
mActualFormat = cf;
#ifdef DEBUG
printf_stderr("Created offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d\n",

Просмотреть файл

@ -568,6 +568,7 @@ CreatePBufferOffscreenContext(const gfxIntSize& aSize,
A2(attrs, LOCAL_WGL_ALPHA_BITS_ARB, aFormat.alpha);
A2(attrs, LOCAL_WGL_DEPTH_BITS_ARB, aFormat.depth);
A2(attrs, LOCAL_WGL_STENCIL_BITS_ARB, aFormat.stencil);
if (aFormat.alpha > 0) {
A2(attrs, LOCAL_WGL_BIND_TO_TEXTURE_RGBA_ARB, LOCAL_GL_TRUE);

Просмотреть файл

@ -458,6 +458,7 @@ members = [
'-nsICanvasRenderingContextWebGL.getUniform',
'-nsICanvasRenderingContextWebGL.getVertexAttrib',
'-nsICanvasRenderingContextWebGL.getShaderParameter',
'-nsICanvasRenderingContextWebGL.getContextAttributes',
# Audio
'nsIDOMNotifyAudioAvailableEvent.frameBuffer',