image/src/VectorImage.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:a368a0bdfe56
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "VectorImage.h"
7
8 #include "gfx2DGlue.h"
9 #include "gfxContext.h"
10 #include "gfxDrawable.h"
11 #include "gfxPlatform.h"
12 #include "gfxUtils.h"
13 #include "imgDecoderObserver.h"
14 #include "mozilla/AutoRestore.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/dom/SVGSVGElement.h"
17 #include "mozilla/gfx/2D.h"
18 #include "mozilla/RefPtr.h"
19 #include "nsIDOMEvent.h"
20 #include "nsIPresShell.h"
21 #include "nsIStreamListener.h"
22 #include "nsMimeTypes.h"
23 #include "nsPresContext.h"
24 #include "nsRect.h"
25 #include "nsStubDocumentObserver.h"
26 #include "nsSVGEffects.h" // for nsSVGRenderingObserver
27 #include "Orientation.h"
28 #include "SVGDocumentWrapper.h"
29 #include "nsIDOMEventListener.h"
30 #include "SurfaceCache.h"
31
32 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
33 #undef GetCurrentTime
34
35 namespace mozilla {
36
37 using namespace dom;
38 using namespace gfx;
39 using namespace layers;
40
41 namespace image {
42
43 // Helper-class: SVGRootRenderingObserver
44 class SVGRootRenderingObserver MOZ_FINAL : public nsSVGRenderingObserver {
45 public:
46 SVGRootRenderingObserver(SVGDocumentWrapper* aDocWrapper,
47 VectorImage* aVectorImage)
48 : nsSVGRenderingObserver()
49 , mDocWrapper(aDocWrapper)
50 , mVectorImage(aVectorImage)
51 , mHonoringInvalidations(true)
52 {
53 MOZ_ASSERT(mDocWrapper, "Need a non-null SVG document wrapper");
54 MOZ_ASSERT(mVectorImage, "Need a non-null VectorImage");
55
56 StartListening();
57 Element* elem = GetTarget();
58 MOZ_ASSERT(elem, "no root SVG node for us to observe");
59
60 nsSVGEffects::AddRenderingObserver(elem, this);
61 mInObserverList = true;
62 }
63
64 virtual ~SVGRootRenderingObserver()
65 {
66 StopListening();
67 }
68
69 void ResumeHonoringInvalidations()
70 {
71 mHonoringInvalidations = true;
72 }
73
74 protected:
75 virtual Element* GetTarget() MOZ_OVERRIDE
76 {
77 return mDocWrapper->GetRootSVGElem();
78 }
79
80 virtual void DoUpdate() MOZ_OVERRIDE
81 {
82 Element* elem = GetTarget();
83 MOZ_ASSERT(elem, "missing root SVG node");
84
85 if (mHonoringInvalidations && !mDocWrapper->ShouldIgnoreInvalidation()) {
86 nsIFrame* frame = elem->GetPrimaryFrame();
87 if (!frame || frame->PresContext()->PresShell()->IsDestroying()) {
88 // We're being destroyed. Bail out.
89 return;
90 }
91
92 // Ignore further invalidations until we draw.
93 mHonoringInvalidations = false;
94
95 mVectorImage->InvalidateObserversOnNextRefreshDriverTick();
96 }
97
98 // Our caller might've removed us from rendering-observer list.
99 // Add ourselves back!
100 if (!mInObserverList) {
101 nsSVGEffects::AddRenderingObserver(elem, this);
102 mInObserverList = true;
103 }
104 }
105
106 // Private data
107 const nsRefPtr<SVGDocumentWrapper> mDocWrapper;
108 VectorImage* const mVectorImage; // Raw pointer because it owns me.
109 bool mHonoringInvalidations;
110 };
111
112 class SVGParseCompleteListener MOZ_FINAL : public nsStubDocumentObserver {
113 public:
114 NS_DECL_ISUPPORTS
115
116 SVGParseCompleteListener(nsIDocument* aDocument,
117 VectorImage* aImage)
118 : mDocument(aDocument)
119 , mImage(aImage)
120 {
121 MOZ_ASSERT(mDocument, "Need an SVG document");
122 MOZ_ASSERT(mImage, "Need an image");
123
124 mDocument->AddObserver(this);
125 }
126
127 ~SVGParseCompleteListener()
128 {
129 if (mDocument) {
130 // The document must have been destroyed before we got our event.
131 // Otherwise this can't happen, since documents hold strong references to
132 // their observers.
133 Cancel();
134 }
135 }
136
137 void EndLoad(nsIDocument* aDocument) MOZ_OVERRIDE
138 {
139 MOZ_ASSERT(aDocument == mDocument, "Got EndLoad for wrong document?");
140
141 // OnSVGDocumentParsed will release our owner's reference to us, so ensure
142 // we stick around long enough to complete our work.
143 nsRefPtr<SVGParseCompleteListener> kungFuDeathGroup(this);
144
145 mImage->OnSVGDocumentParsed();
146 }
147
148 void Cancel()
149 {
150 MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
151 if (mDocument) {
152 mDocument->RemoveObserver(this);
153 mDocument = nullptr;
154 }
155 }
156
157 private:
158 nsCOMPtr<nsIDocument> mDocument;
159 VectorImage* const mImage; // Raw pointer to owner.
160 };
161
162 NS_IMPL_ISUPPORTS(SVGParseCompleteListener, nsIDocumentObserver)
163
164 class SVGLoadEventListener MOZ_FINAL : public nsIDOMEventListener {
165 public:
166 NS_DECL_ISUPPORTS
167
168 SVGLoadEventListener(nsIDocument* aDocument,
169 VectorImage* aImage)
170 : mDocument(aDocument)
171 , mImage(aImage)
172 {
173 MOZ_ASSERT(mDocument, "Need an SVG document");
174 MOZ_ASSERT(mImage, "Need an image");
175
176 mDocument->AddEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), this, true, false);
177 mDocument->AddEventListener(NS_LITERAL_STRING("SVGAbort"), this, true, false);
178 mDocument->AddEventListener(NS_LITERAL_STRING("SVGError"), this, true, false);
179 }
180
181 ~SVGLoadEventListener()
182 {
183 if (mDocument) {
184 // The document must have been destroyed before we got our event.
185 // Otherwise this can't happen, since documents hold strong references to
186 // their observers.
187 Cancel();
188 }
189 }
190
191 NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE
192 {
193 MOZ_ASSERT(mDocument, "Need an SVG document. Received multiple events?");
194
195 // OnSVGDocumentLoaded/OnSVGDocumentError will release our owner's reference
196 // to us, so ensure we stick around long enough to complete our work.
197 nsRefPtr<SVGLoadEventListener> kungFuDeathGroup(this);
198
199 nsAutoString eventType;
200 aEvent->GetType(eventType);
201 MOZ_ASSERT(eventType.EqualsLiteral("MozSVGAsImageDocumentLoad") ||
202 eventType.EqualsLiteral("SVGAbort") ||
203 eventType.EqualsLiteral("SVGError"),
204 "Received unexpected event");
205
206 if (eventType.EqualsLiteral("MozSVGAsImageDocumentLoad")) {
207 mImage->OnSVGDocumentLoaded();
208 } else {
209 mImage->OnSVGDocumentError();
210 }
211
212 return NS_OK;
213 }
214
215 void Cancel()
216 {
217 MOZ_ASSERT(mDocument, "Duplicate call to Cancel");
218 if (mDocument) {
219 mDocument->RemoveEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), this, true);
220 mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGAbort"), this, true);
221 mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGError"), this, true);
222 mDocument = nullptr;
223 }
224 }
225
226 private:
227 nsCOMPtr<nsIDocument> mDocument;
228 VectorImage* const mImage; // Raw pointer to owner.
229 };
230
231 NS_IMPL_ISUPPORTS(SVGLoadEventListener, nsIDOMEventListener)
232
233 // Helper-class: SVGDrawingCallback
234 class SVGDrawingCallback : public gfxDrawingCallback {
235 public:
236 SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper,
237 const nsIntRect& aViewport,
238 const gfxSize& aScale,
239 uint32_t aImageFlags) :
240 mSVGDocumentWrapper(aSVGDocumentWrapper),
241 mViewport(aViewport),
242 mScale(aScale),
243 mImageFlags(aImageFlags)
244 {}
245 virtual bool operator()(gfxContext* aContext,
246 const gfxRect& aFillRect,
247 const GraphicsFilter& aFilter,
248 const gfxMatrix& aTransform);
249 private:
250 nsRefPtr<SVGDocumentWrapper> mSVGDocumentWrapper;
251 const nsIntRect mViewport;
252 const gfxSize mScale;
253 uint32_t mImageFlags;
254 };
255
256 // Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator()
257 bool
258 SVGDrawingCallback::operator()(gfxContext* aContext,
259 const gfxRect& aFillRect,
260 const GraphicsFilter& aFilter,
261 const gfxMatrix& aTransform)
262 {
263 MOZ_ASSERT(mSVGDocumentWrapper, "need an SVGDocumentWrapper");
264
265 // Get (& sanity-check) the helper-doc's presShell
266 nsCOMPtr<nsIPresShell> presShell;
267 if (NS_FAILED(mSVGDocumentWrapper->GetPresShell(getter_AddRefs(presShell)))) {
268 NS_WARNING("Unable to draw -- presShell lookup failed");
269 return false;
270 }
271 MOZ_ASSERT(presShell, "GetPresShell succeeded but returned null");
272
273 gfxContextAutoSaveRestore contextRestorer(aContext);
274
275 // Clip to aFillRect so that we don't paint outside.
276 aContext->NewPath();
277 aContext->Rectangle(aFillRect);
278 aContext->Clip();
279
280 gfxContextMatrixAutoSaveRestore contextMatrixRestorer(aContext);
281 aContext->Multiply(gfxMatrix(aTransform).Invert());
282 aContext->Scale(1.0 / mScale.width, 1.0 / mScale.height);
283
284 nsPresContext* presContext = presShell->GetPresContext();
285 MOZ_ASSERT(presContext, "pres shell w/out pres context");
286
287 nsRect svgRect(presContext->DevPixelsToAppUnits(mViewport.x),
288 presContext->DevPixelsToAppUnits(mViewport.y),
289 presContext->DevPixelsToAppUnits(mViewport.width),
290 presContext->DevPixelsToAppUnits(mViewport.height));
291
292 uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
293 if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) {
294 renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
295 }
296
297 presShell->RenderDocument(svgRect, renderDocFlags,
298 NS_RGBA(0, 0, 0, 0), // transparent
299 aContext);
300
301 return true;
302 }
303
304 // Implement VectorImage's nsISupports-inherited methods
305 NS_IMPL_ISUPPORTS(VectorImage,
306 imgIContainer,
307 nsIStreamListener,
308 nsIRequestObserver)
309
310 //------------------------------------------------------------------------------
311 // Constructor / Destructor
312
313 VectorImage::VectorImage(imgStatusTracker* aStatusTracker,
314 ImageURL* aURI /* = nullptr */) :
315 ImageResource(aURI), // invoke superclass's constructor
316 mIsInitialized(false),
317 mIsFullyLoaded(false),
318 mIsDrawing(false),
319 mHaveAnimations(false),
320 mHasPendingInvalidation(false)
321 {
322 mStatusTrackerInit = new imgStatusTrackerInit(this, aStatusTracker);
323 }
324
325 VectorImage::~VectorImage()
326 {
327 CancelAllListeners();
328 SurfaceCache::Discard(this);
329 }
330
331 //------------------------------------------------------------------------------
332 // Methods inherited from Image.h
333
334 nsresult
335 VectorImage::Init(const char* aMimeType,
336 uint32_t aFlags)
337 {
338 // We don't support re-initialization
339 if (mIsInitialized)
340 return NS_ERROR_ILLEGAL_VALUE;
341
342 MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations && !mError,
343 "Flags unexpectedly set before initialization");
344 MOZ_ASSERT(!strcmp(aMimeType, IMAGE_SVG_XML), "Unexpected mimetype");
345
346 mIsInitialized = true;
347 return NS_OK;
348 }
349
350 nsIntRect
351 VectorImage::FrameRect(uint32_t aWhichFrame)
352 {
353 return nsIntRect::GetMaxSizedIntRect();
354 }
355
356 size_t
357 VectorImage::HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
358 {
359 // We're not storing the source data -- we just feed that directly to
360 // our helper SVG document as we receive it, for it to parse.
361 // So 0 is an appropriate return value here.
362 return 0;
363 }
364
365 size_t
366 VectorImage::HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
367 {
368 // XXXdholbert TODO: return num bytes used by helper SVG doc. (bug 590790)
369 return 0;
370 }
371
372 size_t
373 VectorImage::NonHeapSizeOfDecoded() const
374 {
375 return 0;
376 }
377
378 size_t
379 VectorImage::OutOfProcessSizeOfDecoded() const
380 {
381 return 0;
382 }
383
384 nsresult
385 VectorImage::OnImageDataComplete(nsIRequest* aRequest,
386 nsISupports* aContext,
387 nsresult aStatus,
388 bool aLastPart)
389 {
390 // Call our internal OnStopRequest method, which only talks to our embedded
391 // SVG document. This won't have any effect on our imgStatusTracker.
392 nsresult finalStatus = OnStopRequest(aRequest, aContext, aStatus);
393
394 // Give precedence to Necko failure codes.
395 if (NS_FAILED(aStatus))
396 finalStatus = aStatus;
397
398 // Actually fire OnStopRequest.
399 if (mStatusTracker) {
400 // XXX(seth): Is this seriously the least insane way to do this?
401 nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
402 imgDecoderObserver* observer = clone->GetDecoderObserver();
403 observer->OnStopRequest(aLastPart, finalStatus);
404 ImageStatusDiff diff = mStatusTracker->Difference(clone);
405 mStatusTracker->ApplyDifference(diff);
406 mStatusTracker->SyncNotifyDifference(diff);
407 }
408 return finalStatus;
409 }
410
411 nsresult
412 VectorImage::OnImageDataAvailable(nsIRequest* aRequest,
413 nsISupports* aContext,
414 nsIInputStream* aInStr,
415 uint64_t aSourceOffset,
416 uint32_t aCount)
417 {
418 return OnDataAvailable(aRequest, aContext, aInStr, aSourceOffset, aCount);
419 }
420
421 nsresult
422 VectorImage::OnNewSourceData()
423 {
424 return NS_OK;
425 }
426
427 nsresult
428 VectorImage::StartAnimation()
429 {
430 if (mError)
431 return NS_ERROR_FAILURE;
432
433 MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
434
435 mSVGDocumentWrapper->StartAnimation();
436 return NS_OK;
437 }
438
439 nsresult
440 VectorImage::StopAnimation()
441 {
442 nsresult rv = NS_OK;
443 if (mError) {
444 rv = NS_ERROR_FAILURE;
445 } else {
446 MOZ_ASSERT(mIsFullyLoaded && mHaveAnimations,
447 "Should not have been animating!");
448
449 mSVGDocumentWrapper->StopAnimation();
450 }
451
452 mAnimating = false;
453 return rv;
454 }
455
456 bool
457 VectorImage::ShouldAnimate()
458 {
459 return ImageResource::ShouldAnimate() && mIsFullyLoaded && mHaveAnimations;
460 }
461
462 NS_IMETHODIMP_(void)
463 VectorImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
464 {
465 // We don't care about animation start time.
466 }
467
468 //------------------------------------------------------------------------------
469 // imgIContainer methods
470
471 //******************************************************************************
472 /* readonly attribute int32_t width; */
473 NS_IMETHODIMP
474 VectorImage::GetWidth(int32_t* aWidth)
475 {
476 if (mError || !mIsFullyLoaded) {
477 *aWidth = 0;
478 return NS_ERROR_FAILURE;
479 }
480
481 if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eWidth,
482 *aWidth)) {
483 *aWidth = 0;
484 return NS_ERROR_FAILURE;
485 }
486
487 return NS_OK;
488 }
489
490 //******************************************************************************
491 /* [notxpcom] void requestRefresh ([const] in TimeStamp aTime); */
492 NS_IMETHODIMP_(void)
493 VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime)
494 {
495 // TODO: Implement for b666446.
496 EvaluateAnimation();
497
498 if (mHasPendingInvalidation) {
499 SendInvalidationNotifications();
500 mHasPendingInvalidation = false;
501 }
502 }
503
504 void
505 VectorImage::SendInvalidationNotifications()
506 {
507 // Animated images don't send out invalidation notifications as soon as
508 // they're generated. Instead, InvalidateObserversOnNextRefreshDriverTick
509 // records that there are pending invalidations and then returns immediately.
510 // The notifications are actually sent from RequestRefresh(). We send these
511 // notifications there to ensure that there is actually a document observing
512 // us. Otherwise, the notifications are just wasted effort.
513 //
514 // Non-animated images call this method directly from
515 // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never
516 // called for them. Ordinarily this isn't needed, since we send out
517 // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
518 // SVG document may not be 100% ready to render at that time. In those cases
519 // we would miss the subsequent invalidations if we didn't send out the
520 // notifications directly in |InvalidateObservers...|.
521
522 if (mStatusTracker) {
523 SurfaceCache::Discard(this);
524 mStatusTracker->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
525 mStatusTracker->OnStopFrame();
526 }
527 }
528
529 //******************************************************************************
530 /* readonly attribute int32_t height; */
531 NS_IMETHODIMP
532 VectorImage::GetHeight(int32_t* aHeight)
533 {
534 if (mError || !mIsFullyLoaded) {
535 *aHeight = 0;
536 return NS_ERROR_FAILURE;
537 }
538
539 if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight,
540 *aHeight)) {
541 *aHeight = 0;
542 return NS_ERROR_FAILURE;
543 }
544
545 return NS_OK;
546 }
547
548 //******************************************************************************
549 /* [noscript] readonly attribute nsSize intrinsicSize; */
550 NS_IMETHODIMP
551 VectorImage::GetIntrinsicSize(nsSize* aSize)
552 {
553 if (mError || !mIsFullyLoaded)
554 return NS_ERROR_FAILURE;
555
556 nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
557 if (!rootFrame)
558 return NS_ERROR_FAILURE;
559
560 *aSize = nsSize(-1, -1);
561 IntrinsicSize rfSize = rootFrame->GetIntrinsicSize();
562 if (rfSize.width.GetUnit() == eStyleUnit_Coord)
563 aSize->width = rfSize.width.GetCoordValue();
564 if (rfSize.height.GetUnit() == eStyleUnit_Coord)
565 aSize->height = rfSize.height.GetCoordValue();
566
567 return NS_OK;
568 }
569
570 //******************************************************************************
571 /* [noscript] readonly attribute nsSize intrinsicRatio; */
572 NS_IMETHODIMP
573 VectorImage::GetIntrinsicRatio(nsSize* aRatio)
574 {
575 if (mError || !mIsFullyLoaded)
576 return NS_ERROR_FAILURE;
577
578 nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame();
579 if (!rootFrame)
580 return NS_ERROR_FAILURE;
581
582 *aRatio = rootFrame->GetIntrinsicRatio();
583 return NS_OK;
584 }
585
586 NS_IMETHODIMP_(Orientation)
587 VectorImage::GetOrientation()
588 {
589 return Orientation();
590 }
591
592 //******************************************************************************
593 /* readonly attribute unsigned short type; */
594 NS_IMETHODIMP
595 VectorImage::GetType(uint16_t* aType)
596 {
597 NS_ENSURE_ARG_POINTER(aType);
598
599 *aType = GetType();
600 return NS_OK;
601 }
602
603 //******************************************************************************
604 /* [noscript, notxpcom] uint16_t GetType(); */
605 NS_IMETHODIMP_(uint16_t)
606 VectorImage::GetType()
607 {
608 return imgIContainer::TYPE_VECTOR;
609 }
610
611 //******************************************************************************
612 /* readonly attribute boolean animated; */
613 NS_IMETHODIMP
614 VectorImage::GetAnimated(bool* aAnimated)
615 {
616 if (mError || !mIsFullyLoaded)
617 return NS_ERROR_FAILURE;
618
619 *aAnimated = mSVGDocumentWrapper->IsAnimated();
620 return NS_OK;
621 }
622
623 //******************************************************************************
624 /* [notxpcom] int32_t getFirstFrameDelay (); */
625 int32_t
626 VectorImage::GetFirstFrameDelay()
627 {
628 if (mError)
629 return -1;
630
631 if (!mSVGDocumentWrapper->IsAnimated())
632 return -1;
633
634 // We don't really have a frame delay, so just pretend that we constantly
635 // need updates.
636 return 0;
637 }
638
639
640 //******************************************************************************
641 /* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
642 NS_IMETHODIMP_(bool)
643 VectorImage::FrameIsOpaque(uint32_t aWhichFrame)
644 {
645 if (aWhichFrame > FRAME_MAX_VALUE)
646 NS_WARNING("aWhichFrame outside valid range!");
647
648 return false; // In general, SVG content is not opaque.
649 }
650
651 //******************************************************************************
652 /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
653 * in uint32_t aFlags; */
654 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
655 VectorImage::GetFrame(uint32_t aWhichFrame,
656 uint32_t aFlags)
657 {
658 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
659
660 if (aWhichFrame > FRAME_MAX_VALUE)
661 return nullptr;
662
663 if (mError)
664 return nullptr;
665
666 // Look up height & width
667 // ----------------------
668 nsIntSize imageIntSize;
669 if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eWidth,
670 imageIntSize.width) ||
671 !mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight,
672 imageIntSize.height)) {
673 // We'll get here if our SVG doc has a percent-valued width or height.
674 return nullptr;
675 }
676
677 // Make our surface the size of what will ultimately be drawn to it.
678 // (either the full image size, or the restricted region)
679 RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
680 CreateOffscreenContentDrawTarget(IntSize(imageIntSize.width,
681 imageIntSize.height),
682 SurfaceFormat::B8G8R8A8);
683 nsRefPtr<gfxContext> context = new gfxContext(dt);
684
685 nsresult rv = Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
686 gfxRect(gfxPoint(0,0), gfxIntSize(imageIntSize.width,
687 imageIntSize.height)),
688 nsIntRect(nsIntPoint(0,0), imageIntSize),
689 imageIntSize, nullptr, aWhichFrame, aFlags);
690
691 NS_ENSURE_SUCCESS(rv, nullptr);
692 return dt->Snapshot();
693 }
694
695 //******************************************************************************
696 /* [noscript] ImageContainer getImageContainer(); */
697 NS_IMETHODIMP
698 VectorImage::GetImageContainer(LayerManager* aManager,
699 mozilla::layers::ImageContainer** _retval)
700 {
701 *_retval = nullptr;
702 return NS_OK;
703 }
704
705 struct SVGDrawingParameters
706 {
707 SVGDrawingParameters(gfxContext* aContext,
708 GraphicsFilter aFilter,
709 const gfxMatrix& aUserSpaceToImageSpace,
710 const gfxRect& aFill,
711 const nsIntRect& aSubimage,
712 const nsIntSize& aViewportSize,
713 const SVGImageContext* aSVGContext,
714 float aAnimationTime,
715 uint32_t aFlags)
716 : context(aContext)
717 , filter(aFilter)
718 , fill(aFill)
719 , viewportSize(aViewportSize)
720 , animationTime(aAnimationTime)
721 , svgContext(aSVGContext)
722 , flags(aFlags)
723 {
724 // gfxUtils::DrawPixelSnapped may rasterize this image to a temporary surface
725 // if we hit the tiling path. Unfortunately, the temporary surface isn't
726 // created at the size at which we'll ultimately draw, causing fuzzy output.
727 // To fix this we pre-apply the transform's scaling to the drawing parameters
728 // and remove the scaling from the transform, so the fact that temporary
729 // surfaces won't take the scaling into account doesn't matter. (Bug 600207.)
730 scale = aUserSpaceToImageSpace.ScaleFactors(true);
731 gfxPoint translation(aUserSpaceToImageSpace.GetTranslation());
732
733 // Remove the scaling from the transform.
734 gfxMatrix unscale;
735 unscale.Translate(gfxPoint(translation.x / scale.width,
736 translation.y / scale.height));
737 unscale.Scale(1.0 / scale.width, 1.0 / scale.height);
738 unscale.Translate(-translation);
739 userSpaceToImageSpace = aUserSpaceToImageSpace * unscale;
740
741 // Rescale drawing parameters.
742 IntSize drawableSize(aViewportSize.width / scale.width,
743 aViewportSize.height / scale.height);
744 sourceRect = userSpaceToImageSpace.Transform(aFill);
745 imageRect = IntRect(IntPoint(0, 0), drawableSize);
746 subimage = gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
747 subimage.ScaleRoundOut(1.0 / scale.width, 1.0 / scale.height);
748 }
749
750 gfxContext* context;
751 GraphicsFilter filter;
752 gfxMatrix userSpaceToImageSpace;
753 gfxRect fill;
754 gfxRect subimage;
755 gfxRect sourceRect;
756 IntRect imageRect;
757 nsIntSize viewportSize;
758 gfxSize scale;
759 float animationTime;
760 const SVGImageContext* svgContext;
761 uint32_t flags;
762 };
763
764 //******************************************************************************
765 /* [noscript] void draw(in gfxContext aContext,
766 * in gfxGraphicsFilter aFilter,
767 * [const] in gfxMatrix aUserSpaceToImageSpace,
768 * [const] in gfxRect aFill,
769 * [const] in nsIntRect aSubimage,
770 * [const] in nsIntSize aViewportSize,
771 * [const] in SVGImageContext aSVGContext,
772 * in uint32_t aWhichFrame,
773 * in uint32_t aFlags); */
774 NS_IMETHODIMP
775 VectorImage::Draw(gfxContext* aContext,
776 GraphicsFilter aFilter,
777 const gfxMatrix& aUserSpaceToImageSpace,
778 const gfxRect& aFill,
779 const nsIntRect& aSubimage,
780 const nsIntSize& aViewportSize,
781 const SVGImageContext* aSVGContext,
782 uint32_t aWhichFrame,
783 uint32_t aFlags)
784 {
785 if (aWhichFrame > FRAME_MAX_VALUE)
786 return NS_ERROR_INVALID_ARG;
787
788 NS_ENSURE_ARG_POINTER(aContext);
789 if (mError || !mIsFullyLoaded)
790 return NS_ERROR_FAILURE;
791
792 if (mIsDrawing) {
793 NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
794 return NS_ERROR_FAILURE;
795 }
796
797 if (mAnimationConsumers == 0 && mStatusTracker) {
798 mStatusTracker->OnUnlockedDraw();
799 }
800
801 AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
802 mIsDrawing = true;
803
804 float animTime = (aWhichFrame == FRAME_FIRST) ? 0.0f
805 : mSVGDocumentWrapper->GetCurrentTime();
806 AutoSVGRenderingState autoSVGState(aSVGContext, animTime,
807 mSVGDocumentWrapper->GetRootSVGElem());
808
809 // Pack up the drawing parameters.
810 SVGDrawingParameters params(aContext, aFilter, aUserSpaceToImageSpace, aFill,
811 aSubimage, aViewportSize, aSVGContext, animTime, aFlags);
812
813 // Check the cache.
814 nsRefPtr<gfxDrawable> drawable =
815 SurfaceCache::Lookup(ImageKey(this),
816 SurfaceKey(params.imageRect.Size(), params.scale,
817 aSVGContext, animTime, aFlags));
818
819 // Draw.
820 if (drawable) {
821 Show(drawable, params);
822 } else {
823 CreateDrawableAndShow(params);
824 }
825
826 return NS_OK;
827 }
828
829 void
830 VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
831 {
832 mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
833 mSVGDocumentWrapper->FlushImageTransformInvalidation();
834
835 nsRefPtr<gfxDrawingCallback> cb =
836 new SVGDrawingCallback(mSVGDocumentWrapper,
837 nsIntRect(nsIntPoint(0, 0), aParams.viewportSize),
838 aParams.scale,
839 aParams.flags);
840
841 nsRefPtr<gfxDrawable> svgDrawable =
842 new gfxCallbackDrawable(cb, ThebesIntSize(aParams.imageRect.Size()));
843
844 // Refuse to cache animated images.
845 // XXX(seth): We may remove this restriction in bug 922893.
846 if (mHaveAnimations)
847 return Show(svgDrawable, aParams);
848
849 // If the image is too big to fit in the cache, don't go any further.
850 if (!SurfaceCache::CanHold(aParams.imageRect.Size()))
851 return Show(svgDrawable, aParams);
852
853 // Try to create an offscreen surface.
854 mozilla::RefPtr<mozilla::gfx::DrawTarget> target =
855 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aParams.imageRect.Size(), gfx::SurfaceFormat::B8G8R8A8);
856
857 // If we couldn't create the draw target, it was probably because it would end
858 // up way too big. Generally it also wouldn't fit in the cache, but the prefs
859 // could be set such that the cache isn't the limiting factor.
860 if (!target)
861 return Show(svgDrawable, aParams);
862
863 nsRefPtr<gfxContext> ctx = new gfxContext(target);
864
865 // Actually draw. (We use FILTER_NEAREST since we never scale here.)
866 gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(),
867 ThebesIntRect(aParams.imageRect),
868 ThebesIntRect(aParams.imageRect),
869 ThebesIntRect(aParams.imageRect),
870 ThebesIntRect(aParams.imageRect),
871 gfxImageFormat::ARGB32,
872 GraphicsFilter::FILTER_NEAREST, aParams.flags);
873
874 // Attempt to cache the resulting surface.
875 SurfaceCache::Insert(target,
876 ImageKey(this),
877 SurfaceKey(aParams.imageRect.Size(), aParams.scale,
878 aParams.svgContext, aParams.animationTime,
879 aParams.flags));
880
881 // Draw. Note that if SurfaceCache::Insert failed for whatever reason,
882 // then |target| is all that is keeping the pixel data alive, so we have
883 // to draw before returning from this function.
884 nsRefPtr<gfxDrawable> drawable =
885 new gfxSurfaceDrawable(target, ThebesIntSize(aParams.imageRect.Size()));
886 Show(drawable, aParams);
887 }
888
889
890 void
891 VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
892 {
893 MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
894 gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
895 aParams.userSpaceToImageSpace,
896 aParams.subimage, aParams.sourceRect,
897 ThebesIntRect(aParams.imageRect), aParams.fill,
898 gfxImageFormat::ARGB32,
899 aParams.filter, aParams.flags);
900
901 MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
902 mRenderingObserver->ResumeHonoringInvalidations();
903 }
904
905 //******************************************************************************
906 /* void requestDecode() */
907 NS_IMETHODIMP
908 VectorImage::RequestDecode()
909 {
910 // Nothing to do for SVG images
911 return NS_OK;
912 }
913
914 NS_IMETHODIMP
915 VectorImage::StartDecoding()
916 {
917 // Nothing to do for SVG images
918 return NS_OK;
919 }
920
921 bool
922 VectorImage::IsDecoded()
923 {
924 return mIsFullyLoaded || mError;
925 }
926
927 //******************************************************************************
928 /* void lockImage() */
929 NS_IMETHODIMP
930 VectorImage::LockImage()
931 {
932 // This method is for image-discarding, which only applies to RasterImages.
933 return NS_OK;
934 }
935
936 //******************************************************************************
937 /* void unlockImage() */
938 NS_IMETHODIMP
939 VectorImage::UnlockImage()
940 {
941 // This method is for image-discarding, which only applies to RasterImages.
942 return NS_OK;
943 }
944
945 //******************************************************************************
946 /* void requestDiscard() */
947 NS_IMETHODIMP
948 VectorImage::RequestDiscard()
949 {
950 SurfaceCache::Discard(this);
951 return NS_OK;
952 }
953
954 //******************************************************************************
955 /* void resetAnimation (); */
956 NS_IMETHODIMP
957 VectorImage::ResetAnimation()
958 {
959 if (mError)
960 return NS_ERROR_FAILURE;
961
962 if (!mIsFullyLoaded || !mHaveAnimations) {
963 return NS_OK; // There are no animations to be reset.
964 }
965
966 mSVGDocumentWrapper->ResetAnimation();
967
968 return NS_OK;
969 }
970
971 NS_IMETHODIMP_(float)
972 VectorImage::GetFrameIndex(uint32_t aWhichFrame)
973 {
974 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
975 return aWhichFrame == FRAME_FIRST
976 ? 0.0f
977 : mSVGDocumentWrapper->GetCurrentTime();
978 }
979
980 //------------------------------------------------------------------------------
981 // nsIRequestObserver methods
982
983 //******************************************************************************
984 /* void onStartRequest(in nsIRequest request, in nsISupports ctxt); */
985 NS_IMETHODIMP
986 VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
987 {
988 MOZ_ASSERT(!mSVGDocumentWrapper,
989 "Repeated call to OnStartRequest -- can this happen?");
990
991 mSVGDocumentWrapper = new SVGDocumentWrapper();
992 nsresult rv = mSVGDocumentWrapper->OnStartRequest(aRequest, aCtxt);
993 if (NS_FAILED(rv)) {
994 mSVGDocumentWrapper = nullptr;
995 mError = true;
996 return rv;
997 }
998
999 // Sending StartDecode will block page load until the document's ready. (We
1000 // unblock it by sending StopDecode in OnSVGDocumentLoaded or
1001 // OnSVGDocumentError.)
1002 if (mStatusTracker) {
1003 nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
1004 imgDecoderObserver* observer = clone->GetDecoderObserver();
1005 observer->OnStartDecode();
1006 ImageStatusDiff diff = mStatusTracker->Difference(clone);
1007 mStatusTracker->ApplyDifference(diff);
1008 mStatusTracker->SyncNotifyDifference(diff);
1009 }
1010
1011 // Create a listener to wait until the SVG document is fully loaded, which
1012 // will signal that this image is ready to render. Certain error conditions
1013 // will prevent us from ever getting this notification, so we also create a
1014 // listener that waits for parsing to complete and cancels the
1015 // SVGLoadEventListener if needed. The listeners are automatically attached
1016 // to the document by their constructors.
1017 nsIDocument* document = mSVGDocumentWrapper->GetDocument();
1018 mLoadEventListener = new SVGLoadEventListener(document, this);
1019 mParseCompleteListener = new SVGParseCompleteListener(document, this);
1020
1021 return NS_OK;
1022 }
1023
1024 //******************************************************************************
1025 /* void onStopRequest(in nsIRequest request, in nsISupports ctxt,
1026 in nsresult status); */
1027 NS_IMETHODIMP
1028 VectorImage::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
1029 nsresult aStatus)
1030 {
1031 if (mError)
1032 return NS_ERROR_FAILURE;
1033
1034 return mSVGDocumentWrapper->OnStopRequest(aRequest, aCtxt, aStatus);
1035 }
1036
1037 void
1038 VectorImage::OnSVGDocumentParsed()
1039 {
1040 MOZ_ASSERT(mParseCompleteListener, "Should have the parse complete listener");
1041 MOZ_ASSERT(mLoadEventListener, "Should have the load event listener");
1042
1043 if (!mSVGDocumentWrapper->GetRootSVGElem()) {
1044 // This is an invalid SVG document. It may have failed to parse, or it may
1045 // be missing the <svg> root element, or the <svg> root element may not
1046 // declare the correct namespace. In any of these cases, we'll never be
1047 // notified that the SVG finished loading, so we need to treat this as an error.
1048 OnSVGDocumentError();
1049 }
1050 }
1051
1052 void
1053 VectorImage::CancelAllListeners()
1054 {
1055 if (mParseCompleteListener) {
1056 mParseCompleteListener->Cancel();
1057 mParseCompleteListener = nullptr;
1058 }
1059 if (mLoadEventListener) {
1060 mLoadEventListener->Cancel();
1061 mLoadEventListener = nullptr;
1062 }
1063 }
1064
1065 void
1066 VectorImage::OnSVGDocumentLoaded()
1067 {
1068 MOZ_ASSERT(mSVGDocumentWrapper->GetRootSVGElem(),
1069 "Should have parsed successfully");
1070 MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations,
1071 "These flags shouldn't get set until OnSVGDocumentLoaded. "
1072 "Duplicate calls to OnSVGDocumentLoaded?");
1073
1074 CancelAllListeners();
1075
1076 // XXX Flushing is wasteful if embedding frame hasn't had initial reflow.
1077 mSVGDocumentWrapper->FlushLayout();
1078
1079 mIsFullyLoaded = true;
1080 mHaveAnimations = mSVGDocumentWrapper->IsAnimated();
1081
1082 // Start listening to our image for rendering updates.
1083 mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this);
1084
1085 // Tell *our* observers that we're done loading.
1086 if (mStatusTracker) {
1087 nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
1088 imgDecoderObserver* observer = clone->GetDecoderObserver();
1089
1090 observer->OnStartContainer(); // Signal that width/height are available.
1091 observer->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
1092 observer->OnStopFrame();
1093 observer->OnStopDecode(NS_OK); // Unblock page load.
1094
1095 ImageStatusDiff diff = mStatusTracker->Difference(clone);
1096 mStatusTracker->ApplyDifference(diff);
1097 mStatusTracker->SyncNotifyDifference(diff);
1098 }
1099
1100 EvaluateAnimation();
1101 }
1102
1103 void
1104 VectorImage::OnSVGDocumentError()
1105 {
1106 CancelAllListeners();
1107
1108 // XXXdholbert Need to do something more for the parsing failed case -- right
1109 // now, this just makes us draw the "object" icon, rather than the (jagged)
1110 // "broken image" icon. See bug 594505.
1111 mError = true;
1112
1113 if (mStatusTracker) {
1114 nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
1115 imgDecoderObserver* observer = clone->GetDecoderObserver();
1116
1117 // Unblock page load.
1118 observer->OnStopDecode(NS_ERROR_FAILURE);
1119 ImageStatusDiff diff = mStatusTracker->Difference(clone);
1120 mStatusTracker->ApplyDifference(diff);
1121 mStatusTracker->SyncNotifyDifference(diff);
1122 }
1123 }
1124
1125 //------------------------------------------------------------------------------
1126 // nsIStreamListener method
1127
1128 //******************************************************************************
1129 /* void onDataAvailable(in nsIRequest request, in nsISupports ctxt,
1130 in nsIInputStream inStr, in unsigned long sourceOffset,
1131 in unsigned long count); */
1132 NS_IMETHODIMP
1133 VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt,
1134 nsIInputStream* aInStr, uint64_t aSourceOffset,
1135 uint32_t aCount)
1136 {
1137 if (mError)
1138 return NS_ERROR_FAILURE;
1139
1140 return mSVGDocumentWrapper->OnDataAvailable(aRequest, aCtxt, aInStr,
1141 aSourceOffset, aCount);
1142 }
1143
1144 // --------------------------
1145 // Invalidation helper method
1146
1147 void
1148 VectorImage::InvalidateObserversOnNextRefreshDriverTick()
1149 {
1150 if (mHaveAnimations) {
1151 mHasPendingInvalidation = true;
1152 } else {
1153 SendInvalidationNotifications();
1154 }
1155 }
1156
1157 } // namespace image
1158 } // namespace mozilla

mercurial