Skip to content

Instantly share code, notes, and snippets.

@chinmaygarde
Created June 17, 2019 22:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chinmaygarde/d9a132a2102fde57170290ddf928293b to your computer and use it in GitHub Desktop.
Save chinmaygarde/d9a132a2102fde57170290ddf928293b to your computer and use it in GitHub Desktop.
diff --git a/lib/ui/painting/image_decoder.cc b/lib/ui/painting/image_decoder.cc
index 98d06f669..db560b9fa 100644
--- a/lib/ui/painting/image_decoder.cc
+++ b/lib/ui/painting/image_decoder.cc
@@ -6,6 +6,7 @@
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/trace_event.h"
+#include "third_party/skia/include/codec/SkCodec.h"
namespace flutter {
@@ -31,27 +32,6 @@ bool ImageDecoder::IsValid() const {
return is_valid_;
}
-static sk_sp<SkImage> ImageFromDecompressedData(
- sk_sp<SkData> data,
- ImageDecoder::ImageInfo info,
- const fml::tracing::TraceFlow& flow) {
- TRACE_EVENT0("flutter", __FUNCTION__);
- flow.Step(__FUNCTION__);
- return SkImage::MakeRasterData(info.sk_info, data, info.row_bytes);
-}
-
-static sk_sp<SkImage> ImageFromCompressedData(
- sk_sp<SkData> data,
- const fml::tracing::TraceFlow& flow) {
- TRACE_EVENT0("flutter", __FUNCTION__);
- flow.Step(__FUNCTION__);
- auto image = SkImage::MakeFromEncoded(std::move(data));
- if (!image) {
- return nullptr;
- }
- return image->makeRasterImage();
-}
-
// Get the updated dimensions of the image. If both dimensions are specified,
// use them. If one of them is specified, respect the one that is is and use the
// aspect ratio to calculate the other. If neither dimensions are specified, use
@@ -132,6 +112,93 @@ static sk_sp<SkImage> ResizeRasterImage(sk_sp<SkImage> image,
return scaled_image;
}
+static sk_sp<SkImage> ImageFromDecompressedData(
+ sk_sp<SkData> data,
+ ImageDecoder::ImageInfo info,
+ std::optional<uint32_t> target_width,
+ std::optional<uint32_t> target_height,
+ const fml::tracing::TraceFlow& flow) {
+ TRACE_EVENT0("flutter", __FUNCTION__);
+ flow.Step(__FUNCTION__);
+ auto image = SkImage::MakeRasterData(info.sk_info, data, info.row_bytes);
+
+ if (!image) {
+ FML_LOG(ERROR) << "Could not create image from decompressed bytes.";
+ return nullptr;
+ }
+
+ return ResizeRasterImage(std::move(image), target_width, target_height, flow);
+}
+
+static sk_sp<SkImage> ImageFromCompressedData(
+ sk_sp<SkData> data,
+ std::optional<uint32_t> target_width,
+ std::optional<uint32_t> target_height,
+ const fml::tracing::TraceFlow& flow) {
+ TRACE_EVENT0("flutter", __FUNCTION__);
+ flow.Step(__FUNCTION__);
+
+ auto codec = SkCodec::MakeFromData(data);
+
+ if (!codec) {
+ return nullptr;
+ }
+
+ const auto encoded_info = codec->getInfo();
+
+ if (encoded_info.dimensions().isEmpty()) {
+ return nullptr;
+ }
+
+ const double desired_width =
+ target_width.value_or(encoded_info.dimensions().width());
+ const double desired_height =
+ target_height.value_or(encoded_info.dimensions().height());
+
+ const auto scale_x = desired_width / encoded_info.dimensions().width();
+ const auto scale_y = desired_height / encoded_info.dimensions().height();
+
+ const auto scale = std::min({scale_x, scale_y, 1.0});
+
+ if (scale <= 0.0) {
+ return nullptr;
+ }
+
+ // Unlike the call to ReziedDimensions above, this takes into account the
+ // opinion of SkCodec in deciding the final dimensions of the image.
+ const auto scaled_dimensions = codec->getScaledDimensions(scale);
+
+ if (scaled_dimensions.isEmpty()) {
+ return nullptr;
+ }
+
+ const auto decoded_info = encoded_info.makeWH(scaled_dimensions.width(),
+ scaled_dimensions.height());
+
+ SkBitmap decoded_bitmap;
+ if (!decoded_bitmap.tryAllocPixels(decoded_info)) {
+ FML_LOG(ERROR) << "Could not perform allocation for image decoding.";
+ return nullptr;
+ }
+
+ const auto decompression_result = codec->getPixels(decoded_bitmap.pixmap());
+ if (decompression_result != SkCodec::Result::kSuccess) {
+ FML_LOG(ERROR) << "Could not perform image decompression. Error: "
+ << SkCodec::ResultToString(decompression_result);
+ return nullptr;
+ }
+
+ decoded_bitmap.setImmutable();
+
+ auto decoded_image = SkImage::MakeFromBitmap(decoded_bitmap);
+
+ if (!decoded_image) {
+ return nullptr;
+ }
+
+ return ResizeRasterImage(decoded_image, target_width, target_height, flow);
+}
+
static SkiaGPUObject<SkImage> UploadRasterImage(
sk_sp<SkImage> image,
fml::WeakPtr<GrContext> context,
@@ -172,14 +239,6 @@ static SkiaGPUObject<SkImage> UploadRasterImage(
return {texture_image, queue};
}
-static bool CanUploadToGPUDirectly(ImageDecoder::ImageDescriptor descriptor) {
- // We can upload to the GPU directly (and skip posting a task to the
- // workqueue), if the decompressed image data is given upfront and there is no
- // desire to resize.
- return descriptor.decompressed_image_info && !descriptor.target_width &&
- !descriptor.target_height;
-}
-
void ImageDecoder::Decode(ImageDescriptor descriptor, ImageResult callback) {
TRACE_EVENT0("flutter", __FUNCTION__);
fml::tracing::TraceFlow flow(__FUNCTION__);
@@ -206,74 +265,61 @@ void ImageDecoder::Decode(ImageDescriptor descriptor, ImageResult callback) {
return;
}
- // This is an optional optimization to avoid unnecessarily posting a task to
- // the worker if we know there not going to be any decompression or resizing.
- auto worker = CanUploadToGPUDirectly(descriptor)
- ? runners_.GetIOTaskRunner()
- : runners_.GetConcurrentTaskRunner();
-
- worker->PostTask(fml::MakeCopyable([descriptor, //
- io_manager = io_manager_, //
- io_runner =
- runners_.GetIOTaskRunner(), //
- result, //
- flow = std::move(flow) //
+ runners_.GetConcurrentTaskRunner()->PostTask(
+ fml::MakeCopyable([descriptor, //
+ io_manager = io_manager_, //
+ io_runner = runners_.GetIOTaskRunner(), //
+ result, //
+ flow = std::move(flow) //
]() mutable {
- // Step 1: Decompress the image.
- // On Worker.
-
- sk_sp<SkImage> decompressed;
- if (descriptor.decompressed_image_info) {
- decompressed = ImageFromDecompressedData(
- std::move(descriptor.data),
- descriptor.decompressed_image_info.value(), flow);
- } else {
- decompressed = ImageFromCompressedData(std::move(descriptor.data), flow);
- }
-
- if (!decompressed) {
- FML_LOG(ERROR) << "Could not decompress image.";
- result({}, std::move(flow));
- return;
- }
-
- // Step 2: Resize the image to the required dimensions if necessary.
- // On Worker.
-
- auto resized =
- ResizeRasterImage(std::move(decompressed), descriptor.target_width,
- descriptor.target_height, flow);
- if (!resized) {
- FML_LOG(ERROR) << "Could not resize image to desired size.";
- result({}, std::move(flow));
- return;
- }
-
- // Step 3: Update the image to the GPU.
- // On IO Thread.
-
- fml::TaskRunner::RunNowOrPostTask(
- io_runner, fml::MakeCopyable([io_manager, resized, result,
- flow = std::move(flow)]() mutable {
- if (!io_manager) {
- FML_LOG(ERROR) << "Could not acquire IO manager.";
- return result({}, std::move(flow));
- }
-
- auto uploaded = UploadRasterImage(
- std::move(resized), io_manager->GetResourceContext(),
- io_manager->GetSkiaUnrefQueue(), flow);
-
- if (!uploaded.get()) {
- FML_LOG(ERROR) << "Could not upload image to the GPU.";
- result({}, std::move(flow));
- return;
- }
-
- // Finally, all done.
- result(std::move(uploaded), std::move(flow));
- }));
- }));
+ // Step 1: Decompress the image.
+ // On Worker.
+
+ auto decompressed =
+ descriptor.decompressed_image_info
+ ? ImageFromDecompressedData(
+ std::move(descriptor.data), //
+ descriptor.decompressed_image_info.value(), //
+ descriptor.target_width, //
+ descriptor.target_height, //
+ flow //
+ )
+ : ImageFromCompressedData(std::move(descriptor.data), //
+ descriptor.target_width, //
+ descriptor.target_height, //
+ flow);
+
+ if (!decompressed) {
+ FML_LOG(ERROR) << "Could not decompress image.";
+ result({}, std::move(flow));
+ return;
+ }
+
+ // Step 3: Update the image to the GPU.
+ // On IO Thread.
+
+ io_runner->PostTask(
+ fml::MakeCopyable([io_manager, decompressed, result,
+ flow = std::move(flow)]() mutable {
+ if (!io_manager) {
+ FML_LOG(ERROR) << "Could not acquire IO manager.";
+ return result({}, std::move(flow));
+ }
+
+ auto uploaded = UploadRasterImage(
+ std::move(decompressed), io_manager->GetResourceContext(),
+ io_manager->GetSkiaUnrefQueue(), flow);
+
+ if (!uploaded.get()) {
+ FML_LOG(ERROR) << "Could not upload image to the GPU.";
+ result({}, std::move(flow));
+ return;
+ }
+
+ // Finally, all done.
+ result(std::move(uploaded), std::move(flow));
+ }));
+ }));
}
} // namespace flutter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment