blob: 1ba1c4f4785bb2d0ce75bcf2d98c924f15a3cece [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/ui/painting/image_generator.h"
#include <utility>
#include "flutter/fml/logging.h"
#include "third_party/skia/include/codec/SkEncodedOrigin.h"
#include "third_party/skia/include/codec/SkPixmapUtils.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkImage.h"
namespace flutter {
ImageGenerator::~ImageGenerator() = default;
sk_sp<SkImage> ImageGenerator::GetImage() {
SkImageInfo info = GetInfo();
SkBitmap bitmap;
if (!bitmap.tryAllocPixels(info)) {
FML_DLOG(ERROR) << "Failed to allocate memory for bitmap of size "
<< info.computeMinByteSize() << "B";
return nullptr;
}
const auto& pixmap = bitmap.pixmap();
if (!GetPixels(pixmap.info(), pixmap.writable_addr(), pixmap.rowBytes())) {
FML_DLOG(ERROR) << "Failed to get pixels for image.";
return nullptr;
}
bitmap.setImmutable();
return SkImages::RasterFromBitmap(bitmap);
}
BuiltinSkiaImageGenerator::~BuiltinSkiaImageGenerator() = default;
BuiltinSkiaImageGenerator::BuiltinSkiaImageGenerator(
std::unique_ptr<SkImageGenerator> generator)
: generator_(std::move(generator)) {}
const SkImageInfo& BuiltinSkiaImageGenerator::GetInfo() {
return generator_->getInfo();
}
unsigned int BuiltinSkiaImageGenerator::GetFrameCount() const {
return 1;
}
unsigned int BuiltinSkiaImageGenerator::GetPlayCount() const {
return 1;
}
const ImageGenerator::FrameInfo BuiltinSkiaImageGenerator::GetFrameInfo(
unsigned int frame_index) {
return {.required_frame = std::nullopt,
.duration = 0,
.disposal_method = SkCodecAnimation::DisposalMethod::kKeep};
}
SkISize BuiltinSkiaImageGenerator::GetScaledDimensions(float desired_scale) {
return generator_->getInfo().dimensions();
}
bool BuiltinSkiaImageGenerator::GetPixels(
const SkImageInfo& info,
void* pixels,
size_t row_bytes,
unsigned int frame_index,
std::optional<unsigned int> prior_frame) {
return generator_->getPixels(info, pixels, row_bytes);
}
std::unique_ptr<ImageGenerator> BuiltinSkiaImageGenerator::MakeFromGenerator(
std::unique_ptr<SkImageGenerator> generator) {
if (!generator) {
return nullptr;
}
return std::make_unique<BuiltinSkiaImageGenerator>(std::move(generator));
}
BuiltinSkiaCodecImageGenerator::~BuiltinSkiaCodecImageGenerator() = default;
static SkImageInfo getInfoIncludingExif(SkCodec* codec) {
SkImageInfo info = codec->getInfo();
if (SkEncodedOriginSwapsWidthHeight(codec->getOrigin())) {
info = SkPixmapUtils::SwapWidthHeight(info);
}
if (kUnpremul_SkAlphaType == info.alphaType()) {
// Prefer premul over unpremul (this produces better filtering in general)
info = info.makeAlphaType(kPremul_SkAlphaType);
}
return info;
}
BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator(
std::unique_ptr<SkCodec> codec)
: codec_(std::move(codec)) {
image_info_ = getInfoIncludingExif(codec_.get());
}
BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator(
sk_sp<SkData> buffer)
: codec_(SkCodec::MakeFromData(std::move(buffer)).release()) {
image_info_ = getInfoIncludingExif(codec_.get());
}
const SkImageInfo& BuiltinSkiaCodecImageGenerator::GetInfo() {
return image_info_;
}
unsigned int BuiltinSkiaCodecImageGenerator::GetFrameCount() const {
return codec_->getFrameCount();
}
unsigned int BuiltinSkiaCodecImageGenerator::GetPlayCount() const {
auto repetition_count = codec_->getRepetitionCount();
return repetition_count < 0 ? kInfinitePlayCount : repetition_count + 1;
}
const ImageGenerator::FrameInfo BuiltinSkiaCodecImageGenerator::GetFrameInfo(
unsigned int frame_index) {
SkCodec::FrameInfo info = {};
codec_->getFrameInfo(frame_index, &info);
return {
.required_frame = info.fRequiredFrame == SkCodec::kNoFrame
? std::nullopt
: std::optional<unsigned int>(info.fRequiredFrame),
.duration = static_cast<unsigned int>(info.fDuration),
.disposal_method = info.fDisposalMethod};
}
SkISize BuiltinSkiaCodecImageGenerator::GetScaledDimensions(
float desired_scale) {
SkISize size = codec_->getScaledDimensions(desired_scale);
if (SkEncodedOriginSwapsWidthHeight(codec_->getOrigin())) {
std::swap(size.fWidth, size.fHeight);
}
return size;
}
bool BuiltinSkiaCodecImageGenerator::GetPixels(
const SkImageInfo& info,
void* pixels,
size_t row_bytes,
unsigned int frame_index,
std::optional<unsigned int> prior_frame) {
SkCodec::Options options;
options.fFrameIndex = frame_index;
if (prior_frame.has_value()) {
options.fPriorFrame = prior_frame.value();
}
SkEncodedOrigin origin = codec_->getOrigin();
SkPixmap output_pixmap(info, pixels, row_bytes);
SkPixmap temp_pixmap;
SkBitmap temp_bitmap;
if (origin == kTopLeft_SkEncodedOrigin) {
// We can decode directly into the output buffer.
temp_pixmap = output_pixmap;
} else {
// We need to decode into a different buffer so we can re-orient
// the pixels later.
SkImageInfo temp_info = output_pixmap.info();
if (SkEncodedOriginSwapsWidthHeight(origin)) {
// We'll be decoding into a buffer that has height and width swapped.
temp_info = SkPixmapUtils::SwapWidthHeight(temp_info);
}
if (!temp_bitmap.tryAllocPixels(temp_info)) {
FML_DLOG(ERROR) << "Failed to allocate memory for bitmap of size "
<< temp_info.computeMinByteSize() << "B";
return false;
}
temp_pixmap = temp_bitmap.pixmap();
}
SkCodec::Result result = codec_->getPixels(temp_pixmap, &options);
if (result != SkCodec::kSuccess) {
FML_DLOG(WARNING) << "codec could not get pixels. "
<< SkCodec::ResultToString(result);
return false;
}
if (origin == kTopLeft_SkEncodedOrigin) {
return true;
}
return SkPixmapUtils::Orient(output_pixmap, temp_pixmap, origin);
}
std::unique_ptr<ImageGenerator> BuiltinSkiaCodecImageGenerator::MakeFromData(
sk_sp<SkData> data) {
auto codec = SkCodec::MakeFromData(std::move(data));
if (!codec) {
return nullptr;
}
return std::make_unique<BuiltinSkiaCodecImageGenerator>(std::move(codec));
}
} // namespace flutter