This repository was archived by the owner on Feb 25, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6k
Enable Image encoding by leveraging existing Skia functionality #4762
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
f7ce006
Add basic image encoding with limited format
majido 5472da9
prefer single quote
majido a4e3b63
Address review feedback including:
majido 4a39f8d
Revert changes to run_tests
majido 447d8d5
Update quality documentation
majido 2ecab34
Update license file to make Travis happy
majido 7202b16
minor cleanup
majido e8a8820
Make EncodingFormat a class
majido 813e9ec
fix formatting of encoding_test.dart
majido cd5f265
Switch to returning ByteData
majido c89adb9
Address feedback
majido File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| // 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 "flutter/common/threads.h" | ||
| #include "flutter/lib/ui/painting/image.h" | ||
| #include "flutter/lib/ui/painting/resource_context.h" | ||
| #include "lib/fxl/build_config.h" | ||
| #include "lib/fxl/functional/make_copyable.h" | ||
| #include "lib/tonic/dart_persistent_value.h" | ||
| #include "lib/tonic/dart_state.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" | ||
|
|
||
| using tonic::DartInvoke; | ||
| using tonic::DartPersistentValue; | ||
| using tonic::ToDart; | ||
|
|
||
| namespace blink { | ||
| namespace { | ||
|
|
||
| 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, | ||
| SkEncodedImageFormat format, | ||
| int quality) { | ||
| if (image == nullptr) { | ||
| return nullptr; | ||
| } | ||
| return image->encodeToData(format, quality); | ||
| } | ||
|
|
||
| void EncodeImageAndInvokeDataCallback( | ||
| std::unique_ptr<DartPersistentValue> callback, | ||
| sk_sp<SkImage> image, | ||
| SkEncodedImageFormat format, | ||
| int quality) { | ||
| sk_sp<SkData> encoded = EncodeImage(std::move(image), format, quality); | ||
|
|
||
| Threads::UI()->PostTask( | ||
| fxl::MakeCopyable([callback = std::move(callback), encoded]() mutable { | ||
| InvokeDataCallback(std::move(callback), std::move(encoded)); | ||
| })); | ||
| } | ||
|
|
||
| SkEncodedImageFormat ToSkEncodedImageFormat(int format) { | ||
| // Map the formats exposed in flutter to formats supported in Skia. | ||
| // See: | ||
| // https://github.com/google/skia/blob/master/include/core/SkEncodedImageFormat.h | ||
| switch (format) { | ||
| case 0: | ||
| return SkEncodedImageFormat::kJPEG; | ||
| case 1: | ||
| return SkEncodedImageFormat::kPNG; | ||
| case 2: | ||
| return SkEncodedImageFormat::kWEBP; | ||
| default: | ||
| /* NOTREACHED */ | ||
| return SkEncodedImageFormat::kWEBP; | ||
| } | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| Dart_Handle EncodeImage(CanvasImage* canvas_image, | ||
| int format, | ||
| int quality, | ||
| 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."); | ||
|
|
||
| SkEncodedImageFormat image_format = ToSkEncodedImageFormat(format); | ||
|
|
||
| if (quality > 100) | ||
| quality = 100; | ||
| if (quality < 0) | ||
| quality = 0; | ||
|
|
||
| auto callback = std::make_unique<DartPersistentValue>( | ||
| tonic::DartState::Current(), callback_handle); | ||
| sk_sp<SkImage> image = canvas_image->image(); | ||
|
|
||
| Threads::IO()->PostTask(fxl::MakeCopyable( | ||
| [callback = std::move(callback), image, image_format, quality]() mutable { | ||
| EncodeImageAndInvokeDataCallback(std::move(callback), std::move(image), | ||
| image_format, quality); | ||
| })); | ||
|
|
||
| return Dart_Null(); | ||
| } | ||
|
|
||
| } // namespace blink |
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // 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. | ||
|
|
||
| #ifndef FLUTTER_LIB_UI_PAINTING_IMAGE_ENCODING_H_ | ||
| #define FLUTTER_LIB_UI_PAINTING_IMAGE_ENCODING_H_ | ||
|
|
||
| #include "lib/tonic/dart_library_natives.h" | ||
|
|
||
| namespace blink { | ||
|
|
||
| class CanvasImage; | ||
|
|
||
| Dart_Handle EncodeImage(CanvasImage* canvas_image, | ||
| int format, | ||
| int quality, | ||
| Dart_Handle callback_handle); | ||
|
|
||
| } // namespace blink | ||
|
|
||
| #endif // FLUTTER_LIB_UI_PAINTING_IMAGE_ENCODING_H_ |
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| // 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. | ||
|
|
||
| import 'dart:async'; | ||
| import 'dart:ui'; | ||
| import 'dart:typed_data'; | ||
| import 'dart:io'; | ||
|
|
||
| import 'package:test/test.dart'; | ||
| import 'package:path/path.dart' as path; | ||
|
|
||
| void main() { | ||
| final Image testImage = createSquareTestImage(); | ||
|
|
||
| test('Encode with default arguments', () async { | ||
| ByteData data = await testImage.toByteData(); | ||
| List<int> expected = readFile('square-80.jpg'); | ||
| expect(new Uint8List.view(data.buffer), expected); | ||
| }); | ||
|
|
||
| test('Encode JPEG', () async { | ||
| ByteData data = await testImage.toByteData( | ||
| format: new EncodingFormat.jpeg(quality: 80)); | ||
| List<int> expected = readFile('square-80.jpg'); | ||
| expect(new Uint8List.view(data.buffer), expected); | ||
| }); | ||
|
|
||
| test('Encode PNG', () async { | ||
| ByteData data = | ||
| await testImage.toByteData(format: new EncodingFormat.png()); | ||
| List<int> expected = readFile('square.png'); | ||
| expect(new Uint8List.view(data.buffer), expected); | ||
| }); | ||
|
|
||
| test('Encode WEBP', () async { | ||
| ByteData data = await testImage.toByteData( | ||
| format: new EncodingFormat.webp(quality: 80)); | ||
| List<int> expected = readFile('square-80.webp'); | ||
| expect(new Uint8List.view(data.buffer), expected); | ||
| }); | ||
| } | ||
|
|
||
| Image createSquareTestImage() { | ||
| PictureRecorder recorder = new PictureRecorder(); | ||
| Canvas canvas = new Canvas(recorder, new Rect.fromLTWH(0.0, 0.0, 10.0, 10.0)); | ||
|
|
||
| Paint black = new Paint() | ||
| ..strokeWidth = 1.0 | ||
| ..color = const Color.fromRGBO(0, 0, 0, 1.0); | ||
| Paint green = new Paint() | ||
| ..strokeWidth = 1.0 | ||
| ..color = const Color.fromRGBO(0, 255, 0, 1.0); | ||
|
|
||
| canvas.drawRect(new Rect.fromLTWH(0.0, 0.0, 10.0, 10.0), black); | ||
| canvas.drawRect(new Rect.fromLTWH(2.0, 2.0, 6.0, 6.0), green); | ||
| return recorder.endRecording().toImage(10, 10); | ||
| } | ||
|
|
||
| List<int> readFile(fileName) { | ||
| final file = new File(path.join('flutter', 'testing', 'resources', fileName)); | ||
| return file.readAsBytesSync(); | ||
| } |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or 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
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/google/skia/blob/master/src/image/SkImage.cpp#L117
Skia seem to be using lossless PNG format as the default when encoding format is not specified.
Here, are we using JPEG as the default ?
Or should we pass null and use Skia's default ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass
const EncodingFormat.png()as the format to get lossless PNG encoding.