#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h"
#include <memory>
#include "base/containers/adapters.h"
#include "base/numerics/checked_math.h"
#include "media/base/video_color_space.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/modules/skcms/skcms.h"
#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
#include <arm_neon.h>
#endif
namespace blink {
PNGImageDecoder::PNGImageDecoder(
AlphaOption alpha_option,
HighBitDepthDecodingOption high_bit_depth_decoding_option,
ColorBehavior color_behavior,
wtf_size_t max_decoded_bytes,
wtf_size_t offset)
: … { … }
PNGImageDecoder::~PNGImageDecoder() = default;
String PNGImageDecoder::FilenameExtension() const { … }
const AtomicString& PNGImageDecoder::MimeType() const { … }
bool PNGImageDecoder::SetFailed() { … }
wtf_size_t PNGImageDecoder::DecodeFrameCount() { … }
void PNGImageDecoder::DecodeSize() { … }
void PNGImageDecoder::Decode(wtf_size_t index) { … }
void PNGImageDecoder::Parse(ParseQuery query) { … }
void PNGImageDecoder::ClearFrameBuffer(wtf_size_t index) { … }
bool PNGImageDecoder::CanReusePreviousFrameBuffer(wtf_size_t index) const { … }
void PNGImageDecoder::SetRepetitionCount(int repetition_count) { … }
int PNGImageDecoder::RepetitionCount() const { … }
void PNGImageDecoder::InitializeNewFrame(wtf_size_t index) { … }
static std::unique_ptr<ColorProfile> ParseCicpChunk(
const png_unknown_chunk& chunk) { … }
static inline std::unique_ptr<ColorProfile> ReadColorProfile(png_structp png,
png_infop info) { … }
static inline void ReadHDRMetadata(
png_structp png,
png_infop info,
std::optional<gfx::HDRMetadata>* hdr_metadata) { … }
void PNGImageDecoder::SetColorSpace() { … }
void PNGImageDecoder::SetBitDepth() { … }
bool PNGImageDecoder::ImageIsHighBitDepth() { … }
std::optional<gfx::HDRMetadata> PNGImageDecoder::GetHDRMetadata() const { … }
bool PNGImageDecoder::SetSize(unsigned width, unsigned height) { … }
void PNGImageDecoder::HeaderAvailable() { … }
#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
static inline void SetRGBAPremultiplyRowNeon(png_bytep src_ptr,
const int pixel_count,
ImageFrame::PixelData* dst_pixel,
unsigned* const alpha_mask) {
assert(dst_pixel);
assert(alpha_mask);
constexpr int kPixelsPerLoad = 8;
uint8x8x4_t rgba;
uint8x8_t alpha_mask_vector = vdup_n_u8(255);
auto premultiply = [](uint8x8_t c, uint8x8_t a) {
uint16x8_t ca = vmull_u8(c, a);
return vraddhn_u16(ca, vrshrq_n_u16(ca, 8));
};
int i = pixel_count;
for (; i >= kPixelsPerLoad; i -= kPixelsPerLoad) {
rgba = vld4_u8(src_ptr);
alpha_mask_vector = vand_u8(alpha_mask_vector, rgba.val[3]);
uint64_t alphas_u64 = vget_lane_u64(vreinterpret_u64_u8(rgba.val[3]), 0);
if (~alphas_u64 == 0) {
#if SK_PMCOLOR_BYTE_ORDER(R, G, B, A)
vst4_u8(reinterpret_cast<uint8_t*>(dst_pixel), rgba);
#elif SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
uint8x8x4_t bgra = {rgba.val[2], rgba.val[1], rgba.val[0], rgba.val[3]};
vst4_u8(reinterpret_cast<uint8_t*>(dst_pixel), bgra);
#endif
} else {
#if SK_PMCOLOR_BYTE_ORDER(R, G, B, A)
rgba.val[0] = premultiply(rgba.val[0], rgba.val[3]);
rgba.val[1] = premultiply(rgba.val[1], rgba.val[3]);
rgba.val[2] = premultiply(rgba.val[2], rgba.val[3]);
vst4_u8(reinterpret_cast<uint8_t*>(dst_pixel), rgba);
#elif SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
uint8x8x4_t bgra;
bgra.val[0] = premultiply(rgba.val[2], rgba.val[3]);
bgra.val[1] = premultiply(rgba.val[1], rgba.val[3]);
bgra.val[2] = premultiply(rgba.val[0], rgba.val[3]);
bgra.val[3] = rgba.val[3];
vst4_u8(reinterpret_cast<uint8_t*>(dst_pixel), bgra);
#endif
}
src_ptr += kPixelsPerLoad * 4;
dst_pixel += kPixelsPerLoad;
}
uint64_t alpha_mask_u64 =
vget_lane_u64(vreinterpret_u64_u8(alpha_mask_vector), 0);
alpha_mask_u64 &= (alpha_mask_u64 >> 32);
alpha_mask_u64 &= (alpha_mask_u64 >> 16);
alpha_mask_u64 &= (alpha_mask_u64 >> 8);
*alpha_mask &= alpha_mask_u64;
for (; i > 0; i--, dst_pixel++, src_ptr += 4) {
ImageFrame::SetRGBAPremultiply(dst_pixel, src_ptr[0], src_ptr[1],
src_ptr[2], src_ptr[3]);
*alpha_mask &= src_ptr[3];
}
}
static inline void SetRGBARawRowNeon(png_bytep src_ptr,
const int pixel_count,
ImageFrame::PixelData* dst_pixel,
unsigned* const alpha_mask) {
assert(dst_pixel);
assert(alpha_mask);
constexpr int kPixelsPerLoad = 16;
uint8x16x4_t rgba;
uint8x16_t alpha_mask_vector = vdupq_n_u8(255);
int i = pixel_count;
for (; i >= kPixelsPerLoad; i -= kPixelsPerLoad) {
rgba = vld4q_u8(src_ptr);
alpha_mask_vector = vandq_u8(alpha_mask_vector, rgba.val[3]);
#if SK_PMCOLOR_BYTE_ORDER(R, G, B, A)
vst4q_u8(reinterpret_cast<uint8_t*>(dst_pixel), rgba);
#elif SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
uint8x16x4_t bgra = {rgba.val[2], rgba.val[1], rgba.val[0], rgba.val[3]};
vst4q_u8(reinterpret_cast<uint8_t*>(dst_pixel), bgra);
#endif
src_ptr += kPixelsPerLoad * 4;
dst_pixel += kPixelsPerLoad;
}
uint64_t alpha_mask_u64 =
vget_lane_u64(vreinterpret_u64_u8(vget_low_u8(alpha_mask_vector)), 0);
alpha_mask_u64 &=
vget_lane_u64(vreinterpret_u64_u8(vget_high_u8(alpha_mask_vector)), 0);
alpha_mask_u64 &= (alpha_mask_u64 >> 32);
alpha_mask_u64 &= (alpha_mask_u64 >> 16);
alpha_mask_u64 &= (alpha_mask_u64 >> 8);
*alpha_mask &= alpha_mask_u64;
for (; i > 0; i--, dst_pixel++, src_ptr += 4) {
ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
src_ptr[3]);
*alpha_mask &= src_ptr[3];
}
}
static inline void SetRGBARawRowNoAlphaNeon(png_bytep src_ptr,
const int pixel_count,
ImageFrame::PixelData* dst_pixel) {
assert(dst_pixel);
constexpr int kPixelsPerLoad = 16;
uint8x16x3_t rgb;
int i = pixel_count;
for (; i >= kPixelsPerLoad; i -= kPixelsPerLoad) {
rgb = vld3q_u8(src_ptr);
#if SK_PMCOLOR_BYTE_ORDER(R, G, B, A)
uint8x16x4_t rgba = {rgb.val[0], rgb.val[1], rgb.val[2], vdupq_n_u8(255)};
vst4q_u8(reinterpret_cast<uint8_t*>(dst_pixel), rgba);
#elif SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
uint8x16x4_t bgra = {rgb.val[2], rgb.val[1], rgb.val[0], vdupq_n_u8(255)};
vst4q_u8(reinterpret_cast<uint8_t*>(dst_pixel), bgra);
#endif
src_ptr += kPixelsPerLoad * 3;
dst_pixel += kPixelsPerLoad;
}
for (; i > 0; i--, dst_pixel++, src_ptr += 3) {
ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2], 255);
}
}
#endif
void PNGImageDecoder::RowAvailable(unsigned char* row_buffer,
unsigned row_index,
int) { … }
void PNGImageDecoder::FrameComplete() { … }
bool PNGImageDecoder::FrameIsReceivedAtIndex(wtf_size_t index) const { … }
base::TimeDelta PNGImageDecoder::FrameDurationAtIndex(wtf_size_t index) const { … }
}