#include "ash/public/cpp/image_util.h"
#include <memory>
#include <string>
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/ranges/algorithm.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "ipc/ipc_channel.h"
#include "services/data_decoder/public/cpp/decode_image.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/image/image_skia.h"
namespace ash {
namespace image_util {
namespace {
const int64_t kMaxImageSizeInBytes =
static_cast<int64_t>(IPC::Channel::kMaximumMessageSize);
std::string ReadFileToString(const base::FilePath& path) {
std::string result;
if (!base::ReadFileToString(path, &result)) {
LOG(WARNING) << "Failed reading file";
result.clear();
}
return result;
}
void ToImageSkia(DecodeImageCallback callback, const SkBitmap& bitmap) {
if (bitmap.empty()) {
LOG(WARNING) << "Failed to decode image";
std::move(callback).Run(gfx::ImageSkia());
return;
}
gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
std::move(callback).Run(image);
}
void ToFrames(DecodeAnimationCallback callback,
std::vector<data_decoder::mojom::AnimationFramePtr> raw_frames) {
std::vector<AnimationFrame> frames(raw_frames.size());
base::ranges::transform(
raw_frames, frames.begin(),
[](const data_decoder::mojom::AnimationFramePtr& frame_ptr) {
return AnimationFrame{
gfx::ImageSkia::CreateFrom1xBitmap(frame_ptr->bitmap),
frame_ptr->duration};
});
std::move(callback).Run(std::move(frames));
}
class EmptyImageSkiaSource : public gfx::CanvasImageSource {
public:
explicit EmptyImageSkiaSource(const gfx::Size& size)
: gfx::CanvasImageSource(size) {}
EmptyImageSkiaSource(const EmptyImageSkiaSource&) = delete;
EmptyImageSkiaSource& operator=(const EmptyImageSkiaSource&) = delete;
~EmptyImageSkiaSource() override = default;
private:
void Draw(gfx::Canvas* canvas) override {}
};
}
gfx::ImageSkia CreateEmptyImage(const gfx::Size& size) {
return gfx::ImageSkia(std::make_unique<EmptyImageSkiaSource>(size), size);
}
void DecodeImageFile(DecodeImageCallback callback,
const base::FilePath& file_path,
data_decoder::mojom::ImageCodec codec) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&ReadFileToString, file_path),
base::BindOnce(&DecodeImageData, std::move(callback), codec));
}
void DecodeAnimationFile(DecodeAnimationCallback callback,
const base::FilePath& file_path) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&ReadFileToString, file_path),
base::BindOnce(&DecodeAnimationData, std::move(callback)));
}
void DecodeImageData(DecodeImageCallback callback,
data_decoder::mojom::ImageCodec codec,
const std::string& data) {
if (data.empty()) {
std::move(callback).Run(gfx::ImageSkia());
return;
}
data_decoder::DecodeImageIsolated(
base::as_bytes(base::make_span(data)), codec,
true, kMaxImageSizeInBytes,
gfx::Size(),
base::BindOnce(&ToImageSkia, std::move(callback)));
}
void DecodeAnimationData(DecodeAnimationCallback callback,
const std::string& data) {
if (data.empty()) {
std::move(callback).Run(std::vector<AnimationFrame>());
return;
}
data_decoder::DecodeAnimationIsolated(
base::as_bytes(base::make_span(data)), true,
kMaxImageSizeInBytes, base::BindOnce(&ToFrames, std::move(callback)));
}
}
}