blob: 6db7fcf6791ec9b687bb326671e8d494fb747735 [file] [log] [blame]
// Copyright 2018 The Chromium 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_encoding.h"
#include <memory>
#include <utility>
#include "flutter/common/task_runners.h"
#include "flutter/glue/trace_event.h"
#include "flutter/lib/ui/painting/image.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "lib/fxl/build_config.h"
#include "lib/fxl/functional/make_copyable.h"
#include "lib/tonic/dart_persistent_value.h"
#include "lib/tonic/logging/dart_invoke.h"
#include "lib/tonic/typed_data/uint8_list.h"
#include "third_party/skia/include/core/SkEncodedImageFormat.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkSurface.h"
using tonic::DartInvoke;
using tonic::DartPersistentValue;
using tonic::ToDart;
#ifdef ERROR
#undef ERROR
#endif
namespace blink {
namespace {
// This must be kept in sync with the enum in painting.dart
enum ImageByteFormat {
kRawRGBA,
kRawUnmodified,
kPNG,
};
void InvokeDataCallback(std::unique_ptr<DartPersistentValue> callback,
sk_sp<SkData> buffer) {
tonic::DartState* dart_state = callback->dart_state().get();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
if (!buffer) {
DartInvoke(callback->value(), {Dart_Null()});
} else {
Dart_Handle dart_data = tonic::DartConverter<tonic::Uint8List>::ToDart(
buffer->bytes(), buffer->size());
DartInvoke(callback->value(), {dart_data});
}
}
sk_sp<SkData> EncodeImage(sk_sp<SkImage> image, ImageByteFormat format) {
TRACE_EVENT0("flutter", __FUNCTION__);
if (image == nullptr) {
return nullptr;
}
if (format == kPNG) {
return image->encodeToData(SkEncodedImageFormat::kPNG, 0);
}
// Copy the GPU image snapshot into CPU memory.
auto cpu_snapshot = image->makeRasterImage();
if (!cpu_snapshot) {
FXL_LOG(ERROR) << "Pixel copy failed.";
return nullptr;
}
SkPixmap pixmap;
if (!cpu_snapshot->peekPixels(&pixmap)) {
FXL_LOG(ERROR) << "Pixel address is not available.";
return nullptr;
}
if (format == kRawUnmodified) {
return SkData::MakeWithCopy(pixmap.addr(), pixmap.computeByteSize());
}
FXL_CHECK(format == kRawRGBA);
if (pixmap.colorType() != kRGBA_8888_SkColorType) {
TRACE_EVENT0("flutter", "ConvertToRGBA");
// Convert the pixel data to N32 to adhere to our API contract.
const auto image_info = SkImageInfo::MakeN32Premul(image->width(),
image->height());
auto surface = SkSurface::MakeRaster(image_info);
surface->writePixels(pixmap, 0, 0);
if (!surface->peekPixels(&pixmap)) {
FXL_LOG(ERROR) << "Pixel address is not available.";
return nullptr;
}
return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize());
} else {
return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize());
}
}
void EncodeImageAndInvokeDataCallback(
std::unique_ptr<DartPersistentValue> callback,
sk_sp<SkImage> image,
fxl::RefPtr<fxl::TaskRunner> ui_task_runner,
ImageByteFormat format) {
sk_sp<SkData> encoded = EncodeImage(std::move(image), format);
ui_task_runner->PostTask(
fxl::MakeCopyable([callback = std::move(callback), encoded]() mutable {
InvokeDataCallback(std::move(callback), std::move(encoded));
}));
}
} // namespace
Dart_Handle EncodeImage(CanvasImage* canvas_image,
int format,
Dart_Handle callback_handle) {
if (!canvas_image)
return ToDart("encode called with non-genuine Image.");
if (!Dart_IsClosure(callback_handle))
return ToDart("Callback must be a function.");
ImageByteFormat image_format = static_cast<ImageByteFormat>(format);
auto callback = std::make_unique<DartPersistentValue>(
tonic::DartState::Current(), callback_handle);
sk_sp<SkImage> image = canvas_image->image();
const auto& task_runners = UIDartState::Current()->GetTaskRunners();
task_runners.GetIOTaskRunner()->PostTask(fxl::MakeCopyable(
[callback = std::move(callback), image,
ui_task_runner = task_runners.GetUITaskRunner(),
image_format]() mutable {
EncodeImageAndInvokeDataCallback(std::move(callback),
std::move(image),
std::move(ui_task_runner),
image_format);
}));
return Dart_Null();
}
} // namespace blink