1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:set ts=4 sw=4 et tw=78: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Vladimir Vukicevic <vladimir@pobox.com>
20 : * Portions created by the Initial Developer are Copyright (C) 2005
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Rob Arnold <tellrob@gmail.com>
25 : * Eric Butler <zantifon@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "base/basictypes.h"
42 :
43 : #include "nsIDOMXULElement.h"
44 :
45 : #include "prmem.h"
46 : #include "prenv.h"
47 :
48 : #include "nsIServiceManager.h"
49 : #include "nsMathUtils.h"
50 :
51 : #include "nsContentUtils.h"
52 :
53 : #include "nsIDOMDocument.h"
54 : #include "nsIDocument.h"
55 : #include "nsIDOMCanvasRenderingContext2D.h"
56 : #include "nsICanvasRenderingContextInternal.h"
57 : #include "nsHTMLCanvasElement.h"
58 : #include "nsSVGEffects.h"
59 : #include "nsPresContext.h"
60 : #include "nsIPresShell.h"
61 : #include "nsIVariant.h"
62 :
63 : #include "nsIInterfaceRequestorUtils.h"
64 : #include "nsIFrame.h"
65 : #include "nsDOMError.h"
66 : #include "nsIScriptError.h"
67 :
68 : #include "nsCSSParser.h"
69 : #include "mozilla/css/StyleRule.h"
70 : #include "mozilla/css/Declaration.h"
71 : #include "nsComputedDOMStyle.h"
72 : #include "nsStyleSet.h"
73 :
74 : #include "nsPrintfCString.h"
75 :
76 : #include "nsReadableUtils.h"
77 :
78 : #include "nsColor.h"
79 : #include "nsGfxCIID.h"
80 : #include "nsIScriptSecurityManager.h"
81 : #include "nsIDocShell.h"
82 : #include "nsIDOMWindow.h"
83 : #include "nsPIDOMWindow.h"
84 : #include "nsIDocShell.h"
85 : #include "nsIDocShellTreeItem.h"
86 : #include "nsIDocShellTreeNode.h"
87 : #include "nsIXPConnect.h"
88 : #include "jsapi.h"
89 : #include "nsDisplayList.h"
90 :
91 : #include "nsTArray.h"
92 :
93 : #include "imgIEncoder.h"
94 :
95 : #include "gfxContext.h"
96 : #include "gfxASurface.h"
97 : #include "gfxImageSurface.h"
98 : #include "gfxPlatform.h"
99 : #include "gfxFont.h"
100 : #include "gfxBlur.h"
101 : #include "gfxUtils.h"
102 :
103 : #include "nsFrameManager.h"
104 : #include "nsFrameLoader.h"
105 : #include "nsBidi.h"
106 : #include "nsBidiPresUtils.h"
107 : #include "Layers.h"
108 : #include "CanvasUtils.h"
109 : #include "nsIMemoryReporter.h"
110 : #include "nsStyleUtil.h"
111 : #include "CanvasImageCache.h"
112 :
113 : #include <algorithm>
114 : #include "mozilla/dom/ContentParent.h"
115 : #include "mozilla/ipc/PDocumentRendererParent.h"
116 : #include "mozilla/dom/PBrowserParent.h"
117 : #include "mozilla/ipc/DocumentRendererParent.h"
118 :
119 : // windows.h (included by chromium code) defines this, in its infinite wisdom
120 : #undef DrawText
121 :
122 : using namespace mozilla;
123 : using namespace mozilla::CanvasUtils;
124 : using namespace mozilla::dom;
125 : using namespace mozilla::ipc;
126 : using namespace mozilla::layers;
127 :
128 : static float kDefaultFontSize = 10.0;
129 1464 : static NS_NAMED_LITERAL_STRING(kDefaultFontName, "sans-serif");
130 1464 : static NS_NAMED_LITERAL_STRING(kDefaultFontStyle, "10px sans-serif");
131 :
132 : /* Memory reporter stuff */
133 : static nsIMemoryReporter *gCanvasMemoryReporter = nsnull;
134 : static PRInt64 gCanvasMemoryUsed = 0;
135 :
136 0 : static PRInt64 GetCanvasMemoryUsed() {
137 0 : return gCanvasMemoryUsed;
138 : }
139 :
140 : // This is KIND_OTHER because it's not always clear where in memory the pixels of
141 : // a canvas are stored. Furthermore, this memory will be tracked by the
142 : // underlying surface implementations. See bug 655638 for details.
143 0 : NS_MEMORY_REPORTER_IMPLEMENT(CanvasMemory,
144 : "canvas-2d-pixel-bytes",
145 : KIND_OTHER,
146 : UNITS_BYTES,
147 : GetCanvasMemoryUsed,
148 : "Memory used by 2D canvases. Each canvas requires (width * height * 4) "
149 0 : "bytes.")
150 :
151 : static void
152 0 : CopyContext(gfxContext* dest, gfxContext* src)
153 : {
154 0 : dest->Multiply(src->CurrentMatrix());
155 :
156 0 : nsRefPtr<gfxPath> path = src->CopyPath();
157 0 : dest->NewPath();
158 0 : dest->AppendPath(path);
159 :
160 0 : nsRefPtr<gfxPattern> pattern = src->GetPattern();
161 0 : dest->SetPattern(pattern);
162 :
163 0 : dest->SetLineWidth(src->CurrentLineWidth());
164 0 : dest->SetLineCap(src->CurrentLineCap());
165 0 : dest->SetLineJoin(src->CurrentLineJoin());
166 0 : dest->SetMiterLimit(src->CurrentMiterLimit());
167 0 : dest->SetFillRule(src->CurrentFillRule());
168 :
169 0 : dest->SetAntialiasMode(src->CurrentAntialiasMode());
170 :
171 0 : AutoFallibleTArray<gfxFloat, 10> dashes;
172 : double dashOffset;
173 0 : if (src->CurrentDash(dashes, &dashOffset)) {
174 0 : dest->SetDash(dashes.Elements(), dashes.Length(), dashOffset);
175 : }
176 0 : }
177 :
178 : /**
179 : ** nsCanvasGradient
180 : **/
181 : #define NS_CANVASGRADIENT_PRIVATE_IID \
182 : { 0x491d39d8, 0x4058, 0x42bd, { 0xac, 0x76, 0x70, 0xd5, 0x62, 0x7f, 0x02, 0x10 } }
183 : class nsCanvasGradient MOZ_FINAL : public nsIDOMCanvasGradient
184 0 : {
185 : public:
186 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID)
187 :
188 0 : nsCanvasGradient(gfxPattern* pat)
189 0 : : mPattern(pat)
190 : {
191 0 : }
192 :
193 0 : gfxPattern* GetPattern() {
194 0 : return mPattern;
195 : }
196 :
197 : /* nsIDOMCanvasGradient */
198 0 : NS_IMETHOD AddColorStop (float offset,
199 : const nsAString& colorstr)
200 : {
201 0 : if (!FloatValidate(offset) || offset < 0.0 || offset > 1.0)
202 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
203 :
204 : nscolor color;
205 0 : nsCSSParser parser;
206 0 : nsresult rv = parser.ParseColorString(nsString(colorstr),
207 0 : nsnull, 0, &color);
208 0 : if (NS_FAILED(rv))
209 0 : return NS_ERROR_DOM_SYNTAX_ERR;
210 :
211 0 : mPattern->AddColorStop(offset, gfxRGBA(color));
212 :
213 0 : return NS_OK;
214 : }
215 :
216 : NS_DECL_ISUPPORTS
217 :
218 : protected:
219 : nsRefPtr<gfxPattern> mPattern;
220 : };
221 :
222 : NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasGradient, NS_CANVASGRADIENT_PRIVATE_IID)
223 :
224 0 : NS_IMPL_ADDREF(nsCanvasGradient)
225 0 : NS_IMPL_RELEASE(nsCanvasGradient)
226 :
227 : DOMCI_DATA(CanvasGradient, nsCanvasGradient)
228 :
229 0 : NS_INTERFACE_MAP_BEGIN(nsCanvasGradient)
230 0 : NS_INTERFACE_MAP_ENTRY(nsCanvasGradient)
231 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient)
232 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasGradient)
233 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
234 0 : NS_INTERFACE_MAP_END
235 :
236 : /**
237 : ** nsCanvasPattern
238 : **/
239 : #define NS_CANVASPATTERN_PRIVATE_IID \
240 : { 0xb85c6c8a, 0x0624, 0x4530, { 0xb8, 0xee, 0xff, 0xdf, 0x42, 0xe8, 0x21, 0x6d } }
241 : class nsCanvasPattern MOZ_FINAL : public nsIDOMCanvasPattern
242 0 : {
243 : public:
244 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID)
245 :
246 0 : nsCanvasPattern(gfxPattern* pat,
247 : nsIPrincipal* principalForSecurityCheck,
248 : bool forceWriteOnly,
249 : bool CORSUsed)
250 : : mPattern(pat),
251 : mPrincipal(principalForSecurityCheck),
252 : mForceWriteOnly(forceWriteOnly),
253 0 : mCORSUsed(CORSUsed)
254 : {
255 0 : }
256 :
257 0 : gfxPattern* GetPattern() const {
258 0 : return mPattern;
259 : }
260 :
261 0 : nsIPrincipal* Principal() const { return mPrincipal; }
262 0 : bool GetForceWriteOnly() const { return mForceWriteOnly; }
263 0 : bool GetCORSUsed() const { return mCORSUsed; }
264 :
265 : NS_DECL_ISUPPORTS
266 :
267 : protected:
268 : nsRefPtr<gfxPattern> mPattern;
269 : nsCOMPtr<nsIPrincipal> mPrincipal;
270 : const bool mForceWriteOnly;
271 : const bool mCORSUsed;
272 : };
273 :
274 : NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPattern, NS_CANVASPATTERN_PRIVATE_IID)
275 :
276 0 : NS_IMPL_ADDREF(nsCanvasPattern)
277 0 : NS_IMPL_RELEASE(nsCanvasPattern)
278 :
279 : DOMCI_DATA(CanvasPattern, nsCanvasPattern)
280 :
281 0 : NS_INTERFACE_MAP_BEGIN(nsCanvasPattern)
282 0 : NS_INTERFACE_MAP_ENTRY(nsCanvasPattern)
283 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)
284 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasPattern)
285 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
286 0 : NS_INTERFACE_MAP_END
287 :
288 : /**
289 : ** nsTextMetrics
290 : **/
291 : #define NS_TEXTMETRICS_PRIVATE_IID \
292 : { 0xc5b1c2f9, 0xcb4f, 0x4394, { 0xaf, 0xe0, 0xc6, 0x59, 0x33, 0x80, 0x8b, 0xf3 } }
293 : class nsTextMetrics : public nsIDOMTextMetrics
294 : {
295 : public:
296 0 : nsTextMetrics(float w) : width(w) { }
297 :
298 0 : virtual ~nsTextMetrics() { }
299 :
300 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICS_PRIVATE_IID)
301 :
302 0 : NS_IMETHOD GetWidth(float* w) {
303 0 : *w = width;
304 0 : return NS_OK;
305 : }
306 :
307 : NS_DECL_ISUPPORTS
308 :
309 : private:
310 : float width;
311 : };
312 :
313 : NS_DEFINE_STATIC_IID_ACCESSOR(nsTextMetrics, NS_TEXTMETRICS_PRIVATE_IID)
314 :
315 0 : NS_IMPL_ADDREF(nsTextMetrics)
316 0 : NS_IMPL_RELEASE(nsTextMetrics)
317 :
318 : DOMCI_DATA(TextMetrics, nsTextMetrics)
319 :
320 0 : NS_INTERFACE_MAP_BEGIN(nsTextMetrics)
321 0 : NS_INTERFACE_MAP_ENTRY(nsTextMetrics)
322 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)
323 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TextMetrics)
324 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
325 0 : NS_INTERFACE_MAP_END
326 :
327 : struct nsCanvasBidiProcessor;
328 :
329 : /**
330 : ** nsCanvasRenderingContext2D
331 : **/
332 : class nsCanvasRenderingContext2D :
333 : public nsIDOMCanvasRenderingContext2D,
334 : public nsICanvasRenderingContextInternal
335 : {
336 : public:
337 : nsCanvasRenderingContext2D();
338 : virtual ~nsCanvasRenderingContext2D();
339 :
340 : nsresult Redraw();
341 :
342 : // nsICanvasRenderingContextInternal
343 : NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas);
344 : NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
345 : void Initialize(nsIDocShell *shell, PRInt32 width, PRInt32 height);
346 : NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, PRInt32 width, PRInt32 height);
347 : bool EnsureSurface();
348 : NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter);
349 : NS_IMETHOD GetInputStream(const char* aMimeType,
350 : const PRUnichar* aEncoderOptions,
351 : nsIInputStream **aStream);
352 : NS_IMETHOD GetThebesSurface(gfxASurface **surface);
353 0 : mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot()
354 0 : { return nsnull; }
355 :
356 : NS_IMETHOD SetIsOpaque(bool isOpaque);
357 : NS_IMETHOD Reset();
358 : virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
359 : CanvasLayer *aOldLayer,
360 : LayerManager *aManager);
361 : virtual bool ShouldForceInactiveLayer(LayerManager *aManager);
362 : virtual void MarkContextClean();
363 : NS_IMETHOD SetIsIPC(bool isIPC);
364 : // this rect is in canvas device space
365 : NS_IMETHOD Redraw(const gfxRect &r);
366 : // this rect is in mThebes's current user space
367 : NS_IMETHOD RedrawUser(const gfxRect &r);
368 :
369 : // nsISupports interface + CC
370 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
371 :
372 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCanvasRenderingContext2D, nsIDOMCanvasRenderingContext2D)
373 :
374 : // nsIDOMCanvasRenderingContext2D interface
375 : NS_DECL_NSIDOMCANVASRENDERINGCONTEXT2D
376 :
377 : enum Style {
378 : STYLE_STROKE = 0,
379 : STYLE_FILL,
380 : STYLE_SHADOW,
381 : STYLE_MAX
382 : };
383 :
384 : class PathAutoSaveRestore
385 : {
386 : public:
387 0 : PathAutoSaveRestore(nsCanvasRenderingContext2D* aCtx) :
388 0 : mContext(aCtx->mThebes)
389 : {
390 0 : if (aCtx->mHasPath) {
391 0 : mPath = mContext->CopyPath();
392 : }
393 0 : }
394 0 : ~PathAutoSaveRestore()
395 0 : {
396 0 : mContext->NewPath();
397 0 : if (mPath) {
398 0 : mContext->AppendPath(mPath);
399 : }
400 0 : }
401 : private:
402 : gfxContext *mContext;
403 : nsRefPtr<gfxPath> mPath;
404 : };
405 : friend class PathAutoSaveRestore;
406 :
407 : protected:
408 : /**
409 : * The number of living nsCanvasRenderingContexts. When this goes down to
410 : * 0, we free the premultiply and unpremultiply tables, if they exist.
411 : */
412 : static PRUint32 sNumLivingContexts;
413 :
414 : /**
415 : * Lookup table used to speed up GetImageData().
416 : */
417 : static PRUint8 (*sUnpremultiplyTable)[256];
418 :
419 : /**
420 : * Lookup table used to speed up PutImageData().
421 : */
422 : static PRUint8 (*sPremultiplyTable)[256];
423 :
424 : // Some helpers. Doesn't modify acolor on failure.
425 : nsresult SetStyleFromStringOrInterface(const nsAString& aStr, nsISupports *aInterface, Style aWhichStyle);
426 : nsresult GetStyleAsStringOrInterface(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType, Style aWhichStyle);
427 :
428 : void StyleColorToString(const nscolor& aColor, nsAString& aStr);
429 :
430 : void DirtyAllStyles();
431 : /**
432 : * applies the given style as the current source. If the given style is
433 : * a solid color, aUseGlobalAlpha indicates whether to multiply the alpha
434 : * by global alpha, and is ignored otherwise.
435 : */
436 : void ApplyStyle(Style aWhichStyle, bool aUseGlobalAlpha = true);
437 :
438 : /**
439 : * Creates the unpremultiply lookup table, if it doesn't exist.
440 : */
441 : void EnsureUnpremultiplyTable();
442 :
443 : /**
444 : * Creates the premultiply lookup table, if it doesn't exist.
445 : */
446 : void EnsurePremultiplyTable();
447 :
448 : /**
449 : * Returns the image format this canvas should be allocated using. Takes
450 : * into account mOpaque, platform requirements, etc.
451 : */
452 : gfxASurface::gfxImageFormat GetImageFormat() const;
453 :
454 : // Member vars
455 : PRInt32 mWidth, mHeight;
456 : bool mValid;
457 : bool mZero;
458 : bool mOpaque;
459 : bool mResetLayer;
460 : bool mIPC;
461 :
462 : // the canvas element we're a context of
463 : nsCOMPtr<nsIDOMHTMLCanvasElement> mCanvasElement;
464 0 : nsHTMLCanvasElement *HTMLCanvasElement() {
465 0 : return static_cast<nsHTMLCanvasElement*>(mCanvasElement.get());
466 : }
467 :
468 : // Initialize the Thebes rendering context
469 : void CreateThebes();
470 :
471 : // If mCanvasElement is not provided, then a docshell is
472 : nsCOMPtr<nsIDocShell> mDocShell;
473 :
474 : // our drawing surfaces, contexts, and layers
475 : nsRefPtr<gfxContext> mThebes;
476 : nsRefPtr<gfxASurface> mSurface;
477 : bool mSurfaceCreated;
478 :
479 : PRUint32 mSaveCount;
480 :
481 : /**
482 : * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
483 : * Redraw is called, reset to false when Render is called.
484 : */
485 : bool mIsEntireFrameInvalid;
486 : /**
487 : * When this is set, the first call to Redraw(gfxRect) should set
488 : * mIsEntireFrameInvalid since we expect it will be followed by
489 : * many more Redraw calls.
490 : */
491 : bool mPredictManyRedrawCalls;
492 : /**
493 : * This is set whenever there's a nonempty path set by the API user.
494 : */
495 : bool mHasPath;
496 :
497 : /**
498 : * Number of times we've invalidated before calling redraw
499 : */
500 : PRUint32 mInvalidateCount;
501 : static const PRUint32 kCanvasMaxInvalidateCount = 100;
502 :
503 : /**
504 : * Returns true iff the the given operator should affect areas of the
505 : * destination where the source is transparent. Among other things, this
506 : * implies that a fully transparent source would still affect the canvas.
507 : */
508 0 : bool OperatorAffectsUncoveredAreas(gfxContext::GraphicsOperator op) const
509 : {
510 : return op == gfxContext::OPERATOR_IN ||
511 : op == gfxContext::OPERATOR_OUT ||
512 : op == gfxContext::OPERATOR_DEST_IN ||
513 0 : op == gfxContext::OPERATOR_DEST_ATOP;
514 : }
515 :
516 : /**
517 : * Returns true iff a shadow should be drawn along with a
518 : * drawing operation.
519 : */
520 0 : bool NeedToDrawShadow()
521 : {
522 0 : ContextState& state = CurrentState();
523 :
524 : // The spec says we should not draw shadows when the alpha value is 0,
525 : // regardless of the operator being used.
526 0 : return state.StyleIsColor(STYLE_SHADOW) &&
527 0 : NS_GET_A(state.colorStyles[STYLE_SHADOW]) > 0 &&
528 0 : (state.shadowOffset != gfxPoint(0, 0) || state.shadowBlur != 0);
529 : }
530 :
531 : /**
532 : * Checks the current state to determine if an intermediate surface would
533 : * be necessary to complete a drawing operation. Does not check the
534 : * condition pertaining to global alpha and patterns since that does not
535 : * pertain to all drawing operations.
536 : */
537 0 : bool NeedToUseIntermediateSurface()
538 : {
539 0 : if (!mThebes) {
540 : // Haven't created a surface yet, default is OVER.
541 0 : return OperatorAffectsUncoveredAreas(gfxContext::OPERATOR_OVER);
542 : }
543 :
544 : // certain operators always need an intermediate surface, except
545 : // with quartz since quartz does compositing differently than cairo
546 0 : return OperatorAffectsUncoveredAreas(mThebes->CurrentOperator());
547 :
548 : // XXX there are other unhandled cases but they should be investigated
549 : // first to ensure we aren't using an intermediate surface unecessarily
550 : }
551 :
552 : /**
553 : * If the current operator is "source" then clear the destination before we
554 : * draw into it, to simulate the effect of an unbounded source operator.
555 : */
556 0 : void ClearSurfaceForUnboundedSource()
557 : {
558 0 : if (!mThebes) {
559 : // Haven't created a surface yet, default is OVER.
560 0 : return;
561 : }
562 :
563 0 : gfxContext::GraphicsOperator current = mThebes->CurrentOperator();
564 0 : if (current != gfxContext::OPERATOR_SOURCE)
565 0 : return;
566 0 : mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);
567 : // It doesn't really matter what the source is here, since Paint
568 : // isn't bounded by the source and the mask covers the entire clip
569 : // region.
570 0 : mThebes->Paint();
571 0 : mThebes->SetOperator(current);
572 : }
573 :
574 : /**
575 : * Returns true iff the current source is such that global alpha would not
576 : * be handled correctly without the use of an intermediate surface.
577 : */
578 0 : bool NeedIntermediateSurfaceToHandleGlobalAlpha(Style aWhichStyle)
579 : {
580 0 : return CurrentState().globalAlpha != 1.0 && !CurrentState().StyleIsColor(aWhichStyle);
581 : }
582 :
583 : /**
584 : * Initializes the drawing of a shadow onto the canvas. The returned context
585 : * should have the shadow shape drawn onto it, and then ShadowFinalize
586 : * should be called. The return value is null if an error occurs.
587 : * @param extents The extents of the shadow object, in device space.
588 : * @param blur A newly contructed gfxAlphaBoxBlur, made with the default
589 : * constructor and left uninitialized.
590 : * @remark The lifetime of the return value is tied to the lifetime of
591 : * the gfxAlphaBoxBlur, so it does not need to be ref counted.
592 : */
593 : gfxContext* ShadowInitialize(const gfxRect& extents, gfxAlphaBoxBlur& blur);
594 :
595 : /**
596 : * Completes a shadow drawing operation.
597 : * @param blur The gfxAlphaBoxBlur that was passed to ShadowInitialize.
598 : */
599 : void ShadowFinalize(gfxAlphaBoxBlur& blur);
600 :
601 : /**
602 : * Draws the current path in the given style. Takes care of
603 : * any shadow drawing and will use intermediate surfaces as needed.
604 : *
605 : * If dirtyRect is given, it will contain the user-space dirty
606 : * rectangle of the draw operation.
607 : */
608 : nsresult DrawPath(Style style, gfxRect *dirtyRect = nsnull);
609 :
610 : /**
611 : * Draws a rectangle in the given style; used by FillRect and StrokeRect.
612 : */
613 : nsresult DrawRect(const gfxRect& rect, Style style);
614 :
615 : /**
616 : * Gets the pres shell from either the canvas element or the doc shell
617 : */
618 0 : nsIPresShell *GetPresShell() {
619 0 : nsCOMPtr<nsIContent> content = do_QueryObject(mCanvasElement);
620 0 : if (content) {
621 0 : return content->OwnerDoc()->GetShell();
622 : }
623 0 : if (mDocShell) {
624 0 : nsCOMPtr<nsIPresShell> shell;
625 0 : mDocShell->GetPresShell(getter_AddRefs(shell));
626 0 : return shell.get();
627 : }
628 0 : return nsnull;
629 : }
630 :
631 : // text
632 : enum TextAlign {
633 : TEXT_ALIGN_START,
634 : TEXT_ALIGN_END,
635 : TEXT_ALIGN_LEFT,
636 : TEXT_ALIGN_RIGHT,
637 : TEXT_ALIGN_CENTER
638 : };
639 :
640 : enum TextBaseline {
641 : TEXT_BASELINE_TOP,
642 : TEXT_BASELINE_HANGING,
643 : TEXT_BASELINE_MIDDLE,
644 : TEXT_BASELINE_ALPHABETIC,
645 : TEXT_BASELINE_IDEOGRAPHIC,
646 : TEXT_BASELINE_BOTTOM
647 : };
648 :
649 : gfxFontGroup* GetCurrentFontStyle();
650 : gfxTextRun* MakeTextRun(const PRUnichar* aText,
651 : PRUint32 aLength,
652 : PRUint32 aAppUnitsPerDevUnit,
653 : PRUint32 aFlags);
654 :
655 : enum TextDrawOperation {
656 : TEXT_DRAW_OPERATION_FILL,
657 : TEXT_DRAW_OPERATION_STROKE,
658 : TEXT_DRAW_OPERATION_MEASURE
659 : };
660 :
661 : /*
662 : * Implementation of the fillText, strokeText, and measure functions with
663 : * the operation abstracted to a flag.
664 : */
665 : nsresult DrawOrMeasureText(const nsAString& text,
666 : float x,
667 : float y,
668 : float maxWidth,
669 : TextDrawOperation op,
670 : float* aWidth);
671 :
672 : // style handling
673 : /*
674 : * The previous set style. Is equal to STYLE_MAX when there is no valid
675 : * previous style.
676 : */
677 : Style mLastStyle;
678 : bool mDirtyStyle[STYLE_MAX];
679 :
680 : // state stack handling
681 0 : class ContextState {
682 : public:
683 0 : ContextState() : shadowOffset(0.0, 0.0),
684 : globalAlpha(1.0),
685 : shadowBlur(0.0),
686 : textAlign(TEXT_ALIGN_START),
687 : textBaseline(TEXT_BASELINE_ALPHABETIC),
688 0 : imageSmoothingEnabled(true)
689 0 : { }
690 :
691 0 : ContextState(const ContextState& other)
692 : : shadowOffset(other.shadowOffset),
693 : globalAlpha(other.globalAlpha),
694 : shadowBlur(other.shadowBlur),
695 : font(other.font),
696 : fontGroup(other.fontGroup),
697 : textAlign(other.textAlign),
698 : textBaseline(other.textBaseline),
699 0 : imageSmoothingEnabled(other.imageSmoothingEnabled)
700 : {
701 0 : for (int i = 0; i < STYLE_MAX; i++) {
702 0 : colorStyles[i] = other.colorStyles[i];
703 0 : gradientStyles[i] = other.gradientStyles[i];
704 0 : patternStyles[i] = other.patternStyles[i];
705 : }
706 0 : }
707 :
708 0 : inline void SetColorStyle(Style whichStyle, nscolor color) {
709 0 : colorStyles[whichStyle] = color;
710 0 : gradientStyles[whichStyle] = nsnull;
711 0 : patternStyles[whichStyle] = nsnull;
712 0 : }
713 :
714 0 : inline void SetPatternStyle(Style whichStyle, nsCanvasPattern* pat) {
715 0 : gradientStyles[whichStyle] = nsnull;
716 0 : patternStyles[whichStyle] = pat;
717 0 : }
718 :
719 0 : inline void SetGradientStyle(Style whichStyle, nsCanvasGradient* grad) {
720 0 : gradientStyles[whichStyle] = grad;
721 0 : patternStyles[whichStyle] = nsnull;
722 0 : }
723 :
724 : /**
725 : * returns true iff the given style is a solid color.
726 : */
727 0 : inline bool StyleIsColor(Style whichStyle) const
728 : {
729 0 : return !(patternStyles[whichStyle] ||
730 0 : gradientStyles[whichStyle]);
731 : }
732 :
733 : gfxPoint shadowOffset;
734 : float globalAlpha;
735 : float shadowBlur;
736 :
737 : nsString font;
738 : nsRefPtr<gfxFontGroup> fontGroup;
739 : TextAlign textAlign;
740 : TextBaseline textBaseline;
741 :
742 : nscolor colorStyles[STYLE_MAX];
743 : nsCOMPtr<nsCanvasGradient> gradientStyles[STYLE_MAX];
744 : nsCOMPtr<nsCanvasPattern> patternStyles[STYLE_MAX];
745 :
746 : bool imageSmoothingEnabled;
747 : };
748 :
749 : nsTArray<ContextState> mStyleStack;
750 :
751 0 : inline ContextState& CurrentState() {
752 0 : return mStyleStack[mSaveCount];
753 : }
754 :
755 : // other helpers
756 0 : void GetAppUnitsValues(PRUint32 *perDevPixel, PRUint32 *perCSSPixel) {
757 : // If we don't have a canvas element, we just return something generic.
758 0 : PRUint32 devPixel = 60;
759 0 : PRUint32 cssPixel = 60;
760 :
761 0 : nsIPresShell *ps = GetPresShell();
762 : nsPresContext *pc;
763 :
764 0 : if (!ps) goto FINISH;
765 0 : pc = ps->GetPresContext();
766 0 : if (!pc) goto FINISH;
767 0 : devPixel = pc->AppUnitsPerDevPixel();
768 0 : cssPixel = pc->AppUnitsPerCSSPixel();
769 :
770 : FINISH:
771 0 : if (perDevPixel)
772 0 : *perDevPixel = devPixel;
773 0 : if (perCSSPixel)
774 0 : *perCSSPixel = cssPixel;
775 0 : }
776 :
777 : friend struct nsCanvasBidiProcessor;
778 : };
779 :
780 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCanvasRenderingContext2D)
781 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCanvasRenderingContext2D)
782 :
783 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsCanvasRenderingContext2D)
784 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCanvasRenderingContext2D)
785 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCanvasElement)
786 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
787 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCanvasRenderingContext2D)
788 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCanvasElement)
789 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
790 :
791 : DOMCI_DATA(CanvasRenderingContext2D, nsCanvasRenderingContext2D)
792 :
793 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCanvasRenderingContext2D)
794 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasRenderingContext2D)
795 0 : NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
796 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMCanvasRenderingContext2D)
797 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasRenderingContext2D)
798 0 : NS_INTERFACE_MAP_END
799 :
800 : /**
801 : ** CanvasRenderingContext2D impl
802 : **/
803 :
804 :
805 : // Initialize our static variables.
806 : PRUint32 nsCanvasRenderingContext2D::sNumLivingContexts = 0;
807 : PRUint8 (*nsCanvasRenderingContext2D::sUnpremultiplyTable)[256] = nsnull;
808 : PRUint8 (*nsCanvasRenderingContext2D::sPremultiplyTable)[256] = nsnull;
809 :
810 : nsresult
811 0 : NS_NewCanvasRenderingContext2DThebes(nsIDOMCanvasRenderingContext2D** aResult)
812 : {
813 0 : nsRefPtr<nsIDOMCanvasRenderingContext2D> ctx = new nsCanvasRenderingContext2D();
814 0 : if (!ctx)
815 0 : return NS_ERROR_OUT_OF_MEMORY;
816 :
817 0 : *aResult = ctx.forget().get();
818 0 : return NS_OK;
819 : }
820 :
821 0 : nsCanvasRenderingContext2D::nsCanvasRenderingContext2D()
822 : : mValid(false), mZero(false), mOpaque(false), mResetLayer(true)
823 : , mIPC(false)
824 : , mCanvasElement(nsnull)
825 : , mSaveCount(0), mIsEntireFrameInvalid(false)
826 : , mPredictManyRedrawCalls(false), mHasPath(false), mInvalidateCount(0)
827 0 : , mLastStyle(STYLE_MAX), mStyleStack(20)
828 : {
829 0 : sNumLivingContexts++;
830 0 : }
831 :
832 0 : nsCanvasRenderingContext2D::~nsCanvasRenderingContext2D()
833 : {
834 0 : Reset();
835 0 : sNumLivingContexts--;
836 0 : if (!sNumLivingContexts) {
837 0 : delete[] sUnpremultiplyTable;
838 0 : delete[] sPremultiplyTable;
839 0 : sUnpremultiplyTable = nsnull;
840 0 : sPremultiplyTable = nsnull;
841 : }
842 0 : }
843 :
844 : nsresult
845 0 : nsCanvasRenderingContext2D::Reset()
846 : {
847 0 : if (mCanvasElement) {
848 0 : HTMLCanvasElement()->InvalidateCanvas();
849 : }
850 :
851 : // only do this for non-docshell created contexts,
852 : // since those are the ones that we created a surface for
853 0 : if (mValid && !mDocShell && mSurface)
854 0 : gCanvasMemoryUsed -= mWidth * mHeight * 4;
855 :
856 0 : mSurface = nsnull;
857 0 : mThebes = nsnull;
858 0 : mValid = false;
859 0 : mIsEntireFrameInvalid = false;
860 0 : mPredictManyRedrawCalls = false;
861 0 : return NS_OK;
862 : }
863 :
864 : nsresult
865 0 : nsCanvasRenderingContext2D::SetStyleFromStringOrInterface(const nsAString& aStr,
866 : nsISupports *aInterface,
867 : Style aWhichStyle)
868 : {
869 : nsresult rv;
870 : nscolor color;
871 :
872 0 : if (!aStr.IsVoid()) {
873 : nsIDocument* document = mCanvasElement ?
874 0 : HTMLCanvasElement()->OwnerDoc() : nsnull;
875 :
876 : // Pass the CSS Loader object to the parser, to allow parser error
877 : // reports to include the outer window ID.
878 0 : nsCSSParser parser(document ? document->CSSLoader() : nsnull);
879 0 : rv = parser.ParseColorString(aStr, nsnull, 0, &color);
880 0 : if (NS_FAILED(rv)) {
881 : // Error reporting happens inside the CSS parser
882 0 : return NS_OK;
883 : }
884 :
885 0 : CurrentState().SetColorStyle(aWhichStyle, color);
886 :
887 0 : mDirtyStyle[aWhichStyle] = true;
888 0 : return NS_OK;
889 : }
890 :
891 0 : if (aInterface) {
892 0 : nsCOMPtr<nsCanvasGradient> grad(do_QueryInterface(aInterface));
893 0 : if (grad) {
894 0 : CurrentState().SetGradientStyle(aWhichStyle, grad);
895 0 : mDirtyStyle[aWhichStyle] = true;
896 0 : return NS_OK;
897 : }
898 :
899 0 : nsCOMPtr<nsCanvasPattern> pattern(do_QueryInterface(aInterface));
900 0 : if (pattern) {
901 0 : CurrentState().SetPatternStyle(aWhichStyle, pattern);
902 0 : mDirtyStyle[aWhichStyle] = true;
903 0 : return NS_OK;
904 : }
905 : }
906 :
907 : nsContentUtils::ReportToConsole(
908 : nsIScriptError::warningFlag,
909 : "Canvas",
910 0 : mCanvasElement ? HTMLCanvasElement()->OwnerDoc() : nsnull,
911 : nsContentUtils::eDOM_PROPERTIES,
912 0 : "UnexpectedCanvasVariantStyle");
913 :
914 0 : return NS_OK;
915 : }
916 :
917 : nsresult
918 0 : nsCanvasRenderingContext2D::GetStyleAsStringOrInterface(nsAString& aStr,
919 : nsISupports **aInterface,
920 : PRInt32 *aType,
921 : Style aWhichStyle)
922 : {
923 0 : if (CurrentState().patternStyles[aWhichStyle]) {
924 0 : aStr.SetIsVoid(true);
925 0 : NS_ADDREF(*aInterface = CurrentState().patternStyles[aWhichStyle]);
926 0 : *aType = CMG_STYLE_PATTERN;
927 0 : } else if (CurrentState().gradientStyles[aWhichStyle]) {
928 0 : aStr.SetIsVoid(true);
929 0 : NS_ADDREF(*aInterface = CurrentState().gradientStyles[aWhichStyle]);
930 0 : *aType = CMG_STYLE_GRADIENT;
931 : } else {
932 0 : StyleColorToString(CurrentState().colorStyles[aWhichStyle], aStr);
933 0 : *aInterface = nsnull;
934 0 : *aType = CMG_STYLE_STRING;
935 : }
936 :
937 0 : return NS_OK;
938 : }
939 :
940 : void
941 0 : nsCanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
942 : {
943 : // We can't reuse the normal CSS color stringification code,
944 : // because the spec calls for a different algorithm for canvas.
945 0 : if (NS_GET_A(aColor) == 255) {
946 : CopyUTF8toUTF16(nsPrintfCString(100, "#%02x%02x%02x",
947 : NS_GET_R(aColor),
948 : NS_GET_G(aColor),
949 0 : NS_GET_B(aColor)),
950 0 : aStr);
951 : } else {
952 : CopyUTF8toUTF16(nsPrintfCString(100, "rgba(%d, %d, %d, ",
953 : NS_GET_R(aColor),
954 : NS_GET_G(aColor),
955 0 : NS_GET_B(aColor)),
956 0 : aStr);
957 0 : aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
958 0 : aStr.Append(')');
959 : }
960 0 : }
961 :
962 : void
963 0 : nsCanvasRenderingContext2D::DirtyAllStyles()
964 : {
965 0 : for (int i = 0; i < STYLE_MAX; i++) {
966 0 : mDirtyStyle[i] = true;
967 : }
968 0 : }
969 :
970 : void
971 0 : nsCanvasRenderingContext2D::ApplyStyle(Style aWhichStyle,
972 : bool aUseGlobalAlpha)
973 : {
974 0 : if (mLastStyle == aWhichStyle &&
975 0 : !mDirtyStyle[aWhichStyle] &&
976 : aUseGlobalAlpha)
977 : {
978 : // nothing to do, this is already the set style
979 0 : return;
980 : }
981 :
982 0 : if (!EnsureSurface()) {
983 0 : return;
984 : }
985 :
986 : // if not using global alpha, don't optimize with dirty bit
987 0 : if (aUseGlobalAlpha)
988 0 : mDirtyStyle[aWhichStyle] = false;
989 0 : mLastStyle = aWhichStyle;
990 :
991 0 : nsCanvasPattern* pattern = CurrentState().patternStyles[aWhichStyle];
992 0 : if (pattern) {
993 0 : if (mCanvasElement)
994 : CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
995 : pattern->Principal(),
996 0 : pattern->GetForceWriteOnly(),
997 0 : pattern->GetCORSUsed());
998 :
999 0 : gfxPattern* gpat = pattern->GetPattern();
1000 :
1001 0 : if (CurrentState().imageSmoothingEnabled)
1002 0 : gpat->SetFilter(gfxPattern::FILTER_GOOD);
1003 : else
1004 0 : gpat->SetFilter(gfxPattern::FILTER_NEAREST);
1005 :
1006 0 : mThebes->SetPattern(gpat);
1007 0 : return;
1008 : }
1009 :
1010 0 : if (CurrentState().gradientStyles[aWhichStyle]) {
1011 0 : gfxPattern* gpat = CurrentState().gradientStyles[aWhichStyle]->GetPattern();
1012 0 : mThebes->SetPattern(gpat);
1013 0 : return;
1014 : }
1015 :
1016 0 : gfxRGBA color(CurrentState().colorStyles[aWhichStyle]);
1017 0 : if (aUseGlobalAlpha)
1018 0 : color.a *= CurrentState().globalAlpha;
1019 :
1020 0 : mThebes->SetColor(color);
1021 : }
1022 :
1023 : nsresult
1024 0 : nsCanvasRenderingContext2D::Redraw()
1025 : {
1026 0 : if (mIsEntireFrameInvalid)
1027 0 : return NS_OK;
1028 0 : mIsEntireFrameInvalid = true;
1029 :
1030 0 : if (!mCanvasElement) {
1031 0 : NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
1032 0 : return NS_OK;
1033 : }
1034 :
1035 0 : nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
1036 :
1037 0 : HTMLCanvasElement()->InvalidateCanvasContent(nsnull);
1038 :
1039 0 : return NS_OK;
1040 : }
1041 :
1042 : NS_IMETHODIMP
1043 0 : nsCanvasRenderingContext2D::Redraw(const gfxRect& r)
1044 : {
1045 0 : ++mInvalidateCount;
1046 :
1047 0 : if (mIsEntireFrameInvalid)
1048 0 : return NS_OK;
1049 :
1050 0 : if (mPredictManyRedrawCalls ||
1051 : mInvalidateCount > kCanvasMaxInvalidateCount) {
1052 0 : return Redraw();
1053 : }
1054 :
1055 0 : if (!mCanvasElement) {
1056 0 : NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
1057 0 : return NS_OK;
1058 : }
1059 :
1060 0 : nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
1061 :
1062 0 : HTMLCanvasElement()->InvalidateCanvasContent(&r);
1063 :
1064 0 : return NS_OK;
1065 : }
1066 :
1067 : NS_IMETHODIMP
1068 0 : nsCanvasRenderingContext2D::RedrawUser(const gfxRect& r)
1069 : {
1070 0 : if (mIsEntireFrameInvalid) {
1071 0 : ++mInvalidateCount;
1072 0 : return NS_OK;
1073 : }
1074 :
1075 0 : return Redraw(mThebes->UserToDevice(r));
1076 : }
1077 :
1078 : NS_IMETHODIMP
1079 0 : nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height)
1080 : {
1081 0 : Initialize(NULL, width, height);
1082 0 : return NS_OK;
1083 : }
1084 :
1085 : void
1086 0 : nsCanvasRenderingContext2D::Initialize(nsIDocShell *docShell, PRInt32 width, PRInt32 height)
1087 : {
1088 0 : Reset();
1089 :
1090 0 : NS_ASSERTION(!docShell ^ !mCanvasElement, "Cannot set both docshell and canvas element");
1091 0 : mDocShell = docShell;
1092 :
1093 0 : mWidth = width;
1094 0 : mHeight = height;
1095 :
1096 0 : mResetLayer = true;
1097 0 : mValid = true;
1098 0 : mSurfaceCreated = false;
1099 :
1100 : // set up the initial canvas defaults
1101 0 : mStyleStack.Clear();
1102 0 : mSaveCount = 0;
1103 :
1104 0 : ContextState *state = mStyleStack.AppendElement();
1105 0 : state->globalAlpha = 1.0;
1106 :
1107 0 : state->colorStyles[STYLE_FILL] = NS_RGB(0,0,0);
1108 0 : state->colorStyles[STYLE_STROKE] = NS_RGB(0,0,0);
1109 0 : state->colorStyles[STYLE_SHADOW] = NS_RGBA(0,0,0,0);
1110 0 : DirtyAllStyles();
1111 :
1112 : // always force a redraw, because if the surface dimensions were reset
1113 : // then the surface became cleared, and we need to redraw everything.
1114 0 : Redraw();
1115 :
1116 : return;
1117 : }
1118 :
1119 : void
1120 0 : nsCanvasRenderingContext2D::CreateThebes()
1121 : {
1122 0 : mThebes = new gfxContext(mSurface);
1123 0 : mSurfaceCreated = true;
1124 :
1125 0 : mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);
1126 0 : mThebes->NewPath();
1127 0 : mThebes->Rectangle(gfxRect(0, 0, mWidth, mHeight));
1128 0 : mThebes->Fill();
1129 :
1130 0 : mThebes->SetLineWidth(1.0);
1131 0 : mThebes->SetOperator(gfxContext::OPERATOR_OVER);
1132 0 : mThebes->SetMiterLimit(10.0);
1133 0 : mThebes->SetLineCap(gfxContext::LINE_CAP_BUTT);
1134 0 : mThebes->SetLineJoin(gfxContext::LINE_JOIN_MITER);
1135 :
1136 0 : mThebes->NewPath();
1137 0 : }
1138 :
1139 : NS_IMETHODIMP
1140 0 : nsCanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *docShell,
1141 : gfxASurface *surface,
1142 : PRInt32 width,
1143 : PRInt32 height)
1144 : {
1145 0 : Initialize(docShell, width, height);
1146 :
1147 0 : mSurface = surface;
1148 0 : CreateThebes();
1149 0 : return mValid ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1150 : }
1151 :
1152 : bool
1153 0 : nsCanvasRenderingContext2D::EnsureSurface()
1154 : {
1155 0 : if (!mValid) {
1156 0 : return false;
1157 : }
1158 :
1159 0 : if (mSurface && mThebes && mSurfaceCreated) {
1160 0 : if (mSurface->CairoStatus()) {
1161 0 : return false;
1162 : }
1163 0 : return true;
1164 : }
1165 :
1166 0 : nsRefPtr<gfxASurface> surface;
1167 :
1168 : // Check that the dimensions are sane
1169 0 : if (gfxASurface::CheckSurfaceSize(gfxIntSize(mWidth, mHeight), 0xffff)) {
1170 : // Zero sized surfaces have problems, so just use a 1 by 1.
1171 0 : if (mHeight == 0 || mWidth == 0) {
1172 0 : mZero = true;
1173 0 : mHeight = 1;
1174 0 : mWidth = 1;
1175 : } else {
1176 0 : mZero = false;
1177 : }
1178 :
1179 0 : gfxASurface::gfxImageFormat format = GetImageFormat();
1180 :
1181 0 : if (!PR_GetEnv("MOZ_CANVAS_IMAGE_SURFACE")) {
1182 0 : nsCOMPtr<nsIContent> content = do_QueryObject(mCanvasElement);
1183 0 : nsIDocument* ownerDoc = nsnull;
1184 0 : if (content)
1185 0 : ownerDoc = content->OwnerDoc();
1186 0 : nsRefPtr<LayerManager> layerManager = nsnull;
1187 :
1188 0 : if (ownerDoc)
1189 : layerManager =
1190 0 : nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
1191 :
1192 0 : if (layerManager) {
1193 0 : surface = layerManager->CreateOptimalSurface(gfxIntSize(mWidth, mHeight), format);
1194 : } else {
1195 0 : surface = gfxPlatform::GetPlatform()->
1196 0 : CreateOffscreenSurface(gfxIntSize(mWidth, mHeight), gfxASurface::ContentFromFormat(format));
1197 : }
1198 : }
1199 :
1200 0 : if (!surface || surface->CairoStatus()) {
1201 : // If we couldn't create a surface of the type we want, fall back
1202 : // to an image surface. This lets us handle surface sizes that
1203 : // the underlying cairo backend might not handle.
1204 0 : surface = new gfxImageSurface(gfxIntSize(mWidth, mHeight), format);
1205 0 : if (!surface || surface->CairoStatus()) {
1206 0 : surface = nsnull;
1207 : }
1208 : }
1209 : }
1210 0 : if (surface) {
1211 0 : if (gCanvasMemoryReporter == nsnull) {
1212 0 : gCanvasMemoryReporter = new NS_MEMORY_REPORTER_NAME(CanvasMemory);
1213 0 : NS_RegisterMemoryReporter(gCanvasMemoryReporter);
1214 : }
1215 :
1216 0 : gCanvasMemoryUsed += mWidth * mHeight * 4;
1217 0 : JSContext* context = nsContentUtils::GetCurrentJSContext();
1218 0 : if (context) {
1219 0 : JS_updateMallocCounter(context, mWidth * mHeight * 4);
1220 : }
1221 : } else {
1222 0 : return false;
1223 : }
1224 :
1225 0 : mSurface = surface;
1226 0 : CreateThebes();
1227 :
1228 0 : if (mSurface->CairoStatus()) {
1229 0 : return false;
1230 : }
1231 0 : return true;
1232 : }
1233 :
1234 : NS_IMETHODIMP
1235 0 : nsCanvasRenderingContext2D::SetIsOpaque(bool isOpaque)
1236 : {
1237 0 : if (isOpaque == mOpaque)
1238 0 : return NS_OK;
1239 :
1240 0 : mOpaque = isOpaque;
1241 :
1242 0 : if (mValid) {
1243 : /* If we've already been created, let SetDimensions take care of
1244 : * recreating our surface
1245 : */
1246 0 : return SetDimensions(mWidth, mHeight);
1247 : }
1248 :
1249 0 : return NS_OK;
1250 : }
1251 :
1252 : NS_IMETHODIMP
1253 0 : nsCanvasRenderingContext2D::SetIsIPC(bool isIPC)
1254 : {
1255 0 : if (isIPC == mIPC)
1256 0 : return NS_OK;
1257 :
1258 0 : mIPC = isIPC;
1259 :
1260 0 : if (mValid) {
1261 : /* If we've already been created, let SetDimensions take care of
1262 : * recreating our surface
1263 : */
1264 0 : return SetDimensions(mWidth, mHeight);
1265 : }
1266 :
1267 0 : return NS_OK;
1268 : }
1269 :
1270 : NS_IMETHODIMP
1271 0 : nsCanvasRenderingContext2D::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter)
1272 : {
1273 0 : nsresult rv = NS_OK;
1274 :
1275 0 : if (!EnsureSurface())
1276 0 : return NS_ERROR_FAILURE;
1277 :
1278 0 : nsRefPtr<gfxPattern> pat = new gfxPattern(mSurface);
1279 :
1280 0 : pat->SetFilter(aFilter);
1281 0 : pat->SetExtend(gfxPattern::EXTEND_PAD);
1282 :
1283 0 : gfxContext::GraphicsOperator op = ctx->CurrentOperator();
1284 0 : if (mOpaque)
1285 0 : ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
1286 :
1287 : // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
1288 : // pixel alignment for this stuff!
1289 0 : ctx->NewPath();
1290 0 : ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
1291 0 : ctx->Fill();
1292 :
1293 0 : if (mOpaque)
1294 0 : ctx->SetOperator(op);
1295 :
1296 0 : return rv;
1297 : }
1298 :
1299 : NS_IMETHODIMP
1300 0 : nsCanvasRenderingContext2D::GetInputStream(const char *aMimeType,
1301 : const PRUnichar *aEncoderOptions,
1302 : nsIInputStream **aStream)
1303 : {
1304 0 : if (!EnsureSurface())
1305 0 : return NS_ERROR_FAILURE;
1306 :
1307 : nsresult rv;
1308 0 : const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
1309 0 : nsAutoArrayPtr<char> conid(new (std::nothrow) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
1310 :
1311 0 : if (!conid)
1312 0 : return NS_ERROR_OUT_OF_MEMORY;
1313 :
1314 0 : strcpy(conid, encoderPrefix);
1315 0 : strcat(conid, aMimeType);
1316 :
1317 0 : nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
1318 0 : if (!encoder)
1319 0 : return NS_ERROR_FAILURE;
1320 :
1321 0 : nsAutoArrayPtr<PRUint8> imageBuffer(new (std::nothrow) PRUint8[mWidth * mHeight * 4]);
1322 0 : if (!imageBuffer)
1323 0 : return NS_ERROR_OUT_OF_MEMORY;
1324 :
1325 : nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(imageBuffer.get(),
1326 : gfxIntSize(mWidth, mHeight),
1327 : mWidth * 4,
1328 0 : gfxASurface::ImageFormatARGB32);
1329 :
1330 0 : if (!imgsurf || imgsurf->CairoStatus())
1331 0 : return NS_ERROR_FAILURE;
1332 :
1333 0 : nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
1334 :
1335 0 : if (!ctx || ctx->HasError())
1336 0 : return NS_ERROR_FAILURE;
1337 :
1338 0 : ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
1339 0 : ctx->SetSource(mSurface, gfxPoint(0, 0));
1340 0 : ctx->Paint();
1341 :
1342 0 : rv = encoder->InitFromData(imageBuffer.get(),
1343 : mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
1344 : imgIEncoder::INPUT_FORMAT_HOSTARGB,
1345 0 : nsDependentString(aEncoderOptions));
1346 0 : NS_ENSURE_SUCCESS(rv, rv);
1347 :
1348 0 : return CallQueryInterface(encoder, aStream);
1349 : }
1350 :
1351 : gfxASurface::gfxImageFormat
1352 0 : nsCanvasRenderingContext2D::GetImageFormat() const
1353 : {
1354 0 : gfxASurface::gfxImageFormat format = gfxASurface::ImageFormatARGB32;
1355 :
1356 0 : if (mOpaque)
1357 0 : format = gfxASurface::ImageFormatRGB24;
1358 :
1359 0 : return format;
1360 : }
1361 :
1362 : //
1363 : // nsCanvasRenderingContext2D impl
1364 : //
1365 :
1366 : NS_IMETHODIMP
1367 0 : nsCanvasRenderingContext2D::SetCanvasElement(nsHTMLCanvasElement* aCanvasElement)
1368 : {
1369 0 : mCanvasElement = aCanvasElement;
1370 :
1371 0 : return NS_OK;
1372 : }
1373 :
1374 : NS_IMETHODIMP
1375 0 : nsCanvasRenderingContext2D::GetCanvas(nsIDOMHTMLCanvasElement **canvas)
1376 : {
1377 0 : NS_IF_ADDREF(*canvas = mCanvasElement);
1378 :
1379 0 : return NS_OK;
1380 : }
1381 :
1382 : //
1383 : // state
1384 : //
1385 :
1386 : NS_IMETHODIMP
1387 0 : nsCanvasRenderingContext2D::Save()
1388 : {
1389 0 : if (!EnsureSurface())
1390 0 : return NS_ERROR_FAILURE;
1391 :
1392 0 : ContextState state = CurrentState();
1393 0 : mStyleStack.AppendElement(state);
1394 0 : mThebes->Save();
1395 0 : mSaveCount++;
1396 0 : return NS_OK;
1397 : }
1398 :
1399 : NS_IMETHODIMP
1400 0 : nsCanvasRenderingContext2D::Restore()
1401 : {
1402 0 : if (!EnsureSurface())
1403 0 : return NS_ERROR_FAILURE;
1404 :
1405 0 : if (mSaveCount == 0)
1406 0 : return NS_OK;
1407 :
1408 0 : mStyleStack.RemoveElementAt(mSaveCount);
1409 0 : mThebes->Restore();
1410 :
1411 0 : mLastStyle = STYLE_MAX;
1412 0 : DirtyAllStyles();
1413 :
1414 0 : mSaveCount--;
1415 0 : return NS_OK;
1416 : }
1417 :
1418 : //
1419 : // transformations
1420 : //
1421 :
1422 : NS_IMETHODIMP
1423 0 : nsCanvasRenderingContext2D::Scale(float x, float y)
1424 : {
1425 0 : if (!EnsureSurface())
1426 0 : return NS_ERROR_FAILURE;
1427 :
1428 0 : if (!FloatValidate(x,y))
1429 0 : return NS_OK;
1430 :
1431 0 : mThebes->Scale(x, y);
1432 0 : return NS_OK;
1433 : }
1434 :
1435 : NS_IMETHODIMP
1436 0 : nsCanvasRenderingContext2D::Rotate(float angle)
1437 : {
1438 0 : if (!EnsureSurface())
1439 0 : return NS_ERROR_FAILURE;
1440 :
1441 0 : if (!FloatValidate(angle))
1442 0 : return NS_OK;
1443 :
1444 0 : mThebes->Rotate(angle);
1445 0 : return NS_OK;
1446 : }
1447 :
1448 : NS_IMETHODIMP
1449 0 : nsCanvasRenderingContext2D::Translate(float x, float y)
1450 : {
1451 0 : if (!EnsureSurface())
1452 0 : return NS_ERROR_FAILURE;
1453 :
1454 0 : if (!FloatValidate(x,y))
1455 0 : return NS_OK;
1456 :
1457 0 : mThebes->Translate(gfxPoint(x, y));
1458 0 : return NS_OK;
1459 : }
1460 :
1461 : NS_IMETHODIMP
1462 0 : nsCanvasRenderingContext2D::Transform(float m11, float m12, float m21, float m22, float dx, float dy)
1463 : {
1464 0 : if (!EnsureSurface())
1465 0 : return NS_ERROR_FAILURE;
1466 :
1467 0 : if (!FloatValidate(m11,m12,m21,m22,dx,dy))
1468 0 : return NS_OK;
1469 :
1470 0 : gfxMatrix matrix(m11, m12, m21, m22, dx, dy);
1471 0 : mThebes->Multiply(matrix);
1472 :
1473 0 : return NS_OK;
1474 : }
1475 :
1476 : NS_IMETHODIMP
1477 0 : nsCanvasRenderingContext2D::SetTransform(float m11, float m12, float m21, float m22, float dx, float dy)
1478 : {
1479 0 : if (!EnsureSurface())
1480 0 : return NS_ERROR_FAILURE;
1481 :
1482 0 : if (!FloatValidate(m11,m12,m21,m22,dx,dy))
1483 0 : return NS_OK;
1484 :
1485 0 : gfxMatrix matrix(m11, m12, m21, m22, dx, dy);
1486 0 : mThebes->SetMatrix(matrix);
1487 :
1488 0 : return NS_OK;
1489 : }
1490 :
1491 : NS_IMETHODIMP
1492 0 : nsCanvasRenderingContext2D::SetMozCurrentTransform(JSContext* cx,
1493 : const jsval& matrix)
1494 : {
1495 : nsresult rv;
1496 0 : gfxMatrix newCTM;
1497 :
1498 0 : if (!EnsureSurface())
1499 0 : return NS_ERROR_FAILURE;
1500 :
1501 0 : if (!JSValToMatrix(cx, matrix, &newCTM, &rv)) {
1502 0 : return rv;
1503 : }
1504 :
1505 0 : mThebes->SetMatrix(newCTM);
1506 :
1507 0 : return NS_OK;
1508 : }
1509 :
1510 : NS_IMETHODIMP
1511 0 : nsCanvasRenderingContext2D::GetMozCurrentTransform(JSContext* cx,
1512 : jsval* matrix)
1513 : {
1514 0 : if (!EnsureSurface())
1515 0 : return NS_ERROR_FAILURE;
1516 :
1517 0 : return MatrixToJSVal(mThebes->CurrentMatrix(), cx, matrix);
1518 : }
1519 :
1520 : NS_IMETHODIMP
1521 0 : nsCanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* cx,
1522 : const jsval& matrix)
1523 : {
1524 : nsresult rv;
1525 0 : gfxMatrix newCTMInverse;
1526 :
1527 0 : if (!EnsureSurface())
1528 0 : return NS_ERROR_FAILURE;
1529 :
1530 0 : if (!JSValToMatrix(cx, matrix, &newCTMInverse, &rv)) {
1531 0 : return rv;
1532 : }
1533 :
1534 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
1535 0 : if (!newCTMInverse.IsSingular()) {
1536 0 : mThebes->SetMatrix(newCTMInverse.Invert());
1537 : }
1538 :
1539 0 : return NS_OK;
1540 : }
1541 :
1542 : NS_IMETHODIMP
1543 0 : nsCanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* cx,
1544 : jsval* matrix)
1545 : {
1546 0 : gfxMatrix ctm = mThebes->CurrentMatrix();
1547 :
1548 0 : if (!mThebes->CurrentMatrix().IsSingular()) {
1549 0 : ctm.Invert();
1550 : } else {
1551 0 : double NaN = JSVAL_TO_DOUBLE(JS_GetNaNValue(cx));
1552 0 : ctm = gfxMatrix(NaN, NaN, NaN, NaN, NaN, NaN);
1553 : }
1554 :
1555 0 : return MatrixToJSVal(ctm, cx, matrix);
1556 : }
1557 :
1558 : //
1559 : // colors
1560 : //
1561 :
1562 : NS_IMETHODIMP
1563 0 : nsCanvasRenderingContext2D::SetGlobalAlpha(float aGlobalAlpha)
1564 : {
1565 0 : if (!FloatValidate(aGlobalAlpha) || aGlobalAlpha < 0.0 || aGlobalAlpha > 1.0)
1566 0 : return NS_OK;
1567 :
1568 0 : CurrentState().globalAlpha = aGlobalAlpha;
1569 0 : DirtyAllStyles();
1570 :
1571 0 : return NS_OK;
1572 : }
1573 :
1574 : NS_IMETHODIMP
1575 0 : nsCanvasRenderingContext2D::GetGlobalAlpha(float *aGlobalAlpha)
1576 : {
1577 0 : *aGlobalAlpha = CurrentState().globalAlpha;
1578 0 : return NS_OK;
1579 : }
1580 :
1581 : NS_IMETHODIMP
1582 0 : nsCanvasRenderingContext2D::SetStrokeStyle(nsIVariant *aValue)
1583 : {
1584 0 : if (!aValue)
1585 0 : return NS_ERROR_FAILURE;
1586 :
1587 0 : nsString str;
1588 :
1589 : nsresult rv;
1590 : PRUint16 vtype;
1591 0 : rv = aValue->GetDataType(&vtype);
1592 0 : NS_ENSURE_SUCCESS(rv, rv);
1593 :
1594 0 : if (vtype == nsIDataType::VTYPE_INTERFACE ||
1595 : vtype == nsIDataType::VTYPE_INTERFACE_IS)
1596 : {
1597 : nsIID *iid;
1598 0 : nsCOMPtr<nsISupports> sup;
1599 0 : rv = aValue->GetAsInterface(&iid, getter_AddRefs(sup));
1600 0 : NS_ENSURE_SUCCESS(rv, rv);
1601 0 : if (iid)
1602 0 : NS_Free(iid);
1603 :
1604 0 : str.SetIsVoid(true);
1605 0 : return SetStrokeStyle_multi(str, sup);
1606 : }
1607 :
1608 0 : rv = aValue->GetAsAString(str);
1609 0 : NS_ENSURE_SUCCESS(rv, rv);
1610 :
1611 0 : return SetStrokeStyle_multi(str, nsnull);
1612 : }
1613 :
1614 : NS_IMETHODIMP
1615 0 : nsCanvasRenderingContext2D::GetStrokeStyle(nsIVariant **aResult)
1616 : {
1617 0 : nsCOMPtr<nsIWritableVariant> wv = do_CreateInstance(NS_VARIANT_CONTRACTID);
1618 :
1619 0 : nsCOMPtr<nsISupports> sup;
1620 0 : nsString str;
1621 : PRInt32 t;
1622 0 : nsresult rv = GetStrokeStyle_multi(str, getter_AddRefs(sup), &t);
1623 0 : NS_ENSURE_SUCCESS(rv, rv);
1624 :
1625 0 : if (t == CMG_STYLE_STRING) {
1626 0 : rv = wv->SetAsAString(str);
1627 0 : } else if (t == CMG_STYLE_PATTERN) {
1628 0 : rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasPattern),
1629 0 : sup);
1630 0 : } else if (t == CMG_STYLE_GRADIENT) {
1631 0 : rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasGradient),
1632 0 : sup);
1633 : } else {
1634 0 : NS_ERROR("Unknown type from GetStroke/FillStyle_multi!");
1635 0 : return NS_ERROR_FAILURE;
1636 : }
1637 0 : NS_ENSURE_SUCCESS(rv, rv);
1638 :
1639 0 : NS_IF_ADDREF(*aResult = wv.get());
1640 0 : return NS_OK;
1641 : }
1642 :
1643 : NS_IMETHODIMP
1644 0 : nsCanvasRenderingContext2D::SetFillStyle(nsIVariant *aValue)
1645 : {
1646 0 : if (!aValue)
1647 0 : return NS_ERROR_FAILURE;
1648 :
1649 0 : nsString str;
1650 : nsresult rv;
1651 : PRUint16 vtype;
1652 0 : rv = aValue->GetDataType(&vtype);
1653 0 : NS_ENSURE_SUCCESS(rv, rv);
1654 :
1655 0 : if (vtype == nsIDataType::VTYPE_INTERFACE ||
1656 : vtype == nsIDataType::VTYPE_INTERFACE_IS)
1657 : {
1658 : nsIID *iid;
1659 0 : nsCOMPtr<nsISupports> sup;
1660 0 : rv = aValue->GetAsInterface(&iid, getter_AddRefs(sup));
1661 0 : NS_ENSURE_SUCCESS(rv, rv);
1662 :
1663 0 : str.SetIsVoid(true);
1664 0 : return SetFillStyle_multi(str, sup);
1665 : }
1666 :
1667 0 : rv = aValue->GetAsAString(str);
1668 0 : NS_ENSURE_SUCCESS(rv, rv);
1669 :
1670 0 : return SetFillStyle_multi(str, nsnull);
1671 : }
1672 :
1673 : NS_IMETHODIMP
1674 0 : nsCanvasRenderingContext2D::GetFillStyle(nsIVariant **aResult)
1675 : {
1676 0 : nsCOMPtr<nsIWritableVariant> wv = do_CreateInstance(NS_VARIANT_CONTRACTID);
1677 :
1678 0 : nsCOMPtr<nsISupports> sup;
1679 0 : nsString str;
1680 : PRInt32 t;
1681 0 : nsresult rv = GetFillStyle_multi(str, getter_AddRefs(sup), &t);
1682 0 : NS_ENSURE_SUCCESS(rv, rv);
1683 :
1684 0 : if (t == CMG_STYLE_STRING) {
1685 0 : rv = wv->SetAsAString(str);
1686 0 : } else if (t == CMG_STYLE_PATTERN) {
1687 0 : rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasPattern),
1688 0 : sup);
1689 0 : } else if (t == CMG_STYLE_GRADIENT) {
1690 0 : rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasGradient),
1691 0 : sup);
1692 : } else {
1693 0 : NS_ERROR("Unknown type from GetStroke/FillStyle_multi!");
1694 0 : return NS_ERROR_FAILURE;
1695 : }
1696 0 : NS_ENSURE_SUCCESS(rv, rv);
1697 :
1698 0 : NS_IF_ADDREF(*aResult = wv.get());
1699 0 : return NS_OK;
1700 : }
1701 :
1702 : NS_IMETHODIMP
1703 0 : nsCanvasRenderingContext2D::SetStrokeStyle_multi(const nsAString& aStr, nsISupports *aInterface)
1704 : {
1705 0 : return SetStyleFromStringOrInterface(aStr, aInterface, STYLE_STROKE);
1706 : }
1707 :
1708 : NS_IMETHODIMP
1709 0 : nsCanvasRenderingContext2D::GetStrokeStyle_multi(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType)
1710 : {
1711 0 : return GetStyleAsStringOrInterface(aStr, aInterface, aType, STYLE_STROKE);
1712 : }
1713 :
1714 : NS_IMETHODIMP
1715 0 : nsCanvasRenderingContext2D::SetFillStyle_multi(const nsAString& aStr, nsISupports *aInterface)
1716 : {
1717 0 : return SetStyleFromStringOrInterface(aStr, aInterface, STYLE_FILL);
1718 : }
1719 :
1720 : NS_IMETHODIMP
1721 0 : nsCanvasRenderingContext2D::GetFillStyle_multi(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType)
1722 : {
1723 0 : return GetStyleAsStringOrInterface(aStr, aInterface, aType, STYLE_FILL);
1724 : }
1725 :
1726 : NS_IMETHODIMP
1727 0 : nsCanvasRenderingContext2D::SetMozFillRule(const nsAString& aString)
1728 : {
1729 : gfxContext::FillRule rule;
1730 :
1731 0 : if (!EnsureSurface())
1732 0 : return NS_ERROR_FAILURE;
1733 :
1734 0 : if (aString.EqualsLiteral("evenodd"))
1735 0 : rule = gfxContext::FILL_RULE_EVEN_ODD;
1736 0 : else if (aString.EqualsLiteral("nonzero"))
1737 0 : rule = gfxContext::FILL_RULE_WINDING;
1738 : else
1739 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
1740 0 : return NS_OK;
1741 :
1742 0 : mThebes->SetFillRule(rule);
1743 0 : return NS_OK;
1744 : }
1745 :
1746 : NS_IMETHODIMP
1747 0 : nsCanvasRenderingContext2D::GetMozFillRule(nsAString& aString)
1748 : {
1749 0 : if (!EnsureSurface())
1750 0 : return NS_ERROR_FAILURE;
1751 :
1752 0 : switch (mThebes->CurrentFillRule()) {
1753 : case gfxContext::FILL_RULE_WINDING:
1754 0 : aString.AssignLiteral("nonzero"); break;
1755 : case gfxContext::FILL_RULE_EVEN_ODD:
1756 0 : aString.AssignLiteral("evenodd"); break;
1757 : }
1758 0 : return NS_OK;
1759 : }
1760 :
1761 : //
1762 : // gradients and patterns
1763 : //
1764 : NS_IMETHODIMP
1765 0 : nsCanvasRenderingContext2D::CreateLinearGradient(float x0, float y0, float x1, float y1,
1766 : nsIDOMCanvasGradient **_retval)
1767 : {
1768 0 : if (!FloatValidate(x0,y0,x1,y1))
1769 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1770 :
1771 0 : nsRefPtr<gfxPattern> gradpat = new gfxPattern(x0, y0, x1, y1);
1772 0 : if (!gradpat)
1773 0 : return NS_ERROR_OUT_OF_MEMORY;
1774 :
1775 0 : nsRefPtr<nsIDOMCanvasGradient> grad = new nsCanvasGradient(gradpat);
1776 0 : if (!grad)
1777 0 : return NS_ERROR_OUT_OF_MEMORY;
1778 :
1779 0 : *_retval = grad.forget().get();
1780 0 : return NS_OK;
1781 : }
1782 :
1783 : NS_IMETHODIMP
1784 0 : nsCanvasRenderingContext2D::CreateRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1,
1785 : nsIDOMCanvasGradient **_retval)
1786 : {
1787 0 : if (!FloatValidate(x0,y0,r0,x1,y1,r1))
1788 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1789 :
1790 0 : if (r0 < 0.0 || r1 < 0.0)
1791 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1792 :
1793 0 : nsRefPtr<gfxPattern> gradpat = new gfxPattern(x0, y0, r0, x1, y1, r1);
1794 0 : if (!gradpat)
1795 0 : return NS_ERROR_OUT_OF_MEMORY;
1796 :
1797 0 : nsRefPtr<nsIDOMCanvasGradient> grad = new nsCanvasGradient(gradpat);
1798 0 : if (!grad)
1799 0 : return NS_ERROR_OUT_OF_MEMORY;
1800 :
1801 0 : *_retval = grad.forget().get();
1802 0 : return NS_OK;
1803 : }
1804 :
1805 : NS_IMETHODIMP
1806 0 : nsCanvasRenderingContext2D::CreatePattern(nsIDOMHTMLElement *image,
1807 : const nsAString& repeat,
1808 : nsIDOMCanvasPattern **_retval)
1809 : {
1810 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(image);
1811 0 : if (!content) {
1812 0 : return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
1813 : }
1814 :
1815 : gfxPattern::GraphicsExtend extend;
1816 0 : if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
1817 0 : extend = gfxPattern::EXTEND_REPEAT;
1818 0 : } else if (repeat.EqualsLiteral("repeat-x")) {
1819 : // XX
1820 0 : extend = gfxPattern::EXTEND_REPEAT;
1821 0 : } else if (repeat.EqualsLiteral("repeat-y")) {
1822 : // XX
1823 0 : extend = gfxPattern::EXTEND_REPEAT;
1824 0 : } else if (repeat.EqualsLiteral("no-repeat")) {
1825 0 : extend = gfxPattern::EXTEND_NONE;
1826 : } else {
1827 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
1828 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1829 : }
1830 :
1831 0 : nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(content);
1832 0 : if (canvas) {
1833 0 : nsIntSize size = canvas->GetSize();
1834 0 : if (size.width == 0 || size.height == 0) {
1835 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1836 : }
1837 : }
1838 :
1839 : // The canvas spec says that createPattern should use the first frame
1840 : // of animated images
1841 : nsLayoutUtils::SurfaceFromElementResult res =
1842 0 : nsLayoutUtils::SurfaceFromElement(content->AsElement(),
1843 0 : nsLayoutUtils::SFE_WANT_FIRST_FRAME | nsLayoutUtils::SFE_WANT_NEW_SURFACE);
1844 0 : if (!res.mSurface)
1845 0 : return NS_ERROR_NOT_AVAILABLE;
1846 :
1847 0 : nsRefPtr<gfxPattern> thebespat = new gfxPattern(res.mSurface);
1848 :
1849 0 : thebespat->SetExtend(extend);
1850 :
1851 : nsRefPtr<nsCanvasPattern> pat = new nsCanvasPattern(thebespat, res.mPrincipal,
1852 : res.mIsWriteOnly,
1853 0 : res.mCORSUsed);
1854 0 : *_retval = pat.forget().get();
1855 0 : return NS_OK;
1856 : }
1857 :
1858 : //
1859 : // shadows
1860 : //
1861 : NS_IMETHODIMP
1862 0 : nsCanvasRenderingContext2D::SetShadowOffsetX(float x)
1863 : {
1864 0 : if (!FloatValidate(x))
1865 0 : return NS_OK;
1866 :
1867 0 : CurrentState().shadowOffset.x = x;
1868 0 : return NS_OK;
1869 : }
1870 :
1871 : NS_IMETHODIMP
1872 0 : nsCanvasRenderingContext2D::GetShadowOffsetX(float *x)
1873 : {
1874 0 : *x = static_cast<float>(CurrentState().shadowOffset.x);
1875 0 : return NS_OK;
1876 : }
1877 :
1878 : NS_IMETHODIMP
1879 0 : nsCanvasRenderingContext2D::SetShadowOffsetY(float y)
1880 : {
1881 0 : if (!FloatValidate(y))
1882 0 : return NS_OK;
1883 :
1884 0 : CurrentState().shadowOffset.y = y;
1885 0 : return NS_OK;
1886 : }
1887 :
1888 : NS_IMETHODIMP
1889 0 : nsCanvasRenderingContext2D::GetShadowOffsetY(float *y)
1890 : {
1891 0 : *y = static_cast<float>(CurrentState().shadowOffset.y);
1892 0 : return NS_OK;
1893 : }
1894 :
1895 : NS_IMETHODIMP
1896 0 : nsCanvasRenderingContext2D::SetShadowBlur(float blur)
1897 : {
1898 0 : if (!FloatValidate(blur) || blur < 0.0)
1899 0 : return NS_OK;
1900 :
1901 0 : CurrentState().shadowBlur = blur;
1902 0 : return NS_OK;
1903 : }
1904 :
1905 : NS_IMETHODIMP
1906 0 : nsCanvasRenderingContext2D::GetShadowBlur(float *blur)
1907 : {
1908 0 : *blur = CurrentState().shadowBlur;
1909 0 : return NS_OK;
1910 : }
1911 :
1912 : NS_IMETHODIMP
1913 0 : nsCanvasRenderingContext2D::SetShadowColor(const nsAString& colorstr)
1914 : {
1915 : nsIDocument* document = mCanvasElement ?
1916 0 : HTMLCanvasElement()->OwnerDoc() : nsnull;
1917 :
1918 : // Pass the CSS Loader object to the parser, to allow parser error reports
1919 : // to include the outer window ID.
1920 0 : nsCSSParser parser(document ? document->CSSLoader() : nsnull);
1921 : nscolor color;
1922 0 : nsresult rv = parser.ParseColorString(colorstr, nsnull, 0, &color);
1923 0 : if (NS_FAILED(rv)) {
1924 : // Error reporting happens inside the CSS parser
1925 0 : return NS_OK;
1926 : }
1927 :
1928 0 : CurrentState().SetColorStyle(STYLE_SHADOW, color);
1929 :
1930 0 : mDirtyStyle[STYLE_SHADOW] = true;
1931 :
1932 0 : return NS_OK;
1933 : }
1934 :
1935 : NS_IMETHODIMP
1936 0 : nsCanvasRenderingContext2D::GetShadowColor(nsAString& color)
1937 : {
1938 0 : StyleColorToString(CurrentState().colorStyles[STYLE_SHADOW], color);
1939 :
1940 0 : return NS_OK;
1941 : }
1942 :
1943 : static const gfxFloat SIGMA_MAX = 100;
1944 :
1945 : gfxContext*
1946 0 : nsCanvasRenderingContext2D::ShadowInitialize(const gfxRect& extents, gfxAlphaBoxBlur& blur)
1947 : {
1948 0 : gfxIntSize blurRadius;
1949 :
1950 0 : float shadowBlur = CurrentState().shadowBlur;
1951 0 : gfxFloat sigma = shadowBlur / 2;
1952 : // limit to avoid overly huge temp images
1953 0 : if (sigma > SIGMA_MAX)
1954 0 : sigma = SIGMA_MAX;
1955 0 : blurRadius = gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(sigma, sigma));
1956 :
1957 : // calculate extents
1958 0 : gfxRect drawExtents = extents;
1959 :
1960 : // intersect with clip to avoid making overly huge temp images
1961 0 : gfxMatrix matrix = mThebes->CurrentMatrix();
1962 0 : mThebes->IdentityMatrix();
1963 0 : gfxRect clipExtents = mThebes->GetClipExtents();
1964 0 : mThebes->SetMatrix(matrix);
1965 : // outset by the blur radius so that blurs can leak onto the canvas even
1966 : // when the shape is outside the clipping area
1967 0 : clipExtents.Inflate(blurRadius.width, blurRadius.height);
1968 0 : drawExtents = drawExtents.Intersect(clipExtents - CurrentState().shadowOffset);
1969 :
1970 0 : gfxContext* ctx = blur.Init(drawExtents, gfxIntSize(0,0), blurRadius, nsnull, nsnull);
1971 :
1972 0 : if (!ctx)
1973 0 : return nsnull;
1974 :
1975 0 : return ctx;
1976 : }
1977 :
1978 : void
1979 0 : nsCanvasRenderingContext2D::ShadowFinalize(gfxAlphaBoxBlur& blur)
1980 : {
1981 0 : if (!EnsureSurface())
1982 0 : return;
1983 :
1984 0 : ApplyStyle(STYLE_SHADOW);
1985 : // canvas matrix was already applied, don't apply it twice, but do
1986 : // apply the shadow offset
1987 0 : gfxMatrix matrix = mThebes->CurrentMatrix();
1988 0 : mThebes->IdentityMatrix();
1989 0 : mThebes->Translate(CurrentState().shadowOffset);
1990 :
1991 0 : blur.Paint(mThebes);
1992 0 : mThebes->SetMatrix(matrix);
1993 : }
1994 :
1995 : nsresult
1996 0 : nsCanvasRenderingContext2D::DrawPath(Style style, gfxRect *dirtyRect)
1997 : {
1998 0 : if (!EnsureSurface())
1999 0 : return NS_ERROR_FAILURE;
2000 :
2001 0 : bool doUseIntermediateSurface = false;
2002 :
2003 0 : if (mSurface->GetType() == gfxASurface::SurfaceTypeD2D) {
2004 0 : if (style != STYLE_FILL) {
2005 : // D2D does all operators correctly even if transparent areas of SOURCE
2006 : // affect dest. We need to use an intermediate surface for STROKE because
2007 : // we can't clip to the actual stroke shape easily, but prefer a geometric
2008 : // clip over an intermediate surface for a FILL.
2009 0 : doUseIntermediateSurface = NeedIntermediateSurfaceToHandleGlobalAlpha(style);
2010 : }
2011 : } else {
2012 : /*
2013 : * Need an intermediate surface when:
2014 : * - globalAlpha != 1 and gradients/patterns are used (need to paint_with_alpha)
2015 : * - certain operators are used
2016 : */
2017 0 : doUseIntermediateSurface = NeedToUseIntermediateSurface() ||
2018 0 : NeedIntermediateSurfaceToHandleGlobalAlpha(style);
2019 : }
2020 :
2021 0 : bool doDrawShadow = NeedToDrawShadow();
2022 :
2023 : // Clear the surface if we need to simulate unbounded SOURCE operator
2024 0 : ClearSurfaceForUnboundedSource();
2025 :
2026 0 : if (doDrawShadow) {
2027 0 : gfxMatrix matrix = mThebes->CurrentMatrix();
2028 0 : mThebes->IdentityMatrix();
2029 :
2030 : // calculate extents of path
2031 0 : gfxRect drawExtents;
2032 0 : if (style == STYLE_FILL)
2033 0 : drawExtents = mThebes->GetUserFillExtent();
2034 : else // STYLE_STROKE
2035 0 : drawExtents = mThebes->GetUserStrokeExtent();
2036 :
2037 0 : mThebes->SetMatrix(matrix);
2038 :
2039 0 : gfxAlphaBoxBlur blur;
2040 :
2041 : // no need for a ref here, the blur owns the context
2042 0 : gfxContext* ctx = ShadowInitialize(drawExtents, blur);
2043 0 : if (ctx) {
2044 0 : ApplyStyle(style, false);
2045 0 : CopyContext(ctx, mThebes);
2046 0 : ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
2047 :
2048 0 : if (style == STYLE_FILL)
2049 0 : ctx->Fill();
2050 : else
2051 0 : ctx->Stroke();
2052 :
2053 0 : ShadowFinalize(blur);
2054 : }
2055 : }
2056 :
2057 0 : if (doUseIntermediateSurface) {
2058 0 : nsRefPtr<gfxPath> path = mThebes->CopyPath();
2059 : // if the path didn't copy correctly then we can't restore it, so bail
2060 0 : if (!path)
2061 0 : return NS_ERROR_FAILURE;
2062 :
2063 : // draw onto a pushed group
2064 0 : mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
2065 :
2066 : // XXX for some reason clipping messes up the path when push/popping
2067 : // copying the path seems to fix it, for unknown reasons
2068 0 : mThebes->NewPath();
2069 0 : mThebes->AppendPath(path);
2070 :
2071 : // don't want operators to be applied twice,
2072 0 : if (mSurface->GetType() != gfxASurface::SurfaceTypeD2D) {
2073 0 : mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);
2074 : } else {
2075 : // In the case of D2D OPERATOR_OVER is much faster. So we can just
2076 : // use that since it's the same as SOURCE for a transparent
2077 : // destinations. It would be nice if cairo backends could make this
2078 : // optimization internally but I see no very good way of doing this.
2079 0 : mThebes->SetOperator(gfxContext::OPERATOR_OVER);
2080 : }
2081 : }
2082 :
2083 0 : ApplyStyle(style);
2084 :
2085 0 : if (style == STYLE_FILL) {
2086 0 : if (!doUseIntermediateSurface &&
2087 0 : CurrentState().globalAlpha != 1.0 &&
2088 0 : !CurrentState().StyleIsColor(style))
2089 : {
2090 0 : mThebes->Clip();
2091 0 : mThebes->Paint(CurrentState().globalAlpha);
2092 : } else {
2093 0 : mThebes->Fill();
2094 : }
2095 : } else
2096 0 : mThebes->Stroke();
2097 :
2098 : // XXX do some more work to calculate the extents of shadows
2099 : // XXX handle stroke extents
2100 0 : if (dirtyRect && style == STYLE_FILL && !doDrawShadow) {
2101 0 : *dirtyRect = mThebes->GetUserPathExtent();
2102 : }
2103 :
2104 0 : if (doUseIntermediateSurface) {
2105 0 : mThebes->PopGroupToSource();
2106 0 : DirtyAllStyles();
2107 :
2108 0 : mThebes->Paint(CurrentState().StyleIsColor(style) ? 1.0 : CurrentState().globalAlpha);
2109 : }
2110 :
2111 0 : if (dirtyRect) {
2112 0 : if (style != STYLE_FILL || doDrawShadow) {
2113 : // just use the clip extents
2114 0 : *dirtyRect = mThebes->GetClipExtents();
2115 : }
2116 : }
2117 :
2118 0 : return NS_OK;
2119 : }
2120 :
2121 : //
2122 : // rects
2123 : //
2124 :
2125 : NS_IMETHODIMP
2126 0 : nsCanvasRenderingContext2D::ClearRect(float x, float y, float w, float h)
2127 : {
2128 0 : if (!mSurfaceCreated)
2129 0 : return NS_OK;
2130 :
2131 0 : if (!FloatValidate(x,y,w,h))
2132 0 : return NS_OK;
2133 :
2134 0 : PathAutoSaveRestore pathSR(this);
2135 0 : gfxContextAutoSaveRestore autoSR(mThebes);
2136 :
2137 0 : mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);
2138 0 : mThebes->NewPath();
2139 0 : mThebes->Rectangle(gfxRect(x, y, w, h));
2140 0 : mThebes->Fill();
2141 :
2142 0 : return RedrawUser(mThebes->GetUserPathExtent());
2143 : }
2144 :
2145 : nsresult
2146 0 : nsCanvasRenderingContext2D::DrawRect(const gfxRect& rect, Style style)
2147 : {
2148 0 : if (!EnsureSurface())
2149 0 : return NS_ERROR_FAILURE;
2150 :
2151 0 : if (!FloatValidate(rect.X(), rect.Y(), rect.Width(), rect.Height()))
2152 0 : return NS_OK;
2153 :
2154 0 : PathAutoSaveRestore pathSR(this);
2155 :
2156 0 : mThebes->NewPath();
2157 0 : mThebes->Rectangle(rect);
2158 :
2159 0 : gfxRect dirty;
2160 0 : nsresult rv = DrawPath(style, &dirty);
2161 0 : if (NS_FAILED(rv))
2162 0 : return rv;
2163 :
2164 0 : return RedrawUser(dirty);
2165 : }
2166 :
2167 : NS_IMETHODIMP
2168 0 : nsCanvasRenderingContext2D::FillRect(float x, float y, float w, float h)
2169 : {
2170 0 : return DrawRect(gfxRect(x, y, w, h), STYLE_FILL);
2171 : }
2172 :
2173 : NS_IMETHODIMP
2174 0 : nsCanvasRenderingContext2D::StrokeRect(float x, float y, float w, float h)
2175 : {
2176 0 : if (w == 0.f && h == 0.f) {
2177 0 : return NS_OK;
2178 : }
2179 0 : return DrawRect(gfxRect(x, y, w, h), STYLE_STROKE);
2180 : }
2181 :
2182 : //
2183 : // path bits
2184 : //
2185 :
2186 : NS_IMETHODIMP
2187 0 : nsCanvasRenderingContext2D::BeginPath()
2188 : {
2189 0 : if (!EnsureSurface())
2190 0 : return NS_ERROR_FAILURE;
2191 :
2192 0 : mHasPath = false;
2193 0 : mThebes->NewPath();
2194 0 : return NS_OK;
2195 : }
2196 :
2197 : NS_IMETHODIMP
2198 0 : nsCanvasRenderingContext2D::ClosePath()
2199 : {
2200 0 : if (!EnsureSurface())
2201 0 : return NS_ERROR_FAILURE;
2202 :
2203 0 : mThebes->ClosePath();
2204 0 : return NS_OK;
2205 : }
2206 :
2207 : NS_IMETHODIMP
2208 0 : nsCanvasRenderingContext2D::Fill()
2209 : {
2210 0 : gfxRect dirty;
2211 0 : nsresult rv = DrawPath(STYLE_FILL, &dirty);
2212 0 : if (NS_FAILED(rv))
2213 0 : return rv;
2214 0 : return RedrawUser(dirty);
2215 : }
2216 :
2217 : NS_IMETHODIMP
2218 0 : nsCanvasRenderingContext2D::Stroke()
2219 : {
2220 0 : gfxRect dirty;
2221 0 : nsresult rv = DrawPath(STYLE_STROKE, &dirty);
2222 0 : if (NS_FAILED(rv))
2223 0 : return rv;
2224 0 : return RedrawUser(dirty);
2225 : }
2226 :
2227 : NS_IMETHODIMP
2228 0 : nsCanvasRenderingContext2D::Clip()
2229 : {
2230 0 : if (!EnsureSurface())
2231 0 : return NS_ERROR_FAILURE;
2232 :
2233 0 : mThebes->Clip();
2234 0 : return NS_OK;
2235 : }
2236 :
2237 : NS_IMETHODIMP
2238 0 : nsCanvasRenderingContext2D::MoveTo(float x, float y)
2239 : {
2240 0 : if (!EnsureSurface())
2241 0 : return NS_ERROR_FAILURE;
2242 :
2243 0 : if (!FloatValidate(x,y))
2244 0 : return NS_OK;
2245 :
2246 0 : mHasPath = true;
2247 0 : mThebes->MoveTo(gfxPoint(x, y));
2248 0 : return NS_OK;
2249 : }
2250 :
2251 : NS_IMETHODIMP
2252 0 : nsCanvasRenderingContext2D::LineTo(float x, float y)
2253 : {
2254 0 : if (!EnsureSurface())
2255 0 : return NS_ERROR_FAILURE;
2256 :
2257 0 : if (!FloatValidate(x,y))
2258 0 : return NS_OK;
2259 :
2260 0 : mHasPath = true;
2261 0 : mThebes->LineTo(gfxPoint(x, y));
2262 0 : return NS_OK;
2263 : }
2264 :
2265 : NS_IMETHODIMP
2266 0 : nsCanvasRenderingContext2D::QuadraticCurveTo(float cpx, float cpy, float x, float y)
2267 : {
2268 0 : if (!EnsureSurface())
2269 0 : return NS_ERROR_FAILURE;
2270 :
2271 0 : if (!FloatValidate(cpx,cpy,x,y))
2272 0 : return NS_OK;
2273 :
2274 : // we will always have a current point, since beginPath forces
2275 : // a moveto(0,0)
2276 0 : gfxPoint c = mThebes->CurrentPoint();
2277 0 : gfxPoint p(x,y);
2278 0 : gfxPoint cp(cpx, cpy);
2279 :
2280 0 : mHasPath = true;
2281 0 : mThebes->CurveTo((c+cp*2)/3.0, (p+cp*2)/3.0, p);
2282 :
2283 0 : return NS_OK;
2284 : }
2285 :
2286 : NS_IMETHODIMP
2287 0 : nsCanvasRenderingContext2D::BezierCurveTo(float cp1x, float cp1y,
2288 : float cp2x, float cp2y,
2289 : float x, float y)
2290 : {
2291 0 : if (!EnsureSurface())
2292 0 : return NS_ERROR_FAILURE;
2293 :
2294 0 : if (!FloatValidate(cp1x,cp1y,cp2x,cp2y,x,y))
2295 0 : return NS_OK;
2296 :
2297 0 : mHasPath = true;
2298 : mThebes->CurveTo(gfxPoint(cp1x, cp1y),
2299 : gfxPoint(cp2x, cp2y),
2300 0 : gfxPoint(x, y));
2301 :
2302 0 : return NS_OK;
2303 : }
2304 :
2305 : NS_IMETHODIMP
2306 0 : nsCanvasRenderingContext2D::ArcTo(float x1, float y1, float x2, float y2, float radius)
2307 : {
2308 0 : if (!EnsureSurface())
2309 0 : return NS_ERROR_FAILURE;
2310 :
2311 0 : if (!FloatValidate(x1,y1,x2,y2,radius))
2312 0 : return NS_OK;
2313 :
2314 0 : if (radius < 0)
2315 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
2316 :
2317 0 : mHasPath = true;
2318 :
2319 0 : gfxPoint p0 = mThebes->CurrentPoint();
2320 :
2321 : double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
2322 : bool anticlockwise;
2323 :
2324 0 : if ((x1 == p0.x && y1 == p0.y) || (x1 == x2 && y1 == y2) || radius == 0) {
2325 0 : mThebes->LineTo(gfxPoint(x1, y1));
2326 0 : return NS_OK;
2327 : }
2328 :
2329 0 : dir = (x2-x1)*(p0.y-y1) + (y2-y1)*(x1-p0.x);
2330 0 : if (dir == 0) {
2331 0 : mThebes->LineTo(gfxPoint(x1, y1));
2332 0 : return NS_OK;
2333 : }
2334 :
2335 0 : a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
2336 0 : b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
2337 0 : c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
2338 0 : cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
2339 :
2340 0 : sinx = sqrt(1 - cosx*cosx);
2341 0 : d = radius / ((1 - cosx) / sinx);
2342 :
2343 0 : anx = (x1-p0.x) / sqrt(a2);
2344 0 : any = (y1-p0.y) / sqrt(a2);
2345 0 : bnx = (x1-x2) / sqrt(b2);
2346 0 : bny = (y1-y2) / sqrt(b2);
2347 0 : x3 = x1 - anx*d;
2348 0 : y3 = y1 - any*d;
2349 0 : x4 = x1 - bnx*d;
2350 0 : y4 = y1 - bny*d;
2351 0 : anticlockwise = (dir < 0);
2352 0 : cx = x3 + any*radius*(anticlockwise ? 1 : -1);
2353 0 : cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
2354 0 : angle0 = atan2((y3-cy), (x3-cx));
2355 0 : angle1 = atan2((y4-cy), (x4-cx));
2356 :
2357 0 : mThebes->LineTo(gfxPoint(x3, y3));
2358 :
2359 0 : if (anticlockwise)
2360 0 : mThebes->NegativeArc(gfxPoint(cx, cy), radius, angle0, angle1);
2361 : else
2362 0 : mThebes->Arc(gfxPoint(cx, cy), radius, angle0, angle1);
2363 :
2364 0 : return NS_OK;
2365 : }
2366 :
2367 : NS_IMETHODIMP
2368 0 : nsCanvasRenderingContext2D::Arc(float x, float y, float r, float startAngle, float endAngle, bool ccw)
2369 : {
2370 0 : if (!EnsureSurface())
2371 0 : return NS_ERROR_FAILURE;
2372 :
2373 0 : if (!FloatValidate(x,y,r,startAngle,endAngle))
2374 0 : return NS_OK;
2375 :
2376 0 : if (r < 0.0)
2377 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
2378 :
2379 0 : gfxPoint p(x,y);
2380 :
2381 0 : mHasPath = true;
2382 0 : if (ccw)
2383 0 : mThebes->NegativeArc(p, r, startAngle, endAngle);
2384 : else
2385 0 : mThebes->Arc(p, r, startAngle, endAngle);
2386 0 : return NS_OK;
2387 : }
2388 :
2389 : NS_IMETHODIMP
2390 0 : nsCanvasRenderingContext2D::Rect(float x, float y, float w, float h)
2391 : {
2392 0 : if (!EnsureSurface())
2393 0 : return NS_ERROR_FAILURE;
2394 :
2395 0 : if (!FloatValidate(x,y,w,h))
2396 0 : return NS_OK;
2397 :
2398 0 : mHasPath = true;
2399 0 : mThebes->Rectangle(gfxRect(x, y, w, h));
2400 0 : return NS_OK;
2401 : }
2402 :
2403 : //
2404 : // text
2405 : //
2406 :
2407 : /**
2408 : * Helper function for SetFont that creates a style rule for the given font.
2409 : * @param aFont The CSS font string
2410 : * @param aNode The canvas element
2411 : * @param aResult Pointer in which to place the new style rule.
2412 : * @remark Assumes all pointer arguments are non-null.
2413 : */
2414 : static nsresult
2415 0 : CreateFontStyleRule(const nsAString& aFont,
2416 : nsINode* aNode,
2417 : css::StyleRule** aResult)
2418 : {
2419 0 : nsRefPtr<css::StyleRule> rule;
2420 : bool changed;
2421 :
2422 0 : nsIPrincipal* principal = aNode->NodePrincipal();
2423 0 : nsIDocument* document = aNode->OwnerDoc();
2424 :
2425 0 : nsIURI* docURL = document->GetDocumentURI();
2426 0 : nsIURI* baseURL = document->GetDocBaseURI();
2427 :
2428 : // Pass the CSS Loader object to the parser, to allow parser error reports
2429 : // to include the outer window ID.
2430 0 : nsCSSParser parser(document->CSSLoader());
2431 :
2432 0 : nsresult rv = parser.ParseStyleAttribute(EmptyString(), docURL, baseURL,
2433 0 : principal, getter_AddRefs(rule));
2434 0 : if (NS_FAILED(rv))
2435 0 : return rv;
2436 :
2437 : rv = parser.ParseProperty(eCSSProperty_font, aFont, docURL, baseURL,
2438 : principal, rule->GetDeclaration(), &changed,
2439 0 : false);
2440 0 : if (NS_FAILED(rv))
2441 0 : return rv;
2442 :
2443 : rv = parser.ParseProperty(eCSSProperty_line_height,
2444 0 : NS_LITERAL_STRING("normal"), docURL, baseURL,
2445 : principal, rule->GetDeclaration(), &changed,
2446 0 : false);
2447 0 : if (NS_FAILED(rv))
2448 0 : return rv;
2449 :
2450 0 : rule->RuleMatched();
2451 :
2452 0 : rule.forget(aResult);
2453 0 : return NS_OK;
2454 : }
2455 :
2456 : NS_IMETHODIMP
2457 0 : nsCanvasRenderingContext2D::SetFont(const nsAString& font)
2458 : {
2459 : nsresult rv;
2460 :
2461 : /*
2462 : * If font is defined with relative units (e.g. ems) and the parent
2463 : * style context changes in between calls, setting the font to the
2464 : * same value as previous could result in a different computed value,
2465 : * so we cannot have the optimization where we check if the new font
2466 : * string is equal to the old one.
2467 : */
2468 :
2469 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
2470 0 : if (!content && !mDocShell) {
2471 0 : NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");
2472 0 : return NS_ERROR_FAILURE;
2473 : }
2474 :
2475 0 : nsIPresShell* presShell = GetPresShell();
2476 0 : if (!presShell)
2477 0 : return NS_ERROR_FAILURE;
2478 0 : nsIDocument* document = presShell->GetDocument();
2479 :
2480 0 : nsCOMArray<nsIStyleRule> rules;
2481 :
2482 0 : nsRefPtr<css::StyleRule> rule;
2483 0 : rv = CreateFontStyleRule(font, document, getter_AddRefs(rule));
2484 0 : if (NS_FAILED(rv))
2485 0 : return rv;
2486 :
2487 0 : css::Declaration *declaration = rule->GetDeclaration();
2488 : // The easiest way to see whether we got a syntax error or whether
2489 : // we got 'inherit' or 'initial' is to look at font-size-adjust,
2490 : // which the shorthand resets to either 'none' or
2491 : // '-moz-system-font'.
2492 : // We know the declaration is not !important, so we can use
2493 : // GetNormalBlock().
2494 : const nsCSSValue *fsaVal =
2495 0 : declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
2496 0 : if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
2497 0 : fsaVal->GetUnit() != eCSSUnit_System_Font)) {
2498 : // We got an all-property value or a syntax error. The spec says
2499 : // this value must be ignored.
2500 0 : return NS_OK;
2501 : }
2502 :
2503 0 : rules.AppendObject(rule);
2504 :
2505 0 : nsStyleSet* styleSet = presShell->StyleSet();
2506 :
2507 : // have to get a parent style context for inherit-like relative
2508 : // values (2em, bolder, etc.)
2509 0 : nsRefPtr<nsStyleContext> parentContext;
2510 :
2511 0 : if (content && content->IsInDoc()) {
2512 : // inherit from the canvas element
2513 : parentContext = nsComputedDOMStyle::GetStyleContextForElement(
2514 0 : content->AsElement(),
2515 : nsnull,
2516 0 : presShell);
2517 : } else {
2518 : // otherwise inherit from default
2519 0 : nsRefPtr<css::StyleRule> parentRule;
2520 : rv = CreateFontStyleRule(kDefaultFontStyle,
2521 : document,
2522 0 : getter_AddRefs(parentRule));
2523 0 : if (NS_FAILED(rv))
2524 0 : return rv;
2525 0 : nsCOMArray<nsIStyleRule> parentRules;
2526 0 : parentRules.AppendObject(parentRule);
2527 0 : parentContext = styleSet->ResolveStyleForRules(nsnull, parentRules);
2528 : }
2529 :
2530 0 : if (!parentContext)
2531 0 : return NS_ERROR_FAILURE;
2532 :
2533 : nsRefPtr<nsStyleContext> sc =
2534 0 : styleSet->ResolveStyleForRules(parentContext, rules);
2535 0 : if (!sc)
2536 0 : return NS_ERROR_FAILURE;
2537 0 : const nsStyleFont* fontStyle = sc->GetStyleFont();
2538 :
2539 0 : NS_ASSERTION(fontStyle, "Could not obtain font style");
2540 :
2541 0 : nsIAtom* language = sc->GetStyleFont()->mLanguage;
2542 0 : if (!language) {
2543 0 : language = presShell->GetPresContext()->GetLanguageFromCharset();
2544 : }
2545 :
2546 : // use CSS pixels instead of dev pixels to avoid being affected by page zoom
2547 0 : const PRUint32 aupcp = nsPresContext::AppUnitsPerCSSPixel();
2548 : // un-zoom the font size to avoid being affected by text-only zoom
2549 : //
2550 : // Purposely ignore the font size that respects the user's minimum
2551 : // font preference (fontStyle->mFont.size) in favor of the
2552 : // computed size (fontStyle->mSize). See
2553 : // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
2554 0 : const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mSize);
2555 :
2556 0 : bool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
2557 0 : presShell->GetPresContext()->Type() == nsPresContext::eContext_Print);
2558 :
2559 : gfxFontStyle style(fontStyle->mFont.style,
2560 : fontStyle->mFont.weight,
2561 : fontStyle->mFont.stretch,
2562 0 : NSAppUnitsToFloatPixels(fontSize, float(aupcp)),
2563 : language,
2564 : fontStyle->mFont.sizeAdjust,
2565 : fontStyle->mFont.systemFont,
2566 : printerFont,
2567 : fontStyle->mFont.featureSettings,
2568 0 : fontStyle->mFont.languageOverride);
2569 :
2570 0 : CurrentState().fontGroup =
2571 0 : gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
2572 : &style,
2573 0 : presShell->GetPresContext()->GetUserFontSet());
2574 0 : NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
2575 :
2576 : // The font getter is required to be reserialized based on what we
2577 : // parsed (including having line-height removed). (Older drafts of
2578 : // the spec required font sizes be converted to pixels, but that no
2579 : // longer seems to be required.)
2580 0 : declaration->GetValue(eCSSProperty_font, CurrentState().font);
2581 :
2582 0 : return NS_OK;
2583 : }
2584 :
2585 : NS_IMETHODIMP
2586 0 : nsCanvasRenderingContext2D::GetFont(nsAString& font)
2587 : {
2588 : /* will initilize the value if not set, else does nothing */
2589 0 : GetCurrentFontStyle();
2590 :
2591 0 : font = CurrentState().font;
2592 0 : return NS_OK;
2593 : }
2594 :
2595 : NS_IMETHODIMP
2596 0 : nsCanvasRenderingContext2D::SetTextAlign(const nsAString& ta)
2597 : {
2598 0 : if (ta.EqualsLiteral("start"))
2599 0 : CurrentState().textAlign = TEXT_ALIGN_START;
2600 0 : else if (ta.EqualsLiteral("end"))
2601 0 : CurrentState().textAlign = TEXT_ALIGN_END;
2602 0 : else if (ta.EqualsLiteral("left"))
2603 0 : CurrentState().textAlign = TEXT_ALIGN_LEFT;
2604 0 : else if (ta.EqualsLiteral("right"))
2605 0 : CurrentState().textAlign = TEXT_ALIGN_RIGHT;
2606 0 : else if (ta.EqualsLiteral("center"))
2607 0 : CurrentState().textAlign = TEXT_ALIGN_CENTER;
2608 :
2609 0 : return NS_OK;
2610 : }
2611 :
2612 : NS_IMETHODIMP
2613 0 : nsCanvasRenderingContext2D::GetTextAlign(nsAString& ta)
2614 : {
2615 0 : switch (CurrentState().textAlign)
2616 : {
2617 : case TEXT_ALIGN_START:
2618 0 : ta.AssignLiteral("start");
2619 0 : break;
2620 : case TEXT_ALIGN_END:
2621 0 : ta.AssignLiteral("end");
2622 0 : break;
2623 : case TEXT_ALIGN_LEFT:
2624 0 : ta.AssignLiteral("left");
2625 0 : break;
2626 : case TEXT_ALIGN_RIGHT:
2627 0 : ta.AssignLiteral("right");
2628 0 : break;
2629 : case TEXT_ALIGN_CENTER:
2630 0 : ta.AssignLiteral("center");
2631 0 : break;
2632 : }
2633 :
2634 0 : return NS_OK;
2635 : }
2636 :
2637 : NS_IMETHODIMP
2638 0 : nsCanvasRenderingContext2D::SetTextBaseline(const nsAString& tb)
2639 : {
2640 0 : if (tb.EqualsLiteral("top"))
2641 0 : CurrentState().textBaseline = TEXT_BASELINE_TOP;
2642 0 : else if (tb.EqualsLiteral("hanging"))
2643 0 : CurrentState().textBaseline = TEXT_BASELINE_HANGING;
2644 0 : else if (tb.EqualsLiteral("middle"))
2645 0 : CurrentState().textBaseline = TEXT_BASELINE_MIDDLE;
2646 0 : else if (tb.EqualsLiteral("alphabetic"))
2647 0 : CurrentState().textBaseline = TEXT_BASELINE_ALPHABETIC;
2648 0 : else if (tb.EqualsLiteral("ideographic"))
2649 0 : CurrentState().textBaseline = TEXT_BASELINE_IDEOGRAPHIC;
2650 0 : else if (tb.EqualsLiteral("bottom"))
2651 0 : CurrentState().textBaseline = TEXT_BASELINE_BOTTOM;
2652 :
2653 0 : return NS_OK;
2654 : }
2655 :
2656 : NS_IMETHODIMP
2657 0 : nsCanvasRenderingContext2D::GetTextBaseline(nsAString& tb)
2658 : {
2659 0 : switch (CurrentState().textBaseline)
2660 : {
2661 : case TEXT_BASELINE_TOP:
2662 0 : tb.AssignLiteral("top");
2663 0 : break;
2664 : case TEXT_BASELINE_HANGING:
2665 0 : tb.AssignLiteral("hanging");
2666 0 : break;
2667 : case TEXT_BASELINE_MIDDLE:
2668 0 : tb.AssignLiteral("middle");
2669 0 : break;
2670 : case TEXT_BASELINE_ALPHABETIC:
2671 0 : tb.AssignLiteral("alphabetic");
2672 0 : break;
2673 : case TEXT_BASELINE_IDEOGRAPHIC:
2674 0 : tb.AssignLiteral("ideographic");
2675 0 : break;
2676 : case TEXT_BASELINE_BOTTOM:
2677 0 : tb.AssignLiteral("bottom");
2678 0 : break;
2679 : }
2680 :
2681 0 : return NS_OK;
2682 : }
2683 :
2684 : /*
2685 : * Helper function that replaces the whitespace characters in a string
2686 : * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
2687 : * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
2688 : * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
2689 : * @param str The string whose whitespace characters to replace.
2690 : */
2691 : static inline void
2692 0 : TextReplaceWhitespaceCharacters(nsAutoString& str)
2693 : {
2694 0 : str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
2695 0 : }
2696 :
2697 : NS_IMETHODIMP
2698 0 : nsCanvasRenderingContext2D::FillText(const nsAString& text, float x, float y, float maxWidth)
2699 : {
2700 0 : return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_FILL, nsnull);
2701 : }
2702 :
2703 : NS_IMETHODIMP
2704 0 : nsCanvasRenderingContext2D::StrokeText(const nsAString& text, float x, float y, float maxWidth)
2705 : {
2706 0 : return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_STROKE, nsnull);
2707 : }
2708 :
2709 : NS_IMETHODIMP
2710 0 : nsCanvasRenderingContext2D::MeasureText(const nsAString& rawText,
2711 : nsIDOMTextMetrics** _retval)
2712 : {
2713 : float width;
2714 :
2715 0 : nsresult rv = DrawOrMeasureText(rawText, 0, 0, 0, TEXT_DRAW_OPERATION_MEASURE, &width);
2716 :
2717 0 : if (NS_FAILED(rv))
2718 0 : return rv;
2719 :
2720 0 : nsRefPtr<nsIDOMTextMetrics> textMetrics = new nsTextMetrics(width);
2721 0 : if (!textMetrics.get())
2722 0 : return NS_ERROR_OUT_OF_MEMORY;
2723 :
2724 0 : *_retval = textMetrics.forget().get();
2725 :
2726 0 : return NS_OK;
2727 : }
2728 :
2729 : /**
2730 : * Used for nsBidiPresUtils::ProcessText
2731 : */
2732 : struct NS_STACK_CLASS nsCanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
2733 0 : {
2734 0 : virtual void SetText(const PRUnichar* text, PRInt32 length, nsBidiDirection direction)
2735 : {
2736 0 : mFontgrp->UpdateFontList(); // ensure user font generation is current
2737 : mTextRun = mFontgrp->MakeTextRun(text,
2738 : length,
2739 : mThebes,
2740 : mAppUnitsPerDevPixel,
2741 0 : direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
2742 0 : }
2743 :
2744 0 : virtual nscoord GetWidth()
2745 : {
2746 : gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
2747 : mTextRun->GetLength(),
2748 : mDoMeasureBoundingBox ?
2749 : gfxFont::TIGHT_INK_EXTENTS :
2750 : gfxFont::LOOSE_INK_EXTENTS,
2751 : mThebes,
2752 0 : nsnull);
2753 :
2754 : // this only measures the height; the total width is gotten from the
2755 : // the return value of ProcessText.
2756 0 : if (mDoMeasureBoundingBox) {
2757 0 : textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
2758 0 : mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
2759 : }
2760 :
2761 0 : return NSToCoordRound(textRunMetrics.mAdvanceWidth);
2762 : }
2763 :
2764 0 : virtual void DrawText(nscoord xOffset, nscoord width)
2765 : {
2766 0 : gfxPoint point = mPt;
2767 0 : point.x += xOffset;
2768 :
2769 : // offset is given in terms of left side of string
2770 0 : if (mTextRun->IsRightToLeft()) {
2771 : // Bug 581092 - don't use rounded pixel width to advance to
2772 : // right-hand end of run, because this will cause different
2773 : // glyph positioning for LTR vs RTL drawing of the same
2774 : // glyph string on OS X and DWrite where textrun widths may
2775 : // involve fractional pixels.
2776 : gfxTextRun::Metrics textRunMetrics =
2777 : mTextRun->MeasureText(0,
2778 : mTextRun->GetLength(),
2779 : mDoMeasureBoundingBox ?
2780 : gfxFont::TIGHT_INK_EXTENTS :
2781 : gfxFont::LOOSE_INK_EXTENTS,
2782 : mThebes,
2783 0 : nsnull);
2784 0 : point.x += textRunMetrics.mAdvanceWidth;
2785 : // old code was:
2786 : // point.x += width * mAppUnitsPerDevPixel;
2787 : // TODO: restore this if/when we move to fractional coords
2788 : // throughout the text layout process
2789 : }
2790 :
2791 : mTextRun->Draw(mThebes,
2792 : point,
2793 : mOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE ?
2794 : gfxFont::GLYPH_STROKE : gfxFont::GLYPH_FILL,
2795 : 0,
2796 : mTextRun->GetLength(),
2797 : nsnull,
2798 : nsnull,
2799 0 : nsnull);
2800 0 : }
2801 :
2802 : // current text run
2803 : nsAutoPtr<gfxTextRun> mTextRun;
2804 :
2805 : // pointer to the context, may not be the canvas's context
2806 : // if an intermediate surface is being used
2807 : gfxContext* mThebes;
2808 :
2809 : // position of the left side of the string, alphabetic baseline
2810 : gfxPoint mPt;
2811 :
2812 : // current font
2813 : gfxFontGroup* mFontgrp;
2814 :
2815 : // dev pixel conversion factor
2816 : PRUint32 mAppUnitsPerDevPixel;
2817 :
2818 : // operation (fill or stroke)
2819 : nsCanvasRenderingContext2D::TextDrawOperation mOp;
2820 :
2821 : // union of bounding boxes of all runs, needed for shadows
2822 : gfxRect mBoundingBox;
2823 :
2824 : // true iff the bounding box should be measured
2825 : bool mDoMeasureBoundingBox;
2826 : };
2827 :
2828 : nsresult
2829 0 : nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
2830 : float aX,
2831 : float aY,
2832 : float aMaxWidth,
2833 : TextDrawOperation aOp,
2834 : float* aWidth)
2835 : {
2836 : nsresult rv;
2837 :
2838 0 : if (!FloatValidate(aX, aY, aMaxWidth))
2839 0 : return NS_ERROR_DOM_SYNTAX_ERR;
2840 :
2841 : // spec isn't clear on what should happen if aMaxWidth <= 0, so
2842 : // treat it as an invalid argument
2843 : // technically, 0 should be an invalid value as well, but 0 is the default
2844 : // arg, and there is no way to tell if the default was used
2845 0 : if (aMaxWidth < 0)
2846 0 : return NS_ERROR_INVALID_ARG;
2847 :
2848 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
2849 0 : if (!content && !mDocShell) {
2850 0 : NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");
2851 0 : return NS_ERROR_FAILURE;
2852 : }
2853 :
2854 0 : nsIPresShell* presShell = GetPresShell();
2855 0 : if (!presShell)
2856 0 : return NS_ERROR_FAILURE;
2857 :
2858 0 : nsIDocument* document = presShell->GetDocument();
2859 :
2860 : // replace all the whitespace characters with U+0020 SPACE
2861 0 : nsAutoString textToDraw(aRawText);
2862 0 : TextReplaceWhitespaceCharacters(textToDraw);
2863 :
2864 : // for now, default to ltr if not in doc
2865 0 : bool isRTL = false;
2866 :
2867 0 : if (content && content->IsInDoc()) {
2868 : // try to find the closest context
2869 : nsRefPtr<nsStyleContext> canvasStyle =
2870 0 : nsComputedDOMStyle::GetStyleContextForElement(content->AsElement(),
2871 : nsnull,
2872 0 : presShell);
2873 0 : if (!canvasStyle)
2874 0 : return NS_ERROR_FAILURE;
2875 0 : isRTL = canvasStyle->GetStyleVisibility()->mDirection ==
2876 0 : NS_STYLE_DIRECTION_RTL;
2877 : } else {
2878 0 : isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
2879 : }
2880 :
2881 0 : Style style = aOp == TEXT_DRAW_OPERATION_FILL ? STYLE_FILL : STYLE_STROKE;
2882 :
2883 0 : bool doDrawShadow = NeedToDrawShadow();
2884 0 : bool doUseIntermediateSurface = NeedToUseIntermediateSurface()
2885 0 : || NeedIntermediateSurfaceToHandleGlobalAlpha(style);
2886 :
2887 : // Clear the surface if we need to simulate unbounded SOURCE operator
2888 0 : ClearSurfaceForUnboundedSource();
2889 :
2890 0 : nsCanvasBidiProcessor processor;
2891 :
2892 0 : GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, NULL);
2893 0 : processor.mPt = gfxPoint(aX, aY);
2894 0 : nsRefPtr<nsRenderingContext> ctx;
2895 0 : if (mThebes) {
2896 0 : processor.mThebes = mThebes;
2897 : } else {
2898 0 : ctx = presShell->GetReferenceRenderingContext();
2899 0 : processor.mThebes = ctx->ThebesContext();
2900 : }
2901 0 : processor.mOp = aOp;
2902 0 : processor.mBoundingBox = gfxRect(0, 0, 0, 0);
2903 0 : processor.mDoMeasureBoundingBox = doDrawShadow || !mIsEntireFrameInvalid;
2904 :
2905 0 : processor.mFontgrp = GetCurrentFontStyle();
2906 0 : NS_ASSERTION(processor.mFontgrp, "font group is null");
2907 :
2908 : nscoord totalWidthCoord;
2909 :
2910 : // calls bidi algo twice since it needs the full text width and the
2911 : // bounding boxes before rendering anything
2912 0 : nsBidi bidiEngine;
2913 : rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
2914 0 : textToDraw.Length(),
2915 : isRTL ? NSBIDI_RTL : NSBIDI_LTR,
2916 : presShell->GetPresContext(),
2917 : processor,
2918 : nsBidiPresUtils::MODE_MEASURE,
2919 : nsnull,
2920 : 0,
2921 : &totalWidthCoord,
2922 0 : &bidiEngine);
2923 0 : if (NS_FAILED(rv))
2924 0 : return rv;
2925 :
2926 0 : float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
2927 0 : if (aWidth)
2928 0 : *aWidth = totalWidth;
2929 :
2930 : // if only measuring, don't need to do any more work
2931 0 : if (aOp==TEXT_DRAW_OPERATION_MEASURE)
2932 0 : return NS_OK;
2933 :
2934 0 : if (!EnsureSurface())
2935 0 : return NS_ERROR_FAILURE;
2936 :
2937 0 : processor.mThebes = mThebes;
2938 :
2939 : // offset pt.x based on text align
2940 : gfxFloat anchorX;
2941 :
2942 0 : if (CurrentState().textAlign == TEXT_ALIGN_CENTER)
2943 0 : anchorX = .5;
2944 0 : else if (CurrentState().textAlign == TEXT_ALIGN_LEFT ||
2945 0 : (!isRTL && CurrentState().textAlign == TEXT_ALIGN_START) ||
2946 0 : (isRTL && CurrentState().textAlign == TEXT_ALIGN_END))
2947 0 : anchorX = 0;
2948 : else
2949 0 : anchorX = 1;
2950 :
2951 0 : processor.mPt.x -= anchorX * totalWidth;
2952 :
2953 : // offset pt.y based on text baseline
2954 0 : NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
2955 0 : const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
2956 :
2957 : gfxFloat anchorY;
2958 :
2959 0 : switch (CurrentState().textBaseline)
2960 : {
2961 : case TEXT_BASELINE_HANGING:
2962 : // fall through; best we can do with the information available
2963 : case TEXT_BASELINE_TOP:
2964 0 : anchorY = fontMetrics.emAscent;
2965 0 : break;
2966 : break;
2967 : case TEXT_BASELINE_MIDDLE:
2968 0 : anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
2969 0 : break;
2970 : case TEXT_BASELINE_IDEOGRAPHIC:
2971 : // fall through; best we can do with the information available
2972 : case TEXT_BASELINE_ALPHABETIC:
2973 0 : anchorY = 0;
2974 0 : break;
2975 : case TEXT_BASELINE_BOTTOM:
2976 0 : anchorY = -fontMetrics.emDescent;
2977 0 : break;
2978 : }
2979 :
2980 0 : processor.mPt.y += anchorY;
2981 :
2982 : // correct bounding box to get it to be the correct size/position
2983 0 : processor.mBoundingBox.width = totalWidth;
2984 0 : processor.mBoundingBox.MoveBy(processor.mPt);
2985 :
2986 0 : processor.mPt.x *= processor.mAppUnitsPerDevPixel;
2987 0 : processor.mPt.y *= processor.mAppUnitsPerDevPixel;
2988 :
2989 : // if text is over aMaxWidth, then scale the text horizontally such that its
2990 : // width is precisely aMaxWidth
2991 0 : gfxContextAutoSaveRestore autoSR;
2992 0 : if (aMaxWidth > 0 && totalWidth > aMaxWidth) {
2993 0 : autoSR.SetContext(mThebes);
2994 : // translate the anchor point to 0, then scale and translate back
2995 0 : gfxPoint trans(aX, 0);
2996 0 : mThebes->Translate(trans);
2997 0 : mThebes->Scale(aMaxWidth/totalWidth, 1);
2998 0 : mThebes->Translate(-trans);
2999 : }
3000 :
3001 : // save the previous bounding box
3002 0 : gfxRect boundingBox = processor.mBoundingBox;
3003 :
3004 : // don't ever need to measure the bounding box twice
3005 0 : processor.mDoMeasureBoundingBox = false;
3006 :
3007 0 : if (doDrawShadow) {
3008 : // for some reason the box is too tight, probably rounding error
3009 0 : processor.mBoundingBox.Inflate(2.0);
3010 :
3011 : // this is unnecessarily big is max-width scaling is involved, but it
3012 : // will still produce correct output
3013 0 : gfxRect drawExtents = mThebes->UserToDevice(processor.mBoundingBox);
3014 0 : gfxAlphaBoxBlur blur;
3015 :
3016 0 : gfxContext* ctx = ShadowInitialize(drawExtents, blur);
3017 :
3018 0 : if (ctx) {
3019 0 : ApplyStyle(style, false);
3020 0 : CopyContext(ctx, mThebes);
3021 0 : ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
3022 0 : processor.mThebes = ctx;
3023 :
3024 : rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
3025 0 : textToDraw.Length(),
3026 : isRTL ? NSBIDI_RTL : NSBIDI_LTR,
3027 : presShell->GetPresContext(),
3028 : processor,
3029 : nsBidiPresUtils::MODE_DRAW,
3030 : nsnull,
3031 : 0,
3032 : nsnull,
3033 0 : &bidiEngine);
3034 0 : if (NS_FAILED(rv))
3035 0 : return rv;
3036 :
3037 0 : ShadowFinalize(blur);
3038 : }
3039 :
3040 0 : processor.mThebes = mThebes;
3041 : }
3042 :
3043 0 : gfxContextPathAutoSaveRestore pathSR(mThebes, false);
3044 :
3045 0 : if (doUseIntermediateSurface) {
3046 0 : mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
3047 :
3048 : // don't want operators to be applied twice
3049 0 : mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);
3050 : }
3051 :
3052 0 : ApplyStyle(style);
3053 :
3054 : rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
3055 0 : textToDraw.Length(),
3056 : isRTL ? NSBIDI_RTL : NSBIDI_LTR,
3057 : presShell->GetPresContext(),
3058 : processor,
3059 : nsBidiPresUtils::MODE_DRAW,
3060 : nsnull,
3061 : 0,
3062 : nsnull,
3063 0 : &bidiEngine);
3064 :
3065 : // this needs to be restored before function can return
3066 0 : if (doUseIntermediateSurface) {
3067 0 : mThebes->PopGroupToSource();
3068 0 : DirtyAllStyles();
3069 : }
3070 :
3071 0 : if (NS_FAILED(rv))
3072 0 : return rv;
3073 :
3074 0 : if (doUseIntermediateSurface)
3075 0 : mThebes->Paint(CurrentState().StyleIsColor(style) ? 1.0 : CurrentState().globalAlpha);
3076 :
3077 0 : if (aOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL && !doDrawShadow)
3078 0 : return RedrawUser(boundingBox);
3079 :
3080 0 : return Redraw();
3081 : }
3082 :
3083 : NS_IMETHODIMP
3084 0 : nsCanvasRenderingContext2D::SetMozTextStyle(const nsAString& textStyle)
3085 : {
3086 : // font and mozTextStyle are the same value
3087 0 : return SetFont(textStyle);
3088 : }
3089 :
3090 : NS_IMETHODIMP
3091 0 : nsCanvasRenderingContext2D::GetMozTextStyle(nsAString& textStyle)
3092 : {
3093 : // font and mozTextStyle are the same value
3094 0 : return GetFont(textStyle);
3095 : }
3096 :
3097 : gfxFontGroup*
3098 0 : nsCanvasRenderingContext2D::GetCurrentFontStyle()
3099 : {
3100 : // use lazy initilization for the font group since it's rather expensive
3101 0 : if(!CurrentState().fontGroup) {
3102 0 : nsresult rv = SetMozTextStyle(kDefaultFontStyle);
3103 0 : if (NS_FAILED(rv)) {
3104 0 : gfxFontStyle style;
3105 0 : style.size = kDefaultFontSize;
3106 0 : CurrentState().fontGroup =
3107 0 : gfxPlatform::GetPlatform()->CreateFontGroup(kDefaultFontName,
3108 : &style,
3109 0 : nsnull);
3110 0 : if (CurrentState().fontGroup) {
3111 0 : CurrentState().font = kDefaultFontStyle;
3112 0 : rv = NS_OK;
3113 : } else {
3114 0 : rv = NS_ERROR_OUT_OF_MEMORY;
3115 : }
3116 : }
3117 :
3118 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Default canvas font is invalid");
3119 : }
3120 :
3121 0 : return CurrentState().fontGroup;
3122 : }
3123 :
3124 : gfxTextRun*
3125 0 : nsCanvasRenderingContext2D::MakeTextRun(const PRUnichar* aText,
3126 : PRUint32 aLength,
3127 : PRUint32 aAppUnitsPerDevUnit,
3128 : PRUint32 aFlags)
3129 : {
3130 0 : gfxFontGroup* currentFontStyle = GetCurrentFontStyle();
3131 0 : if (!currentFontStyle)
3132 0 : return nsnull;
3133 0 : currentFontStyle->UpdateFontList(); // ensure user font generation is current
3134 : return currentFontStyle->MakeTextRun(aText, aLength,
3135 0 : mThebes, aAppUnitsPerDevUnit, aFlags);
3136 : }
3137 :
3138 :
3139 : //
3140 : // line caps/joins
3141 : //
3142 : NS_IMETHODIMP
3143 0 : nsCanvasRenderingContext2D::SetLineWidth(float width)
3144 : {
3145 0 : if (!EnsureSurface())
3146 0 : return NS_ERROR_FAILURE;
3147 :
3148 0 : if (!FloatValidate(width) || width <= 0.0)
3149 0 : return NS_OK;
3150 :
3151 0 : mThebes->SetLineWidth(width);
3152 0 : return NS_OK;
3153 : }
3154 :
3155 : NS_IMETHODIMP
3156 0 : nsCanvasRenderingContext2D::GetLineWidth(float *width)
3157 : {
3158 0 : if (!EnsureSurface())
3159 0 : return NS_ERROR_FAILURE;
3160 :
3161 0 : gfxFloat d = mThebes->CurrentLineWidth();
3162 0 : *width = static_cast<float>(d);
3163 0 : return NS_OK;
3164 : }
3165 :
3166 : NS_IMETHODIMP
3167 0 : nsCanvasRenderingContext2D::SetLineCap(const nsAString& capstyle)
3168 : {
3169 0 : if (!EnsureSurface())
3170 0 : return NS_ERROR_FAILURE;
3171 :
3172 : gfxContext::GraphicsLineCap cap;
3173 :
3174 0 : if (capstyle.EqualsLiteral("butt"))
3175 0 : cap = gfxContext::LINE_CAP_BUTT;
3176 0 : else if (capstyle.EqualsLiteral("round"))
3177 0 : cap = gfxContext::LINE_CAP_ROUND;
3178 0 : else if (capstyle.EqualsLiteral("square"))
3179 0 : cap = gfxContext::LINE_CAP_SQUARE;
3180 : else
3181 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3182 0 : return NS_OK;
3183 :
3184 0 : mThebes->SetLineCap(cap);
3185 0 : return NS_OK;
3186 : }
3187 :
3188 : NS_IMETHODIMP
3189 0 : nsCanvasRenderingContext2D::GetLineCap(nsAString& capstyle)
3190 : {
3191 0 : if (!EnsureSurface())
3192 0 : return NS_ERROR_FAILURE;
3193 :
3194 0 : gfxContext::GraphicsLineCap cap = mThebes->CurrentLineCap();
3195 :
3196 0 : if (cap == gfxContext::LINE_CAP_BUTT)
3197 0 : capstyle.AssignLiteral("butt");
3198 0 : else if (cap == gfxContext::LINE_CAP_ROUND)
3199 0 : capstyle.AssignLiteral("round");
3200 0 : else if (cap == gfxContext::LINE_CAP_SQUARE)
3201 0 : capstyle.AssignLiteral("square");
3202 : else
3203 0 : return NS_ERROR_FAILURE;
3204 :
3205 0 : return NS_OK;
3206 : }
3207 :
3208 : NS_IMETHODIMP
3209 0 : nsCanvasRenderingContext2D::SetLineJoin(const nsAString& joinstyle)
3210 : {
3211 0 : if (!EnsureSurface())
3212 0 : return NS_ERROR_FAILURE;
3213 :
3214 : gfxContext::GraphicsLineJoin j;
3215 :
3216 0 : if (joinstyle.EqualsLiteral("round"))
3217 0 : j = gfxContext::LINE_JOIN_ROUND;
3218 0 : else if (joinstyle.EqualsLiteral("bevel"))
3219 0 : j = gfxContext::LINE_JOIN_BEVEL;
3220 0 : else if (joinstyle.EqualsLiteral("miter"))
3221 0 : j = gfxContext::LINE_JOIN_MITER;
3222 : else
3223 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3224 0 : return NS_OK;
3225 :
3226 0 : mThebes->SetLineJoin(j);
3227 0 : return NS_OK;
3228 : }
3229 :
3230 : NS_IMETHODIMP
3231 0 : nsCanvasRenderingContext2D::GetLineJoin(nsAString& joinstyle)
3232 : {
3233 0 : if (!EnsureSurface())
3234 0 : return NS_ERROR_FAILURE;
3235 :
3236 0 : gfxContext::GraphicsLineJoin j = mThebes->CurrentLineJoin();
3237 :
3238 0 : if (j == gfxContext::LINE_JOIN_ROUND)
3239 0 : joinstyle.AssignLiteral("round");
3240 0 : else if (j == gfxContext::LINE_JOIN_BEVEL)
3241 0 : joinstyle.AssignLiteral("bevel");
3242 0 : else if (j == gfxContext::LINE_JOIN_MITER)
3243 0 : joinstyle.AssignLiteral("miter");
3244 : else
3245 0 : return NS_ERROR_FAILURE;
3246 :
3247 0 : return NS_OK;
3248 : }
3249 :
3250 : NS_IMETHODIMP
3251 0 : nsCanvasRenderingContext2D::SetMiterLimit(float miter)
3252 : {
3253 0 : if (!EnsureSurface())
3254 0 : return NS_ERROR_FAILURE;
3255 :
3256 0 : if (!FloatValidate(miter) || miter <= 0.0)
3257 0 : return NS_OK;
3258 :
3259 0 : mThebes->SetMiterLimit(miter);
3260 0 : return NS_OK;
3261 : }
3262 :
3263 : NS_IMETHODIMP
3264 0 : nsCanvasRenderingContext2D::GetMiterLimit(float *miter)
3265 : {
3266 0 : if (!EnsureSurface())
3267 0 : return NS_ERROR_FAILURE;
3268 :
3269 0 : gfxFloat d = mThebes->CurrentMiterLimit();
3270 0 : *miter = static_cast<float>(d);
3271 0 : return NS_OK;
3272 : }
3273 :
3274 : NS_IMETHODIMP
3275 0 : nsCanvasRenderingContext2D::SetMozDash(JSContext *cx, const jsval& patternArray)
3276 : {
3277 0 : if (!EnsureSurface())
3278 0 : return NS_ERROR_FAILURE;
3279 :
3280 0 : AutoFallibleTArray<gfxFloat, 10> dashes;
3281 0 : nsresult rv = JSValToDashArray(cx, patternArray, dashes);
3282 0 : if (NS_SUCCEEDED(rv)) {
3283 0 : mThebes->SetDash(dashes.Elements(), dashes.Length(),
3284 0 : mThebes->CurrentDashOffset());
3285 : }
3286 0 : return rv;
3287 : }
3288 :
3289 : NS_IMETHODIMP
3290 0 : nsCanvasRenderingContext2D::GetMozDash(JSContext* cx, jsval* dashArray)
3291 : {
3292 0 : if (!EnsureSurface())
3293 0 : return NS_ERROR_FAILURE;
3294 :
3295 0 : AutoFallibleTArray<gfxFloat, 10> dashes;
3296 0 : if (!mThebes->CurrentDash(dashes, nsnull)) {
3297 0 : dashes.SetLength(0);
3298 : }
3299 0 : return DashArrayToJSVal(dashes, cx, dashArray);
3300 : }
3301 :
3302 : NS_IMETHODIMP
3303 0 : nsCanvasRenderingContext2D::SetMozDashOffset(float offset)
3304 : {
3305 0 : if (!EnsureSurface())
3306 0 : return NS_ERROR_FAILURE;
3307 :
3308 0 : if (!FloatValidate(offset)) {
3309 0 : return NS_ERROR_ILLEGAL_VALUE;
3310 : }
3311 :
3312 0 : AutoFallibleTArray<gfxFloat, 10> dashes;
3313 0 : if (!mThebes->CurrentDash(dashes, nsnull)) {
3314 : // Either no dash is set or the cairo call failed. Either
3315 : // way, eat the error.
3316 :
3317 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3318 0 : return NS_OK;
3319 : }
3320 0 : NS_ABORT_IF_FALSE(dashes.Length() > 0,
3321 : "CurrentDash() should have returned false");
3322 :
3323 0 : mThebes->SetDash(dashes.Elements(), dashes.Length(),
3324 0 : gfxFloat(offset));
3325 :
3326 0 : return NS_OK;
3327 : }
3328 :
3329 : NS_IMETHODIMP
3330 0 : nsCanvasRenderingContext2D::GetMozDashOffset(float* offset)
3331 : {
3332 0 : if (!EnsureSurface())
3333 0 : return NS_ERROR_FAILURE;
3334 :
3335 0 : *offset = float(mThebes->CurrentDashOffset());
3336 0 : return NS_OK;
3337 : }
3338 :
3339 : NS_IMETHODIMP
3340 0 : nsCanvasRenderingContext2D::IsPointInPath(float x, float y, bool *retVal)
3341 : {
3342 0 : if (!EnsureSurface())
3343 0 : return NS_ERROR_FAILURE;
3344 :
3345 0 : if (!FloatValidate(x,y)) {
3346 0 : *retVal = false;
3347 0 : return NS_OK;
3348 : }
3349 :
3350 0 : gfxPoint pt(x, y);
3351 0 : *retVal = mThebes->PointInFill(mThebes->DeviceToUser(pt));
3352 0 : return NS_OK;
3353 : }
3354 :
3355 : //
3356 : // image
3357 : //
3358 :
3359 : // drawImage(in HTMLImageElement image, in float dx, in float dy);
3360 : // -- render image from 0,0 at dx,dy top-left coords
3361 : // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
3362 : // -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
3363 : // 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);
3364 : // -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
3365 :
3366 : NS_IMETHODIMP
3367 0 : nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1,
3368 : float a2, float a3, float a4, float a5,
3369 : float a6, float a7, float a8,
3370 : PRUint8 optional_argc)
3371 : {
3372 0 : if (!EnsureSurface())
3373 0 : return NS_ERROR_FAILURE;
3374 :
3375 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(imgElt);
3376 0 : if (!content) {
3377 0 : return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
3378 : }
3379 :
3380 0 : nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(content);
3381 0 : if (canvas) {
3382 0 : nsIntSize size = canvas->GetSize();
3383 0 : if (size.width == 0 || size.height == 0) {
3384 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
3385 : }
3386 : }
3387 :
3388 0 : gfxMatrix matrix;
3389 0 : nsRefPtr<gfxPattern> pattern;
3390 0 : gfxIntSize imgSize;
3391 : nsRefPtr<gfxASurface> imgsurf =
3392 0 : CanvasImageCache::Lookup(imgElt, HTMLCanvasElement(), &imgSize);
3393 :
3394 0 : if (!imgsurf) {
3395 : // The canvas spec says that drawImage should draw the first frame
3396 : // of animated images
3397 0 : PRUint32 sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
3398 : nsLayoutUtils::SurfaceFromElementResult res =
3399 0 : nsLayoutUtils::SurfaceFromElement(content->AsElement(), sfeFlags);
3400 0 : if (!res.mSurface) {
3401 : // Spec says to silently do nothing if the element is still loading.
3402 0 : return res.mIsStillLoading ? NS_OK : NS_ERROR_NOT_AVAILABLE;
3403 : }
3404 :
3405 : // Force a copy if we're using drawImage with our destination
3406 : // as a source to work around some Cairo self-copy semantics issues.
3407 0 : if (res.mSurface == mSurface) {
3408 0 : sfeFlags |= nsLayoutUtils::SFE_WANT_NEW_SURFACE;
3409 0 : res = nsLayoutUtils::SurfaceFromElement(content->AsElement(),
3410 0 : sfeFlags);
3411 0 : if (!res.mSurface)
3412 0 : return NS_ERROR_NOT_AVAILABLE;
3413 : }
3414 :
3415 0 : imgsurf = res.mSurface.forget();
3416 0 : imgSize = res.mSize;
3417 :
3418 0 : if (mCanvasElement) {
3419 : CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
3420 : res.mPrincipal,
3421 : res.mIsWriteOnly,
3422 0 : res.mCORSUsed);
3423 : }
3424 :
3425 0 : if (res.mImageRequest) {
3426 : CanvasImageCache::NotifyDrawImage(imgElt, HTMLCanvasElement(),
3427 0 : res.mImageRequest, imgsurf, imgSize);
3428 : }
3429 : }
3430 :
3431 : double sx,sy,sw,sh;
3432 : double dx,dy,dw,dh;
3433 0 : if (optional_argc == 0) {
3434 0 : dx = a1;
3435 0 : dy = a2;
3436 0 : sx = sy = 0.0;
3437 0 : dw = sw = (double) imgSize.width;
3438 0 : dh = sh = (double) imgSize.height;
3439 0 : } else if (optional_argc == 2) {
3440 0 : dx = a1;
3441 0 : dy = a2;
3442 0 : dw = a3;
3443 0 : dh = a4;
3444 0 : sx = sy = 0.0;
3445 0 : sw = (double) imgSize.width;
3446 0 : sh = (double) imgSize.height;
3447 0 : } else if (optional_argc == 6) {
3448 0 : sx = a1;
3449 0 : sy = a2;
3450 0 : sw = a3;
3451 0 : sh = a4;
3452 0 : dx = a5;
3453 0 : dy = a6;
3454 0 : dw = a7;
3455 0 : dh = a8;
3456 : } else {
3457 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3458 0 : return NS_ERROR_INVALID_ARG;
3459 : }
3460 :
3461 0 : if (dw == 0.0 || dh == 0.0) {
3462 : // not really failure, but nothing to do --
3463 : // and noone likes a divide-by-zero
3464 0 : return NS_OK;
3465 : }
3466 :
3467 0 : if (!FloatValidate(sx, sy, sw, sh) || !FloatValidate(dx, dy, dw, dh)) {
3468 0 : return NS_OK;
3469 : }
3470 :
3471 : // check args
3472 0 : if (sx < 0.0 || sy < 0.0 ||
3473 : sw < 0.0 || sw > (double) imgSize.width ||
3474 : sh < 0.0 || sh > (double) imgSize.height ||
3475 : dw < 0.0 || dh < 0.0)
3476 : {
3477 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3478 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
3479 : }
3480 :
3481 0 : matrix.Translate(gfxPoint(sx, sy));
3482 0 : matrix.Scale(sw/dw, sh/dh);
3483 :
3484 0 : pattern = new gfxPattern(imgsurf);
3485 0 : pattern->SetMatrix(matrix);
3486 0 : pattern->SetExtend(gfxPattern::EXTEND_PAD);
3487 :
3488 0 : if (CurrentState().imageSmoothingEnabled)
3489 0 : pattern->SetFilter(gfxPattern::FILTER_GOOD);
3490 : else
3491 0 : pattern->SetFilter(gfxPattern::FILTER_NEAREST);
3492 :
3493 0 : PathAutoSaveRestore pathSR(this);
3494 :
3495 : // Clear the surface if we need to simulate unbounded SOURCE operator
3496 0 : ClearSurfaceForUnboundedSource();
3497 :
3498 : {
3499 0 : gfxContextMatrixAutoSaveRestore autoMatrixSR(mThebes);
3500 :
3501 0 : mThebes->Translate(gfxPoint(dx, dy));
3502 :
3503 0 : gfxRect clip(0, 0, dw, dh);
3504 :
3505 0 : if (NeedToDrawShadow()) {
3506 0 : gfxRect drawExtents = mThebes->UserToDevice(clip);
3507 0 : gfxAlphaBoxBlur blur;
3508 :
3509 0 : gfxContext* ctx = ShadowInitialize(drawExtents, blur);
3510 :
3511 0 : if (ctx) {
3512 0 : CopyContext(ctx, mThebes);
3513 0 : ctx->SetPattern(pattern);
3514 0 : ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
3515 0 : ctx->Clip(clip);
3516 0 : ctx->Paint();
3517 :
3518 0 : ShadowFinalize(blur);
3519 : }
3520 : }
3521 :
3522 0 : mThebes->SetPattern(pattern);
3523 0 : DirtyAllStyles();
3524 :
3525 0 : bool doUseIntermediateSurface = NeedToUseIntermediateSurface();
3526 0 : if (doUseIntermediateSurface) {
3527 0 : gfxContextAutoSaveRestore autoSR(mThebes);
3528 :
3529 : // draw onto a pushed group
3530 0 : mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
3531 0 : mThebes->Clip(clip);
3532 :
3533 : // don't want operators to be applied twice
3534 0 : mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);
3535 :
3536 0 : mThebes->Paint();
3537 0 : mThebes->PopGroupToSource();
3538 0 : mThebes->Paint(CurrentState().globalAlpha);
3539 0 : } else if (CurrentState().globalAlpha == 1.0f &&
3540 0 : mThebes->CurrentOperator() == gfxContext::OPERATOR_OVER) {
3541 : /* Direct2D isn't very good at clipping so use Fill() when we can */
3542 0 : mThebes->NewPath();
3543 0 : mThebes->Rectangle(clip);
3544 0 : mThebes->Fill();
3545 : } else {
3546 0 : gfxContextAutoSaveRestore autoSR(mThebes);
3547 :
3548 : /* we need to use to clip instead of fill for globalAlpha */
3549 0 : mThebes->Clip(clip);
3550 0 : mThebes->Paint(CurrentState().globalAlpha);
3551 : }
3552 :
3553 0 : RedrawUser(clip);
3554 : }
3555 :
3556 0 : return NS_OK;
3557 : }
3558 :
3559 : NS_IMETHODIMP
3560 0 : nsCanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& op)
3561 : {
3562 0 : if (!EnsureSurface())
3563 0 : return NS_ERROR_FAILURE;
3564 :
3565 : gfxContext::GraphicsOperator thebes_op;
3566 :
3567 : #define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \
3568 : if (op.EqualsLiteral(cvsop)) \
3569 : thebes_op = gfxContext::OPERATOR_##thebesop;
3570 :
3571 0 : CANVAS_OP_TO_THEBES_OP("copy", SOURCE)
3572 0 : else CANVAS_OP_TO_THEBES_OP("destination-atop", DEST_ATOP)
3573 0 : else CANVAS_OP_TO_THEBES_OP("destination-in", DEST_IN)
3574 0 : else CANVAS_OP_TO_THEBES_OP("destination-out", DEST_OUT)
3575 0 : else CANVAS_OP_TO_THEBES_OP("destination-over", DEST_OVER)
3576 0 : else CANVAS_OP_TO_THEBES_OP("lighter", ADD)
3577 0 : else CANVAS_OP_TO_THEBES_OP("source-atop", ATOP)
3578 0 : else CANVAS_OP_TO_THEBES_OP("source-in", IN)
3579 0 : else CANVAS_OP_TO_THEBES_OP("source-out", OUT)
3580 0 : else CANVAS_OP_TO_THEBES_OP("source-over", OVER)
3581 0 : else CANVAS_OP_TO_THEBES_OP("xor", XOR)
3582 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3583 0 : else return NS_OK;
3584 :
3585 : #undef CANVAS_OP_TO_THEBES_OP
3586 :
3587 0 : mThebes->SetOperator(thebes_op);
3588 0 : return NS_OK;
3589 : }
3590 :
3591 : NS_IMETHODIMP
3592 0 : nsCanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op)
3593 : {
3594 0 : if (!EnsureSurface())
3595 0 : return NS_ERROR_FAILURE;
3596 :
3597 0 : gfxContext::GraphicsOperator thebes_op = mThebes->CurrentOperator();
3598 :
3599 : #define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \
3600 : if (thebes_op == gfxContext::OPERATOR_##thebesop) \
3601 : op.AssignLiteral(cvsop);
3602 :
3603 0 : CANVAS_OP_TO_THEBES_OP("copy", SOURCE)
3604 0 : else CANVAS_OP_TO_THEBES_OP("destination-atop", DEST_ATOP)
3605 0 : else CANVAS_OP_TO_THEBES_OP("destination-in", DEST_IN)
3606 0 : else CANVAS_OP_TO_THEBES_OP("destination-out", DEST_OUT)
3607 0 : else CANVAS_OP_TO_THEBES_OP("destination-over", DEST_OVER)
3608 0 : else CANVAS_OP_TO_THEBES_OP("lighter", ADD)
3609 0 : else CANVAS_OP_TO_THEBES_OP("source-atop", ATOP)
3610 0 : else CANVAS_OP_TO_THEBES_OP("source-in", IN)
3611 0 : else CANVAS_OP_TO_THEBES_OP("source-out", OUT)
3612 0 : else CANVAS_OP_TO_THEBES_OP("source-over", OVER)
3613 0 : else CANVAS_OP_TO_THEBES_OP("xor", XOR)
3614 0 : else return NS_ERROR_FAILURE;
3615 :
3616 : #undef CANVAS_OP_TO_THEBES_OP
3617 :
3618 0 : return NS_OK;
3619 : }
3620 :
3621 : NS_IMETHODIMP
3622 0 : nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY,
3623 : float aW, float aH,
3624 : const nsAString& aBGColor,
3625 : PRUint32 flags)
3626 : {
3627 0 : if (!EnsureSurface())
3628 0 : return NS_ERROR_FAILURE;
3629 :
3630 0 : NS_ENSURE_ARG(aWindow != nsnull);
3631 :
3632 : // protect against too-large surfaces that will cause allocation
3633 : // or overflow issues
3634 0 : if (!gfxASurface::CheckSurfaceSize(gfxIntSize(PRInt32(aW), PRInt32(aH)),
3635 0 : 0xffff))
3636 0 : return NS_ERROR_FAILURE;
3637 :
3638 : // We can't allow web apps to call this until we fix at least the
3639 : // following potential security issues:
3640 : // -- rendering cross-domain IFRAMEs and then extracting the results
3641 : // -- rendering the user's theme and then extracting the results
3642 : // -- rendering native anonymous content (e.g., file input paths;
3643 : // scrollbars should be allowed)
3644 0 : if (!nsContentUtils::IsCallerTrustedForRead()) {
3645 : // not permitted to use DrawWindow
3646 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3647 0 : return NS_ERROR_DOM_SECURITY_ERR;
3648 : }
3649 :
3650 : // Flush layout updates
3651 0 : if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH))
3652 0 : nsContentUtils::FlushLayoutForTree(aWindow);
3653 :
3654 0 : nsRefPtr<nsPresContext> presContext;
3655 0 : nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
3656 0 : if (win) {
3657 0 : nsIDocShell* docshell = win->GetDocShell();
3658 0 : if (docshell) {
3659 0 : docshell->GetPresContext(getter_AddRefs(presContext));
3660 : }
3661 : }
3662 0 : if (!presContext)
3663 0 : return NS_ERROR_FAILURE;
3664 :
3665 : nscolor bgColor;
3666 :
3667 : nsIDocument* elementDoc = mCanvasElement ?
3668 0 : HTMLCanvasElement()->OwnerDoc() : nsnull;
3669 :
3670 : // Pass the CSS Loader object to the parser, to allow parser error reports
3671 : // to include the outer window ID.
3672 0 : nsCSSParser parser(elementDoc ? elementDoc->CSSLoader() : nsnull);
3673 0 : nsresult rv = parser.ParseColorString(PromiseFlatString(aBGColor),
3674 0 : nsnull, 0, &bgColor);
3675 0 : NS_ENSURE_SUCCESS(rv, rv);
3676 :
3677 0 : nsIPresShell* presShell = presContext->PresShell();
3678 0 : NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
3679 :
3680 : nsRect r(nsPresContext::CSSPixelsToAppUnits(aX),
3681 : nsPresContext::CSSPixelsToAppUnits(aY),
3682 : nsPresContext::CSSPixelsToAppUnits(aW),
3683 0 : nsPresContext::CSSPixelsToAppUnits(aH));
3684 : PRUint32 renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
3685 0 : nsIPresShell::RENDER_DOCUMENT_RELATIVE);
3686 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
3687 0 : renderDocFlags |= nsIPresShell::RENDER_CARET;
3688 : }
3689 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
3690 : renderDocFlags &= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
3691 0 : nsIPresShell::RENDER_DOCUMENT_RELATIVE);
3692 : }
3693 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS) {
3694 0 : renderDocFlags |= nsIPresShell::RENDER_USE_WIDGET_LAYERS;
3695 : }
3696 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
3697 0 : renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
3698 : }
3699 :
3700 0 : rv = presShell->RenderDocument(r, renderDocFlags, bgColor, mThebes);
3701 :
3702 : // get rid of the pattern surface ref, just in case
3703 0 : mThebes->SetColor(gfxRGBA(1,1,1,1));
3704 0 : DirtyAllStyles();
3705 :
3706 : // note that aX and aY are coordinates in the document that
3707 : // we're drawing; aX and aY are drawn to 0,0 in current user
3708 : // space.
3709 0 : RedrawUser(gfxRect(0, 0, aW, aH));
3710 :
3711 0 : return rv;
3712 : }
3713 :
3714 : NS_IMETHODIMP
3715 0 : nsCanvasRenderingContext2D::AsyncDrawXULElement(nsIDOMXULElement* aElem, float aX, float aY,
3716 : float aW, float aH,
3717 : const nsAString& aBGColor,
3718 : PRUint32 flags)
3719 : {
3720 0 : if (!EnsureSurface())
3721 0 : return NS_ERROR_FAILURE;
3722 :
3723 0 : NS_ENSURE_ARG(aElem != nsnull);
3724 :
3725 : // We can't allow web apps to call this until we fix at least the
3726 : // following potential security issues:
3727 : // -- rendering cross-domain IFRAMEs and then extracting the results
3728 : // -- rendering the user's theme and then extracting the results
3729 : // -- rendering native anonymous content (e.g., file input paths;
3730 : // scrollbars should be allowed)
3731 0 : if (!nsContentUtils::IsCallerTrustedForRead()) {
3732 : // not permitted to use DrawWindow
3733 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3734 0 : return NS_ERROR_DOM_SECURITY_ERR;
3735 : }
3736 :
3737 0 : nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(aElem);
3738 0 : if (!loaderOwner)
3739 0 : return NS_ERROR_FAILURE;
3740 :
3741 0 : nsRefPtr<nsFrameLoader> frameloader = loaderOwner->GetFrameLoader();
3742 0 : if (!frameloader)
3743 0 : return NS_ERROR_FAILURE;
3744 :
3745 0 : PBrowserParent *child = frameloader->GetRemoteBrowser();
3746 0 : if (!child) {
3747 : nsCOMPtr<nsIDOMWindow> window =
3748 0 : do_GetInterface(frameloader->GetExistingDocShell());
3749 0 : if (!window)
3750 0 : return NS_ERROR_FAILURE;
3751 :
3752 0 : return DrawWindow(window, aX, aY, aW, aH, aBGColor, flags);
3753 : }
3754 :
3755 : // protect against too-large surfaces that will cause allocation
3756 : // or overflow issues
3757 0 : if (!gfxASurface::CheckSurfaceSize(gfxIntSize(aW, aH), 0xffff))
3758 0 : return NS_ERROR_FAILURE;
3759 :
3760 : bool flush =
3761 0 : (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) == 0;
3762 :
3763 0 : PRUint32 renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
3764 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
3765 0 : renderDocFlags |= nsIPresShell::RENDER_CARET;
3766 : }
3767 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
3768 0 : renderDocFlags &= ~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
3769 : }
3770 :
3771 : nsRect rect(nsPresContext::CSSPixelsToAppUnits(aX),
3772 : nsPresContext::CSSPixelsToAppUnits(aY),
3773 : nsPresContext::CSSPixelsToAppUnits(aW),
3774 0 : nsPresContext::CSSPixelsToAppUnits(aH));
3775 0 : if (mIPC) {
3776 : PDocumentRendererParent *pdocrender =
3777 : child->SendPDocumentRendererConstructor(rect,
3778 0 : mThebes->CurrentMatrix(),
3779 0 : nsString(aBGColor),
3780 : renderDocFlags, flush,
3781 0 : nsIntSize(mWidth, mHeight));
3782 0 : if (!pdocrender)
3783 0 : return NS_ERROR_FAILURE;
3784 :
3785 : DocumentRendererParent *docrender =
3786 0 : static_cast<DocumentRendererParent *>(pdocrender);
3787 :
3788 0 : docrender->SetCanvasContext(this, mThebes);
3789 : }
3790 :
3791 0 : return NS_OK;
3792 : }
3793 :
3794 : //
3795 : // device pixel getting/setting
3796 : //
3797 :
3798 : void
3799 0 : nsCanvasRenderingContext2D::EnsureUnpremultiplyTable() {
3800 0 : if (sUnpremultiplyTable)
3801 0 : return;
3802 :
3803 : // Infallably alloc the unpremultiply table.
3804 0 : sUnpremultiplyTable = new PRUint8[256][256];
3805 :
3806 : // It's important that the array be indexed first by alpha and then by rgb
3807 : // value. When we unpremultiply a pixel, we're guaranteed to do three
3808 : // lookups with the same alpha; indexing by alpha first makes it likely that
3809 : // those three lookups will be close to one another in memory, thus
3810 : // increasing the chance of a cache hit.
3811 :
3812 : // a == 0 case
3813 0 : for (PRUint32 c = 0; c <= 255; c++) {
3814 0 : sUnpremultiplyTable[0][c] = c;
3815 : }
3816 :
3817 0 : for (int a = 1; a <= 255; a++) {
3818 0 : for (int c = 0; c <= 255; c++) {
3819 0 : sUnpremultiplyTable[a][c] = (PRUint8)((c * 255) / a);
3820 : }
3821 : }
3822 : }
3823 :
3824 :
3825 : NS_IMETHODIMP
3826 0 : nsCanvasRenderingContext2D::GetImageData()
3827 : {
3828 : /* Should never be called -- GetImageData_explicit is the QS entry point */
3829 0 : return NS_ERROR_NOT_IMPLEMENTED;
3830 : }
3831 :
3832 : NS_IMETHODIMP
3833 0 : nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
3834 : PRUint8 *aData, PRUint32 aDataLen)
3835 : {
3836 0 : if (!EnsureSurface())
3837 0 : return NS_ERROR_FAILURE;
3838 :
3839 0 : if (!mCanvasElement && !mDocShell) {
3840 0 : NS_ERROR("No canvas element and no docshell in GetImageData!!!");
3841 0 : return NS_ERROR_DOM_SECURITY_ERR;
3842 : }
3843 :
3844 : // Check only if we have a canvas element; if we were created with a docshell,
3845 : // then it's special internal use.
3846 0 : if (mCanvasElement &&
3847 0 : HTMLCanvasElement()->IsWriteOnly() &&
3848 0 : !nsContentUtils::IsCallerTrustedForRead())
3849 : {
3850 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3851 0 : return NS_ERROR_DOM_SECURITY_ERR;
3852 : }
3853 :
3854 0 : if (w == 0 || h == 0 || aDataLen != w * h * 4)
3855 0 : return NS_ERROR_DOM_SYNTAX_ERR;
3856 :
3857 0 : CheckedInt32 rightMost = CheckedInt32(x) + w;
3858 0 : CheckedInt32 bottomMost = CheckedInt32(y) + h;
3859 :
3860 0 : if (!rightMost.valid() || !bottomMost.valid())
3861 0 : return NS_ERROR_DOM_SYNTAX_ERR;
3862 :
3863 : /* Copy the surface contents to the buffer */
3864 : nsRefPtr<gfxImageSurface> tmpsurf =
3865 : new gfxImageSurface(aData,
3866 : gfxIntSize(w, h),
3867 : w * 4,
3868 0 : gfxASurface::ImageFormatARGB32);
3869 :
3870 0 : if (tmpsurf->CairoStatus())
3871 0 : return NS_ERROR_FAILURE;
3872 :
3873 0 : nsRefPtr<gfxContext> tmpctx = new gfxContext(tmpsurf);
3874 :
3875 0 : if (tmpctx->HasError())
3876 0 : return NS_ERROR_FAILURE;
3877 :
3878 0 : if (!mZero) {
3879 0 : gfxRect srcRect(0, 0, mWidth, mHeight);
3880 0 : gfxRect destRect(x, y, w, h);
3881 :
3882 0 : bool finishedPainting = false;
3883 : // In the common case, we want to avoid the Rectangle call.
3884 0 : if (!srcRect.Contains(destRect)) {
3885 : // If the requested area is entirely outside the canvas, we're done.
3886 0 : gfxRect tmp = srcRect.Intersect(destRect);
3887 0 : finishedPainting = tmp.IsEmpty();
3888 :
3889 : // Set clipping region if necessary.
3890 0 : if (!finishedPainting) {
3891 0 : tmpctx->Rectangle(tmp);
3892 : }
3893 : }
3894 :
3895 0 : if (!finishedPainting) {
3896 0 : tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE);
3897 0 : tmpctx->SetSource(mSurface, gfxPoint(-x, -y));
3898 0 : tmpctx->Paint();
3899 : }
3900 : }
3901 :
3902 : // make sure sUnpremultiplyTable has been created
3903 0 : EnsureUnpremultiplyTable();
3904 :
3905 : // NOTE! dst is the same as src, and this relies on reading
3906 : // from src and advancing that ptr before writing to dst.
3907 0 : PRUint8 *src = aData;
3908 0 : PRUint8 *dst = aData;
3909 :
3910 0 : for (PRUint32 j = 0; j < h; j++) {
3911 0 : for (PRUint32 i = 0; i < w; i++) {
3912 : // XXX Is there some useful swizzle MMX we can use here?
3913 : #ifdef IS_LITTLE_ENDIAN
3914 0 : PRUint8 b = *src++;
3915 0 : PRUint8 g = *src++;
3916 0 : PRUint8 r = *src++;
3917 0 : PRUint8 a = *src++;
3918 : #else
3919 : PRUint8 a = *src++;
3920 : PRUint8 r = *src++;
3921 : PRUint8 g = *src++;
3922 : PRUint8 b = *src++;
3923 : #endif
3924 : // Convert to non-premultiplied color
3925 0 : *dst++ = sUnpremultiplyTable[a][r];
3926 0 : *dst++ = sUnpremultiplyTable[a][g];
3927 0 : *dst++ = sUnpremultiplyTable[a][b];
3928 0 : *dst++ = a;
3929 : }
3930 : }
3931 :
3932 0 : return NS_OK;
3933 : }
3934 :
3935 : void
3936 0 : nsCanvasRenderingContext2D::EnsurePremultiplyTable() {
3937 0 : if (sPremultiplyTable)
3938 0 : return;
3939 :
3940 : // Infallably alloc the premultiply table.
3941 0 : sPremultiplyTable = new PRUint8[256][256];
3942 :
3943 : // Like the unpremultiply table, it's important that we index the premultiply
3944 : // table with the alpha value as the first index to ensure good cache
3945 : // performance.
3946 :
3947 0 : for (int a = 0; a <= 255; a++) {
3948 0 : for (int c = 0; c <= 255; c++) {
3949 0 : sPremultiplyTable[a][c] = (a * c + 254) / 255;
3950 : }
3951 : }
3952 : }
3953 :
3954 : // void putImageData (in ImageData d, in float x, in float y);
3955 : NS_IMETHODIMP
3956 0 : nsCanvasRenderingContext2D::PutImageData()
3957 : {
3958 : /* Should never be called -- PutImageData_explicit is the QS entry point */
3959 0 : return NS_ERROR_NOT_IMPLEMENTED;
3960 : }
3961 :
3962 : NS_IMETHODIMP
3963 0 : nsCanvasRenderingContext2D::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
3964 : unsigned char *aData, PRUint32 aDataLen,
3965 : bool hasDirtyRect, PRInt32 dirtyX, PRInt32 dirtyY,
3966 : PRInt32 dirtyWidth, PRInt32 dirtyHeight)
3967 : {
3968 0 : if (!EnsureSurface())
3969 0 : return NS_ERROR_FAILURE;
3970 :
3971 0 : if (w == 0 || h == 0)
3972 0 : return NS_ERROR_DOM_SYNTAX_ERR;
3973 :
3974 0 : gfxRect dirtyRect;
3975 0 : gfxRect imageDataRect(0, 0, w, h);
3976 :
3977 0 : if (hasDirtyRect) {
3978 : // fix up negative dimensions
3979 0 : if (dirtyWidth < 0) {
3980 0 : NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
3981 :
3982 0 : CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
3983 :
3984 0 : if (!checkedDirtyX.valid())
3985 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
3986 :
3987 0 : dirtyX = checkedDirtyX.value();
3988 0 : dirtyWidth = -(int32)dirtyWidth;
3989 : }
3990 :
3991 0 : if (dirtyHeight < 0) {
3992 0 : NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
3993 :
3994 0 : CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
3995 :
3996 0 : if (!checkedDirtyY.valid())
3997 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
3998 :
3999 0 : dirtyY = checkedDirtyY.value();
4000 0 : dirtyHeight = -(int32)dirtyHeight;
4001 : }
4002 :
4003 : // bound the dirty rect within the imageData rectangle
4004 0 : dirtyRect = imageDataRect.Intersect(gfxRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
4005 :
4006 0 : if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
4007 0 : return NS_OK;
4008 : } else {
4009 0 : dirtyRect = imageDataRect;
4010 : }
4011 :
4012 0 : dirtyRect.MoveBy(gfxPoint(x, y));
4013 0 : dirtyRect = gfxRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
4014 :
4015 0 : if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
4016 0 : return NS_OK;
4017 :
4018 0 : PRUint32 len = w * h * 4;
4019 0 : if (aDataLen != len)
4020 0 : return NS_ERROR_DOM_SYNTAX_ERR;
4021 :
4022 : nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
4023 0 : gfxASurface::ImageFormatARGB32);
4024 0 : if (!imgsurf || imgsurf->CairoStatus())
4025 0 : return NS_ERROR_FAILURE;
4026 :
4027 : // ensure premultiply table has been created
4028 0 : EnsurePremultiplyTable();
4029 :
4030 0 : PRUint8 *src = aData;
4031 0 : PRUint8 *dst = imgsurf->Data();
4032 :
4033 0 : for (PRUint32 j = 0; j < h; j++) {
4034 0 : for (PRUint32 i = 0; i < w; i++) {
4035 0 : PRUint8 r = *src++;
4036 0 : PRUint8 g = *src++;
4037 0 : PRUint8 b = *src++;
4038 0 : PRUint8 a = *src++;
4039 :
4040 : // Convert to premultiplied color (losslessly if the input came from getImageData)
4041 : #ifdef IS_LITTLE_ENDIAN
4042 0 : *dst++ = sPremultiplyTable[a][b];
4043 0 : *dst++ = sPremultiplyTable[a][g];
4044 0 : *dst++ = sPremultiplyTable[a][r];
4045 0 : *dst++ = a;
4046 : #else
4047 : *dst++ = a;
4048 : *dst++ = sPremultiplyTable[a][r];
4049 : *dst++ = sPremultiplyTable[a][g];
4050 : *dst++ = sPremultiplyTable[a][b];
4051 : #endif
4052 : }
4053 : }
4054 :
4055 0 : PathAutoSaveRestore pathSR(this);
4056 0 : gfxContextAutoSaveRestore autoSR(mThebes);
4057 :
4058 : // ignore clipping region, as per spec
4059 0 : mThebes->ResetClip();
4060 :
4061 0 : mThebes->IdentityMatrix();
4062 0 : mThebes->NewPath();
4063 0 : mThebes->Rectangle(dirtyRect);
4064 0 : mThebes->SetSource(imgsurf, gfxPoint(x, y));
4065 0 : mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);
4066 0 : mThebes->Fill();
4067 :
4068 0 : return Redraw(dirtyRect);
4069 : }
4070 :
4071 : NS_IMETHODIMP
4072 0 : nsCanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
4073 : {
4074 0 : if (!EnsureSurface()) {
4075 0 : *surface = nsnull;
4076 0 : return NS_ERROR_NOT_AVAILABLE;
4077 : }
4078 :
4079 0 : *surface = mSurface.get();
4080 0 : NS_ADDREF(*surface);
4081 :
4082 0 : return NS_OK;
4083 : }
4084 :
4085 : NS_IMETHODIMP
4086 0 : nsCanvasRenderingContext2D::CreateImageData()
4087 : {
4088 : /* Should never be called; handled entirely in the quickstub */
4089 0 : return NS_ERROR_NOT_IMPLEMENTED;
4090 : }
4091 :
4092 : NS_IMETHODIMP
4093 0 : nsCanvasRenderingContext2D::GetMozImageSmoothingEnabled(bool *retVal)
4094 : {
4095 0 : *retVal = CurrentState().imageSmoothingEnabled;
4096 0 : return NS_OK;
4097 : }
4098 :
4099 : NS_IMETHODIMP
4100 0 : nsCanvasRenderingContext2D::SetMozImageSmoothingEnabled(bool val)
4101 : {
4102 0 : if (val != CurrentState().imageSmoothingEnabled) {
4103 0 : CurrentState().imageSmoothingEnabled = val;
4104 0 : DirtyAllStyles();
4105 : }
4106 :
4107 0 : return NS_OK;
4108 : }
4109 :
4110 : static PRUint8 g2DContextLayerUserData;
4111 :
4112 0 : class CanvasRenderingContext2DUserData : public LayerUserData {
4113 : public:
4114 0 : CanvasRenderingContext2DUserData(nsHTMLCanvasElement *aContent)
4115 0 : : mContent(aContent) {}
4116 0 : static void DidTransactionCallback(void* aData)
4117 : {
4118 0 : static_cast<CanvasRenderingContext2DUserData*>(aData)->mContent->MarkContextClean();
4119 0 : }
4120 :
4121 : private:
4122 : nsRefPtr<nsHTMLCanvasElement> mContent;
4123 : };
4124 :
4125 : already_AddRefed<CanvasLayer>
4126 0 : nsCanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
4127 : CanvasLayer *aOldLayer,
4128 : LayerManager *aManager)
4129 : {
4130 0 : if (!EnsureSurface())
4131 0 : return nsnull;
4132 :
4133 0 : if (!mResetLayer && aOldLayer &&
4134 0 : aOldLayer->HasUserData(&g2DContextLayerUserData)) {
4135 0 : NS_ADDREF(aOldLayer);
4136 0 : return aOldLayer;
4137 : }
4138 :
4139 0 : nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
4140 0 : if (!canvasLayer) {
4141 0 : NS_WARNING("CreateCanvasLayer returned null!");
4142 0 : return nsnull;
4143 : }
4144 0 : CanvasRenderingContext2DUserData *userData = nsnull;
4145 0 : if (aBuilder->IsPaintingToWindow()) {
4146 : // Make the layer tell us whenever a transaction finishes (including
4147 : // the current transaction), so we can clear our invalidation state and
4148 : // start invalidating again. We need to do this for the layer that is
4149 : // being painted to a window (there shouldn't be more than one at a time,
4150 : // and if there is, flushing the invalidation state more often than
4151 : // necessary is harmless).
4152 :
4153 : // The layer will be destroyed when we tear down the presentation
4154 : // (at the latest), at which time this userData will be destroyed,
4155 : // releasing the reference to the element.
4156 : // The userData will receive DidTransactionCallbacks, which flush the
4157 : // the invalidation state to indicate that the canvas is up to date.
4158 0 : userData = new CanvasRenderingContext2DUserData(HTMLCanvasElement());
4159 : canvasLayer->SetDidTransactionCallback(
4160 0 : CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
4161 : }
4162 0 : canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
4163 :
4164 0 : CanvasLayer::Data data;
4165 :
4166 0 : data.mSurface = mSurface.get();
4167 0 : data.mSize = nsIntSize(mWidth, mHeight);
4168 :
4169 0 : canvasLayer->Initialize(data);
4170 0 : PRUint32 flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
4171 0 : canvasLayer->SetContentFlags(flags);
4172 0 : canvasLayer->Updated();
4173 :
4174 0 : mResetLayer = false;
4175 :
4176 0 : return canvasLayer.forget();
4177 : }
4178 :
4179 : bool
4180 0 : nsCanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager *aManager)
4181 : {
4182 0 : return !aManager->CanUseCanvasLayerForSize(gfxIntSize(mWidth, mHeight));
4183 : }
4184 :
4185 : void
4186 0 : nsCanvasRenderingContext2D::MarkContextClean()
4187 : {
4188 0 : if (mInvalidateCount > 0) {
4189 0 : mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
4190 : }
4191 0 : mIsEntireFrameInvalid = false;
4192 0 : mInvalidateCount = 0;
4193 4392 : }
4194 :
|