// Returns true if an image of aWidth x aHeight is allowed and legal. static bool AllowedImageSize(int32_t aWidth, int32_t aHeight) { // reject over-wide or over-tall images const int32_t k64KLimit = 0x0000FFFF; if (MOZ_UNLIKELY(aWidth > k64KLimit || aHeight > k64KLimit )) { NS_WARNING("image too big"); return false; } // protect against invalid sizes if (MOZ_UNLIKELY(aHeight <= 0 || aWidth <= 0)) { return false; } // check to make sure we don't overflow a 32-bit CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * 4; if (MOZ_UNLIKELY(!requiredBytes.isValid())) { NS_WARNING("width or height too large"); return false; } #if defined(XP_MACOSX) // CoreGraphics is limited to images < 32K in *height*, so clamp all surfaces // on the Mac to that height if (MOZ_UNLIKELY(aHeight > SHRT_MAX)) { NS_WARNING("image too big"); return false; } #endif return true; }
void ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv) { gfx::IntRect rect = aRect; // fix up negative dimensions if (rect.width < 0) { CheckedInt32 checkedX = CheckedInt32(rect.x) + rect.width; if (!checkedX.isValid()) { aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return; } rect.x = checkedX.value(); rect.width = -(rect.width); } if (rect.height < 0) { CheckedInt32 checkedY = CheckedInt32(rect.y) + rect.height; if (!checkedY.isValid()) { aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return; } rect.y = checkedY.value(); rect.height = -(rect.height); } mPictureRect = rect; }
void gfxXlibSurface::TakePixmap() { NS_ASSERTION(!mPixmapTaken, "I already own the Pixmap!"); mPixmapTaken = true; // The bit depth returned from Cairo is technically int, but this is // the last place we'd be worried about that scenario. unsigned int bitDepth = cairo_xlib_surface_get_depth(CairoSurface()); MOZ_ASSERT((bitDepth % 8) == 0, "Memory used not recorded correctly"); // Divide by 8 because surface_get_depth gives us the number of *bits* per // pixel. gfxIntSize size = GetSize(); CheckedInt32 totalBytes = CheckedInt32(size.width) * CheckedInt32(size.height) * (bitDepth/8); // Don't do anything in the "else" case. We could add INT32_MAX, but that // would overflow the memory used counter. It would also mean we tried for // a 2G image. For now, we'll just assert, MOZ_ASSERT(totalBytes.isValid(),"Did not expect to exceed 2Gb image"); if (totalBytes.isValid()) { RecordMemoryUsed(totalBytes.value()); } }
size_t BufferSizeFromStrideAndHeight(int32_t aStride, int32_t aHeight, int32_t aExtraBytes) { if (MOZ_UNLIKELY(aHeight <= 0)) { return 0; } // We limit the length returned to values that can be represented by int32_t // because we don't want to allocate buffers any bigger than that. This // allows for a buffer size of over 2 GiB which is already rediculously // large and will make the process janky. (Note the choice of the signed type // is deliberate because we specifically don't want the returned value to // overflow if someone stores the buffer length in an int32_t variable.) CheckedInt32 requiredBytes = CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes); if (MOZ_UNLIKELY(!requiredBytes.isValid())) { gfxWarning() << "Buffer size too big; returning zero"; return 0; } return requiredBytes.value(); }
/* static */ nsresult ImageEncoder::ExtractDataInternal(const nsAString& aType, const nsAString& aOptions, uint8_t* aImageBuffer, int32_t aFormat, const nsIntSize aSize, layers::Image* aImage, nsICanvasRenderingContextInternal* aContext, layers::AsyncCanvasRenderer* aRenderer, 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 (aRenderer) { NS_ConvertUTF16toUTF8 encoderType(aType); rv = aRenderer->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 = SurfaceFormat::A8R8G8B8_UINT32; uint32_t stride = GetAlignedStride<16>(aSize.width * 4); size_t length = BufferSizeFromStrideAndHeight(stride, aSize.height); data.SetCapacity(length); 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; RefPtr<layers::Image> image(aImage); dataSurface = GetBRGADataSourceSurfaceSync(image.forget()); 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 { CheckedInt32 requiredBytes = CheckedInt32(aSize.width) * CheckedInt32(aSize.height) * 4; if (MOZ_UNLIKELY(!requiredBytes.isValid())) { return NS_ERROR_INVALID_ARG; } // 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; }