Created
June 17, 2019 22:41
-
-
Save chinmaygarde/d9a132a2102fde57170290ddf928293b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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