Ejemplo n.º 1
0
nsresult
FrameAnimator::DrawFrameTo(const uint8_t* aSrcData, const IntRect& aSrcRect,
                           uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
                           uint8_t* aDstPixels, const IntRect& aDstRect,
                           BlendMethod aBlendMethod, const Maybe<IntRect>& aBlendRect)
{
  NS_ENSURE_ARG_POINTER(aSrcData);
  NS_ENSURE_ARG_POINTER(aDstPixels);

  // According to both AGIF and APNG specs, offsets are unsigned
  if (aSrcRect.x < 0 || aSrcRect.y < 0) {
    NS_WARNING("FrameAnimator::DrawFrameTo: negative offsets not allowed");
    return NS_ERROR_FAILURE;
  }
  // Outside the destination frame, skip it
  if ((aSrcRect.x > aDstRect.width) || (aSrcRect.y > aDstRect.height)) {
    return NS_OK;
  }

  if (aSrcPaletteLength) {
    // Larger than the destination frame, clip it
    int32_t width = std::min(aSrcRect.width, aDstRect.width - aSrcRect.x);
    int32_t height = std::min(aSrcRect.height, aDstRect.height - aSrcRect.y);

    // The clipped image must now fully fit within destination image frame
    NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
                 (aSrcRect.x + width <= aDstRect.width) &&
                 (aSrcRect.y + height <= aDstRect.height),
                "FrameAnimator::DrawFrameTo: Invalid aSrcRect");

    // clipped image size may be smaller than source, but not larger
    NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
      "FrameAnimator::DrawFrameTo: source must be smaller than dest");

    // Get pointers to image data
    const uint8_t* srcPixels = aSrcData + aSrcPaletteLength;
    uint32_t* dstPixels = reinterpret_cast<uint32_t*>(aDstPixels);
    const uint32_t* colormap = reinterpret_cast<const uint32_t*>(aSrcData);

    // Skip to the right offset
    dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.width);
    if (!aSrcHasAlpha) {
      for (int32_t r = height; r > 0; --r) {
        for (int32_t c = 0; c < width; c++) {
          dstPixels[c] = colormap[srcPixels[c]];
        }
        // Go to the next row in the source resp. destination image
        srcPixels += aSrcRect.width;
        dstPixels += aDstRect.width;
      }
    } else {
      for (int32_t r = height; r > 0; --r) {
        for (int32_t c = 0; c < width; c++) {
          const uint32_t color = colormap[srcPixels[c]];
          if (color) {
            dstPixels[c] = color;
          }
        }
        // Go to the next row in the source resp. destination image
        srcPixels += aSrcRect.width;
        dstPixels += aDstRect.width;
      }
    }
  } else {
    pixman_image_t* src =
      pixman_image_create_bits(
          aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
          aSrcRect.width, aSrcRect.height,
          reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(aSrcData)),
          aSrcRect.width * 4);
    if (!src) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    pixman_image_t* dst =
      pixman_image_create_bits(PIXMAN_a8r8g8b8,
                               aDstRect.width,
                               aDstRect.height,
                               reinterpret_cast<uint32_t*>(aDstPixels),
                               aDstRect.width * 4);
    if (!dst) {
      pixman_image_unref(src);
      return NS_ERROR_OUT_OF_MEMORY;
    }

    // XXX(seth): This is inefficient but we'll remove it quite soon when we
    // move frame compositing into SurfacePipe. For now we need this because
    // RemoveFrameRectFilter has transformed PNG frames with frame rects into
    // imgFrame's with no frame rects, but with a region of 0 alpha where the
    // frame rect should be. This works really nicely if we're using
    // BlendMethod::OVER, but BlendMethod::SOURCE will result in that frame rect
    // area overwriting the previous frame, which makes the animation look
    // wrong. This quick hack fixes that by first compositing the whle new frame
    // with BlendMethod::OVER, and then recopying the area that uses
    // BlendMethod::SOURCE if needed. To make this work, the decoder has to
    // provide a "blend rect" that tells us where to do this. This is just the
    // frame rect, but hidden in a way that makes it invisible to most of the
    // system, so we can keep eliminating dependencies on it.
    auto op = aBlendMethod == BlendMethod::SOURCE ? PIXMAN_OP_SRC
                                                  : PIXMAN_OP_OVER;

    if (aBlendMethod == BlendMethod::OVER || !aBlendRect ||
        (aBlendMethod == BlendMethod::SOURCE && aSrcRect.IsEqualEdges(*aBlendRect))) {
      // We don't need to do anything clever. (Or, in the case where no blend
      // rect was specified, we can't.)
      pixman_image_composite32(op,
                               src,
                               nullptr,
                               dst,
                               0, 0,
                               0, 0,
                               aSrcRect.x, aSrcRect.y,
                               aSrcRect.width, aSrcRect.height);
    } else {
      // We need to do the OVER followed by SOURCE trick above.
      pixman_image_composite32(PIXMAN_OP_OVER,
                               src,
                               nullptr,
                               dst,
                               0, 0,
                               0, 0,
                               aSrcRect.x, aSrcRect.y,
                               aSrcRect.width, aSrcRect.height);
      pixman_image_composite32(PIXMAN_OP_SRC,
                               src,
                               nullptr,
                               dst,
                               aBlendRect->x, aBlendRect->y,
                               0, 0,
                               aBlendRect->x, aBlendRect->y,
                               aBlendRect->width, aBlendRect->height);
    }

    pixman_image_unref(src);
    pixman_image_unref(dst);
  }

  return NS_OK;
}