Exemple #1
0
/* 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);
}