void SourceSurfaceCGBitmapContext::DrawTargetWillChange() { if (mDrawTarget) { // This will break the weak reference we hold to mCg size_t stride = CGBitmapContextGetBytesPerRow(mCg); size_t height = CGBitmapContextGetHeight(mCg); size_t bufLen = BufferSizeFromStrideAndHeight(stride, height); if (bufLen == 0) { mDataHolder.Dealloc(); mData = nullptr; } else { static_assert(sizeof(decltype(mDataHolder[0])) == 1, "mDataHolder.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen"); mDataHolder.Realloc(/* actually an object count */ bufLen); mData = mDataHolder; // copy out the data from the CGBitmapContext // we'll maintain ownership of mData until // we transfer it to mImage memcpy(mData, CGBitmapContextGetData(mCg), bufLen); } // drop the current image for the data associated with the CGBitmapContext if (mImage) CGImageRelease(mImage); mImage = nullptr; mCg = nullptr; mDrawTarget = nullptr; } }
bool DataSourceSurfaceCG::InitFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) { if (aSize.width <= 0 || aSize.height <= 0) { return false; } size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); if (bufLen == 0) { mImage = nullptr; return false; } void *data = malloc(bufLen); memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat))); mFormat = aFormat; mImage = CreateCGImage(data, data, aSize, aStride, aFormat); if (!mImage) { free(data); return false; } return true; }
bool SourceSurfaceAlignedRawData::Init(const IntSize &aSize, SurfaceFormat aFormat, bool aClearMem, uint8_t aClearValue, int32_t aStride) { mFormat = aFormat; mStride = aStride ? aStride : GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat)); size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height); if (bufLen > 0) { bool zeroMem = aClearMem && !aClearValue; static_assert(sizeof(decltype(mArray[0])) == 1, "mArray.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen"); // AlignedArray uses cmalloc to zero mem for a fast path. mArray.Realloc(/* actually an object count */ bufLen, zeroMem); mSize = aSize; if (mArray && aClearMem && aClearValue) { memset(mArray, aClearValue, mStride * aSize.height); } } else { mArray.Dealloc(); mSize.SizeTo(0, 0); } return mArray != nullptr; }
bool SourceSurfaceAlignedRawData::Init(const IntSize &aSize, SurfaceFormat aFormat) { mFormat = aFormat; mStride = GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat)); size_t bufLen = BufferSizeFromStrideAndHeight(mStride, aSize.height); if (bufLen > 0) { static_assert(sizeof(decltype(mArray[0])) == 1, "mArray.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen"); mArray.Realloc(/* actually an object count */ bufLen); mSize = aSize; } else { mArray.Dealloc(); mSize.SizeTo(0, 0); } return mArray != nullptr; }
void CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize, int32_t aSrcStride, int32_t aBytesPerPixel) { MOZ_ASSERT(aBytesPerPixel > 0, "Negative stride for aDst not currently supported"); MOZ_ASSERT(BufferSizeFromStrideAndHeight(aSrcStride, aSrcSize.height) > 0, "How did we end up with a surface with such a big buffer?"); int packedStride = aSrcSize.width * aBytesPerPixel; if (aSrcStride == packedStride) { // aSrc is already packed, so we can copy with a single memcpy. memcpy(aDst, aSrc, packedStride * aSrcSize.height); } else { // memcpy one row at a time. for (int row = 0; row < aSrcSize.height; ++row) { memcpy(aDst, aSrc, packedStride); aSrc += aSrcStride; aDst += packedStride; } } }
bool SourceSurfaceCG::InitFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) { assert(aSize.width >= 0 && aSize.height >= 0); size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); if (bufLen == 0) { mImage = nullptr; return false; } void *data = malloc(bufLen); // Copy all the data except the stride padding on the very last // row since we can't guarantee that is readable. memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat))); mFormat = aFormat; mImage = CreateCGImage(data, data, aSize, aStride, aFormat); return mImage != nullptr; }
/* static */ nsresult ImageEncoder::ExtractDataInternal(const nsAString& aType, const nsAString& aOptions, uint8_t* aImageBuffer, int32_t aFormat, const nsIntSize aSize, layers::Image* aImage, nsICanvasRenderingContextInternal* aContext, nsIInputStream** aStream, imgIEncoder* aEncoder) { if (aSize.IsEmpty()) { return NS_ERROR_INVALID_ARG; } nsCOMPtr<nsIInputStream> imgStream; // get image bytes nsresult rv; if (aImageBuffer) { rv = ImageEncoder::GetInputStream( aSize.width, aSize.height, aImageBuffer, aFormat, aEncoder, nsPromiseFlatString(aOptions).get(), getter_AddRefs(imgStream)); } else if (aContext) { NS_ConvertUTF16toUTF8 encoderType(aType); rv = aContext->GetInputStream(encoderType.get(), nsPromiseFlatString(aOptions).get(), getter_AddRefs(imgStream)); } else if (aImage) { // It is safe to convert PlanarYCbCr format from YUV to RGB off-main-thread. // Other image formats could have problem to convert format off-main-thread. // So here it uses a help function GetBRGADataSourceSurfaceSync() to convert // format on main thread. if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) { nsTArray<uint8_t> data; layers::PlanarYCbCrImage* ycbcrImage = static_cast<layers::PlanarYCbCrImage*> (aImage); gfxImageFormat format = gfxImageFormat::ARGB32; uint32_t stride = GetAlignedStride<16>(aSize.width * 4); size_t length = BufferSizeFromStrideAndHeight(stride, aSize.height); data.SetCapacity(length); gfxUtils::ConvertYCbCrToRGB(*ycbcrImage->GetData(), format, aSize, data.Elements(), stride); rv = aEncoder->InitFromData(data.Elements(), aSize.width * aSize.height * 4, aSize.width, aSize.height, aSize.width * 4, imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions); } else { RefPtr<gfx::DataSourceSurface> dataSurface; dataSurface = GetBRGADataSourceSurfaceSync(aImage); DataSourceSurface::MappedSurface map; if (!dataSurface->Map(gfx::DataSourceSurface::MapType::READ, &map)) { return NS_ERROR_INVALID_ARG; } rv = aEncoder->InitFromData(map.mData, aSize.width * aSize.height * 4, aSize.width, aSize.height, aSize.width * 4, imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions); dataSurface->Unmap(); } if (NS_SUCCEEDED(rv)) { imgStream = do_QueryInterface(aEncoder); } } else { // no context, so we have to encode an empty image // note that if we didn't have a current context, the spec says we're // supposed to just return transparent black pixels of the canvas // dimensions. RefPtr<DataSourceSurface> emptyCanvas = Factory::CreateDataSourceSurfaceWithStride(IntSize(aSize.width, aSize.height), SurfaceFormat::B8G8R8A8, 4 * aSize.width, true); if (NS_WARN_IF(!emptyCanvas)) { return NS_ERROR_INVALID_ARG; } DataSourceSurface::MappedSurface map; if (!emptyCanvas->Map(DataSourceSurface::MapType::WRITE, &map)) { return NS_ERROR_INVALID_ARG; } rv = aEncoder->InitFromData(map.mData, aSize.width * aSize.height * 4, aSize.width, aSize.height, aSize.width * 4, imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions); emptyCanvas->Unmap(); if (NS_SUCCEEDED(rv)) { imgStream = do_QueryInterface(aEncoder); } } NS_ENSURE_SUCCESS(rv, rv); imgStream.forget(aStream); return rv; }
CGImageRef CreateCGImage(CGDataProviderReleaseDataCallback aCallback, void *aInfo, const void *aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) { //XXX: we should avoid creating this colorspace everytime CGColorSpaceRef colorSpace = nullptr; CGBitmapInfo bitinfo = 0; int bitsPerComponent = 0; int bitsPerPixel = 0; switch (aFormat) { case SurfaceFormat::B8G8R8A8: colorSpace = CGColorSpaceCreateDeviceRGB(); bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; bitsPerComponent = 8; bitsPerPixel = 32; break; case SurfaceFormat::B8G8R8X8: colorSpace = CGColorSpaceCreateDeviceRGB(); bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; bitsPerComponent = 8; bitsPerPixel = 32; break; case SurfaceFormat::A8: // XXX: why don't we set a colorspace here? bitsPerComponent = 8; bitsPerPixel = 8; break; default: MOZ_CRASH(); } size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); if (bufLen == 0) { return nullptr; } CGDataProviderRef dataProvider = CGDataProviderCreateWithData(aInfo, aData, bufLen, aCallback); CGImageRef image; if (aFormat == SurfaceFormat::A8) { CGFloat decode[] = {1.0, 0.0}; image = CGImageMaskCreate (aSize.width, aSize.height, bitsPerComponent, bitsPerPixel, aStride, dataProvider, decode, true); } else { image = CGImageCreate (aSize.width, aSize.height, bitsPerComponent, bitsPerPixel, aStride, colorSpace, bitinfo, dataProvider, nullptr, true, kCGRenderingIntentDefault); } CGDataProviderRelease(dataProvider); CGColorSpaceRelease(colorSpace); return image; }