already_AddRefed<gfxASurface> PlanarYCbCrImage::GetAsSurface() { if (mSurface) { nsRefPtr<gfxASurface> result = mSurface.get(); return result.forget(); } gfxImageFormat format = GetOffscreenFormat(); gfxIntSize size(mSize); gfxUtils::GetYCbCrToRGBDestFormatAndSize(mData, format, size); if (size.width > PlanarYCbCrImage::MAX_DIMENSION || size.height > PlanarYCbCrImage::MAX_DIMENSION) { NS_ERROR("Illegal image dest width or height"); return nullptr; } nsRefPtr<gfxImageSurface> imageSurface = new gfxImageSurface(mSize, format); gfxUtils::ConvertYCbCrToRGB(mData, format, mSize, imageSurface->Data(), imageSurface->Stride()); mSurface = imageSurface; return imageSurface.forget(); }
void BasicPlanarYCbCrImage::SetData(const Data& aData) { PlanarYCbCrImage::SetData(aData); // Do some sanity checks to prevent integer overflow if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION || aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) { NS_ERROR("Illegal image source width or height"); return; } gfxASurface::gfxImageFormat format = GetOffscreenFormat(); gfxIntSize size(mScaleHint); gfxUtils::GetYCbCrToRGBDestFormatAndSize(aData, format, size); if (size.width > PlanarYCbCrImage::MAX_DIMENSION || size.height > PlanarYCbCrImage::MAX_DIMENSION) { NS_ERROR("Illegal image dest width or height"); return; } mStride = gfxASurface::FormatStrideForWidth(format, size.width); mDecodedBuffer = AllocateBuffer(size.height * mStride); if (!mDecodedBuffer) { // out of memory return; } gfxUtils::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer, mStride); SetOffscreenFormat(format); mSize = size; }
already_AddRefed<gfx::SourceSurface> BasicPlanarYCbCrImage::GetAsSourceSurface() { NS_ASSERTION(NS_IsMainThread(), "Must be main thread"); if (mSourceSurface) { RefPtr<gfx::SourceSurface> surface(mSourceSurface); return surface.forget(); } if (!mDecodedBuffer) { return PlanarYCbCrImage::GetAsSourceSurface(); } gfxImageFormat format = GetOffscreenFormat(); RefPtr<gfx::SourceSurface> surface; { // Create a DrawTarget so that we can own the data inside mDecodeBuffer. // We create the target out of mDecodedBuffer, and get a snapshot from it. // The draw target is destroyed on scope exit and the surface owns the data. RefPtr<gfx::DrawTarget> drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForData(mDecodedBuffer, mSize, mStride, gfx::ImageFormatToSurfaceFormat(format)); if (!drawTarget) { return nullptr; } surface = drawTarget->Snapshot(); } mRecycleBin->RecycleBuffer(mDecodedBuffer.forget(), mSize.height * mStride); mSourceSurface = surface; return surface.forget(); }
already_AddRefed<gfxASurface> BasicPlanarYCbCrImage::GetAsSurface() { NS_ASSERTION(NS_IsMainThread(), "Must be main thread"); if (mSurface) { nsRefPtr<gfxASurface> result = mSurface.get(); return result.forget(); } if (!mDecodedBuffer) { return PlanarYCbCrImage::GetAsSurface(); } gfxASurface::gfxImageFormat format = GetOffscreenFormat(); nsRefPtr<gfxImageSurface> imgSurface = new gfxImageSurface(mDecodedBuffer, mSize, mStride, format); if (!imgSurface || imgSurface->CairoStatus() != 0) { return nullptr; } // Pass ownership of the buffer to the surface imgSurface->SetData(&imageSurfaceDataKey, mDecodedBuffer.forget(), DestroyBuffer); nsRefPtr<gfxASurface> result = imgSurface.get(); #if defined(XP_MACOSX) nsRefPtr<gfxQuartzImageSurface> quartzSurface = new gfxQuartzImageSurface(imgSurface); if (quartzSurface) { result = quartzSurface.forget(); } #endif mSurface = result; return result.forget(); }
void BasicPlanarYCbCrImage::SetData(const Data& aData) { // Do some sanity checks to prevent integer overflow if (aData.mYSize.width > 16384 || aData.mYSize.height > 16384) { NS_ERROR("Illegal width or height"); return; } gfxASurface::gfxImageFormat format = GetOffscreenFormat(); gfx::YUVType type = gfx::YV12; if (aData.mYSize.width == aData.mCbCrSize.width && aData.mYSize.height == aData.mCbCrSize.height) { type = gfx::YV24; } else if (aData.mYSize.width / 2 == aData.mCbCrSize.width && aData.mYSize.height == aData.mCbCrSize.height) { type = gfx::YV16; } else if (aData.mYSize.width / 2 == aData.mCbCrSize.width && aData.mYSize.height / 2 == aData.mCbCrSize.height ) { type = gfx::YV12; } else { NS_ERROR("YCbCr format not supported"); } // 'prescale' is true if the scaling is to be done as part of the // YCbCr to RGB conversion rather than on the RGB data when rendered. // We don't prescale if the image has an offset. See bug 639415. PRBool prescale = mScaleHint.width > 0 && mScaleHint.height > 0 && aData.mPicX == 0 && aData.mPicY == 0; if (format == gfxASurface::ImageFormatRGB16_565) { #if defined(HAVE_YCBCR_TO_RGB565) if (prescale && gfx::IsConvertYCbCrToRGB565Fast(aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height, type)) { // yuv2rgb16 with scale function not yet available for NEON prescale = PR_FALSE; } else #endif { // yuv2rgb16 function not yet available for non-NEON, and currently // using it requires a number of extra graphics operations, so it's // probably better to fall back to 24-bit RGB. // See https://bugzilla.mozilla.org/show_bug.cgi?id=641196 format = gfxASurface::ImageFormatRGB24; } } gfxIntSize size(prescale ? mScaleHint.width : aData.mPicSize.width, prescale ? mScaleHint.height : aData.mPicSize.height); mStride = gfxASurface::FormatStrideForWidth(format, size.width); mBuffer = new PRUint8[size.height * mStride]; if (!mBuffer) { // out of memory return; } // Convert from YCbCr to RGB now, scaling the image if needed. if (size != aData.mPicSize) { if (format == gfxASurface::ImageFormatRGB24) { gfx::ScaleYCbCrToRGB32(aData.mYChannel, aData.mCbChannel, aData.mCrChannel, mBuffer, aData.mPicSize.width, aData.mPicSize.height, size.width, size.height, aData.mYStride, aData.mCbCrStride, mStride, type, gfx::ROTATE_0, gfx::FILTER_BILINEAR); } else { NS_ERROR("Fail, ScaleYCbCrToRGB format not supported\n"); } } else { // no prescale #if defined(HAVE_YCBCR_TO_RGB565) if (format == gfxASurface::ImageFormatRGB16_565) { gfx::ConvertYCbCrToRGB565(aData.mYChannel, aData.mCbChannel, aData.mCrChannel, mBuffer, aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height, aData.mYStride, aData.mCbCrStride, mStride, type); } else // format != gfxASurface::ImageFormatRGB16_565 #endif gfx::ConvertYCbCrToRGB32(aData.mYChannel, aData.mCbChannel, aData.mCrChannel, mBuffer, aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height, aData.mYStride, aData.mCbCrStride, mStride, type); } SetOffscreenFormat(format); mSize = size; }
void BasicPlanarYCbCrImage::SetData(const Data& aData) { // Do some sanity checks to prevent integer overflow if (aData.mYSize.width > 16384 || aData.mYSize.height > 16384) { NS_ERROR("Illegal width or height"); return; } gfxASurface::gfxImageFormat format = GetOffscreenFormat(); gfx::YUVType type = gfx::YV12; if (aData.mYSize.width == aData.mCbCrSize.width && aData.mYSize.height == aData.mCbCrSize.height) { type = gfx::YV24; } else if (aData.mYSize.width / 2 == aData.mCbCrSize.width && aData.mYSize.height == aData.mCbCrSize.height) { type = gfx::YV16; } else if (aData.mYSize.width / 2 == aData.mCbCrSize.width && aData.mYSize.height / 2 == aData.mCbCrSize.height ) { type = gfx::YV12; } else { NS_ERROR("YCbCr format not supported"); } // 'prescale' is true if the scaling is to be done as part of the // YCbCr to RGB conversion rather than on the RGB data when rendered. PRBool prescale = mScaleHint.width > 0 && mScaleHint.height > 0 && mScaleHint != aData.mPicSize; if (format == gfxASurface::ImageFormatRGB16_565) { #if defined(HAVE_YCBCR_TO_RGB565) if (prescale && !gfx::IsScaleYCbCrToRGB565Fast(aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height, mScaleHint.width, mScaleHint.height, type, gfx::FILTER_BILINEAR) && gfx::IsConvertYCbCrToRGB565Fast(aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height, type)) { prescale = PR_FALSE; } #else // yuv2rgb16 function not available format = gfxASurface::ImageFormatRGB24; #endif } else if (format != gfxASurface::ImageFormatRGB24) { // No other formats are currently supported. format = gfxASurface::ImageFormatRGB24; } if (format == gfxASurface::ImageFormatRGB24) { /* ScaleYCbCrToRGB32 does not support a picture offset, nor 4:4:4 data. See bugs 639415 and 640073. */ if (aData.mPicX != 0 || aData.mPicY != 0 || type == gfx::YV24) prescale = PR_FALSE; } gfxIntSize size(prescale ? mScaleHint.width : aData.mPicSize.width, prescale ? mScaleHint.height : aData.mPicSize.height); mStride = gfxASurface::FormatStrideForWidth(format, size.width); mBuffer = new PRUint8[size.height * mStride]; if (!mBuffer) { // out of memory return; } // Convert from YCbCr to RGB now, scaling the image if needed. if (size != aData.mPicSize) { #if defined(HAVE_YCBCR_TO_RGB565) if (format == gfxASurface::ImageFormatRGB16_565) { gfx::ScaleYCbCrToRGB565(aData.mYChannel, aData.mCbChannel, aData.mCrChannel, mBuffer, aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height, size.width, size.height, aData.mYStride, aData.mCbCrStride, mStride, type, gfx::FILTER_BILINEAR); } else #endif gfx::ScaleYCbCrToRGB32(aData.mYChannel, aData.mCbChannel, aData.mCrChannel, mBuffer, aData.mPicSize.width, aData.mPicSize.height, size.width, size.height, aData.mYStride, aData.mCbCrStride, mStride, type, gfx::ROTATE_0, gfx::FILTER_BILINEAR); } else { // no prescale #if defined(HAVE_YCBCR_TO_RGB565) if (format == gfxASurface::ImageFormatRGB16_565) { gfx::ConvertYCbCrToRGB565(aData.mYChannel, aData.mCbChannel, aData.mCrChannel, mBuffer, aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height, aData.mYStride, aData.mCbCrStride, mStride, type); } else // format != gfxASurface::ImageFormatRGB16_565 #endif gfx::ConvertYCbCrToRGB32(aData.mYChannel, aData.mCbChannel, aData.mCrChannel, mBuffer, aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height, aData.mYStride, aData.mCbCrStride, mStride, type); } SetOffscreenFormat(format); mSize = size; }