/* static */ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvasEl, const Maybe<IntRect>& aCropRect, ErrorResult& aRv) { if (aCanvasEl.Width() == 0 || aCanvasEl.Height() == 0) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } RefPtr<SourceSurface> surface = GetSurfaceFromElement(aGlobal, aCanvasEl, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } // Crop the source surface if needed. RefPtr<SourceSurface> croppedSurface; IntRect cropRect = aCropRect.valueOr(IntRect()); // If the HTMLCanvasElement's rendering context is WebGL, then the snapshot // we got from the HTMLCanvasElement is a DataSourceSurface which is a copy // of the rendering context. We handle cropping in this case. if ((aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL1 || aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL2) && aCropRect.isSome()) { // The _surface_ must be a DataSourceSurface. MOZ_ASSERT(surface->GetType() == SurfaceType::DATA, "The snapshot SourceSurface from WebGL rendering contest is not \ DataSourceSurface."); RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect); cropRect.MoveTo(0, 0); }
LexerTransition<nsGIFDecoder2::State> nsGIFDecoder2::ReadImageDescriptor(const char* aData) { if (mGIFStruct.images_decoded == 1) { if (!HasAnimation()) { // We should've already called PostIsAnimated(); this must be a corrupt // animated image with a first frame timeout of zero. Signal that we're // animated now, before the first-frame decode early exit below, so that // RasterImage can detect that this happened. PostIsAnimated(0); } if (IsFirstFrameDecode()) { // We're about to get a second frame, but we only want the first. Stop // decoding now. FinishInternal(); return Transition::TerminateSuccess(); } if (mDownscaler) { MOZ_ASSERT_UNREACHABLE("Doing downscale-during-decode for an animated " "image?"); mDownscaler.reset(); } } IntRect frameRect; // Get image offsets with respect to the screen origin. frameRect.x = LittleEndian::readUint16(aData + 0); frameRect.y = LittleEndian::readUint16(aData + 2); frameRect.width = LittleEndian::readUint16(aData + 4); frameRect.height = LittleEndian::readUint16(aData + 6); if (!mGIFStruct.images_decoded) { // Work around GIF files where // * at least one of the logical screen dimensions is smaller than the // same dimension in the first image, or // * GIF87a files where the first image's dimensions do not match the // logical screen dimensions. if (mGIFStruct.screen_height < frameRect.height || mGIFStruct.screen_width < frameRect.width || mGIFStruct.version == 87) { mGIFStruct.screen_height = frameRect.height; mGIFStruct.screen_width = frameRect.width; frameRect.MoveTo(0, 0); } // Create the image container with the right size. BeginGIF(); if (HasError()) { // Setting the size led to an error. return Transition::TerminateFailure(); } // If we're doing a metadata decode, we're done. if (IsMetadataDecode()) { CheckForTransparency(frameRect); FinishInternal(); return Transition::TerminateSuccess(); } } // Work around broken GIF files that have zero frame width or height; in this // case, we'll treat the frame as having the same size as the overall image. if (frameRect.height == 0 || frameRect.width == 0) { frameRect.height = mGIFStruct.screen_height; frameRect.width = mGIFStruct.screen_width; // If that still resulted in zero frame width or height, give up. if (frameRect.height == 0 || frameRect.width == 0) { return Transition::TerminateFailure(); } } // Determine |depth| (log base 2 of the number of colors in the palette). bool haveLocalColorTable = false; uint16_t depth = 0; uint8_t packedFields = aData[8]; if (packedFields & PACKED_FIELDS_COLOR_TABLE_BIT) { // Get the palette depth from the local color table. depth = (packedFields & PACKED_FIELDS_TABLE_DEPTH_MASK) + 1; haveLocalColorTable = true; } else { // Get the palette depth from the global color table. depth = mGIFStruct.global_colormap_depth; } // If the transparent color index is greater than the number of colors in the // color table, we may need a higher color depth than |depth| would specify. // Our internal representation of the image will instead use |realDepth|, // which is the smallest color depth that can accomodate the existing palette // *and* the transparent color index. uint16_t realDepth = depth; while (mGIFStruct.tpixel >= (1 << realDepth) && realDepth < 8) { realDepth++; } // Create a mask used to ensure that color values fit within the colormap. mColorMask = 0xFF >> (8 - realDepth); // Determine if this frame is interlaced or not. const bool isInterlaced = packedFields & PACKED_FIELDS_INTERLACED_BIT; // Create the SurfacePipe we'll use to write output for this frame. if (NS_FAILED(BeginImageFrame(frameRect, realDepth, isInterlaced))) { return Transition::TerminateFailure(); } // Clear state from last image. mGIFStruct.pixels_remaining = frameRect.width * frameRect.height; if (haveLocalColorTable) { // We have a local color table, so prepare to read it into the palette of // the current frame. mGIFStruct.local_colormap_size = 1 << depth; if (mGIFStruct.images_decoded == 0) { // The first frame has a local color table. Allocate space for it as we // use a BGRA or BGRX surface for the first frame; such surfaces don't // have their own palettes internally. mColormapSize = sizeof(uint32_t) << realDepth; if (!mGIFStruct.local_colormap) { mGIFStruct.local_colormap = static_cast<uint32_t*>(moz_xmalloc(mColormapSize)); } mColormap = mGIFStruct.local_colormap; } const size_t size = 3 << depth; if (mColormapSize > size) { // Clear the part of the colormap which will be unused with this palette. // If a GIF references an invalid palette entry, ensure the entry is opaque white. // This is needed for Skia as if it isn't, RGBX surfaces will cause blending issues // with Skia. memset(reinterpret_cast<uint8_t*>(mColormap) + size, 0xFF, mColormapSize - size); } MOZ_ASSERT(mColorTablePos == 0); // We read the local color table in unbuffered mode since it can be quite // large and it'd be preferable to avoid unnecessary copies. return Transition::ToUnbuffered(State::FINISHED_LOCAL_COLOR_TABLE, State::LOCAL_COLOR_TABLE, size); } // There's no local color table; copy the global color table into the palette // of the current frame. if (mGIFStruct.images_decoded > 0) { memcpy(mColormap, mGIFStruct.global_colormap, mColormapSize); } else { mColormap = mGIFStruct.global_colormap; } return Transition::To(State::IMAGE_DATA_BLOCK, BLOCK_HEADER_LEN); }