Beispiel #1
0
already_AddRefed<gfxDrawable>
VectorImage::LookupCachedSurface(const SVGDrawingParameters& aParams)
{
  // If we're not allowed to use a cached surface, don't attempt a lookup.
  if (aParams.flags & FLAG_BYPASS_SURFACE_CACHE) {
    return nullptr;
  }

  // We don't do any caching if we have animation, so don't bother with a lookup
  // in this case either.
  if (mHaveAnimations) {
    return nullptr;
  }

  LookupResult result =
    SurfaceCache::Lookup(ImageKey(this),
                         VectorSurfaceKey(aParams.size, aParams.svgContext));
  if (!result) {
    return nullptr;  // No matching surface, or the OS freed the volatile buffer.
  }

  RefPtr<SourceSurface> sourceSurface = result.Surface()->GetSourceSurface();
  if (!sourceSurface) {
    // Something went wrong. (Probably a GPU driver crash or device reset.)
    // Attempt to recover.
    RecoverFromLossOfSurfaces();
    return nullptr;
  }

  RefPtr<gfxDrawable> svgDrawable =
    new gfxSurfaceDrawable(sourceSurface, result.Surface()->GetSize());
  return svgDrawable.forget();
}
Beispiel #2
0
void
VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams)
{
  mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
  mSVGDocumentWrapper->FlushImageTransformInvalidation();

  RefPtr<gfxDrawingCallback> cb =
    new SVGDrawingCallback(mSVGDocumentWrapper,
                           IntRect(IntPoint(0, 0), aParams.viewportSize),
                           aParams.size,
                           aParams.flags);

  RefPtr<gfxDrawable> svgDrawable =
    new gfxCallbackDrawable(cb, aParams.size);

  bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
                     // Refuse to cache animated images:
                     // XXX(seth): We may remove this restriction in bug 922893.
                     mHaveAnimations ||
                     // The image is too big to fit in the cache:
                     !SurfaceCache::CanHold(aParams.size);
  if (bypassCache) {
    return Show(svgDrawable, aParams);
  }

  // We're about to rerasterize, which may mean that some of the previous
  // surfaces we've rasterized aren't useful anymore. We can allow them to
  // expire from the cache by unlocking them here, and then sending out an
  // invalidation. If this image is locked, any surfaces that are still useful
  // will become locked again when Draw touches them, and the remainder will
  // eventually expire.
  SurfaceCache::UnlockSurfaces(ImageKey(this));

  // Try to create an imgFrame, initializing the surface it contains by drawing
  // our gfxDrawable into it. (We use FILTER_NEAREST since we never scale here.)
  RefPtr<imgFrame> frame = new imgFrame;
  nsresult rv =
    frame->InitWithDrawable(svgDrawable, aParams.size,
                            SurfaceFormat::B8G8R8A8,
                            Filter::POINT, aParams.flags);

  // If we couldn't create the frame, it was probably because it would end
  // up way too big. Generally it also wouldn't fit in the cache, but the prefs
  // could be set such that the cache isn't the limiting factor.
  if (NS_FAILED(rv)) {
    return Show(svgDrawable, aParams);
  }

  // Take a strong reference to the frame's surface and make sure it hasn't
  // already been purged by the operating system.
  RefPtr<SourceSurface> surface = frame->GetSurface();
  if (!surface) {
    return Show(svgDrawable, aParams);
  }

  // Attempt to cache the frame.
  SurfaceCache::Insert(frame, ImageKey(this),
                       VectorSurfaceKey(aParams.size,
                                        aParams.svgContext,
                                        aParams.animationTime));

  // Draw.
  RefPtr<gfxDrawable> drawable =
    new gfxSurfaceDrawable(surface, aParams.size);
  Show(drawable, aParams);

  // Send out an invalidation so that surfaces that are still in use get
  // re-locked. See the discussion of the UnlockSurfaces call above.
  mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
                                       GetMaxSizedIntRect());
}
Beispiel #3
0
VectorImage::Draw(gfxContext* aContext,
                  const nsIntSize& aSize,
                  const ImageRegion& aRegion,
                  uint32_t aWhichFrame,
                  Filter aFilter,
                  const Maybe<SVGImageContext>& aSVGContext,
                  uint32_t aFlags)
{
  if (aWhichFrame > FRAME_MAX_VALUE) {
    return DrawResult::BAD_ARGS;
  }

  if (!aContext) {
    return DrawResult::BAD_ARGS;
  }

  if (mError) {
    return DrawResult::BAD_IMAGE;
  }

  if (!mIsFullyLoaded) {
    return DrawResult::NOT_READY;
  }

  if (mIsDrawing) {
    NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
    return DrawResult::TEMPORARY_ERROR;
  }

  if (mAnimationConsumers == 0 && mProgressTracker) {
    mProgressTracker->OnUnlockedDraw();
  }

  AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
  mIsDrawing = true;

  Maybe<SVGImageContext> svgContext;
  // If FLAG_FORCE_PRESERVEASPECTRATIO_NONE bit is set, that mean we should
  // overwrite SVG preserveAspectRatio attibute of this image with none, and
  // always stretch this image to viewport non-uniformly.
  // And we can do this only if the caller pass in the the SVG viewport, via
  // aSVGContext.
  if ((aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) && aSVGContext.isSome()) {
    Maybe<SVGPreserveAspectRatio> aspectRatio =
      Some(SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE,
                                  SVG_MEETORSLICE_UNKNOWN));
    svgContext =
      Some(SVGImageContext(aSVGContext->GetViewportSize(),
                           aspectRatio));
  } else {
    svgContext = aSVGContext;
  }

  float animTime =
    (aWhichFrame == FRAME_FIRST) ? 0.0f
                                 : mSVGDocumentWrapper->GetCurrentTime();
  AutoSVGRenderingState autoSVGState(svgContext, animTime,
                                     mSVGDocumentWrapper->GetRootSVGElem());


  SVGDrawingParameters params(aContext, aSize, aRegion, aFilter,
                              svgContext, animTime, aFlags);

  if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
    CreateSurfaceAndShow(params);
    return DrawResult::SUCCESS;
  }

  LookupResult result =
    SurfaceCache::Lookup(ImageKey(this),
                         VectorSurfaceKey(params.size,
                                          params.svgContext,
                                          params.animationTime));

  // Draw.
  if (result) {
    RefPtr<SourceSurface> surface = result.DrawableRef()->GetSurface();
    if (surface) {
      RefPtr<gfxDrawable> svgDrawable =
        new gfxSurfaceDrawable(surface, result.DrawableRef()->GetSize());
      Show(svgDrawable, params);
      return DrawResult::SUCCESS;
    }

    // We lost our surface due to some catastrophic event.
    RecoverFromLossOfSurfaces();
  }

  CreateSurfaceAndShow(params);

  return DrawResult::SUCCESS;
}
Beispiel #4
0
VectorImage::Draw(gfxContext* aContext,
                  const nsIntSize& aSize,
                  const ImageRegion& aRegion,
                  uint32_t aWhichFrame,
                  GraphicsFilter aFilter,
                  const Maybe<SVGImageContext>& aSVGContext,
                  uint32_t aFlags)
{
  if (aWhichFrame > FRAME_MAX_VALUE) {
    return DrawResult::BAD_ARGS;
  }

  if (!aContext) {
    return DrawResult::BAD_ARGS;
  }

  if (mError) {
    return DrawResult::BAD_IMAGE;
  }

  if (!mIsFullyLoaded) {
    return DrawResult::NOT_READY;
  }

  if (mIsDrawing) {
    NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
    return DrawResult::TEMPORARY_ERROR;
  }

  if (mAnimationConsumers == 0 && mProgressTracker) {
    mProgressTracker->OnUnlockedDraw();
  }

  AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
  mIsDrawing = true;

  float animTime =
    (aWhichFrame == FRAME_FIRST) ? 0.0f
                                 : mSVGDocumentWrapper->GetCurrentTime();
  AutoSVGRenderingState autoSVGState(aSVGContext, animTime,
                                     mSVGDocumentWrapper->GetRootSVGElem());

  // Pack up the drawing parameters.
  SVGDrawingParameters params(aContext, aSize, aRegion, aFilter,
                              aSVGContext, animTime, aFlags);

  if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
    CreateSurfaceAndShow(params);
    return DrawResult::SUCCESS;
  }

  DrawableFrameRef frameRef =
    SurfaceCache::Lookup(ImageKey(this),
                         VectorSurfaceKey(params.size,
                                          params.svgContext,
                                          params.animationTime));

  // Draw.
  if (frameRef) {
    RefPtr<SourceSurface> surface = frameRef->GetSurface();
    if (surface) {
      nsRefPtr<gfxDrawable> svgDrawable =
        new gfxSurfaceDrawable(surface, frameRef->GetSize());
      Show(svgDrawable, params);
      return DrawResult::SUCCESS;
    }

    // We lost our surface due to some catastrophic event.
    RecoverFromLossOfSurfaces();
  }

  CreateSurfaceAndShow(params);

  return DrawResult::SUCCESS;
}
void
VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams)
{
  mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
  mSVGDocumentWrapper->FlushImageTransformInvalidation();

  nsRefPtr<gfxDrawingCallback> cb =
    new SVGDrawingCallback(mSVGDocumentWrapper,
                           nsIntRect(nsIntPoint(0, 0), aParams.viewportSize),
                           aParams.size,
                           aParams.flags);

  nsRefPtr<gfxDrawable> svgDrawable =
    new gfxCallbackDrawable(cb, ThebesIntSize(aParams.size));

  // We take an early exit without using the surface cache if too large,
  // because for vector images this can cause bad perf issues if large sizes
  // are scaled repeatedly (a rather common scenario) that can quickly exhaust
  // the cache.
  // Similar to max image size calculations, this has a max cap and size check.
  // max cap = 8000 (pixels); size check = 5% of cache
  int32_t maxDimension = 8000;
  int32_t maxCacheElemSize = (gfxPrefs::ImageMemSurfaceCacheMaxSizeKB() * 1024) / 20;
  
  bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
                     // Refuse to cache animated images:
                     // XXX(seth): We may remove this restriction in bug 922893.
                     mHaveAnimations ||
                     // The image is too big to fit in the cache:
                     !SurfaceCache::CanHold(aParams.size) ||
                     // Image x or y is larger than our cache cap:
                     aParams.size.width > maxDimension ||
                     aParams.size.height > maxDimension;
  if (!bypassCache) {
    // This is separated out to make sure width and height are sane at this point
    // and the result can't overflow. Note: keep maxDimension low enough so that
    // (maxDimension)^2 x 4 < INT32_MAX.
    // Assuming surface size for any rendered vector image is RGBA, so 4Bpp.
    bypassCache = (aParams.size.width * aParams.size.height * 4) > maxCacheElemSize;
  }

  if (bypassCache)
    return Show(svgDrawable, aParams);

  // Try to create an imgFrame, initializing the surface it contains by drawing
  // our gfxDrawable into it. (We use FILTER_NEAREST since we never scale here.)
  nsRefPtr<imgFrame> frame = new imgFrame;
  nsresult rv =
    frame->InitWithDrawable(svgDrawable, ThebesIntSize(aParams.size),
                            SurfaceFormat::B8G8R8A8,
                            GraphicsFilter::FILTER_NEAREST, aParams.flags);

  // If we couldn't create the frame, it was probably because it would end
  // up way too big. Generally it also wouldn't fit in the cache, but the prefs
  // could be set such that the cache isn't the limiting factor.
  if (NS_FAILED(rv))
    return Show(svgDrawable, aParams);

  // Take a strong reference to the frame's surface and make sure it hasn't
  // already been purged by the operating system.
  RefPtr<SourceSurface> surface = frame->GetSurface();
  if (!surface)
    return Show(svgDrawable, aParams);

  // Attempt to cache the frame.
  SurfaceCache::Insert(frame, ImageKey(this),
                       VectorSurfaceKey(aParams.size,
                                        aParams.svgContext,
                                        aParams.animationTime),
                       Lifetime::Transient);

  // Draw.
  nsRefPtr<gfxDrawable> drawable =
    new gfxSurfaceDrawable(surface, ThebesIntSize(aParams.size));
  Show(drawable, aParams);
}