bool X11DataTextureSourceBasic::Update(gfx::DataSourceSurface* aSurface, nsIntRegion* aDestRegion, gfx::IntPoint* aSrcOffset) { // Reallocate our internal X11 surface if we don't have a DrawTarget yet, // or if we changed surface size or format since last update. if (!mBufferDrawTarget || (aSurface->GetSize() != mBufferDrawTarget->GetSize()) || (aSurface->GetFormat() != mBufferDrawTarget->GetFormat())) { nsRefPtr<gfxASurface> surf; gfxImageFormat imageFormat = SurfaceFormatToImageFormat(aSurface->GetFormat()); Display *display = DefaultXDisplay(); Screen *screen = DefaultScreenOfDisplay(display); XRenderPictFormat *xrenderFormat = gfxXlibSurface::FindRenderFormat(display, imageFormat); if (xrenderFormat) { surf = gfxXlibSurface::Create(screen, xrenderFormat, ThebesIntSize(aSurface->GetSize())); } if (!surf) { NS_WARNING("Couldn't create native surface, fallback to image surface"); surf = new gfxImageSurface(ThebesIntSize(aSurface->GetSize()), imageFormat); } mBufferDrawTarget = gfxPlatform::GetPlatform()-> CreateDrawTargetForSurface(surf, aSurface->GetSize()); } // Image contents have changed, upload to our DrawTarget // If aDestRegion is null, means we're updating the whole surface // Note : Incremental update with a source offset is only used on Mac. NS_ASSERTION(!aSrcOffset, "SrcOffset should not be used with linux OMTC basic"); if (aDestRegion) { nsIntRegionRectIterator iter(*aDestRegion); while (const nsIntRect* iterRect = iter.Next()) { IntRect srcRect(iterRect->x, iterRect->y, iterRect->width, iterRect->height); IntPoint dstPoint(iterRect->x, iterRect->y); // We're uploading regions to our buffer, so let's just copy contents over mBufferDrawTarget->CopySurface(aSurface, srcRect, dstPoint); } } else { // We're uploading the whole buffer, so let's just copy the full surface IntSize size = aSurface->GetSize(); mBufferDrawTarget->CopySurface(aSurface, IntRect(0, 0, size.width, size.height), IntPoint(0, 0)); } return true; }
static TemporaryRef<DataSourceSurface> YInvertImageSurface(DataSourceSurface* aSurf) { RefPtr<DataSourceSurface> temp = Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(), aSurf->GetFormat(), aSurf->Stride()); RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(BackendType::CAIRO, temp->GetData(), temp->GetSize(), temp->Stride(), temp->GetFormat()); nsRefPtr<gfxContext> ctx = new gfxContext(dt); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); ctx->Scale(1.0, -1.0); ctx->Translate(-gfxPoint(0.0, aSurf->GetSize().height)); nsRefPtr<gfxImageSurface> thebesSurf = new gfxImageSurface(aSurf->GetData(), ThebesIntSize(aSurf->GetSize()), aSurf->Stride(), SurfaceFormatToImageFormat(aSurf->GetFormat())); ctx->SetSource(thebesSurf); ctx->Paint(); return temp.forget(); }
void SharedSurface_EGLImage::Fence() { MutexAutoLock lock(mMutex); mGL->MakeCurrent(); if (!mPipeActive) { MOZ_ASSERT(!mSync); MOZ_ASSERT(!mPipeComplete); if (!mPipeFailed) { if (!CreateTexturePipe(mEGL, mGL, mFormats, Size(), &mProdTexForPipe, &mImage)) { mPipeFailed = true; } } if (!mPixels) { SurfaceFormat format = HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; mPixels = Factory::CreateDataSourceSurface(Size(), format); } nsRefPtr<gfxImageSurface> wrappedData = new gfxImageSurface(mPixels->GetData(), ThebesIntSize(mPixels->GetSize()), mPixels->Stride(), SurfaceFormatToImageFormat(mPixels->GetFormat())); ReadScreenIntoImageSurface(mGL, wrappedData); mPixels->MarkDirty(); return; } MOZ_ASSERT(mPipeActive); MOZ_ASSERT(mCurConsGL); if (mEGL->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) && mGL->IsExtensionSupported(GLContext::OES_EGL_sync)) { if (mSync) { MOZ_ALWAYS_TRUE( mEGL->fDestroySync(Display(), mSync) ); mSync = 0; } mSync = mEGL->fCreateSync(Display(), LOCAL_EGL_SYNC_FENCE, nullptr); if (mSync) { mGL->fFlush(); return; } } MOZ_ASSERT(!mSync); mGL->fFinish(); }
void VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams) { MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now"); gfxUtils::DrawPixelSnapped(aParams.context, aDrawable, ThebesIntSize(aParams.size), aParams.region, SurfaceFormat::B8G8R8A8, aParams.filter, aParams.flags, aParams.opacity); MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now"); mRenderingObserver->ResumeHonoringInvalidations(); }
already_AddRefed<gfxASurface> gfxOS2Platform::CreateOffscreenSurface(const IntSize & aSize, gfxContentType contentType) { gfxImageFormat format = OptimalFormatForContent(contentType); int stride = cairo_format_stride_for_width(static_cast<cairo_format_t>(format), aSize.width); // To avoid memory fragmentation, return a standard image surface // for small images (32x32x4 or 64x64x1). Their bitmaps will be // be allocated from libc's heap rather than system memory. nsRefPtr<gfxASurface> surf; if (stride * aSize.height <= 4096) { surf = new gfxImageSurface(ThebesIntSize(aSize), format); } else { surf = new gfxOS2Surface(ThebesIntSize(aSize), format); } return surf.forget(); }
void GLScreenBuffer::Readback(SharedSurface_GL* src, DataSourceSurface* dest) { MOZ_ASSERT(src && dest); DataSourceSurface::MappedSurface ms; dest->Map(DataSourceSurface::MapType::READ, &ms); nsRefPtr<gfxImageSurface> wrappedDest = new gfxImageSurface(ms.mData, ThebesIntSize(dest->GetSize()), ms.mStride, SurfaceFormatToImageFormat(dest->GetFormat())); DeprecatedReadback(src, wrappedDest); dest->Unmap(); }
gfxImageSurface* CopyableCanvasLayer::GetTempSurface(const IntSize& aSize, const gfxImageFormat aFormat) { if (!mCachedTempSurface || aSize.width != mCachedSize.width || aSize.height != mCachedSize.height || aFormat != mCachedFormat) { mCachedTempSurface = new gfxImageSurface(ThebesIntSize(aSize), aFormat); mCachedSize = aSize; mCachedFormat = aFormat; } MOZ_ASSERT(mCachedTempSurface->Stride() == mCachedTempSurface->Width() * 4); return mCachedTempSurface; }
void SharedSurface_Basic::Fence() { mGL->MakeCurrent(); ScopedBindFramebuffer autoFB(mGL, mFB); DataSourceSurface::MappedSurface map; mData->Map(DataSourceSurface::MapType::WRITE, &map); nsRefPtr<gfxImageSurface> wrappedData = new gfxImageSurface(map.mData, ThebesIntSize(mData->GetSize()), map.mStride, SurfaceFormatToImageFormat(mData->GetFormat())); ReadPixelsIntoImageSurface(mGL, wrappedData); mData->Unmap(); }
// Run the test for a texture client and a surface void TestTextureClientSurface(TextureClient* texture, gfxImageSurface* surface) { // client allocation ASSERT_TRUE(texture->CanExposeDrawTarget()); texture->AllocateForSurface(ToIntSize(surface->GetSize())); ASSERT_TRUE(texture->IsAllocated()); ASSERT_TRUE(texture->Lock(OPEN_READ_WRITE)); // client painting RefPtr<DrawTarget> dt = texture->GetAsDrawTarget(); RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface); dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint()); RefPtr<SourceSurface> snapshot = dt->Snapshot(); AssertSurfacesEqual(snapshot, source); dt = nullptr; // drop reference before calling Unlock() texture->Unlock(); // client serialization SurfaceDescriptor descriptor; ASSERT_TRUE(texture->ToSurfaceDescriptor(descriptor)); ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t); // host deserialization RefPtr<TextureHost> host = CreateBackendIndependentTextureHost(descriptor, nullptr, texture->GetFlags()); ASSERT_TRUE(host.get() != nullptr); ASSERT_EQ(host->GetFlags(), texture->GetFlags()); // host read ASSERT_TRUE(host->Lock()); RefPtr<mozilla::gfx::DataSourceSurface> hostDataSurface = host->GetAsSurface(); host->Unlock(); nsRefPtr<gfxImageSurface> hostSurface = new gfxImageSurface(hostDataSurface->GetData(), ThebesIntSize(hostDataSurface->GetSize()), hostDataSurface->Stride(), SurfaceFormatToImageFormat(hostDataSurface->GetFormat())); AssertSurfacesEqual(surface, hostSurface.get()); }
void WriteSnapshotToDumpFile_internal(T* aObj, DataSourceSurface* aSurf) { nsRefPtr<gfxImageSurface> deprecatedSurf = new gfxImageSurface(aSurf->GetData(), ThebesIntSize(aSurf->GetSize()), aSurf->Stride(), SurfaceFormatToImageFormat(aSurf->GetFormat())); nsCString string(aObj->Name()); string.Append('-'); string.AppendInt((uint64_t)aObj); if (gfxUtils::sDumpPaintFile) { fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading()); } deprecatedSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile); if (gfxUtils::sDumpPaintFile) { fprintf_stderr(gfxUtils::sDumpPaintFile, "\";"); } }
void GStreamerReader::VideoPreroll() { /* The first video buffer has reached the video sink. Get width and height */ LOG(PR_LOG_DEBUG, "Video preroll"); GstPad* sinkpad = gst_element_get_static_pad(GST_ELEMENT(mVideoAppSink), "sink"); int PARNumerator, PARDenominator; #if GST_VERSION_MAJOR >= 1 GstCaps* caps = gst_pad_get_current_caps(sinkpad); memset (&mVideoInfo, 0, sizeof (mVideoInfo)); gst_video_info_from_caps(&mVideoInfo, caps); mFormat = mVideoInfo.finfo->format; mPicture.width = mVideoInfo.width; mPicture.height = mVideoInfo.height; PARNumerator = GST_VIDEO_INFO_PAR_N(&mVideoInfo); PARDenominator = GST_VIDEO_INFO_PAR_D(&mVideoInfo); #else GstCaps* caps = gst_pad_get_negotiated_caps(sinkpad); gst_video_format_parse_caps(caps, &mFormat, &mPicture.width, &mPicture.height); if (!gst_video_parse_caps_pixel_aspect_ratio(caps, &PARNumerator, &PARDenominator)) { PARNumerator = 1; PARDenominator = 1; } #endif NS_ASSERTION(mPicture.width && mPicture.height, "invalid video resolution"); // Calculate display size according to pixel aspect ratio. nsIntRect pictureRect(0, 0, mPicture.width, mPicture.height); nsIntSize frameSize = nsIntSize(mPicture.width, mPicture.height); nsIntSize displaySize = nsIntSize(mPicture.width, mPicture.height); ScaleDisplayByAspectRatio(displaySize, float(PARNumerator) / float(PARDenominator)); // If video frame size is overflow, stop playing. if (IsValidVideoRegion(frameSize, pictureRect, displaySize)) { GstStructure* structure = gst_caps_get_structure(caps, 0); gst_structure_get_fraction(structure, "framerate", &fpsNum, &fpsDen); mInfo.mVideo.mDisplay = ThebesIntSize(displaySize.ToIntSize()); mInfo.mVideo.mHasVideo = true; } else { LOG(PR_LOG_DEBUG, "invalid video region"); Eos(); } gst_caps_unref(caps); gst_object_unref(sinkpad); }
bool TextureImageTextureSourceOGL::Update(gfx::DataSourceSurface* aSurface, TextureFlags aFlags, nsIntRegion* aDestRegion, gfx::IntPoint* aSrcOffset) { MOZ_ASSERT(mGL); if (!mGL) { NS_WARNING("trying to update TextureImageTextureSourceOGL without a GLContext"); return false; } MOZ_ASSERT(aSurface); nsIntSize size = ThebesIntSize(aSurface->GetSize()); if (!mTexImage || mTexImage->GetSize() != size || mTexImage->GetContentType() != gfx::ContentForFormat(aSurface->GetFormat())) { if (mAllowBigImage) { // XXX - clarify which size we want to use. IncrementalContentHost will // require the size of the destination surface to be different from // the size of aSurface. // See bug 893300 (tracks the implementation of ContentHost for new textures). mTexImage = mGL->CreateTextureImage(size, gfx::ContentForFormat(aSurface->GetFormat()), WrapMode(mGL, aFlags & TEXTURE_ALLOW_REPEAT), FlagsToGLFlags(aFlags)); } else { mTexImage = CreateBasicTextureImage(mGL, size, gfx::ContentForFormat(aSurface->GetFormat()), WrapMode(mGL, aFlags & TEXTURE_ALLOW_REPEAT), FlagsToGLFlags(aFlags)); } } mTexImage->UpdateFromDataSource(aSurface, aDestRegion, aSrcOffset); if (mTexImage->InUpdate()) { mTexImage->EndUpdate(); } return true; }
void GStreamerReader::VideoPreroll() { /* The first video buffer has reached the video sink. Get width and height */ LOG(PR_LOG_DEBUG, ("Video preroll")); GstPad* sinkpad = gst_element_get_static_pad(GST_ELEMENT(mVideoAppSink), "sink"); #if GST_VERSION_MAJOR >= 1 GstCaps* caps = gst_pad_get_current_caps(sinkpad); memset (&mVideoInfo, 0, sizeof (mVideoInfo)); gst_video_info_from_caps(&mVideoInfo, caps); mFormat = mVideoInfo.finfo->format; mPicture.width = mVideoInfo.width; mPicture.height = mVideoInfo.height; #else GstCaps* caps = gst_pad_get_negotiated_caps(sinkpad); gst_video_format_parse_caps(caps, &mFormat, &mPicture.width, &mPicture.height); #endif GstStructure* structure = gst_caps_get_structure(caps, 0); gst_structure_get_fraction(structure, "framerate", &fpsNum, &fpsDen); NS_ASSERTION(mPicture.width && mPicture.height, "invalid video resolution"); mInfo.mVideo.mDisplay = ThebesIntSize(mPicture.Size()); mInfo.mVideo.mHasVideo = true; gst_caps_unref(caps); gst_object_unref(sinkpad); }
void CanvasLayerD3D9::UpdateSurface() { if (!IsDirty() && mTexture) return; Painted(); if (!mTexture) { CreateTexture(); if (!mTexture) { NS_WARNING("CanvasLayerD3D9::Updated called but no texture present and creation failed!"); return; } } if (mGLContext) { SharedSurface* surf = mGLContext->RequestFrame(); if (!surf) return; SharedSurface_Basic* shareSurf = SharedSurface_Basic::Cast(surf); // WebGL reads entire surface. LockTextureRectD3D9 textureLock(mTexture); if (!textureLock.HasLock()) { NS_WARNING("Failed to lock CanvasLayer texture."); return; } D3DLOCKED_RECT rect = textureLock.GetLockRect(); DataSourceSurface* frameData = shareSurf->GetData(); // Scope for gfxContext, so it's destroyed early. { RefPtr<DrawTarget> mapDt = Factory::CreateDrawTargetForData(BACKEND_CAIRO, (uint8_t*)rect.pBits, shareSurf->Size(), rect.Pitch, FORMAT_B8G8R8A8); nsRefPtr<gfxImageSurface> thebesFrameData = new gfxImageSurface(frameData->GetData(), ThebesIntSize(frameData->GetSize()), frameData->Stride(), SurfaceFormatToImageFormat(frameData->GetFormat())); nsRefPtr<gfxContext> ctx = new gfxContext(mapDt); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); ctx->SetSource(thebesFrameData); ctx->Paint(); mapDt->Flush(); } } else { RECT r; r.left = mBounds.x; r.top = mBounds.y; r.right = mBounds.XMost(); r.bottom = mBounds.YMost(); LockTextureRectD3D9 textureLock(mTexture); if (!textureLock.HasLock()) { NS_WARNING("Failed to lock CanvasLayer texture."); return; } D3DLOCKED_RECT lockedRect = textureLock.GetLockRect(); nsRefPtr<gfxImageSurface> sourceSurface; if (mSurface->GetType() == gfxSurfaceTypeWin32) { sourceSurface = mSurface->GetAsImageSurface(); } else if (mSurface->GetType() == gfxSurfaceTypeImage) { sourceSurface = static_cast<gfxImageSurface*>(mSurface.get()); if (sourceSurface->Format() != gfxImageFormatARGB32 && sourceSurface->Format() != gfxImageFormatRGB24) { return; } } else { sourceSurface = new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height), gfxImageFormatARGB32); nsRefPtr<gfxContext> ctx = new gfxContext(sourceSurface); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); ctx->SetSource(mSurface); ctx->Paint(); } uint8_t *startBits = sourceSurface->Data(); uint32_t sourceStride = sourceSurface->Stride(); if (sourceSurface->Format() != gfxImageFormatARGB32) { mHasAlpha = false; } else { mHasAlpha = true; } for (int y = 0; y < mBounds.height; y++) { memcpy((uint8_t*)lockedRect.pBits + lockedRect.Pitch * y, startBits + sourceStride * y, mBounds.width * 4); } } }
gfxIntSize SharedRGBImage::GetSize() { return ThebesIntSize(mSize); }
bool StreamTextureSourceOGL::RetrieveTextureFromStream() { gl()->MakeCurrent(); SharedSurface* sharedSurf = mStream->SwapConsumer(); if (!sharedSurf) { // We don't have a valid surf to show yet. return false; } gl()->MakeCurrent(); mSize = IntSize(sharedSurf->Size().width, sharedSurf->Size().height); DataSourceSurface* toUpload = nullptr; switch (sharedSurf->Type()) { case SharedSurfaceType::GLTextureShare: { SharedSurface_GLTexture* glTexSurf = SharedSurface_GLTexture::Cast(sharedSurf); mTextureHandle = glTexSurf->ConsTexture(gl()); mTextureTarget = glTexSurf->ConsTextureTarget(); MOZ_ASSERT(mTextureHandle); mFormat = sharedSurf->HasAlpha() ? SurfaceFormat::R8G8B8A8 : SurfaceFormat::R8G8B8X8; break; } case SharedSurfaceType::EGLImageShare: { SharedSurface_EGLImage* eglImageSurf = SharedSurface_EGLImage::Cast(sharedSurf); eglImageSurf->AcquireConsumerTexture(gl(), &mTextureHandle, &mTextureTarget); MOZ_ASSERT(mTextureHandle); mFormat = sharedSurf->HasAlpha() ? SurfaceFormat::R8G8B8A8 : SurfaceFormat::R8G8B8X8; break; } #ifdef XP_MACOSX case SharedSurfaceType::IOSurface: { SharedSurface_IOSurface* glTexSurf = SharedSurface_IOSurface::Cast(sharedSurf); mTextureHandle = glTexSurf->ConsTexture(gl()); mTextureTarget = glTexSurf->ConsTextureTarget(); MOZ_ASSERT(mTextureHandle); mFormat = sharedSurf->HasAlpha() ? SurfaceFormat::R8G8B8A8 : SurfaceFormat::R8G8B8X8; break; } #endif case SharedSurfaceType::Basic: { toUpload = SharedSurface_Basic::Cast(sharedSurf)->GetData(); MOZ_ASSERT(toUpload); break; } default: MOZ_CRASH("Invalid SharedSurface type."); } if (toUpload) { // mBounds seems to end up as (0,0,0,0) a lot, so don't use it? nsIntSize size(ThebesIntSize(toUpload->GetSize())); nsIntRect rect(nsIntPoint(0,0), size); nsIntRegion bounds(rect); mFormat = UploadSurfaceToTexture(gl(), toUpload, bounds, mUploadTexture, true); mTextureHandle = mUploadTexture; mTextureTarget = LOCAL_GL_TEXTURE_2D; } MOZ_ASSERT(mTextureHandle); gl()->fBindTexture(mTextureTarget, mTextureHandle); gl()->fTexParameteri(mTextureTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); gl()->fTexParameteri(mTextureTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); ClearCachedFilter(); return true; }
void CopyableCanvasLayer::UpdateSurface(gfxASurface* aDestSurface, Layer* aMaskLayer) { if (!IsDirty()) return; Painted(); if (mDrawTarget) { mDrawTarget->Flush(); mSurface = gfxPlatform::GetPlatform()->CreateThebesSurfaceAliasForDrawTarget_hack(mDrawTarget); } if (!mGLContext && aDestSurface) { nsRefPtr<gfxContext> tmpCtx = new gfxContext(aDestSurface); tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE); CopyableCanvasLayer::PaintWithOpacity(tmpCtx, 1.0f, aMaskLayer); return; } if (mGLContext) { nsRefPtr<gfxImageSurface> readSurf; RefPtr<DataSourceSurface> readDSurf; nsRefPtr<gfxASurface> resultSurf; SharedSurface* sharedSurf = mGLContext->RequestFrame(); if (!sharedSurf) { NS_WARNING("Null frame received."); return; } IntSize readSize(sharedSurf->Size()); gfxImageFormat format = (GetContentFlags() & CONTENT_OPAQUE) ? gfxImageFormat::RGB24 : gfxImageFormat::ARGB32; if (aDestSurface) { resultSurf = aDestSurface; } else { resultSurf = GetTempSurface(readSize, format); } MOZ_ASSERT(resultSurf); if (resultSurf->CairoStatus() != 0) { MOZ_ASSERT(false, "Bad resultSurf->CairoStatus()."); return; } MOZ_ASSERT(sharedSurf->APIType() == APITypeT::OpenGL); SharedSurface_GL* surfGL = SharedSurface_GL::Cast(sharedSurf); if (surfGL->Type() == SharedSurfaceType::Basic) { // sharedSurf_Basic->mData must outlive readSurf and readDSurf. Alas, // readSurf and readDSurf may not leave the scope they were declared in. SharedSurface_Basic* sharedSurf_Basic = SharedSurface_Basic::Cast(surfGL); readDSurf = sharedSurf_Basic->GetData(); readSurf = new gfxImageSurface(readDSurf->GetData(), ThebesIntSize(readDSurf->GetSize()), readDSurf->Stride(), SurfaceFormatToImageFormat(readDSurf->GetFormat())); } else { if (ToIntSize(resultSurf->GetSize()) != readSize || !(readSurf = resultSurf->GetAsImageSurface()) || readSurf->Format() != format) { readSurf = GetTempSurface(readSize, format); } // Readback handles Flush/MarkDirty. mGLContext->Screen()->Readback(surfGL, readSurf); } MOZ_ASSERT(readSurf); bool needsPremult = surfGL->HasAlpha() && !mIsGLAlphaPremult; if (needsPremult) { readSurf->Flush(); gfxUtils::PremultiplyImageSurface(readSurf); readSurf->MarkDirty(); } if (readSurf != resultSurf) { readSurf->Flush(); nsRefPtr<gfxContext> ctx = new gfxContext(resultSurf); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); ctx->SetSource(readSurf); ctx->Paint(); } // If !aDestSurface then we will end up painting using mSurface, so // stick our surface into mSurface, so that the Paint() path is the same. if (!aDestSurface) { mSurface = resultSurf; } } }
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, ThebesIntSize(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); }