From dd87538311df6d585dede8a941ab66667bf3a69d Mon Sep 17 00:00:00 2001 From: balvinderz Date: Sat, 25 Sep 2021 18:38:33 +0530 Subject: [PATCH 01/34] scaling and image quality done --- packages/image_picker/image_picker/pubspec.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index ba5ce6635ed6..81a59cde52fc 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -24,7 +24,8 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - image_picker_for_web: ^2.1.0 + image_picker_for_web: + path: ../image_picker_for_web image_picker_platform_interface: ^2.3.0 dev_dependencies: From c3c13448614a6fa8242d1780fe7c39573377278d Mon Sep 17 00:00:00 2001 From: balvinderz Date: Sat, 25 Sep 2021 18:39:03 +0530 Subject: [PATCH 02/34] image scaling and quality done --- .../lib/image_picker_for_web.dart | 123 +++++++++++++++--- 1 file changed, 103 insertions(+), 20 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index b170ee3256ab..236933e1f2a6 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -121,6 +121,9 @@ class ImagePickerPlugin extends ImagePickerPlatform { List files = await getFiles( accept: _kAcceptImageMimeType, capture: capture, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, ); return files.first; } @@ -158,7 +161,12 @@ class ImagePickerPlugin extends ImagePickerPlatform { double? maxHeight, int? imageQuality, }) { - return getFiles(accept: _kAcceptImageMimeType, multiple: true); + return getFiles( + accept: _kAcceptImageMimeType, + multiple: true, + imageQuality: imageQuality, + maxHeight: maxHeight, + maxWidth: maxWidth); } /// Injects a file input with the specified accept+capture attributes, and @@ -171,11 +179,13 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// See https://caniuse.com/#feat=html-media-capture @visibleForTesting - Future> getFiles({ - String? accept, - String? capture, - bool multiple = false, - }) { + Future> getFiles( + {String? accept, + String? capture, + bool multiple = false, + double? maxWidth, + double? maxHeight, + int? imageQuality}) { html.FileUploadInputElement input = createInputElement( accept, capture, @@ -183,7 +193,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { ) as html.FileUploadInputElement; _injectAndActivate(input); - return _getSelectedXFiles(input); + return _getSelectedXFiles(input, + maxWidth: maxWidth, maxHeight: maxHeight, imageQuality: imageQuality); } // DOM methods @@ -238,23 +249,29 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Monitors an and returns the selected file(s). - Future> _getSelectedXFiles(html.FileUploadInputElement input) { + Future> _getSelectedXFiles(html.FileUploadInputElement input, + {double? maxWidth, double? maxHeight, int? imageQuality}) { final Completer> _completer = Completer>(); // Observe the input until we can return something - input.onChange.first.then((event) { + input.onChange.first.then((event) async { final files = _handleOnChangeEvent(event); if (!_completer.isCompleted && files != null) { - _completer.complete(files - .map((file) => XFile( - html.Url.createObjectUrl(file), - name: file.name, - length: file.size, - lastModified: DateTime.fromMillisecondsSinceEpoch( - file.lastModified ?? DateTime.now().millisecondsSinceEpoch, - ), - mimeType: file.type, - )) - .toList()); + //Convert the file if one of the parameter is not null + if (imageQuality != null || maxWidth != null || maxHeight != null) { + final convertedFileUris = await Future.wait(files.map((e) async => + await _getCompressedUri(e, imageQuality, maxHeight, maxWidth))); + + int index = 0; + _completer.complete(files.map((file) { + index += 1; + final xFile = file.toXFile(path: convertedFileUris[index - 1]); + return xFile; + }).toList()); + } else { + _completer.complete(files.map((file) { + return file.toXFile(); + }).toList()); + } } }); input.onError.first.then((event) { @@ -310,6 +327,72 @@ class ImagePickerPlugin extends ImagePickerPlatform { _target.children.add(element); element.click(); } + + Future _getCompressedUri(html.File file, int? imageQuality, + double? maxHeight, double? maxWidth) async { + //max width, max height, imageQuality are not supported for gif + if (file.type == "image/gif") { + return html.Url.createObjectUrl(file); + } + + final Completer _completer = Completer(); + final blobUrl = html.Url.createObjectUrl(file); + final image = html.ImageElement(src: blobUrl); + image.onLoad.listen((event) async { + html.Url.revokeObjectUrl(blobUrl); + final canvas = html.CanvasElement(); + final size = _calculateSize(image, maxWidth ?? image.width!.toDouble(), + maxHeight ?? image.height!.toDouble()); + canvas.width = size[0]; + canvas.height = size[1]; + final ctx = canvas.context2D; + if (maxHeight == null && maxWidth == null) { + ctx.drawImage(image, 0, 0); + } else { + ctx.drawImageScaled(image, 0, 0, canvas.width!, canvas.height!); + } + final blob = + await canvas.toBlob(file.type, (imageQuality ?? 100) / 100.0); // Image quality only works for jpeg images + _completer.complete(html.Url.createObjectUrlFromBlob(blob)); + }); + image.onError.listen((event) { + //Return the original image if error comes + _completer.complete(html.Url.createObjectUrl(file)); + }); + return _completer.future; + } + + List _calculateSize( + html.ImageElement img, double maxWidth, double maxHeight) { + var width = img.width!; + var height = img.height!; + + // calculate the width and height, constraining the proportions + if (width > height) { + if (width > maxWidth) { + height = ((height * maxWidth) / width).round(); + width = maxWidth.toInt(); + } + } else { + if (height > maxHeight) { + width = ((width * maxHeight) / height).round(); + height = maxHeight.toInt(); + } + } + return [width, height]; + } +} + +extension on html.File { + XFile toXFile({String? path}) => XFile( + path ?? html.Url.createObjectUrl(this), + name: this.name, + length: this.size, + lastModified: DateTime.fromMillisecondsSinceEpoch( + this.lastModified ?? DateTime.now().millisecondsSinceEpoch, + ), + mimeType: this.type, + ); } // Some tools to override behavior for unit-testing From cf0ee4d1f989e39e75aee2df1dc9b36497c60887 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Mon, 27 Sep 2021 21:25:05 +0530 Subject: [PATCH 03/34] implement max width max height and imageQuality for web --- .../image_picker_for_web/lib/image_picker_for_web.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 236933e1f2a6..2957774a5a2d 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:html' as html; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:image/image.dart'; import 'package:meta/meta.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; @@ -334,7 +335,6 @@ class ImagePickerPlugin extends ImagePickerPlatform { if (file.type == "image/gif") { return html.Url.createObjectUrl(file); } - final Completer _completer = Completer(); final blobUrl = html.Url.createObjectUrl(file); final image = html.ImageElement(src: blobUrl); From 130f78a8dd36fbfc6c234ac36e23822be6ebe626 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Mon, 27 Sep 2021 21:26:30 +0530 Subject: [PATCH 04/34] added maxWidth max height and imageQuality for web --- packages/image_picker/image_picker_for_web/CHANGELOG.md | 4 ++++ packages/image_picker/image_picker_for_web/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index d11ead3bb64e..df305f1dd8a7 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.4 + +* Implemented `maxWidth`, `maxHeight` and `imageQuality` + ## 2.1.3 * Add `implements` to pubspec. diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 895486f3de06..d0d97011c9ec 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 2.1.3 +version: 2.1.4 environment: sdk: ">=2.12.0 <3.0.0" From b8d3d65ccb376a166256e6ab71b4b326cc177c6b Mon Sep 17 00:00:00 2001 From: balvinderz Date: Mon, 27 Sep 2021 21:30:17 +0530 Subject: [PATCH 05/34] dartfmt --- .../lib/image_picker_for_web.dart | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 2957774a5a2d..fc66243ab81b 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:html' as html; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:image/image.dart'; import 'package:meta/meta.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; @@ -93,7 +92,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { String? capture, }) { html.FileUploadInputElement input = - createInputElement(accept, capture) as html.FileUploadInputElement; + createInputElement(accept, capture) as html.FileUploadInputElement; _injectAndActivate(input); return _getSelectedFile(input); } @@ -180,13 +179,12 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// See https://caniuse.com/#feat=html-media-capture @visibleForTesting - Future> getFiles( - {String? accept, - String? capture, - bool multiple = false, - double? maxWidth, - double? maxHeight, - int? imageQuality}) { + Future> getFiles({String? accept, + String? capture, + bool multiple = false, + double? maxWidth, + double? maxHeight, + int? imageQuality}) { html.FileUploadInputElement input = createInputElement( accept, capture, @@ -222,7 +220,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Returns a list of selected files. List? _handleOnChangeEvent(html.Event event) { final html.FileUploadInputElement input = - event.target as html.FileUploadInputElement; + event.target as html.FileUploadInputElement; return _getFilesFromInput(input); } @@ -260,7 +258,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { //Convert the file if one of the parameter is not null if (imageQuality != null || maxWidth != null || maxHeight != null) { final convertedFileUris = await Future.wait(files.map((e) async => - await _getCompressedUri(e, imageQuality, maxHeight, maxWidth))); + await _getCompressedUri(e, imageQuality, maxHeight, maxWidth))); int index = 0; _completer.complete(files.map((file) { @@ -291,7 +289,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { var target = html.querySelector('#${id}'); if (target == null) { final html.Element targetElement = - html.Element.tag('flt-image-picker-inputs')..id = id; + html.Element.tag('flt-image-picker-inputs') + ..id = id; html.querySelector('body')!.children.add(targetElement); target = targetElement; @@ -302,11 +301,10 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Creates an input element that accepts certain file types, and /// allows to `capture` from the device's cameras (where supported) @visibleForTesting - html.Element createInputElement( - String? accept, - String? capture, { - bool multiple = false, - }) { + html.Element createInputElement(String? accept, + String? capture, { + bool multiple = false, + }) { if (_hasOverrides) { return _overrides!.createInputElement(accept, capture); } @@ -352,7 +350,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { ctx.drawImageScaled(image, 0, 0, canvas.width!, canvas.height!); } final blob = - await canvas.toBlob(file.type, (imageQuality ?? 100) / 100.0); // Image quality only works for jpeg images + await canvas.toBlob(file.type, (imageQuality ?? 100) / + 100.0); // Image quality only works for jpeg images _completer.complete(html.Url.createObjectUrlFromBlob(blob)); }); image.onError.listen((event) { @@ -362,8 +361,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { return _completer.future; } - List _calculateSize( - html.ImageElement img, double maxWidth, double maxHeight) { + List _calculateSize(html.ImageElement img, double maxWidth, + double maxHeight) { var width = img.width!; var height = img.height!; @@ -384,12 +383,15 @@ class ImagePickerPlugin extends ImagePickerPlatform { } extension on html.File { - XFile toXFile({String? path}) => XFile( + XFile toXFile({String? path}) => + XFile( path ?? html.Url.createObjectUrl(this), name: this.name, length: this.size, lastModified: DateTime.fromMillisecondsSinceEpoch( - this.lastModified ?? DateTime.now().millisecondsSinceEpoch, + this.lastModified ?? DateTime + .now() + .millisecondsSinceEpoch, ), mimeType: this.type, ); @@ -399,14 +401,14 @@ extension on html.File { /// A function that creates a file input with the passed in `accept` and `capture` attributes. @visibleForTesting typedef OverrideCreateInputFunction = html.Element Function( - String? accept, - String? capture, -); + String? accept, + String? capture, + ); /// A function that extracts list of files from the file `input` passed in. @visibleForTesting typedef OverrideExtractMultipleFilesFromInputFunction = List - Function(html.Element? input); +Function(html.Element? input); /// Overrides for some of the functionality above. @visibleForTesting From be720c29859476d66279063ff467e499f95abe81 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Mon, 27 Sep 2021 21:34:52 +0530 Subject: [PATCH 06/34] revert pubspec.yaml of image_picker --- packages/image_picker/image_picker/pubspec.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 81a59cde52fc..ba5ce6635ed6 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -24,8 +24,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - image_picker_for_web: - path: ../image_picker_for_web + image_picker_for_web: ^2.1.0 image_picker_platform_interface: ^2.3.0 dev_dependencies: From 754cec580e0033f3929112270b045502a4a9afa2 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Mon, 27 Sep 2021 21:37:15 +0530 Subject: [PATCH 07/34] run format --- .../lib/image_picker_for_web.dart | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index fc66243ab81b..1bcd077cc085 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -92,7 +92,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { String? capture, }) { html.FileUploadInputElement input = - createInputElement(accept, capture) as html.FileUploadInputElement; + createInputElement(accept, capture) as html.FileUploadInputElement; _injectAndActivate(input); return _getSelectedFile(input); } @@ -179,12 +179,13 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// See https://caniuse.com/#feat=html-media-capture @visibleForTesting - Future> getFiles({String? accept, - String? capture, - bool multiple = false, - double? maxWidth, - double? maxHeight, - int? imageQuality}) { + Future> getFiles( + {String? accept, + String? capture, + bool multiple = false, + double? maxWidth, + double? maxHeight, + int? imageQuality}) { html.FileUploadInputElement input = createInputElement( accept, capture, @@ -220,7 +221,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Returns a list of selected files. List? _handleOnChangeEvent(html.Event event) { final html.FileUploadInputElement input = - event.target as html.FileUploadInputElement; + event.target as html.FileUploadInputElement; return _getFilesFromInput(input); } @@ -258,7 +259,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { //Convert the file if one of the parameter is not null if (imageQuality != null || maxWidth != null || maxHeight != null) { final convertedFileUris = await Future.wait(files.map((e) async => - await _getCompressedUri(e, imageQuality, maxHeight, maxWidth))); + await _getCompressedUri(e, imageQuality, maxHeight, maxWidth))); int index = 0; _completer.complete(files.map((file) { @@ -289,8 +290,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { var target = html.querySelector('#${id}'); if (target == null) { final html.Element targetElement = - html.Element.tag('flt-image-picker-inputs') - ..id = id; + html.Element.tag('flt-image-picker-inputs')..id = id; html.querySelector('body')!.children.add(targetElement); target = targetElement; @@ -301,10 +301,11 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Creates an input element that accepts certain file types, and /// allows to `capture` from the device's cameras (where supported) @visibleForTesting - html.Element createInputElement(String? accept, - String? capture, { - bool multiple = false, - }) { + html.Element createInputElement( + String? accept, + String? capture, { + bool multiple = false, + }) { if (_hasOverrides) { return _overrides!.createInputElement(accept, capture); } @@ -349,9 +350,10 @@ class ImagePickerPlugin extends ImagePickerPlatform { } else { ctx.drawImageScaled(image, 0, 0, canvas.width!, canvas.height!); } - final blob = - await canvas.toBlob(file.type, (imageQuality ?? 100) / - 100.0); // Image quality only works for jpeg images + final blob = await canvas.toBlob( + file.type, + (imageQuality ?? 100) / + 100.0); // Image quality only works for jpeg images _completer.complete(html.Url.createObjectUrlFromBlob(blob)); }); image.onError.listen((event) { @@ -361,8 +363,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { return _completer.future; } - List _calculateSize(html.ImageElement img, double maxWidth, - double maxHeight) { + List _calculateSize( + html.ImageElement img, double maxWidth, double maxHeight) { var width = img.width!; var height = img.height!; @@ -383,15 +385,12 @@ class ImagePickerPlugin extends ImagePickerPlatform { } extension on html.File { - XFile toXFile({String? path}) => - XFile( + XFile toXFile({String? path}) => XFile( path ?? html.Url.createObjectUrl(this), name: this.name, length: this.size, lastModified: DateTime.fromMillisecondsSinceEpoch( - this.lastModified ?? DateTime - .now() - .millisecondsSinceEpoch, + this.lastModified ?? DateTime.now().millisecondsSinceEpoch, ), mimeType: this.type, ); @@ -401,14 +400,14 @@ extension on html.File { /// A function that creates a file input with the passed in `accept` and `capture` attributes. @visibleForTesting typedef OverrideCreateInputFunction = html.Element Function( - String? accept, - String? capture, - ); + String? accept, + String? capture, +); /// A function that extracts list of files from the file `input` passed in. @visibleForTesting typedef OverrideExtractMultipleFilesFromInputFunction = List -Function(html.Element? input); + Function(html.Element? input); /// Overrides for some of the functionality above. @visibleForTesting From 942ac46543703603a30d771eada10a1dd6773fb0 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 16:57:40 +0530 Subject: [PATCH 08/34] added image resizer class --- .../image_picker/image_picker/pubspec.yaml | 3 +- .../image_picker_for_web_test.dart | 41 +++++ .../lib/image_picker_for_web.dart | 157 +++++------------- .../lib/image_resizer.dart | 77 +++++++++ 4 files changed, 161 insertions(+), 117 deletions(-) create mode 100644 packages/image_picker/image_picker_for_web/lib/image_resizer.dart diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index ba5ce6635ed6..81a59cde52fc 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -24,7 +24,8 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - image_picker_for_web: ^2.1.0 + image_picker_for_web: + path: ../image_picker_for_web image_picker_platform_interface: ^2.3.0 dev_dependencies: diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart index c1025a9f07d3..834939ebc0c4 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart @@ -2,6 +2,7 @@ // 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:convert'; import 'dart:html' as html; import 'dart:typed_data'; @@ -13,14 +14,19 @@ import 'package:integration_test/integration_test.dart'; final String expectedStringContents = 'Hello, world!'; final String otherStringContents = 'Hello again, world!'; +final String pngFileBase64Contents = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAcTAAAHEwHOIA8IAAAB0mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj4KICAgICAgICAgPHBob3Rvc2hvcDpDcmVkaXQ+wqkgR29vZ2xlPC9waG90b3Nob3A6Q3JlZGl0PgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4K43gerQAAAA1JREFUCB1jeOVs+h8ABd8CYkMBAJAAAAAASUVORK5CYII="; + final Uint8List bytes = utf8.encode(expectedStringContents) as Uint8List; final Uint8List otherBytes = utf8.encode(otherStringContents) as Uint8List; +final Uint8List pngFileBytes = utf8.encode(pngFileBase64Contents) as Uint8List; + final Map options = { 'type': 'text/plain', 'lastModified': DateTime.utc(2017, 12, 13).millisecondsSinceEpoch, }; final html.File textFile = html.File([bytes], 'hello.txt', options); final html.File secondTextFile = html.File([otherBytes], 'secondFile.txt'); +final html.File pngImageFile = html.File([pngFileBytes],'testimage.png'); void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -112,6 +118,39 @@ void main() { expect(secondFile.length(), completion(secondTextFile.size)); }); + testWidgets('image is not scaled if maxWidth and maxHeight is not set',(WidgetTester tester) async { + final mockInput = html.FileUploadInputElement(); + final overrides = ImagePickerPluginTestOverrides() + ..createInputElement = ((_, __) => mockInput) + ..getMultipleFilesFromInput = ((_) => [pngImageFile]); + + final plugin = ImagePickerPlugin(overrides: overrides); + + // Init the pick file dialog... + final image = plugin.getImage(source: ImageSource.gallery,); + final imageElement = html.ImageElement(src: pngFileBase64Contents); + final imageloadCompleter = Completer(); + imageElement.onLoad.listen((event) { + print(event); + imageloadCompleter.complete(); + }); + mockInput.dispatchEvent(html.Event('change')); + + await imageloadCompleter.future; + expect(imageElement.width,1); + expect(imageElement.height,1); + final XFile xFile = await image; + final pickedImageElement = html.ImageElement(src: html.Url.createObjectUrl(html.Blob(await xFile.readAsBytes()))); + final newCompleter = Completer(); + pickedImageElement.onLoad.listen((event) { + newCompleter.complete(); + } + ); + await newCompleter.future; + expect(pickedImageElement.width,99); + + }); + // There's no good way of detecting when the user has "aborted" the selection. testWidgets('computeCaptureAttribute', (WidgetTester tester) async { @@ -170,4 +209,6 @@ void main() { expect(input.attributes, contains('multiple')); }); }); + + } diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 1bcd077cc085..d20319ba9e55 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:html' as html; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:image_picker_for_web/image_resizer.dart'; import 'package:meta/meta.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; @@ -23,6 +24,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { late html.Element _target; + final _imageResizer = ImageResizer(); + /// A constructor that allows tests to override the function that creates file inputs. ImagePickerPlugin({ @visibleForTesting ImagePickerPluginTestOverrides? overrides, @@ -92,7 +95,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { String? capture, }) { html.FileUploadInputElement input = - createInputElement(accept, capture) as html.FileUploadInputElement; + createInputElement(accept, capture) as html.FileUploadInputElement; _injectAndActivate(input); return _getSelectedFile(input); } @@ -121,11 +124,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { List files = await getFiles( accept: _kAcceptImageMimeType, capture: capture, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, ); - return files.first; + return _imageResizer.resizeImageIfNeeded(files.first, maxWidth, maxHeight, imageQuality); } /// Returns an [XFile] containing the video that was picked. @@ -160,13 +160,11 @@ class ImagePickerPlugin extends ImagePickerPlatform { double? maxWidth, double? maxHeight, int? imageQuality, - }) { - return getFiles( - accept: _kAcceptImageMimeType, - multiple: true, - imageQuality: imageQuality, - maxHeight: maxHeight, - maxWidth: maxWidth); + }) async { + return await Future.wait((await getFiles( + accept: _kAcceptImageMimeType, multiple: true)) + .map((e) => + _imageResizer.resizeImageIfNeeded(e, maxWidth, maxHeight, imageQuality))); } /// Injects a file input with the specified accept+capture attributes, and @@ -179,13 +177,11 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// See https://caniuse.com/#feat=html-media-capture @visibleForTesting - Future> getFiles( - {String? accept, - String? capture, - bool multiple = false, - double? maxWidth, - double? maxHeight, - int? imageQuality}) { + Future> getFiles({ + String? accept, + String? capture, + bool multiple = false, + }) { html.FileUploadInputElement input = createInputElement( accept, capture, @@ -193,8 +189,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { ) as html.FileUploadInputElement; _injectAndActivate(input); - return _getSelectedXFiles(input, - maxWidth: maxWidth, maxHeight: maxHeight, imageQuality: imageQuality); + return _getSelectedXFiles(input); } // DOM methods @@ -221,7 +216,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Returns a list of selected files. List? _handleOnChangeEvent(html.Event event) { final html.FileUploadInputElement input = - event.target as html.FileUploadInputElement; + event.target as html.FileUploadInputElement; return _getFilesFromInput(input); } @@ -249,29 +244,25 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Monitors an and returns the selected file(s). - Future> _getSelectedXFiles(html.FileUploadInputElement input, - {double? maxWidth, double? maxHeight, int? imageQuality}) { + Future> _getSelectedXFiles(html.FileUploadInputElement input) { final Completer> _completer = Completer>(); // Observe the input until we can return something input.onChange.first.then((event) async { final files = _handleOnChangeEvent(event); if (!_completer.isCompleted && files != null) { - //Convert the file if one of the parameter is not null - if (imageQuality != null || maxWidth != null || maxHeight != null) { - final convertedFileUris = await Future.wait(files.map((e) async => - await _getCompressedUri(e, imageQuality, maxHeight, maxWidth))); - - int index = 0; - _completer.complete(files.map((file) { - index += 1; - final xFile = file.toXFile(path: convertedFileUris[index - 1]); - return xFile; - }).toList()); - } else { - _completer.complete(files.map((file) { - return file.toXFile(); - }).toList()); - } + _completer.complete(files.map((file) { + return XFile( + html.Url.createObjectUrl(file), + name: file.name, + length: file.size, + lastModified: DateTime.fromMillisecondsSinceEpoch( + file.lastModified ?? DateTime + .now() + .millisecondsSinceEpoch, + ), + mimeType: file.type, + ); + }).toList()); } }); input.onError.first.then((event) { @@ -290,7 +281,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { var target = html.querySelector('#${id}'); if (target == null) { final html.Element targetElement = - html.Element.tag('flt-image-picker-inputs')..id = id; + html.Element.tag('flt-image-picker-inputs') + ..id = id; html.querySelector('body')!.children.add(targetElement); target = targetElement; @@ -301,11 +293,10 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Creates an input element that accepts certain file types, and /// allows to `capture` from the device's cameras (where supported) @visibleForTesting - html.Element createInputElement( - String? accept, - String? capture, { - bool multiple = false, - }) { + html.Element createInputElement(String? accept, + String? capture, { + bool multiple = false, + }) { if (_hasOverrides) { return _overrides!.createInputElement(accept, capture); } @@ -327,87 +318,21 @@ class ImagePickerPlugin extends ImagePickerPlatform { _target.children.add(element); element.click(); } - - Future _getCompressedUri(html.File file, int? imageQuality, - double? maxHeight, double? maxWidth) async { - //max width, max height, imageQuality are not supported for gif - if (file.type == "image/gif") { - return html.Url.createObjectUrl(file); - } - final Completer _completer = Completer(); - final blobUrl = html.Url.createObjectUrl(file); - final image = html.ImageElement(src: blobUrl); - image.onLoad.listen((event) async { - html.Url.revokeObjectUrl(blobUrl); - final canvas = html.CanvasElement(); - final size = _calculateSize(image, maxWidth ?? image.width!.toDouble(), - maxHeight ?? image.height!.toDouble()); - canvas.width = size[0]; - canvas.height = size[1]; - final ctx = canvas.context2D; - if (maxHeight == null && maxWidth == null) { - ctx.drawImage(image, 0, 0); - } else { - ctx.drawImageScaled(image, 0, 0, canvas.width!, canvas.height!); - } - final blob = await canvas.toBlob( - file.type, - (imageQuality ?? 100) / - 100.0); // Image quality only works for jpeg images - _completer.complete(html.Url.createObjectUrlFromBlob(blob)); - }); - image.onError.listen((event) { - //Return the original image if error comes - _completer.complete(html.Url.createObjectUrl(file)); - }); - return _completer.future; - } - - List _calculateSize( - html.ImageElement img, double maxWidth, double maxHeight) { - var width = img.width!; - var height = img.height!; - - // calculate the width and height, constraining the proportions - if (width > height) { - if (width > maxWidth) { - height = ((height * maxWidth) / width).round(); - width = maxWidth.toInt(); - } - } else { - if (height > maxHeight) { - width = ((width * maxHeight) / height).round(); - height = maxHeight.toInt(); - } - } - return [width, height]; - } } -extension on html.File { - XFile toXFile({String? path}) => XFile( - path ?? html.Url.createObjectUrl(this), - name: this.name, - length: this.size, - lastModified: DateTime.fromMillisecondsSinceEpoch( - this.lastModified ?? DateTime.now().millisecondsSinceEpoch, - ), - mimeType: this.type, - ); -} // Some tools to override behavior for unit-testing /// A function that creates a file input with the passed in `accept` and `capture` attributes. @visibleForTesting typedef OverrideCreateInputFunction = html.Element Function( - String? accept, - String? capture, -); + String? accept, + String? capture, + ); /// A function that extracts list of files from the file `input` passed in. @visibleForTesting typedef OverrideExtractMultipleFilesFromInputFunction = List - Function(html.Element? input); +Function(html.Element? input); /// Overrides for some of the functionality above. @visibleForTesting diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart new file mode 100644 index 000000000000..1b98ebe5b50c --- /dev/null +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -0,0 +1,77 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'dart:html' as html; + + +/// Resizes images +class ImageResizer { + /// Resizes images if needed + Future resizeImageIfNeeded(XFile file, double? maxWidth, + double? maxHeight, int? imageQuality) async { + if (maxWidth == null && maxHeight == null && imageQuality == null || + file.mimeType == "image/gif") { + return file; + } + final imageLoadCompleter = Completer(); + final imageElement = html.ImageElement(); + imageElement.src = file.path; + imageElement.onLoad.listen((event) { + html.Url.revokeObjectUrl(file.path); + imageLoadCompleter.complete(); + }); + + //Return the original image if error comes + imageElement.onError.listen((event) { + imageLoadCompleter.complete(); + }); + await imageLoadCompleter.future; + + final newImageSize = calculateSize( + imageElement.width!.toDouble(), imageElement.height!.toDouble(), + maxWidth ?? imageElement.width!.toDouble(), + maxHeight ?? imageElement.height!.toDouble()); + final canvas = html.CanvasElement(); + canvas.width = newImageSize.width.toInt(); + canvas.height = newImageSize.height.toInt(); + final context = canvas.context2D; + if (maxHeight == null && maxWidth == null) { + context.drawImage(imageElement, 0, 0); + } else { + context.drawImageScaled( + imageElement, 0, 0, canvas.width!, canvas.height!); + } + final blob = await canvas.toBlob( + file.mimeType, + (imageQuality ?? 100) / + 100.0); // Image quality only works for jpeg images + return XFile( + html.Url.createObjectUrlFromBlob(blob), + mimeType: file.mimeType, + name: file.name, + lastModified: DateTime.now(), + length: blob.size + ); + } + + + /// Calculates the size of the scaled image. + Size calculateSize(double imageWidth, double imageHeight, double maxWidth, + double maxHeight) { + var width = imageWidth; + var height = imageHeight; + var scaledHeight = height; + var scaledWidth = width; + if (width > height) { + if (width > maxWidth) { + scaledHeight = ((height * maxWidth) / width); + } + } else { + if (height > maxHeight) { + scaledWidth = ((width * maxHeight) / height); + } + } + return Size(scaledWidth, scaledHeight); + } +} From 91f92e65b91f9b050190b2c168fea2c62131b49a Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 17:28:17 +0530 Subject: [PATCH 09/34] fix calculate size logic --- .../lib/image_resizer.dart | 88 ++++++++++++------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index 1b98ebe5b50c..cf0afc2e2d7c 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -1,17 +1,20 @@ import 'dart:async'; +import 'dart:math'; import 'dart:ui'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'dart:html' as html; - /// Resizes images class ImageResizer { /// Resizes images if needed Future resizeImageIfNeeded(XFile file, double? maxWidth, double? maxHeight, int? imageQuality) async { - if (maxWidth == null && maxHeight == null && imageQuality == null || + if (maxWidth == null && + maxHeight == null && + _isImageQualityValid(imageQuality) && file.mimeType == "image/gif") { + //TODO Implement maxWidth and maxHeight for image/gif return file; } final imageLoadCompleter = Completer(); @@ -22,16 +25,10 @@ class ImageResizer { imageLoadCompleter.complete(); }); - //Return the original image if error comes - imageElement.onError.listen((event) { - imageLoadCompleter.complete(); - }); await imageLoadCompleter.future; - final newImageSize = calculateSize( - imageElement.width!.toDouble(), imageElement.height!.toDouble(), - maxWidth ?? imageElement.width!.toDouble(), - maxHeight ?? imageElement.height!.toDouble()); + final newImageSize = calculateSize(imageElement.width!.toDouble(), + imageElement.height!.toDouble(), maxWidth, maxHeight); final canvas = html.CanvasElement(); canvas.width = newImageSize.width.toInt(); canvas.height = newImageSize.height.toInt(); @@ -44,34 +41,65 @@ class ImageResizer { } final blob = await canvas.toBlob( file.mimeType, - (imageQuality ?? 100) / + (min(imageQuality ?? 100, 100)) / 100.0); // Image quality only works for jpeg images - return XFile( - html.Url.createObjectUrlFromBlob(blob), + return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: file.mimeType, name: file.name, lastModified: DateTime.now(), - length: blob.size - ); + length: blob.size); } - /// Calculates the size of the scaled image. - Size calculateSize(double imageWidth, double imageHeight, double maxWidth, - double maxHeight) { - var width = imageWidth; - var height = imageHeight; - var scaledHeight = height; - var scaledWidth = width; - if (width > height) { - if (width > maxWidth) { - scaledHeight = ((height * maxWidth) / width); - } - } else { - if (height > maxHeight) { - scaledWidth = ((width * maxHeight) / height); + Size calculateSize(double imageWidth, double imageHeight, double? maxWidth, + double? maxHeight) { + double originalWidth = imageWidth; + double originalHeight = imageHeight; + + bool hasMaxWidth = maxWidth != null; + bool hasMaxHeight = maxHeight != null; + double width = hasMaxWidth ? min(maxWidth, originalWidth) : originalWidth; + double height = + hasMaxHeight ? min(maxHeight, originalHeight) : originalHeight; + bool shouldDownscaleWidth = hasMaxWidth && maxWidth < originalWidth; + bool shouldDownscaleHeight = hasMaxHeight && maxHeight < originalHeight; + bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight; + if (shouldDownscale) { + double downscaledWidth = + ((height / originalHeight) * originalWidth).floorToDouble(); + double downscaledHeight = + ((width / originalWidth) * originalHeight).floorToDouble(); + + if (width < height) { + if (!hasMaxWidth) { + width = downscaledWidth; + } else { + height = downscaledHeight; + } + } else if (height < width) { + if (!hasMaxHeight) { + height = downscaledHeight; + } else { + width = downscaledWidth; + } + } else { + if (originalWidth < originalHeight) { + width = downscaledWidth; + } else if (originalHeight < originalWidth) { + height = downscaledHeight; + } } } - return Size(scaledWidth, scaledHeight); + if (hasMaxHeight) { + assert(height <= maxHeight); + } + if (hasMaxWidth) { + assert(width <= maxWidth); + } + return Size(width, height); + } + + bool _isImageQualityValid(int? imageQuality) { + return imageQuality == null || (imageQuality >= 0 && imageQuality <= 100); } } From b10408f9e8debf2e68b6063fcbc3f98029989105 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 17:31:39 +0530 Subject: [PATCH 10/34] fix if condition in resizeImage --- .../image_picker/image_picker_for_web/lib/image_resizer.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index cf0afc2e2d7c..dfa8c87fabb6 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -11,8 +11,8 @@ class ImageResizer { Future resizeImageIfNeeded(XFile file, double? maxWidth, double? maxHeight, int? imageQuality) async { if (maxWidth == null && - maxHeight == null && - _isImageQualityValid(imageQuality) && + maxHeight == null && + _isImageQualityValid(imageQuality) || file.mimeType == "image/gif") { //TODO Implement maxWidth and maxHeight for image/gif return file; From 42d8191ce1709861c55dbe55d7be6667c491192c Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 17:54:05 +0530 Subject: [PATCH 11/34] image resizer done --- .../lib/image_resizer.dart | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index dfa8c87fabb6..d6aa00aae9b9 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -7,12 +7,13 @@ import 'dart:html' as html; /// Resizes images class ImageResizer { - /// Resizes images if needed + /// Resizes the image if needed + /// Does not support gif image Future resizeImageIfNeeded(XFile file, double? maxWidth, double? maxHeight, int? imageQuality) async { - if (maxWidth == null && - maxHeight == null && - _isImageQualityValid(imageQuality) || + if ((maxWidth == null && + maxHeight == null + ) || !_isImageQualityValid(imageQuality) || file.mimeType == "image/gif") { //TODO Implement maxWidth and maxHeight for image/gif return file; @@ -39,10 +40,11 @@ class ImageResizer { context.drawImageScaled( imageElement, 0, 0, canvas.width!, canvas.height!); } + final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / + 100.0); final blob = await canvas.toBlob( file.mimeType, - (min(imageQuality ?? 100, 100)) / - 100.0); // Image quality only works for jpeg images + calculatedImageQuality); // Image quality only works for jpeg and webp images return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: file.mimeType, name: file.name, @@ -50,7 +52,7 @@ class ImageResizer { length: blob.size); } - /// Calculates the size of the scaled image. + /// Calculates the size of the scaled image from [maxWidth] and [maxHeigth. Size calculateSize(double imageWidth, double imageHeight, double? maxWidth, double? maxHeight) { double originalWidth = imageWidth; @@ -60,15 +62,15 @@ class ImageResizer { bool hasMaxHeight = maxHeight != null; double width = hasMaxWidth ? min(maxWidth, originalWidth) : originalWidth; double height = - hasMaxHeight ? min(maxHeight, originalHeight) : originalHeight; + hasMaxHeight ? min(maxHeight, originalHeight) : originalHeight; bool shouldDownscaleWidth = hasMaxWidth && maxWidth < originalWidth; bool shouldDownscaleHeight = hasMaxHeight && maxHeight < originalHeight; bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight; if (shouldDownscale) { double downscaledWidth = - ((height / originalHeight) * originalWidth).floorToDouble(); + ((height / originalHeight) * originalWidth).floorToDouble(); double downscaledHeight = - ((width / originalWidth) * originalHeight).floorToDouble(); + ((width / originalWidth) * originalHeight).floorToDouble(); if (width < height) { if (!hasMaxWidth) { From 019a738ce899fca3b4a722d4c710c6027040713c Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 19:36:53 +0530 Subject: [PATCH 12/34] added utils test' --- .../lib/image_picker_for_web.dart | 47 +++++------ .../lib/image_resizer.dart | 76 +++--------------- .../lib/image_resizer_utils.dart | 70 ++++++++++++++++ .../test/image_resizer_test.dart | 79 +++++++++++++++++++ 4 files changed, 184 insertions(+), 88 deletions(-) create mode 100644 packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart create mode 100644 packages/image_picker/image_picker_for_web/test/image_resizer_test.dart diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index d20319ba9e55..f758b46235e9 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -24,12 +24,15 @@ class ImagePickerPlugin extends ImagePickerPlatform { late html.Element _target; - final _imageResizer = ImageResizer(); + late ImageResizer _imageResizer; /// A constructor that allows tests to override the function that creates file inputs. ImagePickerPlugin({ @visibleForTesting ImagePickerPluginTestOverrides? overrides, - }) : _overrides = overrides { + @visibleForTesting ImageResizer? imageResizer, + }) : _overrides = overrides + { + _imageResizer = imageResizer ?? ImageResizer(); _target = _ensureInitialized(_kImagePickerInputsDomId); } @@ -95,7 +98,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { String? capture, }) { html.FileUploadInputElement input = - createInputElement(accept, capture) as html.FileUploadInputElement; + createInputElement(accept, capture) as html.FileUploadInputElement; _injectAndActivate(input); return _getSelectedFile(input); } @@ -125,7 +128,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { accept: _kAcceptImageMimeType, capture: capture, ); - return _imageResizer.resizeImageIfNeeded(files.first, maxWidth, maxHeight, imageQuality); + return _imageResizer.resizeImageIfNeeded( + files.first, maxWidth, maxHeight, imageQuality); } /// Returns an [XFile] containing the video that was picked. @@ -161,10 +165,10 @@ class ImagePickerPlugin extends ImagePickerPlatform { double? maxHeight, int? imageQuality, }) async { - return await Future.wait((await getFiles( - accept: _kAcceptImageMimeType, multiple: true)) - .map((e) => - _imageResizer.resizeImageIfNeeded(e, maxWidth, maxHeight, imageQuality))); + return await Future.wait( + (await getFiles(accept: _kAcceptImageMimeType, multiple: true)).map( + (e) => _imageResizer.resizeImageIfNeeded( + e, maxWidth, maxHeight, imageQuality))); } /// Injects a file input with the specified accept+capture attributes, and @@ -216,7 +220,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Returns a list of selected files. List? _handleOnChangeEvent(html.Event event) { final html.FileUploadInputElement input = - event.target as html.FileUploadInputElement; + event.target as html.FileUploadInputElement; return _getFilesFromInput(input); } @@ -256,9 +260,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { name: file.name, length: file.size, lastModified: DateTime.fromMillisecondsSinceEpoch( - file.lastModified ?? DateTime - .now() - .millisecondsSinceEpoch, + file.lastModified ?? DateTime.now().millisecondsSinceEpoch, ), mimeType: file.type, ); @@ -281,8 +283,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { var target = html.querySelector('#${id}'); if (target == null) { final html.Element targetElement = - html.Element.tag('flt-image-picker-inputs') - ..id = id; + html.Element.tag('flt-image-picker-inputs')..id = id; html.querySelector('body')!.children.add(targetElement); target = targetElement; @@ -293,10 +294,11 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Creates an input element that accepts certain file types, and /// allows to `capture` from the device's cameras (where supported) @visibleForTesting - html.Element createInputElement(String? accept, - String? capture, { - bool multiple = false, - }) { + html.Element createInputElement( + String? accept, + String? capture, { + bool multiple = false, + }) { if (_hasOverrides) { return _overrides!.createInputElement(accept, capture); } @@ -320,19 +322,18 @@ class ImagePickerPlugin extends ImagePickerPlatform { } } - // Some tools to override behavior for unit-testing /// A function that creates a file input with the passed in `accept` and `capture` attributes. @visibleForTesting typedef OverrideCreateInputFunction = html.Element Function( - String? accept, - String? capture, - ); + String? accept, + String? capture, +); /// A function that extracts list of files from the file `input` passed in. @visibleForTesting typedef OverrideExtractMultipleFilesFromInputFunction = List -Function(html.Element? input); + Function(html.Element? input); /// Overrides for some of the functionality above. @visibleForTesting diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index d6aa00aae9b9..89d279a78a15 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:math'; -import 'dart:ui'; - +import 'package:image_picker_for_web/image_resizer_utils.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'dart:html' as html; @@ -9,11 +8,10 @@ import 'dart:html' as html; class ImageResizer { /// Resizes the image if needed /// Does not support gif image + /// Future resizeImageIfNeeded(XFile file, double? maxWidth, double? maxHeight, int? imageQuality) async { - if ((maxWidth == null && - maxHeight == null - ) || !_isImageQualityValid(imageQuality) || + if (!imageResizeNeeded(maxWidth, maxHeight, imageQuality) || file.mimeType == "image/gif") { //TODO Implement maxWidth and maxHeight for image/gif return file; @@ -28,8 +26,11 @@ class ImageResizer { await imageLoadCompleter.future; - final newImageSize = calculateSize(imageElement.width!.toDouble(), - imageElement.height!.toDouble(), maxWidth, maxHeight); + final newImageSize = calculateSizeOfScaledImage( + imageElement.width!.toDouble(), + imageElement.height!.toDouble(), + maxWidth, + maxHeight); final canvas = html.CanvasElement(); canvas.width = newImageSize.width.toInt(); canvas.height = newImageSize.height.toInt(); @@ -40,68 +41,13 @@ class ImageResizer { context.drawImageScaled( imageElement, 0, 0, canvas.width!, canvas.height!); } - final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / - 100.0); - final blob = await canvas.toBlob( - file.mimeType, + final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / 100.0); + final blob = await canvas.toBlob(file.mimeType, calculatedImageQuality); // Image quality only works for jpeg and webp images return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: file.mimeType, - name: file.name, + name: "scaled_" + file.name, lastModified: DateTime.now(), length: blob.size); } - - /// Calculates the size of the scaled image from [maxWidth] and [maxHeigth. - Size calculateSize(double imageWidth, double imageHeight, double? maxWidth, - double? maxHeight) { - double originalWidth = imageWidth; - double originalHeight = imageHeight; - - bool hasMaxWidth = maxWidth != null; - bool hasMaxHeight = maxHeight != null; - double width = hasMaxWidth ? min(maxWidth, originalWidth) : originalWidth; - double height = - hasMaxHeight ? min(maxHeight, originalHeight) : originalHeight; - bool shouldDownscaleWidth = hasMaxWidth && maxWidth < originalWidth; - bool shouldDownscaleHeight = hasMaxHeight && maxHeight < originalHeight; - bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight; - if (shouldDownscale) { - double downscaledWidth = - ((height / originalHeight) * originalWidth).floorToDouble(); - double downscaledHeight = - ((width / originalWidth) * originalHeight).floorToDouble(); - - if (width < height) { - if (!hasMaxWidth) { - width = downscaledWidth; - } else { - height = downscaledHeight; - } - } else if (height < width) { - if (!hasMaxHeight) { - height = downscaledHeight; - } else { - width = downscaledWidth; - } - } else { - if (originalWidth < originalHeight) { - width = downscaledWidth; - } else if (originalHeight < originalWidth) { - height = downscaledHeight; - } - } - } - if (hasMaxHeight) { - assert(height <= maxHeight); - } - if (hasMaxWidth) { - assert(width <= maxWidth); - } - return Size(width, height); - } - - bool _isImageQualityValid(int? imageQuality) { - return imageQuality == null || (imageQuality >= 0 && imageQuality <= 100); - } } diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart new file mode 100644 index 000000000000..67a44a2e1d41 --- /dev/null +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart @@ -0,0 +1,70 @@ +import 'dart:math'; + +import 'dart:ui'; + +///a function that checks if an image needs to be resized or not +bool imageResizeNeeded(double? maxWidth, double? maxHeight, int? imageQuality) { + bool resizeNeeded = + (maxWidth != null || maxHeight != null || imageQuality != null); + if (resizeNeeded) { + if (imageQuality != null) { + return isImageQualityValid(imageQuality); + } else { + return true; + } + } else { + return false; + } +} + +/// a function that checks if image quality is between [0,100] or null +bool isImageQualityValid(int imageQuality) { + return (imageQuality >= 0 && imageQuality <= 100); +} + +/// a functions that calculates the size of the scaled image. +/// imageWidth is the width of the image +/// imageHeight is the height of the image +/// maxWidth is the maximum width of the scaled image +/// maxHeight is the maximum height of the scaled image +Size calculateSizeOfScaledImage(double imageWidth, double imageHeight, + double? maxWidth, double? maxHeight) { + double originalWidth = imageWidth; + double originalHeight = imageHeight; + + bool hasMaxWidth = maxWidth != null; + bool hasMaxHeight = maxHeight != null; + double width = hasMaxWidth ? min(maxWidth, originalWidth) : originalWidth; + double height = + hasMaxHeight ? min(maxHeight, originalHeight) : originalHeight; + bool shouldDownscaleWidth = hasMaxWidth && maxWidth < originalWidth; + bool shouldDownscaleHeight = hasMaxHeight && maxHeight < originalHeight; + bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight; + if (shouldDownscale) { + double downscaledWidth = + ((height / originalHeight) * originalWidth).floorToDouble(); + double downscaledHeight = + ((width / originalWidth) * originalHeight).floorToDouble(); + + if (width < height) { + if (!hasMaxWidth) { + width = downscaledWidth; + } else { + height = downscaledHeight; + } + } else if (height < width) { + if (!hasMaxHeight) { + height = downscaledHeight; + } else { + width = downscaledWidth; + } + } else { + if (originalWidth < originalHeight) { + width = downscaledWidth; + } else if (originalHeight < originalWidth) { + height = downscaledHeight; + } + } + } + return Size(width, height); +} diff --git a/packages/image_picker/image_picker_for_web/test/image_resizer_test.dart b/packages/image_picker/image_picker_for_web/test/image_resizer_test.dart new file mode 100644 index 000000000000..8c525723cf20 --- /dev/null +++ b/packages/image_picker/image_picker_for_web/test/image_resizer_test.dart @@ -0,0 +1,79 @@ +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_for_web/image_resizer_utils.dart'; + +void main() { + group('Image Resizer Utils', () { + group("calculateSizeOfScaledImage", () { + test( + "scaled image height and width are same if max width and max height are same as image's width and height ", + () { + expect(calculateSizeOfScaledImage(500, 300, 500, 300), Size(500, 300)); + }); + test( + "scaled image height and width are same if max width and max height are null", + () { + expect( + calculateSizeOfScaledImage(500, 300, null, null), Size(500, 300)); + }); + test("image size is scaled when maxWidth is set", () { + final imageSize = Size(500, 300); + final maxWidth = 400; + final scaledSize = calculateSizeOfScaledImage( + imageSize.width, imageSize.height, maxWidth.toDouble(), null); + expect(scaledSize.height<=imageSize.height,true); + expect(scaledSize.width<=maxWidth,true); + + }); + test("image size is scaled when maxHeight is set", () { + final imageSize = Size(500, 300); + final maxHeight = 400; + final scaledSize = calculateSizeOfScaledImage( + imageSize.width, imageSize.height, null,maxHeight.toDouble()); + expect(scaledSize.height<=maxHeight,true); + expect(scaledSize.width<=imageSize.width,true); + + }); + test("image size is scaled when both maxWidth and maxHeight is set", () { + final imageSize = Size(1120, 2000); + final maxHeight = 1200; + final maxWidth = 99; + final scaledSize = calculateSizeOfScaledImage( + imageSize.width, imageSize.height, maxWidth.toDouble(),maxHeight.toDouble()); + expect(scaledSize.height<=maxHeight,true); + expect(scaledSize.width<=maxWidth,true); + + }); + }); + group("imageResizeNeeded", () { + test("image needs to be resized when maxWidth is set", () { + expect(imageResizeNeeded(50, null, null), true); + }); + test("image needs to be resized when maxHeight is set", () { + expect(imageResizeNeeded(null, 50, null), true); + }); + test("image needs to be resized when imageQuality is set", () { + expect(imageResizeNeeded(null, null, 100), true); + }); + test("image will not be resized when imageQuality is not valid", () { + expect(imageResizeNeeded(null, null, 101), false); + expect(imageResizeNeeded(null, null, -1), false); + }); + }); + group("isImageQualityValid", () { + test("image quality is valid in 0 to 100", () { + expect(isImageQualityValid(50), true); + expect(isImageQualityValid(0), true); + expect(isImageQualityValid(100), true); + }); + test( + "image quality is not valid when imageQuality is less than 0 or greater than 100", + () { + expect(isImageQualityValid(-1), false); + expect(isImageQualityValid(101), false); + }); + }); + }); +} From 2b1842d72eac0a4b0816286a1d49395b22b0870b Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 20:08:38 +0530 Subject: [PATCH 13/34] update readme --- .../image_picker_for_web_test.dart | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart index 834939ebc0c4..8bd95d242a34 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart @@ -14,7 +14,8 @@ import 'package:integration_test/integration_test.dart'; final String expectedStringContents = 'Hello, world!'; final String otherStringContents = 'Hello again, world!'; -final String pngFileBase64Contents = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAcTAAAHEwHOIA8IAAAB0mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj4KICAgICAgICAgPHBob3Rvc2hvcDpDcmVkaXQ+wqkgR29vZ2xlPC9waG90b3Nob3A6Q3JlZGl0PgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4K43gerQAAAA1JREFUCB1jeOVs+h8ABd8CYkMBAJAAAAAASUVORK5CYII="; +final String pngFileBase64Contents = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAcTAAAHEwHOIA8IAAAB0mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj4KICAgICAgICAgPHBob3Rvc2hvcDpDcmVkaXQ+wqkgR29vZ2xlPC9waG90b3Nob3A6Q3JlZGl0PgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4K43gerQAAAA1JREFUCB1jeOVs+h8ABd8CYkMBAJAAAAAASUVORK5CYII="; final Uint8List bytes = utf8.encode(expectedStringContents) as Uint8List; final Uint8List otherBytes = utf8.encode(otherStringContents) as Uint8List; @@ -26,7 +27,7 @@ final Map options = { }; final html.File textFile = html.File([bytes], 'hello.txt', options); final html.File secondTextFile = html.File([otherBytes], 'secondFile.txt'); -final html.File pngImageFile = html.File([pngFileBytes],'testimage.png'); +final html.File pngImageFile = html.File([pngFileBytes], 'testimage.png'); void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -118,7 +119,9 @@ void main() { expect(secondFile.length(), completion(secondTextFile.size)); }); - testWidgets('image is not scaled if maxWidth and maxHeight is not set',(WidgetTester tester) async { + testWidgets( + 'image is not scaled when maxWidth, maxHeight and imageQuality is not set', + (WidgetTester tester) async { final mockInput = html.FileUploadInputElement(); final overrides = ImagePickerPluginTestOverrides() ..createInputElement = ((_, __) => mockInput) @@ -127,28 +130,33 @@ void main() { final plugin = ImagePickerPlugin(overrides: overrides); // Init the pick file dialog... - final image = plugin.getImage(source: ImageSource.gallery,); - final imageElement = html.ImageElement(src: pngFileBase64Contents); - final imageloadCompleter = Completer(); - imageElement.onLoad.listen((event) { - print(event); - imageloadCompleter.complete(); - }); + final file = plugin.getImage( + source: ImageSource.gallery, + ); mockInput.dispatchEvent(html.Event('change')); + final image = (await file); + expect(image.name, "testimage.png"); + }); - await imageloadCompleter.future; - expect(imageElement.width,1); - expect(imageElement.height,1); - final XFile xFile = await image; - final pickedImageElement = html.ImageElement(src: html.Url.createObjectUrl(html.Blob(await xFile.readAsBytes()))); - final newCompleter = Completer(); - pickedImageElement.onLoad.listen((event) { - newCompleter.complete(); - } - ); - await newCompleter.future; - expect(pickedImageElement.width,99); + testWidgets( + 'image is scaled when any of maxWidth, maxHeight or imageQuality is set', + (WidgetTester tester) async { + final mockInput = html.FileUploadInputElement(); + final mockImageElement = html.ImageElement(); + final overrides = ImagePickerPluginTestOverrides() + ..createInputElement = ((_, __) => mockInput) + ..getMultipleFilesFromInput = ((_) => [pngImageFile]); + final plugin = ImagePickerPlugin(overrides: overrides); + + // Init the pick file dialog... + final file = plugin.getImage( + source: ImageSource.gallery, + maxWidth: 500 + ); + mockInput.dispatchEvent(html.Event('change')); + final image = (await file); + expect(image.name, "scaled_testimage.png"); }); // There's no good way of detecting when the user has "aborted" the selection. @@ -209,6 +217,4 @@ void main() { expect(input.attributes, contains('multiple')); }); }); - - } From 5f7a0ddfc3e93ea6b7d3282dcbd9ea7447643f04 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 21:59:09 +0530 Subject: [PATCH 14/34] add image resizer tests --- .../image_picker_for_web/README.md | 3 +- .../image_picker_for_web_test.dart | 73 +++----------- .../integration_test/image_resizer_test.dart | 96 +++++++++++++++++++ .../lib/image_resizer.dart | 12 ++- ...est.dart => image_resizer_utils_test.dart} | 0 5 files changed, 120 insertions(+), 64 deletions(-) create mode 100644 packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart rename packages/image_picker/image_picker_for_web/test/{image_resizer_test.dart => image_resizer_utils_test.dart} (100%) diff --git a/packages/image_picker/image_picker_for_web/README.md b/packages/image_picker/image_picker_for_web/README.md index 73f2dfc4b84f..1b9dd5965111 100644 --- a/packages/image_picker/image_picker_for_web/README.md +++ b/packages/image_picker/image_picker_for_web/README.md @@ -43,7 +43,8 @@ Each browser may implement `capture` any way they please, so it may (or may not) difference in your users' experience. ### pickImage() -The arguments `maxWidth`, `maxHeight` and `imageQuality` are not supported on the web. +The arguments `maxWidth`, `maxHeight` and `imageQuality` are not supported for gif images. +THe argument `imageQuality` only works for jpeg and webp images. ### pickVideo() The argument `maxDuration` is not supported on the web. diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart index 8bd95d242a34..d450565ab026 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart @@ -2,7 +2,6 @@ // 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:convert'; import 'dart:html' as html; import 'dart:typed_data'; @@ -14,20 +13,14 @@ import 'package:integration_test/integration_test.dart'; final String expectedStringContents = 'Hello, world!'; final String otherStringContents = 'Hello again, world!'; -final String pngFileBase64Contents = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAcTAAAHEwHOIA8IAAAB0mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj4KICAgICAgICAgPHBob3Rvc2hvcDpDcmVkaXQ+wqkgR29vZ2xlPC9waG90b3Nob3A6Q3JlZGl0PgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4K43gerQAAAA1JREFUCB1jeOVs+h8ABd8CYkMBAJAAAAAASUVORK5CYII="; - final Uint8List bytes = utf8.encode(expectedStringContents) as Uint8List; final Uint8List otherBytes = utf8.encode(otherStringContents) as Uint8List; -final Uint8List pngFileBytes = utf8.encode(pngFileBase64Contents) as Uint8List; - final Map options = { 'type': 'text/plain', 'lastModified': DateTime.utc(2017, 12, 13).millisecondsSinceEpoch, }; final html.File textFile = html.File([bytes], 'hello.txt', options); final html.File secondTextFile = html.File([otherBytes], 'secondFile.txt'); -final html.File pngImageFile = html.File([pngFileBytes], 'testimage.png'); void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -119,46 +112,6 @@ void main() { expect(secondFile.length(), completion(secondTextFile.size)); }); - testWidgets( - 'image is not scaled when maxWidth, maxHeight and imageQuality is not set', - (WidgetTester tester) async { - final mockInput = html.FileUploadInputElement(); - final overrides = ImagePickerPluginTestOverrides() - ..createInputElement = ((_, __) => mockInput) - ..getMultipleFilesFromInput = ((_) => [pngImageFile]); - - final plugin = ImagePickerPlugin(overrides: overrides); - - // Init the pick file dialog... - final file = plugin.getImage( - source: ImageSource.gallery, - ); - mockInput.dispatchEvent(html.Event('change')); - final image = (await file); - expect(image.name, "testimage.png"); - }); - - testWidgets( - 'image is scaled when any of maxWidth, maxHeight or imageQuality is set', - (WidgetTester tester) async { - final mockInput = html.FileUploadInputElement(); - final mockImageElement = html.ImageElement(); - final overrides = ImagePickerPluginTestOverrides() - ..createInputElement = ((_, __) => mockInput) - ..getMultipleFilesFromInput = ((_) => [pngImageFile]); - - final plugin = ImagePickerPlugin(overrides: overrides); - - // Init the pick file dialog... - final file = plugin.getImage( - source: ImageSource.gallery, - maxWidth: 500 - ); - mockInput.dispatchEvent(html.Event('change')); - final image = (await file); - expect(image.name, "scaled_testimage.png"); - }); - // There's no good way of detecting when the user has "aborted" the selection. testWidgets('computeCaptureAttribute', (WidgetTester tester) async { @@ -198,23 +151,23 @@ void main() { }); testWidgets('accept: any, capture: null, multi: true', - (WidgetTester tester) async { - html.Element input = + (WidgetTester tester) async { + html.Element input = plugin.createInputElement('any', null, multiple: true); - expect(input.attributes, containsPair('accept', 'any')); - expect(input.attributes, isNot(contains('capture'))); - expect(input.attributes, contains('multiple')); - }); + expect(input.attributes, containsPair('accept', 'any')); + expect(input.attributes, isNot(contains('capture'))); + expect(input.attributes, contains('multiple')); + }); testWidgets('accept: any, capture: something, multi: true', - (WidgetTester tester) async { - html.Element input = + (WidgetTester tester) async { + html.Element input = plugin.createInputElement('any', 'something', multiple: true); - expect(input.attributes, containsPair('accept', 'any')); - expect(input.attributes, containsPair('capture', 'something')); - expect(input.attributes, contains('multiple')); - }); + expect(input.attributes, containsPair('accept', 'any')); + expect(input.attributes, containsPair('capture', 'something')); + expect(input.attributes, contains('multiple')); + }); }); -} +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart new file mode 100644 index 000000000000..ded09fe59136 --- /dev/null +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart @@ -0,0 +1,96 @@ +// Copyright 2013 The Flutter 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:html' as html; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_for_web/image_resizer.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:integration_test/integration_test.dart'; + +//This is a sample 10x10 png image +final String pngFileBase64Contents = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEXqQzX+/v6lfubTAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAHEwAABxMBziAPCAAAAAd0SU1FB+UJHgsdDM0ErZoAAAALSURBVAjXY2DABwAAHgABboVHMgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wOS0zMFQxMToyOToxMi0wNDowMHCDC24AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDktMzBUMTE6Mjk6MTItMDQ6MDAB3rPSAAAAAElFTkSuQmCC"; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // Under test... + late ImageResizer imageResizer; + late XFile pngFile; + setUp(() { + imageResizer = ImageResizer(); + final pngHtmlFile = _base64ToFile(pngFileBase64Contents, "pngImage.png"); + pngFile = XFile(html.Url.createObjectUrl(pngHtmlFile), + name: pngHtmlFile.name, mimeType: pngHtmlFile.type); + }); + + testWidgets("image is scaled when maxWidth is set", + (WidgetTester tester) async { + final scaledImage = + await imageResizer.resizeImageIfNeeded(pngFile, 5, null, null); + expect(scaledImage.name, "scaled_${pngFile.name}"); + final scaledImageSize = await _getImageSize(scaledImage); + expect(scaledImageSize, Size(5, 5)); + }); + testWidgets("image is scaled when maxHeight is set", + (WidgetTester tester) async { + final scaledImage = + await imageResizer.resizeImageIfNeeded(pngFile, null, 6, null); + expect(scaledImage.name, "scaled_${pngFile.name}"); + final scaledImageSize = await _getImageSize(scaledImage); + expect(scaledImageSize, Size(6, 6)); + }); + + testWidgets("image is scaled when imageQuality is set", + (WidgetTester tester) async { + final scaledImage = + await imageResizer.resizeImageIfNeeded(pngFile, null, null, 89); + expect(scaledImage.name, "scaled_${pngFile.name}"); + }); + + testWidgets("image is scaled when maxWidth,maxHeight,imageQuality are set", + (WidgetTester tester) async { + final scaledImage = + await imageResizer.resizeImageIfNeeded(pngFile, 3, 4, 89); + expect(scaledImage.name, "scaled_${pngFile.name}"); + }); + + testWidgets("image is not scaled when maxWidth,maxHeight, is set", + (WidgetTester tester) async { + final scaledImage = + await imageResizer.resizeImageIfNeeded(pngFile, null, null, null); + expect(scaledImage.name, pngFile.name); + }); +} + +Future _getImageSize(XFile file) async { + final completer = Completer(); + final image = html.ImageElement(src: file.path); + image.onLoad.listen((event) { + print(image.width); + print(image.height); + completer.complete(Size(image.width!.toDouble(), image.height!.toDouble())); + }); + image.onError.listen((event) { + completer.complete(Size(0, 0)); + }); + return completer.future; +} + +html.File _base64ToFile(String data, String fileName) { + var arr = data.split(','); + var bstr = html.window.atob(arr[1]); + var n = bstr.length, u8arr = Uint8List(n); + + while (n >= 1) { + u8arr[n - 1] = bstr.codeUnitAt(n - 1); + n--; + } + + return html.File([u8arr], fileName); +} diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index 89d279a78a15..c4c1d8a81d0e 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -8,7 +8,6 @@ import 'dart:html' as html; class ImageResizer { /// Resizes the image if needed /// Does not support gif image - /// Future resizeImageIfNeeded(XFile file, double? maxWidth, double? maxHeight, int? imageQuality) async { if (!imageResizeNeeded(maxWidth, maxHeight, imageQuality) || @@ -19,13 +18,20 @@ class ImageResizer { final imageLoadCompleter = Completer(); final imageElement = html.ImageElement(); imageElement.src = file.path; + bool hasError = false; + imageElement.onLoad.listen((event) { html.Url.revokeObjectUrl(file.path); imageLoadCompleter.complete(); }); - + imageElement.onError.listen((event) { + imageLoadCompleter.complete(); + hasError = true; + }); await imageLoadCompleter.future; - + if (hasError) { + return file; + } final newImageSize = calculateSizeOfScaledImage( imageElement.width!.toDouble(), imageElement.height!.toDouble(), diff --git a/packages/image_picker/image_picker_for_web/test/image_resizer_test.dart b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart similarity index 100% rename from packages/image_picker/image_picker_for_web/test/image_resizer_test.dart rename to packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart From f61b965b7775bd199ae3207f41efcf7de8a9223a Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 22:01:43 +0530 Subject: [PATCH 15/34] dartfmt --- .../image_picker_for_web_test.dart | 26 ++++++++-------- .../lib/image_picker_for_web.dart | 3 +- .../lib/image_resizer_utils.dart | 6 ++-- .../test/image_resizer_utils_test.dart | 30 +++++++++++-------- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart index d450565ab026..c1025a9f07d3 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart @@ -151,23 +151,23 @@ void main() { }); testWidgets('accept: any, capture: null, multi: true', - (WidgetTester tester) async { - html.Element input = + (WidgetTester tester) async { + html.Element input = plugin.createInputElement('any', null, multiple: true); - expect(input.attributes, containsPair('accept', 'any')); - expect(input.attributes, isNot(contains('capture'))); - expect(input.attributes, contains('multiple')); - }); + expect(input.attributes, containsPair('accept', 'any')); + expect(input.attributes, isNot(contains('capture'))); + expect(input.attributes, contains('multiple')); + }); testWidgets('accept: any, capture: something, multi: true', - (WidgetTester tester) async { - html.Element input = + (WidgetTester tester) async { + html.Element input = plugin.createInputElement('any', 'something', multiple: true); - expect(input.attributes, containsPair('accept', 'any')); - expect(input.attributes, containsPair('capture', 'something')); - expect(input.attributes, contains('multiple')); - }); + expect(input.attributes, containsPair('accept', 'any')); + expect(input.attributes, containsPair('capture', 'something')); + expect(input.attributes, contains('multiple')); + }); }); -} \ No newline at end of file +} diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index f758b46235e9..6a5efb598a8d 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -30,8 +30,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { ImagePickerPlugin({ @visibleForTesting ImagePickerPluginTestOverrides? overrides, @visibleForTesting ImageResizer? imageResizer, - }) : _overrides = overrides - { + }) : _overrides = overrides { _imageResizer = imageResizer ?? ImageResizer(); _target = _ensureInitialized(_kImagePickerInputsDomId); } diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart index 67a44a2e1d41..befc94915e76 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart @@ -36,15 +36,15 @@ Size calculateSizeOfScaledImage(double imageWidth, double imageHeight, bool hasMaxHeight = maxHeight != null; double width = hasMaxWidth ? min(maxWidth, originalWidth) : originalWidth; double height = - hasMaxHeight ? min(maxHeight, originalHeight) : originalHeight; + hasMaxHeight ? min(maxHeight, originalHeight) : originalHeight; bool shouldDownscaleWidth = hasMaxWidth && maxWidth < originalWidth; bool shouldDownscaleHeight = hasMaxHeight && maxHeight < originalHeight; bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight; if (shouldDownscale) { double downscaledWidth = - ((height / originalHeight) * originalWidth).floorToDouble(); + ((height / originalHeight) * originalWidth).floorToDouble(); double downscaledHeight = - ((width / originalWidth) * originalHeight).floorToDouble(); + ((width / originalWidth) * originalHeight).floorToDouble(); if (width < height) { if (!hasMaxWidth) { diff --git a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart index 8c525723cf20..5d299e829c98 100644 --- a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart +++ b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart @@ -12,62 +12,68 @@ void main() { () { expect(calculateSizeOfScaledImage(500, 300, 500, 300), Size(500, 300)); }); + test( "scaled image height and width are same if max width and max height are null", () { expect( calculateSizeOfScaledImage(500, 300, null, null), Size(500, 300)); }); + test("image size is scaled when maxWidth is set", () { final imageSize = Size(500, 300); final maxWidth = 400; final scaledSize = calculateSizeOfScaledImage( imageSize.width, imageSize.height, maxWidth.toDouble(), null); - expect(scaledSize.height<=imageSize.height,true); - expect(scaledSize.width<=maxWidth,true); - + expect(scaledSize.height <= imageSize.height, true); + expect(scaledSize.width <= maxWidth, true); }); + test("image size is scaled when maxHeight is set", () { final imageSize = Size(500, 300); final maxHeight = 400; final scaledSize = calculateSizeOfScaledImage( - imageSize.width, imageSize.height, null,maxHeight.toDouble()); - expect(scaledSize.height<=maxHeight,true); - expect(scaledSize.width<=imageSize.width,true); - + imageSize.width, imageSize.height, null, maxHeight.toDouble()); + expect(scaledSize.height <= maxHeight, true); + expect(scaledSize.width <= imageSize.width, true); }); + test("image size is scaled when both maxWidth and maxHeight is set", () { final imageSize = Size(1120, 2000); final maxHeight = 1200; final maxWidth = 99; - final scaledSize = calculateSizeOfScaledImage( - imageSize.width, imageSize.height, maxWidth.toDouble(),maxHeight.toDouble()); - expect(scaledSize.height<=maxHeight,true); - expect(scaledSize.width<=maxWidth,true); - + final scaledSize = calculateSizeOfScaledImage(imageSize.width, + imageSize.height, maxWidth.toDouble(), maxHeight.toDouble()); + expect(scaledSize.height <= maxHeight, true); + expect(scaledSize.width <= maxWidth, true); }); }); group("imageResizeNeeded", () { test("image needs to be resized when maxWidth is set", () { expect(imageResizeNeeded(50, null, null), true); }); + test("image needs to be resized when maxHeight is set", () { expect(imageResizeNeeded(null, 50, null), true); }); + test("image needs to be resized when imageQuality is set", () { expect(imageResizeNeeded(null, null, 100), true); }); + test("image will not be resized when imageQuality is not valid", () { expect(imageResizeNeeded(null, null, 101), false); expect(imageResizeNeeded(null, null, -1), false); }); }); + group("isImageQualityValid", () { test("image quality is valid in 0 to 100", () { expect(isImageQualityValid(50), true); expect(isImageQualityValid(0), true); expect(isImageQualityValid(100), true); }); + test( "image quality is not valid when imageQuality is less than 0 or greater than 100", () { From 5b477e6b0d38811321d6436e86ea86e1281beefd Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 22:07:34 +0530 Subject: [PATCH 16/34] add licenses --- packages/image_picker/image_picker/example/lib/main.dart | 1 + .../image_picker_for_web/lib/image_resizer.dart | 6 +++++- .../image_picker_for_web/lib/image_resizer_utils.dart | 5 ++++- .../image_picker_for_web/test/image_resizer_utils_test.dart | 6 ++++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index 0f5ba76db6df..253bd7a3df09 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -115,6 +115,7 @@ class _MyHomePageState extends State { maxHeight: maxHeight, imageQuality: quality, ); + print(pickedFile?.name ??""); setState(() { _imageFile = pickedFile; }); diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index c4c1d8a81d0e..31d6cbaf57b9 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -1,10 +1,14 @@ +// Copyright 2013 The Flutter 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:math'; import 'package:image_picker_for_web/image_resizer_utils.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'dart:html' as html; -/// Resizes images +/// Helper class that resizes image class ImageResizer { /// Resizes the image if needed /// Does not support gif image diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart index befc94915e76..641cfcdcb8be 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart @@ -1,5 +1,8 @@ -import 'dart:math'; +// Copyright 2013 The Flutter 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:math'; import 'dart:ui'; ///a function that checks if an image needs to be resized or not diff --git a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart index 5d299e829c98..39efcf6abafd 100644 --- a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart +++ b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart @@ -1,7 +1,9 @@ -import 'dart:math'; +// Copyright 2013 The Flutter 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:ui'; - import 'package:flutter_test/flutter_test.dart'; + import 'package:image_picker_for_web/image_resizer_utils.dart'; void main() { From 60e0b9f35d641a06a9afca8ff40d37168a2972aa Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 22:12:15 +0530 Subject: [PATCH 17/34] remove path dependancy --- packages/image_picker/image_picker/example/lib/main.dart | 1 - packages/image_picker/image_picker/pubspec.yaml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index 253bd7a3df09..0f5ba76db6df 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -115,7 +115,6 @@ class _MyHomePageState extends State { maxHeight: maxHeight, imageQuality: quality, ); - print(pickedFile?.name ??""); setState(() { _imageFile = pickedFile; }); diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 81a59cde52fc..ba5ce6635ed6 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -24,8 +24,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - image_picker_for_web: - path: ../image_picker_for_web + image_picker_for_web: ^2.1.0 image_picker_platform_interface: ^2.3.0 dev_dependencies: From 7a6332307252120e58d2cc05ab63bfb2c0d055ec Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 22:15:55 +0530 Subject: [PATCH 18/34] fix typo and remove extra space --- packages/image_picker/image_picker_for_web/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/README.md b/packages/image_picker/image_picker_for_web/README.md index 1b9dd5965111..4e82d9c47584 100644 --- a/packages/image_picker/image_picker_for_web/README.md +++ b/packages/image_picker/image_picker_for_web/README.md @@ -44,7 +44,7 @@ difference in your users' experience. ### pickImage() The arguments `maxWidth`, `maxHeight` and `imageQuality` are not supported for gif images. -THe argument `imageQuality` only works for jpeg and webp images. +THe argument `imageQuality` only works for jpeg and webp images. ### pickVideo() The argument `maxDuration` is not supported on the web. @@ -64,7 +64,7 @@ You should be able to use `package:image_picker` _almost_ as normal. Once the user has picked a file, the returned `PickedFile` instance will contain a `network`-accessible URL (pointing to a location within the browser). -The instace will also let you retrieve the bytes of the selected file across all platforms. +The instance will also let you retrieve the bytes of the selected file across all platforms. If you want to use the path directly, your code would need look like this: From c75b036f103a16a45aea213119de3fa40ab96219 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 22:18:55 +0530 Subject: [PATCH 19/34] fix doc comment --- .../image_picker_for_web/lib/image_resizer_utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart index 641cfcdcb8be..e05da2f669b1 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart @@ -20,7 +20,7 @@ bool imageResizeNeeded(double? maxWidth, double? maxHeight, int? imageQuality) { } } -/// a function that checks if image quality is between [0,100] or null +/// a function that checks if image quality is between 0 to 100 bool isImageQualityValid(int imageQuality) { return (imageQuality >= 0 && imageQuality <= 100); } From f5e6d45f3474fc8c033c29278afeeefc96f648b3 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Thu, 30 Sep 2021 22:22:34 +0530 Subject: [PATCH 20/34] remove double spaces --- .../test/image_resizer_utils_test.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart index 39efcf6abafd..af24ff3f28dd 100644 --- a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart +++ b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart @@ -10,7 +10,7 @@ void main() { group('Image Resizer Utils', () { group("calculateSizeOfScaledImage", () { test( - "scaled image height and width are same if max width and max height are same as image's width and height ", + "scaled image height and width are same if max width and max height are same as image's width and height", () { expect(calculateSizeOfScaledImage(500, 300, 500, 300), Size(500, 300)); }); @@ -51,15 +51,15 @@ void main() { }); }); group("imageResizeNeeded", () { - test("image needs to be resized when maxWidth is set", () { + test("image needs to be resized when maxWidth is set", () { expect(imageResizeNeeded(50, null, null), true); }); - test("image needs to be resized when maxHeight is set", () { + test("image needs to be resized when maxHeight is set", () { expect(imageResizeNeeded(null, 50, null), true); }); - test("image needs to be resized when imageQuality is set", () { + test("image needs to be resized when imageQuality is set", () { expect(imageResizeNeeded(null, null, 100), true); }); @@ -77,7 +77,7 @@ void main() { }); test( - "image quality is not valid when imageQuality is less than 0 or greater than 100", + "image quality is not valid when imageQuality is less than 0 or greater than 100", () { expect(isImageQualityValid(-1), false); expect(isImageQualityValid(101), false); From d38de67aed16653069b768c6eebb4e7e706a588c Mon Sep 17 00:00:00 2001 From: balvinderz Date: Sat, 2 Oct 2021 18:38:35 +0530 Subject: [PATCH 21/34] review changes --- .../lib/image_resizer.dart | 59 ++++++++++++------- .../lib/image_resizer_utils.dart | 24 +++----- .../test/image_resizer_utils_test.dart | 23 +++++--- 3 files changed, 60 insertions(+), 46 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index 31d6cbaf57b9..01eac5a8c2fb 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:math'; +import 'dart:ui'; import 'package:image_picker_for_web/image_resizer_utils.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'dart:html' as html; @@ -19,26 +20,36 @@ class ImageResizer { //TODO Implement maxWidth and maxHeight for image/gif return file; } - final imageLoadCompleter = Completer(); + final imageElement = await loadImage(file.path); + if (imageElement == null) { + return file; + } + final canvas = resizeImageElement(imageElement, maxWidth, maxHeight); + return writeCanvasToFile(file, canvas, imageQuality); + } + + /// function that loads the blobUrl into an imageElement + /// returns null if error comes while loading the image + Future loadImage(String blobUrl) { + final imageLoadCompleter = Completer(); final imageElement = html.ImageElement(); - imageElement.src = file.path; - bool hasError = false; + imageElement.src = blobUrl; imageElement.onLoad.listen((event) { - html.Url.revokeObjectUrl(file.path); - imageLoadCompleter.complete(); + html.Url.revokeObjectUrl(blobUrl); + imageLoadCompleter.complete(imageElement); }); imageElement.onError.listen((event) { - imageLoadCompleter.complete(); - hasError = true; + imageLoadCompleter.complete(null); }); - await imageLoadCompleter.future; - if (hasError) { - return file; - } - final newImageSize = calculateSizeOfScaledImage( - imageElement.width!.toDouble(), - imageElement.height!.toDouble(), + return imageLoadCompleter.future; + } + + /// Draws image to a canvas while resizing the image to fit the [maxWidth],[maxHeight] constraints + html.CanvasElement resizeImageElement( + html.ImageElement source, double? maxWidth, double? maxHeight) { + final newImageSize = calculateSizeOfDownScaledImage( + Size(source.width!.toDouble(), source.height!.toDouble()), maxWidth, maxHeight); final canvas = html.CanvasElement(); @@ -46,17 +57,23 @@ class ImageResizer { canvas.height = newImageSize.height.toInt(); final context = canvas.context2D; if (maxHeight == null && maxWidth == null) { - context.drawImage(imageElement, 0, 0); + context.drawImage(source, 0, 0); } else { - context.drawImageScaled( - imageElement, 0, 0, canvas.width!, canvas.height!); + context.drawImageScaled(source, 0, 0, canvas.width!, canvas.height!); } + return canvas; + } + + /// function that converts a canvas element to Xfile + /// [imageQuality] is only supported for jpeg and webp images. + Future writeCanvasToFile( + XFile originalFile, html.CanvasElement canvas, int? imageQuality) async { final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / 100.0); - final blob = await canvas.toBlob(file.mimeType, - calculatedImageQuality); // Image quality only works for jpeg and webp images + final blob = + await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); return XFile(html.Url.createObjectUrlFromBlob(blob), - mimeType: file.mimeType, - name: "scaled_" + file.name, + mimeType: originalFile.mimeType, + name: "scaled_" + originalFile.name, lastModified: DateTime.now(), length: blob.size); } diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart index e05da2f669b1..c2c0da16eed6 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart @@ -7,17 +7,9 @@ import 'dart:ui'; ///a function that checks if an image needs to be resized or not bool imageResizeNeeded(double? maxWidth, double? maxHeight, int? imageQuality) { - bool resizeNeeded = - (maxWidth != null || maxHeight != null || imageQuality != null); - if (resizeNeeded) { - if (imageQuality != null) { - return isImageQualityValid(imageQuality); - } else { - return true; - } - } else { - return false; - } + return imageQuality != null + ? isImageQualityValid(imageQuality) + : (maxWidth != null || maxHeight != null); } /// a function that checks if image quality is between 0 to 100 @@ -25,15 +17,15 @@ bool isImageQualityValid(int imageQuality) { return (imageQuality >= 0 && imageQuality <= 100); } -/// a functions that calculates the size of the scaled image. +/// a functions that calculates the size of the downScaled image. /// imageWidth is the width of the image /// imageHeight is the height of the image /// maxWidth is the maximum width of the scaled image /// maxHeight is the maximum height of the scaled image -Size calculateSizeOfScaledImage(double imageWidth, double imageHeight, - double? maxWidth, double? maxHeight) { - double originalWidth = imageWidth; - double originalHeight = imageHeight; +Size calculateSizeOfDownScaledImage( + Size imageSize, double? maxWidth, double? maxHeight) { + double originalWidth = imageSize.width; + double originalHeight = imageSize.height; bool hasMaxWidth = maxWidth != null; bool hasMaxHeight = maxHeight != null; diff --git a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart index af24ff3f28dd..ce948bf07725 100644 --- a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart +++ b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart @@ -12,21 +12,22 @@ void main() { test( "scaled image height and width are same if max width and max height are same as image's width and height", () { - expect(calculateSizeOfScaledImage(500, 300, 500, 300), Size(500, 300)); + expect(calculateSizeOfDownScaledImage(Size(500, 300), 500, 300), + Size(500, 300)); }); test( "scaled image height and width are same if max width and max height are null", () { - expect( - calculateSizeOfScaledImage(500, 300, null, null), Size(500, 300)); + expect(calculateSizeOfDownScaledImage(Size(500, 300), null, null), + Size(500, 300)); }); test("image size is scaled when maxWidth is set", () { final imageSize = Size(500, 300); final maxWidth = 400; - final scaledSize = calculateSizeOfScaledImage( - imageSize.width, imageSize.height, maxWidth.toDouble(), null); + final scaledSize = calculateSizeOfDownScaledImage( + Size(imageSize.width, imageSize.height), maxWidth.toDouble(), null); expect(scaledSize.height <= imageSize.height, true); expect(scaledSize.width <= maxWidth, true); }); @@ -34,8 +35,10 @@ void main() { test("image size is scaled when maxHeight is set", () { final imageSize = Size(500, 300); final maxHeight = 400; - final scaledSize = calculateSizeOfScaledImage( - imageSize.width, imageSize.height, null, maxHeight.toDouble()); + final scaledSize = calculateSizeOfDownScaledImage( + Size(imageSize.width, imageSize.height), + null, + maxHeight.toDouble()); expect(scaledSize.height <= maxHeight, true); expect(scaledSize.width <= imageSize.width, true); }); @@ -44,8 +47,10 @@ void main() { final imageSize = Size(1120, 2000); final maxHeight = 1200; final maxWidth = 99; - final scaledSize = calculateSizeOfScaledImage(imageSize.width, - imageSize.height, maxWidth.toDouble(), maxHeight.toDouble()); + final scaledSize = calculateSizeOfDownScaledImage( + Size(imageSize.width, imageSize.height), + maxWidth.toDouble(), + maxHeight.toDouble()); expect(scaledSize.height <= maxHeight, true); expect(scaledSize.width <= maxWidth, true); }); From 5c352f63b9adde487ce699d684e73d68c7421f05 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Mon, 4 Oct 2021 20:51:42 +0530 Subject: [PATCH 22/34] fix typo --- .../image_picker_for_web/lib/image_resizer_utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart index c2c0da16eed6..29f752c94504 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart @@ -17,7 +17,7 @@ bool isImageQualityValid(int imageQuality) { return (imageQuality >= 0 && imageQuality <= 100); } -/// a functions that calculates the size of the downScaled image. +/// a function that calculates the size of the downScaled image. /// imageWidth is the width of the image /// imageHeight is the height of the image /// maxWidth is the maximum width of the scaled image From 08b8240d96ab2461a561af401500382047f643a6 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Mon, 4 Oct 2021 21:21:54 +0530 Subject: [PATCH 23/34] use completeError --- .../lib/image_resizer.dart | 31 ++++++++++++------- .../lib/image_resizer_utils.dart | 1 - 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index 01eac5a8c2fb..899dc08f5e49 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -3,8 +3,10 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:html'; import 'dart:math'; import 'dart:ui'; +import 'package:flutter/services.dart'; import 'package:image_picker_for_web/image_resizer_utils.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'dart:html' as html; @@ -20,18 +22,19 @@ class ImageResizer { //TODO Implement maxWidth and maxHeight for image/gif return file; } - final imageElement = await loadImage(file.path); - if (imageElement == null) { + try { + final imageElement = await loadImage(file.path); + final canvas = resizeImageElement(imageElement, maxWidth, maxHeight); + return writeCanvasToFile(file, canvas, imageQuality); + }catch(e){ return file; } - final canvas = resizeImageElement(imageElement, maxWidth, maxHeight); - return writeCanvasToFile(file, canvas, imageQuality); } /// function that loads the blobUrl into an imageElement /// returns null if error comes while loading the image - Future loadImage(String blobUrl) { - final imageLoadCompleter = Completer(); + Future loadImage(String blobUrl) { + final imageLoadCompleter = Completer(); final imageElement = html.ImageElement(); imageElement.src = blobUrl; @@ -40,14 +43,18 @@ class ImageResizer { imageLoadCompleter.complete(imageElement); }); imageElement.onError.listen((event) { - imageLoadCompleter.complete(null); + final excpetion = Exception( + "Error while loading image." + ); + imageElement.remove(); + imageLoadCompleter.completeError(excpetion); }); return imageLoadCompleter.future; } /// Draws image to a canvas while resizing the image to fit the [maxWidth],[maxHeight] constraints - html.CanvasElement resizeImageElement( - html.ImageElement source, double? maxWidth, double? maxHeight) { + html.CanvasElement resizeImageElement(html.ImageElement source, + double? maxWidth, double? maxHeight) { final newImageSize = calculateSizeOfDownScaledImage( Size(source.width!.toDouble(), source.height!.toDouble()), maxWidth, @@ -66,11 +73,11 @@ class ImageResizer { /// function that converts a canvas element to Xfile /// [imageQuality] is only supported for jpeg and webp images. - Future writeCanvasToFile( - XFile originalFile, html.CanvasElement canvas, int? imageQuality) async { + Future writeCanvasToFile(XFile originalFile, + html.CanvasElement canvas, int? imageQuality) async { final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / 100.0); final blob = - await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); + await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: originalFile.mimeType, name: "scaled_" + originalFile.name, diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart index 29f752c94504..776b70da9d79 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart @@ -40,7 +40,6 @@ Size calculateSizeOfDownScaledImage( ((height / originalHeight) * originalWidth).floorToDouble(); double downscaledHeight = ((width / originalWidth) * originalHeight).floorToDouble(); - if (width < height) { if (!hasMaxWidth) { width = downscaledWidth; From 7151d7150fa7222e20b10383263e0dbc8f58e650 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Mon, 4 Oct 2021 21:26:46 +0530 Subject: [PATCH 24/34] remove unused imports --- .../image_picker_for_web/lib/image_resizer.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index 899dc08f5e49..03324f7ee5d5 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -3,10 +3,8 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html'; import 'dart:math'; import 'dart:ui'; -import 'package:flutter/services.dart'; import 'package:image_picker_for_web/image_resizer_utils.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'dart:html' as html; @@ -26,7 +24,7 @@ class ImageResizer { final imageElement = await loadImage(file.path); final canvas = resizeImageElement(imageElement, maxWidth, maxHeight); return writeCanvasToFile(file, canvas, imageQuality); - }catch(e){ + } catch (e) { return file; } } @@ -76,8 +74,8 @@ class ImageResizer { Future writeCanvasToFile(XFile originalFile, html.CanvasElement canvas, int? imageQuality) async { final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / 100.0); - final blob = - await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); + final blob = await canvas.toBlob( + originalFile.mimeType, calculatedImageQuality); return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: originalFile.mimeType, name: "scaled_" + originalFile.name, From a3bbc58159cfc3209a8cbbe9a4c100beb75759e5 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Tue, 5 Oct 2021 00:36:14 +0530 Subject: [PATCH 25/34] dartfmt --- .../image_picker_for_web/lib/image_resizer.dart | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index 03324f7ee5d5..7ecc26024215 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -41,9 +41,7 @@ class ImageResizer { imageLoadCompleter.complete(imageElement); }); imageElement.onError.listen((event) { - final excpetion = Exception( - "Error while loading image." - ); + final excpetion = Exception("Error while loading image."); imageElement.remove(); imageLoadCompleter.completeError(excpetion); }); @@ -51,8 +49,8 @@ class ImageResizer { } /// Draws image to a canvas while resizing the image to fit the [maxWidth],[maxHeight] constraints - html.CanvasElement resizeImageElement(html.ImageElement source, - double? maxWidth, double? maxHeight) { + html.CanvasElement resizeImageElement( + html.ImageElement source, double? maxWidth, double? maxHeight) { final newImageSize = calculateSizeOfDownScaledImage( Size(source.width!.toDouble(), source.height!.toDouble()), maxWidth, @@ -71,11 +69,11 @@ class ImageResizer { /// function that converts a canvas element to Xfile /// [imageQuality] is only supported for jpeg and webp images. - Future writeCanvasToFile(XFile originalFile, - html.CanvasElement canvas, int? imageQuality) async { + Future writeCanvasToFile( + XFile originalFile, html.CanvasElement canvas, int? imageQuality) async { final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / 100.0); - final blob = await canvas.toBlob( - originalFile.mimeType, calculatedImageQuality); + final blob = + await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: originalFile.mimeType, name: "scaled_" + originalFile.name, From d81990bcbef208a722ae469a5849e6789178272d Mon Sep 17 00:00:00 2001 From: Balvinder Singh Gambhir Date: Tue, 5 Oct 2021 00:40:58 +0530 Subject: [PATCH 26/34] fix typo --- .../image_picker/image_picker_for_web/lib/image_resizer.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index 7ecc26024215..6491f59a1f20 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -41,9 +41,9 @@ class ImageResizer { imageLoadCompleter.complete(imageElement); }); imageElement.onError.listen((event) { - final excpetion = Exception("Error while loading image."); + final exception = Exception("Error while loading image."); imageElement.remove(); - imageLoadCompleter.completeError(excpetion); + imageLoadCompleter.completeError(exception); }); return imageLoadCompleter.future; } From c90eff3fbb3c709164f7d0ad19c2aabff9cae347 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Tue, 5 Oct 2021 00:42:07 +0530 Subject: [PATCH 27/34] remove 1 doc comment --- .../image_picker/image_picker_for_web/lib/image_resizer.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart index 6491f59a1f20..165c067a554a 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer.dart @@ -30,7 +30,6 @@ class ImageResizer { } /// function that loads the blobUrl into an imageElement - /// returns null if error comes while loading the image Future loadImage(String blobUrl) { final imageLoadCompleter = Completer(); final imageElement = html.ImageElement(); @@ -41,7 +40,7 @@ class ImageResizer { imageLoadCompleter.complete(imageElement); }); imageElement.onError.listen((event) { - final exception = Exception("Error while loading image."); + final exception = ("Error while loading image."); imageElement.remove(); imageLoadCompleter.completeError(exception); }); From 811b3fb6dffe77b884385fa3c4b5293a467d3ca3 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Tue, 5 Oct 2021 21:06:15 +0530 Subject: [PATCH 28/34] update tests and resizing algorithm --- .../integration_test/image_resizer_test.dart | 34 +++++++++++++++ .../lib/image_resizer_utils.dart | 43 +++---------------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart index ded09fe59136..2843036b2de5 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart @@ -29,6 +29,39 @@ void main() { name: pngHtmlFile.name, mimeType: pngHtmlFile.type); }); + testWidgets("image is loaded correctly ", (WidgetTester tester) async { + final imageElement = await imageResizer.loadImage(pngFile.path); + expect(imageElement.width!, 10); + expect(imageElement.height!, 10); + }); + + testWidgets( + "canvas is loaded with image's width and height when max width and max height are null", + (widgetTester) async { + final imageElement = await imageResizer.loadImage(pngFile.path); + final canvas = imageResizer.resizeImageElement(imageElement, null, null); + expect(canvas.width, imageElement.width); + expect(canvas.height, imageElement.height); + }); + + testWidgets( + "canvas size is scaled when max width and max height are not null", + (widgetTester) async { + final imageElement = await imageResizer.loadImage(pngFile.path); + final canvas = imageResizer.resizeImageElement(imageElement, 8, 8); + expect(canvas.width, 8); + expect(canvas.height, 8); + }); + + testWidgets("resized image is returned after converting canvas to file", + (widgetTester) async { + final imageElement = await imageResizer.loadImage(pngFile.path); + final canvas = imageResizer.resizeImageElement(imageElement, null, null); + final resizedImage = + await imageResizer.writeCanvasToFile(pngFile, canvas, null); + expect(resizedImage.name, "scaled_${pngFile.name}"); + }); + testWidgets("image is scaled when maxWidth is set", (WidgetTester tester) async { final scaledImage = @@ -37,6 +70,7 @@ void main() { final scaledImageSize = await _getImageSize(scaledImage); expect(scaledImageSize, Size(5, 5)); }); + testWidgets("image is scaled when maxHeight is set", (WidgetTester tester) async { final scaledImage = diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart index 776b70da9d79..6ef789254b3f 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart @@ -5,6 +5,8 @@ import 'dart:math'; import 'dart:ui'; +import 'package:flutter/material.dart'; + ///a function that checks if an image needs to be resized or not bool imageResizeNeeded(double? maxWidth, double? maxHeight, int? imageQuality) { return imageQuality != null @@ -24,41 +26,8 @@ bool isImageQualityValid(int imageQuality) { /// maxHeight is the maximum height of the scaled image Size calculateSizeOfDownScaledImage( Size imageSize, double? maxWidth, double? maxHeight) { - double originalWidth = imageSize.width; - double originalHeight = imageSize.height; - - bool hasMaxWidth = maxWidth != null; - bool hasMaxHeight = maxHeight != null; - double width = hasMaxWidth ? min(maxWidth, originalWidth) : originalWidth; - double height = - hasMaxHeight ? min(maxHeight, originalHeight) : originalHeight; - bool shouldDownscaleWidth = hasMaxWidth && maxWidth < originalWidth; - bool shouldDownscaleHeight = hasMaxHeight && maxHeight < originalHeight; - bool shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight; - if (shouldDownscale) { - double downscaledWidth = - ((height / originalHeight) * originalWidth).floorToDouble(); - double downscaledHeight = - ((width / originalWidth) * originalHeight).floorToDouble(); - if (width < height) { - if (!hasMaxWidth) { - width = downscaledWidth; - } else { - height = downscaledHeight; - } - } else if (height < width) { - if (!hasMaxHeight) { - height = downscaledHeight; - } else { - width = downscaledWidth; - } - } else { - if (originalWidth < originalHeight) { - width = downscaledWidth; - } else if (originalHeight < originalWidth) { - height = downscaledHeight; - } - } - } - return Size(width, height); + double widthFactor = maxWidth != null ? imageSize.width / maxWidth : 1; + double heightFactor = maxHeight != null ? imageSize.height / maxHeight : 1; + double resizeFactor = max(widthFactor, heightFactor); + return (resizeFactor > 1 ? imageSize ~/ resizeFactor : imageSize); } From 3e4d7c1db732761957c13fff9f0910e59d892303 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Tue, 5 Oct 2021 21:41:33 +0530 Subject: [PATCH 29/34] Review changes --- packages/image_picker/image_picker_for_web/README.md | 2 +- .../image_picker_for_web/lib/image_picker_for_web.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/README.md b/packages/image_picker/image_picker_for_web/README.md index 4e82d9c47584..c8b85f21cc89 100644 --- a/packages/image_picker/image_picker_for_web/README.md +++ b/packages/image_picker/image_picker_for_web/README.md @@ -44,7 +44,7 @@ difference in your users' experience. ### pickImage() The arguments `maxWidth`, `maxHeight` and `imageQuality` are not supported for gif images. -THe argument `imageQuality` only works for jpeg and webp images. +The argument `imageQuality` only works for jpeg and webp images. ### pickVideo() The argument `maxDuration` is not supported on the web. diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 6a5efb598a8d..ea616f317f96 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -250,7 +250,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { Future> _getSelectedXFiles(html.FileUploadInputElement input) { final Completer> _completer = Completer>(); // Observe the input until we can return something - input.onChange.first.then((event) async { + input.onChange.first.then((event) { final files = _handleOnChangeEvent(event); if (!_completer.isCompleted && files != null) { _completer.complete(files.map((file) { From 554e99674ed596ecef53e3e4c6696c9811fe237a Mon Sep 17 00:00:00 2001 From: balvinderz Date: Wed, 6 Oct 2021 13:04:30 +0530 Subject: [PATCH 30/34] move image resizer files in src --- .../example/integration_test/image_resizer_test.dart | 2 +- .../image_picker_for_web/lib/image_picker_for_web.dart | 2 +- .../image_picker_for_web/lib/{ => src}/image_resizer.dart | 7 ++++--- .../lib/{ => src}/image_resizer_utils.dart | 0 .../test/image_resizer_utils_test.dart | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) rename packages/image_picker/image_picker_for_web/lib/{ => src}/image_resizer.dart (93%) rename packages/image_picker/image_picker_for_web/lib/{ => src}/image_resizer_utils.dart (100%) diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart index 2843036b2de5..1cc73a9db133 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; import 'dart:ui'; import 'package:flutter_test/flutter_test.dart'; -import 'package:image_picker_for_web/image_resizer.dart'; +import 'package:image_picker_for_web/src/image_resizer.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:integration_test/integration_test.dart'; diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index ea616f317f96..bbfd37175dff 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'dart:html' as html; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:image_picker_for_web/image_resizer.dart'; +import 'package:image_picker_for_web/src/image_resizer.dart'; import 'package:meta/meta.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart similarity index 93% rename from packages/image_picker/image_picker_for_web/lib/image_resizer.dart rename to packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart index 165c067a554a..699070c28364 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart @@ -5,7 +5,7 @@ import 'dart:async'; import 'dart:math'; import 'dart:ui'; -import 'package:image_picker_for_web/image_resizer_utils.dart'; +import 'package:image_picker_for_web/src/image_resizer_utils.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'dart:html' as html; @@ -23,7 +23,9 @@ class ImageResizer { try { final imageElement = await loadImage(file.path); final canvas = resizeImageElement(imageElement, maxWidth, maxHeight); - return writeCanvasToFile(file, canvas, imageQuality); + final resizedImage = await writeCanvasToFile(file, canvas, imageQuality); + html.Url.revokeObjectUrl(file.path); + return resizedImage; } catch (e) { return file; } @@ -36,7 +38,6 @@ class ImageResizer { imageElement.src = blobUrl; imageElement.onLoad.listen((event) { - html.Url.revokeObjectUrl(blobUrl); imageLoadCompleter.complete(imageElement); }); imageElement.onError.listen((event) { diff --git a/packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/src/image_resizer_utils.dart similarity index 100% rename from packages/image_picker/image_picker_for_web/lib/image_resizer_utils.dart rename to packages/image_picker/image_picker_for_web/lib/src/image_resizer_utils.dart diff --git a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart index ce948bf07725..352d2bea48a5 100644 --- a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart +++ b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart @@ -4,7 +4,7 @@ import 'dart:ui'; import 'package:flutter_test/flutter_test.dart'; -import 'package:image_picker_for_web/image_resizer_utils.dart'; +import 'package:image_picker_for_web/src/image_resizer_utils.dart'; void main() { group('Image Resizer Utils', () { From d15b8b12953ba2a961a64c31d330e6964fd5b404 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Wed, 6 Oct 2021 19:26:27 +0530 Subject: [PATCH 31/34] dartfmt --- .../image_picker_for_web/lib/src/image_resizer.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart index 699070c28364..b3703e78a7a9 100644 --- a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart @@ -23,7 +23,7 @@ class ImageResizer { try { final imageElement = await loadImage(file.path); final canvas = resizeImageElement(imageElement, maxWidth, maxHeight); - final resizedImage = await writeCanvasToFile(file, canvas, imageQuality); + final resizedImage = await writeCanvasToFile(file, canvas, imageQuality); html.Url.revokeObjectUrl(file.path); return resizedImage; } catch (e) { @@ -49,8 +49,8 @@ class ImageResizer { } /// Draws image to a canvas while resizing the image to fit the [maxWidth],[maxHeight] constraints - html.CanvasElement resizeImageElement( - html.ImageElement source, double? maxWidth, double? maxHeight) { + html.CanvasElement resizeImageElement(html.ImageElement source, + double? maxWidth, double? maxHeight) { final newImageSize = calculateSizeOfDownScaledImage( Size(source.width!.toDouble(), source.height!.toDouble()), maxWidth, @@ -69,11 +69,11 @@ class ImageResizer { /// function that converts a canvas element to Xfile /// [imageQuality] is only supported for jpeg and webp images. - Future writeCanvasToFile( - XFile originalFile, html.CanvasElement canvas, int? imageQuality) async { + Future writeCanvasToFile(XFile originalFile, html.CanvasElement canvas, + int? imageQuality) async { final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / 100.0); final blob = - await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); + await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: originalFile.mimeType, name: "scaled_" + originalFile.name, From b87f0cf1aa1f0625cdcaf1362da19c74f7577ff8 Mon Sep 17 00:00:00 2001 From: balvinderz Date: Wed, 6 Oct 2021 19:33:36 +0530 Subject: [PATCH 32/34] dartfmt again --- .../image_picker_for_web/lib/src/image_resizer.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart index b3703e78a7a9..94d121e6b186 100644 --- a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart @@ -49,8 +49,8 @@ class ImageResizer { } /// Draws image to a canvas while resizing the image to fit the [maxWidth],[maxHeight] constraints - html.CanvasElement resizeImageElement(html.ImageElement source, - double? maxWidth, double? maxHeight) { + html.CanvasElement resizeImageElement( + html.ImageElement source, double? maxWidth, double? maxHeight) { final newImageSize = calculateSizeOfDownScaledImage( Size(source.width!.toDouble(), source.height!.toDouble()), maxWidth, @@ -69,11 +69,11 @@ class ImageResizer { /// function that converts a canvas element to Xfile /// [imageQuality] is only supported for jpeg and webp images. - Future writeCanvasToFile(XFile originalFile, html.CanvasElement canvas, - int? imageQuality) async { + Future writeCanvasToFile( + XFile originalFile, html.CanvasElement canvas, int? imageQuality) async { final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / 100.0); final blob = - await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); + await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: originalFile.mimeType, name: "scaled_" + originalFile.name, From d99a702b8c08cdc10c7715fc6ca5ef19255790d6 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 15 Oct 2021 18:27:41 -0700 Subject: [PATCH 33/34] Fix some review comments --- .../integration_test/image_resizer_test.dart | 2 -- .../lib/image_picker_for_web.dart | 24 +++++++++++++++---- .../lib/src/image_resizer.dart | 8 +++---- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart index 1cc73a9db133..067c7750eb11 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart @@ -106,8 +106,6 @@ Future _getImageSize(XFile file) async { final completer = Completer(); final image = html.ImageElement(src: file.path); image.onLoad.listen((event) { - print(image.width); - print(image.height); completer.complete(Size(image.width!.toDouble(), image.height!.toDouble())); }); image.onError.listen((event) { diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index bbfd37175dff..f13aeb1f0a4e 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -128,7 +128,11 @@ class ImagePickerPlugin extends ImagePickerPlatform { capture: capture, ); return _imageResizer.resizeImageIfNeeded( - files.first, maxWidth, maxHeight, imageQuality); + files.first, + maxWidth, + maxHeight, + imageQuality, + ); } /// Returns an [XFile] containing the video that was picked. @@ -164,10 +168,20 @@ class ImagePickerPlugin extends ImagePickerPlatform { double? maxHeight, int? imageQuality, }) async { - return await Future.wait( - (await getFiles(accept: _kAcceptImageMimeType, multiple: true)).map( - (e) => _imageResizer.resizeImageIfNeeded( - e, maxWidth, maxHeight, imageQuality))); + final List images = await getFiles( + accept: _kAcceptImageMimeType, + multiple: true, + ); + final Iterable> resized = images.map( + (image) => _imageResizer.resizeImageIfNeeded( + image, + maxWidth, + maxHeight, + imageQuality, + ), + ); + + return Future.wait(resized); } /// Injects a file input with the specified accept+capture attributes, and diff --git a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart index 94d121e6b186..6ee7c5f015e2 100644 --- a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart @@ -9,15 +9,15 @@ import 'package:image_picker_for_web/src/image_resizer_utils.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'dart:html' as html; -/// Helper class that resizes image +/// Helper class that resizes images. class ImageResizer { - /// Resizes the image if needed - /// Does not support gif image + /// Resizes the image if needed. + /// (Does not support gif images) Future resizeImageIfNeeded(XFile file, double? maxWidth, double? maxHeight, int? imageQuality) async { if (!imageResizeNeeded(maxWidth, maxHeight, imageQuality) || file.mimeType == "image/gif") { - //TODO Implement maxWidth and maxHeight for image/gif + // Implement maxWidth and maxHeight for image/gif return file; } try { From 5e825ab87914b4b35da550821b726af2332df7c9 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 15 Oct 2021 18:34:01 -0700 Subject: [PATCH 34/34] Slight update in the CHANGELOG --- packages/image_picker/image_picker_for_web/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index df305f1dd8a7..aee8b0863c13 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.1.4 -* Implemented `maxWidth`, `maxHeight` and `imageQuality` +* Implemented `maxWidth`, `maxHeight` and `imageQuality` when selecting images + (except for gifs). ## 2.1.3