blob: 99b5cd14ee362bd3b614215b776a52111297e0fb [file] [log] [blame]
/*
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
* Copyright (C) 2008, 2009 Google, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace blink {
ImageFrame::ImageFrame()
: allocator_(nullptr),
has_alpha_(true),
pixel_format_(kN32),
status_(kFrameEmpty),
disposal_method_(kDisposeNotSpecified),
alpha_blend_source_(kBlendAtopPreviousFrame),
premultiply_alpha_(true),
pixels_changed_(false),
required_previous_frame_index_(kNotFound) {}
ImageFrame& ImageFrame::operator=(const ImageFrame& other) {
if (this == &other)
return *this;
bitmap_ = other.bitmap_;
// Be sure to assign this before calling SetStatus(), since SetStatus() may
// call NotifyBitmapIfPixelsChanged().
pixels_changed_ = other.pixels_changed_;
SetMemoryAllocator(other.GetAllocator());
SetOriginalFrameRect(other.OriginalFrameRect());
SetStatus(other.GetStatus());
SetDuration(other.Duration());
SetDisposalMethod(other.GetDisposalMethod());
SetAlphaBlendSource(other.GetAlphaBlendSource());
SetPremultiplyAlpha(other.PremultiplyAlpha());
// Be sure that this is called after we've called SetStatus(), since we
// look at our status to know what to do with the alpha value.
SetHasAlpha(other.HasAlpha());
pixel_format_ = other.pixel_format_;
SetRequiredPreviousFrameIndex(other.RequiredPreviousFrameIndex());
return *this;
}
void ImageFrame::ClearPixelData() {
bitmap_.reset();
status_ = kFrameEmpty;
// NOTE: Do not reset other members here; ClearFrameBufferCache()
// calls this to free the bitmap data, but other functions like
// InitFrameBuffer() and FrameComplete() may still need to read
// other metadata out of this frame later.
}
void ImageFrame::ZeroFillPixelData() {
bitmap_.eraseARGB(0, 0, 0, 0);
has_alpha_ = true;
}
bool ImageFrame::CopyBitmapData(const ImageFrame& other) {
DCHECK_NE(this, &other);
has_alpha_ = other.has_alpha_;
pixel_format_ = other.pixel_format_;
bitmap_.reset();
SkImageInfo info = other.bitmap_.info();
if (!bitmap_.tryAllocPixels(info)) {
return false;
}
if (!other.bitmap_.readPixels(info, bitmap_.getPixels(), bitmap_.rowBytes(),
0, 0))
return false;
status_ = kFrameInitialized;
return true;
}
bool ImageFrame::TakeBitmapDataIfWritable(ImageFrame* other) {
DCHECK(other);
DCHECK_EQ(kFrameComplete, other->status_);
DCHECK_EQ(kFrameEmpty, status_);
DCHECK_NE(this, other);
if (other->bitmap_.isImmutable())
return false;
has_alpha_ = other->has_alpha_;
pixel_format_ = other->pixel_format_;
bitmap_.reset();
bitmap_.swap(other->bitmap_);
other->status_ = kFrameEmpty;
status_ = kFrameInitialized;
return true;
}
bool ImageFrame::AllocatePixelData(int new_width,
int new_height,
sk_sp<SkColorSpace> color_space) {
// AllocatePixelData() should only be called once.
DCHECK(!Width() && !Height());
SkImageInfo info = SkImageInfo::MakeN32(
new_width, new_height,
premultiply_alpha_ ? kPremul_SkAlphaType : kUnpremul_SkAlphaType,
std::move(color_space));
if (pixel_format_ == kRGBA_F16)
info = info.makeColorType(kRGBA_F16_SkColorType);
bitmap_.setInfo(info);
bool allocated = bitmap_.tryAllocPixels(allocator_);
if (allocated)
status_ = kFrameInitialized;
return allocated;
}
sk_sp<SkImage> ImageFrame::FinalizePixelsAndGetImage() {
DCHECK_EQ(kFrameComplete, status_);
bitmap_.setImmutable();
return SkImage::MakeFromBitmap(bitmap_);
}
void ImageFrame::SetHasAlpha(bool alpha) {
has_alpha_ = alpha;
bitmap_.setAlphaType(ComputeAlphaType());
}
void ImageFrame::SetStatus(Status status) {
status_ = status;
if (status_ == kFrameComplete) {
bitmap_.setAlphaType(ComputeAlphaType());
// Send pending pixels changed notifications now, because we can't do
// this after the bitmap has been marked immutable. We don't set the
// bitmap immutable here because it would defeat
// TakeBitmapDataIfWritable(). Instead we let the bitmap stay mutable
// until someone calls FinalizePixelsAndGetImage() to actually get the
// SkImage.
NotifyBitmapIfPixelsChanged();
}
}
void ImageFrame::ZeroFillFrameRect(const IntRect& rect) {
if (rect.IsEmpty())
return;
bitmap_.eraseArea(rect, SkColorSetARGB(0, 0, 0, 0));
SetHasAlpha(true);
}
static void BlendRGBAF16Buffer(ImageFrame::PixelDataF16* dst,
ImageFrame::PixelDataF16* src,
size_t num_pixels,
SkAlphaType dst_alpha_type) {
// Source is always unpremul, but the blending result might be premul or
// unpremul, depending on the alpha type of the destination pixel passed to
// this function.
SkImageInfo info =
SkImageInfo::Make(num_pixels, 1, kRGBA_F16_SkColorType, dst_alpha_type,
SkColorSpace::MakeSRGBLinear());
sk_sp<SkSurface> surface =
SkSurface::MakeRasterDirect(info, dst, info.minRowBytes());
SkPixmap src_pixmap(info.makeAlphaType(kUnpremul_SkAlphaType), src,
info.minRowBytes());
sk_sp<SkImage> src_image =
SkImage::MakeFromRaster(src_pixmap, nullptr, nullptr);
surface->getCanvas()->drawImage(src_image, 0, 0);
surface->flush();
}
void ImageFrame::BlendRGBARawF16Buffer(PixelDataF16* dst,
PixelDataF16* src,
size_t num_pixels) {
BlendRGBAF16Buffer(dst, src, num_pixels, kUnpremul_SkAlphaType);
}
void ImageFrame::BlendRGBAPremultipliedF16Buffer(PixelDataF16* dst,
PixelDataF16* src,
size_t num_pixels) {
BlendRGBAF16Buffer(dst, src, num_pixels, kPremul_SkAlphaType);
}
static uint8_t BlendChannel(uint8_t src,
uint8_t src_a,
uint8_t dst,
uint8_t dst_a,
unsigned scale) {
unsigned blend_unscaled = src * src_a + dst * dst_a;
DCHECK(blend_unscaled < (1ULL << 32) / scale);
return (blend_unscaled * scale) >> 24;
}
static uint32_t BlendSrcOverDstNonPremultiplied(uint32_t src, uint32_t dst) {
uint8_t src_a = SkGetPackedA32(src);
if (src_a == 0)
return dst;
uint8_t dst_a = SkGetPackedA32(dst);
uint8_t dst_factor_a = (dst_a * SkAlpha255To256(255 - src_a)) >> 8;
DCHECK(src_a + dst_factor_a < (1U << 8));
uint8_t blend_a = src_a + dst_factor_a;
unsigned scale = (1UL << 24) / blend_a;
uint8_t blend_r = BlendChannel(SkGetPackedR32(src), src_a,
SkGetPackedR32(dst), dst_factor_a, scale);
uint8_t blend_g = BlendChannel(SkGetPackedG32(src), src_a,
SkGetPackedG32(dst), dst_factor_a, scale);
uint8_t blend_b = BlendChannel(SkGetPackedB32(src), src_a,
SkGetPackedB32(dst), dst_factor_a, scale);
return SkPackARGB32NoCheck(blend_a, blend_r, blend_g, blend_b);
}
void ImageFrame::BlendRGBARaw(PixelData* dest,
unsigned r,
unsigned g,
unsigned b,
unsigned a) {
*dest =
BlendSrcOverDstNonPremultiplied(SkPackARGB32NoCheck(a, r, g, b), *dest);
}
void ImageFrame::BlendSrcOverDstRaw(PixelData* src, PixelData dst) {
*src = BlendSrcOverDstNonPremultiplied(*src, dst);
}
SkAlphaType ImageFrame::ComputeAlphaType() const {
// If the frame is not fully loaded, there will be transparent pixels,
// so we can't tell skia we're opaque, even for image types that logically
// always are (e.g. jpeg).
if (!has_alpha_ && status_ == kFrameComplete)
return kOpaque_SkAlphaType;
return premultiply_alpha_ ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
}
} // namespace blink