зеркало из https://github.com/mozilla/pjs.git
b=539771; Add support for context attribs to canvas; r=jmuizelaar
This commit is contained in:
Родитель
9bb6cd9bc5
Коммит
5ce28c1a33
|
@ -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',
|
||||
|
|
Загрузка…
Ссылка в новой задаче