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; }