1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Vladimir Vukicevic <vladimir@pobox.com>
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
23 * Eric Butler <zantifon@gmail.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 #define _USE_MATH_DEFINES
46 #include "nsIServiceManager.h"
48 #include "nsContentUtils.h"
50 #include "nsIDOMDocument.h"
51 #include "nsIDocument.h"
52 #include "nsIDOMCanvasRenderingContext2D.h"
53 #include "nsICanvasRenderingContextInternal.h"
54 #include "nsPresContext.h"
55 #include "nsIPresShell.h"
56 #include "nsIVariant.h"
58 #include "imgIRequest.h"
59 #include "imgIContainer.h"
60 #include "gfxIImageFrame.h"
61 #include "nsIDOMHTMLCanvasElement.h"
62 #include "nsICanvasElement.h"
63 #include "nsIDOMHTMLImageElement.h"
64 #include "nsIImageLoadingContent.h"
65 #include "nsIInterfaceRequestorUtils.h"
68 #include "nsDOMError.h"
69 #include "nsIScriptError.h"
71 #include "nsICSSParser.h"
72 #include "nsICSSStyleRule.h"
73 #include "nsInspectorCSSUtils.h"
74 #include "nsStyleSet.h"
76 #include "nsPrintfCString.h"
78 #include "nsReadableUtils.h"
81 #include "nsIRenderingContext.h"
82 #include "nsIDeviceContext.h"
83 #include "nsGfxCIID.h"
84 #include "nsIScriptSecurityManager.h"
85 #include "nsIDocShell.h"
86 #include "nsPresContext.h"
87 #include "nsIPresShell.h"
88 #include "nsIDOMWindow.h"
89 #include "nsPIDOMWindow.h"
90 #include "nsIDocShell.h"
91 #include "nsIDocShellTreeItem.h"
92 #include "nsIDocShellTreeNode.h"
93 #include "nsIXPConnect.h"
99 #include "imgIEncoder.h"
101 #include "gfxContext.h"
102 #include "gfxASurface.h"
103 #include "gfxImageSurface.h"
104 #include "gfxPlatform.h"
106 #include "gfxTextRunCache.h"
109 #include "nsFrameManager.h"
111 #include "nsBidiPresUtils.h"
114 #include "nsHTMLVideoElement.h"
118 #define M_PI 3.14159265358979323846
119 #define M_PI_2 1.57079632679489661923
122 static PRBool
CheckSaneSubrectSize (PRInt32 x
, PRInt32 y
, PRInt32 w
, PRInt32 h
, PRInt32 realWidth
, PRInt32 realHeight
);
124 /* Float validation stuff */
126 #define VALIDATE(_f) if (!JSDOUBLE_IS_FINITE(_f)) return PR_FALSE
128 /* These must take doubles as args, because JSDOUBLE_IS_FINITE expects
129 * to take the address of its argument; we can't cast/convert in the
133 static PRBool
FloatValidate (double f1
) {
138 static PRBool
FloatValidate (double f1
, double f2
) {
139 VALIDATE(f1
); VALIDATE(f2
);
143 static PRBool
FloatValidate (double f1
, double f2
, double f3
) {
144 VALIDATE(f1
); VALIDATE(f2
); VALIDATE(f3
);
148 static PRBool
FloatValidate (double f1
, double f2
, double f3
, double f4
) {
149 VALIDATE(f1
); VALIDATE(f2
); VALIDATE(f3
); VALIDATE(f4
);
153 static PRBool
FloatValidate (double f1
, double f2
, double f3
, double f4
, double f5
) {
154 VALIDATE(f1
); VALIDATE(f2
); VALIDATE(f3
); VALIDATE(f4
); VALIDATE(f5
);
158 static PRBool
FloatValidate (double f1
, double f2
, double f3
, double f4
, double f5
, double f6
) {
159 VALIDATE(f1
); VALIDATE(f2
); VALIDATE(f3
); VALIDATE(f4
); VALIDATE(f5
); VALIDATE(f6
);
168 #define NS_CANVASGRADIENT_PRIVATE_IID \
169 { 0x491d39d8, 0x4058, 0x42bd, { 0xac, 0x76, 0x70, 0xd5, 0x62, 0x7f, 0x02, 0x10 } }
170 class nsCanvasGradient
: public nsIDOMCanvasGradient
173 NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID
)
175 nsCanvasGradient(gfxPattern
* pat
, nsICSSParser
* cssparser
)
176 : mPattern(pat
), mCSSParser(cssparser
)
180 void Apply(gfxContext
* ctx
) {
181 ctx
->SetPattern(mPattern
);
184 /* nsIDOMCanvasGradient */
185 NS_IMETHOD
AddColorStop (float offset
,
186 const nsAString
& colorstr
)
190 if (!FloatValidate(offset
))
191 return NS_ERROR_DOM_SYNTAX_ERR
;
193 if (offset
< 0.0 || offset
> 1.0)
194 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
196 nsresult rv
= mCSSParser
->ParseColorString(nsString(colorstr
), nsnull
, 0, &color
);
198 return NS_ERROR_DOM_SYNTAX_ERR
;
200 mPattern
->AddColorStop(offset
, gfxRGBA(color
));
208 nsRefPtr
<gfxPattern
> mPattern
;
209 nsCOMPtr
<nsICSSParser
> mCSSParser
;
212 NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasGradient
, NS_CANVASGRADIENT_PRIVATE_IID
)
214 NS_IMPL_ADDREF(nsCanvasGradient
)
215 NS_IMPL_RELEASE(nsCanvasGradient
)
217 NS_INTERFACE_MAP_BEGIN(nsCanvasGradient
)
218 NS_INTERFACE_MAP_ENTRY(nsCanvasGradient
)
219 NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient
)
220 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasGradient
)
221 NS_INTERFACE_MAP_ENTRY(nsISupports
)
227 #define NS_CANVASPATTERN_PRIVATE_IID \
228 { 0xb85c6c8a, 0x0624, 0x4530, { 0xb8, 0xee, 0xff, 0xdf, 0x42, 0xe8, 0x21, 0x6d } }
229 class nsCanvasPattern
: public nsIDOMCanvasPattern
232 NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID
)
234 nsCanvasPattern(gfxPattern
* pat
,
235 nsIPrincipal
* principalForSecurityCheck
,
236 PRBool forceWriteOnly
)
238 mPrincipal(principalForSecurityCheck
),
239 mForceWriteOnly(forceWriteOnly
)
243 void Apply(gfxContext
* ctx
) {
244 ctx
->SetPattern(mPattern
);
247 nsIPrincipal
* Principal() { return mPrincipal
; }
248 PRBool
GetForceWriteOnly() { return mForceWriteOnly
; }
253 nsRefPtr
<gfxPattern
> mPattern
;
254 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
255 PRPackedBool mForceWriteOnly
;
258 NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPattern
, NS_CANVASPATTERN_PRIVATE_IID
)
260 NS_IMPL_ADDREF(nsCanvasPattern
)
261 NS_IMPL_RELEASE(nsCanvasPattern
)
263 NS_INTERFACE_MAP_BEGIN(nsCanvasPattern
)
264 NS_INTERFACE_MAP_ENTRY(nsCanvasPattern
)
265 NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern
)
266 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasPattern
)
267 NS_INTERFACE_MAP_ENTRY(nsISupports
)
273 #define NS_TEXTMETRICS_PRIVATE_IID \
274 { 0xc5b1c2f9, 0xcb4f, 0x4394, { 0xaf, 0xe0, 0xc6, 0x59, 0x33, 0x80, 0x8b, 0xf3 } }
275 class nsTextMetrics
: public nsIDOMTextMetrics
278 nsTextMetrics(float w
) : width(w
) { }
280 virtual ~nsTextMetrics() { }
282 NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICS_PRIVATE_IID
)
284 NS_IMETHOD
GetWidth(float* w
) {
295 NS_DEFINE_STATIC_IID_ACCESSOR(nsTextMetrics
, NS_TEXTMETRICS_PRIVATE_IID
)
297 NS_IMPL_ADDREF(nsTextMetrics
)
298 NS_IMPL_RELEASE(nsTextMetrics
)
300 NS_INTERFACE_MAP_BEGIN(nsTextMetrics
)
301 NS_INTERFACE_MAP_ENTRY(nsTextMetrics
)
302 NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics
)
303 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(TextMetrics
)
304 NS_INTERFACE_MAP_ENTRY(nsISupports
)
307 struct nsCanvasBidiProcessor
;
310 ** nsCanvasRenderingContext2D
312 class nsCanvasRenderingContext2D
:
313 public nsIDOMCanvasRenderingContext2D
,
314 public nsICanvasRenderingContextInternal
317 nsCanvasRenderingContext2D();
318 virtual ~nsCanvasRenderingContext2D();
322 // nsICanvasRenderingContextInternal
323 NS_IMETHOD
SetCanvasElement(nsICanvasElement
* aParentCanvas
);
324 NS_IMETHOD
SetDimensions(PRInt32 width
, PRInt32 height
);
325 NS_IMETHOD
Render(gfxContext
*ctx
);
326 NS_IMETHOD
GetInputStream(const char* aMimeType
,
327 const PRUnichar
* aEncoderOptions
,
328 nsIInputStream
**aStream
);
329 NS_IMETHOD
GetThebesSurface(gfxASurface
**surface
);
330 NS_IMETHOD
SetIsOpaque(PRBool isOpaque
);
332 // nsISupports interface
335 // nsIDOMCanvasRenderingContext2D interface
336 NS_DECL_NSIDOMCANVASRENDERINGCONTEXT2D
346 // destroy thebes/image stuff, in preparation for possibly recreating
349 // Some helpers. Doesn't modify acolor on failure.
350 nsresult
SetStyleFromVariant(nsIVariant
* aStyle
, Style aWhichStyle
);
351 void StyleColorToString(const nscolor
& aColor
, nsAString
& aStr
);
353 void DirtyAllStyles();
355 * applies the given style as the current source. If the given style is
356 * a solid color, aUseGlobalAlpha indicates whether to multiply the alpha
357 * by global alpha, and is ignored otherwise.
359 void ApplyStyle(Style aWhichStyle
, PRBool aUseGlobalAlpha
= PR_TRUE
);
361 // If aPrincipal is not subsumed by this canvas element, then
362 // we make the canvas write-only so bad guys can't extract the pixel
363 // data. If forceWriteOnly is set, we force write only to be set
364 // and ignore aPrincipal. (This is used for when the original data came
365 // from a <canvas> that had write-only set.)
366 void DoDrawImageSecurityCheck(nsIPrincipal
* aPrincipal
,
367 PRBool forceWriteOnly
);
370 PRInt32 mWidth
, mHeight
;
372 PRPackedBool mOpaque
;
374 // the canvas element informs us when it's going away,
375 // so these are not nsCOMPtrs
376 nsICanvasElement
* mCanvasElement
;
378 // our CSS parser, for colors and whatnot
379 nsCOMPtr
<nsICSSParser
> mCSSParser
;
382 nsRefPtr
<gfxContext
> mThebes
;
383 nsRefPtr
<gfxASurface
> mSurface
;
388 * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
389 * Redraw is called, reset to false when Render is called.
391 PRBool mIsFrameInvalid
;
394 * Returns true iff the the given operator should affect areas of the
395 * destination where the source is transparent. Among other things, this
396 * implies that a fully transparent source would still affect the canvas.
398 PRBool
OperatorAffectsUncoveredAreas(gfxContext::GraphicsOperator op
) const
401 // XXX certain operators cause 2d.composite.uncovered.* tests to fail
403 return op
== gfxContext::OPERATOR_IN
||
404 op
== gfxContext::OPERATOR_OUT
||
405 op
== gfxContext::OPERATOR_DEST_IN
||
406 op
== gfxContext::OPERATOR_DEST_ATOP
||
407 op
== gfxContext::OPERATOR_SOURCE
;
412 * Returns true iff a shadow should be drawn along with a
415 PRBool
NeedToDrawShadow()
417 ContextState
& state
= CurrentState();
419 // special case the default values as a "don't draw shadows" mode
420 PRBool doDraw
= state
.colorStyles
[STYLE_SHADOW
] != 0 ||
421 state
.shadowOffset
.x
!= 0 ||
422 state
.shadowOffset
.y
!= 0;
423 PRBool isColor
= CurrentState().StyleIsColor(STYLE_SHADOW
);
425 // if not using one of the cooky operators, can avoid drawing a shadow
426 // if the color is fully transparent
427 return (doDraw
|| !isColor
) && (!isColor
||
428 NS_GET_A(state
.colorStyles
[STYLE_SHADOW
]) != 0 ||
429 OperatorAffectsUncoveredAreas(mThebes
->CurrentOperator()));
433 * Checks the current state to determine if an intermediate surface would
434 * be necessary to complete a drawing operation. Does not check the
435 * condition pertaining to global alpha and patterns since that does not
436 * pertain to all drawing operations.
438 PRBool
NeedToUseIntermediateSurface()
440 // certain operators always need an intermediate surface, except
441 // with quartz since quartz does compositing differently than cairo
442 return mThebes
->OriginalSurface()->GetType() != gfxASurface::SurfaceTypeQuartz
&&
443 OperatorAffectsUncoveredAreas(mThebes
->CurrentOperator());
445 // XXX there are other unhandled cases but they should be investigated
446 // first to ensure we aren't using an intermediate surface unecessarily
450 * Returns true iff the current source is such that global alpha would not
451 * be handled correctly without the use of an intermediate surface.
453 PRBool
NeedIntermediateSurfaceToHandleGlobalAlpha(Style aWhichStyle
)
455 return CurrentState().globalAlpha
!= 1.0 && !CurrentState().StyleIsColor(aWhichStyle
);
459 * Initializes the drawing of a shadow onto the canvas. The returned context
460 * should have the shadow shape drawn onto it, and then ShadowFinalize
461 * should be called. The return value is null if an error occurs.
462 * @param extents The extents of the shadow object, in device space.
463 * @param blur A newly contructed gfxAlphaBoxBlur, made with the default
464 * constructor and left uninitialized.
465 * @remark The lifetime of the return value is tied to the lifetime of
466 * the gfxAlphaBoxBlur, so it does not need to be ref counted.
468 gfxContext
* ShadowInitialize(const gfxRect
& extents
, gfxAlphaBoxBlur
& blur
);
471 * Completes a shadow drawing operation.
472 * @param blur The gfxAlphaBoxBlur that was passed to ShadowInitialize.
474 void ShadowFinalize(gfxAlphaBoxBlur
& blur
);
477 * Draws the current path in the given style. Takes care of
478 * any shadow drawing and will use intermediate surfaces as needed.
480 nsresult
DrawPath(Style style
);
483 * Draws a rectangle in the given style; used by FillRect and StrokeRect.
485 nsresult
DrawRect(const gfxRect
& rect
, Style style
);
498 TEXT_BASELINE_HANGING
,
499 TEXT_BASELINE_MIDDLE
,
500 TEXT_BASELINE_ALPHABETIC
,
501 TEXT_BASELINE_IDEOGRAPHIC
,
505 gfxFontGroup
*GetCurrentFontStyle();
507 enum TextDrawOperation
{
508 TEXT_DRAW_OPERATION_FILL
,
509 TEXT_DRAW_OPERATION_STROKE
,
510 TEXT_DRAW_OPERATION_MEASURE
514 * Implementation of the fillText, strokeText, and measure functions with
515 * the operation abstracted to a flag.
517 nsresult
DrawOrMeasureText(const nsAString
& text
,
521 TextDrawOperation op
,
526 * The previous set style. Is equal to STYLE_MAX when there is no valid
530 PRPackedBool mDirtyStyle
[STYLE_MAX
];
532 // state stack handling
535 ContextState() : shadowOffset(0.0, 0.0),
538 textAlign(TEXT_ALIGN_START
),
539 textBaseline(TEXT_BASELINE_ALPHABETIC
) { }
541 ContextState(const ContextState
& other
)
542 : shadowOffset(other
.shadowOffset
),
543 globalAlpha(other
.globalAlpha
),
544 shadowBlur(other
.shadowBlur
),
546 fontGroup(other
.fontGroup
),
547 textAlign(other
.textAlign
),
548 textBaseline(other
.textBaseline
)
550 for (int i
= 0; i
< STYLE_MAX
; i
++) {
551 colorStyles
[i
] = other
.colorStyles
[i
];
552 gradientStyles
[i
] = other
.gradientStyles
[i
];
553 patternStyles
[i
] = other
.patternStyles
[i
];
557 inline void SetColorStyle(Style whichStyle
, nscolor color
) {
558 colorStyles
[whichStyle
] = color
;
559 gradientStyles
[whichStyle
] = nsnull
;
560 patternStyles
[whichStyle
] = nsnull
;
563 inline void SetPatternStyle(Style whichStyle
, nsCanvasPattern
* pat
) {
564 gradientStyles
[whichStyle
] = nsnull
;
565 patternStyles
[whichStyle
] = pat
;
568 inline void SetGradientStyle(Style whichStyle
, nsCanvasGradient
* grad
) {
569 gradientStyles
[whichStyle
] = grad
;
570 patternStyles
[whichStyle
] = nsnull
;
574 * returns true iff the given style is a solid color.
576 inline PRBool
StyleIsColor(Style whichStyle
) const
578 return !(patternStyles
[whichStyle
] ||
579 gradientStyles
[whichStyle
]);
582 gfxPoint shadowOffset
;
587 nsRefPtr
<gfxFontGroup
> fontGroup
;
589 TextBaseline textBaseline
;
591 nscolor colorStyles
[STYLE_MAX
];
592 nsCOMPtr
<nsCanvasGradient
> gradientStyles
[STYLE_MAX
];
593 nsCOMPtr
<nsCanvasPattern
> patternStyles
[STYLE_MAX
];
596 nsTArray
<ContextState
> mStyleStack
;
598 inline ContextState
& CurrentState() {
599 return mStyleStack
[mSaveCount
];
602 // stolen from nsJSUtils
603 static PRBool
ConvertJSValToUint32(PRUint32
* aProp
, JSContext
* aContext
,
605 static PRBool
ConvertJSValToXPCObject(nsISupports
** aSupports
, REFNSIID aIID
,
606 JSContext
* aContext
, jsval aValue
);
607 static PRBool
ConvertJSValToDouble(double* aProp
, JSContext
* aContext
,
611 nsresult
ThebesSurfaceFromElement(nsIDOMElement
*imgElt
,
613 gfxASurface
**aSurface
,
614 PRInt32
*widthOut
, PRInt32
*heightOut
,
615 nsIPrincipal
**prinOut
,
616 PRBool
*forceWriteOnlyOut
);
619 void GetAppUnitsValues(PRUint32
*perDevPixel
, PRUint32
*perCSSPixel
) {
620 // If we don't have a canvas element, we just return something generic.
621 PRUint32 devPixel
= 60;
622 PRUint32 cssPixel
= 60;
624 nsCOMPtr
<nsINode
> elem
= do_QueryInterface(mCanvasElement
);
626 nsIDocument
*doc
= elem
->GetOwnerDoc();
627 if (!doc
) goto FINISH
;
628 nsIPresShell
*ps
= doc
->GetPrimaryShell();
629 if (!ps
) goto FINISH
;
630 nsPresContext
*pc
= ps
->GetPresContext();
631 if (!pc
) goto FINISH
;
632 devPixel
= pc
->AppUnitsPerDevPixel();
633 cssPixel
= pc
->AppUnitsPerCSSPixel();
638 *perDevPixel
= devPixel
;
640 *perCSSPixel
= cssPixel
;
643 friend struct nsCanvasBidiProcessor
;
646 NS_IMPL_ADDREF(nsCanvasRenderingContext2D
)
647 NS_IMPL_RELEASE(nsCanvasRenderingContext2D
)
649 NS_INTERFACE_MAP_BEGIN(nsCanvasRenderingContext2D
)
650 NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasRenderingContext2D
)
651 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal
)
652 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMCanvasRenderingContext2D
)
653 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasRenderingContext2D
)
657 ** CanvasRenderingContext2D impl
661 NS_NewCanvasRenderingContext2D(nsIDOMCanvasRenderingContext2D
** aResult
)
663 nsRefPtr
<nsIDOMCanvasRenderingContext2D
> ctx
= new nsCanvasRenderingContext2D();
665 return NS_ERROR_OUT_OF_MEMORY
;
667 *aResult
= ctx
.forget().get();
671 nsCanvasRenderingContext2D::nsCanvasRenderingContext2D()
672 : mValid(PR_FALSE
), mOpaque(PR_FALSE
), mCanvasElement(nsnull
),
673 mSaveCount(0), mIsFrameInvalid(PR_FALSE
), mStyleStack(20)
677 nsCanvasRenderingContext2D::~nsCanvasRenderingContext2D()
683 nsCanvasRenderingContext2D::Destroy()
688 mIsFrameInvalid
= PR_FALSE
;
692 nsCanvasRenderingContext2D::SetStyleFromVariant(nsIVariant
* aStyle
, Style aWhichStyle
)
698 rv
= aStyle
->GetDataType(¶mType
);
699 NS_ENSURE_SUCCESS(rv
, rv
);
701 if (paramType
== nsIDataType::VTYPE_DOMSTRING
||
702 paramType
== nsIDataType::VTYPE_WSTRING_SIZE_IS
) {
705 if (paramType
== nsIDataType::VTYPE_DOMSTRING
) {
706 rv
= aStyle
->GetAsDOMString(str
);
708 rv
= aStyle
->GetAsAString(str
);
710 NS_ENSURE_SUCCESS(rv
, rv
);
712 rv
= mCSSParser
->ParseColorString(str
, nsnull
, 0, &color
);
714 // Error reporting happens inside the CSS parser
718 CurrentState().SetColorStyle(aWhichStyle
, color
);
720 mDirtyStyle
[aWhichStyle
] = PR_TRUE
;
722 } else if (paramType
== nsIDataType::VTYPE_INTERFACE
||
723 paramType
== nsIDataType::VTYPE_INTERFACE_IS
)
726 nsCOMPtr
<nsISupports
> iface
;
727 rv
= aStyle
->GetAsInterface(&iid
, getter_AddRefs(iface
));
729 nsCOMPtr
<nsCanvasGradient
> grad(do_QueryInterface(iface
));
731 CurrentState().SetGradientStyle(aWhichStyle
, grad
);
732 mDirtyStyle
[aWhichStyle
] = PR_TRUE
;
736 nsCOMPtr
<nsCanvasPattern
> pattern(do_QueryInterface(iface
));
738 CurrentState().SetPatternStyle(aWhichStyle
, pattern
);
739 mDirtyStyle
[aWhichStyle
] = PR_TRUE
;
744 nsContentUtils::ReportToConsole(
745 nsContentUtils::eDOM_PROPERTIES
,
746 "UnexpectedCanvasVariantStyle",
750 nsIScriptError::warningFlag
,
757 nsCanvasRenderingContext2D::StyleColorToString(const nscolor
& aColor
, nsAString
& aStr
)
759 if (NS_GET_A(aColor
) == 255) {
760 CopyUTF8toUTF16(nsPrintfCString(100, "#%02x%02x%02x",
766 // "%0.5f" in nsPrintfCString would use the locale-specific
767 // decimal separator. That's why we have to do this:
768 PRUint32 alpha
= NS_GET_A(aColor
) * 100000 / 255;
769 CopyUTF8toUTF16(nsPrintfCString(100, "rgba(%d, %d, %d, 0.%d)",
779 nsCanvasRenderingContext2D::DirtyAllStyles()
781 for (int i
= 0; i
< STYLE_MAX
; i
++) {
782 mDirtyStyle
[i
] = PR_TRUE
;
787 nsCanvasRenderingContext2D::DoDrawImageSecurityCheck(nsIPrincipal
* aPrincipal
,
788 PRBool forceWriteOnly
)
790 // Callers should ensure that mCanvasElement is non-null before calling this
791 if (!mCanvasElement
) {
792 NS_WARNING("DoDrawImageSecurityCheck called without canvas element!");
796 if (mCanvasElement
->IsWriteOnly())
799 // If we explicitly set WriteOnly just do it and get out
800 if (forceWriteOnly
) {
801 mCanvasElement
->SetWriteOnly();
805 if (aPrincipal
== nsnull
)
808 nsCOMPtr
<nsINode
> elem
= do_QueryInterface(mCanvasElement
);
809 if (elem
) { // XXXbz How could this actually be null?
812 elem
->NodePrincipal()->Subsumes(aPrincipal
, &subsumes
);
814 if (NS_SUCCEEDED(rv
) && subsumes
) {
815 // This canvas has access to that image anyway
820 mCanvasElement
->SetWriteOnly();
824 nsCanvasRenderingContext2D::ApplyStyle(Style aWhichStyle
,
825 PRBool aUseGlobalAlpha
)
827 if (mLastStyle
== aWhichStyle
&&
828 !mDirtyStyle
[aWhichStyle
] &&
831 // nothing to do, this is already the set style
835 // if not using global alpha, don't optimize with dirty bit
837 mDirtyStyle
[aWhichStyle
] = PR_FALSE
;
838 mLastStyle
= aWhichStyle
;
840 nsCanvasPattern
* pattern
= CurrentState().patternStyles
[aWhichStyle
];
845 DoDrawImageSecurityCheck(pattern
->Principal(),
846 pattern
->GetForceWriteOnly());
847 pattern
->Apply(mThebes
);
851 if (CurrentState().gradientStyles
[aWhichStyle
]) {
852 CurrentState().gradientStyles
[aWhichStyle
]->Apply(mThebes
);
856 gfxRGBA
color(CurrentState().colorStyles
[aWhichStyle
]);
858 color
.a
*= CurrentState().globalAlpha
;
860 mThebes
->SetColor(color
);
864 nsCanvasRenderingContext2D::Redraw()
869 if (!mIsFrameInvalid
) {
870 mIsFrameInvalid
= PR_TRUE
;
871 return mCanvasElement
->InvalidateFrame();
878 nsCanvasRenderingContext2D::SetDimensions(PRInt32 width
, PRInt32 height
)
885 // Check that the dimensions are sane
886 if (gfxASurface::CheckSurfaceSize(gfxIntSize(width
, height
), 0xffff)) {
887 gfxASurface::gfxImageFormat format
= gfxASurface::ImageFormatARGB32
;
889 format
= gfxASurface::ImageFormatRGB24
;
891 mSurface
= gfxPlatform::GetPlatform()->CreateOffscreenSurface
892 (gfxIntSize(width
, height
), format
);
894 if (mSurface
->CairoStatus() == 0) {
895 mThebes
= new gfxContext(mSurface
);
899 /* Create dummy surfaces here */
900 if (mSurface
== nsnull
|| mSurface
->CairoStatus() != 0 ||
901 mThebes
== nsnull
|| mThebes
->HasError())
903 mSurface
= new gfxImageSurface(gfxIntSize(1,1), gfxASurface::ImageFormatARGB32
);
904 mThebes
= new gfxContext(mSurface
);
909 // set up the initial canvas defaults
913 ContextState
*state
= mStyleStack
.AppendElement();
914 state
->globalAlpha
= 1.0;
916 state
->colorStyles
[STYLE_FILL
] = NS_RGB(0,0,0);
917 state
->colorStyles
[STYLE_STROKE
] = NS_RGB(0,0,0);
918 state
->colorStyles
[STYLE_SHADOW
] = NS_RGBA(0,0,0,0);
921 mThebes
->SetOperator(gfxContext::OPERATOR_CLEAR
);
923 mThebes
->Rectangle(gfxRect(0, 0, mWidth
, mHeight
));
926 mThebes
->SetLineWidth(1.0);
927 mThebes
->SetOperator(gfxContext::OPERATOR_OVER
);
928 mThebes
->SetMiterLimit(10.0);
929 mThebes
->SetLineCap(gfxContext::LINE_CAP_BUTT
);
930 mThebes
->SetLineJoin(gfxContext::LINE_JOIN_MITER
);
938 nsCanvasRenderingContext2D::SetIsOpaque(PRBool isOpaque
)
940 if (isOpaque
== mOpaque
)
946 /* If we've already been created, let SetDimensions take care of
947 * recreating our surface
949 return SetDimensions(mWidth
, mHeight
);
956 nsCanvasRenderingContext2D::Render(gfxContext
*ctx
)
960 if (!mValid
|| !mSurface
||
961 mSurface
->CairoStatus() ||
963 return NS_ERROR_FAILURE
;
966 return NS_ERROR_FAILURE
;
968 nsRefPtr
<gfxPattern
> pat
= new gfxPattern(mSurface
);
970 gfxContext::GraphicsOperator op
= ctx
->CurrentOperator();
972 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
974 // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
975 // pixel alignment for this stuff!
977 ctx
->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth
, mHeight
), pat
);
981 ctx
->SetOperator(op
);
983 mIsFrameInvalid
= PR_FALSE
;
988 nsCanvasRenderingContext2D::GetInputStream(const char *aMimeType
,
989 const PRUnichar
*aEncoderOptions
,
990 nsIInputStream
**aStream
)
992 if (!mValid
|| !mSurface
||
993 mSurface
->CairoStatus() ||
995 return NS_ERROR_FAILURE
;
998 const char encoderPrefix
[] = "@mozilla.org/image/encoder;2?type=";
999 nsAutoArrayPtr
<char> conid(new (std::nothrow
) char[strlen(encoderPrefix
) + strlen(aMimeType
) + 1]);
1002 return NS_ERROR_OUT_OF_MEMORY
;
1004 strcpy(conid
, encoderPrefix
);
1005 strcat(conid
, aMimeType
);
1007 nsCOMPtr
<imgIEncoder
> encoder
= do_CreateInstance(conid
);
1009 return NS_ERROR_FAILURE
;
1011 nsAutoArrayPtr
<PRUint8
> imageBuffer(new (std::nothrow
) PRUint8
[mWidth
* mHeight
* 4]);
1013 return NS_ERROR_OUT_OF_MEMORY
;
1015 nsRefPtr
<gfxImageSurface
> imgsurf
= new gfxImageSurface(imageBuffer
.get(),
1016 gfxIntSize(mWidth
, mHeight
),
1018 gfxASurface::ImageFormatARGB32
);
1020 if (!imgsurf
|| imgsurf
->CairoStatus())
1021 return NS_ERROR_FAILURE
;
1023 nsRefPtr
<gfxContext
> ctx
= new gfxContext(imgsurf
);
1025 if (!ctx
|| ctx
->HasError())
1026 return NS_ERROR_FAILURE
;
1028 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
1029 ctx
->SetSource(mSurface
, gfxPoint(0, 0));
1032 rv
= encoder
->InitFromData(imageBuffer
.get(),
1033 mWidth
* mHeight
* 4, mWidth
, mHeight
, mWidth
* 4,
1034 imgIEncoder::INPUT_FORMAT_HOSTARGB
,
1035 nsDependentString(aEncoderOptions
));
1036 NS_ENSURE_SUCCESS(rv
, rv
);
1038 return CallQueryInterface(encoder
, aStream
);
1042 // nsCanvasRenderingContext2D impl
1046 nsCanvasRenderingContext2D::SetCanvasElement(nsICanvasElement
* aCanvasElement
)
1048 // don't hold a ref to this!
1049 mCanvasElement
= aCanvasElement
;
1051 // set up our css parser, if necessary
1053 mCSSParser
= do_CreateInstance("@mozilla.org/content/css-parser;1");
1060 nsCanvasRenderingContext2D::GetCanvas(nsIDOMHTMLCanvasElement
**canvas
)
1062 if (mCanvasElement
== nsnull
) {
1067 return CallQueryInterface(mCanvasElement
, canvas
);
1075 nsCanvasRenderingContext2D::Save()
1077 ContextState state
= CurrentState();
1078 mStyleStack
.AppendElement(state
);
1085 nsCanvasRenderingContext2D::Restore()
1087 if (mSaveCount
== 0)
1090 return NS_ERROR_DOM_INVALID_STATE_ERR
;
1092 mStyleStack
.RemoveElementAt(mSaveCount
);
1095 mLastStyle
= STYLE_MAX
;
1107 nsCanvasRenderingContext2D::Scale(float x
, float y
)
1109 if (!FloatValidate(x
,y
))
1110 return NS_ERROR_DOM_SYNTAX_ERR
;
1112 mThebes
->Scale(x
, y
);
1117 nsCanvasRenderingContext2D::Rotate(float angle
)
1119 if (!FloatValidate(angle
))
1120 return NS_ERROR_DOM_SYNTAX_ERR
;
1122 mThebes
->Rotate(angle
);
1127 nsCanvasRenderingContext2D::Translate(float x
, float y
)
1129 if (!FloatValidate(x
,y
))
1130 return NS_ERROR_DOM_SYNTAX_ERR
;
1132 mThebes
->Translate(gfxPoint(x
, y
));
1137 nsCanvasRenderingContext2D::Transform(float m11
, float m12
, float m21
, float m22
, float dx
, float dy
)
1139 if (!FloatValidate(m11
,m12
,m21
,m22
,dx
,dy
))
1140 return NS_ERROR_DOM_SYNTAX_ERR
;
1142 gfxMatrix
matrix(m11
, m12
, m21
, m22
, dx
, dy
);
1143 mThebes
->Multiply(matrix
);
1149 nsCanvasRenderingContext2D::SetTransform(float m11
, float m12
, float m21
, float m22
, float dx
, float dy
)
1151 if (!FloatValidate(m11
,m12
,m21
,m22
,dx
,dy
))
1152 return NS_ERROR_DOM_SYNTAX_ERR
;
1154 gfxMatrix
matrix(m11
, m12
, m21
, m22
, dx
, dy
);
1155 mThebes
->SetMatrix(matrix
);
1165 nsCanvasRenderingContext2D::SetGlobalAlpha(float aGlobalAlpha
)
1167 if (!FloatValidate(aGlobalAlpha
))
1168 return NS_ERROR_DOM_SYNTAX_ERR
;
1170 // ignore invalid values, as per spec
1171 if (aGlobalAlpha
< 0.0 || aGlobalAlpha
> 1.0)
1174 CurrentState().globalAlpha
= aGlobalAlpha
;
1181 nsCanvasRenderingContext2D::GetGlobalAlpha(float *aGlobalAlpha
)
1183 *aGlobalAlpha
= CurrentState().globalAlpha
;
1188 nsCanvasRenderingContext2D::SetStrokeStyle(nsIVariant
* aStyle
)
1190 return SetStyleFromVariant(aStyle
, STYLE_STROKE
);
1194 nsCanvasRenderingContext2D::GetStrokeStyle(nsIVariant
** aStyle
)
1198 nsCOMPtr
<nsIWritableVariant
> var
= do_CreateInstance("@mozilla.org/variant;1");
1200 return NS_ERROR_FAILURE
;
1201 rv
= var
->SetWritable(PR_TRUE
);
1202 NS_ENSURE_SUCCESS(rv
, rv
);
1204 if (CurrentState().patternStyles
[STYLE_STROKE
]) {
1205 rv
= var
->SetAsISupports(CurrentState().patternStyles
[STYLE_STROKE
]);
1206 NS_ENSURE_SUCCESS(rv
, rv
);
1207 } else if (CurrentState().gradientStyles
[STYLE_STROKE
]) {
1208 rv
= var
->SetAsISupports(CurrentState().gradientStyles
[STYLE_STROKE
]);
1209 NS_ENSURE_SUCCESS(rv
, rv
);
1212 StyleColorToString(CurrentState().colorStyles
[STYLE_STROKE
], styleStr
);
1214 rv
= var
->SetAsDOMString(styleStr
);
1215 NS_ENSURE_SUCCESS(rv
, rv
);
1218 *aStyle
= var
.forget().get();
1223 nsCanvasRenderingContext2D::SetFillStyle(nsIVariant
* aStyle
)
1225 return SetStyleFromVariant(aStyle
, STYLE_FILL
);
1229 nsCanvasRenderingContext2D::GetFillStyle(nsIVariant
** aStyle
)
1233 nsCOMPtr
<nsIWritableVariant
> var
= do_CreateInstance("@mozilla.org/variant;1");
1235 return NS_ERROR_FAILURE
;
1236 rv
= var
->SetWritable(PR_TRUE
);
1237 NS_ENSURE_SUCCESS(rv
, rv
);
1239 if (CurrentState().patternStyles
[STYLE_FILL
]) {
1240 rv
= var
->SetAsISupports(CurrentState().patternStyles
[STYLE_FILL
]);
1241 NS_ENSURE_SUCCESS(rv
, rv
);
1242 } else if (CurrentState().gradientStyles
[STYLE_FILL
]) {
1243 rv
= var
->SetAsISupports(CurrentState().gradientStyles
[STYLE_FILL
]);
1244 NS_ENSURE_SUCCESS(rv
, rv
);
1247 StyleColorToString(CurrentState().colorStyles
[STYLE_FILL
], styleStr
);
1249 rv
= var
->SetAsDOMString(styleStr
);
1250 NS_ENSURE_SUCCESS(rv
, rv
);
1253 *aStyle
= var
.forget().get();
1258 // gradients and patterns
1261 nsCanvasRenderingContext2D::CreateLinearGradient(float x0
, float y0
, float x1
, float y1
,
1262 nsIDOMCanvasGradient
**_retval
)
1264 if (!FloatValidate(x0
,y0
,x1
,y1
))
1265 return NS_ERROR_DOM_SYNTAX_ERR
;
1267 nsRefPtr
<gfxPattern
> gradpat
= new gfxPattern(x0
, y0
, x1
, y1
);
1269 return NS_ERROR_OUT_OF_MEMORY
;
1271 nsRefPtr
<nsIDOMCanvasGradient
> grad
= new nsCanvasGradient(gradpat
, mCSSParser
);
1273 return NS_ERROR_OUT_OF_MEMORY
;
1275 *_retval
= grad
.forget().get();
1280 nsCanvasRenderingContext2D::CreateRadialGradient(float x0
, float y0
, float r0
, float x1
, float y1
, float r1
,
1281 nsIDOMCanvasGradient
**_retval
)
1283 if (!FloatValidate(x0
,y0
,r0
,x1
,y1
,r1
))
1284 return NS_ERROR_DOM_SYNTAX_ERR
;
1286 nsRefPtr
<gfxPattern
> gradpat
= new gfxPattern(x0
, y0
, r0
, x1
, y1
, r1
);
1288 return NS_ERROR_OUT_OF_MEMORY
;
1290 nsRefPtr
<nsIDOMCanvasGradient
> grad
= new nsCanvasGradient(gradpat
, mCSSParser
);
1292 return NS_ERROR_OUT_OF_MEMORY
;
1294 *_retval
= grad
.forget().get();
1299 nsCanvasRenderingContext2D::CreatePattern(nsIDOMHTMLElement
*image
,
1300 const nsAString
& repeat
,
1301 nsIDOMCanvasPattern
**_retval
)
1304 gfxPattern::GraphicsExtend extend
;
1306 if (repeat
.IsEmpty() || repeat
.EqualsLiteral("repeat")) {
1307 extend
= gfxPattern::EXTEND_REPEAT
;
1308 } else if (repeat
.EqualsLiteral("repeat-x")) {
1310 extend
= gfxPattern::EXTEND_REPEAT
;
1311 } else if (repeat
.EqualsLiteral("repeat-y")) {
1313 extend
= gfxPattern::EXTEND_REPEAT
;
1314 } else if (repeat
.EqualsLiteral("no-repeat")) {
1315 extend
= gfxPattern::EXTEND_NONE
;
1317 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
1318 return NS_ERROR_DOM_SYNTAX_ERR
;
1321 PRInt32 imgWidth
, imgHeight
;
1322 nsCOMPtr
<nsIPrincipal
> principal
;
1323 PRBool forceWriteOnly
= PR_FALSE
;
1324 nsRefPtr
<gfxASurface
> imgsurf
;
1325 rv
= ThebesSurfaceFromElement(image
, PR_TRUE
,
1326 getter_AddRefs(imgsurf
), &imgWidth
, &imgHeight
,
1327 getter_AddRefs(principal
), &forceWriteOnly
);
1331 nsRefPtr
<gfxPattern
> thebespat
= new gfxPattern(imgsurf
);
1333 thebespat
->SetExtend(extend
);
1335 nsRefPtr
<nsCanvasPattern
> pat
= new nsCanvasPattern(thebespat
, principal
,
1338 return NS_ERROR_OUT_OF_MEMORY
;
1340 *_retval
= pat
.forget().get();
1348 nsCanvasRenderingContext2D::SetShadowOffsetX(float x
)
1350 if (!FloatValidate(x
))
1351 return NS_ERROR_DOM_SYNTAX_ERR
;
1352 CurrentState().shadowOffset
.x
= x
;
1357 nsCanvasRenderingContext2D::GetShadowOffsetX(float *x
)
1359 *x
= static_cast<float>(CurrentState().shadowOffset
.x
);
1364 nsCanvasRenderingContext2D::SetShadowOffsetY(float y
)
1366 if (!FloatValidate(y
))
1367 return NS_ERROR_DOM_SYNTAX_ERR
;
1368 CurrentState().shadowOffset
.y
= y
;
1373 nsCanvasRenderingContext2D::GetShadowOffsetY(float *y
)
1375 *y
= static_cast<float>(CurrentState().shadowOffset
.y
);
1380 nsCanvasRenderingContext2D::SetShadowBlur(float blur
)
1382 if (!FloatValidate(blur
))
1383 return NS_ERROR_DOM_SYNTAX_ERR
;
1386 CurrentState().shadowBlur
= blur
;
1391 nsCanvasRenderingContext2D::GetShadowBlur(float *blur
)
1393 *blur
= CurrentState().shadowBlur
;
1398 nsCanvasRenderingContext2D::SetShadowColor(const nsAString
& colorstr
)
1402 nsresult rv
= mCSSParser
->ParseColorString(nsString(colorstr
), nsnull
, 0, &color
);
1403 if (NS_FAILED(rv
)) {
1404 // Error reporting happens inside the CSS parser
1408 CurrentState().SetColorStyle(STYLE_SHADOW
, color
);
1410 mDirtyStyle
[STYLE_SHADOW
] = PR_TRUE
;
1416 nsCanvasRenderingContext2D::GetShadowColor(nsAString
& color
)
1418 StyleColorToString(CurrentState().colorStyles
[STYLE_SHADOW
], color
);
1424 CopyContext(gfxContext
* dest
, gfxContext
* src
)
1426 dest
->Multiply(src
->CurrentMatrix());
1428 nsRefPtr
<gfxPath
> path
= src
->CopyPath();
1430 dest
->AppendPath(path
);
1432 nsRefPtr
<gfxPattern
> pattern
= src
->GetPattern();
1433 dest
->SetPattern(pattern
);
1435 dest
->SetLineWidth(src
->CurrentLineWidth());
1436 dest
->SetLineCap(src
->CurrentLineCap());
1437 dest
->SetLineJoin(src
->CurrentLineJoin());
1438 dest
->SetMiterLimit(src
->CurrentMiterLimit());
1439 dest
->SetFillRule(src
->CurrentFillRule());
1441 dest
->SetAntialiasMode(src
->CurrentAntialiasMode());
1444 static const gfxFloat SIGMA_MAX
= 25;
1447 nsCanvasRenderingContext2D::ShadowInitialize(const gfxRect
& extents
, gfxAlphaBoxBlur
& blur
)
1449 gfxIntSize blurRadius
;
1451 gfxFloat sigma
= CurrentState().shadowBlur
> 8 ? sqrt(CurrentState().shadowBlur
) : CurrentState().shadowBlur
/ 2;
1452 // limit to avoid overly huge temp images
1453 if (sigma
> SIGMA_MAX
)
1455 blurRadius
= gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(sigma
, sigma
));
1457 // calculate extents
1458 gfxRect drawExtents
= extents
;
1460 // intersect with clip to avoid making overly huge temp images
1461 gfxMatrix matrix
= mThebes
->CurrentMatrix();
1462 mThebes
->IdentityMatrix();
1463 gfxRect clipExtents
= mThebes
->GetClipExtents();
1464 mThebes
->SetMatrix(matrix
);
1465 // outset by the blur radius so that blurs can leak onto the canvas even
1466 // when the shape is outside the clipping area
1467 clipExtents
.Outset(blurRadius
.height
, blurRadius
.width
,
1468 blurRadius
.height
, blurRadius
.width
);
1469 drawExtents
= drawExtents
.Intersect(clipExtents
- CurrentState().shadowOffset
);
1471 gfxContext
* ctx
= blur
.Init(drawExtents
, blurRadius
);
1480 nsCanvasRenderingContext2D::ShadowFinalize(gfxAlphaBoxBlur
& blur
)
1482 ApplyStyle(STYLE_SHADOW
);
1483 // canvas matrix was already applied, don't apply it twice, but do
1484 // apply the shadow offset
1485 gfxMatrix matrix
= mThebes
->CurrentMatrix();
1486 mThebes
->IdentityMatrix();
1487 mThebes
->Translate(CurrentState().shadowOffset
);
1489 blur
.Paint(mThebes
);
1490 mThebes
->SetMatrix(matrix
);
1494 nsCanvasRenderingContext2D::DrawPath(Style style
)
1497 * Need an intermediate surface when:
1498 * - globalAlpha != 1 and gradients/patterns are used (need to paint_with_alpha)
1499 * - certain operators are used and are not on mac (quartz/cairo composite operators don't quite line up)
1501 PRBool doUseIntermediateSurface
= NeedToUseIntermediateSurface() ||
1502 NeedIntermediateSurfaceToHandleGlobalAlpha(style
);
1504 PRBool doDrawShadow
= NeedToDrawShadow();
1507 gfxMatrix matrix
= mThebes
->CurrentMatrix();
1508 mThebes
->IdentityMatrix();
1510 // calculate extents of path
1511 gfxRect drawExtents
;
1512 if (style
== STYLE_FILL
)
1513 drawExtents
= mThebes
->GetUserFillExtent();
1514 else // STYLE_STROKE
1515 drawExtents
= mThebes
->GetUserStrokeExtent();
1517 mThebes
->SetMatrix(matrix
);
1519 gfxAlphaBoxBlur blur
;
1521 // no need for a ref here, the blur owns the context
1522 gfxContext
* ctx
= ShadowInitialize(drawExtents
, blur
);
1524 ApplyStyle(style
, PR_FALSE
);
1525 CopyContext(ctx
, mThebes
);
1526 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
1528 if (style
== STYLE_FILL
)
1533 ShadowFinalize(blur
);
1537 if (doUseIntermediateSurface
) {
1538 nsRefPtr
<gfxPath
> path
= mThebes
->CopyPath();
1539 // if the path didn't copy correctly then we can't restore it, so bail
1541 return NS_ERROR_FAILURE
;
1543 // draw onto a pushed group
1544 mThebes
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
1546 // XXX for some reason clipping messes up the path when push/popping
1547 // copying the path seems to fix it, for unknown reasons
1549 mThebes
->AppendPath(path
);
1551 // don't want operators to be applied twice
1552 mThebes
->SetOperator(gfxContext::OPERATOR_SOURCE
);
1556 if (style
== STYLE_FILL
)
1561 if (doUseIntermediateSurface
) {
1562 mThebes
->PopGroupToSource();
1565 mThebes
->Paint(CurrentState().StyleIsColor(style
) ? 1.0 : CurrentState().globalAlpha
);
1576 nsCanvasRenderingContext2D::ClearRect(float x
, float y
, float w
, float h
)
1578 if (!FloatValidate(x
,y
,w
,h
))
1579 return NS_ERROR_DOM_SYNTAX_ERR
;
1581 gfxContextPathAutoSaveRestore
pathSR(mThebes
);
1582 gfxContextAutoSaveRestore
autoSR(mThebes
);
1584 mThebes
->SetOperator(gfxContext::OPERATOR_CLEAR
);
1586 mThebes
->Rectangle(gfxRect(x
, y
, w
, h
));
1593 nsCanvasRenderingContext2D::DrawRect(const gfxRect
& rect
, Style style
)
1595 if (!FloatValidate(rect
.pos
.x
, rect
.pos
.y
, rect
.size
.width
, rect
.size
.height
))
1596 return NS_ERROR_DOM_SYNTAX_ERR
;
1598 gfxContextPathAutoSaveRestore
pathSR(mThebes
);
1601 mThebes
->Rectangle(rect
);
1603 nsresult rv
= DrawPath(style
);
1611 nsCanvasRenderingContext2D::FillRect(float x
, float y
, float w
, float h
)
1613 return DrawRect(gfxRect(x
, y
, w
, h
), STYLE_FILL
);
1617 nsCanvasRenderingContext2D::StrokeRect(float x
, float y
, float w
, float h
)
1619 return DrawRect(gfxRect(x
, y
, w
, h
), STYLE_STROKE
);
1627 nsCanvasRenderingContext2D::BeginPath()
1634 nsCanvasRenderingContext2D::ClosePath()
1636 mThebes
->ClosePath();
1641 nsCanvasRenderingContext2D::Fill()
1643 nsresult rv
= DrawPath(STYLE_FILL
);
1650 nsCanvasRenderingContext2D::Stroke()
1652 nsresult rv
= DrawPath(STYLE_STROKE
);
1659 nsCanvasRenderingContext2D::Clip()
1666 nsCanvasRenderingContext2D::MoveTo(float x
, float y
)
1668 if (!FloatValidate(x
,y
))
1669 return NS_ERROR_DOM_SYNTAX_ERR
;
1671 mThebes
->MoveTo(gfxPoint(x
, y
));
1676 nsCanvasRenderingContext2D::LineTo(float x
, float y
)
1678 if (!FloatValidate(x
,y
))
1679 return NS_ERROR_DOM_SYNTAX_ERR
;
1681 mThebes
->LineTo(gfxPoint(x
, y
));
1686 nsCanvasRenderingContext2D::QuadraticCurveTo(float cpx
, float cpy
, float x
, float y
)
1688 if (!FloatValidate(cpx
,cpy
,x
,y
))
1689 return NS_ERROR_DOM_SYNTAX_ERR
;
1691 // we will always have a current point, since beginPath forces
1693 gfxPoint c
= mThebes
->CurrentPoint();
1695 gfxPoint
cp(cpx
, cpy
);
1697 mThebes
->CurveTo((c
+cp
*2)/3.0, (p
+cp
*2)/3.0, p
);
1703 nsCanvasRenderingContext2D::BezierCurveTo(float cp1x
, float cp1y
,
1704 float cp2x
, float cp2y
,
1707 if (!FloatValidate(cp1x
,cp1y
,cp2x
,cp2y
,x
,y
))
1708 return NS_ERROR_DOM_SYNTAX_ERR
;
1710 mThebes
->CurveTo(gfxPoint(cp1x
, cp1y
),
1711 gfxPoint(cp2x
, cp2y
),
1718 nsCanvasRenderingContext2D::ArcTo(float x1
, float y1
, float x2
, float y2
, float radius
)
1720 if (!FloatValidate(x1
,y1
,x2
,y2
,radius
))
1721 return NS_ERROR_DOM_SYNTAX_ERR
;
1724 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
1726 /* This is an adaptation of the cairo_arc_to patch from Behdad
1727 * Esfahbod; once that patch is accepted, we should remove this
1728 * and just call cairo_arc_to() directly.
1731 double angle0
, angle1
, angle2
, angled
;
1737 gfxPoint p0
= mThebes
->CurrentPoint();
1739 angle0
= atan2 (p0
.y
- y1
, p0
.x
- x1
); /* angle from (x1,y1) to (p0.x,p0.y) */
1740 angle2
= atan2 (y2
- y1
, x2
- x1
); /* angle from (x1,y1) to (x2,y2) */
1741 angle1
= (angle0
+ angle2
) / 2; /* angle from (x1,y1) to (xc,yc) */
1743 angled
= angle2
- angle0
; /* the angle (p0.x,p0.y)--(x1,y1)--(x2,y2) */
1745 /* Shall we go forward or backward? */
1746 if (angled
> M_PI
|| (angled
< 0 && angled
> -M_PI
)) {
1748 angled
= 2 * M_PI
- angled
;
1758 angle0
+= M_PI_2
; /* angle from (xc,yc) to (p0.x,p0.y) */
1759 angle2
-= M_PI_2
; /* angle from (xc,yc) to (x2,y2) */
1760 angled
/= 2; /* the angle (p0.x,p0.y)--(x1,y1)--(xc,yc) */
1763 /* distance from (x1,y1) to (p0.x,p0.y) */
1764 d0
= sqrt ((p0
.x
-x1
)*(p0
.x
-x1
)+(p0
.y
-y1
)*(p0
.y
-y1
));
1765 /* distance from (x2,y2) to (p0.x,p0.y) */
1766 d2
= sqrt ((x2
-x1
)*(x2
-x1
)+(y2
-y1
)*(y2
-y1
));
1771 if (fabs(cos_
) >= 1e-5) { /* the arc may not fit */
1772 /* min distance of end-points from corner */
1773 double min_d
= d0
< d2
? d0
: d2
;
1774 /* max radius of an arc that fits */
1775 double max_r
= min_d
* sin_
/ cos_
;
1777 if (radius
> max_r
) {
1778 /* arc with requested radius doesn't fit */
1779 radius
= (float) max_r
;
1780 dc
= min_d
/ cos_
; /* distance of (xc,yc) from (x1,y1) */
1785 dc
= radius
/ sin_
; /* distance of (xc,yc) from (x1,y1) */
1788 /* find (cx,cy), the center of the arc */
1789 gfxPoint
c(x1
+ sin(angle1
) * dc
, y1
+ cos(angle1
) * dc
);
1791 /* the arc operation draws the line from current point (p0.x,p0.y)
1792 * to arc center too. */
1795 mThebes
->Arc(c
, radius
, angle0
, angle2
);
1797 mThebes
->NegativeArc(c
, radius
, angle2
, angle0
);
1799 mThebes
->LineTo(gfxPoint(x2
, y2
));
1805 nsCanvasRenderingContext2D::Arc(float x
, float y
, float r
, float startAngle
, float endAngle
, int ccw
)
1807 if (!FloatValidate(x
,y
,r
,startAngle
,endAngle
))
1808 return NS_ERROR_DOM_SYNTAX_ERR
;
1813 mThebes
->NegativeArc(p
, r
, startAngle
, endAngle
);
1815 mThebes
->Arc(p
, r
, startAngle
, endAngle
);
1820 nsCanvasRenderingContext2D::Rect(float x
, float y
, float w
, float h
)
1822 if (!FloatValidate(x
,y
,w
,h
))
1823 return NS_ERROR_DOM_SYNTAX_ERR
;
1825 mThebes
->Rectangle(gfxRect(x
, y
, w
, h
));
1834 * Helper function for SetFont that creates a style rule for the given font.
1835 * @param aFont The CSS font string
1836 * @param aCSSParser The CSS parser of the canvas rendering context
1837 * @param aNode The canvas element
1838 * @param aResult Pointer in which to place the new style rule.
1839 * @remark Assumes all pointer arguments are non-null.
1842 CreateFontStyleRule(const nsAString
& aFont
,
1843 nsICSSParser
* aCSSParser
,
1845 nsICSSStyleRule
** aResult
)
1849 nsCOMPtr
<nsICSSStyleRule
> rule
;
1852 nsIPrincipal
* principal
= aNode
->NodePrincipal();
1853 nsIDocument
* document
= aNode
->GetOwnerDoc();
1855 nsIURI
* docURL
= document
->GetDocumentURI();
1856 nsIURI
* baseURL
= document
->GetBaseURI();
1858 rv
= aCSSParser
->ParseStyleAttribute(
1863 getter_AddRefs(rule
));
1867 rv
= aCSSParser
->ParseProperty(eCSSProperty_font
,
1872 rule
->GetDeclaration(),
1877 // set line height to normal, as per spec
1878 rv
= aCSSParser
->ParseProperty(eCSSProperty_line_height
,
1879 NS_LITERAL_STRING("normal"),
1883 rule
->GetDeclaration(),
1888 rule
.forget(aResult
);
1893 nsCanvasRenderingContext2D::SetFont(const nsAString
& font
)
1898 * If font is defined with relative units (e.g. ems) and the parent
1899 * style context changes in between calls, setting the font to the
1900 * same value as previous could result in a different computed value,
1901 * so we cannot have the optimization where we check if the new font
1902 * string is equal to the old one.
1905 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mCanvasElement
);
1907 NS_WARNING("Canvas element must be an nsIContent and non-null");
1908 return NS_ERROR_FAILURE
;
1911 nsIDocument
* document
= content
->GetOwnerDoc();
1913 nsIPresShell
* presShell
= document
->GetPrimaryShell();
1915 return NS_ERROR_FAILURE
;
1917 nsCString langGroup
;
1918 presShell
->GetPresContext()->GetLangGroup()->ToUTF8String(langGroup
);
1920 nsCOMArray
<nsIStyleRule
> rules
;
1922 nsCOMPtr
<nsICSSStyleRule
> rule
;
1923 rv
= CreateFontStyleRule(font
, mCSSParser
.get(), content
.get(), getter_AddRefs(rule
));
1927 rules
.AppendObject(rule
);
1929 nsStyleSet
* styleSet
= presShell
->StyleSet();
1931 // have to get a parent style context for inherit-like relative
1932 // values (2em, bolder, etc.)
1933 nsRefPtr
<nsStyleContext
> parentContext
;
1935 if (content
->IsInDoc()) {
1936 // inherit from the canvas element
1937 parentContext
= nsInspectorCSSUtils::GetStyleContextForContent(
1942 // otherwise inherit from default (10px sans-serif)
1943 nsCOMPtr
<nsICSSStyleRule
> parentRule
;
1944 rv
= CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
1947 getter_AddRefs(parentRule
));
1950 nsCOMArray
<nsIStyleRule
> parentRules
;
1951 parentRules
.AppendObject(parentRule
);
1952 parentContext
= styleSet
->ResolveStyleForRules(nsnull
, parentRules
);
1956 return NS_ERROR_FAILURE
;
1958 nsRefPtr
<nsStyleContext
> sc
= styleSet
->ResolveStyleForRules(parentContext
, rules
);
1960 return NS_ERROR_FAILURE
;
1961 const nsStyleFont
* fontStyle
= sc
->GetStyleFont();
1963 NS_ASSERTION(fontStyle
, "Could not obtain font style");
1965 // use CSS pixels instead of dev pixels to avoid being affected by page zoom
1966 const PRUint32 aupcp
= nsPresContext::AppUnitsPerCSSPixel();
1967 // un-zoom the font size to avoid being affected by text-only zoom
1968 const nscoord fontSize
= nsStyleFont::UnZoomText(parentContext
->PresContext(), fontStyle
->mFont
.size
);
1970 gfxFontStyle
style(fontStyle
->mFont
.style
,
1971 fontStyle
->mFont
.weight
,
1972 NSAppUnitsToFloatPixels(fontSize
, aupcp
),
1974 fontStyle
->mFont
.sizeAdjust
,
1975 fontStyle
->mFont
.systemFont
,
1976 fontStyle
->mFont
.familyNameQuirks
);
1978 CurrentState().fontGroup
= gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle
->mFont
.name
, &style
, presShell
->GetPresContext()->GetUserFontSet());
1979 NS_ASSERTION(CurrentState().fontGroup
, "Could not get font group");
1980 CurrentState().font
= font
;
1985 nsCanvasRenderingContext2D::GetFont(nsAString
& font
)
1987 /* will initilize the value if not set, else does nothing */
1988 GetCurrentFontStyle();
1990 font
= CurrentState().font
;
1995 nsCanvasRenderingContext2D::SetTextAlign(const nsAString
& ta
)
1997 if (ta
.EqualsLiteral("start"))
1998 CurrentState().textAlign
= TEXT_ALIGN_START
;
1999 else if (ta
.EqualsLiteral("end"))
2000 CurrentState().textAlign
= TEXT_ALIGN_END
;
2001 else if (ta
.EqualsLiteral("left"))
2002 CurrentState().textAlign
= TEXT_ALIGN_LEFT
;
2003 else if (ta
.EqualsLiteral("right"))
2004 CurrentState().textAlign
= TEXT_ALIGN_RIGHT
;
2005 else if (ta
.EqualsLiteral("center"))
2006 CurrentState().textAlign
= TEXT_ALIGN_CENTER
;
2007 // spec says to not throw error for invalid arg, but do it anyway
2009 return NS_ERROR_INVALID_ARG
;
2015 nsCanvasRenderingContext2D::GetTextAlign(nsAString
& ta
)
2017 switch (CurrentState().textAlign
)
2019 case TEXT_ALIGN_START
:
2020 ta
.AssignLiteral("start");
2022 case TEXT_ALIGN_END
:
2023 ta
.AssignLiteral("end");
2025 case TEXT_ALIGN_LEFT
:
2026 ta
.AssignLiteral("left");
2028 case TEXT_ALIGN_RIGHT
:
2029 ta
.AssignLiteral("right");
2031 case TEXT_ALIGN_CENTER
:
2032 ta
.AssignLiteral("center");
2035 NS_ASSERTION(0, "textAlign holds invalid value");
2036 return NS_ERROR_FAILURE
;
2043 nsCanvasRenderingContext2D::SetTextBaseline(const nsAString
& tb
)
2045 if (tb
.EqualsLiteral("top"))
2046 CurrentState().textBaseline
= TEXT_BASELINE_TOP
;
2047 else if (tb
.EqualsLiteral("hanging"))
2048 CurrentState().textBaseline
= TEXT_BASELINE_HANGING
;
2049 else if (tb
.EqualsLiteral("middle"))
2050 CurrentState().textBaseline
= TEXT_BASELINE_MIDDLE
;
2051 else if (tb
.EqualsLiteral("alphabetic"))
2052 CurrentState().textBaseline
= TEXT_BASELINE_ALPHABETIC
;
2053 else if (tb
.EqualsLiteral("ideographic"))
2054 CurrentState().textBaseline
= TEXT_BASELINE_IDEOGRAPHIC
;
2055 else if (tb
.EqualsLiteral("bottom"))
2056 CurrentState().textBaseline
= TEXT_BASELINE_BOTTOM
;
2057 // spec says to not throw error for invalid arg, but do it anyway
2059 return NS_ERROR_INVALID_ARG
;
2065 nsCanvasRenderingContext2D::GetTextBaseline(nsAString
& tb
)
2067 switch (CurrentState().textBaseline
)
2069 case TEXT_BASELINE_TOP
:
2070 tb
.AssignLiteral("top");
2072 case TEXT_BASELINE_HANGING
:
2073 tb
.AssignLiteral("hanging");
2075 case TEXT_BASELINE_MIDDLE
:
2076 tb
.AssignLiteral("middle");
2078 case TEXT_BASELINE_ALPHABETIC
:
2079 tb
.AssignLiteral("alphabetic");
2081 case TEXT_BASELINE_IDEOGRAPHIC
:
2082 tb
.AssignLiteral("ideographic");
2084 case TEXT_BASELINE_BOTTOM
:
2085 tb
.AssignLiteral("bottom");
2088 NS_ASSERTION(0, "textBaseline holds invalid value");
2089 return NS_ERROR_FAILURE
;
2096 * Helper function that replaces the whitespace characters in a string
2097 * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
2098 * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
2099 * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
2100 * @param str The string whose whitespace characters to replace.
2103 TextReplaceWhitespaceCharacters(nsAutoString
& str
)
2105 str
.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
2109 nsCanvasRenderingContext2D::FillText(const nsAString
& text
, float x
, float y
, float maxWidth
)
2111 return DrawOrMeasureText(text
, x
, y
, maxWidth
, TEXT_DRAW_OPERATION_FILL
, nsnull
);
2115 nsCanvasRenderingContext2D::StrokeText(const nsAString
& text
, float x
, float y
, float maxWidth
)
2117 return DrawOrMeasureText(text
, x
, y
, maxWidth
, TEXT_DRAW_OPERATION_STROKE
, nsnull
);
2121 nsCanvasRenderingContext2D::MeasureText(const nsAString
& rawText
,
2122 nsIDOMTextMetrics
** _retval
)
2126 nsresult rv
= DrawOrMeasureText(rawText
, 0, 0, 0, TEXT_DRAW_OPERATION_MEASURE
, &width
);
2131 nsRefPtr
<nsIDOMTextMetrics
> textMetrics
= new nsTextMetrics(width
);
2132 if (!textMetrics
.get())
2133 return NS_ERROR_OUT_OF_MEMORY
;
2135 *_retval
= textMetrics
.forget().get();
2141 * Used for nsBidiPresUtils::ProcessText
2143 struct NS_STACK_CLASS nsCanvasBidiProcessor
: public nsBidiPresUtils::BidiProcessor
2145 virtual void SetText(const PRUnichar
* text
, PRInt32 length
, nsBidiDirection direction
)
2147 mTextRun
= gfxTextRunCache::MakeTextRun(text
,
2151 mAppUnitsPerDevPixel
,
2152 direction
==NSBIDI_RTL
? gfxTextRunFactory::TEXT_IS_RTL
: 0);
2155 virtual nscoord
GetWidth()
2157 gfxTextRun::Metrics textRunMetrics
= mTextRun
->MeasureText(0,
2158 mTextRun
->GetLength(),
2159 mDoMeasureBoundingBox
,
2163 // this only measures the height; the total width is gotten from the
2164 // the return value of ProcessText.
2165 if (mDoMeasureBoundingBox
) {
2166 textRunMetrics
.mBoundingBox
.Scale(1.0 / mAppUnitsPerDevPixel
);
2167 mBoundingBox
= mBoundingBox
.Union(textRunMetrics
.mBoundingBox
);
2170 return static_cast<nscoord
>(textRunMetrics
.mAdvanceWidth
/gfxFloat(mAppUnitsPerDevPixel
));
2173 virtual void DrawText(nscoord xOffset
, nscoord width
)
2175 gfxPoint point
= mPt
;
2176 point
.x
+= xOffset
* mAppUnitsPerDevPixel
;
2178 // offset is given in terms of left side of string
2179 if (mTextRun
->IsRightToLeft())
2180 point
.x
+= width
* mAppUnitsPerDevPixel
;
2182 // stroke or fill the text depending on operation
2183 if (mOp
== nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE
)
2184 mTextRun
->DrawToPath(mThebes
,
2187 mTextRun
->GetLength(),
2191 // mOp == TEXT_DRAW_OPERATION_FILL
2192 mTextRun
->Draw(mThebes
,
2195 mTextRun
->GetLength(),
2202 gfxTextRunCache::AutoTextRun mTextRun
;
2204 // pointer to the context, may not be the canvas's context
2205 // if an intermediate surface is being used
2206 gfxContext
* mThebes
;
2208 // position of the left side of the string, alphabetic baseline
2212 gfxFontGroup
* mFontgrp
;
2214 // dev pixel conversion factor
2215 PRUint32 mAppUnitsPerDevPixel
;
2217 // operation (fill or stroke)
2218 nsCanvasRenderingContext2D::TextDrawOperation mOp
;
2220 // union of bounding boxes of all runs, needed for shadows
2221 gfxRect mBoundingBox
;
2223 // true iff the bounding box should be measured
2224 PRBool mDoMeasureBoundingBox
;
2228 nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString
& aRawText
,
2232 TextDrawOperation aOp
,
2237 if (!FloatValidate(aX
, aY
, aMaxWidth
))
2238 return NS_ERROR_DOM_SYNTAX_ERR
;
2240 // spec isn't clear on what should happen if aMaxWidth <= 0, so
2241 // treat it as an invalid argument
2242 // technically, 0 should be an invalid value as well, but 0 is the default
2243 // arg, and there is no way to tell if the default was used
2245 return NS_ERROR_INVALID_ARG
;
2247 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mCanvasElement
);
2249 NS_WARNING("Canvas element must be an nsIContent and non-null");
2250 return NS_ERROR_FAILURE
;
2253 nsIDocument
* document
= content
->GetOwnerDoc();
2255 nsIPresShell
* presShell
= document
->GetPrimaryShell();
2257 return NS_ERROR_FAILURE
;
2259 nsBidiPresUtils
* bidiUtils
= presShell
->GetPresContext()->GetBidiUtils();
2261 return NS_ERROR_FAILURE
;
2263 // replace all the whitespace characters with U+0020 SPACE
2264 nsAutoString
textToDraw(aRawText
);
2265 TextReplaceWhitespaceCharacters(textToDraw
);
2267 // for now, default to ltr if not in doc
2268 PRBool isRTL
= PR_FALSE
;
2270 if (content
->IsInDoc()) {
2271 // try to find the closest context
2272 nsRefPtr
<nsStyleContext
> canvasStyle
=
2273 nsInspectorCSSUtils::GetStyleContextForContent(content
,
2277 return NS_ERROR_FAILURE
;
2278 isRTL
= canvasStyle
->GetStyleVisibility()->mDirection
==
2279 NS_STYLE_DIRECTION_RTL
;
2282 // don't need to take care of these with stroke since Stroke() does that
2283 PRBool doDrawShadow
= aOp
== TEXT_DRAW_OPERATION_FILL
&& NeedToDrawShadow();
2284 PRBool doUseIntermediateSurface
= aOp
== TEXT_DRAW_OPERATION_FILL
&&
2285 (NeedToUseIntermediateSurface() || NeedIntermediateSurfaceToHandleGlobalAlpha(STYLE_FILL
));
2287 nsCanvasBidiProcessor processor
;
2289 GetAppUnitsValues(&processor
.mAppUnitsPerDevPixel
, NULL
);
2290 processor
.mPt
= gfxPoint(aX
, aY
);
2291 processor
.mThebes
= mThebes
;
2292 processor
.mOp
= aOp
;
2293 processor
.mBoundingBox
= gfxRect(0, 0, 0, 0);
2294 // need to measure size if using an intermediate surface for drawing
2295 processor
.mDoMeasureBoundingBox
= doDrawShadow
;
2297 processor
.mFontgrp
= GetCurrentFontStyle();
2298 NS_ASSERTION(processor
.mFontgrp
, "font group is null");
2302 // calls bidi algo twice since it needs the full text width and the
2303 // bounding boxes before rendering anything
2304 rv
= bidiUtils
->ProcessText(textToDraw
.get(),
2305 textToDraw
.Length(),
2306 isRTL
? NSBIDI_RTL
: NSBIDI_LTR
,
2307 presShell
->GetPresContext(),
2309 nsBidiPresUtils::MODE_MEASURE
,
2317 *aWidth
= static_cast<float>(totalWidth
);
2319 // if only measuring, don't need to do any more work
2320 if (aOp
==TEXT_DRAW_OPERATION_MEASURE
)
2323 // offset pt.x based on text align
2326 if (CurrentState().textAlign
== TEXT_ALIGN_CENTER
)
2328 else if (CurrentState().textAlign
== TEXT_ALIGN_LEFT
||
2329 (!isRTL
&& CurrentState().textAlign
== TEXT_ALIGN_START
) ||
2330 (isRTL
&& CurrentState().textAlign
== TEXT_ALIGN_END
))
2335 processor
.mPt
.x
-= anchorX
* totalWidth
;
2337 // offset pt.y based on text baseline
2338 NS_ASSERTION(processor
.mFontgrp
->FontListLength()>0, "font group contains no fonts");
2339 const gfxFont::Metrics
& fontMetrics
= processor
.mFontgrp
->GetFontAt(0)->GetMetrics();
2343 switch (CurrentState().textBaseline
)
2345 case TEXT_BASELINE_TOP
:
2346 anchorY
= fontMetrics
.emAscent
;
2348 case TEXT_BASELINE_HANGING
:
2349 anchorY
= 0; // currently unavailable
2351 case TEXT_BASELINE_MIDDLE
:
2352 anchorY
= (fontMetrics
.emAscent
- fontMetrics
.emDescent
) * .5f
;
2354 case TEXT_BASELINE_ALPHABETIC
:
2357 case TEXT_BASELINE_IDEOGRAPHIC
:
2358 anchorY
= 0; // currently unvailable
2360 case TEXT_BASELINE_BOTTOM
:
2361 anchorY
= -fontMetrics
.emDescent
;
2364 NS_ASSERTION(0, "mTextBaseline holds invalid value");
2365 return NS_ERROR_FAILURE
;
2368 processor
.mPt
.y
+= anchorY
;
2370 // correct bounding box to get it to be the correct size/position
2371 processor
.mBoundingBox
.size
.width
= totalWidth
;
2372 processor
.mBoundingBox
.MoveBy(processor
.mPt
);
2374 processor
.mPt
.x
*= processor
.mAppUnitsPerDevPixel
;
2375 processor
.mPt
.y
*= processor
.mAppUnitsPerDevPixel
;
2377 // if text is over aMaxWidth, then scale the text horizontally such that its
2378 // width is precisely aMaxWidth
2379 gfxContextAutoSaveRestore autoSR
;
2380 if (aMaxWidth
> 0 && totalWidth
> aMaxWidth
) {
2381 autoSR
.SetContext(mThebes
);
2382 // translate the anchor point to 0, then scale and translate back
2383 gfxPoint
trans(aX
, 0);
2384 mThebes
->Translate(trans
);
2385 mThebes
->Scale(aMaxWidth
/totalWidth
, 1);
2386 mThebes
->Translate(-trans
);
2389 // don't ever need to measure the bounding box twice
2390 processor
.mDoMeasureBoundingBox
= PR_FALSE
;
2393 // for some reason the box is too tight, probably rounding error
2394 processor
.mBoundingBox
.Outset(2.0);
2396 // this is unnecessarily big is max-width scaling is involved, but it
2397 // will still produce correct output
2398 gfxRect drawExtents
= mThebes
->UserToDevice(processor
.mBoundingBox
);
2399 gfxAlphaBoxBlur blur
;
2401 gfxContext
* ctx
= ShadowInitialize(drawExtents
, blur
);
2404 CopyContext(ctx
, mThebes
);
2405 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
2406 processor
.mThebes
= ctx
;
2408 rv
= bidiUtils
->ProcessText(textToDraw
.get(),
2409 textToDraw
.Length(),
2410 isRTL
? NSBIDI_RTL
: NSBIDI_LTR
,
2411 presShell
->GetPresContext(),
2413 nsBidiPresUtils::MODE_DRAW
,
2420 ShadowFinalize(blur
);
2423 processor
.mThebes
= mThebes
;
2426 gfxContextPathAutoSaveRestore
pathSR(mThebes
, PR_FALSE
);
2428 // back up path if stroking
2429 if (aOp
== nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE
)
2431 // doUseIntermediateSurface is mutually exclusive to op == STROKE
2433 if (doUseIntermediateSurface
) {
2434 mThebes
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
2436 // don't want operators to be applied twice
2437 mThebes
->SetOperator(gfxContext::OPERATOR_SOURCE
);
2440 ApplyStyle(STYLE_FILL
);
2443 rv
= bidiUtils
->ProcessText(textToDraw
.get(),
2444 textToDraw
.Length(),
2445 isRTL
? NSBIDI_RTL
: NSBIDI_LTR
,
2446 presShell
->GetPresContext(),
2448 nsBidiPresUtils::MODE_DRAW
,
2453 // this needs to be restored before function can return
2454 if (doUseIntermediateSurface
) {
2455 mThebes
->PopGroupToSource();
2462 if (aOp
== nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE
) {
2463 // DrawPath takes care of all shadows and composite oddities
2464 rv
= DrawPath(STYLE_STROKE
);
2467 } else if (doUseIntermediateSurface
)
2468 mThebes
->Paint(CurrentState().StyleIsColor(STYLE_FILL
) ? 1.0 : CurrentState().globalAlpha
);
2474 nsCanvasRenderingContext2D::SetMozTextStyle(const nsAString
& textStyle
)
2476 // font and mozTextStyle are the same value
2477 return SetFont(textStyle
);
2481 nsCanvasRenderingContext2D::GetMozTextStyle(nsAString
& textStyle
)
2483 // font and mozTextStyle are the same value
2484 return GetFont(textStyle
);
2487 gfxFontGroup
*nsCanvasRenderingContext2D::GetCurrentFontStyle()
2489 // use lazy initilization for the font group since it's rather expensive
2490 if(!CurrentState().fontGroup
) {
2494 SetMozTextStyle(NS_LITERAL_STRING("10px sans-serif"));
2495 NS_ASSERTION(res
== NS_OK
, "Default canvas font is invalid");
2498 return CurrentState().fontGroup
;
2502 nsCanvasRenderingContext2D::MozDrawText(const nsAString
& textToDraw
)
2504 const PRUnichar
* textdata
;
2505 textToDraw
.GetData(&textdata
);
2507 PRUint32 textrunflags
= 0;
2510 GetAppUnitsValues(&aupdp
, NULL
);
2512 gfxTextRunCache::AutoTextRun textRun
;
2513 textRun
= gfxTextRunCache::MakeTextRun(textdata
,
2514 textToDraw
.Length(),
2515 GetCurrentFontStyle(),
2521 return NS_ERROR_FAILURE
;
2523 gfxPoint
pt(0.0f
,0.0f
);
2525 // Fill color is text color
2526 ApplyStyle(STYLE_FILL
);
2528 textRun
->Draw(mThebes
,
2531 textToDraw
.Length(),
2539 nsCanvasRenderingContext2D::MozMeasureText(const nsAString
& textToMeasure
, float *retVal
)
2541 nsCOMPtr
<nsIDOMTextMetrics
> metrics
;
2543 rv
= MeasureText(textToMeasure
, getter_AddRefs(metrics
));
2546 return metrics
->GetWidth(retVal
);
2550 nsCanvasRenderingContext2D::MozPathText(const nsAString
& textToPath
)
2552 const PRUnichar
* textdata
;
2553 textToPath
.GetData(&textdata
);
2555 PRUint32 textrunflags
= 0;
2558 GetAppUnitsValues(&aupdp
, NULL
);
2560 gfxTextRunCache::AutoTextRun textRun
;
2561 textRun
= gfxTextRunCache::MakeTextRun(textdata
,
2562 textToPath
.Length(),
2563 GetCurrentFontStyle(),
2569 return NS_ERROR_FAILURE
;
2571 gfxPoint
pt(0.0f
,0.0f
);
2573 textRun
->DrawToPath(mThebes
,
2576 textToPath
.Length(),
2583 nsCanvasRenderingContext2D::MozTextAlongPath(const nsAString
& textToDraw
, PRBool stroke
)
2585 // Most of this code is copied from its svg equivalent
2586 nsRefPtr
<gfxFlattenedPath
> path(mThebes
->GetFlattenedPath());
2588 const PRUnichar
* textdata
;
2589 textToDraw
.GetData(&textdata
);
2591 PRUint32 textrunflags
= 0;
2594 GetAppUnitsValues(&aupdp
, NULL
);
2596 gfxTextRunCache::AutoTextRun textRun
;
2597 textRun
= gfxTextRunCache::MakeTextRun(textdata
,
2598 textToDraw
.Length(),
2599 GetCurrentFontStyle(),
2605 return NS_ERROR_FAILURE
;
2612 PathChar() : draw(PR_FALSE
), angle(0.0), pos(0.0,0.0) {}
2615 gfxFloat length
= path
->GetLength();
2616 PRUint32 strLength
= textToDraw
.Length();
2618 PathChar
*cp
= new PathChar
[strLength
];
2621 return NS_ERROR_OUT_OF_MEMORY
;
2624 gfxPoint
position(0.0,0.0);
2625 gfxFloat x
= position
.x
;
2626 for (PRUint32 i
= 0; i
< strLength
; i
++)
2628 gfxFloat halfAdvance
= textRun
->GetAdvanceWidth(i
, 1, nsnull
) / (2.0 * aupdp
);
2630 // Check for end of path
2631 if(x
+ halfAdvance
> length
)
2634 if(x
+ halfAdvance
>= 0)
2636 cp
[i
].draw
= PR_TRUE
;
2637 gfxPoint pt
= path
->FindPoint(gfxPoint(x
+ halfAdvance
, position
.y
), &(cp
[i
].angle
));
2639 cp
[i
].pos
= pt
- gfxPoint(cos(cp
[i
].angle
), sin(cp
[i
].angle
)) * halfAdvance
;
2641 x
+= 2 * halfAdvance
;
2645 ApplyStyle(STYLE_STROKE
);
2647 ApplyStyle(STYLE_FILL
);
2649 for(PRUint32 i
= 0; i
< strLength
; i
++)
2651 // Skip non-visible characters
2652 if(!cp
[i
].draw
) continue;
2654 gfxMatrix matrix
= mThebes
->CurrentMatrix();
2657 rot
.Rotate(cp
[i
].angle
);
2658 mThebes
->Multiply(rot
);
2661 rot
.Scale(aupdp
,aupdp
);
2662 gfxPoint pt
= rot
.Transform(cp
[i
].pos
);
2665 textRun
->DrawToPath(mThebes
, pt
, i
, 1, nsnull
, nsnull
);
2667 textRun
->Draw(mThebes
, pt
, i
, 1, nsnull
, nsnull
, nsnull
);
2669 mThebes
->SetMatrix(matrix
);
2681 nsCanvasRenderingContext2D::SetLineWidth(float width
)
2683 if (!FloatValidate(width
))
2684 return NS_ERROR_DOM_SYNTAX_ERR
;
2686 mThebes
->SetLineWidth(width
);
2691 nsCanvasRenderingContext2D::GetLineWidth(float *width
)
2693 gfxFloat d
= mThebes
->CurrentLineWidth();
2694 *width
= static_cast<float>(d
);
2699 nsCanvasRenderingContext2D::SetLineCap(const nsAString
& capstyle
)
2701 gfxContext::GraphicsLineCap cap
;
2703 if (capstyle
.EqualsLiteral("butt"))
2704 cap
= gfxContext::LINE_CAP_BUTT
;
2705 else if (capstyle
.EqualsLiteral("round"))
2706 cap
= gfxContext::LINE_CAP_ROUND
;
2707 else if (capstyle
.EqualsLiteral("square"))
2708 cap
= gfxContext::LINE_CAP_SQUARE
;
2710 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
2711 return NS_ERROR_NOT_IMPLEMENTED
;
2713 mThebes
->SetLineCap(cap
);
2718 nsCanvasRenderingContext2D::GetLineCap(nsAString
& capstyle
)
2720 gfxContext::GraphicsLineCap cap
= mThebes
->CurrentLineCap();
2722 if (cap
== gfxContext::LINE_CAP_BUTT
)
2723 capstyle
.AssignLiteral("butt");
2724 else if (cap
== gfxContext::LINE_CAP_ROUND
)
2725 capstyle
.AssignLiteral("round");
2726 else if (cap
== gfxContext::LINE_CAP_SQUARE
)
2727 capstyle
.AssignLiteral("square");
2729 return NS_ERROR_FAILURE
;
2735 nsCanvasRenderingContext2D::SetLineJoin(const nsAString
& joinstyle
)
2737 gfxContext::GraphicsLineJoin j
;
2739 if (joinstyle
.EqualsLiteral("round"))
2740 j
= gfxContext::LINE_JOIN_ROUND
;
2741 else if (joinstyle
.EqualsLiteral("bevel"))
2742 j
= gfxContext::LINE_JOIN_BEVEL
;
2743 else if (joinstyle
.EqualsLiteral("miter"))
2744 j
= gfxContext::LINE_JOIN_MITER
;
2746 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
2747 return NS_ERROR_NOT_IMPLEMENTED
;
2749 mThebes
->SetLineJoin(j
);
2754 nsCanvasRenderingContext2D::GetLineJoin(nsAString
& joinstyle
)
2756 gfxContext::GraphicsLineJoin j
= mThebes
->CurrentLineJoin();
2758 if (j
== gfxContext::LINE_JOIN_ROUND
)
2759 joinstyle
.AssignLiteral("round");
2760 else if (j
== gfxContext::LINE_JOIN_BEVEL
)
2761 joinstyle
.AssignLiteral("bevel");
2762 else if (j
== gfxContext::LINE_JOIN_MITER
)
2763 joinstyle
.AssignLiteral("miter");
2765 return NS_ERROR_FAILURE
;
2771 nsCanvasRenderingContext2D::SetMiterLimit(float miter
)
2773 if (!FloatValidate(miter
))
2774 return NS_ERROR_DOM_SYNTAX_ERR
;
2776 mThebes
->SetMiterLimit(miter
);
2781 nsCanvasRenderingContext2D::GetMiterLimit(float *miter
)
2783 gfxFloat d
= mThebes
->CurrentMiterLimit();
2784 *miter
= static_cast<float>(d
);
2789 nsCanvasRenderingContext2D::IsPointInPath(float x
, float y
, PRBool
*retVal
)
2791 if (!FloatValidate(x
,y
))
2792 return NS_ERROR_DOM_SYNTAX_ERR
;
2794 *retVal
= mThebes
->PointInFill(gfxPoint(x
,y
));
2802 // drawImage(in HTMLImageElement image, in float dx, in float dy);
2803 // -- render image from 0,0 at dx,dy top-left coords
2804 // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
2805 // -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
2806 // 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);
2807 // -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
2810 nsCanvasRenderingContext2D::DrawImage()
2814 // we can't do a security check without a canvas element, so
2815 // just skip this entirely
2816 if (!mCanvasElement
)
2817 return NS_ERROR_FAILURE
;
2819 nsAXPCNativeCallContext
*ncc
= nsnull
;
2820 rv
= nsContentUtils::XPConnect()->
2821 GetCurrentNativeCallContext(&ncc
);
2822 NS_ENSURE_SUCCESS(rv
, rv
);
2825 return NS_ERROR_FAILURE
;
2827 JSContext
*ctx
= nsnull
;
2829 rv
= ncc
->GetJSContext(&ctx
);
2830 NS_ENSURE_SUCCESS(rv
, rv
);
2833 jsval
*argv
= nsnull
;
2835 ncc
->GetArgc(&argc
);
2836 ncc
->GetArgvPtr(&argv
);
2838 // we always need at least an image and a dx,dy
2840 return NS_ERROR_INVALID_ARG
;
2842 JSAutoRequest
ar(ctx
);
2847 nsCOMPtr
<nsIDOMElement
> imgElt
;
2848 if (!ConvertJSValToXPCObject(getter_AddRefs(imgElt
),
2849 NS_GET_IID(nsIDOMElement
),
2851 return NS_ERROR_DOM_TYPE_MISMATCH_ERR
;
2853 PRInt32 imgWidth
, imgHeight
;
2854 nsCOMPtr
<nsIPrincipal
> principal
;
2855 PRBool forceWriteOnly
= PR_FALSE
;
2857 nsRefPtr
<gfxPattern
> pattern
;
2858 nsRefPtr
<gfxPath
> path
;
2859 nsRefPtr
<gfxASurface
> imgsurf
;
2860 rv
= ThebesSurfaceFromElement(imgElt
, PR_FALSE
,
2861 getter_AddRefs(imgsurf
), &imgWidth
, &imgHeight
,
2862 getter_AddRefs(principal
), &forceWriteOnly
);
2865 DoDrawImageSecurityCheck(principal
, forceWriteOnly
);
2867 gfxContextPathAutoSaveRestore
pathSR(mThebes
, PR_FALSE
);
2869 #define GET_ARG(dest,whicharg) \
2870 do { if (!ConvertJSValToDouble(dest, ctx, whicharg)) { rv = NS_ERROR_INVALID_ARG; goto FINISH; } } while (0)
2875 GET_ARG(&dx
, argv
[1]);
2876 GET_ARG(&dy
, argv
[2]);
2878 dw
= sw
= (double) imgWidth
;
2879 dh
= sh
= (double) imgHeight
;
2880 } else if (argc
== 5) {
2881 GET_ARG(&dx
, argv
[1]);
2882 GET_ARG(&dy
, argv
[2]);
2883 GET_ARG(&dw
, argv
[3]);
2884 GET_ARG(&dh
, argv
[4]);
2886 sw
= (double) imgWidth
;
2887 sh
= (double) imgHeight
;
2888 } else if (argc
== 9) {
2889 GET_ARG(&sx
, argv
[1]);
2890 GET_ARG(&sy
, argv
[2]);
2891 GET_ARG(&sw
, argv
[3]);
2892 GET_ARG(&sh
, argv
[4]);
2893 GET_ARG(&dx
, argv
[5]);
2894 GET_ARG(&dy
, argv
[6]);
2895 GET_ARG(&dw
, argv
[7]);
2896 GET_ARG(&dh
, argv
[8]);
2898 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
2899 rv
= NS_ERROR_INVALID_ARG
;
2904 if (dw
== 0.0 || dh
== 0.0) {
2906 // not really failure, but nothing to do --
2907 // and noone likes a divide-by-zero
2911 if (!FloatValidate(sx
,sy
,sw
,sh
) || !FloatValidate(dx
,dy
,dw
,dh
)) {
2912 rv
= NS_ERROR_DOM_SYNTAX_ERR
;
2917 if (sx
< 0.0 || sy
< 0.0 ||
2918 sw
< 0.0 || sw
> (double) imgWidth
||
2919 sh
< 0.0 || sh
> (double) imgHeight
||
2920 dw
< 0.0 || dh
< 0.0)
2922 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
2923 rv
= NS_ERROR_DOM_INDEX_SIZE_ERR
;
2927 matrix
.Translate(gfxPoint(sx
, sy
));
2928 matrix
.Scale(sw
/dw
, sh
/dh
);
2930 pattern
= new gfxPattern(imgsurf
);
2931 pattern
->SetMatrix(matrix
);
2936 gfxContextAutoSaveRestore
autoSR(mThebes
);
2937 mThebes
->Translate(gfxPoint(dx
, dy
));
2938 mThebes
->SetPattern(pattern
);
2940 gfxRect
clip(0, 0, dw
, dh
);
2942 if (NeedToDrawShadow()) {
2943 gfxRect drawExtents
= mThebes
->UserToDevice(clip
);
2944 gfxAlphaBoxBlur blur
;
2946 gfxContext
* ctx
= ShadowInitialize(drawExtents
, blur
);
2949 CopyContext(ctx
, mThebes
);
2950 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
2954 ShadowFinalize(blur
);
2958 PRBool doUseIntermediateSurface
= NeedToUseIntermediateSurface();
2960 mThebes
->SetPattern(pattern
);
2963 if (doUseIntermediateSurface
) {
2964 // draw onto a pushed group
2965 mThebes
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
2966 mThebes
->Clip(clip
);
2968 // don't want operators to be applied twice
2969 mThebes
->SetOperator(gfxContext::OPERATOR_SOURCE
);
2972 mThebes
->PopGroupToSource();
2974 mThebes
->Clip(clip
);
2976 mThebes
->Paint(CurrentState().globalAlpha
);
2980 // XXX cairo bug workaround; force a clip update on mThebes.
2981 // Otherwise, a pixman clip gets left around somewhere, and pixman
2982 // (Render) does source clipping as well -- so we end up
2983 // compositing with an incorrect clip. This only seems to affect
2984 // fallback cases, which happen when we have CSS scaling going on.
2985 // This will blow away the current path, but we already blew it
2986 // away in this function earlier.
2987 mThebes
->UpdateSurfaceClip();
2991 if (NS_SUCCEEDED(rv
))
2998 nsCanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString
& op
)
3000 gfxContext::GraphicsOperator thebes_op
;
3002 #define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \
3003 if (op.EqualsLiteral(cvsop)) \
3004 thebes_op = gfxContext::OPERATOR_##thebesop;
3006 // XXX "darker" isn't really correct
3007 CANVAS_OP_TO_THEBES_OP("clear", CLEAR
)
3008 else CANVAS_OP_TO_THEBES_OP("copy", SOURCE
)
3009 else CANVAS_OP_TO_THEBES_OP("darker", SATURATE
) // XXX
3010 else CANVAS_OP_TO_THEBES_OP("destination-atop", DEST_ATOP
)
3011 else CANVAS_OP_TO_THEBES_OP("destination-in", DEST_IN
)
3012 else CANVAS_OP_TO_THEBES_OP("destination-out", DEST_OUT
)
3013 else CANVAS_OP_TO_THEBES_OP("destination-over", DEST_OVER
)
3014 else CANVAS_OP_TO_THEBES_OP("lighter", ADD
)
3015 else CANVAS_OP_TO_THEBES_OP("source-atop", ATOP
)
3016 else CANVAS_OP_TO_THEBES_OP("source-in", IN
)
3017 else CANVAS_OP_TO_THEBES_OP("source-out", OUT
)
3018 else CANVAS_OP_TO_THEBES_OP("source-over", OVER
)
3019 else CANVAS_OP_TO_THEBES_OP("xor", XOR
)
3020 // not part of spec, kept here for compat
3021 else CANVAS_OP_TO_THEBES_OP("over", OVER
)
3022 else return NS_ERROR_NOT_IMPLEMENTED
;
3024 #undef CANVAS_OP_TO_THEBES_OP
3026 mThebes
->SetOperator(thebes_op
);
3031 nsCanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString
& op
)
3033 gfxContext::GraphicsOperator thebes_op
= mThebes
->CurrentOperator();
3035 #define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \
3036 if (thebes_op == gfxContext::OPERATOR_##thebesop) \
3037 op.AssignLiteral(cvsop);
3039 // XXX "darker" isn't really correct
3040 CANVAS_OP_TO_THEBES_OP("clear", CLEAR
)
3041 else CANVAS_OP_TO_THEBES_OP("copy", SOURCE
)
3042 else CANVAS_OP_TO_THEBES_OP("darker", SATURATE
) // XXX
3043 else CANVAS_OP_TO_THEBES_OP("destination-atop", DEST_ATOP
)
3044 else CANVAS_OP_TO_THEBES_OP("destination-in", DEST_IN
)
3045 else CANVAS_OP_TO_THEBES_OP("destination-out", DEST_OUT
)
3046 else CANVAS_OP_TO_THEBES_OP("destination-over", DEST_OVER
)
3047 else CANVAS_OP_TO_THEBES_OP("lighter", ADD
)
3048 else CANVAS_OP_TO_THEBES_OP("source-atop", ATOP
)
3049 else CANVAS_OP_TO_THEBES_OP("source-in", IN
)
3050 else CANVAS_OP_TO_THEBES_OP("source-out", OUT
)
3051 else CANVAS_OP_TO_THEBES_OP("source-over", OVER
)
3052 else CANVAS_OP_TO_THEBES_OP("xor", XOR
)
3053 else return NS_ERROR_FAILURE
;
3055 #undef CANVAS_OP_TO_THEBES_OP
3065 nsCanvasRenderingContext2D::ConvertJSValToUint32(PRUint32
* aProp
, JSContext
* aContext
,
3069 if (::JS_ValueToECMAUint32(aContext
, aValue
, &temp
)) {
3070 *aProp
= (PRUint32
)temp
;
3073 ::JS_ReportError(aContext
, "Parameter must be an integer");
3081 nsCanvasRenderingContext2D::ConvertJSValToDouble(double* aProp
, JSContext
* aContext
,
3085 if (::JS_ValueToNumber(aContext
, aValue
, &temp
)) {
3086 *aProp
= (jsdouble
)temp
;
3089 ::JS_ReportError(aContext
, "Parameter must be a number");
3097 nsCanvasRenderingContext2D::ConvertJSValToXPCObject(nsISupports
** aSupports
, REFNSIID aIID
,
3098 JSContext
* aContext
, jsval aValue
)
3100 *aSupports
= nsnull
;
3101 if (JSVAL_IS_NULL(aValue
)) {
3105 if (JSVAL_IS_OBJECT(aValue
)) {
3106 // WrapJS does all the work to recycle an existing wrapper and/or do a QI
3107 nsresult rv
= nsContentUtils::XPConnect()->
3108 WrapJS(aContext
, JSVAL_TO_OBJECT(aValue
), aIID
, (void**)aSupports
);
3110 return NS_SUCCEEDED(rv
);
3116 /* thebes ARGB32 surfaces are ARGB stored as a packed 32-bit integer; on little-endian
3117 * platforms, they appear as BGRA bytes in the surface data. The color values are also
3118 * stored with premultiplied alpha.
3120 * If forceCopy is FALSE, a surface may be returned that's only valid during the current
3121 * operation. If it's TRUE, a copy will always be made that can safely be retained.
3125 nsCanvasRenderingContext2D::ThebesSurfaceFromElement(nsIDOMElement
*imgElt
,
3127 gfxASurface
**aSurface
,
3130 nsIPrincipal
**prinOut
,
3131 PRBool
*forceWriteOnlyOut
)
3135 nsCOMPtr
<nsINode
> node
= do_QueryInterface(imgElt
);
3137 /* If it's a Canvas, grab its internal surface as necessary */
3138 nsCOMPtr
<nsICanvasElement
> canvas
= do_QueryInterface(imgElt
);
3139 if (node
&& canvas
) {
3141 rv
= canvas
->GetSize(&w
, &h
);
3142 NS_ENSURE_SUCCESS(rv
, rv
);
3144 nsRefPtr
<gfxASurface
> sourceSurface
;
3146 if (!forceCopy
&& canvas
->CountContexts() == 1) {
3147 nsICanvasRenderingContextInternal
*srcCanvas
= canvas
->GetContextAtIndex(0);
3148 rv
= srcCanvas
->GetThebesSurface(getter_AddRefs(sourceSurface
));
3149 // force a copy if we couldn't get the surface, or if it's
3150 // the same as what we have
3151 if (sourceSurface
== mSurface
|| NS_FAILED(rv
))
3152 sourceSurface
= nsnull
;
3155 if (sourceSurface
== nsnull
) {
3156 nsRefPtr
<gfxASurface
> surf
=
3157 gfxPlatform::GetPlatform()->CreateOffscreenSurface
3158 (gfxIntSize(w
, h
), gfxASurface::ImageFormatARGB32
);
3159 nsRefPtr
<gfxContext
> ctx
= new gfxContext(surf
);
3160 rv
= canvas
->RenderContexts(ctx
);
3163 sourceSurface
= surf
;
3166 *aSurface
= sourceSurface
.forget().get();
3170 NS_ADDREF(*prinOut
= node
->NodePrincipal());
3171 *forceWriteOnlyOut
= canvas
->IsWriteOnly();
3177 /* Maybe it's <video>? */
3178 nsCOMPtr
<nsIDOMHTMLVideoElement
> ve
= do_QueryInterface(imgElt
);
3180 nsHTMLVideoElement
*video
= static_cast<nsHTMLVideoElement
*>(ve
.get());
3182 /* If it doesn't have a principal, just bail */
3183 nsCOMPtr
<nsIPrincipal
> principal
= video
->GetCurrentPrincipal();
3185 return NS_ERROR_DOM_SECURITY_ERR
;
3187 PRUint32 videoWidth
, videoHeight
;
3188 rv
= video
->GetVideoWidth(&videoWidth
);
3189 rv
|= video
->GetVideoHeight(&videoHeight
);
3191 return NS_ERROR_NOT_AVAILABLE
;
3193 nsRefPtr
<gfxASurface
> surf
=
3194 gfxPlatform::GetPlatform()->CreateOffscreenSurface
3195 (gfxIntSize(videoWidth
, videoHeight
), gfxASurface::ImageFormatARGB32
);
3196 nsRefPtr
<gfxContext
> ctx
= new gfxContext(surf
);
3198 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
3200 video
->Paint(ctx
, gfxRect(0, 0, videoWidth
, videoHeight
));
3202 *aSurface
= surf
.forget().get();
3203 *widthOut
= videoWidth
;
3204 *heightOut
= videoHeight
;
3206 *prinOut
= principal
.forget().get();
3207 *forceWriteOnlyOut
= PR_FALSE
;
3213 /* Finally, check if it's a normal image */
3214 nsCOMPtr
<imgIContainer
> imgContainer
;
3215 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(imgElt
);
3218 return NS_ERROR_NOT_AVAILABLE
;
3220 nsCOMPtr
<imgIRequest
> imgRequest
;
3221 rv
= imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
3222 getter_AddRefs(imgRequest
));
3223 NS_ENSURE_SUCCESS(rv
, rv
);
3225 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3226 return NS_ERROR_NOT_AVAILABLE
;
3229 imgRequest
->GetImageStatus(&status
);
3230 if ((status
& imgIRequest::STATUS_LOAD_COMPLETE
) == 0)
3231 return NS_ERROR_NOT_AVAILABLE
;
3233 // In case of data: URIs, we want to ignore principals;
3234 // they should have the originating content's principal,
3235 // but that's broken at the moment in imgLib.
3236 nsCOMPtr
<nsIURI
> uri
;
3237 rv
= imgRequest
->GetURI(getter_AddRefs(uri
));
3238 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SECURITY_ERR
);
3240 PRBool isDataURI
= PR_FALSE
;
3241 rv
= uri
->SchemeIs("data", &isDataURI
);
3242 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SECURITY_ERR
);
3244 // Data URIs are always OK; set the principal
3245 // to null to indicate that.
3249 rv
= imgRequest
->GetImagePrincipal(prinOut
);
3250 NS_ENSURE_SUCCESS(rv
, rv
);
3251 NS_ENSURE_TRUE(*prinOut
, NS_ERROR_DOM_SECURITY_ERR
);
3254 *forceWriteOnlyOut
= PR_FALSE
;
3256 rv
= imgRequest
->GetImage(getter_AddRefs(imgContainer
));
3257 NS_ENSURE_SUCCESS(rv
, rv
);
3260 return NS_ERROR_NOT_AVAILABLE
;
3262 nsCOMPtr
<gfxIImageFrame
> frame
;
3263 rv
= imgContainer
->GetCurrentFrame(getter_AddRefs(frame
));
3264 NS_ENSURE_SUCCESS(rv
, rv
);
3266 nsCOMPtr
<nsIImage
> img(do_GetInterface(frame
));
3268 PRInt32 imgWidth
, imgHeight
;
3269 rv
= frame
->GetWidth(&imgWidth
);
3270 rv
|= frame
->GetHeight(&imgHeight
);
3272 return NS_ERROR_FAILURE
;
3275 *widthOut
= imgWidth
;
3277 *heightOut
= imgHeight
;
3279 nsRefPtr
<gfxPattern
> gfxpattern
;
3280 img
->GetPattern(getter_AddRefs(gfxpattern
));
3281 nsRefPtr
<gfxASurface
> gfxsurf
= gfxpattern
->GetSurface();
3284 gfxsurf
= new gfxImageSurface (gfxIntSize(imgWidth
, imgHeight
), gfxASurface::ImageFormatARGB32
);
3285 nsRefPtr
<gfxContext
> ctx
= new gfxContext(gfxsurf
);
3287 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
3288 ctx
->SetPattern(gfxpattern
);
3292 *aSurface
= gfxsurf
.forget().get();
3297 /* Check that the rect [x,y,w,h] is a valid subrect of [0,0,realWidth,realHeight]
3298 * without overflowing any integers and the like.
3301 CheckSaneSubrectSize (PRInt32 x
, PRInt32 y
, PRInt32 w
, PRInt32 h
, PRInt32 realWidth
, PRInt32 realHeight
)
3303 if (w
<= 0 || h
<= 0 || x
< 0 || y
< 0)
3306 if (x
>= realWidth
|| w
> (realWidth
- x
) ||
3307 y
>= realHeight
|| h
> (realHeight
- y
))
3314 FlushLayoutForTree(nsIDOMWindow
* aWindow
)
3316 nsCOMPtr
<nsPIDOMWindow
> piWin
= do_QueryInterface(aWindow
);
3320 // Note that because FlushPendingNotifications flushes parents, this
3321 // is O(N^2) in docshell tree depth. However, the docshell tree is
3322 // usually pretty shallow.
3324 nsCOMPtr
<nsIDOMDocument
> domDoc
;
3325 aWindow
->GetDocument(getter_AddRefs(domDoc
));
3326 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(domDoc
);
3328 doc
->FlushPendingNotifications(Flush_Layout
);
3331 nsCOMPtr
<nsIDocShellTreeNode
> node
=
3332 do_QueryInterface(piWin
->GetDocShell());
3334 PRInt32 i
= 0, i_end
;
3335 node
->GetChildCount(&i_end
);
3336 for (; i
< i_end
; ++i
) {
3337 nsCOMPtr
<nsIDocShellTreeItem
> item
;
3338 node
->GetChildAt(i
, getter_AddRefs(item
));
3339 nsCOMPtr
<nsIDOMWindow
> win
= do_GetInterface(item
);
3341 FlushLayoutForTree(win
);
3348 nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow
* aWindow
, PRInt32 aX
, PRInt32 aY
,
3349 PRInt32 aW
, PRInt32 aH
,
3350 const nsAString
& aBGColor
,
3353 NS_ENSURE_ARG(aWindow
!= nsnull
);
3355 // protect against too-large surfaces that will cause allocation
3356 // or overflow issues
3357 if (!gfxASurface::CheckSurfaceSize(gfxIntSize(aW
, aH
), 0xffff))
3358 return NS_ERROR_FAILURE
;
3360 // We can't allow web apps to call this until we fix at least the
3361 // following potential security issues:
3362 // -- rendering cross-domain IFRAMEs and then extracting the results
3363 // -- rendering the user's theme and then extracting the results
3364 // -- rendering native anonymous content (e.g., file input paths;
3365 // scrollbars should be allowed)
3366 if (!nsContentUtils::IsCallerTrustedForRead()) {
3367 // not permitted to use DrawWindow
3368 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3369 return NS_ERROR_DOM_SECURITY_ERR
;
3372 // Flush layout updates
3373 if (!(flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH
))
3374 FlushLayoutForTree(aWindow
);
3376 nsCOMPtr
<nsPresContext
> presContext
;
3377 nsCOMPtr
<nsPIDOMWindow
> win
= do_QueryInterface(aWindow
);
3379 nsIDocShell
* docshell
= win
->GetDocShell();
3381 docshell
->GetPresContext(getter_AddRefs(presContext
));
3385 return NS_ERROR_FAILURE
;
3388 nsresult rv
= mCSSParser
->ParseColorString(PromiseFlatString(aBGColor
),
3389 nsnull
, 0, &bgColor
);
3390 NS_ENSURE_SUCCESS(rv
, rv
);
3392 nsIPresShell
* presShell
= presContext
->PresShell();
3393 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
3395 nsRect
r(nsPresContext::CSSPixelsToAppUnits(aX
),
3396 nsPresContext::CSSPixelsToAppUnits(aY
),
3397 nsPresContext::CSSPixelsToAppUnits(aW
),
3398 nsPresContext::CSSPixelsToAppUnits(aH
));
3399 presShell
->RenderDocument(r
, PR_FALSE
, PR_TRUE
, bgColor
,
3402 // get rid of the pattern surface ref, just in case
3403 mThebes
->SetColor(gfxRGBA(1,1,1,1));
3412 // device pixel getting/setting
3415 // ImageData getImageData (in float x, in float y, in float width, in float height);
3417 nsCanvasRenderingContext2D::GetImageData()
3419 if (!mValid
|| !mCanvasElement
)
3420 return NS_ERROR_FAILURE
;
3422 if (mCanvasElement
->IsWriteOnly() && !nsContentUtils::IsCallerTrustedForRead()) {
3423 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3424 return NS_ERROR_DOM_SECURITY_ERR
;
3427 nsAXPCNativeCallContext
*ncc
= nsnull
;
3428 nsresult rv
= nsContentUtils::XPConnect()->
3429 GetCurrentNativeCallContext(&ncc
);
3430 NS_ENSURE_SUCCESS(rv
, rv
);
3433 return NS_ERROR_FAILURE
;
3435 JSContext
*ctx
= nsnull
;
3437 rv
= ncc
->GetJSContext(&ctx
);
3438 NS_ENSURE_SUCCESS(rv
, rv
);
3441 jsval
*argv
= nsnull
;
3443 ncc
->GetArgc(&argc
);
3444 ncc
->GetArgvPtr(&argv
);
3446 JSAutoRequest
ar(ctx
);
3449 if (!JS_ConvertArguments (ctx
, argc
, argv
, "jjjj", &x
, &y
, &w
, &h
))
3450 return NS_ERROR_DOM_SYNTAX_ERR
;
3452 if (!CheckSaneSubrectSize (x
, y
, w
, h
, mWidth
, mHeight
))
3453 return NS_ERROR_DOM_SYNTAX_ERR
;
3455 nsAutoArrayPtr
<PRUint8
> surfaceData (new (std::nothrow
) PRUint8
[w
* h
* 4]);
3456 int surfaceDataStride
= w
*4;
3457 int surfaceDataOffset
= 0;
3460 return NS_ERROR_OUT_OF_MEMORY
;
3462 nsRefPtr
<gfxImageSurface
> tmpsurf
= new gfxImageSurface(surfaceData
,
3465 gfxASurface::ImageFormatARGB32
);
3466 if (!tmpsurf
|| tmpsurf
->CairoStatus())
3467 return NS_ERROR_FAILURE
;
3469 nsRefPtr
<gfxContext
> tmpctx
= new gfxContext(tmpsurf
);
3471 if (!tmpctx
|| tmpctx
->HasError())
3472 return NS_ERROR_FAILURE
;
3474 tmpctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
3475 tmpctx
->SetSource(mSurface
, gfxPoint(-(int)x
, -(int)y
));
3481 PRUint32 len
= w
* h
* 4;
3482 if (len
> (((PRUint32
)0xfff00000)/sizeof(jsval
)))
3483 return NS_ERROR_INVALID_ARG
;
3485 nsAutoArrayPtr
<jsval
> jsvector(new (std::nothrow
) jsval
[w
* h
* 4]);
3487 return NS_ERROR_OUT_OF_MEMORY
;
3488 jsval
*dest
= jsvector
.get();
3490 for (int j
= 0; j
< h
; j
++) {
3491 row
= surfaceData
+ surfaceDataOffset
+ (surfaceDataStride
* j
);
3492 for (int i
= 0; i
< w
; i
++) {
3493 // XXX Is there some useful swizzle MMX we can use here?
3494 // I guess we have to INT_TO_JSVAL still
3495 #ifdef IS_LITTLE_ENDIAN
3506 // Convert to non-premultiplied color
3513 *dest
++ = INT_TO_JSVAL(r
);
3514 *dest
++ = INT_TO_JSVAL(g
);
3515 *dest
++ = INT_TO_JSVAL(b
);
3516 *dest
++ = INT_TO_JSVAL(a
);
3520 JSObject
*dataArray
= JS_NewArrayObject(ctx
, w
*h
*4, jsvector
.get());
3522 return NS_ERROR_OUT_OF_MEMORY
;
3524 nsAutoGCRoot
arrayGCRoot(&dataArray
, &rv
);
3525 NS_ENSURE_SUCCESS(rv
, rv
);
3527 JSObject
*result
= JS_NewObject(ctx
, NULL
, NULL
, NULL
);
3529 return NS_ERROR_OUT_OF_MEMORY
;
3531 nsAutoGCRoot
resultGCRoot(&result
, &rv
);
3532 NS_ENSURE_SUCCESS(rv
, rv
);
3534 if (!JS_DefineProperty(ctx
, result
, "width", INT_TO_JSVAL(w
), NULL
, NULL
, 0) ||
3535 !JS_DefineProperty(ctx
, result
, "height", INT_TO_JSVAL(h
), NULL
, NULL
, 0) ||
3536 !JS_DefineProperty(ctx
, result
, "data", OBJECT_TO_JSVAL(dataArray
), NULL
, NULL
, 0))
3537 return NS_ERROR_FAILURE
;
3540 ncc
->GetRetValPtr(&retvalPtr
);
3541 *retvalPtr
= OBJECT_TO_JSVAL(result
);
3542 ncc
->SetReturnValueWasSet(PR_TRUE
);
3548 #include "jstypes.h"
3549 JS_FRIEND_API(JSBool
)
3550 js_ArrayToJSUint8Buffer(JSContext
*cx
, JSObject
*obj
, jsuint offset
, jsuint count
,
3554 // void putImageData (in ImageData d, in float x, in float y);
3556 nsCanvasRenderingContext2D::PutImageData()
3561 return NS_ERROR_FAILURE
;
3563 nsAXPCNativeCallContext
*ncc
= nsnull
;
3564 rv
= nsContentUtils::XPConnect()->
3565 GetCurrentNativeCallContext(&ncc
);
3566 NS_ENSURE_SUCCESS(rv
, rv
);
3569 return NS_ERROR_FAILURE
;
3571 JSContext
*ctx
= nsnull
;
3573 rv
= ncc
->GetJSContext(&ctx
);
3574 NS_ENSURE_SUCCESS(rv
, rv
);
3577 jsval
*argv
= nsnull
;
3579 ncc
->GetArgc(&argc
);
3580 ncc
->GetArgvPtr(&argv
);
3582 JSAutoRequest
ar(ctx
);
3584 JSObject
*dataObject
;
3587 if (!JS_ConvertArguments (ctx
, argc
, argv
, "ojj", &dataObject
, &x
, &y
))
3588 return NS_ERROR_DOM_SYNTAX_ERR
;
3591 return NS_ERROR_DOM_SYNTAX_ERR
;
3594 JSObject
*dataArray
;
3597 if (!JS_GetProperty(ctx
, dataObject
, "width", &v
) ||
3598 !JS_ValueToInt32(ctx
, v
, &w
))
3599 return NS_ERROR_DOM_SYNTAX_ERR
;
3601 if (!JS_GetProperty(ctx
, dataObject
, "height", &v
) ||
3602 !JS_ValueToInt32(ctx
, v
, &h
))
3603 return NS_ERROR_DOM_SYNTAX_ERR
;
3605 if (!JS_GetProperty(ctx
, dataObject
, "data", &v
) ||
3606 !JSVAL_IS_OBJECT(v
))
3607 return NS_ERROR_DOM_SYNTAX_ERR
;
3608 dataArray
= JSVAL_TO_OBJECT(v
);
3610 if (!CheckSaneSubrectSize (x
, y
, w
, h
, mWidth
, mHeight
))
3611 return NS_ERROR_DOM_SYNTAX_ERR
;
3614 if (!JS_IsArrayObject(ctx
, dataArray
) ||
3615 !JS_GetArrayLength(ctx
, dataArray
, &arrayLen
) ||
3616 arrayLen
< (jsuint
)(w
* h
* 4))
3617 return NS_ERROR_DOM_SYNTAX_ERR
;
3619 nsAutoArrayPtr
<PRUint8
> imageBuffer(new (std::nothrow
) PRUint8
[w
* h
* 4]);
3621 return NS_ERROR_OUT_OF_MEMORY
;
3623 PRUint8
*imgPtr
= imageBuffer
.get();
3625 JSBool ok
= js_ArrayToJSUint8Buffer(ctx
, dataArray
, 0, w
*h
*4, imageBuffer
);
3627 // no fast path? go slow.
3629 jsval vr
, vg
, vb
, va
;
3630 PRUint8 ir
, ig
, ib
, ia
;
3631 for (int32 j
= 0; j
< h
; j
++) {
3632 for (int32 i
= 0; i
< w
; i
++) {
3633 if (!JS_GetElement(ctx
, dataArray
, (j
*w
*4) + i
*4 + 0, &vr
) ||
3634 !JS_GetElement(ctx
, dataArray
, (j
*w
*4) + i
*4 + 1, &vg
) ||
3635 !JS_GetElement(ctx
, dataArray
, (j
*w
*4) + i
*4 + 2, &vb
) ||
3636 !JS_GetElement(ctx
, dataArray
, (j
*w
*4) + i
*4 + 3, &va
))
3637 return NS_ERROR_DOM_SYNTAX_ERR
;
3639 if (JSVAL_IS_INT(vr
)) ir
= (PRUint8
) JSVAL_TO_INT(vr
);
3640 else if (JSVAL_IS_DOUBLE(vr
)) ir
= (PRUint8
) (*JSVAL_TO_DOUBLE(vr
));
3641 else return NS_ERROR_DOM_SYNTAX_ERR
;
3643 if (JSVAL_IS_INT(vg
)) ig
= (PRUint8
) JSVAL_TO_INT(vg
);
3644 else if (JSVAL_IS_DOUBLE(vg
)) ig
= (PRUint8
) (*JSVAL_TO_DOUBLE(vg
));
3645 else return NS_ERROR_DOM_SYNTAX_ERR
;
3647 if (JSVAL_IS_INT(vb
)) ib
= (PRUint8
) JSVAL_TO_INT(vb
);
3648 else if (JSVAL_IS_DOUBLE(vb
)) ib
= (PRUint8
) (*JSVAL_TO_DOUBLE(vb
));
3649 else return NS_ERROR_DOM_SYNTAX_ERR
;
3651 if (JSVAL_IS_INT(va
)) ia
= (PRUint8
) JSVAL_TO_INT(va
);
3652 else if (JSVAL_IS_DOUBLE(va
)) ia
= (PRUint8
) (*JSVAL_TO_DOUBLE(va
));
3653 else return NS_ERROR_DOM_SYNTAX_ERR
;
3655 // Convert to premultiplied color (losslessly if the input came from getImageData)
3656 ir
= (ir
*ia
+ 254) / 255;
3657 ig
= (ig
*ia
+ 254) / 255;
3658 ib
= (ib
*ia
+ 254) / 255;
3660 #ifdef IS_LITTLE_ENDIAN
3674 /* Walk through and premultiply and swap rgba */
3676 PRUint8 ir
, ig
, ib
, ia
;
3677 PRUint8
*ptr
= imgPtr
;
3678 for (int32 i
= 0; i
< w
*h
; i
++) {
3679 #ifdef IS_LITTLE_ENDIAN
3684 ptr
[0] = (ib
*ia
+ 254) / 255;
3685 ptr
[1] = (ig
*ia
+ 254) / 255;
3686 ptr
[2] = (ir
*ia
+ 254) / 255;
3688 ptr
[0] = (ptr
[0]*ptr
[3] + 254) / 255;
3689 ptr
[1] = (ptr
[1]*ptr
[3] + 254) / 255;
3690 ptr
[2] = (ptr
[2]*ptr
[3] + 254) / 255;
3696 nsRefPtr
<gfxImageSurface
> imgsurf
= new gfxImageSurface(imageBuffer
.get(),
3699 gfxASurface::ImageFormatARGB32
);
3700 if (!imgsurf
|| imgsurf
->CairoStatus())
3701 return NS_ERROR_FAILURE
;
3703 gfxContextPathAutoSaveRestore
pathSR(mThebes
);
3704 gfxContextAutoSaveRestore
autoSR(mThebes
);
3706 mThebes
->IdentityMatrix();
3707 mThebes
->Translate(gfxPoint(x
, y
));
3709 mThebes
->Rectangle(gfxRect(0, 0, w
, h
));
3710 mThebes
->SetSource(imgsurf
, gfxPoint(0, 0));
3711 mThebes
->SetOperator(gfxContext::OPERATOR_SOURCE
);
3718 nsCanvasRenderingContext2D::GetThebesSurface(gfxASurface
**surface
)
3722 return NS_ERROR_NOT_AVAILABLE
;
3725 *surface
= mSurface
.get();
3726 NS_ADDREF(*surface
);
3732 nsCanvasRenderingContext2D::CreateImageData()
3734 if (!mValid
|| !mCanvasElement
)
3735 return NS_ERROR_FAILURE
;
3737 nsAXPCNativeCallContext
*ncc
= nsnull
;
3738 nsresult rv
= nsContentUtils::XPConnect()->
3739 GetCurrentNativeCallContext(&ncc
);
3740 NS_ENSURE_SUCCESS(rv
, rv
);
3743 return NS_ERROR_FAILURE
;
3745 JSContext
*ctx
= nsnull
;
3747 rv
= ncc
->GetJSContext(&ctx
);
3748 NS_ENSURE_SUCCESS(rv
, rv
);
3751 jsval
*argv
= nsnull
;
3753 ncc
->GetArgc(&argc
);
3754 ncc
->GetArgvPtr(&argv
);
3756 JSAutoRequest
ar(ctx
);
3759 if (!JS_ConvertArguments (ctx
, argc
, argv
, "jj", &w
, &h
))
3760 return NS_ERROR_DOM_SYNTAX_ERR
;
3762 if (w
<= 0 || h
<= 0)
3763 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
3765 // check for overflow when calculating len
3766 PRUint32 len0
= w
* h
;
3768 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
3769 PRUint32 len
= len0
* 4;
3770 if (len
/ 4 != len0
)
3771 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
3773 nsAutoArrayPtr
<jsval
> jsvector(new (std::nothrow
) jsval
[w
* h
* 4]);
3775 return NS_ERROR_OUT_OF_MEMORY
;
3777 jsval
*dest
= jsvector
.get();
3778 for (PRUint32 i
= 0; i
< len
; i
++)
3779 *dest
++ = JSVAL_ZERO
;
3781 JSObject
*dataArray
= JS_NewArrayObject(ctx
, w
*h
*4, jsvector
.get());
3783 return NS_ERROR_OUT_OF_MEMORY
;
3785 nsAutoGCRoot
arrayGCRoot(&dataArray
, &rv
);
3786 NS_ENSURE_SUCCESS(rv
, rv
);
3788 JSObject
*result
= JS_NewObject(ctx
, NULL
, NULL
, NULL
);
3790 return NS_ERROR_OUT_OF_MEMORY
;
3792 nsAutoGCRoot
resultGCRoot(&result
, &rv
);
3793 NS_ENSURE_SUCCESS(rv
, rv
);
3795 if (!JS_DefineProperty(ctx
, result
, "width", INT_TO_JSVAL(w
), NULL
, NULL
, 0) ||
3796 !JS_DefineProperty(ctx
, result
, "height", INT_TO_JSVAL(h
), NULL
, NULL
, 0) ||
3797 !JS_DefineProperty(ctx
, result
, "data", OBJECT_TO_JSVAL(dataArray
), NULL
, NULL
, 0))
3798 return NS_ERROR_FAILURE
;
3801 ncc
->GetRetValPtr(&retvalPtr
);
3802 *retvalPtr
= OBJECT_TO_JSVAL(result
);
3803 ncc
->SetReturnValueWasSet(PR_TRUE
);