From 36363f333f525a526fac962fe357d48927ecd84e Mon Sep 17 00:00:00 2001 From: Deep Gaurav Date: Thu, 13 Dec 2018 21:06:05 +0530 Subject: [PATCH 1/6] Use Isolate to Quantize Current computation method is very heavy on older devices. This shifts computation to an isolate using compute. this is only done is filters are null, since i could not find a way to pass filters/functions as message to isolate --- .../lib/palette_generator.dart | 83 +++++++++++++++---- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/packages/palette_generator/lib/palette_generator.dart b/packages/palette_generator/lib/palette_generator.dart index 397625835c2..29753a9e989 100644 --- a/packages/palette_generator/lib/palette_generator.dart +++ b/packages/palette_generator/lib/palette_generator.dart @@ -1040,8 +1040,9 @@ class _ColorCutQuantizer { final Rect region; final List filters; - Iterable _getImagePixels(ByteData pixels, int width, int height, - {Rect region}) sync* { + static Iterable _getImagePixels(ByteData pixels, int width, int height, Rect region + ) sync* { + final int rowStride = width * 4; int rowStart; int rowEnd; @@ -1075,8 +1076,7 @@ class _ColorCutQuantizer { } assert(byteCount == ((rowEnd - rowStart) * (colEnd - colStart) * 4)); } - - bool _shouldIgnoreColor(Color color) { + static bool _shouldIgnoreColor(Color color, List filters) { final HSLColor hslColor = HSLColor.fromColor(color); if (filters != null && filters.isNotEmpty) { for (PaletteFilter filter in filters) { @@ -1106,8 +1106,56 @@ class _ColorCutQuantizer { final ByteData imageData = await image.toByteData(format: ui.ImageByteFormat.rawRgba); + if(filters==null) + return await compute(_quantizefromByte, { + "byteData":imageData, + "maxColors":maxColors, + "width":image.width, + "height":image.height, + "region":region, + "paletteColors":_paletteColors, + "filters":null + }); + else + return _quantizefromByte({ + "byteData":imageData, + "maxColors":maxColors, + "width":image.width, + "height":image.height, + "region":region, + "paletteColors":_paletteColors, + "filters":filters + }); + } + + static List _quantizefromByte(Map map){ + ByteData bd = map["byteData"]; + int maxColors = map["maxColors"]; + int width = map["width"]; + int height = map["height"]; + Rect region = map["region"]; + List filters = map["filters"]; + List _paletteColors =map["paletteColors"]; + + + const int quantizeWordWidth = 5; + const int quantizeChannelWidth = 8; + const int quantizeShift = quantizeChannelWidth - quantizeWordWidth; + const int quantizeWordMask = + ((1 << quantizeWordWidth) - 1) << quantizeShift; + + Color quantizeColor(Color color) { + return Color.fromARGB( + color.alpha, + color.red & quantizeWordMask, + color.green & quantizeWordMask, + color.blue & quantizeWordMask, + ); + } + + final ByteData imageData = bd; final Iterable pixels = - _getImagePixels(imageData, image.width, image.height, region: region); + _getImagePixels(imageData, width, height,region); final Map hist = {}; for (Color pixel in pixels) { // Update the histogram, but only for non-zero alpha values, and for the @@ -1122,7 +1170,7 @@ class _ColorCutQuantizer { } // Now let's remove any colors that the filters want to ignore. hist.removeWhere((Color color, int _) { - return _shouldIgnoreColor(color); + return _shouldIgnoreColor(color,filters); }); if (hist.length <= maxColors) { // The image has fewer colors than the maximum requested, so just return @@ -1134,15 +1182,19 @@ class _ColorCutQuantizer { } else { // We need use quantization to reduce the number of colors _paletteColors.clear(); - _paletteColors.addAll(_quantizePixels(maxColors, hist)); + _paletteColors.addAll( _quantizePixels({"maxColors":maxColors, "histogram":hist},filters)); } return _paletteColors; } - List _quantizePixels( - int maxColors, - Map histogram, + + static List _quantizePixels( + Map args,List filters ) { + print("Isolate started"); + int maxColors = args["maxColors"]; + Map histogram = args["histogram"]; + int volumeComparator(_ColorVolumeBox a, _ColorVolumeBox b) { return b.getVolume().compareTo(a.getVolume()); } @@ -1158,7 +1210,7 @@ class _ColorCutQuantizer { // or there are no more boxes to split _splitBoxes(priorityQueue, maxColors); // Finally, return the average colors of the color boxes. - return _generateAverageColors(priorityQueue); + return _generateAverageColors(priorityQueue,filters); } // Iterate through the [PriorityQueue], popping [_ColorVolumeBox] objects @@ -1166,7 +1218,7 @@ class _ColorCutQuantizer { // remaining box are offered back to the queue. // // The `maxSize` is the maximum number of boxes to split. - void _splitBoxes(PriorityQueue<_ColorVolumeBox> queue, final int maxSize) { + static void _splitBoxes(PriorityQueue<_ColorVolumeBox> queue, final int maxSize) { while (queue.length < maxSize) { final _ColorVolumeBox colorVolumeBox = queue.removeFirst(); if (colorVolumeBox != null && colorVolumeBox.canSplit()) { @@ -1182,12 +1234,13 @@ class _ColorCutQuantizer { } // Generates the average colors from each of the boxes in the queue. - List _generateAverageColors( - PriorityQueue<_ColorVolumeBox> colorVolumeBoxes) { + static List _generateAverageColors( + PriorityQueue<_ColorVolumeBox> colorVolumeBoxes,List filters) { final List colors = []; for (_ColorVolumeBox colorVolumeBox in colorVolumeBoxes.toList()) { final PaletteColor paletteColor = colorVolumeBox.getAverageColor(); - if (!_shouldIgnoreColor(paletteColor.color)) { + + if (!_shouldIgnoreColor(paletteColor.color, filters)) { colors.add(paletteColor); } } From d17081fff6f4d9698d08dc18ee9da4aa2bb5e1a7 Mon Sep 17 00:00:00 2001 From: Deep Gaurav Date: Sun, 15 Sep 2019 10:08:27 +0530 Subject: [PATCH 2/6] Minor formatting changes --- .../lib/palette_generator.dart | 143 ++++++++---------- 1 file changed, 63 insertions(+), 80 deletions(-) diff --git a/packages/palette_generator/lib/palette_generator.dart b/packages/palette_generator/lib/palette_generator.dart index 29753a9e989..fcac4771f30 100644 --- a/packages/palette_generator/lib/palette_generator.dart +++ b/packages/palette_generator/lib/palette_generator.dart @@ -182,15 +182,16 @@ class PaletteGenerator extends Diagnosticable { ImageConfiguration(size: size, devicePixelRatio: 1.0), ); final Completer imageCompleter = Completer(); - Timer loadFailureTimeout; - void imageListener(ImageInfo info, bool synchronousCall) { - loadFailureTimeout?.cancel(); - stream.removeListener(imageListener); - imageCompleter.complete(info.image); - } - + var imageListener = ImageStreamListener( + (ImageInfo info, bool synchronouscall) { + imageCompleter.complete(info.image); + }, + onError: (error, stackTrace) { + imageCompleter.completeError(error); + }, + ); if (timeout != Duration.zero) { - loadFailureTimeout = Timer(timeout, () { + Timer(timeout, () { stream.removeListener(imageListener); imageCompleter.completeError( TimeoutException( @@ -222,7 +223,6 @@ class PaletteGenerator extends Diagnosticable { /// By default, this contains the entire list of predefined targets in /// [PaletteTarget.baseTargets]. final List targets; - /// Returns a list of colors in the [paletteColors], sorted from most /// dominant to least dominant color. Iterable get colors sync* { @@ -1040,9 +1040,8 @@ class _ColorCutQuantizer { final Rect region; final List filters; - static Iterable _getImagePixels(ByteData pixels, int width, int height, Rect region - ) sync* { - + static Iterable _getImagePixels( + ByteData pixels, int width, int height, Rect region) sync* { final int rowStride = width * 4; int rowStart; int rowEnd; @@ -1076,6 +1075,7 @@ class _ColorCutQuantizer { } assert(byteCount == ((rowEnd - rowStart) * (colEnd - colStart) * 4)); } + static bool _shouldIgnoreColor(Color color, List filters) { final HSLColor hslColor = HSLColor.fromColor(color); if (filters != null && filters.isNotEmpty) { @@ -1089,79 +1089,48 @@ class _ColorCutQuantizer { } Future> _quantizeColors(ui.Image image) async { - const int quantizeWordWidth = 5; - const int quantizeChannelWidth = 8; - const int quantizeShift = quantizeChannelWidth - quantizeWordWidth; - const int quantizeWordMask = - ((1 << quantizeWordWidth) - 1) << quantizeShift; - - Color quantizeColor(Color color) { - return Color.fromARGB( - color.alpha, - color.red & quantizeWordMask, - color.green & quantizeWordMask, - color.blue & quantizeWordMask, - ); - } - final ByteData imageData = await image.toByteData(format: ui.ImageByteFormat.rawRgba); - if(filters==null) + if (filters == null) return await compute(_quantizefromByte, { - "byteData":imageData, - "maxColors":maxColors, - "width":image.width, - "height":image.height, - "region":region, - "paletteColors":_paletteColors, - "filters":null - }); + 'byteData': imageData, + 'maxColors': maxColors, + 'width': image.width, + 'height': image.height, + 'region': region, + 'paletteColors': _paletteColors, + 'filters': null + }); else return _quantizefromByte({ - "byteData":imageData, - "maxColors":maxColors, - "width":image.width, - "height":image.height, - "region":region, - "paletteColors":_paletteColors, - "filters":filters + 'byteData': imageData, + 'maxColors': maxColors, + 'width': image.width, + 'height': image.height, + 'region': region, + 'paletteColors': _paletteColors, + 'filters': filters }); } - static List _quantizefromByte(Map map){ - ByteData bd = map["byteData"]; - int maxColors = map["maxColors"]; - int width = map["width"]; - int height = map["height"]; - Rect region = map["region"]; - List filters = map["filters"]; - List _paletteColors =map["paletteColors"]; - - - const int quantizeWordWidth = 5; - const int quantizeChannelWidth = 8; - const int quantizeShift = quantizeChannelWidth - quantizeWordWidth; - const int quantizeWordMask = - ((1 << quantizeWordWidth) - 1) << quantizeShift; - - Color quantizeColor(Color color) { - return Color.fromARGB( - color.alpha, - color.red & quantizeWordMask, - color.green & quantizeWordMask, - color.blue & quantizeWordMask, - ); - } + static List _quantizefromByte(Map map) { + ByteData bd = map['byteData']; + int maxColors = map['maxColors']; + int width = map['width']; + int height = map['height']; + Rect region = map['region']; + List filters = map['filters']; + List _paletteColors = map['paletteColors']; final ByteData imageData = bd; final Iterable pixels = - _getImagePixels(imageData, width, height,region); + _getImagePixels(imageData, width, height, region); final Map hist = {}; for (Color pixel in pixels) { // Update the histogram, but only for non-zero alpha values, and for the // ones we do add, make their alphas opaque so that we can use a Color as // the histogram key. - final Color quantizedColor = quantizeColor(pixel); + final Color quantizedColor = _quantizeColor(pixel); final Color colorKey = quantizedColor.withAlpha(0xff); // Skip pixels that are entirely transparent. if (quantizedColor.alpha != 0x0) { @@ -1170,7 +1139,7 @@ class _ColorCutQuantizer { } // Now let's remove any colors that the filters want to ignore. hist.removeWhere((Color color, int _) { - return _shouldIgnoreColor(color,filters); + return _shouldIgnoreColor(color, filters); }); if (hist.length <= maxColors) { // The image has fewer colors than the maximum requested, so just return @@ -1182,19 +1151,31 @@ class _ColorCutQuantizer { } else { // We need use quantization to reduce the number of colors _paletteColors.clear(); - _paletteColors.addAll( _quantizePixels({"maxColors":maxColors, "histogram":hist},filters)); + _paletteColors.addAll(_quantizePixels( + {'maxColors': maxColors, 'histogram': hist}, filters)); } return _paletteColors; } + static Color _quantizeColor(Color color) { + const int quantizeWordWidth = 5; + const int quantizeChannelWidth = 8; + const int quantizeShift = quantizeChannelWidth - quantizeWordWidth; + const int quantizeWordMask = + ((1 << quantizeWordWidth) - 1) << quantizeShift; + return Color.fromARGB( + color.alpha, + color.red & quantizeWordMask, + color.green & quantizeWordMask, + color.blue & quantizeWordMask, + ); + } static List _quantizePixels( - Map args,List filters - ) { - print("Isolate started"); - int maxColors = args["maxColors"]; - Map histogram = args["histogram"]; - + Map args, List filters) { + int maxColors = args['maxColors']; + Map histogram = args['histogram']; + int volumeComparator(_ColorVolumeBox a, _ColorVolumeBox b) { return b.getVolume().compareTo(a.getVolume()); } @@ -1210,7 +1191,7 @@ class _ColorCutQuantizer { // or there are no more boxes to split _splitBoxes(priorityQueue, maxColors); // Finally, return the average colors of the color boxes. - return _generateAverageColors(priorityQueue,filters); + return _generateAverageColors(priorityQueue, filters); } // Iterate through the [PriorityQueue], popping [_ColorVolumeBox] objects @@ -1218,7 +1199,8 @@ class _ColorCutQuantizer { // remaining box are offered back to the queue. // // The `maxSize` is the maximum number of boxes to split. - static void _splitBoxes(PriorityQueue<_ColorVolumeBox> queue, final int maxSize) { + static void _splitBoxes( + PriorityQueue<_ColorVolumeBox> queue, final int maxSize) { while (queue.length < maxSize) { final _ColorVolumeBox colorVolumeBox = queue.removeFirst(); if (colorVolumeBox != null && colorVolumeBox.canSplit()) { @@ -1235,7 +1217,8 @@ class _ColorCutQuantizer { // Generates the average colors from each of the boxes in the queue. static List _generateAverageColors( - PriorityQueue<_ColorVolumeBox> colorVolumeBoxes,List filters) { + PriorityQueue<_ColorVolumeBox> colorVolumeBoxes, + List filters) { final List colors = []; for (_ColorVolumeBox colorVolumeBox in colorVolumeBoxes.toList()) { final PaletteColor paletteColor = colorVolumeBox.getAverageColor(); From 2d1274db170871d34b6a452ee44b228bcdcb4d1d Mon Sep 17 00:00:00 2001 From: Deep Gaurav Date: Sun, 15 Sep 2019 10:13:09 +0530 Subject: [PATCH 3/6] Update palette_generator.dart --- packages/palette_generator/lib/palette_generator.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/palette_generator/lib/palette_generator.dart b/packages/palette_generator/lib/palette_generator.dart index fcac4771f30..485cf6433ed 100644 --- a/packages/palette_generator/lib/palette_generator.dart +++ b/packages/palette_generator/lib/palette_generator.dart @@ -1121,7 +1121,6 @@ class _ColorCutQuantizer { Rect region = map['region']; List filters = map['filters']; List _paletteColors = map['paletteColors']; - final ByteData imageData = bd; final Iterable pixels = _getImagePixels(imageData, width, height, region); From 903fd4b8ff56a67c348dc32f59332ffbcff8f34d Mon Sep 17 00:00:00 2001 From: Deep Gaurav Date: Sun, 15 Sep 2019 10:17:55 +0530 Subject: [PATCH 4/6] Run dartfmt --- packages/palette_generator/lib/palette_generator.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/palette_generator/lib/palette_generator.dart b/packages/palette_generator/lib/palette_generator.dart index 485cf6433ed..ca9b2de23b3 100644 --- a/packages/palette_generator/lib/palette_generator.dart +++ b/packages/palette_generator/lib/palette_generator.dart @@ -223,6 +223,7 @@ class PaletteGenerator extends Diagnosticable { /// By default, this contains the entire list of predefined targets in /// [PaletteTarget.baseTargets]. final List targets; + /// Returns a list of colors in the [paletteColors], sorted from most /// dominant to least dominant color. Iterable get colors sync* { From 225b4a10954a807f83d6106763059aa0db55be6d Mon Sep 17 00:00:00 2001 From: Deep Gaurav Date: Tue, 24 Sep 2019 21:25:20 +0530 Subject: [PATCH 5/6] Update palette_generator.dart --- packages/palette_generator/lib/palette_generator.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/palette_generator/lib/palette_generator.dart b/packages/palette_generator/lib/palette_generator.dart index ca9b2de23b3..2ed76aedeae 100644 --- a/packages/palette_generator/lib/palette_generator.dart +++ b/packages/palette_generator/lib/palette_generator.dart @@ -182,11 +182,11 @@ class PaletteGenerator extends Diagnosticable { ImageConfiguration(size: size, devicePixelRatio: 1.0), ); final Completer imageCompleter = Completer(); - var imageListener = ImageStreamListener( + final ImageStreamListener imageListener = ImageStreamListener( (ImageInfo info, bool synchronouscall) { imageCompleter.complete(info.image); }, - onError: (error, stackTrace) { + onError: (dynamic error,StackTrace stackTrace) { imageCompleter.completeError(error); }, ); @@ -1114,7 +1114,7 @@ class _ColorCutQuantizer { }); } - static List _quantizefromByte(Map map) { + static List _quantizefromByte(Map map) { ByteData bd = map['byteData']; int maxColors = map['maxColors']; int width = map['width']; From 315bcb6cfe70f45500bf9f68badd79acc6084edb Mon Sep 17 00:00:00 2001 From: Deep Gaurav Date: Tue, 24 Sep 2019 21:46:13 +0530 Subject: [PATCH 6/6] Update palette_generator.dart --- packages/palette_generator/lib/palette_generator.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/palette_generator/lib/palette_generator.dart b/packages/palette_generator/lib/palette_generator.dart index 2ed76aedeae..24225d5ff39 100644 --- a/packages/palette_generator/lib/palette_generator.dart +++ b/packages/palette_generator/lib/palette_generator.dart @@ -186,7 +186,7 @@ class PaletteGenerator extends Diagnosticable { (ImageInfo info, bool synchronouscall) { imageCompleter.complete(info.image); }, - onError: (dynamic error,StackTrace stackTrace) { + onError: (dynamic error, StackTrace stackTrace) { imageCompleter.completeError(error); }, ); @@ -1114,7 +1114,7 @@ class _ColorCutQuantizer { }); } - static List _quantizefromByte(Map map) { + static List _quantizefromByte(Map map) { ByteData bd = map['byteData']; int maxColors = map['maxColors']; int width = map['width'];