2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Vladimir Vukicevic <vladimir@pobox.com>
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2005
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#define _USE_MATH_DEFINES
|
|
|
|
#endif
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include "prmem.h"
|
|
|
|
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
|
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIDOMCanvasRenderingContext2D.h"
|
|
|
|
#include "nsICanvasRenderingContextInternal.h"
|
|
|
|
#include "nsPresContext.h"
|
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsIVariant.h"
|
|
|
|
|
|
|
|
#include "imgIRequest.h"
|
|
|
|
#include "imgIContainer.h"
|
|
|
|
#include "gfxIImageFrame.h"
|
|
|
|
#include "nsIDOMHTMLCanvasElement.h"
|
|
|
|
#include "nsICanvasElement.h"
|
|
|
|
#include "nsIDOMHTMLImageElement.h"
|
|
|
|
#include "nsIImageLoadingContent.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
|
|
#include "nsIImage.h"
|
|
|
|
#include "nsIFrame.h"
|
|
|
|
#include "nsDOMError.h"
|
|
|
|
#include "nsIScriptError.h"
|
|
|
|
|
|
|
|
#include "nsICSSParser.h"
|
2007-07-25 11:21:34 -07:00
|
|
|
#include "nsICSSStyleRule.h"
|
2008-06-17 14:37:57 -07:00
|
|
|
#include "nsInspectorCSSUtils.h"
|
2007-07-25 11:21:34 -07:00
|
|
|
#include "nsStyleSet.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "nsPrintfCString.h"
|
|
|
|
|
|
|
|
#include "nsReadableUtils.h"
|
|
|
|
|
|
|
|
#include "nsColor.h"
|
|
|
|
#include "nsIRenderingContext.h"
|
|
|
|
#include "nsIDeviceContext.h"
|
|
|
|
#include "nsGfxCIID.h"
|
|
|
|
#include "nsIScriptSecurityManager.h"
|
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsPresContext.h"
|
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsIDOMWindow.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsIDocShellTreeItem.h"
|
|
|
|
#include "nsIDocShellTreeNode.h"
|
|
|
|
#include "nsIXPConnect.h"
|
|
|
|
#include "jsapi.h"
|
|
|
|
#include "jsnum.h"
|
|
|
|
|
|
|
|
#include "nsTArray.h"
|
|
|
|
|
|
|
|
#include "imgIEncoder.h"
|
|
|
|
|
|
|
|
#include "gfxContext.h"
|
|
|
|
#include "gfxASurface.h"
|
2007-09-25 13:46:08 -07:00
|
|
|
#include "gfxImageSurface.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "gfxPlatform.h"
|
2007-07-25 11:21:34 -07:00
|
|
|
#include "gfxFont.h"
|
|
|
|
#include "gfxTextRunCache.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "nsFrameManager.h"
|
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
#include "nsBidiPresUtils.h"
|
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
#ifdef MOZ_MEDIA
|
|
|
|
#include "nsHTMLVideoElement.h"
|
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifndef M_PI
|
|
|
|
#define M_PI 3.14159265358979323846
|
|
|
|
#define M_PI_2 1.57079632679489661923
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static PRBool CheckSaneSubrectSize (PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h, PRInt32 realWidth, PRInt32 realHeight);
|
|
|
|
|
|
|
|
/* Float validation stuff */
|
|
|
|
|
|
|
|
#define VALIDATE(_f) if (!JSDOUBLE_IS_FINITE(_f)) return PR_FALSE
|
|
|
|
|
|
|
|
/* These must take doubles as args, because JSDOUBLE_IS_FINITE expects
|
|
|
|
* to take the address of its argument; we can't cast/convert in the
|
|
|
|
* macro.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static PRBool FloatValidate (double f1) {
|
|
|
|
VALIDATE(f1);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool FloatValidate (double f1, double f2) {
|
|
|
|
VALIDATE(f1); VALIDATE(f2);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool FloatValidate (double f1, double f2, double f3) {
|
|
|
|
VALIDATE(f1); VALIDATE(f2); VALIDATE(f3);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool FloatValidate (double f1, double f2, double f3, double f4) {
|
|
|
|
VALIDATE(f1); VALIDATE(f2); VALIDATE(f3); VALIDATE(f4);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool FloatValidate (double f1, double f2, double f3, double f4, double f5) {
|
|
|
|
VALIDATE(f1); VALIDATE(f2); VALIDATE(f3); VALIDATE(f4); VALIDATE(f5);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool FloatValidate (double f1, double f2, double f3, double f4, double f5, double f6) {
|
|
|
|
VALIDATE(f1); VALIDATE(f2); VALIDATE(f3); VALIDATE(f4); VALIDATE(f5); VALIDATE(f6);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef VALIDATE
|
|
|
|
|
|
|
|
/**
|
|
|
|
** nsCanvasGradient
|
|
|
|
**/
|
|
|
|
#define NS_CANVASGRADIENT_PRIVATE_IID \
|
|
|
|
{ 0x491d39d8, 0x4058, 0x42bd, { 0xac, 0x76, 0x70, 0xd5, 0x62, 0x7f, 0x02, 0x10 } }
|
|
|
|
class nsCanvasGradient : public nsIDOMCanvasGradient
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID)
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsCanvasGradient(gfxPattern* pat, nsICSSParser* cssparser)
|
|
|
|
: mPattern(pat), mCSSParser(cssparser)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
void Apply(gfxContext* ctx) {
|
|
|
|
ctx->SetPattern(mPattern);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* nsIDOMCanvasGradient */
|
|
|
|
NS_IMETHOD AddColorStop (float offset,
|
|
|
|
const nsAString& colorstr)
|
|
|
|
{
|
|
|
|
nscolor color;
|
|
|
|
|
|
|
|
if (!FloatValidate(offset))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (offset < 0.0 || offset > 1.0)
|
|
|
|
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
|
|
|
|
2008-02-21 00:37:27 -08:00
|
|
|
nsresult rv = mCSSParser->ParseColorString(nsString(colorstr), nsnull, 0, &color);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mPattern->AddColorStop(offset, gfxRGBA(color));
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
|
|
|
protected:
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxPattern> mPattern;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsICSSParser> mCSSParser;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasGradient, NS_CANVASGRADIENT_PRIVATE_IID)
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF(nsCanvasGradient)
|
|
|
|
NS_IMPL_RELEASE(nsCanvasGradient)
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsCanvasGradient)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsCanvasGradient)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient)
|
|
|
|
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasGradient)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
/**
|
|
|
|
** nsCanvasPattern
|
|
|
|
**/
|
|
|
|
#define NS_CANVASPATTERN_PRIVATE_IID \
|
|
|
|
{ 0xb85c6c8a, 0x0624, 0x4530, { 0xb8, 0xee, 0xff, 0xdf, 0x42, 0xe8, 0x21, 0x6d } }
|
|
|
|
class nsCanvasPattern : public nsIDOMCanvasPattern
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID)
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsCanvasPattern(gfxPattern* pat,
|
2007-11-08 22:03:47 -08:00
|
|
|
nsIPrincipal* principalForSecurityCheck,
|
|
|
|
PRBool forceWriteOnly)
|
2008-07-23 10:50:03 -07:00
|
|
|
: mPattern(pat),
|
2007-11-08 22:03:47 -08:00
|
|
|
mPrincipal(principalForSecurityCheck),
|
|
|
|
mForceWriteOnly(forceWriteOnly)
|
|
|
|
{
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
void Apply(gfxContext* ctx) {
|
|
|
|
ctx->SetPattern(mPattern);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-11-08 22:03:47 -08:00
|
|
|
nsIPrincipal* Principal() { return mPrincipal; }
|
2007-03-22 10:30:00 -07:00
|
|
|
PRBool GetForceWriteOnly() { return mForceWriteOnly; }
|
|
|
|
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
|
|
|
protected:
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxPattern> mPattern;
|
2007-11-08 22:03:47 -08:00
|
|
|
nsCOMPtr<nsIPrincipal> mPrincipal;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRPackedBool mForceWriteOnly;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPattern, NS_CANVASPATTERN_PRIVATE_IID)
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF(nsCanvasPattern)
|
|
|
|
NS_IMPL_RELEASE(nsCanvasPattern)
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsCanvasPattern)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsCanvasPattern)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)
|
|
|
|
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasPattern)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
2008-06-10 16:16:59 -07:00
|
|
|
/**
|
|
|
|
** nsTextMetrics
|
|
|
|
**/
|
|
|
|
#define NS_TEXTMETRICS_PRIVATE_IID \
|
|
|
|
{ 0xc5b1c2f9, 0xcb4f, 0x4394, { 0xaf, 0xe0, 0xc6, 0x59, 0x33, 0x80, 0x8b, 0xf3 } }
|
|
|
|
class nsTextMetrics : public nsIDOMTextMetrics
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
nsTextMetrics(float w) : width(w) { }
|
|
|
|
|
|
|
|
virtual ~nsTextMetrics() { }
|
|
|
|
|
|
|
|
NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICS_PRIVATE_IID)
|
|
|
|
|
|
|
|
NS_IMETHOD GetWidth(float* w) {
|
|
|
|
*w = width;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
|
|
|
private:
|
|
|
|
float width;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_DEFINE_STATIC_IID_ACCESSOR(nsTextMetrics, NS_TEXTMETRICS_PRIVATE_IID)
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF(nsTextMetrics)
|
|
|
|
NS_IMPL_RELEASE(nsTextMetrics)
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsTextMetrics)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsTextMetrics)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)
|
|
|
|
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(TextMetrics)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
struct nsCanvasBidiProcessor;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/**
|
|
|
|
** nsCanvasRenderingContext2D
|
|
|
|
**/
|
|
|
|
class nsCanvasRenderingContext2D :
|
|
|
|
public nsIDOMCanvasRenderingContext2D,
|
|
|
|
public nsICanvasRenderingContextInternal
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
nsCanvasRenderingContext2D();
|
|
|
|
virtual ~nsCanvasRenderingContext2D();
|
|
|
|
|
|
|
|
nsresult Redraw();
|
2008-07-23 10:50:03 -07:00
|
|
|
void SetThebesColor(nscolor c);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// nsICanvasRenderingContextInternal
|
|
|
|
NS_IMETHOD SetCanvasElement(nsICanvasElement* aParentCanvas);
|
|
|
|
NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
|
2007-09-25 13:46:08 -07:00
|
|
|
NS_IMETHOD Render(gfxContext *ctx);
|
|
|
|
NS_IMETHOD GetInputStream(const char* aMimeType,
|
|
|
|
const PRUnichar* aEncoderOptions,
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIInputStream **aStream);
|
2008-04-30 10:04:46 -07:00
|
|
|
NS_IMETHOD GetThebesSurface(gfxASurface **surface);
|
2008-06-03 17:00:32 -07:00
|
|
|
NS_IMETHOD SetIsOpaque(PRBool isOpaque);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// nsISupports interface
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
|
|
|
// nsIDOMCanvasRenderingContext2D interface
|
|
|
|
NS_DECL_NSIDOMCANVASRENDERINGCONTEXT2D
|
|
|
|
|
|
|
|
protected:
|
2008-07-23 10:50:03 -07:00
|
|
|
// destroy thebes/image stuff, in preparation for possibly recreating
|
2007-03-22 10:30:00 -07:00
|
|
|
void Destroy();
|
|
|
|
|
|
|
|
// Some helpers. Doesn't modify acolor on failure.
|
2008-07-23 10:50:03 -07:00
|
|
|
enum Style {
|
2007-03-22 10:30:00 -07:00
|
|
|
STYLE_STROKE = 0,
|
|
|
|
STYLE_FILL,
|
2008-07-23 10:50:03 -07:00
|
|
|
STYLE_SHADOW,
|
|
|
|
STYLE_MAX
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsresult SetStyleFromVariant(nsIVariant* aStyle, Style aWhichStyle);
|
2007-03-22 10:30:00 -07:00
|
|
|
void StyleColorToString(const nscolor& aColor, nsAString& aStr);
|
|
|
|
|
|
|
|
void DirtyAllStyles();
|
2008-07-23 10:50:03 -07:00
|
|
|
void ApplyStyle(Style aWhichStyle);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-08 22:03:47 -08:00
|
|
|
// If aPrincipal is not subsumed by this canvas element, then
|
2007-03-22 10:30:00 -07:00
|
|
|
// we make the canvas write-only so bad guys can't extract the pixel
|
|
|
|
// data. If forceWriteOnly is set, we force write only to be set
|
2007-11-08 22:03:47 -08:00
|
|
|
// and ignore aPrincipal. (This is used for when the original data came
|
2007-03-22 10:30:00 -07:00
|
|
|
// from a <canvas> that had write-only set.)
|
2007-11-08 22:03:47 -08:00
|
|
|
void DoDrawImageSecurityCheck(nsIPrincipal* aPrincipal,
|
|
|
|
PRBool forceWriteOnly);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Member vars
|
|
|
|
PRInt32 mWidth, mHeight;
|
2008-06-03 17:00:32 -07:00
|
|
|
PRPackedBool mValid;
|
|
|
|
PRPackedBool mOpaque;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// the canvas element informs us when it's going away,
|
|
|
|
// so these are not nsCOMPtrs
|
|
|
|
nsICanvasElement* mCanvasElement;
|
|
|
|
|
|
|
|
// our CSS parser, for colors and whatnot
|
|
|
|
nsCOMPtr<nsICSSParser> mCSSParser;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
// yay thebes
|
|
|
|
nsRefPtr<gfxContext> mThebes;
|
|
|
|
nsRefPtr<gfxASurface> mSurface;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
PRUint32 mSaveCount;
|
2008-07-23 10:50:03 -07:00
|
|
|
|
2008-08-04 10:38:09 -07:00
|
|
|
/**
|
|
|
|
* Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
|
|
|
|
* Redraw is called, reset to false when Render is called.
|
|
|
|
*/
|
|
|
|
PRBool mIsFrameInvalid;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
/**
|
|
|
|
* Draws a rectangle in the given style; used by FillRect and StrokeRect.
|
|
|
|
*/
|
|
|
|
nsresult DrawRect(const gfxRect& rect, Style style);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-06-10 16:16:59 -07:00
|
|
|
// text
|
|
|
|
enum TextAlign {
|
|
|
|
TEXT_ALIGN_START,
|
|
|
|
TEXT_ALIGN_END,
|
|
|
|
TEXT_ALIGN_LEFT,
|
|
|
|
TEXT_ALIGN_RIGHT,
|
|
|
|
TEXT_ALIGN_CENTER
|
|
|
|
};
|
|
|
|
|
|
|
|
enum TextBaseline {
|
|
|
|
TEXT_BASELINE_TOP,
|
|
|
|
TEXT_BASELINE_HANGING,
|
|
|
|
TEXT_BASELINE_MIDDLE,
|
|
|
|
TEXT_BASELINE_ALPHABETIC,
|
|
|
|
TEXT_BASELINE_IDEOGRAPHIC,
|
|
|
|
TEXT_BASELINE_BOTTOM
|
|
|
|
};
|
|
|
|
|
2007-07-25 11:21:34 -07:00
|
|
|
gfxFontGroup *GetCurrentFontStyle();
|
2008-06-10 16:16:59 -07:00
|
|
|
|
|
|
|
enum TextDrawOperation {
|
|
|
|
TEXT_DRAW_OPERATION_FILL,
|
2008-07-18 11:29:06 -07:00
|
|
|
TEXT_DRAW_OPERATION_STROKE,
|
|
|
|
TEXT_DRAW_OPERATION_MEASURE
|
2008-06-10 16:16:59 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2008-07-18 11:29:06 -07:00
|
|
|
* Implementation of the fillText, strokeText, and measure functions with
|
|
|
|
* the operation abstracted to a flag.
|
2008-06-10 16:16:59 -07:00
|
|
|
*/
|
2008-07-18 11:29:06 -07:00
|
|
|
nsresult DrawOrMeasureText(const nsAString& text,
|
|
|
|
float x,
|
|
|
|
float y,
|
|
|
|
float maxWidth,
|
|
|
|
TextDrawOperation op,
|
|
|
|
float* aWidth);
|
2007-07-25 11:21:34 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// style handling
|
2008-07-23 10:50:03 -07:00
|
|
|
/*
|
|
|
|
* The previous set style. Is equal to STYLE_MAX when there is no valid
|
|
|
|
* previous style.
|
|
|
|
*/
|
|
|
|
Style mLastStyle;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRPackedBool mDirtyStyle[STYLE_MAX];
|
|
|
|
|
|
|
|
// state stack handling
|
|
|
|
class ContextState {
|
|
|
|
public:
|
2008-06-10 16:16:59 -07:00
|
|
|
ContextState() : globalAlpha(1.0),
|
|
|
|
textAlign(TEXT_ALIGN_START),
|
|
|
|
textBaseline(TEXT_BASELINE_ALPHABETIC) { }
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
ContextState(const ContextState& other)
|
2008-06-10 16:16:59 -07:00
|
|
|
: globalAlpha(other.globalAlpha),
|
|
|
|
font(other.font),
|
|
|
|
fontGroup(other.fontGroup),
|
|
|
|
textAlign(other.textAlign),
|
|
|
|
textBaseline(other.textBaseline)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
for (int i = 0; i < STYLE_MAX; i++) {
|
|
|
|
colorStyles[i] = other.colorStyles[i];
|
|
|
|
gradientStyles[i] = other.gradientStyles[i];
|
|
|
|
patternStyles[i] = other.patternStyles[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
inline void SetColorStyle(Style whichStyle, nscolor color) {
|
2007-03-22 10:30:00 -07:00
|
|
|
colorStyles[whichStyle] = color;
|
|
|
|
gradientStyles[whichStyle] = nsnull;
|
|
|
|
patternStyles[whichStyle] = nsnull;
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
inline void SetPatternStyle(Style whichStyle, nsCanvasPattern* pat) {
|
2007-03-22 10:30:00 -07:00
|
|
|
gradientStyles[whichStyle] = nsnull;
|
|
|
|
patternStyles[whichStyle] = pat;
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
inline void SetGradientStyle(Style whichStyle, nsCanvasGradient* grad) {
|
2007-03-22 10:30:00 -07:00
|
|
|
gradientStyles[whichStyle] = grad;
|
|
|
|
patternStyles[whichStyle] = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
float globalAlpha;
|
2008-06-10 16:16:59 -07:00
|
|
|
|
|
|
|
nsString font;
|
|
|
|
nsRefPtr<gfxFontGroup> fontGroup;
|
|
|
|
TextAlign textAlign;
|
|
|
|
TextBaseline textBaseline;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nscolor colorStyles[STYLE_MAX];
|
|
|
|
nsCOMPtr<nsCanvasGradient> gradientStyles[STYLE_MAX];
|
|
|
|
nsCOMPtr<nsCanvasPattern> patternStyles[STYLE_MAX];
|
|
|
|
};
|
|
|
|
|
|
|
|
nsTArray<ContextState> mStyleStack;
|
|
|
|
|
|
|
|
inline ContextState& CurrentState() {
|
|
|
|
return mStyleStack[mSaveCount];
|
|
|
|
}
|
|
|
|
|
|
|
|
// stolen from nsJSUtils
|
|
|
|
static PRBool ConvertJSValToUint32(PRUint32* aProp, JSContext* aContext,
|
|
|
|
jsval aValue);
|
|
|
|
static PRBool ConvertJSValToXPCObject(nsISupports** aSupports, REFNSIID aIID,
|
|
|
|
JSContext* aContext, jsval aValue);
|
|
|
|
static PRBool ConvertJSValToDouble(double* aProp, JSContext* aContext,
|
|
|
|
jsval aValue);
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
// thebes helpers
|
|
|
|
nsresult ThebesSurfaceFromElement(nsIDOMElement *imgElt,
|
|
|
|
PRBool forceCopy,
|
|
|
|
gfxASurface **aSurface,
|
|
|
|
PRInt32 *widthOut, PRInt32 *heightOut,
|
|
|
|
nsIPrincipal **prinOut,
|
|
|
|
PRBool *forceWriteOnlyOut);
|
2008-03-06 11:56:47 -08:00
|
|
|
|
|
|
|
// other helpers
|
|
|
|
void GetAppUnitsValues(PRUint32 *perDevPixel, PRUint32 *perCSSPixel) {
|
|
|
|
// If we don't have a canvas element, we just return something generic.
|
|
|
|
PRUint32 devPixel = 60;
|
|
|
|
PRUint32 cssPixel = 60;
|
|
|
|
|
|
|
|
nsCOMPtr<nsINode> elem = do_QueryInterface(mCanvasElement);
|
|
|
|
if (elem) {
|
|
|
|
nsIDocument *doc = elem->GetOwnerDoc();
|
|
|
|
if (!doc) goto FINISH;
|
|
|
|
nsIPresShell *ps = doc->GetPrimaryShell();
|
|
|
|
if (!ps) goto FINISH;
|
|
|
|
nsPresContext *pc = ps->GetPresContext();
|
|
|
|
if (!pc) goto FINISH;
|
|
|
|
devPixel = pc->AppUnitsPerDevPixel();
|
|
|
|
cssPixel = pc->AppUnitsPerCSSPixel();
|
|
|
|
}
|
|
|
|
|
|
|
|
FINISH:
|
|
|
|
if (perDevPixel)
|
|
|
|
*perDevPixel = devPixel;
|
2008-03-06 14:03:55 -08:00
|
|
|
if (perCSSPixel)
|
2008-03-06 11:56:47 -08:00
|
|
|
*perCSSPixel = cssPixel;
|
|
|
|
}
|
2008-07-18 11:29:06 -07:00
|
|
|
|
|
|
|
friend struct nsCanvasBidiProcessor;
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF(nsCanvasRenderingContext2D)
|
|
|
|
NS_IMPL_RELEASE(nsCanvasRenderingContext2D)
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsCanvasRenderingContext2D)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasRenderingContext2D)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
|
|
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMCanvasRenderingContext2D)
|
|
|
|
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasRenderingContext2D)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
/**
|
|
|
|
** CanvasRenderingContext2D impl
|
|
|
|
**/
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
NS_NewCanvasRenderingContext2D(nsIDOMCanvasRenderingContext2D** aResult)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<nsIDOMCanvasRenderingContext2D> ctx = new nsCanvasRenderingContext2D();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!ctx)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
*aResult = ctx.forget().get();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCanvasRenderingContext2D::nsCanvasRenderingContext2D()
|
2008-06-03 17:00:32 -07:00
|
|
|
: mValid(PR_FALSE), mOpaque(PR_FALSE), mCanvasElement(nsnull),
|
2008-08-04 10:38:09 -07:00
|
|
|
mSaveCount(0), mIsFrameInvalid(PR_FALSE), mStyleStack(20)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCanvasRenderingContext2D::~nsCanvasRenderingContext2D()
|
|
|
|
{
|
|
|
|
Destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsCanvasRenderingContext2D::Destroy()
|
|
|
|
{
|
2008-02-13 22:53:25 -08:00
|
|
|
mSurface = nsnull;
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes = nsnull;
|
2008-02-13 22:53:25 -08:00
|
|
|
mValid = PR_FALSE;
|
2008-08-04 10:38:09 -07:00
|
|
|
mIsFrameInvalid = PR_FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2008-07-23 10:50:03 -07:00
|
|
|
nsCanvasRenderingContext2D::SetStyleFromVariant(nsIVariant* aStyle, Style aWhichStyle)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
nscolor color;
|
|
|
|
|
|
|
|
PRUint16 paramType;
|
|
|
|
rv = aStyle->GetDataType(¶mType);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (paramType == nsIDataType::VTYPE_DOMSTRING ||
|
|
|
|
paramType == nsIDataType::VTYPE_WSTRING_SIZE_IS) {
|
|
|
|
nsAutoString str;
|
|
|
|
|
|
|
|
if (paramType == nsIDataType::VTYPE_DOMSTRING) {
|
|
|
|
rv = aStyle->GetAsDOMString(str);
|
|
|
|
} else {
|
|
|
|
rv = aStyle->GetAsAString(str);
|
|
|
|
}
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2008-02-21 00:37:27 -08:00
|
|
|
rv = mCSSParser->ParseColorString(str, nsnull, 0, &color);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
// Error reporting happens inside the CSS parser
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
CurrentState().SetColorStyle(aWhichStyle, color);
|
|
|
|
|
|
|
|
mDirtyStyle[aWhichStyle] = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
} else if (paramType == nsIDataType::VTYPE_INTERFACE ||
|
|
|
|
paramType == nsIDataType::VTYPE_INTERFACE_IS)
|
|
|
|
{
|
|
|
|
nsID *iid;
|
|
|
|
nsCOMPtr<nsISupports> iface;
|
|
|
|
rv = aStyle->GetAsInterface(&iid, getter_AddRefs(iface));
|
|
|
|
|
|
|
|
nsCOMPtr<nsCanvasGradient> grad(do_QueryInterface(iface));
|
|
|
|
if (grad) {
|
|
|
|
CurrentState().SetGradientStyle(aWhichStyle, grad);
|
|
|
|
mDirtyStyle[aWhichStyle] = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsCanvasPattern> pattern(do_QueryInterface(iface));
|
|
|
|
if (pattern) {
|
|
|
|
CurrentState().SetPatternStyle(aWhichStyle, pattern);
|
|
|
|
mDirtyStyle[aWhichStyle] = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsContentUtils::ReportToConsole(
|
|
|
|
nsContentUtils::eDOM_PROPERTIES,
|
|
|
|
"UnexpectedCanvasVariantStyle",
|
|
|
|
nsnull, 0,
|
|
|
|
nsnull,
|
|
|
|
EmptyString(), 0, 0,
|
|
|
|
nsIScriptError::warningFlag,
|
|
|
|
"Canvas");
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsCanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
|
|
|
|
{
|
|
|
|
if (NS_GET_A(aColor) == 255) {
|
|
|
|
CopyUTF8toUTF16(nsPrintfCString(100, "#%02x%02x%02x",
|
|
|
|
NS_GET_R(aColor),
|
|
|
|
NS_GET_G(aColor),
|
|
|
|
NS_GET_B(aColor)),
|
|
|
|
aStr);
|
|
|
|
} else {
|
|
|
|
// "%0.5f" in nsPrintfCString would use the locale-specific
|
|
|
|
// decimal separator. That's why we have to do this:
|
|
|
|
PRUint32 alpha = NS_GET_A(aColor) * 100000 / 255;
|
|
|
|
CopyUTF8toUTF16(nsPrintfCString(100, "rgba(%d, %d, %d, 0.%d)",
|
|
|
|
NS_GET_R(aColor),
|
|
|
|
NS_GET_G(aColor),
|
|
|
|
NS_GET_B(aColor),
|
|
|
|
alpha),
|
|
|
|
aStr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsCanvasRenderingContext2D::DirtyAllStyles()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < STYLE_MAX; i++) {
|
|
|
|
mDirtyStyle[i] = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-11-08 22:03:47 -08:00
|
|
|
nsCanvasRenderingContext2D::DoDrawImageSecurityCheck(nsIPrincipal* aPrincipal,
|
|
|
|
PRBool forceWriteOnly)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-03-06 11:56:47 -08:00
|
|
|
// Callers should ensure that mCanvasElement is non-null before calling this
|
|
|
|
if (!mCanvasElement) {
|
|
|
|
NS_WARNING("DoDrawImageSecurityCheck called without canvas element!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mCanvasElement->IsWriteOnly())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If we explicitly set WriteOnly just do it and get out
|
|
|
|
if (forceWriteOnly) {
|
|
|
|
mCanvasElement->SetWriteOnly();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-09-06 16:45:57 -07:00
|
|
|
if (aPrincipal == nsnull)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsINode> elem = do_QueryInterface(mCanvasElement);
|
2007-11-08 22:03:47 -08:00
|
|
|
if (elem) { // XXXbz How could this actually be null?
|
|
|
|
PRBool subsumes;
|
|
|
|
nsresult rv =
|
|
|
|
elem->NodePrincipal()->Subsumes(aPrincipal, &subsumes);
|
2007-07-20 20:11:15 -07:00
|
|
|
|
2007-11-08 22:03:47 -08:00
|
|
|
if (NS_SUCCEEDED(rv) && subsumes) {
|
|
|
|
// This canvas has access to that image anyway
|
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2008-09-06 16:45:57 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mCanvasElement->SetWriteOnly();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-07-23 10:50:03 -07:00
|
|
|
nsCanvasRenderingContext2D::ApplyStyle(Style aWhichStyle)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
if (mLastStyle == aWhichStyle &&
|
|
|
|
!mDirtyStyle[aWhichStyle])
|
|
|
|
{
|
|
|
|
// nothing to do, this is already the set style
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mDirtyStyle[aWhichStyle] = PR_FALSE;
|
|
|
|
mLastStyle = aWhichStyle;
|
|
|
|
|
|
|
|
nsCanvasPattern* pattern = CurrentState().patternStyles[aWhichStyle];
|
|
|
|
if (pattern) {
|
2008-03-06 11:56:47 -08:00
|
|
|
if (!mCanvasElement)
|
|
|
|
return;
|
|
|
|
|
2007-11-08 22:03:47 -08:00
|
|
|
DoDrawImageSecurityCheck(pattern->Principal(),
|
|
|
|
pattern->GetForceWriteOnly());
|
2008-07-23 10:50:03 -07:00
|
|
|
pattern->Apply(mThebes);
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CurrentState().gradientStyles[aWhichStyle]) {
|
2008-07-23 10:50:03 -07:00
|
|
|
CurrentState().gradientStyles[aWhichStyle]->Apply(mThebes);
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
SetThebesColor(CurrentState().colorStyles[aWhichStyle]);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsCanvasRenderingContext2D::Redraw()
|
|
|
|
{
|
2007-09-25 13:46:08 -07:00
|
|
|
if (!mCanvasElement)
|
2008-07-23 10:50:03 -07:00
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-08-04 10:38:09 -07:00
|
|
|
if (!mIsFrameInvalid) {
|
|
|
|
mIsFrameInvalid = PR_TRUE;
|
|
|
|
return mCanvasElement->InvalidateFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-07-23 10:50:03 -07:00
|
|
|
nsCanvasRenderingContext2D::SetThebesColor(nscolor c)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxRGBA color(c);
|
|
|
|
color.a *= CurrentState().globalAlpha;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetColor(color);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height)
|
|
|
|
{
|
|
|
|
Destroy();
|
|
|
|
|
|
|
|
mWidth = width;
|
|
|
|
mHeight = height;
|
|
|
|
|
2008-02-13 22:53:25 -08:00
|
|
|
// Check that the dimensions are sane
|
|
|
|
if (gfxASurface::CheckSurfaceSize(gfxIntSize(width, height), 0xffff)) {
|
2008-06-03 17:00:32 -07:00
|
|
|
gfxASurface::gfxImageFormat format = gfxASurface::ImageFormatARGB32;
|
|
|
|
if (mOpaque)
|
|
|
|
format = gfxASurface::ImageFormatRGB24;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface
|
2008-06-03 17:00:32 -07:00
|
|
|
(gfxIntSize(width, height), format);
|
2008-02-13 22:53:25 -08:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
if (mSurface->CairoStatus() == 0) {
|
|
|
|
mThebes = new gfxContext(mSurface);
|
2008-02-13 22:53:25 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create dummy surfaces here */
|
2008-07-23 10:50:03 -07:00
|
|
|
if (mSurface == nsnull || mSurface->CairoStatus() != 0 ||
|
|
|
|
mThebes == nsnull || mThebes->HasError())
|
2008-02-13 22:53:25 -08:00
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
mSurface = new gfxImageSurface(gfxIntSize(1,1), gfxASurface::ImageFormatARGB32);
|
|
|
|
mThebes = new gfxContext(mSurface);
|
2008-02-13 22:53:25 -08:00
|
|
|
} else {
|
|
|
|
mValid = PR_TRUE;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// set up the initial canvas defaults
|
|
|
|
mStyleStack.Clear();
|
|
|
|
mSaveCount = 0;
|
|
|
|
|
|
|
|
ContextState *state = mStyleStack.AppendElement();
|
|
|
|
state->globalAlpha = 1.0;
|
|
|
|
for (int i = 0; i < STYLE_MAX; i++)
|
|
|
|
state->colorStyles[i] = NS_RGB(0,0,0);
|
2008-07-23 10:50:03 -07:00
|
|
|
mLastStyle = STYLE_MAX;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
DirtyAllStyles();
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);
|
|
|
|
mThebes->NewPath();
|
|
|
|
mThebes->Rectangle(gfxRect(0, 0, mWidth, mHeight));
|
|
|
|
mThebes->Fill();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetLineWidth(1.0);
|
|
|
|
mThebes->SetOperator(gfxContext::OPERATOR_OVER);
|
|
|
|
mThebes->SetMiterLimit(10.0);
|
|
|
|
mThebes->SetLineCap(gfxContext::LINE_CAP_BUTT);
|
|
|
|
mThebes->SetLineJoin(gfxContext::LINE_JOIN_MITER);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->NewPath();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2008-06-03 17:00:32 -07:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetIsOpaque(PRBool isOpaque)
|
|
|
|
{
|
|
|
|
if (isOpaque == mOpaque)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
mOpaque = isOpaque;
|
|
|
|
|
|
|
|
if (mValid) {
|
|
|
|
/* If we've already been created, let SetDimensions take care of
|
|
|
|
* recreating our surface
|
|
|
|
*/
|
|
|
|
return SetDimensions(mWidth, mHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2007-09-25 13:46:08 -07:00
|
|
|
nsCanvasRenderingContext2D::Render(gfxContext *ctx)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
if (!mValid || !mSurface ||
|
|
|
|
mSurface->CairoStatus() ||
|
|
|
|
mThebes->HasError())
|
2007-06-25 09:24:19 -07:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
if (!mSurface)
|
2007-06-25 09:24:19 -07:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxPattern> pat = new gfxPattern(mSurface);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-06-03 17:00:32 -07:00
|
|
|
gfxContext::GraphicsOperator op = ctx->CurrentOperator();
|
|
|
|
if (mOpaque)
|
|
|
|
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
|
|
|
|
// pixel alignment for this stuff!
|
|
|
|
ctx->NewPath();
|
|
|
|
ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
|
|
|
|
ctx->Fill();
|
|
|
|
|
2008-06-03 17:00:32 -07:00
|
|
|
if (mOpaque)
|
|
|
|
ctx->SetOperator(op);
|
|
|
|
|
2008-08-04 10:38:09 -07:00
|
|
|
mIsFrameInvalid = PR_FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2007-09-25 13:46:08 -07:00
|
|
|
nsCanvasRenderingContext2D::GetInputStream(const char *aMimeType,
|
|
|
|
const PRUnichar *aEncoderOptions,
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIInputStream **aStream)
|
|
|
|
{
|
2008-02-13 22:53:25 -08:00
|
|
|
if (!mValid || !mSurface ||
|
2008-07-23 10:50:03 -07:00
|
|
|
mSurface->CairoStatus() ||
|
|
|
|
mThebes->HasError())
|
2007-06-25 09:24:19 -07:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2007-12-21 01:19:15 -08:00
|
|
|
nsresult rv;
|
2007-09-25 13:46:08 -07:00
|
|
|
const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
|
|
|
|
nsAutoArrayPtr<char> conid(new (std::nothrow) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
|
2008-02-13 22:53:25 -08:00
|
|
|
|
|
|
|
if (!conid)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
2007-09-25 13:46:08 -07:00
|
|
|
strcpy(conid, encoderPrefix);
|
|
|
|
strcat(conid, aMimeType);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-09-25 13:46:08 -07:00
|
|
|
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!encoder)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-02-13 22:53:25 -08:00
|
|
|
nsAutoArrayPtr<PRUint8> imageBuffer(new (std::nothrow) PRUint8[mWidth * mHeight * 4]);
|
|
|
|
if (!imageBuffer)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(imageBuffer.get(),
|
|
|
|
gfxIntSize(mWidth, mHeight),
|
|
|
|
mWidth * 4,
|
|
|
|
gfxASurface::ImageFormatARGB32);
|
|
|
|
|
|
|
|
if (!imgsurf || imgsurf->CairoStatus())
|
2008-02-13 22:53:25 -08:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
|
|
|
|
|
|
|
|
if (!ctx || ctx->HasError())
|
|
|
|
return NS_ERROR_FAILURE;
|
2008-02-13 22:53:25 -08:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
|
|
ctx->SetSource(mSurface, gfxPoint(0, 0));
|
|
|
|
ctx->Paint();
|
2008-02-13 22:53:25 -08:00
|
|
|
|
|
|
|
rv = encoder->InitFromData(imageBuffer.get(),
|
|
|
|
mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
|
|
|
|
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
|
|
|
nsDependentString(aEncoderOptions));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return CallQueryInterface(encoder, aStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// nsCanvasRenderingContext2D impl
|
|
|
|
//
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetCanvasElement(nsICanvasElement* aCanvasElement)
|
|
|
|
{
|
|
|
|
// don't hold a ref to this!
|
|
|
|
mCanvasElement = aCanvasElement;
|
|
|
|
|
|
|
|
// set up our css parser, if necessary
|
|
|
|
if (!mCSSParser) {
|
|
|
|
mCSSParser = do_CreateInstance("@mozilla.org/content/css-parser;1");
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetCanvas(nsIDOMHTMLCanvasElement **canvas)
|
|
|
|
{
|
|
|
|
if (mCanvasElement == nsnull) {
|
|
|
|
*canvas = nsnull;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CallQueryInterface(mCanvasElement, canvas);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// state
|
|
|
|
//
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Save()
|
|
|
|
{
|
|
|
|
ContextState state = CurrentState();
|
|
|
|
mStyleStack.AppendElement(state);
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Save();
|
2007-03-22 10:30:00 -07:00
|
|
|
mSaveCount++;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Restore()
|
|
|
|
{
|
2008-01-29 20:37:11 -08:00
|
|
|
if (mSaveCount == 0)
|
|
|
|
return NS_OK;
|
|
|
|
if (mSaveCount < 0)
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
|
|
|
|
|
|
mStyleStack.RemoveElementAt(mSaveCount);
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Restore();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mLastStyle = STYLE_MAX;
|
2007-03-22 10:30:00 -07:00
|
|
|
DirtyAllStyles();
|
|
|
|
|
|
|
|
mSaveCount--;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// transformations
|
|
|
|
//
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Scale(float x, float y)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x,y))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Scale(x, y);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Rotate(float angle)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(angle))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Rotate(angle);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Translate(float x, float y)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x,y))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Translate(gfxPoint(x, y));
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Transform(float m11, float m12, float m21, float m22, float dx, float dy)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(m11,m12,m21,m22,dx,dy))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxMatrix matrix(m11, m12, m21, m22, dx, dy);
|
|
|
|
mThebes->Multiply(matrix);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetTransform(float m11, float m12, float m21, float m22, float dx, float dy)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(m11,m12,m21,m22,dx,dy))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxMatrix matrix(m11, m12, m21, m22, dx, dy);
|
|
|
|
mThebes->SetMatrix(matrix);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// colors
|
|
|
|
//
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetGlobalAlpha(float aGlobalAlpha)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(aGlobalAlpha))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
// ignore invalid values, as per spec
|
|
|
|
if (aGlobalAlpha < 0.0 || aGlobalAlpha > 1.0)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
CurrentState().globalAlpha = aGlobalAlpha;
|
2007-11-07 01:19:22 -08:00
|
|
|
DirtyAllStyles();
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetGlobalAlpha(float *aGlobalAlpha)
|
|
|
|
{
|
|
|
|
*aGlobalAlpha = CurrentState().globalAlpha;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetStrokeStyle(nsIVariant* aStyle)
|
|
|
|
{
|
|
|
|
return SetStyleFromVariant(aStyle, STYLE_STROKE);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetStrokeStyle(nsIVariant** aStyle)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWritableVariant> var = do_CreateInstance("@mozilla.org/variant;1");
|
|
|
|
if (!var)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
rv = var->SetWritable(PR_TRUE);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (CurrentState().patternStyles[STYLE_STROKE]) {
|
|
|
|
rv = var->SetAsISupports(CurrentState().patternStyles[STYLE_STROKE]);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else if (CurrentState().gradientStyles[STYLE_STROKE]) {
|
|
|
|
rv = var->SetAsISupports(CurrentState().gradientStyles[STYLE_STROKE]);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else {
|
|
|
|
nsString styleStr;
|
|
|
|
StyleColorToString(CurrentState().colorStyles[STYLE_STROKE], styleStr);
|
|
|
|
|
|
|
|
rv = var->SetAsDOMString(styleStr);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
*aStyle = var.forget().get();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetFillStyle(nsIVariant* aStyle)
|
|
|
|
{
|
|
|
|
return SetStyleFromVariant(aStyle, STYLE_FILL);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetFillStyle(nsIVariant** aStyle)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWritableVariant> var = do_CreateInstance("@mozilla.org/variant;1");
|
|
|
|
if (!var)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
rv = var->SetWritable(PR_TRUE);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (CurrentState().patternStyles[STYLE_FILL]) {
|
|
|
|
rv = var->SetAsISupports(CurrentState().patternStyles[STYLE_FILL]);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else if (CurrentState().gradientStyles[STYLE_FILL]) {
|
|
|
|
rv = var->SetAsISupports(CurrentState().gradientStyles[STYLE_FILL]);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else {
|
|
|
|
nsString styleStr;
|
|
|
|
StyleColorToString(CurrentState().colorStyles[STYLE_FILL], styleStr);
|
|
|
|
|
|
|
|
rv = var->SetAsDOMString(styleStr);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
*aStyle = var.forget().get();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// gradients and patterns
|
|
|
|
//
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::CreateLinearGradient(float x0, float y0, float x1, float y1,
|
|
|
|
nsIDOMCanvasGradient **_retval)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x0,y0,x1,y1))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxPattern> gradpat = new gfxPattern(x0, y0, x1, y1);
|
|
|
|
if (!gradpat)
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<nsIDOMCanvasGradient> grad = new nsCanvasGradient(gradpat, mCSSParser);
|
|
|
|
if (!grad)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
*_retval = grad.forget().get();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::CreateRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1,
|
|
|
|
nsIDOMCanvasGradient **_retval)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x0,y0,r0,x1,y1,r1))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxPattern> gradpat = new gfxPattern(x0, y0, r0, x1, y1, r1);
|
|
|
|
if (!gradpat)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
nsRefPtr<nsIDOMCanvasGradient> grad = new nsCanvasGradient(gradpat, mCSSParser);
|
|
|
|
if (!grad)
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
*_retval = grad.forget().get();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::CreatePattern(nsIDOMHTMLElement *image,
|
|
|
|
const nsAString& repeat,
|
|
|
|
nsIDOMCanvasPattern **_retval)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxPattern::GraphicsExtend extend;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
|
2008-07-23 10:50:03 -07:00
|
|
|
extend = gfxPattern::EXTEND_REPEAT;
|
2007-03-22 10:30:00 -07:00
|
|
|
} else if (repeat.EqualsLiteral("repeat-x")) {
|
|
|
|
// XX
|
2008-07-23 10:50:03 -07:00
|
|
|
extend = gfxPattern::EXTEND_REPEAT;
|
2007-03-22 10:30:00 -07:00
|
|
|
} else if (repeat.EqualsLiteral("repeat-y")) {
|
|
|
|
// XX
|
2008-07-23 10:50:03 -07:00
|
|
|
extend = gfxPattern::EXTEND_REPEAT;
|
2007-03-22 10:30:00 -07:00
|
|
|
} else if (repeat.EqualsLiteral("no-repeat")) {
|
2008-07-23 10:50:03 -07:00
|
|
|
extend = gfxPattern::EXTEND_NONE;
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 imgWidth, imgHeight;
|
2007-11-08 22:03:47 -08:00
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRBool forceWriteOnly = PR_FALSE;
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxASurface> imgsurf;
|
|
|
|
rv = ThebesSurfaceFromElement(image, PR_TRUE,
|
|
|
|
getter_AddRefs(imgsurf), &imgWidth, &imgHeight,
|
|
|
|
getter_AddRefs(principal), &forceWriteOnly);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxPattern> thebespat = new gfxPattern(imgsurf);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
thebespat->SetExtend(extend);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<nsCanvasPattern> pat = new nsCanvasPattern(thebespat, principal,
|
|
|
|
forceWriteOnly);
|
|
|
|
if (!pat)
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
*_retval = pat.forget().get();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// shadows
|
|
|
|
//
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetShadowOffsetX(float x)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetShadowOffsetX(float *x)
|
|
|
|
{
|
|
|
|
*x = 0.0f;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetShadowOffsetY(float y)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(y))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetShadowOffsetY(float *y)
|
|
|
|
{
|
|
|
|
*y = 0.0f;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetShadowBlur(float blur)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(blur))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetShadowBlur(float *blur)
|
|
|
|
{
|
|
|
|
*blur = 0.0f;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetShadowColor(const nsAString& color)
|
|
|
|
{
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetShadowColor(nsAString& color)
|
|
|
|
{
|
|
|
|
color.SetIsVoid(PR_TRUE);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// rects
|
|
|
|
//
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::ClearRect(float x, float y, float w, float h)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x,y,w,h))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxPath> path = mThebes->CopyPath();
|
|
|
|
|
|
|
|
mThebes->Save();
|
|
|
|
mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);
|
|
|
|
mThebes->NewPath();
|
|
|
|
mThebes->Rectangle(gfxRect(x, y, w, h));
|
|
|
|
mThebes->Fill();
|
|
|
|
mThebes->Restore();
|
|
|
|
|
|
|
|
mThebes->NewPath();
|
|
|
|
mThebes->AppendPath(path);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return Redraw();
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsresult
|
|
|
|
nsCanvasRenderingContext2D::DrawRect(const gfxRect& rect, Style style)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
if (!FloatValidate(rect.pos.x, rect.pos.y, rect.size.width, rect.size.height))
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxPath> path = mThebes->CopyPath();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->NewPath();
|
|
|
|
mThebes->Rectangle(rect);
|
|
|
|
|
|
|
|
ApplyStyle(style);
|
|
|
|
if (style == STYLE_FILL)
|
|
|
|
mThebes->Fill();
|
|
|
|
else // STYLE_STROKE
|
|
|
|
mThebes->Stroke();
|
|
|
|
|
|
|
|
mThebes->NewPath();
|
|
|
|
mThebes->AppendPath(path);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return Redraw();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2008-07-23 10:50:03 -07:00
|
|
|
nsCanvasRenderingContext2D::FillRect(float x, float y, float w, float h)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
return DrawRect(gfxRect(x, y, w, h), STYLE_FILL);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::StrokeRect(float x, float y, float w, float h)
|
|
|
|
{
|
|
|
|
return DrawRect(gfxRect(x, y, w, h), STYLE_STROKE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// path bits
|
|
|
|
//
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::BeginPath()
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->NewPath();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::ClosePath()
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->ClosePath();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Fill()
|
|
|
|
{
|
|
|
|
ApplyStyle(STYLE_FILL);
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Fill();
|
2007-03-22 10:30:00 -07:00
|
|
|
return Redraw();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Stroke()
|
|
|
|
{
|
|
|
|
ApplyStyle(STYLE_STROKE);
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Stroke();
|
2007-03-22 10:30:00 -07:00
|
|
|
return Redraw();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Clip()
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Clip();
|
2008-08-04 10:38:09 -07:00
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::MoveTo(float x, float y)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x,y))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->MoveTo(gfxPoint(x, y));
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::LineTo(float x, float y)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x,y))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->LineTo(gfxPoint(x, y));
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::QuadraticCurveTo(float cpx, float cpy, float x, float y)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(cpx,cpy,x,y))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
// we will always have a current point, since beginPath forces
|
|
|
|
// a moveto(0,0)
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxPoint c = mThebes->CurrentPoint();
|
|
|
|
gfxPoint p(x,y);
|
|
|
|
gfxPoint cp(cpx, cpy);
|
|
|
|
|
|
|
|
mThebes->CurveTo((c+cp*2)/3.0, (p+cp*2)/3.0, p);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::BezierCurveTo(float cp1x, float cp1y,
|
|
|
|
float cp2x, float cp2y,
|
|
|
|
float x, float y)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(cp1x,cp1y,cp2x,cp2y,x,y))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->CurveTo(gfxPoint(cp1x, cp1y),
|
|
|
|
gfxPoint(cp2x, cp2y),
|
|
|
|
gfxPoint(x, y));
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::ArcTo(float x1, float y1, float x2, float y2, float radius)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x1,y1,x2,y2,radius))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (radius <= 0)
|
|
|
|
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
|
|
|
|
|
|
|
/* This is an adaptation of the cairo_arc_to patch from Behdad
|
|
|
|
* Esfahbod; once that patch is accepted, we should remove this
|
|
|
|
* and just call cairo_arc_to() directly.
|
|
|
|
*/
|
|
|
|
|
|
|
|
double angle0, angle1, angle2, angled;
|
|
|
|
double d0, d2;
|
|
|
|
double sin_, cos_;
|
2008-07-23 10:50:03 -07:00
|
|
|
double dc;
|
2007-03-22 10:30:00 -07:00
|
|
|
int forward;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxPoint p0 = mThebes->CurrentPoint();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
angle0 = atan2 (p0.y - y1, p0.x - x1); /* angle from (x1,y1) to (p0.x,p0.y) */
|
2007-03-22 10:30:00 -07:00
|
|
|
angle2 = atan2 (y2 - y1, x2 - x1); /* angle from (x1,y1) to (x2,y2) */
|
|
|
|
angle1 = (angle0 + angle2) / 2; /* angle from (x1,y1) to (xc,yc) */
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
angled = angle2 - angle0; /* the angle (p0.x,p0.y)--(x1,y1)--(x2,y2) */
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* Shall we go forward or backward? */
|
|
|
|
if (angled > M_PI || (angled < 0 && angled > -M_PI)) {
|
|
|
|
angle1 += M_PI;
|
|
|
|
angled = 2 * M_PI - angled;
|
|
|
|
forward = 1;
|
|
|
|
} else {
|
|
|
|
double tmp;
|
|
|
|
tmp = angle0;
|
|
|
|
angle0 = angle2;
|
|
|
|
angle2 = tmp;
|
|
|
|
forward = 0;
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
angle0 += M_PI_2; /* angle from (xc,yc) to (p0.x,p0.y) */
|
2007-03-22 10:30:00 -07:00
|
|
|
angle2 -= M_PI_2; /* angle from (xc,yc) to (x2,y2) */
|
2008-07-23 10:50:03 -07:00
|
|
|
angled /= 2; /* the angle (p0.x,p0.y)--(x1,y1)--(xc,yc) */
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
/* distance from (x1,y1) to (p0.x,p0.y) */
|
|
|
|
d0 = sqrt ((p0.x-x1)*(p0.x-x1)+(p0.y-y1)*(p0.y-y1));
|
|
|
|
/* distance from (x2,y2) to (p0.x,p0.y) */
|
2007-03-22 10:30:00 -07:00
|
|
|
d2 = sqrt ((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
|
|
|
|
|
|
|
|
dc = -1;
|
|
|
|
sin_ = sin(angled);
|
|
|
|
cos_ = cos(angled);
|
|
|
|
if (fabs(cos_) >= 1e-5) { /* the arc may not fit */
|
|
|
|
/* min distance of end-points from corner */
|
|
|
|
double min_d = d0 < d2 ? d0 : d2;
|
|
|
|
/* max radius of an arc that fits */
|
|
|
|
double max_r = min_d * sin_ / cos_;
|
|
|
|
|
|
|
|
if (radius > max_r) {
|
|
|
|
/* arc with requested radius doesn't fit */
|
|
|
|
radius = (float) max_r;
|
|
|
|
dc = min_d / cos_; /* distance of (xc,yc) from (x1,y1) */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dc < 0)
|
|
|
|
dc = radius / sin_; /* distance of (xc,yc) from (x1,y1) */
|
|
|
|
|
|
|
|
|
|
|
|
/* find (cx,cy), the center of the arc */
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxPoint c(x1 + sin(angle1) * dc, y1 + cos(angle1) * dc);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
/* the arc operation draws the line from current point (p0.x,p0.y)
|
2007-03-22 10:30:00 -07:00
|
|
|
* to arc center too. */
|
|
|
|
|
|
|
|
if (forward)
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Arc(c, radius, angle0, angle2);
|
2007-03-22 10:30:00 -07:00
|
|
|
else
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->NegativeArc(c, radius, angle2, angle0);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->LineTo(gfxPoint(x2, y2));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Arc(float x, float y, float r, float startAngle, float endAngle, int ccw)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x,y,r,startAngle,endAngle))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxPoint p(x,y);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (ccw)
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->NegativeArc(p, r, startAngle, endAngle);
|
2007-03-22 10:30:00 -07:00
|
|
|
else
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Arc(p, r, startAngle, endAngle);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::Rect(float x, float y, float w, float h)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x,y,w,h))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Rectangle(gfxRect(x, y, w, h));
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-07-25 11:21:34 -07:00
|
|
|
//
|
|
|
|
// text
|
|
|
|
//
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
/**
|
|
|
|
* Helper function for SetFont that creates a style rule for the given font.
|
|
|
|
* @param aFont The CSS font string
|
|
|
|
* @param aCSSParser The CSS parser of the canvas rendering context
|
|
|
|
* @param aNode The canvas element
|
|
|
|
* @param aResult Pointer in which to place the new style rule.
|
|
|
|
* @remark Assumes all pointer arguments are non-null.
|
|
|
|
*/
|
|
|
|
static nsresult
|
|
|
|
CreateFontStyleRule(const nsAString& aFont,
|
|
|
|
nsICSSParser* aCSSParser,
|
|
|
|
nsINode* aNode,
|
|
|
|
nsICSSStyleRule** aResult)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
nsCOMPtr<nsICSSStyleRule> rule;
|
|
|
|
PRBool changed;
|
|
|
|
|
|
|
|
nsIPrincipal* principal = aNode->NodePrincipal();
|
|
|
|
nsIDocument* document = aNode->GetOwnerDoc();
|
|
|
|
|
|
|
|
nsIURI* docURL = document->GetDocumentURI();
|
|
|
|
nsIURI* baseURL = document->GetBaseURI();
|
|
|
|
|
|
|
|
rv = aCSSParser->ParseStyleAttribute(
|
|
|
|
EmptyString(),
|
|
|
|
docURL,
|
|
|
|
baseURL,
|
|
|
|
principal,
|
|
|
|
getter_AddRefs(rule));
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
rv = aCSSParser->ParseProperty(eCSSProperty_font,
|
|
|
|
aFont,
|
|
|
|
docURL,
|
|
|
|
baseURL,
|
|
|
|
principal,
|
|
|
|
rule->GetDeclaration(),
|
|
|
|
&changed);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
// set line height to normal, as per spec
|
|
|
|
rv = aCSSParser->ParseProperty(eCSSProperty_line_height,
|
|
|
|
NS_LITERAL_STRING("normal"),
|
|
|
|
docURL,
|
|
|
|
baseURL,
|
|
|
|
principal,
|
|
|
|
rule->GetDeclaration(),
|
|
|
|
&changed);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
rule.forget(aResult);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-07-25 11:21:34 -07:00
|
|
|
NS_IMETHODIMP
|
2008-06-10 16:16:59 -07:00
|
|
|
nsCanvasRenderingContext2D::SetFont(const nsAString& font)
|
2007-07-25 11:21:34 -07:00
|
|
|
{
|
2008-06-17 14:37:57 -07:00
|
|
|
nsresult rv;
|
2007-07-25 11:21:34 -07:00
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
/*
|
|
|
|
* If font is defined with relative units (e.g. ems) and the parent
|
|
|
|
* style context changes in between calls, setting the font to the
|
|
|
|
* same value as previous could result in a different computed value,
|
|
|
|
* so we cannot have the optimization where we check if the new font
|
|
|
|
* string is equal to the old one.
|
|
|
|
*/
|
2007-07-25 11:21:34 -07:00
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
|
|
|
|
if (!content) {
|
|
|
|
NS_WARNING("Canvas element must be an nsIContent and non-null");
|
2008-03-06 11:56:47 -08:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2007-07-25 11:21:34 -07:00
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
nsIDocument* document = content->GetOwnerDoc();
|
|
|
|
|
|
|
|
nsIPresShell* presShell = document->GetPrimaryShell();
|
2008-03-06 11:56:47 -08:00
|
|
|
if (!presShell)
|
|
|
|
return NS_ERROR_FAILURE;
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
nsCString langGroup;
|
2008-03-06 11:56:47 -08:00
|
|
|
presShell->GetPresContext()->GetLangGroup()->ToUTF8String(langGroup);
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
nsCOMArray<nsIStyleRule> rules;
|
|
|
|
|
|
|
|
nsCOMPtr<nsICSSStyleRule> rule;
|
2008-06-17 14:37:57 -07:00
|
|
|
rv = CreateFontStyleRule(font, mCSSParser.get(), content.get(), getter_AddRefs(rule));
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
rules.AppendObject(rule);
|
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
nsStyleSet* styleSet = presShell->StyleSet();
|
2007-07-25 11:21:34 -07:00
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
// have to get a parent style context for inherit-like relative
|
|
|
|
// values (2em, bolder, etc.)
|
|
|
|
nsRefPtr<nsStyleContext> parentContext;
|
|
|
|
|
|
|
|
if (content->IsInDoc()) {
|
|
|
|
// inherit from the canvas element
|
|
|
|
parentContext = nsInspectorCSSUtils::GetStyleContextForContent(
|
|
|
|
content,
|
|
|
|
nsnull,
|
|
|
|
presShell);
|
|
|
|
} else {
|
|
|
|
// otherwise inherit from default (10px sans-serif)
|
|
|
|
nsCOMPtr<nsICSSStyleRule> parentRule;
|
|
|
|
rv = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
|
|
|
|
mCSSParser.get(),
|
|
|
|
content.get(),
|
|
|
|
getter_AddRefs(parentRule));
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
nsCOMArray<nsIStyleRule> parentRules;
|
|
|
|
parentRules.AppendObject(parentRule);
|
|
|
|
parentContext = styleSet->ResolveStyleForRules(nsnull, parentRules);
|
2008-06-10 16:16:59 -07:00
|
|
|
}
|
2008-06-17 14:37:57 -07:00
|
|
|
|
|
|
|
if (!parentContext)
|
2008-06-10 16:16:59 -07:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
nsRefPtr<nsStyleContext> sc = styleSet->ResolveStyleForRules(parentContext, rules);
|
|
|
|
if (!sc)
|
|
|
|
return NS_ERROR_FAILURE;
|
2008-07-03 15:02:50 -07:00
|
|
|
const nsStyleFont* fontStyle = sc->GetStyleFont();
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
NS_ASSERTION(fontStyle, "Could not obtain font style");
|
|
|
|
|
2008-07-03 15:02:50 -07:00
|
|
|
// use CSS pixels instead of dev pixels to avoid being affected by page zoom
|
|
|
|
const PRUint32 aupcp = nsPresContext::AppUnitsPerCSSPixel();
|
|
|
|
// un-zoom the font size to avoid being affected by text-only zoom
|
|
|
|
const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mFont.size);
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
gfxFontStyle style(fontStyle->mFont.style,
|
|
|
|
fontStyle->mFont.weight,
|
2008-07-03 15:02:50 -07:00
|
|
|
NSAppUnitsToFloatPixels(fontSize, aupcp),
|
2007-07-25 11:21:34 -07:00
|
|
|
langGroup,
|
|
|
|
fontStyle->mFont.sizeAdjust,
|
|
|
|
fontStyle->mFont.systemFont,
|
|
|
|
fontStyle->mFont.familyNameQuirks);
|
|
|
|
|
2008-06-10 16:16:59 -07:00
|
|
|
CurrentState().fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name, &style);
|
|
|
|
NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
|
|
|
|
CurrentState().font = font;
|
2007-07-25 11:21:34 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2008-06-10 16:16:59 -07:00
|
|
|
nsCanvasRenderingContext2D::GetFont(nsAString& font)
|
2007-07-25 11:21:34 -07:00
|
|
|
{
|
2008-06-10 16:16:59 -07:00
|
|
|
/* will initilize the value if not set, else does nothing */
|
|
|
|
GetCurrentFontStyle();
|
|
|
|
|
|
|
|
font = CurrentState().font;
|
2007-07-25 11:21:34 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-06-10 16:16:59 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetTextAlign(const nsAString& ta)
|
|
|
|
{
|
|
|
|
if (ta.EqualsLiteral("start"))
|
|
|
|
CurrentState().textAlign = TEXT_ALIGN_START;
|
|
|
|
else if (ta.EqualsLiteral("end"))
|
|
|
|
CurrentState().textAlign = TEXT_ALIGN_END;
|
|
|
|
else if (ta.EqualsLiteral("left"))
|
|
|
|
CurrentState().textAlign = TEXT_ALIGN_LEFT;
|
|
|
|
else if (ta.EqualsLiteral("right"))
|
|
|
|
CurrentState().textAlign = TEXT_ALIGN_RIGHT;
|
|
|
|
else if (ta.EqualsLiteral("center"))
|
|
|
|
CurrentState().textAlign = TEXT_ALIGN_CENTER;
|
|
|
|
// spec says to not throw error for invalid arg, but do it anyway
|
|
|
|
else
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetTextAlign(nsAString& ta)
|
2007-07-25 11:21:34 -07:00
|
|
|
{
|
2008-06-10 16:16:59 -07:00
|
|
|
switch (CurrentState().textAlign)
|
2007-07-25 11:21:34 -07:00
|
|
|
{
|
2008-06-10 16:16:59 -07:00
|
|
|
case TEXT_ALIGN_START:
|
|
|
|
ta.AssignLiteral("start");
|
|
|
|
break;
|
|
|
|
case TEXT_ALIGN_END:
|
|
|
|
ta.AssignLiteral("end");
|
|
|
|
break;
|
|
|
|
case TEXT_ALIGN_LEFT:
|
|
|
|
ta.AssignLiteral("left");
|
|
|
|
break;
|
|
|
|
case TEXT_ALIGN_RIGHT:
|
|
|
|
ta.AssignLiteral("right");
|
|
|
|
break;
|
|
|
|
case TEXT_ALIGN_CENTER:
|
|
|
|
ta.AssignLiteral("center");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NS_ASSERTION(0, "textAlign holds invalid value");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetTextBaseline(const nsAString& tb)
|
|
|
|
{
|
|
|
|
if (tb.EqualsLiteral("top"))
|
|
|
|
CurrentState().textBaseline = TEXT_BASELINE_TOP;
|
|
|
|
else if (tb.EqualsLiteral("hanging"))
|
|
|
|
CurrentState().textBaseline = TEXT_BASELINE_HANGING;
|
|
|
|
else if (tb.EqualsLiteral("middle"))
|
|
|
|
CurrentState().textBaseline = TEXT_BASELINE_MIDDLE;
|
|
|
|
else if (tb.EqualsLiteral("alphabetic"))
|
|
|
|
CurrentState().textBaseline = TEXT_BASELINE_ALPHABETIC;
|
|
|
|
else if (tb.EqualsLiteral("ideographic"))
|
|
|
|
CurrentState().textBaseline = TEXT_BASELINE_IDEOGRAPHIC;
|
|
|
|
else if (tb.EqualsLiteral("bottom"))
|
|
|
|
CurrentState().textBaseline = TEXT_BASELINE_BOTTOM;
|
|
|
|
// spec says to not throw error for invalid arg, but do it anyway
|
|
|
|
else
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetTextBaseline(nsAString& tb)
|
|
|
|
{
|
|
|
|
switch (CurrentState().textBaseline)
|
|
|
|
{
|
|
|
|
case TEXT_BASELINE_TOP:
|
|
|
|
tb.AssignLiteral("top");
|
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_HANGING:
|
|
|
|
tb.AssignLiteral("hanging");
|
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_MIDDLE:
|
|
|
|
tb.AssignLiteral("middle");
|
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_ALPHABETIC:
|
|
|
|
tb.AssignLiteral("alphabetic");
|
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_IDEOGRAPHIC:
|
|
|
|
tb.AssignLiteral("ideographic");
|
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_BOTTOM:
|
|
|
|
tb.AssignLiteral("bottom");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NS_ASSERTION(0, "textBaseline holds invalid value");
|
|
|
|
return NS_ERROR_FAILURE;
|
2007-07-25 11:21:34 -07:00
|
|
|
}
|
2008-06-10 16:16:59 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper function that replaces the whitespace characters in a string
|
|
|
|
* with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
|
|
|
|
* U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
|
|
|
|
* TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
|
|
|
|
* @param str The string whose whitespace characters to replace.
|
|
|
|
*/
|
2008-06-17 14:37:57 -07:00
|
|
|
static inline void
|
|
|
|
TextReplaceWhitespaceCharacters(nsAutoString& str)
|
2008-06-10 16:16:59 -07:00
|
|
|
{
|
|
|
|
str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
|
2007-07-25 11:21:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2008-06-10 16:16:59 -07:00
|
|
|
nsCanvasRenderingContext2D::FillText(const nsAString& text, float x, float y, float maxWidth)
|
2007-07-25 11:21:34 -07:00
|
|
|
{
|
2008-07-18 11:29:06 -07:00
|
|
|
return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_FILL, nsnull);
|
2008-06-10 16:16:59 -07:00
|
|
|
}
|
2007-07-25 11:21:34 -07:00
|
|
|
|
2008-06-10 16:16:59 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::StrokeText(const nsAString& text, float x, float y, float maxWidth)
|
|
|
|
{
|
2008-07-18 11:29:06 -07:00
|
|
|
return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_STROKE, nsnull);
|
2008-06-10 16:16:59 -07:00
|
|
|
}
|
2008-03-06 11:56:47 -08:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::MeasureText(const nsAString& rawText,
|
|
|
|
nsIDOMTextMetrics** _retval)
|
|
|
|
{
|
|
|
|
float width;
|
|
|
|
|
|
|
|
nsresult rv = DrawOrMeasureText(rawText, 0, 0, 0, TEXT_DRAW_OPERATION_MEASURE, &width);
|
|
|
|
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
nsRefPtr<nsIDOMTextMetrics> textMetrics = new nsTextMetrics(width);
|
|
|
|
if (!textMetrics.get())
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
*_retval = textMetrics.forget().get();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used for nsBidiPresUtils::ProcessText
|
|
|
|
*/
|
|
|
|
struct NS_STACK_CLASS nsCanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
|
|
|
|
{
|
|
|
|
virtual void SetText(const PRUnichar* text, PRInt32 length, nsBidiDirection direction)
|
|
|
|
{
|
|
|
|
mTextRun = gfxTextRunCache::MakeTextRun(text,
|
|
|
|
length,
|
|
|
|
mFontgrp,
|
2008-07-23 10:50:03 -07:00
|
|
|
mCtx->mThebes,
|
2008-07-18 11:29:06 -07:00
|
|
|
mAppUnitsPerDevPixel,
|
|
|
|
direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual nscoord GetWidth()
|
|
|
|
{
|
|
|
|
PRBool tightBoundingBox = PR_FALSE;
|
|
|
|
gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
|
|
|
|
mTextRun->GetLength(),
|
|
|
|
tightBoundingBox,
|
2008-07-23 10:50:03 -07:00
|
|
|
mCtx->mThebes,
|
2008-07-18 11:29:06 -07:00
|
|
|
nsnull);
|
|
|
|
|
|
|
|
return static_cast<nscoord>(textRunMetrics.mAdvanceWidth/gfxFloat(mAppUnitsPerDevPixel));
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void DrawText(nscoord xOffset, nscoord width)
|
|
|
|
{
|
|
|
|
gfxPoint point = mPt;
|
|
|
|
point.x += xOffset * mAppUnitsPerDevPixel;
|
|
|
|
|
|
|
|
// offset is given in terms of left side of string
|
|
|
|
if (mTextRun->IsRightToLeft())
|
|
|
|
point.x += width * mAppUnitsPerDevPixel;
|
|
|
|
|
|
|
|
// stroke or fill the text depending on operation
|
|
|
|
if (mOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE)
|
2008-07-23 10:50:03 -07:00
|
|
|
mTextRun->DrawToPath(mCtx->mThebes,
|
2008-07-18 11:29:06 -07:00
|
|
|
point,
|
|
|
|
0,
|
|
|
|
mTextRun->GetLength(),
|
|
|
|
nsnull,
|
|
|
|
nsnull);
|
|
|
|
else
|
|
|
|
// mOp == TEXT_DRAW_OPERATION_FILL
|
2008-07-23 10:50:03 -07:00
|
|
|
mTextRun->Draw(mCtx->mThebes,
|
2008-07-18 11:29:06 -07:00
|
|
|
point,
|
|
|
|
0,
|
|
|
|
mTextRun->GetLength(),
|
|
|
|
nsnull,
|
|
|
|
nsnull,
|
|
|
|
nsnull);
|
|
|
|
}
|
|
|
|
|
|
|
|
// current text run
|
|
|
|
gfxTextRunCache::AutoTextRun mTextRun;
|
|
|
|
|
|
|
|
// pointer to the context
|
|
|
|
nsCanvasRenderingContext2D* mCtx;
|
|
|
|
|
|
|
|
// position of the left side of the string, alphabetic baseline
|
|
|
|
gfxPoint mPt;
|
|
|
|
|
|
|
|
// current font
|
|
|
|
gfxFontGroup* mFontgrp;
|
|
|
|
|
|
|
|
// dev pixel conversion factor
|
|
|
|
PRUint32 mAppUnitsPerDevPixel;
|
|
|
|
|
|
|
|
// operation (fill or stroke)
|
|
|
|
nsCanvasRenderingContext2D::TextDrawOperation mOp;
|
|
|
|
};
|
|
|
|
|
2008-06-10 16:16:59 -07:00
|
|
|
nsresult
|
2008-07-18 11:29:06 -07:00
|
|
|
nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
|
|
|
|
float aX,
|
|
|
|
float aY,
|
|
|
|
float aMaxWidth,
|
|
|
|
TextDrawOperation aOp,
|
|
|
|
float* aWidth)
|
2008-06-10 16:16:59 -07:00
|
|
|
{
|
2008-07-18 11:29:06 -07:00
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
if (!FloatValidate(aX, aY, aMaxWidth))
|
2008-06-10 16:16:59 -07:00
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
// spec isn't clear on what should happen if aMaxWidth <= 0, so
|
2008-06-10 16:16:59 -07:00
|
|
|
// treat it as an invalid argument
|
|
|
|
// technically, 0 should be an invalid value as well, but 0 is the default
|
|
|
|
// arg, and there is no way to tell if the default was used
|
2008-07-18 11:29:06 -07:00
|
|
|
if (aMaxWidth < 0)
|
2008-06-10 16:16:59 -07:00
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
|
|
|
|
if (!content) {
|
|
|
|
NS_WARNING("Canvas element must be an nsIContent and non-null");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIDocument* document = content->GetOwnerDoc();
|
|
|
|
|
|
|
|
nsIPresShell* presShell = document->GetPrimaryShell();
|
|
|
|
if (!presShell)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
nsBidiPresUtils* bidiUtils = presShell->GetPresContext()->GetBidiUtils();
|
|
|
|
if (!bidiUtils)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-06-10 16:16:59 -07:00
|
|
|
// replace all the whitespace characters with U+0020 SPACE
|
2008-07-18 11:29:06 -07:00
|
|
|
nsAutoString textToDraw(aRawText);
|
2008-06-10 16:16:59 -07:00
|
|
|
TextReplaceWhitespaceCharacters(textToDraw);
|
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
// for now, default to ltr if not in doc
|
|
|
|
PRBool isRTL = PR_FALSE;
|
2008-07-18 11:29:06 -07:00
|
|
|
|
2008-06-17 14:37:57 -07:00
|
|
|
if (content->IsInDoc()) {
|
|
|
|
// try to find the closest context
|
|
|
|
nsRefPtr<nsStyleContext> canvasStyle =
|
|
|
|
nsInspectorCSSUtils::GetStyleContextForContent(content,
|
|
|
|
nsnull,
|
|
|
|
presShell);
|
|
|
|
if (!canvasStyle)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
isRTL = canvasStyle->GetStyleVisibility()->mDirection ==
|
|
|
|
NS_STYLE_DIRECTION_RTL;
|
|
|
|
}
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
nsCanvasBidiProcessor processor;
|
|
|
|
|
|
|
|
GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, NULL);
|
|
|
|
processor.mPt = gfxPoint(aX, aY);
|
|
|
|
processor.mCtx = this;
|
|
|
|
processor.mOp = aOp;
|
|
|
|
|
|
|
|
processor.mFontgrp = GetCurrentFontStyle();
|
|
|
|
NS_ASSERTION(processor.mFontgrp, "font group is null");
|
|
|
|
|
|
|
|
nscoord totalWidth;
|
|
|
|
|
|
|
|
// currently calls bidi algo twice since it needs the full width before
|
|
|
|
// rendering anything. Can probably restructure function to avoid this if
|
|
|
|
// it's cheaper to store all the runs locally rather than do bidi resolution
|
|
|
|
// twice.
|
|
|
|
rv = bidiUtils->ProcessText(textToDraw.get(),
|
|
|
|
textToDraw.Length(),
|
|
|
|
isRTL ? NSBIDI_RTL : NSBIDI_LTR,
|
|
|
|
presShell->GetPresContext(),
|
|
|
|
processor,
|
|
|
|
nsBidiPresUtils::MODE_MEASURE,
|
|
|
|
nsnull,
|
|
|
|
0,
|
|
|
|
&totalWidth);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
2007-07-25 11:21:34 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
if (aWidth)
|
|
|
|
*aWidth = static_cast<float>(totalWidth);
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
// if only measuring, don't need to do any more work
|
|
|
|
if (aOp==TEXT_DRAW_OPERATION_MEASURE)
|
|
|
|
return NS_OK;
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
// offset pt.x based on text align
|
2008-06-10 16:16:59 -07:00
|
|
|
gfxFloat anchorX;
|
|
|
|
|
|
|
|
if (CurrentState().textAlign == TEXT_ALIGN_CENTER)
|
|
|
|
anchorX = .5;
|
|
|
|
else if (CurrentState().textAlign == TEXT_ALIGN_LEFT ||
|
|
|
|
(!isRTL && CurrentState().textAlign == TEXT_ALIGN_START) ||
|
|
|
|
(isRTL && CurrentState().textAlign == TEXT_ALIGN_END))
|
|
|
|
anchorX = 0;
|
|
|
|
else
|
|
|
|
anchorX = 1;
|
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
processor.mPt.x -= anchorX * totalWidth;
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
// offset pt.y based on text baseline
|
|
|
|
NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
|
|
|
|
const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
|
2008-06-10 16:16:59 -07:00
|
|
|
|
|
|
|
gfxFloat anchorY;
|
|
|
|
|
|
|
|
switch (CurrentState().textBaseline)
|
|
|
|
{
|
|
|
|
case TEXT_BASELINE_TOP:
|
|
|
|
anchorY = fontMetrics.emAscent;
|
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_HANGING:
|
|
|
|
anchorY = 0; // currently unavailable
|
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_MIDDLE:
|
2008-07-18 11:29:06 -07:00
|
|
|
anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
|
2008-06-10 16:16:59 -07:00
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_ALPHABETIC:
|
|
|
|
anchorY = 0;
|
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_IDEOGRAPHIC:
|
|
|
|
anchorY = 0; // currently unvailable
|
|
|
|
break;
|
|
|
|
case TEXT_BASELINE_BOTTOM:
|
|
|
|
anchorY = -fontMetrics.emDescent;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NS_ASSERTION(0, "mTextBaseline holds invalid value");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
processor.mPt.y += anchorY;
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
processor.mPt.x *= processor.mAppUnitsPerDevPixel;
|
|
|
|
processor.mPt.y *= processor.mAppUnitsPerDevPixel;
|
|
|
|
|
|
|
|
// if text is over aMaxWidth, then scale the text horizontally such that its
|
|
|
|
// width is precisely aMaxWidth
|
|
|
|
if (aMaxWidth > 0 && totalWidth > aMaxWidth) {
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Save();
|
2008-06-10 16:16:59 -07:00
|
|
|
// translate the anchor point to 0, then scale and translate back
|
2008-07-18 11:29:06 -07:00
|
|
|
gfxPoint trans(aX, 0);
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Translate(trans);
|
|
|
|
mThebes->Scale(aMaxWidth/totalWidth, 1);
|
|
|
|
mThebes->Translate(-trans);
|
2008-06-10 16:16:59 -07:00
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxPath> path;
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
// back up path if stroking
|
|
|
|
if (aOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE)
|
2008-07-23 10:50:03 -07:00
|
|
|
path = mThebes->CopyPath();
|
2008-07-18 11:29:06 -07:00
|
|
|
else
|
2008-06-10 16:16:59 -07:00
|
|
|
ApplyStyle(STYLE_FILL);
|
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
rv = bidiUtils->ProcessText(textToDraw.get(),
|
|
|
|
textToDraw.Length(),
|
|
|
|
isRTL ? NSBIDI_RTL : NSBIDI_LTR,
|
|
|
|
presShell->GetPresContext(),
|
|
|
|
processor,
|
|
|
|
nsBidiPresUtils::MODE_DRAW,
|
|
|
|
nsnull,
|
|
|
|
0,
|
|
|
|
nsnull);
|
|
|
|
|
|
|
|
// stroke and restore path
|
|
|
|
if (aOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE) {
|
|
|
|
ApplyStyle(STYLE_STROKE);
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Stroke();
|
2007-07-25 11:21:34 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->NewPath();
|
|
|
|
mThebes->AppendPath(path);
|
2008-07-18 11:29:06 -07:00
|
|
|
}
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
// have to restore the context if was modified for maxWidth
|
|
|
|
if (aMaxWidth > 0 && totalWidth > aMaxWidth)
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Restore();
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
2008-06-10 16:16:59 -07:00
|
|
|
|
2008-07-18 11:29:06 -07:00
|
|
|
return Redraw();
|
2008-06-10 16:16:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetMozTextStyle(const nsAString& textStyle)
|
|
|
|
{
|
|
|
|
// font and mozTextStyle are the same value
|
|
|
|
return SetFont(textStyle);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetMozTextStyle(nsAString& textStyle)
|
|
|
|
{
|
|
|
|
// font and mozTextStyle are the same value
|
|
|
|
return GetFont(textStyle);
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxFontGroup *nsCanvasRenderingContext2D::GetCurrentFontStyle()
|
|
|
|
{
|
|
|
|
// use lazy initilization for the font group since it's rather expensive
|
|
|
|
if(!CurrentState().fontGroup) {
|
2008-06-17 14:37:57 -07:00
|
|
|
nsresult res = SetMozTextStyle(NS_LITERAL_STRING("10px sans-serif"));
|
2008-06-10 16:16:59 -07:00
|
|
|
NS_ASSERTION(res == NS_OK, "Default canvas font is invalid");
|
|
|
|
}
|
|
|
|
|
|
|
|
return CurrentState().fontGroup;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::MozDrawText(const nsAString& textToDraw)
|
|
|
|
{
|
|
|
|
const PRUnichar* textdata;
|
|
|
|
textToDraw.GetData(&textdata);
|
|
|
|
|
|
|
|
PRUint32 textrunflags = 0;
|
|
|
|
|
|
|
|
PRUint32 aupdp;
|
|
|
|
GetAppUnitsValues(&aupdp, NULL);
|
|
|
|
|
|
|
|
gfxTextRunCache::AutoTextRun textRun;
|
|
|
|
textRun = gfxTextRunCache::MakeTextRun(textdata,
|
|
|
|
textToDraw.Length(),
|
|
|
|
GetCurrentFontStyle(),
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes,
|
2008-06-10 16:16:59 -07:00
|
|
|
aupdp,
|
|
|
|
textrunflags);
|
|
|
|
|
|
|
|
if(!textRun.get())
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
gfxPoint pt(0.0f,0.0f);
|
|
|
|
|
|
|
|
// Fill color is text color
|
|
|
|
ApplyStyle(STYLE_FILL);
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
textRun->Draw(mThebes,
|
2008-06-10 16:16:59 -07:00
|
|
|
pt,
|
|
|
|
/* offset = */ 0,
|
|
|
|
textToDraw.Length(),
|
|
|
|
nsnull,
|
|
|
|
nsnull,
|
|
|
|
nsnull);
|
2007-07-25 11:21:34 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-06-10 16:16:59 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::MozMeasureText(const nsAString& textToMeasure, float *retVal)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMTextMetrics> metrics;
|
|
|
|
nsresult rv;
|
|
|
|
rv = MeasureText(textToMeasure, getter_AddRefs(metrics));
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
return metrics->GetWidth(retVal);
|
|
|
|
}
|
|
|
|
|
2007-07-25 11:21:34 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::MozPathText(const nsAString& textToPath)
|
|
|
|
{
|
|
|
|
const PRUnichar* textdata;
|
|
|
|
textToPath.GetData(&textdata);
|
|
|
|
|
|
|
|
PRUint32 textrunflags = 0;
|
2008-03-06 11:56:47 -08:00
|
|
|
|
|
|
|
PRUint32 aupdp;
|
|
|
|
GetAppUnitsValues(&aupdp, NULL);
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
gfxTextRunCache::AutoTextRun textRun;
|
|
|
|
textRun = gfxTextRunCache::MakeTextRun(textdata,
|
|
|
|
textToPath.Length(),
|
|
|
|
GetCurrentFontStyle(),
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes,
|
2007-07-25 11:21:34 -07:00
|
|
|
aupdp,
|
|
|
|
textrunflags);
|
|
|
|
|
|
|
|
if(!textRun.get())
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
gfxPoint pt(0.0f,0.0f);
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
textRun->DrawToPath(mThebes,
|
2007-07-25 11:21:34 -07:00
|
|
|
pt,
|
|
|
|
/* offset = */ 0,
|
|
|
|
textToPath.Length(),
|
|
|
|
nsnull,
|
|
|
|
nsnull);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::MozTextAlongPath(const nsAString& textToDraw, PRBool stroke)
|
|
|
|
{
|
|
|
|
// Most of this code is copied from its svg equivalent
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxFlattenedPath> path(mThebes->GetFlattenedPath());
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
const PRUnichar* textdata;
|
|
|
|
textToDraw.GetData(&textdata);
|
|
|
|
|
|
|
|
PRUint32 textrunflags = 0;
|
2008-03-06 11:56:47 -08:00
|
|
|
|
|
|
|
PRUint32 aupdp;
|
|
|
|
GetAppUnitsValues(&aupdp, NULL);
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
gfxTextRunCache::AutoTextRun textRun;
|
|
|
|
textRun = gfxTextRunCache::MakeTextRun(textdata,
|
|
|
|
textToDraw.Length(),
|
|
|
|
GetCurrentFontStyle(),
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes,
|
2007-07-25 11:21:34 -07:00
|
|
|
aupdp,
|
|
|
|
textrunflags);
|
|
|
|
|
|
|
|
if(!textRun.get())
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
struct PathChar
|
|
|
|
{
|
|
|
|
PRBool draw;
|
|
|
|
gfxFloat angle;
|
|
|
|
gfxPoint pos;
|
|
|
|
PathChar() : draw(PR_FALSE), angle(0.0), pos(0.0,0.0) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
gfxFloat length = path->GetLength();
|
|
|
|
PRUint32 strLength = textToDraw.Length();
|
|
|
|
|
|
|
|
PathChar *cp = new PathChar[strLength];
|
|
|
|
|
2008-06-10 16:16:59 -07:00
|
|
|
if (!cp) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
2007-08-03 15:47:48 -07:00
|
|
|
gfxPoint position(0.0,0.0);
|
2007-07-25 11:21:34 -07:00
|
|
|
gfxFloat x = position.x;
|
|
|
|
for (PRUint32 i = 0; i < strLength; i++)
|
|
|
|
{
|
|
|
|
gfxFloat halfAdvance = textRun->GetAdvanceWidth(i, 1, nsnull) / (2.0 * aupdp);
|
|
|
|
|
|
|
|
// Check for end of path
|
|
|
|
if(x + halfAdvance > length)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if(x + halfAdvance >= 0)
|
|
|
|
{
|
|
|
|
cp[i].draw = PR_TRUE;
|
|
|
|
gfxPoint pt = path->FindPoint(gfxPoint(x + halfAdvance, position.y), &(cp[i].angle));
|
|
|
|
|
|
|
|
cp[i].pos = pt - gfxPoint(cos(cp[i].angle), sin(cp[i].angle)) * halfAdvance;
|
|
|
|
}
|
|
|
|
x += 2 * halfAdvance;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(stroke)
|
|
|
|
ApplyStyle(STYLE_STROKE);
|
|
|
|
else
|
|
|
|
ApplyStyle(STYLE_FILL);
|
|
|
|
|
|
|
|
for(PRUint32 i = 0; i < strLength; i++)
|
|
|
|
{
|
|
|
|
// Skip non-visible characters
|
|
|
|
if(!cp[i].draw) continue;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxMatrix matrix = mThebes->CurrentMatrix();
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
gfxMatrix rot;
|
|
|
|
rot.Rotate(cp[i].angle);
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Multiply(rot);
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
rot.Invert();
|
|
|
|
rot.Scale(aupdp,aupdp);
|
|
|
|
gfxPoint pt = rot.Transform(cp[i].pos);
|
|
|
|
|
|
|
|
if(stroke) {
|
2008-07-23 10:50:03 -07:00
|
|
|
textRun->DrawToPath(mThebes, pt, i, 1, nsnull, nsnull);
|
2007-07-25 11:21:34 -07:00
|
|
|
} else {
|
2008-07-23 10:50:03 -07:00
|
|
|
textRun->Draw(mThebes, pt, i, 1, nsnull, nsnull, nsnull);
|
2007-07-25 11:21:34 -07:00
|
|
|
}
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetMatrix(matrix);
|
2007-07-25 11:21:34 -07:00
|
|
|
}
|
|
|
|
|
2008-03-06 11:56:47 -08:00
|
|
|
delete [] cp;
|
2007-07-25 11:21:34 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// line caps/joins
|
|
|
|
//
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetLineWidth(float width)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(width))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetLineWidth(width);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetLineWidth(float *width)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxFloat d = mThebes->CurrentLineWidth();
|
|
|
|
*width = static_cast<float>(d);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetLineCap(const nsAString& capstyle)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxContext::GraphicsLineCap cap;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (capstyle.EqualsLiteral("butt"))
|
2008-07-23 10:50:03 -07:00
|
|
|
cap = gfxContext::LINE_CAP_BUTT;
|
2007-03-22 10:30:00 -07:00
|
|
|
else if (capstyle.EqualsLiteral("round"))
|
2008-07-23 10:50:03 -07:00
|
|
|
cap = gfxContext::LINE_CAP_ROUND;
|
2007-03-22 10:30:00 -07:00
|
|
|
else if (capstyle.EqualsLiteral("square"))
|
2008-07-23 10:50:03 -07:00
|
|
|
cap = gfxContext::LINE_CAP_SQUARE;
|
2007-03-22 10:30:00 -07:00
|
|
|
else
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetLineCap(cap);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetLineCap(nsAString& capstyle)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxContext::GraphicsLineCap cap = mThebes->CurrentLineCap();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
if (cap == gfxContext::LINE_CAP_BUTT)
|
2007-03-22 10:30:00 -07:00
|
|
|
capstyle.AssignLiteral("butt");
|
2008-07-23 10:50:03 -07:00
|
|
|
else if (cap == gfxContext::LINE_CAP_ROUND)
|
2007-03-22 10:30:00 -07:00
|
|
|
capstyle.AssignLiteral("round");
|
2008-07-23 10:50:03 -07:00
|
|
|
else if (cap == gfxContext::LINE_CAP_SQUARE)
|
2007-03-22 10:30:00 -07:00
|
|
|
capstyle.AssignLiteral("square");
|
|
|
|
else
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetLineJoin(const nsAString& joinstyle)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxContext::GraphicsLineJoin j;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (joinstyle.EqualsLiteral("round"))
|
2008-07-23 10:50:03 -07:00
|
|
|
j = gfxContext::LINE_JOIN_ROUND;
|
2007-03-22 10:30:00 -07:00
|
|
|
else if (joinstyle.EqualsLiteral("bevel"))
|
2008-07-23 10:50:03 -07:00
|
|
|
j = gfxContext::LINE_JOIN_BEVEL;
|
2007-03-22 10:30:00 -07:00
|
|
|
else if (joinstyle.EqualsLiteral("miter"))
|
2008-07-23 10:50:03 -07:00
|
|
|
j = gfxContext::LINE_JOIN_MITER;
|
2007-03-22 10:30:00 -07:00
|
|
|
else
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetLineJoin(j);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetLineJoin(nsAString& joinstyle)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxContext::GraphicsLineJoin j = mThebes->CurrentLineJoin();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
if (j == gfxContext::LINE_JOIN_ROUND)
|
2007-03-22 10:30:00 -07:00
|
|
|
joinstyle.AssignLiteral("round");
|
2008-07-23 10:50:03 -07:00
|
|
|
else if (j == gfxContext::LINE_JOIN_BEVEL)
|
2007-03-22 10:30:00 -07:00
|
|
|
joinstyle.AssignLiteral("bevel");
|
2008-07-23 10:50:03 -07:00
|
|
|
else if (j == gfxContext::LINE_JOIN_MITER)
|
2007-03-22 10:30:00 -07:00
|
|
|
joinstyle.AssignLiteral("miter");
|
|
|
|
else
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetMiterLimit(float miter)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(miter))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetMiterLimit(miter);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetMiterLimit(float *miter)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxFloat d = mThebes->CurrentMiterLimit();
|
|
|
|
*miter = static_cast<float>(d);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::IsPointInPath(float x, float y, PRBool *retVal)
|
|
|
|
{
|
|
|
|
if (!FloatValidate(x,y))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
*retVal = mThebes->PointInFill(gfxPoint(x,y));
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// image
|
|
|
|
//
|
|
|
|
|
|
|
|
// drawImage(in HTMLImageElement image, in float dx, in float dy);
|
|
|
|
// -- render image from 0,0 at dx,dy top-left coords
|
|
|
|
// drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
|
|
|
|
// -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
|
|
|
|
// drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
|
|
|
|
// -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::DrawImage()
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
2008-03-06 11:56:47 -08:00
|
|
|
// we can't do a security check without a canvas element, so
|
|
|
|
// just skip this entirely
|
|
|
|
if (!mCanvasElement)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-01-15 07:50:57 -08:00
|
|
|
nsAXPCNativeCallContext *ncc = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
rv = nsContentUtils::XPConnect()->
|
2008-01-15 07:50:57 -08:00
|
|
|
GetCurrentNativeCallContext(&ncc);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (!ncc)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
JSContext *ctx = nsnull;
|
|
|
|
|
|
|
|
rv = ncc->GetJSContext(&ctx);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
PRUint32 argc;
|
|
|
|
jsval *argv = nsnull;
|
|
|
|
|
|
|
|
ncc->GetArgc(&argc);
|
|
|
|
ncc->GetArgvPtr(&argv);
|
|
|
|
|
|
|
|
// we always need at least an image and a dx,dy
|
|
|
|
if (argc < 3)
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2007-09-18 17:26:39 -07:00
|
|
|
|
|
|
|
JSAutoRequest ar(ctx);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
double sx,sy,sw,sh;
|
|
|
|
double dx,dy,dw,dh;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement> imgElt;
|
|
|
|
if (!ConvertJSValToXPCObject(getter_AddRefs(imgElt),
|
|
|
|
NS_GET_IID(nsIDOMElement),
|
|
|
|
ctx, argv[0]))
|
|
|
|
return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
|
|
|
|
|
|
|
|
PRInt32 imgWidth, imgHeight;
|
2007-11-08 22:03:47 -08:00
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRBool forceWriteOnly = PR_FALSE;
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxMatrix matrix;
|
|
|
|
nsRefPtr<gfxPattern> pattern;
|
|
|
|
nsRefPtr<gfxPath> path;
|
|
|
|
nsRefPtr<gfxASurface> imgsurf;
|
|
|
|
rv = ThebesSurfaceFromElement(imgElt, PR_FALSE,
|
|
|
|
getter_AddRefs(imgsurf), &imgWidth, &imgHeight,
|
|
|
|
getter_AddRefs(principal), &forceWriteOnly);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
2007-11-08 22:03:47 -08:00
|
|
|
DoDrawImageSecurityCheck(principal, forceWriteOnly);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#define GET_ARG(dest,whicharg) \
|
2007-09-25 13:46:08 -07:00
|
|
|
do { if (!ConvertJSValToDouble(dest, ctx, whicharg)) { rv = NS_ERROR_INVALID_ARG; goto FINISH; } } while (0)
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
rv = NS_OK;
|
|
|
|
|
|
|
|
if (argc == 3) {
|
|
|
|
GET_ARG(&dx, argv[1]);
|
|
|
|
GET_ARG(&dy, argv[2]);
|
|
|
|
sx = sy = 0.0;
|
|
|
|
dw = sw = (double) imgWidth;
|
|
|
|
dh = sh = (double) imgHeight;
|
|
|
|
} else if (argc == 5) {
|
|
|
|
GET_ARG(&dx, argv[1]);
|
|
|
|
GET_ARG(&dy, argv[2]);
|
|
|
|
GET_ARG(&dw, argv[3]);
|
|
|
|
GET_ARG(&dh, argv[4]);
|
|
|
|
sx = sy = 0.0;
|
|
|
|
sw = (double) imgWidth;
|
|
|
|
sh = (double) imgHeight;
|
|
|
|
} else if (argc == 9) {
|
|
|
|
GET_ARG(&sx, argv[1]);
|
|
|
|
GET_ARG(&sy, argv[2]);
|
|
|
|
GET_ARG(&sw, argv[3]);
|
|
|
|
GET_ARG(&sh, argv[4]);
|
|
|
|
GET_ARG(&dx, argv[5]);
|
|
|
|
GET_ARG(&dy, argv[6]);
|
|
|
|
GET_ARG(&dw, argv[7]);
|
|
|
|
GET_ARG(&dh, argv[8]);
|
|
|
|
} else {
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
rv = NS_ERROR_INVALID_ARG;
|
2007-09-25 13:46:08 -07:00
|
|
|
goto FINISH;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
#undef GET_ARG
|
|
|
|
|
2007-09-25 13:46:08 -07:00
|
|
|
if (dw == 0.0 || dh == 0.0) {
|
|
|
|
rv = NS_OK;
|
|
|
|
// not really failure, but nothing to do --
|
|
|
|
// and noone likes a divide-by-zero
|
|
|
|
goto FINISH;
|
|
|
|
}
|
|
|
|
|
2007-12-24 20:20:45 -08:00
|
|
|
if (!FloatValidate(sx,sy,sw,sh) || !FloatValidate(dx,dy,dw,dh)) {
|
2007-12-24 20:11:02 -08:00
|
|
|
rv = NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
goto FINISH;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// check args
|
|
|
|
if (sx < 0.0 || sy < 0.0 ||
|
|
|
|
sw < 0.0 || sw > (double) imgWidth ||
|
|
|
|
sh < 0.0 || sh > (double) imgHeight ||
|
|
|
|
dw < 0.0 || dh < 0.0)
|
|
|
|
{
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
rv = NS_ERROR_DOM_INDEX_SIZE_ERR;
|
2007-09-25 13:46:08 -07:00
|
|
|
goto FINISH;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2008-07-23 10:50:03 -07:00
|
|
|
|
|
|
|
matrix.Translate(gfxPoint(sx, sy));
|
|
|
|
matrix.Scale(sw/dw, sh/dh);
|
|
|
|
|
|
|
|
pattern = new gfxPattern(imgsurf);
|
|
|
|
pattern->SetMatrix(matrix);
|
|
|
|
|
|
|
|
path = mThebes->CopyPath();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->Save();
|
|
|
|
mThebes->Translate(gfxPoint(dx, dy));
|
|
|
|
mThebes->SetPattern(pattern);
|
|
|
|
mThebes->Clip(gfxRect(0, 0, dw, dh));
|
|
|
|
mThebes->Paint(CurrentState().globalAlpha);
|
|
|
|
mThebes->Restore();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#if 1
|
2008-07-23 10:50:03 -07:00
|
|
|
// XXX cairo bug workaround; force a clip update on mThebes.
|
2007-03-22 10:30:00 -07:00
|
|
|
// Otherwise, a pixman clip gets left around somewhere, and pixman
|
|
|
|
// (Render) does source clipping as well -- so we end up
|
|
|
|
// compositing with an incorrect clip. This only seems to affect
|
|
|
|
// fallback cases, which happen when we have CSS scaling going on.
|
|
|
|
// This will blow away the current path, but we already blew it
|
|
|
|
// away in this function earlier.
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->UpdateSurfaceClip();
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->NewPath();
|
|
|
|
mThebes->AppendPath(path);
|
2007-10-10 12:24:12 -07:00
|
|
|
|
2007-09-25 13:46:08 -07:00
|
|
|
FINISH:
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
rv = Redraw();
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& op)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxContext::GraphicsOperator thebes_op;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
#define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \
|
2007-03-22 10:30:00 -07:00
|
|
|
if (op.EqualsLiteral(cvsop)) \
|
2008-07-23 10:50:03 -07:00
|
|
|
thebes_op = gfxContext::OPERATOR_##thebesop;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// XXX "darker" isn't really correct
|
2008-07-23 10:50:03 -07:00
|
|
|
CANVAS_OP_TO_THEBES_OP("clear", CLEAR)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("copy", SOURCE)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("darker", SATURATE) // XXX
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("destination-atop", DEST_ATOP)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("destination-in", DEST_IN)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("destination-out", DEST_OUT)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("destination-over", DEST_OVER)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("lighter", ADD)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("source-atop", ATOP)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("source-in", IN)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("source-out", OUT)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("source-over", OVER)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("xor", XOR)
|
2007-03-22 10:30:00 -07:00
|
|
|
// not part of spec, kept here for compat
|
2008-07-23 10:50:03 -07:00
|
|
|
else CANVAS_OP_TO_THEBES_OP("over", OVER)
|
2007-03-22 10:30:00 -07:00
|
|
|
else return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
#undef CANVAS_OP_TO_THEBES_OP
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetOperator(thebes_op);
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
gfxContext::GraphicsOperator thebes_op = mThebes->CurrentOperator();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
#define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \
|
|
|
|
if (thebes_op == gfxContext::OPERATOR_##thebesop) \
|
2007-03-22 10:30:00 -07:00
|
|
|
op.AssignLiteral(cvsop);
|
|
|
|
|
|
|
|
// XXX "darker" isn't really correct
|
2008-07-23 10:50:03 -07:00
|
|
|
CANVAS_OP_TO_THEBES_OP("clear", CLEAR)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("copy", SOURCE)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("darker", SATURATE) // XXX
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("destination-atop", DEST_ATOP)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("destination-in", DEST_IN)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("destination-out", DEST_OUT)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("destination-over", DEST_OVER)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("lighter", ADD)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("source-atop", ATOP)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("source-in", IN)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("source-out", OUT)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("source-over", OVER)
|
|
|
|
else CANVAS_OP_TO_THEBES_OP("xor", XOR)
|
2007-03-22 10:30:00 -07:00
|
|
|
else return NS_ERROR_FAILURE;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
#undef CANVAS_OP_TO_THEBES_OP
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Utils
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsCanvasRenderingContext2D::ConvertJSValToUint32(PRUint32* aProp, JSContext* aContext,
|
|
|
|
jsval aValue)
|
|
|
|
{
|
|
|
|
uint32 temp;
|
|
|
|
if (::JS_ValueToECMAUint32(aContext, aValue, &temp)) {
|
|
|
|
*aProp = (PRUint32)temp;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
::JS_ReportError(aContext, "Parameter must be an integer");
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsCanvasRenderingContext2D::ConvertJSValToDouble(double* aProp, JSContext* aContext,
|
|
|
|
jsval aValue)
|
|
|
|
{
|
|
|
|
jsdouble temp;
|
|
|
|
if (::JS_ValueToNumber(aContext, aValue, &temp)) {
|
|
|
|
*aProp = (jsdouble)temp;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
::JS_ReportError(aContext, "Parameter must be a number");
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsCanvasRenderingContext2D::ConvertJSValToXPCObject(nsISupports** aSupports, REFNSIID aIID,
|
|
|
|
JSContext* aContext, jsval aValue)
|
|
|
|
{
|
|
|
|
*aSupports = nsnull;
|
|
|
|
if (JSVAL_IS_NULL(aValue)) {
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (JSVAL_IS_OBJECT(aValue)) {
|
|
|
|
// WrapJS does all the work to recycle an existing wrapper and/or do a QI
|
|
|
|
nsresult rv = nsContentUtils::XPConnect()->
|
|
|
|
WrapJS(aContext, JSVAL_TO_OBJECT(aValue), aIID, (void**)aSupports);
|
|
|
|
|
|
|
|
return NS_SUCCEEDED(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
/* thebes ARGB32 surfaces are ARGB stored as a packed 32-bit integer; on little-endian
|
2007-03-22 10:30:00 -07:00
|
|
|
* platforms, they appear as BGRA bytes in the surface data. The color values are also
|
|
|
|
* stored with premultiplied alpha.
|
2008-04-30 10:04:46 -07:00
|
|
|
*
|
|
|
|
* If forceCopy is FALSE, a surface may be returned that's only valid during the current
|
|
|
|
* operation. If it's TRUE, a copy will always be made that can safely be retained.
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
nsresult
|
2008-07-23 10:50:03 -07:00
|
|
|
nsCanvasRenderingContext2D::ThebesSurfaceFromElement(nsIDOMElement *imgElt,
|
|
|
|
PRBool forceCopy,
|
|
|
|
gfxASurface **aSurface,
|
|
|
|
PRInt32 *widthOut,
|
|
|
|
PRInt32 *heightOut,
|
|
|
|
nsIPrincipal **prinOut,
|
|
|
|
PRBool *forceWriteOnlyOut)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
nsCOMPtr<nsINode> node = do_QueryInterface(imgElt);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
/* If it's a Canvas, grab its internal surface as necessary */
|
|
|
|
nsCOMPtr<nsICanvasElement> canvas = do_QueryInterface(imgElt);
|
|
|
|
if (node && canvas) {
|
|
|
|
PRUint32 w, h;
|
|
|
|
rv = canvas->GetSize(&w, &h);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
nsRefPtr<gfxASurface> sourceSurface;
|
|
|
|
|
|
|
|
if (!forceCopy && canvas->CountContexts() == 1) {
|
|
|
|
nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
|
|
|
|
rv = srcCanvas->GetThebesSurface(getter_AddRefs(sourceSurface));
|
|
|
|
// force a copy if we couldn't get the surface, or if it's
|
|
|
|
// the same as what we have
|
|
|
|
if (sourceSurface == mSurface || NS_FAILED(rv))
|
|
|
|
sourceSurface = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sourceSurface == nsnull) {
|
|
|
|
nsRefPtr<gfxASurface> surf =
|
|
|
|
gfxPlatform::GetPlatform()->CreateOffscreenSurface
|
|
|
|
(gfxIntSize(w, h), gfxASurface::ImageFormatARGB32);
|
|
|
|
nsRefPtr<gfxContext> ctx = new gfxContext(surf);
|
|
|
|
rv = canvas->RenderContexts(ctx);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
sourceSurface = surf;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aSurface = sourceSurface.forget().get();
|
|
|
|
*widthOut = w;
|
|
|
|
*heightOut = h;
|
|
|
|
|
|
|
|
NS_ADDREF(*prinOut = node->NodePrincipal());
|
|
|
|
*forceWriteOnlyOut = canvas->IsWriteOnly();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MOZ_MEDIA
|
|
|
|
/* Maybe it's <video>? */
|
|
|
|
nsCOMPtr<nsIDOMHTMLVideoElement> ve = do_QueryInterface(imgElt);
|
|
|
|
if (node && ve) {
|
|
|
|
nsHTMLVideoElement *video = static_cast<nsHTMLVideoElement*>(ve.get());
|
2008-09-06 16:47:28 -07:00
|
|
|
|
|
|
|
/* If it doesn't have a principal, just bail */
|
|
|
|
nsCOMPtr<nsIPrincipal> principal = video->GetCurrentPrincipal();
|
|
|
|
if (!principal)
|
|
|
|
return NS_ERROR_DOM_SECURITY_ERR;
|
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
PRUint32 videoWidth, videoHeight;
|
|
|
|
rv = video->GetVideoWidth(&videoWidth);
|
|
|
|
rv |= video->GetVideoHeight(&videoHeight);
|
|
|
|
if (NS_FAILED(rv))
|
2007-10-04 11:57:40 -07:00
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
nsRefPtr<gfxASurface> surf =
|
|
|
|
gfxPlatform::GetPlatform()->CreateOffscreenSurface
|
|
|
|
(gfxIntSize(videoWidth, videoHeight), gfxASurface::ImageFormatARGB32);
|
|
|
|
nsRefPtr<gfxContext> ctx = new gfxContext(surf);
|
2007-10-04 11:57:40 -07:00
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
|
|
|
|
|
|
video->Paint(ctx, gfxRect(0, 0, videoWidth, videoHeight));
|
|
|
|
|
|
|
|
*aSurface = surf.forget().get();
|
|
|
|
*widthOut = videoWidth;
|
|
|
|
*heightOut = videoHeight;
|
|
|
|
|
2008-09-06 16:47:28 -07:00
|
|
|
*prinOut = principal.forget().get();
|
2007-03-22 10:30:00 -07:00
|
|
|
*forceWriteOnlyOut = PR_FALSE;
|
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
#endif
|
2007-09-25 13:46:08 -07:00
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
/* Finally, check if it's a normal image */
|
|
|
|
nsCOMPtr<imgIContainer> imgContainer;
|
|
|
|
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgElt);
|
|
|
|
|
|
|
|
if (!imageLoader)
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
|
|
|
|
nsCOMPtr<imgIRequest> imgRequest;
|
|
|
|
rv = imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
|
|
|
getter_AddRefs(imgRequest));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!imgRequest)
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
PRUint32 status;
|
|
|
|
imgRequest->GetImageStatus(&status);
|
|
|
|
if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0)
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-09-06 16:45:57 -07:00
|
|
|
// In case of data: URIs, we want to ignore principals;
|
|
|
|
// they should have the originating content's principal,
|
|
|
|
// but that's broken at the moment in imgLib.
|
2008-08-18 12:40:11 -07:00
|
|
|
nsCOMPtr<nsIURI> uri;
|
2008-09-06 16:45:57 -07:00
|
|
|
rv = imgRequest->GetURI(getter_AddRefs(uri));
|
|
|
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
|
|
|
|
|
|
|
|
PRBool isDataURI = PR_FALSE;
|
|
|
|
rv = uri->SchemeIs("data", &isDataURI);
|
|
|
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
|
|
|
|
|
|
|
|
// Data URIs are always OK; set the principal
|
|
|
|
// to null to indicate that.
|
|
|
|
if (isDataURI) {
|
|
|
|
*prinOut = nsnull;
|
|
|
|
} else {
|
|
|
|
rv = imgRequest->GetImagePrincipal(prinOut);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
NS_ENSURE_TRUE(*prinOut, NS_ERROR_DOM_SECURITY_ERR);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-08-18 12:40:11 -07:00
|
|
|
*forceWriteOnlyOut = PR_FALSE;
|
|
|
|
|
|
|
|
rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (!imgContainer)
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
|
|
|
|
nsCOMPtr<gfxIImageFrame> frame;
|
|
|
|
rv = imgContainer->GetCurrentFrame(getter_AddRefs(frame));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIImage> img(do_GetInterface(frame));
|
|
|
|
|
|
|
|
PRInt32 imgWidth, imgHeight;
|
|
|
|
rv = frame->GetWidth(&imgWidth);
|
|
|
|
rv |= frame->GetHeight(&imgHeight);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
if (widthOut)
|
|
|
|
*widthOut = imgWidth;
|
|
|
|
if (heightOut)
|
|
|
|
*heightOut = imgHeight;
|
|
|
|
|
2008-03-05 22:51:13 -08:00
|
|
|
nsRefPtr<gfxPattern> gfxpattern;
|
|
|
|
img->GetPattern(getter_AddRefs(gfxpattern));
|
|
|
|
nsRefPtr<gfxASurface> gfxsurf = gfxpattern->GetSurface();
|
|
|
|
|
|
|
|
if (!gfxsurf) {
|
|
|
|
gfxsurf = new gfxImageSurface (gfxIntSize(imgWidth, imgHeight), gfxASurface::ImageFormatARGB32);
|
|
|
|
nsRefPtr<gfxContext> ctx = new gfxContext(gfxsurf);
|
|
|
|
|
|
|
|
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
|
|
ctx->SetPattern(gfxpattern);
|
|
|
|
ctx->Paint();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
*aSurface = gfxsurf.forget().get();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that the rect [x,y,w,h] is a valid subrect of [0,0,realWidth,realHeight]
|
|
|
|
* without overflowing any integers and the like.
|
|
|
|
*/
|
|
|
|
PRBool
|
|
|
|
CheckSaneSubrectSize (PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h, PRInt32 realWidth, PRInt32 realHeight)
|
|
|
|
{
|
|
|
|
if (w <= 0 || h <= 0 || x < 0 || y < 0)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
if (x >= realWidth || w > (realWidth - x) ||
|
|
|
|
y >= realHeight || h > (realHeight - y))
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
FlushLayoutForTree(nsIDOMWindow* aWindow)
|
|
|
|
{
|
2008-02-13 22:51:33 -08:00
|
|
|
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
|
|
|
|
if (!piWin)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Note that because FlushPendingNotifications flushes parents, this
|
|
|
|
// is O(N^2) in docshell tree depth. However, the docshell tree is
|
|
|
|
// usually pretty shallow.
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
|
|
|
aWindow->GetDocument(getter_AddRefs(domDoc));
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
|
|
|
if (doc) {
|
|
|
|
doc->FlushPendingNotifications(Flush_Layout);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShellTreeNode> node =
|
|
|
|
do_QueryInterface(piWin->GetDocShell());
|
|
|
|
if (node) {
|
|
|
|
PRInt32 i = 0, i_end;
|
|
|
|
node->GetChildCount(&i_end);
|
|
|
|
for (; i < i_end; ++i) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> item;
|
|
|
|
node->GetChildAt(i, getter_AddRefs(item));
|
|
|
|
nsCOMPtr<nsIDOMWindow> win = do_GetInterface(item);
|
|
|
|
if (win) {
|
|
|
|
FlushLayoutForTree(win);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow* aWindow, PRInt32 aX, PRInt32 aY,
|
|
|
|
PRInt32 aW, PRInt32 aH,
|
|
|
|
const nsAString& aBGColor)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG(aWindow != nsnull);
|
|
|
|
|
|
|
|
// protect against too-large surfaces that will cause allocation
|
|
|
|
// or overflow issues
|
2008-02-13 22:53:25 -08:00
|
|
|
if (!gfxASurface::CheckSurfaceSize(gfxIntSize(aW, aH), 0xffff))
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// We can't allow web apps to call this until we fix at least the
|
|
|
|
// following potential security issues:
|
|
|
|
// -- rendering cross-domain IFRAMEs and then extracting the results
|
|
|
|
// -- rendering the user's theme and then extracting the results
|
|
|
|
// -- rendering native anonymous content (e.g., file input paths;
|
|
|
|
// scrollbars should be allowed)
|
|
|
|
if (!nsContentUtils::IsCallerTrustedForRead()) {
|
|
|
|
// not permitted to use DrawWindow
|
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_ERROR_DOM_SECURITY_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flush layout updates
|
|
|
|
FlushLayoutForTree(aWindow);
|
|
|
|
|
|
|
|
nsCOMPtr<nsPresContext> presContext;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
|
|
|
|
if (win) {
|
|
|
|
nsIDocShell* docshell = win->GetDocShell();
|
|
|
|
if (docshell) {
|
|
|
|
docshell->GetPresContext(getter_AddRefs(presContext));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!presContext)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nscolor bgColor;
|
|
|
|
nsresult rv = mCSSParser->ParseColorString(PromiseFlatString(aBGColor),
|
2008-02-21 00:37:27 -08:00
|
|
|
nsnull, 0, &bgColor);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsIPresShell* presShell = presContext->PresShell();
|
2007-06-20 22:01:21 -07:00
|
|
|
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-20 22:01:21 -07:00
|
|
|
nsRect r(nsPresContext::CSSPixelsToAppUnits(aX),
|
|
|
|
nsPresContext::CSSPixelsToAppUnits(aY),
|
|
|
|
nsPresContext::CSSPixelsToAppUnits(aW),
|
|
|
|
nsPresContext::CSSPixelsToAppUnits(aH));
|
|
|
|
presShell->RenderDocument(r, PR_FALSE, PR_TRUE, bgColor,
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// get rid of the pattern surface ref, just in case
|
2008-07-23 10:50:03 -07:00
|
|
|
mThebes->SetColor(gfxRGBA(1,1,1,1));
|
2007-03-22 10:30:00 -07:00
|
|
|
DirtyAllStyles();
|
|
|
|
|
|
|
|
Redraw();
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// device pixel getting/setting
|
|
|
|
//
|
|
|
|
|
|
|
|
// ImageData getImageData (in float x, in float y, in float width, in float height);
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetImageData()
|
|
|
|
{
|
2008-03-06 11:56:47 -08:00
|
|
|
if (!mValid || !mCanvasElement)
|
2008-02-13 22:53:25 -08:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerTrustedForRead()) {
|
2008-02-13 22:53:25 -08:00
|
|
|
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
|
|
|
return NS_ERROR_DOM_SECURITY_ERR;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-01-15 07:50:57 -08:00
|
|
|
nsAXPCNativeCallContext *ncc = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsresult rv = nsContentUtils::XPConnect()->
|
2008-01-15 07:50:57 -08:00
|
|
|
GetCurrentNativeCallContext(&ncc);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (!ncc)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
JSContext *ctx = nsnull;
|
|
|
|
|
|
|
|
rv = ncc->GetJSContext(&ctx);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
PRUint32 argc;
|
|
|
|
jsval *argv = nsnull;
|
|
|
|
|
|
|
|
ncc->GetArgc(&argc);
|
|
|
|
ncc->GetArgvPtr(&argv);
|
|
|
|
|
2007-09-28 11:15:26 -07:00
|
|
|
JSAutoRequest ar(ctx);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
int32 x, y, w, h;
|
|
|
|
if (!JS_ConvertArguments (ctx, argc, argv, "jjjj", &x, &y, &w, &h))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (!CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-02-13 22:53:25 -08:00
|
|
|
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;
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
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;
|
|
|
|
|
|
|
|
tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
|
|
tmpctx->SetSource(mSurface, gfxPoint(-(int)x, -(int)y));
|
|
|
|
tmpctx->Paint();
|
|
|
|
|
|
|
|
tmpctx = nsnull;
|
|
|
|
tmpsurf = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
PRUint32 len = w * h * 4;
|
|
|
|
if (len > (((PRUint32)0xfff00000)/sizeof(jsval)))
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
|
2007-05-22 20:58:19 -07:00
|
|
|
nsAutoArrayPtr<jsval> jsvector(new (std::nothrow) jsval[w * h * 4]);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!jsvector)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
jsval *dest = jsvector.get();
|
|
|
|
PRUint8 *row;
|
|
|
|
for (int j = 0; j < h; j++) {
|
|
|
|
row = surfaceData + surfaceDataOffset + (surfaceDataStride * j);
|
|
|
|
for (int 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
|
2007-10-26 12:45:20 -07:00
|
|
|
// Convert to non-premultiplied color
|
|
|
|
if (a != 0) {
|
|
|
|
r = (r * 255) / a;
|
|
|
|
g = (g * 255) / a;
|
|
|
|
b = (b * 255) / a;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
*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.get());
|
|
|
|
if (!dataArray)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
nsAutoGCRoot arrayGCRoot(&dataArray, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
JSObject *result = JS_NewObject(ctx, NULL, NULL, NULL);
|
|
|
|
if (!result)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
nsAutoGCRoot resultGCRoot(&result, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (!JS_DefineProperty(ctx, result, "width", INT_TO_JSVAL(w), NULL, NULL, 0) ||
|
|
|
|
!JS_DefineProperty(ctx, result, "height", INT_TO_JSVAL(h), NULL, NULL, 0) ||
|
|
|
|
!JS_DefineProperty(ctx, result, "data", OBJECT_TO_JSVAL(dataArray), NULL, NULL, 0))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
jsval *retvalPtr;
|
|
|
|
ncc->GetRetValPtr(&retvalPtr);
|
|
|
|
*retvalPtr = OBJECT_TO_JSVAL(result);
|
|
|
|
ncc->SetReturnValueWasSet(PR_TRUE);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-08-20 14:47:10 -07:00
|
|
|
extern "C" {
|
2008-08-21 23:23:29 -07:00
|
|
|
#include "jstypes.h"
|
|
|
|
JS_FRIEND_API(JSBool)
|
2008-08-20 14:47:10 -07:00
|
|
|
js_ArrayToJSUint8Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,
|
|
|
|
JSUint8 *dest);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// void putImageData (in ImageData d, in float x, in float y);
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::PutImageData()
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
2008-02-13 22:53:25 -08:00
|
|
|
if (!mValid)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-01-15 07:50:57 -08:00
|
|
|
nsAXPCNativeCallContext *ncc = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
rv = nsContentUtils::XPConnect()->
|
2008-01-15 07:50:57 -08:00
|
|
|
GetCurrentNativeCallContext(&ncc);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (!ncc)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
JSContext *ctx = nsnull;
|
|
|
|
|
|
|
|
rv = ncc->GetJSContext(&ctx);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
PRUint32 argc;
|
|
|
|
jsval *argv = nsnull;
|
|
|
|
|
|
|
|
ncc->GetArgc(&argc);
|
|
|
|
ncc->GetArgvPtr(&argv);
|
|
|
|
|
2007-09-28 11:15:26 -07:00
|
|
|
JSAutoRequest ar(ctx);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
JSObject *dataObject;
|
|
|
|
int32 x, y;
|
|
|
|
|
|
|
|
if (!JS_ConvertArguments (ctx, argc, argv, "ojj", &dataObject, &x, &y))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2008-03-10 00:35:37 -07:00
|
|
|
if (!dataObject)
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
int32 w, h;
|
|
|
|
JSObject *dataArray;
|
|
|
|
jsval v;
|
|
|
|
|
|
|
|
if (!JS_GetProperty(ctx, dataObject, "width", &v) ||
|
|
|
|
!JS_ValueToInt32(ctx, v, &w))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (!JS_GetProperty(ctx, dataObject, "height", &v) ||
|
|
|
|
!JS_ValueToInt32(ctx, v, &h))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (!JS_GetProperty(ctx, dataObject, "data", &v) ||
|
|
|
|
!JSVAL_IS_OBJECT(v))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
dataArray = JSVAL_TO_OBJECT(v);
|
|
|
|
|
|
|
|
if (!CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
jsuint arrayLen;
|
|
|
|
if (!JS_IsArrayObject(ctx, dataArray) ||
|
|
|
|
!JS_GetArrayLength(ctx, dataArray, &arrayLen) ||
|
|
|
|
arrayLen < (jsuint)(w * h * 4))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
2007-05-22 20:58:19 -07:00
|
|
|
nsAutoArrayPtr<PRUint8> imageBuffer(new (std::nothrow) PRUint8[w * h * 4]);
|
2008-02-13 22:53:25 -08:00
|
|
|
if (!imageBuffer)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint8 *imgPtr = imageBuffer.get();
|
2007-10-26 12:45:20 -07:00
|
|
|
|
2008-08-20 14:47:10 -07:00
|
|
|
JSBool ok = js_ArrayToJSUint8Buffer(ctx, dataArray, 0, w*h*4, imageBuffer);
|
|
|
|
|
|
|
|
// no fast path? go slow.
|
|
|
|
if (!ok) {
|
|
|
|
jsval vr, vg, vb, va;
|
|
|
|
PRUint8 ir, ig, ib, ia;
|
|
|
|
for (int32 j = 0; j < h; j++) {
|
|
|
|
for (int32 i = 0; i < w; i++) {
|
|
|
|
if (!JS_GetElement(ctx, dataArray, (j*w*4) + i*4 + 0, &vr) ||
|
|
|
|
!JS_GetElement(ctx, dataArray, (j*w*4) + i*4 + 1, &vg) ||
|
|
|
|
!JS_GetElement(ctx, dataArray, (j*w*4) + i*4 + 2, &vb) ||
|
|
|
|
!JS_GetElement(ctx, dataArray, (j*w*4) + i*4 + 3, &va))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (JSVAL_IS_INT(vr)) ir = (PRUint8) JSVAL_TO_INT(vr);
|
|
|
|
else if (JSVAL_IS_DOUBLE(vr)) ir = (PRUint8) (*JSVAL_TO_DOUBLE(vr));
|
|
|
|
else return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (JSVAL_IS_INT(vg)) ig = (PRUint8) JSVAL_TO_INT(vg);
|
|
|
|
else if (JSVAL_IS_DOUBLE(vg)) ig = (PRUint8) (*JSVAL_TO_DOUBLE(vg));
|
|
|
|
else return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (JSVAL_IS_INT(vb)) ib = (PRUint8) JSVAL_TO_INT(vb);
|
|
|
|
else if (JSVAL_IS_DOUBLE(vb)) ib = (PRUint8) (*JSVAL_TO_DOUBLE(vb));
|
|
|
|
else return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (JSVAL_IS_INT(va)) ia = (PRUint8) JSVAL_TO_INT(va);
|
|
|
|
else if (JSVAL_IS_DOUBLE(va)) ia = (PRUint8) (*JSVAL_TO_DOUBLE(va));
|
|
|
|
else return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
// Convert to premultiplied color (losslessly if the input came from getImageData)
|
|
|
|
ir = (ir*ia + 254) / 255;
|
|
|
|
ig = (ig*ia + 254) / 255;
|
|
|
|
ib = (ib*ia + 254) / 255;
|
|
|
|
|
|
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
|
|
*imgPtr++ = ib;
|
|
|
|
*imgPtr++ = ig;
|
|
|
|
*imgPtr++ = ir;
|
|
|
|
*imgPtr++ = ia;
|
|
|
|
#else
|
|
|
|
*imgPtr++ = ia;
|
|
|
|
*imgPtr++ = ir;
|
|
|
|
*imgPtr++ = ig;
|
|
|
|
*imgPtr++ = ib;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Walk through and premultiply and swap rgba */
|
|
|
|
/* XXX SSE me */
|
|
|
|
PRUint8 ir, ig, ib, ia;
|
|
|
|
PRUint8 *ptr = imgPtr;
|
|
|
|
for (int32 i = 0; i < w*h; i++) {
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef IS_LITTLE_ENDIAN
|
2008-08-20 14:47:10 -07:00
|
|
|
ir = ptr[0];
|
|
|
|
ig = ptr[1];
|
|
|
|
ib = ptr[2];
|
|
|
|
ia = ptr[3];
|
|
|
|
ptr[0] = (ib*ia + 254) / 255;
|
|
|
|
ptr[1] = (ig*ia + 254) / 255;
|
|
|
|
ptr[2] = (ir*ia + 254) / 255;
|
2007-03-22 10:30:00 -07:00
|
|
|
#else
|
2008-08-20 14:47:10 -07:00
|
|
|
ptr[0] = (ptr[0]*ptr[3] + 254) / 255;
|
|
|
|
ptr[1] = (ptr[1]*ptr[3] + 254) / 255;
|
|
|
|
ptr[2] = (ptr[2]*ptr[3] + 254) / 255;
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
2008-08-20 14:47:10 -07:00
|
|
|
ptr += 4;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(imageBuffer.get(),
|
|
|
|
gfxIntSize(w, h),
|
|
|
|
w * 4,
|
|
|
|
gfxASurface::ImageFormatARGB32);
|
|
|
|
if (!imgsurf || imgsurf->CairoStatus())
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsRefPtr<gfxPath> path = mThebes->CopyPath();
|
|
|
|
|
|
|
|
mThebes->Save();
|
|
|
|
mThebes->IdentityMatrix();
|
|
|
|
mThebes->Translate(gfxPoint(x, y));
|
|
|
|
mThebes->NewPath();
|
|
|
|
mThebes->Rectangle(gfxRect(0, 0, w, h));
|
|
|
|
mThebes->SetSource(imgsurf, gfxPoint(0, 0));
|
|
|
|
mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
|
|
mThebes->Fill();
|
|
|
|
mThebes->Restore();
|
|
|
|
|
|
|
|
mThebes->NewPath();
|
|
|
|
mThebes->AppendPath(path);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return Redraw();
|
|
|
|
}
|
2008-04-30 10:04:46 -07:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
|
|
|
|
{
|
2008-07-23 10:50:03 -07:00
|
|
|
if (!mSurface) {
|
2008-04-30 10:04:46 -07:00
|
|
|
*surface = nsnull;
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2008-07-23 10:50:03 -07:00
|
|
|
*surface = mSurface.get();
|
2008-04-30 10:04:46 -07:00
|
|
|
NS_ADDREF(*surface);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-09-02 10:52:22 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsCanvasRenderingContext2D::CreateImageData()
|
|
|
|
{
|
|
|
|
if (!mValid || !mCanvasElement)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsAXPCNativeCallContext *ncc = nsnull;
|
|
|
|
nsresult rv = nsContentUtils::XPConnect()->
|
|
|
|
GetCurrentNativeCallContext(&ncc);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (!ncc)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
JSContext *ctx = nsnull;
|
|
|
|
|
|
|
|
rv = ncc->GetJSContext(&ctx);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
PRUint32 argc;
|
|
|
|
jsval *argv = nsnull;
|
|
|
|
|
|
|
|
ncc->GetArgc(&argc);
|
|
|
|
ncc->GetArgvPtr(&argv);
|
|
|
|
|
|
|
|
JSAutoRequest ar(ctx);
|
|
|
|
|
|
|
|
int32 w, h;
|
|
|
|
if (!JS_ConvertArguments (ctx, argc, argv, "jj", &w, &h))
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
|
|
|
if (w <= 0 || h <= 0)
|
|
|
|
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
|
|
|
|
|
|
|
// check for overflow when calculating len
|
|
|
|
PRUint32 len0 = w * h;
|
|
|
|
if (len0 / w != h)
|
|
|
|
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
|
|
|
PRUint32 len = len0 * 4;
|
|
|
|
if (len / 4 != len0)
|
|
|
|
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
|
|
|
|
|
|
|
nsAutoArrayPtr<jsval> jsvector(new (std::nothrow) jsval[w * h * 4]);
|
|
|
|
if (!jsvector)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
jsval *dest = jsvector.get();
|
|
|
|
for (PRUint32 i = 0; i < len; i++)
|
|
|
|
*dest++ = JSVAL_ZERO;
|
|
|
|
|
|
|
|
JSObject *dataArray = JS_NewArrayObject(ctx, w*h*4, jsvector.get());
|
|
|
|
if (!dataArray)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
nsAutoGCRoot arrayGCRoot(&dataArray, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
JSObject *result = JS_NewObject(ctx, NULL, NULL, NULL);
|
|
|
|
if (!result)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
nsAutoGCRoot resultGCRoot(&result, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (!JS_DefineProperty(ctx, result, "width", INT_TO_JSVAL(w), NULL, NULL, 0) ||
|
|
|
|
!JS_DefineProperty(ctx, result, "height", INT_TO_JSVAL(h), NULL, NULL, 0) ||
|
|
|
|
!JS_DefineProperty(ctx, result, "data", OBJECT_TO_JSVAL(dataArray), NULL, NULL, 0))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
jsval *retvalPtr;
|
|
|
|
ncc->GetRetValPtr(&retvalPtr);
|
|
|
|
*retvalPtr = OBJECT_TO_JSVAL(result);
|
|
|
|
ncc->SetReturnValueWasSet(PR_TRUE);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
}
|
|
|
|
|