[roll] Update third-party dart packages
Updated:
Change-Id: Ic0f7001c3a9ba8ee0e68fec553590d9b79a82d07
diff --git a/image/.gitignore b/image/.gitignore
old mode 100644
new mode 100755
index a5eb6db..db51464
--- a/image/.gitignore
+++ b/image/.gitignore
@@ -1,29 +1,29 @@
-pubspec.lock
-example/packages/
-example/res/packages/
-packages/
-example/res/out-alpha_edge.jpg
-example/res/out-trees.jpg
-example/res/thumbnail-cat-eye04.jpg
-example/res/out-alpha_edge.png
-example/res/thumbnail-diamond_plate_texture.jpg
-test/out/
-lib/docs/
-example/webp_viewer.dart.js
-example/webp_viewer.dart.js.deps
-example/webp_viewer.dart.js.map
-example/webp_viewer.dart.precompiled.js
-example/test_formats.dart.js
-example/test_formats.dart.js.deps
-example/test_formats.dart.js.map
-example/test_formats.dart.precompiled.js
-build/
-packages
-psd_to_html/out/
-.idea/
-.packages
-.pub/
-add.png
-out
-.dart_tool/
-example/thumbnail.png
+pubspec.lock
+example/packages/
+example/res/packages/
+packages/
+example/res/out-alpha_edge.jpg
+example/res/out-trees.jpg
+example/res/thumbnail-cat-eye04.jpg
+example/res/out-alpha_edge.png
+example/res/thumbnail-diamond_plate_texture.jpg
+test/out/
+lib/docs/
+example/webp_viewer.dart.js
+example/webp_viewer.dart.js.deps
+example/webp_viewer.dart.js.map
+example/webp_viewer.dart.precompiled.js
+example/test_formats.dart.js
+example/test_formats.dart.js.deps
+example/test_formats.dart.js.map
+example/test_formats.dart.precompiled.js
+build/
+packages
+psd_to_html/out/
+.idea/
+.packages
+.pub/
+add.png
+out
+.dart_tool/
+example/thumbnail.png
diff --git a/image/.travis.yml b/image/.travis.yml
old mode 100644
new mode 100755
index 2729fa2..ce6d653
--- a/image/.travis.yml
+++ b/image/.travis.yml
@@ -1,16 +1,16 @@
-language: dart
-sudo: false
-dart:
- - dev
-
-dart_task:
- - test
- - dartanalyzer
-
-# Only building master means that we don't run two builds for each pull request.
-branches:
- only: [master]
-
-cache:
- directories:
- - $HOME/.pub-cache
+language: dart
+sudo: false
+dart:
+ - dev
+
+dart_task:
+ - test
+ - dartanalyzer
+
+# Only building master means that we don't run two builds for each pull request.
+branches:
+ only: [master]
+
+cache:
+ directories:
+ - $HOME/.pub-cache
diff --git a/image/BUILD.gn b/image/BUILD.gn
index 0897701..fc13501 100644
--- a/image/BUILD.gn
+++ b/image/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for image-2.0.7
+# This file is generated by importer.py for image-2.0.8
import("//build/dart/dart_library.gni")
diff --git a/image/CHANGELOG.md b/image/CHANGELOG.md
old mode 100644
new mode 100755
index f90901e..9ed3af9
--- a/image/CHANGELOG.md
+++ b/image/CHANGELOG.md
@@ -1,141 +1,149 @@
-## 2.0.7 - February 5, 2019
-
-- Improve JPEG decoding performance.
-- Decode and encode ICC profile data from PNG images.
-
-## 2.0.6 - January 26, 2019
-
-- bakeOrientation will clear the image's exif orientation properties.
-- copyResize will correctly maintain the image's orientation.
-
-## 2.0.5 - December 1, 2018
-
-- Added APNG (animated PNG) encoding.
-- Optimized drawString function.
-
-## 2.0.3 - June 6, 2018
-
-- copyResize can maintain aspect ratio when resizing height by using -1 for the width.
-- Added example for loading and processing images in an isolate.
-
-## 2.0.2 - June 1, 2018
-
-- Re-added decoding of orientation exif value from jpeg images.
-- Added bake_orientation function, which will rotate an image so that it physically matches its orientation exif value,
-useful for rotating an image prior to exporting it to a format that doesn't support exif data.
-
-## 2.0.1 - May 28, 2018
-
-Fix for bad jpeg files when encoding EXIF data.
-
-## 2.0.0 - May 22, 2018
-
-Remove the use of Dart 1 upper-case constants.
-Update SDK dependency to a 2.0 development release.
-
-## 1.1.33 - May 16, 2018
-
- Maintain EXIF data from JPEG images.
-
-## 1.1.32 - May 9, 2018
-
- Remove the use of `part` and `part of` in the main library.
-
-## 1.1.30 - March 10, 2018
-
- Update pubspec to account for the new version of xml package that has been
- published.
-
-## 1.1.29 - September 18, 2017
-
-- Add fixes for strong mode support.
-
-## 1.1.28 - May 27, 2017
-
-- Update pubspec to fix recent pub issues.
-- Rename changelog.txt to CHANGELOG.md.
-- Fix for 8-bit PNG decoding.
-
-## 1.1.27 - May 14, 2017
-
-- Fix crash decoding some jpeg images.
-
-
-## 1.1.24 - January 23, 2015
-
-- PVR encoding/decoding
-- Fix 16-bit tiff decoding
-
-
-## 1.1.23 - September 15, 2014
-
-- Fix alpha for PSD images.
-
-
-## 1.1.22 - July 31, 2014
-
-- Various bug fixes
-
-
-## 1.1.21 - June 19, 2014
-
-- Add drawImage function
-- Update XML dependency to 2.0.0
-
-
-## 1.1.20 - April 26, 2014
-
-- Fix OpenEXR decoder for dart2js
-
-
-## 1.1.19 - April 15, 2014
-
-- OpenEXR fixes.
-
-
-## 1.1.18 - April 06, 2014
-
-- Added OpenEXR format docoder.
-
-
-## 1.1.17 - April 02, 2014
-
-- Add Photoshop PSD format decoder
-
-
-## 1.1.16 - March 24, 2014
-
-- Fix JPEG encoder for compression quality < 100.
-
-
-## 1.1.15 - March 10, 2014
-
-- Update to new version of archive.
-
-
-## 1.1.14 - February 26, 2014
-
-- Optimizations
-
-
-## 1.1.13 - February 16, 2014
-
-- Added TIFF decoder
-
-
-## 1.1.10 - February 11, 2014
-
-- Added APNG animated PNG decoding support.
-- Improved JPEG decoding performance
-- Various bug fixes
-
-
-## 1.1.8 - February 01, 2014
-
-- Added GIF decoding support, including animated gifs.
-
-
-## 1.1.7 - January 28, 2014
-
-- Added WebP decoding support, included animated WebP.
-
+## 2.0.8 - May 8, 2019
+
+- Add ability to quantize an image to any number of colors.
+- Optimizations for the JPEG decoder.
+- Use #AARRGGBB for colors instead of ##AABBGGRR, to be compatible with Flutter image class.
+- Add floodfill drawing function.
+- CopyRectify to transform an arbitrary quad to the full image.
+- Improve performance of CopyResize.
+
+## 2.0.7 - February 5, 2019
+
+- Improve JPEG decoding performance.
+- Decode and encode ICC profile data from PNG images.
+
+## 2.0.6 - January 26, 2019
+
+- bakeOrientation will clear the image's exif orientation properties.
+- copyResize will correctly maintain the image's orientation.
+
+## 2.0.5 - December 1, 2018
+
+- Added APNG (animated PNG) encoding.
+- Optimized drawString function.
+
+## 2.0.3 - June 6, 2018
+
+- copyResize can maintain aspect ratio when resizing height by using -1 for the width.
+- Added example for loading and processing images in an isolate.
+
+## 2.0.2 - June 1, 2018
+
+- Re-added decoding of orientation exif value from jpeg images.
+- Added bake_orientation function, which will rotate an image so that it physically matches its orientation exif value,
+useful for rotating an image prior to exporting it to a format that doesn't support exif data.
+
+## 2.0.1 - May 28, 2018
+
+Fix for bad jpeg files when encoding EXIF data.
+
+## 2.0.0 - May 22, 2018
+
+Remove the use of Dart 1 upper-case constants.
+Update SDK dependency to a 2.0 development release.
+
+## 1.1.33 - May 16, 2018
+
+ Maintain EXIF data from JPEG images.
+
+## 1.1.32 - May 9, 2018
+
+ Remove the use of `part` and `part of` in the main library.
+
+## 1.1.30 - March 10, 2018
+
+ Update pubspec to account for the new version of xml package that has been
+ published.
+
+## 1.1.29 - September 18, 2017
+
+- Add fixes for strong mode support.
+
+## 1.1.28 - May 27, 2017
+
+- Update pubspec to fix recent pub issues.
+- Rename changelog.txt to CHANGELOG.md.
+- Fix for 8-bit PNG decoding.
+
+## 1.1.27 - May 14, 2017
+
+- Fix crash decoding some jpeg images.
+
+
+## 1.1.24 - January 23, 2015
+
+- PVR encoding/decoding
+- Fix 16-bit tiff decoding
+
+
+## 1.1.23 - September 15, 2014
+
+- Fix alpha for PSD images.
+
+
+## 1.1.22 - July 31, 2014
+
+- Various bug fixes
+
+
+## 1.1.21 - June 19, 2014
+
+- Add drawImage function
+- Update XML dependency to 2.0.0
+
+
+## 1.1.20 - April 26, 2014
+
+- Fix OpenEXR decoder for dart2js
+
+
+## 1.1.19 - April 15, 2014
+
+- OpenEXR fixes.
+
+
+## 1.1.18 - April 06, 2014
+
+- Added OpenEXR format docoder.
+
+
+## 1.1.17 - April 02, 2014
+
+- Add Photoshop PSD format decoder
+
+
+## 1.1.16 - March 24, 2014
+
+- Fix JPEG encoder for compression quality < 100.
+
+
+## 1.1.15 - March 10, 2014
+
+- Update to new version of archive.
+
+
+## 1.1.14 - February 26, 2014
+
+- Optimizations
+
+
+## 1.1.13 - February 16, 2014
+
+- Added TIFF decoder
+
+
+## 1.1.10 - February 11, 2014
+
+- Added APNG animated PNG decoding support.
+- Improved JPEG decoding performance
+- Various bug fixes
+
+
+## 1.1.8 - February 01, 2014
+
+- Added GIF decoding support, including animated gifs.
+
+
+## 1.1.7 - January 28, 2014
+
+- Added WebP decoding support, included animated WebP.
diff --git a/image/LICENSE b/image/LICENSE
old mode 100644
new mode 100755
index 0e2b6e5..d605f93
--- a/image/LICENSE
+++ b/image/LICENSE
@@ -1,156 +1,156 @@
- Copyright 2013 Brendan Duncan
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-
-
-Parts of the Image library were ported from the following sources:
-
-==============================================================================
-
- The JPEG encoder/decoder code is derived from the following:
- https://github.com/notmasteryet/jpgjs
- Copyright (C) 2011 notmasteryet
-
- Contributors: Yury Delendik <ydelendik@mozilla.com>
- Brendan Dahl <bdahl@mozilla.com>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-==============================================================================
-
-The WebP encoder/decoder code is derived from the following:
- http://git.chromium.org/gitweb/?p=webm/libwebp.git
- Copyright (c) 2010, Google Inc. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
- * Neither the name of Google nor the names of its contributors may
- be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-==============================================================================
-
-The TIFF decoder code is derived from the following:
- Apache Batik
- http://svn.apache.org/repos/asf/xmlgraphics/batik/trunk
- Copyright 1999-2007 The Apache Software Foundation
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-==============================================================================
-
-The OpenEXR decoder is derived in part from the OpenEXR library:
-
-Copyright (c) 2002-2011, Industrial Light & Magic, a division of
-Lucasfilm Entertainment Company Ltd. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
-Neither the name of Industrial Light & Magic nor the names of its contributors
-may be used to endorse or promote products derived from this software without
-specific prior written permission.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-===============================================================================
-
-The PVRTC compression and decompression code is ported from Jeffrey Lam's
-PvrTcCompressor:
-
-https://bitbucket.org/jthlim/pvrtccompressor
-
-===============================================================================
-
-The pvr container format decoder is derived from the QuickPVR project:
-QuickPVR is Copyright (C) 2010 Limbic Software, Inc.
-http://www.limbicsoftware.com/quickpvr.html
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the Limbic Software, Inc. nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY LIMBIC SOFTWARE, INC. ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL LIMBIC SOFTWARE, INC. BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Copyright 2013 Brendan Duncan
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+Parts of the Image library were ported from the following sources:
+
+==============================================================================
+
+ The JPEG encoder/decoder code is derived from the following:
+ https://github.com/notmasteryet/jpgjs
+ Copyright (C) 2011 notmasteryet
+
+ Contributors: Yury Delendik <ydelendik@mozilla.com>
+ Brendan Dahl <bdahl@mozilla.com>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+==============================================================================
+
+The WebP encoder/decoder code is derived from the following:
+ http://git.chromium.org/gitweb/?p=webm/libwebp.git
+ Copyright (c) 2010, Google Inc. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name of Google nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+==============================================================================
+
+The TIFF decoder code is derived from the following:
+ Apache Batik
+ http://svn.apache.org/repos/asf/xmlgraphics/batik/trunk
+ Copyright 1999-2007 The Apache Software Foundation
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+==============================================================================
+
+The OpenEXR decoder is derived in part from the OpenEXR library:
+
+Copyright (c) 2002-2011, Industrial Light & Magic, a division of
+Lucasfilm Entertainment Company Ltd. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+Neither the name of Industrial Light & Magic nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+===============================================================================
+
+The PVRTC compression and decompression code is ported from Jeffrey Lam's
+PvrTcCompressor:
+
+https://bitbucket.org/jthlim/pvrtccompressor
+
+===============================================================================
+
+The pvr container format decoder is derived from the QuickPVR project:
+QuickPVR is Copyright (C) 2010 Limbic Software, Inc.
+http://www.limbicsoftware.com/quickpvr.html
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Limbic Software, Inc. nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY LIMBIC SOFTWARE, INC. ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL LIMBIC SOFTWARE, INC. BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/image/README.md b/image/README.md
old mode 100644
new mode 100755
index be38dbc..c642532
--- a/image/README.md
+++ b/image/README.md
@@ -1,57 +1,57 @@
-# image
-
-[![Build Status](https://travis-ci.org/brendan-duncan/image.svg?branch=master)](https://travis-ci.org/brendan-duncan/image)
-
-## Overview
-
-A Dart library providing the ability to load, save and manipulate images in a variety of different file formats.
-
-The library has no reliance on `dart:io`, so it can be used for both server and
-web applications.
-
-**Supported Image Formats:**
-
-Read/Write:
-
-- PNG / Animated APNG
-- JPEG
-- Targa
-- GIF / Animated GIF
-- PVR(PVRTC)
-
-Read Only:
-
-- WebP / Animated WebP
-- TIFF
-- Photoshop PSD
-- OpenEXR
-
-
-## [Documentation](https://github.com/brendan-duncan/image/wiki)
-
-## [API](https://www.dartdocs.org/documentation/image/latest/)
-
-## [Examples](https://github.com/brendan-duncan/image/wiki/Examples)
-
-## [Format Decoding Functions](https://github.com/brendan-duncan/image/wiki#format-decoding-functions)
-
-## Samples
-
-Load an image, resize it, and save it as a png:
-
- import 'dart:io' as Io;
- import 'package:image/image.dart';
- void main() {
- // Read an image from file (webp in this case).
- // decodeImage will identify the format of the image and use the appropriate
- // decoder.
- Image image = decodeImage(new Io.File('test.webp').readAsBytesSync());
-
- // Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
- Image thumbnail = copyResize(image, 120);
-
- // Save the thumbnail as a PNG.
- new Io.File('thumbnail.png')
- ..writeAsBytesSync(encodePng(thumbnail));
- }
-
+# image
+
+[![Build Status](https://travis-ci.org/brendan-duncan/image.svg?branch=master)](https://travis-ci.org/brendan-duncan/image)
+
+## Overview
+
+A Dart library providing the ability to load, save and manipulate images in a variety of different file formats.
+
+The library has no reliance on `dart:io`, so it can be used for both server and
+web applications.
+
+**Supported Image Formats:**
+
+Read/Write:
+
+- PNG / Animated APNG
+- JPEG
+- Targa
+- GIF / Animated GIF
+- PVR(PVRTC)
+
+Read Only:
+
+- WebP / Animated WebP
+- TIFF
+- Photoshop PSD
+- OpenEXR
+
+
+## [Documentation](https://github.com/brendan-duncan/image/wiki)
+
+## [API](https://www.dartdocs.org/documentation/image/latest/)
+
+## [Examples](https://github.com/brendan-duncan/image/wiki/Examples)
+
+## [Format Decoding Functions](https://github.com/brendan-duncan/image/wiki#format-decoding-functions)
+
+## Samples
+
+Load an image, resize it, and save it as a png:
+
+ import 'dart:io' as Io;
+ import 'package:image/image.dart';
+ void main() {
+ // Read an image from file (webp in this case).
+ // decodeImage will identify the format of the image and use the appropriate
+ // decoder.
+ Image image = decodeImage(new Io.File('test.webp').readAsBytesSync());
+
+ // Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
+ Image thumbnail = copyResize(image, 120);
+
+ // Save the thumbnail as a PNG.
+ new Io.File('thumbnail.png')
+ ..writeAsBytesSync(encodePng(thumbnail));
+ }
+
diff --git a/image/analysis_options.yaml b/image/analysis_options.yaml
old mode 100644
new mode 100755
index 2e6cdca..0ecbeea
--- a/image/analysis_options.yaml
+++ b/image/analysis_options.yaml
@@ -1 +1 @@
-analyzer:
+analyzer:
diff --git a/image/example/example.dart b/image/example/example.dart
old mode 100644
new mode 100755
index 3472525..eac0c88
--- a/image/example/example.dart
+++ b/image/example/example.dart
@@ -1,14 +1,14 @@
-import 'dart:io';
-import 'package:image/image.dart';
-void main() {
- // Read an image from file (webp in this case).
- // decodeImage will identify the format of the image and use the appropriate
- // decoder.
- Image image = decodeImage(new File('test.webp').readAsBytesSync());
-
- // Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
- Image thumbnail = copyResize(image, 120);
-
- // Save the thumbnail as a PNG.
- new File('thumbnail.png').writeAsBytesSync(encodePng(thumbnail));
-}
+import 'dart:io';
+import 'package:image/image.dart';
+void main() {
+ // Read an image from file (webp in this case).
+ // decodeImage will identify the format of the image and use the appropriate
+ // decoder.
+ Image image = decodeImage(new File('test.webp').readAsBytesSync());
+
+ // Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
+ Image thumbnail = copyResize(image, 120);
+
+ // Save the thumbnail as a PNG.
+ new File('thumbnail.png').writeAsBytesSync(encodePng(thumbnail));
+}
diff --git a/image/example/image_isolate.dart b/image/example/image_isolate.dart
old mode 100644
new mode 100755
index 28c56a0..8070694
--- a/image/example/image_isolate.dart
+++ b/image/example/image_isolate.dart
@@ -1,33 +1,33 @@
-import 'dart:io';
-import 'dart:isolate';
-import 'package:image/image.dart';
-
-class DecodeParam {
- final File file;
- final SendPort sendPort;
- DecodeParam(this.file, this.sendPort);
-}
-
-void decode(DecodeParam param) {
- // Read an image from file (webp in this case).
- // decodeImage will identify the format of the image and use the appropriate
- // decoder.
- Image image = decodeImage(param.file.readAsBytesSync());
- // Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
- Image thumbnail = gaussianBlur(copyResize(image, 120), 5);
- param.sendPort.send(thumbnail);
-}
-
-// Decode and process an image file in a separate thread (isolate) to avoid
-// stalling the main UI thread.
-void main() async {
- ReceivePort receivePort = new ReceivePort();
-
- await Isolate.spawn(decode,
- new DecodeParam(new File('test.webp'), receivePort.sendPort));
-
- // Get the processed image from the isolate.
- Image image = await receivePort.first;
-
- new File('thumbnail.png').writeAsBytesSync(encodePng(image));
-}
+import 'dart:io';
+import 'dart:isolate';
+import 'package:image/image.dart';
+
+class DecodeParam {
+ final File file;
+ final SendPort sendPort;
+ DecodeParam(this.file, this.sendPort);
+}
+
+void decode(DecodeParam param) {
+ // Read an image from file (webp in this case).
+ // decodeImage will identify the format of the image and use the appropriate
+ // decoder.
+ Image image = decodeImage(param.file.readAsBytesSync());
+ // Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
+ Image thumbnail = gaussianBlur(copyResize(image, 120), 5);
+ param.sendPort.send(thumbnail);
+}
+
+// Decode and process an image file in a separate thread (isolate) to avoid
+// stalling the main UI thread.
+void main() async {
+ ReceivePort receivePort = ReceivePort();
+
+ await Isolate.spawn(decode,
+ new DecodeParam(new File('test.webp'), receivePort.sendPort));
+
+ // Get the processed image from the isolate.
+ Image image = await receivePort.first;
+
+ new File('thumbnail.png').writeAsBytesSync(encodePng(image));
+}
diff --git a/image/example/test.webp b/image/example/test.webp
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/lib/image.dart b/image/lib/image.dart
old mode 100644
new mode 100755
index 9303773..b8ccc4f
--- a/image/lib/image.dart
+++ b/image/lib/image.dart
@@ -13,6 +13,7 @@
export 'src/draw/draw_string.dart';
export 'src/draw/fill.dart';
export 'src/draw/fill_rect.dart';
+export 'src/draw/fill_flood.dart';
export 'src/effects/drop_shadow.dart';
@@ -138,6 +139,7 @@
export 'src/transform/bake_orientation.dart';
export 'src/transform/copy_crop.dart';
export 'src/transform/copy_into.dart';
+export 'src/transform/copy_rectify.dart';
export 'src/transform/copy_resize.dart';
export 'src/transform/copy_rotate.dart';
export 'src/transform/flip.dart';
@@ -149,6 +151,7 @@
export 'src/util/min_max.dart';
export 'src/util/neural_quantizer.dart';
export 'src/util/output_buffer.dart';
+export 'src/util/point.dart';
export 'src/util/random.dart';
export 'src/animation.dart';
diff --git a/image/lib/src/animation.dart b/image/lib/src/animation.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/bitmap_font.dart b/image/lib/src/bitmap_font.dart
old mode 100644
new mode 100755
index 213f480..4360e83
--- a/image/lib/src/bitmap_font.dart
+++ b/image/lib/src/bitmap_font.dart
@@ -75,7 +75,7 @@
* .fnt font definition and associated PNG images.
*/
BitmapFont.fromZip(List<int> fileData) {
- Archive arc = new ZipDecoder().decodeBytes(fileData);
+ Archive arc = ZipDecoder().decodeBytes(fileData);
ArchiveFile font_file;
for (int i = 0; i < arc.numberOfFiles(); ++i) {
@@ -89,7 +89,7 @@
throw new ImageException('Invalid font archive');
}
- String font_str = new String.fromCharCodes(font_file.content);
+ String font_str = String.fromCharCodes(font_file.content);
XML.XmlDocument xml;
if (font_str.startsWith('<font>')) {
@@ -222,7 +222,7 @@
'$filename');
}
- Image image = new PngDecoder().decodeImage(imageFile.content);
+ Image image = PngDecoder().decodeImage(imageFile.content);
fontPages[id] = image;
}
@@ -262,7 +262,7 @@
Image fontImage = fontPages[page];
- BitmapFontCharacter ch = new BitmapFontCharacter(id, width, height,
+ BitmapFontCharacter ch = BitmapFontCharacter(id, width, height,
xoffset, yoffset, xadvance, page, chnl);
characters[id] = ch;
@@ -300,17 +300,17 @@
switch (tk[0]) {
case 'info':
var attrs = _parseParameters(tk);
- var info = new XML.XmlElement(new XML.XmlName('info'), attrs, []);
+ var info = XML.XmlElement(new XML.XmlName('info'), attrs, []);
children.add(info);
break;
case 'common':
var attrs = _parseParameters(tk);
- var node = new XML.XmlElement(new XML.XmlName('common'), attrs, []);
+ var node = XML.XmlElement(new XML.XmlName('common'), attrs, []);
children.add(node);
break;
case 'page':
var attrs = _parseParameters(tk);
- var page = new XML.XmlElement(new XML.XmlName('page'), attrs, []);
+ var page = XML.XmlElement(new XML.XmlName('page'), attrs, []);
pageList.add(page);
break;
case 'chars':
@@ -318,7 +318,7 @@
break;
case 'char':
var attrs = _parseParameters(tk);
- var node = new XML.XmlElement(new XML.XmlName('char'), attrs, []);
+ var node = XML.XmlElement(new XML.XmlName('char'), attrs, []);
charList.add(node);
break;
case 'kernings':
@@ -326,31 +326,31 @@
break;
case 'kerning':
var attrs = _parseParameters(tk);
- var node = new XML.XmlElement(new XML.XmlName('kerning'), attrs, []);
+ var node = XML.XmlElement(new XML.XmlName('kerning'), attrs, []);
kerningList.add(node);
break;
}
}
if (charsAttrs != null || charList.isNotEmpty) {
- var node = new XML.XmlElement(new XML.XmlName('chars'), charsAttrs,
+ var node = XML.XmlElement(new XML.XmlName('chars'), charsAttrs,
charList);
children.add(node);
}
if (kerningsAttrs != null || kerningList.isNotEmpty) {
- var node = new XML.XmlElement(new XML.XmlName('kernings'), kerningsAttrs,
+ var node = XML.XmlElement(new XML.XmlName('kernings'), kerningsAttrs,
kerningList);
children.add(node);
}
if (pageList.isNotEmpty) {
- var pages = new XML.XmlElement(new XML.XmlName('pages'), [], pageList);
+ var pages = XML.XmlElement(new XML.XmlName('pages'), [], pageList);
children.add(pages);
}
- var xml = new XML.XmlElement(new XML.XmlName('font'), [], children);
- var doc = new XML.XmlDocument([xml]);
+ var xml = XML.XmlElement(new XML.XmlName('font'), [], children);
+ var doc = XML.XmlDocument([xml]);
return doc;
}
@@ -369,7 +369,7 @@
// Remove all " characters
atk[1] = atk[1].replaceAll('"', '');
- var a = new XML.XmlAttribute(new XML.XmlName(atk[0]), atk[1]);
+ var a = XML.XmlAttribute(new XML.XmlName(atk[0]), atk[1]);
params.add(a);
}
return params;
@@ -404,7 +404,7 @@
this.channel) :
this.width = width,
this.height = height,
- image = new Image(width, height);
+ image = Image(width, height);
String toString() {
Map x = {'id': id, 'width': width, 'height': height, 'xoffset': xoffset,
diff --git a/image/lib/src/color.dart b/image/lib/src/color.dart
old mode 100644
new mode 100755
index 59ea428..233daa1
--- a/image/lib/src/color.dart
+++ b/image/lib/src/color.dart
@@ -60,15 +60,37 @@
var rgb = labToRGB(L, a, b);
return getColor(rgb[0], rgb[1], rgb[2]);
}
+
+ /**
+ * Compare colors from a 3 or 4 dimensional color space
+ */
+ static double distance(List<double> c1, List<double> c2, bool compareAlpha) {
+ double d1 = c1[0] - c2[0];
+ double d2 = c1[1] - c2[1];
+ double d3 = c1[2] - c2[2];
+ if (compareAlpha) {
+ double dA = c1[3] - c2[3];
+ return Math.sqrt(
+ Math.max(d1*d1, (d1-dA)*(d1-dA)) +
+ Math.max(d2*d2, (d2-dA)*(d2-dA)) +
+ Math.max(d3*d3, (d3-dA)*(d3-dA))
+ );
+ } else {
+ return Math.sqrt(
+ d1*d1 +
+ d2*d2 +
+ d3*d3);
+ }
+ }
}
-/// Red channel of a color.
-const int RED = 0;
+/// Blue channel of a color.
+const int BLUE = 0;
/// Green channel of a color.
const int GREEN = 1;
-/// Blue channel of a color.
-const int BLUE = 2;
+/// Red channel of a color.
+const int RED = 2;
/// Alpha channel of a color.
const int ALPHA = 3;
/// Luminance of a color.
@@ -82,17 +104,17 @@
*/
int getColor(int r, int g, int b, [int a = 255]) =>
(clamp255(a) << 24) |
- (clamp255(b) << 16) |
+ (clamp255(r) << 16) |
(clamp255(g) << 8) |
- clamp255(r);
+ (clamp255(b));
/**
* Get the [channel] from the [color].
*/
int getChannel(int color, int channel) =>
- channel == 0 ? getRed(color) :
- channel == 1 ? getGreen(color) :
- channel == 2 ? getBlue(color) :
+ channel == RED ? getRed(color) :
+ channel == GREEN ? getGreen(color) :
+ channel == BLUE ? getBlue(color) :
getAlpha(color);
/**
@@ -100,22 +122,22 @@
* replaced with the given [value].
*/
int setChannel(int color, int channel, int value) =>
- channel == 0 ? setRed(color, value) :
- channel == 1 ? setGreen(color, value) :
- channel == 2 ? setBlue(color, value) :
+ channel == RED ? setRed(color, value) :
+ channel == GREEN ? setGreen(color, value) :
+ channel == BLUE ? setBlue(color, value) :
setAlpha(color, value);
/**
- * Get the red channel from the [color].
+ * Get the blue channel from the [color].
*/
-int getRed(int color) =>
+int getBlue(int color) =>
(color) & 0xff;
/**
- * Returns a new color where the red channel of [color] has been replaced
+ * Returns a new color where the blue channel of [color] has been replaced
* by [value].
*/
-int setRed(int color, int value) =>
+int setBlue(int color, int value) =>
(color & 0xffffff00) | (clamp255(value));
/**
@@ -132,16 +154,16 @@
(color & 0xffff00ff) | (clamp255(value) << 8);
/**
- * Get the blue channel from the [color].
+ * Get the red channel from the [color].
*/
-int getBlue(int color) =>
+int getRed(int color) =>
(color >> 16) & 0xff;
/**
- * Returns a new color where the blue channel of [color] has been replaced
+ * Returns a new color where the red channel of [color] has been replaced
* by [value].
*/
-int setBlue(int color, int value) =>
+int setRed(int color, int value) =>
(color & 0xff00ffff) | (clamp255(value) << 16);
/**
@@ -460,3 +482,86 @@
(G * 255.0).toInt().clamp(0, 255),
(B * 255.0).toInt().clamp(0, 255)];
}
+
+/**
+ * Convert a RGB color to XYZ.
+ */
+List<double> rgbToXYZ(num r, num g, num b) {
+ r = r / 255.0;
+ g = g / 255.0;
+ b = b / 255.0;
+
+ if ( r > 0.04045 ) r = Math.pow((r + 0.055) / 1.055, 2.4);
+ else r = r / 12.92;
+ if ( g > 0.04045 ) g = Math.pow((g + 0.055) / 1.055, 2.4);
+ else g = g / 12.92;
+ if ( b > 0.04045 ) b = Math.pow((b + 0.055) / 1.055, 2.4);
+ else b = b / 12.92;
+
+ r = r * 100.0;
+ g = g * 100.0;
+ b = b * 100.0;
+
+ return [r * 0.4124 + g * 0.3576 + b * 0.1805,
+ r * 0.2126 + g * 0.7152 + b * 0.0722,
+ r * 0.0193 + g * 0.1192 + b * 0.9505];
+}
+
+/**
+ * Convert a XYZ color to CIE-L*ab.
+ */
+List<double> xyzToLab(num x, num y, num z) {
+ x = x / 95.047;
+ y = y / 100.0;
+ z = z / 108.883;
+
+ if (x > 0.008856) x = Math.pow(x, 1/3.0);
+ else x = (7.787 * x) + (16 / 116.0);
+ if (y > 0.008856) y = Math.pow(y, 1/3.0);
+ else y = (7.787 * y) + (16 / 116.0);
+ if (z > 0.008856) z = Math.pow(z, 1/3.0);
+ else z = (7.787 * z) + (16 / 116.0);
+
+ return [(116.0 * y) - 16,
+ 500.0 * (x - y),
+ 200.0 * (y - z)];
+}
+
+/**
+ * Convert a RGB color to CIE-L*ab.
+ */
+List<double> rgbToLab(num r, num g, num b) {
+ r = r / 255.0;
+ g = g / 255.0;
+ b = b / 255.0;
+
+ if ( r > 0.04045 ) r = Math.pow((r + 0.055) / 1.055, 2.4);
+ else r = r / 12.92;
+ if ( g > 0.04045 ) g = Math.pow((g + 0.055) / 1.055, 2.4);
+ else g = g / 12.92;
+ if ( b > 0.04045 ) b = Math.pow((b + 0.055) / 1.055, 2.4);
+ else b = b / 12.92;
+
+ r = r * 100.0;
+ g = g * 100.0;
+ b = b * 100.0;
+
+ double x = r * 0.4124 + g * 0.3576 + b * 0.1805;
+ double y = r * 0.2126 + g * 0.7152 + b * 0.0722;
+ double z = r * 0.0193 + g * 0.1192 + b * 0.9505;
+
+ x = x / 95.047;
+ y = y / 100.0;
+ z = z / 108.883;
+
+ if (x > 0.008856) x = Math.pow(x, 1/3.0);
+ else x = (7.787 * x) + (16 / 116.0);
+ if (y > 0.008856) y = Math.pow(y, 1/3.0);
+ else y = (7.787 * y) + (16 / 116.0);
+ if (z > 0.008856) z = Math.pow(z, 1/3.0);
+ else z = (7.787 * z) + (16 / 116.0);
+
+ return [(116.0 * y) - 16,
+ 500.0 * (x - y),
+ 200.0 * (y - z)];
+}
diff --git a/image/lib/src/draw/draw_char.dart b/image/lib/src/draw/draw_char.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/draw/draw_circle.dart b/image/lib/src/draw/draw_circle.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/draw/draw_image.dart b/image/lib/src/draw/draw_image.dart
old mode 100644
new mode 100755
index 1229ba5..5a07d2f
--- a/image/lib/src/draw/draw_image.dart
+++ b/image/lib/src/draw/draw_image.dart
@@ -1,51 +1,51 @@
-import '../image.dart';
-import 'draw_pixel.dart';
-
-/**
- * Draw the image [src] onto the image [dst].
- *
- * In other words, copyInto will take an rectangular area from src of
- * width [src_w] and height [src_h] at position ([src_x],[src_y]) and place it
- * in a rectangular area of [dst] of width [dst_w] and height [dst_h] at
- * position ([dst_x],[dst_y]).
- *
- * If the source and destination coordinates and width and heights differ,
- * appropriate stretching or shrinking of the image fragment will be performed.
- * The coordinates refer to the upper left corner. This function can be used to
- * copy regions within the same image (if [dst] is the same as [src])
- * but if the regions overlap the results will be unpredictable.
- */
-Image drawImage(Image dst, Image src,
- {int dstX, int dstY, int srcX, int srcY,
- int srcW, int srcH, bool blend: true}) {
- if (dstX == null) {
- dstX = 0;
- }
- if (dstY == null) {
- dstY = 0;
- }
- if (srcX == null) {
- srcX = 0;
- }
- if (srcY == null) {
- srcY = 0;
- }
- if (srcW == null) {
- srcW = src.width;
- }
- if (srcH == null) {
- srcH = src.height;
- }
-
- for (int y = 0; y < srcH; ++y) {
- for (int x = 0; x < srcW; ++x) {
- if (blend) {
- drawPixel(dst, dstX + x, dstY + y, src.getPixel(srcX + x, srcY + y));
- } else {
- dst.setPixel(dstX + x, dstY + y, src.getPixel(srcX + x, srcY + y));
- }
- }
- }
-
- return dst;
-}
+import '../image.dart';
+import 'draw_pixel.dart';
+
+/**
+ * Draw the image [src] onto the image [dst].
+ *
+ * In other words, copyInto will take an rectangular area from src of
+ * width [src_w] and height [src_h] at position ([src_x],[src_y]) and place it
+ * in a rectangular area of [dst] of width [dst_w] and height [dst_h] at
+ * position ([dst_x],[dst_y]).
+ *
+ * If the source and destination coordinates and width and heights differ,
+ * appropriate stretching or shrinking of the image fragment will be performed.
+ * The coordinates refer to the upper left corner. This function can be used to
+ * copy regions within the same image (if [dst] is the same as [src])
+ * but if the regions overlap the results will be unpredictable.
+ */
+Image drawImage(Image dst, Image src,
+ {int dstX, int dstY, int srcX, int srcY,
+ int srcW, int srcH, bool blend: true}) {
+ if (dstX == null) {
+ dstX = 0;
+ }
+ if (dstY == null) {
+ dstY = 0;
+ }
+ if (srcX == null) {
+ srcX = 0;
+ }
+ if (srcY == null) {
+ srcY = 0;
+ }
+ if (srcW == null) {
+ srcW = src.width;
+ }
+ if (srcH == null) {
+ srcH = src.height;
+ }
+
+ for (int y = 0; y < srcH; ++y) {
+ for (int x = 0; x < srcW; ++x) {
+ if (blend) {
+ drawPixel(dst, dstX + x, dstY + y, src.getPixel(srcX + x, srcY + y));
+ } else {
+ dst.setPixel(dstX + x, dstY + y, src.getPixel(srcX + x, srcY + y));
+ }
+ }
+ }
+
+ return dst;
+}
diff --git a/image/lib/src/draw/draw_line.dart b/image/lib/src/draw/draw_line.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/draw/draw_pixel.dart b/image/lib/src/draw/draw_pixel.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/draw/draw_rect.dart b/image/lib/src/draw/draw_rect.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/draw/draw_string.dart b/image/lib/src/draw/draw_string.dart
old mode 100644
new mode 100755
index 3440cf6..6604f25
--- a/image/lib/src/draw/draw_string.dart
+++ b/image/lib/src/draw/draw_string.dart
@@ -4,10 +4,10 @@
import '../image.dart';
import 'draw_pixel.dart';
-var _r_lut = new Uint8List(256);
-var _g_lut = new Uint8List(256);
-var _b_lut = new Uint8List(256);
-var _a_lut = new Uint8List(256);
+var _r_lut = Uint8List(256);
+var _g_lut = Uint8List(256);
+var _b_lut = Uint8List(256);
+var _a_lut = Uint8List(256);
/**
* Draw a string horizontally into [image] horizontally into [image] at position
diff --git a/image/lib/src/draw/fill.dart b/image/lib/src/draw/fill.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/draw/fill_flood.dart b/image/lib/src/draw/fill_flood.dart
new file mode 100755
index 0000000..a613a02
--- /dev/null
+++ b/image/lib/src/draw/fill_flood.dart
@@ -0,0 +1,143 @@
+import 'dart:math' as Math;
+import 'dart:typed_data';
+
+import '../color.dart';
+import '../image.dart';
+
+typedef TestPixel = bool Function(int y, int x);
+typedef MarkPixel = void Function(int y, int x);
+
+/**
+ * Fill the 4-connected shape containing [x],[y] in the image [src] with the given [color]
+ */
+
+Image fillFlood(Image src, int x, int y, int color, {num threshold=0.0, bool compareAlpha=false}) {
+ int srcColor = src.getPixel(x, y);
+ if (!compareAlpha) srcColor = setAlpha(srcColor, 0);
+
+ TestPixel array;
+ if (threshold > 0) {
+ List<double> lab = rgbToLab(getRed(srcColor), getGreen(srcColor), getBlue(srcColor));
+ if (compareAlpha) lab.add(getAlpha(srcColor).toDouble());
+ array = (int y, int x) => _testPixelLabColorDistance(src, x, y, lab, threshold);
+ } else if (!compareAlpha) {
+ array = (int y, int x) => setAlpha(src.getPixel(x, y), 0) != srcColor;
+ } else {
+ array = (int y, int x) => src.getPixel(x, y) != srcColor;
+ }
+
+ MarkPixel mark = (int y, int x) => src.setPixel(x, y, color);
+ _fill4(src, x, y, array, mark);
+ return src;
+}
+
+/**
+ * Create a mask describing the 4-connected shape containing [x],[y] in the image [src]
+ */
+
+Uint8List maskFlood(Image src, int x, int y, {num threshold=0.0, bool compareAlpha=false, int fillValue=255}) {
+ int srcColor = src.getPixel(x, y);
+ if (!compareAlpha) srcColor = setAlpha(srcColor, 0);
+ Uint8List ret = Uint8List(src.width * src.height);
+
+ TestPixel array;
+ if (threshold > 0) {
+ List<double> lab = rgbToLab(getRed(srcColor), getGreen(srcColor), getBlue(srcColor));
+ if (compareAlpha) lab.add(getAlpha(srcColor).toDouble());
+ array = (int y, int x) => ret[y * src.width + x] != 0 || _testPixelLabColorDistance(src, x, y, lab, threshold);
+ } else if (!compareAlpha) {
+ array = (int y, int x) => ret[y * src.width + x] != 0 || setAlpha(src.getPixel(x, y), 0) != srcColor;
+ } else {
+ array = (int y, int x) => ret[y * src.width + x] != 0 || src.getPixel(x, y) != srcColor;
+ }
+
+ MarkPixel mark = (int y, int x) => ret[y * src.width + x] = fillValue;
+ _fill4(src, x, y, array, mark);
+ return ret;
+}
+
+bool _testPixelLabColorDistance(Image src, int x, int y, List<double> refColor, num threshold) {
+ int pixel = src.getPixel(x, y);
+ bool compareAlpha = refColor.length > 3;
+ List<double> pixelColor = rgbToLab(getRed(pixel), getGreen(pixel), getBlue(pixel));
+ if (compareAlpha) pixelColor.add(getAlpha(pixel).toDouble());
+ return Color.distance(pixelColor, refColor, compareAlpha) > threshold;
+}
+
+/**
+ * Adam Milazzo (2015). A More Efficient Flood Fill.
+ * http://www.adammil.net/blog/v126_A_More_Efficient_Flood_Fill.html
+ */
+
+void _fill4(Image src, int x, int y, TestPixel array, MarkPixel mark) {
+
+ // at this point, we know array(y,x) is clear, and we want to move as far as possible to the upper-left. moving
+ // up is much more important than moving left, so we could try to make this smarter by sometimes moving to
+ // the right if doing so would allow us to move further up, but it doesn't seem worth the complexity
+ while(true)
+ {
+ int ox = x, oy = y;
+ while (y != 0 && !array(y-1, x)) y--;
+ while (x != 0 && !array(y, x-1)) x--;
+ if (x == ox && y == oy) break;
+ }
+ _fill4Core(src, x, y, array, mark);
+}
+
+void _fill4Core(Image src, int x, int y, TestPixel array, MarkPixel mark) {
+ // at this point, we know that array(y,x) is clear, and array(y-1,x) and array(y,x-1) are set.
+ // we'll begin scanning down and to the right, attempting to fill an entire rectangular block
+ int lastRowLength = 0; // the number of cells that were clear in the last row we scanned
+
+ do {
+ int rowLength = 0, sx = x; // keep track of how long this row is. sx is the starting x for the main scan below
+ // now we want to handle a case like |***|, where we fill 3 cells in the first row and then after we move to
+ // the second row we find the first | **| cell is filled, ending our rectangular scan. rather than handling
+ // this via the recursion below, we'll increase the starting value of 'x' and reduce the last row length to
+ // match. then we'll continue trying to set the narrower rectangular block
+ if (lastRowLength != 0 && array(y, x)) { // if this is not the first row and the leftmost cell is filled...
+ do {
+ if (--lastRowLength == 0) return; // shorten the row. if it's full, we're done
+ } while (array(y, ++x)); // otherwise, update the starting point of the main scan to match
+ sx = x;
+ } else {
+ // we also want to handle the opposite case, | **|, where we begin scanning a 2-wide rectangular block and
+ // then find on the next row that it has |***| gotten wider on the left. again, we could handle this
+ // with recursion but we'd prefer to adjust x and lastRowLength instead
+ for (; x != 0 && !array(y, x-1); rowLength++, lastRowLength++) {
+ mark(y, --x); // to avoid scanning the cells twice, we'll fill them and update rowLength here
+ // if there's something above the new starting point, handle that recursively. this deals with cases
+ // like |* **| when we begin filling from (2,0), move down to (2,1), and then move left to (0,1).
+ // the |****| main scan assumes the portion of the previous row from x to x+lastRowLength has already
+ // been filled. adjusting x and lastRowLength breaks that assumption in this case, so we must fix it
+ if (y != 0 && !array(y-1, x)) _fill4(src, x, y-1, array, mark); // use _Fill since there may be more up and left
+ }
+ }
+
+ // now at this point we can begin to scan the current row in the rectangular block. the span of the previous
+ // row from x (inclusive) to x+lastRowLength (exclusive) has already been filled, so we don't need to
+ // check it. so scan across to the right in the current row
+ for (; sx < src.width && !array(y, sx); rowLength++, sx++) mark(y, sx);
+ // now we've scanned this row. if the block is rectangular, then the previous row has already been scanned,
+ // so we don't need to look upwards and we're going to scan the next row in the next iteration so we don't
+ // need to look downwards. however, if the block is not rectangular, we may need to look upwards or rightwards
+ // for some portion of the row. if this row was shorter than the last row, we may need to look rightwards near
+ // the end, as in the case of |*****|, where the first row is 5 cells long and the second row is 3 cells long.
+ // we must look to the right |*** *| of the single cell at the end of the second row, i.e. at (4,1)
+ if (rowLength < lastRowLength) {
+ for (int end=x+lastRowLength; ++sx < end; ) // 'end' is the end of the previous row, so scan the current row to
+ { // there. any clear cells would have been connected to the previous
+ if (!array(y, sx)) _fill4Core(src, sx, y, array, mark); // row. the cells up and left must be set so use FillCore
+ }
+ }
+ // alternately, if this row is longer than the previous row, as in the case |*** *| then we must look above
+ // the end of the row, i.e at (4,0) |*****|
+ else if (rowLength > lastRowLength && y != 0) // if this row is longer and we're not already at the top...
+ {
+ for (int ux=x+lastRowLength; ++ux<sx; ) { // sx is the end of the current row
+ if (!array(y-1, ux)) _fill4(src, ux, y-1, array, mark); // since there may be clear cells up and left, use _Fill
+ }
+ }
+ lastRowLength = rowLength; // record the new row length
+ } while (lastRowLength != 0 && ++y < src.height); // if we get to a full row or to the bottom, we're done
+}
diff --git a/image/lib/src/draw/fill_rect.dart b/image/lib/src/draw/fill_rect.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/effects/drop_shadow.dart b/image/lib/src/effects/drop_shadow.dart
old mode 100644
new mode 100755
index fe69f50..f86b0d7
--- a/image/lib/src/effects/drop_shadow.dart
+++ b/image/lib/src/effects/drop_shadow.dart
@@ -44,7 +44,7 @@
newImageHeight = shadowHeight + shadowOffsetY + vShadow;
}
- Image dst = new Image(newImageWidth, newImageHeight);
+ Image dst = Image(newImageWidth, newImageHeight);
dst.fill(0x00ffffff);
copyInto(dst, src, dstX: shadowOffsetX, dstY: shadowOffsetY);
diff --git a/image/lib/src/exif_data.dart b/image/lib/src/exif_data.dart
old mode 100644
new mode 100755
index 78f5e56..1b8cc10
--- a/image/lib/src/exif_data.dart
+++ b/image/lib/src/exif_data.dart
@@ -1,35 +1,35 @@
-import 'dart:typed_data';
-
-/**
- * Exif data stored with an image.
- */
-class ExifData {
- static const int CAMERA_MAKE = 0x010F; // string
- static const int CAMERA_MODEL = 0x0110; // string
- static const int DATE_TIME = 0x0132; // string
- static const int ORIENTATION = 0x0112; // int
-
- List<Uint8List> rawData;
- Map<int, dynamic> data;
-
- ExifData()
- : data = new Map<int, dynamic>();
-
- ExifData.from(ExifData other)
- : data = (other == null) ?
- new Map<int, dynamic>() :
- new Map<int, dynamic>.from(other.data) {
- if (other != null && other.rawData != null) {
- rawData = new List<Uint8List>(other.rawData.length);
- for (int i = 0; i < other.rawData.length; ++i) {
- rawData[i] = new Uint8List.fromList(other.rawData[i]);
- }
- }
- }
-
- bool get hasRawData => rawData != null && rawData.isNotEmpty;
-
- bool get hasOrientation => data.containsKey(ORIENTATION);
- int get orientation => data[ORIENTATION];
- set orientation(int value) => data[ORIENTATION] = value;
-}
+import 'dart:typed_data';
+
+/**
+ * Exif data stored with an image.
+ */
+class ExifData {
+ static const int CAMERA_MAKE = 0x010F; // string
+ static const int CAMERA_MODEL = 0x0110; // string
+ static const int DATE_TIME = 0x0132; // string
+ static const int ORIENTATION = 0x0112; // int
+
+ List<Uint8List> rawData;
+ Map<int, dynamic> data;
+
+ ExifData()
+ : data = Map<int, dynamic>();
+
+ ExifData.from(ExifData other)
+ : data = (other == null) ?
+ new Map<int, dynamic>() :
+ new Map<int, dynamic>.from(other.data) {
+ if (other != null && other.rawData != null) {
+ rawData = List<Uint8List>(other.rawData.length);
+ for (int i = 0; i < other.rawData.length; ++i) {
+ rawData[i] = Uint8List.fromList(other.rawData[i]);
+ }
+ }
+ }
+
+ bool get hasRawData => rawData != null && rawData.isNotEmpty;
+
+ bool get hasOrientation => data.containsKey(ORIENTATION);
+ int get orientation => data[ORIENTATION];
+ set orientation(int value) => data[ORIENTATION] = value;
+}
diff --git a/image/lib/src/filter/adjust_color.dart b/image/lib/src/filter/adjust_color.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/brightness.dart b/image/lib/src/filter/brightness.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/bump_to_normal.dart b/image/lib/src/filter/bump_to_normal.dart
old mode 100644
new mode 100755
index da81d12..41c5dcb
--- a/image/lib/src/filter/bump_to_normal.dart
+++ b/image/lib/src/filter/bump_to_normal.dart
@@ -11,7 +11,7 @@
* the strength of the normal image.
*/
Image bumpToNormal(Image src, {double strength: 2.0}) {
- Image dest = new Image.from(src);
+ Image dest = Image.from(src);
for (var y = 0; y < src.height; ++y) {
for (var x = 0; x < src.width; ++x) {
diff --git a/image/lib/src/filter/color_offset.dart b/image/lib/src/filter/color_offset.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/contrast.dart b/image/lib/src/filter/contrast.dart
old mode 100644
new mode 100755
index 311bc4c..3188b1e
--- a/image/lib/src/filter/contrast.dart
+++ b/image/lib/src/filter/contrast.dart
@@ -23,7 +23,7 @@
contrast = contrast / 100.0;
contrast = contrast * contrast;
- _contrast = new Uint8List(256);
+ _contrast = Uint8List(256);
for (int i = 0; i < 256; ++i) {
_contrast[i] =
clamp255((((((i / 255.0) - 0.5) * contrast) + 0.5) * 255.0).toInt());
diff --git a/image/lib/src/filter/convolution.dart b/image/lib/src/filter/convolution.dart
old mode 100644
new mode 100755
index bcb7802..4008e7f
--- a/image/lib/src/filter/convolution.dart
+++ b/image/lib/src/filter/convolution.dart
@@ -12,7 +12,7 @@
*/
Image convolution(Image src, List<num> filter,
[num filterDiv = 1.0, num offset = 0.0]) {
- Image tmp = new Image.from(src);
+ Image tmp = Image.from(src);
for (int y = 0; y < src.height; ++y) {
for (int x = 0; x < src.width; ++x) {
diff --git a/image/lib/src/filter/emboss.dart b/image/lib/src/filter/emboss.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/gaussian_blur.dart b/image/lib/src/filter/gaussian_blur.dart
old mode 100644
new mode 100755
index 0679e24..5bc657d
--- a/image/lib/src/filter/gaussian_blur.dart
+++ b/image/lib/src/filter/gaussian_blur.dart
@@ -25,7 +25,7 @@
double sigma = radius * (2.0 / 3.0);
double s = 2.0 * sigma * sigma;
- kernel = new SeperableKernel(radius);
+ kernel = SeperableKernel(radius);
double sum = 0.0;
for (int x = -radius; x <= radius; ++x) {
diff --git a/image/lib/src/filter/grayscale.dart b/image/lib/src/filter/grayscale.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/invert.dart b/image/lib/src/filter/invert.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/noise.dart b/image/lib/src/filter/noise.dart
old mode 100644
new mode 100755
index e5cca94..dd4d8ef
--- a/image/lib/src/filter/noise.dart
+++ b/image/lib/src/filter/noise.dart
@@ -24,7 +24,7 @@
Image noise(Image image, double sigma, {int type: NOISE_GAUSSIAN,
Math.Random random}) {
if (random == null) {
- random = new Math.Random();
+ random = Math.Random();
}
double nsigma = sigma;
diff --git a/image/lib/src/filter/normalize.dart b/image/lib/src/filter/normalize.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/pixelate.dart b/image/lib/src/filter/pixelate.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/quantize.dart b/image/lib/src/filter/quantize.dart
old mode 100644
new mode 100755
index a92645d..39278bc
--- a/image/lib/src/filter/quantize.dart
+++ b/image/lib/src/filter/quantize.dart
@@ -1,11 +1,27 @@
+import 'package:image/src/util/octree_quantizer.dart';
+
import '../image.dart';
import '../util/neural_quantizer.dart';
+enum QuantizeMethod {
+ neuralNet,
+ octree
+}
+
/**
* Quantize the number of colors in image to 256.
*/
-Image quantize(Image src) {
- NeuralQuantizer quant = new NeuralQuantizer(src);
+Image quantize(Image src, {int numberOfColors=256,
+ QuantizeMethod method=QuantizeMethod.neuralNet}) {
+ if (method == QuantizeMethod.octree || numberOfColors < 4) {
+ OctreeQuantizer oct = OctreeQuantizer(src, numberOfColors: numberOfColors);
+ for (int i = 0, len = src.length; i < len; ++i) {
+ src[i] = oct.getQuantizedColor(src[i]);
+ }
+ return src;
+ }
+
+ NeuralQuantizer quant = NeuralQuantizer(src, numberOfColors: numberOfColors);
for (int i = 0, len = src.length; i < len; ++i) {
src[i] = quant.getQuantizedColor(src[i]);
}
diff --git a/image/lib/src/filter/remap_colors.dart b/image/lib/src/filter/remap_colors.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/scale_rgba.dart b/image/lib/src/filter/scale_rgba.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/seperable_convolution.dart b/image/lib/src/filter/seperable_convolution.dart
old mode 100644
new mode 100755
index 1099b45..6ff5a5a
--- a/image/lib/src/filter/seperable_convolution.dart
+++ b/image/lib/src/filter/seperable_convolution.dart
@@ -9,7 +9,7 @@
*/
Image seperableConvolution(Image src, SeperableKernel kernel) {
// Apply the filter horizontally
- Image tmp = new Image.from(src);
+ Image tmp = Image.from(src);
kernel.apply(src, tmp, horizontal: true);
// Apply the filter vertically, applying back to the original image.
diff --git a/image/lib/src/filter/seperable_kernel.dart b/image/lib/src/filter/seperable_kernel.dart
old mode 100644
new mode 100755
index d254c22..db0b39e
--- a/image/lib/src/filter/seperable_kernel.dart
+++ b/image/lib/src/filter/seperable_kernel.dart
@@ -12,7 +12,7 @@
* Create a seperable convolution kernel for the given [radius].
*/
SeperableKernel(int radius) :
- coefficients = new List<double>(2 * radius + 1),
+ coefficients = List<double>(2 * radius + 1),
this.size = radius;
/**
diff --git a/image/lib/src/filter/sepia.dart b/image/lib/src/filter/sepia.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/smooth.dart b/image/lib/src/filter/smooth.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/sobel.dart b/image/lib/src/filter/sobel.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/filter/vignette.dart b/image/lib/src/filter/vignette.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/fonts/arial_14.dart b/image/lib/src/fonts/arial_14.dart
old mode 100644
new mode 100755
index 2437404..288ee3c
--- a/image/lib/src/fonts/arial_14.dart
+++ b/image/lib/src/fonts/arial_14.dart
@@ -3,7 +3,7 @@
/**
* 14px Arial font for use with [drawString] and [drawChar].
*/
-final BitmapFont arial_14 = new BitmapFont.fromZip(_ARIAL_14);
+final BitmapFont arial_14 = BitmapFont.fromZip(_ARIAL_14);
const List<int> _ARIAL_14 = const [
80, 75, 3, 4, 20, 0, 0, 8, 8, 0, 184, 182, 42, 68, 36, 55, 216, 103, 135, 15, 0, 0, 133, 15, 0, 0, 12, 0, 0, 0, 97, 114, 105, 97, 108, 95, 49, 52, 46, 112,
diff --git a/image/lib/src/fonts/arial_24.dart b/image/lib/src/fonts/arial_24.dart
old mode 100644
new mode 100755
index 338adb0..b838e04
--- a/image/lib/src/fonts/arial_24.dart
+++ b/image/lib/src/fonts/arial_24.dart
@@ -3,7 +3,7 @@
/**
* 24px Arial font for use with [drawString] and [drawChar].
*/
-final BitmapFont arial_24 = new BitmapFont.fromZip(_ARIAL_24);
+final BitmapFont arial_24 = BitmapFont.fromZip(_ARIAL_24);
const List<int> _ARIAL_24 = const [
80, 75, 3, 4, 20, 0, 0, 8, 8, 0, 131, 182, 42, 68, 22, 124, 124, 222, 0, 29, 0, 0, 62, 29, 0, 0, 12, 0, 0, 0, 97, 114, 105, 97, 108, 95, 50, 52, 46, 112, 110, 103, 141, 121, 103, 80, 19, 80, 179, 118, 18, 66, 143, 180, 208, 155, 128, 64, 164, 67, 232, 16, 122, 239, 85, 5, 68, 64, 122, 83, 58, 132, 158, 0, 130, 244, 142, 160, 16, 170, 2, 138, 72, 87, 164, 5, 165, 119, 4, 233, 66, 144, 222, 36, 116, 233, 215, 119, 190, 31, 223, 251, 235, 206, 61, 179, 115, 102, 127, 236, 236, 60, 115, 102, 207, 238, 179, 243, 196, 27, 27, 106, 221, 33, 99, 38, 3, 0, 0, 119, 116, 180, 213, 77, 1, 0, 48, 230, 159, 207, 66, 66, 244, 239, 86, 224, 212, 200, 0, 0, 216, 8, 117, 212, 85, 204, 145, 115, 123, 79, 18, 211, 75, 229, 234, 49, 16, 18, 52, 21, 109, 148, 42, 7, 248, 159, 189, 80, 38, 137, 81, 38, 129, 130, 56, 101, 192, 240, 191, 1, 163, 11, 81, 191, 2, 91, 10, 85, 79, 145, 206, 63, 31, 84, 43, 184, 120, 42, 107, 111, 79, 79, 83, 134, 135, 73, 184, 79, 86, 152, 148, 106, 38, 51, 75, 254, 239, 206, 120, 234, 219, 124, 191, 179, 37, 151, 235, 165, 219, 155, 51, 82, 165, 219, 107, 33, 177, 142, 219, 72, 254, 176, 135, 183, 75, 46, 156, 24, 73, 186, 5, 129, 136, 228, 149, 157, 144, 117, 89, 241, 144, 158, 139, 37, 23, 137, 133, 32, 141, 105, 194, 245, 217, 191, 65, 91, 30, 194, 163, 183, 29, 112, 219, 5, 201, 73, 249, 208, 253, 212, 72, 202, 222, 75, 73, 37, 100, 120, 251, 34, 233, 16, 95, 196, 6, 112, 145, 197, 230, 72, 242, 110, 151, 28, 241, 83, 202, 145, 174, 253, 254, 19, 85, 7, 195, 205, 215, 107, 30, 85, 3, 236, 172, 203, 45, 249, 163, 178, 55, 161, 206, 139, 46, 191, 123, 2, 194, 148, 76, 17, 101, 73, 60, 198, 97, 239, 62, 242, 213, 198, 126, 94, 49, 218, 189, 121, 23, 113, 239, 73, 208, 208, 10, 195, 179, 33, 214, 136, 189, 239, 44, 127, 31, 15, 68, 242, 209, 76, 231, 47, 237, 111, 96, 190, 194, 3, 143, 17, 57, 247, 207, 57, 90, 61, 114, 166, 200, 71, 149, 14, 170, 88, 44, 46, 153, 218, 203, 189, 63, 165, 167, 119, 12, 30, 60, 217, 96, 37, 88, 140, 28, 191, 100, 7, 189, 253, 230, 145, 121, 120, 67, 41, 7, 108, 85, 251, 148, 164, 224, 238, 23, 182, 16, 199, 18, 217, 208, 55, 44, 112, 166, 124, 233, 193, 199, 70, 32, 182, 159, 232, 179, 43, 237, 116, 199, 172, 255, 82, 252, 194, 205, 249, 244, 208, 24, 93, 206, 123, 47, 210, 16, 100, 68, 151, 76, 144, 186, 246, 197, 86, 34, 201, 169, 200, 191, 216, 199, 203, 118, 109, 152, 107, 17, 112, 233, 228, 179, 59, 72, 104, 75, 240, 71, 252, 254, 213, 167, 63, 27, 71, 65, 218, 27, 254, 113, 138, 181, 3, 231, 121, 227, 42, 133, 147, 75, 146, 243, 153, 191, 227, 162, 2, 18, 105, 148, 172, 130, 205, 235, 226, 244, 130, 89, 230, 37, 50, 10, 40, 214, 185, 153, 144, 2, 98, 97, 117, 198, 82, 45, 13, 188, 41, 141, 74, 103, 186, 37, 187, 125, 9, 97, 185, 159, 170, 199, 161, 199, 159, 190, 244, 38, 213, 181, 149, 31, 51, 172, 37, 230, 159, 213, 42, 218, 90, 138, 79, 41, 202, 252, 13, 110, 192, 87, 113, 144, 131, 66, 22, 25, 62, 106, 42, 176, 22, 132, 5, 70, 31, 14, 226, 4, 70, 99, 110, 182, 89, 247, 103, 10, 61, 18, 167, 220, 94, 133, 133, 185, 71, 138, 237, 228, 19, 222, 69, 136, 182, 71, 120, 177, 134, 210, 134, 197, 99, 103, 152, 196, 55, 28, 162, 66, 133, 181, 219, 185, 119, 114, 25, 35, 121, 73, 67, 207, 50, 158, 232, 47, 115, 79, 94, 216, 166, 27, 76, 202, 89, 214, 100, 168, 217, 124, 98, 95, 186, 124, 206, 153, 102, 114, 97, 231, 186, 204, 32, 68, 136, 161, 0, 214, 146, 251, 193, 229, 246, 181, 200, 14, 28, 84, 185, 231, 146, 40, 190, 233, 176, 213, 26, 47, 132, 122, 34, 35, 22, 217, 144, 81, 223, 162, 131, 203, 9, 153, 172, 210, 2, 191, 140, 67, 206, 66, 168, 184, 227, 80, 191, 20, 118, 156, 144, 35, 154, 98, 0, 184, 175, 216, 199, 40, 84, 247, 165, 189, 51, 68, 58, 37, 202, 78, 59, 210, 230, 95, 54, 116, 23, 179, 132, 221, 117, 160, 230, 185, 112, 66, 253, 156, 216, 145, 246, 8, 65, 14, 11, 241, 168, 247, 142, 146, 206, 0, 187, 117, 75, 105, 104, 10, 171, 5, 122, 202, 65, 146, 81, 122, 119, 206, 70, 66, 91, 26, 40, 179, 205, 24, 172, 182, 55, 168, 55, 16, 235, 199, 216, 14, 69, 250, 182, 70, 230, 96, 103, 131, 136, 49, 50, 99, 149, 52, 248, 6, 150, 136, 187, 73, 30, 140, 186, 104, 216, 142, 213, 67, 240, 99, 25, 204, 38, 74, 63, 180, 91, 70, 61, 248, 41, 184, 183, 55, 136, 0, 131, 210, 111, 2, 137, 177, 53, 166, 98, 49, 185, 66, 87, 156, 178, 78, 249, 213, 170, 77, 253, 101, 140, 3, 171, 54, 90, 82, 47, 223, 139, 160, 158, 47, 240, 87, 21, 208, 33, 7, 116, 169, 62, 240, 227, 193, 168, 88, 179, 212, 103, 63, 88, 239, 158, 205, 72, 166, 189, 4, 126, 225, 77, 94, 250, 4, 50, 138, 123, 130, 157, 229, 209, 193, 217, 158, 109, 188, 2, 25, 221, 163, 190, 10, 101, 198, 59, 222, 17, 4, 133, 138, 231, 236, 6, 83, 34, 201, 10, 142, 51, 69, 170, 226, 161, 125, 103, 140, 108, 112, 222, 87, 224, 55, 36, 44, 17, 47, 176, 153, 61, 143, 101, 210, 116, 8, 87, 71, 47, 161, 138, 72, 202, 240, 167, 223, 92, 32, 68, 111, 156, 92, 174, 94, 216, 205, 208, 168, 164, 223, 254, 17, 35, 117, 208, 142, 197, 197, 12, 10, 63, 188, 234, 111, 164, 199, 59, 230, 14, 174, 174, 13, 59, 24, 129, 63, 215, 2, 210, 126, 176, 56, 73, 208, 130, 248, 140, 107, 69, 180, 255, 82, 62, 92, 153, 6,
diff --git a/image/lib/src/fonts/arial_48.dart b/image/lib/src/fonts/arial_48.dart
old mode 100644
new mode 100755
index 34d42a6..a156382
--- a/image/lib/src/fonts/arial_48.dart
+++ b/image/lib/src/fonts/arial_48.dart
@@ -3,7 +3,7 @@
/**
* 48px Arial font for use with [drawString] and [drawChar].
*/
-final BitmapFont arial_48 = new BitmapFont.fromZip(_ARIAL_48);
+final BitmapFont arial_48 = BitmapFont.fromZip(_ARIAL_48);
const List<int> _ARIAL_48 = const
[80, 75, 3, 4, 20, 0, 0, 8, 8, 0, 187, 182, 42, 68, 95, 169, 84, 175, 60, 66, 0, 0, 49, 67, 0, 0, 12, 0, 0, 0, 97, 114, 105, 97, 108, 95, 52, 56, 46, 112, 110, 103, 164, 187, 85, 80, 92, 77, 224, 237, 59, 131, 187, 7, 201, 64, 112, 11, 110, 131, 133, 224, 238, 16, 130, 59, 9, 131, 12, 4, 6, 215, 193, 33, 193, 157, 96, 131, 36, 192, 135, 195, 224, 26, 66, 176, 32, 67, 66, 128, 76, 112, 39, 184, 219, 0, 55, 255, 115, 238, 173, 243, 112, 30, 239, 170, 93, 187, 118, 213, 126, 233, 174, 213, 221, 235, 183, 30, 58, 193, 64, 79, 157, 148, 232, 41, 17, 0, 0, 32, 213, 212, 80, 49, 2, 0, 8, 160, 255, 190, 69, 8, 240, 254, 189, 93, 253, 135, 138, 1, 0, 165, 107, 77, 21, 197, 87, 1, 232, 3, 235, 164, 198, 242, 246, 186, 34, 18, 130, 8, 138, 255, 247, 249, 194, 74, 129, 197, 74, 49, 16, 173, 64, 192, 141, 35, 26, 16, 118, 181, 160, 125, 36, 202, 168, 153, 237, 244, 27, 37, 103, 100, 50, 247, 250, 128, 177, 95, 254, 118, 206, 249, 102, 78, 136, 251, 255, 183, 42, 30, 13, 18, 27, 31, 167, 132, 48, 219, 109, 15, 203, 143, 247, 203, 158, 251, 254, 24, 221, 156, 128, 185, 172, 219, 247, 103, 185, 13, 210, 33, 143, 143, 143, 152, 161, 71, 204, 71, 162, 117, 130, 28, 9, 130, 241, 237, 222, 97, 73, 246, 245, 226, 190, 71, 57, 152, 213, 242, 145, 45, 220, 95, 104, 56, 55, 120, 72, 22, 125, 228, 203, 53, 52, 143, 237, 118, 50, 85, 143, 87, 172, 177, 28, 154, 200, 180, 80, 223, 53, 20, 104, 222, 150, 246, 117, 27, 63, 37, 252, 209, 82, 59, 4, 6, 15, 88, 9, 120, 22, 10, 22, 41, 74, 94, 73, 224, 140, 243, 164, 94, 15, 157, 104, 163, 28, 249, 244, 86, 183, 249, 124, 168, 196, 182, 74, 194, 83, 240, 212, 247, 153, 115, 58, 119, 178, 243, 208, 217, 53, 185, 57, 35, 189, 163, 91, 178, 8, 142, 195, 163, 210, 103, 61, 65, 87, 177, 202, 237, 71, 177, 184, 250, 239, 125, 2, 99, 129, 252, 243, 193, 112, 207, 196, 113, 255, 214, 254, 249, 237, 68, 231, 63, 223, 123, 15, 165, 221, 136, 119, 190, 201, 251, 56, 38, 181, 168, 94, 74, 230, 52, 249, 72, 9, 31, 105, 236, 46, 144, 188, 212, 163, 114, 136, 226, 254, 112, 2, 184, 125, 195, 26, 242, 53, 107, 79, 217, 105, 60, 144, 187, 87, 104, 118, 186, 81, 56, 52, 165, 122, 237, 42, 232, 91, 47, 56, 46, 148, 148, 176, 239, 177, 165, 45, 42, 186, 158, 103, 209, 126, 223, 118, 56, 125, 185, 230, 166, 199, 81, 31, 236, 148, 50, 252, 116, 47, 127, 109, 206, 243, 222, 25, 83, 40, 194, 41, 159, 42, 216, 16, 191, 73, 182, 76, 178, 96, 25, 195, 56, 145, 120, 221, 209, 247, 29, 108, 181, 175, 236, 216, 29, 39, 167, 156, 18, 205, 248, 39, 191, 138, 185, 45, 189, 245, 180, 33, 13, 27, 70, 58, 148, 169, 111, 245, 83, 143, 82, 4, 226, 123, 111, 61, 214, 230, 240, 183, 32, 141, 126, 56, 251, 164, 22, 215, 124, 239, 245, 77, 175, 244, 133, 160, 70, 189, 249, 150, 16, 5, 232, 87, 197, 90, 92, 181, 190, 147, 48, 153, 95, 129, 177, 162, 31, 214, 122, 112, 101, 136, 121, 205, 132, 73, 107, 205, 156, 108, 75, 95, 180, 7, 137, 134, 71, 137, 228, 194, 157, 77, 153, 190, 180, 155, 207, 70, 187, 210, 128, 250, 228, 4, 181, 249, 93, 154, 236, 6, 159, 245, 37, 52, 160, 146, 233, 125, 94, 183, 153, 40, 153, 238, 55, 192, 214, 9, 251, 133, 167, 149, 52, 205, 70, 186, 197, 78, 215, 194, 9, 75, 43, 119, 230, 76, 240, 156, 149, 207, 24, 121, 215, 117, 248, 98, 189, 150, 79, 99, 190, 155, 82, 216, 148, 248, 230, 223, 93, 170, 214, 149, 125, 110, 243, 161, 152, 97, 140, 58, 7, 183, 124, 206, 49, 161, 208, 205, 21, 76, 48, 20, 255, 218, 191, 107, 231, 97, 214, 108, 238, 28, 221, 55, 87, 83, 165, 90, 27, 251, 190, 54, 15, 220, 90, 226, 138, 80, 123, 169, 172, 62, 99, 150, 179, 126, 7, 197, 149, 163, 16, 191, 125, 138, 34, 254, 118, 22, 83, 17, 106, 78, 32, 135, 206, 106, 104, 140, 237, 94, 230, 58, 6, 54, 15, 210, 25, 205, 12, 153, 82, 47, 201, 68, 187, 170, 70, 62, 19, 149, 250, 82, 240, 83, 86, 1, 100, 64, 5, 215, 194, 192, 136, 29, 99, 215, 9, 19, 7, 182, 4, 233, 31, 203, 233, 127, 142, 92, 15, 85, 242, 172, 69, 76, 110, 125, 144, 198, 77, 174, 139, 2, 83, 40, 135, 122, 197, 59, 15, 221, 134, 91, 103, 237, 92, 117, 229, 221, 181, 41, 135, 0, 146, 182, 45, 241, 9, 2, 214, 180, 41, 94, 181, 13, 191, 224, 12, 114, 73, 131, 99, 11, 186, 201, 184, 164, 139, 22, 4, 178, 104, 106, 64, 38, 176, 169, 142, 163, 7, 159, 157, 39, 248, 126, 140, 151, 176, 137, 14, 114, 206, 156, 207, 254, 185, 16, 172, 29, 248, 100, 108, 218, 215, 211, 191, 108, 208, 78, 127, 100, 131, 13, 53, 106, 152, 193, 136, 87, 180, 8, 239, 99, 160, 144, 118, 167, 63, 42, 166, 87, 221, 225, 109, 75, 141, 173, 129, 154, 206, 255, 254, 238, 117, 139, 191, 81, 8, 3, 6, 167, 154, 148, 143, 181, 152, 44, 191, 24, 250, 117, 148, 154, 50, 176, 33, 166, 141, 38, 47, 156, 93, 106, 88, 242, 196, 70, 143, 28, 223, 169, 18, 114, 224, 67, 43, 247, 73, 136, 27, 12, 189, 67, 243, 48, 104, 134, 48, 103, 19, 56, 120, 164, 188, 130, 171, 177, 92, 3, 225, 163, 196, 252, 151, 60, 152, 53, 243, 55, 21, 146, 30, 158, 230, 188, 217, 182, 109, 86, 210, 192, 214, 144, 229, 24, 59, 174, 199, 224, 134, 90, 130, 86, 20, 225, 160, 21, 102, 189, 111, 223, 232, 192, 109, 155, 197, 224, 180, 73, 167, 150, 182, 31, 67, 136, 233, 132, 35, 158, 104, 167, 118, 155, 129, 231, 73, 3, 61, 21, 28, 198, 123, 54, 194, 216, 90, 29, 123, 53, 147, 155, 20, 64, 210, 47, 34, 14, 38, 22, 17, 164, 159, 61, 172, 36, 94, 4, 22, 85, 59, 21, 165, 151, 158, 248, 119, 15, 143, 175, 98, 210, 108, 103, 57, 182, 133, 142, 24, 72, 174, 184, 40, 94, 146, 91, 50, 75, 65, 214, 29, 21, 65, 236, 111, 181, 151, 128, 200, 180, 221, 184, 199, 180, 40, 1, 194, 149, 84, 128, 8, 92, 98, 31, 230, 171, 8, 236, 223, 128, 64, 251, 244, 130, 66, 133, 222, 72, 102, 158, 248, 219, 141, 4, 11, 207, 72, 124, 70, 144, 218, 203, 189, 1, 31, 83, 62, 154, 125, 143, 78, 130, 70, 75, 44, 152, 62, 11, 166, 74, 116, 102, 185, 164, 45, 45, 65, 60, 27, 25, 149, 62, 151, 56, 139, 193, 50, 15, 57, 24, 61, 230, 64, 13, 81, 70, 213, 40, 201, 10, 63, 223, 159, 250, 175, 113, 207, 104, 200, 220, 155, 154, 201, 63, 10, 76, 165, 53, 203, 113, 98, 69, 133, 244, 74, 12, 55, 73, 46, 134, 54, 144, 125, 54, 18, 123, 60, 193, 124, 126, 23, 137, 135, 15, 157, 135, 131, 105, 186, 123, 251, 104, 120, 168, 27, 96, 161, 173, 101, 5, 87, 81, 220, 226, 122, 167, 35, 0, 119, 72, 161, 167, 209, 42, 27, 2, 191, 234, 217, 134, 159, 18, 253, 147, 51, 51, 14, 4, 233, 150, 133, 68, 252, 11, 183, 185, 0, 251, 241, 208, 39, 50, 153, 156, 138, 32, 168, 135, 157, 23, 169, 30, 188, 223, 15, 5, 80, 38, 88, 114, 163, 113, 123, 65, 216, 43, 194, 231, 116, 172, 238, 93, 49, 132, 126, 73, 129, 108, 164, 232, 172, 16, 105, 197, 167, 21, 231, 189, 255, 239, 219, 202, 159, 81, 9, 94, 113, 103, 218, 134, 11, 79, 28, 168, 70, 100, 195, 130, 132, 31, 74, 77, 186, 127, 8, 91, 1, 212, 60, 45, 249, 84, 247, 7, 250, 121, 71, 169, 38, 66, 35, 234, 156, 49, 188, 93, 201, 126, 133, 239, 111, 231, 185, 142, 196, 182, 109, 21, 4, 73, 48, 103, 39, 140, 250, 52, 202, 49, 109, 139, 135, 134, 176, 62, 147, 152, 117, 131, 106, 21, 179, 226, 38, 40, 158, 212, 78, 190, 95, 38, 8, 237, 120, 7, 124, 86, 191, 96, 56, 132, 38, 29, 40, 189, 138, 62, 238, 86, 245, 207, 143, 200, 207, 9, 97, 71, 169, 189, 102, 60, 225, 64, 197, 54, 244, 55, 60, 223, 80, 88, 120, 163, 108, 36, 158, 77, 155, 238, 29, 136, 97, 171, 150, 22, 249, 235, 211, 120, 68, 154, 37, 191, 114, 26, 31, 5, 126, 73, 139, 108, 116, 104, 221, 24, 69, 36, 41, 63, 214, 227, 102, 135, 37, 189, 233, 12, 110, 140, 160, 55, 12, 164, 122, 220, 88, 14, 217, 131, 111, 206, 240, 174, 245, 201, 36, 252, 52, 177, 172, 170, 79, 191, 108, 51, 176, 106, 124, 138, 110, 22, 74, 134, 78, 77, 124, 157, 193, 131, 66, 246, 233, 125, 142, 37, 185, 245, 181, 70, 219, 79, 237, 232, 101, 201, 89, 43, 195, 255, 115, 171, 161, 234, 195, 93, 107, 6, 173, 29, 22, 149, 40, 35, 91, 46, 14, 9, 17, 73, 241, 254, 171, 212, 119, 167, 67, 190, 189, 1, 53, 80, 62, 251, 2, 184, 52, 161, 134, 91, 74, 33, 159, 112, 139, 2, 40, 97, 96, 189, 229, 201, 95, 83, 143, 129, 74, 28, 72, 148, 193, 107, 3, 190, 185, 74, 95, 63, 156, 228, 164, 202, 254, 239, 43, 25, 139, 138, 160, 155, 127, 35, 235, 151, 252, 180, 202, 148, 165, 229, 198, 94, 112, 85, 175, 99, 167, 202, 46, 96, 21, 18, 158, 193, 78, 141, 85, 214, 8, 60, 154, 95, 43, 54, 77, 229, 31, 121, 65, 167, 42, 186, 219, 226, 113, 182, 123, 47, 54, 19, 155, 67, 140, 182, 147, 146, 229, 243, 79, 34, 92, 20, 18, 196, 225, 99, 140, 118, 64, 144, 170, 60, 106, 82, 171, 184, 105, 21, 195, 2, 143, 79, 199, 235, 4, 84, 8, 3, 122, 21, 247, 109, 185, 125, 74, 131, 183, 208, 27, 117, 7, 209, 243, 198, 19, 83, 50, 246, 133, 174, 186, 146, 79, 255, 68, 177, 141, 58, 169, 57, 216, 75, 98, 46, 245, 78, 44, 86, 187, 151, 125, 126, 6, 201, 147, 33, 41, 113, 173, 27, 107, 176, 44, 241, 161, 78, 216, 224, 75, 30, 217, 14, 3, 82, 164, 189, 137, 198, 39, 140, 49, 23, 132, 189, 109, 132, 19, 11, 74, 255, 228, 111, 248, 117, 198, 201, 179, 119, 32, 109, 117, 16, 23, 108, 230, 76, 61, 253, 222, 182, 138, 118, 62, 145, 17, 46, 69, 8, 49, 64, 81, 63, 57, 126, 228, 66, 225, 165, 213, 55, 236, 239, 99, 31, 88, 127, 7, 248, 65, 124, 100, 179, 156, 126, 249, 220, 131, 56, 78, 148, 196, 247, 253, 34, 56, 20, 51, 110, 219, 6, 37, 120, 85, 148, 90, 187, 12, 147, 249, 21, 64, 29, 34, 99, 142, 255, 78, 39, 155, 131, 134, 54, 57, 163, 119, 133, 226, 103, 153, 147, 105, 95, 63, 240, 246, 69, 187, 88, 130, 7, 36, 15, 169, 246, 126, 99, 123, 47, 60, 223, 112, 224, 56, 87, 202, 126, 25, 64, 37, 13, 97, 39, 70, 54, 58, 182, 110, 84, 33, 146, 70, 246, 142, 91, 57, 235, 212, 53, 111, 6, 63, 139, 178, 2, 29, 172, 124, 216, 219, 238, 14, 17, 241, 39, 38, 214, 79, 216, 72, 71, 126, 218, 0, 9, 111, 192, 178, 202, 115, 22, 62, 29, 7, 212, 221, 194, 92, 253, 207, 247, 219, 233, 19, 194, 235, 58, 190, 131, 211, 30, 55, 182, 156, 185, 147, 221, 89, 122, 103, 234, 54, 146, 98, 248, 214, 41, 165, 25, 99, 103, 130, 253, 92, 150, 158, 84, 219, 18, 253, 213, 204, 111, 36, 51, 227, 177, 44, 13, 93, 18, 181, 213, 184, 158, 149, 133, 77, 244, 135, 40, 125, 89, 80, 229, 3, 100, 105, 98, 218, 102, 236, 223, 235, 217, 5, 247, 114, 56, 254, 139, 130, 114, 88, 160, 159, 131, 244, 85, 210, 44, 0, 74, 161, 57, 153, 53, 223, 175, 216, 3, 193, 85, 0, 165, 174, 133, 250, 10, 179, 108, 49, 138, 227, 237, 61, 135, 111, 239, 105, 211, 79, 53, 205, 73, 139, 162, 25, 127, 173, 80, 43,
diff --git a/image/lib/src/formats/decode_info.dart b/image/lib/src/formats/decode_info.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/decoder.dart b/image/lib/src/formats/decoder.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/encoder.dart b/image/lib/src/formats/encoder.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/exr/exr_attribute.dart b/image/lib/src/formats/exr/exr_attribute.dart
old mode 100644
new mode 100755
index 5510f33..5daa2e9
--- a/image/lib/src/formats/exr/exr_attribute.dart
+++ b/image/lib/src/formats/exr/exr_attribute.dart
@@ -1,10 +1,10 @@
-import '../../util/input_buffer.dart';
-
-class ExrAttribute {
- String name;
- String type;
- int size;
- InputBuffer data;
-
- ExrAttribute(this.name, this.type, this.size, this.data);
-}
+import '../../util/input_buffer.dart';
+
+class ExrAttribute {
+ String name;
+ String type;
+ int size;
+ InputBuffer data;
+
+ ExrAttribute(this.name, this.type, this.size, this.data);
+}
diff --git a/image/lib/src/formats/exr/exr_b44_compressor.dart b/image/lib/src/formats/exr/exr_b44_compressor.dart
old mode 100644
new mode 100755
index 07daf1a..a7e3842
--- a/image/lib/src/formats/exr/exr_b44_compressor.dart
+++ b/image/lib/src/formats/exr/exr_b44_compressor.dart
@@ -1,36 +1,36 @@
-import 'dart:typed_data';
-
-import '../../image_exception.dart';
-import '../../internal/internal.dart';
-import '../../util/input_buffer.dart';
-import 'exr_compressor.dart';
-import 'exr_part.dart';
-
-abstract class ExrB44Compressor extends ExrCompressor {
- factory ExrB44Compressor(ExrPart header, int maxScanLineSize, int numScanLines,
- bool optFlatFields) = InternalExrB44Compressor;
-}
-
-@internal
-class InternalExrB44Compressor extends InternalExrCompressor implements ExrB44Compressor {
- InternalExrB44Compressor(ExrPart header, int maxScanLineSize, this._numScanLines,
- bool optFlatFields) :
- super(header) {
- }
-
- int numScanLines() => _numScanLines;
-
- Uint8List compress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- throw new ImageException('B44 compression not yet supported.');
- }
-
- Uint8List uncompress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- throw new ImageException('B44 compression not yet supported.');
- }
-
- //int _maxScanLineSize;
- int _numScanLines;
- //bool _optFlatFields;
-}
+import 'dart:typed_data';
+
+import '../../image_exception.dart';
+import '../../internal/internal.dart';
+import '../../util/input_buffer.dart';
+import 'exr_compressor.dart';
+import 'exr_part.dart';
+
+abstract class ExrB44Compressor extends ExrCompressor {
+ factory ExrB44Compressor(ExrPart header, int maxScanLineSize, int numScanLines,
+ bool optFlatFields) = InternalExrB44Compressor;
+}
+
+@internal
+class InternalExrB44Compressor extends InternalExrCompressor implements ExrB44Compressor {
+ InternalExrB44Compressor(ExrPart header, int maxScanLineSize, this._numScanLines,
+ bool optFlatFields) :
+ super(header) {
+ }
+
+ int numScanLines() => _numScanLines;
+
+ Uint8List compress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ throw new ImageException('B44 compression not yet supported.');
+ }
+
+ Uint8List uncompress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ throw new ImageException('B44 compression not yet supported.');
+ }
+
+ //int _maxScanLineSize;
+ int _numScanLines;
+ //bool _optFlatFields;
+}
diff --git a/image/lib/src/formats/exr/exr_channel.dart b/image/lib/src/formats/exr/exr_channel.dart
old mode 100644
new mode 100755
index 3a751f8..19ee809
--- a/image/lib/src/formats/exr/exr_channel.dart
+++ b/image/lib/src/formats/exr/exr_channel.dart
@@ -1,76 +1,76 @@
-import '../../image_exception.dart';
-import '../../hdr/hdr_image.dart';
-import '../../util/input_buffer.dart';
-
-class ExrChannel {
- static const int TYPE_UINT = HdrImage.UINT;
- static const int TYPE_HALF = HdrImage.HALF;
- static const int TYPE_FLOAT = HdrImage.FLOAT;
-
- // Channel Names
-
- /// Luminance
- static const String Y = 'Y';
- /// Chroma RY
- static const String RY = 'RY';
- /// Chroma BY
- static const String BY = 'BY';
- /// Red for colored mattes
- static const String AR = 'AR';
- /// Green for colored mattes
- static const String AG = 'AG';
- /// Blue for colored mattes
- static const String AB = 'AB';
- /// Distance of the front of a sample from the viewer
- static const String Z = 'Z';
- /// Distance of the back of a sample from the viewer
- static const String ZBack = 'ZBack';
- /// Alpha/opacity
- static const String A = 'A';
- /// Red value of a sample
- static const String R = 'R';
- /// Green value of a sample
- static const String G = 'G';
- /// Blue value of a sample
- static const String B = 'B';
- /// A numerical identifier for the object represented by a sample.
- static const String ID = 'id';
-
- String name;
- int type;
- int size;
- bool pLinear;
- int xSampling;
- int ySampling;
-
- ExrChannel(InputBuffer input) {
- name = input.readString();
- if (name == null || name.isEmpty) {
- name = null;
- return;
- }
- type = input.readUint32();
- int i = input.readByte();
- assert(i == 0 || i == 1);
- pLinear = i == 1;
- input.skip(3);
- xSampling = input.readUint32();
- ySampling = input.readUint32();
-
- switch (type) {
- case TYPE_UINT:
- size = 4;
- break;
- case TYPE_HALF:
- size = 2;
- break;
- case TYPE_FLOAT:
- size = 4;
- break;
- default:
- throw new ImageException('EXR Invalid pixel type: $type');
- }
- }
-
- bool get isValid => name != null;
-}
+import '../../image_exception.dart';
+import '../../hdr/hdr_image.dart';
+import '../../util/input_buffer.dart';
+
+class ExrChannel {
+ static const int TYPE_UINT = HdrImage.UINT;
+ static const int TYPE_HALF = HdrImage.HALF;
+ static const int TYPE_FLOAT = HdrImage.FLOAT;
+
+ // Channel Names
+
+ /// Luminance
+ static const String Y = 'Y';
+ /// Chroma RY
+ static const String RY = 'RY';
+ /// Chroma BY
+ static const String BY = 'BY';
+ /// Red for colored mattes
+ static const String AR = 'AR';
+ /// Green for colored mattes
+ static const String AG = 'AG';
+ /// Blue for colored mattes
+ static const String AB = 'AB';
+ /// Distance of the front of a sample from the viewer
+ static const String Z = 'Z';
+ /// Distance of the back of a sample from the viewer
+ static const String ZBack = 'ZBack';
+ /// Alpha/opacity
+ static const String A = 'A';
+ /// Red value of a sample
+ static const String R = 'R';
+ /// Green value of a sample
+ static const String G = 'G';
+ /// Blue value of a sample
+ static const String B = 'B';
+ /// A numerical identifier for the object represented by a sample.
+ static const String ID = 'id';
+
+ String name;
+ int type;
+ int size;
+ bool pLinear;
+ int xSampling;
+ int ySampling;
+
+ ExrChannel(InputBuffer input) {
+ name = input.readString();
+ if (name == null || name.isEmpty) {
+ name = null;
+ return;
+ }
+ type = input.readUint32();
+ int i = input.readByte();
+ assert(i == 0 || i == 1);
+ pLinear = i == 1;
+ input.skip(3);
+ xSampling = input.readUint32();
+ ySampling = input.readUint32();
+
+ switch (type) {
+ case TYPE_UINT:
+ size = 4;
+ break;
+ case TYPE_HALF:
+ size = 2;
+ break;
+ case TYPE_FLOAT:
+ size = 4;
+ break;
+ default:
+ throw new ImageException('EXR Invalid pixel type: $type');
+ }
+ }
+
+ bool get isValid => name != null;
+}
diff --git a/image/lib/src/formats/exr/exr_compressor.dart b/image/lib/src/formats/exr/exr_compressor.dart
old mode 100644
new mode 100755
index b0b0e5f..71e99b3
--- a/image/lib/src/formats/exr/exr_compressor.dart
+++ b/image/lib/src/formats/exr/exr_compressor.dart
@@ -1,103 +1,103 @@
-import 'dart:typed_data';
-
-import '../../image_exception.dart';
-import '../../internal/internal.dart';
-import '../../util/input_buffer.dart';
-import 'exr_b44_compressor.dart';
-import 'exr_part.dart';
-import 'exr_piz_compressor.dart';
-import 'exr_pxr24_compressor.dart';
-import 'exr_rle_compressor.dart';
-import 'exr_zip_compressor.dart';
-
-abstract class ExrCompressor {
- static const int NO_COMPRESSION = 0;
- static const int RLE_COMPRESSION = 1;
- static const int ZIPS_COMPRESSION = 2;
- static const int ZIP_COMPRESSION = 3;
- static const int PIZ_COMPRESSION = 4;
- static const int PXR24_COMPRESSION = 5;
- static const int B44_COMPRESSION = 6;
- static const int B44A_COMPRESSION = 7;
-
- int decodedWidth = 0;
- int decodedHeight = 0;
-
- factory ExrCompressor(int type, ExrPart hdr, int maxScanLineSize,
- [int numScanLines]) {
- switch (type) {
- case RLE_COMPRESSION:
- return new ExrRleCompressor(hdr, maxScanLineSize);
- case ZIPS_COMPRESSION:
- return new ExrZipCompressor(hdr, maxScanLineSize,
- numScanLines == null ? 1 : numScanLines);
- case ZIP_COMPRESSION:
- return new ExrZipCompressor(hdr, maxScanLineSize,
- numScanLines == null ? 16 : numScanLines);
- case PIZ_COMPRESSION:
- return new ExrPizCompressor(hdr, maxScanLineSize,
- numScanLines == null ? 32 : numScanLines);
- case PXR24_COMPRESSION:
- return new ExrPxr24Compressor(hdr, maxScanLineSize,
- numScanLines == null ? 16 : numScanLines);
- case B44_COMPRESSION:
- return new ExrB44Compressor(hdr, maxScanLineSize,
- numScanLines == null ? 32 : numScanLines, false);
- case B44A_COMPRESSION:
- return new ExrB44Compressor(hdr, maxScanLineSize,
- numScanLines == null ? 32 : numScanLines, true);
- default:
- throw new ImageException('Invalid compression type: $type');
- }
- }
-
- factory ExrCompressor.tile(int type, int tileLineSize, int numTileLines,
- ExrPart hdr) {
- switch (type) {
- case RLE_COMPRESSION:
- return new ExrRleCompressor(hdr, (tileLineSize * numTileLines));
- case ZIPS_COMPRESSION:
- case ZIP_COMPRESSION:
- return new ExrZipCompressor(hdr, tileLineSize, numTileLines);
- case PIZ_COMPRESSION:
- return new ExrPizCompressor(hdr, tileLineSize, numTileLines);
- case PXR24_COMPRESSION:
- return new ExrPxr24Compressor(hdr, tileLineSize, numTileLines);
- case B44_COMPRESSION:
- return new ExrB44Compressor(hdr, tileLineSize, numTileLines, false);
- case B44A_COMPRESSION:
- return new ExrB44Compressor(hdr, tileLineSize, numTileLines, true);
- default:
- throw new ImageException('Invalid compression type: $type');
- }
- }
-
- ExrCompressor._(this._header);
-
- int numScanLines();
-
- Uint8List compress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- throw new ImageException('Unsupported compression type');
- }
-
- Uint8List uncompress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- throw new ImageException('Unsupported compression type');
- }
-
- ExrPart _header;
-}
-
-@internal
-abstract class InternalExrCompressor extends ExrCompressor {
- InternalExrCompressor(InternalExrPart header) : super._(header);
-
- InternalExrPart get header => _header;
-
- int numSamples(int s, int a, int b) {
- int a1 = a ~/ s;
- int b1 = b ~/ s;
- return b1 - a1 + ((a1 * s < a) ? 0: 1);
- }
-}
+import 'dart:typed_data';
+
+import '../../image_exception.dart';
+import '../../internal/internal.dart';
+import '../../util/input_buffer.dart';
+import 'exr_b44_compressor.dart';
+import 'exr_part.dart';
+import 'exr_piz_compressor.dart';
+import 'exr_pxr24_compressor.dart';
+import 'exr_rle_compressor.dart';
+import 'exr_zip_compressor.dart';
+
+abstract class ExrCompressor {
+ static const int NO_COMPRESSION = 0;
+ static const int RLE_COMPRESSION = 1;
+ static const int ZIPS_COMPRESSION = 2;
+ static const int ZIP_COMPRESSION = 3;
+ static const int PIZ_COMPRESSION = 4;
+ static const int PXR24_COMPRESSION = 5;
+ static const int B44_COMPRESSION = 6;
+ static const int B44A_COMPRESSION = 7;
+
+ int decodedWidth = 0;
+ int decodedHeight = 0;
+
+ factory ExrCompressor(int type, ExrPart hdr, int maxScanLineSize,
+ [int numScanLines]) {
+ switch (type) {
+ case RLE_COMPRESSION:
+ return new ExrRleCompressor(hdr, maxScanLineSize);
+ case ZIPS_COMPRESSION:
+ return new ExrZipCompressor(hdr, maxScanLineSize,
+ numScanLines == null ? 1 : numScanLines);
+ case ZIP_COMPRESSION:
+ return new ExrZipCompressor(hdr, maxScanLineSize,
+ numScanLines == null ? 16 : numScanLines);
+ case PIZ_COMPRESSION:
+ return new ExrPizCompressor(hdr, maxScanLineSize,
+ numScanLines == null ? 32 : numScanLines);
+ case PXR24_COMPRESSION:
+ return new ExrPxr24Compressor(hdr, maxScanLineSize,
+ numScanLines == null ? 16 : numScanLines);
+ case B44_COMPRESSION:
+ return new ExrB44Compressor(hdr, maxScanLineSize,
+ numScanLines == null ? 32 : numScanLines, false);
+ case B44A_COMPRESSION:
+ return new ExrB44Compressor(hdr, maxScanLineSize,
+ numScanLines == null ? 32 : numScanLines, true);
+ default:
+ throw new ImageException('Invalid compression type: $type');
+ }
+ }
+
+ factory ExrCompressor.tile(int type, int tileLineSize, int numTileLines,
+ ExrPart hdr) {
+ switch (type) {
+ case RLE_COMPRESSION:
+ return new ExrRleCompressor(hdr, (tileLineSize * numTileLines));
+ case ZIPS_COMPRESSION:
+ case ZIP_COMPRESSION:
+ return new ExrZipCompressor(hdr, tileLineSize, numTileLines);
+ case PIZ_COMPRESSION:
+ return new ExrPizCompressor(hdr, tileLineSize, numTileLines);
+ case PXR24_COMPRESSION:
+ return new ExrPxr24Compressor(hdr, tileLineSize, numTileLines);
+ case B44_COMPRESSION:
+ return new ExrB44Compressor(hdr, tileLineSize, numTileLines, false);
+ case B44A_COMPRESSION:
+ return new ExrB44Compressor(hdr, tileLineSize, numTileLines, true);
+ default:
+ throw new ImageException('Invalid compression type: $type');
+ }
+ }
+
+ ExrCompressor._(this._header);
+
+ int numScanLines();
+
+ Uint8List compress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ throw new ImageException('Unsupported compression type');
+ }
+
+ Uint8List uncompress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ throw new ImageException('Unsupported compression type');
+ }
+
+ ExrPart _header;
+}
+
+@internal
+abstract class InternalExrCompressor extends ExrCompressor {
+ InternalExrCompressor(InternalExrPart header) : super._(header);
+
+ InternalExrPart get header => _header;
+
+ int numSamples(int s, int a, int b) {
+ int a1 = a ~/ s;
+ int b1 = b ~/ s;
+ return b1 - a1 + ((a1 * s < a) ? 0: 1);
+ }
+}
diff --git a/image/lib/src/formats/exr/exr_huffman.dart b/image/lib/src/formats/exr/exr_huffman.dart
old mode 100644
new mode 100755
index b90c415..b5a0d3e
--- a/image/lib/src/formats/exr/exr_huffman.dart
+++ b/image/lib/src/formats/exr/exr_huffman.dart
@@ -1,345 +1,345 @@
-import 'dart:typed_data';
-
-import '../../image_exception.dart';
-import '../../util/input_buffer.dart';
-
-class ExrHuffman {
- static void uncompress(InputBuffer compressed, int nCompressed,
- Uint16List raw, int nRaw) {
- if (nCompressed == 0) {
- if (nRaw != 0) {
- throw new ImageException('Incomplete huffman data');
- }
-
- return;
- }
-
- int start = compressed.offset;
-
- int im = compressed.readUint32();
- int iM = compressed.readUint32();
- compressed.skip(4); // tableLength
- int nBits = compressed.readUint32();
-
- if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE) {
- throw new ImageException('Invalid huffman table size');
- }
-
- compressed.skip(4);
-
- List<int> freq = new List<int>(HUF_ENCSIZE);
- freq.fillRange(0, HUF_ENCSIZE, 0);
-
- List<ExrHufDec> hdec = new List<ExrHufDec>(HUF_DECSIZE);
- for (int i = 0; i < HUF_DECSIZE; ++i) {
- hdec[i] = new ExrHufDec();
- }
-
- unpackEncTable(compressed, nCompressed - 20, im, iM, freq);
-
- if (nBits > 8 * (nCompressed - (compressed.offset - start))) {
- throw new ImageException("Error in header for Huffman-encoded data "
- "(invalid number of bits).");
- }
-
- buildDecTable(freq, im, iM, hdec);
- decode(freq, hdec, compressed, nBits, iM, nRaw, raw);
- }
-
- static void decode(List<int> hcode, List<ExrHufDec> hdecod,
- InputBuffer input, int ni, int rlc, int no,
- Uint16List out) {
- List<int> c_lc = [0, 0];
- int ie = input.offset + (ni + 7) ~/ 8; // input byte size
- int oi = 0;
-
- // Loop on input bytes
-
- while (input.offset < ie) {
- getChar(c_lc, input);
-
- // Access decoding table
- while (c_lc[1] >= HUF_DECBITS) {
- ExrHufDec pl = hdecod[(c_lc[0] >> (c_lc[1] - HUF_DECBITS)) & HUF_DECMASK];
-
- if (pl.len != 0) {
- // Get short code
- c_lc[1] -= pl.len;
- oi = getCode(pl.lit, rlc, c_lc, input, out, oi, no);
- } else {
- if (pl.p == null) {
- throw new ImageException("Error in Huffman-encoded data "
- "(invalid code).");
- }
-
- // Search long code
- int j;
- for (j = 0; j < pl.lit; j++) {
- int l = hufLength(hcode[pl.p[j]]);
-
- while (c_lc[1] < l && input.offset < ie) { // get more bits
- getChar(c_lc, input);
- }
-
- if (c_lc[1] >= l) {
- if (hufCode(hcode[pl.p[j]]) ==
- ((c_lc[0] >> (c_lc[1] - l)) & ((1 << l) - 1))) {
- // Found : get long code
- c_lc[1] -= l;
- oi = getCode(pl.p[j], rlc, c_lc, input, out, oi, no);
- break;
- }
- }
- }
-
- if (j == pl.lit) {
- throw new ImageException("Error in Huffman-encoded data "
- "(invalid code).");
- }
- }
- }
- }
-
- // Get remaining (short) codes
- int i = (8 - ni) & 7;
- c_lc[0] >>= i;
- c_lc[1] -= i;
-
- while (c_lc[1] > 0) {
- ExrHufDec pl = hdecod[(c_lc[0] << (HUF_DECBITS - c_lc[1])) & HUF_DECMASK];
-
- if (pl.len != 0) {
- c_lc[1] -= pl.len;
- oi = getCode(pl.lit, rlc, c_lc, input, out, oi, no);
- } else {
- throw new ImageException("Error in Huffman-encoded data "
- "(invalid code).");
- }
- }
-
- if (oi != no) {
- throw new ImageException("Error in Huffman-encoded data "
- "(decoded data are shorter than expected).");
- }
- }
-
- static int getCode(int po, int rlc, List<int> c_lc, InputBuffer input,
- Uint16List out, int oi, int oe) {
- if (po == rlc) {
- if (c_lc[1] < 8) {
- getChar(c_lc, input);
- }
-
- c_lc[1] -= 8;
-
- int cs = (c_lc[0] >> c_lc[1]) & 0xff;
-
- if (oi + cs > oe) {
- throw new ImageException("Error in Huffman-encoded data "
- "(decoded data are longer than expected).");
- }
-
- int s = out[oi - 1];
-
- while (cs-- > 0) {
- out[oi++] = s;
- }
- } else if (oi < oe) {
- out[oi++] = po;
- } else {
- throw new ImageException("Error in Huffman-encoded data "
- "(decoded data are longer than expected).");
- }
- return oi;
- }
-
-
- static void buildDecTable(List<int> hcode, int im, int iM,
- List<ExrHufDec> hdecod) {
- // Init hashtable & loop on all codes.
- // Assumes that hufClearDecTable(hdecod) has already been called.
- for (; im <= iM; im++) {
- int c = hufCode(hcode[im]);
- int l = hufLength(hcode[im]);
-
- if (c >> l != 0) {
- // Error: c is supposed to be an l-bit code,
- // but c contains a value that is greater
- // than the largest l-bit number.
- throw new ImageException("Error in Huffman-encoded data "
- "(invalid code table entry).");
- }
-
- if (l > HUF_DECBITS) {
- // Long code: add a secondary entry
- ExrHufDec pl = hdecod[(c >> (l - HUF_DECBITS))];
-
- if (pl.len != 0) {
- // Error: a short code has already
- // been stored in table entry *pl.
- throw new ImageException("Error in Huffman-encoded data "
- "(invalid code table entry).");
- }
-
- pl.lit++;
-
- if (pl.p != null) {
- List<int> p = pl.p;
- pl.p = new List<int>(pl.lit);
-
- for (int i = 0; i < pl.lit - 1; ++i) {
- pl.p[i] = p[i];
- }
- } else {
- pl.p = [0];
- }
-
- pl.p[pl.lit - 1] = im;
- } else if (l != 0) {
- // Short code: init all primary entries
- int pi = (c << (HUF_DECBITS - l));
- ExrHufDec pl = hdecod[pi];
-
- for (int i = 1 << (HUF_DECBITS - l); i > 0; i--, pi++) {
- pl = hdecod[pi];
- if (pl.len != 0 || pl.p != null) {
- // Error: a short code or a long code has
- // already been stored in table entry *pl.
- throw new ImageException("Error in Huffman-encoded data "
- "(invalid code table entry).");
- }
-
- pl.len = l;
- pl.lit = im;
- }
- }
- }
- }
-
- static void unpackEncTable(InputBuffer p, int ni, int im, int iM,
- List<int> hcode) {
- int pcode = p.offset;
- List<int> c_lc = [0, 0];
-
- for (; im <= iM; im++) {
- if (p.offset - pcode > ni) {
- throw new ImageException("Error in Huffman-encoded data "
- "(unexpected end of code table data).");
- }
-
- int l = hcode[im] = getBits(6, c_lc, p); // code length
-
- if (l == LONG_ZEROCODE_RUN) {
- if (p.offset - pcode > ni) {
- throw new ImageException("Error in Huffman-encoded data "
- "(unexpected end of code table data).");
- }
-
- int zerun = getBits(8, c_lc, p) + SHORTEST_LONG_RUN;
-
- if (im + zerun > iM + 1) {
- throw new ImageException("Error in Huffman-encoded data "
- "(code table is longer than expected).");
- }
-
- while (zerun-- != 0) {
- hcode[im++] = 0;
- }
-
- im--;
- } else if (l >= SHORT_ZEROCODE_RUN) {
- int zerun = l - SHORT_ZEROCODE_RUN + 2;
-
- if (im + zerun > iM + 1) {
- throw new ImageException("Error in Huffman-encoded data "
- "(code table is longer than expected).");
- }
-
- while (zerun-- != 0) {
- hcode[im++] = 0;
- }
-
- im--;
- }
- }
-
- canonicalCodeTable(hcode);
- }
-
- static int hufLength(int code) => code & 63;
-
- static int hufCode(int code) => code >> 6;
-
- static void canonicalCodeTable(List<int> hcode) {
- List<int> n = new List<int>(59);
- n.fillRange(0, 59, 0);
-
- // For each i from 0 through 58, count the
- // number of different codes of length i, and
- // store the count in n[i].
-
- for (int i = 0; i < HUF_ENCSIZE; ++i) {
- n[hcode[i]] += 1;
- }
-
- // For each i from 58 through 1, compute the
- // numerically lowest code with length i, and
- // store that code in n[i].
-
- int c = 0;
-
- for (int i = 58; i > 0; --i) {
- int nc = ((c + n[i]) >> 1);
- n[i] = c;
- c = nc;
- }
-
- // hcode[i] contains the length, l, of the
- // code for symbol i. Assign the next available
- // code of length l to the symbol and store both
- // l and the code in hcode[i].
-
- for (int i = 0; i < HUF_ENCSIZE; ++i) {
- int l = hcode[i];
- if (l > 0) {
- hcode[i] = l | (n[l]++ << 6);
- }
- }
- }
-
- static void getChar(List<int> c_lc, InputBuffer input) {
- c_lc[0] = ((c_lc[0] << 8) | input.readByte()) & MASK_64;
- c_lc[1] = (c_lc[1] + 8) & MASK_32;
- }
-
- static int getBits(int nBits, List<int> c_lc, InputBuffer input) {
- while (c_lc[1] < nBits) {
- c_lc[0] = ((c_lc[0] << 8) | input.readByte()) & MASK_64;
- c_lc[1] = (c_lc[1] + 8) & MASK_32;
- }
-
- c_lc[1] -= nBits;
-
- return (c_lc[0] >> c_lc[1]) & ((1 << nBits) - 1);
- }
-
- static const int MASK_32 = (1 << 32) - 1;
- static const int MASK_64 = (1 << 64) - 1;
- static const int HUF_ENCBITS = 16; // literal (value) bit length
- static const int HUF_DECBITS = 14; // decoding bit size (>= 8)
-
- static const int HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1; // encoding table size
- static const int HUF_DECSIZE = 1 << HUF_DECBITS; // decoding table size
- static const int HUF_DECMASK = HUF_DECSIZE - 1;
-
- static const int SHORT_ZEROCODE_RUN = 59;
- static const int LONG_ZEROCODE_RUN = 63;
- static const int SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;
- static const int LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN;
-}
-
-class ExrHufDec {
- int len = 0;
- int lit = 0;
- List<int> p;
-}
+import 'dart:typed_data';
+
+import '../../image_exception.dart';
+import '../../util/input_buffer.dart';
+
+class ExrHuffman {
+ static void uncompress(InputBuffer compressed, int nCompressed,
+ Uint16List raw, int nRaw) {
+ if (nCompressed == 0) {
+ if (nRaw != 0) {
+ throw new ImageException('Incomplete huffman data');
+ }
+
+ return;
+ }
+
+ int start = compressed.offset;
+
+ int im = compressed.readUint32();
+ int iM = compressed.readUint32();
+ compressed.skip(4); // tableLength
+ int nBits = compressed.readUint32();
+
+ if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE) {
+ throw new ImageException('Invalid huffman table size');
+ }
+
+ compressed.skip(4);
+
+ List<int> freq = List<int>(HUF_ENCSIZE);
+ freq.fillRange(0, HUF_ENCSIZE, 0);
+
+ List<ExrHufDec> hdec = List<ExrHufDec>(HUF_DECSIZE);
+ for (int i = 0; i < HUF_DECSIZE; ++i) {
+ hdec[i] = ExrHufDec();
+ }
+
+ unpackEncTable(compressed, nCompressed - 20, im, iM, freq);
+
+ if (nBits > 8 * (nCompressed - (compressed.offset - start))) {
+ throw new ImageException("Error in header for Huffman-encoded data "
+ "(invalid number of bits).");
+ }
+
+ buildDecTable(freq, im, iM, hdec);
+ decode(freq, hdec, compressed, nBits, iM, nRaw, raw);
+ }
+
+ static void decode(List<int> hcode, List<ExrHufDec> hdecod,
+ InputBuffer input, int ni, int rlc, int no,
+ Uint16List out) {
+ List<int> c_lc = [0, 0];
+ int ie = input.offset + (ni + 7) ~/ 8; // input byte size
+ int oi = 0;
+
+ // Loop on input bytes
+
+ while (input.offset < ie) {
+ getChar(c_lc, input);
+
+ // Access decoding table
+ while (c_lc[1] >= HUF_DECBITS) {
+ ExrHufDec pl = hdecod[(c_lc[0] >> (c_lc[1] - HUF_DECBITS)) & HUF_DECMASK];
+
+ if (pl.len != 0) {
+ // Get short code
+ c_lc[1] -= pl.len;
+ oi = getCode(pl.lit, rlc, c_lc, input, out, oi, no);
+ } else {
+ if (pl.p == null) {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(invalid code).");
+ }
+
+ // Search long code
+ int j;
+ for (j = 0; j < pl.lit; j++) {
+ int l = hufLength(hcode[pl.p[j]]);
+
+ while (c_lc[1] < l && input.offset < ie) { // get more bits
+ getChar(c_lc, input);
+ }
+
+ if (c_lc[1] >= l) {
+ if (hufCode(hcode[pl.p[j]]) ==
+ ((c_lc[0] >> (c_lc[1] - l)) & ((1 << l) - 1))) {
+ // Found : get long code
+ c_lc[1] -= l;
+ oi = getCode(pl.p[j], rlc, c_lc, input, out, oi, no);
+ break;
+ }
+ }
+ }
+
+ if (j == pl.lit) {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(invalid code).");
+ }
+ }
+ }
+ }
+
+ // Get remaining (short) codes
+ int i = (8 - ni) & 7;
+ c_lc[0] >>= i;
+ c_lc[1] -= i;
+
+ while (c_lc[1] > 0) {
+ ExrHufDec pl = hdecod[(c_lc[0] << (HUF_DECBITS - c_lc[1])) & HUF_DECMASK];
+
+ if (pl.len != 0) {
+ c_lc[1] -= pl.len;
+ oi = getCode(pl.lit, rlc, c_lc, input, out, oi, no);
+ } else {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(invalid code).");
+ }
+ }
+
+ if (oi != no) {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(decoded data are shorter than expected).");
+ }
+ }
+
+ static int getCode(int po, int rlc, List<int> c_lc, InputBuffer input,
+ Uint16List out, int oi, int oe) {
+ if (po == rlc) {
+ if (c_lc[1] < 8) {
+ getChar(c_lc, input);
+ }
+
+ c_lc[1] -= 8;
+
+ int cs = (c_lc[0] >> c_lc[1]) & 0xff;
+
+ if (oi + cs > oe) {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(decoded data are longer than expected).");
+ }
+
+ int s = out[oi - 1];
+
+ while (cs-- > 0) {
+ out[oi++] = s;
+ }
+ } else if (oi < oe) {
+ out[oi++] = po;
+ } else {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(decoded data are longer than expected).");
+ }
+ return oi;
+ }
+
+
+ static void buildDecTable(List<int> hcode, int im, int iM,
+ List<ExrHufDec> hdecod) {
+ // Init hashtable & loop on all codes.
+ // Assumes that hufClearDecTable(hdecod) has already been called.
+ for (; im <= iM; im++) {
+ int c = hufCode(hcode[im]);
+ int l = hufLength(hcode[im]);
+
+ if (c >> l != 0) {
+ // Error: c is supposed to be an l-bit code,
+ // but c contains a value that is greater
+ // than the largest l-bit number.
+ throw new ImageException("Error in Huffman-encoded data "
+ "(invalid code table entry).");
+ }
+
+ if (l > HUF_DECBITS) {
+ // Long code: add a secondary entry
+ ExrHufDec pl = hdecod[(c >> (l - HUF_DECBITS))];
+
+ if (pl.len != 0) {
+ // Error: a short code has already
+ // been stored in table entry *pl.
+ throw new ImageException("Error in Huffman-encoded data "
+ "(invalid code table entry).");
+ }
+
+ pl.lit++;
+
+ if (pl.p != null) {
+ List<int> p = pl.p;
+ pl.p = List<int>(pl.lit);
+
+ for (int i = 0; i < pl.lit - 1; ++i) {
+ pl.p[i] = p[i];
+ }
+ } else {
+ pl.p = [0];
+ }
+
+ pl.p[pl.lit - 1] = im;
+ } else if (l != 0) {
+ // Short code: init all primary entries
+ int pi = (c << (HUF_DECBITS - l));
+ ExrHufDec pl = hdecod[pi];
+
+ for (int i = 1 << (HUF_DECBITS - l); i > 0; i--, pi++) {
+ pl = hdecod[pi];
+ if (pl.len != 0 || pl.p != null) {
+ // Error: a short code or a long code has
+ // already been stored in table entry *pl.
+ throw new ImageException("Error in Huffman-encoded data "
+ "(invalid code table entry).");
+ }
+
+ pl.len = l;
+ pl.lit = im;
+ }
+ }
+ }
+ }
+
+ static void unpackEncTable(InputBuffer p, int ni, int im, int iM,
+ List<int> hcode) {
+ int pcode = p.offset;
+ List<int> c_lc = [0, 0];
+
+ for (; im <= iM; im++) {
+ if (p.offset - pcode > ni) {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(unexpected end of code table data).");
+ }
+
+ int l = hcode[im] = getBits(6, c_lc, p); // code length
+
+ if (l == LONG_ZEROCODE_RUN) {
+ if (p.offset - pcode > ni) {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(unexpected end of code table data).");
+ }
+
+ int zerun = getBits(8, c_lc, p) + SHORTEST_LONG_RUN;
+
+ if (im + zerun > iM + 1) {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(code table is longer than expected).");
+ }
+
+ while (zerun-- != 0) {
+ hcode[im++] = 0;
+ }
+
+ im--;
+ } else if (l >= SHORT_ZEROCODE_RUN) {
+ int zerun = l - SHORT_ZEROCODE_RUN + 2;
+
+ if (im + zerun > iM + 1) {
+ throw new ImageException("Error in Huffman-encoded data "
+ "(code table is longer than expected).");
+ }
+
+ while (zerun-- != 0) {
+ hcode[im++] = 0;
+ }
+
+ im--;
+ }
+ }
+
+ canonicalCodeTable(hcode);
+ }
+
+ static int hufLength(int code) => code & 63;
+
+ static int hufCode(int code) => code >> 6;
+
+ static void canonicalCodeTable(List<int> hcode) {
+ List<int> n = List<int>(59);
+ n.fillRange(0, 59, 0);
+
+ // For each i from 0 through 58, count the
+ // number of different codes of length i, and
+ // store the count in n[i].
+
+ for (int i = 0; i < HUF_ENCSIZE; ++i) {
+ n[hcode[i]] += 1;
+ }
+
+ // For each i from 58 through 1, compute the
+ // numerically lowest code with length i, and
+ // store that code in n[i].
+
+ int c = 0;
+
+ for (int i = 58; i > 0; --i) {
+ int nc = ((c + n[i]) >> 1);
+ n[i] = c;
+ c = nc;
+ }
+
+ // hcode[i] contains the length, l, of the
+ // code for symbol i. Assign the next available
+ // code of length l to the symbol and store both
+ // l and the code in hcode[i].
+
+ for (int i = 0; i < HUF_ENCSIZE; ++i) {
+ int l = hcode[i];
+ if (l > 0) {
+ hcode[i] = l | (n[l]++ << 6);
+ }
+ }
+ }
+
+ static void getChar(List<int> c_lc, InputBuffer input) {
+ c_lc[0] = ((c_lc[0] << 8) | input.readByte()) & MASK_64;
+ c_lc[1] = (c_lc[1] + 8) & MASK_32;
+ }
+
+ static int getBits(int nBits, List<int> c_lc, InputBuffer input) {
+ while (c_lc[1] < nBits) {
+ c_lc[0] = ((c_lc[0] << 8) | input.readByte()) & MASK_64;
+ c_lc[1] = (c_lc[1] + 8) & MASK_32;
+ }
+
+ c_lc[1] -= nBits;
+
+ return (c_lc[0] >> c_lc[1]) & ((1 << nBits) - 1);
+ }
+
+ static const int MASK_32 = (1 << 32) - 1;
+ static const int MASK_64 = (1 << 64) - 1;
+ static const int HUF_ENCBITS = 16; // literal (value) bit length
+ static const int HUF_DECBITS = 14; // decoding bit size (>= 8)
+
+ static const int HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1; // encoding table size
+ static const int HUF_DECSIZE = 1 << HUF_DECBITS; // decoding table size
+ static const int HUF_DECMASK = HUF_DECSIZE - 1;
+
+ static const int SHORT_ZEROCODE_RUN = 59;
+ static const int LONG_ZEROCODE_RUN = 63;
+ static const int SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;
+ static const int LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN;
+}
+
+class ExrHufDec {
+ int len = 0;
+ int lit = 0;
+ List<int> p;
+}
diff --git a/image/lib/src/formats/exr/exr_image.dart b/image/lib/src/formats/exr/exr_image.dart
old mode 100644
new mode 100755
index c250589..4c431e3
--- a/image/lib/src/formats/exr/exr_image.dart
+++ b/image/lib/src/formats/exr/exr_image.dart
@@ -15,7 +15,7 @@
List<InternalExrPart> _parts = [];
ExrImage(List<int> bytes) {
- InputBuffer input = new InputBuffer(bytes);
+ InputBuffer input = InputBuffer(bytes);
int magic = input.readUint32();
if (magic != MAGIC) {
throw new ImageException('File is not an OpenEXR image file.');
@@ -33,13 +33,13 @@
}
if (!_isMultiPart()) {
- ExrPart part = new InternalExrPart(_isTiled(), input);
+ ExrPart part = InternalExrPart(_isTiled(), input);
if (part.isValid) {
_parts.add(part);
}
} else {
while (true) {
- ExrPart part = new InternalExrPart(_isTiled(), input);
+ ExrPart part = InternalExrPart(_isTiled(), input);
if (!part.isValid) {
break;
}
@@ -66,7 +66,7 @@
* Parse just enough of the file to identify that it's an EXR image.
*/
static bool isValidFile(List<int> bytes) {
- InputBuffer input = new InputBuffer(bytes);
+ InputBuffer input = InputBuffer(bytes);
int magic = input.readUint32();
if (magic != MAGIC) {
@@ -137,9 +137,9 @@
HdrImage framebuffer = part.framebuffer;
ExrCompressor compressor = part.compressor;
List<Uint32List> offsets = part.offsets;
- //Uint32List fbi = new Uint32List(part.channels.length);
+ //Uint32List fbi = Uint32List(part.channels.length);
- InputBuffer imgData = new InputBuffer.from(input);
+ InputBuffer imgData = InputBuffer.from(input);
for (int ly = 0, l = 0; ly < part.numYLevels; ++ly) {
for (int lx = 0; lx < part.numXLevels; ++lx, ++l) {
for (int ty = 0, oi = 0; ty < part.numYTiles[ly]; ++ty) {
@@ -234,13 +234,13 @@
//int minY = part.top;
//int maxY = minY + part.linesInBuffer - 1;
- Uint32List fbi = new Uint32List(part.channels.length);
+ Uint32List fbi = Uint32List(part.channels.length);
//int total = 0;
//int xx = 0;
int yy = 0;
- InputBuffer imgData = new InputBuffer.from(input);
+ InputBuffer imgData = InputBuffer.from(input);
for (int offset in offsets) {
imgData.offset = offset;
diff --git a/image/lib/src/formats/exr/exr_part.dart b/image/lib/src/formats/exr/exr_part.dart
old mode 100644
new mode 100755
index 0917076..e0d7416
--- a/image/lib/src/formats/exr/exr_part.dart
+++ b/image/lib/src/formats/exr/exr_part.dart
@@ -1,384 +1,384 @@
-import 'dart:math' as Math;
-import 'dart:typed_data';
-
-import '../../image_exception.dart';
-import '../../internal/internal.dart';
-import '../../hdr/hdr_image.dart';
-import '../../util/input_buffer.dart';
-import 'exr_attribute.dart';
-import 'exr_channel.dart';
-import 'exr_compressor.dart';
-
-class ExrPart {
- /// The framebuffer for this exr part.
- HdrImage framebuffer = new HdrImage();
- /// The channels present in this part.
- List<ExrChannel> channels = [];
- /// The extra attributes read from the part header.
- Map<String, ExrAttribute> attributes = {};
- /// The display window (see the openexr documentation).
- List<int> displayWindow;
- /// The data window (see the openexr documentation).
- List<int> dataWindow;
- /// width of the data window
- int width;
- /// Height of the data window
- int height;
- double pixelAspectRatio = 1.0;
- double screenWindowCenterX = 0.0;
- double screenWindowCenterY = 0.0;
- double screenWindowWidth = 1.0;
- Float32List chromaticities;
-
- ExrPart(this._tiled, InputBuffer input) {
- //_type = _tiled ? ExrPart.TYPE_TILE : ExrPart.TYPE_SCANLINE;
-
- while (true) {
- String name = input.readString();
- if (name == null || name.isEmpty) {
- break;
- }
-
- String type = input.readString();
- int size = input.readUint32();
- InputBuffer value = input.readBytes(size);
-
- attributes[name] = new ExrAttribute(name, type, size, value);
-
- switch (name) {
- case 'channels':
- while (true) {
- ExrChannel channel = new ExrChannel(value);
- if (!channel.isValid) {
- break;
- }
- channels.add(channel);
- }
- break;
- case 'chromaticities':
- chromaticities = new Float32List(8);
- chromaticities[0] = value.readFloat32();
- chromaticities[1] = value.readFloat32();
- chromaticities[2] = value.readFloat32();
- chromaticities[3] = value.readFloat32();
- chromaticities[4] = value.readFloat32();
- chromaticities[5] = value.readFloat32();
- chromaticities[6] = value.readFloat32();
- chromaticities[7] = value.readFloat32();
- break;
- case 'compression':
- _compressionType = value.readByte();
- if (_compressionType > 7) {
- throw new ImageException('EXR Invalid compression type');
- }
- break;
- case 'dataWindow':
- dataWindow = [value.readInt32(), value.readInt32(),
- value.readInt32(), value.readInt32()];
- width = (dataWindow[2] - dataWindow[0]) + 1;
- height = (dataWindow[3] - dataWindow[1]) + 1;
- break;
- case 'displayWindow':
- displayWindow = [value.readInt32(), value.readInt32(),
- value.readInt32(), value.readInt32()];
- break;
- case 'lineOrder':
- //_lineOrder = value.readByte();
- break;
- case 'pixelAspectRatio':
- pixelAspectRatio = value.readFloat32();
- break;
- case 'screenWindowCenter':
- screenWindowCenterX = value.readFloat32();
- screenWindowCenterY = value.readFloat32();
- break;
- case 'screenWindowWidth':
- screenWindowWidth = value.readFloat32();
- break;
- case 'tiles':
- _tileWidth = value.readUint32();
- _tileHeight = value.readUint32();
- int mode = value.readByte();
- _tileLevelMode = mode & 0xf;
- _tileRoundingMode = (mode >> 4) & 0xf;
- break;
- case 'type':
- String s = value.readString();
- if (s == 'deepscanline') {
- //this._type = TYPE_DEEP_SCANLINE;
- } else if (s == 'deeptile') {
- //this._type = TYPE_DEEP_TILE;
- } else {
- throw new ImageException('EXR Invalid type: $s');
- }
- break;
- default:
- break;
- }
- }
-
- if (_tiled) {
- _numXLevels = _calculateNumXLevels(left, right, top, bottom);
- _numYLevels = _calculateNumYLevels(left, right, top, bottom);
- if (_tileLevelMode != RIPMAP_LEVELS) {
- _numYLevels = 1;
- }
-
- _numXTiles = new List<int>(_numXLevels);
- _numYTiles = new List<int>(_numYLevels);
-
- _calculateNumTiles(_numXTiles, _numXLevels, left, right, _tileWidth,
- _tileRoundingMode);
-
- _calculateNumTiles(_numYTiles, _numYLevels, top, bottom, _tileHeight,
- _tileRoundingMode);
-
- _bytesPerPixel = _calculateBytesPerPixel();
- _maxBytesPerTileLine = _bytesPerPixel * _tileWidth;
- //_tileBufferSize = _maxBytesPerTileLine * _tileHeight;
-
- _compressor = new ExrCompressor(_compressionType, this,
- _maxBytesPerTileLine, _tileHeight);
-
- _offsets = new List<Uint32List>(_numXLevels * _numYLevels);
- for (int ly = 0, l = 0; ly < _numYLevels; ++ly) {
- for (int lx = 0; lx < _numXLevels; ++lx, ++l) {
- _offsets[l] = new Uint32List(_numXTiles[lx] * _numYTiles[ly]);
- }
- }
- } else {
- _bytesPerLine = new Uint32List(height + 1);
- for (ExrChannel ch in channels) {
- int nBytes = ch.size * width ~/ ch.xSampling;
- for (int y = 0; y < height; ++y) {
- if ((y + top) % ch.ySampling == 0) {
- _bytesPerLine[y] += nBytes;
- }
- }
- }
-
- int maxBytesPerLine = 0;
- for (int y = 0; y < height; ++y) {
- maxBytesPerLine = Math.max(maxBytesPerLine, _bytesPerLine[y]);
- }
-
- _compressor = new ExrCompressor(_compressionType, this, maxBytesPerLine);
-
- _linesInBuffer = _compressor.numScanLines();
- //_lineBufferSize = maxBytesPerLine * _linesInBuffer;
-
- _offsetInLineBuffer = new Uint32List(_bytesPerLine.length);
-
- int offset = 0;
- for (int i = 0; i <= _bytesPerLine.length - 1; ++i) {
- if (i % _linesInBuffer == 0) {
- offset = 0;
- }
- _offsetInLineBuffer[i] = offset;
- offset += _bytesPerLine[i];
- }
-
- int numOffsets = ((height + _linesInBuffer) ~/ _linesInBuffer) - 1;
- _offsets = [new Uint32List(numOffsets)];
- }
- }
-
- int get left => dataWindow[0];
-
- int get top => dataWindow[1];
-
- int get right => dataWindow[2];
-
- int get bottom => dataWindow[3];
-
- /**
- * Was this part successfully decoded?
- */
- bool get isValid => width != null;
-
- int _calculateNumXLevels(int minX, int maxX, int minY, int maxY) {
- int num = 0;
-
- switch (_tileLevelMode) {
- case ONE_LEVEL:
- num = 1;
- break;
- case MIPMAP_LEVELS:
- int w = maxX - minX + 1;
- int h = maxY - minY + 1;
- num = _roundLog2(Math.max(w, h), _tileRoundingMode) + 1;
- break;
- case RIPMAP_LEVELS:
- int w = maxX - minX + 1;
- num = _roundLog2(w, _tileRoundingMode) + 1;
- break;
- default:
- throw new ImageException("Unknown LevelMode format.");
- }
-
- return num;
- }
-
-
- int _calculateNumYLevels(int minX, int maxX, int minY, int maxY) {
- int num = 0;
-
- switch (_tileLevelMode) {
- case ONE_LEVEL:
- num = 1;
- break;
- case MIPMAP_LEVELS:
- int w = (maxX - minX) + 1;
- int h = (maxY - minY) + 1;
- num = _roundLog2(Math.max(w, h), _tileRoundingMode) + 1;
- break;
- case RIPMAP_LEVELS:
- int h = (maxY - minY) + 1;
- num = _roundLog2(h, _tileRoundingMode) + 1;
- break;
- default:
- throw new ImageException('Unknown LevelMode format.');
- }
-
- return num;
- }
-
- int _roundLog2(int x, int rmode) {
- return (rmode == ROUND_DOWN) ? _floorLog2(x) : _ceilLog2(x);
- }
-
- int _floorLog2(int x) {
- int y = 0;
-
- while (x > 1) {
- y += 1;
- x >>= 1;
- }
-
- return y;
- }
-
-
- int _ceilLog2(int x) {
- int y = 0;
- int r = 0;
-
- while (x > 1) {
- if (x & 1 != 0) {
- r = 1;
- }
-
- y += 1;
- x >>= 1;
- }
-
- return y + r;
- }
-
- int _calculateBytesPerPixel() {
- int bytesPerPixel = 0;
-
- for (ExrChannel ch in channels) {
- bytesPerPixel += ch.size;
- }
-
- return bytesPerPixel;
- }
-
- void _calculateNumTiles(List<int> numTiles, int numLevels,
- int min, int max, int size, int rmode) {
- for (int i = 0; i < numLevels; i++) {
- numTiles[i] = (_levelSize(min, max, i, rmode) + size - 1) ~/ size;
- }
- }
-
- int _levelSize(int min, int max, int l, int rmode) {
- if (l < 0) {
- throw new ImageException('Argument not in valid range.');
- }
-
- int a = (max - min) + 1;
- int b = (1 << l);
- int size = a ~/ b;
-
- if (rmode == ROUND_UP && size * b < a) {
- size += 1;
- }
-
- return Math.max(size, 1);
- }
-
- static const int TYPE_SCANLINE = 0;
- static const int TYPE_TILE = 1;
- static const int TYPE_DEEP_SCANLINE = 2;
- static const int TYPE_DEEP_TILE = 3;
-
- static const int INCREASING_Y = 0;
- static const int DECREASING_Y = 1;
- static const int RANDOM_Y = 2;
-
- static const int ONE_LEVEL = 0;
- static const int MIPMAP_LEVELS = 1;
- static const int RIPMAP_LEVELS = 2;
-
- static const int ROUND_DOWN = 0;
- static const int ROUND_UP = 1;
-
- //int _type;
- //int _lineOrder = INCREASING_Y;
- int _compressionType = ExrCompressor.NO_COMPRESSION;
- List<Uint32List> _offsets;
-
- Uint32List _bytesPerLine;
- ExrCompressor _compressor;
- int _linesInBuffer;
- //int _lineBufferSize;
- Uint32List _offsetInLineBuffer;
-
- bool _tiled;
- int _tileWidth;
- int _tileHeight;
- int _tileLevelMode;
- int _tileRoundingMode;
- List<int> _numXTiles;
- List<int> _numYTiles;
- int _numXLevels;
- int _numYLevels;
- int _bytesPerPixel;
- int _maxBytesPerTileLine;
- //int _tileBufferSize;
-}
-
-@internal
-class InternalExrPart extends ExrPart {
- InternalExrPart(bool tiled, InputBuffer input) : super(tiled, input);
-
- List<Uint32List> get offsets => _offsets;
-
- ExrCompressor get compressor => _compressor;
- int get linesInBuffer => _linesInBuffer;
- Uint32List get offsetInLineBuffer => _offsetInLineBuffer;
-
- bool get tiled => _tiled;
- int get tileWidth => _tileWidth;
- int get tileHeight => _tileHeight;
- List<int> get numXTiles => _numXTiles;
- List<int> get numYTiles => _numYTiles;
- int get numXLevels => _numXLevels;
- int get numYLevels => _numYLevels;
-
- void readOffsets(InputBuffer input) {
- if (_tiled) {
- for (int i = 0; i < _offsets.length; ++i) {
- for (int j = 0; j < _offsets[i].length; ++j) {
- _offsets[i][j] = input.readUint64();
- }
- }
- } else {
- int numOffsets = _offsets[0].length;
- for (int i = 0; i < numOffsets; ++i) {
- _offsets[0][i] = input.readUint64();
- }
- }
- }
-}
+import 'dart:math' as Math;
+import 'dart:typed_data';
+
+import '../../image_exception.dart';
+import '../../internal/internal.dart';
+import '../../hdr/hdr_image.dart';
+import '../../util/input_buffer.dart';
+import 'exr_attribute.dart';
+import 'exr_channel.dart';
+import 'exr_compressor.dart';
+
+class ExrPart {
+ /// The framebuffer for this exr part.
+ HdrImage framebuffer = HdrImage();
+ /// The channels present in this part.
+ List<ExrChannel> channels = [];
+ /// The extra attributes read from the part header.
+ Map<String, ExrAttribute> attributes = {};
+ /// The display window (see the openexr documentation).
+ List<int> displayWindow;
+ /// The data window (see the openexr documentation).
+ List<int> dataWindow;
+ /// width of the data window
+ int width;
+ /// Height of the data window
+ int height;
+ double pixelAspectRatio = 1.0;
+ double screenWindowCenterX = 0.0;
+ double screenWindowCenterY = 0.0;
+ double screenWindowWidth = 1.0;
+ Float32List chromaticities;
+
+ ExrPart(this._tiled, InputBuffer input) {
+ //_type = _tiled ? ExrPart.TYPE_TILE : ExrPart.TYPE_SCANLINE;
+
+ while (true) {
+ String name = input.readString();
+ if (name == null || name.isEmpty) {
+ break;
+ }
+
+ String type = input.readString();
+ int size = input.readUint32();
+ InputBuffer value = input.readBytes(size);
+
+ attributes[name] = ExrAttribute(name, type, size, value);
+
+ switch (name) {
+ case 'channels':
+ while (true) {
+ ExrChannel channel = ExrChannel(value);
+ if (!channel.isValid) {
+ break;
+ }
+ channels.add(channel);
+ }
+ break;
+ case 'chromaticities':
+ chromaticities = Float32List(8);
+ chromaticities[0] = value.readFloat32();
+ chromaticities[1] = value.readFloat32();
+ chromaticities[2] = value.readFloat32();
+ chromaticities[3] = value.readFloat32();
+ chromaticities[4] = value.readFloat32();
+ chromaticities[5] = value.readFloat32();
+ chromaticities[6] = value.readFloat32();
+ chromaticities[7] = value.readFloat32();
+ break;
+ case 'compression':
+ _compressionType = value.readByte();
+ if (_compressionType > 7) {
+ throw new ImageException('EXR Invalid compression type');
+ }
+ break;
+ case 'dataWindow':
+ dataWindow = [value.readInt32(), value.readInt32(),
+ value.readInt32(), value.readInt32()];
+ width = (dataWindow[2] - dataWindow[0]) + 1;
+ height = (dataWindow[3] - dataWindow[1]) + 1;
+ break;
+ case 'displayWindow':
+ displayWindow = [value.readInt32(), value.readInt32(),
+ value.readInt32(), value.readInt32()];
+ break;
+ case 'lineOrder':
+ //_lineOrder = value.readByte();
+ break;
+ case 'pixelAspectRatio':
+ pixelAspectRatio = value.readFloat32();
+ break;
+ case 'screenWindowCenter':
+ screenWindowCenterX = value.readFloat32();
+ screenWindowCenterY = value.readFloat32();
+ break;
+ case 'screenWindowWidth':
+ screenWindowWidth = value.readFloat32();
+ break;
+ case 'tiles':
+ _tileWidth = value.readUint32();
+ _tileHeight = value.readUint32();
+ int mode = value.readByte();
+ _tileLevelMode = mode & 0xf;
+ _tileRoundingMode = (mode >> 4) & 0xf;
+ break;
+ case 'type':
+ String s = value.readString();
+ if (s == 'deepscanline') {
+ //this._type = TYPE_DEEP_SCANLINE;
+ } else if (s == 'deeptile') {
+ //this._type = TYPE_DEEP_TILE;
+ } else {
+ throw new ImageException('EXR Invalid type: $s');
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (_tiled) {
+ _numXLevels = _calculateNumXLevels(left, right, top, bottom);
+ _numYLevels = _calculateNumYLevels(left, right, top, bottom);
+ if (_tileLevelMode != RIPMAP_LEVELS) {
+ _numYLevels = 1;
+ }
+
+ _numXTiles = List<int>(_numXLevels);
+ _numYTiles = List<int>(_numYLevels);
+
+ _calculateNumTiles(_numXTiles, _numXLevels, left, right, _tileWidth,
+ _tileRoundingMode);
+
+ _calculateNumTiles(_numYTiles, _numYLevels, top, bottom, _tileHeight,
+ _tileRoundingMode);
+
+ _bytesPerPixel = _calculateBytesPerPixel();
+ _maxBytesPerTileLine = _bytesPerPixel * _tileWidth;
+ //_tileBufferSize = _maxBytesPerTileLine * _tileHeight;
+
+ _compressor = ExrCompressor(_compressionType, this,
+ _maxBytesPerTileLine, _tileHeight);
+
+ _offsets = List<Uint32List>(_numXLevels * _numYLevels);
+ for (int ly = 0, l = 0; ly < _numYLevels; ++ly) {
+ for (int lx = 0; lx < _numXLevels; ++lx, ++l) {
+ _offsets[l] = Uint32List(_numXTiles[lx] * _numYTiles[ly]);
+ }
+ }
+ } else {
+ _bytesPerLine = Uint32List(height + 1);
+ for (ExrChannel ch in channels) {
+ int nBytes = ch.size * width ~/ ch.xSampling;
+ for (int y = 0; y < height; ++y) {
+ if ((y + top) % ch.ySampling == 0) {
+ _bytesPerLine[y] += nBytes;
+ }
+ }
+ }
+
+ int maxBytesPerLine = 0;
+ for (int y = 0; y < height; ++y) {
+ maxBytesPerLine = Math.max(maxBytesPerLine, _bytesPerLine[y]);
+ }
+
+ _compressor = ExrCompressor(_compressionType, this, maxBytesPerLine);
+
+ _linesInBuffer = _compressor.numScanLines();
+ //_lineBufferSize = maxBytesPerLine * _linesInBuffer;
+
+ _offsetInLineBuffer = Uint32List(_bytesPerLine.length);
+
+ int offset = 0;
+ for (int i = 0; i <= _bytesPerLine.length - 1; ++i) {
+ if (i % _linesInBuffer == 0) {
+ offset = 0;
+ }
+ _offsetInLineBuffer[i] = offset;
+ offset += _bytesPerLine[i];
+ }
+
+ int numOffsets = ((height + _linesInBuffer) ~/ _linesInBuffer) - 1;
+ _offsets = [new Uint32List(numOffsets)];
+ }
+ }
+
+ int get left => dataWindow[0];
+
+ int get top => dataWindow[1];
+
+ int get right => dataWindow[2];
+
+ int get bottom => dataWindow[3];
+
+ /**
+ * Was this part successfully decoded?
+ */
+ bool get isValid => width != null;
+
+ int _calculateNumXLevels(int minX, int maxX, int minY, int maxY) {
+ int num = 0;
+
+ switch (_tileLevelMode) {
+ case ONE_LEVEL:
+ num = 1;
+ break;
+ case MIPMAP_LEVELS:
+ int w = maxX - minX + 1;
+ int h = maxY - minY + 1;
+ num = _roundLog2(Math.max(w, h), _tileRoundingMode) + 1;
+ break;
+ case RIPMAP_LEVELS:
+ int w = maxX - minX + 1;
+ num = _roundLog2(w, _tileRoundingMode) + 1;
+ break;
+ default:
+ throw new ImageException("Unknown LevelMode format.");
+ }
+
+ return num;
+ }
+
+
+ int _calculateNumYLevels(int minX, int maxX, int minY, int maxY) {
+ int num = 0;
+
+ switch (_tileLevelMode) {
+ case ONE_LEVEL:
+ num = 1;
+ break;
+ case MIPMAP_LEVELS:
+ int w = (maxX - minX) + 1;
+ int h = (maxY - minY) + 1;
+ num = _roundLog2(Math.max(w, h), _tileRoundingMode) + 1;
+ break;
+ case RIPMAP_LEVELS:
+ int h = (maxY - minY) + 1;
+ num = _roundLog2(h, _tileRoundingMode) + 1;
+ break;
+ default:
+ throw new ImageException('Unknown LevelMode format.');
+ }
+
+ return num;
+ }
+
+ int _roundLog2(int x, int rmode) {
+ return (rmode == ROUND_DOWN) ? _floorLog2(x) : _ceilLog2(x);
+ }
+
+ int _floorLog2(int x) {
+ int y = 0;
+
+ while (x > 1) {
+ y += 1;
+ x >>= 1;
+ }
+
+ return y;
+ }
+
+
+ int _ceilLog2(int x) {
+ int y = 0;
+ int r = 0;
+
+ while (x > 1) {
+ if (x & 1 != 0) {
+ r = 1;
+ }
+
+ y += 1;
+ x >>= 1;
+ }
+
+ return y + r;
+ }
+
+ int _calculateBytesPerPixel() {
+ int bytesPerPixel = 0;
+
+ for (ExrChannel ch in channels) {
+ bytesPerPixel += ch.size;
+ }
+
+ return bytesPerPixel;
+ }
+
+ void _calculateNumTiles(List<int> numTiles, int numLevels,
+ int min, int max, int size, int rmode) {
+ for (int i = 0; i < numLevels; i++) {
+ numTiles[i] = (_levelSize(min, max, i, rmode) + size - 1) ~/ size;
+ }
+ }
+
+ int _levelSize(int min, int max, int l, int rmode) {
+ if (l < 0) {
+ throw new ImageException('Argument not in valid range.');
+ }
+
+ int a = (max - min) + 1;
+ int b = (1 << l);
+ int size = a ~/ b;
+
+ if (rmode == ROUND_UP && size * b < a) {
+ size += 1;
+ }
+
+ return Math.max(size, 1);
+ }
+
+ static const int TYPE_SCANLINE = 0;
+ static const int TYPE_TILE = 1;
+ static const int TYPE_DEEP_SCANLINE = 2;
+ static const int TYPE_DEEP_TILE = 3;
+
+ static const int INCREASING_Y = 0;
+ static const int DECREASING_Y = 1;
+ static const int RANDOM_Y = 2;
+
+ static const int ONE_LEVEL = 0;
+ static const int MIPMAP_LEVELS = 1;
+ static const int RIPMAP_LEVELS = 2;
+
+ static const int ROUND_DOWN = 0;
+ static const int ROUND_UP = 1;
+
+ //int _type;
+ //int _lineOrder = INCREASING_Y;
+ int _compressionType = ExrCompressor.NO_COMPRESSION;
+ List<Uint32List> _offsets;
+
+ Uint32List _bytesPerLine;
+ ExrCompressor _compressor;
+ int _linesInBuffer;
+ //int _lineBufferSize;
+ Uint32List _offsetInLineBuffer;
+
+ bool _tiled;
+ int _tileWidth;
+ int _tileHeight;
+ int _tileLevelMode;
+ int _tileRoundingMode;
+ List<int> _numXTiles;
+ List<int> _numYTiles;
+ int _numXLevels;
+ int _numYLevels;
+ int _bytesPerPixel;
+ int _maxBytesPerTileLine;
+ //int _tileBufferSize;
+}
+
+@internal
+class InternalExrPart extends ExrPart {
+ InternalExrPart(bool tiled, InputBuffer input) : super(tiled, input);
+
+ List<Uint32List> get offsets => _offsets;
+
+ ExrCompressor get compressor => _compressor;
+ int get linesInBuffer => _linesInBuffer;
+ Uint32List get offsetInLineBuffer => _offsetInLineBuffer;
+
+ bool get tiled => _tiled;
+ int get tileWidth => _tileWidth;
+ int get tileHeight => _tileHeight;
+ List<int> get numXTiles => _numXTiles;
+ List<int> get numYTiles => _numYTiles;
+ int get numXLevels => _numXLevels;
+ int get numYLevels => _numYLevels;
+
+ void readOffsets(InputBuffer input) {
+ if (_tiled) {
+ for (int i = 0; i < _offsets.length; ++i) {
+ for (int j = 0; j < _offsets[i].length; ++j) {
+ _offsets[i][j] = input.readUint64();
+ }
+ }
+ } else {
+ int numOffsets = _offsets[0].length;
+ for (int i = 0; i < numOffsets; ++i) {
+ _offsets[0][i] = input.readUint64();
+ }
+ }
+ }
+}
diff --git a/image/lib/src/formats/exr/exr_piz_compressor.dart b/image/lib/src/formats/exr/exr_piz_compressor.dart
old mode 100644
new mode 100755
index 198e62f..3de8788
--- a/image/lib/src/formats/exr/exr_piz_compressor.dart
+++ b/image/lib/src/formats/exr/exr_piz_compressor.dart
@@ -1,184 +1,184 @@
-import 'dart:typed_data';
-
-import '../../image_exception.dart';
-import '../../internal/internal.dart';
-import '../../util/input_buffer.dart';
-import '../../util/output_buffer.dart';
-import 'exr_channel.dart';
-import 'exr_compressor.dart';
-import 'exr_huffman.dart';
-import 'exr_part.dart';
-import 'exr_wavelet.dart';
-
-/**
- * Wavelet compression
- */
-abstract class ExrPizCompressor extends ExrCompressor {
- factory ExrPizCompressor(ExrPart header, int maxScanLineSize,
- int numScanLines) = InternalExrPizCompressor;
-}
-
-@internal
-class InternalExrPizCompressor extends InternalExrCompressor implements ExrPizCompressor {
- InternalExrPizCompressor(ExrPart header, this._maxScanLineSize, this._numScanLines) :
- super(header) {
- _channelData = new List<_PizChannelData>(header.channels.length);
- for (int i = 0; i < _channelData.length; ++i) {
- _channelData[i] = new _PizChannelData();
- }
-
- int tmpBufferSize = (_maxScanLineSize * _numScanLines) ~/ 2;
- _tmpBuffer = new Uint16List(tmpBufferSize);
- }
-
- int numScanLines() => _numScanLines;
-
- Uint8List compress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- throw new ImageException('Piz compression not yet supported.');
- }
-
- Uint8List uncompress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- if (width == null) {
- width = header.width;
- }
- if (height == null) {
- height = header.linesInBuffer;
- }
-
- int minX = x;
- int maxX = x + width - 1;
- int minY = y;
- int maxY = y + height - 1;
-
- if (maxX > header.width) {
- maxX = header.width - 1;
- }
- if (maxY > header.height) {
- maxY = header.height - 1;
- }
-
- decodedWidth = (maxX - minX) + 1;
- decodedHeight = (maxY - minY) + 1;
-
- int tmpBufferEnd = 0;
- List<ExrChannel> channels = header.channels;
- final int numChannels = channels.length;
-
- for (int i = 0; i < numChannels; ++i) {
- ExrChannel ch = channels[i];
- _PizChannelData cd = _channelData[i];
- cd.start = tmpBufferEnd;
- cd.end = cd.start;
-
- cd.nx = numSamples(ch.xSampling, minX, maxX);
- cd.ny = numSamples(ch.ySampling, minY, maxY);
- cd.ys = ch.ySampling;
-
- cd.size = ch.size ~/ 2; //2=size(HALF)
-
- tmpBufferEnd += cd.nx * cd.ny * cd.size;
- }
-
- int minNonZero = inPtr.readUint16();
- int maxNonZero = inPtr.readUint16();
-
- if (maxNonZero >= BITMAP_SIZE) {
- throw new ImageException("Error in header for PIZ-compressed data "
- "(invalid bitmap size).");
- }
-
- Uint8List bitmap = new Uint8List(BITMAP_SIZE);
- if (minNonZero <= maxNonZero) {
- InputBuffer b = inPtr.readBytes(maxNonZero - minNonZero + 1);
- for (int i = 0, j = minNonZero, len = b.length; i < len; ++i) {
- bitmap[j++] = b[i];
- }
- }
-
- Uint16List lut = new Uint16List(USHORT_RANGE);
- int maxValue = _reverseLutFromBitmap(bitmap, lut);
-
- // Huffman decoding
- int length = inPtr.readUint32();
- ExrHuffman.uncompress(inPtr, length, _tmpBuffer, tmpBufferEnd);
-
- // Wavelet decoding
- for (int i = 0; i < numChannels; ++i) {
- _PizChannelData cd = _channelData[i];
- for (int j = 0; j < cd.size; ++j) {
- ExrWavelet.decode(_tmpBuffer, cd.start + j, cd.nx, cd.size, cd.ny,
- cd.nx * cd.size, maxValue);
- }
- }
-
- // Expand the pixel data to their original range
- _applyLut(lut, _tmpBuffer, tmpBufferEnd);
-
- if (_output == null) {
- _output = new OutputBuffer(size: (_maxScanLineSize * _numScanLines) +
- (65536 + 8192));
- }
- _output.rewind();
-
- //int count = 0;
- // Rearrange the pixel data into the format expected by the caller.
- for (int y = minY; y <= maxY; ++y) {
- for (int i = 0; i < numChannels; ++i) {
- _PizChannelData cd = _channelData[i];
-
- if ((y % cd.ys) != 0) {
- continue;
- }
-
- for (int x = cd.nx * cd.size; x > 0; --x) {
- _output.writeUint16(_tmpBuffer[cd.end++]);
- }
- }
- }
-
- return _output.getBytes();
- }
-
- void _applyLut(List<int> lut, List<int> data, int nData) {
- for (int i = 0; i < nData; ++i) {
- data[i] = lut[data[i]];
- }
- }
-
- int _reverseLutFromBitmap(Uint8List bitmap, Uint16List lut) {
- int k = 0;
- for (int i = 0; i < USHORT_RANGE; ++i) {
- if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7))) != 0) {
- lut[k++] = i;
- }
- }
-
- int n = k - 1;
-
- while (k < USHORT_RANGE) {
- lut[k++] = 0;
- }
-
- return n; // maximum k where lut[k] is non-zero,
- }
-
- static const int USHORT_RANGE = 1 << 16;
- static const int BITMAP_SIZE = 8192;
-
- OutputBuffer _output;
- int _maxScanLineSize;
- int _numScanLines;
- List<_PizChannelData> _channelData;
- Uint16List _tmpBuffer;
-}
-
-class _PizChannelData {
- int start;
- int end;
- int nx;
- int ny;
- int ys;
- int size;
-}
+import 'dart:typed_data';
+
+import '../../image_exception.dart';
+import '../../internal/internal.dart';
+import '../../util/input_buffer.dart';
+import '../../util/output_buffer.dart';
+import 'exr_channel.dart';
+import 'exr_compressor.dart';
+import 'exr_huffman.dart';
+import 'exr_part.dart';
+import 'exr_wavelet.dart';
+
+/**
+ * Wavelet compression
+ */
+abstract class ExrPizCompressor extends ExrCompressor {
+ factory ExrPizCompressor(ExrPart header, int maxScanLineSize,
+ int numScanLines) = InternalExrPizCompressor;
+}
+
+@internal
+class InternalExrPizCompressor extends InternalExrCompressor implements ExrPizCompressor {
+ InternalExrPizCompressor(ExrPart header, this._maxScanLineSize, this._numScanLines) :
+ super(header) {
+ _channelData = List<_PizChannelData>(header.channels.length);
+ for (int i = 0; i < _channelData.length; ++i) {
+ _channelData[i] = _PizChannelData();
+ }
+
+ int tmpBufferSize = (_maxScanLineSize * _numScanLines) ~/ 2;
+ _tmpBuffer = Uint16List(tmpBufferSize);
+ }
+
+ int numScanLines() => _numScanLines;
+
+ Uint8List compress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ throw new ImageException('Piz compression not yet supported.');
+ }
+
+ Uint8List uncompress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ if (width == null) {
+ width = header.width;
+ }
+ if (height == null) {
+ height = header.linesInBuffer;
+ }
+
+ int minX = x;
+ int maxX = x + width - 1;
+ int minY = y;
+ int maxY = y + height - 1;
+
+ if (maxX > header.width) {
+ maxX = header.width - 1;
+ }
+ if (maxY > header.height) {
+ maxY = header.height - 1;
+ }
+
+ decodedWidth = (maxX - minX) + 1;
+ decodedHeight = (maxY - minY) + 1;
+
+ int tmpBufferEnd = 0;
+ List<ExrChannel> channels = header.channels;
+ final int numChannels = channels.length;
+
+ for (int i = 0; i < numChannels; ++i) {
+ ExrChannel ch = channels[i];
+ _PizChannelData cd = _channelData[i];
+ cd.start = tmpBufferEnd;
+ cd.end = cd.start;
+
+ cd.nx = numSamples(ch.xSampling, minX, maxX);
+ cd.ny = numSamples(ch.ySampling, minY, maxY);
+ cd.ys = ch.ySampling;
+
+ cd.size = ch.size ~/ 2; //2=size(HALF)
+
+ tmpBufferEnd += cd.nx * cd.ny * cd.size;
+ }
+
+ int minNonZero = inPtr.readUint16();
+ int maxNonZero = inPtr.readUint16();
+
+ if (maxNonZero >= BITMAP_SIZE) {
+ throw new ImageException("Error in header for PIZ-compressed data "
+ "(invalid bitmap size).");
+ }
+
+ Uint8List bitmap = Uint8List(BITMAP_SIZE);
+ if (minNonZero <= maxNonZero) {
+ InputBuffer b = inPtr.readBytes(maxNonZero - minNonZero + 1);
+ for (int i = 0, j = minNonZero, len = b.length; i < len; ++i) {
+ bitmap[j++] = b[i];
+ }
+ }
+
+ Uint16List lut = Uint16List(USHORT_RANGE);
+ int maxValue = _reverseLutFromBitmap(bitmap, lut);
+
+ // Huffman decoding
+ int length = inPtr.readUint32();
+ ExrHuffman.uncompress(inPtr, length, _tmpBuffer, tmpBufferEnd);
+
+ // Wavelet decoding
+ for (int i = 0; i < numChannels; ++i) {
+ _PizChannelData cd = _channelData[i];
+ for (int j = 0; j < cd.size; ++j) {
+ ExrWavelet.decode(_tmpBuffer, cd.start + j, cd.nx, cd.size, cd.ny,
+ cd.nx * cd.size, maxValue);
+ }
+ }
+
+ // Expand the pixel data to their original range
+ _applyLut(lut, _tmpBuffer, tmpBufferEnd);
+
+ if (_output == null) {
+ _output = OutputBuffer(size: (_maxScanLineSize * _numScanLines) +
+ (65536 + 8192));
+ }
+ _output.rewind();
+
+ //int count = 0;
+ // Rearrange the pixel data into the format expected by the caller.
+ for (int y = minY; y <= maxY; ++y) {
+ for (int i = 0; i < numChannels; ++i) {
+ _PizChannelData cd = _channelData[i];
+
+ if ((y % cd.ys) != 0) {
+ continue;
+ }
+
+ for (int x = cd.nx * cd.size; x > 0; --x) {
+ _output.writeUint16(_tmpBuffer[cd.end++]);
+ }
+ }
+ }
+
+ return _output.getBytes();
+ }
+
+ void _applyLut(List<int> lut, List<int> data, int nData) {
+ for (int i = 0; i < nData; ++i) {
+ data[i] = lut[data[i]];
+ }
+ }
+
+ int _reverseLutFromBitmap(Uint8List bitmap, Uint16List lut) {
+ int k = 0;
+ for (int i = 0; i < USHORT_RANGE; ++i) {
+ if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7))) != 0) {
+ lut[k++] = i;
+ }
+ }
+
+ int n = k - 1;
+
+ while (k < USHORT_RANGE) {
+ lut[k++] = 0;
+ }
+
+ return n; // maximum k where lut[k] is non-zero,
+ }
+
+ static const int USHORT_RANGE = 1 << 16;
+ static const int BITMAP_SIZE = 8192;
+
+ OutputBuffer _output;
+ int _maxScanLineSize;
+ int _numScanLines;
+ List<_PizChannelData> _channelData;
+ Uint16List _tmpBuffer;
+}
+
+class _PizChannelData {
+ int start;
+ int end;
+ int nx;
+ int ny;
+ int ys;
+ int size;
+}
diff --git a/image/lib/src/formats/exr/exr_pxr24_compressor.dart b/image/lib/src/formats/exr/exr_pxr24_compressor.dart
old mode 100644
new mode 100755
index 6aafd0b..b3cd998
--- a/image/lib/src/formats/exr/exr_pxr24_compressor.dart
+++ b/image/lib/src/formats/exr/exr_pxr24_compressor.dart
@@ -1,135 +1,135 @@
-import 'dart:typed_data';
-
-import 'package:archive/archive.dart';
-
-import '../../image_exception.dart';
-import '../../util/input_buffer.dart';
-import '../../util/output_buffer.dart';
-import 'exr_channel.dart';
-import 'exr_compressor.dart';
-import 'exr_part.dart';
-
-abstract class ExrPxr24Compressor extends ExrCompressor {
- factory ExrPxr24Compressor(ExrPart header, int maxScanLineSize,
- int numScanLines) = InternalExrPxr24Compressor;
-}
-
-class InternalExrPxr24Compressor extends InternalExrCompressor implements ExrPxr24Compressor {
- InternalExrPxr24Compressor(ExrPart header, this._maxScanLineSize, this._numScanLines) :
- super(header) {
- }
-
- int numScanLines() => _numScanLines;
-
- Uint8List compress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- throw new ImageException('Pxr24 compression not yet supported.');
- }
-
- Uint8List uncompress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- List<int> data = _zlib.decodeBytes(inPtr.toUint8List());
- if (data == null) {
- throw new ImageException('Error decoding pxr24 compressed data');
- }
-
- if (_output == null) {
- _output = new OutputBuffer(size: _numScanLines * _maxScanLineSize);
- }
- _output.rewind();
-
- int tmpEnd = 0;
- List<int> ptr = [0, 0, 0, 0];
- Uint32List pixel = new Uint32List(1);
- Uint8List pixelBytes = new Uint8List.view(pixel.buffer);
-
- if (width == null) {
- width = header.width;
- }
- if (height == null) {
- height = header.linesInBuffer;
- }
-
- int minX = x;
- int maxX = x + width - 1;
- int minY = y;
- int maxY = y + height - 1;
-
- if (maxX > header.width) {
- maxX = header.width - 1;
- }
- if (maxY > header.height) {
- maxY = header.height - 1;
- }
-
- decodedWidth = (maxX - minX) + 1;
- decodedHeight = (maxY - minY) + 1;
-
- int numChannels = header.channels.length;
- for (int yi = minY; yi <= maxY; ++yi) {
-
- for (int ci = 0; ci < numChannels; ++ci) {
- ExrChannel ch = header.channels[ci];
- if ((y % ch.ySampling) != 0) {
- continue;
- }
-
- int n = numSamples(ch.xSampling, minX, maxX);
- pixel[0] = 0;
-
- switch (ch.type) {
- case ExrChannel.TYPE_UINT:
- ptr[0] = tmpEnd;
- ptr[1] = ptr[0] + n;
- ptr[2] = ptr[1] + n;
- tmpEnd = ptr[2] + n;
- for (int j = 0; j < n; ++j) {
- int diff = (data[ptr[0]++] << 24) |
- (data[ptr[1]++] << 16) |
- (data[ptr[2]++] << 8);
- pixel[0] += diff;
- for (int k = 0; k < 4; ++k) {
- _output.writeByte(pixelBytes[k]);
- }
- }
- break;
- case ExrChannel.TYPE_HALF:
- ptr[0] = tmpEnd;
- ptr[1] = ptr[0] + n;
- tmpEnd = ptr[1] + n;
- for (int j = 0; j < n; ++j) {
- int diff = (data[ptr[0]++] << 8) | data[ptr[1]++];
- pixel[0] += diff;
-
- for (int k = 0; k < 2; ++k) {
- _output.writeByte(pixelBytes[k]);
- }
- }
- break;
- case ExrChannel.TYPE_FLOAT:
- ptr[0] = tmpEnd;
- ptr[1] = ptr[0] + n;
- ptr[2] = ptr[1] + n;
- tmpEnd = ptr[2] + n;
- for (int j = 0; j < n; ++j) {
- int diff = (data[ptr[0]++] << 24) |
- (data[ptr[1]++] << 16) |
- (data[ptr[2]++] << 8);
- pixel[0] += diff;
- for (int k = 0; k < 4; ++k) {
- _output.writeByte(pixelBytes[k]);
- }
- }
- break;
- }
- }
- }
-
- return _output.getBytes();
- }
-
- ZLibDecoder _zlib = new ZLibDecoder();
- int _maxScanLineSize;
- int _numScanLines;
- OutputBuffer _output;
-}
+import 'dart:typed_data';
+
+import 'package:archive/archive.dart';
+
+import '../../image_exception.dart';
+import '../../util/input_buffer.dart';
+import '../../util/output_buffer.dart';
+import 'exr_channel.dart';
+import 'exr_compressor.dart';
+import 'exr_part.dart';
+
+abstract class ExrPxr24Compressor extends ExrCompressor {
+ factory ExrPxr24Compressor(ExrPart header, int maxScanLineSize,
+ int numScanLines) = InternalExrPxr24Compressor;
+}
+
+class InternalExrPxr24Compressor extends InternalExrCompressor implements ExrPxr24Compressor {
+ InternalExrPxr24Compressor(ExrPart header, this._maxScanLineSize, this._numScanLines) :
+ super(header) {
+ }
+
+ int numScanLines() => _numScanLines;
+
+ Uint8List compress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ throw new ImageException('Pxr24 compression not yet supported.');
+ }
+
+ Uint8List uncompress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ List<int> data = _zlib.decodeBytes(inPtr.toUint8List());
+ if (data == null) {
+ throw new ImageException('Error decoding pxr24 compressed data');
+ }
+
+ if (_output == null) {
+ _output = OutputBuffer(size: _numScanLines * _maxScanLineSize);
+ }
+ _output.rewind();
+
+ int tmpEnd = 0;
+ List<int> ptr = [0, 0, 0, 0];
+ Uint32List pixel = Uint32List(1);
+ Uint8List pixelBytes = Uint8List.view(pixel.buffer);
+
+ if (width == null) {
+ width = header.width;
+ }
+ if (height == null) {
+ height = header.linesInBuffer;
+ }
+
+ int minX = x;
+ int maxX = x + width - 1;
+ int minY = y;
+ int maxY = y + height - 1;
+
+ if (maxX > header.width) {
+ maxX = header.width - 1;
+ }
+ if (maxY > header.height) {
+ maxY = header.height - 1;
+ }
+
+ decodedWidth = (maxX - minX) + 1;
+ decodedHeight = (maxY - minY) + 1;
+
+ int numChannels = header.channels.length;
+ for (int yi = minY; yi <= maxY; ++yi) {
+
+ for (int ci = 0; ci < numChannels; ++ci) {
+ ExrChannel ch = header.channels[ci];
+ if ((y % ch.ySampling) != 0) {
+ continue;
+ }
+
+ int n = numSamples(ch.xSampling, minX, maxX);
+ pixel[0] = 0;
+
+ switch (ch.type) {
+ case ExrChannel.TYPE_UINT:
+ ptr[0] = tmpEnd;
+ ptr[1] = ptr[0] + n;
+ ptr[2] = ptr[1] + n;
+ tmpEnd = ptr[2] + n;
+ for (int j = 0; j < n; ++j) {
+ int diff = (data[ptr[0]++] << 24) |
+ (data[ptr[1]++] << 16) |
+ (data[ptr[2]++] << 8);
+ pixel[0] += diff;
+ for (int k = 0; k < 4; ++k) {
+ _output.writeByte(pixelBytes[k]);
+ }
+ }
+ break;
+ case ExrChannel.TYPE_HALF:
+ ptr[0] = tmpEnd;
+ ptr[1] = ptr[0] + n;
+ tmpEnd = ptr[1] + n;
+ for (int j = 0; j < n; ++j) {
+ int diff = (data[ptr[0]++] << 8) | data[ptr[1]++];
+ pixel[0] += diff;
+
+ for (int k = 0; k < 2; ++k) {
+ _output.writeByte(pixelBytes[k]);
+ }
+ }
+ break;
+ case ExrChannel.TYPE_FLOAT:
+ ptr[0] = tmpEnd;
+ ptr[1] = ptr[0] + n;
+ ptr[2] = ptr[1] + n;
+ tmpEnd = ptr[2] + n;
+ for (int j = 0; j < n; ++j) {
+ int diff = (data[ptr[0]++] << 24) |
+ (data[ptr[1]++] << 16) |
+ (data[ptr[2]++] << 8);
+ pixel[0] += diff;
+ for (int k = 0; k < 4; ++k) {
+ _output.writeByte(pixelBytes[k]);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return _output.getBytes();
+ }
+
+ ZLibDecoder _zlib = ZLibDecoder();
+ int _maxScanLineSize;
+ int _numScanLines;
+ OutputBuffer _output;
+}
diff --git a/image/lib/src/formats/exr/exr_rle_compressor.dart b/image/lib/src/formats/exr/exr_rle_compressor.dart
old mode 100644
new mode 100755
index e94cad8..3b537da
--- a/image/lib/src/formats/exr/exr_rle_compressor.dart
+++ b/image/lib/src/formats/exr/exr_rle_compressor.dart
@@ -1,100 +1,100 @@
-import 'dart:typed_data';
-
-import '../../image_exception.dart';
-import '../../util/input_buffer.dart';
-import '../../util/output_buffer.dart';
-import 'exr_compressor.dart';
-import 'exr_part.dart';
-
-abstract class ExrRleCompressor extends ExrCompressor {
- factory ExrRleCompressor(ExrPart header, int maxScanLineSize) = InternalExrRleCompressor;
-}
-
-class InternalExrRleCompressor extends InternalExrCompressor implements ExrRleCompressor {
- InternalExrRleCompressor(ExrPart header, int maxScanLineSize) :
- super(header) {
- }
-
- int numScanLines() => 1;
-
- Uint8List compress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- throw new ImageException('Rle compression not yet supported.');
- }
-
- Uint8List uncompress(InputBuffer inPtr, int x, int y,
- [int width, int height]) {
- OutputBuffer out = new OutputBuffer(size: inPtr.length * 2);
-
- if (width == null) {
- width = header.width;
- }
- if (height == null) {
- height = header.linesInBuffer;
- }
-
- int minX = x;
- int maxX = x + width - 1;
- int minY = y;
- int maxY = y + height - 1;
-
- if (maxX > header.width) {
- maxX = header.width - 1;
- }
- if (maxY > header.height) {
- maxY = header.height - 1;
- }
-
- decodedWidth = (maxX - minX) + 1;
- decodedHeight = (maxY - minY) + 1;
-
- while (!inPtr.isEOS) {
- int n = inPtr.readInt8();
- if (n < 0) {
- int count = -n;
- while (count-- > 0) {
- out.writeByte(inPtr.readByte());
- }
- } else {
- int count = n;
- while (count-- >= 0) {
- out.writeByte(inPtr.readByte());
- }
- }
- }
-
- Uint8List data = out.getBytes();
-
- // Predictor
- for (int i = 1, len = data.length; i < len; ++i) {
- data[i] = data[i - 1] + data[i] - 128;
- }
-
- // Reorder the pixel data
- if (_outCache == null || _outCache.length != data.length) {
- _outCache = new Uint8List(data.length);
- }
-
- final int len = data.length;
- int t1 = 0;
- int t2 = (len + 1) ~/ 2;
- int si = 0;
-
- while (true) {
- if (si < len) {
- _outCache[si++] = data[t1++];
- } else {
- break;
- }
- if (si < len) {
- _outCache[si++] = data[t2++];
- } else {
- break;
- }
- }
-
- return _outCache;
- }
-
- Uint8List _outCache;
-}
+import 'dart:typed_data';
+
+import '../../image_exception.dart';
+import '../../util/input_buffer.dart';
+import '../../util/output_buffer.dart';
+import 'exr_compressor.dart';
+import 'exr_part.dart';
+
+abstract class ExrRleCompressor extends ExrCompressor {
+ factory ExrRleCompressor(ExrPart header, int maxScanLineSize) = InternalExrRleCompressor;
+}
+
+class InternalExrRleCompressor extends InternalExrCompressor implements ExrRleCompressor {
+ InternalExrRleCompressor(ExrPart header, int maxScanLineSize) :
+ super(header) {
+ }
+
+ int numScanLines() => 1;
+
+ Uint8List compress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ throw new ImageException('Rle compression not yet supported.');
+ }
+
+ Uint8List uncompress(InputBuffer inPtr, int x, int y,
+ [int width, int height]) {
+ OutputBuffer out = OutputBuffer(size: inPtr.length * 2);
+
+ if (width == null) {
+ width = header.width;
+ }
+ if (height == null) {
+ height = header.linesInBuffer;
+ }
+
+ int minX = x;
+ int maxX = x + width - 1;
+ int minY = y;
+ int maxY = y + height - 1;
+
+ if (maxX > header.width) {
+ maxX = header.width - 1;
+ }
+ if (maxY > header.height) {
+ maxY = header.height - 1;
+ }
+
+ decodedWidth = (maxX - minX) + 1;
+ decodedHeight = (maxY - minY) + 1;
+
+ while (!inPtr.isEOS) {
+ int n = inPtr.readInt8();
+ if (n < 0) {
+ int count = -n;
+ while (count-- > 0) {
+ out.writeByte(inPtr.readByte());
+ }
+ } else {
+ int count = n;
+ while (count-- >= 0) {
+ out.writeByte(inPtr.readByte());
+ }
+ }
+ }
+
+ Uint8List data = out.getBytes();
+
+ // Predictor
+ for (int i = 1, len = data.length; i < len; ++i) {
+ data[i] = data[i - 1] + data[i] - 128;
+ }
+
+ // Reorder the pixel data
+ if (_outCache == null || _outCache.length != data.length) {
+ _outCache = Uint8List(data.length);
+ }
+
+ final int len = data.length;
+ int t1 = 0;
+ int t2 = (len + 1) ~/ 2;
+ int si = 0;
+
+ while (true) {
+ if (si < len) {
+ _outCache[si++] = data[t1++];
+ } else {
+ break;
+ }
+ if (si < len) {
+ _outCache[si++] = data[t2++];
+ } else {
+ break;
+ }
+ }
+
+ return _outCache;
+ }
+
+ Uint8List _outCache;
+}
diff --git a/image/lib/src/formats/exr/exr_wavelet.dart b/image/lib/src/formats/exr/exr_wavelet.dart
old mode 100644
new mode 100755
index a8de536..407daf3
--- a/image/lib/src/formats/exr/exr_wavelet.dart
+++ b/image/lib/src/formats/exr/exr_wavelet.dart
@@ -1,154 +1,154 @@
-import 'dart:typed_data';
-
-import '../../internal/bit_operators.dart';
-
-class ExrWavelet {
- static void decode(Uint16List input, int si, int nx, int ox, int ny, int oy,
- int mx) {
- bool w14 = (mx < (1 << 14));
- int n = (nx > ny) ? ny : nx;
- int p = 1;
- int p2;
-
- // Search max level
- while (p <= n) {
- p <<= 1;
- }
-
- p >>= 1;
- p2 = p;
- p >>= 1;
-
- List<int> a_b = [0, 0];
-
- // Hierarchical loop on smaller dimension n
- while (p >= 1) {
- int py = si;
- int ey = si + oy * (ny - p2);
- int oy1 = oy * p;
- int oy2 = oy * p2;
- int ox1 = ox * p;
- int ox2 = ox * p2;
- int i00, i01, i10, i11;
-
- // Y loop
- for (; py <= ey; py += oy2) {
- int px = py;
- int ex = py + ox * (nx - p2);
-
- // X loop
- for (; px <= ex; px += ox2) {
- int p01 = px + ox1;
- int p10 = px + oy1;
- int p11 = p10 + ox1;
-
- // 2D wavelet decoding
- if (w14) {
- wdec14(input[px], input[p10], a_b);
- i00 = a_b[0];
- i10 = a_b[1];
-
- wdec14(input[p01], input[p11], a_b);
- i01 = a_b[0];
- i11 = a_b[1];
-
- wdec14(i00, i01, a_b);
- input[px] = a_b[0];
- input[p01] = a_b[1];
-
- wdec14(i10, i11, a_b);
- input[p10] = a_b[0];
- input[p11] = a_b[1];
- } else {
- wdec16(input[px], input[p10], a_b);
- i00 = a_b[0];
- i10 = a_b[1];
-
- wdec16(input[p01], input[p11], a_b);
- i01 = a_b[0];
- i11 = a_b[1];
-
- wdec16(i00, i01, a_b);
- input[px] = a_b[0];
- input[p01] = a_b[1];
-
- wdec16(i10, i11, a_b);
- input[p10] = a_b[0];
- input[p11] = a_b[1];
- }
- }
-
- // Decode (1D) odd column (still in Y loop)
- if (nx & p != 0) {
- int p10 = px + oy1;
-
- if (w14) {
- wdec14(input[px], input[p10], a_b);
- i00 = a_b[0];
- input[p10] = a_b[1];
- } else {
- wdec16(input[px], input[p10], a_b);
- i00 = a_b[0];
- input[p10] = a_b[1];
- }
-
- input[px] = i00;
- }
- }
-
- // Decode (1D) odd line (must loop in X)
- if (ny & p != 0) {
- int px = py;
- int ex = py + ox * (nx - p2);
-
- for (; px <= ex; px += ox2) {
- int p01 = px + ox1;
-
- if (w14 != 0) {
- wdec14(input[px], input[p01], a_b);
- i00 = a_b[0];
- input[p01] = a_b[1];
- } else {
- wdec16(input[px], input[p01], a_b);
- i00 = a_b[0];
- input[p01] = a_b[1];
- }
-
- input[px] = i00;
- }
- }
-
- // Next level
- p2 = p;
- p >>= 1;
- }
- }
-
- static const int NBITS = 16;
- static const int A_OFFSET = 1 << (NBITS - 1);
- static const int M_OFFSET = 1 << (NBITS - 1);
- static const int MOD_MASK = (1 << NBITS) - 1;
-
- static void wdec14(int l, int h, List<int> a_b) {
- int ls = uint16ToInt16(l);
- int hs = uint16ToInt16(h);
-
- int hi = hs;
- int ai = ls + (hi & 1) + (hi >> 1);
-
- int as = ai;
- int bs = ai - hi;
-
- a_b[0] = as;
- a_b[1] = bs;
- }
-
- static void wdec16(int l, int h, List<int> a_b) {
- int m = l;
- int d = h;
- int bb = (m - (d >> 1)) & MOD_MASK;
- int aa = (d + bb - A_OFFSET) & MOD_MASK;
- a_b[1] = bb;
- a_b[0] = aa;
- }
-}
+import 'dart:typed_data';
+
+import '../../internal/bit_operators.dart';
+
+class ExrWavelet {
+ static void decode(Uint16List input, int si, int nx, int ox, int ny, int oy,
+ int mx) {
+ bool w14 = (mx < (1 << 14));
+ int n = (nx > ny) ? ny : nx;
+ int p = 1;
+ int p2;
+
+ // Search max level
+ while (p <= n) {
+ p <<= 1;
+ }
+
+ p >>= 1;
+ p2 = p;
+ p >>= 1;
+
+ List<int> a_b = [0, 0];
+
+ // Hierarchical loop on smaller dimension n
+ while (p >= 1) {
+ int py = si;
+ int ey = si + oy * (ny - p2);
+ int oy1 = oy * p;
+ int oy2 = oy * p2;
+ int ox1 = ox * p;
+ int ox2 = ox * p2;
+ int i00, i01, i10, i11;
+
+ // Y loop
+ for (; py <= ey; py += oy2) {
+ int px = py;
+ int ex = py + ox * (nx - p2);
+
+ // X loop
+ for (; px <= ex; px += ox2) {
+ int p01 = px + ox1;
+ int p10 = px + oy1;
+ int p11 = p10 + ox1;
+
+ // 2D wavelet decoding
+ if (w14) {
+ wdec14(input[px], input[p10], a_b);
+ i00 = a_b[0];
+ i10 = a_b[1];
+
+ wdec14(input[p01], input[p11], a_b);
+ i01 = a_b[0];
+ i11 = a_b[1];
+
+ wdec14(i00, i01, a_b);
+ input[px] = a_b[0];
+ input[p01] = a_b[1];
+
+ wdec14(i10, i11, a_b);
+ input[p10] = a_b[0];
+ input[p11] = a_b[1];
+ } else {
+ wdec16(input[px], input[p10], a_b);
+ i00 = a_b[0];
+ i10 = a_b[1];
+
+ wdec16(input[p01], input[p11], a_b);
+ i01 = a_b[0];
+ i11 = a_b[1];
+
+ wdec16(i00, i01, a_b);
+ input[px] = a_b[0];
+ input[p01] = a_b[1];
+
+ wdec16(i10, i11, a_b);
+ input[p10] = a_b[0];
+ input[p11] = a_b[1];
+ }
+ }
+
+ // Decode (1D) odd column (still in Y loop)
+ if (nx & p != 0) {
+ int p10 = px + oy1;
+
+ if (w14) {
+ wdec14(input[px], input[p10], a_b);
+ i00 = a_b[0];
+ input[p10] = a_b[1];
+ } else {
+ wdec16(input[px], input[p10], a_b);
+ i00 = a_b[0];
+ input[p10] = a_b[1];
+ }
+
+ input[px] = i00;
+ }
+ }
+
+ // Decode (1D) odd line (must loop in X)
+ if (ny & p != 0) {
+ int px = py;
+ int ex = py + ox * (nx - p2);
+
+ for (; px <= ex; px += ox2) {
+ int p01 = px + ox1;
+
+ if (w14 != 0) {
+ wdec14(input[px], input[p01], a_b);
+ i00 = a_b[0];
+ input[p01] = a_b[1];
+ } else {
+ wdec16(input[px], input[p01], a_b);
+ i00 = a_b[0];
+ input[p01] = a_b[1];
+ }
+
+ input[px] = i00;
+ }
+ }
+
+ // Next level
+ p2 = p;
+ p >>= 1;
+ }
+ }
+
+ static const int NBITS = 16;
+ static const int A_OFFSET = 1 << (NBITS - 1);
+ static const int M_OFFSET = 1 << (NBITS - 1);
+ static const int MOD_MASK = (1 << NBITS) - 1;
+
+ static void wdec14(int l, int h, List<int> a_b) {
+ int ls = uint16ToInt16(l);
+ int hs = uint16ToInt16(h);
+
+ int hi = hs;
+ int ai = ls + (hi & 1) + (hi >> 1);
+
+ int as = ai;
+ int bs = ai - hi;
+
+ a_b[0] = as;
+ a_b[1] = bs;
+ }
+
+ static void wdec16(int l, int h, List<int> a_b) {
+ int m = l;
+ int d = h;
+ int bb = (m - (d >> 1)) & MOD_MASK;
+ int aa = (d + bb - A_OFFSET) & MOD_MASK;
+ a_b[1] = bb;
+ a_b[0] = aa;
+ }
+}
diff --git a/image/lib/src/formats/exr/exr_zip_compressor.dart b/image/lib/src/formats/exr/exr_zip_compressor.dart
old mode 100644
new mode 100755
index edb0053..085aaf9
--- a/image/lib/src/formats/exr/exr_zip_compressor.dart
+++ b/image/lib/src/formats/exr/exr_zip_compressor.dart
@@ -1,88 +1,88 @@
-import 'dart:typed_data';
-
-import 'package:archive/archive.dart';
-
-import '../../image_exception.dart';
-import '../../util/input_buffer.dart';
-import 'exr_compressor.dart';
-import 'exr_part.dart';
-
-abstract class ExrZipCompressor extends ExrCompressor {
- factory ExrZipCompressor(ExrPart header, int maxScanLineSize,
- int numScanLines) = InternalExrZipCompressor;
-}
-
-class InternalExrZipCompressor extends InternalExrCompressor implements ExrZipCompressor {
- ZLibDecoder zlib = new ZLibDecoder();
-
- InternalExrZipCompressor(ExrPart header, int maxScanLineSize, this._numScanLines) :
- super(header) {
- }
-
- int numScanLines() => _numScanLines;
-
- Uint8List compress(InputBuffer input, int x, int y,
- [int width, int height]) {
- throw new ImageException('Zip compression not yet supported');
- }
-
- Uint8List uncompress(InputBuffer input, int x, int y,
- [int width, int height]) {
- Uint8List data = zlib.decodeBytes(input.toUint8List());
-
- if (width == null) {
- width = header.width;
- }
- if (height == null) {
- height = header.linesInBuffer;
- }
-
- int minX = x;
- int maxX = x + width - 1;
- int minY = y;
- int maxY = y + height - 1;
-
- if (maxX > header.width) {
- maxX = header.width - 1;
- }
- if (maxY > header.height) {
- maxY = header.height - 1;
- }
-
- decodedWidth = (maxX - minX) + 1;
- decodedHeight = (maxY - minY) + 1;
-
- // Predictor
- for (int i = 1, len = data.length; i < len; ++i) {
- data[i] = data[i - 1] + data[i] - 128;
- }
-
- // Reorder the pixel data
- if (_outCache == null || _outCache.length != data.length) {
- _outCache = new Uint8List(data.length);
- }
-
- final int len = data.length;
- int t1 = 0;
- int t2 = (len + 1) ~/ 2;
- int si = 0;
-
- while (true) {
- if (si < len) {
- _outCache[si++] = data[t1++];
- } else {
- break;
- }
- if (si < len) {
- _outCache[si++] = data[t2++];
- } else {
- break;
- }
- }
-
- return _outCache;
- }
-
- int _numScanLines;
- Uint8List _outCache;
-}
+import 'dart:typed_data';
+
+import 'package:archive/archive.dart';
+
+import '../../image_exception.dart';
+import '../../util/input_buffer.dart';
+import 'exr_compressor.dart';
+import 'exr_part.dart';
+
+abstract class ExrZipCompressor extends ExrCompressor {
+ factory ExrZipCompressor(ExrPart header, int maxScanLineSize,
+ int numScanLines) = InternalExrZipCompressor;
+}
+
+class InternalExrZipCompressor extends InternalExrCompressor implements ExrZipCompressor {
+ ZLibDecoder zlib = ZLibDecoder();
+
+ InternalExrZipCompressor(ExrPart header, int maxScanLineSize, this._numScanLines) :
+ super(header) {
+ }
+
+ int numScanLines() => _numScanLines;
+
+ Uint8List compress(InputBuffer input, int x, int y,
+ [int width, int height]) {
+ throw new ImageException('Zip compression not yet supported');
+ }
+
+ Uint8List uncompress(InputBuffer input, int x, int y,
+ [int width, int height]) {
+ Uint8List data = zlib.decodeBytes(input.toUint8List());
+
+ if (width == null) {
+ width = header.width;
+ }
+ if (height == null) {
+ height = header.linesInBuffer;
+ }
+
+ int minX = x;
+ int maxX = x + width - 1;
+ int minY = y;
+ int maxY = y + height - 1;
+
+ if (maxX > header.width) {
+ maxX = header.width - 1;
+ }
+ if (maxY > header.height) {
+ maxY = header.height - 1;
+ }
+
+ decodedWidth = (maxX - minX) + 1;
+ decodedHeight = (maxY - minY) + 1;
+
+ // Predictor
+ for (int i = 1, len = data.length; i < len; ++i) {
+ data[i] = data[i - 1] + data[i] - 128;
+ }
+
+ // Reorder the pixel data
+ if (_outCache == null || _outCache.length != data.length) {
+ _outCache = Uint8List(data.length);
+ }
+
+ final int len = data.length;
+ int t1 = 0;
+ int t2 = (len + 1) ~/ 2;
+ int si = 0;
+
+ while (true) {
+ if (si < len) {
+ _outCache[si++] = data[t1++];
+ } else {
+ break;
+ }
+ if (si < len) {
+ _outCache[si++] = data[t2++];
+ } else {
+ break;
+ }
+ }
+
+ return _outCache;
+ }
+
+ int _numScanLines;
+ Uint8List _outCache;
+}
diff --git a/image/lib/src/formats/exr_decoder.dart b/image/lib/src/formats/exr_decoder.dart
old mode 100644
new mode 100755
index ca38ef2..ef5dc4c
--- a/image/lib/src/formats/exr_decoder.dart
+++ b/image/lib/src/formats/exr_decoder.dart
@@ -1,98 +1,98 @@
-import '../animation.dart';
-import '../image.dart';
-import '../hdr/hdr_image.dart';
-import '../hdr/hdr_to_image.dart';
-import 'decode_info.dart';
-import 'decoder.dart';
-import 'exr/exr_image.dart';
-
-/**
- * Decode an OpenEXR formatted image.
- *
- * OpenEXR is a format developed by Industrial Light & Magic, with collaboration
- * from other companies such as Weta and Pixar, for storing hight dynamic
- * range (HDR) images for use in digital visual effects production. It supports
- * a wide range of features, including 16-bit or 32-bit floating-point channels;
- * lossless and lossy data compression; arbitrary image channels for storing
- * any combination of data, such as red, green, blue, alpha, luminance and
- * chroma channels, depth, surface normal, motion vectors, etc. It can also
- * store images in scanline or tiled format; multiple views for stereo images;
- * multiple parts; etc.
- *
- * Because OpenEXR is a high-dynamic-range (HDR) format, it must be converted
- * to a low-dynamic-range (LDR) image for display, or for use as an OpenGL
- * texture (for example). This process is called tone-mapping. Currently only
- * a simple tone-mapping function is provided with a single [exposure]
- * parameter. More tone-mapping functionality will be added.
- */
-class ExrDecoder extends Decoder {
- ExrImage exrImage;
- /// Exposure for tone-mapping the hdr image to an [Image], applied during
- /// [decodeFrame].
- double exposure;
- double gamma;
- bool reinhard;
- double bloomAmount;
- double bloomRadius;
-
- ExrDecoder({this.exposure: 1.0});
-
- bool isValidFile(List<int> data) {
- return ExrImage.isValidFile(data);
- }
-
- DecodeInfo startDecode(List<int> data) {
- exrImage = new ExrImage(data);
- return exrImage;
- }
-
- int numFrames() => exrImage != null ? exrImage.parts.length : 0;
-
- Image decodeFrame(int frame) {
- if (exrImage == null) {
- return null;
- }
-
- return hdrToImage(exrImage.getPart(frame).framebuffer,
- exposure: exposure);
- }
-
- HdrImage decodeHdrFrame(int frame) {
- if (exrImage == null) {
- return null;
- }
- if (frame >= exrImage.parts.length) {
- return null;
- }
- return exrImage.parts[frame].framebuffer;
- }
-
- Image decodeImage(List<int> bytes, {int frame: 0}) {
- if (startDecode(bytes) == null) {
- return null;
- }
-
- return decodeFrame(frame);
- }
-
- HdrImage decodeHdrImage(List<int> bytes, {int frame: 0}) {
- if (startDecode(bytes) == null) {
- return null;
- }
- return decodeHdrFrame(frame);
- }
-
- Animation decodeAnimation(List<int> data) {
- Image image = decodeImage(data);
- if (image == null) {
- return null;
- }
-
- Animation anim = new Animation();
- anim.width = image.width;
- anim.height = image.height;
- anim.addFrame(image);
-
- return anim;
- }
-}
+import '../animation.dart';
+import '../image.dart';
+import '../hdr/hdr_image.dart';
+import '../hdr/hdr_to_image.dart';
+import 'decode_info.dart';
+import 'decoder.dart';
+import 'exr/exr_image.dart';
+
+/**
+ * Decode an OpenEXR formatted image.
+ *
+ * OpenEXR is a format developed by Industrial Light & Magic, with collaboration
+ * from other companies such as Weta and Pixar, for storing hight dynamic
+ * range (HDR) images for use in digital visual effects production. It supports
+ * a wide range of features, including 16-bit or 32-bit floating-point channels;
+ * lossless and lossy data compression; arbitrary image channels for storing
+ * any combination of data, such as red, green, blue, alpha, luminance and
+ * chroma channels, depth, surface normal, motion vectors, etc. It can also
+ * store images in scanline or tiled format; multiple views for stereo images;
+ * multiple parts; etc.
+ *
+ * Because OpenEXR is a high-dynamic-range (HDR) format, it must be converted
+ * to a low-dynamic-range (LDR) image for display, or for use as an OpenGL
+ * texture (for example). This process is called tone-mapping. Currently only
+ * a simple tone-mapping function is provided with a single [exposure]
+ * parameter. More tone-mapping functionality will be added.
+ */
+class ExrDecoder extends Decoder {
+ ExrImage exrImage;
+ /// Exposure for tone-mapping the hdr image to an [Image], applied during
+ /// [decodeFrame].
+ double exposure;
+ double gamma;
+ bool reinhard;
+ double bloomAmount;
+ double bloomRadius;
+
+ ExrDecoder({this.exposure: 1.0});
+
+ bool isValidFile(List<int> data) {
+ return ExrImage.isValidFile(data);
+ }
+
+ DecodeInfo startDecode(List<int> data) {
+ exrImage = ExrImage(data);
+ return exrImage;
+ }
+
+ int numFrames() => exrImage != null ? exrImage.parts.length : 0;
+
+ Image decodeFrame(int frame) {
+ if (exrImage == null) {
+ return null;
+ }
+
+ return hdrToImage(exrImage.getPart(frame).framebuffer,
+ exposure: exposure);
+ }
+
+ HdrImage decodeHdrFrame(int frame) {
+ if (exrImage == null) {
+ return null;
+ }
+ if (frame >= exrImage.parts.length) {
+ return null;
+ }
+ return exrImage.parts[frame].framebuffer;
+ }
+
+ Image decodeImage(List<int> bytes, {int frame: 0}) {
+ if (startDecode(bytes) == null) {
+ return null;
+ }
+
+ return decodeFrame(frame);
+ }
+
+ HdrImage decodeHdrImage(List<int> bytes, {int frame: 0}) {
+ if (startDecode(bytes) == null) {
+ return null;
+ }
+ return decodeHdrFrame(frame);
+ }
+
+ Animation decodeAnimation(List<int> data) {
+ Image image = decodeImage(data);
+ if (image == null) {
+ return null;
+ }
+
+ Animation anim = Animation();
+ anim.width = image.width;
+ anim.height = image.height;
+ anim.addFrame(image);
+
+ return anim;
+ }
+}
diff --git a/image/lib/src/formats/formats.dart b/image/lib/src/formats/formats.dart
old mode 100644
new mode 100755
index 51aa445..611864e
--- a/image/lib/src/formats/formats.dart
+++ b/image/lib/src/formats/formats.dart
@@ -25,39 +25,39 @@
// The various decoders will be creating a Uint8List for their InputStream
// if the data isn't already that type, so do it once here to avoid having to
// do it multiple times.
- Uint8List bytes = new Uint8List.fromList(data);
+ Uint8List bytes = Uint8List.fromList(data);
- JpegDecoder jpg = new JpegDecoder();
+ JpegDecoder jpg = JpegDecoder();
if (jpg.isValidFile(bytes)) {
return jpg;
}
- PngDecoder png = new PngDecoder();
+ PngDecoder png = PngDecoder();
if (png.isValidFile(bytes)) {
return png;
}
- GifDecoder gif = new GifDecoder();
+ GifDecoder gif = GifDecoder();
if (gif.isValidFile(bytes)) {
return gif;
}
- WebPDecoder webp = new WebPDecoder();
+ WebPDecoder webp = WebPDecoder();
if (webp.isValidFile(bytes)) {
return webp;
}
- TiffDecoder tiff = new TiffDecoder();
+ TiffDecoder tiff = TiffDecoder();
if (tiff.isValidFile(bytes)) {
return tiff;
}
- PsdDecoder psd = new PsdDecoder();
+ PsdDecoder psd = PsdDecoder();
if (psd.isValidFile(bytes)) {
return psd;
}
- ExrDecoder exr = new ExrDecoder();
+ ExrDecoder exr = ExrDecoder();
if (exr.isValidFile(bytes)) {
return exr;
}
diff --git a/image/lib/src/formats/gif/gif_color_map.dart b/image/lib/src/formats/gif/gif_color_map.dart
old mode 100644
new mode 100755
index 4a5acd3..5325a42
--- a/image/lib/src/formats/gif/gif_color_map.dart
+++ b/image/lib/src/formats/gif/gif_color_map.dart
@@ -10,7 +10,7 @@
GifColorMap(int numColors) :
this.numColors = numColors,
- colors = new Uint8List(numColors * 3) {
+ colors = Uint8List(numColors * 3) {
bitsPerPixel = _bitSize(numColors);
}
diff --git a/image/lib/src/formats/gif/gif_image_desc.dart b/image/lib/src/formats/gif/gif_image_desc.dart
old mode 100644
new mode 100755
index b8b252e..c4b0b88
--- a/image/lib/src/formats/gif/gif_image_desc.dart
+++ b/image/lib/src/formats/gif/gif_image_desc.dart
@@ -25,7 +25,7 @@
interlaced = (b & 0x40) != 0;
if (b & 0x80 != 0) {
- colorMap = new GifColorMap(1 << bitsPerPixel);
+ colorMap = GifColorMap(1 << bitsPerPixel);
for (int i = 0; i < colorMap.numColors; ++i) {
colorMap.setColor(i, input.readByte(), input.readByte(),
diff --git a/image/lib/src/formats/gif/gif_info.dart b/image/lib/src/formats/gif/gif_info.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/gif_decoder.dart b/image/lib/src/formats/gif_decoder.dart
old mode 100644
new mode 100755
index 5eb9e42..57d9f78
--- a/image/lib/src/formats/gif_decoder.dart
+++ b/image/lib/src/formats/gif_decoder.dart
@@ -26,8 +26,8 @@
* Is the given file a valid Gif image?
*/
bool isValidFile(List<int> bytes) {
- _input = new InputBuffer(bytes);
- info = new GifInfo();
+ _input = InputBuffer(bytes);
+ info = GifInfo();
return _getInfo();
}
@@ -44,9 +44,9 @@
* If the file is not a valid Gif image, null is returned.
*/
GifInfo startDecode(List<int> bytes) {
- _input = new InputBuffer(bytes);
+ _input = InputBuffer(bytes);
- info = new GifInfo();
+ info = GifInfo();
if (!_getInfo()) {
return null;
}
@@ -167,18 +167,18 @@
return null;
}
- Animation anim = new Animation();
+ Animation anim = Animation();
anim.width = info.width;
anim.height = info.height;
anim.loopCount = _repeat;
- Image lastImage = new Image(info.width, info.height);
+ Image lastImage = Image(info.width, info.height);
for (int i = 0; i < info.numFrames; ++i) {
//_frame = i;
if (lastImage == null) {
- lastImage = new Image(info.width, info.height);
+ lastImage = Image(info.width, info.height);
} else {
- lastImage = new Image.from(lastImage);
+ lastImage = Image.from(lastImage);
}
GifImageDesc frame = info.frames[i];
@@ -211,7 +211,7 @@
if (_input.isEOS) {
return null;
}
- InternalGifImageDesc gifImage = new InternalGifImageDesc(_input);
+ InternalGifImageDesc gifImage = InternalGifImageDesc(_input);
_input.skip(1);
_skipRemainder();
return gifImage;
@@ -259,8 +259,8 @@
_pixelCount = width * height;
- Image image = new Image(width, height);
- Uint8List line = new Uint8List(width);
+ Image image = Image(width, height);
+ Uint8List line = Uint8List(width);
if (gifImage.interlaced) {
int row = gifImage.y;
@@ -314,7 +314,7 @@
// Is there a global color map?
if (b & 0x80 != 0) {
- info.globalColorMap = new GifColorMap(1 << bitsPerPixel);
+ info.globalColorMap = GifColorMap(1 << bitsPerPixel);
// Get the global color map:
for (int i = 0; i < info.globalColorMap.numColors; ++i) {
@@ -581,10 +581,10 @@
}
void _initDecode() {
- _buffer = new Uint8List(256);
- _stack = new Uint8List(LZ_MAX_CODE);
- _suffix = new Uint8List(LZ_MAX_CODE + 1);
- _prefix = new Uint32List(LZ_MAX_CODE + 1);
+ _buffer = Uint8List(256);
+ _stack = Uint8List(LZ_MAX_CODE);
+ _suffix = Uint8List(LZ_MAX_CODE + 1);
+ _prefix = Uint32List(LZ_MAX_CODE + 1);
}
InputBuffer _input;
diff --git a/image/lib/src/formats/gif_encoder.dart b/image/lib/src/formats/gif_encoder.dart
old mode 100644
new mode 100755
index ee62520..bc19f6e
--- a/image/lib/src/formats/gif_encoder.dart
+++ b/image/lib/src/formats/gif_encoder.dart
@@ -15,12 +15,12 @@
void addFrame(Image image, {int duration}) {
if (output == null) {
- output = new OutputBuffer();
+ output = OutputBuffer();
if (duration != null) {
this.delay = duration;
}
- _lastColorMap = new NeuralQuantizer(image);
+ _lastColorMap = NeuralQuantizer(image);
_lastImage = _lastColorMap.getIndexMap(image);
_width = image.width;
_height = image.height;
@@ -38,7 +38,7 @@
if (duration != null) {
this.delay = duration;
}
- _lastColorMap = new NeuralQuantizer(image);
+ _lastColorMap = NeuralQuantizer(image);
_lastImage = _lastColorMap.getIndexMap(image);
}
@@ -121,13 +121,13 @@
_curAccum = 0;
_curBits = 0;
_blockSize = 0;
- _block = new Uint8List(256);
+ _block = Uint8List(256);
const int initCodeSize = 8;
output.writeByte(initCodeSize);
- Int32List hTab = new Int32List(HSIZE);
- Int32List codeTab = new Int32List(HSIZE);
+ Int32List hTab = Int32List(HSIZE);
+ Int32List codeTab = Int32List(HSIZE);
int remaining = width * height;
int curPixel = 0;
diff --git a/image/lib/src/formats/jpeg/jpeg.dart b/image/lib/src/formats/jpeg/jpeg.dart
old mode 100644
new mode 100755
index 848af20..73b4da5
--- a/image/lib/src/formats/jpeg/jpeg.dart
+++ b/image/lib/src/formats/jpeg/jpeg.dart
@@ -1,86 +1,86 @@
-class Jpeg {
- static const List<int> dctZigZag = const [
- 0, 1, 8, 16, 9, 2, 3, 10,
- 17, 24, 32, 25, 18, 11, 4, 5,
- 12, 19, 26, 33, 40, 48, 41, 34,
- 27, 20, 13, 6, 7, 14, 21, 28,
- 35, 42, 49, 56, 57, 50, 43, 36,
- 29, 22, 15, 23, 30, 37, 44, 51,
- 58, 59, 52, 45, 38, 31, 39, 46,
- 53, 60, 61, 54, 47, 55, 62, 63,
- 63, 63, 63, 63, 63, 63, 63, 63, // extra entries for safety in decoder
- 63, 63, 63, 63, 63, 63, 63, 63 ];
-
- static const int DCTSIZE = 8; // The basic DCT block is 8x8 samples
- static const int DCTSIZE2 = 64; // DCTSIZE squared; # of elements in a block
- static const int NUM_QUANT_TBLS = 4; // Quantization tables are numbered 0..3
- static const int NUM_HUFF_TBLS = 4; // Huffman tables are numbered 0..3
- static const int NUM_ARITH_TBLS = 16; // Arith-coding tables are numbered 0..15
- static const int MAX_COMPS_IN_SCAN = 4; // JPEG limit on # of components in one scan
- static const int MAX_SAMP_FACTOR = 4; // JPEG limit on sampling factors
-
- static const int M_SOF0 = 0xc0;
- static const int M_SOF1 = 0xc1;
- static const int M_SOF2 = 0xc2;
- static const int M_SOF3 = 0xc3;
-
- static const int M_SOF5 = 0xc5;
- static const int M_SOF6 = 0xc6;
- static const int M_SOF7 = 0xc7;
-
- static const int M_JPG = 0xc8;
- static const int M_SOF9 = 0xc9;
- static const int M_SOF10 = 0xca;
- static const int M_SOF11 = 0xcb;
-
- static const int M_SOF13 = 0xcd;
- static const int M_SOF14 = 0xce;
- static const int M_SOF15 = 0xcf;
-
- static const int M_DHT = 0xc4;
-
- static const int M_DAC = 0xcc;
-
- static const int M_RST0 = 0xd0;
- static const int M_RST1 = 0xd1;
- static const int M_RST2 = 0xd2;
- static const int M_RST3 = 0xd3;
- static const int M_RST4 = 0xd4;
- static const int M_RST5 = 0xd5;
- static const int M_RST6 = 0xd6;
- static const int M_RST7 = 0xd7;
-
- static const int M_SOI = 0xd8;
- static const int M_EOI = 0xd9;
- static const int M_SOS = 0xda;
- static const int M_DQT = 0xdb;
- static const int M_DNL = 0xdc;
- static const int M_DRI = 0xdd;
- static const int M_DHP = 0xde;
- static const int M_EXP = 0xdf;
-
- static const int M_APP0 = 0xe0; // JFIF, JFXX, CIFF, AVI1, Ocad
- static const int M_APP1 = 0xe1; // EXIF, ExtendedXMP, XMP, QVCI, FLIR
- static const int M_APP2 = 0xe2; // ICC_Profile, FPXR, MPF, PreviewImage
- static const int M_APP3 = 0xe3; // Meta, Stim, PreviewImage
- static const int M_APP4 = 0xe4; // Scalado, FPXR, PreviewImage
- static const int M_APP5 = 0xe5; // RMETA, PreviewImage
- static const int M_APP6 = 0xe6; // EPPIM, NITF, HP_TDHD, GoPro
- static const int M_APP7 = 0xe7; // Pentax, Qualcomm
- static const int M_APP8 = 0xe8; // SPIFF
- static const int M_APP9 = 0xe9; // MediaJukebox
- static const int M_APP10 = 0xea; // Comment
- static const int M_APP11 = 0xeb; // Jpeg-HDR
- static const int M_APP12 = 0xec; // PictureInfo, Ducky
- static const int M_APP13 = 0xed; // Photoshop, Adobe_CM
- static const int M_APP14 = 0xee; // ADOBE
- static const int M_APP15 = 0xef; // GraphicConverter
-
- static const int M_JPG0 = 0xf0;
- static const int M_JPG13 = 0xfd;
- static const int M_COM = 0xfe;
-
- static const int M_TEM = 0x01;
-
- static const int M_ERROR = 0x100;
-}
+class Jpeg {
+ static const dctZigZag = const [
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, // extra entries for safety in decoder
+ 63, 63, 63, 63, 63, 63, 63, 63 ];
+
+ static const int DCTSIZE = 8; // The basic DCT block is 8x8 samples
+ static const int DCTSIZE2 = 64; // DCTSIZE squared; # of elements in a block
+ static const int NUM_QUANT_TBLS = 4; // Quantization tables are numbered 0..3
+ static const int NUM_HUFF_TBLS = 4; // Huffman tables are numbered 0..3
+ static const int NUM_ARITH_TBLS = 16; // Arith-coding tables are numbered 0..15
+ static const int MAX_COMPS_IN_SCAN = 4; // JPEG limit on # of components in one scan
+ static const int MAX_SAMP_FACTOR = 4; // JPEG limit on sampling factors
+
+ static const int M_SOF0 = 0xc0;
+ static const int M_SOF1 = 0xc1;
+ static const int M_SOF2 = 0xc2;
+ static const int M_SOF3 = 0xc3;
+
+ static const int M_SOF5 = 0xc5;
+ static const int M_SOF6 = 0xc6;
+ static const int M_SOF7 = 0xc7;
+
+ static const int M_JPG = 0xc8;
+ static const int M_SOF9 = 0xc9;
+ static const int M_SOF10 = 0xca;
+ static const int M_SOF11 = 0xcb;
+
+ static const int M_SOF13 = 0xcd;
+ static const int M_SOF14 = 0xce;
+ static const int M_SOF15 = 0xcf;
+
+ static const int M_DHT = 0xc4;
+
+ static const int M_DAC = 0xcc;
+
+ static const int M_RST0 = 0xd0;
+ static const int M_RST1 = 0xd1;
+ static const int M_RST2 = 0xd2;
+ static const int M_RST3 = 0xd3;
+ static const int M_RST4 = 0xd4;
+ static const int M_RST5 = 0xd5;
+ static const int M_RST6 = 0xd6;
+ static const int M_RST7 = 0xd7;
+
+ static const int M_SOI = 0xd8;
+ static const int M_EOI = 0xd9;
+ static const int M_SOS = 0xda;
+ static const int M_DQT = 0xdb;
+ static const int M_DNL = 0xdc;
+ static const int M_DRI = 0xdd;
+ static const int M_DHP = 0xde;
+ static const int M_EXP = 0xdf;
+
+ static const int M_APP0 = 0xe0; // JFIF, JFXX, CIFF, AVI1, Ocad
+ static const int M_APP1 = 0xe1; // EXIF, ExtendedXMP, XMP, QVCI, FLIR
+ static const int M_APP2 = 0xe2; // ICC_Profile, FPXR, MPF, PreviewImage
+ static const int M_APP3 = 0xe3; // Meta, Stim, PreviewImage
+ static const int M_APP4 = 0xe4; // Scalado, FPXR, PreviewImage
+ static const int M_APP5 = 0xe5; // RMETA, PreviewImage
+ static const int M_APP6 = 0xe6; // EPPIM, NITF, HP_TDHD, GoPro
+ static const int M_APP7 = 0xe7; // Pentax, Qualcomm
+ static const int M_APP8 = 0xe8; // SPIFF
+ static const int M_APP9 = 0xe9; // MediaJukebox
+ static const int M_APP10 = 0xea; // Comment
+ static const int M_APP11 = 0xeb; // Jpeg-HDR
+ static const int M_APP12 = 0xec; // PictureInfo, Ducky
+ static const int M_APP13 = 0xed; // Photoshop, Adobe_CM
+ static const int M_APP14 = 0xee; // ADOBE
+ static const int M_APP15 = 0xef; // GraphicConverter
+
+ static const int M_JPG0 = 0xf0;
+ static const int M_JPG13 = 0xfd;
+ static const int M_COM = 0xfe;
+
+ static const int M_TEM = 0x01;
+
+ static const int M_ERROR = 0x100;
+}
diff --git a/image/lib/src/formats/jpeg/jpeg_adobe.dart b/image/lib/src/formats/jpeg/jpeg_adobe.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/jpeg/jpeg_component.dart b/image/lib/src/formats/jpeg/jpeg_component.dart
old mode 100644
new mode 100755
index 7af35d5..44b8221
--- a/image/lib/src/formats/jpeg/jpeg_component.dart
+++ b/image/lib/src/formats/jpeg/jpeg_component.dart
@@ -1,20 +1,20 @@
-import 'dart:typed_data';
-
-class JpegComponent {
- int h;
- int v;
- final List<Int16List> quantizationTableList;
- int quantizationIndex;
- int blocksPerLine;
- int blocksPerColumn;
- List blocks;
- List huffmanTableDC;
- List huffmanTableAC;
- int pred;
-
- JpegComponent(this.h, this.v, this.quantizationTableList,
- this.quantizationIndex);
-
- Int16List get quantizationTable =>
- quantizationTableList[quantizationIndex];
-}
+import 'dart:typed_data';
+
+class JpegComponent {
+ int hSamples;
+ int vSamples;
+ final List<Int16List> quantizationTableList;
+ int quantizationIndex;
+ int blocksPerLine;
+ int blocksPerColumn;
+ List blocks;
+ List huffmanTableDC;
+ List huffmanTableAC;
+ int pred;
+
+ JpegComponent(this.hSamples, this.vSamples, this.quantizationTableList,
+ this.quantizationIndex);
+
+ Int16List get quantizationTable =>
+ quantizationTableList[quantizationIndex];
+}
diff --git a/image/lib/src/formats/jpeg/jpeg_data.dart b/image/lib/src/formats/jpeg/jpeg_data.dart
old mode 100644
new mode 100755
index 73517f1..f32f91f
--- a/image/lib/src/formats/jpeg/jpeg_data.dart
+++ b/image/lib/src/formats/jpeg/jpeg_data.dart
@@ -1,1152 +1,1185 @@
-import 'dart:typed_data';
-
-import '../../exif_data.dart';
-import '../../image_exception.dart';
-import '../../internal/bit_operators.dart';
-import '../../util/input_buffer.dart';
-import 'jpeg.dart';
-import 'jpeg_adobe.dart';
-import 'jpeg_component.dart';
-import 'jpeg_frame.dart';
-import 'jpeg_info.dart';
-import 'jpeg_jfif.dart';
-import 'jpeg_scan.dart';
-
-class JpegData {
- InputBuffer input;
- JpegJfif jfif;
- JpegAdobe adobe;
- JpegFrame frame;
- int resetInterval;
- ExifData exif = new ExifData();
- final List<Int16List> quantizationTables = new List(Jpeg.NUM_QUANT_TBLS);
- final List<JpegFrame> frames = [];
- final List huffmanTablesAC = [];
- final List huffmanTablesDC = [];
- final List<Map> components = [];
-
- bool validate(List<int> bytes) {
- input = new InputBuffer(bytes, bigEndian: true);
-
- int marker = _nextMarker();
- if (marker != Jpeg.M_SOI) {
- return false;
- }
-
- bool hasSOF = false;
- bool hasSOS = false;
-
- marker = _nextMarker();
- while (marker != Jpeg.M_EOI && !input.isEOS) { // EOI (End of image)
- _skipBlock();
- switch (marker) {
- case Jpeg.M_SOF0: // SOF0 (Start of Frame, Baseline DCT)
- case Jpeg.M_SOF1: // SOF1 (Start of Frame, Extended DCT)
- case Jpeg.M_SOF2: // SOF2 (Start of Frame, Progressive DCT)
- hasSOF = true;
- break;
- case Jpeg.M_SOS: // SOS (Start of Scan)
- hasSOS = true;
- break;
- default:
- }
-
- marker = _nextMarker();
- }
-
- return hasSOF && hasSOS;
- }
-
- JpegInfo readInfo(List<int> bytes) {
- input = new InputBuffer(bytes, bigEndian: true);
-
- int marker = _nextMarker();
- if (marker != Jpeg.M_SOI) {
- return null;
- }
-
- JpegInfo info = new JpegInfo();
-
- bool hasSOF = false;
- bool hasSOS = false;
-
- marker = _nextMarker();
- while (marker != Jpeg.M_EOI && !input.isEOS) { // EOI (End of image)
- switch (marker) {
- case Jpeg.M_SOF0: // SOF0 (Start of Frame, Baseline DCT)
- case Jpeg.M_SOF1: // SOF1 (Start of Frame, Extended DCT)
- case Jpeg.M_SOF2: // SOF2 (Start of Frame, Progressive DCT)
- hasSOF = true;
- _readFrame(marker, _readBlock());
- break;
- case Jpeg.M_SOS: // SOS (Start of Scan)
- hasSOS = true;
- _skipBlock();
- break;
- default:
- _skipBlock();
- break;
- }
-
- marker = _nextMarker();
- }
-
- if (frame != null) {
- info.width = frame.samplesPerLine;
- info.height = frame.scanLines;
- }
- frame = null;
- frames.clear();
-
- return (hasSOF && hasSOS) ? info : null;
- }
-
- void read(List<int> bytes) {
- input = new InputBuffer(bytes, bigEndian: true);
-
- _read();
-
- if (frames.length != 1) {
- throw new ImageException('Only single frame JPEGs supported');
- }
-
- for (int i = 0; i < frame.componentsOrder.length; ++i) {
- /*JpegComponent component =*/ frame.components[frame.componentsOrder[i]];
- }
-
- for (int i = 0; i < frame.componentsOrder.length; ++i) {
- JpegComponent component = frame.components[frame.componentsOrder[i]];
- components.add({
- 'scaleX': component.h / frame.maxH,
- 'scaleY': component.v / frame.maxV,
- 'lines': _buildComponentData(frame, component)
- });
- }
- }
-
- int get width => frame.samplesPerLine;
-
- int get height => frame.scanLines;
-
- Uint8List getData(int width, int height) {
- num scaleX = 1;
- num scaleY = 1;
- Map component1;
- Map component2;
- Map component3;
- Map component4;
- Uint8List component1Line;
- Uint8List component2Line;
- Uint8List component3Line;
- Uint8List component4Line;
- int offset = 0;
- int Y, Cb, Cr, K, C, M, Ye, R, G, B;
- bool colorTransform = false;
- int dataLength = width * height * components.length;
- Uint8List data = new Uint8List(dataLength);
-
- switch (components.length) {
- case 1:
- component1 = components[0];
- var lines = component1['lines'];
- var sy = component1['scaleY'];
- var sx = component1['scaleX'];
- for (int y = 0; y < height; y++) {
- component1Line = lines[(y * sy * scaleY).toInt()];
- for (int x = 0; x < width; x++) {
- Y = component1Line[(x *sx * scaleX).toInt()];
- data[offset++] = Y;
- }
- }
- break;
- case 2:
- // PDF might compress two component data in custom color-space
- component1 = components[0];
- component2 = components[1];
- for (int y = 0; y < height; y++) {
- component1Line = component1['lines'][(y * component1['scaleY'] * scaleY)];
- component2Line = component2['lines'][(y * component2['scaleY'] * scaleY)];
- for (int x = 0; x < width; x++) {
- Y = component1Line[(x * component1['scaleX'] * scaleX).toInt()];
- data[offset++] = Y;
- Y = component2Line[(x * component2['scaleX'] * scaleX).toInt()];
- data[offset++] = Y;
- }
- }
- break;
- case 3:
- // The default transform for three components is true
- colorTransform = true;
-
- component1 = components[0];
- component2 = components[1];
- component3 = components[2];
-
- double sy1 = (component1['scaleY'] * scaleY).toDouble();
- double sy2 = (component2['scaleY'] * scaleY).toDouble();
- double sy3 = (component3['scaleY'] * scaleY).toDouble();
- double sx1 = (component1['scaleX'] * scaleX).toDouble();
- double sx2 = (component2['scaleX'] * scaleX).toDouble();
- double sx3 = (component3['scaleX'] * scaleX).toDouble();
-
- List<Uint8List> lines1 = component1['lines'];
- List<Uint8List> lines2 = component2['lines'];
- List<Uint8List> lines3 = component3['lines'];
-
- for (int y = 0; y < height; y++) {
- component1Line = lines1[(y * sy1).toInt()];
- component2Line = lines2[(y * sy2).toInt()];
- component3Line = lines3[(y * sy3).toInt()];
-
- for (int x = 0; x < width; x++) {
- if (!colorTransform) {
- data[offset++] = component1Line[(x * sx1).toInt()];
- data[offset++] = component2Line[(x * sx2).toInt()];
- data[offset++] = component3Line[(x * sx3).toInt()];
- } else {
- Y = component1Line[(x * sx1).toInt()] << 8;
- Cb = component2Line[(x * sx2).toInt()] - 128;
- Cr = component3Line[(x * sx3).toInt()] - 128;
-
- R = shiftR((Y + 359 * Cr + 128), 8);
- G = shiftR((Y - 88 * Cb - 183 * Cr + 128), 8);
- B = shiftR((Y + 454 * Cb + 128), 8);
-
- data[offset++] = _clamp8(R);
- data[offset++] = _clamp8(G);
- data[offset++] = _clamp8(B);
- }
- }
- }
- break;
- case 4:
- if (adobe == null) {
- throw new ImageException('Unsupported color mode (4 components)');
- }
- // The default transform for four components is false
- colorTransform = false;
- // The adobe transform marker overrides any previous setting
- if (adobe.transformCode != 0) {
- colorTransform = true;
- }
-
- component1 = components[0];
- component2 = components[1];
- component3 = components[2];
- component4 = components[3];
-
- var sx1 = component1['scaleX'] * scaleX;
- var sx2 = component2['scaleX'] * scaleX;
- var sx3 = component3['scaleX'] * scaleX;
- var sx4 = component4['scaleX'] * scaleX;
- var sy1 = component1['scaleY'] * scaleY;
- var sy2 = component2['scaleY'] * scaleY;
- var sy3 = component3['scaleY'] * scaleY;
- var sy4 = component4['scaleY'] * scaleY;
-
- var lines1 = component1['lines'];
- var lines2 = component2['lines'];
- var lines3 = component3['lines'];
- var lines4 = component4['lines'];
-
- for (int y = 0; y < height; y++) {
- component1Line = lines1[(y * sy1).toInt()];
- component2Line = lines2[(y * sy2).toInt()];
- component3Line = lines3[(y * sy3).toInt()];
- component4Line = lines4[(y * sy4).toInt()];
- for (int x = 0; x < width; x++) {
- if (!colorTransform) {
- C = component1Line[(x * sx1).toInt()];
- M = component2Line[(x * sx2).toInt()];
- Ye = component3Line[(x * sx3).toInt()];
- K = component4Line[(x * sx4).toInt()];
- } else {
- Y = component1Line[(x * sx1).toInt()];
- Cb = component2Line[(x * sx2).toInt()];
- Cr = component3Line[(x * sx3).toInt()];
- K = component4Line[(x * sx4).toInt()];
-
- C = 255 - _clamp8((Y + 1.402 * (Cr - 128)).toInt());
- M = 255 - _clamp8((Y - 0.3441363 * (Cb - 128) -
- 0.71413636 * (Cr - 128)).toInt());
- Ye = 255 - _clamp8((Y + 1.772 * (Cb - 128)).toInt());
- }
-
- data[offset++] = C;
- data[offset++] = M;
- data[offset++] = Ye;
- data[offset++] = K;
- }
- }
- break;
- default:
- throw new ImageException('Unsupported color mode');
- }
-
- return data;
- }
-
- void _read() {
- int marker = _nextMarker();
- if (marker != Jpeg.M_SOI) { // SOI (Start of Image)
- throw new ImageException('Start Of Image marker not found.');
- }
-
- marker = _nextMarker();
- while (marker != Jpeg.M_EOI && !input.isEOS) {
- InputBuffer block = _readBlock();
- switch (marker) {
- case Jpeg.M_APP0:
- case Jpeg.M_APP1:
- case Jpeg.M_APP2:
- case Jpeg.M_APP3:
- case Jpeg.M_APP4:
- case Jpeg.M_APP5:
- case Jpeg.M_APP6:
- case Jpeg.M_APP7:
- case Jpeg.M_APP8:
- case Jpeg.M_APP9:
- case Jpeg.M_APP10:
- case Jpeg.M_APP11:
- case Jpeg.M_APP12:
- case Jpeg.M_APP13:
- case Jpeg.M_APP14:
- case Jpeg.M_APP15:
- case Jpeg.M_COM:
- _readAppData(marker, block);
- break;
-
- case Jpeg.M_DQT: // DQT (Define Quantization Tables)
- _readDQT(block);
- break;
-
- case Jpeg.M_SOF0: // SOF0 (Start of Frame, Baseline DCT)
- case Jpeg.M_SOF1: // SOF1 (Start of Frame, Extended DCT)
- case Jpeg.M_SOF2: // SOF2 (Start of Frame, Progressive DCT)
- _readFrame(marker, block);
- break;
-
- case Jpeg.M_SOF3:
- case Jpeg.M_SOF5:
- case Jpeg.M_SOF6:
- case Jpeg.M_SOF7:
- case Jpeg.M_JPG:
- case Jpeg.M_SOF9:
- case Jpeg.M_SOF10:
- case Jpeg.M_SOF11:
- case Jpeg.M_SOF13:
- case Jpeg.M_SOF14:
- case Jpeg.M_SOF15:
- throw new ImageException('Unhandled frame type ${marker.toRadixString(16)}');
-
- case Jpeg.M_DHT: // DHT (Define Huffman Tables)
- _readDHT(block);
- break;
-
- case Jpeg.M_DRI: // DRI (Define Restart Interval)
- _readDRI(block);
- break;
-
- case Jpeg.M_SOS: // SOS (Start of Scan)
- _readSOS(block);
- break;
-
- case 0xff: // Fill bytes
- if (input[0] != 0xff) {
- input.offset--;
- }
- break;
-
- default:
- if (input[-3] == 0xff && input[-2] >= 0xc0 && input[-2] <= 0xfe) {
- // could be incorrect encoding -- last 0xFF byte of the previous
- // block was eaten by the encoder
- input.offset -= 3;
- break;
- }
-
- if (marker != 0) {
- throw new ImageException('Unknown JPEG marker ' +
- marker.toRadixString(16));
- }
- break;
- }
-
- marker = _nextMarker();
- }
- }
-
- void _skipBlock() {
- int length = input.readUint16();
- if (length < 2) {
- throw new ImageException('Invalid Block');
- }
- input.offset += length - 2;
- }
-
- InputBuffer _readBlock() {
- int length = input.readUint16();
- if (length < 2) {
- throw new ImageException('Invalid Block');
- }
- return input.readBytes(length - 2);
- }
-
- int _nextMarker() {
- int c = 0;
- if (input.isEOS) {
- return c;
- }
-
- do {
- do {
- c = input.readByte();
- } while (c != 0xff && !input.isEOS);
-
- if (input.isEOS) {
- return c;
- }
-
- do {
- c = input.readByte();
- } while (c == 0xff && !input.isEOS);
- } while (c == 0 && !input.isEOS);
-
- return c;
- }
-
- num _readExifValue(InputBuffer block, int format) {
- const FMT_BYTE = 1;
- //const FMT_STRING = 2;
- const FMT_USHORT = 3;
- const FMT_ULONG = 4;
- const FMT_URATIONAL = 5;
- const FMT_SBYTE = 6;
- //const FMT_UNDEFINED = 7;
- const FMT_SSHORT = 8;
- const FMT_SLONG = 9;
- const FMT_SRATIONAL = 10;
- const FMT_SINGLE = 11;
- const FMT_DOUBLE = 12;
-
- switch (format) {
- case FMT_SBYTE:
- return block.readInt8();
- case FMT_BYTE:
- return block.readByte();
- case FMT_USHORT:
- return block.readUint16();
- case FMT_ULONG:
- return block.readUint32();
- case FMT_URATIONAL:
- case FMT_SRATIONAL: {
- int num = block.readInt32();
- int den = block.readInt32();
- if (den == 0) {
- return 0.0;
- }
- return num / den;
- }
- case FMT_SSHORT:
- return block.readInt16();
- case FMT_SLONG:
- return block.readInt32();
- // Not sure if this is correct (never seen float used in Exif format)
- case FMT_SINGLE:
- return block.readFloat32();
- case FMT_DOUBLE:
- return block.readFloat64();
- default:
- return 0;
- }
- }
-
- void _readExifDir(InputBuffer block, [int nesting = 0]) {
- if (nesting > 4) {
- return; // Maximum Exif directory nesting exceeded (corrupt Exif header)
- }
-
- int numDirEntries = block.readUint16();
-
- const TAG_ORIENTATION = 0x0112;
- const TAG_INTEROP_OFFSET = 0xA005;
- const TAG_EXIF_OFFSET = 0x8769;
- const maxFormats = 12;
- const bytesPerFormat = const [0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8];
-
- for (int di = 0; di < numDirEntries; ++di) {
- int tag = block.readUint16();
- int format = block.readUint16();
- int components = block.readUint32();
-
- if (format - 1 >= maxFormats) {
- continue;
- }
-
- // too many components
- if (components > 0x10000) {
- continue;
- }
-
- int byteCount = bytesPerFormat[format];
-
- // If its bigger than 4 bytes, the dir entry contains an offset.
- if (byteCount > 4) {
- int offset = block.readUint32();
- if (offset + byteCount > block.length) {
- continue; // Bogus pointer offset and / or bytecount value
- }
-
- //ValuePtr = OffsetBase+OffsetVal;
- }
-
- switch (tag) {
- case TAG_ORIENTATION: {
- num orientation = _readExifValue(block, format);
- exif.orientation = orientation.toInt();
- }
- break;
- case TAG_EXIF_OFFSET:
- case TAG_INTEROP_OFFSET:
- break;
- default:
- // skip unknown tags
- break;
- }
- }
- }
-
- void _readExifData(InputBuffer block) {
- if (exif.rawData == null) {
- exif.rawData = new List<Uint8List>();
- }
-
- Uint8List rawData = new Uint8List.fromList(block.toUint8List());
- exif.rawData.add(rawData);
-
- const EXIF_TAG = 0x45786966; // Exif\0\0
- if (block.readUint32() != EXIF_TAG) {
- return;
- }
- if (block.readUint16() != 0) {
- return;
- }
-
- bool saveEndian = block.bigEndian;
-
- // Exif Directory
- String alignment = block.readString(2);
- if (alignment == 'II') { // Exif is in Intel order
- block.bigEndian = false;
- } else if (alignment == 'MM') { // Exif section in Motorola order
- block.bigEndian = true;
- } else {
- return;
- }
-
- block.skip(2);
-
- int offset = block.readUint32();
- if (offset < 8 || offset > 16){
- if (offset > block.length - 16) {
- // invalid offset for first Exif IFD value ;
- block.bigEndian = saveEndian;
- return;
- }
- }
-
- if (offset > 8) {
- block.skip(offset - 8);
- }
-
- _readExifDir(block);
-
- block.bigEndian = saveEndian;
- }
-
- void _readAppData(int marker, InputBuffer block) {
- InputBuffer appData = block;
-
- if (marker == Jpeg.M_APP0) {
- // 'JFIF\0'
- if (appData[0] == 0x4A && appData[1] == 0x46 &&
- appData[2] == 0x49 && appData[3] == 0x46 && appData[4] == 0) {
- jfif = new JpegJfif();
- jfif.majorVersion = appData[5];
- jfif.minorVersion = appData[6];
- jfif.densityUnits = appData[7];
- jfif.xDensity = shiftL(appData[8], 8) | appData[9];
- jfif.yDensity = shiftL(appData[10], 8) | appData[11];
- jfif.thumbWidth = appData[12];
- jfif.thumbHeight = appData[13];
- int thumbSize = 3 * jfif.thumbWidth * jfif.thumbHeight;
- jfif.thumbData = appData.subset(14 + thumbSize, offset: 14);
- }
- } else if (marker == Jpeg.M_APP1) {
- // 'EXIF\0'
- _readExifData(appData);
- } else if (marker == Jpeg.M_APP14) {
- // 'Adobe\0'
- if (appData[0] == 0x41 && appData[1] == 0x64 &&
- appData[2] == 0x6F && appData[3] == 0x62 &&
- appData[4] == 0x65 && appData[5] == 0) {
- adobe = new JpegAdobe();
- adobe.version = appData[6];
- adobe.flags0 = shiftL(appData[7], 8) | appData[8];
- adobe.flags1 = shiftL(appData[9], 8) | appData[10];
- adobe.transformCode = appData[11];
- }
- } else {
- //print("!!!! UNHANDLED APP TAG 0x${marker.toRadixString(16)}");
- }
- }
-
- void _readDQT(InputBuffer block) {
- while (!block.isEOS) {
- int n = block.readByte();
- int prec = shiftR(n, 4);
- n &= 0x0F;
-
- if (n >= Jpeg.NUM_QUANT_TBLS) {
- throw new ImageException('Invalid number of quantization tables');
- }
-
- if (quantizationTables[n] == null) {
- quantizationTables[n] = new Int16List(64);
- }
-
- Int16List tableData = quantizationTables[n];
- for (int i = 0; i < Jpeg.DCTSIZE2; i++) {
- int tmp;
- if (prec != 0) {
- tmp = block.readUint16();
- } else {
- tmp = block.readByte();
- }
-
- tableData[Jpeg.dctZigZag[i]] = tmp;
- }
- }
-
- if (!block.isEOS) {
- throw new ImageException('Bad length for DQT block');
- }
- }
-
- void _readFrame(int marker, InputBuffer block) {
- if (frame != null) {
- throw new ImageException('Duplicate JPG frame data found.');
- }
-
- frame = new JpegFrame();
- frame.extended = (marker == Jpeg.M_SOF1);
- frame.progressive = (marker == Jpeg.M_SOF2);
- frame.precision = block.readByte();
- frame.scanLines = block.readUint16();
- frame.samplesPerLine = block.readUint16();
-
- int numComponents = block.readByte();
-
- for (int i = 0; i < numComponents; i++) {
- int componentId = block.readByte();
- int x = block.readByte();
- int h = shiftR(x, 4) & 15;
- int v = x & 15;
- int qId = block.readByte();
- frame.componentsOrder.add(componentId);
- frame.components[componentId] =
- new JpegComponent(h, v, quantizationTables, qId);
- }
-
- frame.prepare();
- frames.add(frame);
- }
-
- void _readDHT(InputBuffer block) {
- while (!block.isEOS) {
- int index = block.readByte();
-
- Uint8List bits = new Uint8List(16);
- int count = 0;
- for (int j = 0; j < 16; j++) {
- bits[j] = block.readByte();
- count += bits[j];
- }
-
- Uint8List huffmanValues = new Uint8List(count);
- for (int j = 0; j < count; j++) {
- huffmanValues[j] = block.readByte();
- }
-
- List ht;
- if (index & 0x10 != 0) { // AC table definition
- index -= 0x10;
- ht = huffmanTablesAC;
- } else { // DC table definition
- ht = huffmanTablesDC;
- }
-
- if (ht.length <= index) {
- ht.length = index + 1;
- }
-
- ht[index] = _buildHuffmanTable(bits, huffmanValues);
- }
- }
-
- void _readDRI(InputBuffer block) {
- resetInterval = block.readUint16();
- }
-
- void _readSOS(InputBuffer block) {
- int n = block.readByte();
- if (n < 1 || n > Jpeg.MAX_COMPS_IN_SCAN) {
- throw new ImageException('Invalid SOS block');
- }
-
- List components = new List(n);
- for (int i = 0; i < n; i++) {
- int id = block.readByte();
- int c = block.readByte();
-
- if (!frame.components.containsKey(id)) {
- throw new ImageException('Invalid Component in SOS block');
- }
-
- JpegComponent component = frame.components[id];
- components[i] = component;
-
- int dc_tbl_no = shiftR(c, 4) & 15;
- int ac_tbl_no = c & 15;
-
- if (dc_tbl_no < huffmanTablesDC.length) {
- component.huffmanTableDC = huffmanTablesDC[dc_tbl_no];
- }
- if (ac_tbl_no < huffmanTablesAC.length) {
- component.huffmanTableAC = huffmanTablesAC[ac_tbl_no];
- }
- }
-
- int spectralStart = block.readByte();
- int spectralEnd = block.readByte();
- int successiveApproximation = block.readByte();
-
- int Ah = shiftR(successiveApproximation, 4) & 15;
- int Al = successiveApproximation & 15;
-
- new JpegScan(input, frame, components, resetInterval,
- spectralStart, spectralEnd, Ah, Al).decode();
- }
-
- List _buildHuffmanTable(Uint8List codeLengths, Uint8List values) {
- int k = 0;
- List code = [];
- int length = 16;
-
- while (length > 0 && (codeLengths[length - 1] == 0)) {
- length--;
- }
-
- code.add(new _JpegHuffman());
-
- _JpegHuffman p = code[0];
- _JpegHuffman q;
-
- for (int i = 0; i < length; i++) {
- for (int j = 0; j < codeLengths[i]; j++) {
- p = code.removeLast();
- if (p.children.length <= p.index) {
- p.children.length = p.index + 1;
- }
- p.children[p.index] = values[k];
- while (p.index > 0) {
- p = code.removeLast();
- }
- p.index++;
- code.add(p);
- while (code.length <= i) {
- q = new _JpegHuffman();
- code.add(q);
- if (p.children.length <= p.index) {
- p.children.length = p.index + 1;
- }
- p.children[p.index] = q.children;
- p = q;
- }
- k++;
- }
-
- if ((i + 1) < length) {
- // p here points to last code
- q = new _JpegHuffman();
- code.add(q);
- if (p.children.length <= p.index) {
- p.children.length = p.index + 1;
- }
- p.children[p.index] = q.children;
- p = q;
- }
- }
-
- return code[0].children;
- }
-
- List<Uint8List> _buildComponentData(JpegFrame frame,
- JpegComponent component) {
- final int blocksPerLine = component.blocksPerLine;
- final int blocksPerColumn = component.blocksPerColumn;
- int samplesPerLine = shiftL(blocksPerLine, 3);
- Int32List R = new Int32List(64);
- Uint8List r = new Uint8List(64);
- List<Uint8List> lines = new List(blocksPerColumn * 8);
-
- int l = 0;
- for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
- int scanLine = shiftL(blockRow, 3);
- for (int i = 0; i < 8; i++) {
- lines[l++] = new Uint8List(samplesPerLine);
- }
-
- for (int blockCol = 0; blockCol < blocksPerLine; blockCol++) {
- _quantizeAndInverse(component.quantizationTable,
- component.blocks[blockRow][blockCol],
- r, R);
-
- int offset = 0;
- int sample = shiftL(blockCol, 3);
- for (int j = 0; j < 8; j++) {
- Uint8List line = lines[scanLine + j];
- for (int i = 0; i < 8; i++) {
- line[sample + i] = r[offset++];
- }
- }
- }
- }
-
- return lines;
- }
-
- static int toFix(double val) {
- const int FIXED_POINT = 20;
- const int ONE = 1 << FIXED_POINT;
-
- return (val * ONE).toInt() & 0xffffffff;
- }
-
- static int _clamp8(int i) => i < 0 ? 0 : i > 255 ? 255 : i;
-
- static Uint8List dctClip;
-
- /**
- * Quantize the coefficients and apply IDCT.
- *
- * A port of poppler's IDCT method which in turn is taken from:
- * Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
- * "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
- * IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, 988-991.
- */
- void _quantizeAndInverse(Int16List quantizationTable,
- Int32List coefBlock,
- Uint8List dataOut,
- Int32List dataIn) {
- Int32List p = dataIn;
-
- const int dctClipOffset = 256;
- const int dctClipLength = 768;
- if (dctClip == null) {
- dctClip = new Uint8List(dctClipLength);
- int i;
- for (i = -256; i < 0; ++i) {
- dctClip[dctClipOffset + i] = 0;
- }
- for (i = 0; i < 256; ++i) {
- dctClip[dctClipOffset + i] = i;
- }
- for (i = 256; i < 512; ++i) {
- dctClip[dctClipOffset + i] = 255;
- }
- }
-
- // IDCT constants (20.12 fixed point format)
- const int COS_1 = 4017; // cos(pi/16)*4096
- const int SIN_1 = 799; // sin(pi/16)*4096
- const int COS_3 = 3406; // cos(3*pi/16)*4096
- const int SIN_3 = 2276; // sin(3*pi/16)*4096
- const int COS_6 = 1567; // cos(6*pi/16)*4096
- const int SIN_6 = 3784; // sin(6*pi/16)*4096
- const int SQRT_2 = 5793; // sqrt(2)*4096
- const int SQRT_1D2 = 2896; // sqrt(2) / 2
-
- // de-quantize
- for (int i = 0; i < 64; i++) {
- p[i] = (coefBlock[i] * quantizationTable[i]);
- }
-
- // inverse DCT on rows
- int row = 0;
- for (int i = 0; i < 8; ++i, row += 8) {
- // check for all-zero AC coefficients
- if (p[1 + row] == 0 &&
- p[2 + row] == 0 &&
- p[3 + row] == 0 &&
- p[4 + row] == 0 &&
- p[5 + row] == 0 &&
- p[6 + row] == 0 &&
- p[7 + row] == 0) {
- int t = shiftR((SQRT_2 * p[0 + row] + 512), 10);
- p[row + 0] = t;
- p[row + 1] = t;
- p[row + 2] = t;
- p[row + 3] = t;
- p[row + 4] = t;
- p[row + 5] = t;
- p[row + 6] = t;
- p[row + 7] = t;
- continue;
- }
-
- // stage 4
- int v0 = shiftR((SQRT_2 * p[0 + row] + 128), 8);
- int v1 = shiftR((SQRT_2 * p[4 + row] + 128), 8);
- int v2 = p[2 + row];
- int v3 = p[6 + row];
- int v4 = shiftR((SQRT_1D2 * (p[1 + row] - p[7 + row]) + 128), 8);
- int v7 = shiftR((SQRT_1D2 * (p[1 + row] + p[7 + row]) + 128), 8);
- int v5 = shiftL(p[3 + row], 4);
- int v6 = shiftL(p[5 + row], 4);
-
- // stage 3
- int t = shiftR((v0 - v1 + 1), 1);
- v0 = shiftR((v0 + v1 + 1), 1);
- v1 = t;
- t = shiftR((v2 * SIN_6 + v3 * COS_6 + 128), 8);
- v2 = shiftR((v2 * COS_6 - v3 * SIN_6 + 128), 8);
- v3 = t;
- t = shiftR((v4 - v6 + 1), 1);
- v4 = shiftR((v4 + v6 + 1), 1);
- v6 = t;
- t = shiftR((v7 + v5 + 1), 1);
- v5 = shiftR((v7 - v5 + 1), 1);
- v7 = t;
-
- // stage 2
- t = shiftR((v0 - v3 + 1), 1);
- v0 = shiftR((v0 + v3 + 1), 1);
- v3 = t;
- t = shiftR((v1 - v2 + 1), 1);
- v1 = shiftR((v1 + v2 + 1), 1);
- v2 = t;
- t = shiftR((v4 * SIN_3 + v7 * COS_3 + 2048), 12);
- v4 = shiftR((v4 * COS_3 - v7 * SIN_3 + 2048), 12);
- v7 = t;
- t = shiftR((v5 * SIN_1 + v6 * COS_1 + 2048), 12);
- v5 = shiftR((v5 * COS_1 - v6 * SIN_1 + 2048), 12);
- v6 = t;
-
- // stage 1
- p[0 + row] = (v0 + v7);
- p[7 + row] = (v0 - v7);
- p[1 + row] = (v1 + v6);
- p[6 + row] = (v1 - v6);
- p[2 + row] = (v2 + v5);
- p[5 + row] = (v2 - v5);
- p[3 + row] = (v3 + v4);
- p[4 + row] = (v3 - v4);
- }
-
- // inverse DCT on columns
- for (int i = 0; i < 8; ++i) {
- int col = i;
-
- // check for all-zero AC coefficients
- if (p[1 * 8 + col] == 0 &&
- p[2 * 8 + col] == 0 &&
- p[3 * 8 + col] == 0 &&
- p[4 * 8 + col] == 0 &&
- p[5 * 8 + col] == 0 &&
- p[6 * 8 + col] == 0 &&
- p[7 * 8 + col] == 0) {
- int t = shiftR((SQRT_2 * dataIn[i] + 8192), 14);
- p[0 * 8 + col] = t;
- p[1 * 8 + col] = t;
- p[2 * 8 + col] = t;
- p[3 * 8 + col] = t;
- p[4 * 8 + col] = t;
- p[5 * 8 + col] = t;
- p[6 * 8 + col] = t;
- p[7 * 8 + col] = t;
- continue;
- }
-
- // stage 4
- int v0 = shiftR((SQRT_2 * p[0 * 8 + col] + 2048), 12);
- int v1 = shiftR((SQRT_2 * p[4 * 8 + col] + 2048), 12);
- int v2 = p[2 * 8 + col];
- int v3 = p[6 * 8 + col];
- int v4 = shiftR((SQRT_1D2 * (p[1 * 8 + col] - p[7 * 8 + col]) + 2048), 12);
- int v7 = shiftR((SQRT_1D2 * (p[1 * 8 + col] + p[7 * 8 + col]) + 2048), 12);
- int v5 = p[3 * 8 + col];
- int v6 = p[5 * 8 + col];
-
- // stage 3
- int t = shiftR((v0 - v1 + 1), 1);
- v0 = shiftR((v0 + v1 + 1), 1);
- v1 = t;
- t = shiftR((v2 * SIN_6 + v3 * COS_6 + 2048), 12);
- v2 = shiftR((v2 * COS_6 - v3 * SIN_6 + 2048), 12);
- v3 = t;
- t = shiftR((v4 - v6 + 1), 1);
- v4 = shiftR((v4 + v6 + 1), 1);
- v6 = t;
- t = shiftR((v7 + v5 + 1), 1);
- v5 = shiftR((v7 - v5 + 1), 1);
- v7 = t;
-
- // stage 2
- t = shiftR((v0 - v3 + 1), 1);
- v0 = shiftR((v0 + v3 + 1), 1);
- v3 = t;
- t = shiftR((v1 - v2 + 1), 1);
- v1 = shiftR((v1 + v2 + 1), 1);
- v2 = t;
- t = shiftR((v4 * SIN_3 + v7 * COS_3 + 2048), 12);
- v4 = shiftR((v4 * COS_3 - v7 * SIN_3 + 2048), 12);
- v7 = t;
- t = shiftR((v5 * SIN_1 + v6 * COS_1 + 2048), 12);
- v5 = shiftR((v5 * COS_1 - v6 * SIN_1 + 2048), 12);
- v6 = t;
-
- // stage 1
- p[0 * 8 + col] = (v0 + v7);
- p[7 * 8 + col] = (v0 - v7);
- p[1 * 8 + col] = (v1 + v6);
- p[6 * 8 + col] = (v1 - v6);
- p[2 * 8 + col] = (v2 + v5);
- p[5 * 8 + col] = (v2 - v5);
- p[3 * 8 + col] = (v3 + v4);
- p[4 * 8 + col] = (v3 - v4);
- }
-
- // convert to 8-bit integers
- for (int i = 0; i < 64; ++i) {
- dataOut[i] = dctClip[(dctClipOffset + 128 + shiftR((p[i] + 8), 4))];
- }
- }
-
- static const CRR = const [
- -179, -178, -177, -175, -174, -172, -171, -170, -168, -167, -165, -164, -163,
- -161, -160, -158, -157, -156, -154, -153, -151, -150, -149, -147, -146, -144, -143,
- -142, -140, -139, -137, -136, -135, -133, -132, -130, -129, -128, -126, -125,
- -123, -122, -121, -119, -118, -116, -115, -114, -112, -111, -109, -108, -107,
- -105, -104, -102, -101, -100, -98, -97, -95, -94, -93, -91, -90, -88, -87, -86,
- -84, -83, -81, -80, -79, -77, -76, -74, -73, -72, -70, -69, -67, -66, -64, -63,
- -62, -60, -59, -57, -56, -55, -53, -52, -50, -49, -48, -46, -45, -43, -42, -41,
- -39, -38, -36, -35, -34, -32, -31, -29, -28, -27, -25, -24, -22, -21, -20, -18,
- -17, -15, -14, -13, -11, -10, -8, -7, -6, -4, -3, -1, 0, 1, 3, 4, 6, 7, 8, 10,
- 11, 13, 14, 15, 17, 18, 20, 21, 22, 24, 25, 27, 28, 29, 31, 32, 34, 35, 36, 38,
- 39, 41, 42, 43, 45, 46, 48, 49, 50, 52, 53, 55, 56, 57, 59, 60, 62, 63, 64, 66,
- 67, 69, 70, 72, 73, 74, 76, 77, 79, 80, 81, 83, 84, 86, 87, 88, 90, 91, 93, 94,
- 95, 97, 98, 100, 101, 102, 104, 105, 107, 108, 109, 111, 112, 114, 115, 116, 118,
- 119, 121, 122, 123, 125, 126, 128, 129, 130, 132, 133, 135, 136, 137, 139, 140,
- 142, 143, 144, 146, 147, 149, 150, 151, 153, 154, 156, 157, 158, 160, 161, 163,
- 164, 165, 167, 168, 170, 171, 172, 174, 175, 177, 178
- ];
-
- static const CRG = const [
- 5990656, 5943854, 5897052, 5850250, 5803448, 5756646, 5709844, 5663042, 5616240,
- 5569438, 5522636, 5475834, 5429032, 5382230, 5335428, 5288626, 5241824, 5195022,
- 5148220, 5101418, 5054616, 5007814, 4961012, 4914210, 4867408, 4820606, 4773804,
- 4727002, 4680200, 4633398, 4586596, 4539794, 4492992, 4446190, 4399388, 4352586,
- 4305784, 4258982, 4212180, 4165378, 4118576, 4071774, 4024972, 3978170, 3931368,
- 3884566, 3837764, 3790962, 3744160, 3697358, 3650556, 3603754, 3556952, 3510150,
- 3463348, 3416546, 3369744, 3322942, 3276140, 3229338, 3182536, 3135734, 3088932,
- 3042130, 2995328, 2948526, 2901724, 2854922, 2808120, 2761318, 2714516, 2667714,
- 2620912, 2574110, 2527308, 2480506, 2433704, 2386902, 2340100, 2293298, 2246496,
- 2199694, 2152892, 2106090, 2059288, 2012486, 1965684, 1918882, 1872080, 1825278,
- 1778476, 1731674, 1684872, 1638070, 1591268, 1544466, 1497664, 1450862, 1404060,
- 1357258, 1310456, 1263654, 1216852, 1170050, 1123248, 1076446, 1029644, 982842,
- 936040, 889238, 842436, 795634, 748832, 702030, 655228, 608426, 561624, 514822,
- 468020, 421218, 374416, 327614, 280812, 234010, 187208, 140406, 93604, 46802, 0,
- -46802, -93604, -140406, -187208, -234010, -280812, -327614, -374416, -421218,
- -468020, -514822, -561624, -608426, -655228, -702030, -748832, -795634, -842436,
- -889238, -936040, -982842, -1029644, -1076446, -1123248, -1170050, -1216852,
- -1263654, -1310456, -1357258, -1404060, -1450862, -1497664, -1544466, -1591268,
- -1638070, -1684872, -1731674, -1778476, -1825278, -1872080, -1918882, -1965684,
- -2012486, -2059288, -2106090, -2152892, -2199694, -2246496, -2293298, -2340100,
- -2386902, -2433704, -2480506, -2527308, -2574110, -2620912, -2667714, -2714516,
- -2761318, -2808120, -2854922, -2901724, -2948526, -2995328, -3042130, -3088932,
- -3135734, -3182536, -3229338, -3276140, -3322942, -3369744, -3416546, -3463348,
- -3510150, -3556952, -3603754, -3650556, -3697358, -3744160, -3790962, -3837764,
- -3884566, -3931368, -3978170, -4024972, -4071774, -4118576, -4165378, -4212180,
- -4258982, -4305784, -4352586, -4399388, -4446190, -4492992, -4539794, -4586596,
- -4633398, -4680200, -4727002, -4773804, -4820606, -4867408, -4914210, -4961012,
- -5007814, -5054616, -5101418, -5148220, -5195022, -5241824, -5288626, -5335428,
- -5382230, -5429032, -5475834, -5522636, -5569438, -5616240, -5663042, -5709844,
- -5756646, -5803448, -5850250, -5897052, -5943854
- ];
-
- static const CBG = const [
- 2919680, 2897126, 2874572, 2852018, 2829464, 2806910, 2784356, 2761802, 2739248, 2716694,
- 2694140, 2671586, 2649032, 2626478, 2603924, 2581370, 2558816, 2536262, 2513708,
- 2491154, 2468600, 2446046, 2423492, 2400938, 2378384, 2355830, 2333276, 2310722,
- 2288168, 2265614, 2243060, 2220506, 2197952, 2175398, 2152844, 2130290, 2107736,
- 2085182, 2062628, 2040074, 2017520, 1994966, 1972412, 1949858, 1927304, 1904750,
- 1882196, 1859642, 1837088, 1814534, 1791980, 1769426, 1746872, 1724318, 1701764,
- 1679210, 1656656, 1634102, 1611548, 1588994, 1566440, 1543886, 1521332, 1498778,
- 1476224, 1453670, 1431116, 1408562, 1386008, 1363454, 1340900, 1318346, 1295792,
- 1273238, 1250684, 1228130, 1205576, 1183022, 1160468, 1137914, 1115360,
- 1092806, 1070252, 1047698, 1025144, 1002590, 980036, 957482, 934928, 912374,
- 889820, 867266, 844712, 822158, 799604, 777050, 754496, 731942, 709388, 686834,
- 664280, 641726, 619172, 596618, 574064, 551510, 528956, 506402, 483848, 461294,
- 438740, 416186, 393632, 371078, 348524, 325970, 303416, 280862, 258308, 235754,
- 213200, 190646, 168092, 145538, 122984, 100430, 77876, 55322, 32768, 10214, -12340,
- -34894, -57448, -80002, -102556, -125110, -147664, -170218, -192772, -215326,
- -237880, -260434, -282988, -305542, -328096, -350650, -373204, -395758, -418312,
- -440866, -463420, -485974, -508528, -531082, -553636, -576190, -598744, -621298,
- -643852, -666406, -688960, -711514, -734068, -756622, -779176, -801730, -824284,
- -846838, -869392, -891946, -914500, -937054, -959608, -982162, -1004716, -1027270,
- -1049824, -1072378, -1094932, -1117486, -1140040, -1162594, -1185148, -1207702,
- -1230256, -1252810, -1275364, -1297918, -1320472, -1343026, -1365580, -1388134,
- -1410688, -1433242, -1455796, -1478350, -1500904, -1523458, -1546012, -1568566,
- -1591120, -1613674, -1636228, -1658782, -1681336, -1703890, -1726444, -1748998,
- -1771552, -1794106, -1816660, -1839214, -1861768, -1884322, -1906876, -1929430,
- -1951984, -1974538, -1997092, -2019646, -2042200, -2064754, -2087308, -2109862,
- -2132416, -2154970, -2177524, -2200078, -2222632, -2245186, -2267740, -2290294,
- -2312848, -2335402, -2357956, -2380510, -2403064, -2425618, -2448172, -2470726,
- -2493280, -2515834, -2538388, -2560942, -2583496, -2606050, -2628604, -2651158,
- -2673712, -2696266, -2718820, -2741374, -2763928, -2786482, -2809036, -2831590
- ];
-
- static const CBB = const [
- -227, -225, -223, -222, -220, -218, -216, -214, -213, -211, -209, -207, -206,
- -204, -202, -200, -198, -197, -195, -193, -191, -190, -188,
- -186, -184, -183, -181, -179, -177, -175, -174, -172, -170, -168, -167, -165,
- -163, -161, -159, -158, -156, -154, -152, -151, -149, -147, -145, -144, -142,
- -140, -138, -136, -135, -133, -131, -129, -128, -126, -124, -122, -120, -119, -117,
- -115, -113, -112, -110, -108, -106, -105, -103, -101, -99, -97, -96, -94, -92,
- -90, -89, -87, -85, -83, -82, -80, -78, -76, -74, -73, -71, -69, -67, -66, -64,
- -62, -60, -58, -57, -55, -53, -51, -50, -48, -46, -44, -43, -41, -39, -37, -35,
- -34, -32, -30, -28, -27, -25, -23, -21, -19, -18, -16, -14, -12, -11, -9, -7,
- -5, -4, -2, 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 18, 19, 21, 23, 25, 27, 28, 30, 32,
- 34, 35, 37, 39, 41, 43, 44, 46, 48, 50, 51, 53, 55, 57, 58, 60, 62, 64, 66, 67,
- 69, 71, 73, 74, 76, 78, 80, 82, 83, 85, 87, 89, 90, 92, 94, 96, 97, 99, 101, 103,
- 105, 106, 108, 110, 112, 113, 115, 117, 119, 120, 122, 124, 126, 128, 129, 131,
- 133, 135, 136, 138, 140, 142, 144, 145, 147, 149, 151, 152, 154, 156, 158, 159,
- 161, 163, 165, 167, 168, 170, 172, 174, 175, 177, 179, 181, 183, 184, 186, 188,
- 190, 191, 193, 195, 197, 198, 200, 202, 204, 206, 207, 209, 211, 213, 214, 216,
- 218, 220, 222, 223, 225
- ];
-}
-
-class _JpegHuffman {
- List children = [];
- int index = 0;
-}
-
-/*class _JPEG_HuffTables {
- bool ac_table = false;
- Uint32List look_up = new Uint32List(256);
- Uint32List look_up2 = new Uint32List(256);
- Uint8List code_size = new Uint8List(256);
- Uint32List tree = new Uint32List(512);
-}*/
+import 'dart:typed_data';
+
+import '../../exif_data.dart';
+import '../../image_exception.dart';
+import '../../internal/bit_operators.dart';
+import '../../util/input_buffer.dart';
+import 'jpeg.dart';
+import 'jpeg_adobe.dart';
+import 'jpeg_component.dart';
+import 'jpeg_frame.dart';
+import 'jpeg_info.dart';
+import 'jpeg_jfif.dart';
+import 'jpeg_scan.dart';
+
+class JpegData {
+ InputBuffer input;
+ JpegJfif jfif;
+ JpegAdobe adobe;
+ JpegFrame frame;
+ int resetInterval;
+ ExifData exif = ExifData();
+ final List<Int16List> quantizationTables = List(Jpeg.NUM_QUANT_TBLS);
+ final List<JpegFrame> frames = [];
+ final List huffmanTablesAC = [];
+ final List huffmanTablesDC = [];
+ final List<_ComponentData> components = [];
+
+ bool validate(List<int> bytes) {
+ input = InputBuffer(bytes, bigEndian: true);
+
+ int marker = _nextMarker();
+ if (marker != Jpeg.M_SOI) {
+ return false;
+ }
+
+ bool hasSOF = false;
+ bool hasSOS = false;
+
+ marker = _nextMarker();
+ while (marker != Jpeg.M_EOI && !input.isEOS) { // EOI (End of image)
+ _skipBlock();
+ switch (marker) {
+ case Jpeg.M_SOF0: // SOF0 (Start of Frame, Baseline DCT)
+ case Jpeg.M_SOF1: // SOF1 (Start of Frame, Extended DCT)
+ case Jpeg.M_SOF2: // SOF2 (Start of Frame, Progressive DCT)
+ hasSOF = true;
+ break;
+ case Jpeg.M_SOS: // SOS (Start of Scan)
+ hasSOS = true;
+ break;
+ default:
+ }
+
+ marker = _nextMarker();
+ }
+
+ return hasSOF && hasSOS;
+ }
+
+ JpegInfo readInfo(List<int> bytes) {
+ input = InputBuffer(bytes, bigEndian: true);
+
+ int marker = _nextMarker();
+ if (marker != Jpeg.M_SOI) {
+ return null;
+ }
+
+ JpegInfo info = JpegInfo();
+
+ bool hasSOF = false;
+ bool hasSOS = false;
+
+ marker = _nextMarker();
+ while (marker != Jpeg.M_EOI && !input.isEOS) { // EOI (End of image)
+ switch (marker) {
+ case Jpeg.M_SOF0: // SOF0 (Start of Frame, Baseline DCT)
+ case Jpeg.M_SOF1: // SOF1 (Start of Frame, Extended DCT)
+ case Jpeg.M_SOF2: // SOF2 (Start of Frame, Progressive DCT)
+ hasSOF = true;
+ _readFrame(marker, _readBlock());
+ break;
+ case Jpeg.M_SOS: // SOS (Start of Scan)
+ hasSOS = true;
+ _skipBlock();
+ break;
+ default:
+ _skipBlock();
+ break;
+ }
+
+ marker = _nextMarker();
+ }
+
+ if (frame != null) {
+ info.width = frame.samplesPerLine;
+ info.height = frame.scanLines;
+ }
+ frame = null;
+ frames.clear();
+
+ return (hasSOF && hasSOS) ? info : null;
+ }
+
+ void read(List<int> bytes) {
+ input = InputBuffer(bytes, bigEndian: true);
+
+ _read();
+
+ if (frames.length != 1) {
+ throw new ImageException('Only single frame JPEGs supported');
+ }
+
+ for (int i = 0; i < frame.componentsOrder.length; ++i) {
+ /*JpegComponent component =*/ frame.components[frame.componentsOrder[i]];
+ }
+
+ for (int i = 0; i < frame.componentsOrder.length; ++i) {
+ JpegComponent component = frame.components[frame.componentsOrder[i]];
+ components.add(_ComponentData(component.hSamples, frame.maxHSamples,
+ component.vSamples, frame.maxVSamples,
+ _buildComponentData(frame, component)));
+ }
+ }
+
+ int get width => frame.samplesPerLine;
+
+ int get height => frame.scanLines;
+
+ Uint8List getData(int width, int height) {
+ num scaleX = 1;
+ num scaleY = 1;
+ _ComponentData component1;
+ _ComponentData component2;
+ _ComponentData component3;
+ _ComponentData component4;
+ Uint8List component1Line;
+ Uint8List component2Line;
+ Uint8List component3Line;
+ Uint8List component4Line;
+ int offset = 0;
+ int Y, Cb, Cr, K, C, M, Ye, R, G, B;
+ bool colorTransform = false;
+ int dataLength = width * height * components.length;
+ Uint8List data = Uint8List(dataLength);
+
+ switch (components.length) {
+ case 1:
+ component1 = components[0];
+ var lines = component1.lines;
+ int hShift1 = component1.hScaleShift;
+ int vShift1 = component1.vScaleShift;
+ for (int y = 0; y < height; y++) {
+ int y1 = y >> vShift1;
+ component1Line = lines[y1];
+ for (int x = 0; x < width; x++) {
+ int x1 = x >> hShift1;
+ Y = component1Line[x1];
+ data[offset++] = Y;
+ }
+ }
+ break;
+ case 2:
+ // PDF might compress two component data in custom color-space
+ component1 = components[0];
+ component2 = components[1];
+ int hShift1 = component1.hScaleShift;
+ int vShift1 = component1.vScaleShift;
+ int hShift2 = component2.hScaleShift;
+ int vShift2 = component2.vScaleShift;
+
+ for (int y = 0; y < height; y++) {
+ int y1 = y >> vShift1;
+ int y2 = y >> vShift2;
+ component1Line = component1.lines[y1];
+ component2Line = component2.lines[y2];
+
+ for (int x = 0; x < width; x++) {
+ int x1 = x >> hShift1;
+ int x2 = x >> hShift2;
+
+ Y = component1Line[x1];
+ data[offset++] = Y;
+
+ Y = component2Line[x2];
+ data[offset++] = Y;
+ }
+ }
+ break;
+ case 3:
+ // The default transform for three components is true
+ colorTransform = true;
+
+ component1 = components[0];
+ component2 = components[1];
+ component3 = components[2];
+
+ var lines1 = component1.lines;
+ var lines2 = component2.lines;
+ var lines3 = component3.lines;
+
+ int hShift1 = component1.hScaleShift;
+ int vShift1 = component1.vScaleShift;
+ int hShift2 = component2.hScaleShift;
+ int vShift2 = component2.vScaleShift;
+ int hShift3 = component3.hScaleShift;
+ int vShift3 = component3.vScaleShift;
+
+ for (int y = 0; y < height; y++) {
+ int y1 = y >> vShift1;
+ int y2 = y >> vShift2;
+ int y3 = y >> vShift3;
+
+ component1Line = lines1[y1];
+ component2Line = lines2[y2];
+ component3Line = lines3[y3];
+
+ for (int x = 0; x < width; x++) {
+ int x1 = x >> hShift1;
+ int x2 = x >> hShift2;
+ int x3 = x >> hShift3;
+
+ if (!colorTransform) {
+ data[offset++] = component1Line[x1];
+ data[offset++] = component2Line[x2];
+ data[offset++] = component3Line[x3];
+ } else {
+ Y = component1Line[x1] << 8;
+ Cb = component2Line[x2] - 128;
+ Cr = component3Line[x3] - 128;
+
+ R = shiftR((Y + 359 * Cr + 128), 8);
+ G = shiftR((Y - 88 * Cb - 183 * Cr + 128), 8);
+ B = shiftR((Y + 454 * Cb + 128), 8);
+
+ data[offset++] = _clamp8(R);
+ data[offset++] = _clamp8(G);
+ data[offset++] = _clamp8(B);
+ }
+ }
+ }
+ break;
+ case 4:
+ if (adobe == null) {
+ throw new ImageException('Unsupported color mode (4 components)');
+ }
+ // The default transform for four components is false
+ colorTransform = false;
+ // The adobe transform marker overrides any previous setting
+ if (adobe.transformCode != 0) {
+ colorTransform = true;
+ }
+
+ component1 = components[0];
+ component2 = components[1];
+ component3 = components[2];
+ component4 = components[3];
+
+ var lines1 = component1.lines;
+ var lines2 = component2.lines;
+ var lines3 = component3.lines;
+ var lines4 = component4.lines;
+
+ int hShift1 = component1.hScaleShift;
+ int vShift1 = component1.vScaleShift;
+ int hShift2 = component2.hScaleShift;
+ int vShift2 = component2.vScaleShift;
+ int hShift3 = component3.hScaleShift;
+ int vShift3 = component3.vScaleShift;
+ int hShift4 = component4.hScaleShift;
+ int vShift4 = component4.vScaleShift;
+
+ for (int y = 0; y < height; y++) {
+ int y1 = y >> vShift1;
+ int y2 = y >> vShift2;
+ int y3 = y >> vShift3;
+ int y4 = y >> vShift4;
+ component1Line = lines1[y1];
+ component2Line = lines2[y2];
+ component3Line = lines3[y3];
+ component4Line = lines4[y4];
+ for (int x = 0; x < width; x++) {
+ int x1 = x >> hShift1;
+ int x2 = x >> hShift2;
+ int x3 = x >> hShift3;
+ int x4 = x >> hShift4;
+ if (!colorTransform) {
+ C = component1Line[x1];
+ M = component2Line[x2];
+ Ye = component3Line[x3];
+ K = component4Line[x4];
+ } else {
+ Y = component1Line[x1];
+ Cb = component2Line[x2];
+ Cr = component3Line[x3];
+ K = component4Line[x4];
+
+ C = 255 - _clamp8((Y + 1.402 * (Cr - 128)).toInt());
+ M = 255 - _clamp8((Y - 0.3441363 * (Cb - 128) -
+ 0.71413636 * (Cr - 128)).toInt());
+ Ye = 255 - _clamp8((Y + 1.772 * (Cb - 128)).toInt());
+ }
+
+ data[offset++] = C;
+ data[offset++] = M;
+ data[offset++] = Ye;
+ data[offset++] = K;
+ }
+ }
+ break;
+ default:
+ throw new ImageException('Unsupported color mode');
+ }
+
+ return data;
+ }
+
+ void _read() {
+ int marker = _nextMarker();
+ if (marker != Jpeg.M_SOI) { // SOI (Start of Image)
+ throw new ImageException('Start Of Image marker not found.');
+ }
+
+ marker = _nextMarker();
+ while (marker != Jpeg.M_EOI && !input.isEOS) {
+ InputBuffer block = _readBlock();
+ switch (marker) {
+ case Jpeg.M_APP0:
+ case Jpeg.M_APP1:
+ case Jpeg.M_APP2:
+ case Jpeg.M_APP3:
+ case Jpeg.M_APP4:
+ case Jpeg.M_APP5:
+ case Jpeg.M_APP6:
+ case Jpeg.M_APP7:
+ case Jpeg.M_APP8:
+ case Jpeg.M_APP9:
+ case Jpeg.M_APP10:
+ case Jpeg.M_APP11:
+ case Jpeg.M_APP12:
+ case Jpeg.M_APP13:
+ case Jpeg.M_APP14:
+ case Jpeg.M_APP15:
+ case Jpeg.M_COM:
+ _readAppData(marker, block);
+ break;
+
+ case Jpeg.M_DQT: // DQT (Define Quantization Tables)
+ _readDQT(block);
+ break;
+
+ case Jpeg.M_SOF0: // SOF0 (Start of Frame, Baseline DCT)
+ case Jpeg.M_SOF1: // SOF1 (Start of Frame, Extended DCT)
+ case Jpeg.M_SOF2: // SOF2 (Start of Frame, Progressive DCT)
+ _readFrame(marker, block);
+ break;
+
+ case Jpeg.M_SOF3:
+ case Jpeg.M_SOF5:
+ case Jpeg.M_SOF6:
+ case Jpeg.M_SOF7:
+ case Jpeg.M_JPG:
+ case Jpeg.M_SOF9:
+ case Jpeg.M_SOF10:
+ case Jpeg.M_SOF11:
+ case Jpeg.M_SOF13:
+ case Jpeg.M_SOF14:
+ case Jpeg.M_SOF15:
+ throw new ImageException('Unhandled frame type ${marker.toRadixString(16)}');
+
+ case Jpeg.M_DHT: // DHT (Define Huffman Tables)
+ _readDHT(block);
+ break;
+
+ case Jpeg.M_DRI: // DRI (Define Restart Interval)
+ _readDRI(block);
+ break;
+
+ case Jpeg.M_SOS: // SOS (Start of Scan)
+ _readSOS(block);
+ break;
+
+ case 0xff: // Fill bytes
+ if (input[0] != 0xff) {
+ input.offset--;
+ }
+ break;
+
+ default:
+ if (input[-3] == 0xff && input[-2] >= 0xc0 && input[-2] <= 0xfe) {
+ // could be incorrect encoding -- last 0xFF byte of the previous
+ // block was eaten by the encoder
+ input.offset -= 3;
+ break;
+ }
+
+ if (marker != 0) {
+ throw new ImageException('Unknown JPEG marker ' +
+ marker.toRadixString(16));
+ }
+ break;
+ }
+
+ marker = _nextMarker();
+ }
+ }
+
+ void _skipBlock() {
+ int length = input.readUint16();
+ if (length < 2) {
+ throw new ImageException('Invalid Block');
+ }
+ input.offset += length - 2;
+ }
+
+ InputBuffer _readBlock() {
+ int length = input.readUint16();
+ if (length < 2) {
+ throw new ImageException('Invalid Block');
+ }
+ return input.readBytes(length - 2);
+ }
+
+ int _nextMarker() {
+ int c = 0;
+ if (input.isEOS) {
+ return c;
+ }
+
+ do {
+ do {
+ c = input.readByte();
+ } while (c != 0xff && !input.isEOS);
+
+ if (input.isEOS) {
+ return c;
+ }
+
+ do {
+ c = input.readByte();
+ } while (c == 0xff && !input.isEOS);
+ } while (c == 0 && !input.isEOS);
+
+ return c;
+ }
+
+ num _readExifValue(InputBuffer block, int format) {
+ const FMT_BYTE = 1;
+ //const FMT_STRING = 2;
+ const FMT_USHORT = 3;
+ const FMT_ULONG = 4;
+ const FMT_URATIONAL = 5;
+ const FMT_SBYTE = 6;
+ //const FMT_UNDEFINED = 7;
+ const FMT_SSHORT = 8;
+ const FMT_SLONG = 9;
+ const FMT_SRATIONAL = 10;
+ const FMT_SINGLE = 11;
+ const FMT_DOUBLE = 12;
+
+ switch (format) {
+ case FMT_SBYTE:
+ return block.readInt8();
+ case FMT_BYTE:
+ return block.readByte();
+ case FMT_USHORT:
+ return block.readUint16();
+ case FMT_ULONG:
+ return block.readUint32();
+ case FMT_URATIONAL:
+ case FMT_SRATIONAL: {
+ int num = block.readInt32();
+ int den = block.readInt32();
+ if (den == 0) {
+ return 0.0;
+ }
+ return num / den;
+ }
+ case FMT_SSHORT:
+ return block.readInt16();
+ case FMT_SLONG:
+ return block.readInt32();
+ // Not sure if this is correct (never seen float used in Exif format)
+ case FMT_SINGLE:
+ return block.readFloat32();
+ case FMT_DOUBLE:
+ return block.readFloat64();
+ default:
+ return 0;
+ }
+ }
+
+ void _readExifDir(InputBuffer block, [int nesting = 0]) {
+ if (nesting > 4) {
+ return; // Maximum Exif directory nesting exceeded (corrupt Exif header)
+ }
+
+ int numDirEntries = block.readUint16();
+
+ const TAG_ORIENTATION = 0x0112;
+ const TAG_INTEROP_OFFSET = 0xA005;
+ const TAG_EXIF_OFFSET = 0x8769;
+ const maxFormats = 12;
+ const bytesPerFormat = const [0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8];
+
+ for (int di = 0; di < numDirEntries; ++di) {
+ int tag = block.readUint16();
+ int format = block.readUint16();
+ int components = block.readUint32();
+
+ if (format - 1 >= maxFormats) {
+ continue;
+ }
+
+ // too many components
+ if (components > 0x10000) {
+ continue;
+ }
+
+ int byteCount = bytesPerFormat[format];
+
+ // If its bigger than 4 bytes, the dir entry contains an offset.
+ if (byteCount > 4) {
+ int offset = block.readUint32();
+ if (offset + byteCount > block.length) {
+ continue; // Bogus pointer offset and / or bytecount value
+ }
+
+ //ValuePtr = OffsetBase+OffsetVal;
+ }
+
+ switch (tag) {
+ case TAG_ORIENTATION: {
+ num orientation = _readExifValue(block, format);
+ exif.orientation = orientation.toInt();
+ }
+ break;
+ case TAG_EXIF_OFFSET:
+ case TAG_INTEROP_OFFSET:
+ break;
+ default:
+ // skip unknown tags
+ break;
+ }
+ }
+ }
+
+ void _readExifData(InputBuffer block) {
+ if (exif.rawData == null) {
+ exif.rawData = List<Uint8List>();
+ }
+
+ Uint8List rawData = Uint8List.fromList(block.toUint8List());
+ exif.rawData.add(rawData);
+
+ const EXIF_TAG = 0x45786966; // Exif\0\0
+ if (block.readUint32() != EXIF_TAG) {
+ return;
+ }
+ if (block.readUint16() != 0) {
+ return;
+ }
+
+ bool saveEndian = block.bigEndian;
+
+ // Exif Directory
+ String alignment = block.readString(2);
+ if (alignment == 'II') { // Exif is in Intel order
+ block.bigEndian = false;
+ } else if (alignment == 'MM') { // Exif section in Motorola order
+ block.bigEndian = true;
+ } else {
+ return;
+ }
+
+ block.skip(2);
+
+ int offset = block.readUint32();
+ if (offset < 8 || offset > 16){
+ if (offset > block.length - 16) {
+ // invalid offset for first Exif IFD value ;
+ block.bigEndian = saveEndian;
+ return;
+ }
+ }
+
+ if (offset > 8) {
+ block.skip(offset - 8);
+ }
+
+ _readExifDir(block);
+
+ block.bigEndian = saveEndian;
+ }
+
+ void _readAppData(int marker, InputBuffer block) {
+ InputBuffer appData = block;
+
+ if (marker == Jpeg.M_APP0) {
+ // 'JFIF\0'
+ if (appData[0] == 0x4A && appData[1] == 0x46 &&
+ appData[2] == 0x49 && appData[3] == 0x46 && appData[4] == 0) {
+ jfif = JpegJfif();
+ jfif.majorVersion = appData[5];
+ jfif.minorVersion = appData[6];
+ jfif.densityUnits = appData[7];
+ jfif.xDensity = shiftL(appData[8], 8) | appData[9];
+ jfif.yDensity = shiftL(appData[10], 8) | appData[11];
+ jfif.thumbWidth = appData[12];
+ jfif.thumbHeight = appData[13];
+ int thumbSize = 3 * jfif.thumbWidth * jfif.thumbHeight;
+ jfif.thumbData = appData.subset(14 + thumbSize, offset: 14);
+ }
+ } else if (marker == Jpeg.M_APP1) {
+ // 'EXIF\0'
+ _readExifData(appData);
+ } else if (marker == Jpeg.M_APP14) {
+ // 'Adobe\0'
+ if (appData[0] == 0x41 && appData[1] == 0x64 &&
+ appData[2] == 0x6F && appData[3] == 0x62 &&
+ appData[4] == 0x65 && appData[5] == 0) {
+ adobe = JpegAdobe();
+ adobe.version = appData[6];
+ adobe.flags0 = shiftL(appData[7], 8) | appData[8];
+ adobe.flags1 = shiftL(appData[9], 8) | appData[10];
+ adobe.transformCode = appData[11];
+ }
+ } else {
+ //print("!!!! UNHANDLED APP TAG 0x${marker.toRadixString(16)}");
+ }
+ }
+
+ void _readDQT(InputBuffer block) {
+ while (!block.isEOS) {
+ int n = block.readByte();
+ int prec = shiftR(n, 4);
+ n &= 0x0F;
+
+ if (n >= Jpeg.NUM_QUANT_TBLS) {
+ throw new ImageException('Invalid number of quantization tables');
+ }
+
+ if (quantizationTables[n] == null) {
+ quantizationTables[n] = Int16List(64);
+ }
+
+ Int16List tableData = quantizationTables[n];
+ for (int i = 0; i < Jpeg.DCTSIZE2; i++) {
+ int tmp;
+ if (prec != 0) {
+ tmp = block.readUint16();
+ } else {
+ tmp = block.readByte();
+ }
+
+ tableData[Jpeg.dctZigZag[i]] = tmp;
+ }
+ }
+
+ if (!block.isEOS) {
+ throw new ImageException('Bad length for DQT block');
+ }
+ }
+
+ void _readFrame(int marker, InputBuffer block) {
+ if (frame != null) {
+ throw new ImageException('Duplicate JPG frame data found.');
+ }
+
+ frame = JpegFrame();
+ frame.extended = (marker == Jpeg.M_SOF1);
+ frame.progressive = (marker == Jpeg.M_SOF2);
+ frame.precision = block.readByte();
+ frame.scanLines = block.readUint16();
+ frame.samplesPerLine = block.readUint16();
+
+ int numComponents = block.readByte();
+
+ for (int i = 0; i < numComponents; i++) {
+ int componentId = block.readByte();
+ int x = block.readByte();
+ int h = shiftR(x, 4) & 15;
+ int v = x & 15;
+ int qId = block.readByte();
+ frame.componentsOrder.add(componentId);
+ frame.components[componentId] =
+ new JpegComponent(h, v, quantizationTables, qId);
+ }
+
+ frame.prepare();
+ frames.add(frame);
+ }
+
+ void _readDHT(InputBuffer block) {
+ while (!block.isEOS) {
+ int index = block.readByte();
+
+ Uint8List bits = Uint8List(16);
+ int count = 0;
+ for (int j = 0; j < 16; j++) {
+ bits[j] = block.readByte();
+ count += bits[j];
+ }
+
+ Uint8List huffmanValues = Uint8List(count);
+ for (int j = 0; j < count; j++) {
+ huffmanValues[j] = block.readByte();
+ }
+
+ List ht;
+ if (index & 0x10 != 0) { // AC table definition
+ index -= 0x10;
+ ht = huffmanTablesAC;
+ } else { // DC table definition
+ ht = huffmanTablesDC;
+ }
+
+ if (ht.length <= index) {
+ ht.length = index + 1;
+ }
+
+ ht[index] = _buildHuffmanTable(bits, huffmanValues);
+ }
+ }
+
+ void _readDRI(InputBuffer block) {
+ resetInterval = block.readUint16();
+ }
+
+ void _readSOS(InputBuffer block) {
+ int n = block.readByte();
+ if (n < 1 || n > Jpeg.MAX_COMPS_IN_SCAN) {
+ throw new ImageException('Invalid SOS block');
+ }
+
+ List components = List(n);
+ for (int i = 0; i < n; i++) {
+ int id = block.readByte();
+ int c = block.readByte();
+
+ if (!frame.components.containsKey(id)) {
+ throw new ImageException('Invalid Component in SOS block');
+ }
+
+ JpegComponent component = frame.components[id];
+ components[i] = component;
+
+ int dc_tbl_no = shiftR(c, 4) & 15;
+ int ac_tbl_no = c & 15;
+
+ if (dc_tbl_no < huffmanTablesDC.length) {
+ component.huffmanTableDC = huffmanTablesDC[dc_tbl_no];
+ }
+ if (ac_tbl_no < huffmanTablesAC.length) {
+ component.huffmanTableAC = huffmanTablesAC[ac_tbl_no];
+ }
+ }
+
+ int spectralStart = block.readByte();
+ int spectralEnd = block.readByte();
+ int successiveApproximation = block.readByte();
+
+ int Ah = shiftR(successiveApproximation, 4) & 15;
+ int Al = successiveApproximation & 15;
+
+ new JpegScan(input, frame, components, resetInterval,
+ spectralStart, spectralEnd, Ah, Al).decode();
+ }
+
+ List _buildHuffmanTable(Uint8List codeLengths, Uint8List values) {
+ int k = 0;
+ List code = [];
+ int length = 16;
+
+ while (length > 0 && (codeLengths[length - 1] == 0)) {
+ length--;
+ }
+
+ code.add(new _JpegHuffman());
+
+ _JpegHuffman p = code[0];
+ _JpegHuffman q;
+
+ for (int i = 0; i < length; i++) {
+ for (int j = 0; j < codeLengths[i]; j++) {
+ p = code.removeLast();
+ if (p.children.length <= p.index) {
+ p.children.length = p.index + 1;
+ }
+ p.children[p.index] = values[k];
+ while (p.index > 0) {
+ p = code.removeLast();
+ }
+ p.index++;
+ code.add(p);
+ while (code.length <= i) {
+ q = _JpegHuffman();
+ code.add(q);
+ if (p.children.length <= p.index) {
+ p.children.length = p.index + 1;
+ }
+ p.children[p.index] = q.children;
+ p = q;
+ }
+ k++;
+ }
+
+ if ((i + 1) < length) {
+ // p here points to last code
+ q = _JpegHuffman();
+ code.add(q);
+ if (p.children.length <= p.index) {
+ p.children.length = p.index + 1;
+ }
+ p.children[p.index] = q.children;
+ p = q;
+ }
+ }
+
+ return code[0].children;
+ }
+
+ List<Uint8List> _buildComponentData(JpegFrame frame,
+ JpegComponent component) {
+ final int blocksPerLine = component.blocksPerLine;
+ final int blocksPerColumn = component.blocksPerColumn;
+ int samplesPerLine = shiftL(blocksPerLine, 3);
+ Int32List R = Int32List(64);
+ Uint8List r = Uint8List(64);
+ List<Uint8List> lines = List(blocksPerColumn * 8);
+
+ int l = 0;
+ for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
+ int scanLine = shiftL(blockRow, 3);
+ for (int i = 0; i < 8; i++) {
+ lines[l++] = Uint8List(samplesPerLine);
+ }
+
+ for (int blockCol = 0; blockCol < blocksPerLine; blockCol++) {
+ _quantizeAndInverse(component.quantizationTable,
+ component.blocks[blockRow][blockCol],
+ r, R);
+
+ int offset = 0;
+ int sample = shiftL(blockCol, 3);
+ for (int j = 0; j < 8; j++) {
+ Uint8List line = lines[scanLine + j];
+ for (int i = 0; i < 8; i++) {
+ line[sample + i] = r[offset++];
+ }
+ }
+ }
+ }
+
+ return lines;
+ }
+
+ static int toFix(double val) {
+ const int FIXED_POINT = 20;
+ const int ONE = 1 << FIXED_POINT;
+ return (val * ONE).toInt() & 0xffffffff;
+ }
+
+ static int _clamp8(int i) => i < 0 ? 0 : i > 255 ? 255 : i;
+
+ static Uint8List dctClip;
+
+ /**
+ * Quantize the coefficients and apply IDCT.
+ *
+ * A port of poppler's IDCT method which in turn is taken from:
+ * Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
+ * "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
+ * IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, 988-991.
+ */
+ void _quantizeAndInverse(Int16List quantizationTable,
+ Int32List coefBlock,
+ Uint8List dataOut,
+ Int32List dataIn) {
+ Int32List p = dataIn;
+
+ const int dctClipOffset = 256;
+ const int dctClipLength = 768;
+ if (dctClip == null) {
+ dctClip = Uint8List(dctClipLength);
+ int i;
+ for (i = -256; i < 0; ++i) {
+ dctClip[dctClipOffset + i] = 0;
+ }
+ for (i = 0; i < 256; ++i) {
+ dctClip[dctClipOffset + i] = i;
+ }
+ for (i = 256; i < 512; ++i) {
+ dctClip[dctClipOffset + i] = 255;
+ }
+ }
+
+ // IDCT constants (20.12 fixed point format)
+ const int COS_1 = 4017; // cos(pi/16)*4096
+ const int SIN_1 = 799; // sin(pi/16)*4096
+ const int COS_3 = 3406; // cos(3*pi/16)*4096
+ const int SIN_3 = 2276; // sin(3*pi/16)*4096
+ const int COS_6 = 1567; // cos(6*pi/16)*4096
+ const int SIN_6 = 3784; // sin(6*pi/16)*4096
+ const int SQRT_2 = 5793; // sqrt(2)*4096
+ const int SQRT_1D2 = 2896; // sqrt(2) / 2
+
+ // de-quantize
+ for (int i = 0; i < 64; i++) {
+ p[i] = (coefBlock[i] * quantizationTable[i]);
+ }
+
+ // inverse DCT on rows
+ int row = 0;
+ for (int i = 0; i < 8; ++i, row += 8) {
+ // check for all-zero AC coefficients
+ if (p[1 + row] == 0 &&
+ p[2 + row] == 0 &&
+ p[3 + row] == 0 &&
+ p[4 + row] == 0 &&
+ p[5 + row] == 0 &&
+ p[6 + row] == 0 &&
+ p[7 + row] == 0) {
+ int t = shiftR((SQRT_2 * p[0 + row] + 512), 10);
+ p[row + 0] = t;
+ p[row + 1] = t;
+ p[row + 2] = t;
+ p[row + 3] = t;
+ p[row + 4] = t;
+ p[row + 5] = t;
+ p[row + 6] = t;
+ p[row + 7] = t;
+ continue;
+ }
+
+ // stage 4
+ int v0 = shiftR((SQRT_2 * p[0 + row] + 128), 8);
+ int v1 = shiftR((SQRT_2 * p[4 + row] + 128), 8);
+ int v2 = p[2 + row];
+ int v3 = p[6 + row];
+ int v4 = shiftR((SQRT_1D2 * (p[1 + row] - p[7 + row]) + 128), 8);
+ int v7 = shiftR((SQRT_1D2 * (p[1 + row] + p[7 + row]) + 128), 8);
+ int v5 = shiftL(p[3 + row], 4);
+ int v6 = shiftL(p[5 + row], 4);
+
+ // stage 3
+ int t = shiftR((v0 - v1 + 1), 1);
+ v0 = shiftR((v0 + v1 + 1), 1);
+ v1 = t;
+ t = shiftR((v2 * SIN_6 + v3 * COS_6 + 128), 8);
+ v2 = shiftR((v2 * COS_6 - v3 * SIN_6 + 128), 8);
+ v3 = t;
+ t = shiftR((v4 - v6 + 1), 1);
+ v4 = shiftR((v4 + v6 + 1), 1);
+ v6 = t;
+ t = shiftR((v7 + v5 + 1), 1);
+ v5 = shiftR((v7 - v5 + 1), 1);
+ v7 = t;
+
+ // stage 2
+ t = shiftR((v0 - v3 + 1), 1);
+ v0 = shiftR((v0 + v3 + 1), 1);
+ v3 = t;
+ t = shiftR((v1 - v2 + 1), 1);
+ v1 = shiftR((v1 + v2 + 1), 1);
+ v2 = t;
+ t = shiftR((v4 * SIN_3 + v7 * COS_3 + 2048), 12);
+ v4 = shiftR((v4 * COS_3 - v7 * SIN_3 + 2048), 12);
+ v7 = t;
+ t = shiftR((v5 * SIN_1 + v6 * COS_1 + 2048), 12);
+ v5 = shiftR((v5 * COS_1 - v6 * SIN_1 + 2048), 12);
+ v6 = t;
+
+ // stage 1
+ p[0 + row] = (v0 + v7);
+ p[7 + row] = (v0 - v7);
+ p[1 + row] = (v1 + v6);
+ p[6 + row] = (v1 - v6);
+ p[2 + row] = (v2 + v5);
+ p[5 + row] = (v2 - v5);
+ p[3 + row] = (v3 + v4);
+ p[4 + row] = (v3 - v4);
+ }
+
+ // inverse DCT on columns
+ for (int i = 0; i < 8; ++i) {
+ int col = i;
+
+ // check for all-zero AC coefficients
+ if (p[1 * 8 + col] == 0 &&
+ p[2 * 8 + col] == 0 &&
+ p[3 * 8 + col] == 0 &&
+ p[4 * 8 + col] == 0 &&
+ p[5 * 8 + col] == 0 &&
+ p[6 * 8 + col] == 0 &&
+ p[7 * 8 + col] == 0) {
+ int t = shiftR((SQRT_2 * dataIn[i] + 8192), 14);
+ p[0 * 8 + col] = t;
+ p[1 * 8 + col] = t;
+ p[2 * 8 + col] = t;
+ p[3 * 8 + col] = t;
+ p[4 * 8 + col] = t;
+ p[5 * 8 + col] = t;
+ p[6 * 8 + col] = t;
+ p[7 * 8 + col] = t;
+ continue;
+ }
+
+ // stage 4
+ int v0 = shiftR((SQRT_2 * p[0 * 8 + col] + 2048), 12);
+ int v1 = shiftR((SQRT_2 * p[4 * 8 + col] + 2048), 12);
+ int v2 = p[2 * 8 + col];
+ int v3 = p[6 * 8 + col];
+ int v4 = shiftR((SQRT_1D2 * (p[1 * 8 + col] - p[7 * 8 + col]) + 2048), 12);
+ int v7 = shiftR((SQRT_1D2 * (p[1 * 8 + col] + p[7 * 8 + col]) + 2048), 12);
+ int v5 = p[3 * 8 + col];
+ int v6 = p[5 * 8 + col];
+
+ // stage 3
+ int t = shiftR((v0 - v1 + 1), 1);
+ v0 = shiftR((v0 + v1 + 1), 1);
+ v1 = t;
+ t = shiftR((v2 * SIN_6 + v3 * COS_6 + 2048), 12);
+ v2 = shiftR((v2 * COS_6 - v3 * SIN_6 + 2048), 12);
+ v3 = t;
+ t = shiftR((v4 - v6 + 1), 1);
+ v4 = shiftR((v4 + v6 + 1), 1);
+ v6 = t;
+ t = shiftR((v7 + v5 + 1), 1);
+ v5 = shiftR((v7 - v5 + 1), 1);
+ v7 = t;
+
+ // stage 2
+ t = shiftR((v0 - v3 + 1), 1);
+ v0 = shiftR((v0 + v3 + 1), 1);
+ v3 = t;
+ t = shiftR((v1 - v2 + 1), 1);
+ v1 = shiftR((v1 + v2 + 1), 1);
+ v2 = t;
+ t = shiftR((v4 * SIN_3 + v7 * COS_3 + 2048), 12);
+ v4 = shiftR((v4 * COS_3 - v7 * SIN_3 + 2048), 12);
+ v7 = t;
+ t = shiftR((v5 * SIN_1 + v6 * COS_1 + 2048), 12);
+ v5 = shiftR((v5 * COS_1 - v6 * SIN_1 + 2048), 12);
+ v6 = t;
+
+ // stage 1
+ p[0 * 8 + col] = (v0 + v7);
+ p[7 * 8 + col] = (v0 - v7);
+ p[1 * 8 + col] = (v1 + v6);
+ p[6 * 8 + col] = (v1 - v6);
+ p[2 * 8 + col] = (v2 + v5);
+ p[5 * 8 + col] = (v2 - v5);
+ p[3 * 8 + col] = (v3 + v4);
+ p[4 * 8 + col] = (v3 - v4);
+ }
+
+ // convert to 8-bit integers
+ for (int i = 0; i < 64; ++i) {
+ dataOut[i] = dctClip[(dctClipOffset + 128 + shiftR((p[i] + 8), 4))];
+ }
+ }
+
+ static const CRR = const [
+ -179, -178, -177, -175, -174, -172, -171, -170, -168, -167, -165, -164, -163,
+ -161, -160, -158, -157, -156, -154, -153, -151, -150, -149, -147, -146, -144, -143,
+ -142, -140, -139, -137, -136, -135, -133, -132, -130, -129, -128, -126, -125,
+ -123, -122, -121, -119, -118, -116, -115, -114, -112, -111, -109, -108, -107,
+ -105, -104, -102, -101, -100, -98, -97, -95, -94, -93, -91, -90, -88, -87, -86,
+ -84, -83, -81, -80, -79, -77, -76, -74, -73, -72, -70, -69, -67, -66, -64, -63,
+ -62, -60, -59, -57, -56, -55, -53, -52, -50, -49, -48, -46, -45, -43, -42, -41,
+ -39, -38, -36, -35, -34, -32, -31, -29, -28, -27, -25, -24, -22, -21, -20, -18,
+ -17, -15, -14, -13, -11, -10, -8, -7, -6, -4, -3, -1, 0, 1, 3, 4, 6, 7, 8, 10,
+ 11, 13, 14, 15, 17, 18, 20, 21, 22, 24, 25, 27, 28, 29, 31, 32, 34, 35, 36, 38,
+ 39, 41, 42, 43, 45, 46, 48, 49, 50, 52, 53, 55, 56, 57, 59, 60, 62, 63, 64, 66,
+ 67, 69, 70, 72, 73, 74, 76, 77, 79, 80, 81, 83, 84, 86, 87, 88, 90, 91, 93, 94,
+ 95, 97, 98, 100, 101, 102, 104, 105, 107, 108, 109, 111, 112, 114, 115, 116, 118,
+ 119, 121, 122, 123, 125, 126, 128, 129, 130, 132, 133, 135, 136, 137, 139, 140,
+ 142, 143, 144, 146, 147, 149, 150, 151, 153, 154, 156, 157, 158, 160, 161, 163,
+ 164, 165, 167, 168, 170, 171, 172, 174, 175, 177, 178
+ ];
+
+ static const CRG = const [
+ 5990656, 5943854, 5897052, 5850250, 5803448, 5756646, 5709844, 5663042, 5616240,
+ 5569438, 5522636, 5475834, 5429032, 5382230, 5335428, 5288626, 5241824, 5195022,
+ 5148220, 5101418, 5054616, 5007814, 4961012, 4914210, 4867408, 4820606, 4773804,
+ 4727002, 4680200, 4633398, 4586596, 4539794, 4492992, 4446190, 4399388, 4352586,
+ 4305784, 4258982, 4212180, 4165378, 4118576, 4071774, 4024972, 3978170, 3931368,
+ 3884566, 3837764, 3790962, 3744160, 3697358, 3650556, 3603754, 3556952, 3510150,
+ 3463348, 3416546, 3369744, 3322942, 3276140, 3229338, 3182536, 3135734, 3088932,
+ 3042130, 2995328, 2948526, 2901724, 2854922, 2808120, 2761318, 2714516, 2667714,
+ 2620912, 2574110, 2527308, 2480506, 2433704, 2386902, 2340100, 2293298, 2246496,
+ 2199694, 2152892, 2106090, 2059288, 2012486, 1965684, 1918882, 1872080, 1825278,
+ 1778476, 1731674, 1684872, 1638070, 1591268, 1544466, 1497664, 1450862, 1404060,
+ 1357258, 1310456, 1263654, 1216852, 1170050, 1123248, 1076446, 1029644, 982842,
+ 936040, 889238, 842436, 795634, 748832, 702030, 655228, 608426, 561624, 514822,
+ 468020, 421218, 374416, 327614, 280812, 234010, 187208, 140406, 93604, 46802, 0,
+ -46802, -93604, -140406, -187208, -234010, -280812, -327614, -374416, -421218,
+ -468020, -514822, -561624, -608426, -655228, -702030, -748832, -795634, -842436,
+ -889238, -936040, -982842, -1029644, -1076446, -1123248, -1170050, -1216852,
+ -1263654, -1310456, -1357258, -1404060, -1450862, -1497664, -1544466, -1591268,
+ -1638070, -1684872, -1731674, -1778476, -1825278, -1872080, -1918882, -1965684,
+ -2012486, -2059288, -2106090, -2152892, -2199694, -2246496, -2293298, -2340100,
+ -2386902, -2433704, -2480506, -2527308, -2574110, -2620912, -2667714, -2714516,
+ -2761318, -2808120, -2854922, -2901724, -2948526, -2995328, -3042130, -3088932,
+ -3135734, -3182536, -3229338, -3276140, -3322942, -3369744, -3416546, -3463348,
+ -3510150, -3556952, -3603754, -3650556, -3697358, -3744160, -3790962, -3837764,
+ -3884566, -3931368, -3978170, -4024972, -4071774, -4118576, -4165378, -4212180,
+ -4258982, -4305784, -4352586, -4399388, -4446190, -4492992, -4539794, -4586596,
+ -4633398, -4680200, -4727002, -4773804, -4820606, -4867408, -4914210, -4961012,
+ -5007814, -5054616, -5101418, -5148220, -5195022, -5241824, -5288626, -5335428,
+ -5382230, -5429032, -5475834, -5522636, -5569438, -5616240, -5663042, -5709844,
+ -5756646, -5803448, -5850250, -5897052, -5943854
+ ];
+
+ static const CBG = const [
+ 2919680, 2897126, 2874572, 2852018, 2829464, 2806910, 2784356, 2761802, 2739248, 2716694,
+ 2694140, 2671586, 2649032, 2626478, 2603924, 2581370, 2558816, 2536262, 2513708,
+ 2491154, 2468600, 2446046, 2423492, 2400938, 2378384, 2355830, 2333276, 2310722,
+ 2288168, 2265614, 2243060, 2220506, 2197952, 2175398, 2152844, 2130290, 2107736,
+ 2085182, 2062628, 2040074, 2017520, 1994966, 1972412, 1949858, 1927304, 1904750,
+ 1882196, 1859642, 1837088, 1814534, 1791980, 1769426, 1746872, 1724318, 1701764,
+ 1679210, 1656656, 1634102, 1611548, 1588994, 1566440, 1543886, 1521332, 1498778,
+ 1476224, 1453670, 1431116, 1408562, 1386008, 1363454, 1340900, 1318346, 1295792,
+ 1273238, 1250684, 1228130, 1205576, 1183022, 1160468, 1137914, 1115360,
+ 1092806, 1070252, 1047698, 1025144, 1002590, 980036, 957482, 934928, 912374,
+ 889820, 867266, 844712, 822158, 799604, 777050, 754496, 731942, 709388, 686834,
+ 664280, 641726, 619172, 596618, 574064, 551510, 528956, 506402, 483848, 461294,
+ 438740, 416186, 393632, 371078, 348524, 325970, 303416, 280862, 258308, 235754,
+ 213200, 190646, 168092, 145538, 122984, 100430, 77876, 55322, 32768, 10214, -12340,
+ -34894, -57448, -80002, -102556, -125110, -147664, -170218, -192772, -215326,
+ -237880, -260434, -282988, -305542, -328096, -350650, -373204, -395758, -418312,
+ -440866, -463420, -485974, -508528, -531082, -553636, -576190, -598744, -621298,
+ -643852, -666406, -688960, -711514, -734068, -756622, -779176, -801730, -824284,
+ -846838, -869392, -891946, -914500, -937054, -959608, -982162, -1004716, -1027270,
+ -1049824, -1072378, -1094932, -1117486, -1140040, -1162594, -1185148, -1207702,
+ -1230256, -1252810, -1275364, -1297918, -1320472, -1343026, -1365580, -1388134,
+ -1410688, -1433242, -1455796, -1478350, -1500904, -1523458, -1546012, -1568566,
+ -1591120, -1613674, -1636228, -1658782, -1681336, -1703890, -1726444, -1748998,
+ -1771552, -1794106, -1816660, -1839214, -1861768, -1884322, -1906876, -1929430,
+ -1951984, -1974538, -1997092, -2019646, -2042200, -2064754, -2087308, -2109862,
+ -2132416, -2154970, -2177524, -2200078, -2222632, -2245186, -2267740, -2290294,
+ -2312848, -2335402, -2357956, -2380510, -2403064, -2425618, -2448172, -2470726,
+ -2493280, -2515834, -2538388, -2560942, -2583496, -2606050, -2628604, -2651158,
+ -2673712, -2696266, -2718820, -2741374, -2763928, -2786482, -2809036, -2831590
+ ];
+
+ static const CBB = const [
+ -227, -225, -223, -222, -220, -218, -216, -214, -213, -211, -209, -207, -206,
+ -204, -202, -200, -198, -197, -195, -193, -191, -190, -188,
+ -186, -184, -183, -181, -179, -177, -175, -174, -172, -170, -168, -167, -165,
+ -163, -161, -159, -158, -156, -154, -152, -151, -149, -147, -145, -144, -142,
+ -140, -138, -136, -135, -133, -131, -129, -128, -126, -124, -122, -120, -119, -117,
+ -115, -113, -112, -110, -108, -106, -105, -103, -101, -99, -97, -96, -94, -92,
+ -90, -89, -87, -85, -83, -82, -80, -78, -76, -74, -73, -71, -69, -67, -66, -64,
+ -62, -60, -58, -57, -55, -53, -51, -50, -48, -46, -44, -43, -41, -39, -37, -35,
+ -34, -32, -30, -28, -27, -25, -23, -21, -19, -18, -16, -14, -12, -11, -9, -7,
+ -5, -4, -2, 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 18, 19, 21, 23, 25, 27, 28, 30, 32,
+ 34, 35, 37, 39, 41, 43, 44, 46, 48, 50, 51, 53, 55, 57, 58, 60, 62, 64, 66, 67,
+ 69, 71, 73, 74, 76, 78, 80, 82, 83, 85, 87, 89, 90, 92, 94, 96, 97, 99, 101, 103,
+ 105, 106, 108, 110, 112, 113, 115, 117, 119, 120, 122, 124, 126, 128, 129, 131,
+ 133, 135, 136, 138, 140, 142, 144, 145, 147, 149, 151, 152, 154, 156, 158, 159,
+ 161, 163, 165, 167, 168, 170, 172, 174, 175, 177, 179, 181, 183, 184, 186, 188,
+ 190, 191, 193, 195, 197, 198, 200, 202, 204, 206, 207, 209, 211, 213, 214, 216,
+ 218, 220, 222, 223, 225
+ ];
+}
+
+class _JpegHuffman {
+ List children = [];
+ int index = 0;
+}
+
+class _ComponentData {
+ int hSamples;
+ int maxHSamples;
+ int vSamples;
+ int maxVSamples;
+ List<Uint8List> lines;
+ int hScaleShift;
+ int vScaleShift;
+ _ComponentData(this.hSamples, this.maxHSamples, this.vSamples,
+ this.maxVSamples, this.lines)
+ : hScaleShift = (hSamples == 1 && maxHSamples == 2) ? 1 : 0
+ , vScaleShift = (vSamples == 1 && maxVSamples == 2) ? 1 : 0;
+}
diff --git a/image/lib/src/formats/jpeg/jpeg_frame.dart b/image/lib/src/formats/jpeg/jpeg_frame.dart
old mode 100644
new mode 100755
index bac3e4d..776f5db
--- a/image/lib/src/formats/jpeg/jpeg_frame.dart
+++ b/image/lib/src/formats/jpeg/jpeg_frame.dart
@@ -1,55 +1,51 @@
-import 'dart:typed_data';
-
-import 'jpeg_component.dart';
-
-class JpegFrame {
- bool extended;
- bool progressive;
- int precision;
- int scanLines;
- int samplesPerLine;
- int maxH = 0;
- int maxV = 0;
- int mcusPerLine;
- int mcusPerColumn;
- final Map<int, JpegComponent> components = {};
- final List<int> componentsOrder = new List<int>();
-
- void prepare() {
- for (int componentId in components.keys) {
- JpegComponent component = components[componentId];
- if (maxH < component.h) {
- maxH = component.h;
- }
- if (maxV < component.v) {
- maxV = component.v;
- }
- }
-
- mcusPerLine = (samplesPerLine / 8 / maxH).ceil();
- mcusPerColumn = (scanLines / 8 / maxV).ceil();
-
- for (int componentId in components.keys) {
- JpegComponent component = components[componentId];
- int blocksPerLine = ((samplesPerLine / 8).ceil() *
- component.h / maxH).ceil();
- int blocksPerColumn = ((scanLines / 8).ceil() *
- component.v / maxV).ceil();
- int blocksPerLineForMcu = mcusPerLine * component.h;
- int blocksPerColumnForMcu = mcusPerColumn * component.v;
-
- List blocks = new List(blocksPerColumnForMcu);
- for (int i = 0; i < blocksPerColumnForMcu; i++) {
- List row = new List(blocksPerLineForMcu);
- for (int j = 0; j < blocksPerLineForMcu; j++) {
- row[j] = new Int32List(64);
- }
- blocks[i] = row;
- }
-
- component.blocksPerLine = blocksPerLine;
- component.blocksPerColumn = blocksPerColumn;
- component.blocks = blocks;
- }
- }
-}
+import 'dart:math';
+import 'dart:typed_data';
+import 'jpeg_component.dart';
+
+class JpegFrame {
+ bool extended;
+ bool progressive;
+ int precision;
+ int scanLines;
+ int samplesPerLine;
+ int maxHSamples = 0;
+ int maxVSamples = 0;
+ int mcusPerLine;
+ int mcusPerColumn;
+ final Map<int, JpegComponent> components = {};
+ final List<int> componentsOrder = List<int>();
+
+ void prepare() {
+ for (int componentId in components.keys) {
+ JpegComponent component = components[componentId];
+ maxHSamples = max(maxHSamples, component.hSamples);
+ maxVSamples = max(maxVSamples, component.vSamples);
+ }
+
+ mcusPerLine = (samplesPerLine / 8 / maxHSamples).ceil();
+ mcusPerColumn = (scanLines / 8 / maxVSamples).ceil();
+
+ for (int componentId in components.keys) {
+ JpegComponent component = components[componentId];
+ int blocksPerLine = ((samplesPerLine / 8).ceil() *
+ component.hSamples / maxHSamples).ceil();
+ int blocksPerColumn = ((scanLines / 8).ceil() *
+ component.vSamples / maxVSamples).ceil();
+ int blocksPerLineForMcu = mcusPerLine * component.hSamples;
+ int blocksPerColumnForMcu = mcusPerColumn * component.vSamples;
+
+ List blocks = List(blocksPerColumnForMcu);
+ for (int i = 0; i < blocksPerColumnForMcu; i++) {
+ List row = List(blocksPerLineForMcu);
+ for (int j = 0; j < blocksPerLineForMcu; j++) {
+ row[j] = Int32List(64);
+ }
+ blocks[i] = row;
+ }
+
+ component.blocksPerLine = blocksPerLine;
+ component.blocksPerColumn = blocksPerColumn;
+ component.blocks = blocks;
+ }
+ }
+}
diff --git a/image/lib/src/formats/jpeg/jpeg_info.dart b/image/lib/src/formats/jpeg/jpeg_info.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/jpeg/jpeg_jfif.dart b/image/lib/src/formats/jpeg/jpeg_jfif.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/jpeg/jpeg_scan.dart b/image/lib/src/formats/jpeg/jpeg_scan.dart
old mode 100644
new mode 100755
index cef2595..50b2ccf
--- a/image/lib/src/formats/jpeg/jpeg_scan.dart
+++ b/image/lib/src/formats/jpeg/jpeg_scan.dart
@@ -1,323 +1,323 @@
-import '../../image_exception.dart';
-import '../../util/input_buffer.dart';
-import 'jpeg.dart';
-import 'jpeg_component.dart';
-import 'jpeg_frame.dart';
-
-
-class JpegScan {
- InputBuffer input;
- JpegFrame frame;
- int precision;
- int samplesPerLine;
- int scanLines;
- int mcusPerLine;
- bool progressive;
- int maxH;
- int maxV;
- List components;
- int resetInterval;
- int spectralStart;
- int spectralEnd;
- int successivePrev;
- int successive;
-
- int bitsData = 0;
- int bitsCount = 0;
- int eobrun = 0;
- int successiveACState = 0;
- int successiveACNextValue;
-
- JpegScan(this.input, this.frame, this.components,
- this.resetInterval, this.spectralStart, this.spectralEnd,
- this.successivePrev, this.successive) {
- precision = frame.precision;
- samplesPerLine = frame.samplesPerLine;
- scanLines = frame.scanLines;
- mcusPerLine = frame.mcusPerLine;
- progressive = frame.progressive;
- maxH = frame.maxH;
- maxV = frame.maxV;
- }
-
- void decode() {
- int componentsLength = components.length;
- JpegComponent component;
- var decodeFn;
-
- if (progressive) {
- if (spectralStart == 0) {
- decodeFn = successivePrev == 0 ? _decodeDCFirst : _decodeDCSuccessive;
- } else {
- decodeFn = successivePrev == 0 ? _decodeACFirst : _decodeACSuccessive;
- }
- } else {
- decodeFn = _decodeBaseline;
- }
-
- int mcu = 0;
-
- int mcuExpected;
- if (componentsLength == 1) {
- mcuExpected = (components[0].blocksPerLine * components[0].blocksPerColumn);
- } else {
- mcuExpected = (mcusPerLine * frame.mcusPerColumn);
- }
-
- if (resetInterval == null || resetInterval == 0) {
- resetInterval = mcuExpected;
- }
-
- int h, v;
- while (mcu < mcuExpected) {
- // reset interval stuff
- for (int i = 0; i < componentsLength; i++) {
- components[i].pred = 0;
- }
- eobrun = 0;
-
- if (componentsLength == 1) {
- component = components[0];
- for (int n = 0; n < resetInterval; n++) {
- _decodeBlock(component, decodeFn, mcu);
- mcu++;
- }
- } else {
- for (int n = 0; n < resetInterval; n++) {
- for (int i = 0; i < componentsLength; i++) {
- component = components[i];
- h = component.h;
- v = component.v;
- for (int j = 0; j < v; j++) {
- for (int k = 0; k < h; k++) {
- _decodeMcu(component, decodeFn, mcu, j, k);
- }
- }
- }
- mcu++;
- }
- }
-
- // find marker
- bitsCount = 0;
- int m1 = input[0];
- int m2 = input[1];
- if (m1 == 0xff) {
- if (m2 >= Jpeg.M_RST0 && m2 <= Jpeg.M_RST7) {
- input.offset += 2;
- } else {
- break;
- }
- }
- }
- }
-
- int _readBit() {
- if (bitsCount > 0) {
- bitsCount--;
- return (bitsData >> bitsCount) & 1;
- }
-
- if (input.isEOS) {
- return null;
- }
-
- bitsData = input.readByte();
- if (bitsData == 0xff) {
- int nextByte = input.readByte();
- if (nextByte != 0) {
- throw new ImageException('unexpected marker: ' +
- ((bitsData << 8) | nextByte).toRadixString(16));
- }
- }
-
- bitsCount = 7;
- return (bitsData >> 7) & 1;
- }
-
- int _decodeHuffman(tree) {
- var node = tree;
- int bit;
- while ((bit = _readBit()) != null) {
- node = node[bit];
- if (node is num) {
- return node.toInt();
- }
- }
-
- return null;
- }
-
- int _receive(int length) {
- int n = 0;
- while (length > 0) {
- int bit = _readBit();
- if (bit == null) {
- return null;
- }
- n = ((n << 1) | bit);
- length--;
- }
- return n;
- }
-
- int _receiveAndExtend(int length) {
- if (length == 1) {
- return _readBit() == 1 ? 1 : -1;
- }
- int n = _receive(length);
- if (n >= (1 << (length - 1))) {
- return n;
- }
- return n + (-1 << length) + 1;
- }
-
- void _decodeBaseline(JpegComponent component, List zz) {
- int t = _decodeHuffman(component.huffmanTableDC);
- int diff = t == 0 ? 0 : _receiveAndExtend(t);
- component.pred += diff;
- zz[0] = component.pred;
-
- int k = 1;
- while (k < 64) {
- var rs = _decodeHuffman(component.huffmanTableAC);
- int s = rs & 15;
- int r = rs >> 4;
- if (s == 0) {
- if (r < 15) {
- break;
- }
- k += 16;
- continue;
- }
-
- k += r;
-
- s = _receiveAndExtend(s);
-
- int z = Jpeg.dctZigZag[k];
- zz[z] = s;
- k++;
- }
- }
-
- void _decodeDCFirst(JpegComponent component, List zz) {
- int t = _decodeHuffman(component.huffmanTableDC);
- int diff = (t == 0) ? 0 : (_receiveAndExtend(t) << successive);
- component.pred += diff;
- zz[0] = component.pred;
- }
-
- void _decodeDCSuccessive(JpegComponent component, List zz) {
- zz[0] = (zz[0] | (_readBit() << successive));
- }
-
- void _decodeACFirst(JpegComponent component, List zz) {
- if (eobrun > 0) {
- eobrun--;
- return;
- }
- int k = spectralStart;
- int e = spectralEnd;
- while (k <= e) {
- int rs = _decodeHuffman(component.huffmanTableAC);
- int s = rs & 15;
- int r = rs >> 4;
- if (s == 0) {
- if (r < 15) {
- eobrun = (_receive(r) + (1 << r) - 1);
- break;
- }
- k += 16;
- continue;
- }
- k += r;
- int z = Jpeg.dctZigZag[k];
- zz[z] = (_receiveAndExtend(s) * (1 << successive));
- k++;
- }
- }
-
- void _decodeACSuccessive(JpegComponent component, zz) {
- int k = spectralStart;
- int e = spectralEnd;
- int s = 0;
- int r = 0;
- while (k <= e) {
- int z = Jpeg.dctZigZag[k];
- switch (successiveACState) {
- case 0: // initial state
- int rs = _decodeHuffman(component.huffmanTableAC);
- if (rs == null) continue;
- s = rs & 15;
- r = rs >> 4;
- if (s == 0) {
- if (r < 15) {
- eobrun = (_receive(r) + (1 << r));
- successiveACState = 4;
- } else {
- r = 16;
- successiveACState = 1;
- }
- } else {
- if (s != 1) {
- throw new ImageException('invalid ACn encoding');
- }
- successiveACNextValue = _receiveAndExtend(s);
- successiveACState = r != 0 ? 2 : 3;
- }
- continue;
- case 1: // skipping r zero items
- case 2:
- if (zz[z] != 0) {
- zz[z] += (_readBit() << successive);
- } else {
- r--;
- if (r == 0) {
- successiveACState = successiveACState == 2 ? 3 : 0;
- }
- }
- break;
- case 3: // set value for a zero item
- if (zz[z] != 0) {
- zz[z] += (_readBit() << successive);
- } else {
- zz[z] = (successiveACNextValue << successive);
- successiveACState = 0;
- }
- break;
- case 4: // eob
- if (zz[z] != 0) {
- zz[z] += (_readBit() << successive);
- }
- break;
- }
- k++;
- }
- if (successiveACState == 4) {
- eobrun--;
- if (eobrun == 0) {
- successiveACState = 0;
- }
- }
- }
-
- void _decodeMcu(JpegComponent component, decodeFn,
- int mcu, int row, int col) {
- int mcuRow = (mcu ~/ mcusPerLine);
- int mcuCol = mcu % mcusPerLine;
- int blockRow = mcuRow * component.v + row;
- int blockCol = mcuCol * component.h + col;
- if (blockRow >= component.blocks.length ||
- blockCol >= component.blocks[blockRow].length) {
- return;
- }
- decodeFn(component, component.blocks[blockRow][blockCol]);
- }
-
- void _decodeBlock(JpegComponent component, decodeFn, int mcu) {
- int blockRow = mcu ~/ component.blocksPerLine;
- int blockCol = mcu % component.blocksPerLine;
- decodeFn(component, component.blocks[blockRow][blockCol]);
- }
-}
+import '../../image_exception.dart';
+import '../../util/input_buffer.dart';
+import 'jpeg.dart';
+import 'jpeg_component.dart';
+import 'jpeg_frame.dart';
+
+
+class JpegScan {
+ InputBuffer input;
+ JpegFrame frame;
+ int precision;
+ int samplesPerLine;
+ int scanLines;
+ int mcusPerLine;
+ bool progressive;
+ int maxH;
+ int maxV;
+ List components;
+ int resetInterval;
+ int spectralStart;
+ int spectralEnd;
+ int successivePrev;
+ int successive;
+
+ int bitsData = 0;
+ int bitsCount = 0;
+ int eobrun = 0;
+ int successiveACState = 0;
+ int successiveACNextValue;
+
+ JpegScan(this.input, this.frame, this.components,
+ this.resetInterval, this.spectralStart, this.spectralEnd,
+ this.successivePrev, this.successive) {
+ precision = frame.precision;
+ samplesPerLine = frame.samplesPerLine;
+ scanLines = frame.scanLines;
+ mcusPerLine = frame.mcusPerLine;
+ progressive = frame.progressive;
+ maxH = frame.maxHSamples;
+ maxV = frame.maxVSamples;
+ }
+
+ void decode() {
+ int componentsLength = components.length;
+ JpegComponent component;
+ var decodeFn;
+
+ if (progressive) {
+ if (spectralStart == 0) {
+ decodeFn = successivePrev == 0 ? _decodeDCFirst : _decodeDCSuccessive;
+ } else {
+ decodeFn = successivePrev == 0 ? _decodeACFirst : _decodeACSuccessive;
+ }
+ } else {
+ decodeFn = _decodeBaseline;
+ }
+
+ int mcu = 0;
+
+ int mcuExpected;
+ if (componentsLength == 1) {
+ mcuExpected = (components[0].blocksPerLine * components[0].blocksPerColumn);
+ } else {
+ mcuExpected = (mcusPerLine * frame.mcusPerColumn);
+ }
+
+ if (resetInterval == null || resetInterval == 0) {
+ resetInterval = mcuExpected;
+ }
+
+ int h, v;
+ while (mcu < mcuExpected) {
+ // reset interval stuff
+ for (int i = 0; i < componentsLength; i++) {
+ components[i].pred = 0;
+ }
+ eobrun = 0;
+
+ if (componentsLength == 1) {
+ component = components[0];
+ for (int n = 0; n < resetInterval; n++) {
+ _decodeBlock(component, decodeFn, mcu);
+ mcu++;
+ }
+ } else {
+ for (int n = 0; n < resetInterval; n++) {
+ for (int i = 0; i < componentsLength; i++) {
+ component = components[i];
+ h = component.hSamples;
+ v = component.vSamples;
+ for (int j = 0; j < v; j++) {
+ for (int k = 0; k < h; k++) {
+ _decodeMcu(component, decodeFn, mcu, j, k);
+ }
+ }
+ }
+ mcu++;
+ }
+ }
+
+ // find marker
+ bitsCount = 0;
+ int m1 = input[0];
+ int m2 = input[1];
+ if (m1 == 0xff) {
+ if (m2 >= Jpeg.M_RST0 && m2 <= Jpeg.M_RST7) {
+ input.offset += 2;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ int _readBit() {
+ if (bitsCount > 0) {
+ bitsCount--;
+ return (bitsData >> bitsCount) & 1;
+ }
+
+ if (input.isEOS) {
+ return null;
+ }
+
+ bitsData = input.readByte();
+ if (bitsData == 0xff) {
+ int nextByte = input.readByte();
+ if (nextByte != 0) {
+ throw new ImageException('unexpected marker: ' +
+ ((bitsData << 8) | nextByte).toRadixString(16));
+ }
+ }
+
+ bitsCount = 7;
+ return (bitsData >> 7) & 1;
+ }
+
+ int _decodeHuffman(tree) {
+ var node = tree;
+ int bit;
+ while ((bit = _readBit()) != null) {
+ node = node[bit];
+ if (node is num) {
+ return node.toInt();
+ }
+ }
+
+ return null;
+ }
+
+ int _receive(int length) {
+ int n = 0;
+ while (length > 0) {
+ int bit = _readBit();
+ if (bit == null) {
+ return null;
+ }
+ n = ((n << 1) | bit);
+ length--;
+ }
+ return n;
+ }
+
+ int _receiveAndExtend(int length) {
+ if (length == 1) {
+ return _readBit() == 1 ? 1 : -1;
+ }
+ int n = _receive(length);
+ if (n >= (1 << (length - 1))) {
+ return n;
+ }
+ return n + (-1 << length) + 1;
+ }
+
+ void _decodeBaseline(JpegComponent component, List zz) {
+ int t = _decodeHuffman(component.huffmanTableDC);
+ int diff = t == 0 ? 0 : _receiveAndExtend(t);
+ component.pred += diff;
+ zz[0] = component.pred;
+
+ int k = 1;
+ while (k < 64) {
+ var rs = _decodeHuffman(component.huffmanTableAC);
+ int s = rs & 15;
+ int r = rs >> 4;
+ if (s == 0) {
+ if (r < 15) {
+ break;
+ }
+ k += 16;
+ continue;
+ }
+
+ k += r;
+
+ s = _receiveAndExtend(s);
+
+ int z = Jpeg.dctZigZag[k];
+ zz[z] = s;
+ k++;
+ }
+ }
+
+ void _decodeDCFirst(JpegComponent component, List zz) {
+ int t = _decodeHuffman(component.huffmanTableDC);
+ int diff = (t == 0) ? 0 : (_receiveAndExtend(t) << successive);
+ component.pred += diff;
+ zz[0] = component.pred;
+ }
+
+ void _decodeDCSuccessive(JpegComponent component, List zz) {
+ zz[0] = (zz[0] | (_readBit() << successive));
+ }
+
+ void _decodeACFirst(JpegComponent component, List zz) {
+ if (eobrun > 0) {
+ eobrun--;
+ return;
+ }
+ int k = spectralStart;
+ int e = spectralEnd;
+ while (k <= e) {
+ int rs = _decodeHuffman(component.huffmanTableAC);
+ int s = rs & 15;
+ int r = rs >> 4;
+ if (s == 0) {
+ if (r < 15) {
+ eobrun = (_receive(r) + (1 << r) - 1);
+ break;
+ }
+ k += 16;
+ continue;
+ }
+ k += r;
+ int z = Jpeg.dctZigZag[k];
+ zz[z] = (_receiveAndExtend(s) * (1 << successive));
+ k++;
+ }
+ }
+
+ void _decodeACSuccessive(JpegComponent component, zz) {
+ int k = spectralStart;
+ int e = spectralEnd;
+ int s = 0;
+ int r = 0;
+ while (k <= e) {
+ int z = Jpeg.dctZigZag[k];
+ switch (successiveACState) {
+ case 0: // initial state
+ int rs = _decodeHuffman(component.huffmanTableAC);
+ if (rs == null) continue;
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (r < 15) {
+ eobrun = (_receive(r) + (1 << r));
+ successiveACState = 4;
+ } else {
+ r = 16;
+ successiveACState = 1;
+ }
+ } else {
+ if (s != 1) {
+ throw new ImageException('invalid ACn encoding');
+ }
+ successiveACNextValue = _receiveAndExtend(s);
+ successiveACState = r != 0 ? 2 : 3;
+ }
+ continue;
+ case 1: // skipping r zero items
+ case 2:
+ if (zz[z] != 0) {
+ zz[z] += (_readBit() << successive);
+ } else {
+ r--;
+ if (r == 0) {
+ successiveACState = successiveACState == 2 ? 3 : 0;
+ }
+ }
+ break;
+ case 3: // set value for a zero item
+ if (zz[z] != 0) {
+ zz[z] += (_readBit() << successive);
+ } else {
+ zz[z] = (successiveACNextValue << successive);
+ successiveACState = 0;
+ }
+ break;
+ case 4: // eob
+ if (zz[z] != 0) {
+ zz[z] += (_readBit() << successive);
+ }
+ break;
+ }
+ k++;
+ }
+ if (successiveACState == 4) {
+ eobrun--;
+ if (eobrun == 0) {
+ successiveACState = 0;
+ }
+ }
+ }
+
+ void _decodeMcu(JpegComponent component, decodeFn,
+ int mcu, int row, int col) {
+ int mcuRow = (mcu ~/ mcusPerLine);
+ int mcuCol = mcu % mcusPerLine;
+ int blockRow = mcuRow * component.vSamples + row;
+ int blockCol = mcuCol * component.hSamples + col;
+ if (blockRow >= component.blocks.length ||
+ blockCol >= component.blocks[blockRow].length) {
+ return;
+ }
+ decodeFn(component, component.blocks[blockRow][blockCol]);
+ }
+
+ void _decodeBlock(JpegComponent component, decodeFn, int mcu) {
+ int blockRow = mcu ~/ component.blocksPerLine;
+ int blockCol = mcu % component.blocksPerLine;
+ decodeFn(component, component.blocks[blockRow][blockCol]);
+ }
+}
diff --git a/image/lib/src/formats/jpeg_decoder.dart b/image/lib/src/formats/jpeg_decoder.dart
old mode 100644
new mode 100755
index aae1637..284a4b4
--- a/image/lib/src/formats/jpeg_decoder.dart
+++ b/image/lib/src/formats/jpeg_decoder.dart
@@ -1,134 +1,134 @@
-import 'dart:typed_data';
-
-import '../animation.dart';
-import '../color.dart';
-import '../exif_data.dart';
-import '../image.dart';
-import '../image_exception.dart';
-import '../util/input_buffer.dart';
-import 'decode_info.dart';
-import 'decoder.dart';
-import 'jpeg/jpeg_data.dart';
-import 'jpeg/jpeg_info.dart';
-
-/**
- * Decode a jpeg encoded image.
- */
-class JpegDecoder extends Decoder {
- JpegInfo info;
- InputBuffer input;
-
- /**
- * Is the given file a valid JPEG image?
- */
- bool isValidFile(List<int> data) {
- return new JpegData().validate(data);
- }
-
- DecodeInfo startDecode(List<int> data) {
- input = new InputBuffer(data, bigEndian: true);
- info = new JpegData().readInfo(data);
- return info;
- }
-
- int numFrames() => info == null ? 0 : info.numFrames;
-
- Image decodeFrame(int frame) {
- if (input == null) {
- return null;
- }
- JpegData jpeg = new JpegData();
- jpeg.read(input.buffer);
-
- if (jpeg.frames.length != 1) {
- throw new ImageException('only single frame JPEGs supported');
- }
-
- Image image = new Image(jpeg.width, jpeg.height, Image.RGB);
-
- _copyToImage(jpeg, image);
-
- return image;
- }
-
- Image decodeImage(List<int> data, {int frame: 0}) {
- JpegData jpeg = new JpegData();
- jpeg.read(data);
-
- if (jpeg.frames.length != 1) {
- throw new ImageException('only single frame JPEGs supported');
- }
-
- Image image = new Image(jpeg.width, jpeg.height, Image.RGB);
-
- _copyToImage(jpeg, image);
-
- return image;
- }
-
- Animation decodeAnimation(List<int> data) {
- Image image = decodeImage(data);
- if (image == null) {
- return null;
- }
-
- Animation anim = new Animation();
- anim.width = image.width;
- anim.height = image.height;
- anim.addFrame(image);
-
- return anim;
- }
-
- void _copyToImage(JpegData jpeg, Image imageData) {
- imageData.exif = new ExifData.from(jpeg.exif);
-
- final width = imageData.width;
- final height = imageData.height;
- final data = jpeg.getData(width, height);
- final components = jpeg.components;
-
- int i = 0;
- int j = 0;
- switch (components.length) {
- case 1: // Luminance
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int Y = data[i++];
- imageData[j++] = getColor(Y, Y, Y, 255);
- }
- }
- break;
- case 3: // RGB
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int R = data[i++];
- int G = data[i++];
- int B = data[i++];
-
- int c = getColor(R, G, B, 255);
- imageData[j++] = c;
- }
- }
- break;
- case 4: // CMYK
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int C = data[i++];
- int M = data[i++];
- int Y = data[i++];
- int K = data[i++];
-
- int R = (C * (K)) >> 8;
- int G = (M * (K)) >> 8;
- int B = (Y * (K)) >> 8;
-
- imageData[j++] = getColor(R, G, B, 255);
- }
- }
- break;
- default:
- throw 'Unsupported color mode';
- }
- }
-}
+import 'dart:typed_data';
+
+import '../animation.dart';
+import '../color.dart';
+import '../exif_data.dart';
+import '../image.dart';
+import '../image_exception.dart';
+import '../util/input_buffer.dart';
+import 'decode_info.dart';
+import 'decoder.dart';
+import 'jpeg/jpeg_data.dart';
+import 'jpeg/jpeg_info.dart';
+
+/**
+ * Decode a jpeg encoded image.
+ */
+class JpegDecoder extends Decoder {
+ JpegInfo info;
+ InputBuffer input;
+
+ /**
+ * Is the given file a valid JPEG image?
+ */
+ bool isValidFile(List<int> data) {
+ return new JpegData().validate(data);
+ }
+
+ DecodeInfo startDecode(List<int> data) {
+ input = InputBuffer(data, bigEndian: true);
+ info = JpegData().readInfo(data);
+ return info;
+ }
+
+ int numFrames() => info == null ? 0 : info.numFrames;
+
+ Image decodeFrame(int frame) {
+ if (input == null) {
+ return null;
+ }
+ JpegData jpeg = JpegData();
+ jpeg.read(input.buffer);
+
+ if (jpeg.frames.length != 1) {
+ throw new ImageException('only single frame JPEGs supported');
+ }
+
+ Image image = Image(jpeg.width, jpeg.height, Image.RGB);
+
+ _copyToImage(jpeg, image);
+
+ return image;
+ }
+
+ Image decodeImage(List<int> data, {int frame: 0}) {
+ JpegData jpeg = JpegData();
+ jpeg.read(data);
+
+ if (jpeg.frames.length != 1) {
+ throw new ImageException('only single frame JPEGs supported');
+ }
+
+ Image image = Image(jpeg.width, jpeg.height, Image.RGB);
+
+ _copyToImage(jpeg, image);
+
+ return image;
+ }
+
+ Animation decodeAnimation(List<int> data) {
+ Image image = decodeImage(data);
+ if (image == null) {
+ return null;
+ }
+
+ Animation anim = Animation();
+ anim.width = image.width;
+ anim.height = image.height;
+ anim.addFrame(image);
+
+ return anim;
+ }
+
+ void _copyToImage(JpegData jpeg, Image imageData) {
+ imageData.exif = ExifData.from(jpeg.exif);
+
+ final width = imageData.width;
+ final height = imageData.height;
+ final data = jpeg.getData(width, height);
+ final components = jpeg.components;
+
+ int i = 0;
+ int j = 0;
+ switch (components.length) {
+ case 1: // Luminance
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int Y = data[i++];
+ imageData[j++] = getColor(Y, Y, Y, 255);
+ }
+ }
+ break;
+ case 3: // RGB
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int R = data[i++];
+ int G = data[i++];
+ int B = data[i++];
+
+ int c = getColor(R, G, B, 255);
+ imageData[j++] = c;
+ }
+ }
+ break;
+ case 4: // CMYK
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int C = data[i++];
+ int M = data[i++];
+ int Y = data[i++];
+ int K = data[i++];
+
+ int R = (C * (K)) >> 8;
+ int G = (M * (K)) >> 8;
+ int B = (Y * (K)) >> 8;
+
+ imageData[j++] = getColor(R, G, B, 255);
+ }
+ }
+ break;
+ default:
+ throw 'Unsupported color mode';
+ }
+ }
+}
diff --git a/image/lib/src/formats/jpeg_encoder.dart b/image/lib/src/formats/jpeg_encoder.dart
old mode 100644
new mode 100755
index 8e738d7..943867d
--- a/image/lib/src/formats/jpeg_encoder.dart
+++ b/image/lib/src/formats/jpeg_encoder.dart
@@ -39,7 +39,7 @@
}
List<int> encodeImage(Image image) {
- OutputBuffer fp = new OutputBuffer(bigEndian: true);
+ OutputBuffer fp = OutputBuffer(bigEndian: true);
// Add JPEG headers
_writeMarker(fp, Jpeg.M_SOI);
@@ -83,9 +83,9 @@
p -= ((x + col) - quadWidth + 4);
}
- int r = imageData[p++];
- int g = imageData[p++];
int b = imageData[p++];
+ int g = imageData[p++];
+ int r = imageData[p++];
// calculate YUV values
YDU[pos] = ((RGB_YUV_TABLE[r] +
@@ -187,7 +187,7 @@
List _computeHuffmanTbl(List nrcodes, List std_table) {
int codevalue = 0;
int pos_in_table = 0;
- List HT = new List();
+ List HT = List();
for (int k = 1; k <= 16; k++) {
for (int j = 1; j <= nrcodes[k]; j++) {
int index = std_table[pos_in_table];
@@ -575,24 +575,24 @@
_bytepos = 7;
}
- final YTable = new Uint8List(64);
- final UVTable = new Uint8List(64);
- final fdtbl_Y = new Float32List(64);
- final fdtbl_UV = new Float32List(64);
+ final YTable = Uint8List(64);
+ final UVTable = Uint8List(64);
+ final fdtbl_Y = Float32List(64);
+ final fdtbl_UV = Float32List(64);
List YDC_HT;
List UVDC_HT;
List YAC_HT;
List UVAC_HT;
- final bitcode = new List(65535);
- final category = new List(65535);
- final outputfDCTQuant = new List<int>(64);
- final DU = new List(64);
+ final bitcode = List(65535);
+ final category = List(65535);
+ final outputfDCTQuant = List<int>(64);
+ final DU = List(64);
- final Float32List YDU = new Float32List(64);
- final Float32List UDU = new Float32List(64);
- final Float32List VDU = new Float32List(64);
- final Int32List RGB_YUV_TABLE = new Int32List(2048);
+ final Float32List YDU = Float32List(64);
+ final Float32List UDU = Float32List(64);
+ final Float32List VDU = Float32List(64);
+ final Int32List RGB_YUV_TABLE = Int32List(2048);
int currentQuality;
static const List<int> ZIGZAG = const [
diff --git a/image/lib/src/formats/png/png_frame.dart b/image/lib/src/formats/png/png_frame.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/png/png_info.dart b/image/lib/src/formats/png/png_info.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/png_decoder.dart b/image/lib/src/formats/png_decoder.dart
old mode 100644
new mode 100755
index ed67081..e52f06f
--- a/image/lib/src/formats/png_decoder.dart
+++ b/image/lib/src/formats/png_decoder.dart
@@ -24,7 +24,7 @@
* Is the given file a valid PNG image?
*/
bool isValidFile(List<int> data) {
- InputBuffer input = new InputBuffer(data, bigEndian: true);
+ InputBuffer input = InputBuffer(data, bigEndian: true);
InputBuffer pngHeader = input.readBytes(8);
const PNG_HEADER = const [137, 80, 78, 71, 13, 10, 26, 10];
for (int i = 0; i < 8; ++i) {
@@ -43,7 +43,7 @@
* process the frames until they are requested with decodeFrame.
*/
DecodeInfo startDecode(List<int> data) {
- _input = new InputBuffer(data, bigEndian: true);
+ _input = InputBuffer(data, bigEndian: true);
InputBuffer pngHeader = _input.readBytes(8);
const PNG_HEADER = const [137, 80, 78, 71, 13, 10, 26, 10];
@@ -59,9 +59,9 @@
String chunkType = _input.readString(4);
switch (chunkType) {
case 'IHDR':
- InputBuffer hdr = new InputBuffer.from(_input.readBytes(chunkSize));
+ InputBuffer hdr = InputBuffer.from(_input.readBytes(chunkSize));
List<int> hdrBytes = hdr.toUint8List();
- _info = new InternalPngInfo();
+ _info = InternalPngInfo();
_info.width = hdr.readUint32();
_info.height = hdr.readUint32();
_info.bits = hdr.readByte();
@@ -158,7 +158,7 @@
_input.skip(4); // CRC
break;
case 'fcTL': // Frame control chunk
- PngFrame frame = new InternalPngFrame();
+ PngFrame frame = InternalPngFrame();
_info.frames.add(frame);
frame.sequenceNumber = _input.readUint32();
frame.width = _input.readUint32();
@@ -287,17 +287,17 @@
format = Image.RGB;
}
- Image image = new Image(width, height, format);
+ Image image = Image(width, height, format);
- List<int> uncompressed = new ZLibDecoder().decodeBytes(imageData);
+ List<int> uncompressed = ZLibDecoder().decodeBytes(imageData);
// input is the decompressed data.
- InputBuffer input = new InputBuffer(uncompressed, bigEndian: true);
+ InputBuffer input = InputBuffer(uncompressed, bigEndian: true);
_resetBits();
// Set up a LUT to transform colors for gamma correction.
if (_info.colorLut == null) {
- _info.colorLut = new List<int>(256);
+ _info.colorLut = List<int>(256);
for (int i = 0; i < 256; ++i) {
int c = i;
/*if (info.gamma != null) {
@@ -337,7 +337,7 @@
_info.width = origW;
_info.height = origH;
- image.iccProfile = new ICCProfileData(_info.iCCPName,
+ image.iccProfile = ICCProfileData(_info.iCCPName,
ICCPCompression.deflate,
_info.iCCPData);
@@ -356,7 +356,7 @@
return null;
}
- Animation anim = new Animation();
+ Animation anim = Animation();
anim.width = _info.width;
anim.height = _info.height;
@@ -367,13 +367,13 @@
}
int dispose = PngFrame.APNG_DISPOSE_OP_BACKGROUND;
- Image lastImage = new Image(_info.width, _info.height);
+ Image lastImage = Image(_info.width, _info.height);
for (int i = 0; i < _info.numFrames; ++i) {
//_frame = i;
if (lastImage == null) {
- lastImage = new Image(_info.width, _info.height);
+ lastImage = Image(_info.width, _info.height);
} else {
- lastImage = new Image.from(lastImage);
+ lastImage = Image.from(lastImage);
}
PngFrame frame = _info.frames[i];
@@ -415,7 +415,7 @@
final int bpp = (pixelDepth + 7) >> 3;
final int rowBytes = (pixelDepth * passWidth + 7) >> 3;
- final List<int> line = new List<int>.filled(rowBytes, 0);
+ final List<int> line = List<int>.filled(rowBytes, 0);
final List<List<int>> inData = [line, line];
final List<int> pixel = [0, 0, 0, 0];
@@ -437,7 +437,7 @@
// reset the bit stream counter.
_resetBits();
- InputBuffer rowInput = new InputBuffer(row, bigEndian: true);
+ InputBuffer rowInput = InputBuffer(row, bigEndian: true);
final int blockHeight = xStep;
final int blockWidth = xStep - xOffset;
@@ -476,7 +476,7 @@
final int rowBytes = (((w * pixelDepth + 7)) >> 3);
final int bpp = (pixelDepth + 7) >> 3;
- final List<int> line = new List<int>.filled(rowBytes, 0);
+ final List<int> line = List<int>.filled(rowBytes, 0);
final List<List<int>> inData = [line, line];
final List<int> pixel = [0, 0, 0, 0];
@@ -496,7 +496,7 @@
// reset the bit stream counter.
_resetBits();
- InputBuffer rowInput = new InputBuffer(inData[ri], bigEndian: true);
+ InputBuffer rowInput = InputBuffer(inData[ri], bigEndian: true);
for (int x = 0; x < w; ++x) {
_readPixel(rowInput, pixel);
diff --git a/image/lib/src/formats/png_encoder.dart b/image/lib/src/formats/png_encoder.dart
old mode 100644
new mode 100755
index 7c7c6b6..26df9da
--- a/image/lib/src/formats/png_encoder.dart
+++ b/image/lib/src/formats/png_encoder.dart
@@ -24,7 +24,7 @@
blendMethod = image.blendMethod;
if (output == null) {
- output = new OutputBuffer(bigEndian: true);
+ output = OutputBuffer(bigEndian: true);
format = image.format;
_width = image.width;
@@ -40,12 +40,12 @@
}
// Include room for the filter bytes (1 byte per row).
- List<int> filteredImage = new Uint8List((image.width * image.height *
+ List<int> filteredImage = Uint8List((image.width * image.height *
image.format) + image.height);
_filter(image, filteredImage);
- List<int> compressed = new ZLibEncoder().encode(filteredImage,
+ List<int> compressed = ZLibEncoder().encode(filteredImage,
level: level);
if (isAnimated) {
@@ -57,7 +57,7 @@
_writeChunk(output, 'IDAT', compressed);
} else {
// fdAT chunk
- OutputBuffer fdat = new OutputBuffer(bigEndian: true);
+ OutputBuffer fdat = OutputBuffer(bigEndian: true);
fdat.writeUint32(sequenceNumber);
fdat.writeBytes(compressed);
_writeChunk(output, 'fdAT', fdat.getBytes());
@@ -115,7 +115,7 @@
output.writeBytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
// IHDR chunk
- OutputBuffer chunk = new OutputBuffer(bigEndian: true);
+ OutputBuffer chunk = OutputBuffer(bigEndian: true);
chunk.writeUint32(width);
chunk.writeUint32(height);
chunk.writeByte(8);
@@ -127,14 +127,14 @@
}
void _writeAnimationControlChunk() {
- OutputBuffer chunk = new OutputBuffer(bigEndian: true);
+ OutputBuffer chunk = OutputBuffer(bigEndian: true);
chunk.writeUint32(_frames); // number of frames
chunk.writeUint32(repeat); // loop count
_writeChunk(output, 'acTL', chunk.getBytes());
}
void _writeFrameControlChunk() {
- OutputBuffer chunk = new OutputBuffer(bigEndian: true);
+ OutputBuffer chunk = OutputBuffer(bigEndian: true);
chunk.writeUint32(sequenceNumber);
chunk.writeUint32(_width);
chunk.writeUint32(_height);
@@ -152,7 +152,7 @@
return;
}
- OutputBuffer chunk = new OutputBuffer(bigEndian: true);
+ OutputBuffer chunk = OutputBuffer(bigEndian: true);
// name
chunk.writeBytes(iccp.name.codeUnits);
@@ -389,5 +389,5 @@
static const int FILTER_AGRESSIVE = 5;
// Table of CRCs of all 8-bit messages.
- //final List<int> _crcTable = new List<int>(256);
+ //final List<int> _crcTable = List<int>(256);
}
diff --git a/image/lib/src/formats/psd/effect/psd_bevel_effect.dart b/image/lib/src/formats/psd/effect/psd_bevel_effect.dart
old mode 100644
new mode 100755
index 0d09e74..91ce269
--- a/image/lib/src/formats/psd/effect/psd_bevel_effect.dart
+++ b/image/lib/src/formats/psd/effect/psd_bevel_effect.dart
@@ -1,18 +1,18 @@
-import 'psd_effect.dart';
-
-class PsdBevelEffect extends PsdEffect {
- int angle;
- int strength;
- int blur;
- String highlightBlendMode;
- String shadowBlendMode;
- List<int> highlightColor;
- List<int> shadowColor;
- int bevelStyle;
- int highlightOpacity;
- int shadowOpacity;
- bool globalAngle;
- int upOrDown;
- List<int> realHighlightColor;
- List<int> realShadowColor;
-}
+import 'psd_effect.dart';
+
+class PsdBevelEffect extends PsdEffect {
+ int angle;
+ int strength;
+ int blur;
+ String highlightBlendMode;
+ String shadowBlendMode;
+ List<int> highlightColor;
+ List<int> shadowColor;
+ int bevelStyle;
+ int highlightOpacity;
+ int shadowOpacity;
+ bool globalAngle;
+ int upOrDown;
+ List<int> realHighlightColor;
+ List<int> realShadowColor;
+}
diff --git a/image/lib/src/formats/psd/effect/psd_drop_shadow_effect.dart b/image/lib/src/formats/psd/effect/psd_drop_shadow_effect.dart
old mode 100644
new mode 100755
index 5fcf275..112d8d2
--- a/image/lib/src/formats/psd/effect/psd_drop_shadow_effect.dart
+++ b/image/lib/src/formats/psd/effect/psd_drop_shadow_effect.dart
@@ -1,13 +1,13 @@
-import 'psd_effect.dart';
-
-class PsdDropShadowEffect extends PsdEffect {
- int blur;
- int intensity;
- int angle;
- int distance;
- List<int> color;
- String blendMode;
- bool globalAngle;
- int opacity;
- List<int> nativeColor;
-}
+import 'psd_effect.dart';
+
+class PsdDropShadowEffect extends PsdEffect {
+ int blur;
+ int intensity;
+ int angle;
+ int distance;
+ List<int> color;
+ String blendMode;
+ bool globalAngle;
+ int opacity;
+ List<int> nativeColor;
+}
diff --git a/image/lib/src/formats/psd/effect/psd_effect.dart b/image/lib/src/formats/psd/effect/psd_effect.dart
old mode 100644
new mode 100755
index 508a569..6b65fec
--- a/image/lib/src/formats/psd/effect/psd_effect.dart
+++ b/image/lib/src/formats/psd/effect/psd_effect.dart
@@ -1,4 +1,4 @@
-class PsdEffect {
- int version;
- bool enabled;
-}
+class PsdEffect {
+ int version;
+ bool enabled;
+}
diff --git a/image/lib/src/formats/psd/effect/psd_inner_glow_effect.dart b/image/lib/src/formats/psd/effect/psd_inner_glow_effect.dart
old mode 100644
new mode 100755
index 2658164..1dfc21e
--- a/image/lib/src/formats/psd/effect/psd_inner_glow_effect.dart
+++ b/image/lib/src/formats/psd/effect/psd_inner_glow_effect.dart
@@ -1,11 +1,11 @@
-import 'psd_effect.dart';
-
-class PsdInnerGlowEffect extends PsdEffect {
- int blur;
- int intensity;
- List<int> color;
- String blendMode;
- int opacity;
- bool invert;
- List<int> nativeColor;
-}
+import 'psd_effect.dart';
+
+class PsdInnerGlowEffect extends PsdEffect {
+ int blur;
+ int intensity;
+ List<int> color;
+ String blendMode;
+ int opacity;
+ bool invert;
+ List<int> nativeColor;
+}
diff --git a/image/lib/src/formats/psd/effect/psd_inner_shadow_effect.dart b/image/lib/src/formats/psd/effect/psd_inner_shadow_effect.dart
old mode 100644
new mode 100755
index f8b7c67..6ac10aa
--- a/image/lib/src/formats/psd/effect/psd_inner_shadow_effect.dart
+++ b/image/lib/src/formats/psd/effect/psd_inner_shadow_effect.dart
@@ -1,13 +1,13 @@
-import 'psd_effect.dart';
-
-class PsdInnerShadowEffect extends PsdEffect {
- int blur;
- int intensity;
- int angle;
- int distance;
- List<int> color;
- String blendMode;
- bool globalAngle;
- int opacity;
- List<int> nativeColor;
-}
+import 'psd_effect.dart';
+
+class PsdInnerShadowEffect extends PsdEffect {
+ int blur;
+ int intensity;
+ int angle;
+ int distance;
+ List<int> color;
+ String blendMode;
+ bool globalAngle;
+ int opacity;
+ List<int> nativeColor;
+}
diff --git a/image/lib/src/formats/psd/effect/psd_outer_glow_effect.dart b/image/lib/src/formats/psd/effect/psd_outer_glow_effect.dart
old mode 100644
new mode 100755
index 551e3b1..db86a3c
--- a/image/lib/src/formats/psd/effect/psd_outer_glow_effect.dart
+++ b/image/lib/src/formats/psd/effect/psd_outer_glow_effect.dart
@@ -1,10 +1,10 @@
-import 'psd_effect.dart';
-
-class PsdOuterGlowEffect extends PsdEffect {
- int blur;
- int intensity;
- List<int> color;
- String blendMode;
- int opacity;
- List<int> nativeColor;
-}
+import 'psd_effect.dart';
+
+class PsdOuterGlowEffect extends PsdEffect {
+ int blur;
+ int intensity;
+ List<int> color;
+ String blendMode;
+ int opacity;
+ List<int> nativeColor;
+}
diff --git a/image/lib/src/formats/psd/effect/psd_solid_fill_effect.dart b/image/lib/src/formats/psd/effect/psd_solid_fill_effect.dart
old mode 100644
new mode 100755
index bec7fe2..5b8ce6e
--- a/image/lib/src/formats/psd/effect/psd_solid_fill_effect.dart
+++ b/image/lib/src/formats/psd/effect/psd_solid_fill_effect.dart
@@ -1,8 +1,8 @@
-import 'psd_effect.dart';
-
-class PsdSolidFillEffect extends PsdEffect {
- String blendMode;
- List<int> color;
- int opacity;
- List<int> nativeColor;
-}
+import 'psd_effect.dart';
+
+class PsdSolidFillEffect extends PsdEffect {
+ String blendMode;
+ List<int> color;
+ int opacity;
+ List<int> nativeColor;
+}
diff --git a/image/lib/src/formats/psd/layer_data/psd_layer_additional_data.dart b/image/lib/src/formats/psd/layer_data/psd_layer_additional_data.dart
old mode 100644
new mode 100755
index cd33751..21f9732
--- a/image/lib/src/formats/psd/layer_data/psd_layer_additional_data.dart
+++ b/image/lib/src/formats/psd/layer_data/psd_layer_additional_data.dart
@@ -1,10 +1,10 @@
-import '../../../util/input_buffer.dart';
-import '../psd_layer_data.dart';
-
-class PsdLayerAdditionalData extends PsdLayerData {
- InputBuffer data;
-
- PsdLayerAdditionalData(String tag, InputBuffer data) :
- this.data = data,
- super.type(tag);
-}
+import '../../../util/input_buffer.dart';
+import '../psd_layer_data.dart';
+
+class PsdLayerAdditionalData extends PsdLayerData {
+ InputBuffer data;
+
+ PsdLayerAdditionalData(String tag, InputBuffer data) :
+ this.data = data,
+ super.type(tag);
+}
diff --git a/image/lib/src/formats/psd/layer_data/psd_layer_section_divider.dart b/image/lib/src/formats/psd/layer_data/psd_layer_section_divider.dart
old mode 100644
new mode 100755
index 40f50fe..71ae146
--- a/image/lib/src/formats/psd/layer_data/psd_layer_section_divider.dart
+++ b/image/lib/src/formats/psd/layer_data/psd_layer_section_divider.dart
@@ -1,38 +1,38 @@
-import '../../../image_exception.dart';
-import '../../../util/input_buffer.dart';
-import '../psd_layer_data.dart';
-
-class PsdLayerSectionDivider extends PsdLayerData {
- static const String TAG = 'lsct';
-
- static const int NORMAL = 0;
- static const int OPEN_FOLDER = 1;
- static const int CLOSED_FOLDER = 2;
- static const int SECTION_DIVIDER = 3;
-
- static const int SUBTYPE_NORMAL = 0;
- static const int SUBTYPE_SCENE_GROUP = 1;
-
- int type;
- String key;
- int subType = SUBTYPE_NORMAL;
-
- PsdLayerSectionDivider(String tag, InputBuffer data) :
- super.type(tag) {
- int len = data.length;
-
- type = data.readUint32();
-
- if (len >= 12) {
- String sig = data.readString(4);
- if (sig != '8BIM') {
- throw new ImageException('Invalid key in layer additional data');
- }
- key = data.readString(4);
- }
-
- if (len >= 16) {
- subType = data.readUint32();
- }
- }
-}
+import '../../../image_exception.dart';
+import '../../../util/input_buffer.dart';
+import '../psd_layer_data.dart';
+
+class PsdLayerSectionDivider extends PsdLayerData {
+ static const String TAG = 'lsct';
+
+ static const int NORMAL = 0;
+ static const int OPEN_FOLDER = 1;
+ static const int CLOSED_FOLDER = 2;
+ static const int SECTION_DIVIDER = 3;
+
+ static const int SUBTYPE_NORMAL = 0;
+ static const int SUBTYPE_SCENE_GROUP = 1;
+
+ int type;
+ String key;
+ int subType = SUBTYPE_NORMAL;
+
+ PsdLayerSectionDivider(String tag, InputBuffer data) :
+ super.type(tag) {
+ int len = data.length;
+
+ type = data.readUint32();
+
+ if (len >= 12) {
+ String sig = data.readString(4);
+ if (sig != '8BIM') {
+ throw new ImageException('Invalid key in layer additional data');
+ }
+ key = data.readString(4);
+ }
+
+ if (len >= 16) {
+ subType = data.readUint32();
+ }
+ }
+}
diff --git a/image/lib/src/formats/psd/psd_blending_ranges.dart b/image/lib/src/formats/psd/psd_blending_ranges.dart
old mode 100644
new mode 100755
index 320bd29..bb5e850
--- a/image/lib/src/formats/psd/psd_blending_ranges.dart
+++ b/image/lib/src/formats/psd/psd_blending_ranges.dart
@@ -1,39 +1,39 @@
-import 'dart:typed_data';
-
-import '../../util/input_buffer.dart';
-
-class PsdBlendingRanges {
- int grayBlackSrc;
- int grayWhiteSrc;
- int grayBlackDst;
- int grayWhiteDst;
- Uint16List blackSrc;
- Uint16List whiteSrc;
- Uint16List blackDst;
- Uint16List whiteDst;
-
- PsdBlendingRanges(InputBuffer input) {
- grayBlackSrc = input.readUint16();
- grayWhiteSrc = input.readUint16();
-
- grayBlackDst = input.readUint16();
- grayWhiteDst = input.readUint16();
-
- int len = input.length;
- int numChannels = len ~/ 8;
-
- if (numChannels > 0) {
- blackSrc = new Uint16List(numChannels);
- whiteSrc = new Uint16List(numChannels);
- blackDst = new Uint16List(numChannels);
- whiteDst = new Uint16List(numChannels);
-
- for (int i = 0; i < numChannels; ++i) {
- blackSrc[i] = input.readUint16();
- whiteSrc[i] = input.readUint16();
- blackDst[i] = input.readUint16();
- whiteDst[i] = input.readUint16();
- }
- }
- }
+import 'dart:typed_data';
+
+import '../../util/input_buffer.dart';
+
+class PsdBlendingRanges {
+ int grayBlackSrc;
+ int grayWhiteSrc;
+ int grayBlackDst;
+ int grayWhiteDst;
+ Uint16List blackSrc;
+ Uint16List whiteSrc;
+ Uint16List blackDst;
+ Uint16List whiteDst;
+
+ PsdBlendingRanges(InputBuffer input) {
+ grayBlackSrc = input.readUint16();
+ grayWhiteSrc = input.readUint16();
+
+ grayBlackDst = input.readUint16();
+ grayWhiteDst = input.readUint16();
+
+ int len = input.length;
+ int numChannels = len ~/ 8;
+
+ if (numChannels > 0) {
+ blackSrc = Uint16List(numChannels);
+ whiteSrc = Uint16List(numChannels);
+ blackDst = Uint16List(numChannels);
+ whiteDst = Uint16List(numChannels);
+
+ for (int i = 0; i < numChannels; ++i) {
+ blackSrc[i] = input.readUint16();
+ whiteSrc[i] = input.readUint16();
+ blackDst[i] = input.readUint16();
+ whiteDst[i] = input.readUint16();
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/image/lib/src/formats/psd/psd_channel.dart b/image/lib/src/formats/psd/psd_channel.dart
old mode 100644
new mode 100755
index 2568bd0..7a95ce6
--- a/image/lib/src/formats/psd/psd_channel.dart
+++ b/image/lib/src/formats/psd/psd_channel.dart
@@ -1,119 +1,119 @@
-import 'dart:typed_data';
-
-import '../../image_exception.dart';
-import '../../util/input_buffer.dart';
-
-class PsdChannel {
- static const int RED = 0;
- static const int GREEN = 1;
- static const int BLUE = 2;
- static const int BLACK = 3;
- static const int ALPHA = -1;
- static const int MASK = -2;
- static const int REAL_MASK = -3;
-
- static const int COMPRESS_NONE = 0;
- static const int COMPRESS_RLE = 1;
- static const int COMPRESS_ZIP = 2;
- static const int COMPRESS_ZIP_PREDICTOR = 3;
-
- int id;
- int dataLength;
- Uint8List data;
-
- PsdChannel(this.id, this.dataLength);
-
- PsdChannel.read(InputBuffer input, this.id, int width, int height,
- int bitDepth, int compression, Uint16List lineLengths,
- int planeNumber) {
- readPlane(input, width, height, bitDepth, compression, lineLengths,
- planeNumber);
- }
-
- void readPlane(InputBuffer input, int width, int height, int bitDepth,
- [int compression, Uint16List lineLengths, int planeNum = 0]) {
- if (compression == null) {
- compression = input.readUint16();
- }
-
- switch (compression) {
- case COMPRESS_NONE:
- _readPlaneUncompressed(input, width, height, bitDepth);
- break;
- case COMPRESS_RLE:
- if (lineLengths == null) {
- lineLengths = _readLineLengths(input, height);
- }
- _readPlaneRleCompressed(input, width, height, bitDepth, lineLengths,
- planeNum);
- break;
- default:
- throw new ImageException('Unsupported compression: $compression');
- }
- }
-
- Uint16List _readLineLengths(InputBuffer input, int height) {
- Uint16List lineLengths = new Uint16List(height);
- for (int i = 0; i < height; ++i) {
- lineLengths[i] = input.readUint16();
- }
- return lineLengths;
- }
-
- void _readPlaneUncompressed(InputBuffer input, int width, int height,
- int bitDepth) {
- int len = width * height;
- if (bitDepth == 16) {
- len *= 2;
- }
- if (len > input.length) {
- data = new Uint8List(len);
- data.fillRange(0, len, 255);
- return;
- }
-
- InputBuffer imgData = input.readBytes(len);
- data = imgData.toUint8List();
- }
-
- void _readPlaneRleCompressed(InputBuffer input, int width, int height,
- int bitDepth, Uint16List lineLengths,
- int planeNum) {
- int len = width * height;
- if (bitDepth == 16) {
- len *= 2;
- }
- data = new Uint8List(len);
- int pos = 0;
- int lineIndex = planeNum * height;
- if (lineIndex >= lineLengths.length) {
- data.fillRange(0, data.length, 255);
- return;
- }
-
- for (int i = 0; i < height; ++i) {
- int len = lineLengths[lineIndex++];
- InputBuffer s = input.readBytes(len);
- _decodeRLE(s, data, pos);
- pos += width;
- }
- }
-
- void _decodeRLE(InputBuffer src, Uint8List dst, int dstIndex) {
- while (!src.isEOS) {
- int n = src.readInt8();
- if (n < 0) {
- n = 1 - n;
- int b = src.readByte();
- for (int i = 0; i < n; ++i) {
- dst[dstIndex++] = b;
- }
- } else {
- n++;
- for (int i = 0; i < n; ++i) {
- dst[dstIndex++] = src.readByte();
- }
- }
- }
- }
-}
+import 'dart:typed_data';
+
+import '../../image_exception.dart';
+import '../../util/input_buffer.dart';
+
+class PsdChannel {
+ static const int RED = 0;
+ static const int GREEN = 1;
+ static const int BLUE = 2;
+ static const int BLACK = 3;
+ static const int ALPHA = -1;
+ static const int MASK = -2;
+ static const int REAL_MASK = -3;
+
+ static const int COMPRESS_NONE = 0;
+ static const int COMPRESS_RLE = 1;
+ static const int COMPRESS_ZIP = 2;
+ static const int COMPRESS_ZIP_PREDICTOR = 3;
+
+ int id;
+ int dataLength;
+ Uint8List data;
+
+ PsdChannel(this.id, this.dataLength);
+
+ PsdChannel.read(InputBuffer input, this.id, int width, int height,
+ int bitDepth, int compression, Uint16List lineLengths,
+ int planeNumber) {
+ readPlane(input, width, height, bitDepth, compression, lineLengths,
+ planeNumber);
+ }
+
+ void readPlane(InputBuffer input, int width, int height, int bitDepth,
+ [int compression, Uint16List lineLengths, int planeNum = 0]) {
+ if (compression == null) {
+ compression = input.readUint16();
+ }
+
+ switch (compression) {
+ case COMPRESS_NONE:
+ _readPlaneUncompressed(input, width, height, bitDepth);
+ break;
+ case COMPRESS_RLE:
+ if (lineLengths == null) {
+ lineLengths = _readLineLengths(input, height);
+ }
+ _readPlaneRleCompressed(input, width, height, bitDepth, lineLengths,
+ planeNum);
+ break;
+ default:
+ throw new ImageException('Unsupported compression: $compression');
+ }
+ }
+
+ Uint16List _readLineLengths(InputBuffer input, int height) {
+ Uint16List lineLengths = Uint16List(height);
+ for (int i = 0; i < height; ++i) {
+ lineLengths[i] = input.readUint16();
+ }
+ return lineLengths;
+ }
+
+ void _readPlaneUncompressed(InputBuffer input, int width, int height,
+ int bitDepth) {
+ int len = width * height;
+ if (bitDepth == 16) {
+ len *= 2;
+ }
+ if (len > input.length) {
+ data = Uint8List(len);
+ data.fillRange(0, len, 255);
+ return;
+ }
+
+ InputBuffer imgData = input.readBytes(len);
+ data = imgData.toUint8List();
+ }
+
+ void _readPlaneRleCompressed(InputBuffer input, int width, int height,
+ int bitDepth, Uint16List lineLengths,
+ int planeNum) {
+ int len = width * height;
+ if (bitDepth == 16) {
+ len *= 2;
+ }
+ data = Uint8List(len);
+ int pos = 0;
+ int lineIndex = planeNum * height;
+ if (lineIndex >= lineLengths.length) {
+ data.fillRange(0, data.length, 255);
+ return;
+ }
+
+ for (int i = 0; i < height; ++i) {
+ int len = lineLengths[lineIndex++];
+ InputBuffer s = input.readBytes(len);
+ _decodeRLE(s, data, pos);
+ pos += width;
+ }
+ }
+
+ void _decodeRLE(InputBuffer src, Uint8List dst, int dstIndex) {
+ while (!src.isEOS) {
+ int n = src.readInt8();
+ if (n < 0) {
+ n = 1 - n;
+ int b = src.readByte();
+ for (int i = 0; i < n; ++i) {
+ dst[dstIndex++] = b;
+ }
+ } else {
+ n++;
+ for (int i = 0; i < n; ++i) {
+ dst[dstIndex++] = src.readByte();
+ }
+ }
+ }
+ }
+}
diff --git a/image/lib/src/formats/psd/psd_image.dart b/image/lib/src/formats/psd/psd_image.dart
old mode 100644
new mode 100755
index 0641644..b2a3ce7
--- a/image/lib/src/formats/psd/psd_image.dart
+++ b/image/lib/src/formats/psd/psd_image.dart
@@ -1,617 +1,617 @@
-import 'dart:math' as Math;
-import 'dart:typed_data';
-
-import '../decode_info.dart';
-import '../../color.dart';
-import '../../image.dart';
-import '../../image_exception.dart';
-import '../../util/input_buffer.dart';
-import 'psd_channel.dart';
-import 'psd_image_resource.dart';
-import 'psd_layer.dart';
-
-class PsdImage extends DecodeInfo {
- static const int SIGNATURE = 0x38425053; // '8BPS'
-
- static const int COLORMODE_BITMAP = 0;
- static const int COLORMODE_GRAYSCALE = 1;
- static const int COLORMODE_INDEXED = 2;
- static const int COLORMODE_RGB = 3;
- static const int COLORMODE_CMYK = 4;
- static const int COLORMODE_MULTICHANNEL = 7;
- static const int COLORMODE_DUOTONE = 8;
- static const int COLORMODE_LAB = 9;
-
- int signature;
- int version;
- int channels;
- int depth;
- int colorMode;
- List<PsdLayer> layers;
- List<PsdChannel> mergeImageChannels;
- Image mergedImage;
- Map<int, PsdImageResource> imageResources = {};
- bool hasAlpha = false;
-
- PsdImage(List<int> bytes) {
- _input = new InputBuffer(bytes, bigEndian: true);
-
- _readHeader();
- if (!isValid) {
- return;
- }
-
- int len = _input.readUint32();
- /*_colorData =*/ _input.readBytes(len);
-
- len = _input.readUint32();
- _imageResourceData = _input.readBytes(len);
-
- len = _input.readUint32();
- _layerAndMaskData = _input.readBytes(len);
-
- _imageData = _input.readBytes(_input.length);
- }
-
- bool get isValid => signature == SIGNATURE;
-
- /// The number of frames that can be decoded.
- int get numFrames => 1;
-
- /**
- * Decode the raw psd structure without rendering the output image.
- * Use [renderImage] to render the output image.
- */
- bool decode() {
- if (!isValid || _input == null) {
- return false;
- }
-
- // Color Mode Data Block:
- // Indexed and duotone images have palette data in colorData...
- _readColorModeData();
-
- // Image Resource Block:
- // Image resources are used to store non-pixel data associated with images,
- // such as pen tool paths.
- _readImageResources();
-
- _readLayerAndMaskData();
-
- _readMergeImageData();
-
- _input = null;
- //_colorData = null;
- _imageResourceData = null;
- _layerAndMaskData = null;
- _imageData = null;
-
- return true;
- }
-
- Image decodeImage() {
- if (!decode()) {
- return null;
- }
-
- return renderImage();
- }
-
- Image renderImage() {
- if (mergedImage != null) {
- return mergedImage;
- }
-
- mergedImage = new Image(width, height);
- mergedImage.fill(0);
-
- Uint8List pixels = mergedImage.getBytes();
-
- for (int li = 0; li < layers.length; ++li) {
- PsdLayer layer = layers[li];
- if (!layer.isVisible()) {
- continue;
- }
-
- double opacity = layer.opacity / 255.0;
- int blendMode = layer.blendMode;
-
- //int ns = depth == 16 ? 2 : 1;
- Uint8List srcP = layer.layerImage.getBytes();
-
- for (int y = 0, sy = layer.top, si = 0; y < layer.height; ++y, ++sy) {
- int di = (layer.top + y) * width * 4 + layer.left * 4;
- for (int x = 0, sx = layer.left; x < layer.width; ++x, ++sx) {
- int br = srcP[si++];
- int bg = srcP[si++];
- int bb = srcP[si++];
- int ba = srcP[si++];
-
- if (sx >= 0 && sx < width && sy >= 0 && sy < height) {
- int ar = pixels[di];
- int ag = pixels[di + 1];
- int ab = pixels[di + 2];
- int aa = pixels[di + 3];
-
- _blend(ar, ag, ab, aa, br, bg, bb, ba, blendMode, opacity,
- pixels, di);
- }
-
- di += 4;
- }
- }
- }
-
- return mergedImage;
- }
-
- void _blend(int ar, int ag, int ab, int aa,
- int br, int bg, int bb, int ba,
- int blendMode, double opacity,
- Uint8List pixels, int di) {
- int r = br;
- int g = bg;
- int b = bb;
- int a = ba;
- double da = (ba / 255.0) * opacity;
-
- switch (blendMode) {
- case PsdLayer.BLEND_PASSTHROUGH:
- r = ar;
- g = ag;
- b = ab;
- a = aa;
- break;
- case PsdLayer.BLEND_NORMAL:
- break;
- case PsdLayer.BLEND_DISSOLVE:
- break;
- case PsdLayer.BLEND_DARKEN:
- r = _blendDarken(ar, br);
- g = _blendDarken(ag, bg);
- b = _blendDarken(ab, bb);
- break;
- case PsdLayer.BLEND_MULTIPLY:
- r = _blendMultiply(ar, br);
- g = _blendMultiply(ag, bg);
- b = _blendMultiply(ab, bb);
- break;
- case PsdLayer.BLEND_COLOR_BURN:
- r = _blendColorBurn(ar, br);
- g = _blendColorBurn(ag, bg);
- b = _blendColorBurn(ab, bb);
- break;
- case PsdLayer.BLEND_LINEAR_BURN:
- r = _blendLinearBurn(ar, br);
- g = _blendLinearBurn(ag, bg);
- b = _blendLinearBurn(ab, bb);
- break;
- case PsdLayer.BLEND_DARKEN_COLOR:
- break;
- case PsdLayer.BLEND_LIGHTEN:
- r = _blendLighten(ar, br);
- g = _blendLighten(ag, bg);
- b = _blendLighten(ab, bb);
- break;
- case PsdLayer.BLEND_SCREEN:
- r = _blendScreen(ar, br);
- g = _blendScreen(ag, bg);
- b = _blendScreen(ab, bb);
- break;
- case PsdLayer.BLEND_COLOR_DODGE:
- r = _blendColorDodge(ar, br);
- g = _blendColorDodge(ag, bg);
- b = _blendColorDodge(ab, bb);
- break;
- case PsdLayer.BLEND_LINEAR_DODGE:
- r = _blendLinearDodge(ar, br);
- g = _blendLinearDodge(ag, bg);
- b = _blendLinearDodge(ab, bb);
- break;
- case PsdLayer.BLEND_LIGHTER_COLOR:
- break;
- case PsdLayer.BLEND_OVERLAY:
- r = _blendOverlay(ar, br, aa, ba);
- g = _blendOverlay(ag, bg, aa, ba);
- b = _blendOverlay(ab, bb, aa, ba);
- break;
- case PsdLayer.BLEND_SOFT_LIGHT:
- r = _blendSoftLight(ar, br);
- g = _blendSoftLight(ag, bg);
- b = _blendSoftLight(ab, bb);
- break;
- case PsdLayer.BLEND_HARD_LIGHT:
- r = _blendHardLight(ar, br);
- g = _blendHardLight(ag, bg);
- b = _blendHardLight(ab, bb);
- break;
- case PsdLayer.BLEND_VIVID_LIGHT:
- r = _blendVividLight(ar, br);
- g = _blendVividLight(ag, bg);
- b = _blendVividLight(ab, bb);
- break;
- case PsdLayer.BLEND_LINEAR_LIGHT:
- r = _blendLinearLight(ar, br);
- g = _blendLinearLight(ag, bg);
- b = _blendLinearLight(ab, bb);
- break;
- case PsdLayer.BLEND_PIN_LIGHT:
- r = _blendPinLight(ar, br);
- g = _blendPinLight(ag, bg);
- b = _blendPinLight(ab, bb);
- break;
- case PsdLayer.BLEND_HARD_MIX:
- r = _blendHardMix(ar, br);
- g = _blendHardMix(ag, bg);
- b = _blendHardMix(ab, bb);
- break;
- case PsdLayer.BLEND_DIFFERENCE:
- r = _blendDifference(ar, br);
- g = _blendDifference(ag, bg);
- b = _blendDifference(ab, bb);
- break;
- case PsdLayer.BLEND_EXCLUSION:
- r = _blendExclusion(ar, br);
- g = _blendExclusion(ag, bg);
- b = _blendExclusion(ab, bb);
- break;
- case PsdLayer.BLEND_SUBTRACT:
- break;
- case PsdLayer.BLEND_DIVIDE:
- break;
- case PsdLayer.BLEND_HUE:
- break;
- case PsdLayer.BLEND_SATURATION:
- break;
- case PsdLayer.BLEND_COLOR:
- break;
- case PsdLayer.BLEND_LUMINOSITY:
- break;
- }
-
- r = ((ar * (1.0 - da)) + (r * da)).toInt();
- g = ((ag * (1.0 - da)) + (g * da)).toInt();
- b = ((ab * (1.0 - da)) + (b * da)).toInt();
- a = ((aa * (1.0 - da)) + (a * da)).toInt();
-
- pixels[di++] = r;
- pixels[di++] = g;
- pixels[di++] = b;
- pixels[di++] = a;
- }
-
- static int _blendLighten(int a, int b) {
- return Math.max(a, b);
- }
-
- static int _blendDarken(int a, int b) {
- return Math.min(a, b);
- }
-
- static int _blendMultiply(int a, int b) {
- return (a * b) >> 8;
- }
-
- static int _blendOverlay(int a, int b, int aAlpha, int bAlpha) {
- double x = a / 255.0;
- double y = b / 255.0;
- double aa = aAlpha / 255.0;
- double ba = bAlpha / 255.0;
-
- double z;
- if (2.0 * x < aa) {
- z = 2.0 * y * x + y * (1.0 - aa) + x * (1.0 - ba);
- } else {
- z = ba * aa - 2.0 * (aa - x) * (ba - y) +
- y * (1.0 - aa) + x * (1.0 - ba);
- }
-
- return (z * 255.0).toInt().clamp(0, 255);
- }
-
- static int _blendColorBurn(int a, int b) {
- if (b == 0) {
- return 0; // We don't want to divide by zero
- }
- int c = (255.0 * (1.0 - (1.0 - (a / 255.0)) / (b / 255.0))).toInt();
- return c.clamp(0, 255);
- }
-
- static int _blendLinearBurn(int a, int b) {
- return (a + b - 255).clamp(0, 255);
- }
-
- static int _blendScreen(int a, int b) {
- return (255 - ((255 - b) * (255 - a))).clamp(0, 255);
- }
-
- static int _blendColorDodge(int a, int b) {
- if (b == 255) {
- return 255;
- }
- return (((a / 255) / (1.0 - (b / 255.0))) * 255.0).toInt().clamp(0, 255);
- }
-
- static int _blendLinearDodge(int a, int b) {
- return (b + a > 255) ? 0xff : a + b;
- }
-
- static int _blendSoftLight(int a, int b) {
- double aa = a / 255.0;
- double bb = b / 255.0;
- return (255.0 * ((1.0 - bb) * bb * aa +
- bb * (1.0 - (1.0 - bb) * (1.0 - aa)))).round();
- }
-
- static int _blendHardLight(int bottom, int top) {
- double a = top / 255.0;
- double b = bottom / 255.0;
- if (b < 0.5) {
- return (255.0 * 2.0 * a * b).round();
- } else {
- return (255.0 * (1.0 - 2.0 * (1.0 - a) * (1.0 - b))).round();
- }
- }
-
- static int _blendVividLight(int bottom, int top) {
- if ( top < 128) {
- return _blendColorBurn(bottom, 2 * top);
- } else {
- return _blendColorDodge(bottom, 2 * (top - 128));
- }
- }
-
- static int _blendLinearLight(int bottom, int top) {
- if (top < 128) {
- return _blendLinearBurn(bottom, 2 * top);
- } else {
- return _blendLinearDodge(bottom, 2 * (top - 128));
- }
- }
-
- static int _blendPinLight(int bottom, int top) {
- return (top < 128) ?
- _blendDarken(bottom, 2 * top) :
- _blendLighten(bottom, 2 * (top - 128));
- }
-
- static int _blendHardMix(int bottom, int top) {
- return (top < 255 - bottom) ? 0 : 255;
- }
-
- static int _blendDifference(int bottom, int top) {
- return (top - bottom).abs();
- }
-
- static int _blendExclusion(int bottom, int top) {
- return (top + bottom - 2 * top * bottom / 255.0).round();
- }
-
- void _readHeader() {
- signature = _input.readUint32();
- version = _input.readUint16();
-
- // version should be 1 (2 for PSB files).
- if (version != 1) {
- signature = 0;
- return;
- }
-
- // padding should be all 0's
- InputBuffer padding = _input.readBytes(6);
- for (int i = 0; i < 6; ++i) {
- if (padding[i] != 0) {
- signature = 0;
- return;
- }
- }
-
- channels = _input.readUint16();
- height = _input.readUint32();
- width = _input.readUint32();
- depth = _input.readUint16();
- colorMode = _input.readUint16();
- }
-
- void _readColorModeData() {
- // TODO support indexed and duotone images.
- }
-
- void _readImageResources() {
- _imageResourceData.rewind();
- while (!_imageResourceData.isEOS) {
- int blockSignature = _imageResourceData.readUint32();
- int blockId = _imageResourceData.readUint16();
-
- int len = _imageResourceData.readByte();
- String blockName = _imageResourceData.readString(len);
- // name string is padded to an even size
- if (len & 1 == 0) {
- _imageResourceData.skip(1);
- }
-
- len = _imageResourceData.readUint32();
- InputBuffer blockData = _imageResourceData.readBytes(len);
- // blocks are padded to an even length.
- if (len & 1 == 1) {
- _imageResourceData.skip(1);
- }
-
- if (blockSignature == RESOURCE_BLOCK_SIGNATURE) {
- imageResources[blockId] = new PsdImageResource(blockId, blockName,
- blockData);
- }
- }
- }
-
- void _readLayerAndMaskData() {
- _layerAndMaskData.rewind();
- int len = _layerAndMaskData.readUint32();
- if ((len & 1) != 0) {
- len++;
- }
-
- InputBuffer layerData = _layerAndMaskData.readBytes(len);
-
- layers = [];
- if (len > 0) {
- int count = layerData.readInt16();
- // If it is a negative number, its absolute value is the number of
- // layers and the first alpha channel contains the transparency data for
- // the merged result.
- if (count < 0) {
- hasAlpha = true;
- count = -count;
- }
-
- for (int i = 0; i < count; ++i) {
- PsdLayer layer = new PsdLayer(layerData);
- layers.add(layer);
- }
- }
-
- for (int i = 0; i < layers.length; ++i) {
- layers[i].readImageData(layerData, this);
- }
-
- // Global layer mask info
- len = _layerAndMaskData.readUint32();
- InputBuffer maskData = _layerAndMaskData.readBytes(len);
- if (len > 0) {
- /*int colorSpace =*/ maskData.readUint16();
- /*int rc =*/ maskData.readUint16();
- /*int gc =*/ maskData.readUint16();
- /*int bc =*/ maskData.readUint16();
- /*int ac =*/ maskData.readUint16();
- /*int opacity =*/ maskData.readUint16(); // 0-100
- /*int kind =*/ maskData.readByte();
- }
- }
-
- void _readMergeImageData() {
- _imageData.rewind();
- int compression = _imageData.readUint16();
-
- Uint16List lineLengths;
- if (compression == PsdChannel.COMPRESS_RLE) {
- int numLines = height * this.channels;
- lineLengths = new Uint16List(numLines);
- for (int i = 0; i < numLines; ++i) {
- lineLengths[i] = _imageData.readUint16();
- }
- }
-
- mergeImageChannels = [];
- for (int i = 0; i < channels; ++i) {
- mergeImageChannels.add(new PsdChannel.read(_imageData, i == 3 ? -1 : i,
- width, height, depth, compression,
- lineLengths, i));
- }
-
- mergedImage = createImageFromChannels(colorMode, depth,
- width, height,
- mergeImageChannels);
- }
-
- static int _ch(List<int> data, int si, int ns) {
- return ns == 1 ? data[si] :
- ((data[si] << 8) | data[si + 1]) >> 8;
- }
-
- static Image createImageFromChannels(int colorMode, int bitDepth, int width,
- int height,
- List<PsdChannel> channelList) {
- Image output = new Image(width, height);
- Uint8List pixels = output.getBytes();
-
- Map<int, PsdChannel> channels = {};
- for (PsdChannel ch in channelList) {
- channels[ch.id] = ch;
- }
-
- int numChannels = channelList.length;
- int ns = (bitDepth == 8) ? 1 : (bitDepth == 16) ? 2 : -1;
- if (ns == -1) {
- throw new ImageException('PSD: unsupported bit depth: $bitDepth');
- }
-
- final channel0 = channels[0];
- final channel1 = channels[1];
- final channel2 = channels[2];
- final channel_1 = channels[-1];
-
- for (int y = 0, di = 0, si = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x, si += ns) {
- switch (colorMode) {
- case COLORMODE_RGB:
- int xi = di;
- pixels[di++] = _ch(channel0.data, si, ns);
- pixels[di++] = _ch(channel1.data, si, ns);
- pixels[di++] = _ch(channel2.data, si, ns);
- pixels[di++] = numChannels >= 4 ?
- _ch(channel_1.data, si, ns) : 255;
-
- var r = pixels[xi];
- var g = pixels[xi + 1];
- var b = pixels[xi + 2];
- var a = pixels[xi + 3];
- if (a != 0) {
- // Photoshop/Gimp blend the image against white (argh!),
- // which is not what we want for compositing. Invert the blend
- // operation to try and undo the damage.
- pixels[xi] = (((r + a) - 255) * 255) ~/ a;
- pixels[xi + 1] = (((g + a) - 255) * 255) ~/ a;
- pixels[xi + 2] = (((b + a) - 255) * 255) ~/ a;
- }
- break;
- case COLORMODE_LAB:
- int L = _ch(channel0.data, si, ns) * 100 >> 8;
- int a = _ch(channel1.data, si, ns) - 128;
- int b = _ch(channel2.data, si, ns) - 128;
- int alpha = numChannels >= 4 ?
- _ch(channel_1.data, si, ns) : 255;
- List<int> rgb = labToRGB(L, a, b);
- pixels[di++] = rgb[0];
- pixels[di++] = rgb[1];
- pixels[di++] = rgb[2];
- pixels[di++] = alpha;
- break;
- case COLORMODE_GRAYSCALE:
- int gray = _ch(channel0.data, si, ns);
- int alpha = numChannels >= 2 ?
- _ch(channel_1.data, si, ns) : 255;
- pixels[di++] = gray;
- pixels[di++] = gray;
- pixels[di++] = gray;
- pixels[di++] = alpha;
- break;
- case COLORMODE_CMYK:
- int c = _ch(channel0.data, si, ns);
- int m = _ch(channel1.data, si, ns);
- int y = _ch(channel2.data, si, ns);
- int k = _ch(channels[numChannels == 4 ? -1 : 3].data, si, ns);
- int alpha = numChannels >= 5 ?
- _ch(channel_1.data, si, ns) : 255;
- List<int> rgb = cmykToRGB(255 - c, 255 - m, 255 - y, 255 - k);
- pixels[di++] = rgb[0];
- pixels[di++] = rgb[1];
- pixels[di++] = rgb[2];
- pixels[di++] = alpha;
- break;
- default:
- throw new ImageException('Unhandled color mode: $colorMode');
- }
- }
- }
-
- return output;
- }
-
- static const int RESOURCE_BLOCK_SIGNATURE = 0x3842494d; // '8BIM'
-
- InputBuffer _input;
- //InputBuffer _colorData;
- InputBuffer _imageResourceData;
- InputBuffer _layerAndMaskData;
- InputBuffer _imageData;
-}
+import 'dart:math' as Math;
+import 'dart:typed_data';
+
+import '../decode_info.dart';
+import '../../color.dart';
+import '../../image.dart';
+import '../../image_exception.dart';
+import '../../util/input_buffer.dart';
+import 'psd_channel.dart';
+import 'psd_image_resource.dart';
+import 'psd_layer.dart';
+
+class PsdImage extends DecodeInfo {
+ static const int SIGNATURE = 0x38425053; // '8BPS'
+
+ static const int COLORMODE_BITMAP = 0;
+ static const int COLORMODE_GRAYSCALE = 1;
+ static const int COLORMODE_INDEXED = 2;
+ static const int COLORMODE_RGB = 3;
+ static const int COLORMODE_CMYK = 4;
+ static const int COLORMODE_MULTICHANNEL = 7;
+ static const int COLORMODE_DUOTONE = 8;
+ static const int COLORMODE_LAB = 9;
+
+ int signature;
+ int version;
+ int channels;
+ int depth;
+ int colorMode;
+ List<PsdLayer> layers;
+ List<PsdChannel> mergeImageChannels;
+ Image mergedImage;
+ Map<int, PsdImageResource> imageResources = {};
+ bool hasAlpha = false;
+
+ PsdImage(List<int> bytes) {
+ _input = InputBuffer(bytes, bigEndian: true);
+
+ _readHeader();
+ if (!isValid) {
+ return;
+ }
+
+ int len = _input.readUint32();
+ /*_colorData =*/ _input.readBytes(len);
+
+ len = _input.readUint32();
+ _imageResourceData = _input.readBytes(len);
+
+ len = _input.readUint32();
+ _layerAndMaskData = _input.readBytes(len);
+
+ _imageData = _input.readBytes(_input.length);
+ }
+
+ bool get isValid => signature == SIGNATURE;
+
+ /// The number of frames that can be decoded.
+ int get numFrames => 1;
+
+ /**
+ * Decode the raw psd structure without rendering the output image.
+ * Use [renderImage] to render the output image.
+ */
+ bool decode() {
+ if (!isValid || _input == null) {
+ return false;
+ }
+
+ // Color Mode Data Block:
+ // Indexed and duotone images have palette data in colorData...
+ _readColorModeData();
+
+ // Image Resource Block:
+ // Image resources are used to store non-pixel data associated with images,
+ // such as pen tool paths.
+ _readImageResources();
+
+ _readLayerAndMaskData();
+
+ _readMergeImageData();
+
+ _input = null;
+ //_colorData = null;
+ _imageResourceData = null;
+ _layerAndMaskData = null;
+ _imageData = null;
+
+ return true;
+ }
+
+ Image decodeImage() {
+ if (!decode()) {
+ return null;
+ }
+
+ return renderImage();
+ }
+
+ Image renderImage() {
+ if (mergedImage != null) {
+ return mergedImage;
+ }
+
+ mergedImage = Image(width, height);
+ mergedImage.fill(0);
+
+ Uint8List pixels = mergedImage.getBytes();
+
+ for (int li = 0; li < layers.length; ++li) {
+ PsdLayer layer = layers[li];
+ if (!layer.isVisible()) {
+ continue;
+ }
+
+ double opacity = layer.opacity / 255.0;
+ int blendMode = layer.blendMode;
+
+ //int ns = depth == 16 ? 2 : 1;
+ Uint8List srcP = layer.layerImage.getBytes();
+
+ for (int y = 0, sy = layer.top, si = 0; y < layer.height; ++y, ++sy) {
+ int di = (layer.top + y) * width * 4 + layer.left * 4;
+ for (int x = 0, sx = layer.left; x < layer.width; ++x, ++sx) {
+ int br = srcP[si++];
+ int bg = srcP[si++];
+ int bb = srcP[si++];
+ int ba = srcP[si++];
+
+ if (sx >= 0 && sx < width && sy >= 0 && sy < height) {
+ int ar = pixels[di];
+ int ag = pixels[di + 1];
+ int ab = pixels[di + 2];
+ int aa = pixels[di + 3];
+
+ _blend(ar, ag, ab, aa, br, bg, bb, ba, blendMode, opacity,
+ pixels, di);
+ }
+
+ di += 4;
+ }
+ }
+ }
+
+ return mergedImage;
+ }
+
+ void _blend(int ar, int ag, int ab, int aa,
+ int br, int bg, int bb, int ba,
+ int blendMode, double opacity,
+ Uint8List pixels, int di) {
+ int r = br;
+ int g = bg;
+ int b = bb;
+ int a = ba;
+ double da = (ba / 255.0) * opacity;
+
+ switch (blendMode) {
+ case PsdLayer.BLEND_PASSTHROUGH:
+ r = ar;
+ g = ag;
+ b = ab;
+ a = aa;
+ break;
+ case PsdLayer.BLEND_NORMAL:
+ break;
+ case PsdLayer.BLEND_DISSOLVE:
+ break;
+ case PsdLayer.BLEND_DARKEN:
+ r = _blendDarken(ar, br);
+ g = _blendDarken(ag, bg);
+ b = _blendDarken(ab, bb);
+ break;
+ case PsdLayer.BLEND_MULTIPLY:
+ r = _blendMultiply(ar, br);
+ g = _blendMultiply(ag, bg);
+ b = _blendMultiply(ab, bb);
+ break;
+ case PsdLayer.BLEND_COLOR_BURN:
+ r = _blendColorBurn(ar, br);
+ g = _blendColorBurn(ag, bg);
+ b = _blendColorBurn(ab, bb);
+ break;
+ case PsdLayer.BLEND_LINEAR_BURN:
+ r = _blendLinearBurn(ar, br);
+ g = _blendLinearBurn(ag, bg);
+ b = _blendLinearBurn(ab, bb);
+ break;
+ case PsdLayer.BLEND_DARKEN_COLOR:
+ break;
+ case PsdLayer.BLEND_LIGHTEN:
+ r = _blendLighten(ar, br);
+ g = _blendLighten(ag, bg);
+ b = _blendLighten(ab, bb);
+ break;
+ case PsdLayer.BLEND_SCREEN:
+ r = _blendScreen(ar, br);
+ g = _blendScreen(ag, bg);
+ b = _blendScreen(ab, bb);
+ break;
+ case PsdLayer.BLEND_COLOR_DODGE:
+ r = _blendColorDodge(ar, br);
+ g = _blendColorDodge(ag, bg);
+ b = _blendColorDodge(ab, bb);
+ break;
+ case PsdLayer.BLEND_LINEAR_DODGE:
+ r = _blendLinearDodge(ar, br);
+ g = _blendLinearDodge(ag, bg);
+ b = _blendLinearDodge(ab, bb);
+ break;
+ case PsdLayer.BLEND_LIGHTER_COLOR:
+ break;
+ case PsdLayer.BLEND_OVERLAY:
+ r = _blendOverlay(ar, br, aa, ba);
+ g = _blendOverlay(ag, bg, aa, ba);
+ b = _blendOverlay(ab, bb, aa, ba);
+ break;
+ case PsdLayer.BLEND_SOFT_LIGHT:
+ r = _blendSoftLight(ar, br);
+ g = _blendSoftLight(ag, bg);
+ b = _blendSoftLight(ab, bb);
+ break;
+ case PsdLayer.BLEND_HARD_LIGHT:
+ r = _blendHardLight(ar, br);
+ g = _blendHardLight(ag, bg);
+ b = _blendHardLight(ab, bb);
+ break;
+ case PsdLayer.BLEND_VIVID_LIGHT:
+ r = _blendVividLight(ar, br);
+ g = _blendVividLight(ag, bg);
+ b = _blendVividLight(ab, bb);
+ break;
+ case PsdLayer.BLEND_LINEAR_LIGHT:
+ r = _blendLinearLight(ar, br);
+ g = _blendLinearLight(ag, bg);
+ b = _blendLinearLight(ab, bb);
+ break;
+ case PsdLayer.BLEND_PIN_LIGHT:
+ r = _blendPinLight(ar, br);
+ g = _blendPinLight(ag, bg);
+ b = _blendPinLight(ab, bb);
+ break;
+ case PsdLayer.BLEND_HARD_MIX:
+ r = _blendHardMix(ar, br);
+ g = _blendHardMix(ag, bg);
+ b = _blendHardMix(ab, bb);
+ break;
+ case PsdLayer.BLEND_DIFFERENCE:
+ r = _blendDifference(ar, br);
+ g = _blendDifference(ag, bg);
+ b = _blendDifference(ab, bb);
+ break;
+ case PsdLayer.BLEND_EXCLUSION:
+ r = _blendExclusion(ar, br);
+ g = _blendExclusion(ag, bg);
+ b = _blendExclusion(ab, bb);
+ break;
+ case PsdLayer.BLEND_SUBTRACT:
+ break;
+ case PsdLayer.BLEND_DIVIDE:
+ break;
+ case PsdLayer.BLEND_HUE:
+ break;
+ case PsdLayer.BLEND_SATURATION:
+ break;
+ case PsdLayer.BLEND_COLOR:
+ break;
+ case PsdLayer.BLEND_LUMINOSITY:
+ break;
+ }
+
+ r = ((ar * (1.0 - da)) + (r * da)).toInt();
+ g = ((ag * (1.0 - da)) + (g * da)).toInt();
+ b = ((ab * (1.0 - da)) + (b * da)).toInt();
+ a = ((aa * (1.0 - da)) + (a * da)).toInt();
+
+ pixels[di++] = r;
+ pixels[di++] = g;
+ pixels[di++] = b;
+ pixels[di++] = a;
+ }
+
+ static int _blendLighten(int a, int b) {
+ return Math.max(a, b);
+ }
+
+ static int _blendDarken(int a, int b) {
+ return Math.min(a, b);
+ }
+
+ static int _blendMultiply(int a, int b) {
+ return (a * b) >> 8;
+ }
+
+ static int _blendOverlay(int a, int b, int aAlpha, int bAlpha) {
+ double x = a / 255.0;
+ double y = b / 255.0;
+ double aa = aAlpha / 255.0;
+ double ba = bAlpha / 255.0;
+
+ double z;
+ if (2.0 * x < aa) {
+ z = 2.0 * y * x + y * (1.0 - aa) + x * (1.0 - ba);
+ } else {
+ z = ba * aa - 2.0 * (aa - x) * (ba - y) +
+ y * (1.0 - aa) + x * (1.0 - ba);
+ }
+
+ return (z * 255.0).toInt().clamp(0, 255);
+ }
+
+ static int _blendColorBurn(int a, int b) {
+ if (b == 0) {
+ return 0; // We don't want to divide by zero
+ }
+ int c = (255.0 * (1.0 - (1.0 - (a / 255.0)) / (b / 255.0))).toInt();
+ return c.clamp(0, 255);
+ }
+
+ static int _blendLinearBurn(int a, int b) {
+ return (a + b - 255).clamp(0, 255);
+ }
+
+ static int _blendScreen(int a, int b) {
+ return (255 - ((255 - b) * (255 - a))).clamp(0, 255);
+ }
+
+ static int _blendColorDodge(int a, int b) {
+ if (b == 255) {
+ return 255;
+ }
+ return (((a / 255) / (1.0 - (b / 255.0))) * 255.0).toInt().clamp(0, 255);
+ }
+
+ static int _blendLinearDodge(int a, int b) {
+ return (b + a > 255) ? 0xff : a + b;
+ }
+
+ static int _blendSoftLight(int a, int b) {
+ double aa = a / 255.0;
+ double bb = b / 255.0;
+ return (255.0 * ((1.0 - bb) * bb * aa +
+ bb * (1.0 - (1.0 - bb) * (1.0 - aa)))).round();
+ }
+
+ static int _blendHardLight(int bottom, int top) {
+ double a = top / 255.0;
+ double b = bottom / 255.0;
+ if (b < 0.5) {
+ return (255.0 * 2.0 * a * b).round();
+ } else {
+ return (255.0 * (1.0 - 2.0 * (1.0 - a) * (1.0 - b))).round();
+ }
+ }
+
+ static int _blendVividLight(int bottom, int top) {
+ if ( top < 128) {
+ return _blendColorBurn(bottom, 2 * top);
+ } else {
+ return _blendColorDodge(bottom, 2 * (top - 128));
+ }
+ }
+
+ static int _blendLinearLight(int bottom, int top) {
+ if (top < 128) {
+ return _blendLinearBurn(bottom, 2 * top);
+ } else {
+ return _blendLinearDodge(bottom, 2 * (top - 128));
+ }
+ }
+
+ static int _blendPinLight(int bottom, int top) {
+ return (top < 128) ?
+ _blendDarken(bottom, 2 * top) :
+ _blendLighten(bottom, 2 * (top - 128));
+ }
+
+ static int _blendHardMix(int bottom, int top) {
+ return (top < 255 - bottom) ? 0 : 255;
+ }
+
+ static int _blendDifference(int bottom, int top) {
+ return (top - bottom).abs();
+ }
+
+ static int _blendExclusion(int bottom, int top) {
+ return (top + bottom - 2 * top * bottom / 255.0).round();
+ }
+
+ void _readHeader() {
+ signature = _input.readUint32();
+ version = _input.readUint16();
+
+ // version should be 1 (2 for PSB files).
+ if (version != 1) {
+ signature = 0;
+ return;
+ }
+
+ // padding should be all 0's
+ InputBuffer padding = _input.readBytes(6);
+ for (int i = 0; i < 6; ++i) {
+ if (padding[i] != 0) {
+ signature = 0;
+ return;
+ }
+ }
+
+ channels = _input.readUint16();
+ height = _input.readUint32();
+ width = _input.readUint32();
+ depth = _input.readUint16();
+ colorMode = _input.readUint16();
+ }
+
+ void _readColorModeData() {
+ // TODO support indexed and duotone images.
+ }
+
+ void _readImageResources() {
+ _imageResourceData.rewind();
+ while (!_imageResourceData.isEOS) {
+ int blockSignature = _imageResourceData.readUint32();
+ int blockId = _imageResourceData.readUint16();
+
+ int len = _imageResourceData.readByte();
+ String blockName = _imageResourceData.readString(len);
+ // name string is padded to an even size
+ if (len & 1 == 0) {
+ _imageResourceData.skip(1);
+ }
+
+ len = _imageResourceData.readUint32();
+ InputBuffer blockData = _imageResourceData.readBytes(len);
+ // blocks are padded to an even length.
+ if (len & 1 == 1) {
+ _imageResourceData.skip(1);
+ }
+
+ if (blockSignature == RESOURCE_BLOCK_SIGNATURE) {
+ imageResources[blockId] = PsdImageResource(blockId, blockName,
+ blockData);
+ }
+ }
+ }
+
+ void _readLayerAndMaskData() {
+ _layerAndMaskData.rewind();
+ int len = _layerAndMaskData.readUint32();
+ if ((len & 1) != 0) {
+ len++;
+ }
+
+ InputBuffer layerData = _layerAndMaskData.readBytes(len);
+
+ layers = [];
+ if (len > 0) {
+ int count = layerData.readInt16();
+ // If it is a negative number, its absolute value is the number of
+ // layers and the first alpha channel contains the transparency data for
+ // the merged result.
+ if (count < 0) {
+ hasAlpha = true;
+ count = -count;
+ }
+
+ for (int i = 0; i < count; ++i) {
+ PsdLayer layer = PsdLayer(layerData);
+ layers.add(layer);
+ }
+ }
+
+ for (int i = 0; i < layers.length; ++i) {
+ layers[i].readImageData(layerData, this);
+ }
+
+ // Global layer mask info
+ len = _layerAndMaskData.readUint32();
+ InputBuffer maskData = _layerAndMaskData.readBytes(len);
+ if (len > 0) {
+ /*int colorSpace =*/ maskData.readUint16();
+ /*int rc =*/ maskData.readUint16();
+ /*int gc =*/ maskData.readUint16();
+ /*int bc =*/ maskData.readUint16();
+ /*int ac =*/ maskData.readUint16();
+ /*int opacity =*/ maskData.readUint16(); // 0-100
+ /*int kind =*/ maskData.readByte();
+ }
+ }
+
+ void _readMergeImageData() {
+ _imageData.rewind();
+ int compression = _imageData.readUint16();
+
+ Uint16List lineLengths;
+ if (compression == PsdChannel.COMPRESS_RLE) {
+ int numLines = height * this.channels;
+ lineLengths = Uint16List(numLines);
+ for (int i = 0; i < numLines; ++i) {
+ lineLengths[i] = _imageData.readUint16();
+ }
+ }
+
+ mergeImageChannels = [];
+ for (int i = 0; i < channels; ++i) {
+ mergeImageChannels.add(new PsdChannel.read(_imageData, i == 3 ? -1 : i,
+ width, height, depth, compression,
+ lineLengths, i));
+ }
+
+ mergedImage = createImageFromChannels(colorMode, depth,
+ width, height,
+ mergeImageChannels);
+ }
+
+ static int _ch(List<int> data, int si, int ns) {
+ return ns == 1 ? data[si] :
+ ((data[si] << 8) | data[si + 1]) >> 8;
+ }
+
+ static Image createImageFromChannels(int colorMode, int bitDepth, int width,
+ int height,
+ List<PsdChannel> channelList) {
+ Image output = Image(width, height);
+ Uint8List pixels = output.getBytes();
+
+ Map<int, PsdChannel> channels = {};
+ for (PsdChannel ch in channelList) {
+ channels[ch.id] = ch;
+ }
+
+ int numChannels = channelList.length;
+ int ns = (bitDepth == 8) ? 1 : (bitDepth == 16) ? 2 : -1;
+ if (ns == -1) {
+ throw new ImageException('PSD: unsupported bit depth: $bitDepth');
+ }
+
+ final channel0 = channels[0];
+ final channel1 = channels[1];
+ final channel2 = channels[2];
+ final channel_1 = channels[-1];
+
+ for (int y = 0, di = 0, si = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x, si += ns) {
+ switch (colorMode) {
+ case COLORMODE_RGB:
+ int xi = di;
+ pixels[di++] = _ch(channel2.data, si, ns);
+ pixels[di++] = _ch(channel1.data, si, ns);
+ pixels[di++] = _ch(channel0.data, si, ns);
+ pixels[di++] = numChannels >= 4 ?
+ _ch(channel_1.data, si, ns) : 255;
+
+ var b = pixels[xi];
+ var g = pixels[xi + 1];
+ var r = pixels[xi + 2];
+ var a = pixels[xi + 3];
+ if (a != 0) {
+ // Photoshop/Gimp blend the image against white (argh!),
+ // which is not what we want for compositing. Invert the blend
+ // operation to try and undo the damage.
+ pixels[xi] = (((b + a) - 255) * 255) ~/ a;
+ pixels[xi + 1] = (((g + a) - 255) * 255) ~/ a;
+ pixels[xi + 2] = (((r + a) - 255) * 255) ~/ a;
+ }
+ break;
+ case COLORMODE_LAB:
+ int L = _ch(channel0.data, si, ns) * 100 >> 8;
+ int a = _ch(channel1.data, si, ns) - 128;
+ int b = _ch(channel2.data, si, ns) - 128;
+ int alpha = numChannels >= 4 ?
+ _ch(channel_1.data, si, ns) : 255;
+ List<int> rgb = labToRGB(L, a, b);
+ pixels[di++] = rgb[2];
+ pixels[di++] = rgb[1];
+ pixels[di++] = rgb[0];
+ pixels[di++] = alpha;
+ break;
+ case COLORMODE_GRAYSCALE:
+ int gray = _ch(channel0.data, si, ns);
+ int alpha = numChannels >= 2 ?
+ _ch(channel_1.data, si, ns) : 255;
+ pixels[di++] = gray;
+ pixels[di++] = gray;
+ pixels[di++] = gray;
+ pixels[di++] = alpha;
+ break;
+ case COLORMODE_CMYK:
+ int c = _ch(channel0.data, si, ns);
+ int m = _ch(channel1.data, si, ns);
+ int y = _ch(channel2.data, si, ns);
+ int k = _ch(channels[numChannels == 4 ? -1 : 3].data, si, ns);
+ int alpha = numChannels >= 5 ?
+ _ch(channel_1.data, si, ns) : 255;
+ List<int> rgb = cmykToRGB(255 - c, 255 - m, 255 - y, 255 - k);
+ pixels[di++] = rgb[2];
+ pixels[di++] = rgb[1];
+ pixels[di++] = rgb[0];
+ pixels[di++] = alpha;
+ break;
+ default:
+ throw new ImageException('Unhandled color mode: $colorMode');
+ }
+ }
+ }
+
+ return output;
+ }
+
+ static const int RESOURCE_BLOCK_SIGNATURE = 0x3842494d; // '8BIM'
+
+ InputBuffer _input;
+ //InputBuffer _colorData;
+ InputBuffer _imageResourceData;
+ InputBuffer _layerAndMaskData;
+ InputBuffer _imageData;
+}
diff --git a/image/lib/src/formats/psd/psd_image_resource.dart b/image/lib/src/formats/psd/psd_image_resource.dart
old mode 100644
new mode 100755
index 3ab3fa8..e6e6e69
--- a/image/lib/src/formats/psd/psd_image_resource.dart
+++ b/image/lib/src/formats/psd/psd_image_resource.dart
@@ -1,9 +1,9 @@
-import '../../util/input_buffer.dart';
-
-class PsdImageResource {
- int id;
- String name;
- InputBuffer data;
-
- PsdImageResource(this.id, this.name, this.data);
-}
+import '../../util/input_buffer.dart';
+
+class PsdImageResource {
+ int id;
+ String name;
+ InputBuffer data;
+
+ PsdImageResource(this.id, this.name, this.data);
+}
diff --git a/image/lib/src/formats/psd/psd_layer.dart b/image/lib/src/formats/psd/psd_layer.dart
old mode 100644
new mode 100755
index a46d69b..9e25c12
--- a/image/lib/src/formats/psd/psd_layer.dart
+++ b/image/lib/src/formats/psd/psd_layer.dart
@@ -1,333 +1,333 @@
-import '../../image.dart';
-import '../../image_exception.dart';
-import '../../util/input_buffer.dart';
-import 'psd_blending_ranges.dart';
-import 'psd_channel.dart';
-import 'psd_image.dart';
-import 'psd_layer_data.dart';
-import 'psd_mask.dart';
-import 'effect/psd_bevel_effect.dart';
-import 'effect/psd_drop_shadow_effect.dart';
-import 'effect/psd_effect.dart';
-import 'effect/psd_inner_glow_effect.dart';
-import 'effect/psd_inner_shadow_effect.dart';
-import 'effect/psd_outer_glow_effect.dart';
-import 'effect/psd_solid_fill_effect.dart';
-import 'layer_data/psd_layer_additional_data.dart';
-import 'layer_data/psd_layer_section_divider.dart';
-
-class PsdLayer {
- int top;
- int left;
- int bottom;
- int right;
- int width;
- int height;
- int blendMode;
- int opacity;
- int clipping;
- int flags;
- int compression;
- String name;
- List<PsdChannel> channels;
- PsdMask mask;
- PsdBlendingRanges blendingRanges;
- Map<String, PsdLayerData> additionalData = {};
- List<PsdLayer> children = [];
- PsdLayer parent;
- Image layerImage;
- List<PsdEffect> effects = [];
-
- static const int SIGNATURE = 0x3842494d; // '8BIM'
-
- static const int BLEND_PASSTHROUGH = 0x70617373; // 'pass'
- static const int BLEND_NORMAL = 0x6e6f726d; // 'norm'
- static const int BLEND_DISSOLVE = 0x64697373; // 'diss'
- static const int BLEND_DARKEN = 0x6461726b; // 'dark'
- static const int BLEND_MULTIPLY = 0x6d756c20; // 'mul '
- static const int BLEND_COLOR_BURN = 0x69646976; // 'idiv'
- static const int BLEND_LINEAR_BURN = 0x6c62726e; // 'lbrn'
- static const int BLEND_DARKEN_COLOR = 0x646b436c; // 'dkCl'
- static const int BLEND_LIGHTEN = 0x6c697465; // 'lite'
- static const int BLEND_SCREEN = 0x7363726e; // 'scrn'
- static const int BLEND_COLOR_DODGE = 0x64697620; // 'div '
- static const int BLEND_LINEAR_DODGE = 0x6c646467; // 'lddg'
- static const int BLEND_LIGHTER_COLOR = 0x6c67436c; // 'lgCl'
- static const int BLEND_OVERLAY = 0x6f766572; // 'over'
- static const int BLEND_SOFT_LIGHT = 0x734c6974; // 'sLit'
- static const int BLEND_HARD_LIGHT = 0x684c6974; // 'hLit'
- static const int BLEND_VIVID_LIGHT = 0x764c6974; // 'vLit'
- static const int BLEND_LINEAR_LIGHT = 0x6c4c6974; // lLit'
- static const int BLEND_PIN_LIGHT = 0x704c6974; // 'pLit'
- static const int BLEND_HARD_MIX = 0x684d6978; // 'hMix'
- static const int BLEND_DIFFERENCE = 0x64696666; // 'diff'
- static const int BLEND_EXCLUSION = 0x736d7564; // 'smud'
- static const int BLEND_SUBTRACT = 0x66737562; // 'fsub'
- static const int BLEND_DIVIDE = 0x66646976; // 'fdiv'
- static const int BLEND_HUE = 0x68756520; // 'hue '
- static const int BLEND_SATURATION = 0x73617420; // 'sat '
- static const int BLEND_COLOR = 0x636f6c72; // 'colr'
- static const int BLEND_LUMINOSITY = 0x6c756d20; // 'lum '
-
- static const int FLAG_TRANSPARENCY_PROTECTED = 1;
- static const int FLAG_HIDDEN = 2;
- static const int FLAG_OBSOLETE = 4;
- static const int FLAG_PHOTOSHOP_5 = 8;
- static const int FLAG_PIXEL_DATA_IRRELEVANT_TO_APPEARANCE = 16;
-
-
- PsdLayer([InputBuffer input]) {
- if (input == null) {
- return;
- }
-
- top = input.readInt32();
- left = input.readInt32();
- bottom = input.readInt32();
- right = input.readInt32();
- width = right - left;
- height = bottom - top;
-
- channels = [];
- int numChannels = input.readUint16();
- for (int i = 0; i < numChannels; ++i) {
- int id = input.readInt16();
- int len = input.readUint32();
- channels.add(new PsdChannel(id, len));
- }
-
- int sig = input.readUint32();
- if (sig != SIGNATURE) {
- throw new ImageException('Invalid PSD layer signature: '
- '${sig.toRadixString(16)}');
- }
-
- blendMode = input.readUint32();
- opacity = input.readByte();
- clipping = input.readByte();
- flags = input.readByte();
-
- int filler = input.readByte(); // should be 0
- if (filler != 0) {
- throw new ImageException('Invalid PSD layer data');
- }
-
- int len = input.readUint32();
- InputBuffer extra = input.readBytes(len);
-
- if (len > 0) {
- // Mask Data
- len = extra.readUint32();
- assert(len == 0 || len == 20 || len == 36);
- if (len > 0) {
- InputBuffer maskData = extra.readBytes(len);
- mask = new PsdMask(maskData);
- }
-
- // Layer Blending Ranges
- len = extra.readUint32();
- if (len > 0) {
- InputBuffer data = extra.readBytes(len);
- blendingRanges = new PsdBlendingRanges(data);
- }
-
- // Layer name
- len = extra.readByte();
- name = extra.readString(len);
- // Layer name is padded to a multiple of 4 bytes.
- int padding = (4 - (len % 4)) - 1;
- if (padding > 0) {
- extra.skip(padding);
- }
-
- // Additional layer sections
- while (!extra.isEOS) {
- int sig = extra.readUint32();
- if (sig != SIGNATURE) {
- throw new ImageException('PSD invalid signature for layer additional '
- 'data: ${sig.toRadixString(16)}');
- }
-
- String tag = extra.readString(4);
-
- len = extra.readUint32();
- InputBuffer data = extra.readBytes(len);
- // pad to an even byte count.
- if (len & 1 == 1) {
- extra.skip(1);
- }
-
- additionalData[tag] = new PsdLayerData(tag, data);
-
- // Layer effects data
- if (tag == 'lrFX') {
- var fxData = (additionalData['lrFX'] as PsdLayerAdditionalData);
- var data = new InputBuffer.from(fxData.data);
- /*int version =*/ data.readUint16();
- int numFx = data.readUint16();
-
- for (int j = 0; j < numFx; ++j) {
- /*var tag =*/ data.readString(4); // 8BIM
- var fxTag = data.readString(4);
- int size = data.readUint32();
-
- if (fxTag == 'dsdw') {
- var fx = new PsdDropShadowEffect();
- effects.add(fx);
- fx.version = data.readUint32();
- fx.blur = data.readUint32();
- fx.intensity = data.readUint32();
- fx.angle = data.readUint32();
- fx.distance = data.readUint32();
- fx.color = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- fx.blendMode = data.readString(8);
- fx.enabled = data.readByte() != 0;
- fx.globalAngle = data.readByte() != 0;
- fx.opacity = data.readByte();
- fx.nativeColor = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- } else if (fxTag == 'isdw') {
- var fx = new PsdInnerShadowEffect();
- effects.add(fx);
- fx.version = data.readUint32();
- fx.blur = data.readUint32();
- fx.intensity = data.readUint32();
- fx.angle = data.readUint32();
- fx.distance = data.readUint32();
- fx.color = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- fx.blendMode = data.readString(8);
- fx.enabled = data.readByte() != 0;
- fx.globalAngle = data.readByte() != 0;
- fx.opacity = data.readByte();
- fx.nativeColor = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- } else if (fxTag == 'oglw') {
- var fx = new PsdOuterGlowEffect();
- effects.add(fx);
- fx.version = data.readUint32();
- fx.blur = data.readUint32();
- fx.intensity = data.readUint32();
- fx.color = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- fx.blendMode = data.readString(8);
- fx.enabled = data.readByte() != 0;
- fx.opacity = data.readByte();
- if (fx.version == 2) {
- fx.nativeColor = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- }
- } else if (fxTag == 'iglw') {
- var fx = new PsdInnerGlowEffect();
- effects.add(fx);
- fx.version = data.readUint32();
- fx.blur = data.readUint32();
- fx.intensity = data.readUint32();
- fx.color = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- fx.blendMode = data.readString(8);
- fx.enabled = data.readByte() != 0;
- fx.opacity = data.readByte();
- if (fx.version == 2) {
- fx.invert = data.readByte() != 0;
- fx.nativeColor = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- }
- } else if (fxTag == 'bevl') {
- var fx = new PsdBevelEffect();
- effects.add(fx);
- fx.version = data.readUint32();
- fx.angle = data.readUint32();
- fx.strength = data.readUint32();
- fx.blur = data.readUint32();
- fx.highlightBlendMode = data.readString(8);
- fx.shadowBlendMode = data.readString(8);
- fx.highlightColor = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- fx.shadowColor = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- fx.bevelStyle = data.readByte();
- fx.highlightOpacity = data.readByte();
- fx.shadowOpacity = data.readByte();
- fx.enabled = data.readByte() != 0;
- fx.globalAngle = data.readByte() != 0;
- fx.upOrDown = data.readByte();
- if (fx.version == 2) {
- fx.realHighlightColor = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- fx.realShadowColor = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- }
- } else if (fxTag == 'sofi') {
- var fx = new PsdSolidFillEffect();
- effects.add(fx);
- fx.version = data.readUint32();
- fx.blendMode = data.readString(4);
- fx.color = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- fx.opacity = data.readByte();
- fx.enabled = data.readByte() != 0;
- fx.nativeColor = [data.readUint16(), data.readUint16(),
- data.readUint16(), data.readUint16(),
- data.readUint16()];
- } else {
- data.skip(size);
- }
- }
- }
- }
- }
- }
-
- /**
- * Is this layer visible?
- */
- bool isVisible() => flags & FLAG_HIDDEN == 0;
-
- /**
- * Is this layer a folder?
- */
- int type() {
- if (additionalData.containsKey(PsdLayerSectionDivider.TAG)) {
- PsdLayerSectionDivider section = additionalData[PsdLayerSectionDivider.TAG];
- return section.type;
- }
- return PsdLayerSectionDivider.NORMAL;
- }
-
- /**
- * Get the channel for the given [id].
- * Returns null if the layer does not have the given channel.
- */
- PsdChannel getChannel(int id) {
- for (int i = 0; i < channels.length; ++i) {
- if (channels[i].id == id) {
- return channels[i];
- }
- }
- return null;
- }
-
- void readImageData(InputBuffer input, PsdImage psd) {
- for (int i = 0; i < channels.length; ++i) {
- channels[i].readPlane(input, width, height, psd.depth);
- }
-
- layerImage = PsdImage.createImageFromChannels(psd.colorMode, psd.depth,
- width, height, channels);
- }
-}
+import '../../image.dart';
+import '../../image_exception.dart';
+import '../../util/input_buffer.dart';
+import 'psd_blending_ranges.dart';
+import 'psd_channel.dart';
+import 'psd_image.dart';
+import 'psd_layer_data.dart';
+import 'psd_mask.dart';
+import 'effect/psd_bevel_effect.dart';
+import 'effect/psd_drop_shadow_effect.dart';
+import 'effect/psd_effect.dart';
+import 'effect/psd_inner_glow_effect.dart';
+import 'effect/psd_inner_shadow_effect.dart';
+import 'effect/psd_outer_glow_effect.dart';
+import 'effect/psd_solid_fill_effect.dart';
+import 'layer_data/psd_layer_additional_data.dart';
+import 'layer_data/psd_layer_section_divider.dart';
+
+class PsdLayer {
+ int top;
+ int left;
+ int bottom;
+ int right;
+ int width;
+ int height;
+ int blendMode;
+ int opacity;
+ int clipping;
+ int flags;
+ int compression;
+ String name;
+ List<PsdChannel> channels;
+ PsdMask mask;
+ PsdBlendingRanges blendingRanges;
+ Map<String, PsdLayerData> additionalData = {};
+ List<PsdLayer> children = [];
+ PsdLayer parent;
+ Image layerImage;
+ List<PsdEffect> effects = [];
+
+ static const int SIGNATURE = 0x3842494d; // '8BIM'
+
+ static const int BLEND_PASSTHROUGH = 0x70617373; // 'pass'
+ static const int BLEND_NORMAL = 0x6e6f726d; // 'norm'
+ static const int BLEND_DISSOLVE = 0x64697373; // 'diss'
+ static const int BLEND_DARKEN = 0x6461726b; // 'dark'
+ static const int BLEND_MULTIPLY = 0x6d756c20; // 'mul '
+ static const int BLEND_COLOR_BURN = 0x69646976; // 'idiv'
+ static const int BLEND_LINEAR_BURN = 0x6c62726e; // 'lbrn'
+ static const int BLEND_DARKEN_COLOR = 0x646b436c; // 'dkCl'
+ static const int BLEND_LIGHTEN = 0x6c697465; // 'lite'
+ static const int BLEND_SCREEN = 0x7363726e; // 'scrn'
+ static const int BLEND_COLOR_DODGE = 0x64697620; // 'div '
+ static const int BLEND_LINEAR_DODGE = 0x6c646467; // 'lddg'
+ static const int BLEND_LIGHTER_COLOR = 0x6c67436c; // 'lgCl'
+ static const int BLEND_OVERLAY = 0x6f766572; // 'over'
+ static const int BLEND_SOFT_LIGHT = 0x734c6974; // 'sLit'
+ static const int BLEND_HARD_LIGHT = 0x684c6974; // 'hLit'
+ static const int BLEND_VIVID_LIGHT = 0x764c6974; // 'vLit'
+ static const int BLEND_LINEAR_LIGHT = 0x6c4c6974; // lLit'
+ static const int BLEND_PIN_LIGHT = 0x704c6974; // 'pLit'
+ static const int BLEND_HARD_MIX = 0x684d6978; // 'hMix'
+ static const int BLEND_DIFFERENCE = 0x64696666; // 'diff'
+ static const int BLEND_EXCLUSION = 0x736d7564; // 'smud'
+ static const int BLEND_SUBTRACT = 0x66737562; // 'fsub'
+ static const int BLEND_DIVIDE = 0x66646976; // 'fdiv'
+ static const int BLEND_HUE = 0x68756520; // 'hue '
+ static const int BLEND_SATURATION = 0x73617420; // 'sat '
+ static const int BLEND_COLOR = 0x636f6c72; // 'colr'
+ static const int BLEND_LUMINOSITY = 0x6c756d20; // 'lum '
+
+ static const int FLAG_TRANSPARENCY_PROTECTED = 1;
+ static const int FLAG_HIDDEN = 2;
+ static const int FLAG_OBSOLETE = 4;
+ static const int FLAG_PHOTOSHOP_5 = 8;
+ static const int FLAG_PIXEL_DATA_IRRELEVANT_TO_APPEARANCE = 16;
+
+
+ PsdLayer([InputBuffer input]) {
+ if (input == null) {
+ return;
+ }
+
+ top = input.readInt32();
+ left = input.readInt32();
+ bottom = input.readInt32();
+ right = input.readInt32();
+ width = right - left;
+ height = bottom - top;
+
+ channels = [];
+ int numChannels = input.readUint16();
+ for (int i = 0; i < numChannels; ++i) {
+ int id = input.readInt16();
+ int len = input.readUint32();
+ channels.add(new PsdChannel(id, len));
+ }
+
+ int sig = input.readUint32();
+ if (sig != SIGNATURE) {
+ throw new ImageException('Invalid PSD layer signature: '
+ '${sig.toRadixString(16)}');
+ }
+
+ blendMode = input.readUint32();
+ opacity = input.readByte();
+ clipping = input.readByte();
+ flags = input.readByte();
+
+ int filler = input.readByte(); // should be 0
+ if (filler != 0) {
+ throw new ImageException('Invalid PSD layer data');
+ }
+
+ int len = input.readUint32();
+ InputBuffer extra = input.readBytes(len);
+
+ if (len > 0) {
+ // Mask Data
+ len = extra.readUint32();
+ assert(len == 0 || len == 20 || len == 36);
+ if (len > 0) {
+ InputBuffer maskData = extra.readBytes(len);
+ mask = PsdMask(maskData);
+ }
+
+ // Layer Blending Ranges
+ len = extra.readUint32();
+ if (len > 0) {
+ InputBuffer data = extra.readBytes(len);
+ blendingRanges = PsdBlendingRanges(data);
+ }
+
+ // Layer name
+ len = extra.readByte();
+ name = extra.readString(len);
+ // Layer name is padded to a multiple of 4 bytes.
+ int padding = (4 - (len % 4)) - 1;
+ if (padding > 0) {
+ extra.skip(padding);
+ }
+
+ // Additional layer sections
+ while (!extra.isEOS) {
+ int sig = extra.readUint32();
+ if (sig != SIGNATURE) {
+ throw new ImageException('PSD invalid signature for layer additional '
+ 'data: ${sig.toRadixString(16)}');
+ }
+
+ String tag = extra.readString(4);
+
+ len = extra.readUint32();
+ InputBuffer data = extra.readBytes(len);
+ // pad to an even byte count.
+ if (len & 1 == 1) {
+ extra.skip(1);
+ }
+
+ additionalData[tag] = PsdLayerData(tag, data);
+
+ // Layer effects data
+ if (tag == 'lrFX') {
+ var fxData = (additionalData['lrFX'] as PsdLayerAdditionalData);
+ var data = InputBuffer.from(fxData.data);
+ /*int version =*/ data.readUint16();
+ int numFx = data.readUint16();
+
+ for (int j = 0; j < numFx; ++j) {
+ /*var tag =*/ data.readString(4); // 8BIM
+ var fxTag = data.readString(4);
+ int size = data.readUint32();
+
+ if (fxTag == 'dsdw') {
+ var fx = PsdDropShadowEffect();
+ effects.add(fx);
+ fx.version = data.readUint32();
+ fx.blur = data.readUint32();
+ fx.intensity = data.readUint32();
+ fx.angle = data.readUint32();
+ fx.distance = data.readUint32();
+ fx.color = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ fx.blendMode = data.readString(8);
+ fx.enabled = data.readByte() != 0;
+ fx.globalAngle = data.readByte() != 0;
+ fx.opacity = data.readByte();
+ fx.nativeColor = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ } else if (fxTag == 'isdw') {
+ var fx = PsdInnerShadowEffect();
+ effects.add(fx);
+ fx.version = data.readUint32();
+ fx.blur = data.readUint32();
+ fx.intensity = data.readUint32();
+ fx.angle = data.readUint32();
+ fx.distance = data.readUint32();
+ fx.color = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ fx.blendMode = data.readString(8);
+ fx.enabled = data.readByte() != 0;
+ fx.globalAngle = data.readByte() != 0;
+ fx.opacity = data.readByte();
+ fx.nativeColor = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ } else if (fxTag == 'oglw') {
+ var fx = PsdOuterGlowEffect();
+ effects.add(fx);
+ fx.version = data.readUint32();
+ fx.blur = data.readUint32();
+ fx.intensity = data.readUint32();
+ fx.color = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ fx.blendMode = data.readString(8);
+ fx.enabled = data.readByte() != 0;
+ fx.opacity = data.readByte();
+ if (fx.version == 2) {
+ fx.nativeColor = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ }
+ } else if (fxTag == 'iglw') {
+ var fx = PsdInnerGlowEffect();
+ effects.add(fx);
+ fx.version = data.readUint32();
+ fx.blur = data.readUint32();
+ fx.intensity = data.readUint32();
+ fx.color = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ fx.blendMode = data.readString(8);
+ fx.enabled = data.readByte() != 0;
+ fx.opacity = data.readByte();
+ if (fx.version == 2) {
+ fx.invert = data.readByte() != 0;
+ fx.nativeColor = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ }
+ } else if (fxTag == 'bevl') {
+ var fx = PsdBevelEffect();
+ effects.add(fx);
+ fx.version = data.readUint32();
+ fx.angle = data.readUint32();
+ fx.strength = data.readUint32();
+ fx.blur = data.readUint32();
+ fx.highlightBlendMode = data.readString(8);
+ fx.shadowBlendMode = data.readString(8);
+ fx.highlightColor = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ fx.shadowColor = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ fx.bevelStyle = data.readByte();
+ fx.highlightOpacity = data.readByte();
+ fx.shadowOpacity = data.readByte();
+ fx.enabled = data.readByte() != 0;
+ fx.globalAngle = data.readByte() != 0;
+ fx.upOrDown = data.readByte();
+ if (fx.version == 2) {
+ fx.realHighlightColor = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ fx.realShadowColor = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ }
+ } else if (fxTag == 'sofi') {
+ var fx = PsdSolidFillEffect();
+ effects.add(fx);
+ fx.version = data.readUint32();
+ fx.blendMode = data.readString(4);
+ fx.color = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ fx.opacity = data.readByte();
+ fx.enabled = data.readByte() != 0;
+ fx.nativeColor = [data.readUint16(), data.readUint16(),
+ data.readUint16(), data.readUint16(),
+ data.readUint16()];
+ } else {
+ data.skip(size);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Is this layer visible?
+ */
+ bool isVisible() => flags & FLAG_HIDDEN == 0;
+
+ /**
+ * Is this layer a folder?
+ */
+ int type() {
+ if (additionalData.containsKey(PsdLayerSectionDivider.TAG)) {
+ PsdLayerSectionDivider section = additionalData[PsdLayerSectionDivider.TAG];
+ return section.type;
+ }
+ return PsdLayerSectionDivider.NORMAL;
+ }
+
+ /**
+ * Get the channel for the given [id].
+ * Returns null if the layer does not have the given channel.
+ */
+ PsdChannel getChannel(int id) {
+ for (int i = 0; i < channels.length; ++i) {
+ if (channels[i].id == id) {
+ return channels[i];
+ }
+ }
+ return null;
+ }
+
+ void readImageData(InputBuffer input, PsdImage psd) {
+ for (int i = 0; i < channels.length; ++i) {
+ channels[i].readPlane(input, width, height, psd.depth);
+ }
+
+ layerImage = PsdImage.createImageFromChannels(psd.colorMode, psd.depth,
+ width, height, channels);
+ }
+}
diff --git a/image/lib/src/formats/psd/psd_layer_data.dart b/image/lib/src/formats/psd/psd_layer_data.dart
old mode 100644
new mode 100755
index 7a9830d..09e2a38
--- a/image/lib/src/formats/psd/psd_layer_data.dart
+++ b/image/lib/src/formats/psd/psd_layer_data.dart
@@ -1,18 +1,18 @@
-import '../../util/input_buffer.dart';
-import 'layer_data/psd_layer_additional_data.dart';
-import 'layer_data/psd_layer_section_divider.dart';
-
-class PsdLayerData {
- String tag;
-
- factory PsdLayerData(String tag, InputBuffer data) {
- switch (tag) {
- case PsdLayerSectionDivider.TAG:
- return new PsdLayerSectionDivider(tag, data);
- default:
- return new PsdLayerAdditionalData(tag, data);
- }
- }
-
- PsdLayerData.type(this.tag);
-}
+import '../../util/input_buffer.dart';
+import 'layer_data/psd_layer_additional_data.dart';
+import 'layer_data/psd_layer_section_divider.dart';
+
+class PsdLayerData {
+ String tag;
+
+ factory PsdLayerData(String tag, InputBuffer data) {
+ switch (tag) {
+ case PsdLayerSectionDivider.TAG:
+ return new PsdLayerSectionDivider(tag, data);
+ default:
+ return new PsdLayerAdditionalData(tag, data);
+ }
+ }
+
+ PsdLayerData.type(this.tag);
+}
diff --git a/image/lib/src/formats/psd/psd_mask.dart b/image/lib/src/formats/psd/psd_mask.dart
old mode 100644
new mode 100755
index d63e34b..90233d5
--- a/image/lib/src/formats/psd/psd_mask.dart
+++ b/image/lib/src/formats/psd/psd_mask.dart
@@ -1,40 +1,40 @@
-import '../../util/input_buffer.dart';
-
-class PsdMask {
- int top;
- int left;
- int right;
- int bottom;
- int defaultColor;
- int flags;
- int params = 0;
-
- PsdMask(InputBuffer input) {
- int len = input.length;
-
- top = input.readUint32();
- left = input.readUint32();
- right = input.readUint32();
- bottom = input.readUint32();
- defaultColor = input.readByte();
- flags = input.readByte();
-
- if (len == 20) {
- input.skip(2);
- } else {
- flags = input.readByte();
-
- defaultColor = input.readByte();
- top = input.readUint32();
- left = input.readUint32();
- right = input.readUint32();
- bottom = input.readUint32();
- }
- }
-
- bool get relative => flags & 1 != 0;
-
- bool get disabled => flags & 2 != 0;
-
- bool get invert => flags & 4 != 0;
-}
+import '../../util/input_buffer.dart';
+
+class PsdMask {
+ int top;
+ int left;
+ int right;
+ int bottom;
+ int defaultColor;
+ int flags;
+ int params = 0;
+
+ PsdMask(InputBuffer input) {
+ int len = input.length;
+
+ top = input.readUint32();
+ left = input.readUint32();
+ right = input.readUint32();
+ bottom = input.readUint32();
+ defaultColor = input.readByte();
+ flags = input.readByte();
+
+ if (len == 20) {
+ input.skip(2);
+ } else {
+ flags = input.readByte();
+
+ defaultColor = input.readByte();
+ top = input.readUint32();
+ left = input.readUint32();
+ right = input.readUint32();
+ bottom = input.readUint32();
+ }
+ }
+
+ bool get relative => flags & 1 != 0;
+
+ bool get disabled => flags & 2 != 0;
+
+ bool get invert => flags & 4 != 0;
+}
diff --git a/image/lib/src/formats/psd_decoder.dart b/image/lib/src/formats/psd_decoder.dart
old mode 100644
new mode 100755
index 44d328c..1d9ef46
--- a/image/lib/src/formats/psd_decoder.dart
+++ b/image/lib/src/formats/psd_decoder.dart
@@ -1,95 +1,95 @@
-import '../animation.dart';
-import '../image.dart';
-import 'decoder.dart';
-import 'decode_info.dart';
-import 'psd/psd_image.dart';
-
-/**
- * Decode a Photoshop PSD image.
- */
-class PsdDecoder extends Decoder {
- PsdImage info;
-
- /**
- * A light-weight function to test if the given file is able to be decoded
- * by this Decoder.
- */
- bool isValidFile(List<int> bytes) {
- return new PsdImage(bytes).isValid;
- }
-
- /**
- * Decode a raw PSD image without rendering it to a flat image.
- */
- PsdImage decodePsd(List<int> bytes) {
- PsdImage psd = new PsdImage(bytes);
- if (!psd.decode()) {
- return null;
- }
- return psd;
- }
-
- /**
- * Decode the file and extract a single image from it. If the file is
- * animated, the specified [frame] will be decoded. If there was a problem
- * decoding the file, null is returned.
- */
- Image decodeImage(List<int> bytes, {int frame: 0}) {
- startDecode(bytes);
- return decodeFrame(frame);
- }
-
- /**
- * Decode all of the frames from an animation. If the file is not an
- * animation, a single frame animation is returned. If there was a problem
- * decoding the file, null is returned.
- */
- Animation decodeAnimation(List<int> bytes) {
- if (startDecode(bytes) == null) {
- return null;
- }
-
- Animation anim = new Animation();
- anim.width = info.width;
- anim.height = info.height;
- anim.frameType = Animation.PAGE;
- for (int i = 0, len = numFrames(); i < len; ++i) {
- Image image = decodeFrame(i);
- if (i == null) {
- continue;
- }
- anim.addFrame(image);
- }
-
- return anim;
- }
-
- /**
- * Start decoding the data as an animation sequence, but don't actually
- * process the frames until they are requested with decodeFrame.
- */
- DecodeInfo startDecode(List<int> bytes) {
- info = new PsdImage(bytes);
- return info;
- }
-
- /**
- * How many frames are available to be decoded. [startDecode] should have
- * been called first. Non animated image files will have a single frame.
- */
- int numFrames() => info != null ? info.numFrames : 0;
-
- /**
- * Decode a single frame from the data stat was set with [startDecode].
- * If [frame] is out of the range of available frames, null is returned.
- * Non animated image files will only have [frame] 0. An [AnimationFrame]
- * is returned, which provides the image, and top-left coordinates of the
- * image, as animated frames may only occupy a subset of the canvas.
- */
- Image decodeFrame(int frame) {
- if (info == null) {
- return null;
- }
- return info.decodeImage();
- }
-}
+import '../animation.dart';
+import '../image.dart';
+import 'decoder.dart';
+import 'decode_info.dart';
+import 'psd/psd_image.dart';
+
+/**
+ * Decode a Photoshop PSD image.
+ */
+class PsdDecoder extends Decoder {
+ PsdImage info;
+
+ /**
+ * A light-weight function to test if the given file is able to be decoded
+ * by this Decoder.
+ */
+ bool isValidFile(List<int> bytes) {
+ return new PsdImage(bytes).isValid;
+ }
+
+ /**
+ * Decode a raw PSD image without rendering it to a flat image.
+ */
+ PsdImage decodePsd(List<int> bytes) {
+ PsdImage psd = PsdImage(bytes);
+ if (!psd.decode()) {
+ return null;
+ }
+ return psd;
+ }
+
+ /**
+ * Decode the file and extract a single image from it. If the file is
+ * animated, the specified [frame] will be decoded. If there was a problem
+ * decoding the file, null is returned.
+ */
+ Image decodeImage(List<int> bytes, {int frame: 0}) {
+ startDecode(bytes);
+ return decodeFrame(frame);
+ }
+
+ /**
+ * Decode all of the frames from an animation. If the file is not an
+ * animation, a single frame animation is returned. If there was a problem
+ * decoding the file, null is returned.
+ */
+ Animation decodeAnimation(List<int> bytes) {
+ if (startDecode(bytes) == null) {
+ return null;
+ }
+
+ Animation anim = Animation();
+ anim.width = info.width;
+ anim.height = info.height;
+ anim.frameType = Animation.PAGE;
+ for (int i = 0, len = numFrames(); i < len; ++i) {
+ Image image = decodeFrame(i);
+ if (i == null) {
+ continue;
+ }
+ anim.addFrame(image);
+ }
+
+ return anim;
+ }
+
+ /**
+ * Start decoding the data as an animation sequence, but don't actually
+ * process the frames until they are requested with decodeFrame.
+ */
+ DecodeInfo startDecode(List<int> bytes) {
+ info = PsdImage(bytes);
+ return info;
+ }
+
+ /**
+ * How many frames are available to be decoded. [startDecode] should have
+ * been called first. Non animated image files will have a single frame.
+ */
+ int numFrames() => info != null ? info.numFrames : 0;
+
+ /**
+ * Decode a single frame from the data stat was set with [startDecode].
+ * If [frame] is out of the range of available frames, null is returned.
+ * Non animated image files will only have [frame] 0. An [AnimationFrame]
+ * is returned, which provides the image, and top-left coordinates of the
+ * image, as animated frames may only occupy a subset of the canvas.
+ */
+ Image decodeFrame(int frame) {
+ if (info == null) {
+ return null;
+ }
+ return info.decodeImage();
+ }
+}
diff --git a/image/lib/src/formats/pvrtc/pvrtc_bit_utility.dart b/image/lib/src/formats/pvrtc/pvrtc_bit_utility.dart
old mode 100644
new mode 100755
index 00d4ef9..86cb522
--- a/image/lib/src/formats/pvrtc/pvrtc_bit_utility.dart
+++ b/image/lib/src/formats/pvrtc/pvrtc_bit_utility.dart
@@ -1,187 +1,187 @@
-class BitUtility {
- static bool isPowerOf2(int x) => (x & (x - 1)) == 0;
-
- static int rotateRight(int value, int shift) =>
- (value >> shift) | (value << (32 - shift));
-
- static const List<int> BITSCALE_5_TO_8 = const [
- 0, 8, 16, 24, 32, 41, 49, 57, 65, 74,
- 82, 90, 98, 106, 115, 123, 131, 139, 148, 156,
- 164, 172, 180, 189, 197, 205, 213, 222, 230, 238,
- 246, 255];
-
- static const List<int> BITSCALE_4_TO_8 = const [
- 0, 17, 34, 51, 68, 85, 102, 119, 136, 153,
- 170, 187, 204, 221, 238, 255];
-
- static const List<int> BITSCALE_3_TO_8 = const [
- 0, 36, 72, 109, 145, 182, 218, 255];
-
- static const List<int> BITSCALE_8_TO_5_FLOOR = const [
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
- 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
- 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
- 3, 3, 3, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
- 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 9, 9, 9, 9, 9,
- 9, 9, 9, 10, 10, 10, 10, 10, 10, 10,
- 10, 11, 11, 11, 11, 11, 11, 11, 11, 12,
- 12, 12, 12, 12, 12, 12, 12, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 14, 14, 14, 14,
- 14, 14, 14, 14, 15, 15, 15, 15, 15, 15,
- 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
- 17, 17, 17, 17, 17, 17, 17, 17, 17, 18,
- 18, 18, 18, 18, 18, 18, 18, 19, 19, 19,
- 19, 19, 19, 19, 19, 20, 20, 20, 20, 20,
- 20, 20, 20, 21, 21, 21, 21, 21, 21, 21,
- 21, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 23, 23, 23, 23, 23, 23, 23, 23, 24, 24,
- 24, 24, 24, 24, 24, 24, 25, 25, 25, 25,
- 25, 25, 25, 25, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 27, 27, 27, 27, 27, 27, 27,
- 27, 28, 28, 28, 28, 28, 28, 28, 28, 29,
- 29, 29, 29, 29, 29, 29, 29, 30, 30, 30,
- 30, 30, 30, 30, 30, 31];
-
- static const List<int> BITSCALE_8_TO_4_FLOOR = const [
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
- 10, 10, 10, 10, 10, 10, 10, 11, 11, 11,
- 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 11, 11, 11, 12, 12, 12, 12, 12, 12,
- 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
- 12, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 15];
-
- static const List<int> BITSCALE_8_TO_3_FLOOR = const [
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 7];
-
- static const List<int> BITSCALE_8_TO_5_CEIL = const [
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 2,
- 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
- 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
- 4, 4, 4, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 8, 8,
- 8, 8, 8, 8, 8, 8, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
- 10, 10, 10, 11, 11, 11, 11, 11, 11, 11,
- 11, 12, 12, 12, 12, 12, 12, 12, 12, 13,
- 13, 13, 13, 13, 13, 13, 13, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 15, 15, 15, 15,
- 15, 15, 15, 15, 16, 16, 16, 16, 16, 16,
- 16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
- 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
- 20, 20, 20, 20, 20, 21, 21, 21, 21, 21,
- 21, 21, 21, 22, 22, 22, 22, 22, 22, 22,
- 22, 23, 23, 23, 23, 23, 23, 23, 23, 23,
- 24, 24, 24, 24, 24, 24, 24, 24, 25, 25,
- 25, 25, 25, 25, 25, 25, 26, 26, 26, 26,
- 26, 26, 26, 26, 27, 27, 27, 27, 27, 27,
- 27, 27, 27, 28, 28, 28, 28, 28, 28, 28,
- 28, 29, 29, 29, 29, 29, 29, 29, 29, 30,
- 30, 30, 30, 30, 30, 30, 30, 31, 31, 31,
- 31, 31, 31, 31, 31, 31];
-
- static const List<int> BITSCALE_8_TO_4_CEIL = const [
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 10, 10, 10, 10, 10, 10,
- 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
- 10, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 11, 11, 11, 11, 11, 11, 11, 12, 12,
- 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
- 12, 12, 12, 12, 12, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 15,
- 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15];
-
- static const List<int> BITSCALE_8_TO_3_CEIL = const [
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7];
-}
+class BitUtility {
+ static bool isPowerOf2(int x) => (x & (x - 1)) == 0;
+
+ static int rotateRight(int value, int shift) =>
+ (value >> shift) | (value << (32 - shift));
+
+ static const List<int> BITSCALE_5_TO_8 = const [
+ 0, 8, 16, 24, 32, 41, 49, 57, 65, 74,
+ 82, 90, 98, 106, 115, 123, 131, 139, 148, 156,
+ 164, 172, 180, 189, 197, 205, 213, 222, 230, 238,
+ 246, 255];
+
+ static const List<int> BITSCALE_4_TO_8 = const [
+ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153,
+ 170, 187, 204, 221, 238, 255];
+
+ static const List<int> BITSCALE_3_TO_8 = const [
+ 0, 36, 72, 109, 145, 182, 218, 255];
+
+ static const List<int> BITSCALE_8_TO_5_FLOOR = const [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
+ 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
+ 3, 3, 3, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
+ 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 9, 9, 9, 9, 9,
+ 9, 9, 9, 10, 10, 10, 10, 10, 10, 10,
+ 10, 11, 11, 11, 11, 11, 11, 11, 11, 12,
+ 12, 12, 12, 12, 12, 12, 12, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 14, 14, 14, 14,
+ 14, 14, 14, 14, 15, 15, 15, 15, 15, 15,
+ 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 18,
+ 18, 18, 18, 18, 18, 18, 18, 19, 19, 19,
+ 19, 19, 19, 19, 19, 20, 20, 20, 20, 20,
+ 20, 20, 20, 21, 21, 21, 21, 21, 21, 21,
+ 21, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 23, 23, 23, 23, 23, 23, 23, 23, 24, 24,
+ 24, 24, 24, 24, 24, 24, 25, 25, 25, 25,
+ 25, 25, 25, 25, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 27, 27, 27, 27, 27, 27, 27,
+ 27, 28, 28, 28, 28, 28, 28, 28, 28, 29,
+ 29, 29, 29, 29, 29, 29, 29, 30, 30, 30,
+ 30, 30, 30, 30, 30, 31];
+
+ static const List<int> BITSCALE_8_TO_4_FLOOR = const [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 15];
+
+ static const List<int> BITSCALE_8_TO_3_FLOOR = const [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 7];
+
+ static const List<int> BITSCALE_8_TO_5_CEIL = const [
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
+ 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+ 4, 4, 4, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 8, 8,
+ 8, 8, 8, 8, 8, 8, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
+ 10, 10, 10, 11, 11, 11, 11, 11, 11, 11,
+ 11, 12, 12, 12, 12, 12, 12, 12, 12, 13,
+ 13, 13, 13, 13, 13, 13, 13, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 15, 15, 15, 15,
+ 15, 15, 15, 15, 16, 16, 16, 16, 16, 16,
+ 16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
+ 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
+ 20, 20, 20, 20, 20, 21, 21, 21, 21, 21,
+ 21, 21, 21, 22, 22, 22, 22, 22, 22, 22,
+ 22, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 24, 24, 24, 24, 24, 24, 24, 24, 25, 25,
+ 25, 25, 25, 25, 25, 25, 26, 26, 26, 26,
+ 26, 26, 26, 26, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 28, 28, 28, 28, 28, 28, 28,
+ 28, 29, 29, 29, 29, 29, 29, 29, 29, 30,
+ 30, 30, 30, 30, 30, 30, 30, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31];
+
+ static const List<int> BITSCALE_8_TO_4_CEIL = const [
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15];
+
+ static const List<int> BITSCALE_8_TO_3_CEIL = const [
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7];
+}
diff --git a/image/lib/src/formats/pvrtc/pvrtc_color.dart b/image/lib/src/formats/pvrtc/pvrtc_color.dart
old mode 100644
new mode 100755
index bfb771b..3060cba
--- a/image/lib/src/formats/pvrtc/pvrtc_color.dart
+++ b/image/lib/src/formats/pvrtc/pvrtc_color.dart
@@ -1,107 +1,107 @@
-class PvrtcColorRgb {
- int r;
- int g;
- int b;
-
- PvrtcColorRgb([this.r = 0, this.g = 0, this.b = 0]);
-
- PvrtcColorRgb.from(PvrtcColorRgb other)
- : r = other.r
- , g = other.g
- , b = other.b;
-
- PvrtcColorRgb copy() => new PvrtcColorRgb.from(this);
-
- PvrtcColorRgb operator *(int x) =>
- new PvrtcColorRgb(r * x, g * x, b * x);
-
- PvrtcColorRgb operator +(PvrtcColorRgb x) =>
- new PvrtcColorRgb(r + x.r, g + x.g, b + x.b);
-
- PvrtcColorRgb operator -(PvrtcColorRgb x) =>
- new PvrtcColorRgb(r - x.r, g - x.g, b - x.b);
-
- int dotProd(PvrtcColorRgb x) => r * x.r + g * x.g + b * x.b;
-
- void setMin(PvrtcColorRgb c) {
- if (c.r < r) {
- r = c.r;
- }
- if (c.g < g) {
- g = c.g;
- }
- if (c.b < b) {
- b = c.b;
- }
- }
-
- void setMax(PvrtcColorRgb c) {
- if (c.r > r) {
- r = c.r;
- }
- if (c.g > g) {
- g = c.g;
- }
- if (c.b > b) {
- b = c.b;
- }
- }
-}
-
-class PvrtcColorRgba {
- int r;
- int g;
- int b;
- int a;
-
- PvrtcColorRgba([this.r = 0, this.g = 0, this.b = 0, this.a = 0]);
-
- PvrtcColorRgba.from(PvrtcColorRgba other)
- : r = other.r
- , g = other.g
- , b = other.b
- , a = other.a;
-
- PvrtcColorRgba copy() => new PvrtcColorRgba.from(this);
-
- PvrtcColorRgba operator *(int x) =>
- new PvrtcColorRgba(r * x, g * x, b * x, a * x);
-
- PvrtcColorRgba operator +(PvrtcColorRgba x) =>
- new PvrtcColorRgba(r + x.r, g + x.g, b + x.b, a + x.a);
-
- PvrtcColorRgba operator -(PvrtcColorRgba x) =>
- new PvrtcColorRgba(r - x.r, g - x.g, b - x.b, a - x.a);
-
- int dotProd(PvrtcColorRgba x) => r * x.r + g * x.g + b * x.b + a * x.a;
-
- void setMin(PvrtcColorRgba c) {
- if (c.r < r) {
- r = c.r;
- }
- if (c.g < g) {
- g = c.g;
- }
- if (c.b < b) {
- b = c.b;
- }
- if (c.a < a) {
- a = c.a;
- }
- }
-
- void setMax(PvrtcColorRgba c) {
- if (c.r > r) {
- r = c.r;
- }
- if (c.g > g) {
- g = c.g;
- }
- if (c.b > b) {
- b = c.b;
- }
- if (c.a > a) {
- a = c.a;
- }
- }
-}
+class PvrtcColorRgb {
+ int r;
+ int g;
+ int b;
+
+ PvrtcColorRgb([this.r = 0, this.g = 0, this.b = 0]);
+
+ PvrtcColorRgb.from(PvrtcColorRgb other)
+ : r = other.r
+ , g = other.g
+ , b = other.b;
+
+ PvrtcColorRgb copy() => new PvrtcColorRgb.from(this);
+
+ PvrtcColorRgb operator *(int x) =>
+ new PvrtcColorRgb(r * x, g * x, b * x);
+
+ PvrtcColorRgb operator +(PvrtcColorRgb x) =>
+ new PvrtcColorRgb(r + x.r, g + x.g, b + x.b);
+
+ PvrtcColorRgb operator -(PvrtcColorRgb x) =>
+ new PvrtcColorRgb(r - x.r, g - x.g, b - x.b);
+
+ int dotProd(PvrtcColorRgb x) => r * x.r + g * x.g + b * x.b;
+
+ void setMin(PvrtcColorRgb c) {
+ if (c.r < r) {
+ r = c.r;
+ }
+ if (c.g < g) {
+ g = c.g;
+ }
+ if (c.b < b) {
+ b = c.b;
+ }
+ }
+
+ void setMax(PvrtcColorRgb c) {
+ if (c.r > r) {
+ r = c.r;
+ }
+ if (c.g > g) {
+ g = c.g;
+ }
+ if (c.b > b) {
+ b = c.b;
+ }
+ }
+}
+
+class PvrtcColorRgba {
+ int r;
+ int g;
+ int b;
+ int a;
+
+ PvrtcColorRgba([this.r = 0, this.g = 0, this.b = 0, this.a = 0]);
+
+ PvrtcColorRgba.from(PvrtcColorRgba other)
+ : r = other.r
+ , g = other.g
+ , b = other.b
+ , a = other.a;
+
+ PvrtcColorRgba copy() => new PvrtcColorRgba.from(this);
+
+ PvrtcColorRgba operator *(int x) =>
+ new PvrtcColorRgba(r * x, g * x, b * x, a * x);
+
+ PvrtcColorRgba operator +(PvrtcColorRgba x) =>
+ new PvrtcColorRgba(r + x.r, g + x.g, b + x.b, a + x.a);
+
+ PvrtcColorRgba operator -(PvrtcColorRgba x) =>
+ new PvrtcColorRgba(r - x.r, g - x.g, b - x.b, a - x.a);
+
+ int dotProd(PvrtcColorRgba x) => r * x.r + g * x.g + b * x.b + a * x.a;
+
+ void setMin(PvrtcColorRgba c) {
+ if (c.r < r) {
+ r = c.r;
+ }
+ if (c.g < g) {
+ g = c.g;
+ }
+ if (c.b < b) {
+ b = c.b;
+ }
+ if (c.a < a) {
+ a = c.a;
+ }
+ }
+
+ void setMax(PvrtcColorRgba c) {
+ if (c.r > r) {
+ r = c.r;
+ }
+ if (c.g > g) {
+ g = c.g;
+ }
+ if (c.b > b) {
+ b = c.b;
+ }
+ if (c.a > a) {
+ a = c.a;
+ }
+ }
+}
diff --git a/image/lib/src/formats/pvrtc/pvrtc_color_bounding_box.dart b/image/lib/src/formats/pvrtc/pvrtc_color_bounding_box.dart
old mode 100644
new mode 100755
index 3af0fad..036a55c
--- a/image/lib/src/formats/pvrtc/pvrtc_color_bounding_box.dart
+++ b/image/lib/src/formats/pvrtc/pvrtc_color_bounding_box.dart
@@ -1,13 +1,13 @@
-class PvrtcColorBoundingBox {
- var min;
- var max;
-
- PvrtcColorBoundingBox(min, max)
- : this.min = min.copy()
- , this.max = max.copy();
-
- void add(c) {
- min.setMin(c);
- max.setMax(c);
- }
-}
+class PvrtcColorBoundingBox {
+ var min;
+ var max;
+
+ PvrtcColorBoundingBox(min, max)
+ : this.min = min.copy()
+ , this.max = max.copy();
+
+ void add(c) {
+ min.setMin(c);
+ max.setMax(c);
+ }
+}
diff --git a/image/lib/src/formats/pvrtc/pvrtc_decoder.dart b/image/lib/src/formats/pvrtc/pvrtc_decoder.dart
old mode 100644
new mode 100755
index 968b7b7..1355e0d
--- a/image/lib/src/formats/pvrtc/pvrtc_decoder.dart
+++ b/image/lib/src/formats/pvrtc/pvrtc_decoder.dart
@@ -1,595 +1,595 @@
-import 'dart:typed_data';
-
-import '../../color.dart';
-import '../../image.dart';
-import '../../util/input_buffer.dart';
-import 'pvrtc_color.dart';
-import 'pvrtc_packet.dart';
-
-/**
- * Ported from Jeffrey Lim's PVRTC encoder/decoder,
- * https://bitbucket.org/jthlim/pvrtccompressor
- */
-class PvrtcDecoder {
- Image decodePvr(List<int> data) {
- // Use a heuristic to detect potential apple PVRTC formats
- if (_countBits(data.length) == 1) {
- // very likely to be apple PVRTC
- var image = decodeApplePVRTC(data);
- if (image != null) {
- return image;
- }
- }
-
- var input = new InputBuffer(data, bigEndian: false);
- var magic = input.readUint32();
- if (magic == 0x03525650) {
- return decodePVR3(data);
- }
-
- return decodePVR2(data);
- }
-
- Image decodeApplePVRTC(List<int> data) {
- // additional heuristic
- const int HEADER_SIZE = 52;
- if (data.length > HEADER_SIZE) {
- InputBuffer input = new InputBuffer(data, bigEndian: false);
- // Header
- int size = input.readUint32();
- if (size == HEADER_SIZE) {
- return null;
- }
- /*int height =*/ input.readUint32();
- /*int width =*/ input.readUint32();
- /*int mipcount =*/ input.readUint32();
- /*int flags =*/ input.readUint32();
- /*int texdatasize =*/ input.readUint32();
- /*int bpp =*/ input.readUint32();
- /*int rmask =*/ input.readUint32();
- /*int gmask =*/ input.readUint32();
- /*int bmask =*/ input.readUint32();
- int magic = input.readUint32();
- if (magic == 0x21525650) {
- // this looks more like a PowerVR file.
- return null;
- }
- }
-
- //const int PVRTC2 = 1;
- //const int PVRTC4 = 2;
-
- int mode = 1;
- int res = 8;
- int size = data.length;
- //int format = 0;
-
- // this is a tough one, could be 2bpp 8x8, 4bpp 8x8
- if (size == 32) {
- // assume 4bpp, 8x8
- mode = 0;
- res = 8;
- } else {
- // Detect if it's 2bpp or 4bpp
- int shift = 0;
- int test2bpp = 0x40; // 16x16
- int test4bpp = 0x80; // 16x16
-
- while (shift < 10) {
- int s2 = shift << 1;
-
- if ((test2bpp << s2) & size != 0) {
- res = 16 << shift;
- mode = 1;
- //format = PVRTC2;
- break;
- }
-
- if ((test4bpp << s2) & size != 0) {
- res = 16 << shift;
- mode = 0;
- //format = PVRTC4;
- break;
- }
-
- ++shift;
- }
-
- if (shift == 10) {
- // no mode could be found.
- return null;
- }
- }
-
- // there is no reliable way to know if it's a 2bpp or 4bpp file. Assuming
- int width = res;
- int height = res;
- int bpp = (mode + 1) * 2;
- //int numMips = 0;
-
- if (bpp == 4) {
- // 2bpp is currently unsupported
- return null;
- }
-
- return decodeRgba4bpp(width, height, data as TypedData);
- }
-
- Image decodePVR2(List<int> data) {
- int length = data.length;
-
- const int HEADER_SIZE = 52;
- const int PVRTEX_CUBEMAP = (1 << 12);
- const int PVR_PIXELTYPE_MASK = 0xff;
- const int PVR_TYPE_RGBA4444 = 0x10;
- const int PVR_TYPE_RGBA5551 = 0x11;
- const int PVR_TYPE_RGBA8888 = 0x12;
- const int PVR_TYPE_RGB565 = 0x13;
- const int PVR_TYPE_RGB555 = 0x14;
- const int PVR_TYPE_RGB888 = 0x15;
- const int PVR_TYPE_I8 = 0x16;
- const int PVR_TYPE_AI8 = 0x17;
- const int PVR_TYPE_PVRTC2 = 0x18;
- const int PVR_TYPE_PVRTC4 = 0x19;
-
- if (length < HEADER_SIZE) {
- return null;
- }
-
- InputBuffer input = new InputBuffer(data, bigEndian: false);
- // Header
- int size = input.readUint32();
- int height = input.readUint32();
- int width = input.readUint32();
- /*int mipcount =*/ input.readUint32();
- int flags = input.readUint32();
- /*int texdatasize =*/ input.readUint32();
- int bpp = input.readUint32();
- /*int rmask =*/ input.readUint32();
- /*int gmask =*/ input.readUint32();
- /*int bmask =*/ input.readUint32();
- int amask = input.readUint32();
- int magic = input.readUint32();
- int numtex = input.readUint32();
-
- if (size != HEADER_SIZE || magic != 0x21525650) {
- return null;
- }
-
- if (numtex < 1) {
- numtex = (flags & PVRTEX_CUBEMAP) != 0 ? 6 : 1;
- }
-
- if (numtex != 1) {
- // only 1 surface supported currently
- return null;
- }
-
- if (width * height * bpp / 8 > length - HEADER_SIZE) {
- return null;
- }
-
- int ptype = flags & PVR_PIXELTYPE_MASK;
-
- switch (ptype) {
- case PVR_TYPE_RGBA4444:
- Image image = new Image(width, height);
- Uint8List out = image.getBytes();
- int oi = 0;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- int v1 = input.readByte();
- int v2 = input.readByte();
- int a = (v1 & 0x0f) << 4;
- int b = (v1 & 0xf0);
- int g = (v2 & 0x0f) << 4;
- int r = (v2 & 0xf0);
-
- out[oi++] = r;
- out[oi++] = g;
- out[oi++] = b;
- out[oi++] = a;
- }
- }
- return image;
- case PVR_TYPE_RGBA5551:
- Image image = new Image(width, height);
- Uint8List out = image.getBytes();
- int oi = 0;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- int v = input.readUint16();
-
- int r = (v & 0xf800) >> 8;
- int g = (v & 0x07c0) >> 3;
- int b = (v & 0x003e) << 2;
- int a = (v & 0x0001) != 0 ? 255 : 0;
-
- out[oi++] = r;
- out[oi++] = g;
- out[oi++] = b;
- out[oi++] = a;
- }
- }
- return image;
- case PVR_TYPE_RGBA8888:
- Image image = new Image(width, height);
- Uint8List out = image.getBytes();
- int oi = 0;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- out[oi++] = input.readByte();
- out[oi++] = input.readByte();
- out[oi++] = input.readByte();
- out[oi++] = input.readByte();
- }
- }
- return image;
- case PVR_TYPE_RGB565:
- Image image = new Image(width, height);
- Uint8List out = image.getBytes();
- int oi = 0;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- int v = input.readUint16();
- int b = (v & 0x001f) << 3;
- int g = (v & 0x07e0) >> 3;
- int r = (v & 0xf800) >> 8;
- int a = 255;
- out[oi++] = r;
- out[oi++] = g;
- out[oi++] = b;
- out[oi++] = a;
- }
- }
- return image;
- case PVR_TYPE_RGB555:
- Image image = new Image(width, height);
- Uint8List out = image.getBytes();
- int oi = 0;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- int v = input.readUint16();
- int r = (v & 0x001f) << 3;
- int g = (v & 0x03e0) >> 2;
- int b = (v & 0x7c00) >> 7;
- int a = 255;
- out[oi++] = r;
- out[oi++] = g;
- out[oi++] = b;
- out[oi++] = a;
- }
- }
- return image;
- case PVR_TYPE_RGB888:
- Image image = new Image(width, height);
- Uint8List out = image.getBytes();
- int oi = 0;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- out[oi++] = input.readByte();
- out[oi++] = input.readByte();
- out[oi++] = input.readByte();
- out[oi++] = 255;
- }
- }
- return image;
- case PVR_TYPE_I8:
- Image image = new Image(width, height);
- Uint8List out = image.getBytes();
- int oi = 0;
- for (int y = 0; y < height; ++y) {
- for(int x = 0; x < width; ++x) {
- int i = input.readByte();
- out[oi++] = i;
- out[oi++] = i;
- out[oi++] = i;
- out[oi++] = 255;
- }
- }
- return image;
- case PVR_TYPE_AI8:
- Image image = new Image(width, height);
- Uint8List out = image.getBytes();
- int oi = 0;
- for (int y = 0; y < height; ++y) {
- for(int x = 0; x < width; ++x) {
- int i = input.readByte();
- int a = input.readByte();
- out[oi++] = i;
- out[oi++] = i;
- out[oi++] = i;
- out[oi++] = a;
- }
- }
- return image;
- case PVR_TYPE_PVRTC2:
- // Currently unsupported
- return null;
- case PVR_TYPE_PVRTC4:
- return amask == 0 ? decodeRgb4bpp(width, height, input.toUint8List()) :
- decodeRgba4bpp(width, height, input.toUint8List());
- }
-
- // Unknown format
- return null;
- }
-
- Image decodePVR3(List<int> data) {
- //const int PVR3_PVRTC_2BPP_RGB = 0;
- //const int PVR3_PVRTC_2BPP_RGBA = 1;
- const int PVR3_PVRTC_4BPP_RGB = 2;
- const int PVR3_PVRTC_4BPP_RGBA = 3;
- /*const int PVR3_PVRTC2_2BPP = 4;
- const int PVR3_PVRTC2_4BPP = 5;
- const int PVR3_ETC1 = 6;
- const int PVR3_DXT1 = 7;
- const int PVR3_DXT2 = 8;
- const int PVR3_DXT3 = 9;
- const int PVR3_DXT4 = 10;
- const int PVR3_DXT5 = 11;
- const int PVR3_BC1 = 7;
- const int PVR3_BC2 = 9;
- const int PVR3_BC3 = 11;
- const int PVR3_BC4 = 12;
- const int PVR3_BC5 = 13;
- const int PVR3_BC6 = 14;
- const int PVR3_BC7 = 15;
- const int PVR3_UYVY = 16;
- const int PVR3_YUY2 = 17;
- const int PVR3_BW_1BPP = 18;
- const int PVR3_R9G9B9E5 = 19;
- const int PVR3_RGBG8888 = 20;
- const int PVR3_GRGB8888 = 21;
- const int PVR3_ETC2_RGB = 22;
- const int PVR3_ETC2_RGBA = 23;
- const int PVR3_ETC2_RGB_A1 = 24;
- const int PVR3_EAC_R11_U = 25;
- const int PVR3_EAC_R11_S = 26;
- const int PVR3_EAC_RG11_U = 27;
- const int PVR3_EAC_RG11_S = 28;*/
-
- InputBuffer input = new InputBuffer(data);
-
- // Header
- int version = input.readUint32();
- if (version != 0x03525650) {
- return null;
- }
-
- /*int flags =*/ input.readUint32();
- var format = input.readUint32();
- var order = [input.readByte(), input.readByte(),
- input.readByte(), input.readByte()];
- /*int colorspace =*/ input.readUint32();
- /*int channeltype =*/ input.readUint32();
- int height = input.readUint32();
- int width = input.readUint32();
- /*int depth =*/ input.readUint32();
- /*int num_surfaces =*/ input.readUint32();
- /*int num_faces =*/ input.readUint32();
- /*int mipcount =*/ input.readUint32();
- int metadata_size = input.readUint32();
-
- input.skip(metadata_size);
-
- if (order[0] == 0) {
- switch (format) {
- case PVR3_PVRTC_4BPP_RGB:
- return decodeRgb4bpp(width, height, input.toUint8List());
- case PVR3_PVRTC_4BPP_RGBA:
- return decodeRgba4bpp(width, height, input.toUint8List());
- /*case PVR3_PVRTC_2BPP_RGB:
- return null;
- case PVR3_PVRTC_2BPP_RGBA:
- return null;
- case PVR3_PVRTC2_2BPP:
- return null;
- case PVR3_PVRTC2_4BPP:
- return null;
- case PVR3_ETC1:
- return null;
- case PVR3_DXT1:
- return null;
- case PVR3_DXT2:
- return null;
- case PVR3_DXT3:
- return null;
- case PVR3_DXT4:
- return null;
- case PVR3_DXT5:
- return null;
- case PVR3_BC1:
- return null;
- case PVR3_BC2:
- return null;
- case PVR3_BC3:
- return null;
- case PVR3_BC4:
- return null;
- case PVR3_BC5:
- return null;
- case PVR3_BC6:
- return null;
- case PVR3_BC7:
- return null;
- case PVR3_UYVY:
- return null;
- case PVR3_YUY2:
- return null;
- case PVR3_BW_1BPP:
- return null;
- case PVR3_R9G9B9E5:
- return null;
- case PVR3_RGBG8888:
- return null;
- case PVR3_GRGB8888:
- return null;
- case PVR3_ETC2_RGB:
- return null;
- case PVR3_ETC2_RGBA:
- return null;
- case PVR3_ETC2_RGB_A1:
- return null;
- case PVR3_EAC_R11_U:
- return null;
- case PVR3_EAC_R11_S:
- return null;
- case PVR3_EAC_RG11_U:
- return null;
- case PVR3_EAC_RG11_S:
- return null;*/
- }
- }
-
- return null;
- }
-
- int _countBits(int x) {
- x = (x - ((x >> 1) & 0x55555555)) & 0xffffffff;
- x = ((x & 0x33333333) + ((x >> 2) & 0x33333333)) & 0xffffffff;
- x = (x + (x >> 4)) & 0xffffffff;
- x &= 0xf0f0f0f;
- x = ((x * 0x01010101) & 0xffffffff) >> 24;
- return x;
- }
-
- Image decodeRgb4bpp(int width, int height, TypedData data) {
- var result = new Image(width, height, Image.RGB);
-
- final int blocks = width ~/ 4;
- final int blockMask = blocks - 1;
-
- final packet = new PvrtcPacket(data);
- final p0 = new PvrtcPacket(data);
- final p1 = new PvrtcPacket(data);
- final p2 = new PvrtcPacket(data);
- final p3 = new PvrtcPacket(data);
- final c = new PvrtcColorRgb();
- const factors = PvrtcPacket.BILINEAR_FACTORS;
- const weights = PvrtcPacket.WEIGHTS;
-
- for (int y = 0; y < blocks; ++y) {
- for (int x = 0; x < blocks; ++x) {
- packet.setBlock(x, y);
-
- int mod = packet.modulationData;
- int weightIndex = 4 * packet.usePunchthroughAlpha;
- int factorIndex = 0;
-
- for (int py = 0; py < 4; ++py) {
- int yOffset = (py < 2) ? -1 : 0;
- int y0 = (y + yOffset) & blockMask;
- int y1 = (y0 + 1) & blockMask;
- int pyi = (py + y * 4) * width;
-
- for (int px = 0; px < 4; ++px) {
- int xOffset = (px < 2) ? -1 : 0;
- int x0 = (x + xOffset) & blockMask;
- int x1 = (x0 + 1) & blockMask;
-
- p0.setBlock(x0, y0);
- p1.setBlock(x1, y0);
- p2.setBlock(x0, y1);
- p3.setBlock(x1, y1);
-
- var ca = p0.getColorRgbA() * factors[factorIndex][0] +
- p1.getColorRgbA() * factors[factorIndex][1] +
- p2.getColorRgbA() * factors[factorIndex][2] +
- p3.getColorRgbA() * factors[factorIndex][3];
-
- var cb = p0.getColorRgbB() * factors[factorIndex][0] +
- p1.getColorRgbB() * factors[factorIndex][1] +
- p2.getColorRgbB() * factors[factorIndex][2] +
- p3.getColorRgbB() * factors[factorIndex][3];
-
- var w = weights[weightIndex + mod & 3];
-
- c.r = (ca.r * w[0] + cb.r * w[1]) >> 7;
- c.g = (ca.g * w[0] + cb.g * w[1]) >> 7;
- c.b = (ca.b * w[0] + cb.b * w[1]) >> 7;
-
- int pi = (pyi + (px + x * 4));
-
- result[pi] = getColor(c.r, c.g, c.b, 255);
-
- mod >>= 2;
- factorIndex++;
- }
- }
- }
- }
-
- return result;
- }
-
- Image decodeRgba4bpp(int width, int height, TypedData data) {
- var result = new Image(width, height, Image.RGBA);
-
- final int blocks = width ~/ 4;
- final int blockMask = blocks - 1;
-
- final packet = new PvrtcPacket(data);
- final p0 = new PvrtcPacket(data);
- final p1 = new PvrtcPacket(data);
- final p2 = new PvrtcPacket(data);
- final p3 = new PvrtcPacket(data);
- final c = new PvrtcColorRgba();
- const factors = PvrtcPacket.BILINEAR_FACTORS;
- const weights = PvrtcPacket.WEIGHTS;
-
- for (int y = 0; y < blocks; ++y) {
- for (int x = 0; x < blocks; ++x) {
- packet.setBlock(x, y);
-
- int mod = packet.modulationData;
- int weightIndex = 4 * packet.usePunchthroughAlpha;
- int factorIndex = 0;
-
- for (int py = 0; py < 4; ++py) {
- int yOffset = (py < 2) ? -1 : 0;
- int y0 = (y + yOffset) & blockMask;
- int y1 = (y0 + 1) & blockMask;
- int pyi = (py + y * 4) * width;
-
- for (int px = 0; px < 4; ++px) {
- int xOffset = (px < 2) ? -1 : 0;
- int x0 = (x + xOffset) & blockMask;
- int x1 = (x0 + 1) & blockMask;
-
- p0.setBlock(x0, y0);
- p1.setBlock(x1, y0);
- p2.setBlock(x0, y1);
- p3.setBlock(x1, y1);
-
- var ca = p0.getColorRgbaA() * factors[factorIndex][0] +
- p1.getColorRgbaA() * factors[factorIndex][1] +
- p2.getColorRgbaA() * factors[factorIndex][2] +
- p3.getColorRgbaA() * factors[factorIndex][3];
-
- var cb = p0.getColorRgbaB() * factors[factorIndex][0] +
- p1.getColorRgbaB() * factors[factorIndex][1] +
- p2.getColorRgbaB() * factors[factorIndex][2] +
- p3.getColorRgbaB() * factors[factorIndex][3];
-
- var w = weights[weightIndex + mod & 3];
-
- c.r = (ca.r * w[0] + cb.r * w[1]) >> 7;
- c.g = (ca.g * w[0] + cb.g * w[1]) >> 7;
- c.b = (ca.b * w[0] + cb.b * w[1]) >> 7;
- c.a = (ca.a * w[2] + cb.a * w[3]) >> 7;
-
- int pi = (pyi + (px + x * 4));
-
- result[pi] = getColor(c.r, c.g, c.b, c.a);
-
- mod >>= 2;
- factorIndex++;
- }
- }
- }
- }
-
- return result;
- }
-}
+import 'dart:typed_data';
+
+import '../../color.dart';
+import '../../image.dart';
+import '../../util/input_buffer.dart';
+import 'pvrtc_color.dart';
+import 'pvrtc_packet.dart';
+
+/**
+ * Ported from Jeffrey Lim's PVRTC encoder/decoder,
+ * https://bitbucket.org/jthlim/pvrtccompressor
+ */
+class PvrtcDecoder {
+ Image decodePvr(List<int> data) {
+ // Use a heuristic to detect potential apple PVRTC formats
+ if (_countBits(data.length) == 1) {
+ // very likely to be apple PVRTC
+ var image = decodeApplePVRTC(data);
+ if (image != null) {
+ return image;
+ }
+ }
+
+ var input = InputBuffer(data, bigEndian: false);
+ var magic = input.readUint32();
+ if (magic == 0x03525650) {
+ return decodePVR3(data);
+ }
+
+ return decodePVR2(data);
+ }
+
+ Image decodeApplePVRTC(List<int> data) {
+ // additional heuristic
+ const int HEADER_SIZE = 52;
+ if (data.length > HEADER_SIZE) {
+ InputBuffer input = InputBuffer(data, bigEndian: false);
+ // Header
+ int size = input.readUint32();
+ if (size == HEADER_SIZE) {
+ return null;
+ }
+ /*int height =*/ input.readUint32();
+ /*int width =*/ input.readUint32();
+ /*int mipcount =*/ input.readUint32();
+ /*int flags =*/ input.readUint32();
+ /*int texdatasize =*/ input.readUint32();
+ /*int bpp =*/ input.readUint32();
+ /*int rmask =*/ input.readUint32();
+ /*int gmask =*/ input.readUint32();
+ /*int bmask =*/ input.readUint32();
+ int magic = input.readUint32();
+ if (magic == 0x21525650) {
+ // this looks more like a PowerVR file.
+ return null;
+ }
+ }
+
+ //const int PVRTC2 = 1;
+ //const int PVRTC4 = 2;
+
+ int mode = 1;
+ int res = 8;
+ int size = data.length;
+ //int format = 0;
+
+ // this is a tough one, could be 2bpp 8x8, 4bpp 8x8
+ if (size == 32) {
+ // assume 4bpp, 8x8
+ mode = 0;
+ res = 8;
+ } else {
+ // Detect if it's 2bpp or 4bpp
+ int shift = 0;
+ int test2bpp = 0x40; // 16x16
+ int test4bpp = 0x80; // 16x16
+
+ while (shift < 10) {
+ int s2 = shift << 1;
+
+ if ((test2bpp << s2) & size != 0) {
+ res = 16 << shift;
+ mode = 1;
+ //format = PVRTC2;
+ break;
+ }
+
+ if ((test4bpp << s2) & size != 0) {
+ res = 16 << shift;
+ mode = 0;
+ //format = PVRTC4;
+ break;
+ }
+
+ ++shift;
+ }
+
+ if (shift == 10) {
+ // no mode could be found.
+ return null;
+ }
+ }
+
+ // there is no reliable way to know if it's a 2bpp or 4bpp file. Assuming
+ int width = res;
+ int height = res;
+ int bpp = (mode + 1) * 2;
+ //int numMips = 0;
+
+ if (bpp == 4) {
+ // 2bpp is currently unsupported
+ return null;
+ }
+
+ return decodeRgba4bpp(width, height, data as TypedData);
+ }
+
+ Image decodePVR2(List<int> data) {
+ int length = data.length;
+
+ const int HEADER_SIZE = 52;
+ const int PVRTEX_CUBEMAP = (1 << 12);
+ const int PVR_PIXELTYPE_MASK = 0xff;
+ const int PVR_TYPE_RGBA4444 = 0x10;
+ const int PVR_TYPE_RGBA5551 = 0x11;
+ const int PVR_TYPE_RGBA8888 = 0x12;
+ const int PVR_TYPE_RGB565 = 0x13;
+ const int PVR_TYPE_RGB555 = 0x14;
+ const int PVR_TYPE_RGB888 = 0x15;
+ const int PVR_TYPE_I8 = 0x16;
+ const int PVR_TYPE_AI8 = 0x17;
+ const int PVR_TYPE_PVRTC2 = 0x18;
+ const int PVR_TYPE_PVRTC4 = 0x19;
+
+ if (length < HEADER_SIZE) {
+ return null;
+ }
+
+ InputBuffer input = InputBuffer(data, bigEndian: false);
+ // Header
+ int size = input.readUint32();
+ int height = input.readUint32();
+ int width = input.readUint32();
+ /*int mipcount =*/ input.readUint32();
+ int flags = input.readUint32();
+ /*int texdatasize =*/ input.readUint32();
+ int bpp = input.readUint32();
+ /*int rmask =*/ input.readUint32();
+ /*int gmask =*/ input.readUint32();
+ /*int bmask =*/ input.readUint32();
+ int amask = input.readUint32();
+ int magic = input.readUint32();
+ int numtex = input.readUint32();
+
+ if (size != HEADER_SIZE || magic != 0x21525650) {
+ return null;
+ }
+
+ if (numtex < 1) {
+ numtex = (flags & PVRTEX_CUBEMAP) != 0 ? 6 : 1;
+ }
+
+ if (numtex != 1) {
+ // only 1 surface supported currently
+ return null;
+ }
+
+ if (width * height * bpp / 8 > length - HEADER_SIZE) {
+ return null;
+ }
+
+ int ptype = flags & PVR_PIXELTYPE_MASK;
+
+ switch (ptype) {
+ case PVR_TYPE_RGBA4444:
+ Image image = Image(width, height);
+ Uint8List out = image.getBytes();
+ int oi = 0;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ int v1 = input.readByte();
+ int v2 = input.readByte();
+ int a = (v1 & 0x0f) << 4;
+ int b = (v1 & 0xf0);
+ int g = (v2 & 0x0f) << 4;
+ int r = (v2 & 0xf0);
+
+ out[oi++] = r;
+ out[oi++] = g;
+ out[oi++] = b;
+ out[oi++] = a;
+ }
+ }
+ return image;
+ case PVR_TYPE_RGBA5551:
+ Image image = Image(width, height);
+ Uint8List out = image.getBytes();
+ int oi = 0;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ int v = input.readUint16();
+
+ int r = (v & 0xf800) >> 8;
+ int g = (v & 0x07c0) >> 3;
+ int b = (v & 0x003e) << 2;
+ int a = (v & 0x0001) != 0 ? 255 : 0;
+
+ out[oi++] = r;
+ out[oi++] = g;
+ out[oi++] = b;
+ out[oi++] = a;
+ }
+ }
+ return image;
+ case PVR_TYPE_RGBA8888:
+ Image image = Image(width, height);
+ Uint8List out = image.getBytes();
+ int oi = 0;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ out[oi++] = input.readByte();
+ out[oi++] = input.readByte();
+ out[oi++] = input.readByte();
+ out[oi++] = input.readByte();
+ }
+ }
+ return image;
+ case PVR_TYPE_RGB565:
+ Image image = Image(width, height);
+ Uint8List out = image.getBytes();
+ int oi = 0;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ int v = input.readUint16();
+ int b = (v & 0x001f) << 3;
+ int g = (v & 0x07e0) >> 3;
+ int r = (v & 0xf800) >> 8;
+ int a = 255;
+ out[oi++] = r;
+ out[oi++] = g;
+ out[oi++] = b;
+ out[oi++] = a;
+ }
+ }
+ return image;
+ case PVR_TYPE_RGB555:
+ Image image = Image(width, height);
+ Uint8List out = image.getBytes();
+ int oi = 0;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ int v = input.readUint16();
+ int r = (v & 0x001f) << 3;
+ int g = (v & 0x03e0) >> 2;
+ int b = (v & 0x7c00) >> 7;
+ int a = 255;
+ out[oi++] = r;
+ out[oi++] = g;
+ out[oi++] = b;
+ out[oi++] = a;
+ }
+ }
+ return image;
+ case PVR_TYPE_RGB888:
+ Image image = Image(width, height);
+ Uint8List out = image.getBytes();
+ int oi = 0;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ out[oi++] = input.readByte();
+ out[oi++] = input.readByte();
+ out[oi++] = input.readByte();
+ out[oi++] = 255;
+ }
+ }
+ return image;
+ case PVR_TYPE_I8:
+ Image image = Image(width, height);
+ Uint8List out = image.getBytes();
+ int oi = 0;
+ for (int y = 0; y < height; ++y) {
+ for(int x = 0; x < width; ++x) {
+ int i = input.readByte();
+ out[oi++] = i;
+ out[oi++] = i;
+ out[oi++] = i;
+ out[oi++] = 255;
+ }
+ }
+ return image;
+ case PVR_TYPE_AI8:
+ Image image = Image(width, height);
+ Uint8List out = image.getBytes();
+ int oi = 0;
+ for (int y = 0; y < height; ++y) {
+ for(int x = 0; x < width; ++x) {
+ int i = input.readByte();
+ int a = input.readByte();
+ out[oi++] = i;
+ out[oi++] = i;
+ out[oi++] = i;
+ out[oi++] = a;
+ }
+ }
+ return image;
+ case PVR_TYPE_PVRTC2:
+ // Currently unsupported
+ return null;
+ case PVR_TYPE_PVRTC4:
+ return amask == 0 ? decodeRgb4bpp(width, height, input.toUint8List()) :
+ decodeRgba4bpp(width, height, input.toUint8List());
+ }
+
+ // Unknown format
+ return null;
+ }
+
+ Image decodePVR3(List<int> data) {
+ //const int PVR3_PVRTC_2BPP_RGB = 0;
+ //const int PVR3_PVRTC_2BPP_RGBA = 1;
+ const int PVR3_PVRTC_4BPP_RGB = 2;
+ const int PVR3_PVRTC_4BPP_RGBA = 3;
+ /*const int PVR3_PVRTC2_2BPP = 4;
+ const int PVR3_PVRTC2_4BPP = 5;
+ const int PVR3_ETC1 = 6;
+ const int PVR3_DXT1 = 7;
+ const int PVR3_DXT2 = 8;
+ const int PVR3_DXT3 = 9;
+ const int PVR3_DXT4 = 10;
+ const int PVR3_DXT5 = 11;
+ const int PVR3_BC1 = 7;
+ const int PVR3_BC2 = 9;
+ const int PVR3_BC3 = 11;
+ const int PVR3_BC4 = 12;
+ const int PVR3_BC5 = 13;
+ const int PVR3_BC6 = 14;
+ const int PVR3_BC7 = 15;
+ const int PVR3_UYVY = 16;
+ const int PVR3_YUY2 = 17;
+ const int PVR3_BW_1BPP = 18;
+ const int PVR3_R9G9B9E5 = 19;
+ const int PVR3_RGBG8888 = 20;
+ const int PVR3_GRGB8888 = 21;
+ const int PVR3_ETC2_RGB = 22;
+ const int PVR3_ETC2_RGBA = 23;
+ const int PVR3_ETC2_RGB_A1 = 24;
+ const int PVR3_EAC_R11_U = 25;
+ const int PVR3_EAC_R11_S = 26;
+ const int PVR3_EAC_RG11_U = 27;
+ const int PVR3_EAC_RG11_S = 28;*/
+
+ InputBuffer input = InputBuffer(data);
+
+ // Header
+ int version = input.readUint32();
+ if (version != 0x03525650) {
+ return null;
+ }
+
+ /*int flags =*/ input.readUint32();
+ var format = input.readUint32();
+ var order = [input.readByte(), input.readByte(),
+ input.readByte(), input.readByte()];
+ /*int colorspace =*/ input.readUint32();
+ /*int channeltype =*/ input.readUint32();
+ int height = input.readUint32();
+ int width = input.readUint32();
+ /*int depth =*/ input.readUint32();
+ /*int num_surfaces =*/ input.readUint32();
+ /*int num_faces =*/ input.readUint32();
+ /*int mipcount =*/ input.readUint32();
+ int metadata_size = input.readUint32();
+
+ input.skip(metadata_size);
+
+ if (order[0] == 0) {
+ switch (format) {
+ case PVR3_PVRTC_4BPP_RGB:
+ return decodeRgb4bpp(width, height, input.toUint8List());
+ case PVR3_PVRTC_4BPP_RGBA:
+ return decodeRgba4bpp(width, height, input.toUint8List());
+ /*case PVR3_PVRTC_2BPP_RGB:
+ return null;
+ case PVR3_PVRTC_2BPP_RGBA:
+ return null;
+ case PVR3_PVRTC2_2BPP:
+ return null;
+ case PVR3_PVRTC2_4BPP:
+ return null;
+ case PVR3_ETC1:
+ return null;
+ case PVR3_DXT1:
+ return null;
+ case PVR3_DXT2:
+ return null;
+ case PVR3_DXT3:
+ return null;
+ case PVR3_DXT4:
+ return null;
+ case PVR3_DXT5:
+ return null;
+ case PVR3_BC1:
+ return null;
+ case PVR3_BC2:
+ return null;
+ case PVR3_BC3:
+ return null;
+ case PVR3_BC4:
+ return null;
+ case PVR3_BC5:
+ return null;
+ case PVR3_BC6:
+ return null;
+ case PVR3_BC7:
+ return null;
+ case PVR3_UYVY:
+ return null;
+ case PVR3_YUY2:
+ return null;
+ case PVR3_BW_1BPP:
+ return null;
+ case PVR3_R9G9B9E5:
+ return null;
+ case PVR3_RGBG8888:
+ return null;
+ case PVR3_GRGB8888:
+ return null;
+ case PVR3_ETC2_RGB:
+ return null;
+ case PVR3_ETC2_RGBA:
+ return null;
+ case PVR3_ETC2_RGB_A1:
+ return null;
+ case PVR3_EAC_R11_U:
+ return null;
+ case PVR3_EAC_R11_S:
+ return null;
+ case PVR3_EAC_RG11_U:
+ return null;
+ case PVR3_EAC_RG11_S:
+ return null;*/
+ }
+ }
+
+ return null;
+ }
+
+ int _countBits(int x) {
+ x = (x - ((x >> 1) & 0x55555555)) & 0xffffffff;
+ x = ((x & 0x33333333) + ((x >> 2) & 0x33333333)) & 0xffffffff;
+ x = (x + (x >> 4)) & 0xffffffff;
+ x &= 0xf0f0f0f;
+ x = ((x * 0x01010101) & 0xffffffff) >> 24;
+ return x;
+ }
+
+ Image decodeRgb4bpp(int width, int height, TypedData data) {
+ var result = Image(width, height, Image.RGB);
+
+ final int blocks = width ~/ 4;
+ final int blockMask = blocks - 1;
+
+ final packet = PvrtcPacket(data);
+ final p0 = PvrtcPacket(data);
+ final p1 = PvrtcPacket(data);
+ final p2 = PvrtcPacket(data);
+ final p3 = PvrtcPacket(data);
+ final c = PvrtcColorRgb();
+ const factors = PvrtcPacket.BILINEAR_FACTORS;
+ const weights = PvrtcPacket.WEIGHTS;
+
+ for (int y = 0; y < blocks; ++y) {
+ for (int x = 0; x < blocks; ++x) {
+ packet.setBlock(x, y);
+
+ int mod = packet.modulationData;
+ int weightIndex = 4 * packet.usePunchthroughAlpha;
+ int factorIndex = 0;
+
+ for (int py = 0; py < 4; ++py) {
+ int yOffset = (py < 2) ? -1 : 0;
+ int y0 = (y + yOffset) & blockMask;
+ int y1 = (y0 + 1) & blockMask;
+ int pyi = (py + y * 4) * width;
+
+ for (int px = 0; px < 4; ++px) {
+ int xOffset = (px < 2) ? -1 : 0;
+ int x0 = (x + xOffset) & blockMask;
+ int x1 = (x0 + 1) & blockMask;
+
+ p0.setBlock(x0, y0);
+ p1.setBlock(x1, y0);
+ p2.setBlock(x0, y1);
+ p3.setBlock(x1, y1);
+
+ var ca = p0.getColorRgbA() * factors[factorIndex][0] +
+ p1.getColorRgbA() * factors[factorIndex][1] +
+ p2.getColorRgbA() * factors[factorIndex][2] +
+ p3.getColorRgbA() * factors[factorIndex][3];
+
+ var cb = p0.getColorRgbB() * factors[factorIndex][0] +
+ p1.getColorRgbB() * factors[factorIndex][1] +
+ p2.getColorRgbB() * factors[factorIndex][2] +
+ p3.getColorRgbB() * factors[factorIndex][3];
+
+ var w = weights[weightIndex + mod & 3];
+
+ c.r = (ca.r * w[0] + cb.r * w[1]) >> 7;
+ c.g = (ca.g * w[0] + cb.g * w[1]) >> 7;
+ c.b = (ca.b * w[0] + cb.b * w[1]) >> 7;
+
+ int pi = (pyi + (px + x * 4));
+
+ result[pi] = getColor(c.r, c.g, c.b, 255);
+
+ mod >>= 2;
+ factorIndex++;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ Image decodeRgba4bpp(int width, int height, TypedData data) {
+ var result = Image(width, height, Image.RGBA);
+
+ final int blocks = width ~/ 4;
+ final int blockMask = blocks - 1;
+
+ final packet = PvrtcPacket(data);
+ final p0 = PvrtcPacket(data);
+ final p1 = PvrtcPacket(data);
+ final p2 = PvrtcPacket(data);
+ final p3 = PvrtcPacket(data);
+ final c = PvrtcColorRgba();
+ const factors = PvrtcPacket.BILINEAR_FACTORS;
+ const weights = PvrtcPacket.WEIGHTS;
+
+ for (int y = 0; y < blocks; ++y) {
+ for (int x = 0; x < blocks; ++x) {
+ packet.setBlock(x, y);
+
+ int mod = packet.modulationData;
+ int weightIndex = 4 * packet.usePunchthroughAlpha;
+ int factorIndex = 0;
+
+ for (int py = 0; py < 4; ++py) {
+ int yOffset = (py < 2) ? -1 : 0;
+ int y0 = (y + yOffset) & blockMask;
+ int y1 = (y0 + 1) & blockMask;
+ int pyi = (py + y * 4) * width;
+
+ for (int px = 0; px < 4; ++px) {
+ int xOffset = (px < 2) ? -1 : 0;
+ int x0 = (x + xOffset) & blockMask;
+ int x1 = (x0 + 1) & blockMask;
+
+ p0.setBlock(x0, y0);
+ p1.setBlock(x1, y0);
+ p2.setBlock(x0, y1);
+ p3.setBlock(x1, y1);
+
+ var ca = p0.getColorRgbaA() * factors[factorIndex][0] +
+ p1.getColorRgbaA() * factors[factorIndex][1] +
+ p2.getColorRgbaA() * factors[factorIndex][2] +
+ p3.getColorRgbaA() * factors[factorIndex][3];
+
+ var cb = p0.getColorRgbaB() * factors[factorIndex][0] +
+ p1.getColorRgbaB() * factors[factorIndex][1] +
+ p2.getColorRgbaB() * factors[factorIndex][2] +
+ p3.getColorRgbaB() * factors[factorIndex][3];
+
+ var w = weights[weightIndex + mod & 3];
+
+ c.r = (ca.r * w[0] + cb.r * w[1]) >> 7;
+ c.g = (ca.g * w[0] + cb.g * w[1]) >> 7;
+ c.b = (ca.b * w[0] + cb.b * w[1]) >> 7;
+ c.a = (ca.a * w[2] + cb.a * w[3]) >> 7;
+
+ int pi = (pyi + (px + x * 4));
+
+ result[pi] = getColor(c.r, c.g, c.b, c.a);
+
+ mod >>= 2;
+ factorIndex++;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/image/lib/src/formats/pvrtc/pvrtc_encoder.dart b/image/lib/src/formats/pvrtc/pvrtc_encoder.dart
old mode 100644
new mode 100755
index fd91dc7..ca1241d
--- a/image/lib/src/formats/pvrtc/pvrtc_encoder.dart
+++ b/image/lib/src/formats/pvrtc/pvrtc_encoder.dart
@@ -1,368 +1,368 @@
-import 'dart:typed_data';
-
-import '../../color.dart';
-import '../../image.dart';
-import '../../image_exception.dart';
-import '../../util/output_buffer.dart';
-import 'pvrtc_bit_utility.dart';
-import 'pvrtc_color.dart';
-import 'pvrtc_color_bounding_box.dart';
-import 'pvrtc_packet.dart';
-
-/**
- * Ported from Jeffrey Lim's PVRTC encoder/decoder,
- * https://bitbucket.org/jthlim/pvrtccompressor
- */
-class PvrtcEncoder {
- // PVR Format
- static const int PVR_AUTO = -1;
- static const int PVR_RGB_2BPP = 0;
- static const int PVR_RGBA_2BPP = 1;
- static const int PVR_RGB_4BPP = 2;
- static const int PVR_RGBA_4BPP = 3;
-
- Uint8List encodePvr(Image bitmap, {int format: PVR_AUTO}) {
- OutputBuffer output = new OutputBuffer();
-
- var pvrtc;
- if (format == PVR_AUTO) {
- if (bitmap.format == Image.RGB) {
- pvrtc = encodeRgb4Bpp(bitmap);
- format = PVR_RGB_4BPP;
- } else {
- pvrtc = encodeRgba4Bpp(bitmap);
- format = PVR_RGBA_4BPP;
- }
- } else if (format == PVR_RGB_2BPP) {
- //pvrtc = encodeRgb2Bpp(bitmap);
- pvrtc = encodeRgb4Bpp(bitmap);
- } else if (format == PVR_RGBA_2BPP) {
- //pvrtc = encodeRgba2Bpp(bitmap);
- pvrtc = encodeRgba4Bpp(bitmap);
- } else if (format == PVR_RGB_4BPP) {
- pvrtc = encodeRgb4Bpp(bitmap);
- } else if (format == PVR_RGBA_4BPP) {
- pvrtc = encodeRgba4Bpp(bitmap);
- }
-
- int version = 55727696;
- int flags = 0;
- int pixelFormat = format;
- int channelOrder = 0;
- int colorSpace = 0;
- int channelType = 0;
- int height = bitmap.height;
- int width = bitmap.width;
- int depth = 1;
- int numSurfaces = 1;
- int numFaces = 1;
- int mipmapCount = 1;
- int metaDataSize = 0;
-
- output.writeUint32(version);
- output.writeUint32(flags);
- output.writeUint32(pixelFormat);
- output.writeUint32(channelOrder);
- output.writeUint32(colorSpace);
- output.writeUint32(channelType);
- output.writeUint32(height);
- output.writeUint32(width);
- output.writeUint32(depth);
- output.writeUint32(numSurfaces);
- output.writeUint32(numFaces);
- output.writeUint32(mipmapCount);
- output.writeUint32(metaDataSize);
-
- output.writeBytes(pvrtc);
-
- return output.getBytes();
- }
-
- Uint8List encodeRgb4Bpp(Image bitmap) {
- if (bitmap.width != bitmap.height) {
- throw new ImageException('PVRTC requires a square image.');
- }
-
- if (!BitUtility.isPowerOf2(bitmap.width)) {
- throw new ImageException('PVRTC requires a power-of-two sized image.');
- }
-
- final int size = bitmap.width;
- final int blocks = size ~/ 4;
- final int blockMask = blocks - 1;
-
- var bitmapData = bitmap.getBytes();
-
- // Allocate enough data for encoding the image.
- var outputData = new Uint8List((bitmap.width * bitmap.height) ~/ 2);
- var packet = new PvrtcPacket(outputData);
- var p0 = new PvrtcPacket(outputData);
- var p1 = new PvrtcPacket(outputData);
- var p2 = new PvrtcPacket(outputData);
- var p3 = new PvrtcPacket(outputData);
-
- for (int y = 0; y < blocks; ++y) {
- for (int x = 0; x < blocks; ++x) {
- packet.setBlock(x, y);
- packet.usePunchthroughAlpha = 0;
- var cbb = _calculateBoundingBoxRgb(bitmap, x, y);
- packet.setColorRgbA(cbb.min);
- packet.setColorRgbB(cbb.max);
- }
- }
-
- const factors = PvrtcPacket.BILINEAR_FACTORS;
-
- for (int y = 0; y < blocks; ++y) {
- for (int x = 0; x < blocks; ++x) {
- int factorIndex = 0;
- final pixelIndex = (y * 4 * size + x * 4) * 4;
-
- int modulationData = 0;
-
- for (int py = 0; py < 4; ++py) {
- final int yOffset = (py < 2) ? -1 : 0;
- final int y0 = (y + yOffset) & blockMask;
- final int y1 = (y0 + 1) & blockMask;
-
- for(int px = 0; px < 4; ++px) {
- final int xOffset = (px < 2) ? -1 : 0;
- final int x0 = (x + xOffset) & blockMask;
- final int x1 = (x0 + 1) & blockMask;
-
- p0.setBlock(x0, y0);
- p1.setBlock(x1, y0);
- p2.setBlock(x0, y1);
- p3.setBlock(x1, y1);
-
- var ca = p0.getColorRgbA() * factors[factorIndex][0] +
- p1.getColorRgbA() * factors[factorIndex][1] +
- p2.getColorRgbA() * factors[factorIndex][2] +
- p3.getColorRgbA() * factors[factorIndex][3];
-
- var cb = p0.getColorRgbB() * factors[factorIndex][0] +
- p1.getColorRgbB() * factors[factorIndex][1] +
- p2.getColorRgbB() * factors[factorIndex][2] +
- p3.getColorRgbB() * factors[factorIndex][3];
-
- int pi = pixelIndex + ((py * size + px) * 4);
- int r = bitmapData[pi];
- int g = bitmapData[pi + 1];
- int b = bitmapData[pi + 2];
-
- var d = cb - ca;
- var p = new PvrtcColorRgb(r * 16, g * 16, b * 16);
- var v = p - ca;
-
- // PVRTC uses weightings of 0, 3/8, 5/8 and 1
- // The boundaries for these are 3/16, 1/2 (=8/16), 13/16
- int projection = v.dotProd(d) * 16;
- int lengthSquared = d.dotProd(d);
- if (projection > 3 * lengthSquared) {
- modulationData++;
- }
- if (projection > 8 * lengthSquared) {
- modulationData++;
- }
- if (projection > 13 * lengthSquared) {
- modulationData++;
- }
-
- modulationData = BitUtility.rotateRight(modulationData, 2);
-
- factorIndex++;
- }
- }
-
- packet.setBlock(x, y);
- packet.modulationData = modulationData;
- }
- }
-
- return outputData;
- }
-
-
- Uint8List encodeRgba4Bpp(Image bitmap) {
- if (bitmap.width != bitmap.height) {
- throw new ImageException('PVRTC requires a square image.');
- }
-
- if (!BitUtility.isPowerOf2(bitmap.width)) {
- throw new ImageException('PVRTC requires a power-of-two sized image.');
- }
-
- final int size = bitmap.width;
- final int blocks = size ~/ 4;
- final int blockMask = blocks - 1;
-
- var bitmapData = bitmap.getBytes();
-
- // Allocate enough data for encoding the image.
- var outputData = new Uint8List((bitmap.width * bitmap.height) ~/ 2);
- var packet = new PvrtcPacket(outputData);
- var p0 = new PvrtcPacket(outputData);
- var p1 = new PvrtcPacket(outputData);
- var p2 = new PvrtcPacket(outputData);
- var p3 = new PvrtcPacket(outputData);
-
- for (int y = 0; y < blocks; ++y) {
- for (int x = 0; x < blocks; ++x) {
- packet.setBlock(x, y);
- packet.usePunchthroughAlpha = 0;
- var cbb = _calculateBoundingBoxRgba(bitmap, x, y);
- packet.setColorRgbaA(cbb.min);
- packet.setColorRgbaB(cbb.max);
- }
- }
-
- const factors = PvrtcPacket.BILINEAR_FACTORS;
-
- for (int y = 0; y < blocks; ++y) {
- for (int x = 0; x < blocks; ++x) {
- int factorIndex = 0;
- final pixelIndex = (y * 4 * size + x * 4) * 4;
-
- int modulationData = 0;
-
- for (int py = 0; py < 4; ++py) {
- final int yOffset = (py < 2) ? -1 : 0;
- final int y0 = (y + yOffset) & blockMask;
- final int y1 = (y0 + 1) & blockMask;
-
- for(int px = 0; px < 4; ++px) {
- final int xOffset = (px < 2) ? -1 : 0;
- final int x0 = (x + xOffset) & blockMask;
- final int x1 = (x0 + 1) & blockMask;
-
- p0.setBlock(x0, y0);
- p1.setBlock(x1, y0);
- p2.setBlock(x0, y1);
- p3.setBlock(x1, y1);
-
- var ca = p0.getColorRgbaA() * factors[factorIndex][0] +
- p1.getColorRgbaA() * factors[factorIndex][1] +
- p2.getColorRgbaA() * factors[factorIndex][2] +
- p3.getColorRgbaA() * factors[factorIndex][3];
-
- var cb = p0.getColorRgbaB() * factors[factorIndex][0] +
- p1.getColorRgbaB() * factors[factorIndex][1] +
- p2.getColorRgbaB() * factors[factorIndex][2] +
- p3.getColorRgbaB() * factors[factorIndex][3];
-
- int pi = pixelIndex + ((py * size + px) * 4);
- int r = bitmapData[pi];
- int g = bitmapData[pi + 1];
- int b = bitmapData[pi + 2];
- int a = bitmapData[pi + 3];
-
- var d = cb - ca;
- var p = new PvrtcColorRgba(r * 16, g * 16, b * 16, a * 16);
- var v = p - ca;
-
- // PVRTC uses weightings of 0, 3/8, 5/8 and 1
- // The boundaries for these are 3/16, 1/2 (=8/16), 13/16
- int projection = v.dotProd(d) * 16;
- int lengthSquared = d.dotProd(d);
-
- if (projection > 3 * lengthSquared) {
- modulationData++;
- }
- if (projection > 8 * lengthSquared) {
- modulationData++;
- }
- if (projection > 13 * lengthSquared) {
- modulationData++;
- }
-
- modulationData = BitUtility.rotateRight(modulationData, 2);
-
- factorIndex++;
- }
- }
-
- packet.setBlock(x, y);
- packet.modulationData = modulationData;
- }
- }
-
- return outputData;
- }
-
- static PvrtcColorBoundingBox _calculateBoundingBoxRgb(Image bitmap,
- int blockX,
- int blockY) {
- int size = bitmap.width;
- int pi = (blockY * 4 * size + blockX * 4);
-
- _pixel(i) {
- int c = bitmap[pi + i];
- return new PvrtcColorRgb(getRed(c), getGreen(c), getBlue(c));
- }
-
- var cbb = new PvrtcColorBoundingBox(_pixel(0), _pixel(0));
- cbb.add(_pixel(1));
- cbb.add(_pixel(2));
- cbb.add(_pixel(3));
-
- cbb.add(_pixel(size));
- cbb.add(_pixel(size + 1));
- cbb.add(_pixel(size + 2));
- cbb.add(_pixel(size + 3));
-
- cbb.add(_pixel(2 * size));
- cbb.add(_pixel(2 * size + 1));
- cbb.add(_pixel(2 * size + 2));
- cbb.add(_pixel(2 * size + 3));
-
- cbb.add(_pixel(3 * size));
- cbb.add(_pixel(3 * size + 1));
- cbb.add(_pixel(3 * size + 2));
- cbb.add(_pixel(3 * size + 3));
-
- return cbb;
- }
-
- static PvrtcColorBoundingBox _calculateBoundingBoxRgba(Image bitmap,
- int blockX,
- int blockY) {
- int size = bitmap.width;
- int pi = (blockY * 4 * size + blockX * 4);
-
- _pixel(i) {
- int c = bitmap[pi + i];
- return new PvrtcColorRgba(getRed(c), getGreen(c), getBlue(c), getAlpha(c));
- }
-
- var cbb = new PvrtcColorBoundingBox(_pixel(0), _pixel(0));
- cbb.add(_pixel(1));
- cbb.add(_pixel(2));
- cbb.add(_pixel(3));
-
- cbb.add(_pixel(size));
- cbb.add(_pixel(size + 1));
- cbb.add(_pixel(size + 2));
- cbb.add(_pixel(size + 3));
-
- cbb.add(_pixel(2 * size));
- cbb.add(_pixel(2 * size + 1));
- cbb.add(_pixel(2 * size + 2));
- cbb.add(_pixel(2 * size + 3));
-
- cbb.add(_pixel(3 * size));
- cbb.add(_pixel(3 * size + 1));
- cbb.add(_pixel(3 * size + 2));
- cbb.add(_pixel(3 * size + 3));
-
- return cbb;
- }
-
- /*static void _getPacket(packet, packetData, index) {
- index *= 2;
- packet.modulationData = packetData[index];
- packet.colorData = packetData[index + 1];
- }*/
-
- static const MODULATION_LUT =
- const [ 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3 ];
-}
+import 'dart:typed_data';
+
+import '../../color.dart';
+import '../../image.dart';
+import '../../image_exception.dart';
+import '../../util/output_buffer.dart';
+import 'pvrtc_bit_utility.dart';
+import 'pvrtc_color.dart';
+import 'pvrtc_color_bounding_box.dart';
+import 'pvrtc_packet.dart';
+
+/**
+ * Ported from Jeffrey Lim's PVRTC encoder/decoder,
+ * https://bitbucket.org/jthlim/pvrtccompressor
+ */
+class PvrtcEncoder {
+ // PVR Format
+ static const int PVR_AUTO = -1;
+ static const int PVR_RGB_2BPP = 0;
+ static const int PVR_RGBA_2BPP = 1;
+ static const int PVR_RGB_4BPP = 2;
+ static const int PVR_RGBA_4BPP = 3;
+
+ Uint8List encodePvr(Image bitmap, {int format: PVR_AUTO}) {
+ OutputBuffer output = OutputBuffer();
+
+ var pvrtc;
+ if (format == PVR_AUTO) {
+ if (bitmap.format == Image.RGB) {
+ pvrtc = encodeRgb4Bpp(bitmap);
+ format = PVR_RGB_4BPP;
+ } else {
+ pvrtc = encodeRgba4Bpp(bitmap);
+ format = PVR_RGBA_4BPP;
+ }
+ } else if (format == PVR_RGB_2BPP) {
+ //pvrtc = encodeRgb2Bpp(bitmap);
+ pvrtc = encodeRgb4Bpp(bitmap);
+ } else if (format == PVR_RGBA_2BPP) {
+ //pvrtc = encodeRgba2Bpp(bitmap);
+ pvrtc = encodeRgba4Bpp(bitmap);
+ } else if (format == PVR_RGB_4BPP) {
+ pvrtc = encodeRgb4Bpp(bitmap);
+ } else if (format == PVR_RGBA_4BPP) {
+ pvrtc = encodeRgba4Bpp(bitmap);
+ }
+
+ int version = 55727696;
+ int flags = 0;
+ int pixelFormat = format;
+ int channelOrder = 0;
+ int colorSpace = 0;
+ int channelType = 0;
+ int height = bitmap.height;
+ int width = bitmap.width;
+ int depth = 1;
+ int numSurfaces = 1;
+ int numFaces = 1;
+ int mipmapCount = 1;
+ int metaDataSize = 0;
+
+ output.writeUint32(version);
+ output.writeUint32(flags);
+ output.writeUint32(pixelFormat);
+ output.writeUint32(channelOrder);
+ output.writeUint32(colorSpace);
+ output.writeUint32(channelType);
+ output.writeUint32(height);
+ output.writeUint32(width);
+ output.writeUint32(depth);
+ output.writeUint32(numSurfaces);
+ output.writeUint32(numFaces);
+ output.writeUint32(mipmapCount);
+ output.writeUint32(metaDataSize);
+
+ output.writeBytes(pvrtc);
+
+ return output.getBytes();
+ }
+
+ Uint8List encodeRgb4Bpp(Image bitmap) {
+ if (bitmap.width != bitmap.height) {
+ throw new ImageException('PVRTC requires a square image.');
+ }
+
+ if (!BitUtility.isPowerOf2(bitmap.width)) {
+ throw new ImageException('PVRTC requires a power-of-two sized image.');
+ }
+
+ final int size = bitmap.width;
+ final int blocks = size ~/ 4;
+ final int blockMask = blocks - 1;
+
+ var bitmapData = bitmap.getBytes();
+
+ // Allocate enough data for encoding the image.
+ var outputData = Uint8List((bitmap.width * bitmap.height) ~/ 2);
+ var packet = PvrtcPacket(outputData);
+ var p0 = PvrtcPacket(outputData);
+ var p1 = PvrtcPacket(outputData);
+ var p2 = PvrtcPacket(outputData);
+ var p3 = PvrtcPacket(outputData);
+
+ for (int y = 0; y < blocks; ++y) {
+ for (int x = 0; x < blocks; ++x) {
+ packet.setBlock(x, y);
+ packet.usePunchthroughAlpha = 0;
+ var cbb = _calculateBoundingBoxRgb(bitmap, x, y);
+ packet.setColorRgbA(cbb.min);
+ packet.setColorRgbB(cbb.max);
+ }
+ }
+
+ const factors = PvrtcPacket.BILINEAR_FACTORS;
+
+ for (int y = 0; y < blocks; ++y) {
+ for (int x = 0; x < blocks; ++x) {
+ int factorIndex = 0;
+ final pixelIndex = (y * 4 * size + x * 4) * 4;
+
+ int modulationData = 0;
+
+ for (int py = 0; py < 4; ++py) {
+ final int yOffset = (py < 2) ? -1 : 0;
+ final int y0 = (y + yOffset) & blockMask;
+ final int y1 = (y0 + 1) & blockMask;
+
+ for(int px = 0; px < 4; ++px) {
+ final int xOffset = (px < 2) ? -1 : 0;
+ final int x0 = (x + xOffset) & blockMask;
+ final int x1 = (x0 + 1) & blockMask;
+
+ p0.setBlock(x0, y0);
+ p1.setBlock(x1, y0);
+ p2.setBlock(x0, y1);
+ p3.setBlock(x1, y1);
+
+ var ca = p0.getColorRgbA() * factors[factorIndex][0] +
+ p1.getColorRgbA() * factors[factorIndex][1] +
+ p2.getColorRgbA() * factors[factorIndex][2] +
+ p3.getColorRgbA() * factors[factorIndex][3];
+
+ var cb = p0.getColorRgbB() * factors[factorIndex][0] +
+ p1.getColorRgbB() * factors[factorIndex][1] +
+ p2.getColorRgbB() * factors[factorIndex][2] +
+ p3.getColorRgbB() * factors[factorIndex][3];
+
+ int pi = pixelIndex + ((py * size + px) * 4);
+ int r = bitmapData[pi];
+ int g = bitmapData[pi + 1];
+ int b = bitmapData[pi + 2];
+
+ var d = cb - ca;
+ var p = PvrtcColorRgb(r * 16, g * 16, b * 16);
+ var v = p - ca;
+
+ // PVRTC uses weightings of 0, 3/8, 5/8 and 1
+ // The boundaries for these are 3/16, 1/2 (=8/16), 13/16
+ int projection = v.dotProd(d) * 16;
+ int lengthSquared = d.dotProd(d);
+ if (projection > 3 * lengthSquared) {
+ modulationData++;
+ }
+ if (projection > 8 * lengthSquared) {
+ modulationData++;
+ }
+ if (projection > 13 * lengthSquared) {
+ modulationData++;
+ }
+
+ modulationData = BitUtility.rotateRight(modulationData, 2);
+
+ factorIndex++;
+ }
+ }
+
+ packet.setBlock(x, y);
+ packet.modulationData = modulationData;
+ }
+ }
+
+ return outputData;
+ }
+
+
+ Uint8List encodeRgba4Bpp(Image bitmap) {
+ if (bitmap.width != bitmap.height) {
+ throw new ImageException('PVRTC requires a square image.');
+ }
+
+ if (!BitUtility.isPowerOf2(bitmap.width)) {
+ throw new ImageException('PVRTC requires a power-of-two sized image.');
+ }
+
+ final int size = bitmap.width;
+ final int blocks = size ~/ 4;
+ final int blockMask = blocks - 1;
+
+ var bitmapData = bitmap.getBytes();
+
+ // Allocate enough data for encoding the image.
+ var outputData = Uint8List((bitmap.width * bitmap.height) ~/ 2);
+ var packet = PvrtcPacket(outputData);
+ var p0 = PvrtcPacket(outputData);
+ var p1 = PvrtcPacket(outputData);
+ var p2 = PvrtcPacket(outputData);
+ var p3 = PvrtcPacket(outputData);
+
+ for (int y = 0; y < blocks; ++y) {
+ for (int x = 0; x < blocks; ++x) {
+ packet.setBlock(x, y);
+ packet.usePunchthroughAlpha = 0;
+ var cbb = _calculateBoundingBoxRgba(bitmap, x, y);
+ packet.setColorRgbaA(cbb.min);
+ packet.setColorRgbaB(cbb.max);
+ }
+ }
+
+ const factors = PvrtcPacket.BILINEAR_FACTORS;
+
+ for (int y = 0; y < blocks; ++y) {
+ for (int x = 0; x < blocks; ++x) {
+ int factorIndex = 0;
+ final pixelIndex = (y * 4 * size + x * 4) * 4;
+
+ int modulationData = 0;
+
+ for (int py = 0; py < 4; ++py) {
+ final int yOffset = (py < 2) ? -1 : 0;
+ final int y0 = (y + yOffset) & blockMask;
+ final int y1 = (y0 + 1) & blockMask;
+
+ for(int px = 0; px < 4; ++px) {
+ final int xOffset = (px < 2) ? -1 : 0;
+ final int x0 = (x + xOffset) & blockMask;
+ final int x1 = (x0 + 1) & blockMask;
+
+ p0.setBlock(x0, y0);
+ p1.setBlock(x1, y0);
+ p2.setBlock(x0, y1);
+ p3.setBlock(x1, y1);
+
+ var ca = p0.getColorRgbaA() * factors[factorIndex][0] +
+ p1.getColorRgbaA() * factors[factorIndex][1] +
+ p2.getColorRgbaA() * factors[factorIndex][2] +
+ p3.getColorRgbaA() * factors[factorIndex][3];
+
+ var cb = p0.getColorRgbaB() * factors[factorIndex][0] +
+ p1.getColorRgbaB() * factors[factorIndex][1] +
+ p2.getColorRgbaB() * factors[factorIndex][2] +
+ p3.getColorRgbaB() * factors[factorIndex][3];
+
+ int pi = pixelIndex + ((py * size + px) * 4);
+ int r = bitmapData[pi];
+ int g = bitmapData[pi + 1];
+ int b = bitmapData[pi + 2];
+ int a = bitmapData[pi + 3];
+
+ var d = cb - ca;
+ var p = PvrtcColorRgba(r * 16, g * 16, b * 16, a * 16);
+ var v = p - ca;
+
+ // PVRTC uses weightings of 0, 3/8, 5/8 and 1
+ // The boundaries for these are 3/16, 1/2 (=8/16), 13/16
+ int projection = v.dotProd(d) * 16;
+ int lengthSquared = d.dotProd(d);
+
+ if (projection > 3 * lengthSquared) {
+ modulationData++;
+ }
+ if (projection > 8 * lengthSquared) {
+ modulationData++;
+ }
+ if (projection > 13 * lengthSquared) {
+ modulationData++;
+ }
+
+ modulationData = BitUtility.rotateRight(modulationData, 2);
+
+ factorIndex++;
+ }
+ }
+
+ packet.setBlock(x, y);
+ packet.modulationData = modulationData;
+ }
+ }
+
+ return outputData;
+ }
+
+ static PvrtcColorBoundingBox _calculateBoundingBoxRgb(Image bitmap,
+ int blockX,
+ int blockY) {
+ int size = bitmap.width;
+ int pi = (blockY * 4 * size + blockX * 4);
+
+ _pixel(i) {
+ int c = bitmap[pi + i];
+ return new PvrtcColorRgb(getRed(c), getGreen(c), getBlue(c));
+ }
+
+ var cbb = PvrtcColorBoundingBox(_pixel(0), _pixel(0));
+ cbb.add(_pixel(1));
+ cbb.add(_pixel(2));
+ cbb.add(_pixel(3));
+
+ cbb.add(_pixel(size));
+ cbb.add(_pixel(size + 1));
+ cbb.add(_pixel(size + 2));
+ cbb.add(_pixel(size + 3));
+
+ cbb.add(_pixel(2 * size));
+ cbb.add(_pixel(2 * size + 1));
+ cbb.add(_pixel(2 * size + 2));
+ cbb.add(_pixel(2 * size + 3));
+
+ cbb.add(_pixel(3 * size));
+ cbb.add(_pixel(3 * size + 1));
+ cbb.add(_pixel(3 * size + 2));
+ cbb.add(_pixel(3 * size + 3));
+
+ return cbb;
+ }
+
+ static PvrtcColorBoundingBox _calculateBoundingBoxRgba(Image bitmap,
+ int blockX,
+ int blockY) {
+ int size = bitmap.width;
+ int pi = (blockY * 4 * size + blockX * 4);
+
+ _pixel(i) {
+ int c = bitmap[pi + i];
+ return new PvrtcColorRgba(getRed(c), getGreen(c), getBlue(c), getAlpha(c));
+ }
+
+ var cbb = PvrtcColorBoundingBox(_pixel(0), _pixel(0));
+ cbb.add(_pixel(1));
+ cbb.add(_pixel(2));
+ cbb.add(_pixel(3));
+
+ cbb.add(_pixel(size));
+ cbb.add(_pixel(size + 1));
+ cbb.add(_pixel(size + 2));
+ cbb.add(_pixel(size + 3));
+
+ cbb.add(_pixel(2 * size));
+ cbb.add(_pixel(2 * size + 1));
+ cbb.add(_pixel(2 * size + 2));
+ cbb.add(_pixel(2 * size + 3));
+
+ cbb.add(_pixel(3 * size));
+ cbb.add(_pixel(3 * size + 1));
+ cbb.add(_pixel(3 * size + 2));
+ cbb.add(_pixel(3 * size + 3));
+
+ return cbb;
+ }
+
+ /*static void _getPacket(packet, packetData, index) {
+ index *= 2;
+ packet.modulationData = packetData[index];
+ packet.colorData = packetData[index + 1];
+ }*/
+
+ static const MODULATION_LUT =
+ const [ 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3 ];
+}
diff --git a/image/lib/src/formats/pvrtc/pvrtc_packet.dart b/image/lib/src/formats/pvrtc/pvrtc_packet.dart
old mode 100644
new mode 100755
index cabe1e6..71fc51d
--- a/image/lib/src/formats/pvrtc/pvrtc_packet.dart
+++ b/image/lib/src/formats/pvrtc/pvrtc_packet.dart
@@ -1,299 +1,299 @@
-import 'dart:typed_data';
-
-import 'pvrtc_bit_utility.dart';
-import 'pvrtc_color.dart';
-
-/**
- * Ported from Jeffrey Lim's PVRTC encoder/decoder,
- * https://bitbucket.org/jthlim/pvrtccompressor
- */
-class PvrtcPacket {
- Uint32List rawData;
- int index;
-
- PvrtcPacket(TypedData data)
- : rawData = new Uint32List.view(data.buffer);
-
- void setBlock(int x, int y) => setIndex(_getMortonNumber(x, y));
-
- void setIndex(int i) {
- // A PvrtcPacket uses 2 uint32 values, so get the physical index
- // from the logical index by multiplying by 2.
- index = i << 1;
- // Pull in the values from the raw data.
- _update();
- }
-
- int get modulationData => rawData[index];
-
- set modulationData(int x) => rawData[index] = x;
-
- int get colorData => rawData[index + 1];
-
- set colorData(int x) => rawData[index + 1] = x;
-
- int get usePunchthroughAlpha => _usePunchthroughAlpha;
-
- set usePunchthroughAlpha(int x) {
- _usePunchthroughAlpha = x;
- colorData = _getColorData();
- }
-
- int get colorA => _colorA;
-
- set colorA(int x) {
- _colorA = x;
- colorData = _getColorData();
- }
-
- int get colorAIsOpaque => _colorAIsOpaque;
-
- set colorAIsOpaque(int x) {
- _colorAIsOpaque = x;
- colorData = _getColorData();
- }
-
- int get colorB => _colorB;
-
- set colorB(int x) {
- _colorB = x;
- colorData = _getColorData();
- }
-
- int get colorBIsOpaque => _colorBIsOpaque;
-
- set colorBIsOpaque(int x) {
- _colorBIsOpaque = x;
- colorData = _getColorData();
- }
-
- void setColorRgbA(PvrtcColorRgb c) {
- int r = BitUtility.BITSCALE_8_TO_5_FLOOR[c.r];
- int g = BitUtility.BITSCALE_8_TO_5_FLOOR[c.g];
- int b = BitUtility.BITSCALE_8_TO_4_FLOOR[c.b];
- colorA = r << 9 | g << 4 | b;
- colorAIsOpaque = 1;
- }
-
- void setColorRgbaA(PvrtcColorRgba c) {
- int a = BitUtility.BITSCALE_8_TO_3_FLOOR[c.a];
- if (a == 7) {
- int r = BitUtility.BITSCALE_8_TO_5_FLOOR[c.r];
- int g = BitUtility.BITSCALE_8_TO_5_FLOOR[c.g];
- int b = BitUtility.BITSCALE_8_TO_4_FLOOR[c.b];
- colorA = r << 9 | g << 4 | b;
- colorAIsOpaque = 1;
- } else {
- int r = BitUtility.BITSCALE_8_TO_4_FLOOR[c.r];
- int g = BitUtility.BITSCALE_8_TO_4_FLOOR[c.g];
- int b = BitUtility.BITSCALE_8_TO_3_FLOOR[c.b];
- colorA = a << 11 | r << 7 | g << 3 | b;
- colorAIsOpaque = 0;
- }
- }
-
- void setColorRgbB(PvrtcColorRgb c) {
- int r = BitUtility.BITSCALE_8_TO_5_CEIL[c.r];
- int g = BitUtility.BITSCALE_8_TO_5_CEIL[c.g];
- int b = BitUtility.BITSCALE_8_TO_5_CEIL[c.b];
- colorB = r << 10 | g << 5 | b;
- colorBIsOpaque = 1;
- }
-
- void setColorRgbaB(PvrtcColorRgba c) {
- int a = BitUtility.BITSCALE_8_TO_3_CEIL[c.a];
- if (a == 7) {
- int r = BitUtility.BITSCALE_8_TO_5_CEIL[c.r];
- int g = BitUtility.BITSCALE_8_TO_5_CEIL[c.g];
- int b = BitUtility.BITSCALE_8_TO_5_CEIL[c.b];
- colorB = r << 10 | g << 5 | b;
- colorBIsOpaque = 1;
- } else {
- int r = BitUtility.BITSCALE_8_TO_4_CEIL[c.r];
- int g = BitUtility.BITSCALE_8_TO_4_CEIL[c.g];
- int b = BitUtility.BITSCALE_8_TO_4_CEIL[c.b];
- colorB = a << 12 | r << 8 | g << 4 | b;
- colorBIsOpaque = 0;
- }
- }
-
- PvrtcColorRgb getColorRgbA() {
- if (colorAIsOpaque != 0) {
- var r = colorA >> 9;
- var g = colorA >> 4 & 0x1f;
- var b = colorA & 0xf;
- return new PvrtcColorRgb(BitUtility.BITSCALE_5_TO_8[r],
- BitUtility.BITSCALE_5_TO_8[g],
- BitUtility.BITSCALE_4_TO_8[b]);
- } else {
- var r = (colorA >> 7) & 0xf;
- var g = (colorA >> 3) & 0xf;
- var b = colorA & 7;
- return new PvrtcColorRgb(BitUtility.BITSCALE_4_TO_8[r],
- BitUtility.BITSCALE_4_TO_8[g],
- BitUtility.BITSCALE_3_TO_8[b]);
- }
- }
-
- PvrtcColorRgba getColorRgbaA() {
- if (colorAIsOpaque != 0) {
- var r = colorA >> 9;
- var g = colorA >> 4 & 0x1f;
- var b = colorA & 0xf;
- return new PvrtcColorRgba(BitUtility.BITSCALE_5_TO_8[r],
- BitUtility.BITSCALE_5_TO_8[g],
- BitUtility.BITSCALE_4_TO_8[b],
- 255);
- } else {
- var a = colorA >> 11 & 7;
- var r = (colorA >> 7) & 0xf;
- var g = (colorA >> 3) & 0xf;
- var b = colorA & 7;
- return new PvrtcColorRgba(BitUtility.BITSCALE_4_TO_8[r],
- BitUtility.BITSCALE_4_TO_8[g],
- BitUtility.BITSCALE_3_TO_8[b],
- BitUtility.BITSCALE_3_TO_8[a]);
- }
- }
-
- PvrtcColorRgb getColorRgbB() {
- if (colorBIsOpaque != 0) {
- var r = colorB >> 10;
- var g = colorB >> 5 & 0x1f;
- var b = colorB & 0x1f;
- return new PvrtcColorRgb(BitUtility.BITSCALE_5_TO_8[r],
- BitUtility.BITSCALE_5_TO_8[g],
- BitUtility.BITSCALE_5_TO_8[b]);
- } else {
- var r = colorB >> 8 & 0xf;
- var g = colorB >> 4 & 0xf;
- var b = colorB & 0xf;
- return new PvrtcColorRgb(BitUtility.BITSCALE_4_TO_8[r],
- BitUtility.BITSCALE_4_TO_8[g],
- BitUtility.BITSCALE_4_TO_8[b]);
- }
- }
-
- PvrtcColorRgba getColorRgbaB() {
- if (colorBIsOpaque != 0) {
- var r = colorB >> 10;
- var g = colorB >> 5 & 0x1f;
- var b = colorB & 0x1f;
- return new PvrtcColorRgba(BitUtility.BITSCALE_5_TO_8[r],
- BitUtility.BITSCALE_5_TO_8[g],
- BitUtility.BITSCALE_5_TO_8[b],
- 255);
- } else {
- var a = colorB >> 12 & 7;
- var r = colorB >> 8 & 0xf;
- var g = colorB >> 4 & 0xf;
- var b = colorB & 0xf;
- return new PvrtcColorRgba(BitUtility.BITSCALE_4_TO_8[r],
- BitUtility.BITSCALE_4_TO_8[g],
- BitUtility.BITSCALE_4_TO_8[b],
- BitUtility.BITSCALE_3_TO_8[a]);
- }
- }
-
- int _usePunchthroughAlpha = 0;
- int _colorA = 0;
- int _colorAIsOpaque = 0;
- int _colorB = 0;
- int _colorBIsOpaque = 0;
-
- int _getColorData() =>
- ((usePunchthroughAlpha & 1)) |
- ((colorA & BITS_14) << 1) |
- ((colorAIsOpaque & 1) << 15) |
- ((colorB & BITS_15) << 16) |
- ((colorBIsOpaque & 1) << 31);
-
- void _update() {
- int x = colorData;
- usePunchthroughAlpha = x & 1;
- colorA = (x >> 1) & BITS_14;
- colorAIsOpaque = (x >> 15) & 1;
- colorB = (x >> 16) & BITS_15;
- colorBIsOpaque = (x >> 31) & 1;
- }
-
- static int _getMortonNumber(int x, int y) {
- return MORTON_TABLE[x >> 8] << 17 | MORTON_TABLE[y >> 8] << 16 |
- MORTON_TABLE[x & 0xFF] << 1 | MORTON_TABLE[y & 0xFF];
- }
-
- static const BITS_14 = (1 << 14) - 1;
- static const BITS_15 = (1 << 15) - 1;
-
- static const BILINEAR_FACTORS = const [
- const [ 4, 4, 4, 4 ],
- const [ 2, 6, 2, 6 ],
- const [ 8, 0, 8, 0 ],
- const [ 6, 2, 6, 2 ],
-
- const [ 2, 2, 6, 6 ],
- const [ 1, 3, 3, 9 ],
- const [ 4, 0, 12, 0 ],
- const [ 3, 1, 9, 3 ],
-
- const [ 8, 8, 0, 0 ],
- const [ 4, 12, 0, 0 ],
- const [ 16, 0, 0, 0 ],
- const [ 12, 4, 0, 0 ],
-
- const [ 6, 6, 2, 2 ],
- const [ 3, 9, 1, 3 ],
- const [ 12, 0, 4, 0 ],
- const [ 9, 3, 3, 1 ],
- ];
-
- // Weights are { colorA, colorB, alphaA, alphaB }
- static const WEIGHTS = const [
- // Weights for Mode=0
- const [ 8, 0, 8, 0 ],
- const [ 5, 3, 5, 3 ],
- const [ 3, 5, 3, 5 ],
- const [ 0, 8, 0, 8 ],
-
- // Weights for Mode=1
- const [ 8, 0, 8, 0 ],
- const [ 4, 4, 4, 4 ],
- const [ 4, 4, 0, 0 ],
- const [ 0, 8, 0, 8 ],
- ];
-
- static const MORTON_TABLE = const [
- 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
- 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
- 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
- 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
- 0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
- 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
- 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
- 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
- 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
- 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
- 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
- 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
- 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
- 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
- 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
- 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
- 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
- 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
- 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
- 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
- 0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
- 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
- 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
- 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
- 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
- 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
- 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
- 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
- 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
- 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
- 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
- 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555
- ];
-}
+import 'dart:typed_data';
+
+import 'pvrtc_bit_utility.dart';
+import 'pvrtc_color.dart';
+
+/**
+ * Ported from Jeffrey Lim's PVRTC encoder/decoder,
+ * https://bitbucket.org/jthlim/pvrtccompressor
+ */
+class PvrtcPacket {
+ Uint32List rawData;
+ int index;
+
+ PvrtcPacket(TypedData data)
+ : rawData = Uint32List.view(data.buffer);
+
+ void setBlock(int x, int y) => setIndex(_getMortonNumber(x, y));
+
+ void setIndex(int i) {
+ // A PvrtcPacket uses 2 uint32 values, so get the physical index
+ // from the logical index by multiplying by 2.
+ index = i << 1;
+ // Pull in the values from the raw data.
+ _update();
+ }
+
+ int get modulationData => rawData[index];
+
+ set modulationData(int x) => rawData[index] = x;
+
+ int get colorData => rawData[index + 1];
+
+ set colorData(int x) => rawData[index + 1] = x;
+
+ int get usePunchthroughAlpha => _usePunchthroughAlpha;
+
+ set usePunchthroughAlpha(int x) {
+ _usePunchthroughAlpha = x;
+ colorData = _getColorData();
+ }
+
+ int get colorA => _colorA;
+
+ set colorA(int x) {
+ _colorA = x;
+ colorData = _getColorData();
+ }
+
+ int get colorAIsOpaque => _colorAIsOpaque;
+
+ set colorAIsOpaque(int x) {
+ _colorAIsOpaque = x;
+ colorData = _getColorData();
+ }
+
+ int get colorB => _colorB;
+
+ set colorB(int x) {
+ _colorB = x;
+ colorData = _getColorData();
+ }
+
+ int get colorBIsOpaque => _colorBIsOpaque;
+
+ set colorBIsOpaque(int x) {
+ _colorBIsOpaque = x;
+ colorData = _getColorData();
+ }
+
+ void setColorRgbA(PvrtcColorRgb c) {
+ int r = BitUtility.BITSCALE_8_TO_5_FLOOR[c.r];
+ int g = BitUtility.BITSCALE_8_TO_5_FLOOR[c.g];
+ int b = BitUtility.BITSCALE_8_TO_4_FLOOR[c.b];
+ colorA = r << 9 | g << 4 | b;
+ colorAIsOpaque = 1;
+ }
+
+ void setColorRgbaA(PvrtcColorRgba c) {
+ int a = BitUtility.BITSCALE_8_TO_3_FLOOR[c.a];
+ if (a == 7) {
+ int r = BitUtility.BITSCALE_8_TO_5_FLOOR[c.r];
+ int g = BitUtility.BITSCALE_8_TO_5_FLOOR[c.g];
+ int b = BitUtility.BITSCALE_8_TO_4_FLOOR[c.b];
+ colorA = r << 9 | g << 4 | b;
+ colorAIsOpaque = 1;
+ } else {
+ int r = BitUtility.BITSCALE_8_TO_4_FLOOR[c.r];
+ int g = BitUtility.BITSCALE_8_TO_4_FLOOR[c.g];
+ int b = BitUtility.BITSCALE_8_TO_3_FLOOR[c.b];
+ colorA = a << 11 | r << 7 | g << 3 | b;
+ colorAIsOpaque = 0;
+ }
+ }
+
+ void setColorRgbB(PvrtcColorRgb c) {
+ int r = BitUtility.BITSCALE_8_TO_5_CEIL[c.r];
+ int g = BitUtility.BITSCALE_8_TO_5_CEIL[c.g];
+ int b = BitUtility.BITSCALE_8_TO_5_CEIL[c.b];
+ colorB = r << 10 | g << 5 | b;
+ colorBIsOpaque = 1;
+ }
+
+ void setColorRgbaB(PvrtcColorRgba c) {
+ int a = BitUtility.BITSCALE_8_TO_3_CEIL[c.a];
+ if (a == 7) {
+ int r = BitUtility.BITSCALE_8_TO_5_CEIL[c.r];
+ int g = BitUtility.BITSCALE_8_TO_5_CEIL[c.g];
+ int b = BitUtility.BITSCALE_8_TO_5_CEIL[c.b];
+ colorB = r << 10 | g << 5 | b;
+ colorBIsOpaque = 1;
+ } else {
+ int r = BitUtility.BITSCALE_8_TO_4_CEIL[c.r];
+ int g = BitUtility.BITSCALE_8_TO_4_CEIL[c.g];
+ int b = BitUtility.BITSCALE_8_TO_4_CEIL[c.b];
+ colorB = a << 12 | r << 8 | g << 4 | b;
+ colorBIsOpaque = 0;
+ }
+ }
+
+ PvrtcColorRgb getColorRgbA() {
+ if (colorAIsOpaque != 0) {
+ var r = colorA >> 9;
+ var g = colorA >> 4 & 0x1f;
+ var b = colorA & 0xf;
+ return new PvrtcColorRgb(BitUtility.BITSCALE_5_TO_8[r],
+ BitUtility.BITSCALE_5_TO_8[g],
+ BitUtility.BITSCALE_4_TO_8[b]);
+ } else {
+ var r = (colorA >> 7) & 0xf;
+ var g = (colorA >> 3) & 0xf;
+ var b = colorA & 7;
+ return new PvrtcColorRgb(BitUtility.BITSCALE_4_TO_8[r],
+ BitUtility.BITSCALE_4_TO_8[g],
+ BitUtility.BITSCALE_3_TO_8[b]);
+ }
+ }
+
+ PvrtcColorRgba getColorRgbaA() {
+ if (colorAIsOpaque != 0) {
+ var r = colorA >> 9;
+ var g = colorA >> 4 & 0x1f;
+ var b = colorA & 0xf;
+ return new PvrtcColorRgba(BitUtility.BITSCALE_5_TO_8[r],
+ BitUtility.BITSCALE_5_TO_8[g],
+ BitUtility.BITSCALE_4_TO_8[b],
+ 255);
+ } else {
+ var a = colorA >> 11 & 7;
+ var r = (colorA >> 7) & 0xf;
+ var g = (colorA >> 3) & 0xf;
+ var b = colorA & 7;
+ return new PvrtcColorRgba(BitUtility.BITSCALE_4_TO_8[r],
+ BitUtility.BITSCALE_4_TO_8[g],
+ BitUtility.BITSCALE_3_TO_8[b],
+ BitUtility.BITSCALE_3_TO_8[a]);
+ }
+ }
+
+ PvrtcColorRgb getColorRgbB() {
+ if (colorBIsOpaque != 0) {
+ var r = colorB >> 10;
+ var g = colorB >> 5 & 0x1f;
+ var b = colorB & 0x1f;
+ return new PvrtcColorRgb(BitUtility.BITSCALE_5_TO_8[r],
+ BitUtility.BITSCALE_5_TO_8[g],
+ BitUtility.BITSCALE_5_TO_8[b]);
+ } else {
+ var r = colorB >> 8 & 0xf;
+ var g = colorB >> 4 & 0xf;
+ var b = colorB & 0xf;
+ return new PvrtcColorRgb(BitUtility.BITSCALE_4_TO_8[r],
+ BitUtility.BITSCALE_4_TO_8[g],
+ BitUtility.BITSCALE_4_TO_8[b]);
+ }
+ }
+
+ PvrtcColorRgba getColorRgbaB() {
+ if (colorBIsOpaque != 0) {
+ var r = colorB >> 10;
+ var g = colorB >> 5 & 0x1f;
+ var b = colorB & 0x1f;
+ return new PvrtcColorRgba(BitUtility.BITSCALE_5_TO_8[r],
+ BitUtility.BITSCALE_5_TO_8[g],
+ BitUtility.BITSCALE_5_TO_8[b],
+ 255);
+ } else {
+ var a = colorB >> 12 & 7;
+ var r = colorB >> 8 & 0xf;
+ var g = colorB >> 4 & 0xf;
+ var b = colorB & 0xf;
+ return new PvrtcColorRgba(BitUtility.BITSCALE_4_TO_8[r],
+ BitUtility.BITSCALE_4_TO_8[g],
+ BitUtility.BITSCALE_4_TO_8[b],
+ BitUtility.BITSCALE_3_TO_8[a]);
+ }
+ }
+
+ int _usePunchthroughAlpha = 0;
+ int _colorA = 0;
+ int _colorAIsOpaque = 0;
+ int _colorB = 0;
+ int _colorBIsOpaque = 0;
+
+ int _getColorData() =>
+ ((usePunchthroughAlpha & 1)) |
+ ((colorA & BITS_14) << 1) |
+ ((colorAIsOpaque & 1) << 15) |
+ ((colorB & BITS_15) << 16) |
+ ((colorBIsOpaque & 1) << 31);
+
+ void _update() {
+ int x = colorData;
+ usePunchthroughAlpha = x & 1;
+ colorA = (x >> 1) & BITS_14;
+ colorAIsOpaque = (x >> 15) & 1;
+ colorB = (x >> 16) & BITS_15;
+ colorBIsOpaque = (x >> 31) & 1;
+ }
+
+ static int _getMortonNumber(int x, int y) {
+ return MORTON_TABLE[x >> 8] << 17 | MORTON_TABLE[y >> 8] << 16 |
+ MORTON_TABLE[x & 0xFF] << 1 | MORTON_TABLE[y & 0xFF];
+ }
+
+ static const BITS_14 = (1 << 14) - 1;
+ static const BITS_15 = (1 << 15) - 1;
+
+ static const BILINEAR_FACTORS = const [
+ const [ 4, 4, 4, 4 ],
+ const [ 2, 6, 2, 6 ],
+ const [ 8, 0, 8, 0 ],
+ const [ 6, 2, 6, 2 ],
+
+ const [ 2, 2, 6, 6 ],
+ const [ 1, 3, 3, 9 ],
+ const [ 4, 0, 12, 0 ],
+ const [ 3, 1, 9, 3 ],
+
+ const [ 8, 8, 0, 0 ],
+ const [ 4, 12, 0, 0 ],
+ const [ 16, 0, 0, 0 ],
+ const [ 12, 4, 0, 0 ],
+
+ const [ 6, 6, 2, 2 ],
+ const [ 3, 9, 1, 3 ],
+ const [ 12, 0, 4, 0 ],
+ const [ 9, 3, 3, 1 ],
+ ];
+
+ // Weights are { colorA, colorB, alphaA, alphaB }
+ static const WEIGHTS = const [
+ // Weights for Mode=0
+ const [ 8, 0, 8, 0 ],
+ const [ 5, 3, 5, 3 ],
+ const [ 3, 5, 3, 5 ],
+ const [ 0, 8, 0, 8 ],
+
+ // Weights for Mode=1
+ const [ 8, 0, 8, 0 ],
+ const [ 4, 4, 4, 4 ],
+ const [ 4, 4, 0, 0 ],
+ const [ 0, 8, 0, 8 ],
+ ];
+
+ static const MORTON_TABLE = const [
+ 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
+ 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
+ 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
+ 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
+ 0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
+ 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
+ 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
+ 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
+ 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
+ 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
+ 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
+ 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
+ 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
+ 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
+ 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
+ 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
+ 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
+ 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
+ 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
+ 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
+ 0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
+ 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
+ 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
+ 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
+ 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
+ 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
+ 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
+ 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
+ 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
+ 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
+ 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
+ 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555
+ ];
+}
diff --git a/image/lib/src/formats/tga/tga_info.dart b/image/lib/src/formats/tga/tga_info.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/tga_decoder.dart b/image/lib/src/formats/tga_decoder.dart
old mode 100644
new mode 100755
index 7c73200..1bf9053
--- a/image/lib/src/formats/tga_decoder.dart
+++ b/image/lib/src/formats/tga_decoder.dart
@@ -18,7 +18,7 @@
* Is the given file a valid TGA image?
*/
bool isValidFile(List<int> data) {
- InputBuffer input = new InputBuffer(data,
+ InputBuffer input = InputBuffer(data,
bigEndian: true);
InputBuffer header = input.readBytes(18);
@@ -33,8 +33,8 @@
}
DecodeInfo startDecode(List<int> data) {
- info = new TgaInfo();
- input = new InputBuffer(data, bigEndian: true);
+ info = TgaInfo();
+ input = InputBuffer(data, bigEndian: true);
InputBuffer header = input.readBytes(18);
if (header[2] != 2) {
@@ -60,7 +60,7 @@
}
input.offset = info.imageOffset;
- Image image = new Image(info.width, info.height, Image.RGB);
+ Image image = Image(info.width, info.height, Image.RGB);
for (int y = image.height - 1; y >= 0; --y) {
for (int x = 0; x < image.width; ++x) {
int b = input.readByte();
@@ -88,7 +88,7 @@
return null;
}
- Animation anim = new Animation();
+ Animation anim = Animation();
anim.width = image.width;
anim.height = image.height;
anim.addFrame(image);
diff --git a/image/lib/src/formats/tga_encoder.dart b/image/lib/src/formats/tga_encoder.dart
old mode 100644
new mode 100755
index 2287c67..7143fbd
--- a/image/lib/src/formats/tga_encoder.dart
+++ b/image/lib/src/formats/tga_encoder.dart
@@ -8,9 +8,9 @@
*/
class TgaEncoder extends Encoder {
List<int> encodeImage(Image image) {
- OutputBuffer out = new OutputBuffer(bigEndian: true);
+ OutputBuffer out = OutputBuffer(bigEndian: true);
- List<int> header = new List<int>(18);
+ List<int> header = List<int>(18);
header.fillRange(0, 18, 0);
header[2] = 2;
header[12] = image.width & 0xff;
diff --git a/image/lib/src/formats/tiff/tiff_bit_reader.dart b/image/lib/src/formats/tiff/tiff_bit_reader.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/tiff/tiff_entry.dart b/image/lib/src/formats/tiff/tiff_entry.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/tiff/tiff_fax_decoder.dart b/image/lib/src/formats/tiff/tiff_fax_decoder.dart
old mode 100644
new mode 100755
index 9e8421c..097be05
--- a/image/lib/src/formats/tiff/tiff_fax_decoder.dart
+++ b/image/lib/src/formats/tiff/tiff_fax_decoder.dart
@@ -25,8 +25,8 @@
int oneD;
TiffFaxDecoder(this.fillOrder, this.width, this.height) {
- prevChangingElems = new List<int>(width);
- currChangingElems = new List<int>(width);
+ prevChangingElems = List<int>(width);
+ currChangingElems = List<int>(width);
}
/**
@@ -197,7 +197,7 @@
int scanlineStride = (width + 7) ~/ 8;
int a0, a1, b1, b2;
- List<int> b = new List<int>(2);
+ List<int> b = List<int>(2);
int entry, code, bits;
bool isWhite;
int currIndex = 0;
@@ -347,7 +347,7 @@
List<int> temp;
// Return values from getNextChangingElement
- List<int> b = new List<int>(2);
+ List<int> b = List<int>(2);
uncompressedMode = ((tiffT6Options & 0x02) >> 1);
diff --git a/image/lib/src/formats/tiff/tiff_image.dart b/image/lib/src/formats/tiff/tiff_image.dart
old mode 100644
new mode 100755
index 8c539de..5c3a8e3
--- a/image/lib/src/formats/tiff/tiff_image.dart
+++ b/image/lib/src/formats/tiff/tiff_image.dart
@@ -50,14 +50,14 @@
HdrImage hdrImage;
TiffImage(InputBuffer p) {
- InputBuffer p3 = new InputBuffer.from(p);
+ InputBuffer p3 = InputBuffer.from(p);
int numDirEntries = p.readUint16();
for (int i = 0; i < numDirEntries; ++i) {
int tag = p.readUint16();
int type = p.readUint16();
int numValues = p.readUint32();
- TiffEntry entry = new TiffEntry(tag, type, numValues);
+ TiffEntry entry = TiffEntry(tag, type, numValues);
// The value for the tag is either stored in another location,
// or within the tag itself (if the size fits in 4 bytes).
@@ -223,7 +223,7 @@
compression != null;
Image decode(InputBuffer p) {
- image = new Image(width, height);
+ image = Image(width, height);
for (int tileY = 0, ti = 0; tileY < tilesY; ++tileY) {
for (int tileX = 0; tileX < tilesX; ++tileX, ++ti) {
_decodeTile(p, tileX, tileY);
@@ -233,7 +233,7 @@
}
HdrImage decodeHdr(InputBuffer p) {
- hdrImage = new HdrImage.create(width, height, 4, HdrImage.HALF);
+ hdrImage = HdrImage.create(width, height, 4, HdrImage.HALF);
for (int tileY = 0, ti = 0; tileY < tilesY; ++tileY) {
for (int tileX = 0; tileX < tilesX; ++tileX, ++ti) {
_decodeTile(p, tileX, tileY);
@@ -270,8 +270,8 @@
bdata = p;
} else if (compression == COMPRESSION_LZW) {
- bdata = new InputBuffer(new Uint8List(bytesInThisTile));
- LzwDecoder decoder = new LzwDecoder();
+ bdata = InputBuffer(new Uint8List(bytesInThisTile));
+ LzwDecoder decoder = LzwDecoder();
try {
decoder.decode(new InputBuffer.from(p, offset: 0, length: byteCount),
bdata.buffer);
@@ -290,27 +290,27 @@
}
}
} else if (compression == COMPRESSION_PACKBITS) {
- bdata = new InputBuffer(new Uint8List(bytesInThisTile));
+ bdata = InputBuffer(new Uint8List(bytesInThisTile));
_decodePackbits(p, bytesInThisTile, bdata.buffer);
} else if (compression == COMPRESSION_DEFLATE) {
List<int> data = p.toList(0, byteCount);
- List<int> outData = new Inflate(data).getBytes();
- bdata = new InputBuffer(outData);
+ List<int> outData = Inflate(data).getBytes();
+ bdata = InputBuffer(outData);
} else if (compression == COMPRESSION_ZIP) {
List<int> data = p.toList(0, byteCount);
- List<int> outData = new ZLibDecoder().decodeBytes(data);
- bdata = new InputBuffer(outData);
+ List<int> outData = ZLibDecoder().decodeBytes(data);
+ bdata = InputBuffer(outData);
} else if (compression == COMPRESSION_OLD_JPEG) {
if (image == null) {
- image = new Image(width, height);
+ image = Image(width, height);
}
List<int> data = p.toList(0, byteCount);
- Image tile = new JpegDecoder().decodeImage(data);
+ Image tile = JpegDecoder().decodeImage(data);
_jpegToImage(tile, image, outX, outY, tileWidth, tileHeight);
if (hdrImage != null) {
- hdrImage = new HdrImage.fromImage(image);
+ hdrImage = HdrImage.fromImage(image);
}
return;
} else {
@@ -540,12 +540,12 @@
} else {
bytesInThisTile = (tileWidth ~/ 8 + 1) * tileHeight;
}
- bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight));
+ bdata = InputBuffer(new Uint8List(tileWidth * tileHeight));
_decodePackbits(p, bytesInThisTile, bdata.buffer);
} else if (compression == COMPRESSION_LZW) {
- bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight));
+ bdata = InputBuffer(new Uint8List(tileWidth * tileHeight));
- LzwDecoder decoder = new LzwDecoder();
+ LzwDecoder decoder = LzwDecoder();
decoder.decode(new InputBuffer.from(p, length: byteCount), bdata.buffer);
// Horizontal Differencing Predictor
@@ -560,21 +560,21 @@
}
}
} else if (compression == COMPRESSION_CCITT_RLE) {
- bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight));
+ bdata = InputBuffer(new Uint8List(tileWidth * tileHeight));
try {
new TiffFaxDecoder(fillOrder, tileWidth, tileHeight).
decode1D(bdata, p, 0, tileHeight);
} catch (_) {
}
} else if (compression == COMPRESSION_CCITT_FAX3) {
- bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight));
+ bdata = InputBuffer(new Uint8List(tileWidth * tileHeight));
try {
new TiffFaxDecoder(fillOrder, tileWidth, tileHeight).
decode2D(bdata, p, 0, tileHeight, t4Options);
} catch (_) {
}
} else if (compression == COMPRESSION_CCITT_FAX4) {
- bdata = new InputBuffer(new Uint8List(tileWidth * tileHeight));
+ bdata = InputBuffer(new Uint8List(tileWidth * tileHeight));
try {
new TiffFaxDecoder(fillOrder, tileWidth, tileHeight).
decodeT6(bdata, p, 0, tileHeight, t6Options);
@@ -582,12 +582,12 @@
}
} else if (compression == COMPRESSION_ZIP) {
List<int> data = p.toList(0, byteCount);
- List<int> outData = new ZLibDecoder().decodeBytes(data);
- bdata = new InputBuffer(outData);
+ List<int> outData = ZLibDecoder().decodeBytes(data);
+ bdata = InputBuffer(outData);
} else if (compression == COMPRESSION_DEFLATE) {
List<int> data = p.toList(0, byteCount);
- List<int> outData = new Inflate(data).getBytes();
- bdata = new InputBuffer(outData);
+ List<int> outData = Inflate(data).getBytes();
+ bdata = InputBuffer(outData);
} else if (compression == COMPRESSION_NONE) {
bdata = p;
} else {
@@ -598,7 +598,7 @@
return;
}
- TiffBitReader br = new TiffBitReader(bdata);
+ TiffBitReader br = TiffBitReader(bdata);
final int white = isWhiteZero ? 0xff000000 : 0xffffffff;
final int black = isWhiteZero ? 0xffffffff : 0xff000000;
diff --git a/image/lib/src/formats/tiff/tiff_info.dart b/image/lib/src/formats/tiff/tiff_info.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/formats/tiff/tiff_lzw_decoder.dart b/image/lib/src/formats/tiff/tiff_lzw_decoder.dart
old mode 100644
new mode 100755
index d11d8a1..aaf0a6e
--- a/image/lib/src/formats/tiff/tiff_lzw_decoder.dart
+++ b/image/lib/src/formats/tiff/tiff_lzw_decoder.dart
@@ -114,8 +114,8 @@
* Initialize the string table.
*/
void _initializeStringTable() {
- _table = new Uint8List(LZ_MAX_CODE + 1);
- _prefix = new Uint32List(LZ_MAX_CODE + 1);
+ _table = Uint8List(LZ_MAX_CODE + 1);
+ _prefix = Uint32List(LZ_MAX_CODE + 1);
_prefix.fillRange(0, _prefix.length, NO_SUCH_CODE);
for (int i = 0; i < 256; i++) {
@@ -138,7 +138,7 @@
List<int> _out;
int _outPointer;
- Uint8List _buffer = new Uint8List(4096);
+ Uint8List _buffer = Uint8List(4096);
Uint8List _table;
Uint32List _prefix;
int _tableIndex;
diff --git a/image/lib/src/formats/tiff_decoder.dart b/image/lib/src/formats/tiff_decoder.dart
old mode 100644
new mode 100755
index 817bcfc..12ef08f
--- a/image/lib/src/formats/tiff_decoder.dart
+++ b/image/lib/src/formats/tiff_decoder.dart
@@ -23,7 +23,7 @@
* If the file is not a valid Gif image, null is returned.
*/
TiffInfo startDecode(List<int> bytes) {
- _input = new InputBuffer(new Uint8List.fromList(bytes));
+ _input = InputBuffer(new Uint8List.fromList(bytes));
info = _readHeader(_input);
return info;
}
@@ -55,7 +55,7 @@
* decoding the file, null is returned.
*/
Image decodeImage(List<int> data, {int frame: 0}) {
- InputBuffer ptr = new InputBuffer(new Uint8List.fromList(data));
+ InputBuffer ptr = InputBuffer(new Uint8List.fromList(data));
TiffInfo info = _readHeader(ptr);
if (info == null) {
@@ -66,7 +66,7 @@
}
HdrImage decodeHdrImage(List<int> data, {int frame: 0}) {
- InputBuffer ptr = new InputBuffer(new Uint8List.fromList(data));
+ InputBuffer ptr = InputBuffer(new Uint8List.fromList(data));
TiffInfo info = _readHeader(ptr);
if (info == null) {
@@ -86,7 +86,7 @@
return null;
}
- Animation anim = new Animation();
+ Animation anim = Animation();
anim.width = info.width;
anim.height = info.height;
anim.frameType = Animation.PAGE;
@@ -105,7 +105,7 @@
* Read the TIFF header and IFD blocks.
*/
TiffInfo _readHeader(InputBuffer p) {
- TiffInfo info = new TiffInfo();
+ TiffInfo info = TiffInfo();
int byteOrder = p.readUint16();
if (byteOrder != TIFF_LITTLE_ENDIAN &&
byteOrder != TIFF_BIG_ENDIAN) {
@@ -128,13 +128,13 @@
int offset = p.readUint32();
info.ifdOffset = offset;
- InputBuffer p2 = new InputBuffer.from(p);
+ InputBuffer p2 = InputBuffer.from(p);
p2.offset = offset;
while (offset != 0) {
TiffImage img;
try {
- img = new TiffImage(p2);
+ img = TiffImage(p2);
if (!img.isValid) {
break;
}
diff --git a/image/lib/src/formats/webp/vp8.dart b/image/lib/src/formats/webp/vp8.dart
old mode 100644
new mode 100755
index 04682aa..9d27dfd
--- a/image/lib/src/formats/webp/vp8.dart
+++ b/image/lib/src/formats/webp/vp8.dart
@@ -1,1974 +1,1974 @@
-import 'dart:typed_data';
-
-import '../../image.dart';
-import '../../util/input_buffer.dart';
-import 'vp8_bit_reader.dart';
-import 'vp8_filter.dart';
-import 'vp8_types.dart';
-import 'webp_alpha.dart';
-import 'webp_info.dart';
-
-/**
- * WebP lossy format.
- */
-class VP8 {
- InputBuffer input;
- InternalWebPInfo _webp;
-
- VP8(InputBuffer input, this._webp) :
- this.input = input;
-
- WebPInfo get webp => _webp;
-
- bool decodeHeader() {
- int bits = input.readUint24();
-
- final bool keyFrame = (bits & 1) == 0;
- if (!keyFrame) {
- return false;
- }
-
- if (((bits >> 1) & 7) > 3) {
- return false; // unknown profile
- }
-
- if (((bits >> 4) & 1) == 0) {
- return false; // first frame is invisible!
- }
-
- _frameHeader.keyFrame = (bits & 1) == 0;
- _frameHeader.profile = (bits >> 1) & 7;
- _frameHeader.show = (bits >> 4) & 1;
- _frameHeader.partitionLength = (bits >> 5);
-
- int signature = input.readUint24();
- if (signature != VP8_SIGNATURE) {
- return false;
- }
-
- webp.width = input.readUint16();
- webp.height = input.readUint16();
-
- return true;
- }
-
- Image decode() {
- if (!_getHeaders()) {
- return null;
- }
-
- output = new Image(webp.width, webp.height);
-
- // Will allocate memory and prepare everything.
- if (!_initFrame()) {
- return null;
- }
-
- // Main decoding loop
- if (!_parseFrame()) {
- return null;
- }
-
- return output;
- }
-
- bool _getHeaders() {
- if (!decodeHeader()) {
- return false;
- }
-
- _proba = new VP8Proba();
- for (int i = 0; i < NUM_MB_SEGMENTS; ++i) {
- _dqm[i] = new VP8QuantMatrix();
- }
-
- _picHeader.width = webp.width;
- _picHeader.height = webp.height;
- _picHeader.xscale = (webp.width >> 8) >> 6;
- _picHeader.yscale = (webp.height >> 8) >> 6;
-
- _cropTop = 0;
- _cropLeft = 0;
- _cropRight = webp.width;
- _cropBottom = webp.height;
-
- _mbWidth = (webp.width + 15) >> 4;
- _mbHeight = (webp.height + 15) >> 4;
-
- _segment = 0;
-
- br = new VP8BitReader(input.subset(_frameHeader.partitionLength));
- input.skip(_frameHeader.partitionLength);
-
- _picHeader.colorspace = br.get();
- _picHeader.clampType = br.get();
-
- if (!_parseSegmentHeader(_segmentHeader, _proba)) {
- return false;
- }
-
- // Filter specs
- if (!_parseFilterHeader()) {
- return false;
- }
-
- if (!_parsePartitions(input)) {
- return false;
- }
-
- // quantizer change
- _parseQuant();
-
- // Frame buffer marking
- br.get(); // ignore the value of update_proba_
-
- _parseProba();
-
- return true;
- }
-
- bool _parseSegmentHeader(VP8SegmentHeader hdr, VP8Proba proba) {
- hdr.useSegment = br.get() != 0;
- if (hdr.useSegment) {
- hdr.updateMap = br.get() != 0;
- if (br.get() != 0) { // update data
- hdr.absoluteDelta = br.get() != 0;
- for (int s = 0; s < NUM_MB_SEGMENTS; ++s) {
- hdr.quantizer[s] = br.get() != 0 ? br.getSignedValue(7) : 0;
- }
- for (int s = 0; s < NUM_MB_SEGMENTS; ++s) {
- hdr.filterStrength[s] = br.get() != 0 ? br.getSignedValue(6) : 0;
- }
- }
- if (hdr.updateMap) {
- for (int s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
- proba.segments[s] = br.get() != 0 ? br.getValue(8) : 255;
- }
- }
- } else {
- hdr.updateMap = false;
- }
-
- return true;
- }
-
- bool _parseFilterHeader() {
- VP8FilterHeader hdr = _filterHeader;
- hdr.simple = br.get() != 0;
- hdr.level = br.getValue(6);
- hdr.sharpness = br.getValue(3);
- hdr.useLfDelta = br.get() != 0;
- if (hdr.useLfDelta) {
- if (br.get() != 0) { // update lf-delta?
- for (int i = 0; i < NUM_REF_LF_DELTAS; ++i) {
- if (br.get() != 0) {
- hdr.refLfDelta[i] = br.getSignedValue(6);
- }
- }
-
- for (int i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
- if (br.get() != 0) {
- hdr.modeLfDelta[i] = br.getSignedValue(6);
- }
- }
- }
- }
-
- _filterType = (hdr.level == 0) ? 0 : hdr.simple ? 1 : 2;
-
- return true;
- }
-
- /**
- * This function returns VP8_STATUS_SUSPENDED if we don't have all the
- * necessary data in 'buf'.
- * This case is not necessarily an error (for incremental decoding).
- * Still, no bitreader is ever initialized to make it possible to read
- * unavailable memory.
- * If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
- * is returned, and this is an unrecoverable error.
- * If the partitions were positioned ok, VP8_STATUS_OK is returned.
- */
- bool _parsePartitions(InputBuffer input) {
- int sz = 0;
- int bufEnd = input.length;
-
- _numPartitions = 1 << br.getValue(2);
- int lastPart = _numPartitions - 1;
- int partStart = lastPart * 3;
- if (bufEnd < partStart) {
- // we can't even read the sizes with sz[]! That's a failure.
- return false;
- }
-
- for (int p = 0; p < lastPart; ++p) {
- InputBuffer szb = input.peekBytes(3, sz);
- final int psize = szb[0] | (szb[1] << 8) | (szb[2] << 16);
- int partEnd = partStart + psize;
- if (partEnd > bufEnd) {
- partEnd = bufEnd;
- }
-
- InputBuffer pin = input.subset(partEnd - partStart, position: partStart);
- _partitions[p] = new VP8BitReader(pin);
- partStart = partEnd;
- sz += 3;
- }
-
- InputBuffer pin = input.subset(bufEnd - partStart,
- position: input.position + partStart);
- _partitions[lastPart] = new VP8BitReader(pin);
-
- // Init is ok, but there's not enough data
- return (partStart < bufEnd) ? true : false;
- }
-
- void _parseQuant() {
- final int base_q0 = br.getValue(7);
- final int dqy1_dc = br.get() != 0 ? br.getSignedValue(4) : 0;
- final int dqy2_dc = br.get() != 0 ? br.getSignedValue(4) : 0;
- final int dqy2_ac = br.get() != 0 ? br.getSignedValue(4) : 0;
- final int dquv_dc = br.get() != 0 ? br.getSignedValue(4) : 0;
- final int dquv_ac = br.get() != 0 ? br.getSignedValue(4) : 0;
-
- VP8SegmentHeader hdr = _segmentHeader;
-
- for (int i = 0; i < NUM_MB_SEGMENTS; ++i) {
- int q;
- if (hdr.useSegment) {
- q = hdr.quantizer[i];
- if (!hdr.absoluteDelta) {
- q += base_q0;
- }
- } else {
- if (i > 0) {
- _dqm[i] = _dqm[0];
- continue;
- } else {
- q = base_q0;
- }
- }
-
- VP8QuantMatrix m = _dqm[i];
- m.y1Mat[0] = DC_TABLE[_clip(q + dqy1_dc, 127)];
- m.y1Mat[1] = AC_TABLE[_clip(q + 0, 127)];
-
- m.y2Mat[0] = DC_TABLE[_clip(q + dqy2_dc, 127)] * 2;
- // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16.
- // The smallest precision for that is '(x*6349) >> 12' but 16 is a good
- // word size.
- m.y2Mat[1] = (AC_TABLE[_clip(q + dqy2_ac, 127)] * 101581) >> 16;
- if (m.y2Mat[1] < 8) {
- m.y2Mat[1] = 8;
- }
-
- m.uvMat[0] = DC_TABLE[_clip(q + dquv_dc, 117)];
- m.uvMat[1] = AC_TABLE[_clip(q + dquv_ac, 127)];
-
- m.uvQuant = q + dquv_ac; // for dithering strength evaluation
- }
- }
-
- void _parseProba() {
- VP8Proba proba = _proba;
- for (int t = 0; t < NUM_TYPES; ++t) {
- for (int b = 0; b < NUM_BANDS; ++b) {
- for (int c = 0; c < NUM_CTX; ++c) {
- for (int p = 0; p < NUM_PROBAS; ++p) {
- final int v = br.getBit(COEFFS_UPDATE_PROBA[t][b][c][p]) != 0 ?
- br.getValue(8) : COEFFS_PROBA_0[t][b][c][p];
- proba.bands[t][b].probas[c][p] = v;
- }
- }
- }
- }
-
- _useSkipProba = br.get() != 0;
- if (_useSkipProba) {
- _skipP = br.getValue(8);
- }
- }
-
- /**
- * Precompute the filtering strength for each segment and each i4x4/i16x16
- * mode.
- */
- void _precomputeFilterStrengths() {
- if (_filterType > 0) {
- VP8FilterHeader hdr = _filterHeader;
- for (int s = 0; s < NUM_MB_SEGMENTS; ++s) {
- // First, compute the initial level
- int baseLevel;
- if (_segmentHeader.useSegment) {
- baseLevel = _segmentHeader.filterStrength[s];
- if (!_segmentHeader.absoluteDelta) {
- baseLevel += hdr.level;
- }
- } else {
- baseLevel = hdr.level;
- }
-
- for (int i4x4 = 0; i4x4 <= 1; ++i4x4) {
- VP8FInfo info = _fStrengths[s][i4x4];
- int level = baseLevel;
- if (hdr.useLfDelta) {
- level += hdr.refLfDelta[0];
- if (i4x4 != 0) {
- level += hdr.modeLfDelta[0];
- }
- }
-
- level = (level < 0) ? 0 : (level > 63) ? 63 : level;
- if (level > 0) {
- int ilevel = level;
- if (hdr.sharpness > 0) {
- if (hdr.sharpness > 4) {
- ilevel >>= 2;
- } else {
- ilevel >>= 1;
- }
-
- if (ilevel > 9 - hdr.sharpness) {
- ilevel = 9 - hdr.sharpness;
- }
- }
-
- if (ilevel < 1) {
- ilevel = 1;
- }
-
- info.fInnerLevel = ilevel;
- info.fLimit = 2 * level + ilevel;
- info.hevThresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
- } else {
- info.fLimit = 0; // no filtering
- }
-
- info.fInner = i4x4 != 0;
- }
- }
- }
- }
-
- bool _initFrame() {
- if (_webp.alphaData != null) {
- _alphaData = _webp.alphaData;
- }
-
- _fStrengths = new List<List<VP8FInfo>>(NUM_MB_SEGMENTS);
- for (int i = 0; i < NUM_MB_SEGMENTS; ++i) {
- _fStrengths[i] = [new VP8FInfo(), new VP8FInfo()];
- }
-
- _yuvT = new List<VP8TopSamples>(_mbWidth);
- for (int i = 0; i < _mbWidth; ++i) {
- _yuvT[i] = new VP8TopSamples();
- }
-
- _yuvBlock = new Uint8List(YUV_SIZE);
-
- _intraT = new Uint8List(4 * _mbWidth);
-
- _cacheYStride = 16 * _mbWidth;
- _cacheUVStride = 8 * _mbWidth;
-
- final int extra_rows = FILTER_EXTRA_ROWS[_filterType];
- final int extra_y = extra_rows * _cacheYStride;
- final int extra_uv = (extra_rows ~/ 2) * _cacheUVStride;
-
- _cacheY = new InputBuffer(new Uint8List(16 * _cacheYStride + extra_y),
- offset: extra_y);
-
- _cacheU = new InputBuffer(new Uint8List(8 * _cacheUVStride + extra_uv),
- offset: extra_uv);
-
- _cacheV = new InputBuffer(new Uint8List(8 * _cacheUVStride + extra_uv),
- offset: extra_uv);
-
- _tmpY = new InputBuffer(new Uint8List(webp.width));
-
- final int uvWidth = (webp.width + 1) >> 1;
- _tmpU = new InputBuffer(new Uint8List(uvWidth));
- _tmpV = new InputBuffer(new Uint8List(uvWidth));
-
- // Define the area where we can skip in-loop filtering, in case of cropping.
- //
- // 'Simple' filter reads two luma samples outside of the macroblock
- // and filters one. It doesn't filter the chroma samples. Hence, we can
- // avoid doing the in-loop filtering before crop_top/crop_left position.
- // For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
- // Means: there's a dependency chain that goes all the way up to the
- // top-left corner of the picture (MB #0). We must filter all the previous
- // macroblocks.
- {
- final int extraPixels = FILTER_EXTRA_ROWS[_filterType];
- if (_filterType == 2) {
- // For complex filter, we need to preserve the dependency chain.
- _tlMbX = 0;
- _tlMbY = 0;
- } else {
- // For simple filter, we can filter only the cropped region.
- // We include 'extra_pixels' on the other side of the boundary, since
- // vertical or horizontal filtering of the previous macroblock can
- // modify some abutting pixels.
- _tlMbX = (_cropLeft - extraPixels) ~/ 16;
- _tlMbY = (_cropTop - extraPixels) ~/ 16;
- if (_tlMbX < 0) {
- _tlMbX = 0;
- }
- if (_tlMbY < 0) {
- _tlMbY = 0;
- }
- }
-
- // We need some 'extra' pixels on the right/bottom.
- _brMbY = (_cropBottom + 15 + extraPixels) ~/ 16;
- _brMbX = (_cropRight + 15 + extraPixels) ~/ 16;
- if (_brMbX > _mbWidth) {
- _brMbX = _mbWidth;
- }
- if (_brMbY > _mbHeight) {
- _brMbY = _mbHeight;
- }
- }
-
- _mbInfo = new List<VP8MB>(_mbWidth + 1);
- _mbData = new List<VP8MBData>(_mbWidth);
- _fInfo = new List<VP8FInfo>(_mbWidth);
-
- for (int i = 0; i < _mbWidth; ++i) {
- _mbInfo[i] = new VP8MB();
- _mbData[i] = new VP8MBData();
- }
- _mbInfo[_mbWidth] = new VP8MB();
-
- _precomputeFilterStrengths();
-
- // Init critical function pointers and look-up tables.
- _dsp = new VP8Filter();
- return true;
- }
-
- bool _parseFrame() {
- for (_mbY = 0; _mbY < _brMbY; ++_mbY) {
- // Parse bitstream for this row.
- VP8BitReader tokenBr = _partitions[_mbY & (_numPartitions - 1)];
- for (; _mbX < _mbWidth; ++_mbX) {
- if (!_decodeMB(tokenBr)) {
- return false;
- }
- }
-
- // Prepare for next scanline
- VP8MB left = _mbInfo[0];
- left.nz = 0;
- left.nzDc = 0;
- _intraL.fillRange(0, _intraL.length, B_DC_PRED);
- _mbX = 0;
-
- // Reconstruct, filter and emit the row.
- if (!_processRow()) {
- return false;
- }
- }
-
- return true;
- }
-
- bool _processRow() {
- _reconstructRow();
-
- bool useFilter = (_filterType > 0) && (_mbY >= _tlMbY) && (_mbY <= _brMbY);
- return _finishRow(useFilter);
- }
-
- void _reconstructRow() {
- int mb_y = _mbY;
- InputBuffer y_dst = new InputBuffer(_yuvBlock, offset: Y_OFF);
- InputBuffer u_dst = new InputBuffer(_yuvBlock, offset: U_OFF);
- InputBuffer v_dst = new InputBuffer(_yuvBlock, offset: V_OFF);
-
- for (int mb_x = 0; mb_x < _mbWidth; ++mb_x) {
- VP8MBData block = _mbData[mb_x];
-
- // Rotate in the left samples from previously decoded block. We move four
- // pixels at a time for alignment reason, and because of in-loop filter.
- if (mb_x > 0) {
- for (int j = -1; j < 16; ++j) {
- y_dst.memcpy(j * BPS - 4, 4, y_dst, j * BPS + 12);
- }
-
- for (int j = -1; j < 8; ++j) {
- u_dst.memcpy(j * BPS - 4, 4, u_dst, j * BPS + 4);
- v_dst.memcpy(j * BPS - 4, 4, v_dst, j * BPS + 4);
- }
- } else {
- for (int j = 0; j < 16; ++j) {
- y_dst[j * BPS - 1] = 129;
- }
-
- for (int j = 0; j < 8; ++j) {
- u_dst[j * BPS - 1] = 129;
- v_dst[j * BPS - 1] = 129;
- }
-
- // Init top-left sample on left column too
- if (mb_y > 0) {
- y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
- }
- }
-
- // bring top samples into the cache
- VP8TopSamples top_yuv = _yuvT[mb_x];
- Int16List coeffs = block.coeffs;
- int bits = block.nonZeroY;
-
- if (mb_y > 0) {
- y_dst.memcpy(-BPS, 16, top_yuv.y);
- u_dst.memcpy(-BPS, 8, top_yuv.u);
- v_dst.memcpy(-BPS, 8, top_yuv.v);
- } else if (mb_x == 0) {
- // we only need to do this init once at block (0,0).
- // Afterward, it remains valid for the whole topmost row.
- y_dst.memset(-BPS - 1, 16 + 4 + 1, 127);
- u_dst.memset(-BPS - 1, 8 + 1, 127);
- v_dst.memset(-BPS - 1, 8 + 1, 127);
- }
-
- // predict and add residuals
- if (block.isIntra4x4) { // 4x4
- InputBuffer topRight = new InputBuffer.from(y_dst, offset: -BPS + 16);
- Uint32List topRight32 = topRight.toUint32List();
-
- if (mb_y > 0) {
- if (mb_x >= _mbWidth - 1) { // on rightmost border
- topRight.memset(0, 4, top_yuv.y[15]);
- } else {
- topRight.memcpy(0, 4, _yuvT[mb_x + 1].y);
- }
- }
-
- // replicate the top-right pixels below
- int p = topRight32[0];
- topRight32[3 * BPS] = p;
- topRight32[2 * BPS] = p;
- topRight32[BPS] = p;
-
- // predict and add residuals for all 4x4 blocks in turn.
- for (int n = 0; n < 16; ++n, bits = (bits << 2) & 0xffffffff) {
- InputBuffer dst = new InputBuffer.from(y_dst, offset: kScan[n]);
-
- VP8Filter.PredLuma4[block.imodes[n]](dst);
-
- _doTransform(bits, new InputBuffer(coeffs, offset: n * 16), dst);
- }
- } else { // 16x16
- int predFunc = _checkMode(mb_x, mb_y, block.imodes[0]);
-
- VP8Filter.PredLuma16[predFunc](y_dst);
- if (bits != 0) {
- for (int n = 0; n < 16; ++n, bits = (bits << 2) & 0xffffffff) {
- InputBuffer dst = new InputBuffer.from(y_dst, offset: kScan[n]);
-
- _doTransform(bits, new InputBuffer(coeffs, offset: n * 16), dst);
- }
- }
- }
-
- // Chroma
- int bits_uv = block.nonZeroUV;
- int pred_func = _checkMode(mb_x, mb_y, block.uvmode);
- VP8Filter.PredChroma8[pred_func](u_dst);
- VP8Filter.PredChroma8[pred_func](v_dst);
-
- InputBuffer c1 = new InputBuffer(coeffs, offset: 16 * 16);
- _doUVTransform(bits_uv, c1, u_dst);
-
- InputBuffer c2 = new InputBuffer(coeffs, offset: 20 * 16);
- _doUVTransform(bits_uv >> 8, c2, v_dst);
-
- // stash away top samples for next block
- if (mb_y < _mbHeight - 1) {
- top_yuv.y.setRange(0, 16, y_dst.toUint8List(), 15 * BPS);
- top_yuv.u.setRange(0, 8, u_dst.toUint8List(), 7 * BPS);
- top_yuv.v.setRange(0, 8, v_dst.toUint8List(), 7 * BPS);
- }
-
- // Transfer reconstructed samples from yuv_b_ cache to final destination.
- int y_out = mb_x * 16; // dec->cache_y_ +
- int u_out = mb_x * 8; // dec->cache_u_ +
- int v_out = mb_x * 8; // _dec->cache_v_ +
-
- for (int j = 0; j < 16; ++j) {
- int start = y_out + j * _cacheYStride;
- _cacheY.memcpy(start, 16, y_dst, j * BPS);
- }
-
- for (int j = 0; j < 8; ++j) {
- int start = u_out + j * _cacheUVStride;
- _cacheU.memcpy(start, 8, u_dst, j * BPS);
-
- start = v_out + j * _cacheUVStride;
- _cacheV.memcpy(start, 8, v_dst, j * BPS);
- }
- }
- }
-
- static const List<int> kScan = const [
- 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
- 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
- 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
- 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS ];
-
- static int _checkMode(int mb_x, int mb_y, int mode) {
- if (mode == B_DC_PRED) {
- if (mb_x == 0) {
- return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
- } else {
- return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
- }
- }
- return mode;
- }
-
- void _doTransform(int bits, InputBuffer src, InputBuffer dst) {
- switch (bits >> 30) {
- case 3:
- _dsp.transform(src, dst, false);
- break;
- case 2:
- _dsp.transformAC3(src, dst);
- break;
- case 1:
- _dsp.transformDC(src, dst);
- break;
- default:
- break;
- }
- }
-
- void _doUVTransform(int bits, InputBuffer src, InputBuffer dst) {
- if (bits & 0xff != 0) { // any non-zero coeff at all?
- if (bits & 0xaa != 0) { // any non-zero AC coefficient?
- // note we don't use the AC3 variant for U/V
- _dsp.transformUV(src, dst);
- } else {
- _dsp.transformDCUV(src, dst);
- }
- }
- }
-
- // vertical position of a MB
- int MACROBLOCK_VPOS(int mb_y) => mb_y * 16;
-
- /**
- * kFilterExtraRows[] = How many extra lines are needed on the MB boundary
- * for caching, given a filtering level.
- * Simple filter: up to 2 luma samples are read and 1 is written.
- * Complex filter: up to 4 luma samples are read and 3 are written. Same for
- * U/V, so it's 8 samples total (because of the 2x upsampling).
- */
- static const List<int> kFilterExtraRows = const [ 0, 2, 8 ];
-
- void _doFilter(int mbX, int mbY) {
- final int yBps = _cacheYStride;
- VP8FInfo fInfo = _fInfo[mbX];
- InputBuffer yDst = new InputBuffer.from(_cacheY, offset: mbX * 16);
- final int ilevel = fInfo.fInnerLevel;
- final int limit = fInfo.fLimit;
- if (limit == 0) {
- return;
- }
-
- if (_filterType == 1) { // simple
- if (mbX > 0) {
- _dsp.simpleHFilter16(yDst, yBps, limit + 4);
- }
- if (fInfo.fInner) {
- _dsp.simpleHFilter16i(yDst, yBps, limit);
- }
- if (mbY > 0) {
- _dsp.simpleVFilter16(yDst, yBps, limit + 4);
- }
- if (fInfo.fInner) {
- _dsp.simpleVFilter16i(yDst, yBps, limit);
- }
- } else { // complex
- final int uvBps = _cacheUVStride;
- InputBuffer uDst = new InputBuffer.from(_cacheU, offset: mbX * 8);
- InputBuffer vDst = new InputBuffer.from(_cacheV, offset: mbX * 8);
-
- final int hevThresh = fInfo.hevThresh;
- if (mbX > 0) {
- _dsp.hFilter16(yDst, yBps, limit + 4, ilevel, hevThresh);
- _dsp.hFilter8(uDst, vDst, uvBps, limit + 4, ilevel, hevThresh);
- }
- if (fInfo.fInner) {
- _dsp.hFilter16i(yDst, yBps, limit, ilevel, hevThresh);
- _dsp.hFilter8i(uDst, vDst, uvBps, limit, ilevel, hevThresh);
- }
- if (mbY > 0) {
- _dsp.vFilter16(yDst, yBps, limit + 4, ilevel, hevThresh);
- _dsp.vFilter8(uDst, vDst, uvBps, limit + 4, ilevel, hevThresh);
- }
- if (fInfo.fInner) {
- _dsp.vFilter16i(yDst, yBps, limit, ilevel, hevThresh);
- _dsp.vFilter8i(uDst, vDst, uvBps, limit, ilevel, hevThresh);
- }
- }
- }
-
- /**
- * Filter the decoded macroblock row (if needed)
- */
- void _filterRow() {
- for (int mbX = _tlMbX; mbX < _brMbX; ++mbX) {
- _doFilter(mbX, _mbY);
- }
- }
-
- void _ditherRow() {
- }
-
-
- /**
- * This function is called after a row of macroblocks is finished decoding.
- * It also takes into account the following restrictions:
- *
- * * In case of in-loop filtering, we must hold off sending some of the bottom
- * pixels as they are yet unfiltered. They will be when the next macroblock
- * row is decoded. Meanwhile, we must preserve them by rotating them in the
- * cache area. This doesn't hold for the very bottom row of the uncropped
- * picture of course.
- * * we must clip the remaining pixels against the cropping area. The VP8Io
- * struct must have the following fields set correctly before calling put():
- */
- bool _finishRow(bool useFilter) {
- final int extraYRows = kFilterExtraRows[_filterType];
- final int ySize = extraYRows * _cacheYStride;
- final int uvSize = (extraYRows ~/ 2) * _cacheUVStride;
- InputBuffer yDst = new InputBuffer.from(_cacheY, offset: -ySize);
- InputBuffer uDst = new InputBuffer.from(_cacheU, offset: -uvSize);
- InputBuffer vDst = new InputBuffer.from(_cacheV, offset: -uvSize);
- final int mbY = _mbY;
- final bool isFirstRow = (mbY == 0);
- final bool isLastRow = (mbY >= _brMbY - 1);
- int yStart = MACROBLOCK_VPOS(mbY);
- int yEnd = MACROBLOCK_VPOS(mbY + 1);
-
- if (useFilter) {
- _filterRow();
- }
-
- if (_dither) {
- _ditherRow();
- }
-
- if (!isFirstRow) {
- yStart -= extraYRows;
- _y = new InputBuffer.from(yDst);
- _u = new InputBuffer.from(uDst);
- _v = new InputBuffer.from(vDst);
- } else {
- _y = new InputBuffer.from(_cacheY);
- _u = new InputBuffer.from(_cacheU);
- _v = new InputBuffer.from(_cacheV);
- }
-
- if (!isLastRow) {
- yEnd -= extraYRows;
- }
-
- if (yEnd > _cropBottom) {
- yEnd = _cropBottom; // make sure we don't overflow on last row.
- }
-
- _a = null;
- if (_alphaData != null && yStart < yEnd) {
- _a = _decompressAlphaRows(yStart, yEnd - yStart);
- if (_a == null) {
- return false;
- }
- }
-
- if (yStart < _cropTop) {
- final int deltaY = _cropTop - yStart;
- yStart = _cropTop;
-
- _y.offset += _cacheYStride * deltaY;
- _u.offset += _cacheUVStride * (deltaY >> 1);
- _v.offset += _cacheUVStride * (deltaY >> 1);
-
- if (_a != null) {
- _a.offset += webp.width * deltaY;
- }
- }
-
- if (yStart < yEnd) {
- _y.offset += _cropLeft;
- _u.offset += _cropLeft >> 1;
- _v.offset += _cropLeft >> 1;
- if (_a != null) {
- _a.offset += _cropLeft;
- }
-
- _put(yStart - _cropTop, _cropRight - _cropLeft, yEnd - yStart);
- }
-
- // rotate top samples if needed
- if (!isLastRow) {
- _cacheY.memcpy(-ySize, ySize, yDst, 16 * _cacheYStride);
- _cacheU.memcpy(-uvSize, uvSize, uDst, 8 * _cacheUVStride);
- _cacheV.memcpy(-uvSize, uvSize, vDst, 8 * _cacheUVStride);
- }
-
- return true;
- }
-
- bool _put(int mbY, int mbW, int mbH) {
- if (mbW <= 0 || mbH <= 0) {
- return false;
- }
-
- /*int numLinesOut =*/ _emitFancyRGB(mbY, mbW, mbH);
- _emitAlphaRGB(mbY, mbW, mbH);
-
- //_lastY += numLinesOut;
-
- return true;
- }
-
- int _clip8(int v) {
- int d = ((v & XOR_YUV_MASK2) == 0) ? (v >> YUV_FIX2) : (v < 0) ? 0 : 255;
- return d;
- }
-
- int _yuvToR(int y, int v) {
- return _clip8(kYScale * y + kVToR * v + kRCst);
- }
-
- int _yuvToG(int y, int u, int v) {
- return _clip8(kYScale * y - kUToG * u - kVToG * v + kGCst);
- }
-
- int _yuvToB(int y, int u) {
- return _clip8(kYScale * y + kUToB * u + kBCst);
- }
-
- void _yuvToRgb(int y, int u, int v, InputBuffer rgb) {
- rgb[0] = _yuvToR(y, v);
- rgb[1] = _yuvToG(y, u, v);
- rgb[2] = _yuvToB(y, u);
- }
-
- void _yuvToRgba(int y, int u, int v, InputBuffer rgba) {
- _yuvToRgb(y, u, v, rgba);
- rgba[3] = 0xff;
- }
-
- void _upsample(InputBuffer topY, InputBuffer bottomY,
- InputBuffer topU, InputBuffer topV,
- InputBuffer curU, InputBuffer curV,
- InputBuffer topDst, InputBuffer bottomDst,
- int len) {
- int LOAD_UV(int u, int v) => ((u) | ((v) << 16));
-
- final int lastPixelPair = (len - 1) >> 1;
- int tl_uv = LOAD_UV(topU[0], topV[0]); // top-left sample
- int l_uv = LOAD_UV(curU[0], curV[0]); // left-sample
-
- final int uv0 = (3 * tl_uv + l_uv + 0x00020002) >> 2;
- _yuvToRgba(topY[0], uv0 & 0xff, (uv0 >> 16), topDst);
-
- if (bottomY != null) {
- final int uv0 = (3 * l_uv + tl_uv + 0x00020002) >> 2;
- _yuvToRgba(bottomY[0], uv0 & 0xff, (uv0 >> 16), bottomDst);
- }
-
- for (int x = 1; x <= lastPixelPair; ++x) {
- final int t_uv = LOAD_UV(topU[x], topV[x]); // top sample
- final int uv = LOAD_UV(curU[x], curV[x]); // sample
- // precompute invariant values associated with first and second diagonals
- final int avg = tl_uv + t_uv + l_uv + uv + 0x00080008;
- final int diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3;
- final int diag_03 = (avg + 2 * (tl_uv + uv)) >> 3;
-
- int uv0 = (diag_12 + tl_uv) >> 1;
- int uv1 = (diag_03 + t_uv) >> 1;
-
- _yuvToRgba(topY[2 * x - 1], uv0 & 0xff, (uv0 >> 16),
- new InputBuffer.from(topDst, offset: (2 * x - 1) * 4));
-
- _yuvToRgba(topY[2 * x - 0], uv1 & 0xff, (uv1 >> 16),
- new InputBuffer.from(topDst, offset: (2 * x - 0) * 4));
-
- if (bottomY != null) {
- uv0 = (diag_03 + l_uv) >> 1;
- uv1 = (diag_12 + uv) >> 1;
-
- _yuvToRgba(bottomY[2 * x - 1], uv0 & 0xff, (uv0 >> 16),
- new InputBuffer.from(bottomDst, offset: (2 * x - 1) * 4));
-
- _yuvToRgba(bottomY[2 * x], uv1 & 0xff, (uv1 >> 16),
- new InputBuffer.from(bottomDst, offset: (2 * x + 0) * 4));
- }
-
- tl_uv = t_uv;
- l_uv = uv;
- }
-
- if ((len & 1) == 0) {
- final int uv0 = (3 * tl_uv + l_uv + 0x00020002) >> 2;
- _yuvToRgba(topY[len - 1], uv0 & 0xff, (uv0 >> 16),
- new InputBuffer.from(topDst, offset: (len - 1) * 4));
-
- if (bottomY != null) {
- final int uv0 = (3 * l_uv + tl_uv + 0x00020002) >> 2;
- _yuvToRgba(bottomY[len - 1], uv0 & 0xff, (uv0 >> 16),
- new InputBuffer.from(bottomDst, offset: (len - 1) * 4));
- }
- }
- }
-
- void _emitAlphaRGB(int mbY, int mbW, int mbH) {
- if (_a == null) {
- return;
- }
-
- final int stride = webp.width * 4;
- InputBuffer alpha = new InputBuffer.from(_a);
- int startY = mbY;
- int numRows = mbH;
-
- // Compensate for the 1-line delay of the fancy upscaler.
- // This is similar to EmitFancyRGB().
- if (startY == 0) {
- // We don't process the last row yet. It'll be done during the next call.
- --numRows;
- } else {
- --startY;
- // Fortunately, *alpha data is persistent, so we can go back
- // one row and finish alpha blending, now that the fancy upscaler
- // completed the YUV->RGB interpolation.
- alpha.offset -= webp.width;
- }
-
- InputBuffer dst = new InputBuffer(output.getBytes(), offset: startY * stride + 3);
-
- if (_cropTop + mbY + mbH == _cropBottom) {
- // If it's the very last call, we process all the remaining rows!
- numRows = _cropBottom - _cropTop - startY;
- }
-
- for (int y = 0; y < numRows; ++y) {
- for (int x = 0; x < mbW; ++x) {
- final int alphaValue = alpha[x];
- dst[4 * x] = alphaValue & 0xff;
- }
-
- alpha.offset += webp.width;
- dst.offset += stride;
- }
- }
-
-
- int _emitFancyRGB(int mbY, int mbW, int mbH) {
- int numLinesOut = mbH; // a priori guess
- InputBuffer dst = new InputBuffer(output.getBytes(), offset: mbY * webp.width * 4);
- InputBuffer curY = new InputBuffer.from(_y);
- InputBuffer curU = new InputBuffer.from(_u);
- InputBuffer curV = new InputBuffer.from(_v);
- int y = mbY;
- final int yEnd = mbY + mbH;
- final int uvW = (mbW + 1) >> 1;
- final int stride = webp.width * 4;
- InputBuffer topU = new InputBuffer.from(_tmpU);
- InputBuffer topV = new InputBuffer.from(_tmpV);
-
- if (y == 0) {
- // First line is special cased. We mirror the u/v samples at boundary.
- _upsample(curY, null, curU, curV, curU, curV, dst, null, mbW);
- } else {
- // We can finish the left-over line from previous call.
- _upsample(_tmpY, curY, topU, topV, curU, curV,
- new InputBuffer.from(dst, offset: -stride), dst, mbW);
- ++numLinesOut;
- }
-
- // Loop over each output pairs of row.
- topU.buffer = curU.buffer;
- topV.buffer = curV.buffer;
- for (; y + 2 < yEnd; y += 2) {
- topU.offset = curU.offset;
- topV.offset = curV.offset;
- curU.offset += _cacheUVStride;
- curV.offset += _cacheUVStride;
- dst.offset += 2 * stride;
- curY.offset += 2 * _cacheYStride;
- _upsample(new InputBuffer.from(curY, offset: -_cacheYStride), curY,
- topU, topV, curU, curV,
- new InputBuffer.from(dst, offset: -stride), dst, mbW);
- }
-
- // move to last row
- curY.offset += _cacheYStride;
- if (_cropTop + yEnd < _cropBottom) {
- // Save the unfinished samples for next call (as we're not done yet).
- _tmpY.memcpy(0, mbW, curY);
- _tmpU.memcpy(0, uvW, curU);
- _tmpV.memcpy(0, uvW, curV);
- // The fancy upsampler leaves a row unfinished behind
- // (except for the very last row)
- numLinesOut--;
- } else {
- // Process the very last row of even-sized picture
- if ((yEnd & 1) == 0) {
- _upsample(curY, null, curU, curV, curU, curV,
- new InputBuffer.from(dst, offset: stride), null, mbW);
- }
- }
-
- return numLinesOut;
- }
-
- InputBuffer _decompressAlphaRows(int row, int numRows) {
- final int width = webp.width;
- final int height = webp.height;
-
- if (row < 0 || numRows <= 0 || row + numRows > height) {
- return null; // sanity check.
- }
-
- if (row == 0) {
- _alphaPlane = new Uint8List(width * height);
- _alpha = new WebPAlpha(_alphaData, width, height);
- }
-
- if (!_alpha.isAlphaDecoded) {
- if (!_alpha.decode(row, numRows, _alphaPlane)) {
- return null;
- }
- }
-
- // Return a pointer to the current decoded row.
- return new InputBuffer(_alphaPlane, offset: row * width);
- }
-
- bool _decodeMB(VP8BitReader tokenBr) {
- VP8MB left = _mbInfo[0];
- VP8MB mb = _mbInfo[1 + _mbX];
- VP8MBData block = _mbData[_mbX];
- bool skip;
-
- // Note: we don't save segment map (yet), as we don't expect
- // to decode more than 1 keyframe.
- if (_segmentHeader.updateMap) {
- // Hardcoded tree parsing
- _segment = br.getBit(_proba.segments[0]) == 0 ?
- br.getBit(_proba.segments[1]) :
- 2 + br.getBit(_proba.segments[2]);
- }
-
- skip = _useSkipProba ? br.getBit(_skipP) != 0 : false;
-
- _parseIntraMode();
-
- if (!skip) {
- skip = _parseResiduals(mb, tokenBr);
- } else {
- left.nz = mb.nz = 0;
- if (!block.isIntra4x4) {
- left.nzDc = mb.nzDc = 0;
- }
- block.nonZeroY = 0;
- block.nonZeroUV = 0;
- }
-
- if (_filterType > 0) { // store filter info
- _fInfo[_mbX] = _fStrengths[_segment][block.isIntra4x4 ? 1 : 0];
- VP8FInfo finfo = _fInfo[_mbX];
- finfo.fInner = finfo.fInner || !skip;
- }
-
- return true;
- }
-
- bool _parseResiduals(VP8MB mb, VP8BitReader tokenBr) {
- var bands = _proba.bands;
- List<VP8BandProbas> acProba;
- VP8QuantMatrix q = _dqm[_segment];
- VP8MBData block = _mbData[_mbX];
- InputBuffer dst = new InputBuffer(block.coeffs);
- //int di = 0;
- VP8MB leftMb = _mbInfo[0];
- int tnz;
- int lnz;
- int nonZeroY = 0;
- int nonZeroUV = 0;
- int outTopNz;
- int outLeftNz;
- int first;
-
- dst.memset(0, dst.length, 0);
-
- if (!block.isIntra4x4) { // parse DC
- InputBuffer dc = new InputBuffer(new Int16List(16));
- final int ctx = mb.nzDc + leftMb.nzDc;
- final int nz = _getCoeffs(tokenBr, bands[1], ctx, q.y2Mat, 0, dc);
- mb.nzDc = leftMb.nzDc = (nz > 0) ? 1 : 0;
- if (nz > 1) { // more than just the DC -> perform the full transform
- _transformWHT(dc, dst);
- } else { // only DC is non-zero -> inlined simplified transform
- final int dc0 = (dc[0] + 3) >> 3;
- for (int i = 0; i < 16 * 16; i += 16) {
- dst[i] = dc0;
- }
- }
-
- first = 1;
- acProba = bands[0];
- } else {
- first = 0;
- acProba = bands[3];
- }
-
- tnz = mb.nz & 0x0f;
- lnz = leftMb.nz & 0x0f;
- for (int y = 0; y < 4; ++y) {
- int l = lnz & 1;
- int nzCoeffs = 0;
- for (int x = 0; x < 4; ++x) {
- final int ctx = l + (tnz & 1);
- final int nz = _getCoeffs(tokenBr, acProba, ctx, q.y1Mat, first, dst);
- l = (nz > first) ? 1 : 0;
- tnz = (tnz >> 1) | (l << 7);
- nzCoeffs = _nzCodeBits(nzCoeffs, nz, dst[0] != 0 ? 1 : 0);
- dst.offset += 16;
- }
-
- tnz >>= 4;
- lnz = (lnz >> 1) | (l << 7);
- nonZeroY = (nonZeroY << 8) | nzCoeffs;
- }
- outTopNz = tnz;
- outLeftNz = lnz >> 4;
-
- for (int ch = 0; ch < 4; ch += 2) {
- int nzCoeffs = 0;
- tnz = mb.nz >> (4 + ch);
- lnz = leftMb.nz >> (4 + ch);
- for (int y = 0; y < 2; ++y) {
- int l = lnz & 1;
- for (int x = 0; x < 2; ++x) {
- final int ctx = l + (tnz & 1);
- final int nz = _getCoeffs(tokenBr, bands[2], ctx, q.uvMat, 0, dst);
- l = (nz > 0) ? 1 : 0;
- tnz = (tnz >> 1) | (l << 3);
- nzCoeffs = _nzCodeBits(nzCoeffs, nz, dst[0] != 0 ? 1 : 0);
- dst.offset += 16;
- }
-
- tnz >>= 2;
- lnz = (lnz >> 1) | (l << 5);
- }
-
- // Note: we don't really need the per-4x4 details for U/V blocks.
- nonZeroUV |= nzCoeffs << (4 * ch);
- outTopNz |= (tnz << 4) << ch;
- outLeftNz |= (lnz & 0xf0) << ch;
- }
-
- mb.nz = outTopNz;
- leftMb.nz = outLeftNz;
-
- block.nonZeroY = nonZeroY;
- block.nonZeroUV = nonZeroUV;
-
- // We look at the mode-code of each block and check if some blocks have less
- // than three non-zero coeffs (code < 2). This is to avoid dithering flat and
- // empty blocks.
- block.dither = (nonZeroUV & 0xaaaa) != 0 ? 0 : q.dither;
-
- // will be used for further optimization
- return (nonZeroY | nonZeroUV) == 0;
- }
-
- void _transformWHT(InputBuffer src, InputBuffer out) {
- Int32List tmp = new Int32List(16);
-
- int oi = 0;
- for (int i = 0; i < 4; ++i) {
- final int a0 = src[0 + i] + src[12 + i];
- final int a1 = src[4 + i] + src[ 8 + i];
- final int a2 = src[4 + i] - src[ 8 + i];
- final int a3 = src[0 + i] - src[12 + i];
- tmp[0 + i] = a0 + a1;
- tmp[8 + i] = a0 - a1;
- tmp[4 + i] = a3 + a2;
- tmp[12 + i] = a3 - a2;
- }
-
- for (int i = 0; i < 4; ++i) {
- final int dc = tmp[0 + i * 4] + 3; // w/ rounder
- final int a0 = dc + tmp[3 + i * 4];
- final int a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
- final int a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
- final int a3 = dc - tmp[3 + i * 4];
- out[oi + 0] = (a0 + a1) >> 3;
- out[oi + 16] = (a3 + a2) >> 3;
- out[oi + 32] = (a0 - a1) >> 3;
- out[oi + 48] = (a3 - a2) >> 3;
-
- oi += 64;
- }
- }
-
- int _nzCodeBits(int nz_coeffs, int nz, int dc_nz) {
- nz_coeffs <<= 2;
- nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz;
- return nz_coeffs;
- }
-
- static const List<int> kBands = const [
- 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0];
-
- static const List<int> kCat3 = const [ 173, 148, 140 ];
- static const List<int> kCat4 = const [ 176, 155, 140, 135 ];
- static const List<int> kCat5 = const [ 180, 157, 141, 134, 130 ];
- static const List<int> kCat6 = const [ 254, 254, 243, 230, 196, 177, 153,
- 140, 133, 130, 129 ];
- static const List<List<int>> kCat3456 = const [ kCat3, kCat4, kCat5, kCat6 ];
- static const List<int> kZigzag = const [ 0, 1, 4, 8, 5, 2, 3, 6, 9, 12,
- 13, 10, 7, 11, 14, 15 ];
-
- /**
- * See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
- */
- int _getLargeValue(VP8BitReader br, List<int> p) {
- int v;
- if (br.getBit(p[3]) == 0) {
- if (br.getBit(p[4]) == 0) {
- v = 2;
- } else {
- v = 3 + br.getBit(p[5]);
- }
- } else {
- if (br.getBit(p[6]) == 0) {
- if (br.getBit(p[7]) == 0) {
- v = 5 + br.getBit(159);
- } else {
- v = 7 + 2 * br.getBit(165);
- v += br.getBit(145);
- }
- } else {
- final int bit1 = br.getBit(p[8]);
- final int bit0 = br.getBit(p[9 + bit1]);
- final int cat = 2 * bit1 + bit0;
- v = 0;
- List<int> tab = kCat3456[cat];
- for (int i = 0, len = tab.length; i < len; ++i) {
- v += v + br.getBit(tab[i]);
- }
- v += 3 + (8 << cat);
- }
- }
- return v;
- }
-
-
- /**
- * Returns the position of the last non-zero coeff plus one
- */
- int _getCoeffs(VP8BitReader br, List<VP8BandProbas> prob,
- int ctx, List<int> dq, int n, InputBuffer out) {
- // n is either 0 or 1 here. kBands[n] is not necessary for extracting '*p'.
- List<int> p = prob[n].probas[ctx];
- for (; n < 16; ++n) {
- if (br.getBit(p[0]) == 0) {
- return n; // previous coeff was last non-zero coeff
- }
-
- while (br.getBit(p[1]) == 0) { // sequence of zero coeffs
- p = prob[kBands[++n]].probas[0];
- if (n == 16) {
- return 16;
- }
- }
-
- { // non zero coeff
- List<Uint8List> p_ctx = prob[kBands[n + 1]].probas;
- int v;
- if (br.getBit(p[2]) == 0) {
- v = 1;
- p = p_ctx[1];
- } else {
- v = _getLargeValue(br, p);
- p = p_ctx[2];
- }
-
- out[kZigzag[n]] = br.getSigned(v) * dq[n > 0 ? 1 : 0];
- }
- }
- return 16;
- }
-
- void _parseIntraMode() {
- int ti = 4 * _mbX;
- int li = 0;
- Uint8List top = _intraT;
- Uint8List left = _intraL;
-
- VP8MBData block = _mbData[_mbX];
-
- // decide for B_PRED first
- block.isIntra4x4 = br.getBit(145) == 0;
- if (!block.isIntra4x4) {
- // Hardcoded 16x16 intra-mode decision tree.
- final int ymode =
- br.getBit(156) != 0 ?
- (br.getBit(128) != 0 ? TM_PRED : H_PRED) :
- (br.getBit(163) != 0 ? V_PRED : DC_PRED);
- block.imodes[0] = ymode;
-
- top.fillRange(ti, ti + 4, ymode);
- left.fillRange(li, li + 4, ymode);
- } else {
- Uint8List modes = block.imodes;
- int mi = 0;
- for (int y = 0; y < 4; ++y) {
- int ymode = left[y];
- for (int x = 0; x < 4; ++x) {
- List<int> prob = kBModesProba[top[ti + x]][ymode];
-
- // Generic tree-parsing
- int b = br.getBit(prob[0]);
- int i = kYModesIntra4[b];
-
- while (i > 0) {
- i = kYModesIntra4[2 * i + br.getBit(prob[i])];
- }
-
- ymode = -i;
- top[ti + x] = ymode;
- }
-
- modes.setRange(mi, mi + 4, top, ti);
-
- mi += 4;
- left[y] = ymode;
- }
- }
-
- // Hardcoded UVMode decision tree
- block.uvmode = br.getBit(142) == 0 ? DC_PRED
- : br.getBit(114) == 0 ? V_PRED
- : br.getBit(183) != 0 ? TM_PRED : H_PRED;
- }
-
- // Main data source
- VP8BitReader br;
-
- Image output;
-
- VP8Filter _dsp;
-
- // headers
- VP8FrameHeader _frameHeader = new VP8FrameHeader();
- VP8PictureHeader _picHeader = new VP8PictureHeader();
- VP8FilterHeader _filterHeader = new VP8FilterHeader();
- VP8SegmentHeader _segmentHeader = new VP8SegmentHeader();
-
- int _cropLeft;
- int _cropRight;
- int _cropTop;
- int _cropBottom;
-
- /// Width in macroblock units.
- int _mbWidth;
- /// Height in macroblock units.
- int _mbHeight;
-
- // Macroblock to process/filter, depending on cropping and filter_type.
- int _tlMbX; // top-left MB that must be in-loop filtered
- int _tlMbY;
- int _brMbX; // last bottom-right MB that must be decoded
- int _brMbY;
-
- // number of partitions.
- int _numPartitions;
- // per-partition boolean decoders.
- List<VP8BitReader> _partitions = new List<VP8BitReader>(MAX_NUM_PARTITIONS);
-
- // Dithering strength, deduced from decoding options
- bool _dither = false; // whether to use dithering or not
- //VP8Random _ditheringRand; // random generator for dithering
-
- // dequantization (one set of DC/AC dequant factor per segment)
- List<VP8QuantMatrix> _dqm = new List<VP8QuantMatrix>(NUM_MB_SEGMENTS);
-
- // probabilities
- VP8Proba _proba;
- bool _useSkipProba;
- int _skipP;
-
- // Boundary data cache and persistent buffers.
- /// top intra modes values: 4 * _mbWidth
- Uint8List _intraT;
- /// left intra modes values
- Uint8List _intraL = new Uint8List(4);
-
- /// uint8, segment of the currently parsed block
- int _segment;
- /// top y/u/v samples
- List<VP8TopSamples> _yuvT;
-
- /// contextual macroblock info (mb_w_ + 1)
- List<VP8MB> _mbInfo;
- /// filter strength info
- List<VP8FInfo> _fInfo;
- /// main block for Y/U/V (size = YUV_SIZE)
- Uint8List _yuvBlock;
-
- /// macroblock row for storing unfiltered samples
- InputBuffer _cacheY;
- InputBuffer _cacheU;
- InputBuffer _cacheV;
- int _cacheYStride;
- int _cacheUVStride;
-
- InputBuffer _tmpY;
- InputBuffer _tmpU;
- InputBuffer _tmpV;
-
- InputBuffer _y;
- InputBuffer _u;
- InputBuffer _v;
- InputBuffer _a;
-
- /// main memory chunk for the above data. Persistent.
- //Uint8List _mem;
-
- // Per macroblock non-persistent infos.
- /// current position, in macroblock units
- int _mbX = 0;
- int _mbY = 0;
- /// parsed reconstruction data
- List<VP8MBData> _mbData;
-
- /// 0=off, 1=simple, 2=complex
- int _filterType;
- /// precalculated per-segment/type
- List<List<VP8FInfo>> _fStrengths;
-
- // Alpha
- /// alpha-plane decoder object
- WebPAlpha _alpha;
- /// compressed alpha data (if present)
- InputBuffer _alphaData;
- /// true if alpha_data_ is decoded in alpha_plane_
- //int _isAlphaDecoded;
- /// output. Persistent, contains the whole data.
- Uint8List _alphaPlane;
-
- // extensions
- //int _layerColorspace;
- /// compressed layer data (if present)
- //Uint8List _layerData;
-
- static int _clip(int v, int M) {
- return v < 0 ? 0 : v > M ? M : v;
- }
-
- static const List<int> kYModesIntra4 = const [
- -B_DC_PRED, 1,
- -B_TM_PRED, 2,
- -B_VE_PRED, 3,
- 4, 6,
- -B_HE_PRED, 5,
- -B_RD_PRED, -B_VR_PRED,
- -B_LD_PRED, 7,
- -B_VL_PRED, 8,
- -B_HD_PRED, -B_HU_PRED ];
-
- static const List<List<List<int>>> kBModesProba = const [
- const [ const [ 231, 120, 48, 89, 115, 113, 120, 152, 112 ],
- const [ 152, 179, 64, 126, 170, 118, 46, 70, 95 ],
- const [ 175, 69, 143, 80, 85, 82, 72, 155, 103 ],
- const [ 56, 58, 10, 171, 218, 189, 17, 13, 152 ],
- const [ 114, 26, 17, 163, 44, 195, 21, 10, 173 ],
- const [ 121, 24, 80, 195, 26, 62, 44, 64, 85 ],
- const [ 144, 71, 10, 38, 171, 213, 144, 34, 26 ],
- const [ 170, 46, 55, 19, 136, 160, 33, 206, 71 ],
- const [ 63, 20, 8, 114, 114, 208, 12, 9, 226 ],
- const [ 81, 40, 11, 96, 182, 84, 29, 16, 36 ] ],
- const [ const [ 134, 183, 89, 137, 98, 101, 106, 165, 148 ],
- const [ 72, 187, 100, 130, 157, 111, 32, 75, 80 ],
- const [ 66, 102, 167, 99, 74, 62, 40, 234, 128 ],
- const [ 41, 53, 9, 178, 241, 141, 26, 8, 107 ],
- const [ 74, 43, 26, 146, 73, 166, 49, 23, 157 ],
- const [ 65, 38, 105, 160, 51, 52, 31, 115, 128 ],
- const [ 104, 79, 12, 27, 217, 255, 87, 17, 7 ],
- const [ 87, 68, 71, 44, 114, 51, 15, 186, 23 ],
- const [ 47, 41, 14, 110, 182, 183, 21, 17, 194 ],
- const [ 66, 45, 25, 102, 197, 189, 23, 18, 22 ] ],
- const [ const [ 88, 88, 147, 150, 42, 46, 45, 196, 205 ],
- const [ 43, 97, 183, 117, 85, 38, 35, 179, 61 ],
- const [ 39, 53, 200, 87, 26, 21, 43, 232, 171 ],
- const [ 56, 34, 51, 104, 114, 102, 29, 93, 77 ],
- const [ 39, 28, 85, 171, 58, 165, 90, 98, 64 ],
- const [ 34, 22, 116, 206, 23, 34, 43, 166, 73 ],
- const [ 107, 54, 32, 26, 51, 1, 81, 43, 31 ],
- const [ 68, 25, 106, 22, 64, 171, 36, 225, 114 ],
- const [ 34, 19, 21, 102, 132, 188, 16, 76, 124 ],
- const [ 62, 18, 78, 95, 85, 57, 50, 48, 51 ] ],
- const [ const [ 193, 101, 35, 159, 215, 111, 89, 46, 111 ],
- const [ 60, 148, 31, 172, 219, 228, 21, 18, 111 ],
- const [ 112, 113, 77, 85, 179, 255, 38, 120, 114 ],
- const [ 40, 42, 1, 196, 245, 209, 10, 25, 109 ],
- const [ 88, 43, 29, 140, 166, 213, 37, 43, 154 ],
- const [ 61, 63, 30, 155, 67, 45, 68, 1, 209 ],
- const [ 100, 80, 8, 43, 154, 1, 51, 26, 71 ],
- const [ 142, 78, 78, 16, 255, 128, 34, 197, 171 ],
- const [ 41, 40, 5, 102, 211, 183, 4, 1, 221 ],
- const [ 51, 50, 17, 168, 209, 192, 23, 25, 82 ] ],
- const [ const [ 138, 31, 36, 171, 27, 166, 38, 44, 229 ],
- const [ 67, 87, 58, 169, 82, 115, 26, 59, 179 ],
- const [ 63, 59, 90, 180, 59, 166, 93, 73, 154 ],
- const [ 40, 40, 21, 116, 143, 209, 34, 39, 175 ],
- const [ 47, 15, 16, 183, 34, 223, 49, 45, 183 ],
- const [ 46, 17, 33, 183, 6, 98, 15, 32, 183 ],
- const [ 57, 46, 22, 24, 128, 1, 54, 17, 37 ],
- const [ 65, 32, 73, 115, 28, 128, 23, 128, 205 ],
- const [ 40, 3, 9, 115, 51, 192, 18, 6, 223 ],
- const [ 87, 37, 9, 115, 59, 77, 64, 21, 47 ] ],
- const [ const [ 104, 55, 44, 218, 9, 54, 53, 130, 226 ],
- const [ 64, 90, 70, 205, 40, 41, 23, 26, 57 ],
- const [ 54, 57, 112, 184, 5, 41, 38, 166, 213 ],
- const [ 30, 34, 26, 133, 152, 116, 10, 32, 134 ],
- const [ 39, 19, 53, 221, 26, 114, 32, 73, 255 ],
- const [ 31, 9, 65, 234, 2, 15, 1, 118, 73 ],
- const [ 75, 32, 12, 51, 192, 255, 160, 43, 51 ],
- const [ 88, 31, 35, 67, 102, 85, 55, 186, 85 ],
- const [ 56, 21, 23, 111, 59, 205, 45, 37, 192 ],
- const [ 55, 38, 70, 124, 73, 102, 1, 34, 98 ] ],
- const [ const [ 125, 98, 42, 88, 104, 85, 117, 175, 82 ],
- const [ 95, 84, 53, 89, 128, 100, 113, 101, 45 ],
- const [ 75, 79, 123, 47, 51, 128, 81, 171, 1 ],
- const [ 57, 17, 5, 71, 102, 57, 53, 41, 49 ],
- const [ 38, 33, 13, 121, 57, 73, 26, 1, 85 ],
- const [ 41, 10, 67, 138, 77, 110, 90, 47, 114 ],
- const [ 115, 21, 2, 10, 102, 255, 166, 23, 6 ],
- const [ 101, 29, 16, 10, 85, 128, 101, 196, 26 ],
- const [ 57, 18, 10, 102, 102, 213, 34, 20, 43 ],
- const [ 117, 20, 15, 36, 163, 128, 68, 1, 26 ] ],
- const [ const [ 102, 61, 71, 37, 34, 53, 31, 243, 192 ],
- const [ 69, 60, 71, 38, 73, 119, 28, 222, 37 ],
- const [ 68, 45, 128, 34, 1, 47, 11, 245, 171 ],
- const [ 62, 17, 19, 70, 146, 85, 55, 62, 70 ],
- const [ 37, 43, 37, 154, 100, 163, 85, 160, 1 ],
- const [ 63, 9, 92, 136, 28, 64, 32, 201, 85 ],
- const [ 75, 15, 9, 9, 64, 255, 184, 119, 16 ],
- const [ 86, 6, 28, 5, 64, 255, 25, 248, 1 ],
- const [ 56, 8, 17, 132, 137, 255, 55, 116, 128 ],
- const [ 58, 15, 20, 82, 135, 57, 26, 121, 40 ] ],
- const [ const [ 164, 50, 31, 137, 154, 133, 25, 35, 218 ],
- const [ 51, 103, 44, 131, 131, 123, 31, 6, 158 ],
- const [ 86, 40, 64, 135, 148, 224, 45, 183, 128 ],
- const [ 22, 26, 17, 131, 240, 154, 14, 1, 209 ],
- const [ 45, 16, 21, 91, 64, 222, 7, 1, 197 ],
- const [ 56, 21, 39, 155, 60, 138, 23, 102, 213 ],
- const [ 83, 12, 13, 54, 192, 255, 68, 47, 28 ],
- const [ 85, 26, 85, 85, 128, 128, 32, 146, 171 ],
- const [ 18, 11, 7, 63, 144, 171, 4, 4, 246 ],
- const [ 35, 27, 10, 146, 174, 171, 12, 26, 128 ] ],
- const [ const [ 190, 80, 35, 99, 180, 80, 126, 54, 45 ],
- const [ 85, 126, 47, 87, 176, 51, 41, 20, 32 ],
- const [ 101, 75, 128, 139, 118, 146, 116, 128, 85 ],
- const [ 56, 41, 15, 176, 236, 85, 37, 9, 62 ],
- const [ 71, 30, 17, 119, 118, 255, 17, 18, 138 ],
- const [ 101, 38, 60, 138, 55, 70, 43, 26, 142 ],
- const [ 146, 36, 19, 30, 171, 255, 97, 27, 20 ],
- const [ 138, 45, 61, 62, 219, 1, 81, 188, 64 ],
- const [ 32, 41, 20, 117, 151, 142, 20, 21, 163 ],
- const [ 112, 19, 12, 61, 195, 128, 48, 4, 24 ] ] ];
-
- static const List COEFFS_PROBA_0 = const [
- const [ const [ const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ]
- ],
- const [ const [ 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 ],
- const [ 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 ],
- const [ 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 ]
- ],
- const [ const [ 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 ],
- const [ 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 ],
- const [ 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 ],
- ],
- const [ const [ 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 ],
- const [ 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 ],
- const [ 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 ],
- ],
- const [ const [ 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 ],
- const [ 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 ],
- const [ 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 ]
- ],
- const [ const [ 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 ],
- const [ 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 ],
- const [ 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 ]
- ],
- const [ const [ 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 ],
- const [ 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 ],
- const [ 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 ]
- ],
- const [ const [ 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ]
- ]
- ],
- const [ const [ const [ 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 ],
- const [ 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 ],
- const [ 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 ]
- ],
- const [ const [ 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 ],
- const [ 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 ],
- const [ 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 ]
- ],
- const [ const [ 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 ],
- const [ 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 ],
- const [ 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 ]
- ],
- const [ const [ 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 ],
- const [ 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 ],
- const [ 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 ]
- ],
- const [ const [ 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 ],
- const [ 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 ],
- const [ 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 ]
- ],
- const [ const [ 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 ],
- const [ 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 ],
- const [ 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 ]
- ],
- const [ const [ 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 ],
- const [ 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 ],
- const [ 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 ]
- ],
- const [ const [ 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 ],
- const [ 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 ],
- const [ 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 ]
- ]
- ],
- const [ const [ const [ 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 ],
- const [ 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 ],
- const [ 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 ]
- ],
- const [ const [ 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 ],
- const [ 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 ],
- const [ 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 ]
- ],
- const [ const [ 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 ],
- const [ 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 ],
- const [ 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 ]
- ],
- const [ const [ 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 ],
- const [ 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 ],
- const [ 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 ]
- ],
- const [ const [ 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 ],
- const [ 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 ],
- const [ 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ]
- ],
- const [ const [ 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 ]
- ],
- const [ const [ 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 ],
- const [ 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 ],
- const [ 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 ]
- ],
- const [ const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ]
- ]
- ],
- const [ const [ const [ 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 ],
- const [ 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 ],
- const [ 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 ]
- ],
- const [ const [ 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 ],
- const [ 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 ],
- const [ 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 ]
- ],
- const [ const [ 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 ],
- const [ 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 ],
- const [ 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 ]
- ],
- const [ const [ 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 ],
- const [ 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 ],
- const [ 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 ]
- ],
- const [ const [ 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 ],
- const [ 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 ],
- const [ 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 ]
- ],
- const [ const [ 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 ],
- const [ 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 ],
- const [ 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 ]
- ],
- const [ const [ 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 ],
- const [ 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 ],
- const [ 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 ]
- ],
- const [ const [ 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
- const [ 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ]
- ]
- ] ];
-
-
- static const List COEFFS_UPDATE_PROBA = const [
- const [ const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 ],
- const [ 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 ],
- const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ]
- ],
- const [ const [ const [ 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 ],
- const [ 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 ]
- ],
- const [ const [ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ]
- ],
- const [ const [ const [ 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ]
- ],
- const [ const [ const [ 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ],
- const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
- const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
- ] ] ];
-
- // Paragraph 14.1
- static const List<int> DC_TABLE = const [ // uint8
- 4, 5, 6, 7, 8, 9, 10, 10,
- 11, 12, 13, 14, 15, 16, 17, 17,
- 18, 19, 20, 20, 21, 21, 22, 22,
- 23, 23, 24, 25, 25, 26, 27, 28,
- 29, 30, 31, 32, 33, 34, 35, 36,
- 37, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 46, 47, 48, 49, 50,
- 51, 52, 53, 54, 55, 56, 57, 58,
- 59, 60, 61, 62, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 72, 73, 74,
- 75, 76, 76, 77, 78, 79, 80, 81,
- 82, 83, 84, 85, 86, 87, 88, 89,
- 91, 93, 95, 96, 98, 100, 101, 102,
- 104, 106, 108, 110, 112, 114, 116, 118,
- 122, 124, 126, 128, 130, 132, 134, 136,
- 138, 140, 143, 145, 148, 151, 154, 157];
-
- static const List<int> AC_TABLE = const [ // uint16
- 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30, 31, 32, 33, 34, 35,
- 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, 57, 58, 60,
- 62, 64, 66, 68, 70, 72, 74, 76,
- 78, 80, 82, 84, 86, 88, 90, 92,
- 94, 96, 98, 100, 102, 104, 106, 108,
- 110, 112, 114, 116, 119, 122, 125, 128,
- 131, 134, 137, 140, 143, 146, 149, 152,
- 155, 158, 161, 164, 167, 170, 173, 177,
- 181, 185, 189, 193, 197, 201, 205, 209,
- 213, 217, 221, 225, 229, 234, 239, 245,
- 249, 254, 259, 264, 269, 274, 279, 284];
-
- /**
- * FILTER_EXTRA_ROWS = How many extra lines are needed on the MB boundary
- * for caching, given a filtering level.
- * Simple filter: up to 2 luma samples are read and 1 is written.
- * Complex filter: up to 4 luma samples are read and 3 are written. Same for
- * U/V, so it's 8 samples total (because of the 2x upsampling).
- */
- static const List<int> FILTER_EXTRA_ROWS = const [ 0, 2, 8 ];
-
- static const int VP8_SIGNATURE = 0x2a019d;
-
- static const int MB_FEATURE_TREE_PROBS = 3;
- static const int NUM_MB_SEGMENTS = 4;
- static const int NUM_REF_LF_DELTAS = 4;
- static const int NUM_MODE_LF_DELTAS = 4; // I4x4, ZERO, *, SPLIT
- static const int MAX_NUM_PARTITIONS = 8;
-
- static const int B_DC_PRED = 0; // 4x4 modes
- static const int B_TM_PRED = 1;
- static const int B_VE_PRED = 2;
- static const int B_HE_PRED = 3;
- static const int B_RD_PRED = 4;
- static const int B_VR_PRED = 5;
- static const int B_LD_PRED = 6;
- static const int B_VL_PRED = 7;
- static const int B_HD_PRED = 8;
- static const int B_HU_PRED = 9;
- static const int NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED;
-
- // Luma16 or UV modes
- static const int DC_PRED = B_DC_PRED;
- static const int V_PRED = B_VE_PRED;
- static const int H_PRED = B_HE_PRED;
- static const int TM_PRED = B_TM_PRED;
- static const int B_PRED = NUM_BMODES;
-
- // special modes
- static const int B_DC_PRED_NOTOP = 4;
- static const int B_DC_PRED_NOLEFT = 5;
- static const int B_DC_PRED_NOTOPLEFT = 6;
- static const int NUM_B_DC_MODES = 7;
-
- // Probabilities
- static const int NUM_TYPES = 4;
- static const int NUM_BANDS = 8;
- static const int NUM_CTX = 3;
- static const int NUM_PROBAS = 11;
-
- static const int BPS = 32; // this is the common stride used by yuv[]
- static const int YUV_SIZE = (BPS * 17 + BPS * 9);
- static const int Y_SIZE = (BPS * 17);
- static const int Y_OFF = (BPS * 1 + 8);
- static const int U_OFF = (Y_OFF + BPS * 16 + BPS);
- static const int V_OFF = (U_OFF + 16);
-
- static const int YUV_FIX = 16; // fixed-point precision for RGB->YUV
- static const int YUV_HALF = 1 << (YUV_FIX - 1);
- static const int YUV_MASK = (256 << YUV_FIX) - 1;
- static const int YUV_RANGE_MIN = -227; // min value of r/g/b output
- static const int YUV_RANGE_MAX = 256 + 226; // max value of r/g/b output
- static const int YUV_FIX2 = 14; // fixed-point precision for YUV->RGB
- static const int YUV_HALF2 = 1 << (YUV_FIX2 - 1);
- static const int YUV_MASK2 = (256 << YUV_FIX2) - 1;
- static const int XOR_YUV_MASK2 = (-YUV_MASK2 - 1);
-
- // These constants are 14b fixed-point version of ITU-R BT.601 constants.
- static const int kYScale = 19077; // 1.164 = 255 / 219
- static const int kVToR = 26149; // 1.596 = 255 / 112 * 0.701
- static const int kUToG = 6419; // 0.391 = 255 / 112 * 0.886 * 0.114 / 0.587
- static const int kVToG = 13320; // 0.813 = 255 / 112 * 0.701 * 0.299 / 0.587
- static const int kUToB = 33050; // 2.018 = 255 / 112 * 0.886
- static const int kRCst = (-kYScale * 16 - kVToR * 128 + YUV_HALF2);
- static const int kGCst = (-kYScale * 16 + kUToG * 128 + kVToG * 128 + YUV_HALF2);
- static const int kBCst = (-kYScale * 16 - kUToB * 128 + YUV_HALF2);
-}
+import 'dart:typed_data';
+
+import '../../image.dart';
+import '../../util/input_buffer.dart';
+import 'vp8_bit_reader.dart';
+import 'vp8_filter.dart';
+import 'vp8_types.dart';
+import 'webp_alpha.dart';
+import 'webp_info.dart';
+
+/**
+ * WebP lossy format.
+ */
+class VP8 {
+ InputBuffer input;
+ InternalWebPInfo _webp;
+
+ VP8(InputBuffer input, this._webp) :
+ this.input = input;
+
+ WebPInfo get webp => _webp;
+
+ bool decodeHeader() {
+ int bits = input.readUint24();
+
+ final bool keyFrame = (bits & 1) == 0;
+ if (!keyFrame) {
+ return false;
+ }
+
+ if (((bits >> 1) & 7) > 3) {
+ return false; // unknown profile
+ }
+
+ if (((bits >> 4) & 1) == 0) {
+ return false; // first frame is invisible!
+ }
+
+ _frameHeader.keyFrame = (bits & 1) == 0;
+ _frameHeader.profile = (bits >> 1) & 7;
+ _frameHeader.show = (bits >> 4) & 1;
+ _frameHeader.partitionLength = (bits >> 5);
+
+ int signature = input.readUint24();
+ if (signature != VP8_SIGNATURE) {
+ return false;
+ }
+
+ webp.width = input.readUint16();
+ webp.height = input.readUint16();
+
+ return true;
+ }
+
+ Image decode() {
+ if (!_getHeaders()) {
+ return null;
+ }
+
+ output = Image(webp.width, webp.height);
+
+ // Will allocate memory and prepare everything.
+ if (!_initFrame()) {
+ return null;
+ }
+
+ // Main decoding loop
+ if (!_parseFrame()) {
+ return null;
+ }
+
+ return output;
+ }
+
+ bool _getHeaders() {
+ if (!decodeHeader()) {
+ return false;
+ }
+
+ _proba = VP8Proba();
+ for (int i = 0; i < NUM_MB_SEGMENTS; ++i) {
+ _dqm[i] = VP8QuantMatrix();
+ }
+
+ _picHeader.width = webp.width;
+ _picHeader.height = webp.height;
+ _picHeader.xscale = (webp.width >> 8) >> 6;
+ _picHeader.yscale = (webp.height >> 8) >> 6;
+
+ _cropTop = 0;
+ _cropLeft = 0;
+ _cropRight = webp.width;
+ _cropBottom = webp.height;
+
+ _mbWidth = (webp.width + 15) >> 4;
+ _mbHeight = (webp.height + 15) >> 4;
+
+ _segment = 0;
+
+ br = VP8BitReader(input.subset(_frameHeader.partitionLength));
+ input.skip(_frameHeader.partitionLength);
+
+ _picHeader.colorspace = br.get();
+ _picHeader.clampType = br.get();
+
+ if (!_parseSegmentHeader(_segmentHeader, _proba)) {
+ return false;
+ }
+
+ // Filter specs
+ if (!_parseFilterHeader()) {
+ return false;
+ }
+
+ if (!_parsePartitions(input)) {
+ return false;
+ }
+
+ // quantizer change
+ _parseQuant();
+
+ // Frame buffer marking
+ br.get(); // ignore the value of update_proba_
+
+ _parseProba();
+
+ return true;
+ }
+
+ bool _parseSegmentHeader(VP8SegmentHeader hdr, VP8Proba proba) {
+ hdr.useSegment = br.get() != 0;
+ if (hdr.useSegment) {
+ hdr.updateMap = br.get() != 0;
+ if (br.get() != 0) { // update data
+ hdr.absoluteDelta = br.get() != 0;
+ for (int s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ hdr.quantizer[s] = br.get() != 0 ? br.getSignedValue(7) : 0;
+ }
+ for (int s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ hdr.filterStrength[s] = br.get() != 0 ? br.getSignedValue(6) : 0;
+ }
+ }
+ if (hdr.updateMap) {
+ for (int s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
+ proba.segments[s] = br.get() != 0 ? br.getValue(8) : 255;
+ }
+ }
+ } else {
+ hdr.updateMap = false;
+ }
+
+ return true;
+ }
+
+ bool _parseFilterHeader() {
+ VP8FilterHeader hdr = _filterHeader;
+ hdr.simple = br.get() != 0;
+ hdr.level = br.getValue(6);
+ hdr.sharpness = br.getValue(3);
+ hdr.useLfDelta = br.get() != 0;
+ if (hdr.useLfDelta) {
+ if (br.get() != 0) { // update lf-delta?
+ for (int i = 0; i < NUM_REF_LF_DELTAS; ++i) {
+ if (br.get() != 0) {
+ hdr.refLfDelta[i] = br.getSignedValue(6);
+ }
+ }
+
+ for (int i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
+ if (br.get() != 0) {
+ hdr.modeLfDelta[i] = br.getSignedValue(6);
+ }
+ }
+ }
+ }
+
+ _filterType = (hdr.level == 0) ? 0 : hdr.simple ? 1 : 2;
+
+ return true;
+ }
+
+ /**
+ * This function returns VP8_STATUS_SUSPENDED if we don't have all the
+ * necessary data in 'buf'.
+ * This case is not necessarily an error (for incremental decoding).
+ * Still, no bitreader is ever initialized to make it possible to read
+ * unavailable memory.
+ * If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
+ * is returned, and this is an unrecoverable error.
+ * If the partitions were positioned ok, VP8_STATUS_OK is returned.
+ */
+ bool _parsePartitions(InputBuffer input) {
+ int sz = 0;
+ int bufEnd = input.length;
+
+ _numPartitions = 1 << br.getValue(2);
+ int lastPart = _numPartitions - 1;
+ int partStart = lastPart * 3;
+ if (bufEnd < partStart) {
+ // we can't even read the sizes with sz[]! That's a failure.
+ return false;
+ }
+
+ for (int p = 0; p < lastPart; ++p) {
+ InputBuffer szb = input.peekBytes(3, sz);
+ final int psize = szb[0] | (szb[1] << 8) | (szb[2] << 16);
+ int partEnd = partStart + psize;
+ if (partEnd > bufEnd) {
+ partEnd = bufEnd;
+ }
+
+ InputBuffer pin = input.subset(partEnd - partStart, position: partStart);
+ _partitions[p] = VP8BitReader(pin);
+ partStart = partEnd;
+ sz += 3;
+ }
+
+ InputBuffer pin = input.subset(bufEnd - partStart,
+ position: input.position + partStart);
+ _partitions[lastPart] = VP8BitReader(pin);
+
+ // Init is ok, but there's not enough data
+ return (partStart < bufEnd) ? true : false;
+ }
+
+ void _parseQuant() {
+ final int base_q0 = br.getValue(7);
+ final int dqy1_dc = br.get() != 0 ? br.getSignedValue(4) : 0;
+ final int dqy2_dc = br.get() != 0 ? br.getSignedValue(4) : 0;
+ final int dqy2_ac = br.get() != 0 ? br.getSignedValue(4) : 0;
+ final int dquv_dc = br.get() != 0 ? br.getSignedValue(4) : 0;
+ final int dquv_ac = br.get() != 0 ? br.getSignedValue(4) : 0;
+
+ VP8SegmentHeader hdr = _segmentHeader;
+
+ for (int i = 0; i < NUM_MB_SEGMENTS; ++i) {
+ int q;
+ if (hdr.useSegment) {
+ q = hdr.quantizer[i];
+ if (!hdr.absoluteDelta) {
+ q += base_q0;
+ }
+ } else {
+ if (i > 0) {
+ _dqm[i] = _dqm[0];
+ continue;
+ } else {
+ q = base_q0;
+ }
+ }
+
+ VP8QuantMatrix m = _dqm[i];
+ m.y1Mat[0] = DC_TABLE[_clip(q + dqy1_dc, 127)];
+ m.y1Mat[1] = AC_TABLE[_clip(q + 0, 127)];
+
+ m.y2Mat[0] = DC_TABLE[_clip(q + dqy2_dc, 127)] * 2;
+ // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16.
+ // The smallest precision for that is '(x*6349) >> 12' but 16 is a good
+ // word size.
+ m.y2Mat[1] = (AC_TABLE[_clip(q + dqy2_ac, 127)] * 101581) >> 16;
+ if (m.y2Mat[1] < 8) {
+ m.y2Mat[1] = 8;
+ }
+
+ m.uvMat[0] = DC_TABLE[_clip(q + dquv_dc, 117)];
+ m.uvMat[1] = AC_TABLE[_clip(q + dquv_ac, 127)];
+
+ m.uvQuant = q + dquv_ac; // for dithering strength evaluation
+ }
+ }
+
+ void _parseProba() {
+ VP8Proba proba = _proba;
+ for (int t = 0; t < NUM_TYPES; ++t) {
+ for (int b = 0; b < NUM_BANDS; ++b) {
+ for (int c = 0; c < NUM_CTX; ++c) {
+ for (int p = 0; p < NUM_PROBAS; ++p) {
+ final int v = br.getBit(COEFFS_UPDATE_PROBA[t][b][c][p]) != 0 ?
+ br.getValue(8) : COEFFS_PROBA_0[t][b][c][p];
+ proba.bands[t][b].probas[c][p] = v;
+ }
+ }
+ }
+ }
+
+ _useSkipProba = br.get() != 0;
+ if (_useSkipProba) {
+ _skipP = br.getValue(8);
+ }
+ }
+
+ /**
+ * Precompute the filtering strength for each segment and each i4x4/i16x16
+ * mode.
+ */
+ void _precomputeFilterStrengths() {
+ if (_filterType > 0) {
+ VP8FilterHeader hdr = _filterHeader;
+ for (int s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ // First, compute the initial level
+ int baseLevel;
+ if (_segmentHeader.useSegment) {
+ baseLevel = _segmentHeader.filterStrength[s];
+ if (!_segmentHeader.absoluteDelta) {
+ baseLevel += hdr.level;
+ }
+ } else {
+ baseLevel = hdr.level;
+ }
+
+ for (int i4x4 = 0; i4x4 <= 1; ++i4x4) {
+ VP8FInfo info = _fStrengths[s][i4x4];
+ int level = baseLevel;
+ if (hdr.useLfDelta) {
+ level += hdr.refLfDelta[0];
+ if (i4x4 != 0) {
+ level += hdr.modeLfDelta[0];
+ }
+ }
+
+ level = (level < 0) ? 0 : (level > 63) ? 63 : level;
+ if (level > 0) {
+ int ilevel = level;
+ if (hdr.sharpness > 0) {
+ if (hdr.sharpness > 4) {
+ ilevel >>= 2;
+ } else {
+ ilevel >>= 1;
+ }
+
+ if (ilevel > 9 - hdr.sharpness) {
+ ilevel = 9 - hdr.sharpness;
+ }
+ }
+
+ if (ilevel < 1) {
+ ilevel = 1;
+ }
+
+ info.fInnerLevel = ilevel;
+ info.fLimit = 2 * level + ilevel;
+ info.hevThresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
+ } else {
+ info.fLimit = 0; // no filtering
+ }
+
+ info.fInner = i4x4 != 0;
+ }
+ }
+ }
+ }
+
+ bool _initFrame() {
+ if (_webp.alphaData != null) {
+ _alphaData = _webp.alphaData;
+ }
+
+ _fStrengths = List<List<VP8FInfo>>(NUM_MB_SEGMENTS);
+ for (int i = 0; i < NUM_MB_SEGMENTS; ++i) {
+ _fStrengths[i] = [new VP8FInfo(), new VP8FInfo()];
+ }
+
+ _yuvT = List<VP8TopSamples>(_mbWidth);
+ for (int i = 0; i < _mbWidth; ++i) {
+ _yuvT[i] = VP8TopSamples();
+ }
+
+ _yuvBlock = Uint8List(YUV_SIZE);
+
+ _intraT = Uint8List(4 * _mbWidth);
+
+ _cacheYStride = 16 * _mbWidth;
+ _cacheUVStride = 8 * _mbWidth;
+
+ final int extra_rows = FILTER_EXTRA_ROWS[_filterType];
+ final int extra_y = extra_rows * _cacheYStride;
+ final int extra_uv = (extra_rows ~/ 2) * _cacheUVStride;
+
+ _cacheY = InputBuffer(new Uint8List(16 * _cacheYStride + extra_y),
+ offset: extra_y);
+
+ _cacheU = InputBuffer(new Uint8List(8 * _cacheUVStride + extra_uv),
+ offset: extra_uv);
+
+ _cacheV = InputBuffer(new Uint8List(8 * _cacheUVStride + extra_uv),
+ offset: extra_uv);
+
+ _tmpY = InputBuffer(new Uint8List(webp.width));
+
+ final int uvWidth = (webp.width + 1) >> 1;
+ _tmpU = InputBuffer(new Uint8List(uvWidth));
+ _tmpV = InputBuffer(new Uint8List(uvWidth));
+
+ // Define the area where we can skip in-loop filtering, in case of cropping.
+ //
+ // 'Simple' filter reads two luma samples outside of the macroblock
+ // and filters one. It doesn't filter the chroma samples. Hence, we can
+ // avoid doing the in-loop filtering before crop_top/crop_left position.
+ // For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
+ // Means: there's a dependency chain that goes all the way up to the
+ // top-left corner of the picture (MB #0). We must filter all the previous
+ // macroblocks.
+ {
+ final int extraPixels = FILTER_EXTRA_ROWS[_filterType];
+ if (_filterType == 2) {
+ // For complex filter, we need to preserve the dependency chain.
+ _tlMbX = 0;
+ _tlMbY = 0;
+ } else {
+ // For simple filter, we can filter only the cropped region.
+ // We include 'extra_pixels' on the other side of the boundary, since
+ // vertical or horizontal filtering of the previous macroblock can
+ // modify some abutting pixels.
+ _tlMbX = (_cropLeft - extraPixels) ~/ 16;
+ _tlMbY = (_cropTop - extraPixels) ~/ 16;
+ if (_tlMbX < 0) {
+ _tlMbX = 0;
+ }
+ if (_tlMbY < 0) {
+ _tlMbY = 0;
+ }
+ }
+
+ // We need some 'extra' pixels on the right/bottom.
+ _brMbY = (_cropBottom + 15 + extraPixels) ~/ 16;
+ _brMbX = (_cropRight + 15 + extraPixels) ~/ 16;
+ if (_brMbX > _mbWidth) {
+ _brMbX = _mbWidth;
+ }
+ if (_brMbY > _mbHeight) {
+ _brMbY = _mbHeight;
+ }
+ }
+
+ _mbInfo = List<VP8MB>(_mbWidth + 1);
+ _mbData = List<VP8MBData>(_mbWidth);
+ _fInfo = List<VP8FInfo>(_mbWidth);
+
+ for (int i = 0; i < _mbWidth; ++i) {
+ _mbInfo[i] = VP8MB();
+ _mbData[i] = VP8MBData();
+ }
+ _mbInfo[_mbWidth] = VP8MB();
+
+ _precomputeFilterStrengths();
+
+ // Init critical function pointers and look-up tables.
+ _dsp = VP8Filter();
+ return true;
+ }
+
+ bool _parseFrame() {
+ for (_mbY = 0; _mbY < _brMbY; ++_mbY) {
+ // Parse bitstream for this row.
+ VP8BitReader tokenBr = _partitions[_mbY & (_numPartitions - 1)];
+ for (; _mbX < _mbWidth; ++_mbX) {
+ if (!_decodeMB(tokenBr)) {
+ return false;
+ }
+ }
+
+ // Prepare for next scanline
+ VP8MB left = _mbInfo[0];
+ left.nz = 0;
+ left.nzDc = 0;
+ _intraL.fillRange(0, _intraL.length, B_DC_PRED);
+ _mbX = 0;
+
+ // Reconstruct, filter and emit the row.
+ if (!_processRow()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool _processRow() {
+ _reconstructRow();
+
+ bool useFilter = (_filterType > 0) && (_mbY >= _tlMbY) && (_mbY <= _brMbY);
+ return _finishRow(useFilter);
+ }
+
+ void _reconstructRow() {
+ int mb_y = _mbY;
+ InputBuffer y_dst = InputBuffer(_yuvBlock, offset: Y_OFF);
+ InputBuffer u_dst = InputBuffer(_yuvBlock, offset: U_OFF);
+ InputBuffer v_dst = InputBuffer(_yuvBlock, offset: V_OFF);
+
+ for (int mb_x = 0; mb_x < _mbWidth; ++mb_x) {
+ VP8MBData block = _mbData[mb_x];
+
+ // Rotate in the left samples from previously decoded block. We move four
+ // pixels at a time for alignment reason, and because of in-loop filter.
+ if (mb_x > 0) {
+ for (int j = -1; j < 16; ++j) {
+ y_dst.memcpy(j * BPS - 4, 4, y_dst, j * BPS + 12);
+ }
+
+ for (int j = -1; j < 8; ++j) {
+ u_dst.memcpy(j * BPS - 4, 4, u_dst, j * BPS + 4);
+ v_dst.memcpy(j * BPS - 4, 4, v_dst, j * BPS + 4);
+ }
+ } else {
+ for (int j = 0; j < 16; ++j) {
+ y_dst[j * BPS - 1] = 129;
+ }
+
+ for (int j = 0; j < 8; ++j) {
+ u_dst[j * BPS - 1] = 129;
+ v_dst[j * BPS - 1] = 129;
+ }
+
+ // Init top-left sample on left column too
+ if (mb_y > 0) {
+ y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
+ }
+ }
+
+ // bring top samples into the cache
+ VP8TopSamples top_yuv = _yuvT[mb_x];
+ Int16List coeffs = block.coeffs;
+ int bits = block.nonZeroY;
+
+ if (mb_y > 0) {
+ y_dst.memcpy(-BPS, 16, top_yuv.y);
+ u_dst.memcpy(-BPS, 8, top_yuv.u);
+ v_dst.memcpy(-BPS, 8, top_yuv.v);
+ } else if (mb_x == 0) {
+ // we only need to do this init once at block (0,0).
+ // Afterward, it remains valid for the whole topmost row.
+ y_dst.memset(-BPS - 1, 16 + 4 + 1, 127);
+ u_dst.memset(-BPS - 1, 8 + 1, 127);
+ v_dst.memset(-BPS - 1, 8 + 1, 127);
+ }
+
+ // predict and add residuals
+ if (block.isIntra4x4) { // 4x4
+ InputBuffer topRight = InputBuffer.from(y_dst, offset: -BPS + 16);
+ Uint32List topRight32 = topRight.toUint32List();
+
+ if (mb_y > 0) {
+ if (mb_x >= _mbWidth - 1) { // on rightmost border
+ topRight.memset(0, 4, top_yuv.y[15]);
+ } else {
+ topRight.memcpy(0, 4, _yuvT[mb_x + 1].y);
+ }
+ }
+
+ // replicate the top-right pixels below
+ int p = topRight32[0];
+ topRight32[3 * BPS] = p;
+ topRight32[2 * BPS] = p;
+ topRight32[BPS] = p;
+
+ // predict and add residuals for all 4x4 blocks in turn.
+ for (int n = 0; n < 16; ++n, bits = (bits << 2) & 0xffffffff) {
+ InputBuffer dst = InputBuffer.from(y_dst, offset: kScan[n]);
+
+ VP8Filter.PredLuma4[block.imodes[n]](dst);
+
+ _doTransform(bits, new InputBuffer(coeffs, offset: n * 16), dst);
+ }
+ } else { // 16x16
+ int predFunc = _checkMode(mb_x, mb_y, block.imodes[0]);
+
+ VP8Filter.PredLuma16[predFunc](y_dst);
+ if (bits != 0) {
+ for (int n = 0; n < 16; ++n, bits = (bits << 2) & 0xffffffff) {
+ InputBuffer dst = InputBuffer.from(y_dst, offset: kScan[n]);
+
+ _doTransform(bits, new InputBuffer(coeffs, offset: n * 16), dst);
+ }
+ }
+ }
+
+ // Chroma
+ int bits_uv = block.nonZeroUV;
+ int pred_func = _checkMode(mb_x, mb_y, block.uvmode);
+ VP8Filter.PredChroma8[pred_func](u_dst);
+ VP8Filter.PredChroma8[pred_func](v_dst);
+
+ InputBuffer c1 = InputBuffer(coeffs, offset: 16 * 16);
+ _doUVTransform(bits_uv, c1, u_dst);
+
+ InputBuffer c2 = InputBuffer(coeffs, offset: 20 * 16);
+ _doUVTransform(bits_uv >> 8, c2, v_dst);
+
+ // stash away top samples for next block
+ if (mb_y < _mbHeight - 1) {
+ top_yuv.y.setRange(0, 16, y_dst.toUint8List(), 15 * BPS);
+ top_yuv.u.setRange(0, 8, u_dst.toUint8List(), 7 * BPS);
+ top_yuv.v.setRange(0, 8, v_dst.toUint8List(), 7 * BPS);
+ }
+
+ // Transfer reconstructed samples from yuv_b_ cache to final destination.
+ int y_out = mb_x * 16; // dec->cache_y_ +
+ int u_out = mb_x * 8; // dec->cache_u_ +
+ int v_out = mb_x * 8; // _dec->cache_v_ +
+
+ for (int j = 0; j < 16; ++j) {
+ int start = y_out + j * _cacheYStride;
+ _cacheY.memcpy(start, 16, y_dst, j * BPS);
+ }
+
+ for (int j = 0; j < 8; ++j) {
+ int start = u_out + j * _cacheUVStride;
+ _cacheU.memcpy(start, 8, u_dst, j * BPS);
+
+ start = v_out + j * _cacheUVStride;
+ _cacheV.memcpy(start, 8, v_dst, j * BPS);
+ }
+ }
+ }
+
+ static const List<int> kScan = const [
+ 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
+ 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
+ 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
+ 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS ];
+
+ static int _checkMode(int mb_x, int mb_y, int mode) {
+ if (mode == B_DC_PRED) {
+ if (mb_x == 0) {
+ return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
+ } else {
+ return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
+ }
+ }
+ return mode;
+ }
+
+ void _doTransform(int bits, InputBuffer src, InputBuffer dst) {
+ switch (bits >> 30) {
+ case 3:
+ _dsp.transform(src, dst, false);
+ break;
+ case 2:
+ _dsp.transformAC3(src, dst);
+ break;
+ case 1:
+ _dsp.transformDC(src, dst);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void _doUVTransform(int bits, InputBuffer src, InputBuffer dst) {
+ if (bits & 0xff != 0) { // any non-zero coeff at all?
+ if (bits & 0xaa != 0) { // any non-zero AC coefficient?
+ // note we don't use the AC3 variant for U/V
+ _dsp.transformUV(src, dst);
+ } else {
+ _dsp.transformDCUV(src, dst);
+ }
+ }
+ }
+
+ // vertical position of a MB
+ int MACROBLOCK_VPOS(int mb_y) => mb_y * 16;
+
+ /**
+ * kFilterExtraRows[] = How many extra lines are needed on the MB boundary
+ * for caching, given a filtering level.
+ * Simple filter: up to 2 luma samples are read and 1 is written.
+ * Complex filter: up to 4 luma samples are read and 3 are written. Same for
+ * U/V, so it's 8 samples total (because of the 2x upsampling).
+ */
+ static const List<int> kFilterExtraRows = const [ 0, 2, 8 ];
+
+ void _doFilter(int mbX, int mbY) {
+ final int yBps = _cacheYStride;
+ VP8FInfo fInfo = _fInfo[mbX];
+ InputBuffer yDst = InputBuffer.from(_cacheY, offset: mbX * 16);
+ final int ilevel = fInfo.fInnerLevel;
+ final int limit = fInfo.fLimit;
+ if (limit == 0) {
+ return;
+ }
+
+ if (_filterType == 1) { // simple
+ if (mbX > 0) {
+ _dsp.simpleHFilter16(yDst, yBps, limit + 4);
+ }
+ if (fInfo.fInner) {
+ _dsp.simpleHFilter16i(yDst, yBps, limit);
+ }
+ if (mbY > 0) {
+ _dsp.simpleVFilter16(yDst, yBps, limit + 4);
+ }
+ if (fInfo.fInner) {
+ _dsp.simpleVFilter16i(yDst, yBps, limit);
+ }
+ } else { // complex
+ final int uvBps = _cacheUVStride;
+ InputBuffer uDst = InputBuffer.from(_cacheU, offset: mbX * 8);
+ InputBuffer vDst = InputBuffer.from(_cacheV, offset: mbX * 8);
+
+ final int hevThresh = fInfo.hevThresh;
+ if (mbX > 0) {
+ _dsp.hFilter16(yDst, yBps, limit + 4, ilevel, hevThresh);
+ _dsp.hFilter8(uDst, vDst, uvBps, limit + 4, ilevel, hevThresh);
+ }
+ if (fInfo.fInner) {
+ _dsp.hFilter16i(yDst, yBps, limit, ilevel, hevThresh);
+ _dsp.hFilter8i(uDst, vDst, uvBps, limit, ilevel, hevThresh);
+ }
+ if (mbY > 0) {
+ _dsp.vFilter16(yDst, yBps, limit + 4, ilevel, hevThresh);
+ _dsp.vFilter8(uDst, vDst, uvBps, limit + 4, ilevel, hevThresh);
+ }
+ if (fInfo.fInner) {
+ _dsp.vFilter16i(yDst, yBps, limit, ilevel, hevThresh);
+ _dsp.vFilter8i(uDst, vDst, uvBps, limit, ilevel, hevThresh);
+ }
+ }
+ }
+
+ /**
+ * Filter the decoded macroblock row (if needed)
+ */
+ void _filterRow() {
+ for (int mbX = _tlMbX; mbX < _brMbX; ++mbX) {
+ _doFilter(mbX, _mbY);
+ }
+ }
+
+ void _ditherRow() {
+ }
+
+
+ /**
+ * This function is called after a row of macroblocks is finished decoding.
+ * It also takes into account the following restrictions:
+ *
+ * * In case of in-loop filtering, we must hold off sending some of the bottom
+ * pixels as they are yet unfiltered. They will be when the next macroblock
+ * row is decoded. Meanwhile, we must preserve them by rotating them in the
+ * cache area. This doesn't hold for the very bottom row of the uncropped
+ * picture of course.
+ * * we must clip the remaining pixels against the cropping area. The VP8Io
+ * struct must have the following fields set correctly before calling put():
+ */
+ bool _finishRow(bool useFilter) {
+ final int extraYRows = kFilterExtraRows[_filterType];
+ final int ySize = extraYRows * _cacheYStride;
+ final int uvSize = (extraYRows ~/ 2) * _cacheUVStride;
+ InputBuffer yDst = InputBuffer.from(_cacheY, offset: -ySize);
+ InputBuffer uDst = InputBuffer.from(_cacheU, offset: -uvSize);
+ InputBuffer vDst = InputBuffer.from(_cacheV, offset: -uvSize);
+ final int mbY = _mbY;
+ final bool isFirstRow = (mbY == 0);
+ final bool isLastRow = (mbY >= _brMbY - 1);
+ int yStart = MACROBLOCK_VPOS(mbY);
+ int yEnd = MACROBLOCK_VPOS(mbY + 1);
+
+ if (useFilter) {
+ _filterRow();
+ }
+
+ if (_dither) {
+ _ditherRow();
+ }
+
+ if (!isFirstRow) {
+ yStart -= extraYRows;
+ _y = InputBuffer.from(yDst);
+ _u = InputBuffer.from(uDst);
+ _v = InputBuffer.from(vDst);
+ } else {
+ _y = InputBuffer.from(_cacheY);
+ _u = InputBuffer.from(_cacheU);
+ _v = InputBuffer.from(_cacheV);
+ }
+
+ if (!isLastRow) {
+ yEnd -= extraYRows;
+ }
+
+ if (yEnd > _cropBottom) {
+ yEnd = _cropBottom; // make sure we don't overflow on last row.
+ }
+
+ _a = null;
+ if (_alphaData != null && yStart < yEnd) {
+ _a = _decompressAlphaRows(yStart, yEnd - yStart);
+ if (_a == null) {
+ return false;
+ }
+ }
+
+ if (yStart < _cropTop) {
+ final int deltaY = _cropTop - yStart;
+ yStart = _cropTop;
+
+ _y.offset += _cacheYStride * deltaY;
+ _u.offset += _cacheUVStride * (deltaY >> 1);
+ _v.offset += _cacheUVStride * (deltaY >> 1);
+
+ if (_a != null) {
+ _a.offset += webp.width * deltaY;
+ }
+ }
+
+ if (yStart < yEnd) {
+ _y.offset += _cropLeft;
+ _u.offset += _cropLeft >> 1;
+ _v.offset += _cropLeft >> 1;
+ if (_a != null) {
+ _a.offset += _cropLeft;
+ }
+
+ _put(yStart - _cropTop, _cropRight - _cropLeft, yEnd - yStart);
+ }
+
+ // rotate top samples if needed
+ if (!isLastRow) {
+ _cacheY.memcpy(-ySize, ySize, yDst, 16 * _cacheYStride);
+ _cacheU.memcpy(-uvSize, uvSize, uDst, 8 * _cacheUVStride);
+ _cacheV.memcpy(-uvSize, uvSize, vDst, 8 * _cacheUVStride);
+ }
+
+ return true;
+ }
+
+ bool _put(int mbY, int mbW, int mbH) {
+ if (mbW <= 0 || mbH <= 0) {
+ return false;
+ }
+
+ /*int numLinesOut =*/ _emitFancyRGB(mbY, mbW, mbH);
+ _emitAlphaRGB(mbY, mbW, mbH);
+
+ //_lastY += numLinesOut;
+
+ return true;
+ }
+
+ int _clip8(int v) {
+ int d = ((v & XOR_YUV_MASK2) == 0) ? (v >> YUV_FIX2) : (v < 0) ? 0 : 255;
+ return d;
+ }
+
+ int _yuvToR(int y, int v) {
+ return _clip8(kYScale * y + kVToR * v + kRCst);
+ }
+
+ int _yuvToG(int y, int u, int v) {
+ return _clip8(kYScale * y - kUToG * u - kVToG * v + kGCst);
+ }
+
+ int _yuvToB(int y, int u) {
+ return _clip8(kYScale * y + kUToB * u + kBCst);
+ }
+
+ void _yuvToRgb(int y, int u, int v, InputBuffer rgb) {
+ rgb[2] = _yuvToR(y, v);
+ rgb[1] = _yuvToG(y, u, v);
+ rgb[0] = _yuvToB(y, u);
+ }
+
+ void _yuvToRgba(int y, int u, int v, InputBuffer rgba) {
+ _yuvToRgb(y, u, v, rgba);
+ rgba[3] = 0xff;
+ }
+
+ void _upsample(InputBuffer topY, InputBuffer bottomY,
+ InputBuffer topU, InputBuffer topV,
+ InputBuffer curU, InputBuffer curV,
+ InputBuffer topDst, InputBuffer bottomDst,
+ int len) {
+ int LOAD_UV(int u, int v) => ((u) | ((v) << 16));
+
+ final int lastPixelPair = (len - 1) >> 1;
+ int tl_uv = LOAD_UV(topU[0], topV[0]); // top-left sample
+ int l_uv = LOAD_UV(curU[0], curV[0]); // left-sample
+
+ final int uv0 = (3 * tl_uv + l_uv + 0x00020002) >> 2;
+ _yuvToRgba(topY[0], uv0 & 0xff, (uv0 >> 16), topDst);
+
+ if (bottomY != null) {
+ final int uv0 = (3 * l_uv + tl_uv + 0x00020002) >> 2;
+ _yuvToRgba(bottomY[0], uv0 & 0xff, (uv0 >> 16), bottomDst);
+ }
+
+ for (int x = 1; x <= lastPixelPair; ++x) {
+ final int t_uv = LOAD_UV(topU[x], topV[x]); // top sample
+ final int uv = LOAD_UV(curU[x], curV[x]); // sample
+ // precompute invariant values associated with first and second diagonals
+ final int avg = tl_uv + t_uv + l_uv + uv + 0x00080008;
+ final int diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3;
+ final int diag_03 = (avg + 2 * (tl_uv + uv)) >> 3;
+
+ int uv0 = (diag_12 + tl_uv) >> 1;
+ int uv1 = (diag_03 + t_uv) >> 1;
+
+ _yuvToRgba(topY[2 * x - 1], uv0 & 0xff, (uv0 >> 16),
+ new InputBuffer.from(topDst, offset: (2 * x - 1) * 4));
+
+ _yuvToRgba(topY[2 * x - 0], uv1 & 0xff, (uv1 >> 16),
+ new InputBuffer.from(topDst, offset: (2 * x - 0) * 4));
+
+ if (bottomY != null) {
+ uv0 = (diag_03 + l_uv) >> 1;
+ uv1 = (diag_12 + uv) >> 1;
+
+ _yuvToRgba(bottomY[2 * x - 1], uv0 & 0xff, (uv0 >> 16),
+ new InputBuffer.from(bottomDst, offset: (2 * x - 1) * 4));
+
+ _yuvToRgba(bottomY[2 * x], uv1 & 0xff, (uv1 >> 16),
+ new InputBuffer.from(bottomDst, offset: (2 * x + 0) * 4));
+ }
+
+ tl_uv = t_uv;
+ l_uv = uv;
+ }
+
+ if ((len & 1) == 0) {
+ final int uv0 = (3 * tl_uv + l_uv + 0x00020002) >> 2;
+ _yuvToRgba(topY[len - 1], uv0 & 0xff, (uv0 >> 16),
+ new InputBuffer.from(topDst, offset: (len - 1) * 4));
+
+ if (bottomY != null) {
+ final int uv0 = (3 * l_uv + tl_uv + 0x00020002) >> 2;
+ _yuvToRgba(bottomY[len - 1], uv0 & 0xff, (uv0 >> 16),
+ new InputBuffer.from(bottomDst, offset: (len - 1) * 4));
+ }
+ }
+ }
+
+ void _emitAlphaRGB(int mbY, int mbW, int mbH) {
+ if (_a == null) {
+ return;
+ }
+
+ final int stride = webp.width * 4;
+ InputBuffer alpha = InputBuffer.from(_a);
+ int startY = mbY;
+ int numRows = mbH;
+
+ // Compensate for the 1-line delay of the fancy upscaler.
+ // This is similar to EmitFancyRGB().
+ if (startY == 0) {
+ // We don't process the last row yet. It'll be done during the next call.
+ --numRows;
+ } else {
+ --startY;
+ // Fortunately, *alpha data is persistent, so we can go back
+ // one row and finish alpha blending, now that the fancy upscaler
+ // completed the YUV->RGB interpolation.
+ alpha.offset -= webp.width;
+ }
+
+ InputBuffer dst = InputBuffer(output.getBytes(), offset: startY * stride + 3);
+
+ if (_cropTop + mbY + mbH == _cropBottom) {
+ // If it's the very last call, we process all the remaining rows!
+ numRows = _cropBottom - _cropTop - startY;
+ }
+
+ for (int y = 0; y < numRows; ++y) {
+ for (int x = 0; x < mbW; ++x) {
+ final int alphaValue = alpha[x];
+ dst[4 * x] = alphaValue & 0xff;
+ }
+
+ alpha.offset += webp.width;
+ dst.offset += stride;
+ }
+ }
+
+
+ int _emitFancyRGB(int mbY, int mbW, int mbH) {
+ int numLinesOut = mbH; // a priori guess
+ InputBuffer dst = InputBuffer(output.getBytes(), offset: mbY * webp.width * 4);
+ InputBuffer curY = InputBuffer.from(_y);
+ InputBuffer curU = InputBuffer.from(_u);
+ InputBuffer curV = InputBuffer.from(_v);
+ int y = mbY;
+ final int yEnd = mbY + mbH;
+ final int uvW = (mbW + 1) >> 1;
+ final int stride = webp.width * 4;
+ InputBuffer topU = InputBuffer.from(_tmpU);
+ InputBuffer topV = InputBuffer.from(_tmpV);
+
+ if (y == 0) {
+ // First line is special cased. We mirror the u/v samples at boundary.
+ _upsample(curY, null, curU, curV, curU, curV, dst, null, mbW);
+ } else {
+ // We can finish the left-over line from previous call.
+ _upsample(_tmpY, curY, topU, topV, curU, curV,
+ new InputBuffer.from(dst, offset: -stride), dst, mbW);
+ ++numLinesOut;
+ }
+
+ // Loop over each output pairs of row.
+ topU.buffer = curU.buffer;
+ topV.buffer = curV.buffer;
+ for (; y + 2 < yEnd; y += 2) {
+ topU.offset = curU.offset;
+ topV.offset = curV.offset;
+ curU.offset += _cacheUVStride;
+ curV.offset += _cacheUVStride;
+ dst.offset += 2 * stride;
+ curY.offset += 2 * _cacheYStride;
+ _upsample(new InputBuffer.from(curY, offset: -_cacheYStride), curY,
+ topU, topV, curU, curV,
+ new InputBuffer.from(dst, offset: -stride), dst, mbW);
+ }
+
+ // move to last row
+ curY.offset += _cacheYStride;
+ if (_cropTop + yEnd < _cropBottom) {
+ // Save the unfinished samples for next call (as we're not done yet).
+ _tmpY.memcpy(0, mbW, curY);
+ _tmpU.memcpy(0, uvW, curU);
+ _tmpV.memcpy(0, uvW, curV);
+ // The fancy upsampler leaves a row unfinished behind
+ // (except for the very last row)
+ numLinesOut--;
+ } else {
+ // Process the very last row of even-sized picture
+ if ((yEnd & 1) == 0) {
+ _upsample(curY, null, curU, curV, curU, curV,
+ new InputBuffer.from(dst, offset: stride), null, mbW);
+ }
+ }
+
+ return numLinesOut;
+ }
+
+ InputBuffer _decompressAlphaRows(int row, int numRows) {
+ final int width = webp.width;
+ final int height = webp.height;
+
+ if (row < 0 || numRows <= 0 || row + numRows > height) {
+ return null; // sanity check.
+ }
+
+ if (row == 0) {
+ _alphaPlane = Uint8List(width * height);
+ _alpha = WebPAlpha(_alphaData, width, height);
+ }
+
+ if (!_alpha.isAlphaDecoded) {
+ if (!_alpha.decode(row, numRows, _alphaPlane)) {
+ return null;
+ }
+ }
+
+ // Return a pointer to the current decoded row.
+ return new InputBuffer(_alphaPlane, offset: row * width);
+ }
+
+ bool _decodeMB(VP8BitReader tokenBr) {
+ VP8MB left = _mbInfo[0];
+ VP8MB mb = _mbInfo[1 + _mbX];
+ VP8MBData block = _mbData[_mbX];
+ bool skip;
+
+ // Note: we don't save segment map (yet), as we don't expect
+ // to decode more than 1 keyframe.
+ if (_segmentHeader.updateMap) {
+ // Hardcoded tree parsing
+ _segment = br.getBit(_proba.segments[0]) == 0 ?
+ br.getBit(_proba.segments[1]) :
+ 2 + br.getBit(_proba.segments[2]);
+ }
+
+ skip = _useSkipProba ? br.getBit(_skipP) != 0 : false;
+
+ _parseIntraMode();
+
+ if (!skip) {
+ skip = _parseResiduals(mb, tokenBr);
+ } else {
+ left.nz = mb.nz = 0;
+ if (!block.isIntra4x4) {
+ left.nzDc = mb.nzDc = 0;
+ }
+ block.nonZeroY = 0;
+ block.nonZeroUV = 0;
+ }
+
+ if (_filterType > 0) { // store filter info
+ _fInfo[_mbX] = _fStrengths[_segment][block.isIntra4x4 ? 1 : 0];
+ VP8FInfo finfo = _fInfo[_mbX];
+ finfo.fInner = finfo.fInner || !skip;
+ }
+
+ return true;
+ }
+
+ bool _parseResiduals(VP8MB mb, VP8BitReader tokenBr) {
+ var bands = _proba.bands;
+ List<VP8BandProbas> acProba;
+ VP8QuantMatrix q = _dqm[_segment];
+ VP8MBData block = _mbData[_mbX];
+ InputBuffer dst = InputBuffer(block.coeffs);
+ //int di = 0;
+ VP8MB leftMb = _mbInfo[0];
+ int tnz;
+ int lnz;
+ int nonZeroY = 0;
+ int nonZeroUV = 0;
+ int outTopNz;
+ int outLeftNz;
+ int first;
+
+ dst.memset(0, dst.length, 0);
+
+ if (!block.isIntra4x4) { // parse DC
+ InputBuffer dc = InputBuffer(new Int16List(16));
+ final int ctx = mb.nzDc + leftMb.nzDc;
+ final int nz = _getCoeffs(tokenBr, bands[1], ctx, q.y2Mat, 0, dc);
+ mb.nzDc = leftMb.nzDc = (nz > 0) ? 1 : 0;
+ if (nz > 1) { // more than just the DC -> perform the full transform
+ _transformWHT(dc, dst);
+ } else { // only DC is non-zero -> inlined simplified transform
+ final int dc0 = (dc[0] + 3) >> 3;
+ for (int i = 0; i < 16 * 16; i += 16) {
+ dst[i] = dc0;
+ }
+ }
+
+ first = 1;
+ acProba = bands[0];
+ } else {
+ first = 0;
+ acProba = bands[3];
+ }
+
+ tnz = mb.nz & 0x0f;
+ lnz = leftMb.nz & 0x0f;
+ for (int y = 0; y < 4; ++y) {
+ int l = lnz & 1;
+ int nzCoeffs = 0;
+ for (int x = 0; x < 4; ++x) {
+ final int ctx = l + (tnz & 1);
+ final int nz = _getCoeffs(tokenBr, acProba, ctx, q.y1Mat, first, dst);
+ l = (nz > first) ? 1 : 0;
+ tnz = (tnz >> 1) | (l << 7);
+ nzCoeffs = _nzCodeBits(nzCoeffs, nz, dst[0] != 0 ? 1 : 0);
+ dst.offset += 16;
+ }
+
+ tnz >>= 4;
+ lnz = (lnz >> 1) | (l << 7);
+ nonZeroY = (nonZeroY << 8) | nzCoeffs;
+ }
+ outTopNz = tnz;
+ outLeftNz = lnz >> 4;
+
+ for (int ch = 0; ch < 4; ch += 2) {
+ int nzCoeffs = 0;
+ tnz = mb.nz >> (4 + ch);
+ lnz = leftMb.nz >> (4 + ch);
+ for (int y = 0; y < 2; ++y) {
+ int l = lnz & 1;
+ for (int x = 0; x < 2; ++x) {
+ final int ctx = l + (tnz & 1);
+ final int nz = _getCoeffs(tokenBr, bands[2], ctx, q.uvMat, 0, dst);
+ l = (nz > 0) ? 1 : 0;
+ tnz = (tnz >> 1) | (l << 3);
+ nzCoeffs = _nzCodeBits(nzCoeffs, nz, dst[0] != 0 ? 1 : 0);
+ dst.offset += 16;
+ }
+
+ tnz >>= 2;
+ lnz = (lnz >> 1) | (l << 5);
+ }
+
+ // Note: we don't really need the per-4x4 details for U/V blocks.
+ nonZeroUV |= nzCoeffs << (4 * ch);
+ outTopNz |= (tnz << 4) << ch;
+ outLeftNz |= (lnz & 0xf0) << ch;
+ }
+
+ mb.nz = outTopNz;
+ leftMb.nz = outLeftNz;
+
+ block.nonZeroY = nonZeroY;
+ block.nonZeroUV = nonZeroUV;
+
+ // We look at the mode-code of each block and check if some blocks have less
+ // than three non-zero coeffs (code < 2). This is to avoid dithering flat and
+ // empty blocks.
+ block.dither = (nonZeroUV & 0xaaaa) != 0 ? 0 : q.dither;
+
+ // will be used for further optimization
+ return (nonZeroY | nonZeroUV) == 0;
+ }
+
+ void _transformWHT(InputBuffer src, InputBuffer out) {
+ Int32List tmp = Int32List(16);
+
+ int oi = 0;
+ for (int i = 0; i < 4; ++i) {
+ final int a0 = src[0 + i] + src[12 + i];
+ final int a1 = src[4 + i] + src[ 8 + i];
+ final int a2 = src[4 + i] - src[ 8 + i];
+ final int a3 = src[0 + i] - src[12 + i];
+ tmp[0 + i] = a0 + a1;
+ tmp[8 + i] = a0 - a1;
+ tmp[4 + i] = a3 + a2;
+ tmp[12 + i] = a3 - a2;
+ }
+
+ for (int i = 0; i < 4; ++i) {
+ final int dc = tmp[0 + i * 4] + 3; // w/ rounder
+ final int a0 = dc + tmp[3 + i * 4];
+ final int a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
+ final int a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
+ final int a3 = dc - tmp[3 + i * 4];
+ out[oi + 0] = (a0 + a1) >> 3;
+ out[oi + 16] = (a3 + a2) >> 3;
+ out[oi + 32] = (a0 - a1) >> 3;
+ out[oi + 48] = (a3 - a2) >> 3;
+
+ oi += 64;
+ }
+ }
+
+ int _nzCodeBits(int nz_coeffs, int nz, int dc_nz) {
+ nz_coeffs <<= 2;
+ nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz;
+ return nz_coeffs;
+ }
+
+ static const List<int> kBands = const [
+ 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0];
+
+ static const List<int> kCat3 = const [ 173, 148, 140 ];
+ static const List<int> kCat4 = const [ 176, 155, 140, 135 ];
+ static const List<int> kCat5 = const [ 180, 157, 141, 134, 130 ];
+ static const List<int> kCat6 = const [ 254, 254, 243, 230, 196, 177, 153,
+ 140, 133, 130, 129 ];
+ static const List<List<int>> kCat3456 = const [ kCat3, kCat4, kCat5, kCat6 ];
+ static const List<int> kZigzag = const [ 0, 1, 4, 8, 5, 2, 3, 6, 9, 12,
+ 13, 10, 7, 11, 14, 15 ];
+
+ /**
+ * See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
+ */
+ int _getLargeValue(VP8BitReader br, List<int> p) {
+ int v;
+ if (br.getBit(p[3]) == 0) {
+ if (br.getBit(p[4]) == 0) {
+ v = 2;
+ } else {
+ v = 3 + br.getBit(p[5]);
+ }
+ } else {
+ if (br.getBit(p[6]) == 0) {
+ if (br.getBit(p[7]) == 0) {
+ v = 5 + br.getBit(159);
+ } else {
+ v = 7 + 2 * br.getBit(165);
+ v += br.getBit(145);
+ }
+ } else {
+ final int bit1 = br.getBit(p[8]);
+ final int bit0 = br.getBit(p[9 + bit1]);
+ final int cat = 2 * bit1 + bit0;
+ v = 0;
+ List<int> tab = kCat3456[cat];
+ for (int i = 0, len = tab.length; i < len; ++i) {
+ v += v + br.getBit(tab[i]);
+ }
+ v += 3 + (8 << cat);
+ }
+ }
+ return v;
+ }
+
+
+ /**
+ * Returns the position of the last non-zero coeff plus one
+ */
+ int _getCoeffs(VP8BitReader br, List<VP8BandProbas> prob,
+ int ctx, List<int> dq, int n, InputBuffer out) {
+ // n is either 0 or 1 here. kBands[n] is not necessary for extracting '*p'.
+ List<int> p = prob[n].probas[ctx];
+ for (; n < 16; ++n) {
+ if (br.getBit(p[0]) == 0) {
+ return n; // previous coeff was last non-zero coeff
+ }
+
+ while (br.getBit(p[1]) == 0) { // sequence of zero coeffs
+ p = prob[kBands[++n]].probas[0];
+ if (n == 16) {
+ return 16;
+ }
+ }
+
+ { // non zero coeff
+ List<Uint8List> p_ctx = prob[kBands[n + 1]].probas;
+ int v;
+ if (br.getBit(p[2]) == 0) {
+ v = 1;
+ p = p_ctx[1];
+ } else {
+ v = _getLargeValue(br, p);
+ p = p_ctx[2];
+ }
+
+ out[kZigzag[n]] = br.getSigned(v) * dq[n > 0 ? 1 : 0];
+ }
+ }
+ return 16;
+ }
+
+ void _parseIntraMode() {
+ int ti = 4 * _mbX;
+ int li = 0;
+ Uint8List top = _intraT;
+ Uint8List left = _intraL;
+
+ VP8MBData block = _mbData[_mbX];
+
+ // decide for B_PRED first
+ block.isIntra4x4 = br.getBit(145) == 0;
+ if (!block.isIntra4x4) {
+ // Hardcoded 16x16 intra-mode decision tree.
+ final int ymode =
+ br.getBit(156) != 0 ?
+ (br.getBit(128) != 0 ? TM_PRED : H_PRED) :
+ (br.getBit(163) != 0 ? V_PRED : DC_PRED);
+ block.imodes[0] = ymode;
+
+ top.fillRange(ti, ti + 4, ymode);
+ left.fillRange(li, li + 4, ymode);
+ } else {
+ Uint8List modes = block.imodes;
+ int mi = 0;
+ for (int y = 0; y < 4; ++y) {
+ int ymode = left[y];
+ for (int x = 0; x < 4; ++x) {
+ List<int> prob = kBModesProba[top[ti + x]][ymode];
+
+ // Generic tree-parsing
+ int b = br.getBit(prob[0]);
+ int i = kYModesIntra4[b];
+
+ while (i > 0) {
+ i = kYModesIntra4[2 * i + br.getBit(prob[i])];
+ }
+
+ ymode = -i;
+ top[ti + x] = ymode;
+ }
+
+ modes.setRange(mi, mi + 4, top, ti);
+
+ mi += 4;
+ left[y] = ymode;
+ }
+ }
+
+ // Hardcoded UVMode decision tree
+ block.uvmode = br.getBit(142) == 0 ? DC_PRED
+ : br.getBit(114) == 0 ? V_PRED
+ : br.getBit(183) != 0 ? TM_PRED : H_PRED;
+ }
+
+ // Main data source
+ VP8BitReader br;
+
+ Image output;
+
+ VP8Filter _dsp;
+
+ // headers
+ VP8FrameHeader _frameHeader = VP8FrameHeader();
+ VP8PictureHeader _picHeader = VP8PictureHeader();
+ VP8FilterHeader _filterHeader = VP8FilterHeader();
+ VP8SegmentHeader _segmentHeader = VP8SegmentHeader();
+
+ int _cropLeft;
+ int _cropRight;
+ int _cropTop;
+ int _cropBottom;
+
+ /// Width in macroblock units.
+ int _mbWidth;
+ /// Height in macroblock units.
+ int _mbHeight;
+
+ // Macroblock to process/filter, depending on cropping and filter_type.
+ int _tlMbX; // top-left MB that must be in-loop filtered
+ int _tlMbY;
+ int _brMbX; // last bottom-right MB that must be decoded
+ int _brMbY;
+
+ // number of partitions.
+ int _numPartitions;
+ // per-partition boolean decoders.
+ List<VP8BitReader> _partitions = List<VP8BitReader>(MAX_NUM_PARTITIONS);
+
+ // Dithering strength, deduced from decoding options
+ bool _dither = false; // whether to use dithering or not
+ //VP8Random _ditheringRand; // random generator for dithering
+
+ // dequantization (one set of DC/AC dequant factor per segment)
+ List<VP8QuantMatrix> _dqm = List<VP8QuantMatrix>(NUM_MB_SEGMENTS);
+
+ // probabilities
+ VP8Proba _proba;
+ bool _useSkipProba;
+ int _skipP;
+
+ // Boundary data cache and persistent buffers.
+ /// top intra modes values: 4 * _mbWidth
+ Uint8List _intraT;
+ /// left intra modes values
+ Uint8List _intraL = Uint8List(4);
+
+ /// uint8, segment of the currently parsed block
+ int _segment;
+ /// top y/u/v samples
+ List<VP8TopSamples> _yuvT;
+
+ /// contextual macroblock info (mb_w_ + 1)
+ List<VP8MB> _mbInfo;
+ /// filter strength info
+ List<VP8FInfo> _fInfo;
+ /// main block for Y/U/V (size = YUV_SIZE)
+ Uint8List _yuvBlock;
+
+ /// macroblock row for storing unfiltered samples
+ InputBuffer _cacheY;
+ InputBuffer _cacheU;
+ InputBuffer _cacheV;
+ int _cacheYStride;
+ int _cacheUVStride;
+
+ InputBuffer _tmpY;
+ InputBuffer _tmpU;
+ InputBuffer _tmpV;
+
+ InputBuffer _y;
+ InputBuffer _u;
+ InputBuffer _v;
+ InputBuffer _a;
+
+ /// main memory chunk for the above data. Persistent.
+ //Uint8List _mem;
+
+ // Per macroblock non-persistent infos.
+ /// current position, in macroblock units
+ int _mbX = 0;
+ int _mbY = 0;
+ /// parsed reconstruction data
+ List<VP8MBData> _mbData;
+
+ /// 0=off, 1=simple, 2=complex
+ int _filterType;
+ /// precalculated per-segment/type
+ List<List<VP8FInfo>> _fStrengths;
+
+ // Alpha
+ /// alpha-plane decoder object
+ WebPAlpha _alpha;
+ /// compressed alpha data (if present)
+ InputBuffer _alphaData;
+ /// true if alpha_data_ is decoded in alpha_plane_
+ //int _isAlphaDecoded;
+ /// output. Persistent, contains the whole data.
+ Uint8List _alphaPlane;
+
+ // extensions
+ //int _layerColorspace;
+ /// compressed layer data (if present)
+ //Uint8List _layerData;
+
+ static int _clip(int v, int M) {
+ return v < 0 ? 0 : v > M ? M : v;
+ }
+
+ static const List<int> kYModesIntra4 = const [
+ -B_DC_PRED, 1,
+ -B_TM_PRED, 2,
+ -B_VE_PRED, 3,
+ 4, 6,
+ -B_HE_PRED, 5,
+ -B_RD_PRED, -B_VR_PRED,
+ -B_LD_PRED, 7,
+ -B_VL_PRED, 8,
+ -B_HD_PRED, -B_HU_PRED ];
+
+ static const List<List<List<int>>> kBModesProba = const [
+ const [ const [ 231, 120, 48, 89, 115, 113, 120, 152, 112 ],
+ const [ 152, 179, 64, 126, 170, 118, 46, 70, 95 ],
+ const [ 175, 69, 143, 80, 85, 82, 72, 155, 103 ],
+ const [ 56, 58, 10, 171, 218, 189, 17, 13, 152 ],
+ const [ 114, 26, 17, 163, 44, 195, 21, 10, 173 ],
+ const [ 121, 24, 80, 195, 26, 62, 44, 64, 85 ],
+ const [ 144, 71, 10, 38, 171, 213, 144, 34, 26 ],
+ const [ 170, 46, 55, 19, 136, 160, 33, 206, 71 ],
+ const [ 63, 20, 8, 114, 114, 208, 12, 9, 226 ],
+ const [ 81, 40, 11, 96, 182, 84, 29, 16, 36 ] ],
+ const [ const [ 134, 183, 89, 137, 98, 101, 106, 165, 148 ],
+ const [ 72, 187, 100, 130, 157, 111, 32, 75, 80 ],
+ const [ 66, 102, 167, 99, 74, 62, 40, 234, 128 ],
+ const [ 41, 53, 9, 178, 241, 141, 26, 8, 107 ],
+ const [ 74, 43, 26, 146, 73, 166, 49, 23, 157 ],
+ const [ 65, 38, 105, 160, 51, 52, 31, 115, 128 ],
+ const [ 104, 79, 12, 27, 217, 255, 87, 17, 7 ],
+ const [ 87, 68, 71, 44, 114, 51, 15, 186, 23 ],
+ const [ 47, 41, 14, 110, 182, 183, 21, 17, 194 ],
+ const [ 66, 45, 25, 102, 197, 189, 23, 18, 22 ] ],
+ const [ const [ 88, 88, 147, 150, 42, 46, 45, 196, 205 ],
+ const [ 43, 97, 183, 117, 85, 38, 35, 179, 61 ],
+ const [ 39, 53, 200, 87, 26, 21, 43, 232, 171 ],
+ const [ 56, 34, 51, 104, 114, 102, 29, 93, 77 ],
+ const [ 39, 28, 85, 171, 58, 165, 90, 98, 64 ],
+ const [ 34, 22, 116, 206, 23, 34, 43, 166, 73 ],
+ const [ 107, 54, 32, 26, 51, 1, 81, 43, 31 ],
+ const [ 68, 25, 106, 22, 64, 171, 36, 225, 114 ],
+ const [ 34, 19, 21, 102, 132, 188, 16, 76, 124 ],
+ const [ 62, 18, 78, 95, 85, 57, 50, 48, 51 ] ],
+ const [ const [ 193, 101, 35, 159, 215, 111, 89, 46, 111 ],
+ const [ 60, 148, 31, 172, 219, 228, 21, 18, 111 ],
+ const [ 112, 113, 77, 85, 179, 255, 38, 120, 114 ],
+ const [ 40, 42, 1, 196, 245, 209, 10, 25, 109 ],
+ const [ 88, 43, 29, 140, 166, 213, 37, 43, 154 ],
+ const [ 61, 63, 30, 155, 67, 45, 68, 1, 209 ],
+ const [ 100, 80, 8, 43, 154, 1, 51, 26, 71 ],
+ const [ 142, 78, 78, 16, 255, 128, 34, 197, 171 ],
+ const [ 41, 40, 5, 102, 211, 183, 4, 1, 221 ],
+ const [ 51, 50, 17, 168, 209, 192, 23, 25, 82 ] ],
+ const [ const [ 138, 31, 36, 171, 27, 166, 38, 44, 229 ],
+ const [ 67, 87, 58, 169, 82, 115, 26, 59, 179 ],
+ const [ 63, 59, 90, 180, 59, 166, 93, 73, 154 ],
+ const [ 40, 40, 21, 116, 143, 209, 34, 39, 175 ],
+ const [ 47, 15, 16, 183, 34, 223, 49, 45, 183 ],
+ const [ 46, 17, 33, 183, 6, 98, 15, 32, 183 ],
+ const [ 57, 46, 22, 24, 128, 1, 54, 17, 37 ],
+ const [ 65, 32, 73, 115, 28, 128, 23, 128, 205 ],
+ const [ 40, 3, 9, 115, 51, 192, 18, 6, 223 ],
+ const [ 87, 37, 9, 115, 59, 77, 64, 21, 47 ] ],
+ const [ const [ 104, 55, 44, 218, 9, 54, 53, 130, 226 ],
+ const [ 64, 90, 70, 205, 40, 41, 23, 26, 57 ],
+ const [ 54, 57, 112, 184, 5, 41, 38, 166, 213 ],
+ const [ 30, 34, 26, 133, 152, 116, 10, 32, 134 ],
+ const [ 39, 19, 53, 221, 26, 114, 32, 73, 255 ],
+ const [ 31, 9, 65, 234, 2, 15, 1, 118, 73 ],
+ const [ 75, 32, 12, 51, 192, 255, 160, 43, 51 ],
+ const [ 88, 31, 35, 67, 102, 85, 55, 186, 85 ],
+ const [ 56, 21, 23, 111, 59, 205, 45, 37, 192 ],
+ const [ 55, 38, 70, 124, 73, 102, 1, 34, 98 ] ],
+ const [ const [ 125, 98, 42, 88, 104, 85, 117, 175, 82 ],
+ const [ 95, 84, 53, 89, 128, 100, 113, 101, 45 ],
+ const [ 75, 79, 123, 47, 51, 128, 81, 171, 1 ],
+ const [ 57, 17, 5, 71, 102, 57, 53, 41, 49 ],
+ const [ 38, 33, 13, 121, 57, 73, 26, 1, 85 ],
+ const [ 41, 10, 67, 138, 77, 110, 90, 47, 114 ],
+ const [ 115, 21, 2, 10, 102, 255, 166, 23, 6 ],
+ const [ 101, 29, 16, 10, 85, 128, 101, 196, 26 ],
+ const [ 57, 18, 10, 102, 102, 213, 34, 20, 43 ],
+ const [ 117, 20, 15, 36, 163, 128, 68, 1, 26 ] ],
+ const [ const [ 102, 61, 71, 37, 34, 53, 31, 243, 192 ],
+ const [ 69, 60, 71, 38, 73, 119, 28, 222, 37 ],
+ const [ 68, 45, 128, 34, 1, 47, 11, 245, 171 ],
+ const [ 62, 17, 19, 70, 146, 85, 55, 62, 70 ],
+ const [ 37, 43, 37, 154, 100, 163, 85, 160, 1 ],
+ const [ 63, 9, 92, 136, 28, 64, 32, 201, 85 ],
+ const [ 75, 15, 9, 9, 64, 255, 184, 119, 16 ],
+ const [ 86, 6, 28, 5, 64, 255, 25, 248, 1 ],
+ const [ 56, 8, 17, 132, 137, 255, 55, 116, 128 ],
+ const [ 58, 15, 20, 82, 135, 57, 26, 121, 40 ] ],
+ const [ const [ 164, 50, 31, 137, 154, 133, 25, 35, 218 ],
+ const [ 51, 103, 44, 131, 131, 123, 31, 6, 158 ],
+ const [ 86, 40, 64, 135, 148, 224, 45, 183, 128 ],
+ const [ 22, 26, 17, 131, 240, 154, 14, 1, 209 ],
+ const [ 45, 16, 21, 91, 64, 222, 7, 1, 197 ],
+ const [ 56, 21, 39, 155, 60, 138, 23, 102, 213 ],
+ const [ 83, 12, 13, 54, 192, 255, 68, 47, 28 ],
+ const [ 85, 26, 85, 85, 128, 128, 32, 146, 171 ],
+ const [ 18, 11, 7, 63, 144, 171, 4, 4, 246 ],
+ const [ 35, 27, 10, 146, 174, 171, 12, 26, 128 ] ],
+ const [ const [ 190, 80, 35, 99, 180, 80, 126, 54, 45 ],
+ const [ 85, 126, 47, 87, 176, 51, 41, 20, 32 ],
+ const [ 101, 75, 128, 139, 118, 146, 116, 128, 85 ],
+ const [ 56, 41, 15, 176, 236, 85, 37, 9, 62 ],
+ const [ 71, 30, 17, 119, 118, 255, 17, 18, 138 ],
+ const [ 101, 38, 60, 138, 55, 70, 43, 26, 142 ],
+ const [ 146, 36, 19, 30, 171, 255, 97, 27, 20 ],
+ const [ 138, 45, 61, 62, 219, 1, 81, 188, 64 ],
+ const [ 32, 41, 20, 117, 151, 142, 20, 21, 163 ],
+ const [ 112, 19, 12, 61, 195, 128, 48, 4, 24 ] ] ];
+
+ static const List COEFFS_PROBA_0 = const [
+ const [ const [ const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ]
+ ],
+ const [ const [ 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 ],
+ const [ 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 ],
+ const [ 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 ],
+ const [ 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 ],
+ const [ 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 ],
+ ],
+ const [ const [ 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 ],
+ const [ 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 ],
+ const [ 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 ],
+ ],
+ const [ const [ 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 ],
+ const [ 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 ],
+ const [ 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 ],
+ const [ 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 ],
+ const [ 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 ],
+ const [ 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 ],
+ const [ 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ]
+ ]
+ ],
+ const [ const [ const [ 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 ],
+ const [ 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 ],
+ const [ 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 ]
+ ],
+ const [ const [ 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 ],
+ const [ 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 ],
+ const [ 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 ]
+ ],
+ const [ const [ 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 ],
+ const [ 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 ],
+ const [ 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 ]
+ ],
+ const [ const [ 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 ],
+ const [ 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 ],
+ const [ 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 ],
+ const [ 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 ],
+ const [ 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 ],
+ const [ 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 ],
+ const [ 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 ],
+ const [ 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 ],
+ const [ 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 ],
+ const [ 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 ],
+ const [ 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 ]
+ ]
+ ],
+ const [ const [ const [ 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 ],
+ const [ 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 ],
+ const [ 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 ]
+ ],
+ const [ const [ 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 ],
+ const [ 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 ],
+ const [ 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 ],
+ const [ 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 ],
+ const [ 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 ],
+ const [ 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 ],
+ const [ 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 ],
+ const [ 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 ],
+ const [ 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 ],
+ const [ 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 ],
+ const [ 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 ]
+ ],
+ const [ const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 ]
+ ]
+ ],
+ const [ const [ const [ 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 ],
+ const [ 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 ],
+ const [ 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 ]
+ ],
+ const [ const [ 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 ],
+ const [ 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 ],
+ const [ 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 ]
+ ],
+ const [ const [ 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 ],
+ const [ 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 ],
+ const [ 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 ]
+ ],
+ const [ const [ 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 ],
+ const [ 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 ],
+ const [ 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 ]
+ ],
+ const [ const [ 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 ],
+ const [ 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 ],
+ const [ 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 ],
+ const [ 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 ],
+ const [ 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 ],
+ const [ 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 ],
+ const [ 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 ]
+ ],
+ const [ const [ 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ],
+ const [ 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 ]
+ ]
+ ] ];
+
+
+ static const List COEFFS_UPDATE_PROBA = const [
+ const [ const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 ],
+ const [ 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 ],
+ const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ]
+ ],
+ const [ const [ const [ 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 ],
+ const [ 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ]
+ ],
+ const [ const [ const [ 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ]
+ ],
+ const [ const [ const [ 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ],
+ const [ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+ const [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+ ] ] ];
+
+ // Paragraph 14.1
+ static const List<int> DC_TABLE = const [ // uint8
+ 4, 5, 6, 7, 8, 9, 10, 10,
+ 11, 12, 13, 14, 15, 16, 17, 17,
+ 18, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 25, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 76, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89,
+ 91, 93, 95, 96, 98, 100, 101, 102,
+ 104, 106, 108, 110, 112, 114, 116, 118,
+ 122, 124, 126, 128, 130, 132, 134, 136,
+ 138, 140, 143, 145, 148, 151, 154, 157];
+
+ static const List<int> AC_TABLE = const [ // uint16
+ 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 60,
+ 62, 64, 66, 68, 70, 72, 74, 76,
+ 78, 80, 82, 84, 86, 88, 90, 92,
+ 94, 96, 98, 100, 102, 104, 106, 108,
+ 110, 112, 114, 116, 119, 122, 125, 128,
+ 131, 134, 137, 140, 143, 146, 149, 152,
+ 155, 158, 161, 164, 167, 170, 173, 177,
+ 181, 185, 189, 193, 197, 201, 205, 209,
+ 213, 217, 221, 225, 229, 234, 239, 245,
+ 249, 254, 259, 264, 269, 274, 279, 284];
+
+ /**
+ * FILTER_EXTRA_ROWS = How many extra lines are needed on the MB boundary
+ * for caching, given a filtering level.
+ * Simple filter: up to 2 luma samples are read and 1 is written.
+ * Complex filter: up to 4 luma samples are read and 3 are written. Same for
+ * U/V, so it's 8 samples total (because of the 2x upsampling).
+ */
+ static const List<int> FILTER_EXTRA_ROWS = const [ 0, 2, 8 ];
+
+ static const int VP8_SIGNATURE = 0x2a019d;
+
+ static const int MB_FEATURE_TREE_PROBS = 3;
+ static const int NUM_MB_SEGMENTS = 4;
+ static const int NUM_REF_LF_DELTAS = 4;
+ static const int NUM_MODE_LF_DELTAS = 4; // I4x4, ZERO, *, SPLIT
+ static const int MAX_NUM_PARTITIONS = 8;
+
+ static const int B_DC_PRED = 0; // 4x4 modes
+ static const int B_TM_PRED = 1;
+ static const int B_VE_PRED = 2;
+ static const int B_HE_PRED = 3;
+ static const int B_RD_PRED = 4;
+ static const int B_VR_PRED = 5;
+ static const int B_LD_PRED = 6;
+ static const int B_VL_PRED = 7;
+ static const int B_HD_PRED = 8;
+ static const int B_HU_PRED = 9;
+ static const int NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED;
+
+ // Luma16 or UV modes
+ static const int DC_PRED = B_DC_PRED;
+ static const int V_PRED = B_VE_PRED;
+ static const int H_PRED = B_HE_PRED;
+ static const int TM_PRED = B_TM_PRED;
+ static const int B_PRED = NUM_BMODES;
+
+ // special modes
+ static const int B_DC_PRED_NOTOP = 4;
+ static const int B_DC_PRED_NOLEFT = 5;
+ static const int B_DC_PRED_NOTOPLEFT = 6;
+ static const int NUM_B_DC_MODES = 7;
+
+ // Probabilities
+ static const int NUM_TYPES = 4;
+ static const int NUM_BANDS = 8;
+ static const int NUM_CTX = 3;
+ static const int NUM_PROBAS = 11;
+
+ static const int BPS = 32; // this is the common stride used by yuv[]
+ static const int YUV_SIZE = (BPS * 17 + BPS * 9);
+ static const int Y_SIZE = (BPS * 17);
+ static const int Y_OFF = (BPS * 1 + 8);
+ static const int U_OFF = (Y_OFF + BPS * 16 + BPS);
+ static const int V_OFF = (U_OFF + 16);
+
+ static const int YUV_FIX = 16; // fixed-point precision for RGB->YUV
+ static const int YUV_HALF = 1 << (YUV_FIX - 1);
+ static const int YUV_MASK = (256 << YUV_FIX) - 1;
+ static const int YUV_RANGE_MIN = -227; // min value of r/g/b output
+ static const int YUV_RANGE_MAX = 256 + 226; // max value of r/g/b output
+ static const int YUV_FIX2 = 14; // fixed-point precision for YUV->RGB
+ static const int YUV_HALF2 = 1 << (YUV_FIX2 - 1);
+ static const int YUV_MASK2 = (256 << YUV_FIX2) - 1;
+ static const int XOR_YUV_MASK2 = (-YUV_MASK2 - 1);
+
+ // These constants are 14b fixed-point version of ITU-R BT.601 constants.
+ static const int kYScale = 19077; // 1.164 = 255 / 219
+ static const int kVToR = 26149; // 1.596 = 255 / 112 * 0.701
+ static const int kUToG = 6419; // 0.391 = 255 / 112 * 0.886 * 0.114 / 0.587
+ static const int kVToG = 13320; // 0.813 = 255 / 112 * 0.701 * 0.299 / 0.587
+ static const int kUToB = 33050; // 2.018 = 255 / 112 * 0.886
+ static const int kRCst = (-kYScale * 16 - kVToR * 128 + YUV_HALF2);
+ static const int kGCst = (-kYScale * 16 + kUToG * 128 + kVToG * 128 + YUV_HALF2);
+ static const int kBCst = (-kYScale * 16 - kUToB * 128 + YUV_HALF2);
+}
diff --git a/image/lib/src/formats/webp/vp8_bit_reader.dart b/image/lib/src/formats/webp/vp8_bit_reader.dart
old mode 100644
new mode 100755
index dcd962b..70bc97b
--- a/image/lib/src/formats/webp/vp8_bit_reader.dart
+++ b/image/lib/src/formats/webp/vp8_bit_reader.dart
@@ -1,132 +1,132 @@
-import '../../util/input_buffer.dart';
-
-class VP8BitReader {
- InputBuffer input;
-
- // boolean decoder
- int _range; // current range minus 1. In [127, 254] interval.
- int _value; // current value
- int _bits; // number of valid bits left
- bool _eof = false;
-
- VP8BitReader(this.input) {
- _range = 255 - 1;
- _value = 0;
- _bits = -8; // to load the very first 8bits
- }
-
- int getValue(int bits) {
- int v = 0;
- while (bits-- > 0) {
- v |= getBit(0x80) << bits;
- }
- return v;
- }
-
- int getSigned(int v) {
- final int split = (_range >> 1);
- final int bit = _bitUpdate(split);
- _shift();
- return bit != 0 ? -v : v;
- }
-
- int getSignedValue(int bits) {
- final int value = getValue(bits);
- return get() == 1 ? -value : value;
- }
-
- int get() {
- return getValue(1);
- }
-
- int getBit(int prob) {
- final int split = (_range * prob) >> 8;
- final int bit = _bitUpdate(split);
- if (_range <= 0x7e) {
- _shift();
- }
- return bit;
- }
-
- int _bitUpdate(int split) {
- // Make sure we have a least BITS bits in 'value_'
- if (_bits < 0) {
- _loadNewBytes();
- }
-
- final int pos = _bits;
- final int value = (_value >> pos);
- if (value > split) {
- _range -= split + 1;
- _value -= (split + 1) << pos;
- return 1;
- } else {
- _range = split;
- return 0;
- }
- }
-
- void _shift() {
- final int shift = LOG_2_RANGE[_range];
- _range = NEW_RANGE[_range];
- _bits -= shift;
- }
-
- void _loadNewBytes() {
- // Read 'BITS' bits at a time if possible.
- if (input.length >= 1) {
- // convert memory type to register type (with some zero'ing!)
- int bits = input.readByte();
- _value = bits | (_value << BITS);
- _bits += (BITS);
- } else {
- _loadFinalBytes(); // no need to be inlined
- }
- }
-
- void _loadFinalBytes() {
- // Only read 8bits at a time
- if (!input.isEOS) {
- _value = input.readByte() | (_value << 8);
- _bits += 8;
- } else if (!_eof) {
- // These are not strictly needed, but it makes the behaviour
- // consistent for both USE_RIGHT_JUSTIFY and !USE_RIGHT_JUSTIFY.
- _value <<= 8;
- _bits += 8;
- _eof = true;
- }
- }
-
- static const int BITS = 8;
-
- // Read a bit with proba 'prob'. Speed-critical function!
- static const List<int> LOG_2_RANGE = const [
- 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0];
-
- static const List<int> NEW_RANGE = const [
- 127, 127, 191, 127, 159, 191, 223, 127,
- 143, 159, 175, 191, 207, 223, 239, 127,
- 135, 143, 151, 159, 167, 175, 183, 191,
- 199, 207, 215, 223, 231, 239, 247, 127,
- 131, 135, 139, 143, 147, 151, 155, 159,
- 163, 167, 171, 175, 179, 183, 187, 191,
- 195, 199, 203, 207, 211, 215, 219, 223,
- 227, 231, 235, 239, 243, 247, 251, 127,
- 129, 131, 133, 135, 137, 139, 141, 143,
- 145, 147, 149, 151, 153, 155, 157, 159,
- 161, 163, 165, 167, 169, 171, 173, 175,
- 177, 179, 181, 183, 185, 187, 189, 191,
- 193, 195, 197, 199, 201, 203, 205, 207,
- 209, 211, 213, 215, 217, 219, 221, 223,
- 225, 227, 229, 231, 233, 235, 237, 239,
- 241, 243, 245, 247, 249, 251, 253, 127];
-}
+import '../../util/input_buffer.dart';
+
+class VP8BitReader {
+ InputBuffer input;
+
+ // boolean decoder
+ int _range; // current range minus 1. In [127, 254] interval.
+ int _value; // current value
+ int _bits; // number of valid bits left
+ bool _eof = false;
+
+ VP8BitReader(this.input) {
+ _range = 255 - 1;
+ _value = 0;
+ _bits = -8; // to load the very first 8bits
+ }
+
+ int getValue(int bits) {
+ int v = 0;
+ while (bits-- > 0) {
+ v |= getBit(0x80) << bits;
+ }
+ return v;
+ }
+
+ int getSigned(int v) {
+ final int split = (_range >> 1);
+ final int bit = _bitUpdate(split);
+ _shift();
+ return bit != 0 ? -v : v;
+ }
+
+ int getSignedValue(int bits) {
+ final int value = getValue(bits);
+ return get() == 1 ? -value : value;
+ }
+
+ int get() {
+ return getValue(1);
+ }
+
+ int getBit(int prob) {
+ final int split = (_range * prob) >> 8;
+ final int bit = _bitUpdate(split);
+ if (_range <= 0x7e) {
+ _shift();
+ }
+ return bit;
+ }
+
+ int _bitUpdate(int split) {
+ // Make sure we have a least BITS bits in 'value_'
+ if (_bits < 0) {
+ _loadNewBytes();
+ }
+
+ final int pos = _bits;
+ final int value = (_value >> pos);
+ if (value > split) {
+ _range -= split + 1;
+ _value -= (split + 1) << pos;
+ return 1;
+ } else {
+ _range = split;
+ return 0;
+ }
+ }
+
+ void _shift() {
+ final int shift = LOG_2_RANGE[_range];
+ _range = NEW_RANGE[_range];
+ _bits -= shift;
+ }
+
+ void _loadNewBytes() {
+ // Read 'BITS' bits at a time if possible.
+ if (input.length >= 1) {
+ // convert memory type to register type (with some zero'ing!)
+ int bits = input.readByte();
+ _value = bits | (_value << BITS);
+ _bits += (BITS);
+ } else {
+ _loadFinalBytes(); // no need to be inlined
+ }
+ }
+
+ void _loadFinalBytes() {
+ // Only read 8bits at a time
+ if (!input.isEOS) {
+ _value = input.readByte() | (_value << 8);
+ _bits += 8;
+ } else if (!_eof) {
+ // These are not strictly needed, but it makes the behaviour
+ // consistent for both USE_RIGHT_JUSTIFY and !USE_RIGHT_JUSTIFY.
+ _value <<= 8;
+ _bits += 8;
+ _eof = true;
+ }
+ }
+
+ static const int BITS = 8;
+
+ // Read a bit with proba 'prob'. Speed-critical function!
+ static const List<int> LOG_2_RANGE = const [
+ 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0];
+
+ static const List<int> NEW_RANGE = const [
+ 127, 127, 191, 127, 159, 191, 223, 127,
+ 143, 159, 175, 191, 207, 223, 239, 127,
+ 135, 143, 151, 159, 167, 175, 183, 191,
+ 199, 207, 215, 223, 231, 239, 247, 127,
+ 131, 135, 139, 143, 147, 151, 155, 159,
+ 163, 167, 171, 175, 179, 183, 187, 191,
+ 195, 199, 203, 207, 211, 215, 219, 223,
+ 227, 231, 235, 239, 243, 247, 251, 127,
+ 129, 131, 133, 135, 137, 139, 141, 143,
+ 145, 147, 149, 151, 153, 155, 157, 159,
+ 161, 163, 165, 167, 169, 171, 173, 175,
+ 177, 179, 181, 183, 185, 187, 189, 191,
+ 193, 195, 197, 199, 201, 203, 205, 207,
+ 209, 211, 213, 215, 217, 219, 221, 223,
+ 225, 227, 229, 231, 233, 235, 237, 239,
+ 241, 243, 245, 247, 249, 251, 253, 127];
+}
diff --git a/image/lib/src/formats/webp/vp8_filter.dart b/image/lib/src/formats/webp/vp8_filter.dart
old mode 100644
new mode 100755
index 4a11a80..2ab6f3a
--- a/image/lib/src/formats/webp/vp8_filter.dart
+++ b/image/lib/src/formats/webp/vp8_filter.dart
@@ -1,720 +1,720 @@
-import 'dart:typed_data';
-
-import '../../internal/bit_operators.dart';
-import '../../util/input_buffer.dart';
-import 'vp8.dart';
-
-class VP8Filter {
- VP8Filter() {
- _initTables();
- }
-
- void simpleVFilter16(InputBuffer p, int stride, int thresh) {
- InputBuffer p2 = new InputBuffer.from(p);
- for (int i = 0; i < 16; ++i) {
- p2.offset = p.offset + i;
- if (_needsFilter(p2, stride, thresh)) {
- _doFilter2(p2, stride);
- }
- }
- }
-
- void simpleHFilter16(InputBuffer p, int stride, int thresh) {
- InputBuffer p2 = new InputBuffer.from(p);
- for (int i = 0; i < 16; ++i) {
- p2.offset = p.offset + i * stride;
- if (_needsFilter(p2, 1, thresh)) {
- _doFilter2(p2, 1);
- }
- }
- }
-
- void simpleVFilter16i(InputBuffer p, int stride, int thresh) {
- InputBuffer p2 = new InputBuffer.from(p);
- for (int k = 3; k > 0; --k) {
- p2.offset += 4 * stride;
- simpleVFilter16(p2, stride, thresh);
- }
- }
-
- void simpleHFilter16i(InputBuffer p, int stride, int thresh) {
- InputBuffer p2 = new InputBuffer.from(p);
- for (int k = 3; k > 0; --k) {
- p2.offset += 4;
- simpleHFilter16(p2, stride, thresh);
- }
- }
-
- // on macroblock edges
- void vFilter16(InputBuffer p, int stride, int thresh, int ithresh,
- int hev_thresh) {
- _filterLoop26(p, stride, 1, 16, thresh, ithresh, hev_thresh);
- }
-
- void hFilter16(InputBuffer p, int stride, int thresh, int ithresh,
- int hev_thresh) {
- _filterLoop26(p, 1, stride, 16, thresh, ithresh, hev_thresh);
- }
-
- // on three inner edges
- void vFilter16i(InputBuffer p, int stride, int thresh, int ithresh,
- int hev_thresh) {
- InputBuffer p2 = new InputBuffer.from(p);
- for (int k = 3; k > 0; --k) {
- p2.offset += 4 * stride;
- _filterLoop24(p2, stride, 1, 16, thresh, ithresh, hev_thresh);
- }
- }
-
- void hFilter16i(InputBuffer p, int stride, int thresh, int ithresh,
- int hev_thresh) {
- InputBuffer p2 = new InputBuffer.from(p);
- for (int k = 3; k > 0; --k) {
- p2.offset += 4;
- _filterLoop24(p2, 1, stride, 16, thresh, ithresh, hev_thresh);
- }
- }
-
- /**
- * 8-pixels wide variant, for chroma filtering
- */
- void vFilter8(InputBuffer u, InputBuffer v, int stride, int thresh, int ithresh,
- int hev_thresh) {
- _filterLoop26(u, stride, 1, 8, thresh, ithresh, hev_thresh);
- _filterLoop26(v, stride, 1, 8, thresh, ithresh, hev_thresh);
- }
-
- void hFilter8(InputBuffer u, InputBuffer v, int stride, int thresh, int ithresh,
- int hev_thresh) {
- _filterLoop26(u, 1, stride, 8, thresh, ithresh, hev_thresh);
- _filterLoop26(v, 1, stride, 8, thresh, ithresh, hev_thresh);
- }
-
- void vFilter8i(InputBuffer u, InputBuffer v, int stride, int thresh, int ithresh,
- int hev_thresh) {
- InputBuffer u2 = new InputBuffer.from(u, offset: 4 * stride);
- InputBuffer v2 = new InputBuffer.from(v, offset: 4 * stride);
- _filterLoop24(u2, stride, 1, 8, thresh, ithresh, hev_thresh);
- _filterLoop24(v2, stride, 1, 8, thresh, ithresh, hev_thresh);
- }
-
- void hFilter8i(InputBuffer u, InputBuffer v, int stride, int thresh, int ithresh,
- int hev_thresh) {
- InputBuffer u2 = new InputBuffer.from(u, offset: 4);
- InputBuffer v2 = new InputBuffer.from(v, offset: 4);
- _filterLoop24(u2, 1, stride, 8, thresh, ithresh, hev_thresh);
- _filterLoop24(v2, 1, stride, 8, thresh, ithresh, hev_thresh);
- }
-
- void _filterLoop26(InputBuffer p, int hstride, int vstride, int size,
- int thresh, int ithresh, int hev_thresh) {
- InputBuffer p2 = new InputBuffer.from(p);
- while (size-- > 0) {
- if (_needsFilter2(p2, hstride, thresh, ithresh)) {
- if (_hev(p2, hstride, hev_thresh)) {
- _doFilter2(p2, hstride);
- } else {
- _doFilter6(p2, hstride);
- }
- }
- p2.offset += vstride;
- }
- }
-
- void _filterLoop24(InputBuffer p, int hstride, int vstride, int size,
- int thresh, int ithresh, int hev_thresh) {
- InputBuffer p2 = new InputBuffer.from(p);
- while (size-- > 0) {
- if (_needsFilter2(p2, hstride, thresh, ithresh)) {
- if (_hev(p2, hstride, hev_thresh)) {
- _doFilter2(p2, hstride);
- } else {
- _doFilter4(p2, hstride);
- }
- }
- p2.offset += vstride;
- }
- }
-
- /**
- * 4 pixels in, 2 pixels out
- */
- void _doFilter2(InputBuffer p, int step) {
- final int p1 = p[-2 * step];
- final int p0 = p[-step];
- final int q0 = p[0];
- final int q1 = p[step];
- final int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
- final int a1 = sclip2[112 + shiftR((a + 4), 3)];
- final int a2 = sclip2[112 + shiftR((a + 3), 3)];
- p[-step] = clip1[255 + p0 + a2];
- p[0] = clip1[255 + q0 - a1];
- }
-
- /**
- * 4 pixels in, 4 pixels out
- */
- void _doFilter4(InputBuffer p, int step) {
- final int p1 = p[-2 * step];
- final int p0 = p[-step];
- final int q0 = p[0];
- final int q1 = p[step];
- final int a = 3 * (q0 - p0);
- final int a1 = sclip2[112 + shiftR((a + 4), 3)];
- final int a2 = sclip2[112 + shiftR((a + 3), 3)];
- final int a3 = shiftR(a1 + 1, 1);
- p[-2 * step] = clip1[255 + p1 + a3];
- p[-step] = clip1[255 + p0 + a2];
- p[0] = clip1[255 + q0 - a1];
- p[step] = clip1[255 + q1 - a3];
- }
-
- /**
- * 6 pixels in, 6 pixels out
- */
- void _doFilter6(InputBuffer p, int step) {
- final int p2 = p[-3 * step];
- final int p1 = p[-2 * step];
- final int p0 = p[-step];
- final int q0 = p[0];
- final int q1 = p[step];
- final int q2 = p[2 * step];
- final int a = sclip1[1020 + 3 * (q0 - p0) + sclip1[1020 + p1 - q1]];
- final int a1 = shiftR(27 * a + 63, 7); // eq. to ((3 * a + 7) * 9) >> 7
- final int a2 = shiftR(18 * a + 63, 7); // eq. to ((2 * a + 7) * 9) >> 7
- final int a3 = shiftR(9 * a + 63, 7); // eq. to ((1 * a + 7) * 9) >> 7
- p[-3 * step] = clip1[255 + p2 + a3];
- p[-2 * step] = clip1[255 + p1 + a2];
- p[-step] = clip1[255 + p0 + a1];
- p[0] = clip1[255 + q0 - a1];
- p[step] = clip1[255 + q1 - a2];
- p[2 * step] = clip1[255 + q2 - a3];
- }
-
- bool _hev(InputBuffer p, int step, int thresh) {
- final int p1 = p[-2 * step];
- final int p0 = p[-step];
- final int q0 = p[0];
- final int q1 = p[step];
- return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
- }
-
- bool _needsFilter(InputBuffer p, int step, int thresh) {
- final int p1 = p[-2 * step];
- final int p0 = p[-step];
- final int q0 = p[0];
- final int q1 = p[step];
- return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
- }
-
- bool _needsFilter2(InputBuffer p, int step, int t, int it) {
- final int p3 = p[-4 * step];
- final int p2 = p[-3 * step];
- final int p1 = p[-2 * step];
- final int p0 = p[-step];
- final int q0 = p[0];
- final int q1 = p[step];
- final int q2 = p[2 * step];
- final int q3 = p[3 * step];
- if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t) {
- return false;
- }
-
- return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it &&
- abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it &&
- abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it;
- }
-
- void transformOne(InputBuffer src, InputBuffer dst) {
- Int32List C = new Int32List(4 * 4);
- int si = 0;
- int di = 0;
- int tmp = 0;
- for (int i = 0; i < 4; ++i) { // vertical pass
- final int a = src[si] + src[si + 8]; // [-4096, 4094]
- final int b = src[si] - src[si + 8]; // [-4095, 4095]
- final int c = _mul(src[si + 4], kC2) - _mul(src[si + 12], kC1); // [-3783, 3783]
- final int d = _mul(src[si + 4], kC1) + _mul(src[si + 12], kC2); // [-3785, 3781]
- C[tmp++] = a + d; // [-7881, 7875]
- C[tmp++] = b + c; // [-7878, 7878]
- C[tmp++] = b - c; // [-7878, 7878]
- C[tmp++] = a - d; // [-7877, 7879]
- si++;
- }
-
- // Each pass is expanding the dynamic range by ~3.85 (upper bound).
- // The exact value is (2. + (kC1 + kC2) / 65536).
- // After the second pass, maximum interval is [-3794, 3794], assuming
- // an input in [-2048, 2047] interval. We then need to add a dst value
- // in the [0, 255] range.
- // In the worst case scenario, the input to clip_8b() can be as large as
- // [-60713, 60968].
- tmp = 0;
- for (int i = 0; i < 4; ++i) { // horizontal pass
- final int dc = C[tmp] + 4;
- final int a = dc + C[tmp + 8];
- final int b = dc - C[tmp + 8];
- final int c = _mul(C[tmp + 4], kC2) - _mul(C[tmp + 12], kC1);
- final int d = _mul(C[tmp + 4], kC1) + _mul(C[tmp + 12], kC2);
- _store(dst, di, 0, 0, a + d);
- _store(dst, di, 1, 0, b + c);
- _store(dst, di, 2, 0, b - c);
- _store(dst, di, 3, 0, a - d);
- tmp++;
- di += VP8.BPS;
- }
- }
-
-
- void transform(InputBuffer src, InputBuffer dst, bool doTwo) {
- transformOne(src, dst);
- if (doTwo) {
- transformOne(new InputBuffer.from(src, offset: 16),
- new InputBuffer.from(dst, offset: 4));
- }
- }
-
- void transformUV(InputBuffer src, InputBuffer dst) {
- transform(src, dst, true);
- transform(new InputBuffer.from(src, offset: 2 * 16),
- new InputBuffer.from(dst, offset: 4 * VP8.BPS), true);
- }
-
- void transformDC(InputBuffer src, InputBuffer dst) {
- final int DC = src[0] + 4;
- for (int j = 0; j < 4; ++j) {
- for (int i = 0; i < 4; ++i) {
- _store(dst, 0, i, j, DC);
- }
- }
- }
-
- void transformDCUV(InputBuffer src, InputBuffer dst) {
- if (src[0 * 16] != 0) {
- transformDC(src, dst);
- }
- if (src[1 * 16] != 0) {
- transformDC(new InputBuffer.from(src, offset: 1 * 16),
- new InputBuffer.from(dst, offset: 4));
- }
- if (src[2 * 16] != 0) {
- transformDC(new InputBuffer.from(src, offset: 2 * 16),
- new InputBuffer.from(dst, offset: 4 * VP8.BPS));
- }
- if (src[3 * 16] != 0) {
- transformDC(new InputBuffer.from(src, offset: 3 * 16),
- new InputBuffer.from(dst, offset: 4 * VP8.BPS + 4));
- }
- }
-
- /**
- * Simplified transform when only in[0], in[1] and in[4] are non-zero
- */
- void transformAC3(InputBuffer src, InputBuffer dst) {
- final int a = src[0] + 4;
- final int c4 = _mul(src[4], kC2);
- final int d4 = _mul(src[4], kC1);
- final int c1 = _mul(src[1], kC2);
- final int d1 = _mul(src[1], kC1);
- _store2(dst, 0, a + d4, d1, c1);
- _store2(dst, 1, a + c4, d1, c1);
- _store2(dst, 2, a - c4, d1, c1);
- _store2(dst, 3, a - d4, d1, c1);
- }
-
- static int AVG3(a, b, c) => shiftR(((a) + 2 * (b) + (c) + 2), 2);
- static int AVG2(a, b) => shiftR(((a) + (b) + 1), 1);
-
- static void VE4(InputBuffer dst) {
- int top = -VP8.BPS; // dst +
- final List<int> vals = [
- AVG3(dst[top - 1], dst[top], dst[top + 1]),
- AVG3(dst[top], dst[top + 1], dst[top + 2]),
- AVG3(dst[top + 1], dst[top + 2], dst[top + 3]),
- AVG3(dst[top + 2], dst[top + 3], dst[top + 4])];
-
- for (int i = 0; i < 4; ++i) {
- dst.memcpy(i * VP8.BPS, 4, vals);
- }
- }
-
- static void HE4(InputBuffer dst) {
- final int A = dst[-1 - VP8.BPS];
- final int B = dst[-1];
- final int C = dst[-1 + VP8.BPS];
- final int D = dst[-1 + 2 * VP8.BPS];
- final int E = dst[-1 + 3 * VP8.BPS];
-
- InputBuffer d2 = new InputBuffer.from(dst);
-
- d2.toUint32List()[0] = 0x01010101 * AVG3(A, B, C);
- d2.offset += VP8.BPS;
- d2.toUint32List()[0] = 0x01010101 * AVG3(B, C, D);
- d2.offset += VP8.BPS;
- d2.toUint32List()[0] = 0x01010101 * AVG3(C, D, E);
- d2.offset += VP8.BPS;
- d2.toUint32List()[0] = 0x01010101 * AVG3(D, E, E);
- }
-
- static void DC4(InputBuffer dst) { // DC
- int dc = 4;
- for (int i = 0; i < 4; ++i) {
- dc += dst[i - VP8.BPS] + dst[-1 + i * VP8.BPS];
- }
- dc >>= 3;
- for (int i = 0; i < 4; ++i) {
- dst.memset(i * VP8.BPS, 4, dc);
- }
- }
-
- static void trueMotion(InputBuffer dst, int size) {
- int di = 0;
- int top = -VP8.BPS; // dst +
- int clip0 = 255 - dst[top - 1]; // clip1 +
-
- for (int y = 0; y < size; ++y) {
- int clip = clip0 + dst[di - 1];
- for (int x = 0; x < size; ++x) {
- dst[di + x] = clip1[clip + dst[top + x]];
- }
-
- di += VP8.BPS;
- }
- }
-
- static void TM4(InputBuffer dst) {
- trueMotion(dst, 4);
- }
-
- static void TM8uv(InputBuffer dst) {
- trueMotion(dst, 8);
- }
-
- static void TM16(InputBuffer dst) {
- trueMotion(dst, 16);
- }
-
- static int DST(x, y) => x + y * VP8.BPS;
-
- /**
- * Down-right
- */
- static void RD4(InputBuffer dst) {
- final int I = dst[-1 + 0 * VP8.BPS];
- final int J = dst[-1 + 1 * VP8.BPS];
- final int K = dst[-1 + 2 * VP8.BPS];
- final int L = dst[-1 + 3 * VP8.BPS];
- final int X = dst[-1 - VP8.BPS];
- final int A = dst[0 - VP8.BPS];
- final int B = dst[1 - VP8.BPS];
- final int C = dst[2 - VP8.BPS];
- final int D = dst[3 - VP8.BPS];
-
- dst[DST(0, 3)] = AVG3(J, K, L);
- dst[DST(0, 2)] = dst[DST(1, 3)] = AVG3(I, J, K);
- dst[DST(0, 1)] = dst[DST(1, 2)] = dst[DST(2, 3)] = AVG3(X, I, J);
- dst[DST(0, 0)] = dst[DST(1, 1)] = dst[DST(2, 2)] = dst[DST(3, 3)] = AVG3(A, X, I);
- dst[DST(1, 0)] = dst[DST(2, 1)] = dst[DST(3, 2)] = AVG3(B, A, X);
- dst[DST(2, 0)] = dst[DST(3, 1)] = AVG3(C, B, A);
- dst[DST(3, 0)] = AVG3(D, C, B);
- }
-
- /**
- * Down-Left
- */
- static void LD4(InputBuffer dst) {
- final int A = dst[0 - VP8.BPS];
- final int B = dst[1 - VP8.BPS];
- final int C = dst[2 - VP8.BPS];
- final int D = dst[3 - VP8.BPS];
- final int E = dst[4 - VP8.BPS];
- final int F = dst[5 - VP8.BPS];
- final int G = dst[6 - VP8.BPS];
- final int H = dst[7 - VP8.BPS];
- dst[DST(0, 0)] = AVG3(A, B, C);
- dst[DST(1, 0)] = dst[DST(0, 1)] = AVG3(B, C, D);
- dst[DST(2, 0)] = dst[DST(1, 1)] = dst[DST(0, 2)] = AVG3(C, D, E);
- dst[DST(3, 0)] = dst[DST(2, 1)] = dst[DST(1, 2)] = dst[DST(0, 3)] = AVG3(D, E, F);
- dst[DST(3, 1)] = dst[DST(2, 2)] = dst[DST(1, 3)] = AVG3(E, F, G);
- dst[DST(3, 2)] = dst[DST(2, 3)] = AVG3(F, G, H);
- dst[DST(3, 3)] = AVG3(G, H, H);
- }
-
- /**
- * Vertical-Right
- */
- static void VR4(InputBuffer dst) {
- final int I = dst[-1 + 0 * VP8.BPS];
- final int J = dst[-1 + 1 * VP8.BPS];
- final int K = dst[-1 + 2 * VP8.BPS];
- final int X = dst[-1 - VP8.BPS];
- final int A = dst[0 - VP8.BPS];
- final int B = dst[1 - VP8.BPS];
- final int C = dst[2 - VP8.BPS];
- final int D = dst[3 - VP8.BPS];
- dst[DST(0, 0)] = dst[DST(1, 2)] = AVG2(X, A);
- dst[DST(1, 0)] = dst[DST(2, 2)] = AVG2(A, B);
- dst[DST(2, 0)] = dst[DST(3, 2)] = AVG2(B, C);
- dst[DST(3, 0)] = AVG2(C, D);
-
- dst[DST(0, 3)] = AVG3(K, J, I);
- dst[DST(0, 2)] = AVG3(J, I, X);
- dst[DST(0, 1)] = dst[DST(1, 3)] = AVG3(I, X, A);
- dst[DST(1, 1)] = dst[DST(2, 3)] = AVG3(X, A, B);
- dst[DST(2, 1)] = dst[DST(3, 3)] = AVG3(A, B, C);
- dst[DST(3, 1)] = AVG3(B, C, D);
- }
-
- /**
- * Vertical-Left
- */
- static void VL4(InputBuffer dst) {
- final int A = dst[0 - VP8.BPS];
- final int B = dst[1 - VP8.BPS];
- final int C = dst[2 - VP8.BPS];
- final int D = dst[3 - VP8.BPS];
- final int E = dst[4 - VP8.BPS];
- final int F = dst[5 - VP8.BPS];
- final int G = dst[6 - VP8.BPS];
- final int H = dst[7 - VP8.BPS];
- dst[DST(0, 0)] = AVG2(A, B);
- dst[DST(1, 0)] = dst[DST(0, 2)] = AVG2(B, C);
- dst[DST(2, 0)] = dst[DST(1, 2)] = AVG2(C, D);
- dst[DST(3, 0)] = dst[DST(2, 2)] = AVG2(D, E);
-
- dst[DST(0, 1)] = AVG3(A, B, C);
- dst[DST(1, 1)] = dst[DST(0, 3)] = AVG3(B, C, D);
- dst[DST(2, 1)] = dst[DST(1, 3)] = AVG3(C, D, E);
- dst[DST(3, 1)] = dst[DST(2, 3)] = AVG3(D, E, F);
- dst[DST(3, 2)] = AVG3(E, F, G);
- dst[DST(3, 3)] = AVG3(F, G, H);
- }
-
- /**
- * Horizontal-Up
- */
- static void HU4(InputBuffer dst) {
- final int I = dst[-1 + 0 * VP8.BPS];
- final int J = dst[-1 + 1 * VP8.BPS];
- final int K = dst[-1 + 2 * VP8.BPS];
- final int L = dst[-1 + 3 * VP8.BPS];
- dst[DST(0, 0)] = AVG2(I, J);
- dst[DST(2, 0)] = dst[DST(0, 1)] = AVG2(J, K);
- dst[DST(2, 1)] = dst[DST(0, 2)] = AVG2(K, L);
- dst[DST(1, 0)] = AVG3(I, J, K);
- dst[DST(3, 0)] = dst[DST(1, 1)] = AVG3(J, K, L);
- dst[DST(3, 1)] = dst[DST(1, 2)] = AVG3(K, L, L);
- dst[DST(3, 2)] = dst[DST(2, 2)] = dst[DST(0, 3)] = dst[DST(1, 3)] =
- dst[DST(2, 3)] = dst[DST(3, 3)] = L;
- }
-
- /**
- * Horizontal-Down
- */
- static void HD4(InputBuffer dst) {
- final int I = dst[-1 + 0 * VP8.BPS];
- final int J = dst[-1 + 1 * VP8.BPS];
- final int K = dst[-1 + 2 * VP8.BPS];
- final int L = dst[-1 + 3 * VP8.BPS];
- final int X = dst[-1 - VP8.BPS];
- final int A = dst[0 - VP8.BPS];
- final int B = dst[1 - VP8.BPS];
- final int C = dst[2 - VP8.BPS];
-
- dst[DST(0, 0)] = dst[DST(2, 1)] = AVG2(I, X);
- dst[DST(0, 1)] = dst[DST(2, 2)] = AVG2(J, I);
- dst[DST(0, 2)] = dst[DST(2, 3)] = AVG2(K, J);
- dst[DST(0, 3)] = AVG2(L, K);
-
- dst[DST(3, 0)] = AVG3(A, B, C);
- dst[DST(2, 0)] = AVG3(X, A, B);
- dst[DST(1, 0)] = dst[DST(3, 1)] = AVG3(I, X, A);
- dst[DST(1, 1)] = dst[DST(3, 2)] = AVG3(J, I, X);
- dst[DST(1, 2)] = dst[DST(3, 3)] = AVG3(K, J, I);
- dst[DST(1, 3)] = AVG3(L, K, J);
- }
-
- static void VE16(InputBuffer dst) { // vertical
- for (int j = 0; j < 16; ++j) {
- dst.memcpy(j * VP8.BPS, 16, dst, -VP8.BPS);
- }
- }
-
- static void HE16(InputBuffer dst) { // horizontal
- int di = 0;
- for (int j = 16; j > 0; --j) {
- dst.memset(di, 16, dst[di - 1]);
- di += VP8.BPS;
- }
- }
-
- static void Put16(int v, InputBuffer dst) {
- for (int j = 0; j < 16; ++j) {
- dst.memset(j * VP8.BPS, 16, v);
- }
- }
-
- static void DC16(InputBuffer dst) { // DC
- int DC = 16;
- for (int j = 0; j < 16; ++j) {
- DC += dst[-1 + j * VP8.BPS] + dst[j - VP8.BPS];
- }
- Put16(DC >> 5, dst);
- }
-
- /**
- * DC with top samples not available
- */
- static void DC16NoTop(InputBuffer dst) {
- int DC = 8;
- for (int j = 0; j < 16; ++j) {
- DC += dst[-1 + j * VP8.BPS];
- }
- Put16(DC >> 4, dst);
- }
-
- /**
- * DC with left samples not available
- */
- static void DC16NoLeft(InputBuffer dst) {
- int DC = 8;
- for (int i = 0; i < 16; ++i) {
- DC += dst[i - VP8.BPS];
- }
- Put16(DC >> 4, dst);
- }
-
- /**
- * DC with no top and left samples
- */
- static void DC16NoTopLeft(InputBuffer dst) {
- Put16(0x80, dst);
- }
-
- static void VE8uv(InputBuffer dst) {
- for (int j = 0; j < 8; ++j) {
- dst.memcpy(j * VP8.BPS, 8, dst, -VP8.BPS);
- }
- }
-
- static void HE8uv(InputBuffer dst) {
- int di = 0;
- for (int j = 0; j < 8; ++j) {
- dst.memset(di, 8, dst[di - 1]);
- di += VP8.BPS;
- }
- }
-
- /**
- * helper for chroma-DC predictions
- */
- static void Put8x8uv(int value, InputBuffer dst) {
- for (int j = 0; j < 8; ++j) {
- dst.memset(j * VP8.BPS, 8, value);
- }
- }
-
- static void DC8uv(InputBuffer dst) {
- int dc0 = 8;
- for (int i = 0; i < 8; ++i) {
- dc0 += dst[i - VP8.BPS] + dst[-1 + i * VP8.BPS];
- }
- Put8x8uv(dc0 >> 4, dst);
- }
-
- /**
- * DC with no left samples
- */
- static void DC8uvNoLeft(InputBuffer dst) {
- int dc0 = 4;
- for (int i = 0; i < 8; ++i) {
- dc0 += dst[i - VP8.BPS];
- }
- Put8x8uv(dc0 >> 3, dst);
- }
-
- /**
- * DC with no top samples
- */
- static void DC8uvNoTop(InputBuffer dst) {
- int dc0 = 4;
- for (int i = 0; i < 8; ++i) {
- dc0 += dst[-1 + i * VP8.BPS];
- }
- Put8x8uv(dc0 >> 3, dst);
- }
-
- /**
- * DC with nothing
- */
- static void DC8uvNoTopLeft(InputBuffer dst) {
- Put8x8uv(0x80, dst);
- }
-
- static const List PredLuma4 = const [
- DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4 ];
-
- static const List PredLuma16 = const [
- DC16, TM16, VE16, HE16, DC16NoTop, DC16NoLeft, DC16NoTopLeft ];
-
- static const List PredChroma8 = const [
- DC8uv, TM8uv, VE8uv, HE8uv, DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft ];
-
-
- static const int kC1 = 20091 + (1 << 16);
- static const int kC2 = 35468;
-
- static int _mul(int a, int b) {
- int c = a * b;
- return shiftR(c, 16);
- }
-
- static void _store(InputBuffer dst, int di, int x, int y, int v) {
- dst[di + x + y * VP8.BPS] = _clip8b(dst[di + x + y * VP8.BPS] + (v >> 3));
- }
-
- static void _store2(InputBuffer dst, int y, int dc, int d, int c) {
- _store(dst, 0, 0, y, dc + d);
- _store(dst, 0, 1, y, dc + c);
- _store(dst, 0, 2, y, dc - c);
- _store(dst, 0, 3, y, dc - d);
- }
-
- /// abs(i)
- static Uint8List abs0 = new Uint8List(255 + 255 + 1);
- /// abs(i)>>1
- static Uint8List abs1 = new Uint8List(255 + 255 + 1);
- /// clips [-1020, 1020] to [-128, 127]
- static Int8List sclip1 = new Int8List(1020 + 1020 + 1);
- /// clips [-112, 112] to [-16, 15]
- static Int8List sclip2 = new Int8List(112 + 112 + 1);
- /// clips [-255,510] to [0,255]
- static Uint8List clip1 = new Uint8List(255 + 510 + 1);
-
- static void _initTables() {
- if (!_tablesInitialized) {
- for (int i = -255; i <= 255; ++i) {
- abs0[255 + i] = (i < 0) ? -i : i;
- abs1[255 + i] = abs0[255 + i] >> 1;
- }
- for (int i = -1020; i <= 1020; ++i) {
- sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i;
- }
- for (int i = -112; i <= 112; ++i) {
- sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i;
- }
- for (int i = -255; i <= 255 + 255; ++i) {
- clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
- }
- _tablesInitialized = true;
- }
- }
-
- static int _clip8b(int v) {
- return ((v & -256) == 0) ? v : (v < 0) ? 0 : 255;
- }
-
- //static int __maxN = 0;
-
- static bool _tablesInitialized = false;
-}
+import 'dart:typed_data';
+
+import '../../internal/bit_operators.dart';
+import '../../util/input_buffer.dart';
+import 'vp8.dart';
+
+class VP8Filter {
+ VP8Filter() {
+ _initTables();
+ }
+
+ void simpleVFilter16(InputBuffer p, int stride, int thresh) {
+ InputBuffer p2 = InputBuffer.from(p);
+ for (int i = 0; i < 16; ++i) {
+ p2.offset = p.offset + i;
+ if (_needsFilter(p2, stride, thresh)) {
+ _doFilter2(p2, stride);
+ }
+ }
+ }
+
+ void simpleHFilter16(InputBuffer p, int stride, int thresh) {
+ InputBuffer p2 = InputBuffer.from(p);
+ for (int i = 0; i < 16; ++i) {
+ p2.offset = p.offset + i * stride;
+ if (_needsFilter(p2, 1, thresh)) {
+ _doFilter2(p2, 1);
+ }
+ }
+ }
+
+ void simpleVFilter16i(InputBuffer p, int stride, int thresh) {
+ InputBuffer p2 = InputBuffer.from(p);
+ for (int k = 3; k > 0; --k) {
+ p2.offset += 4 * stride;
+ simpleVFilter16(p2, stride, thresh);
+ }
+ }
+
+ void simpleHFilter16i(InputBuffer p, int stride, int thresh) {
+ InputBuffer p2 = InputBuffer.from(p);
+ for (int k = 3; k > 0; --k) {
+ p2.offset += 4;
+ simpleHFilter16(p2, stride, thresh);
+ }
+ }
+
+ // on macroblock edges
+ void vFilter16(InputBuffer p, int stride, int thresh, int ithresh,
+ int hev_thresh) {
+ _filterLoop26(p, stride, 1, 16, thresh, ithresh, hev_thresh);
+ }
+
+ void hFilter16(InputBuffer p, int stride, int thresh, int ithresh,
+ int hev_thresh) {
+ _filterLoop26(p, 1, stride, 16, thresh, ithresh, hev_thresh);
+ }
+
+ // on three inner edges
+ void vFilter16i(InputBuffer p, int stride, int thresh, int ithresh,
+ int hev_thresh) {
+ InputBuffer p2 = InputBuffer.from(p);
+ for (int k = 3; k > 0; --k) {
+ p2.offset += 4 * stride;
+ _filterLoop24(p2, stride, 1, 16, thresh, ithresh, hev_thresh);
+ }
+ }
+
+ void hFilter16i(InputBuffer p, int stride, int thresh, int ithresh,
+ int hev_thresh) {
+ InputBuffer p2 = InputBuffer.from(p);
+ for (int k = 3; k > 0; --k) {
+ p2.offset += 4;
+ _filterLoop24(p2, 1, stride, 16, thresh, ithresh, hev_thresh);
+ }
+ }
+
+ /**
+ * 8-pixels wide variant, for chroma filtering
+ */
+ void vFilter8(InputBuffer u, InputBuffer v, int stride, int thresh, int ithresh,
+ int hev_thresh) {
+ _filterLoop26(u, stride, 1, 8, thresh, ithresh, hev_thresh);
+ _filterLoop26(v, stride, 1, 8, thresh, ithresh, hev_thresh);
+ }
+
+ void hFilter8(InputBuffer u, InputBuffer v, int stride, int thresh, int ithresh,
+ int hev_thresh) {
+ _filterLoop26(u, 1, stride, 8, thresh, ithresh, hev_thresh);
+ _filterLoop26(v, 1, stride, 8, thresh, ithresh, hev_thresh);
+ }
+
+ void vFilter8i(InputBuffer u, InputBuffer v, int stride, int thresh, int ithresh,
+ int hev_thresh) {
+ InputBuffer u2 = InputBuffer.from(u, offset: 4 * stride);
+ InputBuffer v2 = InputBuffer.from(v, offset: 4 * stride);
+ _filterLoop24(u2, stride, 1, 8, thresh, ithresh, hev_thresh);
+ _filterLoop24(v2, stride, 1, 8, thresh, ithresh, hev_thresh);
+ }
+
+ void hFilter8i(InputBuffer u, InputBuffer v, int stride, int thresh, int ithresh,
+ int hev_thresh) {
+ InputBuffer u2 = InputBuffer.from(u, offset: 4);
+ InputBuffer v2 = InputBuffer.from(v, offset: 4);
+ _filterLoop24(u2, 1, stride, 8, thresh, ithresh, hev_thresh);
+ _filterLoop24(v2, 1, stride, 8, thresh, ithresh, hev_thresh);
+ }
+
+ void _filterLoop26(InputBuffer p, int hstride, int vstride, int size,
+ int thresh, int ithresh, int hev_thresh) {
+ InputBuffer p2 = InputBuffer.from(p);
+ while (size-- > 0) {
+ if (_needsFilter2(p2, hstride, thresh, ithresh)) {
+ if (_hev(p2, hstride, hev_thresh)) {
+ _doFilter2(p2, hstride);
+ } else {
+ _doFilter6(p2, hstride);
+ }
+ }
+ p2.offset += vstride;
+ }
+ }
+
+ void _filterLoop24(InputBuffer p, int hstride, int vstride, int size,
+ int thresh, int ithresh, int hev_thresh) {
+ InputBuffer p2 = InputBuffer.from(p);
+ while (size-- > 0) {
+ if (_needsFilter2(p2, hstride, thresh, ithresh)) {
+ if (_hev(p2, hstride, hev_thresh)) {
+ _doFilter2(p2, hstride);
+ } else {
+ _doFilter4(p2, hstride);
+ }
+ }
+ p2.offset += vstride;
+ }
+ }
+
+ /**
+ * 4 pixels in, 2 pixels out
+ */
+ void _doFilter2(InputBuffer p, int step) {
+ final int p1 = p[-2 * step];
+ final int p0 = p[-step];
+ final int q0 = p[0];
+ final int q1 = p[step];
+ final int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
+ final int a1 = sclip2[112 + shiftR((a + 4), 3)];
+ final int a2 = sclip2[112 + shiftR((a + 3), 3)];
+ p[-step] = clip1[255 + p0 + a2];
+ p[0] = clip1[255 + q0 - a1];
+ }
+
+ /**
+ * 4 pixels in, 4 pixels out
+ */
+ void _doFilter4(InputBuffer p, int step) {
+ final int p1 = p[-2 * step];
+ final int p0 = p[-step];
+ final int q0 = p[0];
+ final int q1 = p[step];
+ final int a = 3 * (q0 - p0);
+ final int a1 = sclip2[112 + shiftR((a + 4), 3)];
+ final int a2 = sclip2[112 + shiftR((a + 3), 3)];
+ final int a3 = shiftR(a1 + 1, 1);
+ p[-2 * step] = clip1[255 + p1 + a3];
+ p[-step] = clip1[255 + p0 + a2];
+ p[0] = clip1[255 + q0 - a1];
+ p[step] = clip1[255 + q1 - a3];
+ }
+
+ /**
+ * 6 pixels in, 6 pixels out
+ */
+ void _doFilter6(InputBuffer p, int step) {
+ final int p2 = p[-3 * step];
+ final int p1 = p[-2 * step];
+ final int p0 = p[-step];
+ final int q0 = p[0];
+ final int q1 = p[step];
+ final int q2 = p[2 * step];
+ final int a = sclip1[1020 + 3 * (q0 - p0) + sclip1[1020 + p1 - q1]];
+ final int a1 = shiftR(27 * a + 63, 7); // eq. to ((3 * a + 7) * 9) >> 7
+ final int a2 = shiftR(18 * a + 63, 7); // eq. to ((2 * a + 7) * 9) >> 7
+ final int a3 = shiftR(9 * a + 63, 7); // eq. to ((1 * a + 7) * 9) >> 7
+ p[-3 * step] = clip1[255 + p2 + a3];
+ p[-2 * step] = clip1[255 + p1 + a2];
+ p[-step] = clip1[255 + p0 + a1];
+ p[0] = clip1[255 + q0 - a1];
+ p[step] = clip1[255 + q1 - a2];
+ p[2 * step] = clip1[255 + q2 - a3];
+ }
+
+ bool _hev(InputBuffer p, int step, int thresh) {
+ final int p1 = p[-2 * step];
+ final int p0 = p[-step];
+ final int q0 = p[0];
+ final int q1 = p[step];
+ return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
+ }
+
+ bool _needsFilter(InputBuffer p, int step, int thresh) {
+ final int p1 = p[-2 * step];
+ final int p0 = p[-step];
+ final int q0 = p[0];
+ final int q1 = p[step];
+ return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
+ }
+
+ bool _needsFilter2(InputBuffer p, int step, int t, int it) {
+ final int p3 = p[-4 * step];
+ final int p2 = p[-3 * step];
+ final int p1 = p[-2 * step];
+ final int p0 = p[-step];
+ final int q0 = p[0];
+ final int q1 = p[step];
+ final int q2 = p[2 * step];
+ final int q3 = p[3 * step];
+ if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t) {
+ return false;
+ }
+
+ return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it &&
+ abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it &&
+ abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it;
+ }
+
+ void transformOne(InputBuffer src, InputBuffer dst) {
+ Int32List C = Int32List(4 * 4);
+ int si = 0;
+ int di = 0;
+ int tmp = 0;
+ for (int i = 0; i < 4; ++i) { // vertical pass
+ final int a = src[si] + src[si + 8]; // [-4096, 4094]
+ final int b = src[si] - src[si + 8]; // [-4095, 4095]
+ final int c = _mul(src[si + 4], kC2) - _mul(src[si + 12], kC1); // [-3783, 3783]
+ final int d = _mul(src[si + 4], kC1) + _mul(src[si + 12], kC2); // [-3785, 3781]
+ C[tmp++] = a + d; // [-7881, 7875]
+ C[tmp++] = b + c; // [-7878, 7878]
+ C[tmp++] = b - c; // [-7878, 7878]
+ C[tmp++] = a - d; // [-7877, 7879]
+ si++;
+ }
+
+ // Each pass is expanding the dynamic range by ~3.85 (upper bound).
+ // The exact value is (2. + (kC1 + kC2) / 65536).
+ // After the second pass, maximum interval is [-3794, 3794], assuming
+ // an input in [-2048, 2047] interval. We then need to add a dst value
+ // in the [0, 255] range.
+ // In the worst case scenario, the input to clip_8b() can be as large as
+ // [-60713, 60968].
+ tmp = 0;
+ for (int i = 0; i < 4; ++i) { // horizontal pass
+ final int dc = C[tmp] + 4;
+ final int a = dc + C[tmp + 8];
+ final int b = dc - C[tmp + 8];
+ final int c = _mul(C[tmp + 4], kC2) - _mul(C[tmp + 12], kC1);
+ final int d = _mul(C[tmp + 4], kC1) + _mul(C[tmp + 12], kC2);
+ _store(dst, di, 0, 0, a + d);
+ _store(dst, di, 1, 0, b + c);
+ _store(dst, di, 2, 0, b - c);
+ _store(dst, di, 3, 0, a - d);
+ tmp++;
+ di += VP8.BPS;
+ }
+ }
+
+
+ void transform(InputBuffer src, InputBuffer dst, bool doTwo) {
+ transformOne(src, dst);
+ if (doTwo) {
+ transformOne(new InputBuffer.from(src, offset: 16),
+ new InputBuffer.from(dst, offset: 4));
+ }
+ }
+
+ void transformUV(InputBuffer src, InputBuffer dst) {
+ transform(src, dst, true);
+ transform(new InputBuffer.from(src, offset: 2 * 16),
+ new InputBuffer.from(dst, offset: 4 * VP8.BPS), true);
+ }
+
+ void transformDC(InputBuffer src, InputBuffer dst) {
+ final int DC = src[0] + 4;
+ for (int j = 0; j < 4; ++j) {
+ for (int i = 0; i < 4; ++i) {
+ _store(dst, 0, i, j, DC);
+ }
+ }
+ }
+
+ void transformDCUV(InputBuffer src, InputBuffer dst) {
+ if (src[0 * 16] != 0) {
+ transformDC(src, dst);
+ }
+ if (src[1 * 16] != 0) {
+ transformDC(new InputBuffer.from(src, offset: 1 * 16),
+ new InputBuffer.from(dst, offset: 4));
+ }
+ if (src[2 * 16] != 0) {
+ transformDC(new InputBuffer.from(src, offset: 2 * 16),
+ new InputBuffer.from(dst, offset: 4 * VP8.BPS));
+ }
+ if (src[3 * 16] != 0) {
+ transformDC(new InputBuffer.from(src, offset: 3 * 16),
+ new InputBuffer.from(dst, offset: 4 * VP8.BPS + 4));
+ }
+ }
+
+ /**
+ * Simplified transform when only in[0], in[1] and in[4] are non-zero
+ */
+ void transformAC3(InputBuffer src, InputBuffer dst) {
+ final int a = src[0] + 4;
+ final int c4 = _mul(src[4], kC2);
+ final int d4 = _mul(src[4], kC1);
+ final int c1 = _mul(src[1], kC2);
+ final int d1 = _mul(src[1], kC1);
+ _store2(dst, 0, a + d4, d1, c1);
+ _store2(dst, 1, a + c4, d1, c1);
+ _store2(dst, 2, a - c4, d1, c1);
+ _store2(dst, 3, a - d4, d1, c1);
+ }
+
+ static int AVG3(a, b, c) => shiftR(((a) + 2 * (b) + (c) + 2), 2);
+ static int AVG2(a, b) => shiftR(((a) + (b) + 1), 1);
+
+ static void VE4(InputBuffer dst) {
+ int top = -VP8.BPS; // dst +
+ final List<int> vals = [
+ AVG3(dst[top - 1], dst[top], dst[top + 1]),
+ AVG3(dst[top], dst[top + 1], dst[top + 2]),
+ AVG3(dst[top + 1], dst[top + 2], dst[top + 3]),
+ AVG3(dst[top + 2], dst[top + 3], dst[top + 4])];
+
+ for (int i = 0; i < 4; ++i) {
+ dst.memcpy(i * VP8.BPS, 4, vals);
+ }
+ }
+
+ static void HE4(InputBuffer dst) {
+ final int A = dst[-1 - VP8.BPS];
+ final int B = dst[-1];
+ final int C = dst[-1 + VP8.BPS];
+ final int D = dst[-1 + 2 * VP8.BPS];
+ final int E = dst[-1 + 3 * VP8.BPS];
+
+ InputBuffer d2 = InputBuffer.from(dst);
+
+ d2.toUint32List()[0] = 0x01010101 * AVG3(A, B, C);
+ d2.offset += VP8.BPS;
+ d2.toUint32List()[0] = 0x01010101 * AVG3(B, C, D);
+ d2.offset += VP8.BPS;
+ d2.toUint32List()[0] = 0x01010101 * AVG3(C, D, E);
+ d2.offset += VP8.BPS;
+ d2.toUint32List()[0] = 0x01010101 * AVG3(D, E, E);
+ }
+
+ static void DC4(InputBuffer dst) { // DC
+ int dc = 4;
+ for (int i = 0; i < 4; ++i) {
+ dc += dst[i - VP8.BPS] + dst[-1 + i * VP8.BPS];
+ }
+ dc >>= 3;
+ for (int i = 0; i < 4; ++i) {
+ dst.memset(i * VP8.BPS, 4, dc);
+ }
+ }
+
+ static void trueMotion(InputBuffer dst, int size) {
+ int di = 0;
+ int top = -VP8.BPS; // dst +
+ int clip0 = 255 - dst[top - 1]; // clip1 +
+
+ for (int y = 0; y < size; ++y) {
+ int clip = clip0 + dst[di - 1];
+ for (int x = 0; x < size; ++x) {
+ dst[di + x] = clip1[clip + dst[top + x]];
+ }
+
+ di += VP8.BPS;
+ }
+ }
+
+ static void TM4(InputBuffer dst) {
+ trueMotion(dst, 4);
+ }
+
+ static void TM8uv(InputBuffer dst) {
+ trueMotion(dst, 8);
+ }
+
+ static void TM16(InputBuffer dst) {
+ trueMotion(dst, 16);
+ }
+
+ static int DST(x, y) => x + y * VP8.BPS;
+
+ /**
+ * Down-right
+ */
+ static void RD4(InputBuffer dst) {
+ final int I = dst[-1 + 0 * VP8.BPS];
+ final int J = dst[-1 + 1 * VP8.BPS];
+ final int K = dst[-1 + 2 * VP8.BPS];
+ final int L = dst[-1 + 3 * VP8.BPS];
+ final int X = dst[-1 - VP8.BPS];
+ final int A = dst[0 - VP8.BPS];
+ final int B = dst[1 - VP8.BPS];
+ final int C = dst[2 - VP8.BPS];
+ final int D = dst[3 - VP8.BPS];
+
+ dst[DST(0, 3)] = AVG3(J, K, L);
+ dst[DST(0, 2)] = dst[DST(1, 3)] = AVG3(I, J, K);
+ dst[DST(0, 1)] = dst[DST(1, 2)] = dst[DST(2, 3)] = AVG3(X, I, J);
+ dst[DST(0, 0)] = dst[DST(1, 1)] = dst[DST(2, 2)] = dst[DST(3, 3)] = AVG3(A, X, I);
+ dst[DST(1, 0)] = dst[DST(2, 1)] = dst[DST(3, 2)] = AVG3(B, A, X);
+ dst[DST(2, 0)] = dst[DST(3, 1)] = AVG3(C, B, A);
+ dst[DST(3, 0)] = AVG3(D, C, B);
+ }
+
+ /**
+ * Down-Left
+ */
+ static void LD4(InputBuffer dst) {
+ final int A = dst[0 - VP8.BPS];
+ final int B = dst[1 - VP8.BPS];
+ final int C = dst[2 - VP8.BPS];
+ final int D = dst[3 - VP8.BPS];
+ final int E = dst[4 - VP8.BPS];
+ final int F = dst[5 - VP8.BPS];
+ final int G = dst[6 - VP8.BPS];
+ final int H = dst[7 - VP8.BPS];
+ dst[DST(0, 0)] = AVG3(A, B, C);
+ dst[DST(1, 0)] = dst[DST(0, 1)] = AVG3(B, C, D);
+ dst[DST(2, 0)] = dst[DST(1, 1)] = dst[DST(0, 2)] = AVG3(C, D, E);
+ dst[DST(3, 0)] = dst[DST(2, 1)] = dst[DST(1, 2)] = dst[DST(0, 3)] = AVG3(D, E, F);
+ dst[DST(3, 1)] = dst[DST(2, 2)] = dst[DST(1, 3)] = AVG3(E, F, G);
+ dst[DST(3, 2)] = dst[DST(2, 3)] = AVG3(F, G, H);
+ dst[DST(3, 3)] = AVG3(G, H, H);
+ }
+
+ /**
+ * Vertical-Right
+ */
+ static void VR4(InputBuffer dst) {
+ final int I = dst[-1 + 0 * VP8.BPS];
+ final int J = dst[-1 + 1 * VP8.BPS];
+ final int K = dst[-1 + 2 * VP8.BPS];
+ final int X = dst[-1 - VP8.BPS];
+ final int A = dst[0 - VP8.BPS];
+ final int B = dst[1 - VP8.BPS];
+ final int C = dst[2 - VP8.BPS];
+ final int D = dst[3 - VP8.BPS];
+ dst[DST(0, 0)] = dst[DST(1, 2)] = AVG2(X, A);
+ dst[DST(1, 0)] = dst[DST(2, 2)] = AVG2(A, B);
+ dst[DST(2, 0)] = dst[DST(3, 2)] = AVG2(B, C);
+ dst[DST(3, 0)] = AVG2(C, D);
+
+ dst[DST(0, 3)] = AVG3(K, J, I);
+ dst[DST(0, 2)] = AVG3(J, I, X);
+ dst[DST(0, 1)] = dst[DST(1, 3)] = AVG3(I, X, A);
+ dst[DST(1, 1)] = dst[DST(2, 3)] = AVG3(X, A, B);
+ dst[DST(2, 1)] = dst[DST(3, 3)] = AVG3(A, B, C);
+ dst[DST(3, 1)] = AVG3(B, C, D);
+ }
+
+ /**
+ * Vertical-Left
+ */
+ static void VL4(InputBuffer dst) {
+ final int A = dst[0 - VP8.BPS];
+ final int B = dst[1 - VP8.BPS];
+ final int C = dst[2 - VP8.BPS];
+ final int D = dst[3 - VP8.BPS];
+ final int E = dst[4 - VP8.BPS];
+ final int F = dst[5 - VP8.BPS];
+ final int G = dst[6 - VP8.BPS];
+ final int H = dst[7 - VP8.BPS];
+ dst[DST(0, 0)] = AVG2(A, B);
+ dst[DST(1, 0)] = dst[DST(0, 2)] = AVG2(B, C);
+ dst[DST(2, 0)] = dst[DST(1, 2)] = AVG2(C, D);
+ dst[DST(3, 0)] = dst[DST(2, 2)] = AVG2(D, E);
+
+ dst[DST(0, 1)] = AVG3(A, B, C);
+ dst[DST(1, 1)] = dst[DST(0, 3)] = AVG3(B, C, D);
+ dst[DST(2, 1)] = dst[DST(1, 3)] = AVG3(C, D, E);
+ dst[DST(3, 1)] = dst[DST(2, 3)] = AVG3(D, E, F);
+ dst[DST(3, 2)] = AVG3(E, F, G);
+ dst[DST(3, 3)] = AVG3(F, G, H);
+ }
+
+ /**
+ * Horizontal-Up
+ */
+ static void HU4(InputBuffer dst) {
+ final int I = dst[-1 + 0 * VP8.BPS];
+ final int J = dst[-1 + 1 * VP8.BPS];
+ final int K = dst[-1 + 2 * VP8.BPS];
+ final int L = dst[-1 + 3 * VP8.BPS];
+ dst[DST(0, 0)] = AVG2(I, J);
+ dst[DST(2, 0)] = dst[DST(0, 1)] = AVG2(J, K);
+ dst[DST(2, 1)] = dst[DST(0, 2)] = AVG2(K, L);
+ dst[DST(1, 0)] = AVG3(I, J, K);
+ dst[DST(3, 0)] = dst[DST(1, 1)] = AVG3(J, K, L);
+ dst[DST(3, 1)] = dst[DST(1, 2)] = AVG3(K, L, L);
+ dst[DST(3, 2)] = dst[DST(2, 2)] = dst[DST(0, 3)] = dst[DST(1, 3)] =
+ dst[DST(2, 3)] = dst[DST(3, 3)] = L;
+ }
+
+ /**
+ * Horizontal-Down
+ */
+ static void HD4(InputBuffer dst) {
+ final int I = dst[-1 + 0 * VP8.BPS];
+ final int J = dst[-1 + 1 * VP8.BPS];
+ final int K = dst[-1 + 2 * VP8.BPS];
+ final int L = dst[-1 + 3 * VP8.BPS];
+ final int X = dst[-1 - VP8.BPS];
+ final int A = dst[0 - VP8.BPS];
+ final int B = dst[1 - VP8.BPS];
+ final int C = dst[2 - VP8.BPS];
+
+ dst[DST(0, 0)] = dst[DST(2, 1)] = AVG2(I, X);
+ dst[DST(0, 1)] = dst[DST(2, 2)] = AVG2(J, I);
+ dst[DST(0, 2)] = dst[DST(2, 3)] = AVG2(K, J);
+ dst[DST(0, 3)] = AVG2(L, K);
+
+ dst[DST(3, 0)] = AVG3(A, B, C);
+ dst[DST(2, 0)] = AVG3(X, A, B);
+ dst[DST(1, 0)] = dst[DST(3, 1)] = AVG3(I, X, A);
+ dst[DST(1, 1)] = dst[DST(3, 2)] = AVG3(J, I, X);
+ dst[DST(1, 2)] = dst[DST(3, 3)] = AVG3(K, J, I);
+ dst[DST(1, 3)] = AVG3(L, K, J);
+ }
+
+ static void VE16(InputBuffer dst) { // vertical
+ for (int j = 0; j < 16; ++j) {
+ dst.memcpy(j * VP8.BPS, 16, dst, -VP8.BPS);
+ }
+ }
+
+ static void HE16(InputBuffer dst) { // horizontal
+ int di = 0;
+ for (int j = 16; j > 0; --j) {
+ dst.memset(di, 16, dst[di - 1]);
+ di += VP8.BPS;
+ }
+ }
+
+ static void Put16(int v, InputBuffer dst) {
+ for (int j = 0; j < 16; ++j) {
+ dst.memset(j * VP8.BPS, 16, v);
+ }
+ }
+
+ static void DC16(InputBuffer dst) { // DC
+ int DC = 16;
+ for (int j = 0; j < 16; ++j) {
+ DC += dst[-1 + j * VP8.BPS] + dst[j - VP8.BPS];
+ }
+ Put16(DC >> 5, dst);
+ }
+
+ /**
+ * DC with top samples not available
+ */
+ static void DC16NoTop(InputBuffer dst) {
+ int DC = 8;
+ for (int j = 0; j < 16; ++j) {
+ DC += dst[-1 + j * VP8.BPS];
+ }
+ Put16(DC >> 4, dst);
+ }
+
+ /**
+ * DC with left samples not available
+ */
+ static void DC16NoLeft(InputBuffer dst) {
+ int DC = 8;
+ for (int i = 0; i < 16; ++i) {
+ DC += dst[i - VP8.BPS];
+ }
+ Put16(DC >> 4, dst);
+ }
+
+ /**
+ * DC with no top and left samples
+ */
+ static void DC16NoTopLeft(InputBuffer dst) {
+ Put16(0x80, dst);
+ }
+
+ static void VE8uv(InputBuffer dst) {
+ for (int j = 0; j < 8; ++j) {
+ dst.memcpy(j * VP8.BPS, 8, dst, -VP8.BPS);
+ }
+ }
+
+ static void HE8uv(InputBuffer dst) {
+ int di = 0;
+ for (int j = 0; j < 8; ++j) {
+ dst.memset(di, 8, dst[di - 1]);
+ di += VP8.BPS;
+ }
+ }
+
+ /**
+ * helper for chroma-DC predictions
+ */
+ static void Put8x8uv(int value, InputBuffer dst) {
+ for (int j = 0; j < 8; ++j) {
+ dst.memset(j * VP8.BPS, 8, value);
+ }
+ }
+
+ static void DC8uv(InputBuffer dst) {
+ int dc0 = 8;
+ for (int i = 0; i < 8; ++i) {
+ dc0 += dst[i - VP8.BPS] + dst[-1 + i * VP8.BPS];
+ }
+ Put8x8uv(dc0 >> 4, dst);
+ }
+
+ /**
+ * DC with no left samples
+ */
+ static void DC8uvNoLeft(InputBuffer dst) {
+ int dc0 = 4;
+ for (int i = 0; i < 8; ++i) {
+ dc0 += dst[i - VP8.BPS];
+ }
+ Put8x8uv(dc0 >> 3, dst);
+ }
+
+ /**
+ * DC with no top samples
+ */
+ static void DC8uvNoTop(InputBuffer dst) {
+ int dc0 = 4;
+ for (int i = 0; i < 8; ++i) {
+ dc0 += dst[-1 + i * VP8.BPS];
+ }
+ Put8x8uv(dc0 >> 3, dst);
+ }
+
+ /**
+ * DC with nothing
+ */
+ static void DC8uvNoTopLeft(InputBuffer dst) {
+ Put8x8uv(0x80, dst);
+ }
+
+ static const List PredLuma4 = const [
+ DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4 ];
+
+ static const List PredLuma16 = const [
+ DC16, TM16, VE16, HE16, DC16NoTop, DC16NoLeft, DC16NoTopLeft ];
+
+ static const List PredChroma8 = const [
+ DC8uv, TM8uv, VE8uv, HE8uv, DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft ];
+
+
+ static const int kC1 = 20091 + (1 << 16);
+ static const int kC2 = 35468;
+
+ static int _mul(int a, int b) {
+ int c = a * b;
+ return shiftR(c, 16);
+ }
+
+ static void _store(InputBuffer dst, int di, int x, int y, int v) {
+ dst[di + x + y * VP8.BPS] = _clip8b(dst[di + x + y * VP8.BPS] + (v >> 3));
+ }
+
+ static void _store2(InputBuffer dst, int y, int dc, int d, int c) {
+ _store(dst, 0, 0, y, dc + d);
+ _store(dst, 0, 1, y, dc + c);
+ _store(dst, 0, 2, y, dc - c);
+ _store(dst, 0, 3, y, dc - d);
+ }
+
+ /// abs(i)
+ static Uint8List abs0 = Uint8List(255 + 255 + 1);
+ /// abs(i)>>1
+ static Uint8List abs1 = Uint8List(255 + 255 + 1);
+ /// clips [-1020, 1020] to [-128, 127]
+ static Int8List sclip1 = Int8List(1020 + 1020 + 1);
+ /// clips [-112, 112] to [-16, 15]
+ static Int8List sclip2 = Int8List(112 + 112 + 1);
+ /// clips [-255,510] to [0,255]
+ static Uint8List clip1 = Uint8List(255 + 510 + 1);
+
+ static void _initTables() {
+ if (!_tablesInitialized) {
+ for (int i = -255; i <= 255; ++i) {
+ abs0[255 + i] = (i < 0) ? -i : i;
+ abs1[255 + i] = abs0[255 + i] >> 1;
+ }
+ for (int i = -1020; i <= 1020; ++i) {
+ sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i;
+ }
+ for (int i = -112; i <= 112; ++i) {
+ sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i;
+ }
+ for (int i = -255; i <= 255 + 255; ++i) {
+ clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
+ }
+ _tablesInitialized = true;
+ }
+ }
+
+ static int _clip8b(int v) {
+ return ((v & -256) == 0) ? v : (v < 0) ? 0 : 255;
+ }
+
+ //static int __maxN = 0;
+
+ static bool _tablesInitialized = false;
+}
diff --git a/image/lib/src/formats/webp/vp8_types.dart b/image/lib/src/formats/webp/vp8_types.dart
old mode 100644
new mode 100755
index ce329bd..de08bd1
--- a/image/lib/src/formats/webp/vp8_types.dart
+++ b/image/lib/src/formats/webp/vp8_types.dart
@@ -1,214 +1,214 @@
-import 'dart:typed_data';
-
-import 'vp8.dart';
-
-class VP8FrameHeader {
- bool keyFrame;
- int profile; // uint8
- int show; // uint8
- int partitionLength; // uint32
-}
-
-class VP8PictureHeader {
- int width; // uint16
- int height; // uint16
- int xscale; // uint8
- int yscale; // uint8
- int colorspace; // uint8, 0 = YCbCr
- int clampType; // uint8
-}
-
-/**
- * Segment features
- */
-class VP8SegmentHeader {
- bool useSegment = false;
- /// whether to update the segment map or not
- bool updateMap = false;
- /// absolute or delta values for quantizer and filter
- bool absoluteDelta = true;
- /// quantization changes
- Int8List quantizer = new Int8List(VP8.NUM_MB_SEGMENTS);
- /// filter strength for segments
- Int8List filterStrength = new Int8List(VP8.NUM_MB_SEGMENTS);
-}
-
-/**
- * All the probas associated to one band
- */
-class VP8BandProbas {
- List<Uint8List> probas = new List<Uint8List>(VP8.NUM_CTX);
- VP8BandProbas() {
- for (int i = 0; i < VP8.NUM_CTX; ++i) {
- probas[i] = new Uint8List(VP8.NUM_PROBAS);
- }
- }
-}
-
-/**
- * Struct collecting all frame-persistent probabilities.
- */
-class VP8Proba {
- Uint8List segments = new Uint8List(VP8.MB_FEATURE_TREE_PROBS);
- /// Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
- List<List<VP8BandProbas>> bands = new List(VP8.NUM_TYPES);
-
- VP8Proba() {
- for (int i = 0; i < VP8.NUM_TYPES; ++i) {
- bands[i] = new List<VP8BandProbas>(VP8.NUM_BANDS);
- for (int j = 0; j < VP8.NUM_BANDS; ++j) {
- bands[i][j] = new VP8BandProbas();
- }
- }
-
- segments.fillRange(0, segments.length, 255);
- }
-}
-
-/**
- * Filter parameters
- */
-class VP8FilterHeader {
- bool simple; // 0=complex, 1=simple
- int level; // [0..63]
- int sharpness; // [0..7]
- bool useLfDelta;
- Int32List refLfDelta = new Int32List(VP8.NUM_REF_LF_DELTAS);
- Int32List modeLfDelta = new Int32List(VP8.NUM_MODE_LF_DELTAS);
-}
-
-//------------------------------------------------------------------------------
-// Informations about the macroblocks.
-
-/**
- * filter specs
- */
-class VP8FInfo {
- int fLimit = 0; // uint8_t, filter limit in [3..189], or 0 if no filtering
- int fInnerLevel = 0; // uint8_t, inner limit in [1..63]
- bool fInner = false; // uint8_t, do inner filtering?
- int hevThresh = 0; // uint8_t, high edge variance threshold in [0..2]
-}
-
-/**
- * Top/Left Contexts used for syntax-parsing
- */
-class VP8MB{
- int nz = 0; // uint8_t, non-zero AC/DC coeffs (4bit for luma + 4bit for chroma)
- int nzDc = 0; // uint8_t, non-zero DC coeff (1bit)
-}
-
-/**
- * Dequantization matrices
- */
-class VP8QuantMatrix {
- Int32List y1Mat = new Int32List(2);
- Int32List y2Mat = new Int32List(2);
- Int32List uvMat = new Int32List(2);
-
- int uvQuant; // U/V quantizer value
- int dither; // dithering amplitude (0 = off, max=255)
-}
-
-/**
- * Data needed to reconstruct a macroblock
- */
-class VP8MBData {
- /// 384 coeffs = (16+4+4) * 4*4
- Int16List coeffs = new Int16List(384);
- bool isIntra4x4; // true if intra4x4
- /// one 16x16 mode (#0) or sixteen 4x4 modes
- Uint8List imodes = new Uint8List(16);
- /// chroma prediction mode
- int uvmode;
- // bit-wise info about the content of each sub-4x4 blocks (in decoding order).
- // Each of the 4x4 blocks for y/u/v is associated with a 2b code according to:
- // code=0 -> no coefficient
- // code=1 -> only DC
- // code=2 -> first three coefficients are non-zero
- // code=3 -> more than three coefficients are non-zero
- // This allows to call specialized transform functions.
- int nonZeroY;
- int nonZeroUV;
- /// uint8_t, local dithering strength (deduced from non_zero_*)
- int dither;
-}
-
-/**
- * Saved top samples, per macroblock. Fits into a cache-line.
- */
-class VP8TopSamples {
- Uint8List y = new Uint8List(16);
- Uint8List u = new Uint8List(8);
- Uint8List v = new Uint8List(8);
-}
-
-class VP8Random {
- int _index1;
- int _index2;
- Uint32List _table = new Uint32List(RANDOM_TABLE_SIZE);
- int _amplitude;
-
- /**
- * Initializes random generator with an amplitude 'dithering' in range [0..1].
- */
- VP8Random(double dithering) {
- _table.setRange(0, RANDOM_TABLE_SIZE, _RANDOM_TABLE);
- _index1 = 0;
- _index2 = 31;
- _amplitude = (dithering < 0.0) ? 0 :
- (dithering > 1.0) ? (1 << RANDOM_DITHER_FIX) :
- ((1 << RANDOM_DITHER_FIX) * dithering).toInt();
- }
-
- /**
- * Returns a centered pseudo-random number with 'num_bits' amplitude.
- * (uses D.Knuth's Difference-based random generator).
- * 'amp' is in RANDOM_DITHER_FIX fixed-point precision.
- */
- int randomBits2(int numBits, int amp) {
- int diff = _table[_index1] - _table[_index2];
- if (diff < 0) {
- diff += (1 << 31);
- }
-
- _table[_index1] = diff;
-
- if (++_index1 == RANDOM_TABLE_SIZE) {
- _index1 = 0;
- }
- if (++_index2 == RANDOM_TABLE_SIZE) {
- _index2 = 0;
- }
-
- // sign-extend, 0-center
- diff = (diff << 1) >> (32 - numBits);
- // restrict range
- diff = (diff * amp) >> RANDOM_DITHER_FIX;
- // shift back to 0.5-center
- diff += 1 << (numBits - 1);
-
- return diff;
- }
-
- int randomBits(int numBits) {
- return randomBits2(numBits, _amplitude);
- }
-
- /// fixed-point precision for dithering
- static const int RANDOM_DITHER_FIX = 8;
- static const int RANDOM_TABLE_SIZE = 55;
-
- // 31b-range values
- static const List<int> _RANDOM_TABLE = const [
- 0x0de15230, 0x03b31886, 0x775faccb, 0x1c88626a, 0x68385c55, 0x14b3b828,
- 0x4a85fef8, 0x49ddb84b, 0x64fcf397, 0x5c550289, 0x4a290000, 0x0d7ec1da,
- 0x5940b7ab, 0x5492577d, 0x4e19ca72, 0x38d38c69, 0x0c01ee65, 0x32a1755f,
- 0x5437f652, 0x5abb2c32, 0x0faa57b1, 0x73f533e7, 0x685feeda, 0x7563cce2,
- 0x6e990e83, 0x4730a7ed, 0x4fc0d9c6, 0x496b153c, 0x4f1403fa, 0x541afb0c,
- 0x73990b32, 0x26d7cb1c, 0x6fcc3706, 0x2cbb77d8, 0x75762f2a, 0x6425ccdd,
- 0x24b35461, 0x0a7d8715, 0x220414a8, 0x141ebf67, 0x56b41583, 0x73e502e3,
- 0x44cab16f, 0x28264d42, 0x73baaefb, 0x0a50ebed, 0x1d6ab6fb, 0x0d3ad40b,
- 0x35db3b68, 0x2b081e83, 0x77ce6b95, 0x5181e5f0, 0x78853bbc, 0x009f9494,
- 0x27e5ed3c];
-}
+import 'dart:typed_data';
+
+import 'vp8.dart';
+
+class VP8FrameHeader {
+ bool keyFrame;
+ int profile; // uint8
+ int show; // uint8
+ int partitionLength; // uint32
+}
+
+class VP8PictureHeader {
+ int width; // uint16
+ int height; // uint16
+ int xscale; // uint8
+ int yscale; // uint8
+ int colorspace; // uint8, 0 = YCbCr
+ int clampType; // uint8
+}
+
+/**
+ * Segment features
+ */
+class VP8SegmentHeader {
+ bool useSegment = false;
+ /// whether to update the segment map or not
+ bool updateMap = false;
+ /// absolute or delta values for quantizer and filter
+ bool absoluteDelta = true;
+ /// quantization changes
+ Int8List quantizer = Int8List(VP8.NUM_MB_SEGMENTS);
+ /// filter strength for segments
+ Int8List filterStrength = Int8List(VP8.NUM_MB_SEGMENTS);
+}
+
+/**
+ * All the probas associated to one band
+ */
+class VP8BandProbas {
+ List<Uint8List> probas = List<Uint8List>(VP8.NUM_CTX);
+ VP8BandProbas() {
+ for (int i = 0; i < VP8.NUM_CTX; ++i) {
+ probas[i] = Uint8List(VP8.NUM_PROBAS);
+ }
+ }
+}
+
+/**
+ * Struct collecting all frame-persistent probabilities.
+ */
+class VP8Proba {
+ Uint8List segments = Uint8List(VP8.MB_FEATURE_TREE_PROBS);
+ /// Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
+ List<List<VP8BandProbas>> bands = List(VP8.NUM_TYPES);
+
+ VP8Proba() {
+ for (int i = 0; i < VP8.NUM_TYPES; ++i) {
+ bands[i] = List<VP8BandProbas>(VP8.NUM_BANDS);
+ for (int j = 0; j < VP8.NUM_BANDS; ++j) {
+ bands[i][j] = VP8BandProbas();
+ }
+ }
+
+ segments.fillRange(0, segments.length, 255);
+ }
+}
+
+/**
+ * Filter parameters
+ */
+class VP8FilterHeader {
+ bool simple; // 0=complex, 1=simple
+ int level; // [0..63]
+ int sharpness; // [0..7]
+ bool useLfDelta;
+ Int32List refLfDelta = Int32List(VP8.NUM_REF_LF_DELTAS);
+ Int32List modeLfDelta = Int32List(VP8.NUM_MODE_LF_DELTAS);
+}
+
+//------------------------------------------------------------------------------
+// Informations about the macroblocks.
+
+/**
+ * filter specs
+ */
+class VP8FInfo {
+ int fLimit = 0; // uint8_t, filter limit in [3..189], or 0 if no filtering
+ int fInnerLevel = 0; // uint8_t, inner limit in [1..63]
+ bool fInner = false; // uint8_t, do inner filtering?
+ int hevThresh = 0; // uint8_t, high edge variance threshold in [0..2]
+}
+
+/**
+ * Top/Left Contexts used for syntax-parsing
+ */
+class VP8MB{
+ int nz = 0; // uint8_t, non-zero AC/DC coeffs (4bit for luma + 4bit for chroma)
+ int nzDc = 0; // uint8_t, non-zero DC coeff (1bit)
+}
+
+/**
+ * Dequantization matrices
+ */
+class VP8QuantMatrix {
+ Int32List y1Mat = Int32List(2);
+ Int32List y2Mat = Int32List(2);
+ Int32List uvMat = Int32List(2);
+
+ int uvQuant; // U/V quantizer value
+ int dither; // dithering amplitude (0 = off, max=255)
+}
+
+/**
+ * Data needed to reconstruct a macroblock
+ */
+class VP8MBData {
+ /// 384 coeffs = (16+4+4) * 4*4
+ Int16List coeffs = Int16List(384);
+ bool isIntra4x4; // true if intra4x4
+ /// one 16x16 mode (#0) or sixteen 4x4 modes
+ Uint8List imodes = Uint8List(16);
+ /// chroma prediction mode
+ int uvmode;
+ // bit-wise info about the content of each sub-4x4 blocks (in decoding order).
+ // Each of the 4x4 blocks for y/u/v is associated with a 2b code according to:
+ // code=0 -> no coefficient
+ // code=1 -> only DC
+ // code=2 -> first three coefficients are non-zero
+ // code=3 -> more than three coefficients are non-zero
+ // This allows to call specialized transform functions.
+ int nonZeroY;
+ int nonZeroUV;
+ /// uint8_t, local dithering strength (deduced from non_zero_*)
+ int dither;
+}
+
+/**
+ * Saved top samples, per macroblock. Fits into a cache-line.
+ */
+class VP8TopSamples {
+ Uint8List y = Uint8List(16);
+ Uint8List u = Uint8List(8);
+ Uint8List v = Uint8List(8);
+}
+
+class VP8Random {
+ int _index1;
+ int _index2;
+ Uint32List _table = Uint32List(RANDOM_TABLE_SIZE);
+ int _amplitude;
+
+ /**
+ * Initializes random generator with an amplitude 'dithering' in range [0..1].
+ */
+ VP8Random(double dithering) {
+ _table.setRange(0, RANDOM_TABLE_SIZE, _RANDOM_TABLE);
+ _index1 = 0;
+ _index2 = 31;
+ _amplitude = (dithering < 0.0) ? 0 :
+ (dithering > 1.0) ? (1 << RANDOM_DITHER_FIX) :
+ ((1 << RANDOM_DITHER_FIX) * dithering).toInt();
+ }
+
+ /**
+ * Returns a centered pseudo-random number with 'num_bits' amplitude.
+ * (uses D.Knuth's Difference-based random generator).
+ * 'amp' is in RANDOM_DITHER_FIX fixed-point precision.
+ */
+ int randomBits2(int numBits, int amp) {
+ int diff = _table[_index1] - _table[_index2];
+ if (diff < 0) {
+ diff += (1 << 31);
+ }
+
+ _table[_index1] = diff;
+
+ if (++_index1 == RANDOM_TABLE_SIZE) {
+ _index1 = 0;
+ }
+ if (++_index2 == RANDOM_TABLE_SIZE) {
+ _index2 = 0;
+ }
+
+ // sign-extend, 0-center
+ diff = (diff << 1) >> (32 - numBits);
+ // restrict range
+ diff = (diff * amp) >> RANDOM_DITHER_FIX;
+ // shift back to 0.5-center
+ diff += 1 << (numBits - 1);
+
+ return diff;
+ }
+
+ int randomBits(int numBits) {
+ return randomBits2(numBits, _amplitude);
+ }
+
+ /// fixed-point precision for dithering
+ static const int RANDOM_DITHER_FIX = 8;
+ static const int RANDOM_TABLE_SIZE = 55;
+
+ // 31b-range values
+ static const List<int> _RANDOM_TABLE = const [
+ 0x0de15230, 0x03b31886, 0x775faccb, 0x1c88626a, 0x68385c55, 0x14b3b828,
+ 0x4a85fef8, 0x49ddb84b, 0x64fcf397, 0x5c550289, 0x4a290000, 0x0d7ec1da,
+ 0x5940b7ab, 0x5492577d, 0x4e19ca72, 0x38d38c69, 0x0c01ee65, 0x32a1755f,
+ 0x5437f652, 0x5abb2c32, 0x0faa57b1, 0x73f533e7, 0x685feeda, 0x7563cce2,
+ 0x6e990e83, 0x4730a7ed, 0x4fc0d9c6, 0x496b153c, 0x4f1403fa, 0x541afb0c,
+ 0x73990b32, 0x26d7cb1c, 0x6fcc3706, 0x2cbb77d8, 0x75762f2a, 0x6425ccdd,
+ 0x24b35461, 0x0a7d8715, 0x220414a8, 0x141ebf67, 0x56b41583, 0x73e502e3,
+ 0x44cab16f, 0x28264d42, 0x73baaefb, 0x0a50ebed, 0x1d6ab6fb, 0x0d3ad40b,
+ 0x35db3b68, 0x2b081e83, 0x77ce6b95, 0x5181e5f0, 0x78853bbc, 0x009f9494,
+ 0x27e5ed3c];
+}
diff --git a/image/lib/src/formats/webp/vp8l.dart b/image/lib/src/formats/webp/vp8l.dart
old mode 100644
new mode 100755
index f7b068e..84c6736
--- a/image/lib/src/formats/webp/vp8l.dart
+++ b/image/lib/src/formats/webp/vp8l.dart
@@ -1,900 +1,901 @@
-import 'dart:typed_data';
-
-import '../../color.dart';
-import '../../image.dart';
-import '../../image_exception.dart';
-import '../../internal/internal.dart';
-import '../../util/input_buffer.dart';
-import 'vp8l_bit_reader.dart';
-import 'vp8l_color_cache.dart';
-import 'vp8l_transform.dart';
-import 'webp_huffman.dart';
-import 'webp_info.dart';
-
-/**
- * WebP lossless format.
- */
-class VP8L {
- InputBuffer input;
- VP8LBitReader br;
- WebPInfo webp;
- Image image;
-
- VP8L(InputBuffer input, WebPInfo webp) :
- this.input = input,
- this.webp = webp,
- this.br = new VP8LBitReader(input) {
- }
-
- bool decodeHeader() {
- int signature = br.readBits(8);
- if (signature != VP8L_MAGIC_BYTE) {
- return false;
- }
-
- webp.format = WebPInfo.FORMAT_LOSSLESS;
- webp.width = br.readBits(14) + 1;
- webp.height = br.readBits(14) + 1;
- webp.hasAlpha = br.readBits(1) != 0;
- int version = br.readBits(3);
-
- if (version != VP8L_VERSION) {
- return false;
- }
-
- return true;
- }
-
- Image decode() {
- _lastPixel = 0;
-
- if (!decodeHeader()) {
- return null;
- }
-
- _decodeImageStream(webp.width, webp.height, true);
-
- _allocateInternalBuffers32b();
-
- image = new Image(webp.width, webp.height);
-
- if (!_decodeImageData(_pixels, webp.width, webp.height,
- webp.height, _processRows)) {
- return null;
- }
-
- return image;
- }
-
- bool _allocateInternalBuffers32b() {
- final int numPixels = webp.width * webp.height;
- // Scratch buffer corresponding to top-prediction row for transforming the
- // first row in the row-blocks. Not needed for paletted alpha.
- final int cacheTopPixels = webp.width;
- // Scratch buffer for temporary BGRA storage. Not needed for paletted alpha.
- final int cachePixels = webp.width * _NUM_ARGB_CACHE_ROWS;
- final int totalNumPixels = numPixels + cacheTopPixels + cachePixels;
-
- Uint32List pixels32 = new Uint32List(totalNumPixels);
- _pixels = pixels32;
- _pixels8 = new Uint8List.view(pixels32.buffer);
- _argbCache = numPixels + cacheTopPixels;
-
- return true;
- }
-
- bool _allocateInternalBuffers8b() {
- final int totalNumPixels = webp.width * webp.height;
- _argbCache = 0;
- // pad the byteBuffer to a multiple of 4
- int n = totalNumPixels + (4 - (totalNumPixels % 4));
- _pixels8 = new Uint8List(n);
- _pixels = new Uint32List.view(_pixels8.buffer);
- return true;
- }
-
- bool _readTransform(List<int> transformSize) {
- bool ok = true;
-
- int type = br.readBits(2);
-
- // Each transform type can only be present once in the stream.
- if ((_transformsSeen & (1 << type)) != 0) {
- return false;
- }
- _transformsSeen |= (1 << type);
-
- VP8LTransform transform = new VP8LTransform();
- _transforms.add(transform);
-
- transform.type = type;
- transform.xsize = transformSize[0];
- transform.ysize = transformSize[1];
-
- switch (type) {
- case VP8LTransform.PREDICTOR_TRANSFORM:
- case VP8LTransform.CROSS_COLOR_TRANSFORM:
- transform.bits = br.readBits(3) + 2;
- transform.data = _decodeImageStream(
- _subSampleSize(transform.xsize, transform.bits),
- _subSampleSize(transform.ysize, transform.bits), false);
- break;
- case VP8LTransform.COLOR_INDEXING_TRANSFORM:
- final int numColors = br.readBits(8) + 1;
- final int bits = (numColors > 16) ? 0 :
- (numColors > 4) ? 1 :
- (numColors > 2) ? 2 : 3;
- transformSize[0] = _subSampleSize(transform.xsize, bits);
- transform.bits = bits;
- transform.data = _decodeImageStream(numColors, 1, false);
- ok = _expandColorMap(numColors, transform);
- break;
- case VP8LTransform.SUBTRACT_GREEN:
- break;
- default:
- throw new ImageException('Invalid WebP tranform type: $type');
- }
-
- return ok;
- }
-
- Uint32List _decodeImageStream(int xsize, int ysize, bool isLevel0) {
- int transformXsize = xsize;
- int transformYsize = ysize;
- int colorCacheBits = 0;
-
- // Read the transforms (may recurse).
- if (isLevel0) {
- while (br.readBits(1) != 0) {
- List<int> sizes = [transformXsize, transformYsize];
- if (!_readTransform(sizes)) {
- throw new ImageException('Invalid Transform');
- }
- transformXsize = sizes[0];
- transformYsize = sizes[1];
- }
- }
-
- // Color cache
- if (br.readBits(1) != 0) {
- colorCacheBits = br.readBits(4);
- bool ok = (colorCacheBits >= 1 && colorCacheBits <= MAX_CACHE_BITS);
- if (!ok) {
- throw new ImageException('Invalid Color Cache');
- }
- }
-
- // Read the Huffman codes (may recurse).
- if (!_readHuffmanCodes(transformXsize, transformYsize,
- colorCacheBits, isLevel0)) {
- throw new ImageException('Invalid Huffman Codes');
- }
-
- // Finish setting up the color-cache
- if (colorCacheBits > 0) {
- _colorCacheSize = 1 << colorCacheBits;
- _colorCache = new VP8LColorCache(colorCacheBits);
- } else {
- _colorCacheSize = 0;
- }
-
- webp.width = transformXsize;
- webp.height = transformYsize;
- final int numBits = _huffmanSubsampleBits;
- _huffmanXsize = _subSampleSize(transformXsize, numBits);
- _huffmanMask = (numBits == 0) ? ~0 : (1 << numBits) - 1;
-
- if (isLevel0) {
- // Reset for future DECODE_DATA_FUNC() calls.
- _lastPixel = 0;
- return null;
- }
-
- final int totalSize = transformXsize * transformYsize;
- Uint32List data = new Uint32List(totalSize);
-
- // Use the Huffman trees to decode the LZ77 encoded data.
- if (!_decodeImageData(data, transformXsize, transformYsize,
- transformYsize, null)) {
- throw new ImageException('Failed to decode image data.');
- }
-
- // Reset for future DECODE_DATA_FUNC() calls.
- _lastPixel = 0;
-
- return data;
- }
-
- bool _decodeImageData(data, int width, int height,
- int lastRow, processFunc) {
- int row = _lastPixel ~/ width;
- int col = _lastPixel % width;
-
- HTreeGroup htreeGroup = _getHtreeGroupForPos(col, row);
-
- int src = _lastPixel;
- int lastCached = src;
- int srcEnd = width * height; // End of data
- int srcLast = width * lastRow; // Last pixel to decode
-
- const int lenCodeLimit = NUM_LITERAL_CODES + NUM_LENGTH_CODES;
- final int colorCacheLimit = lenCodeLimit + _colorCacheSize;
-
- VP8LColorCache colorCache = (_colorCacheSize > 0) ? _colorCache : null;
- final int mask = _huffmanMask;
-
- while (!br.isEOS && src < srcLast) {
- // Only update when changing tile. Note we could use this test:
- // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed
- // but that's actually slower and needs storing the previous col/row.
- if ((col & mask) == 0) {
- htreeGroup = _getHtreeGroupForPos(col, row);
- }
-
- br.fillBitWindow();
- int code = htreeGroup.htrees[_GREEN].readSymbol(br);
-
- if (code < NUM_LITERAL_CODES) { // Literal
- int red = htreeGroup.htrees[_RED].readSymbol(br);
- int green = code;
- br.fillBitWindow();
- int blue = htreeGroup.htrees[_BLUE].readSymbol(br);
- int alpha = htreeGroup.htrees[_ALPHA].readSymbol(br);
-
- int c = (alpha << 24) | (red << 16) | (green << 8) | blue;
- data[src] = c;
-
- ++src;
- ++col;
-
- if (col >= width) {
- col = 0;
- ++row;
- if ((row % _NUM_ARGB_CACHE_ROWS == 0) && (processFunc != null)) {
- processFunc(row);
- }
-
- if (colorCache != null) {
- while (lastCached < src) {
- colorCache.insert(data[lastCached]);
- lastCached++;
- }
- }
- }
- } else if (code < lenCodeLimit) { // Backward reference
- final int lengthSym = code - NUM_LITERAL_CODES;
- final int length = _getCopyLength(lengthSym);
- final int distSymbol = htreeGroup.htrees[_DIST].readSymbol(br);
-
- br.fillBitWindow();
- int distCode = _getCopyDistance(distSymbol);
- int dist = _planeCodeToDistance(width, distCode);
-
- if (src < dist || srcEnd - src < length) {
- return false;
- } else {
- for (int i = 0; i < length; ++i) {
- data[src + i] = data[src + (i - dist)];
- }
- src += length;
- }
- col += length;
- while (col >= width) {
- col -= width;
- ++row;
- if ((row % _NUM_ARGB_CACHE_ROWS == 0) && (processFunc != null)) {
- processFunc(row);
- }
- }
- if (src < srcLast) {
- if ((col & mask) != 0) {
- htreeGroup = _getHtreeGroupForPos(col, row);
- }
- if (colorCache != null) {
- while (lastCached < src) {
- colorCache.insert(data[lastCached]);
- lastCached++;
- }
- }
- }
- } else if (code < colorCacheLimit) { // Color cache
- final int key = code - lenCodeLimit;
-
- while (lastCached < src) {
- colorCache.insert(data[lastCached]);
- lastCached++;
- }
-
- data[src] = colorCache.lookup(key);
-
- ++src;
- ++col;
-
- if (col >= width) {
- col = 0;
- ++row;
- if ((row % _NUM_ARGB_CACHE_ROWS == 0) && (processFunc != null)) {
- processFunc(row);
- }
-
- if (colorCache != null) {
- while (lastCached < src) {
- colorCache.insert(data[lastCached]);
- lastCached++;
- }
- }
- }
- } else { // Not reached
- return false;
- }
- }
-
- // Process the remaining rows corresponding to last row-block.
- if (processFunc != null) {
- processFunc(row);
- }
-
- if (br.isEOS && src < srcEnd) {
- return false;
- }
-
- _lastPixel = src;
-
- return true;
- }
-
- /**
- * Row-processing for the special case when alpha data contains only one
- * transform (color indexing), and trivial non-green literals.
- */
- bool _is8bOptimizable() {
- if (_colorCacheSize > 0) {
- return false;
- }
- // When the Huffman tree contains only one symbol, we can skip the
- // call to ReadSymbol() for red/blue/alpha channels.
- for (int i = 0; i < _numHtreeGroups; ++i) {
- List<HuffmanTree> htrees = _htreeGroups[i].htrees;
- if (htrees[_RED].numNodes > 1) {
- return false;
- }
- if (htrees[_BLUE].numNodes > 1) {
- return false;
- }
- if (htrees[_ALPHA].numNodes > 1) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Special row-processing that only stores the alpha data.
- */
- void _extractAlphaRows(int row) {
- final int numRows = row - _lastRow;
- if (numRows <= 0) {
- return; // Nothing to be done.
- }
-
- _applyInverseTransforms(numRows, webp.width * _lastRow);
-
- // Extract alpha (which is stored in the green plane).
- final int width = webp.width; // the final width (!= dec->width_)
- final int cachePixs = width * numRows;
-
- final int di = width * _lastRow;
- InputBuffer src = new InputBuffer(_pixels, offset: _argbCache);
-
- for (int i = 0; i < cachePixs; ++i) {
- _opaque[di + i] = (src[i] >> 8) & 0xff;
- }
-
- _lastRow = row;
- }
-
- bool _decodeAlphaData(int width, int height, int lastRow) {
- int row = _lastPixel ~/ width;
- int col = _lastPixel % width;
-
- HTreeGroup htreeGroup = _getHtreeGroupForPos(col, row);
- int pos = _lastPixel; // current position
- final int end = width * height; // End of data
- final int last = width * lastRow; // Last pixel to decode
- final int lenCodeLimit = NUM_LITERAL_CODES + NUM_LENGTH_CODES;
- final int mask = _huffmanMask;
-
- while (!br.isEOS && pos < last) {
- // Only update when changing tile.
- if ((col & mask) == 0) {
- htreeGroup = _getHtreeGroupForPos(col, row);
- }
-
- br.fillBitWindow();
-
- int code = htreeGroup.htrees[_GREEN].readSymbol(br);
- if (code < NUM_LITERAL_CODES) { // Literal
- _pixels8[pos] = code;
- ++pos;
- ++col;
- if (col >= width) {
- col = 0;
- ++row;
- if (row % _NUM_ARGB_CACHE_ROWS == 0) {
- _extractPalettedAlphaRows(row);
- }
- }
- } else if (code < lenCodeLimit) { // Backward reference
- final int lengthSym = code - NUM_LITERAL_CODES;
- final int length = _getCopyLength(lengthSym);
- final int distSymbol = htreeGroup.htrees[_DIST].readSymbol(br);
-
- br.fillBitWindow();
-
- int distCode = _getCopyDistance(distSymbol);
- int dist = _planeCodeToDistance(width, distCode);
-
- if (pos >= dist && end - pos >= length) {
- for (int i = 0; i < length; ++i) {
- _pixels8[pos + i] = _pixels8[pos + i - dist];
- }
- } else {
- _lastPixel = pos;
- return true;
- }
-
- pos += length;
- col += length;
-
- while (col >= width) {
- col -= width;
- ++row;
- if (row % _NUM_ARGB_CACHE_ROWS == 0) {
- _extractPalettedAlphaRows(row);
- }
- }
-
- if (pos < last && (col & mask) != 0) {
- htreeGroup = _getHtreeGroupForPos(col, row);
- }
- } else { // Not reached
- return false;
- }
- }
-
- // Process the remaining rows corresponding to last row-block.
- _extractPalettedAlphaRows(row);
-
- _lastPixel = pos;
-
- return true;
- }
-
- void _extractPalettedAlphaRows(int row) {
- final int numRows = row - _lastRow;
- InputBuffer pIn = new InputBuffer(_pixels8, offset: webp.width * _lastRow);
- if (numRows > 0) {
- _applyInverseTransformsAlpha(numRows, pIn);
- }
- _lastRow = row;
- }
-
- /**
- * Special method for paletted alpha data.
- */
- void _applyInverseTransformsAlpha(int numRows, InputBuffer rows) {
- final int startRow = _lastRow;
- final int endRow = startRow + numRows;
- InputBuffer rowsOut = new InputBuffer(_opaque, offset: _ioWidth * startRow);
- VP8LTransform transform = _transforms[0];
-
- transform.colorIndexInverseTransformAlpha(startRow, endRow, rows, rowsOut);
- }
-
- /**
- * Processes (transforms, scales & color-converts) the rows decoded after the
- * last call.
- */
- //static int __count = 0;
- void _processRows(int row) {
- int rows = webp.width * _lastRow; // offset into _pixels
- final int numRows = row - _lastRow;
-
- if (numRows <= 0) {
- return; // Nothing to be done.
- }
-
- _applyInverseTransforms(numRows, rows);
-
- //int count = 0;
- //int di = rows;
- for (int y = 0, pi = _argbCache, dy = _lastRow; y < numRows; ++y, ++dy) {
- for (int x = 0; x < webp.width; ++x, ++pi) {
- int c = _pixels[pi];
- int r = getRed(c);
- int g = getGreen(c);
- int b = getBlue(c);
- int a = getAlpha(c);
- // rearrange the ARGB webp color to RGBA image color.
- image.setPixel(x, dy, getColor(b, g, r, a));
- }
- }
-
- _lastRow = row;
- }
-
- void _applyInverseTransforms(int numRows, int rows) {
- int n = _transforms.length;
- final int cachePixs = webp.width * numRows;
- final int startRow = _lastRow;
- final int endRow = startRow + numRows;
- int rowsIn = rows;
- int rowsOut = _argbCache;
-
- // Inverse transforms.
- _pixels.setRange(rowsOut, rowsOut + cachePixs, _pixels, rowsIn);
-
- while (n-- > 0) {
- VP8LTransform transform = _transforms[n];
- transform.inverseTransform(startRow, endRow, _pixels, rowsIn,
- _pixels, rowsOut);
- rowsIn = rowsOut;
- }
- }
-
- bool _readHuffmanCodes(int xsize, int ysize, int colorCacheBits,
- bool allowRecursion) {
- Uint32List huffmanImage;
- int numHtreeGroups = 1;
-
- if (allowRecursion && br.readBits(1) != 0) {
- // use meta Huffman codes.
- final int huffmanPrecision = br.readBits(3) + 2;
- final int huffmanXsize = _subSampleSize(xsize, huffmanPrecision);
- final int huffmanYsize = _subSampleSize(ysize, huffmanPrecision);
- final int huffmanPixs = huffmanXsize * huffmanYsize;
-
- huffmanImage = _decodeImageStream(huffmanXsize, huffmanYsize, false);
-
- _huffmanSubsampleBits = huffmanPrecision;
-
- for (int i = 0; i < huffmanPixs; ++i) {
- // The huffman data is stored in red and green bytes.
- final int group = (huffmanImage[i] >> 8) & 0xffff;
- huffmanImage[i] = group;
- if (group >= numHtreeGroups) {
- numHtreeGroups = group + 1;
- }
- }
- }
-
- assert(numHtreeGroups <= 0x10000);
-
- List<HTreeGroup> htreeGroups = new List<HTreeGroup>(numHtreeGroups);
- for (int i = 0; i < numHtreeGroups; ++i) {
- htreeGroups[i] = new HTreeGroup();
-
- for (int j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
- int alphabetSize = ALPHABET_SIZE[j];
- if (j == 0 && colorCacheBits > 0) {
- alphabetSize += 1 << colorCacheBits;
- }
-
- if (!_readHuffmanCode(alphabetSize, htreeGroups[i].htrees[j])) {
- return false;
- }
- }
- }
-
- // All OK. Finalize pointers and return.
- _huffmanImage = huffmanImage;
- _numHtreeGroups = numHtreeGroups;
- _htreeGroups = htreeGroups;
-
- return true;
- }
-
- bool _readHuffmanCode(int alphabetSize, HuffmanTree tree) {
- bool ok = false;
- final int simpleCode = br.readBits(1);
-
- // Read symbols, codes & code lengths directly.
- if (simpleCode != 0) {
- List<int> symbols = [0, 0];
- List<int> codes = [0, 0];
- List<int> codeLengths = [0, 0];
-
- final int numSymbols = br.readBits(1) + 1;
- final int firstSymbolLenCode = br.readBits(1);
-
- // The first code is either 1 bit or 8 bit code.
- symbols[0] = br.readBits((firstSymbolLenCode == 0) ? 1 : 8);
- codes[0] = 0;
- codeLengths[0] = numSymbols - 1;
-
- // The second code (if present), is always 8 bit long.
- if (numSymbols == 2) {
- symbols[1] = br.readBits(8);
- codes[1] = 1;
- codeLengths[1] = numSymbols - 1;
- }
-
- ok = tree.buildExplicit(codeLengths, codes, symbols,
- alphabetSize, numSymbols);
- } else {
- // Decode Huffman-coded code lengths.
- Int32List codeLengthCodeLengths =
- new Int32List(_NUM_CODE_LENGTH_CODES);
-
- final int numCodes = br.readBits(4) + 4;
- if (numCodes > _NUM_CODE_LENGTH_CODES) {
- return false;
- }
-
- Int32List codeLengths = new Int32List(alphabetSize);
-
- for (int i = 0; i < numCodes; ++i) {
- codeLengthCodeLengths[_CODE_LENGTH_CODE_ORDER[i]] = br.readBits(3);
- }
-
- ok = _readHuffmanCodeLengths(codeLengthCodeLengths, alphabetSize,
- codeLengths);
-
- if (ok) {
- ok = tree.buildImplicit(codeLengths, alphabetSize);
- }
- }
-
- return ok;
- }
-
- bool _readHuffmanCodeLengths(List<int> codeLengthCodeLengths,
- int numSymbols, List<int> codeLengths) {
- //bool ok = false;
- int symbol;
- int max_symbol;
- int prev_code_len = DEFAULT_CODE_LENGTH;
- HuffmanTree tree = new HuffmanTree();
-
- if (!tree.buildImplicit(codeLengthCodeLengths, _NUM_CODE_LENGTH_CODES)) {
- return false;
- }
-
- if (br.readBits(1) != 0) { // use length
- final int length_nbits = 2 + 2 * br.readBits(3);
- max_symbol = 2 + br.readBits(length_nbits);
- if (max_symbol > numSymbols) {
- return false;
- }
- } else {
- max_symbol = numSymbols;
- }
-
- symbol = 0;
- while (symbol < numSymbols) {
- int code_len;
- if (max_symbol-- == 0) {
- break;
- }
-
- br.fillBitWindow();
-
- code_len = tree.readSymbol(br);
-
- if (code_len < _CODE_LENGTH_LITERALS) {
- codeLengths[symbol++] = code_len;
- if (code_len != 0) {
- prev_code_len = code_len;
- }
- } else {
- final bool usePrev = (code_len == _CODE_LENGTH_REPEAT_CODE);
- final int slot = code_len - _CODE_LENGTH_LITERALS;
- final int extra_bits = _CODE_LENGTH_EXTRA_BITS[slot];
- final int repeat_offset = _CODE_LENGTH_REPEAT_OFFSETS[slot];
- int repeat = br.readBits(extra_bits) + repeat_offset;
-
- if (symbol + repeat > numSymbols) {
- return false;
- } else {
- final int length = usePrev ? prev_code_len : 0;
- while (repeat-- > 0) {
- codeLengths[symbol++] = length;
- }
- }
- }
- }
-
- return true;
- }
-
- int _getCopyDistance(int distanceSymbol) {
- if (distanceSymbol < 4) {
- return distanceSymbol + 1;
- }
- int extraBits = (distanceSymbol - 2) >> 1;
- int offset = (2 + (distanceSymbol & 1)) << extraBits;
- return offset + br.readBits(extraBits) + 1;
- }
-
- int _getCopyLength(int lengthSymbol) {
- // Length and distance prefixes are encoded the same way.
- return _getCopyDistance(lengthSymbol);
- }
-
- int _planeCodeToDistance(int xsize, int planeCode) {
- if (planeCode > _CODE_TO_PLANE_CODES) {
- return planeCode - _CODE_TO_PLANE_CODES;
- } else {
- final int distCode = _CODE_TO_PLANE[planeCode - 1];
- final int yoffset = distCode >> 4;
- final int xoffset = 8 - (distCode & 0xf);
- final int dist = yoffset * xsize + xoffset;
- // dist<1 can happen if xsize is very small
- return (dist >= 1) ? dist : 1;
- }
- }
-
- /**
- * Computes sampled size of 'size' when sampling using 'sampling bits'.
- */
- static int _subSampleSize(int size, int samplingBits) {
- return (size + (1 << samplingBits) - 1) >> samplingBits;
- }
-
- /**
- * For security reason, we need to remap the color map to span
- * the total possible bundled values, and not just the num_colors.
- */
- bool _expandColorMap(int numColors, VP8LTransform transform) {
- final int finalNumColors = 1 << (8 >> transform.bits);
- Uint32List newColorMap = new Uint32List(finalNumColors);
- Uint8List data = new Uint8List.view(transform.data.buffer);
- Uint8List newData = new Uint8List.view(newColorMap.buffer);
-
- newColorMap[0] = transform.data[0];
-
- int len = 4 * numColors;
-
- int i;
- for (i = 4; i < len; ++i) {
- // Equivalent to AddPixelEq(), on a byte-basis.
- newData[i] = (data[i] + newData[i - 4]) & 0xff;
- }
-
- for (len = 4 * finalNumColors; i < len; ++i) {
- newData[i] = 0;
- }
-
- transform.data = newColorMap;
-
- return true;
- }
-
- int _getMetaIndex(Uint32List image, int xsize, int bits, int x, int y) {
- if (bits == 0) {
- return 0;
- }
- return image[xsize * (y >> bits) + (x >> bits)];
- }
-
- HTreeGroup _getHtreeGroupForPos(int x, int y) {
- int metaIndex = _getMetaIndex(_huffmanImage, _huffmanXsize,
- _huffmanSubsampleBits, x, y);
- if (_htreeGroups[metaIndex] == null) {
- _htreeGroups[metaIndex] = new HTreeGroup();
- }
- return _htreeGroups[metaIndex];
- }
-
- static const int _GREEN = 0;
- static const int _RED = 1;
- static const int _BLUE = 2;
- static const int _ALPHA = 3;
- static const int _DIST = 4;
-
- static const int _NUM_ARGB_CACHE_ROWS = 16;
-
- static const int _NUM_CODE_LENGTH_CODES = 19;
-
- static const List<int> _CODE_LENGTH_CODE_ORDER = const [
- 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
-
- static const int _CODE_TO_PLANE_CODES = 120;
- static const List<int> _CODE_TO_PLANE = const [
- 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a,
- 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a,
- 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b,
- 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03,
- 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c,
- 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e,
- 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b,
- 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f,
- 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b,
- 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41,
- 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f,
- 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70];
-
- static const int _CODE_LENGTH_LITERALS = 16;
- static const int _CODE_LENGTH_REPEAT_CODE = 16;
- static const List<int> _CODE_LENGTH_EXTRA_BITS = const [2, 3, 7];
- static const List<int> _CODE_LENGTH_REPEAT_OFFSETS = const [ 3, 3, 11 ];
-
- static const List<int> ALPHABET_SIZE = const [
- NUM_LITERAL_CODES + NUM_LENGTH_CODES,
- NUM_LITERAL_CODES, NUM_LITERAL_CODES,
- NUM_LITERAL_CODES, NUM_DISTANCE_CODES];
-
- static const int VP8L_MAGIC_BYTE = 0x2f;
- static const int VP8L_VERSION = 0;
-
- int _lastPixel = 0;
- int _lastRow = 0;
-
- int _colorCacheSize = 0;
- VP8LColorCache _colorCache;
-
- int _huffmanMask = 0;
- int _huffmanSubsampleBits = 0;
- int _huffmanXsize = 0;
- Uint32List _huffmanImage;
- int _numHtreeGroups = 0;
- List<HTreeGroup> _htreeGroups = [];
- List<VP8LTransform> _transforms = [];
- int _transformsSeen = 0;
-
- Uint32List _pixels;
- Uint8List _pixels8;
- int _argbCache;
-
- Uint8List _opaque;
-
- int _ioWidth;
- int _ioHeight;
-
- static const int ARGB_BLACK = 0xff000000;
- static const int MAX_CACHE_BITS = 11;
- static const int HUFFMAN_CODES_PER_META_CODE = 5;
-
- static const int DEFAULT_CODE_LENGTH = 8;
- static const int MAX_ALLOWED_CODE_LENGTH = 15;
-
- static const int NUM_LITERAL_CODES = 256;
- static const int NUM_LENGTH_CODES = 24;
- static const int NUM_DISTANCE_CODES = 40;
- static const int CODE_LENGTH_CODES = 19;
-}
-
-@internal
-class InternalVP8L extends VP8L {
- InternalVP8L(InputBuffer input, WebPInfo webp) : super(input, webp);
-
- List<VP8LTransform> get transforms => _transforms;
- Uint32List get pixels => _pixels;
-
- Uint8List get opaque => _opaque;
- set opaque(Uint8List value) => _opaque = value;
-
- int get ioWidth => _ioWidth;
- set ioWidth(int width) => _ioWidth = width;
- int get ioHeight => _ioHeight;
- set ioHeight(int height) => _ioHeight = height;
-
- bool decodeImageData(data, int width, int height, int lastRow, processFunc) =>
- _decodeImageData(data, width, height, lastRow, processFunc);
-
- Uint32List decodeImageStream(int xsize, int ysize, bool isLevel0) =>
- _decodeImageStream(xsize, ysize, isLevel0);
-
- bool allocateInternalBuffers32b() => _allocateInternalBuffers32b();
-
- bool allocateInternalBuffers8b() => _allocateInternalBuffers8b();
-
- bool decodeAlphaData(int width, int height, int lastRow) =>
- _decodeAlphaData(width, height, lastRow);
-
- bool is8bOptimizable() => _is8bOptimizable();
-
- void extractAlphaRows(int row) => _extractAlphaRows(row);
-
- static int subSampleSize(int size, int samplingBits) => VP8L._subSampleSize(size, samplingBits);
-}
+import 'dart:typed_data';
+
+import '../../color.dart';
+import '../../image.dart';
+import '../../image_exception.dart';
+import '../../internal/internal.dart';
+import '../../util/input_buffer.dart';
+import 'vp8l_bit_reader.dart';
+import 'vp8l_color_cache.dart';
+import 'vp8l_transform.dart';
+import 'webp_huffman.dart';
+import 'webp_info.dart';
+
+/**
+ * WebP lossless format.
+ */
+class VP8L {
+ InputBuffer input;
+ VP8LBitReader br;
+ WebPInfo webp;
+ Image image;
+
+ VP8L(InputBuffer input, WebPInfo webp) :
+ this.input = input,
+ this.webp = webp,
+ this.br = VP8LBitReader(input) {
+ }
+
+ bool decodeHeader() {
+ int signature = br.readBits(8);
+ if (signature != VP8L_MAGIC_BYTE) {
+ return false;
+ }
+
+ webp.format = WebPInfo.FORMAT_LOSSLESS;
+ webp.width = br.readBits(14) + 1;
+ webp.height = br.readBits(14) + 1;
+ webp.hasAlpha = br.readBits(1) != 0;
+ int version = br.readBits(3);
+
+ if (version != VP8L_VERSION) {
+ return false;
+ }
+
+ return true;
+ }
+
+ Image decode() {
+ _lastPixel = 0;
+
+ if (!decodeHeader()) {
+ return null;
+ }
+
+ _decodeImageStream(webp.width, webp.height, true);
+
+ _allocateInternalBuffers32b();
+
+ image = Image(webp.width, webp.height);
+
+ if (!_decodeImageData(_pixels, webp.width, webp.height,
+ webp.height, _processRows)) {
+ return null;
+ }
+
+ return image;
+ }
+
+ bool _allocateInternalBuffers32b() {
+ final int numPixels = webp.width * webp.height;
+ // Scratch buffer corresponding to top-prediction row for transforming the
+ // first row in the row-blocks. Not needed for paletted alpha.
+ final int cacheTopPixels = webp.width;
+ // Scratch buffer for temporary BGRA storage. Not needed for paletted alpha.
+ final int cachePixels = webp.width * _NUM_ARGB_CACHE_ROWS;
+ final int totalNumPixels = numPixels + cacheTopPixels + cachePixels;
+
+ Uint32List pixels32 = Uint32List(totalNumPixels);
+ _pixels = pixels32;
+ _pixels8 = Uint8List.view(pixels32.buffer);
+ _argbCache = numPixels + cacheTopPixels;
+
+ return true;
+ }
+
+ bool _allocateInternalBuffers8b() {
+ final int totalNumPixels = webp.width * webp.height;
+ _argbCache = 0;
+ // pad the byteBuffer to a multiple of 4
+ int n = totalNumPixels + (4 - (totalNumPixels % 4));
+ _pixels8 = Uint8List(n);
+ _pixels = Uint32List.view(_pixels8.buffer);
+ return true;
+ }
+
+ bool _readTransform(List<int> transformSize) {
+ bool ok = true;
+
+ int type = br.readBits(2);
+
+ // Each transform type can only be present once in the stream.
+ if ((_transformsSeen & (1 << type)) != 0) {
+ return false;
+ }
+ _transformsSeen |= (1 << type);
+
+ VP8LTransform transform = VP8LTransform();
+ _transforms.add(transform);
+
+ transform.type = type;
+ transform.xsize = transformSize[0];
+ transform.ysize = transformSize[1];
+
+ switch (type) {
+ case VP8LTransform.PREDICTOR_TRANSFORM:
+ case VP8LTransform.CROSS_COLOR_TRANSFORM:
+ transform.bits = br.readBits(3) + 2;
+ transform.data = _decodeImageStream(
+ _subSampleSize(transform.xsize, transform.bits),
+ _subSampleSize(transform.ysize, transform.bits), false);
+ break;
+ case VP8LTransform.COLOR_INDEXING_TRANSFORM:
+ final int numColors = br.readBits(8) + 1;
+ final int bits = (numColors > 16) ? 0 :
+ (numColors > 4) ? 1 :
+ (numColors > 2) ? 2 : 3;
+ transformSize[0] = _subSampleSize(transform.xsize, bits);
+ transform.bits = bits;
+ transform.data = _decodeImageStream(numColors, 1, false);
+ ok = _expandColorMap(numColors, transform);
+ break;
+ case VP8LTransform.SUBTRACT_GREEN:
+ break;
+ default:
+ throw new ImageException('Invalid WebP tranform type: $type');
+ }
+
+ return ok;
+ }
+
+ Uint32List _decodeImageStream(int xsize, int ysize, bool isLevel0) {
+ int transformXsize = xsize;
+ int transformYsize = ysize;
+ int colorCacheBits = 0;
+
+ // Read the transforms (may recurse).
+ if (isLevel0) {
+ while (br.readBits(1) != 0) {
+ List<int> sizes = [transformXsize, transformYsize];
+ if (!_readTransform(sizes)) {
+ throw new ImageException('Invalid Transform');
+ }
+ transformXsize = sizes[0];
+ transformYsize = sizes[1];
+ }
+ }
+
+ // Color cache
+ if (br.readBits(1) != 0) {
+ colorCacheBits = br.readBits(4);
+ bool ok = (colorCacheBits >= 1 && colorCacheBits <= MAX_CACHE_BITS);
+ if (!ok) {
+ throw new ImageException('Invalid Color Cache');
+ }
+ }
+
+ // Read the Huffman codes (may recurse).
+ if (!_readHuffmanCodes(transformXsize, transformYsize,
+ colorCacheBits, isLevel0)) {
+ throw new ImageException('Invalid Huffman Codes');
+ }
+
+ // Finish setting up the color-cache
+ if (colorCacheBits > 0) {
+ _colorCacheSize = 1 << colorCacheBits;
+ _colorCache = VP8LColorCache(colorCacheBits);
+ } else {
+ _colorCacheSize = 0;
+ }
+
+ webp.width = transformXsize;
+ webp.height = transformYsize;
+ final int numBits = _huffmanSubsampleBits;
+ _huffmanXsize = _subSampleSize(transformXsize, numBits);
+ _huffmanMask = (numBits == 0) ? ~0 : (1 << numBits) - 1;
+
+ if (isLevel0) {
+ // Reset for future DECODE_DATA_FUNC() calls.
+ _lastPixel = 0;
+ return null;
+ }
+
+ final int totalSize = transformXsize * transformYsize;
+ Uint32List data = Uint32List(totalSize);
+
+ // Use the Huffman trees to decode the LZ77 encoded data.
+ if (!_decodeImageData(data, transformXsize, transformYsize,
+ transformYsize, null)) {
+ throw new ImageException('Failed to decode image data.');
+ }
+
+ // Reset for future DECODE_DATA_FUNC() calls.
+ _lastPixel = 0;
+
+ return data;
+ }
+
+ bool _decodeImageData(data, int width, int height,
+ int lastRow, processFunc) {
+ int row = _lastPixel ~/ width;
+ int col = _lastPixel % width;
+
+ HTreeGroup htreeGroup = _getHtreeGroupForPos(col, row);
+
+ int src = _lastPixel;
+ int lastCached = src;
+ int srcEnd = width * height; // End of data
+ int srcLast = width * lastRow; // Last pixel to decode
+
+ const int lenCodeLimit = NUM_LITERAL_CODES + NUM_LENGTH_CODES;
+ final int colorCacheLimit = lenCodeLimit + _colorCacheSize;
+
+ VP8LColorCache colorCache = (_colorCacheSize > 0) ? _colorCache : null;
+ final int mask = _huffmanMask;
+
+ while (!br.isEOS && src < srcLast) {
+ // Only update when changing tile. Note we could use this test:
+ // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed
+ // but that's actually slower and needs storing the previous col/row.
+ if ((col & mask) == 0) {
+ htreeGroup = _getHtreeGroupForPos(col, row);
+ }
+
+ br.fillBitWindow();
+ int code = htreeGroup.htrees[_GREEN].readSymbol(br);
+
+ if (code < NUM_LITERAL_CODES) { // Literal
+ int red = htreeGroup.htrees[_RED].readSymbol(br);
+ int green = code;
+ br.fillBitWindow();
+ int blue = htreeGroup.htrees[_BLUE].readSymbol(br);
+ int alpha = htreeGroup.htrees[_ALPHA].readSymbol(br);
+
+ int c = getColor(red, green, blue, alpha);
+ data[src] = c;
+
+ ++src;
+ ++col;
+
+ if (col >= width) {
+ col = 0;
+ ++row;
+ if ((row % _NUM_ARGB_CACHE_ROWS == 0) && (processFunc != null)) {
+ processFunc(row);
+ }
+
+ if (colorCache != null) {
+ while (lastCached < src) {
+ colorCache.insert(data[lastCached]);
+ lastCached++;
+ }
+ }
+ }
+ } else if (code < lenCodeLimit) { // Backward reference
+ final int lengthSym = code - NUM_LITERAL_CODES;
+ final int length = _getCopyLength(lengthSym);
+ final int distSymbol = htreeGroup.htrees[_DIST].readSymbol(br);
+
+ br.fillBitWindow();
+ int distCode = _getCopyDistance(distSymbol);
+ int dist = _planeCodeToDistance(width, distCode);
+
+ if (src < dist || srcEnd - src < length) {
+ return false;
+ } else {
+ for (int i = 0; i < length; ++i) {
+ data[src + i] = data[src + (i - dist)];
+ }
+ src += length;
+ }
+ col += length;
+ while (col >= width) {
+ col -= width;
+ ++row;
+ if ((row % _NUM_ARGB_CACHE_ROWS == 0) && (processFunc != null)) {
+ processFunc(row);
+ }
+ }
+ if (src < srcLast) {
+ if ((col & mask) != 0) {
+ htreeGroup = _getHtreeGroupForPos(col, row);
+ }
+ if (colorCache != null) {
+ while (lastCached < src) {
+ colorCache.insert(data[lastCached]);
+ lastCached++;
+ }
+ }
+ }
+ } else if (code < colorCacheLimit) { // Color cache
+ final int key = code - lenCodeLimit;
+
+ while (lastCached < src) {
+ colorCache.insert(data[lastCached]);
+ lastCached++;
+ }
+
+ data[src] = colorCache.lookup(key);
+
+ ++src;
+ ++col;
+
+ if (col >= width) {
+ col = 0;
+ ++row;
+ if ((row % _NUM_ARGB_CACHE_ROWS == 0) && (processFunc != null)) {
+ processFunc(row);
+ }
+
+ if (colorCache != null) {
+ while (lastCached < src) {
+ colorCache.insert(data[lastCached]);
+ lastCached++;
+ }
+ }
+ }
+ } else { // Not reached
+ return false;
+ }
+ }
+
+ // Process the remaining rows corresponding to last row-block.
+ if (processFunc != null) {
+ processFunc(row);
+ }
+
+ if (br.isEOS && src < srcEnd) {
+ return false;
+ }
+
+ _lastPixel = src;
+
+ return true;
+ }
+
+ /**
+ * Row-processing for the special case when alpha data contains only one
+ * transform (color indexing), and trivial non-green literals.
+ */
+ bool _is8bOptimizable() {
+ if (_colorCacheSize > 0) {
+ return false;
+ }
+ // When the Huffman tree contains only one symbol, we can skip the
+ // call to ReadSymbol() for red/blue/alpha channels.
+ for (int i = 0; i < _numHtreeGroups; ++i) {
+ List<HuffmanTree> htrees = _htreeGroups[i].htrees;
+ if (htrees[_RED].numNodes > 1) {
+ return false;
+ }
+ if (htrees[_BLUE].numNodes > 1) {
+ return false;
+ }
+ if (htrees[_ALPHA].numNodes > 1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Special row-processing that only stores the alpha data.
+ */
+ void _extractAlphaRows(int row) {
+ final int numRows = row - _lastRow;
+ if (numRows <= 0) {
+ return; // Nothing to be done.
+ }
+
+ _applyInverseTransforms(numRows, webp.width * _lastRow);
+
+ // Extract alpha (which is stored in the green plane).
+ final int width = webp.width; // the final width (!= dec->width_)
+ final int cachePixs = width * numRows;
+
+ final int di = width * _lastRow;
+ InputBuffer src = InputBuffer(_pixels, offset: _argbCache);
+
+ for (int i = 0; i < cachePixs; ++i) {
+ _opaque[di + i] = (src[i] >> 8) & 0xff;
+ }
+
+ _lastRow = row;
+ }
+
+ bool _decodeAlphaData(int width, int height, int lastRow) {
+ int row = _lastPixel ~/ width;
+ int col = _lastPixel % width;
+
+ HTreeGroup htreeGroup = _getHtreeGroupForPos(col, row);
+ int pos = _lastPixel; // current position
+ final int end = width * height; // End of data
+ final int last = width * lastRow; // Last pixel to decode
+ final int lenCodeLimit = NUM_LITERAL_CODES + NUM_LENGTH_CODES;
+ final int mask = _huffmanMask;
+
+ while (!br.isEOS && pos < last) {
+ // Only update when changing tile.
+ if ((col & mask) == 0) {
+ htreeGroup = _getHtreeGroupForPos(col, row);
+ }
+
+ br.fillBitWindow();
+
+ int code = htreeGroup.htrees[_GREEN].readSymbol(br);
+ if (code < NUM_LITERAL_CODES) { // Literal
+ _pixels8[pos] = code;
+ ++pos;
+ ++col;
+ if (col >= width) {
+ col = 0;
+ ++row;
+ if (row % _NUM_ARGB_CACHE_ROWS == 0) {
+ _extractPalettedAlphaRows(row);
+ }
+ }
+ } else if (code < lenCodeLimit) { // Backward reference
+ final int lengthSym = code - NUM_LITERAL_CODES;
+ final int length = _getCopyLength(lengthSym);
+ final int distSymbol = htreeGroup.htrees[_DIST].readSymbol(br);
+
+ br.fillBitWindow();
+
+ int distCode = _getCopyDistance(distSymbol);
+ int dist = _planeCodeToDistance(width, distCode);
+
+ if (pos >= dist && end - pos >= length) {
+ for (int i = 0; i < length; ++i) {
+ _pixels8[pos + i] = _pixels8[pos + i - dist];
+ }
+ } else {
+ _lastPixel = pos;
+ return true;
+ }
+
+ pos += length;
+ col += length;
+
+ while (col >= width) {
+ col -= width;
+ ++row;
+ if (row % _NUM_ARGB_CACHE_ROWS == 0) {
+ _extractPalettedAlphaRows(row);
+ }
+ }
+
+ if (pos < last && (col & mask) != 0) {
+ htreeGroup = _getHtreeGroupForPos(col, row);
+ }
+ } else { // Not reached
+ return false;
+ }
+ }
+
+ // Process the remaining rows corresponding to last row-block.
+ _extractPalettedAlphaRows(row);
+
+ _lastPixel = pos;
+
+ return true;
+ }
+
+ void _extractPalettedAlphaRows(int row) {
+ final int numRows = row - _lastRow;
+ InputBuffer pIn = InputBuffer(_pixels8, offset: webp.width * _lastRow);
+ if (numRows > 0) {
+ _applyInverseTransformsAlpha(numRows, pIn);
+ }
+ _lastRow = row;
+ }
+
+ /**
+ * Special method for paletted alpha data.
+ */
+ void _applyInverseTransformsAlpha(int numRows, InputBuffer rows) {
+ final int startRow = _lastRow;
+ final int endRow = startRow + numRows;
+ InputBuffer rowsOut = InputBuffer(_opaque, offset: _ioWidth * startRow);
+ VP8LTransform transform = _transforms[0];
+
+ transform.colorIndexInverseTransformAlpha(startRow, endRow, rows, rowsOut);
+ }
+
+ /**
+ * Processes (transforms, scales & color-converts) the rows decoded after the
+ * last call.
+ */
+ //static int __count = 0;
+ void _processRows(int row) {
+ int rows = webp.width * _lastRow; // offset into _pixels
+ final int numRows = row - _lastRow;
+
+ if (numRows <= 0) {
+ return; // Nothing to be done.
+ }
+
+ _applyInverseTransforms(numRows, rows);
+
+ //int count = 0;
+ //int di = rows;
+ for (int y = 0, pi = _argbCache, dy = _lastRow; y < numRows; ++y, ++dy) {
+ for (int x = 0; x < webp.width; ++x, ++pi) {
+ int c = _pixels[pi];
+ image.setPixel(x, dy, c);
+ //int r = getRed(c);
+ //int g = getGreen(c);
+ //int b = getBlue(c);
+ //int a = getAlpha(c);
+ // rearrange the ARGB webp color to RGBA image color.
+ //image.setPixel(x, dy, getColor(r, g, b, a));
+ }
+ }
+
+ _lastRow = row;
+ }
+
+ void _applyInverseTransforms(int numRows, int rows) {
+ int n = _transforms.length;
+ final int cachePixs = webp.width * numRows;
+ final int startRow = _lastRow;
+ final int endRow = startRow + numRows;
+ int rowsIn = rows;
+ int rowsOut = _argbCache;
+
+ // Inverse transforms.
+ _pixels.setRange(rowsOut, rowsOut + cachePixs, _pixels, rowsIn);
+
+ while (n-- > 0) {
+ VP8LTransform transform = _transforms[n];
+ transform.inverseTransform(startRow, endRow, _pixels, rowsIn,
+ _pixels, rowsOut);
+ rowsIn = rowsOut;
+ }
+ }
+
+ bool _readHuffmanCodes(int xsize, int ysize, int colorCacheBits,
+ bool allowRecursion) {
+ Uint32List huffmanImage;
+ int numHtreeGroups = 1;
+
+ if (allowRecursion && br.readBits(1) != 0) {
+ // use meta Huffman codes.
+ final int huffmanPrecision = br.readBits(3) + 2;
+ final int huffmanXsize = _subSampleSize(xsize, huffmanPrecision);
+ final int huffmanYsize = _subSampleSize(ysize, huffmanPrecision);
+ final int huffmanPixs = huffmanXsize * huffmanYsize;
+
+ huffmanImage = _decodeImageStream(huffmanXsize, huffmanYsize, false);
+
+ _huffmanSubsampleBits = huffmanPrecision;
+
+ for (int i = 0; i < huffmanPixs; ++i) {
+ // The huffman data is stored in red and green bytes.
+ final int group = (huffmanImage[i] >> 8) & 0xffff;
+ huffmanImage[i] = group;
+ if (group >= numHtreeGroups) {
+ numHtreeGroups = group + 1;
+ }
+ }
+ }
+
+ assert(numHtreeGroups <= 0x10000);
+
+ List<HTreeGroup> htreeGroups = List<HTreeGroup>(numHtreeGroups);
+ for (int i = 0; i < numHtreeGroups; ++i) {
+ htreeGroups[i] = HTreeGroup();
+
+ for (int j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
+ int alphabetSize = ALPHABET_SIZE[j];
+ if (j == 0 && colorCacheBits > 0) {
+ alphabetSize += 1 << colorCacheBits;
+ }
+
+ if (!_readHuffmanCode(alphabetSize, htreeGroups[i].htrees[j])) {
+ return false;
+ }
+ }
+ }
+
+ // All OK. Finalize pointers and return.
+ _huffmanImage = huffmanImage;
+ _numHtreeGroups = numHtreeGroups;
+ _htreeGroups = htreeGroups;
+
+ return true;
+ }
+
+ bool _readHuffmanCode(int alphabetSize, HuffmanTree tree) {
+ bool ok = false;
+ final int simpleCode = br.readBits(1);
+
+ // Read symbols, codes & code lengths directly.
+ if (simpleCode != 0) {
+ List<int> symbols = [0, 0];
+ List<int> codes = [0, 0];
+ List<int> codeLengths = [0, 0];
+
+ final int numSymbols = br.readBits(1) + 1;
+ final int firstSymbolLenCode = br.readBits(1);
+
+ // The first code is either 1 bit or 8 bit code.
+ symbols[0] = br.readBits((firstSymbolLenCode == 0) ? 1 : 8);
+ codes[0] = 0;
+ codeLengths[0] = numSymbols - 1;
+
+ // The second code (if present), is always 8 bit long.
+ if (numSymbols == 2) {
+ symbols[1] = br.readBits(8);
+ codes[1] = 1;
+ codeLengths[1] = numSymbols - 1;
+ }
+
+ ok = tree.buildExplicit(codeLengths, codes, symbols,
+ alphabetSize, numSymbols);
+ } else {
+ // Decode Huffman-coded code lengths.
+ Int32List codeLengthCodeLengths =
+ new Int32List(_NUM_CODE_LENGTH_CODES);
+
+ final int numCodes = br.readBits(4) + 4;
+ if (numCodes > _NUM_CODE_LENGTH_CODES) {
+ return false;
+ }
+
+ Int32List codeLengths = Int32List(alphabetSize);
+
+ for (int i = 0; i < numCodes; ++i) {
+ codeLengthCodeLengths[_CODE_LENGTH_CODE_ORDER[i]] = br.readBits(3);
+ }
+
+ ok = _readHuffmanCodeLengths(codeLengthCodeLengths, alphabetSize,
+ codeLengths);
+
+ if (ok) {
+ ok = tree.buildImplicit(codeLengths, alphabetSize);
+ }
+ }
+
+ return ok;
+ }
+
+ bool _readHuffmanCodeLengths(List<int> codeLengthCodeLengths,
+ int numSymbols, List<int> codeLengths) {
+ //bool ok = false;
+ int symbol;
+ int max_symbol;
+ int prev_code_len = DEFAULT_CODE_LENGTH;
+ HuffmanTree tree = HuffmanTree();
+
+ if (!tree.buildImplicit(codeLengthCodeLengths, _NUM_CODE_LENGTH_CODES)) {
+ return false;
+ }
+
+ if (br.readBits(1) != 0) { // use length
+ final int length_nbits = 2 + 2 * br.readBits(3);
+ max_symbol = 2 + br.readBits(length_nbits);
+ if (max_symbol > numSymbols) {
+ return false;
+ }
+ } else {
+ max_symbol = numSymbols;
+ }
+
+ symbol = 0;
+ while (symbol < numSymbols) {
+ int code_len;
+ if (max_symbol-- == 0) {
+ break;
+ }
+
+ br.fillBitWindow();
+
+ code_len = tree.readSymbol(br);
+
+ if (code_len < _CODE_LENGTH_LITERALS) {
+ codeLengths[symbol++] = code_len;
+ if (code_len != 0) {
+ prev_code_len = code_len;
+ }
+ } else {
+ final bool usePrev = (code_len == _CODE_LENGTH_REPEAT_CODE);
+ final int slot = code_len - _CODE_LENGTH_LITERALS;
+ final int extra_bits = _CODE_LENGTH_EXTRA_BITS[slot];
+ final int repeat_offset = _CODE_LENGTH_REPEAT_OFFSETS[slot];
+ int repeat = br.readBits(extra_bits) + repeat_offset;
+
+ if (symbol + repeat > numSymbols) {
+ return false;
+ } else {
+ final int length = usePrev ? prev_code_len : 0;
+ while (repeat-- > 0) {
+ codeLengths[symbol++] = length;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ int _getCopyDistance(int distanceSymbol) {
+ if (distanceSymbol < 4) {
+ return distanceSymbol + 1;
+ }
+ int extraBits = (distanceSymbol - 2) >> 1;
+ int offset = (2 + (distanceSymbol & 1)) << extraBits;
+ return offset + br.readBits(extraBits) + 1;
+ }
+
+ int _getCopyLength(int lengthSymbol) {
+ // Length and distance prefixes are encoded the same way.
+ return _getCopyDistance(lengthSymbol);
+ }
+
+ int _planeCodeToDistance(int xsize, int planeCode) {
+ if (planeCode > _CODE_TO_PLANE_CODES) {
+ return planeCode - _CODE_TO_PLANE_CODES;
+ } else {
+ final int distCode = _CODE_TO_PLANE[planeCode - 1];
+ final int yoffset = distCode >> 4;
+ final int xoffset = 8 - (distCode & 0xf);
+ final int dist = yoffset * xsize + xoffset;
+ // dist<1 can happen if xsize is very small
+ return (dist >= 1) ? dist : 1;
+ }
+ }
+
+ /**
+ * Computes sampled size of 'size' when sampling using 'sampling bits'.
+ */
+ static int _subSampleSize(int size, int samplingBits) {
+ return (size + (1 << samplingBits) - 1) >> samplingBits;
+ }
+
+ /**
+ * For security reason, we need to remap the color map to span
+ * the total possible bundled values, and not just the num_colors.
+ */
+ bool _expandColorMap(int numColors, VP8LTransform transform) {
+ final int finalNumColors = 1 << (8 >> transform.bits);
+ Uint32List newColorMap = Uint32List(finalNumColors);
+ Uint8List data = Uint8List.view(transform.data.buffer);
+ Uint8List newData = Uint8List.view(newColorMap.buffer);
+
+ newColorMap[0] = transform.data[0];
+
+ int len = 4 * numColors;
+
+ int i;
+ for (i = 4; i < len; ++i) {
+ // Equivalent to AddPixelEq(), on a byte-basis.
+ newData[i] = (data[i] + newData[i - 4]) & 0xff;
+ }
+
+ for (len = 4 * finalNumColors; i < len; ++i) {
+ newData[i] = 0;
+ }
+
+ transform.data = newColorMap;
+
+ return true;
+ }
+
+ int _getMetaIndex(Uint32List image, int xsize, int bits, int x, int y) {
+ if (bits == 0) {
+ return 0;
+ }
+ return image[xsize * (y >> bits) + (x >> bits)];
+ }
+
+ HTreeGroup _getHtreeGroupForPos(int x, int y) {
+ int metaIndex = _getMetaIndex(_huffmanImage, _huffmanXsize,
+ _huffmanSubsampleBits, x, y);
+ if (_htreeGroups[metaIndex] == null) {
+ _htreeGroups[metaIndex] = HTreeGroup();
+ }
+ return _htreeGroups[metaIndex];
+ }
+
+ static const int _GREEN = 0;
+ static const int _RED = 1;
+ static const int _BLUE = 2;
+ static const int _ALPHA = 3;
+ static const int _DIST = 4;
+
+ static const int _NUM_ARGB_CACHE_ROWS = 16;
+
+ static const int _NUM_CODE_LENGTH_CODES = 19;
+
+ static const List<int> _CODE_LENGTH_CODE_ORDER = const [
+ 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
+
+ static const int _CODE_TO_PLANE_CODES = 120;
+ static const List<int> _CODE_TO_PLANE = const [
+ 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a,
+ 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a,
+ 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b,
+ 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03,
+ 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c,
+ 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e,
+ 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b,
+ 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f,
+ 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b,
+ 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41,
+ 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f,
+ 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70];
+
+ static const int _CODE_LENGTH_LITERALS = 16;
+ static const int _CODE_LENGTH_REPEAT_CODE = 16;
+ static const List<int> _CODE_LENGTH_EXTRA_BITS = const [2, 3, 7];
+ static const List<int> _CODE_LENGTH_REPEAT_OFFSETS = const [ 3, 3, 11 ];
+
+ static const List<int> ALPHABET_SIZE = const [
+ NUM_LITERAL_CODES + NUM_LENGTH_CODES,
+ NUM_LITERAL_CODES, NUM_LITERAL_CODES,
+ NUM_LITERAL_CODES, NUM_DISTANCE_CODES];
+
+ static const int VP8L_MAGIC_BYTE = 0x2f;
+ static const int VP8L_VERSION = 0;
+
+ int _lastPixel = 0;
+ int _lastRow = 0;
+
+ int _colorCacheSize = 0;
+ VP8LColorCache _colorCache;
+
+ int _huffmanMask = 0;
+ int _huffmanSubsampleBits = 0;
+ int _huffmanXsize = 0;
+ Uint32List _huffmanImage;
+ int _numHtreeGroups = 0;
+ List<HTreeGroup> _htreeGroups = [];
+ List<VP8LTransform> _transforms = [];
+ int _transformsSeen = 0;
+
+ Uint32List _pixels;
+ Uint8List _pixels8;
+ int _argbCache;
+
+ Uint8List _opaque;
+
+ int _ioWidth;
+ int _ioHeight;
+
+ static const int ARGB_BLACK = 0xff000000;
+ static const int MAX_CACHE_BITS = 11;
+ static const int HUFFMAN_CODES_PER_META_CODE = 5;
+
+ static const int DEFAULT_CODE_LENGTH = 8;
+ static const int MAX_ALLOWED_CODE_LENGTH = 15;
+
+ static const int NUM_LITERAL_CODES = 256;
+ static const int NUM_LENGTH_CODES = 24;
+ static const int NUM_DISTANCE_CODES = 40;
+ static const int CODE_LENGTH_CODES = 19;
+}
+
+@internal
+class InternalVP8L extends VP8L {
+ InternalVP8L(InputBuffer input, WebPInfo webp) : super(input, webp);
+
+ List<VP8LTransform> get transforms => _transforms;
+ Uint32List get pixels => _pixels;
+
+ Uint8List get opaque => _opaque;
+ set opaque(Uint8List value) => _opaque = value;
+
+ int get ioWidth => _ioWidth;
+ set ioWidth(int width) => _ioWidth = width;
+ int get ioHeight => _ioHeight;
+ set ioHeight(int height) => _ioHeight = height;
+
+ bool decodeImageData(data, int width, int height, int lastRow, processFunc) =>
+ _decodeImageData(data, width, height, lastRow, processFunc);
+
+ Uint32List decodeImageStream(int xsize, int ysize, bool isLevel0) =>
+ _decodeImageStream(xsize, ysize, isLevel0);
+
+ bool allocateInternalBuffers32b() => _allocateInternalBuffers32b();
+
+ bool allocateInternalBuffers8b() => _allocateInternalBuffers8b();
+
+ bool decodeAlphaData(int width, int height, int lastRow) =>
+ _decodeAlphaData(width, height, lastRow);
+
+ bool is8bOptimizable() => _is8bOptimizable();
+
+ void extractAlphaRows(int row) => _extractAlphaRows(row);
+
+ static int subSampleSize(int size, int samplingBits) => VP8L._subSampleSize(size, samplingBits);
+}
diff --git a/image/lib/src/formats/webp/vp8l_bit_reader.dart b/image/lib/src/formats/webp/vp8l_bit_reader.dart
old mode 100644
new mode 100755
index 716b0ac..0a6359b
--- a/image/lib/src/formats/webp/vp8l_bit_reader.dart
+++ b/image/lib/src/formats/webp/vp8l_bit_reader.dart
@@ -1,98 +1,98 @@
-import 'dart:typed_data';
-
-import '../../image_exception.dart';
-import '../../util/input_buffer.dart';
-
-class VP8LBitReader {
- int bitPos = 0;
-
- VP8LBitReader(this._input) {
- _buffer8 = new Uint8List.view(_buffer.buffer);
- _buffer8[0] = _input.readByte();
- _buffer8[1] = _input.readByte();
- _buffer8[2] = _input.readByte();
- _buffer8[3] = _input.readByte();
- _buffer8[4] = _input.readByte();
- _buffer8[5] = _input.readByte();
- _buffer8[6] = _input.readByte();
- _buffer8[7] = _input.readByte();
- }
-
- /**
- * Return the prefetched bits, so they can be looked up.
- */
- int prefetchBits() {
- int b2 = 0;
- if (bitPos < 32) {
- b2 = (_buffer[0] >> bitPos) +
- ((_buffer[1] & BIT_MASK[bitPos]) * (BIT_MASK[32 - bitPos] + 1));
- } else if (bitPos == 32) {
- b2 = _buffer[1];
- } else {
- b2 = _buffer[1] >> (bitPos - 32);
- }
- return b2;
- }
-
- bool get isEOS => (_input.isEOS && bitPos >= LBITS);
-
- /**
- * Advances the read buffer by 4 bytes to make room for reading next 32 bits.
- */
- void fillBitWindow() {
- if (bitPos >= WBITS) {
- _shiftBytes();
- }
- }
-
- /**
- * Reads the specified number of bits from Read Buffer.
- */
- int readBits(int numBits) {
- // Flag an error if end_of_stream or n_bits is more than allowed limit.
- if (!isEOS && numBits < MAX_NUM_BIT_READ) {
- //final int value = (buffer >> bitPos) & BIT_MASK[numBits];
- final int value = prefetchBits() & BIT_MASK[numBits];
- bitPos += numBits;
- _shiftBytes();
- return value;
- } else {
- throw new ImageException('Not enough data in input.');
- }
- }
-
- /**
- * If not at EOS, reload up to LBITS byte-by-byte
- */
- void _shiftBytes() {
- while (bitPos >= 8 && !_input.isEOS) {
- int b = _input.readByte();
- // buffer >>= 8
- _buffer[0] = (_buffer[0] >> 8) + ((_buffer[1] & 0xff) * 0x1000000);
- _buffer[1] >>= 8;
- // buffer |= b << (LBITS - 8)
- _buffer[1] |= b * 0x1000000;
- bitPos -= 8;
- }
- }
-
- InputBuffer _input;
- Uint32List _buffer = new Uint32List(2);
- Uint8List _buffer8;
-
- /// The number of bytes used for the bit buffer.
- static const int VALUE_SIZE = 8;
- static const int MAX_NUM_BIT_READ = 25;
- /// Number of bits prefetched.
- static const int LBITS = 64;
- /// Minimum number of bytes needed after fillBitWindow.
- static const int WBITS = 32;
- /// Number of bytes needed to store WBITS bits.
- static const int LOG8_WBITS = 4;
-
- static const List<int> BIT_MASK = const [
- 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383,
- 32767, 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607,
- 16777215, 33554431, 67108863, 134217727, 268435455, 536870911,
- 1073741823, 2147483647, 4294967295];
-}
+import 'dart:typed_data';
+
+import '../../image_exception.dart';
+import '../../util/input_buffer.dart';
+
+class VP8LBitReader {
+ int bitPos = 0;
+
+ VP8LBitReader(this._input) {
+ _buffer8 = Uint8List.view(_buffer.buffer);
+ _buffer8[0] = _input.readByte();
+ _buffer8[1] = _input.readByte();
+ _buffer8[2] = _input.readByte();
+ _buffer8[3] = _input.readByte();
+ _buffer8[4] = _input.readByte();
+ _buffer8[5] = _input.readByte();
+ _buffer8[6] = _input.readByte();
+ _buffer8[7] = _input.readByte();
+ }
+
+ /**
+ * Return the prefetched bits, so they can be looked up.
+ */
+ int prefetchBits() {
+ int b2 = 0;
+ if (bitPos < 32) {
+ b2 = (_buffer[0] >> bitPos) +
+ ((_buffer[1] & BIT_MASK[bitPos]) * (BIT_MASK[32 - bitPos] + 1));
+ } else if (bitPos == 32) {
+ b2 = _buffer[1];
+ } else {
+ b2 = _buffer[1] >> (bitPos - 32);
+ }
+ return b2;
+ }
+
+ bool get isEOS => (_input.isEOS && bitPos >= LBITS);
+
+ /**
+ * Advances the read buffer by 4 bytes to make room for reading next 32 bits.
+ */
+ void fillBitWindow() {
+ if (bitPos >= WBITS) {
+ _shiftBytes();
+ }
+ }
+
+ /**
+ * Reads the specified number of bits from Read Buffer.
+ */
+ int readBits(int numBits) {
+ // Flag an error if end_of_stream or n_bits is more than allowed limit.
+ if (!isEOS && numBits < MAX_NUM_BIT_READ) {
+ //final int value = (buffer >> bitPos) & BIT_MASK[numBits];
+ final int value = prefetchBits() & BIT_MASK[numBits];
+ bitPos += numBits;
+ _shiftBytes();
+ return value;
+ } else {
+ throw new ImageException('Not enough data in input.');
+ }
+ }
+
+ /**
+ * If not at EOS, reload up to LBITS byte-by-byte
+ */
+ void _shiftBytes() {
+ while (bitPos >= 8 && !_input.isEOS) {
+ int b = _input.readByte();
+ // buffer >>= 8
+ _buffer[0] = (_buffer[0] >> 8) + ((_buffer[1] & 0xff) * 0x1000000);
+ _buffer[1] >>= 8;
+ // buffer |= b << (LBITS - 8)
+ _buffer[1] |= b * 0x1000000;
+ bitPos -= 8;
+ }
+ }
+
+ InputBuffer _input;
+ Uint32List _buffer = Uint32List(2);
+ Uint8List _buffer8;
+
+ /// The number of bytes used for the bit buffer.
+ static const int VALUE_SIZE = 8;
+ static const int MAX_NUM_BIT_READ = 25;
+ /// Number of bits prefetched.
+ static const int LBITS = 64;
+ /// Minimum number of bytes needed after fillBitWindow.
+ static const int WBITS = 32;
+ /// Number of bytes needed to store WBITS bits.
+ static const int LOG8_WBITS = 4;
+
+ static const List<int> BIT_MASK = const [
+ 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383,
+ 32767, 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607,
+ 16777215, 33554431, 67108863, 134217727, 268435455, 536870911,
+ 1073741823, 2147483647, 4294967295];
+}
diff --git a/image/lib/src/formats/webp/vp8l_color_cache.dart b/image/lib/src/formats/webp/vp8l_color_cache.dart
old mode 100644
new mode 100755
index c4e04a2..2b3c41a
--- a/image/lib/src/formats/webp/vp8l_color_cache.dart
+++ b/image/lib/src/formats/webp/vp8l_color_cache.dart
@@ -1,22 +1,22 @@
-import 'dart:typed_data';
-
-class VP8LColorCache {
- final Uint32List colors; // color entries
- final int hashShift; // Hash shift: 32 - hash_bits.
-
- VP8LColorCache(int hashBits) :
- colors = new Uint32List(1 << hashBits),
- hashShift = 32 - hashBits;
-
- void insert(int argb) {
- final int a = (argb * _HASH_MUL) & 0xffffffff;
- final int key = (a >> hashShift);
- colors[key] = argb;
- }
-
- int lookup(int key) {
- return colors[key];
- }
-
- static const int _HASH_MUL = 0x1e35a7bd;
-}
+import 'dart:typed_data';
+
+class VP8LColorCache {
+ final Uint32List colors; // color entries
+ final int hashShift; // Hash shift: 32 - hash_bits.
+
+ VP8LColorCache(int hashBits) :
+ colors = Uint32List(1 << hashBits),
+ hashShift = 32 - hashBits;
+
+ void insert(int argb) {
+ final int a = (argb * _HASH_MUL) & 0xffffffff;
+ final int key = (a >> hashShift);
+ colors[key] = argb;
+ }
+
+ int lookup(int key) {
+ return colors[key];
+ }
+
+ static const int _HASH_MUL = 0x1e35a7bd;
+}
diff --git a/image/lib/src/formats/webp/vp8l_transform.dart b/image/lib/src/formats/webp/vp8l_transform.dart
old mode 100644
new mode 100755
index 8b683cf..340a83f
--- a/image/lib/src/formats/webp/vp8l_transform.dart
+++ b/image/lib/src/formats/webp/vp8l_transform.dart
@@ -1,472 +1,472 @@
-import 'dart:typed_data';
-
-import '../../internal/bit_operators.dart';
-import '../../util/input_buffer.dart';
-import 'vp8l.dart';
-
-class VP8LTransform {
- // enum VP8LImageTransformType
- static const int PREDICTOR_TRANSFORM = 0;
- static const int CROSS_COLOR_TRANSFORM = 1;
- static const int SUBTRACT_GREEN = 2;
- static const int COLOR_INDEXING_TRANSFORM = 3;
-
- int type = 0;
- int xsize = 0;
- int ysize = 0;
- Uint32List data;
- int bits = 0;
-
- void inverseTransform(int rowStart, int rowEnd,
- Uint32List inData,
- int rowsIn,
- Uint32List outData,
- int rowsOut) {
- final int width = xsize;
-
- switch (type) {
- case SUBTRACT_GREEN:
- addGreenToBlueAndRed(outData, rowsOut,
- rowsOut + (rowEnd - rowStart) * width);
- break;
- case PREDICTOR_TRANSFORM:
- predictorInverseTransform(rowStart, rowEnd, outData, rowsOut);
- if (rowEnd != ysize) {
- // The last predicted row in this iteration will be the top-pred row
- // for the first row in next iteration.
- int start = rowsOut - width;
- int end = start + width;
- int offset = rowsOut + (rowEnd - rowStart - 1) * width;
- outData.setRange(start, end, inData, offset);
- }
- break;
- case CROSS_COLOR_TRANSFORM:
- colorSpaceInverseTransform(rowStart, rowEnd, outData, rowsOut);
- break;
- case COLOR_INDEXING_TRANSFORM:
- if (rowsIn == rowsOut && bits > 0) {
- // Move packed pixels to the end of unpacked region, so that unpacking
- // can occur seamlessly.
- // Also, note that this is the only transform that applies on
- // the effective width of VP8LSubSampleSize(xsize_, bits_). All other
- // transforms work on effective width of xsize_.
- final int outStride = (rowEnd - rowStart) * width;
- final int inStride = (rowEnd - rowStart) *
- InternalVP8L.subSampleSize(xsize, bits);
-
- int src = rowsOut + outStride - inStride;
- outData.setRange(src, src + inStride, inData, rowsOut);
-
- colorIndexInverseTransform(rowStart, rowEnd, inData, src,
- outData, rowsOut);
- } else {
- colorIndexInverseTransform(rowStart, rowEnd, inData, rowsIn,
- outData, rowsOut);
- }
- break;
- }
- }
-
- void colorIndexInverseTransformAlpha(int yStart, int yEnd,
- InputBuffer src, InputBuffer dst) {
- final int bitsPerPixel = 8 >> bits;
- final int width = xsize;
- Uint32List colorMap = this.data;
- if (bitsPerPixel < 8) {
- final int pixelsPerByte = 1 << bits;
- final int countMask = pixelsPerByte - 1;
- final bit_mask = (1 << bitsPerPixel) - 1;
- for (int y = yStart; y < yEnd; ++y) {
- int packed_pixels = 0;
- for (int x = 0; x < width; ++x) {
- // We need to load fresh 'packed_pixels' once every
- // 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte
- // is a power of 2, so can just use a mask for that, instead of
- // decrementing a counter.
- if ((x & countMask) == 0) {
- packed_pixels = _getAlphaIndex(src[0]);
- src.offset++;
- }
- int p = _getAlphaValue(colorMap[packed_pixels & bit_mask]);;
- dst[0] = p;
- dst.offset++;
- packed_pixels >>= bitsPerPixel;
- }
- }
- } else {
- for (int y = yStart; y < yEnd; ++y) {
- for (int x = 0; x < width; ++x) {
- int index = _getAlphaIndex(src[0]);
- src.offset++;
- dst[0] = _getAlphaValue(colorMap[index]);
- dst.offset++;
- }
- }
- }
- }
-
- void colorIndexInverseTransform(int yStart, int yEnd,
- Uint32List inData, int src,
- Uint32List outData, int dst) {
- final int bitsPerPixel = 8 >> bits;
- final int width = xsize;
- Uint32List colorMap = this.data;
- if (bitsPerPixel < 8) {
- final int pixelsPerByte = 1 << bits;
- final int countMask = pixelsPerByte - 1;
- final bit_mask = (1 << bitsPerPixel) - 1;
- for (int y = yStart; y < yEnd; ++y) {
- int packed_pixels = 0;
- for (int x = 0; x < width; ++x) {
- // We need to load fresh 'packed_pixels' once every
- // 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte
- // is a power of 2, so can just use a mask for that, instead of
- // decrementing a counter.
- if ((x & countMask) == 0) {
- packed_pixels = _getARGBIndex(inData[src++]);
- }
- outData[dst++] = _getARGBValue(colorMap[packed_pixels & bit_mask]);
- packed_pixels >>= bitsPerPixel;
- }
- }
- } else {
- for (int y = yStart; y < yEnd; ++y) {
- for (int x = 0; x < width; ++x) {
- outData[dst++] = _getARGBValue(colorMap[_getARGBIndex(inData[src++])]);
- }
- }
- }
- }
-
- /**
- * Color space inverse transform.
- */
- void colorSpaceInverseTransform(int yStart, int yEnd, Uint32List outData,
- int data) {
- final int width = xsize;
- final int mask = (1 << bits) - 1;
- final int tilesPerRow = InternalVP8L.subSampleSize(width, bits);
- int y = yStart;
- int predRow = (y >> bits) * tilesPerRow; //this.data +
-
- while (y < yEnd) {
- int pred = predRow; // this.data+
- _VP8LMultipliers m = new _VP8LMultipliers();
-
- for (int x = 0; x < width; ++x) {
- if ((x & mask) == 0) {
- m.colorCode = this.data[pred++];
- }
-
- outData[data + x] = m.transformColor(outData[data + x], true);
- }
-
- data += width;
- ++y;
-
- if ((y & mask) == 0) {
- predRow += tilesPerRow;;
- }
- }
- }
-
- /**
- * Inverse prediction.
- */
- void predictorInverseTransform(int yStart, int yEnd, Uint32List outData,
- int data) {
- final int width = xsize;
- if (yStart == 0) { // First Row follows the L (mode=1) mode.
- final int pred0 = _predictor0(outData, outData[data - 1], 0);
- _addPixelsEq(outData, data, pred0);
- for (int x = 1; x < width; ++x) {
- final int pred1 = _predictor1(outData, outData[data + x - 1], 0);
- _addPixelsEq(outData, data + x, pred1);
- }
- data += width;
- ++yStart;
- }
-
- int y = yStart;
- final int mask = (1 << bits) - 1;
- final int tilesPerRow = InternalVP8L.subSampleSize(width, bits);
- int predModeBase = (y >> bits) * tilesPerRow; //this.data +
-
- while (y < yEnd) {
- final int pred2 = _predictor2(outData, outData[data - 1], data - width);
- int predModeSrc = predModeBase; //this.data +
-
- // First pixel follows the T (mode=2) mode.
- _addPixelsEq(outData, data, pred2);
-
- // .. the rest:
- int k = (this.data[predModeSrc++] >> 8) & 0xf;
-
- var predFunc = PREDICTORS[k];
- for (int x = 1; x < width; ++x) {
- if ((x & mask) == 0) { // start of tile. Read predictor function.
- int k = ((this.data[predModeSrc++]) >> 8) & 0xf;
- predFunc = PREDICTORS[k];
- }
- int d = outData[data + x - 1];
- int pred = predFunc(outData, d, data + x - width);
- _addPixelsEq(outData, data + x, pred);
- }
-
- data += width;
- ++y;
-
- if ((y & mask) == 0) { // Use the same mask, since tiles are squares.
- predModeBase += tilesPerRow;
- }
- }
- }
-
- /**
- * Add green to blue and red channels (i.e. perform the inverse transform of
- * 'subtract green').
- */
- void addGreenToBlueAndRed(Uint32List pixels, int data, int dataEnd) {
- while (data < dataEnd) {
- final int argb = pixels[data];
- final int green = ((argb >> 8) & 0xff);
- int redBlue = (argb & 0x00ff00ff);
- redBlue += (green << 16) | green;
- redBlue &= 0x00ff00ff;
- pixels[data++] = (argb & 0xff00ff00) | redBlue;
- }
- }
-
- static int _getARGBIndex(int idx) {
- return (idx >> 8) & 0xff;
- }
-
- static int _getAlphaIndex(int idx) {
- return idx;
- }
-
- static int _getARGBValue(int val) {
- return val;
- }
-
- static int _getAlphaValue(int val) {
- return (val >> 8) & 0xff;
- }
-
- /**
- * In-place sum of each component with mod 256.
- */
- static void _addPixelsEq(Uint32List pixels, int a, int b) {
- int pa = pixels[a];
- final int alphaAndGreen = (pa & 0xff00ff00) + (b & 0xff00ff00);
- final int redAndBlue = (pa & 0x00ff00ff) + (b & 0x00ff00ff);
- pixels[a] = (alphaAndGreen & 0xff00ff00) | (redAndBlue & 0x00ff00ff);
- }
-
- static int _average2(int a0, int a1) {
- return (((a0 ^ a1) & 0xfefefefe) >> 1) + (a0 & a1);
- }
-
- static int _average3(int a0, int a1, int a2) {
- return _average2(_average2(a0, a2), a1);
- }
-
- static int _average4(int a0, int a1, int a2, int a3) {
- return _average2(_average2(a0, a1), _average2(a2, a3));
- }
-
- /**
- * Return 0, when a is a negative integer.
- * Return 255, when a is positive.
- */
- static int _clip255(int a) {
- if (a < 0) {
- return 0;
- }
- if (a > 255) {
- return 255;
- }
- return a;
- }
-
- static int _addSubtractComponentFull(int a, int b, int c) {
- return _clip255(a + b - c);
- }
-
-
- static int _clampedAddSubtractFull(int c0, int c1, int c2) {
- final int a = _addSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24);
- final int r = _addSubtractComponentFull((c0 >> 16) & 0xff,
- (c1 >> 16) & 0xff,
- (c2 >> 16) & 0xff);
- final int g = _addSubtractComponentFull((c0 >> 8) & 0xff,
- (c1 >> 8) & 0xff,
- (c2 >> 8) & 0xff);
- final int b = _addSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff);
- return (a << 24) | (r << 16) | (g << 8) | b;
- }
-
- static int _addSubtractComponentHalf(int a, int b) {
- return _clip255(a + (a - b) ~/ 2);
- }
-
- static int _clampedAddSubtractHalf(int c0, int c1, int c2) {
- final int avg = _average2(c0, c1);
- final int a = _addSubtractComponentHalf(avg >> 24, c2 >> 24);
- final int r = _addSubtractComponentHalf((avg >> 16) & 0xff, (c2 >> 16) & 0xff);
- final int g = _addSubtractComponentHalf((avg >> 8) & 0xff, (c2 >> 8) & 0xff);
- final int b = _addSubtractComponentHalf((avg >> 0) & 0xff, (c2 >> 0) & 0xff);
- return (a << 24) | (r << 16) | (g << 8) | b;
- }
-
- static int _sub3(int a, int b, int c) {
- final int pb = b - c;
- final int pa = a - c;
- return pb.abs() - pa.abs();
- }
-
- static int _select(int a, int b, int c) {
- final int pa_minus_pb =
- _sub3((a >> 24) , (b >> 24) , (c >> 24) ) +
- _sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) +
- _sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) +
- _sub3((a ) & 0xff, (b ) & 0xff, (c ) & 0xff);
- return (pa_minus_pb <= 0) ? a : b;
- }
-
- //--------------------------------------------------------------------------
- // Predictors
-
- static int _predictor0(Uint32List pixels, int left, int top) {
- return VP8L.ARGB_BLACK;
- }
-
- static int _predictor1(Uint32List pixels, int left, int top) {
- return left;
- }
-
- static int _predictor2(Uint32List pixels, int left, int top) {
- return pixels[top];
- }
-
- static int _predictor3(Uint32List pixels, int left, int top) {
- return pixels[top + 1];
- }
-
- static int _predictor4(Uint32List pixels, int left, int top) {
- return pixels[top -1];
- }
-
- static int _predictor5(Uint32List pixels, int left, int top) {
- return _average3(left, pixels[top], pixels[top + 1]);
- }
-
- static int _predictor6(Uint32List pixels, int left, int top) {
- return _average2(left, pixels[top - 1]);
- }
-
- static int _predictor7(Uint32List pixels, int left, int top) {
- return _average2(left, pixels[top]);
- }
-
- static int _predictor8(Uint32List pixels, int left, int top) {
- return _average2(pixels[top -1], pixels[top]);
- }
-
- static int _predictor9(Uint32List pixels, int left, int top) {
- return _average2(pixels[top], pixels[top + 1]);
- }
-
- static int _predictor10(Uint32List pixels, int left, int top) {
- return _average4(left, pixels[top -1], pixels[top], pixels[top + 1]);
- }
-
- static int _predictor11(Uint32List pixels, int left, int top) {
- return _select(pixels[top], left, pixels[top - 1]);
- }
-
- static int _predictor12(Uint32List pixels, int left, int top) {
- return _clampedAddSubtractFull(left, pixels[top], pixels[top - 1]);
- }
-
- static int _predictor13(Uint32List pixels, int left, int top) {
- return _clampedAddSubtractHalf(left, pixels[top], pixels[top - 1]);
- }
-
- static final List PREDICTORS = [
- _predictor0, _predictor1, _predictor2, _predictor3,
- _predictor4, _predictor5, _predictor6, _predictor7,
- _predictor8, _predictor9, _predictor10, _predictor11,
- _predictor12, _predictor13,
- _predictor0, _predictor0 ];
-}
-
-class _VP8LMultipliers {
- final Uint8List data = new Uint8List(3);
-
- // Note: the members are uint8_t, so that any negative values are
- // automatically converted to "mod 256" values.
- int get greenToRed => data[0];
-
- set greenToRed(int m) => data[0] = m;
-
- int get greenToBlue => data[1];
-
- set greenToBlue(int m) => data[1] = m;
-
- int get redToBlue => data[2];
-
- set redToBlue(int m) => data[2] = m;
-
- void clear() {
- data[0] = 0;
- data[1] = 0;
- data[2] = 0;
- }
-
- void set colorCode(int colorCode) {
- data[0] = (colorCode >> 0) & 0xff;
- data[1] = (colorCode >> 8) & 0xff;
- data[2] = (colorCode >> 16) & 0xff;
- }
-
- int get colorCode => 0xff000000 |
- (data[2] << 16) |
- (data[1] << 8) |
- data[0];
-
- int transformColor(int argb, bool inverse) {
- final int green = (argb >> 8) & 0xff;
- final int red = (argb >> 16) & 0xff;
- int newRed = red;
- int newBlue = argb & 0xff;
-
- if (inverse) {
- int g = colorTransformDelta(greenToRed, green);
- newRed = (newRed + g) & 0xffffffff;
- newRed &= 0xff;
- newBlue = (newBlue + colorTransformDelta(greenToBlue, green)) & 0xffffffff;
- newBlue = (newBlue + colorTransformDelta(redToBlue, newRed)) & 0xffffffff;
- newBlue &= 0xff;
- } else {
- newRed -= colorTransformDelta(greenToRed, green);
- newRed &= 0xff;
- newBlue -= colorTransformDelta(greenToBlue, green);
- newBlue -= colorTransformDelta(redToBlue, red);
- newBlue &= 0xff;
- }
-
- int c = (argb & 0xff00ff00) | ((newRed << 16) & 0xffffffff) | (newBlue);
- return c;
- }
-
- int colorTransformDelta(int colorPred, int color) {
- // There's a bug in dart2js (issue 16497) that requires I do this a bit
- // convoluted to avoid having the optimizer butcher the code.
- int a = uint8ToInt8(colorPred);
- int b = uint8ToInt8(color);
- int d = int32ToUint32(a * b);
- return d >> 5;
- }
-}
-
+import 'dart:typed_data';
+
+import '../../internal/bit_operators.dart';
+import '../../util/input_buffer.dart';
+import 'vp8l.dart';
+
+class VP8LTransform {
+ // enum VP8LImageTransformType
+ static const int PREDICTOR_TRANSFORM = 0;
+ static const int CROSS_COLOR_TRANSFORM = 1;
+ static const int SUBTRACT_GREEN = 2;
+ static const int COLOR_INDEXING_TRANSFORM = 3;
+
+ int type = 0;
+ int xsize = 0;
+ int ysize = 0;
+ Uint32List data;
+ int bits = 0;
+
+ void inverseTransform(int rowStart, int rowEnd,
+ Uint32List inData,
+ int rowsIn,
+ Uint32List outData,
+ int rowsOut) {
+ final int width = xsize;
+
+ switch (type) {
+ case SUBTRACT_GREEN:
+ addGreenToBlueAndRed(outData, rowsOut,
+ rowsOut + (rowEnd - rowStart) * width);
+ break;
+ case PREDICTOR_TRANSFORM:
+ predictorInverseTransform(rowStart, rowEnd, outData, rowsOut);
+ if (rowEnd != ysize) {
+ // The last predicted row in this iteration will be the top-pred row
+ // for the first row in next iteration.
+ int start = rowsOut - width;
+ int end = start + width;
+ int offset = rowsOut + (rowEnd - rowStart - 1) * width;
+ outData.setRange(start, end, inData, offset);
+ }
+ break;
+ case CROSS_COLOR_TRANSFORM:
+ colorSpaceInverseTransform(rowStart, rowEnd, outData, rowsOut);
+ break;
+ case COLOR_INDEXING_TRANSFORM:
+ if (rowsIn == rowsOut && bits > 0) {
+ // Move packed pixels to the end of unpacked region, so that unpacking
+ // can occur seamlessly.
+ // Also, note that this is the only transform that applies on
+ // the effective width of VP8LSubSampleSize(xsize_, bits_). All other
+ // transforms work on effective width of xsize_.
+ final int outStride = (rowEnd - rowStart) * width;
+ final int inStride = (rowEnd - rowStart) *
+ InternalVP8L.subSampleSize(xsize, bits);
+
+ int src = rowsOut + outStride - inStride;
+ outData.setRange(src, src + inStride, inData, rowsOut);
+
+ colorIndexInverseTransform(rowStart, rowEnd, inData, src,
+ outData, rowsOut);
+ } else {
+ colorIndexInverseTransform(rowStart, rowEnd, inData, rowsIn,
+ outData, rowsOut);
+ }
+ break;
+ }
+ }
+
+ void colorIndexInverseTransformAlpha(int yStart, int yEnd,
+ InputBuffer src, InputBuffer dst) {
+ final int bitsPerPixel = 8 >> bits;
+ final int width = xsize;
+ Uint32List colorMap = this.data;
+ if (bitsPerPixel < 8) {
+ final int pixelsPerByte = 1 << bits;
+ final int countMask = pixelsPerByte - 1;
+ final bit_mask = (1 << bitsPerPixel) - 1;
+ for (int y = yStart; y < yEnd; ++y) {
+ int packed_pixels = 0;
+ for (int x = 0; x < width; ++x) {
+ // We need to load fresh 'packed_pixels' once every
+ // 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte
+ // is a power of 2, so can just use a mask for that, instead of
+ // decrementing a counter.
+ if ((x & countMask) == 0) {
+ packed_pixels = _getAlphaIndex(src[0]);
+ src.offset++;
+ }
+ int p = _getAlphaValue(colorMap[packed_pixels & bit_mask]);;
+ dst[0] = p;
+ dst.offset++;
+ packed_pixels >>= bitsPerPixel;
+ }
+ }
+ } else {
+ for (int y = yStart; y < yEnd; ++y) {
+ for (int x = 0; x < width; ++x) {
+ int index = _getAlphaIndex(src[0]);
+ src.offset++;
+ dst[0] = _getAlphaValue(colorMap[index]);
+ dst.offset++;
+ }
+ }
+ }
+ }
+
+ void colorIndexInverseTransform(int yStart, int yEnd,
+ Uint32List inData, int src,
+ Uint32List outData, int dst) {
+ final int bitsPerPixel = 8 >> bits;
+ final int width = xsize;
+ Uint32List colorMap = this.data;
+ if (bitsPerPixel < 8) {
+ final int pixelsPerByte = 1 << bits;
+ final int countMask = pixelsPerByte - 1;
+ final bit_mask = (1 << bitsPerPixel) - 1;
+ for (int y = yStart; y < yEnd; ++y) {
+ int packed_pixels = 0;
+ for (int x = 0; x < width; ++x) {
+ // We need to load fresh 'packed_pixels' once every
+ // 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte
+ // is a power of 2, so can just use a mask for that, instead of
+ // decrementing a counter.
+ if ((x & countMask) == 0) {
+ packed_pixels = _getARGBIndex(inData[src++]);
+ }
+ outData[dst++] = _getARGBValue(colorMap[packed_pixels & bit_mask]);
+ packed_pixels >>= bitsPerPixel;
+ }
+ }
+ } else {
+ for (int y = yStart; y < yEnd; ++y) {
+ for (int x = 0; x < width; ++x) {
+ outData[dst++] = _getARGBValue(colorMap[_getARGBIndex(inData[src++])]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Color space inverse transform.
+ */
+ void colorSpaceInverseTransform(int yStart, int yEnd, Uint32List outData,
+ int data) {
+ final int width = xsize;
+ final int mask = (1 << bits) - 1;
+ final int tilesPerRow = InternalVP8L.subSampleSize(width, bits);
+ int y = yStart;
+ int predRow = (y >> bits) * tilesPerRow; //this.data +
+
+ while (y < yEnd) {
+ int pred = predRow; // this.data+
+ _VP8LMultipliers m = _VP8LMultipliers();
+
+ for (int x = 0; x < width; ++x) {
+ if ((x & mask) == 0) {
+ m.colorCode = this.data[pred++];
+ }
+
+ outData[data + x] = m.transformColor(outData[data + x], true);
+ }
+
+ data += width;
+ ++y;
+
+ if ((y & mask) == 0) {
+ predRow += tilesPerRow;;
+ }
+ }
+ }
+
+ /**
+ * Inverse prediction.
+ */
+ void predictorInverseTransform(int yStart, int yEnd, Uint32List outData,
+ int data) {
+ final int width = xsize;
+ if (yStart == 0) { // First Row follows the L (mode=1) mode.
+ final int pred0 = _predictor0(outData, outData[data - 1], 0);
+ _addPixelsEq(outData, data, pred0);
+ for (int x = 1; x < width; ++x) {
+ final int pred1 = _predictor1(outData, outData[data + x - 1], 0);
+ _addPixelsEq(outData, data + x, pred1);
+ }
+ data += width;
+ ++yStart;
+ }
+
+ int y = yStart;
+ final int mask = (1 << bits) - 1;
+ final int tilesPerRow = InternalVP8L.subSampleSize(width, bits);
+ int predModeBase = (y >> bits) * tilesPerRow; //this.data +
+
+ while (y < yEnd) {
+ final int pred2 = _predictor2(outData, outData[data - 1], data - width);
+ int predModeSrc = predModeBase; //this.data +
+
+ // First pixel follows the T (mode=2) mode.
+ _addPixelsEq(outData, data, pred2);
+
+ // .. the rest:
+ int k = (this.data[predModeSrc++] >> 8) & 0xf;
+
+ var predFunc = PREDICTORS[k];
+ for (int x = 1; x < width; ++x) {
+ if ((x & mask) == 0) { // start of tile. Read predictor function.
+ int k = ((this.data[predModeSrc++]) >> 8) & 0xf;
+ predFunc = PREDICTORS[k];
+ }
+ int d = outData[data + x - 1];
+ int pred = predFunc(outData, d, data + x - width);
+ _addPixelsEq(outData, data + x, pred);
+ }
+
+ data += width;
+ ++y;
+
+ if ((y & mask) == 0) { // Use the same mask, since tiles are squares.
+ predModeBase += tilesPerRow;
+ }
+ }
+ }
+
+ /**
+ * Add green to blue and red channels (i.e. perform the inverse transform of
+ * 'subtract green').
+ */
+ void addGreenToBlueAndRed(Uint32List pixels, int data, int dataEnd) {
+ while (data < dataEnd) {
+ final int argb = pixels[data];
+ final int green = ((argb >> 8) & 0xff);
+ int redBlue = (argb & 0x00ff00ff);
+ redBlue += (green << 16) | green;
+ redBlue &= 0x00ff00ff;
+ pixels[data++] = (argb & 0xff00ff00) | redBlue;
+ }
+ }
+
+ static int _getARGBIndex(int idx) {
+ return (idx >> 8) & 0xff;
+ }
+
+ static int _getAlphaIndex(int idx) {
+ return idx;
+ }
+
+ static int _getARGBValue(int val) {
+ return val;
+ }
+
+ static int _getAlphaValue(int val) {
+ return (val >> 8) & 0xff;
+ }
+
+ /**
+ * In-place sum of each component with mod 256.
+ */
+ static void _addPixelsEq(Uint32List pixels, int a, int b) {
+ int pa = pixels[a];
+ final int alphaAndGreen = (pa & 0xff00ff00) + (b & 0xff00ff00);
+ final int redAndBlue = (pa & 0x00ff00ff) + (b & 0x00ff00ff);
+ pixels[a] = (alphaAndGreen & 0xff00ff00) | (redAndBlue & 0x00ff00ff);
+ }
+
+ static int _average2(int a0, int a1) {
+ return (((a0 ^ a1) & 0xfefefefe) >> 1) + (a0 & a1);
+ }
+
+ static int _average3(int a0, int a1, int a2) {
+ return _average2(_average2(a0, a2), a1);
+ }
+
+ static int _average4(int a0, int a1, int a2, int a3) {
+ return _average2(_average2(a0, a1), _average2(a2, a3));
+ }
+
+ /**
+ * Return 0, when a is a negative integer.
+ * Return 255, when a is positive.
+ */
+ static int _clip255(int a) {
+ if (a < 0) {
+ return 0;
+ }
+ if (a > 255) {
+ return 255;
+ }
+ return a;
+ }
+
+ static int _addSubtractComponentFull(int a, int b, int c) {
+ return _clip255(a + b - c);
+ }
+
+
+ static int _clampedAddSubtractFull(int c0, int c1, int c2) {
+ final int a = _addSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24);
+ final int r = _addSubtractComponentFull((c0 >> 16) & 0xff,
+ (c1 >> 16) & 0xff,
+ (c2 >> 16) & 0xff);
+ final int g = _addSubtractComponentFull((c0 >> 8) & 0xff,
+ (c1 >> 8) & 0xff,
+ (c2 >> 8) & 0xff);
+ final int b = _addSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff);
+ return (a << 24) | (r << 16) | (g << 8) | b;
+ }
+
+ static int _addSubtractComponentHalf(int a, int b) {
+ return _clip255(a + (a - b) ~/ 2);
+ }
+
+ static int _clampedAddSubtractHalf(int c0, int c1, int c2) {
+ final int avg = _average2(c0, c1);
+ final int a = _addSubtractComponentHalf(avg >> 24, c2 >> 24);
+ final int r = _addSubtractComponentHalf((avg >> 16) & 0xff, (c2 >> 16) & 0xff);
+ final int g = _addSubtractComponentHalf((avg >> 8) & 0xff, (c2 >> 8) & 0xff);
+ final int b = _addSubtractComponentHalf((avg >> 0) & 0xff, (c2 >> 0) & 0xff);
+ return (a << 24) | (r << 16) | (g << 8) | b;
+ }
+
+ static int _sub3(int a, int b, int c) {
+ final int pb = b - c;
+ final int pa = a - c;
+ return pb.abs() - pa.abs();
+ }
+
+ static int _select(int a, int b, int c) {
+ final int pa_minus_pb =
+ _sub3((a >> 24) , (b >> 24) , (c >> 24) ) +
+ _sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) +
+ _sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) +
+ _sub3((a ) & 0xff, (b ) & 0xff, (c ) & 0xff);
+ return (pa_minus_pb <= 0) ? a : b;
+ }
+
+ //--------------------------------------------------------------------------
+ // Predictors
+
+ static int _predictor0(Uint32List pixels, int left, int top) {
+ return VP8L.ARGB_BLACK;
+ }
+
+ static int _predictor1(Uint32List pixels, int left, int top) {
+ return left;
+ }
+
+ static int _predictor2(Uint32List pixels, int left, int top) {
+ return pixels[top];
+ }
+
+ static int _predictor3(Uint32List pixels, int left, int top) {
+ return pixels[top + 1];
+ }
+
+ static int _predictor4(Uint32List pixels, int left, int top) {
+ return pixels[top -1];
+ }
+
+ static int _predictor5(Uint32List pixels, int left, int top) {
+ return _average3(left, pixels[top], pixels[top + 1]);
+ }
+
+ static int _predictor6(Uint32List pixels, int left, int top) {
+ return _average2(left, pixels[top - 1]);
+ }
+
+ static int _predictor7(Uint32List pixels, int left, int top) {
+ return _average2(left, pixels[top]);
+ }
+
+ static int _predictor8(Uint32List pixels, int left, int top) {
+ return _average2(pixels[top -1], pixels[top]);
+ }
+
+ static int _predictor9(Uint32List pixels, int left, int top) {
+ return _average2(pixels[top], pixels[top + 1]);
+ }
+
+ static int _predictor10(Uint32List pixels, int left, int top) {
+ return _average4(left, pixels[top -1], pixels[top], pixels[top + 1]);
+ }
+
+ static int _predictor11(Uint32List pixels, int left, int top) {
+ return _select(pixels[top], left, pixels[top - 1]);
+ }
+
+ static int _predictor12(Uint32List pixels, int left, int top) {
+ return _clampedAddSubtractFull(left, pixels[top], pixels[top - 1]);
+ }
+
+ static int _predictor13(Uint32List pixels, int left, int top) {
+ return _clampedAddSubtractHalf(left, pixels[top], pixels[top - 1]);
+ }
+
+ static final List PREDICTORS = [
+ _predictor0, _predictor1, _predictor2, _predictor3,
+ _predictor4, _predictor5, _predictor6, _predictor7,
+ _predictor8, _predictor9, _predictor10, _predictor11,
+ _predictor12, _predictor13,
+ _predictor0, _predictor0 ];
+}
+
+class _VP8LMultipliers {
+ final Uint8List data = Uint8List(3);
+
+ // Note: the members are uint8_t, so that any negative values are
+ // automatically converted to "mod 256" values.
+ int get greenToRed => data[0];
+
+ set greenToRed(int m) => data[0] = m;
+
+ int get greenToBlue => data[1];
+
+ set greenToBlue(int m) => data[1] = m;
+
+ int get redToBlue => data[2];
+
+ set redToBlue(int m) => data[2] = m;
+
+ void clear() {
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ }
+
+ void set colorCode(int colorCode) {
+ data[0] = (colorCode >> 0) & 0xff;
+ data[1] = (colorCode >> 8) & 0xff;
+ data[2] = (colorCode >> 16) & 0xff;
+ }
+
+ int get colorCode => 0xff000000 |
+ (data[2] << 16) |
+ (data[1] << 8) |
+ data[0];
+
+ int transformColor(int argb, bool inverse) {
+ final int green = (argb >> 8) & 0xff;
+ final int red = (argb >> 16) & 0xff;
+ int newRed = red;
+ int newBlue = argb & 0xff;
+
+ if (inverse) {
+ int g = colorTransformDelta(greenToRed, green);
+ newRed = (newRed + g) & 0xffffffff;
+ newRed &= 0xff;
+ newBlue = (newBlue + colorTransformDelta(greenToBlue, green)) & 0xffffffff;
+ newBlue = (newBlue + colorTransformDelta(redToBlue, newRed)) & 0xffffffff;
+ newBlue &= 0xff;
+ } else {
+ newRed -= colorTransformDelta(greenToRed, green);
+ newRed &= 0xff;
+ newBlue -= colorTransformDelta(greenToBlue, green);
+ newBlue -= colorTransformDelta(redToBlue, red);
+ newBlue &= 0xff;
+ }
+
+ int c = (argb & 0xff00ff00) | ((newRed << 16) & 0xffffffff) | (newBlue);
+ return c;
+ }
+
+ int colorTransformDelta(int colorPred, int color) {
+ // There's a bug in dart2js (issue 16497) that requires I do this a bit
+ // convoluted to avoid having the optimizer butcher the code.
+ int a = uint8ToInt8(colorPred);
+ int b = uint8ToInt8(color);
+ int d = int32ToUint32(a * b);
+ return d >> 5;
+ }
+}
+
diff --git a/image/lib/src/formats/webp/webp_alpha.dart b/image/lib/src/formats/webp/webp_alpha.dart
old mode 100644
new mode 100755
index 7d23c43..7767232
--- a/image/lib/src/formats/webp/webp_alpha.dart
+++ b/image/lib/src/formats/webp/webp_alpha.dart
@@ -1,146 +1,146 @@
-import 'dart:typed_data';
-
-import '../../util/input_buffer.dart';
-import 'vp8l.dart';
-import 'vp8l_transform.dart';
-import 'webp_filters.dart';
-import 'webp_info.dart';
-
-class WebPAlpha {
- InputBuffer input;
- int width = 0;
- int height = 0;
- int method = 0;
- int filter = 0;
- int preProcessing = 0;
- int rsrv = 1;
- bool isAlphaDecoded = false;
-
- WebPAlpha(this.input, this.width, this.height) {
- int b = input.readByte();
- method = b & 0x03;
- filter = (b >> 2) & 0x03;
- preProcessing = (b >> 4) & 0x03;
- rsrv = (b >> 6) & 0x03;
-
- if (isValid) {
- if (method == ALPHA_NO_COMPRESSION) {
- final int alphaDecodedSize = width * height;
- if (input.length < alphaDecodedSize) {
- rsrv = 1;
- }
- } else if (method == ALPHA_LOSSLESS_COMPRESSION) {
- if (!_decodeAlphaHeader()) {
- rsrv = 1;
- }
- } else {
- rsrv = 1;
- }
- }
- }
-
- bool get isValid {
- if (method < ALPHA_NO_COMPRESSION ||
- method > ALPHA_LOSSLESS_COMPRESSION ||
- filter >= WebPFilters.FILTER_LAST ||
- preProcessing > ALPHA_PREPROCESSED_LEVELS ||
- rsrv != 0) {
- return false;
- }
- return true;
- }
-
- bool decode(int row, int numRows, Uint8List output) {
- if (!isValid) {
- return false;
- }
-
- var unfilterFunc = WebPFilters.UNFILTERS[filter];
-
- if (method == ALPHA_NO_COMPRESSION) {
- final int offset = row * width;
- final int numPixels = numRows * width;
-
- output.setRange(offset, numPixels, input.buffer,
- input.position + offset);
- } else {
- if (!_decodeAlphaImageStream(row + numRows, output)) {
- return false;
- }
- }
-
- if (unfilterFunc != null) {
- unfilterFunc(width, height, width, row, numRows, output);
- }
-
- if (preProcessing == ALPHA_PREPROCESSED_LEVELS) {
- if (!_dequantizeLevels(output, width, height, row, numRows)) {
- return false;
- }
- }
-
- if (row + numRows == height) {
- isAlphaDecoded = true;
- }
-
- return true;
- }
-
- bool _dequantizeLevels(Uint8List data, int width, int height,
- int row, int num_rows) {
- if (data == null || width <= 0 || height <= 0 || row < 0 || num_rows < 0 ||
- row + num_rows > height) {
- return false;
- }
- return true;
- }
-
-
- bool _decodeAlphaImageStream(int lastRow, Uint8List output) {
- _vp8l.opaque = output;
- // Decode (with special row processing).
- return _use8bDecode ?
- _vp8l.decodeAlphaData(_vp8l.webp.width, _vp8l.webp.height, lastRow) :
- _vp8l.decodeImageData(_vp8l.pixels, _vp8l.webp.width, _vp8l.webp.height,
- lastRow, _vp8l.extractAlphaRows);
- }
-
- bool _decodeAlphaHeader() {
- WebPInfo webp = new WebPInfo();
- webp.width = width;
- webp.height = height;
-
- _vp8l = new InternalVP8L(input, webp);
- _vp8l.ioWidth = width;
- _vp8l.ioHeight = height;
-
- _vp8l.decodeImageStream(webp.width, webp.height, true);
-
- // Special case: if alpha data uses only the color indexing transform and
- // doesn't use color cache (a frequent case), we will use DecodeAlphaData()
- // method that only needs allocation of 1 byte per pixel (alpha channel).
- if (_vp8l.transforms.length == 1 &&
- _vp8l.transforms[0].type == VP8LTransform.COLOR_INDEXING_TRANSFORM &&
- _vp8l.is8bOptimizable()) {
- _use8bDecode = true;
- _vp8l.allocateInternalBuffers8b();
- } else {
- _use8bDecode = false;
- _vp8l.allocateInternalBuffers32b();
- }
-
- return true;
- }
-
- InternalVP8L _vp8l;
- /// Although alpha channel
- /// requires only 1 byte per
- /// pixel, sometimes VP8LDecoder may need to allocate
- /// 4 bytes per pixel internally during decode.
- bool _use8bDecode = false;
-
- // Alpha related constants.
- static const int ALPHA_NO_COMPRESSION = 0;
- static const int ALPHA_LOSSLESS_COMPRESSION = 1;
- static const int ALPHA_PREPROCESSED_LEVELS = 1;
-}
+import 'dart:typed_data';
+
+import '../../util/input_buffer.dart';
+import 'vp8l.dart';
+import 'vp8l_transform.dart';
+import 'webp_filters.dart';
+import 'webp_info.dart';
+
+class WebPAlpha {
+ InputBuffer input;
+ int width = 0;
+ int height = 0;
+ int method = 0;
+ int filter = 0;
+ int preProcessing = 0;
+ int rsrv = 1;
+ bool isAlphaDecoded = false;
+
+ WebPAlpha(this.input, this.width, this.height) {
+ int b = input.readByte();
+ method = b & 0x03;
+ filter = (b >> 2) & 0x03;
+ preProcessing = (b >> 4) & 0x03;
+ rsrv = (b >> 6) & 0x03;
+
+ if (isValid) {
+ if (method == ALPHA_NO_COMPRESSION) {
+ final int alphaDecodedSize = width * height;
+ if (input.length < alphaDecodedSize) {
+ rsrv = 1;
+ }
+ } else if (method == ALPHA_LOSSLESS_COMPRESSION) {
+ if (!_decodeAlphaHeader()) {
+ rsrv = 1;
+ }
+ } else {
+ rsrv = 1;
+ }
+ }
+ }
+
+ bool get isValid {
+ if (method < ALPHA_NO_COMPRESSION ||
+ method > ALPHA_LOSSLESS_COMPRESSION ||
+ filter >= WebPFilters.FILTER_LAST ||
+ preProcessing > ALPHA_PREPROCESSED_LEVELS ||
+ rsrv != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ bool decode(int row, int numRows, Uint8List output) {
+ if (!isValid) {
+ return false;
+ }
+
+ var unfilterFunc = WebPFilters.UNFILTERS[filter];
+
+ if (method == ALPHA_NO_COMPRESSION) {
+ final int offset = row * width;
+ final int numPixels = numRows * width;
+
+ output.setRange(offset, numPixels, input.buffer,
+ input.position + offset);
+ } else {
+ if (!_decodeAlphaImageStream(row + numRows, output)) {
+ return false;
+ }
+ }
+
+ if (unfilterFunc != null) {
+ unfilterFunc(width, height, width, row, numRows, output);
+ }
+
+ if (preProcessing == ALPHA_PREPROCESSED_LEVELS) {
+ if (!_dequantizeLevels(output, width, height, row, numRows)) {
+ return false;
+ }
+ }
+
+ if (row + numRows == height) {
+ isAlphaDecoded = true;
+ }
+
+ return true;
+ }
+
+ bool _dequantizeLevels(Uint8List data, int width, int height,
+ int row, int num_rows) {
+ if (data == null || width <= 0 || height <= 0 || row < 0 || num_rows < 0 ||
+ row + num_rows > height) {
+ return false;
+ }
+ return true;
+ }
+
+
+ bool _decodeAlphaImageStream(int lastRow, Uint8List output) {
+ _vp8l.opaque = output;
+ // Decode (with special row processing).
+ return _use8bDecode ?
+ _vp8l.decodeAlphaData(_vp8l.webp.width, _vp8l.webp.height, lastRow) :
+ _vp8l.decodeImageData(_vp8l.pixels, _vp8l.webp.width, _vp8l.webp.height,
+ lastRow, _vp8l.extractAlphaRows);
+ }
+
+ bool _decodeAlphaHeader() {
+ WebPInfo webp = WebPInfo();
+ webp.width = width;
+ webp.height = height;
+
+ _vp8l = InternalVP8L(input, webp);
+ _vp8l.ioWidth = width;
+ _vp8l.ioHeight = height;
+
+ _vp8l.decodeImageStream(webp.width, webp.height, true);
+
+ // Special case: if alpha data uses only the color indexing transform and
+ // doesn't use color cache (a frequent case), we will use DecodeAlphaData()
+ // method that only needs allocation of 1 byte per pixel (alpha channel).
+ if (_vp8l.transforms.length == 1 &&
+ _vp8l.transforms[0].type == VP8LTransform.COLOR_INDEXING_TRANSFORM &&
+ _vp8l.is8bOptimizable()) {
+ _use8bDecode = true;
+ _vp8l.allocateInternalBuffers8b();
+ } else {
+ _use8bDecode = false;
+ _vp8l.allocateInternalBuffers32b();
+ }
+
+ return true;
+ }
+
+ InternalVP8L _vp8l;
+ /// Although alpha channel
+ /// requires only 1 byte per
+ /// pixel, sometimes VP8LDecoder may need to allocate
+ /// 4 bytes per pixel internally during decode.
+ bool _use8bDecode = false;
+
+ // Alpha related constants.
+ static const int ALPHA_NO_COMPRESSION = 0;
+ static const int ALPHA_LOSSLESS_COMPRESSION = 1;
+ static const int ALPHA_PREPROCESSED_LEVELS = 1;
+}
diff --git a/image/lib/src/formats/webp/webp_filters.dart b/image/lib/src/formats/webp/webp_filters.dart
old mode 100644
new mode 100755
index 497b150..e16e883
--- a/image/lib/src/formats/webp/webp_filters.dart
+++ b/image/lib/src/formats/webp/webp_filters.dart
@@ -1,188 +1,188 @@
-import 'dart:typed_data';
-
-import '../../util/input_buffer.dart';
-
-class WebPFilters {
- // Filters.
- static const int FILTER_NONE = 0;
- static const int FILTER_HORIZONTAL = 1;
- static const int FILTER_VERTICAL = 2;
- static const int FILTER_GRADIENT = 3;
- static const int FILTER_LAST = FILTER_GRADIENT + 1; // end marker
- static const int FILTER_BEST = 5;
- static const int FILTER_FAST = 6;
-
- static const List FILTERS = const [
- null, // WEBP_FILTER_NONE
- horizontalFilter, // WEBP_FILTER_HORIZONTAL
- verticalFilter, // WEBP_FILTER_VERTICAL
- gradientFilter // WEBP_FILTER_GRADIENT
- ];
-
-
- static const List UNFILTERS = const [
- null, // WEBP_FILTER_NONE
- horizontalUnfilter, // WEBP_FILTER_HORIZONTAL
- verticalUnfilter, // WEBP_FILTER_VERTICAL
- gradientUnfilter // WEBP_FILTER_GRADIENT
- ];
-
- static void horizontalFilter(Uint8List data, int width, int height,
- int stride, Uint8List filteredData) {
- _doHorizontalFilter(data, width, height, stride, 0, height, false,
- filteredData);
- }
-
- static void horizontalUnfilter(int width, int height, int stride, int row,
- int numRows, Uint8List data) {
- _doHorizontalFilter(data, width, height, stride, row, numRows, true, data);
- }
-
- static void verticalFilter(Uint8List data, int width, int height,
- int stride, Uint8List filteredData) {
- _doVerticalFilter(data, width, height, stride, 0, height, false,
- filteredData);
- }
-
- static void verticalUnfilter(int width, int height, int stride, int row,
- int num_rows, Uint8List data) {
- _doVerticalFilter(data, width, height, stride, row, num_rows, true, data);
- }
-
- static void gradientFilter(Uint8List data, int width, int height,
- int stride, Uint8List filteredData) {
- _doGradientFilter(data, width, height, stride, 0, height, false,
- filteredData);
- }
-
- static void gradientUnfilter(int width, int height, int stride, int row,
- int num_rows, Uint8List data) {
- _doGradientFilter(data, width, height, stride, row, num_rows, true, data);
- }
-
- static void _predictLine(InputBuffer src, InputBuffer pred, InputBuffer dst, int length,
- bool inverse) {
- if (inverse) {
- for (int i = 0; i < length; ++i) {
- dst[i] = src[i] + pred[i];
- }
- } else {
- for (int i = 0; i < length; ++i) {
- dst[i] = src[i] - pred[i];
- }
- }
- }
-
- static void _doHorizontalFilter(Uint8List src,
- int width, int height, int stride,
- int row, int numRows,
- bool inverse, Uint8List out) {
- final int startOffset = row * stride;
- final int lastRow = row + numRows;
- InputBuffer s = new InputBuffer(src, offset: startOffset);
- InputBuffer o = new InputBuffer(src, offset: startOffset);
- InputBuffer preds = new InputBuffer.from(inverse ? o : s);
-
- if (row == 0) {
- // Leftmost pixel is the same as input for topmost scanline.
- o[0] = s[0];
- _predictLine(new InputBuffer.from(s, offset: 1), preds,
- new InputBuffer.from(o, offset: 1), width - 1, inverse);
- row = 1;
- preds.offset += stride;
- s.offset += stride;
- o.offset += stride;
- }
-
- // Filter line-by-line.
- while (row < lastRow) {
- // Leftmost pixel is predicted from above.
- _predictLine(s, new InputBuffer.from(preds, offset: -stride), o, 1, inverse);
- _predictLine(new InputBuffer.from(s, offset: 1), preds,
- new InputBuffer.from(o, offset: 1), width - 1, inverse);
- ++row;
- preds.offset += stride;
- s.offset += stride;
- o.offset += stride;
- }
- }
-
- static void _doVerticalFilter(Uint8List src,
- int width, int height, int stride,
- int row, int numRows,
- bool inverse, Uint8List out) {
- final int startOffset = row * stride;
- final int last_row = row + numRows;
- InputBuffer s = new InputBuffer(src, offset: startOffset);
- InputBuffer o = new InputBuffer(out, offset: startOffset);
- InputBuffer preds = new InputBuffer.from(inverse ? o : s);
-
- if (row == 0) {
- // Very first top-left pixel is copied.
- o[0] = s[0];
- // Rest of top scan-line is left-predicted.
- _predictLine(new InputBuffer.from(s, offset: 1), preds,
- new InputBuffer.from(o, offset: 1), width - 1,
- inverse);
- row = 1;
- s.offset += stride;
- o.offset += stride;
- } else {
- // We are starting from in-between. Make sure 'preds' points to prev row.
- preds.offset -= stride;
- }
-
- // Filter line-by-line.
- while (row < last_row) {
- _predictLine(s, preds, o, width, inverse);
- ++row;
- preds.offset += stride;
- s.offset += stride;
- o.offset += stride;
- }
- }
-
- static int _gradientPredictor(int a, int b, int c) {
- final int g = a + b - c;
- return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
- }
-
- static void _doGradientFilter(Uint8List src,
- int width, int height, int stride,
- int row, int numRows,
- bool inverse, Uint8List out) {
- final int startOffset = row * stride;
- final int lastRow = row + numRows;
- InputBuffer s = new InputBuffer(src, offset: startOffset);
- InputBuffer o = new InputBuffer(out, offset: startOffset);
- InputBuffer preds = new InputBuffer.from(inverse ? o : s);
-
- // left prediction for top scan-line
- if (row == 0) {
- o[0] = s[0];
- _predictLine(new InputBuffer.from(s, offset: 1), preds,
- new InputBuffer.from(o, offset: 1), width - 1, inverse);
- row = 1;
- preds.offset += stride;
- s.offset += stride;
- o.offset += stride;
- }
-
- // Filter line-by-line.
- while (row < lastRow) {
- // leftmost pixel: predict from above.
- _predictLine(s, new InputBuffer.from(preds, offset: -stride),
- o, 1, inverse);
- for (int w = 1; w < width; ++w) {
- final int pred = _gradientPredictor(preds[w - 1],
- preds[w - stride],
- preds[w - stride - 1]);
- o[w] = s[w] + (inverse ? pred : -pred);
- }
- ++row;
- preds.offset += stride;
- s.offset += stride;
- o.offset += stride;
- }
- }
-}
+import 'dart:typed_data';
+
+import '../../util/input_buffer.dart';
+
+class WebPFilters {
+ // Filters.
+ static const int FILTER_NONE = 0;
+ static const int FILTER_HORIZONTAL = 1;
+ static const int FILTER_VERTICAL = 2;
+ static const int FILTER_GRADIENT = 3;
+ static const int FILTER_LAST = FILTER_GRADIENT + 1; // end marker
+ static const int FILTER_BEST = 5;
+ static const int FILTER_FAST = 6;
+
+ static const List FILTERS = const [
+ null, // WEBP_FILTER_NONE
+ horizontalFilter, // WEBP_FILTER_HORIZONTAL
+ verticalFilter, // WEBP_FILTER_VERTICAL
+ gradientFilter // WEBP_FILTER_GRADIENT
+ ];
+
+
+ static const List UNFILTERS = const [
+ null, // WEBP_FILTER_NONE
+ horizontalUnfilter, // WEBP_FILTER_HORIZONTAL
+ verticalUnfilter, // WEBP_FILTER_VERTICAL
+ gradientUnfilter // WEBP_FILTER_GRADIENT
+ ];
+
+ static void horizontalFilter(Uint8List data, int width, int height,
+ int stride, Uint8List filteredData) {
+ _doHorizontalFilter(data, width, height, stride, 0, height, false,
+ filteredData);
+ }
+
+ static void horizontalUnfilter(int width, int height, int stride, int row,
+ int numRows, Uint8List data) {
+ _doHorizontalFilter(data, width, height, stride, row, numRows, true, data);
+ }
+
+ static void verticalFilter(Uint8List data, int width, int height,
+ int stride, Uint8List filteredData) {
+ _doVerticalFilter(data, width, height, stride, 0, height, false,
+ filteredData);
+ }
+
+ static void verticalUnfilter(int width, int height, int stride, int row,
+ int num_rows, Uint8List data) {
+ _doVerticalFilter(data, width, height, stride, row, num_rows, true, data);
+ }
+
+ static void gradientFilter(Uint8List data, int width, int height,
+ int stride, Uint8List filteredData) {
+ _doGradientFilter(data, width, height, stride, 0, height, false,
+ filteredData);
+ }
+
+ static void gradientUnfilter(int width, int height, int stride, int row,
+ int num_rows, Uint8List data) {
+ _doGradientFilter(data, width, height, stride, row, num_rows, true, data);
+ }
+
+ static void _predictLine(InputBuffer src, InputBuffer pred, InputBuffer dst, int length,
+ bool inverse) {
+ if (inverse) {
+ for (int i = 0; i < length; ++i) {
+ dst[i] = src[i] + pred[i];
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ dst[i] = src[i] - pred[i];
+ }
+ }
+ }
+
+ static void _doHorizontalFilter(Uint8List src,
+ int width, int height, int stride,
+ int row, int numRows,
+ bool inverse, Uint8List out) {
+ final int startOffset = row * stride;
+ final int lastRow = row + numRows;
+ InputBuffer s = InputBuffer(src, offset: startOffset);
+ InputBuffer o = InputBuffer(src, offset: startOffset);
+ InputBuffer preds = InputBuffer.from(inverse ? o : s);
+
+ if (row == 0) {
+ // Leftmost pixel is the same as input for topmost scanline.
+ o[0] = s[0];
+ _predictLine(new InputBuffer.from(s, offset: 1), preds,
+ new InputBuffer.from(o, offset: 1), width - 1, inverse);
+ row = 1;
+ preds.offset += stride;
+ s.offset += stride;
+ o.offset += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < lastRow) {
+ // Leftmost pixel is predicted from above.
+ _predictLine(s, new InputBuffer.from(preds, offset: -stride), o, 1, inverse);
+ _predictLine(new InputBuffer.from(s, offset: 1), preds,
+ new InputBuffer.from(o, offset: 1), width - 1, inverse);
+ ++row;
+ preds.offset += stride;
+ s.offset += stride;
+ o.offset += stride;
+ }
+ }
+
+ static void _doVerticalFilter(Uint8List src,
+ int width, int height, int stride,
+ int row, int numRows,
+ bool inverse, Uint8List out) {
+ final int startOffset = row * stride;
+ final int last_row = row + numRows;
+ InputBuffer s = InputBuffer(src, offset: startOffset);
+ InputBuffer o = InputBuffer(out, offset: startOffset);
+ InputBuffer preds = InputBuffer.from(inverse ? o : s);
+
+ if (row == 0) {
+ // Very first top-left pixel is copied.
+ o[0] = s[0];
+ // Rest of top scan-line is left-predicted.
+ _predictLine(new InputBuffer.from(s, offset: 1), preds,
+ new InputBuffer.from(o, offset: 1), width - 1,
+ inverse);
+ row = 1;
+ s.offset += stride;
+ o.offset += stride;
+ } else {
+ // We are starting from in-between. Make sure 'preds' points to prev row.
+ preds.offset -= stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ _predictLine(s, preds, o, width, inverse);
+ ++row;
+ preds.offset += stride;
+ s.offset += stride;
+ o.offset += stride;
+ }
+ }
+
+ static int _gradientPredictor(int a, int b, int c) {
+ final int g = a + b - c;
+ return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
+ }
+
+ static void _doGradientFilter(Uint8List src,
+ int width, int height, int stride,
+ int row, int numRows,
+ bool inverse, Uint8List out) {
+ final int startOffset = row * stride;
+ final int lastRow = row + numRows;
+ InputBuffer s = InputBuffer(src, offset: startOffset);
+ InputBuffer o = InputBuffer(out, offset: startOffset);
+ InputBuffer preds = InputBuffer.from(inverse ? o : s);
+
+ // left prediction for top scan-line
+ if (row == 0) {
+ o[0] = s[0];
+ _predictLine(new InputBuffer.from(s, offset: 1), preds,
+ new InputBuffer.from(o, offset: 1), width - 1, inverse);
+ row = 1;
+ preds.offset += stride;
+ s.offset += stride;
+ o.offset += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < lastRow) {
+ // leftmost pixel: predict from above.
+ _predictLine(s, new InputBuffer.from(preds, offset: -stride),
+ o, 1, inverse);
+ for (int w = 1; w < width; ++w) {
+ final int pred = _gradientPredictor(preds[w - 1],
+ preds[w - stride],
+ preds[w - stride - 1]);
+ o[w] = s[w] + (inverse ? pred : -pred);
+ }
+ ++row;
+ preds.offset += stride;
+ s.offset += stride;
+ o.offset += stride;
+ }
+ }
+}
diff --git a/image/lib/src/formats/webp/webp_frame.dart b/image/lib/src/formats/webp/webp_frame.dart
old mode 100644
new mode 100755
index 1e88b78..ab1dd9e
--- a/image/lib/src/formats/webp/webp_frame.dart
+++ b/image/lib/src/formats/webp/webp_frame.dart
@@ -1,54 +1,54 @@
-import '../../internal/internal.dart';
-import '../../util/input_buffer.dart';
-
-/**
- * Decodes a frame from a WebP animation.
- */
-class WebPFrame {
- /// The x coordinate of the upper left corner of the frame.
- int x;
- /// The y coordinate of the upper left corner of the frame.
- int y;
- /// The width of the frame.
- int width;
- /// The height of the frame.
- int height;
- /// How long the frame should be displayed, in milliseconds.
- int duration;
- /// Indicates how the current frame is to be treated after it has been
- /// displayed (before rendering the next frame) on the canvas.
- /// If true, the frame is cleared to the background color. If false,
- /// frame is left and the next frame drawn over it.
- bool clearFrame;
-
- WebPFrame(InputBuffer input, int size) {
- x = input.readUint24() * 2;
- y = input.readUint24() * 2;
- width = input.readUint24() + 1;
- height = input.readUint24() + 1;
- duration = input.readUint24();
- int b = input.readByte();
- _reserved = (b & 0x7F) >> 7;
- clearFrame = (b & 0x1) != 0;
-
- _framePosition = input.position;
- _frameSize = size - _ANIMF_HEADER_SIZE;
- }
-
- bool get isValid => _reserved == 0;
-
- int _reserved = 1;
- int _framePosition;
- int _frameSize;
-
- // Size of an animation frame header.
- static const int _ANIMF_HEADER_SIZE = 16;
-}
-
-@internal
-class InternalWebPFrame extends WebPFrame {
- InternalWebPFrame(InputBuffer input, int size) : super(input, size);
-
- int get framePosition => _framePosition;
- int get frameSize => _frameSize;
-}
+import '../../internal/internal.dart';
+import '../../util/input_buffer.dart';
+
+/**
+ * Decodes a frame from a WebP animation.
+ */
+class WebPFrame {
+ /// The x coordinate of the upper left corner of the frame.
+ int x;
+ /// The y coordinate of the upper left corner of the frame.
+ int y;
+ /// The width of the frame.
+ int width;
+ /// The height of the frame.
+ int height;
+ /// How long the frame should be displayed, in milliseconds.
+ int duration;
+ /// Indicates how the current frame is to be treated after it has been
+ /// displayed (before rendering the next frame) on the canvas.
+ /// If true, the frame is cleared to the background color. If false,
+ /// frame is left and the next frame drawn over it.
+ bool clearFrame;
+
+ WebPFrame(InputBuffer input, int size) {
+ x = input.readUint24() * 2;
+ y = input.readUint24() * 2;
+ width = input.readUint24() + 1;
+ height = input.readUint24() + 1;
+ duration = input.readUint24();
+ int b = input.readByte();
+ _reserved = (b & 0x7F) >> 7;
+ clearFrame = (b & 0x1) != 0;
+
+ _framePosition = input.position;
+ _frameSize = size - _ANIMF_HEADER_SIZE;
+ }
+
+ bool get isValid => _reserved == 0;
+
+ int _reserved = 1;
+ int _framePosition;
+ int _frameSize;
+
+ // Size of an animation frame header.
+ static const int _ANIMF_HEADER_SIZE = 16;
+}
+
+@internal
+class InternalWebPFrame extends WebPFrame {
+ InternalWebPFrame(InputBuffer input, int size) : super(input, size);
+
+ int get framePosition => _framePosition;
+ int get frameSize => _frameSize;
+}
diff --git a/image/lib/src/formats/webp/webp_huffman.dart b/image/lib/src/formats/webp/webp_huffman.dart
old mode 100644
new mode 100755
index 9b4fa79..7ae311f
--- a/image/lib/src/formats/webp/webp_huffman.dart
+++ b/image/lib/src/formats/webp/webp_huffman.dart
@@ -1,328 +1,328 @@
-import 'dart:typed_data';
-
-import '../../internal/internal.dart';
-import 'vp8l.dart';
-import 'vp8l_bit_reader.dart';
-
-/**
- * Huffman Tree.
- */
-@internal
-class HuffmanTree {
- static const int HUFF_LUT_BITS = 7;
- static const int HUFF_LUT = (1 << HUFF_LUT_BITS);
- // Fast lookup for short bit lengths.
- Uint8List lutBits = new Uint8List(HUFF_LUT);
- Int16List lutSymbol = new Int16List(HUFF_LUT);
- Int16List lutJump = new Int16List(HUFF_LUT);
-
- /// all the nodes, starting at root, stored as a single int array, where
- /// each node occupies two ints as [symbol, children].
- Int32List tree;
- /// max number of nodes
- int maxNodes = 0;
- /// number of currently occupied nodes
- int numNodes = 0;
-
- HuffmanTree([int numLeaves = 0]) {
- _init(numLeaves);
- }
-
- bool _init(int numLeaves) {
- if (numLeaves == 0) {
- return false;
- }
-
- maxNodes = (numLeaves << 1) - 1;
- tree = new Int32List(maxNodes << 1);
- tree[1] = -1;
- numNodes = 1;
- lutBits.fillRange(0, lutBits.length, 255);
-
- return true;
- }
-
- bool buildImplicit(List<int> codeLengths, int codeLengthsSize) {
- int numSymbols = 0;
- int rootSymbol = 0;
-
- // Find out number of symbols and the root symbol.
- for (int symbol = 0; symbol < codeLengthsSize; ++symbol) {
- if (codeLengths[symbol] > 0) {
- // Note: code length = 0 indicates non-existent symbol.
- ++numSymbols;
- rootSymbol = symbol;
- }
- }
-
- // Initialize the tree. Will fail for num_symbols = 0
- if (!_init(numSymbols)) {
- return false;
- }
-
- // Build tree.
- if (numSymbols == 1) {
- // Trivial case.
- final int maxSymbol = codeLengthsSize;
- if (rootSymbol < 0 || rootSymbol >= maxSymbol) {
- return false;
- }
-
- return _addSymbol(rootSymbol, 0, 0);
- }
-
- // Normal case.
-
- // Get Huffman codes from the code lengths.
- Int32List codes = new Int32List(codeLengthsSize);
-
- if (!_huffmanCodeLengthsToCodes(codeLengths, codeLengthsSize, codes)) {
- return false;
- }
-
- // Add symbols one-by-one.
- for (int symbol = 0; symbol < codeLengthsSize; ++symbol) {
- if (codeLengths[symbol] > 0) {
- if (!_addSymbol(symbol, codes[symbol], codeLengths[symbol])) {
- return false;
- }
- }
- }
-
- return _isFull();
- }
-
- bool buildExplicit(List<int> codeLengths,
- List<int> codes,
- List<int> symbols,
- int maxSymbol,
- int numSymbols) {
- // Initialize the tree. Will fail if num_symbols = 0.
- if (!_init(numSymbols)) {
- return false;
- }
-
- // Add symbols one-by-one.
- for (int i = 0; i < numSymbols; ++i) {
- if (codes[i] != -1) {
- if (symbols[i] < 0 || symbols[i] >= maxSymbol) {
- return _isFull();
- }
-
- if (!_addSymbol(symbols[i], codes[i], codeLengths[i])) {
- return _isFull();
- }
- }
- }
-
- return _isFull();
- }
-
- /**
- * Decodes the next Huffman code from bit-stream.
- * input.fillBitWindow() needs to be called at minimum every second call
- * to ReadSymbol, in order to pre-fetch enough bits.
- */
- int readSymbol(VP8LBitReader br) {
- int node = 0;
- int bits = br.prefetchBits();
- int newBitPos = br.bitPos;
- // Check if we find the bit combination from the Huffman lookup table.
- int lut_ix = bits & (HUFF_LUT - 1);
- int lut_bits = lutBits[lut_ix];
-
- if (lut_bits <= HUFF_LUT_BITS) {
- br.bitPos = br.bitPos + lut_bits;
- return this.lutSymbol[lut_ix];
- }
-
- node += this.lutJump[lut_ix];
- newBitPos += HUFF_LUT_BITS;
- bits >>= HUFF_LUT_BITS;
-
- // Decode the value from a binary tree.
- do {
- node = _nextNode(node, bits & 1);
- bits >>= 1;
- ++newBitPos;
- } while (_nodeIsNotLeaf(node));
-
- br.bitPos = newBitPos;
-
- return _nodeSymbol(node);
- }
-
- bool _addSymbol(int symbol, int code, int codeLength) {
- int step = HUFF_LUT_BITS;
- int baseCode;
- int node = 0;
-
- if (codeLength <= HUFF_LUT_BITS) {
- baseCode = _reverseBitsShort(code, codeLength);
- for (int i = 0; i < (1 << (HUFF_LUT_BITS - codeLength)); ++i) {
- final int idx = baseCode | (i << codeLength);
- lutSymbol[idx] = symbol;
- lutBits[idx] = codeLength;
- }
- } else {
- baseCode = _reverseBitsShort((code >> (codeLength - HUFF_LUT_BITS)),
- HUFF_LUT_BITS);
- }
-
- while (codeLength-- > 0) {
- if (node >= maxNodes) {
- return false;
- }
-
- if (_nodeIsEmpty(node)) {
- if (_isFull()) {
- // error: too many symbols.
- return false;
- }
-
- _assignChildren(node);
- } else if (!_nodeIsNotLeaf(node)) {
- // leaf is already occupied.
- return false;
- }
-
- node += _nodeChildren(node) + ((code >> codeLength) & 1);
-
- if (--step == 0) {
- lutJump[baseCode] = node;
- }
- }
-
- if (_nodeIsEmpty(node)) {
- // turn newly created node into a leaf.
- _nodeSetChildren(node, 0);
- } else if (_nodeIsNotLeaf(node)) {
- // trying to assign a symbol to already used code.
- return false;
- }
-
- // Add symbol in this node.
- _nodeSetSymbol(node, symbol);
-
- return true;
- }
-
- // Pre-reversed 4-bit values.
- static const List<int> _REVERSED_BITS = const [
- 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
- 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf];
-
- int _reverseBitsShort(int bits, int numBits) {
- int v = (_REVERSED_BITS[bits & 0xf] << 4) | _REVERSED_BITS[bits >> 4];
- return v >> (8 - numBits);
- }
-
- bool _isFull() {
- return (numNodes == maxNodes);
- }
-
- int _nextNode(int node, int rightChild) {
- return node + _nodeChildren(node) + rightChild;
- }
-
- int _nodeSymbol(int node) => tree[(node << 1)];
-
- void _nodeSetSymbol(int node, int symbol) {
- tree[(node << 1)] = symbol;
- }
-
- int _nodeChildren(int node) => tree[(node << 1) + 1];
-
- void _nodeSetChildren(int node, int children) {
- tree[(node << 1) + 1] = children;
- }
-
- bool _nodeIsNotLeaf(int node) => tree[(node << 1) + 1] != 0;
-
- bool _nodeIsEmpty(int node) => tree[(node << 1) + 1] < 0;
-
- void _assignChildren(int node) {
- int children = numNodes;
- _nodeSetChildren(node, children - node);
-
- numNodes += 2;
-
- _nodeSetChildren(children, -1);
- _nodeSetChildren(children + 1, -1);
- }
-
- bool _huffmanCodeLengthsToCodes(List<int> codeLengths, int codeLengthsSize,
- List<int> huffCodes) {
- int symbol;
- int codeLen;
- Int32List codeLengthHist =
- new Int32List(VP8L.MAX_ALLOWED_CODE_LENGTH + 1);
- int currCode;
- Int32List nextCodes =
- new Int32List(VP8L.MAX_ALLOWED_CODE_LENGTH + 1);
- int maxCodeLength = 0;
-
- // Calculate max code length.
- for (symbol = 0; symbol < codeLengthsSize; ++symbol) {
- if (codeLengths[symbol] > maxCodeLength) {
- maxCodeLength = codeLengths[symbol];
- }
- }
-
- if (maxCodeLength > VP8L.MAX_ALLOWED_CODE_LENGTH) {
- return false;
- }
-
- // Calculate code length histogram.
- for (symbol = 0; symbol < codeLengthsSize; ++symbol) {
- ++codeLengthHist[codeLengths[symbol]];
- }
-
- codeLengthHist[0] = 0;
-
- // Calculate the initial values of 'next_codes' for each code length.
- // next_codes[code_len] denotes the code to be assigned to the next symbol
- // of code length 'code_len'.
- currCode = 0;
- // Unused, as code length = 0 implies code doesn't exist.
- nextCodes[0] = -1;
-
- for (codeLen = 1; codeLen <= maxCodeLength; ++codeLen) {
- currCode = (currCode + codeLengthHist[codeLen - 1]) << 1;
- nextCodes[codeLen] = currCode;
- }
-
- // Get symbols.
- for (symbol = 0; symbol < codeLengthsSize; ++symbol) {
- if (codeLengths[symbol] > 0) {
- huffCodes[symbol] = nextCodes[codeLengths[symbol]]++;
- } else {
- huffCodes[symbol] = -1;
- }
- }
-
- return true;
- }
-}
-
-/**
- * A group of huffman trees.
- */
-@internal
-class HTreeGroup {
- final List<HuffmanTree> htrees =
- new List<HuffmanTree>(VP8L.HUFFMAN_CODES_PER_META_CODE);
-
- HTreeGroup() {
- for (int i = 0, len = htrees.length; i < len; ++i) {
- htrees[i] = new HuffmanTree();
- }
- }
-
- HuffmanTree operator[](int index) {
- if (htrees[index] == null) {
- htrees[index] = new HuffmanTree();
- }
- return htrees[index];
- }
-}
+import 'dart:typed_data';
+
+import '../../internal/internal.dart';
+import 'vp8l.dart';
+import 'vp8l_bit_reader.dart';
+
+/**
+ * Huffman Tree.
+ */
+@internal
+class HuffmanTree {
+ static const int HUFF_LUT_BITS = 7;
+ static const int HUFF_LUT = (1 << HUFF_LUT_BITS);
+ // Fast lookup for short bit lengths.
+ Uint8List lutBits = Uint8List(HUFF_LUT);
+ Int16List lutSymbol = Int16List(HUFF_LUT);
+ Int16List lutJump = Int16List(HUFF_LUT);
+
+ /// all the nodes, starting at root, stored as a single int array, where
+ /// each node occupies two ints as [symbol, children].
+ Int32List tree;
+ /// max number of nodes
+ int maxNodes = 0;
+ /// number of currently occupied nodes
+ int numNodes = 0;
+
+ HuffmanTree([int numLeaves = 0]) {
+ _init(numLeaves);
+ }
+
+ bool _init(int numLeaves) {
+ if (numLeaves == 0) {
+ return false;
+ }
+
+ maxNodes = (numLeaves << 1) - 1;
+ tree = Int32List(maxNodes << 1);
+ tree[1] = -1;
+ numNodes = 1;
+ lutBits.fillRange(0, lutBits.length, 255);
+
+ return true;
+ }
+
+ bool buildImplicit(List<int> codeLengths, int codeLengthsSize) {
+ int numSymbols = 0;
+ int rootSymbol = 0;
+
+ // Find out number of symbols and the root symbol.
+ for (int symbol = 0; symbol < codeLengthsSize; ++symbol) {
+ if (codeLengths[symbol] > 0) {
+ // Note: code length = 0 indicates non-existent symbol.
+ ++numSymbols;
+ rootSymbol = symbol;
+ }
+ }
+
+ // Initialize the tree. Will fail for num_symbols = 0
+ if (!_init(numSymbols)) {
+ return false;
+ }
+
+ // Build tree.
+ if (numSymbols == 1) {
+ // Trivial case.
+ final int maxSymbol = codeLengthsSize;
+ if (rootSymbol < 0 || rootSymbol >= maxSymbol) {
+ return false;
+ }
+
+ return _addSymbol(rootSymbol, 0, 0);
+ }
+
+ // Normal case.
+
+ // Get Huffman codes from the code lengths.
+ Int32List codes = Int32List(codeLengthsSize);
+
+ if (!_huffmanCodeLengthsToCodes(codeLengths, codeLengthsSize, codes)) {
+ return false;
+ }
+
+ // Add symbols one-by-one.
+ for (int symbol = 0; symbol < codeLengthsSize; ++symbol) {
+ if (codeLengths[symbol] > 0) {
+ if (!_addSymbol(symbol, codes[symbol], codeLengths[symbol])) {
+ return false;
+ }
+ }
+ }
+
+ return _isFull();
+ }
+
+ bool buildExplicit(List<int> codeLengths,
+ List<int> codes,
+ List<int> symbols,
+ int maxSymbol,
+ int numSymbols) {
+ // Initialize the tree. Will fail if num_symbols = 0.
+ if (!_init(numSymbols)) {
+ return false;
+ }
+
+ // Add symbols one-by-one.
+ for (int i = 0; i < numSymbols; ++i) {
+ if (codes[i] != -1) {
+ if (symbols[i] < 0 || symbols[i] >= maxSymbol) {
+ return _isFull();
+ }
+
+ if (!_addSymbol(symbols[i], codes[i], codeLengths[i])) {
+ return _isFull();
+ }
+ }
+ }
+
+ return _isFull();
+ }
+
+ /**
+ * Decodes the next Huffman code from bit-stream.
+ * input.fillBitWindow() needs to be called at minimum every second call
+ * to ReadSymbol, in order to pre-fetch enough bits.
+ */
+ int readSymbol(VP8LBitReader br) {
+ int node = 0;
+ int bits = br.prefetchBits();
+ int newBitPos = br.bitPos;
+ // Check if we find the bit combination from the Huffman lookup table.
+ int lut_ix = bits & (HUFF_LUT - 1);
+ int lut_bits = lutBits[lut_ix];
+
+ if (lut_bits <= HUFF_LUT_BITS) {
+ br.bitPos = br.bitPos + lut_bits;
+ return this.lutSymbol[lut_ix];
+ }
+
+ node += this.lutJump[lut_ix];
+ newBitPos += HUFF_LUT_BITS;
+ bits >>= HUFF_LUT_BITS;
+
+ // Decode the value from a binary tree.
+ do {
+ node = _nextNode(node, bits & 1);
+ bits >>= 1;
+ ++newBitPos;
+ } while (_nodeIsNotLeaf(node));
+
+ br.bitPos = newBitPos;
+
+ return _nodeSymbol(node);
+ }
+
+ bool _addSymbol(int symbol, int code, int codeLength) {
+ int step = HUFF_LUT_BITS;
+ int baseCode;
+ int node = 0;
+
+ if (codeLength <= HUFF_LUT_BITS) {
+ baseCode = _reverseBitsShort(code, codeLength);
+ for (int i = 0; i < (1 << (HUFF_LUT_BITS - codeLength)); ++i) {
+ final int idx = baseCode | (i << codeLength);
+ lutSymbol[idx] = symbol;
+ lutBits[idx] = codeLength;
+ }
+ } else {
+ baseCode = _reverseBitsShort((code >> (codeLength - HUFF_LUT_BITS)),
+ HUFF_LUT_BITS);
+ }
+
+ while (codeLength-- > 0) {
+ if (node >= maxNodes) {
+ return false;
+ }
+
+ if (_nodeIsEmpty(node)) {
+ if (_isFull()) {
+ // error: too many symbols.
+ return false;
+ }
+
+ _assignChildren(node);
+ } else if (!_nodeIsNotLeaf(node)) {
+ // leaf is already occupied.
+ return false;
+ }
+
+ node += _nodeChildren(node) + ((code >> codeLength) & 1);
+
+ if (--step == 0) {
+ lutJump[baseCode] = node;
+ }
+ }
+
+ if (_nodeIsEmpty(node)) {
+ // turn newly created node into a leaf.
+ _nodeSetChildren(node, 0);
+ } else if (_nodeIsNotLeaf(node)) {
+ // trying to assign a symbol to already used code.
+ return false;
+ }
+
+ // Add symbol in this node.
+ _nodeSetSymbol(node, symbol);
+
+ return true;
+ }
+
+ // Pre-reversed 4-bit values.
+ static const List<int> _REVERSED_BITS = const [
+ 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
+ 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf];
+
+ int _reverseBitsShort(int bits, int numBits) {
+ int v = (_REVERSED_BITS[bits & 0xf] << 4) | _REVERSED_BITS[bits >> 4];
+ return v >> (8 - numBits);
+ }
+
+ bool _isFull() {
+ return (numNodes == maxNodes);
+ }
+
+ int _nextNode(int node, int rightChild) {
+ return node + _nodeChildren(node) + rightChild;
+ }
+
+ int _nodeSymbol(int node) => tree[(node << 1)];
+
+ void _nodeSetSymbol(int node, int symbol) {
+ tree[(node << 1)] = symbol;
+ }
+
+ int _nodeChildren(int node) => tree[(node << 1) + 1];
+
+ void _nodeSetChildren(int node, int children) {
+ tree[(node << 1) + 1] = children;
+ }
+
+ bool _nodeIsNotLeaf(int node) => tree[(node << 1) + 1] != 0;
+
+ bool _nodeIsEmpty(int node) => tree[(node << 1) + 1] < 0;
+
+ void _assignChildren(int node) {
+ int children = numNodes;
+ _nodeSetChildren(node, children - node);
+
+ numNodes += 2;
+
+ _nodeSetChildren(children, -1);
+ _nodeSetChildren(children + 1, -1);
+ }
+
+ bool _huffmanCodeLengthsToCodes(List<int> codeLengths, int codeLengthsSize,
+ List<int> huffCodes) {
+ int symbol;
+ int codeLen;
+ Int32List codeLengthHist =
+ new Int32List(VP8L.MAX_ALLOWED_CODE_LENGTH + 1);
+ int currCode;
+ Int32List nextCodes =
+ new Int32List(VP8L.MAX_ALLOWED_CODE_LENGTH + 1);
+ int maxCodeLength = 0;
+
+ // Calculate max code length.
+ for (symbol = 0; symbol < codeLengthsSize; ++symbol) {
+ if (codeLengths[symbol] > maxCodeLength) {
+ maxCodeLength = codeLengths[symbol];
+ }
+ }
+
+ if (maxCodeLength > VP8L.MAX_ALLOWED_CODE_LENGTH) {
+ return false;
+ }
+
+ // Calculate code length histogram.
+ for (symbol = 0; symbol < codeLengthsSize; ++symbol) {
+ ++codeLengthHist[codeLengths[symbol]];
+ }
+
+ codeLengthHist[0] = 0;
+
+ // Calculate the initial values of 'next_codes' for each code length.
+ // next_codes[code_len] denotes the code to be assigned to the next symbol
+ // of code length 'code_len'.
+ currCode = 0;
+ // Unused, as code length = 0 implies code doesn't exist.
+ nextCodes[0] = -1;
+
+ for (codeLen = 1; codeLen <= maxCodeLength; ++codeLen) {
+ currCode = (currCode + codeLengthHist[codeLen - 1]) << 1;
+ nextCodes[codeLen] = currCode;
+ }
+
+ // Get symbols.
+ for (symbol = 0; symbol < codeLengthsSize; ++symbol) {
+ if (codeLengths[symbol] > 0) {
+ huffCodes[symbol] = nextCodes[codeLengths[symbol]]++;
+ } else {
+ huffCodes[symbol] = -1;
+ }
+ }
+
+ return true;
+ }
+}
+
+/**
+ * A group of huffman trees.
+ */
+@internal
+class HTreeGroup {
+ final List<HuffmanTree> htrees =
+ new List<HuffmanTree>(VP8L.HUFFMAN_CODES_PER_META_CODE);
+
+ HTreeGroup() {
+ for (int i = 0, len = htrees.length; i < len; ++i) {
+ htrees[i] = HuffmanTree();
+ }
+ }
+
+ HuffmanTree operator[](int index) {
+ if (htrees[index] == null) {
+ htrees[index] = HuffmanTree();
+ }
+ return htrees[index];
+ }
+}
diff --git a/image/lib/src/formats/webp/webp_info.dart b/image/lib/src/formats/webp/webp_info.dart
old mode 100644
new mode 100755
index 866c92b..f8b9305
--- a/image/lib/src/formats/webp/webp_info.dart
+++ b/image/lib/src/formats/webp/webp_info.dart
@@ -1,63 +1,63 @@
-import '../../internal/internal.dart';
-import '../../util/input_buffer.dart';
-import '../decode_info.dart';
-import 'webp_frame.dart';
-
-/**
- * Features gathered from the bitstream
- */
-class WebPInfo extends DecodeInfo {
- // enum Format
- static const int FORMAT_UNDEFINED = 0;
- static const int FORMAT_LOSSY = 1;
- static const int FORMAT_LOSSLESS = 2;
- static const int FORMAT_ANIMATED = 3;
-
- /// True if the bitstream contains an alpha channel.
- bool hasAlpha = false;
- /// True if the bitstream is an animation.
- bool hasAnimation = false;
- /// 0 = undefined (/mixed), 1 = lossy, 2 = lossless, 3 = animated
- int format = FORMAT_UNDEFINED;
- /// ICCP data string.
- String iccp = '';
- /// EXIF data string.
- String exif = '';
- /// XMP data string.
- String xmp = '';
- /// How many times the animation should loop.
- int animLoopCount = 0;
- /// Information about each animation frame.
- List<WebPFrame> frames = [];
-
- int get numFrames => frames.length;
-
- int _frame;
- int _numFrames;
-
- InputBuffer _alphaData;
- int _alphaSize;
- int _vp8Position;
- int _vp8Size;
-}
-
-@internal
-class InternalWebPInfo extends WebPInfo {
- int get frame => _frame;
- set frame(int value) => _frame = value;
-
- int get numFrames => _numFrames;
- set numFrames(int value) => _numFrames = value;
-
- InputBuffer get alphaData => _alphaData;
- set alphaData(InputBuffer buffer) => _alphaData = buffer;
-
- int get alphaSize => _alphaSize;
- set alphaSize(int value) => _alphaSize = value;
-
- int get vp8Position => _vp8Position;
- set vp8Position(int value) => _vp8Position = value;
-
- int get vp8Size => _vp8Size;
- set vp8Size(int value) => _vp8Size = value;
-}
+import '../../internal/internal.dart';
+import '../../util/input_buffer.dart';
+import '../decode_info.dart';
+import 'webp_frame.dart';
+
+/**
+ * Features gathered from the bitstream
+ */
+class WebPInfo extends DecodeInfo {
+ // enum Format
+ static const int FORMAT_UNDEFINED = 0;
+ static const int FORMAT_LOSSY = 1;
+ static const int FORMAT_LOSSLESS = 2;
+ static const int FORMAT_ANIMATED = 3;
+
+ /// True if the bitstream contains an alpha channel.
+ bool hasAlpha = false;
+ /// True if the bitstream is an animation.
+ bool hasAnimation = false;
+ /// 0 = undefined (/mixed), 1 = lossy, 2 = lossless, 3 = animated
+ int format = FORMAT_UNDEFINED;
+ /// ICCP data string.
+ String iccp = '';
+ /// EXIF data string.
+ String exif = '';
+ /// XMP data string.
+ String xmp = '';
+ /// How many times the animation should loop.
+ int animLoopCount = 0;
+ /// Information about each animation frame.
+ List<WebPFrame> frames = [];
+
+ int get numFrames => frames.length;
+
+ int _frame;
+ int _numFrames;
+
+ InputBuffer _alphaData;
+ int _alphaSize;
+ int _vp8Position;
+ int _vp8Size;
+}
+
+@internal
+class InternalWebPInfo extends WebPInfo {
+ int get frame => _frame;
+ set frame(int value) => _frame = value;
+
+ int get numFrames => _numFrames;
+ set numFrames(int value) => _numFrames = value;
+
+ InputBuffer get alphaData => _alphaData;
+ set alphaData(InputBuffer buffer) => _alphaData = buffer;
+
+ int get alphaSize => _alphaSize;
+ set alphaSize(int value) => _alphaSize = value;
+
+ int get vp8Position => _vp8Position;
+ set vp8Position(int value) => _vp8Position = value;
+
+ int get vp8Size => _vp8Size;
+ set vp8Size(int value) => _vp8Size = value;
+}
diff --git a/image/lib/src/formats/webp_decoder.dart b/image/lib/src/formats/webp_decoder.dart
old mode 100644
new mode 100755
index 638396e..672928c
--- a/image/lib/src/formats/webp_decoder.dart
+++ b/image/lib/src/formats/webp_decoder.dart
@@ -1,365 +1,365 @@
-import '../animation.dart';
-import '../color.dart';
-import '../image.dart';
-import '../transform/copy_into.dart';
-import '../util/input_buffer.dart';
-import 'decoder.dart';
-import 'webp/vp8.dart';
-import 'webp/vp8l.dart';
-import 'webp/webp_frame.dart';
-import 'webp/webp_info.dart';
-
-/**
- * Decode a WebP formatted image. This supports lossless (vp8l), lossy (vp8),
- * lossy+alpha, and animated WebP images.
- */
-class WebPDecoder extends Decoder {
- InternalWebPInfo _info;
-
- WebPDecoder([List<int> bytes]) {
- if (bytes != null) {
- startDecode(bytes);
- }
- }
-
- WebPInfo get info => _info;
-
- /**
- * Is the given file a valid WebP image?
- */
- bool isValidFile(List<int> bytes) {
- _input = new InputBuffer(bytes);
- if (!_getHeader(_input)) {
- return false;
- }
- return true;
- }
-
- /**
- * How many frames are available to decode?
- *
- * You should have prepared the decoder by either passing the file bytes
- * to the constructor, or calling getInfo.
- */
- int numFrames() => (_info != null) ? _info.numFrames : 0;
-
- /**
- * Validate the file is a WebP image and get information about it.
- * If the file is not a valid WebP image, null is returned.
- */
- WebPInfo startDecode(List<int> bytes) {
- _input = new InputBuffer(bytes);
-
- if (!_getHeader(_input)) {
- return null;
- }
-
- _info = new InternalWebPInfo();
- if (!_getInfo(_input, _info)) {
- return null;
- }
-
- switch (_info.format) {
- case WebPInfo.FORMAT_ANIMATED:
- return _info;
- case WebPInfo.FORMAT_LOSSLESS:
- _input.offset = _info.vp8Position;
- VP8L vp8l = new VP8L(_input, _info);
- if (!vp8l.decodeHeader()) {
- return null;
- }
- return _info;
- case WebPInfo.FORMAT_LOSSY:
- _input.offset = _info.vp8Position;
- VP8 vp8 = new VP8(_input, _info);
- if (!vp8.decodeHeader()) {
- return null;
- }
- return _info;
- }
-
- return null;
- }
-
- Image decodeFrame(int frame) {
- if (_input == null || _info == null) {
- return null;
- }
-
- if (_info.hasAnimation) {
- if (frame >= _info.frames.length || frame < 0) {
- return null;
- }
-
- InternalWebPFrame f = _info.frames[frame];
- InputBuffer frameData = _input.subset(f.frameSize,
- position: f.framePosition);
-
- return _decodeFrame(frameData, frame: frame);
- }
-
- if (_info.format == WebPInfo.FORMAT_LOSSLESS) {
- InputBuffer data = _input.subset(_info.vp8Size,
- position: _info.vp8Position);
- return new VP8L(data, _info).decode();
- } else if (_info.format == WebPInfo.FORMAT_LOSSY) {
- InputBuffer data = _input.subset(_info.vp8Size,
- position: _info.vp8Position);
- return new VP8(data, _info).decode();
- }
-
- return null;
- }
-
- /**
- * Decode a WebP formatted file stored in [bytes] into an Image.
- * If it's not a valid webp file, null is returned.
- * If the webp file stores animated frames, only the first image will
- * be returned. Use [decodeAnimation] to decode the full animation.
- */
- Image decodeImage(List<int> bytes, {int frame: 0}) {
- startDecode(bytes);
- _info.frame = 0;
- _info.numFrames = 1;
- return decodeFrame(frame);
- }
-
- /**
- * Decode all of the frames of an animated webp. For single image webps,
- * this will return an animation with a single frame.
- */
-Animation decodeAnimation(List<int> bytes) {
- if (startDecode(bytes) == null) {
- return null;
- }
-
- _info.numFrames = _info.numFrames;
-
- Animation anim = new Animation();
- anim.width = _info.width;
- anim.height = _info.height;
- anim.loopCount = _info.animLoopCount;
-
- if (_info.hasAnimation) {
- Image lastImage = new Image(_info.width, _info.height);
- for (int i = 0; i < _info.numFrames; ++i) {
- _info.frame = i;
- if (lastImage == null) {
- lastImage = new Image(_info.width, _info.height);
- } else {
- lastImage = new Image.from(lastImage);
- }
-
- WebPFrame frame = _info.frames[i];
- Image image = decodeFrame(i);
- if (image == null) {
- return null;
- }
-
- if (lastImage != null) {
- if (frame.clearFrame) {
- lastImage.fill(_info.backgroundColor);
- }
- copyInto(lastImage, image, dstX: frame.x, dstY: frame.y);
- } else {
- lastImage = image;
- }
-
- lastImage.duration = frame.duration;
- anim.addFrame(lastImage);
- }
- } else {
- Image image = decodeImage(bytes);
- if (image == null) {
- return null;
- }
-
- anim.addFrame(image);
- }
-
- return anim;
- }
-
-
- Image _decodeFrame(InputBuffer input, {int frame: 0}) {
- InternalWebPInfo webp = new InternalWebPInfo();
- if (!_getInfo(input, webp)) {
- return null;
- }
-
- if (webp.format == 0) {
- return null;
- }
-
- webp.frame = _info.frame;
- webp.numFrames = _info.numFrames;
-
- if (webp.hasAnimation) {
- if (frame >= webp.frames.length || frame < 0) {
- return null;
- }
- InternalWebPFrame f = webp.frames[frame];
- InputBuffer frameData = input.subset(f.frameSize,
- position: f.framePosition);
-
- return _decodeFrame(frameData, frame: frame);
- } else {
- InputBuffer data = input.subset(webp.vp8Size,
- position: webp.vp8Position);
- if (webp.format == WebPInfo.FORMAT_LOSSLESS) {
- return new VP8L(data, webp).decode();
- } else if (webp.format == WebPInfo.FORMAT_LOSSY) {
- return new VP8(data, webp).decode();
- }
- }
-
- return null;
- }
-
- bool _getHeader(InputBuffer input) {
- // Validate the webp format header
- String tag = input.readString(4);
- if (tag != 'RIFF') {
- return false;
- }
-
- /*int fileSize =*/ input.readUint32();
-
- tag = input.readString(4);
- if (tag != 'WEBP') {
- return false;
- }
-
- return true;
- }
-
- bool _getInfo(InputBuffer input, InternalWebPInfo webp) {
- bool found = false;
- while (!input.isEOS && !found) {
- String tag = input.readString(4);
- int size = input.readUint32();
- // For odd sized chunks, there's a 1 byte padding at the end.
- int diskSize = ((size + 1) >> 1) << 1;
- int p = input.position;
-
- switch (tag) {
- case 'VP8X':
- if (!_getVp8xInfo(input, webp)) {
- return false;
- }
- break;
- case 'VP8 ':
- webp.vp8Position = input.position;
- webp.vp8Size = size;
- webp.format = WebPInfo.FORMAT_LOSSY;
- found = true;
- break;
- case 'VP8L':
- webp.vp8Position = input.position;
- webp.vp8Size = size;
- webp.format = WebPInfo.FORMAT_LOSSLESS;
- found = true;
- break;
- case 'ALPH':
- webp.alphaData = new InputBuffer(input.buffer,
- bigEndian: input.bigEndian);
- webp.alphaData.offset = input.offset;
- webp.alphaSize = size;
- input.skip(diskSize);
- break;
- case 'ANIM':
- webp.format = WebPInfo.FORMAT_ANIMATED;
- if (!_getAnimInfo(input, webp)) {
- return false;
- }
- break;
- case 'ANMF':
- if (!_getAnimFrameInfo(input, webp, size)) {
- return false;
- }
- break;
- case 'ICCP':
- webp.iccp = input.readString(size);
- break;
- case 'EXIF':
- webp.exif = input.readString(size);
- break;
- case 'XMP ':
- webp.xmp = input.readString(size);
- break;
- default:
- print('UNKNOWN WEBP TAG: $tag');
- input.skip(diskSize);
- break;
- }
-
- int remainder = diskSize - (input.position - p);
- if (remainder > 0) {
- input.skip(remainder);
- }
- }
-
- /**
- * The alpha flag might not have been set, but it does in fact have alpha
- * if there is an ALPH chunk.
- */
- if (!webp.hasAlpha) {
- webp.hasAlpha = webp.alphaData != null;
- }
-
- return webp.format != 0;
- }
-
- bool _getVp8xInfo(InputBuffer input, WebPInfo webp) {
- int b = input.readByte();
- if ((b & 0xc0) != 0) {
- return false;
- }
- //int icc = (b >> 5) & 0x1;
- int alpha = (b >> 4) & 0x1;
- //int exif = (b >> 3) & 0x1;
- //int xmp = (b >> 2) & 0x1;
- int a = (b >> 1) & 0x1;
-
- if (b & 0x1 != 0) {
- return false;
- }
-
- if (input.readUint24() != 0) {
- return false;
- }
- int w = input.readUint24() + 1;
- int h = input.readUint24() + 1;
-
- webp.width = w;
- webp.height = h;
- webp.hasAnimation = a != 0;
- webp.hasAlpha = alpha != 0;
-
- return true;
- }
-
- bool _getAnimInfo(InputBuffer input, WebPInfo webp) {
- int c = input.readUint32();
- webp.animLoopCount = input.readUint16();
-
- // Color is stored in blue,green,red,alpha order.
- int a = getRed(c);
- int r = getGreen(c);
- int g = getBlue(c);
- int b = getAlpha(c);
- webp.backgroundColor = getColor(r, g, b, a);
- return true;
- }
-
- bool _getAnimFrameInfo(InputBuffer input, WebPInfo webp, int size) {
- InternalWebPFrame frame = new InternalWebPFrame(input, size);
- if (!frame.isValid) {
- return false;
- }
- webp.frames.add(frame);
- return true;
- }
-
- InputBuffer _input;
-}
+import '../animation.dart';
+import '../color.dart';
+import '../image.dart';
+import '../transform/copy_into.dart';
+import '../util/input_buffer.dart';
+import 'decoder.dart';
+import 'webp/vp8.dart';
+import 'webp/vp8l.dart';
+import 'webp/webp_frame.dart';
+import 'webp/webp_info.dart';
+
+/**
+ * Decode a WebP formatted image. This supports lossless (vp8l), lossy (vp8),
+ * lossy+alpha, and animated WebP images.
+ */
+class WebPDecoder extends Decoder {
+ InternalWebPInfo _info;
+
+ WebPDecoder([List<int> bytes]) {
+ if (bytes != null) {
+ startDecode(bytes);
+ }
+ }
+
+ WebPInfo get info => _info;
+
+ /**
+ * Is the given file a valid WebP image?
+ */
+ bool isValidFile(List<int> bytes) {
+ _input = InputBuffer(bytes);
+ if (!_getHeader(_input)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * How many frames are available to decode?
+ *
+ * You should have prepared the decoder by either passing the file bytes
+ * to the constructor, or calling getInfo.
+ */
+ int numFrames() => (_info != null) ? _info.numFrames : 0;
+
+ /**
+ * Validate the file is a WebP image and get information about it.
+ * If the file is not a valid WebP image, null is returned.
+ */
+ WebPInfo startDecode(List<int> bytes) {
+ _input = InputBuffer(bytes);
+
+ if (!_getHeader(_input)) {
+ return null;
+ }
+
+ _info = InternalWebPInfo();
+ if (!_getInfo(_input, _info)) {
+ return null;
+ }
+
+ switch (_info.format) {
+ case WebPInfo.FORMAT_ANIMATED:
+ return _info;
+ case WebPInfo.FORMAT_LOSSLESS:
+ _input.offset = _info.vp8Position;
+ VP8L vp8l = VP8L(_input, _info);
+ if (!vp8l.decodeHeader()) {
+ return null;
+ }
+ return _info;
+ case WebPInfo.FORMAT_LOSSY:
+ _input.offset = _info.vp8Position;
+ VP8 vp8 = VP8(_input, _info);
+ if (!vp8.decodeHeader()) {
+ return null;
+ }
+ return _info;
+ }
+
+ return null;
+ }
+
+ Image decodeFrame(int frame) {
+ if (_input == null || _info == null) {
+ return null;
+ }
+
+ if (_info.hasAnimation) {
+ if (frame >= _info.frames.length || frame < 0) {
+ return null;
+ }
+
+ InternalWebPFrame f = _info.frames[frame];
+ InputBuffer frameData = _input.subset(f.frameSize,
+ position: f.framePosition);
+
+ return _decodeFrame(frameData, frame: frame);
+ }
+
+ if (_info.format == WebPInfo.FORMAT_LOSSLESS) {
+ InputBuffer data = _input.subset(_info.vp8Size,
+ position: _info.vp8Position);
+ return new VP8L(data, _info).decode();
+ } else if (_info.format == WebPInfo.FORMAT_LOSSY) {
+ InputBuffer data = _input.subset(_info.vp8Size,
+ position: _info.vp8Position);
+ return new VP8(data, _info).decode();
+ }
+
+ return null;
+ }
+
+ /**
+ * Decode a WebP formatted file stored in [bytes] into an Image.
+ * If it's not a valid webp file, null is returned.
+ * If the webp file stores animated frames, only the first image will
+ * be returned. Use [decodeAnimation] to decode the full animation.
+ */
+ Image decodeImage(List<int> bytes, {int frame: 0}) {
+ startDecode(bytes);
+ _info.frame = 0;
+ _info.numFrames = 1;
+ return decodeFrame(frame);
+ }
+
+ /**
+ * Decode all of the frames of an animated webp. For single image webps,
+ * this will return an animation with a single frame.
+ */
+Animation decodeAnimation(List<int> bytes) {
+ if (startDecode(bytes) == null) {
+ return null;
+ }
+
+ _info.numFrames = _info.numFrames;
+
+ Animation anim = Animation();
+ anim.width = _info.width;
+ anim.height = _info.height;
+ anim.loopCount = _info.animLoopCount;
+
+ if (_info.hasAnimation) {
+ Image lastImage = Image(_info.width, _info.height);
+ for (int i = 0; i < _info.numFrames; ++i) {
+ _info.frame = i;
+ if (lastImage == null) {
+ lastImage = Image(_info.width, _info.height);
+ } else {
+ lastImage = Image.from(lastImage);
+ }
+
+ WebPFrame frame = _info.frames[i];
+ Image image = decodeFrame(i);
+ if (image == null) {
+ return null;
+ }
+
+ if (lastImage != null) {
+ if (frame.clearFrame) {
+ lastImage.fill(_info.backgroundColor);
+ }
+ copyInto(lastImage, image, dstX: frame.x, dstY: frame.y);
+ } else {
+ lastImage = image;
+ }
+
+ lastImage.duration = frame.duration;
+ anim.addFrame(lastImage);
+ }
+ } else {
+ Image image = decodeImage(bytes);
+ if (image == null) {
+ return null;
+ }
+
+ anim.addFrame(image);
+ }
+
+ return anim;
+ }
+
+
+ Image _decodeFrame(InputBuffer input, {int frame: 0}) {
+ InternalWebPInfo webp = InternalWebPInfo();
+ if (!_getInfo(input, webp)) {
+ return null;
+ }
+
+ if (webp.format == 0) {
+ return null;
+ }
+
+ webp.frame = _info.frame;
+ webp.numFrames = _info.numFrames;
+
+ if (webp.hasAnimation) {
+ if (frame >= webp.frames.length || frame < 0) {
+ return null;
+ }
+ InternalWebPFrame f = webp.frames[frame];
+ InputBuffer frameData = input.subset(f.frameSize,
+ position: f.framePosition);
+
+ return _decodeFrame(frameData, frame: frame);
+ } else {
+ InputBuffer data = input.subset(webp.vp8Size,
+ position: webp.vp8Position);
+ if (webp.format == WebPInfo.FORMAT_LOSSLESS) {
+ return new VP8L(data, webp).decode();
+ } else if (webp.format == WebPInfo.FORMAT_LOSSY) {
+ return new VP8(data, webp).decode();
+ }
+ }
+
+ return null;
+ }
+
+ bool _getHeader(InputBuffer input) {
+ // Validate the webp format header
+ String tag = input.readString(4);
+ if (tag != 'RIFF') {
+ return false;
+ }
+
+ /*int fileSize =*/ input.readUint32();
+
+ tag = input.readString(4);
+ if (tag != 'WEBP') {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool _getInfo(InputBuffer input, InternalWebPInfo webp) {
+ bool found = false;
+ while (!input.isEOS && !found) {
+ String tag = input.readString(4);
+ int size = input.readUint32();
+ // For odd sized chunks, there's a 1 byte padding at the end.
+ int diskSize = ((size + 1) >> 1) << 1;
+ int p = input.position;
+
+ switch (tag) {
+ case 'VP8X':
+ if (!_getVp8xInfo(input, webp)) {
+ return false;
+ }
+ break;
+ case 'VP8 ':
+ webp.vp8Position = input.position;
+ webp.vp8Size = size;
+ webp.format = WebPInfo.FORMAT_LOSSY;
+ found = true;
+ break;
+ case 'VP8L':
+ webp.vp8Position = input.position;
+ webp.vp8Size = size;
+ webp.format = WebPInfo.FORMAT_LOSSLESS;
+ found = true;
+ break;
+ case 'ALPH':
+ webp.alphaData = InputBuffer(input.buffer,
+ bigEndian: input.bigEndian);
+ webp.alphaData.offset = input.offset;
+ webp.alphaSize = size;
+ input.skip(diskSize);
+ break;
+ case 'ANIM':
+ webp.format = WebPInfo.FORMAT_ANIMATED;
+ if (!_getAnimInfo(input, webp)) {
+ return false;
+ }
+ break;
+ case 'ANMF':
+ if (!_getAnimFrameInfo(input, webp, size)) {
+ return false;
+ }
+ break;
+ case 'ICCP':
+ webp.iccp = input.readString(size);
+ break;
+ case 'EXIF':
+ webp.exif = input.readString(size);
+ break;
+ case 'XMP ':
+ webp.xmp = input.readString(size);
+ break;
+ default:
+ print('UNKNOWN WEBP TAG: $tag');
+ input.skip(diskSize);
+ break;
+ }
+
+ int remainder = diskSize - (input.position - p);
+ if (remainder > 0) {
+ input.skip(remainder);
+ }
+ }
+
+ /**
+ * The alpha flag might not have been set, but it does in fact have alpha
+ * if there is an ALPH chunk.
+ */
+ if (!webp.hasAlpha) {
+ webp.hasAlpha = webp.alphaData != null;
+ }
+
+ return webp.format != 0;
+ }
+
+ bool _getVp8xInfo(InputBuffer input, WebPInfo webp) {
+ int b = input.readByte();
+ if ((b & 0xc0) != 0) {
+ return false;
+ }
+ //int icc = (b >> 5) & 0x1;
+ int alpha = (b >> 4) & 0x1;
+ //int exif = (b >> 3) & 0x1;
+ //int xmp = (b >> 2) & 0x1;
+ int a = (b >> 1) & 0x1;
+
+ if (b & 0x1 != 0) {
+ return false;
+ }
+
+ if (input.readUint24() != 0) {
+ return false;
+ }
+ int w = input.readUint24() + 1;
+ int h = input.readUint24() + 1;
+
+ webp.width = w;
+ webp.height = h;
+ webp.hasAnimation = a != 0;
+ webp.hasAlpha = alpha != 0;
+
+ return true;
+ }
+
+ bool _getAnimInfo(InputBuffer input, WebPInfo webp) {
+ int c = input.readUint32();
+ webp.animLoopCount = input.readUint16();
+
+ // Color is stored in blue,green,red,alpha order.
+ int a = getRed(c);
+ int r = getGreen(c);
+ int g = getBlue(c);
+ int b = getAlpha(c);
+ webp.backgroundColor = getColor(r, g, b, a);
+ return true;
+ }
+
+ bool _getAnimFrameInfo(InputBuffer input, WebPInfo webp, int size) {
+ InternalWebPFrame frame = InternalWebPFrame(input, size);
+ if (!frame.isValid) {
+ return false;
+ }
+ webp.frames.add(frame);
+ return true;
+ }
+
+ InputBuffer _input;
+}
diff --git a/image/lib/src/formats/webp_encoder.dart b/image/lib/src/formats/webp_encoder.dart
old mode 100644
new mode 100755
index 1d246a0..5f4f8af
--- a/image/lib/src/formats/webp_encoder.dart
+++ b/image/lib/src/formats/webp_encoder.dart
@@ -1,126 +1,126 @@
-import 'dart:typed_data';
-
-import '../animation.dart';
-import '../image.dart';
-import '../util/output_buffer.dart';
-import 'encoder.dart';
-
-/**
- * Encode an image to the PNG format.
- */
-class WebPEncoder extends Encoder {
- static const int LOSSLESS = 0;
- static const int LOSSY = 1;
-
- int format;
- num quality;
-
- /**
- * [format] can be [LOSSY] or [LOSSLESS].
- * [quality] is controls lossy compression, in the range
- * 0 (smallest file) and 100 (biggest).
- */
- WebPEncoder({this.format: LOSSY,
- this.quality: 100});
-
- /**
- * Add a frame to be encoded. Call [finish] to encode the added frames.
- * If only one frame is added, a single-image WebP is encoded; otherwise
- * if there are more than one frame, a multi-frame animated WebP is encoded.
- */
- void addFrame(Image image, {int duration}) {
- if (output == null) {
- output = new OutputBuffer();
-
- if (duration != null) {
- this.delay = duration;
- }
- _lastImage = _encodeImage(image);
- _width = image.width;
- _height = image.height;
- return;
- }
-
- if (_encodedFrames == 0) {
- _writeHeader(_width, _height);
- }
-
- _addImage(_lastImage, _width, _height);
- _encodedFrames++;
-
- if (duration != null) {
- this.delay = duration;
- }
-
- _lastImage = _encodeImage(image);
- }
-
- /**
- * Encode the images that were added with [addFrame].
- */
- List<int> finish() {
- List<int> bytes;
- if (output == null) {
- return bytes;
- }
-
- /*if (_encodedFrames == 0) {
- _writeHeader(_width, _height);
- } else {
- _writeGraphicsCtrlExt();
- }
-
- _addImage(_lastImage, _width, _height, _lastColorMap.colorMap, 256);
-
- output.writeByte(TERMINATE_RECORD_TYPE);
-
- _lastImage = null;
- _encodedFrames = 0;*/
-
- bytes = output.getBytes();
- output = null;
-
- return bytes;
- }
-
- /**
- * Encode a single frame image.
- */
- List<int> encodeImage(Image image) {
- addFrame(image);
- return finish();
- }
-
- /**
- * Does this encoder support animation?
- */
- bool get supportsAnimation => true;
-
- /**
- * Encode an animation.
- */
- List<int> encodeAnimation(Animation anim) {
- for (Image f in anim) {
- addFrame(f, duration: f.duration);
- }
- return finish();
- }
-
- Uint8List _encodeImage(Image image) {
- return null;
- }
-
- void _writeHeader(int width, int height) {
-
- }
-
- void _addImage(Uint8List image, int width, int height) {
- }
-
- OutputBuffer output;
- int delay;
- Uint8List _lastImage;
- int _width;
- int _height;
- int _encodedFrames = 0;
-}
+import 'dart:typed_data';
+
+import '../animation.dart';
+import '../image.dart';
+import '../util/output_buffer.dart';
+import 'encoder.dart';
+
+/**
+ * Encode an image to the PNG format.
+ */
+class WebPEncoder extends Encoder {
+ static const int LOSSLESS = 0;
+ static const int LOSSY = 1;
+
+ int format;
+ num quality;
+
+ /**
+ * [format] can be [LOSSY] or [LOSSLESS].
+ * [quality] is controls lossy compression, in the range
+ * 0 (smallest file) and 100 (biggest).
+ */
+ WebPEncoder({this.format: LOSSY,
+ this.quality: 100});
+
+ /**
+ * Add a frame to be encoded. Call [finish] to encode the added frames.
+ * If only one frame is added, a single-image WebP is encoded; otherwise
+ * if there are more than one frame, a multi-frame animated WebP is encoded.
+ */
+ void addFrame(Image image, {int duration}) {
+ if (output == null) {
+ output = OutputBuffer();
+
+ if (duration != null) {
+ this.delay = duration;
+ }
+ _lastImage = _encodeImage(image);
+ _width = image.width;
+ _height = image.height;
+ return;
+ }
+
+ if (_encodedFrames == 0) {
+ _writeHeader(_width, _height);
+ }
+
+ _addImage(_lastImage, _width, _height);
+ _encodedFrames++;
+
+ if (duration != null) {
+ this.delay = duration;
+ }
+
+ _lastImage = _encodeImage(image);
+ }
+
+ /**
+ * Encode the images that were added with [addFrame].
+ */
+ List<int> finish() {
+ List<int> bytes;
+ if (output == null) {
+ return bytes;
+ }
+
+ /*if (_encodedFrames == 0) {
+ _writeHeader(_width, _height);
+ } else {
+ _writeGraphicsCtrlExt();
+ }
+
+ _addImage(_lastImage, _width, _height, _lastColorMap.colorMap, 256);
+
+ output.writeByte(TERMINATE_RECORD_TYPE);
+
+ _lastImage = null;
+ _encodedFrames = 0;*/
+
+ bytes = output.getBytes();
+ output = null;
+
+ return bytes;
+ }
+
+ /**
+ * Encode a single frame image.
+ */
+ List<int> encodeImage(Image image) {
+ addFrame(image);
+ return finish();
+ }
+
+ /**
+ * Does this encoder support animation?
+ */
+ bool get supportsAnimation => true;
+
+ /**
+ * Encode an animation.
+ */
+ List<int> encodeAnimation(Animation anim) {
+ for (Image f in anim) {
+ addFrame(f, duration: f.duration);
+ }
+ return finish();
+ }
+
+ Uint8List _encodeImage(Image image) {
+ return null;
+ }
+
+ void _writeHeader(int width, int height) {
+
+ }
+
+ void _addImage(Uint8List image, int width, int height) {
+ }
+
+ OutputBuffer output;
+ int delay;
+ Uint8List _lastImage;
+ int _width;
+ int _height;
+ int _encodedFrames = 0;
+}
diff --git a/image/lib/src/hdr/half.dart b/image/lib/src/hdr/half.dart
old mode 100644
new mode 100755
index dfe879e..36b7b85
--- a/image/lib/src/hdr/half.dart
+++ b/image/lib/src/hdr/half.dart
@@ -1,384 +1,384 @@
-import 'dart:typed_data';
-
-import '../internal/bit_operators.dart';
-
-/**
- * A 16-bit floating-point number, used by high-dynamic-range image formats
- * as a more efficient storage for floating-point values that don't require
- * full 32-bit precision. A list of Half floats can be stored in a [Uint16List],
- * and converted to a double using the [HalfToDouble] static method.
- *
- * This class is derived from the OpenEXR library.
- */
-class Half {
- Half([num f]) {
- if (f != null) {
- _h = DoubleToHalf(f);
- }
- }
-
- Half.fromBits(int bits) :
- _h = bits {
- if (_toFloatFloat32 == null) {
- _initialize();
- }
- }
-
- static double HalfToDouble(int bits) {
- if (_toFloatFloat32 == null) {
- _initialize();
- }
- return _toFloatFloat32[bits];
- }
-
- static int DoubleToHalf(num f) {
- if (_toFloatFloat32 == null) {
- _initialize();
- }
-
- f = f.toDouble();
- int x_i = float32ToUint32(f);
- if (f == 0.0) {
- // Common special case - zero.
- // Preserve the zero's sign bit.
- return x_i >> 16;
- }
-
- // We extract the combined sign and exponent, e, from our
- // floating-point number, f. Then we convert e to the sign
- // and exponent of the half number via a table lookup.
- //
- // For the most common case, where a normalized half is produced,
- // the table lookup returns a non-zero value; in this case, all
- // we have to do is round f's significand to 10 bits and combine
- // the result with e.
- //
- // For all other cases (overflow, zeroes, denormalized numbers
- // resulting from underflow, infinities and NANs), the table
- // lookup returns zero, and we call a longer, non-inline function
- // to do the float-to-half conversion.
- int e = (x_i >> 23) & 0x000001ff;
-
- e = _eLut[e];
-
- if (e != 0) {
- // Simple case - round the significand, m, to 10
- // bits and combine it with the sign and exponent.
- int m = x_i & 0x007fffff;
- return e + ((m + 0x00000fff + ((m >> 13) & 1)) >> 13);
- }
-
- // Difficult case - call a function.
- return _convert(x_i);
- }
-
- double toDouble() => _toFloatFloat32[_h];
-
- /**
- * Unary minus
- */
- Half operator-() => new Half.fromBits(_h ^ 0x8000);
-
- /**
- * Addition operator for Half or num left operands.
- */
- Half operator+(f) {
- return new Half(toDouble() + f.toDouble());
- }
-
- /**
- * Subtraction operator for Half or num left operands.
- */
- Half operator-(f) {
- return new Half(toDouble() - f.toDouble());
- }
-
- Half operator*(f) {
- return new Half(toDouble() * f.toDouble());
- }
-
- Half operator/(f) {
- return new Half(toDouble() / f.toDouble());
- }
-
- /**
- * Round to n-bit precision (n should be between 0 and 10).
- * After rounding, the significand's 10-n least significant
- * bits will be zero.
- */
- Half round(int n) {
- if (n >= 10) {
- return this;
- }
-
- // Disassemble h into the sign, s,
- // and the combined exponent and significand, e.
- int s = _h & 0x8000;
- int e = _h & 0x7fff;
-
- // Round the exponent and significand to the nearest value
- // where ones occur only in the (10-n) most significant bits.
- // Note that the exponent adjusts automatically if rounding
- // up causes the significand to overflow.
-
- e >>= 9 - n;
- e += e & 1;
- e <<= 9 - n;
-
- // Check for exponent overflow.
- if (e >= 0x7c00) {
- // Overflow occurred -- truncate instead of rounding.
- e = _h;
- e >>= 10 - n;
- e <<= 10 - n;
- }
-
- // Put the original sign bit back.
-
- return new Half.fromBits(s | e);
- }
-
- /**
- * Returns true if h is a normalized number, a denormalized number or zero.
- */
- bool isFinite() {
- int e = (_h >> 10) & 0x001f;
- return e < 31;
- }
-
- /**
- * Returns true if h is a normalized number.
- */
- bool isNormalized() {
- int e = (_h >> 10) & 0x001f;
- return e > 0 && e < 31;
- }
-
- /**
- * Returns true if h is a denormalized number.
- */
- bool isDenormalized() {
- int e = (_h >> 10) & 0x001f;
- int m = _h & 0x3ff;
- return e == 0 && m != 0;
- }
-
- /**
- * Returns true if h is zero.
- */
- bool isZero() {
- return (_h & 0x7fff) == 0;
- }
-
- /**
- * Returns true if h is a NAN.
- */
- bool isNan() {
- int e = (_h >> 10) & 0x001f;
- int m = _h & 0x3ff;
- return e == 31 && m != 0;
- }
-
- /**
- * Returns true if h is a positive or a negative infinity.
- */
- bool isInfinity() {
- int e = (_h >> 10) & 0x001f;
- int m = _h & 0x3ff;
- return e == 31 && m == 0;
- }
-
- /**
- * Returns true if the sign bit of h is set (negative).
- */
- bool isNegative() {
- return (_h & 0x8000) != 0;
- }
-
- /**
- * Returns +infinity.
- */
- static Half posInf() => new Half.fromBits(0x7c00);
-
- /**
- * Returns -infinity.
- */
- static Half negInf() => new Half.fromBits(0xfc00);
-
- /**
- * Returns a NAN with the bit pattern 0111111111111111.
- */
- static Half qNan() => new Half.fromBits(0x7fff);
-
- /**
- * Returns a NAN with the bit pattern 0111110111111111.
- */
- static Half sNan() => new Half.fromBits(0x7dff);
-
- int bits() => _h;
-
- void setBits(int bits) {
- _h = bits;
- }
-
- static int _convert(int i) {
- // Our floating point number, f, is represented by the bit
- // pattern in integer i. Disassemble that bit pattern into
- // the sign, s, the exponent, e, and the significand, m.
- // Shift s into the position where it will go in in the
- // resulting half number.
- // Adjust e, accounting for the different exponent bias
- // of float and half (127 versus 15).
- int s = (i >> 16) & 0x00008000;
- int e = ((i >> 23) & 0x000000ff) - (127 - 15);
- int m = i & 0x007fffff;
-
- // Now reassemble s, e and m into a half:
- if (e <= 0) {
- if (e < -10) {
- // E is less than -10. The absolute value of f is
- // less than HALF_MIN (f may be a small normalized
- // float, a denormalized float or a zero).
- //
- // We convert f to a half zero with the same sign as f.
- return s;
- }
-
- // E is between -10 and 0. F is a normalized float
- // whose magnitude is less than HALF_NRM_MIN.
- //
- // We convert f to a denormalized half.
-
- // Add an explicit leading 1 to the significand.
-
- m = m | 0x00800000;
-
- // Round to m to the nearest (10+e)-bit value (with e between
- // -10 and 0); in case of a tie, round to the nearest even value.
- //
- // Rounding may cause the significand to overflow and make
- // our number normalized. Because of the way a half's bits
- // are laid out, we don't have to treat this case separately;
- // the code below will handle it correctly.
-
- int t = 14 - e;
- int a = (1 << (t - 1)) - 1;
- int b = (m >> t) & 1;
-
- m = (m + a + b) >> t;
-
- // Assemble the half from s, e (zero) and m.
- return s | m;
- } else if (e == 0xff - (127 - 15)) {
- if (m == 0) {
- // F is an infinity; convert f to a half
- // infinity with the same sign as f.
- return s | 0x7c00;
- } else {
- // F is a NAN; we produce a half NAN that preserves
- // the sign bit and the 10 leftmost bits of the
- // significand of f, with one exception: If the 10
- // leftmost bits are all zero, the NAN would turn
- // into an infinity, so we have to set at least one
- // bit in the significand.
-
- m >>= 13;
- return s | 0x7c00 | m | ((m == 0) ? 1 : 0);
- }
- } else {
- // E is greater than zero. F is a normalized float.
- // We try to convert f to a normalized half.
-
- // Round to m to the nearest 10-bit value. In case of
- // a tie, round to the nearest even value.
- m = m + 0x00000fff + ((m >> 13) & 1);
-
- if (m & 0x00800000 != 0) {
- m = 0; // overflow in significand,
- e += 1; // adjust exponent
- }
-
- // Handle exponent overflow
-
- if (e > 30) {
- return s | 0x7c00; // if this returns, the half becomes an
- } // infinity with the same sign as f.
-
- // Assemble the half from s, e and m.
- return s | (e << 10) | (m >> 13);
- }
- }
-
- static void _initialize() {
- if (_toFloatUint32 != null) {
- return;
- }
- _toFloatUint32 = new Uint32List(1 << 16);
- _toFloatFloat32 = new Float32List.view(_toFloatUint32.buffer);
- _eLut = new Uint16List(1 << 9);
-
- // Init eLut
- for (int i = 0; i < 0x100; i++) {
- int e = (i & 0x0ff) - (127 - 15);
-
- if (e <= 0 || e >= 30) {
- // Special case
- _eLut[i] = 0;
- _eLut[i | 0x100] = 0;
- } else {
- // Common case - normalized half, no exponent overflow possible
- _eLut[i] = (e << 10);
- _eLut[i | 0x100] = ((e << 10) | 0x8000);
- }
- }
-
- // Init toFloat
- const int iMax = (1 << 16);
- for (int i = 0; i < iMax; i++) {
- _toFloatUint32[i] = _halfToFloat(i);
- }
- }
-
- static int _halfToFloat(int y) {
- int s = (y >> 15) & 0x00000001;
- int e = (y >> 10) & 0x0000001f;
- int m = y & 0x000003ff;
-
- if (e == 0) {
- if (m == 0) {
- // Plus or minus zero
- return s << 31;
- } else {
- // Denormalized number -- renormalize it
- while ((m & 0x00000400) == 0) {
- m <<= 1;
- e -= 1;
- }
-
- e += 1;
- m &= ~0x00000400;
- }
- } else if (e == 31) {
- if (m == 0) {
- // Positive or negative infinity
- return (s << 31) | 0x7f800000;
- } else {
- // Nan -- preserve sign and significand bits
- return (s << 31) | 0x7f800000 | (m << 13);
- }
- }
-
- // Normalized number
- e = e + (127 - 15);
- m = m << 13;
-
- // Assemble s, e and m.
- return (s << 31) | (e << 23) | m;
- }
-
- int _h;
-
- static Uint32List _toFloatUint32;
- static Float32List _toFloatFloat32;
- static Uint16List _eLut;
-}
+import 'dart:typed_data';
+
+import '../internal/bit_operators.dart';
+
+/**
+ * A 16-bit floating-point number, used by high-dynamic-range image formats
+ * as a more efficient storage for floating-point values that don't require
+ * full 32-bit precision. A list of Half floats can be stored in a [Uint16List],
+ * and converted to a double using the [HalfToDouble] static method.
+ *
+ * This class is derived from the OpenEXR library.
+ */
+class Half {
+ Half([num f]) {
+ if (f != null) {
+ _h = DoubleToHalf(f);
+ }
+ }
+
+ Half.fromBits(int bits) :
+ _h = bits {
+ if (_toFloatFloat32 == null) {
+ _initialize();
+ }
+ }
+
+ static double HalfToDouble(int bits) {
+ if (_toFloatFloat32 == null) {
+ _initialize();
+ }
+ return _toFloatFloat32[bits];
+ }
+
+ static int DoubleToHalf(num f) {
+ if (_toFloatFloat32 == null) {
+ _initialize();
+ }
+
+ f = f.toDouble();
+ int x_i = float32ToUint32(f);
+ if (f == 0.0) {
+ // Common special case - zero.
+ // Preserve the zero's sign bit.
+ return x_i >> 16;
+ }
+
+ // We extract the combined sign and exponent, e, from our
+ // floating-point number, f. Then we convert e to the sign
+ // and exponent of the half number via a table lookup.
+ //
+ // For the most common case, where a normalized half is produced,
+ // the table lookup returns a non-zero value; in this case, all
+ // we have to do is round f's significand to 10 bits and combine
+ // the result with e.
+ //
+ // For all other cases (overflow, zeroes, denormalized numbers
+ // resulting from underflow, infinities and NANs), the table
+ // lookup returns zero, and we call a longer, non-inline function
+ // to do the float-to-half conversion.
+ int e = (x_i >> 23) & 0x000001ff;
+
+ e = _eLut[e];
+
+ if (e != 0) {
+ // Simple case - round the significand, m, to 10
+ // bits and combine it with the sign and exponent.
+ int m = x_i & 0x007fffff;
+ return e + ((m + 0x00000fff + ((m >> 13) & 1)) >> 13);
+ }
+
+ // Difficult case - call a function.
+ return _convert(x_i);
+ }
+
+ double toDouble() => _toFloatFloat32[_h];
+
+ /**
+ * Unary minus
+ */
+ Half operator-() => new Half.fromBits(_h ^ 0x8000);
+
+ /**
+ * Addition operator for Half or num left operands.
+ */
+ Half operator+(f) {
+ return new Half(toDouble() + f.toDouble());
+ }
+
+ /**
+ * Subtraction operator for Half or num left operands.
+ */
+ Half operator-(f) {
+ return new Half(toDouble() - f.toDouble());
+ }
+
+ Half operator*(f) {
+ return new Half(toDouble() * f.toDouble());
+ }
+
+ Half operator/(f) {
+ return new Half(toDouble() / f.toDouble());
+ }
+
+ /**
+ * Round to n-bit precision (n should be between 0 and 10).
+ * After rounding, the significand's 10-n least significant
+ * bits will be zero.
+ */
+ Half round(int n) {
+ if (n >= 10) {
+ return this;
+ }
+
+ // Disassemble h into the sign, s,
+ // and the combined exponent and significand, e.
+ int s = _h & 0x8000;
+ int e = _h & 0x7fff;
+
+ // Round the exponent and significand to the nearest value
+ // where ones occur only in the (10-n) most significant bits.
+ // Note that the exponent adjusts automatically if rounding
+ // up causes the significand to overflow.
+
+ e >>= 9 - n;
+ e += e & 1;
+ e <<= 9 - n;
+
+ // Check for exponent overflow.
+ if (e >= 0x7c00) {
+ // Overflow occurred -- truncate instead of rounding.
+ e = _h;
+ e >>= 10 - n;
+ e <<= 10 - n;
+ }
+
+ // Put the original sign bit back.
+
+ return new Half.fromBits(s | e);
+ }
+
+ /**
+ * Returns true if h is a normalized number, a denormalized number or zero.
+ */
+ bool isFinite() {
+ int e = (_h >> 10) & 0x001f;
+ return e < 31;
+ }
+
+ /**
+ * Returns true if h is a normalized number.
+ */
+ bool isNormalized() {
+ int e = (_h >> 10) & 0x001f;
+ return e > 0 && e < 31;
+ }
+
+ /**
+ * Returns true if h is a denormalized number.
+ */
+ bool isDenormalized() {
+ int e = (_h >> 10) & 0x001f;
+ int m = _h & 0x3ff;
+ return e == 0 && m != 0;
+ }
+
+ /**
+ * Returns true if h is zero.
+ */
+ bool isZero() {
+ return (_h & 0x7fff) == 0;
+ }
+
+ /**
+ * Returns true if h is a NAN.
+ */
+ bool isNan() {
+ int e = (_h >> 10) & 0x001f;
+ int m = _h & 0x3ff;
+ return e == 31 && m != 0;
+ }
+
+ /**
+ * Returns true if h is a positive or a negative infinity.
+ */
+ bool isInfinity() {
+ int e = (_h >> 10) & 0x001f;
+ int m = _h & 0x3ff;
+ return e == 31 && m == 0;
+ }
+
+ /**
+ * Returns true if the sign bit of h is set (negative).
+ */
+ bool isNegative() {
+ return (_h & 0x8000) != 0;
+ }
+
+ /**
+ * Returns +infinity.
+ */
+ static Half posInf() => new Half.fromBits(0x7c00);
+
+ /**
+ * Returns -infinity.
+ */
+ static Half negInf() => new Half.fromBits(0xfc00);
+
+ /**
+ * Returns a NAN with the bit pattern 0111111111111111.
+ */
+ static Half qNan() => new Half.fromBits(0x7fff);
+
+ /**
+ * Returns a NAN with the bit pattern 0111110111111111.
+ */
+ static Half sNan() => new Half.fromBits(0x7dff);
+
+ int bits() => _h;
+
+ void setBits(int bits) {
+ _h = bits;
+ }
+
+ static int _convert(int i) {
+ // Our floating point number, f, is represented by the bit
+ // pattern in integer i. Disassemble that bit pattern into
+ // the sign, s, the exponent, e, and the significand, m.
+ // Shift s into the position where it will go in in the
+ // resulting half number.
+ // Adjust e, accounting for the different exponent bias
+ // of float and half (127 versus 15).
+ int s = (i >> 16) & 0x00008000;
+ int e = ((i >> 23) & 0x000000ff) - (127 - 15);
+ int m = i & 0x007fffff;
+
+ // Now reassemble s, e and m into a half:
+ if (e <= 0) {
+ if (e < -10) {
+ // E is less than -10. The absolute value of f is
+ // less than HALF_MIN (f may be a small normalized
+ // float, a denormalized float or a zero).
+ //
+ // We convert f to a half zero with the same sign as f.
+ return s;
+ }
+
+ // E is between -10 and 0. F is a normalized float
+ // whose magnitude is less than HALF_NRM_MIN.
+ //
+ // We convert f to a denormalized half.
+
+ // Add an explicit leading 1 to the significand.
+
+ m = m | 0x00800000;
+
+ // Round to m to the nearest (10+e)-bit value (with e between
+ // -10 and 0); in case of a tie, round to the nearest even value.
+ //
+ // Rounding may cause the significand to overflow and make
+ // our number normalized. Because of the way a half's bits
+ // are laid out, we don't have to treat this case separately;
+ // the code below will handle it correctly.
+
+ int t = 14 - e;
+ int a = (1 << (t - 1)) - 1;
+ int b = (m >> t) & 1;
+
+ m = (m + a + b) >> t;
+
+ // Assemble the half from s, e (zero) and m.
+ return s | m;
+ } else if (e == 0xff - (127 - 15)) {
+ if (m == 0) {
+ // F is an infinity; convert f to a half
+ // infinity with the same sign as f.
+ return s | 0x7c00;
+ } else {
+ // F is a NAN; we produce a half NAN that preserves
+ // the sign bit and the 10 leftmost bits of the
+ // significand of f, with one exception: If the 10
+ // leftmost bits are all zero, the NAN would turn
+ // into an infinity, so we have to set at least one
+ // bit in the significand.
+
+ m >>= 13;
+ return s | 0x7c00 | m | ((m == 0) ? 1 : 0);
+ }
+ } else {
+ // E is greater than zero. F is a normalized float.
+ // We try to convert f to a normalized half.
+
+ // Round to m to the nearest 10-bit value. In case of
+ // a tie, round to the nearest even value.
+ m = m + 0x00000fff + ((m >> 13) & 1);
+
+ if (m & 0x00800000 != 0) {
+ m = 0; // overflow in significand,
+ e += 1; // adjust exponent
+ }
+
+ // Handle exponent overflow
+
+ if (e > 30) {
+ return s | 0x7c00; // if this returns, the half becomes an
+ } // infinity with the same sign as f.
+
+ // Assemble the half from s, e and m.
+ return s | (e << 10) | (m >> 13);
+ }
+ }
+
+ static void _initialize() {
+ if (_toFloatUint32 != null) {
+ return;
+ }
+ _toFloatUint32 = Uint32List(1 << 16);
+ _toFloatFloat32 = Float32List.view(_toFloatUint32.buffer);
+ _eLut = Uint16List(1 << 9);
+
+ // Init eLut
+ for (int i = 0; i < 0x100; i++) {
+ int e = (i & 0x0ff) - (127 - 15);
+
+ if (e <= 0 || e >= 30) {
+ // Special case
+ _eLut[i] = 0;
+ _eLut[i | 0x100] = 0;
+ } else {
+ // Common case - normalized half, no exponent overflow possible
+ _eLut[i] = (e << 10);
+ _eLut[i | 0x100] = ((e << 10) | 0x8000);
+ }
+ }
+
+ // Init toFloat
+ const int iMax = (1 << 16);
+ for (int i = 0; i < iMax; i++) {
+ _toFloatUint32[i] = _halfToFloat(i);
+ }
+ }
+
+ static int _halfToFloat(int y) {
+ int s = (y >> 15) & 0x00000001;
+ int e = (y >> 10) & 0x0000001f;
+ int m = y & 0x000003ff;
+
+ if (e == 0) {
+ if (m == 0) {
+ // Plus or minus zero
+ return s << 31;
+ } else {
+ // Denormalized number -- renormalize it
+ while ((m & 0x00000400) == 0) {
+ m <<= 1;
+ e -= 1;
+ }
+
+ e += 1;
+ m &= ~0x00000400;
+ }
+ } else if (e == 31) {
+ if (m == 0) {
+ // Positive or negative infinity
+ return (s << 31) | 0x7f800000;
+ } else {
+ // Nan -- preserve sign and significand bits
+ return (s << 31) | 0x7f800000 | (m << 13);
+ }
+ }
+
+ // Normalized number
+ e = e + (127 - 15);
+ m = m << 13;
+
+ // Assemble s, e and m.
+ return (s << 31) | (e << 23) | m;
+ }
+
+ int _h;
+
+ static Uint32List _toFloatUint32;
+ static Float32List _toFloatFloat32;
+ static Uint16List _eLut;
+}
diff --git a/image/lib/src/hdr/hdr_bloom.dart b/image/lib/src/hdr/hdr_bloom.dart
old mode 100644
new mode 100755
index 7b07283..8580117
--- a/image/lib/src/hdr/hdr_bloom.dart
+++ b/image/lib/src/hdr/hdr_bloom.dart
@@ -1,76 +1,76 @@
-import 'dart:math' as Math;
-import 'dart:typed_data';
-
-import 'hdr_image.dart';
-
-/**
- * Applies an HDR bloom filter to the image, in-place.
- */
-HdrImage hdrBloom(HdrImage hdr, {double radius: 0.01, double weight: 0.1}) {
- double _lerp(double t, double a, double b) => (1.0 - t) * a + t * b;
-
- //int nPix = xResolution * yResolution;
- // Possibly apply bloom effect to image
- if (radius > 0.0 && weight > 0.0) {
- // Compute image-space extent of bloom effect
- int bloomSupport = (radius * Math.max(hdr.width, hdr.height)).ceil();
- int bloomWidth = bloomSupport ~/ 2;
- // Initialize bloom filter table
- Float32List bloomFilter = new Float32List(bloomWidth * bloomWidth);
- for (int i = 0; i < bloomWidth * bloomWidth; ++i) {
- double dist = Math.sqrt(i / bloomWidth);
- bloomFilter[i] = Math.pow(Math.max(0.0, 1.0 - dist), 4.0);
- }
-
- // Apply bloom filter to image pixels
- Float32List bloomImage = new Float32List(3 * hdr.width * hdr.height);
- for (int y = 0, offset = 0; y < hdr.height; ++y) {
- for (int x = 0; x < hdr.width; ++x, ++offset) {
- // Compute bloom for pixel _(x,y)_
- // Compute extent of pixels contributing bloom
- int x0 = Math.max(0, x - bloomWidth);
- int x1 = Math.min(x + bloomWidth, hdr.width - 1);
- int y0 = Math.max(0, y - bloomWidth);
- int y1 = Math.min(y + bloomWidth, hdr.height - 1);
-
- double sumWt = 0.0;
- for (int by = y0; by <= y1; ++by) {
- for (int bx = x0; bx <= x1; ++bx) {
- // Accumulate bloom from pixel $(bx,by)$
- int dx = x - bx;
- int dy = y - by;
- if (dx == 0 && dy == 0) {
- continue;
- }
- int dist2 = dx * dx + dy * dy;
- if (dist2 < bloomWidth * bloomWidth) {
- //int bloomOffset = bx + by * hdr.width;
- double wt = bloomFilter[dist2];
-
- sumWt += wt;
-
- bloomImage[3 * offset] += wt * hdr.getRed(bx, by);
- bloomImage[3 * offset + 1] += wt * hdr.getGreen(bx, by);
- bloomImage[3 * offset + 2] += wt * hdr.getBlue(bx, by);
- }
- }
- }
-
- bloomImage[3 * offset] /= sumWt;
- bloomImage[3 * offset + 1] /= sumWt;
- bloomImage[3 * offset + 2] /= sumWt;
- }
- }
-
- // Mix bloom effect into each pixel
- for (int y = 0, offset = 0; y < hdr.height; ++y) {
- for (int x = 0; x < hdr.width; ++x, offset += 3) {
- hdr.setRed(x, y, _lerp(weight, hdr.getRed(x, y), bloomImage[offset]));
- hdr.setGreen(x, y, _lerp(weight, hdr.getGreen(x, y), bloomImage[offset + 1]));
- hdr.setBlue(x, y, _lerp(weight, hdr.getBlue(x, y), bloomImage[offset + 2]));
- }
- }
- }
-
- return hdr;
-}
+import 'dart:math' as Math;
+import 'dart:typed_data';
+
+import 'hdr_image.dart';
+
+/**
+ * Applies an HDR bloom filter to the image, in-place.
+ */
+HdrImage hdrBloom(HdrImage hdr, {double radius: 0.01, double weight: 0.1}) {
+ double _lerp(double t, double a, double b) => (1.0 - t) * a + t * b;
+
+ //int nPix = xResolution * yResolution;
+ // Possibly apply bloom effect to image
+ if (radius > 0.0 && weight > 0.0) {
+ // Compute image-space extent of bloom effect
+ int bloomSupport = (radius * Math.max(hdr.width, hdr.height)).ceil();
+ int bloomWidth = bloomSupport ~/ 2;
+ // Initialize bloom filter table
+ Float32List bloomFilter = Float32List(bloomWidth * bloomWidth);
+ for (int i = 0; i < bloomWidth * bloomWidth; ++i) {
+ double dist = Math.sqrt(i / bloomWidth);
+ bloomFilter[i] = Math.pow(Math.max(0.0, 1.0 - dist), 4.0);
+ }
+
+ // Apply bloom filter to image pixels
+ Float32List bloomImage = Float32List(3 * hdr.width * hdr.height);
+ for (int y = 0, offset = 0; y < hdr.height; ++y) {
+ for (int x = 0; x < hdr.width; ++x, ++offset) {
+ // Compute bloom for pixel _(x,y)_
+ // Compute extent of pixels contributing bloom
+ int x0 = Math.max(0, x - bloomWidth);
+ int x1 = Math.min(x + bloomWidth, hdr.width - 1);
+ int y0 = Math.max(0, y - bloomWidth);
+ int y1 = Math.min(y + bloomWidth, hdr.height - 1);
+
+ double sumWt = 0.0;
+ for (int by = y0; by <= y1; ++by) {
+ for (int bx = x0; bx <= x1; ++bx) {
+ // Accumulate bloom from pixel $(bx,by)$
+ int dx = x - bx;
+ int dy = y - by;
+ if (dx == 0 && dy == 0) {
+ continue;
+ }
+ int dist2 = dx * dx + dy * dy;
+ if (dist2 < bloomWidth * bloomWidth) {
+ //int bloomOffset = bx + by * hdr.width;
+ double wt = bloomFilter[dist2];
+
+ sumWt += wt;
+
+ bloomImage[3 * offset] += wt * hdr.getRed(bx, by);
+ bloomImage[3 * offset + 1] += wt * hdr.getGreen(bx, by);
+ bloomImage[3 * offset + 2] += wt * hdr.getBlue(bx, by);
+ }
+ }
+ }
+
+ bloomImage[3 * offset] /= sumWt;
+ bloomImage[3 * offset + 1] /= sumWt;
+ bloomImage[3 * offset + 2] /= sumWt;
+ }
+ }
+
+ // Mix bloom effect into each pixel
+ for (int y = 0, offset = 0; y < hdr.height; ++y) {
+ for (int x = 0; x < hdr.width; ++x, offset += 3) {
+ hdr.setRed(x, y, _lerp(weight, hdr.getRed(x, y), bloomImage[offset]));
+ hdr.setGreen(x, y, _lerp(weight, hdr.getGreen(x, y), bloomImage[offset + 1]));
+ hdr.setBlue(x, y, _lerp(weight, hdr.getBlue(x, y), bloomImage[offset + 2]));
+ }
+ }
+ }
+
+ return hdr;
+}
diff --git a/image/lib/src/hdr/hdr_gamma.dart b/image/lib/src/hdr/hdr_gamma.dart
old mode 100644
new mode 100755
index 731a7b9..caa92d3
--- a/image/lib/src/hdr/hdr_gamma.dart
+++ b/image/lib/src/hdr/hdr_gamma.dart
@@ -1,22 +1,22 @@
-import 'dart:math' as Math;
-
-import 'hdr_image.dart';
-
-/**
- * Apply gamma scaling to the HDR image, in-place.
- */
-HdrImage hdrGamma(HdrImage hdr, {double gamma: 2.2}) {
- for (int y = 0; y < hdr.height; ++y) {
- for (int x = 0; x < hdr.width; ++x) {
- double r = Math.pow(hdr.getRed(x, y), 1.0 / gamma);
- double g = Math.pow(hdr.getGreen(x, y), 1.0 / gamma);
- double b = Math.pow(hdr.getBlue(x, y), 1.0 / gamma);
-
- hdr.setRed(x, y, r);
- hdr.setGreen(x, y, g);
- hdr.setBlue(x, y, b);
- }
- }
-
- return hdr;
-}
+import 'dart:math' as Math;
+
+import 'hdr_image.dart';
+
+/**
+ * Apply gamma scaling to the HDR image, in-place.
+ */
+HdrImage hdrGamma(HdrImage hdr, {double gamma: 2.2}) {
+ for (int y = 0; y < hdr.height; ++y) {
+ for (int x = 0; x < hdr.width; ++x) {
+ double r = Math.pow(hdr.getRed(x, y), 1.0 / gamma);
+ double g = Math.pow(hdr.getGreen(x, y), 1.0 / gamma);
+ double b = Math.pow(hdr.getBlue(x, y), 1.0 / gamma);
+
+ hdr.setRed(x, y, r);
+ hdr.setGreen(x, y, g);
+ hdr.setBlue(x, y, b);
+ }
+ }
+
+ return hdr;
+}
diff --git a/image/lib/src/hdr/hdr_image.dart b/image/lib/src/hdr/hdr_image.dart
old mode 100644
new mode 100755
index 125a292..d95299c
--- a/image/lib/src/hdr/hdr_image.dart
+++ b/image/lib/src/hdr/hdr_image.dart
@@ -1,273 +1,273 @@
-import 'dart:typed_data';
-
-import '../image.dart';
-import 'hdr_slice.dart';
-
-/**
- * A high dynamic range RGBA image stored in 16-bit or 32-bit floating-point
- * channels.
- */
-class HdrImage {
- static const int HALF = 1;
- static const int FLOAT = 2;
- static const int UINT = 0;
-
- /// Red value of a sample
- static const String R = 'R';
- /// Green value of a sample
- static const String G = 'G';
- /// Blue value of a sample
- static const String B = 'B';
- /// Alpha/opacity
- static const String A = 'A';
- /// Distance of the front of a sample from the viewer
- static const String Z = 'Z';
- /// A numerical identifier for the object represented by a sample.
- static const String ID = 'id';
-
- final Map<String, HdrSlice> slices = {};
- HdrSlice red;
- HdrSlice green;
- HdrSlice blue;
- HdrSlice alpha;
- HdrSlice depth;
-
- HdrImage() {
- }
-
- /**
- * Create an RGB[A] image.
- */
- HdrImage.create(int width, int height, int channels, int format) {
- if (channels < 0 || channels > 4) {
- return;
- }
- if (format != HALF && format != FLOAT && format != UINT) {
- return;
- }
-
- const List<String> channelList = const [R, G, B, A];
- for (int i = 0; i < channels; ++i) {
- addSlice(new HdrSlice(channelList[i], width, height, format));
- }
- }
-
- /**
- * Create a copy of the [other] HdrImage.
- */
- HdrImage.from(HdrImage other) {
- for (String ch in other.slices.keys) {
- HdrSlice slice = other.slices[ch];
- addSlice(new HdrSlice.from(slice));
- }
- }
-
- /**
- * Create an HDR image from a LDR [Image] by transforming the channel values
- * to the range [0, 1].
- */
- HdrImage.fromImage(Image other) {
- addSlice(new HdrSlice(R, other.width, other.height, HALF));
- addSlice(new HdrSlice(G, other.width, other.height, HALF));
- addSlice(new HdrSlice(B, other.width, other.height, HALF));
- if (other.format == Image.RGBA) {
- addSlice(new HdrSlice(A, other.width, other.height, HALF));
- }
- Uint8List rgb = other.getBytes();
- for (int y = 0, si = 0; y < other.height; ++y) {
- for (int x = 0; x < other.width; ++x) {
- red.setFloat(x, y, rgb[si++] / 255.0);
- green.setFloat(x, y, rgb[si++] / 255.0);
- blue.setFloat(x, y, rgb[si++] / 255.0);
- if (alpha != null) {
- alpha.setFloat(x, y, rgb[si++] / 255.0);
- }
- }
- }
- }
-
- /**
- * Does the image have any color channels?
- */
- bool get hasColor => red != null || green != null || blue != null;
-
- /**
- * Does the image have an alpha channel?
- */
- bool get hasAlpha => alpha != null;
-
- /**
- * Does the image have a depth channel?
- */
- bool get hasDepth => depth != null;
-
- /**
- * The width of the framebuffer.
- */
- int get width => slices.isEmpty ? 0 : slices.values.first.width;
-
- /**
- * The height of the framebuffer.
- */
- int get height => slices.isEmpty ? 0 : slices.values.first.height;
-
- /**
- * Get the value of the red channel at the given pixel coordinates [x], [y].
- */
- double getRed(int x, int y) {
- return red != null ? red.getFloat(x, y) : 0.0;
- }
-
- /**
- * Set the value of the red channel at the given pixel coordinates [x], [y].
- */
- void setRed(int x, int y, double c) {
- if (red != null) {
- red.setFloat(x, y, c);
- }
- }
-
- void setRedInt(int x, int y, int c) {
- if (red != null) {
- red.setInt(x, y, c);
- }
- }
-
- /**
- * Get the value of the green channel at the given pixel coordinates [x], [y].
- */
- double getGreen(int x, int y) {
- return green != null ? green.getFloat(x, y) : 0.0;
- }
-
- /**
- * Set the value of the green channel at the given pixel coordinates [x], [y].
- */
- void setGreen(int x, int y, double c) {
- if (green != null) {
- green.setFloat(x, y, c);
- }
- }
-
- void setGreenInt(int x, int y, int c) {
- if (green != null) {
- green.setInt(x, y, c);
- }
- }
-
- /**
- * Get the value of the blue channel at the given pixel coordinates [x], [y].
- */
- double getBlue(int x, int y) {
- return blue != null ? blue.getFloat(x, y) : 0.0;
- }
-
- /**
- * Set the value of the blue channel at the given pixel coordinates [x], [y].
- */
- void setBlue(int x, int y, double c) {
- if (blue != null) {
- blue.setFloat(x, y, c);
- }
- }
-
- void setBlueInt(int x, int y, int c) {
- if (blue != null) {
- blue.setInt(x, y, c);
- }
- }
-
- /**
- * Get the value of the alpha channel at the given pixel coordinates [x], [y].
- */
- double getAlpha(int x, int y) {
- return alpha != null ? alpha.getFloat(x, y) : 0.0;
- }
-
- /**
- * Set the value of the alpha channel at the given pixel coordinates [x], [y].
- */
- void setAlpha(int x, int y, double c) {
- if (alpha != null) {
- alpha.setFloat(x, y, c);
- }
- }
-
- void setAlphaInt(int x, int y, int c) {
- if (alpha != null) {
- alpha.setInt(x, y, c);
- }
- }
-
- /**
- * Get the value of the depth channel at the given pixel coordinates [x], [y].
- */
- double getDepth(int x, int y) {
- return depth != null ? depth.getFloat(x, y) : 0.0;
- }
-
- /**
- * Set the value of the depth channel at the given pixel coordinates [x], [y].
- */
- void setDepth(int x, int y, double c) {
- if (depth != null) {
- depth.setFloat(x, y, c);
- }
- }
-
- /**
- * Does this image contain the given channel?
- */
- bool hasChannel(String ch) => slices.containsKey(ch);
-
- /**
- * Access a framebuffer slice by name.
- */
- HdrSlice operator[](String ch) => slices[ch];
-
- /**
- * Add a channel [slice] to the
- */
- void addSlice(HdrSlice slice) {
- String ch = slice.name;
- slices[ch] = slice;
- switch (ch) {
- case R:
- red = slice;
- break;
- case G:
- green = slice;
- break;
- case B:
- blue = slice;
- break;
- case A:
- alpha = slice;
- break;
- case Z:
- depth = slice;
- break;
- }
- }
-
- /**
- * Convert the framebuffer to an floating-point image, as a sequence of
- * floats in RGBA order.
- */
- Float32List toFloatRgba() {
- Float32List rgba = new Float32List(width * height * 4);
- int w = width;
- int h = height;
- for (int y = 0, di = 0; y < h; ++y) {
- for (int x = 0; x < w; ++x) {
- rgba[di++] = red == null ? 0.0 : red.getFloat(x, y);
- rgba[di++] = green == null ? 0.0 : green.getFloat(x, y);
- rgba[di++] = blue == null ? 0.0 : blue.getFloat(x, y);
- rgba[di++] = alpha == null ? 1.0 : alpha.getFloat(x, y);
- }
- }
-
- return rgba;
- }
-}
-
+import 'dart:typed_data';
+
+import '../image.dart';
+import 'hdr_slice.dart';
+
+/**
+ * A high dynamic range RGBA image stored in 16-bit or 32-bit floating-point
+ * channels.
+ */
+class HdrImage {
+ static const int HALF = 1;
+ static const int FLOAT = 2;
+ static const int UINT = 0;
+
+ /// Red value of a sample
+ static const String R = 'R';
+ /// Green value of a sample
+ static const String G = 'G';
+ /// Blue value of a sample
+ static const String B = 'B';
+ /// Alpha/opacity
+ static const String A = 'A';
+ /// Distance of the front of a sample from the viewer
+ static const String Z = 'Z';
+ /// A numerical identifier for the object represented by a sample.
+ static const String ID = 'id';
+
+ final Map<String, HdrSlice> slices = {};
+ HdrSlice red;
+ HdrSlice green;
+ HdrSlice blue;
+ HdrSlice alpha;
+ HdrSlice depth;
+
+ HdrImage() {
+ }
+
+ /**
+ * Create an RGB[A] image.
+ */
+ HdrImage.create(int width, int height, int channels, int format) {
+ if (channels < 0 || channels > 4) {
+ return;
+ }
+ if (format != HALF && format != FLOAT && format != UINT) {
+ return;
+ }
+
+ const List<String> channelList = const [R, G, B, A];
+ for (int i = 0; i < channels; ++i) {
+ addSlice(new HdrSlice(channelList[i], width, height, format));
+ }
+ }
+
+ /**
+ * Create a copy of the [other] HdrImage.
+ */
+ HdrImage.from(HdrImage other) {
+ for (String ch in other.slices.keys) {
+ HdrSlice slice = other.slices[ch];
+ addSlice(new HdrSlice.from(slice));
+ }
+ }
+
+ /**
+ * Create an HDR image from a LDR [Image] by transforming the channel values
+ * to the range [0, 1].
+ */
+ HdrImage.fromImage(Image other) {
+ addSlice(new HdrSlice(R, other.width, other.height, HALF));
+ addSlice(new HdrSlice(G, other.width, other.height, HALF));
+ addSlice(new HdrSlice(B, other.width, other.height, HALF));
+ if (other.format == Image.RGBA) {
+ addSlice(new HdrSlice(A, other.width, other.height, HALF));
+ }
+ Uint8List rgb = other.getBytes();
+ for (int y = 0, si = 0; y < other.height; ++y) {
+ for (int x = 0; x < other.width; ++x) {
+ red.setFloat(x, y, rgb[si++] / 255.0);
+ green.setFloat(x, y, rgb[si++] / 255.0);
+ blue.setFloat(x, y, rgb[si++] / 255.0);
+ if (alpha != null) {
+ alpha.setFloat(x, y, rgb[si++] / 255.0);
+ }
+ }
+ }
+ }
+
+ /**
+ * Does the image have any color channels?
+ */
+ bool get hasColor => red != null || green != null || blue != null;
+
+ /**
+ * Does the image have an alpha channel?
+ */
+ bool get hasAlpha => alpha != null;
+
+ /**
+ * Does the image have a depth channel?
+ */
+ bool get hasDepth => depth != null;
+
+ /**
+ * The width of the framebuffer.
+ */
+ int get width => slices.isEmpty ? 0 : slices.values.first.width;
+
+ /**
+ * The height of the framebuffer.
+ */
+ int get height => slices.isEmpty ? 0 : slices.values.first.height;
+
+ /**
+ * Get the value of the red channel at the given pixel coordinates [x], [y].
+ */
+ double getRed(int x, int y) {
+ return red != null ? red.getFloat(x, y) : 0.0;
+ }
+
+ /**
+ * Set the value of the red channel at the given pixel coordinates [x], [y].
+ */
+ void setRed(int x, int y, double c) {
+ if (red != null) {
+ red.setFloat(x, y, c);
+ }
+ }
+
+ void setRedInt(int x, int y, int c) {
+ if (red != null) {
+ red.setInt(x, y, c);
+ }
+ }
+
+ /**
+ * Get the value of the green channel at the given pixel coordinates [x], [y].
+ */
+ double getGreen(int x, int y) {
+ return green != null ? green.getFloat(x, y) : 0.0;
+ }
+
+ /**
+ * Set the value of the green channel at the given pixel coordinates [x], [y].
+ */
+ void setGreen(int x, int y, double c) {
+ if (green != null) {
+ green.setFloat(x, y, c);
+ }
+ }
+
+ void setGreenInt(int x, int y, int c) {
+ if (green != null) {
+ green.setInt(x, y, c);
+ }
+ }
+
+ /**
+ * Get the value of the blue channel at the given pixel coordinates [x], [y].
+ */
+ double getBlue(int x, int y) {
+ return blue != null ? blue.getFloat(x, y) : 0.0;
+ }
+
+ /**
+ * Set the value of the blue channel at the given pixel coordinates [x], [y].
+ */
+ void setBlue(int x, int y, double c) {
+ if (blue != null) {
+ blue.setFloat(x, y, c);
+ }
+ }
+
+ void setBlueInt(int x, int y, int c) {
+ if (blue != null) {
+ blue.setInt(x, y, c);
+ }
+ }
+
+ /**
+ * Get the value of the alpha channel at the given pixel coordinates [x], [y].
+ */
+ double getAlpha(int x, int y) {
+ return alpha != null ? alpha.getFloat(x, y) : 0.0;
+ }
+
+ /**
+ * Set the value of the alpha channel at the given pixel coordinates [x], [y].
+ */
+ void setAlpha(int x, int y, double c) {
+ if (alpha != null) {
+ alpha.setFloat(x, y, c);
+ }
+ }
+
+ void setAlphaInt(int x, int y, int c) {
+ if (alpha != null) {
+ alpha.setInt(x, y, c);
+ }
+ }
+
+ /**
+ * Get the value of the depth channel at the given pixel coordinates [x], [y].
+ */
+ double getDepth(int x, int y) {
+ return depth != null ? depth.getFloat(x, y) : 0.0;
+ }
+
+ /**
+ * Set the value of the depth channel at the given pixel coordinates [x], [y].
+ */
+ void setDepth(int x, int y, double c) {
+ if (depth != null) {
+ depth.setFloat(x, y, c);
+ }
+ }
+
+ /**
+ * Does this image contain the given channel?
+ */
+ bool hasChannel(String ch) => slices.containsKey(ch);
+
+ /**
+ * Access a framebuffer slice by name.
+ */
+ HdrSlice operator[](String ch) => slices[ch];
+
+ /**
+ * Add a channel [slice] to the
+ */
+ void addSlice(HdrSlice slice) {
+ String ch = slice.name;
+ slices[ch] = slice;
+ switch (ch) {
+ case R:
+ red = slice;
+ break;
+ case G:
+ green = slice;
+ break;
+ case B:
+ blue = slice;
+ break;
+ case A:
+ alpha = slice;
+ break;
+ case Z:
+ depth = slice;
+ break;
+ }
+ }
+
+ /**
+ * Convert the framebuffer to an floating-point image, as a sequence of
+ * floats in RGBA order.
+ */
+ Float32List toFloatRgba() {
+ Float32List rgba = Float32List(width * height * 4);
+ int w = width;
+ int h = height;
+ for (int y = 0, di = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ rgba[di++] = red == null ? 0.0 : red.getFloat(x, y);
+ rgba[di++] = green == null ? 0.0 : green.getFloat(x, y);
+ rgba[di++] = blue == null ? 0.0 : blue.getFloat(x, y);
+ rgba[di++] = alpha == null ? 1.0 : alpha.getFloat(x, y);
+ }
+ }
+
+ return rgba;
+ }
+}
+
diff --git a/image/lib/src/hdr/hdr_slice.dart b/image/lib/src/hdr/hdr_slice.dart
old mode 100644
new mode 100755
index af343ad..2da0002
--- a/image/lib/src/hdr/hdr_slice.dart
+++ b/image/lib/src/hdr/hdr_slice.dart
@@ -1,92 +1,92 @@
-import 'dart:typed_data';
-
-import 'half.dart';
-import 'hdr_image.dart';
-
-/**
- * A slice is the data for an image framebuffer for a single channel.
- */
-class HdrSlice {
- final String name;
- final int width;
- final int height;
- /// Indicates the type of data stored by the slice, either [HdrImage.HALF],
- /// [HdrImage.FLOAT], or [HdrImage.UINT].
- final int type;
- /// [data] will be either Uint16List, Float32List, or Uint32List depending
- /// on the type being HALF, FLOAT or UINT respectively.
- final data;
-
- HdrSlice(this.name, int width, int height, int type) :
- this.width = width,
- this.height = height,
- this.type = type,
- data = type == HdrImage.HALF ? new Uint16List(width * height) :
- type == HdrImage.FLOAT ? new Float32List(width * height) :
- new Uint32List(width * height);
-
- /**
- * Create a copy of the [other] HdrSlice.
- */
- HdrSlice.from(HdrSlice other) :
- name = other.name ,
- width = other.width,
- height = other.height,
- type = other.type,
- data = other.type == HdrImage.HALF ? new Uint16List.fromList(other.data) :
- other.type == HdrImage.FLOAT ? new Float32List.fromList(other.data) :
- new Uint32List.fromList(other.data);
-
- /**
- * Get the raw bytes of the data buffer.
- */
- Uint8List getBytes() => new Uint8List.view(data.buffer);
-
- /**
- * Does this channel store floating-point data?
- */
- bool get isFloat => type == HdrImage.FLOAT || type == HdrImage.HALF;
-
- /**
- * Get the float value of the sample at the coordinates [x],[y].
- * [Half] samples are converted to double.
- * An exception will occur if the slice stores UINT data.
- */
- double getFloat(int x, int y) {
- int pi = y * width + x;
- double s = (type == HdrImage.HALF) ?
- Half.HalfToDouble(data[pi]) : data[pi];
- return s;
- }
-
- /**
- * Set the float value of the sample at the coordinates [x],[y] for
- * [FLOAT] or [HALF] slices.
- */
- void setFloat(int x, int y, double v) {
- int pi = y * width + x;
- if (type == HdrImage.FLOAT) {
- data[pi] = v;
- } else if (type == HdrImage.HALF) {
- data[pi] = Half.DoubleToHalf(v);
- }
- }
-
- /**
- * Get the int value of the sample at the coordinates [x],[y].
- * An exception will occur if the slice stores FLOAT or HALF data.
- */
- int getInt(int x, int y) {
- int pi = y * width + x;
- return data[pi];
- }
-
- /**
- * Set the int value of the sample at the coordinates [x],[y] for [UINT]
- * slices.
- */
- void setInt(int x, int y, int v) {
- int pi = y * width + x;
- data[pi] = v;
- }
-}
+import 'dart:typed_data';
+
+import 'half.dart';
+import 'hdr_image.dart';
+
+/**
+ * A slice is the data for an image framebuffer for a single channel.
+ */
+class HdrSlice {
+ final String name;
+ final int width;
+ final int height;
+ /// Indicates the type of data stored by the slice, either [HdrImage.HALF],
+ /// [HdrImage.FLOAT], or [HdrImage.UINT].
+ final int type;
+ /// [data] will be either Uint16List, Float32List, or Uint32List depending
+ /// on the type being HALF, FLOAT or UINT respectively.
+ final data;
+
+ HdrSlice(this.name, int width, int height, int type) :
+ this.width = width,
+ this.height = height,
+ this.type = type,
+ data = type == HdrImage.HALF ? new Uint16List(width * height) :
+ type == HdrImage.FLOAT ? new Float32List(width * height) :
+ new Uint32List(width * height);
+
+ /**
+ * Create a copy of the [other] HdrSlice.
+ */
+ HdrSlice.from(HdrSlice other) :
+ name = other.name ,
+ width = other.width,
+ height = other.height,
+ type = other.type,
+ data = other.type == HdrImage.HALF ? new Uint16List.fromList(other.data) :
+ other.type == HdrImage.FLOAT ? new Float32List.fromList(other.data) :
+ new Uint32List.fromList(other.data);
+
+ /**
+ * Get the raw bytes of the data buffer.
+ */
+ Uint8List getBytes() => new Uint8List.view(data.buffer);
+
+ /**
+ * Does this channel store floating-point data?
+ */
+ bool get isFloat => type == HdrImage.FLOAT || type == HdrImage.HALF;
+
+ /**
+ * Get the float value of the sample at the coordinates [x],[y].
+ * [Half] samples are converted to double.
+ * An exception will occur if the slice stores UINT data.
+ */
+ double getFloat(int x, int y) {
+ int pi = y * width + x;
+ double s = (type == HdrImage.HALF) ?
+ Half.HalfToDouble(data[pi]) : data[pi];
+ return s;
+ }
+
+ /**
+ * Set the float value of the sample at the coordinates [x],[y] for
+ * [FLOAT] or [HALF] slices.
+ */
+ void setFloat(int x, int y, double v) {
+ int pi = y * width + x;
+ if (type == HdrImage.FLOAT) {
+ data[pi] = v;
+ } else if (type == HdrImage.HALF) {
+ data[pi] = Half.DoubleToHalf(v);
+ }
+ }
+
+ /**
+ * Get the int value of the sample at the coordinates [x],[y].
+ * An exception will occur if the slice stores FLOAT or HALF data.
+ */
+ int getInt(int x, int y) {
+ int pi = y * width + x;
+ return data[pi];
+ }
+
+ /**
+ * Set the int value of the sample at the coordinates [x],[y] for [UINT]
+ * slices.
+ */
+ void setInt(int x, int y, int v) {
+ int pi = y * width + x;
+ data[pi] = v;
+ }
+}
diff --git a/image/lib/src/hdr/hdr_to_image.dart b/image/lib/src/hdr/hdr_to_image.dart
old mode 100644
new mode 100755
index 3963c83..00d3da9
--- a/image/lib/src/hdr/hdr_to_image.dart
+++ b/image/lib/src/hdr/hdr_to_image.dart
@@ -1,90 +1,90 @@
-import 'dart:math' as Math;
-import 'dart:typed_data';
-
-import '../image.dart';
-import '../image_exception.dart';
-import 'hdr_image.dart';
-
-/**
- * Convert a high dynamic range image to a low dynamic range image,
- * with optional exposure control.
- */
-Image hdrToImage(HdrImage hdr, {double exposure}) {
- double _knee(double x, double f) {
- return Math.log(x * f + 1.0) / f;
- }
-
- double _gamma(double h, double m) {
- double x = Math.max(0.0, h * m);
-
- if (x > 1.0) {
- x = 1.0 + _knee(x - 1, 0.184874);
- }
-
- return (Math.pow(x, 0.4545) * 84.66);
- }
-
- Image image = new Image(hdr.width, hdr.height);
- Uint8List pixels = image.getBytes();
-
- if (!hdr.hasColor) {
- throw new ImageException('Only RGB[A] images are currently supported.');
- }
-
- double m = exposure != null ?
- Math.pow(2.0, (exposure + 2.47393).clamp(-20.0, 20.0)) :
- 1.0;
-
- for (int y = 0, di = 0; y < hdr.height; ++y) {
- for (int x = 0; x < hdr.width; ++x) {
- double r = hdr.getRed(x, y);
- double g = hdr.getGreen(x, y);
- double b = hdr.getBlue(x, y);
-
- if (r.isInfinite || r.isNaN) {
- r = 0.0;
- }
- if (g.isInfinite || g.isNaN) {
- g = 0.0;
- }
- if (b.isInfinite || b.isNaN) {
- b = 0.0;
- }
-
- double ri, gi, bi;
- if (exposure != null) {
- ri = _gamma(r, m);
- gi = _gamma(g, m);
- bi = _gamma(b, m);
- } else {
- ri = (r * 255.0);
- gi = (g * 255.0);
- bi = (b * 255.0);
- }
-
- // Normalize the color
- double mi = Math.max(ri, Math.max(gi, bi));
- if (mi > 255.0) {
- ri = 255.0 * (ri / mi);
- gi = 255.0 * (gi / mi);
- bi = 255.0 * (bi / mi);
- }
-
- pixels[di++] = ri.toInt().clamp(0, 255);
- pixels[di++] = gi.toInt().clamp(0, 255);
- pixels[di++] = bi.toInt().clamp(0, 255);
-
- if (hdr.alpha != null) {
- double a = hdr.alpha.getFloat(x, y);
- if (a.isInfinite || a.isNaN) {
- a = 1.0;
- }
- pixels[di++] = (a * 255.0).toInt().clamp(0, 255);
- } else {
- pixels[di++] = 255;
- }
- }
- }
-
- return image;
-}
+import 'dart:math' as Math;
+import 'dart:typed_data';
+
+import '../image.dart';
+import '../image_exception.dart';
+import 'hdr_image.dart';
+
+/**
+ * Convert a high dynamic range image to a low dynamic range image,
+ * with optional exposure control.
+ */
+Image hdrToImage(HdrImage hdr, {double exposure}) {
+ double _knee(double x, double f) {
+ return Math.log(x * f + 1.0) / f;
+ }
+
+ double _gamma(double h, double m) {
+ double x = Math.max(0.0, h * m);
+
+ if (x > 1.0) {
+ x = 1.0 + _knee(x - 1, 0.184874);
+ }
+
+ return (Math.pow(x, 0.4545) * 84.66);
+ }
+
+ Image image = Image(hdr.width, hdr.height);
+ Uint8List pixels = image.getBytes();
+
+ if (!hdr.hasColor) {
+ throw new ImageException('Only RGB[A] images are currently supported.');
+ }
+
+ double m = exposure != null ?
+ Math.pow(2.0, (exposure + 2.47393).clamp(-20.0, 20.0)) :
+ 1.0;
+
+ for (int y = 0, di = 0; y < hdr.height; ++y) {
+ for (int x = 0; x < hdr.width; ++x) {
+ double r = hdr.getRed(x, y);
+ double g = hdr.getGreen(x, y);
+ double b = hdr.getBlue(x, y);
+
+ if (r.isInfinite || r.isNaN) {
+ r = 0.0;
+ }
+ if (g.isInfinite || g.isNaN) {
+ g = 0.0;
+ }
+ if (b.isInfinite || b.isNaN) {
+ b = 0.0;
+ }
+
+ double ri, gi, bi;
+ if (exposure != null) {
+ ri = _gamma(r, m);
+ gi = _gamma(g, m);
+ bi = _gamma(b, m);
+ } else {
+ ri = (r * 255.0);
+ gi = (g * 255.0);
+ bi = (b * 255.0);
+ }
+
+ // Normalize the color
+ double mi = Math.max(ri, Math.max(gi, bi));
+ if (mi > 255.0) {
+ ri = 255.0 * (ri / mi);
+ gi = 255.0 * (gi / mi);
+ bi = 255.0 * (bi / mi);
+ }
+
+ pixels[di++] = ri.toInt().clamp(0, 255);
+ pixels[di++] = gi.toInt().clamp(0, 255);
+ pixels[di++] = bi.toInt().clamp(0, 255);
+
+ if (hdr.alpha != null) {
+ double a = hdr.alpha.getFloat(x, y);
+ if (a.isInfinite || a.isNaN) {
+ a = 1.0;
+ }
+ pixels[di++] = (a * 255.0).toInt().clamp(0, 255);
+ } else {
+ pixels[di++] = 255;
+ }
+ }
+ }
+
+ return image;
+}
diff --git a/image/lib/src/hdr/reinhard_tone_map.dart b/image/lib/src/hdr/reinhard_tone_map.dart
old mode 100644
new mode 100755
index 3627b0c..b32400e
--- a/image/lib/src/hdr/reinhard_tone_map.dart
+++ b/image/lib/src/hdr/reinhard_tone_map.dart
@@ -1,47 +1,47 @@
-import 'dart:math' as Math;
-
-import 'hdr_image.dart';
-
-/**
- * Applies Reinhard tone mapping to the hdr image, in-place.
- */
-HdrImage reinhardToneMap(HdrImage hdr) {
- const List<double> yw = const [0.212671, 0.715160, 0.072169 ];
-
- // Compute world adaptation luminance, _Ywa_
- double Ywa = 0.0;
- for (int y = 0; y < hdr.height; ++y) {
- for (int x = 0; x < hdr.width; ++x) {
- double r = hdr.getRed(x, y);
- double g = hdr.getGreen(x, y);
- double b = hdr.getBlue(x, y);
-
- double lum = yw[0] * r + yw[1] * g + yw[2] * b;
- if (lum > 1.0e-4) {
- Ywa += Math.log(lum);
- }
- }
- }
-
- Ywa = Math.exp(Ywa / (hdr.width * hdr.height));
-
- double invY2 = 1.0 / (Ywa * Ywa);
-
- for (int y = 0; y < hdr.height; ++y) {
- for (int x = 0; x < hdr.width; ++x) {
- double r = hdr.getRed(x, y);
- double g = hdr.getGreen(x, y);
- double b = hdr.getBlue(x, y);
-
- double lum = yw[0] * r + yw[1] * g + yw[2] * b;
-
- double s = (1.0 + lum * invY2) / (1.0 + lum);
-
- hdr.setRed(x, y, r * s);
- hdr.setGreen(x, y, g * s);
- hdr.setBlue(x, y, b * s);
- }
- }
-
- return hdr;
-}
+import 'dart:math' as Math;
+
+import 'hdr_image.dart';
+
+/**
+ * Applies Reinhard tone mapping to the hdr image, in-place.
+ */
+HdrImage reinhardToneMap(HdrImage hdr) {
+ const List<double> yw = const [0.212671, 0.715160, 0.072169 ];
+
+ // Compute world adaptation luminance, _Ywa_
+ double Ywa = 0.0;
+ for (int y = 0; y < hdr.height; ++y) {
+ for (int x = 0; x < hdr.width; ++x) {
+ double r = hdr.getRed(x, y);
+ double g = hdr.getGreen(x, y);
+ double b = hdr.getBlue(x, y);
+
+ double lum = yw[0] * r + yw[1] * g + yw[2] * b;
+ if (lum > 1.0e-4) {
+ Ywa += Math.log(lum);
+ }
+ }
+ }
+
+ Ywa = Math.exp(Ywa / (hdr.width * hdr.height));
+
+ double invY2 = 1.0 / (Ywa * Ywa);
+
+ for (int y = 0; y < hdr.height; ++y) {
+ for (int x = 0; x < hdr.width; ++x) {
+ double r = hdr.getRed(x, y);
+ double g = hdr.getGreen(x, y);
+ double b = hdr.getBlue(x, y);
+
+ double lum = yw[0] * r + yw[1] * g + yw[2] * b;
+
+ double s = (1.0 + lum * invY2) / (1.0 + lum);
+
+ hdr.setRed(x, y, r * s);
+ hdr.setGreen(x, y, g * s);
+ hdr.setBlue(x, y, b * s);
+ }
+ }
+
+ return hdr;
+}
diff --git a/image/lib/src/icc_profile_data.dart b/image/lib/src/icc_profile_data.dart
old mode 100644
new mode 100755
index 8c40a04..823cceb
--- a/image/lib/src/icc_profile_data.dart
+++ b/image/lib/src/icc_profile_data.dart
@@ -1,45 +1,45 @@
-import 'dart:typed_data';
-import 'package:archive/archive.dart';
-
-enum ICCPCompression {
- none,
- deflate
-}
-
-/**
- * ICC Profile data stored with an image.
- */
-class ICCProfileData {
- String name = "";
- ICCPCompression compression;
- Uint8List data;
-
- ICCProfileData(this.name, this.compression, this.data);
-
- ICCProfileData.from(ICCProfileData other)
- : name = other.name,
- compression = other.compression,
- data = new Uint8List.fromList(other.data);
-
- /// Returns the compressed data of the ICC Profile, compressing the stored
- /// data as necessary.
- Uint8List compressed() {
- if (compression == ICCPCompression.deflate) {
- return data;
- }
- data = ZLibEncoder().encode(data);
- compression = ICCPCompression.deflate;
- return data;
- }
-
- /// Returns the uncompressed data of the ICC Profile, decompressing the stored
- /// data as necessary.
- Uint8List decompressed() {
- if (compression == ICCPCompression.deflate) {
- return data;
- }
- data = ZLibDecoder().decodeBytes(data);
- compression = ICCPCompression.none;
- return data;
- }
-}
+import 'dart:typed_data';
+import 'package:archive/archive.dart';
+
+enum ICCPCompression {
+ none,
+ deflate
+}
+
+/**
+ * ICC Profile data stored with an image.
+ */
+class ICCProfileData {
+ String name = "";
+ ICCPCompression compression;
+ Uint8List data;
+
+ ICCProfileData(this.name, this.compression, this.data);
+
+ ICCProfileData.from(ICCProfileData other)
+ : name = other.name,
+ compression = other.compression,
+ data = Uint8List.fromList(other.data);
+
+ /// Returns the compressed data of the ICC Profile, compressing the stored
+ /// data as necessary.
+ Uint8List compressed() {
+ if (compression == ICCPCompression.deflate) {
+ return data;
+ }
+ data = ZLibEncoder().encode(data);
+ compression = ICCPCompression.deflate;
+ return data;
+ }
+
+ /// Returns the uncompressed data of the ICC Profile, decompressing the stored
+ /// data as necessary.
+ Uint8List decompressed() {
+ if (compression == ICCPCompression.deflate) {
+ return data;
+ }
+ data = ZLibDecoder().decodeBytes(data);
+ compression = ICCPCompression.none;
+ return data;
+ }
+}
diff --git a/image/lib/src/image.dart b/image/lib/src/image.dart
old mode 100644
new mode 100755
index 762e805..d190340
--- a/image/lib/src/image.dart
+++ b/image/lib/src/image.dart
@@ -75,8 +75,8 @@
ICCProfileData iccp]) :
this.width = width,
this.height = height,
- data = new Uint32List(width * height),
- exif = new ExifData.from(exif),
+ data = Uint32List(width * height),
+ exif = ExifData.from(exif),
iccProfile = iccp;
/**
@@ -91,8 +91,8 @@
disposeMethod = other.disposeMethod,
blendMethod = other.blendMethod,
_format = other._format,
- data = new Uint32List.fromList(other.data),
- exif = new ExifData.from(other.exif),
+ data = Uint32List.fromList(other.data),
+ exif = ExifData.from(other.exif),
iccProfile = other.iccProfile;
/**
@@ -105,7 +105,7 @@
* For example, given an Html Canvas, you could create an image:
* var bytes = canvas.getContext('2d').getImageData(0, 0,
* canvas.width, canvas.height).data;
- * Image image = new Image.fromBytes(canvas.width, canvas.height, bytes);
+ * Image image = Image.fromBytes(canvas.width, canvas.height, bytes);
*/
Image.fromBytes(int width, int height, List<int> bytes,
[this._format = RGBA, ExifData exif, ICCProfileData iccp]) :
@@ -117,7 +117,7 @@
bytes is Uint8ClampedList ? new Uint32List.view(bytes.buffer) :
bytes is Uint32List ? new Uint32List.view(bytes.buffer) :
new Uint32List.view(new Uint8List.fromList(bytes).buffer),
- exif = new ExifData.from(exif),
+ exif = ExifData.from(exif),
iccProfile = iccp;
/**
diff --git a/image/lib/src/image_exception.dart b/image/lib/src/image_exception.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/internal/bit_operators.dart b/image/lib/src/internal/bit_operators.dart
old mode 100644
new mode 100755
index ab3f4e6..e5eae4a
--- a/image/lib/src/internal/bit_operators.dart
+++ b/image/lib/src/internal/bit_operators.dart
@@ -84,21 +84,21 @@
return __float32ToUint32[0];
}
-final Uint8List __uint8 = new Uint8List(1);
-final Int8List __uint8ToInt8 = new Int8List.view(__uint8.buffer);
+final Uint8List __uint8 = Uint8List(1);
+final Int8List __uint8ToInt8 = Int8List.view(__uint8.buffer);
-final Uint16List __uint16 = new Uint16List(1);
-final Int16List __uint16ToInt16 = new Int16List.view(__uint16.buffer);
+final Uint16List __uint16 = Uint16List(1);
+final Int16List __uint16ToInt16 = Int16List.view(__uint16.buffer);
-final Uint32List __uint32 = new Uint32List(1);
-final Int32List __uint32ToInt32 = new Int32List.view(__uint32.buffer);
-final Float32List __uint32ToFloat32 = new Float32List.view(__uint32.buffer);
+final Uint32List __uint32 = Uint32List(1);
+final Int32List __uint32ToInt32 = Int32List.view(__uint32.buffer);
+final Float32List __uint32ToFloat32 = Float32List.view(__uint32.buffer);
-final Int32List __int32 = new Int32List(1);
-final Uint32List __int32ToUint32 = new Uint32List.view(__int32.buffer);
+final Int32List __int32 = Int32List(1);
+final Uint32List __int32ToUint32 = Uint32List.view(__int32.buffer);
-final Float32List __float32 = new Float32List(1);
-final Uint32List __float32ToUint32 = new Uint32List.view(__float32.buffer);
+final Float32List __float32 = Float32List(1);
+final Uint32List __float32ToUint32 = Uint32List.view(__float32.buffer);
-final Uint64List __uint64 = new Uint64List(1);
-final Float64List __uint64ToFloat64 = new Float64List.view(__uint64.buffer);
\ No newline at end of file
+final Uint64List __uint64 = Uint64List(1);
+final Float64List __uint64ToFloat64 = Float64List.view(__uint64.buffer);
\ No newline at end of file
diff --git a/image/lib/src/internal/clamp.dart b/image/lib/src/internal/clamp.dart
old mode 100644
new mode 100755
index ec3157a..554bc6f
--- a/image/lib/src/internal/clamp.dart
+++ b/image/lib/src/internal/clamp.dart
@@ -1,3 +1,3 @@
-int clamp(int x, int a, int b) => x.clamp(a, b);
-
-int clamp255(int x) => x.clamp(0, 255);
+int clamp(int x, int a, int b) => x.clamp(a, b);
+
+int clamp255(int x) => x.clamp(0, 255);
diff --git a/image/lib/src/internal/internal.dart b/image/lib/src/internal/internal.dart
old mode 100644
new mode 100755
index 2289c3b..4c14ba7
--- a/image/lib/src/internal/internal.dart
+++ b/image/lib/src/internal/internal.dart
@@ -1,7 +1,7 @@
-/// Annotates a function or class that's for internal use within the image
-/// library and is not to be exported as part of the main API.
-const _Internal internal = const _Internal();
-
-class _Internal {
- const _Internal();
-}
+/// Annotates a function or class that's for internal use within the image
+/// library and is not to be exported as part of the main API.
+const _Internal internal = const _Internal();
+
+class _Internal {
+ const _Internal();
+}
diff --git a/image/lib/src/transform/bake_orientation.dart b/image/lib/src/transform/bake_orientation.dart
old mode 100644
new mode 100755
index b05245b..13392e2
--- a/image/lib/src/transform/bake_orientation.dart
+++ b/image/lib/src/transform/bake_orientation.dart
@@ -1,38 +1,38 @@
-import '../image.dart';
-import '../exif_data.dart';
-import 'flip.dart';
-import 'copy_rotate.dart';
-
-/**
- * If [image] has an orientation value in its exif data, this will rotate the
- * image so that it physically matches its orientation. This can be used to
- * bake the orientation of the image for image formats that don't support exif
- * data.
- */
-Image bakeOrientation(Image image) {
- Image bakedImage = new Image.from(image);
- if (!image.exif.hasOrientation || image.exif.orientation == 1) {
- return bakedImage;
- }
-
- // Clear the exif data.
- // TODO: only clear the orientation property
- bakedImage.exif = new ExifData();
- switch (image.exif.orientation) {
- case 2:
- return flipHorizontal(bakedImage);
- case 3:
- return flip(bakedImage, FLIP_BOTH);
- case 4:
- return flipHorizontal(copyRotate(bakedImage, 180));
- case 5:
- return flipHorizontal(copyRotate(bakedImage, 90));
- case 6:
- return copyRotate(bakedImage, 90);
- case 7:
- return flipHorizontal(copyRotate(bakedImage, -90));
- case 8:
- return copyRotate(bakedImage, -90);
- }
- return bakedImage;
-}
+import '../image.dart';
+import '../exif_data.dart';
+import 'flip.dart';
+import 'copy_rotate.dart';
+
+/**
+ * If [image] has an orientation value in its exif data, this will rotate the
+ * image so that it physically matches its orientation. This can be used to
+ * bake the orientation of the image for image formats that don't support exif
+ * data.
+ */
+Image bakeOrientation(Image image) {
+ Image bakedImage = Image.from(image);
+ if (!image.exif.hasOrientation || image.exif.orientation == 1) {
+ return bakedImage;
+ }
+
+ // Clear the exif data.
+ // TODO: only clear the orientation property
+ bakedImage.exif = ExifData();
+ switch (image.exif.orientation) {
+ case 2:
+ return flipHorizontal(bakedImage);
+ case 3:
+ return flip(bakedImage, FLIP_BOTH);
+ case 4:
+ return flipHorizontal(copyRotate(bakedImage, 180));
+ case 5:
+ return flipHorizontal(copyRotate(bakedImage, 90));
+ case 6:
+ return copyRotate(bakedImage, 90);
+ case 7:
+ return flipHorizontal(copyRotate(bakedImage, -90));
+ case 8:
+ return copyRotate(bakedImage, -90);
+ }
+ return bakedImage;
+}
diff --git a/image/lib/src/transform/copy_crop.dart b/image/lib/src/transform/copy_crop.dart
old mode 100644
new mode 100755
index ea765ba..4c03c09
--- a/image/lib/src/transform/copy_crop.dart
+++ b/image/lib/src/transform/copy_crop.dart
@@ -4,7 +4,7 @@
* Returns a cropped copy of [src].
*/
Image copyCrop(Image src, int x, int y, int w, int h) {
- Image dst = new Image(w, h, src.format, src.exif, src.iccProfile);
+ Image dst = Image(w, h, src.format, src.exif, src.iccProfile);
for (int yi = 0, sy = y; yi < h; ++yi, ++sy) {
for (int xi = 0, sx = x; xi < w; ++xi, ++sx) {
diff --git a/image/lib/src/transform/copy_into.dart b/image/lib/src/transform/copy_into.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/transform/copy_rectify.dart b/image/lib/src/transform/copy_rectify.dart
new file mode 100755
index 0000000..6eebcdb
--- /dev/null
+++ b/image/lib/src/transform/copy_rectify.dart
@@ -0,0 +1,25 @@
+import '../image.dart';
+import '../util/point.dart';
+
+/**
+ * Returns a copy of the [src] image, where the given rectangle
+ * has been mapped to the full image.
+ */
+Image copyRectify(Image src, {Point topLeft, Point topRight, Point bottomLeft,
+ Point bottomRight, Image toImage = null}) {
+ Image dst = toImage == null ? Image.from(src) : toImage;
+ for (int y = 0; y < dst.height; ++y) {
+ double v = y / (dst.height - 1);
+ for (int x = 0; x < dst.width; ++x) {
+ double u = x / (dst.width - 1);
+ // bilinear interpolation
+ Point srcPixelCoord = topLeft * (1 - u) * (1 - v) +
+ topRight * (u) * (1 - v) +
+ bottomLeft * (1 - u) * (v) +
+ bottomRight * (u) * (v);
+ var srcPixel = src.getPixel(srcPixelCoord.xi, srcPixelCoord.yi);
+ dst.setPixel(x, y, srcPixel);
+ }
+ }
+ return dst;
+}
diff --git a/image/lib/src/transform/copy_resize.dart b/image/lib/src/transform/copy_resize.dart
old mode 100644
new mode 100755
index 37856c9..03df52e
--- a/image/lib/src/transform/copy_resize.dart
+++ b/image/lib/src/transform/copy_resize.dart
@@ -14,7 +14,7 @@
* of [src] and [height].
*/
Image copyResize(Image src, int width, [int height = -1,
- int interpolation = LINEAR]) {
+ int interpolation = NEAREST]) {
if (width <= 0 && height <= 0) {
throw new ImageException('Invalid size');
}
@@ -29,7 +29,7 @@
width = (height * (src.width / src.height)).toInt();
}
- Image dst = new Image(width, height, src.format, src.exif, src.iccProfile);
+ Image dst = Image(width, height, src.format, src.exif, src.iccProfile);
double dy = src.height / height;
double dx = src.width / width;
@@ -69,6 +69,17 @@
dst.setPixel(x, y, getColor(r ~/ np, g ~/ np, b ~/ np, a ~/ np));
}
}
+ } else if (interpolation == NEAREST) {
+ final scaleX = Int32List(width);
+ for (int x = 0; x < width; ++x) {
+ scaleX[x] = (x * dx).toInt();
+ }
+ for (int y = 0; y < height; ++y) {
+ int y2 = (y * dy).toInt();
+ for (int x = 0; x < width; ++x) {
+ dst.setPixel(x, y, src.getPixel(scaleX[x], y2));
+ }
+ }
} else {
// Copy the pixels from this image to the new image.
for (int y = 0; y < height; ++y) {
diff --git a/image/lib/src/transform/copy_rotate.dart b/image/lib/src/transform/copy_rotate.dart
old mode 100644
new mode 100755
index fd099f9..31e3c2f
--- a/image/lib/src/transform/copy_rotate.dart
+++ b/image/lib/src/transform/copy_rotate.dart
@@ -17,7 +17,7 @@
int iangle = nangle ~/ 90.0;
switch (iangle) {
case 1: // 90 deg.
- Image dst = new Image(src.height, src.width, src.format, src.exif,
+ Image dst = Image(src.height, src.width, src.format, src.exif,
src.iccProfile);
for (int y = 0; y < dst.height; ++y) {
for (int x = 0; x < dst.width; ++x) {
@@ -26,7 +26,7 @@
}
return dst;
case 2: // 180 deg.
- Image dst = new Image(src.width, src.height, src.format, src.exif,
+ Image dst = Image(src.width, src.height, src.format, src.exif,
src.iccProfile);
for (int y = 0; y < dst.height; ++y) {
for (int x = 0; x < dst.width; ++x) {
@@ -35,7 +35,7 @@
}
return dst;
case 3: // 270 deg.
- Image dst = new Image(src.height, src.width, src.format, src.exif,
+ Image dst = Image(src.height, src.width, src.format, src.exif,
src.iccProfile);
for (int y = 0; y < dst.height; ++y) {
for (int x = 0; x < dst.width; ++x) {
@@ -61,7 +61,7 @@
double dw2 = 0.5 * (ux + vx);
double dh2 = 0.5 * (uy + vy);
- Image dst = new Image((ux + vx).toInt(), (uy + vy).toInt(), Image.RGBA,
+ Image dst = Image((ux + vx).toInt(), (uy + vy).toInt(), Image.RGBA,
src.exif, src.iccProfile);
for (int y = 0; y < dst.height; ++y) {
diff --git a/image/lib/src/transform/flip.dart b/image/lib/src/transform/flip.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/transform/trim.dart b/image/lib/src/transform/trim.dart
old mode 100644
new mode 100755
index 9a88394..1fa9f54
--- a/image/lib/src/transform/trim.dart
+++ b/image/lib/src/transform/trim.dart
@@ -110,7 +110,7 @@
List<int> crop = findTrim(src, mode: mode, sides: sides);
- Image dst = new Image(crop[2], crop[3], Image.RGBA, src.exif, src.iccProfile);
+ Image dst = Image(crop[2], crop[3], Image.RGBA, src.exif, src.iccProfile);
copyInto(dst, src, srcX: crop[0], srcY: crop[1],
srcW: crop[2], srcH: crop[3], blend: false);
diff --git a/image/lib/src/util/clip_line.dart b/image/lib/src/util/clip_line.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/util/input_buffer.dart b/image/lib/src/util/input_buffer.dart
old mode 100644
new mode 100755
index 7d42795..ba5338e
--- a/image/lib/src/util/input_buffer.dart
+++ b/image/lib/src/util/input_buffer.dart
@@ -172,7 +172,7 @@
InputBuffer s = readBytes(len);
Uint8List bytes = s.toUint8List();
- String str = new String.fromCharCodes(bytes);
+ String str = String.fromCharCodes(bytes);
return str;
}
diff --git a/image/lib/src/util/interpolation.dart b/image/lib/src/util/interpolation.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/util/min_max.dart b/image/lib/src/util/min_max.dart
old mode 100644
new mode 100755
diff --git a/image/lib/src/util/neural_quantizer.dart b/image/lib/src/util/neural_quantizer.dart
old mode 100644
new mode 100755
index fced820..1551bec
--- a/image/lib/src/util/neural_quantizer.dart
+++ b/image/lib/src/util/neural_quantizer.dart
@@ -1,8 +1,9 @@
+import 'dart:math';
import 'dart:typed_data';
-
import '../color.dart';
import '../image.dart';
import '../image_exception.dart';
+import 'quantizer.dart';
/* NeuQuant Neural-Net Quantization Algorithm
* ------------------------------------------
@@ -29,18 +30,20 @@
*/
/**
- * Compute a 256 color map that best represents the given image.
+ * Compute a color map with a given number of colors that best represents
+ * the given image.
*/
-class NeuralQuantizer {
- Uint8List colorMap = new Uint8List(NET_SIZE * 3);
+class NeuralQuantizer extends Quantizer {
+ Uint8List colorMap;
- NeuralQuantizer(Image image) {
+ NeuralQuantizer(Image image, {int numberOfColors=256}) {
if (image.width * image.height < MAX_PRIME) {
throw new ImageException('Image is too small');
}
- _setupArrays();
+ _initialize(numberOfColors);
+ _setupArrays();
addImage(image);
}
@@ -100,13 +103,28 @@
* Convert the [image] to an index map, mapping to this [colorMap].
*/
Uint8List getIndexMap(Image image) {
- Uint8List map = new Uint8List(image.width * image.height);
+ Uint8List map = Uint8List(image.width * image.height);
for (int i = 0, len = image.length; i < len; ++i) {
map[i] = lookup(image[i]);
}
return map;
}
+ void _initialize(int numberOfColors) {
+ NET_SIZE = max(numberOfColors, 4); // number of colours used
+ CUT_NET_SIZE = NET_SIZE - SPECIALS;
+ MAX_NET_POS = NET_SIZE - 1;
+ INIT_RAD = NET_SIZE ~/ 8; // for 256 cols, radius starts at 32
+ INIT_BIAS_RADIUS = INIT_RAD * RADIUS_BIAS;
+ _network = List<double>(NET_SIZE * 3);
+ _colorMap = Int32List(NET_SIZE * 4);
+ _bias = List<double>(NET_SIZE);
+ _freq = List<double>(NET_SIZE);
+ colorMap = Uint8List(NET_SIZE * 3);
+ SPECIALS = 3; // number of reserved colours used
+ BG_COLOR = SPECIALS - 1;
+ }
+
void _copyColorMap() {
for (int i = 0, p = 0, q = 0; i < NET_SIZE; ++i) {
colorMap[p++] = _colorMap[q + 2].abs() & 0xff;
@@ -259,7 +277,6 @@
}
}
-
void _learn(Image image) {
int biasRadius = INIT_BIAS_RADIUS;
int alphadec = 30 + ((_sampleFac - 1) ~/ 3);
@@ -460,16 +477,16 @@
static const int NUM_CYCLES = 100; // no. of learning cycles
- static const int NET_SIZE = 256; // number of colours used
- static const int SPECIALS = 3; // number of reserved colours used
- static const int BG_COLOR = SPECIALS - 1; // reserved background colour
- static const int CUT_NET_SIZE = NET_SIZE - SPECIALS;
- static const int MAX_NET_POS = NET_SIZE - 1;
+ int NET_SIZE = 16; // number of colours used
+ int SPECIALS = 3; // number of reserved colours used
+ int BG_COLOR; // reserved background colour
+ int CUT_NET_SIZE;
+ int MAX_NET_POS;
- static const int INIT_RAD = NET_SIZE ~/ 8; // for 256 cols, radius starts at 32
+ int INIT_RAD; // for 256 cols, radius starts at 32
static const int RADIUS_BIAS_SHIFT = 6;
static const int RADIUS_BIAS = 1 << RADIUS_BIAS_SHIFT;
- static const int INIT_BIAS_RADIUS = INIT_RAD * RADIUS_BIAS;
+ int INIT_BIAS_RADIUS;
static const int RADIUS_DEC = 30; // factor of 1/30 each cycle
static const int ALPHA_BIAS_SHIFT = 10; // alpha starts at 1
@@ -480,12 +497,12 @@
static const double BETA_GAMMA = BETA * GAMMA;
/// the network itself
- List<double> _network = new List<double>(NET_SIZE * 3);
- Int32List _colorMap = new Int32List(NET_SIZE * 4);
- Int32List _netIndex = new Int32List(256);
+ List<double> _network ;
+ Int32List _colorMap;
+ Int32List _netIndex = Int32List(256);
// bias and freq arrays for learning
- List<double> _bias = new List<double>(NET_SIZE);
- List<double> _freq = new List<double>(NET_SIZE);
+ List<double> _bias;
+ List<double> _freq;
// four primes near 500 - assume no image has a length so large
// that it is divisible by all four primes
diff --git a/image/lib/src/util/octree_quantizer.dart b/image/lib/src/util/octree_quantizer.dart
new file mode 100755
index 0000000..ee1ed43
--- /dev/null
+++ b/image/lib/src/util/octree_quantizer.dart
@@ -0,0 +1,204 @@
+import '../color.dart';
+import '../image.dart';
+import 'quantizer.dart';
+
+// Color quantization using octree,
+// from https://rosettacode.org/wiki/Color_quantization/C
+class OctreeQuantizer extends Quantizer {
+ _OctreeNode _root;
+
+ OctreeQuantizer(Image image, {int numberOfColors=256}) {
+ _root = _OctreeNode(0, 0, null);
+
+ _HeapNode heap = _HeapNode();
+ for (int si = 0; si < image.length; ++si) {
+ int c = image[si];
+ int r = getRed(c);
+ int g = getGreen(c);
+ int b = getBlue(c);
+ _heapAdd(heap, _nodeInsert(_root, r, g, b));
+ }
+
+ int nc = numberOfColors + 1;
+ while (heap.n > nc) {
+ _heapAdd(heap, _nodeFold(_popHeap(heap)));
+ }
+
+ for (int i = 1; i < heap.n; i++) {
+ var got = heap.buf[i];
+ int c = got.count;
+ got.r = (got.r / c).round();
+ got.g = (got.g / c).round();
+ got.b = (got.b / c).round();
+ }
+ }
+
+ /**
+ * Find the index of the closest color to [c] in the [colorMap].
+ */
+ int getQuantizedColor(int c) {
+ int r = getRed(c);
+ int g = getGreen(c);
+ int b = getBlue(c);
+ var root = _root;
+
+ for (int bit = 1 << 7; bit != 0; bit >>= 1) {
+ int i = ((g & bit) != 0 ? 1 : 0) * 4 +
+ ((r & bit) != 0 ? 1 : 0) * 2 +
+ ((b & bit) != 0 ? 1 : 0);
+ if (root.children[i] == null) {
+ break;
+ }
+ root = root.children[i];
+ }
+
+ r = root.r;
+ g = root.g;
+ b = root.b;
+ return getColor(r, g, b, 255);
+ }
+
+ int _compareNode(_OctreeNode a, _OctreeNode b) {
+ if (a.childCount < b.childCount) {
+ return -1;
+ }
+ if (a.childCount > b.childCount) {
+ return 1;
+ }
+
+ int ac = a.count >> a.depth;
+ int bc = b.count >> b.depth;
+ return (ac < bc) ? -1 : (ac > bc) ? 1 : 0;
+ }
+
+
+ _OctreeNode _nodeInsert(_OctreeNode root, int r, int g, int b) {
+ int depth = 0;
+ for (int bit = 1 << 7; ++depth < 8; bit >>= 1) {
+ int i = ((g & bit) != 0 ? 1 : 0) * 4 +
+ ((r & bit) != 0 ? 1 : 0) * 2 + ((b & bit) != 0 ? 1 : 0);
+ if (root.children[i] == null) {
+ root.children[i] = _OctreeNode(i, depth, root);
+ }
+
+ root = root.children[i];
+ }
+
+ root.r += r;
+ root.g += g;
+ root.b += b;
+ root.count++;
+ return root;
+ }
+
+ _OctreeNode _nodeFold(_OctreeNode p) {
+ if (p.childCount > 0) {
+ return null;
+ }
+ var q = p.parent;
+ q.count += p.count;
+
+ q.r += p.r;
+ q.g += p.g;
+ q.b += p.b;
+ q.childCount--;
+ q.children[p.childIndex] = null;
+ return q;
+ }
+
+ static const _ON_INHEAP = 1;
+
+ _OctreeNode _popHeap(_HeapNode h) {
+ if (h.n <= 1) {
+ return null;
+ }
+
+ _OctreeNode ret = h.buf[1];
+ h.buf[1] = h.buf.removeLast();
+ h.buf[1].heap_idx = 1;
+ _downHeap(h, h.buf[1]);
+
+ return ret;
+ }
+
+ void _heapAdd(_HeapNode h, _OctreeNode p) {
+ if ((p.flags & _ON_INHEAP) != 0) {
+ _downHeap(h, p);
+ _upHeap(h, p);
+ return;
+ }
+
+ p.flags |= _ON_INHEAP;
+ p.heap_idx = h.n;
+ h.buf.add(p);
+ _upHeap(h, p);
+ }
+
+ void _downHeap(_HeapNode h, _OctreeNode p) {
+ int n = p.heap_idx;
+ while (true) {
+ int m = n * 2;
+ if (m >= h.n) {
+ break;
+ }
+ if ((m + 1) < h.n && _compareNode(h.buf[m], h.buf[m + 1]) > 0) {
+ m++;
+ }
+
+ if (_compareNode(p, h.buf[m]) <= 0) {
+ break;
+ }
+
+ h.buf[n] = h.buf[m];
+ h.buf[n].heap_idx = n;
+ n = m;
+ }
+
+ h.buf[n] = p;
+ p.heap_idx = n;
+ }
+
+ void _upHeap(_HeapNode h, _OctreeNode p) {
+ int n = p.heap_idx;
+ _OctreeNode prev;
+
+ while (n > 1) {
+ prev = h.buf[n ~/ 2];
+ if (_compareNode(p, prev) >= 0) {
+ break;
+ }
+
+ h.buf[n] = prev;
+ prev.heap_idx = n;
+ n ~/= 2;
+ }
+ h.buf[n] = p;
+ p.heap_idx = n;
+ }
+}
+
+class _OctreeNode {
+ // sum of all colors represented by this node.
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ int count = 0;
+ int heap_idx = 0;
+ List<_OctreeNode> children = List<_OctreeNode>(8);
+ _OctreeNode parent = null;
+ int childCount = 0;
+ int childIndex = 0;
+ int flags = 0;
+ int depth = 0;
+
+ _OctreeNode(this.childIndex, this.depth, this.parent) {
+ if (parent != null) {
+ parent.childCount++;
+ }
+ }
+}
+
+class _HeapNode {
+ List<_OctreeNode> buf = [null];
+ int get n => buf.length;
+}
diff --git a/image/lib/src/util/output_buffer.dart b/image/lib/src/util/output_buffer.dart
old mode 100644
new mode 100755
index 855fa66..8de0a06
--- a/image/lib/src/util/output_buffer.dart
+++ b/image/lib/src/util/output_buffer.dart
@@ -10,7 +10,7 @@
* Create a byte buffer for writing.
*/
OutputBuffer({int size: _BLOCK_SIZE, this.bigEndian: false}) :
- _buffer = new Uint8List(size == null ? _BLOCK_SIZE : size),
+ _buffer = Uint8List(size == null ? _BLOCK_SIZE : size),
length = 0;
void rewind() {
@@ -28,7 +28,7 @@
* Clear the buffer.
*/
void clear() {
- _buffer = new Uint8List(_BLOCK_SIZE);
+ _buffer = Uint8List(_BLOCK_SIZE);
length = 0;
}
@@ -120,7 +120,7 @@
void _expandBuffer([int required]) {
int blockSize = (required != null) ? required :
(_buffer.length == 0) ? _BLOCK_SIZE : (_buffer.length * 2);
- Uint8List newBuffer = new Uint8List(_buffer.length + blockSize);
+ Uint8List newBuffer = Uint8List(_buffer.length + blockSize);
newBuffer.setRange(0, _buffer.length, _buffer);
_buffer = newBuffer;
}
diff --git a/image/lib/src/util/point.dart b/image/lib/src/util/point.dart
new file mode 100755
index 0000000..8378713
--- /dev/null
+++ b/image/lib/src/util/point.dart
@@ -0,0 +1,22 @@
+
+class Point {
+ num x;
+ num y;
+
+ get xi { return x.toInt(); }
+ get yi { return y.toInt(); }
+
+ Point([this.x = 0, this.y = 0]);
+
+ Point.from(Point other)
+ : x = other.x
+ , y = other.y;
+
+ Point operator*(double s) {
+ return Point(x * s, y * s);
+ }
+
+ Point operator+(Point rhs) {
+ return Point(x + rhs.x, y + rhs.y);
+ }
+}
diff --git a/image/lib/src/util/quantizer.dart b/image/lib/src/util/quantizer.dart
new file mode 100755
index 0000000..0770797
--- /dev/null
+++ b/image/lib/src/util/quantizer.dart
@@ -0,0 +1,7 @@
+
+abstract class Quantizer {
+ /**
+ * Find the index of the closest color to [c] in the [colorMap].
+ */
+ int getQuantizedColor(int c);
+}
diff --git a/image/lib/src/util/random.dart b/image/lib/src/util/random.dart
old mode 100644
new mode 100755
diff --git a/image/pubspec.yaml b/image/pubspec.yaml
old mode 100644
new mode 100755
index 5e7ec47..08b84c2
--- a/image/pubspec.yaml
+++ b/image/pubspec.yaml
@@ -1,13 +1,13 @@
-name: image
-version: 2.0.7
-author: Brendan Duncan <brendanduncan@gmail.com>
-description: Provides server and web apps the ability to load, manipulate, and save images with various image file formats including PNG, JPEG, GIF, WebP, TIFF, TGA, PSD, PVR, and OpenEXR.
-homepage: https://github.com/brendan-duncan/image
-documentation: https://github.com/brendan-duncan/image/wiki
-environment:
- sdk: '>=2.0.0-dev.49.0 <3.0.0'
-dependencies:
- archive: '>=1.0.16 <3.0.0'
- xml: '>=3.2.5 <4.0.0'
-dev_dependencies:
- test: '>=0.12.42 <2.0.0'
+name: image
+version: 2.0.8
+author: Brendan Duncan <brendanduncan@gmail.com>
+description: Provides server and web apps the ability to load, manipulate, and save images with various image file formats including PNG, JPEG, GIF, WebP, TIFF, TGA, PSD, PVR, and OpenEXR.
+homepage: https://github.com/brendan-duncan/image
+documentation: https://github.com/brendan-duncan/image/wiki
+environment:
+ sdk: '>=2.0.0-dev.49.0 <3.0.0'
+dependencies:
+ archive: '>=1.0.16 <3.0.0'
+ xml: '>=3.2.5 <4.0.0'
+dev_dependencies:
+ test: '>=0.12.42 <2.0.0'
diff --git a/image/tool/travis.sh b/image/tool/travis.sh
index fe6764a..10bff95 100755
--- a/image/tool/travis.sh
+++ b/image/tool/travis.sh
@@ -1,10 +1,10 @@
-#!/bin/bash
-
-# Fast fail the script on failures.
-set -e
-
-# Analyze the code.
-dartanalyzer --fatal-warnings lib/image.dart
-
-# Run the tests.
-pub run build_runner test
+#!/bin/bash
+
+# Fast fail the script on failures.
+set -e
+
+# Analyze the code.
+dartanalyzer --fatal-warnings lib/image.dart
+
+# Run the tests.
+pub run build_runner test
diff --git a/image/web/filter_lab.dart b/image/web/filter_lab.dart
old mode 100644
new mode 100755
index 547046b..c56498e
--- a/image/web/filter_lab.dart
+++ b/image/web/filter_lab.dart
@@ -1,378 +1,378 @@
-import 'dart:html' as Html;
-import 'package:image/image.dart';
-
-Html.ImageData filterImageData;
-Html.CanvasElement canvas;
-Html.DivElement logDiv;
-Image origImage;
-
-void _addControl(String label, String value, Html.DivElement parent,
- callback) {
- Html.LabelElement amountLabel = new Html.LabelElement();
- amountLabel.text = label + ':';
- var amountEdit = new Html.InputElement();
- amountEdit.value = value;
- amountEdit.id = label + '_edit';
- amountEdit.onChange.listen((e) {
- try {
- double d = double.parse(amountEdit.value);
- callback(d);
- } catch (e) {
- }
- });
- amountLabel.htmlFor = label + '_edit';
- parent.append(amountLabel);
- parent.append(amountEdit);
- parent.append(new Html.ParagraphElement());
-}
-
-
-void testSepia() {
- Html.DivElement sidebar = Html.document.querySelector('#sidebar');
- sidebar.children.clear();
-
- var label = new Html.Element.tag('h1');
- label.text = 'Sepia';
- sidebar.children.add(label);
-
- double amount = 1.0;
-
- void _apply() {
- Stopwatch t = new Stopwatch();
- t.start();
- Image image = new Image.from(origImage);
- image = sepia(image, amount: amount);
-
- // Fill the buffer with our image data.
- filterImageData.data.setRange(0, filterImageData.data.length,
- image.getBytes());
- // Draw the buffer onto the canvas.
- canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
- canvas.context2D.putImageData(filterImageData, 0, 0);
- logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
- print(t.elapsedMilliseconds / 1000.0);
- }
-
- _addControl('Amount', amount.toString(), sidebar, (v) {
- amount = v;
- _apply();
- });
-
- _apply();
-}
-
-void testSobel() {
- Html.DivElement sidebar = Html.document.querySelector('#sidebar');
- sidebar.children.clear();
-
- var label = new Html.Element.tag('h1');
- label.text = 'Sepia';
- sidebar.children.add(label);
-
- double amount = 1.0;
-
- void _apply() {
- Stopwatch t = new Stopwatch();
- t.start();
- Image image = new Image.from(origImage);
- image = sobel(image, amount: amount);
-
- // Fill the buffer with our image data.
- filterImageData.data.setRange(0, filterImageData.data.length,
- image.getBytes());
- // Draw the buffer onto the canvas.
- canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
- canvas.context2D.putImageData(filterImageData, 0, 0);
- logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
- print(t.elapsedMilliseconds / 1000.0);
- }
-
- _addControl('Amount', amount.toString(), sidebar, (v) {
- amount = v;
- _apply();
- });
-
- _apply();
-}
-
-void testGaussian() {
- Html.DivElement sidebar = Html.document.querySelector('#sidebar');
- sidebar.children.clear();
-
- var label = new Html.Element.tag('h1');
- label.text = 'Gaussian Blur';
- sidebar.children.add(label);
-
- int radius = 5;
-
- void _apply() {
- Stopwatch t = new Stopwatch();
- t.start();
- Image image = new Image.from(origImage);
- image = gaussianBlur(image, radius);
-
- // Fill the buffer with our image data.
- filterImageData.data.setRange(0, filterImageData.data.length,
- image.getBytes());
- // Draw the buffer onto the canvas.
- canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
- canvas.context2D.putImageData(filterImageData, 0, 0);
- logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
- print(t.elapsedMilliseconds / 1000.0);
- }
-
- _addControl('Radius', radius.toString(), sidebar, (v) {
- radius = v.toInt();
- _apply();
- });
-
- _apply();
-}
-
-void testVignette() {
- Html.DivElement sidebar = Html.document.querySelector('#sidebar');
- sidebar.children.clear();
-
- var label = new Html.Element.tag('h1');
- label.text = 'Vignette';
- sidebar.children.add(label);
-
- double start = 0.3;
- double end = 0.75;
- double amount = 1.0;
-
- void _apply() {
- Stopwatch t = new Stopwatch();
- t.start();
- Image image = new Image.from(origImage);
- image = vignette(image, start: start, end: end, amount: amount);
-
- // Fill the buffer with our image data.
- filterImageData.data.setRange(0, filterImageData.data.length,
- image.getBytes());
- // Draw the buffer onto the canvas.
- canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
- canvas.context2D.putImageData(filterImageData, 0, 0);
- logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
- print(t.elapsedMilliseconds / 1000.0);
- }
-
- _addControl('Start', start.toString(), sidebar, (v) {
- start = v;
- _apply();
- });
-
- _addControl('End', end.toString(), sidebar, (v) {
- end = v;
- _apply();
- });
-
- _addControl('Amount', amount.toString(), sidebar, (v) {
- amount = v;
- _apply();
- });
-
- _apply();
-}
-
-void testPixelate() {
- Html.DivElement sidebar = Html.document.querySelector('#sidebar');
- sidebar.children.clear();
-
- var label = new Html.Element.tag('h1');
- label.text = 'Pixelate';
- sidebar.children.add(label);
-
- int blockSize = 5;
-
- void _apply() {
- Stopwatch t = new Stopwatch();
- t.start();
- Image image = new Image.from(origImage);
- image = pixelate(image, blockSize);
-
- // Fill the buffer with our image data.
- filterImageData.data.setRange(0, filterImageData.data.length,
- image.getBytes());
- // Draw the buffer onto the canvas.
- canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
- canvas.context2D.putImageData(filterImageData, 0, 0);
- logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
- print(t.elapsedMilliseconds / 1000.0);
- }
-
- _addControl('blockSize', blockSize.toString(), sidebar, (v) {
- blockSize = v.toInt();
- _apply();
- });
-
- _apply();
-}
-
-void testColorOffset() {
- Html.DivElement sidebar = Html.document.querySelector('#sidebar');
- sidebar.children.clear();
-
- var label = new Html.Element.tag('h1');
- label.text = 'Pixelate';
- sidebar.children.add(label);
-
- int red = 0;
- int green = 0;
- int blue = 0;
- int alpha = 0;
-
- void _apply() {
- Stopwatch t = new Stopwatch();
- t.start();
- Image image = new Image.from(origImage);
- image = colorOffset(image, red, green, blue, alpha);
-
- // Fill the buffer with our image data.
- filterImageData.data.setRange(0, filterImageData.data.length,
- image.getBytes());
- // Draw the buffer onto the canvas.
- canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
- canvas.context2D.putImageData(filterImageData, 0, 0);
- logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
- print(t.elapsedMilliseconds / 1000.0);
- }
-
- _addControl('red', red.toString(), sidebar, (v) {
- red = v.toInt();
- _apply();
- });
-
- _addControl('green', red.toString(), sidebar, (v) {
- green = v.toInt();
- _apply();
- });
-
- _addControl('blue', red.toString(), sidebar, (v) {
- blue = v.toInt();
- _apply();
- });
-
- _addControl('alpha', red.toString(), sidebar, (v) {
- alpha = v.toInt();
- _apply();
- });
-
- _apply();
-}
-
-void testAdjustColor() {
- Html.DivElement sidebar = Html.document.querySelector('#sidebar');
- sidebar.children.clear();
-
- var label = new Html.Element.tag('h1');
- label.text = 'Adjust Color';
- sidebar.children.add(label);
-
- double contrast = 1.0;
- double saturation = 1.0;
- double brightness = 1.0;
- double gamma = 0.8;
- double exposure = 0.3;
- double hue = 0.0;
- double amount = 1.0;
-
- void _apply() {
- Stopwatch t = new Stopwatch();
- t.start();
- Image image = new Image.from(origImage);
-
- image = adjustColor(image, contrast: contrast, saturation: saturation,
- brightness: brightness, gamma: gamma, exposure: exposure,
- hue: hue, amount: amount);
-
- // Fill the buffer with our image data.
- filterImageData.data.setRange(0, filterImageData.data.length,
- image.getBytes());
- // Draw the buffer onto the canvas.
- canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
- canvas.context2D.putImageData(filterImageData, 0, 0);
-
- logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
- print(t.elapsedMilliseconds / 1000.0);
- }
-
- _addControl('Contrast', contrast.toString(), sidebar, (v) {
- contrast = v;
- _apply();
- });
-
- _addControl('Saturation', saturation.toString(), sidebar, (v) {
- saturation = v;
- _apply();
- });
-
- _addControl('Brightness', brightness.toString(), sidebar, (v) {
- brightness = v;
- _apply();
- });
-
- _addControl('Gamma', gamma.toString(), sidebar, (v) {
- gamma = v;
- _apply();
- });
-
- _addControl('Exposure', exposure.toString(), sidebar, (v) {
- exposure = v;
- _apply();
- });
-
- _addControl('Hue', hue.toString(), sidebar, (v) {
- hue = v;
- _apply();
- });
-
- _addControl('Amount', amount.toString(), sidebar, (v) {
- amount = v;
- _apply();
- });
-
- _apply();
-}
-
-void main() {
- canvas = Html.document.querySelector('#filter_canvas');
- logDiv = Html.document.querySelector('#log');
-
- Html.SelectElement menu = Html.document.querySelector('#FilterType');
- menu.onChange.listen((e) {
- if (menu.value == 'Pixelate') {
- testPixelate();
- } else if (menu.value == 'Sepia') {
- testSepia();
- } else if (menu.value == 'Gaussian') {
- testGaussian();
- } else if (menu.value == 'Adjust Color') {
- testAdjustColor();
- } else if (menu.value == 'Sobel') {
- testSobel();
- } else if (menu.value == 'Vignette') {
- testVignette();
- } else if (menu.value == 'Color Offset') {
- testColorOffset();
- }
- });
-
- Html.ImageElement img = new Html.ImageElement();
- img.src = 'res/big_buck_bunny.jpg';
- img.onLoad.listen((e) {
- var c = new Html.CanvasElement();
- c.width = img.width;
- c.height = img.height;
- c.context2D.drawImage(img, 0, 0);
-
- var imageData = c.context2D.getImageData(0, 0, img.width, img.height);
- origImage = new Image.fromBytes(img.width, img.height, imageData.data);
-
- canvas.width = img.width;
- canvas.height = img.height;
- filterImageData = canvas.context2D.createImageData(img.width, img.height);
-
- testSepia();
- });
-}
+import 'dart:html' as Html;
+import 'package:image/image.dart';
+
+Html.ImageData filterImageData;
+Html.CanvasElement canvas;
+Html.DivElement logDiv;
+Image origImage;
+
+void _addControl(String label, String value, Html.DivElement parent,
+ callback) {
+ Html.LabelElement amountLabel = Html.LabelElement();
+ amountLabel.text = label + ':';
+ var amountEdit = Html.InputElement();
+ amountEdit.value = value;
+ amountEdit.id = label + '_edit';
+ amountEdit.onChange.listen((e) {
+ try {
+ double d = double.parse(amountEdit.value);
+ callback(d);
+ } catch (e) {
+ }
+ });
+ amountLabel.htmlFor = label + '_edit';
+ parent.append(amountLabel);
+ parent.append(amountEdit);
+ parent.append(new Html.ParagraphElement());
+}
+
+
+void testSepia() {
+ Html.DivElement sidebar = Html.document.querySelector('#sidebar');
+ sidebar.children.clear();
+
+ var label = Html.Element.tag('h1');
+ label.text = 'Sepia';
+ sidebar.children.add(label);
+
+ double amount = 1.0;
+
+ void _apply() {
+ Stopwatch t = Stopwatch();
+ t.start();
+ Image image = Image.from(origImage);
+ image = sepia(image, amount: amount);
+
+ // Fill the buffer with our image data.
+ filterImageData.data.setRange(0, filterImageData.data.length,
+ image.getBytes());
+ // Draw the buffer onto the canvas.
+ canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
+ canvas.context2D.putImageData(filterImageData, 0, 0);
+ logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
+ print(t.elapsedMilliseconds / 1000.0);
+ }
+
+ _addControl('Amount', amount.toString(), sidebar, (v) {
+ amount = v;
+ _apply();
+ });
+
+ _apply();
+}
+
+void testSobel() {
+ Html.DivElement sidebar = Html.document.querySelector('#sidebar');
+ sidebar.children.clear();
+
+ var label = Html.Element.tag('h1');
+ label.text = 'Sepia';
+ sidebar.children.add(label);
+
+ double amount = 1.0;
+
+ void _apply() {
+ Stopwatch t = Stopwatch();
+ t.start();
+ Image image = Image.from(origImage);
+ image = sobel(image, amount: amount);
+
+ // Fill the buffer with our image data.
+ filterImageData.data.setRange(0, filterImageData.data.length,
+ image.getBytes());
+ // Draw the buffer onto the canvas.
+ canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
+ canvas.context2D.putImageData(filterImageData, 0, 0);
+ logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
+ print(t.elapsedMilliseconds / 1000.0);
+ }
+
+ _addControl('Amount', amount.toString(), sidebar, (v) {
+ amount = v;
+ _apply();
+ });
+
+ _apply();
+}
+
+void testGaussian() {
+ Html.DivElement sidebar = Html.document.querySelector('#sidebar');
+ sidebar.children.clear();
+
+ var label = Html.Element.tag('h1');
+ label.text = 'Gaussian Blur';
+ sidebar.children.add(label);
+
+ int radius = 5;
+
+ void _apply() {
+ Stopwatch t = Stopwatch();
+ t.start();
+ Image image = Image.from(origImage);
+ image = gaussianBlur(image, radius);
+
+ // Fill the buffer with our image data.
+ filterImageData.data.setRange(0, filterImageData.data.length,
+ image.getBytes());
+ // Draw the buffer onto the canvas.
+ canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
+ canvas.context2D.putImageData(filterImageData, 0, 0);
+ logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
+ print(t.elapsedMilliseconds / 1000.0);
+ }
+
+ _addControl('Radius', radius.toString(), sidebar, (v) {
+ radius = v.toInt();
+ _apply();
+ });
+
+ _apply();
+}
+
+void testVignette() {
+ Html.DivElement sidebar = Html.document.querySelector('#sidebar');
+ sidebar.children.clear();
+
+ var label = Html.Element.tag('h1');
+ label.text = 'Vignette';
+ sidebar.children.add(label);
+
+ double start = 0.3;
+ double end = 0.75;
+ double amount = 1.0;
+
+ void _apply() {
+ Stopwatch t = Stopwatch();
+ t.start();
+ Image image = Image.from(origImage);
+ image = vignette(image, start: start, end: end, amount: amount);
+
+ // Fill the buffer with our image data.
+ filterImageData.data.setRange(0, filterImageData.data.length,
+ image.getBytes());
+ // Draw the buffer onto the canvas.
+ canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
+ canvas.context2D.putImageData(filterImageData, 0, 0);
+ logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
+ print(t.elapsedMilliseconds / 1000.0);
+ }
+
+ _addControl('Start', start.toString(), sidebar, (v) {
+ start = v;
+ _apply();
+ });
+
+ _addControl('End', end.toString(), sidebar, (v) {
+ end = v;
+ _apply();
+ });
+
+ _addControl('Amount', amount.toString(), sidebar, (v) {
+ amount = v;
+ _apply();
+ });
+
+ _apply();
+}
+
+void testPixelate() {
+ Html.DivElement sidebar = Html.document.querySelector('#sidebar');
+ sidebar.children.clear();
+
+ var label = Html.Element.tag('h1');
+ label.text = 'Pixelate';
+ sidebar.children.add(label);
+
+ int blockSize = 5;
+
+ void _apply() {
+ Stopwatch t = Stopwatch();
+ t.start();
+ Image image = Image.from(origImage);
+ image = pixelate(image, blockSize);
+
+ // Fill the buffer with our image data.
+ filterImageData.data.setRange(0, filterImageData.data.length,
+ image.getBytes());
+ // Draw the buffer onto the canvas.
+ canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
+ canvas.context2D.putImageData(filterImageData, 0, 0);
+ logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
+ print(t.elapsedMilliseconds / 1000.0);
+ }
+
+ _addControl('blockSize', blockSize.toString(), sidebar, (v) {
+ blockSize = v.toInt();
+ _apply();
+ });
+
+ _apply();
+}
+
+void testColorOffset() {
+ Html.DivElement sidebar = Html.document.querySelector('#sidebar');
+ sidebar.children.clear();
+
+ var label = Html.Element.tag('h1');
+ label.text = 'Pixelate';
+ sidebar.children.add(label);
+
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ int alpha = 0;
+
+ void _apply() {
+ Stopwatch t = Stopwatch();
+ t.start();
+ Image image = Image.from(origImage);
+ image = colorOffset(image, red, green, blue, alpha);
+
+ // Fill the buffer with our image data.
+ filterImageData.data.setRange(0, filterImageData.data.length,
+ image.getBytes());
+ // Draw the buffer onto the canvas.
+ canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
+ canvas.context2D.putImageData(filterImageData, 0, 0);
+ logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
+ print(t.elapsedMilliseconds / 1000.0);
+ }
+
+ _addControl('red', red.toString(), sidebar, (v) {
+ red = v.toInt();
+ _apply();
+ });
+
+ _addControl('green', red.toString(), sidebar, (v) {
+ green = v.toInt();
+ _apply();
+ });
+
+ _addControl('blue', red.toString(), sidebar, (v) {
+ blue = v.toInt();
+ _apply();
+ });
+
+ _addControl('alpha', red.toString(), sidebar, (v) {
+ alpha = v.toInt();
+ _apply();
+ });
+
+ _apply();
+}
+
+void testAdjustColor() {
+ Html.DivElement sidebar = Html.document.querySelector('#sidebar');
+ sidebar.children.clear();
+
+ var label = Html.Element.tag('h1');
+ label.text = 'Adjust Color';
+ sidebar.children.add(label);
+
+ double contrast = 1.0;
+ double saturation = 1.0;
+ double brightness = 1.0;
+ double gamma = 0.8;
+ double exposure = 0.3;
+ double hue = 0.0;
+ double amount = 1.0;
+
+ void _apply() {
+ Stopwatch t = Stopwatch();
+ t.start();
+ Image image = Image.from(origImage);
+
+ image = adjustColor(image, contrast: contrast, saturation: saturation,
+ brightness: brightness, gamma: gamma, exposure: exposure,
+ hue: hue, amount: amount);
+
+ // Fill the buffer with our image data.
+ filterImageData.data.setRange(0, filterImageData.data.length,
+ image.getBytes());
+ // Draw the buffer onto the canvas.
+ canvas.context2D.clearRect(0, 0, canvas.width, canvas.height);
+ canvas.context2D.putImageData(filterImageData, 0, 0);
+
+ logDiv.text = 'TIME: ${t.elapsedMilliseconds / 1000.0}';
+ print(t.elapsedMilliseconds / 1000.0);
+ }
+
+ _addControl('Contrast', contrast.toString(), sidebar, (v) {
+ contrast = v;
+ _apply();
+ });
+
+ _addControl('Saturation', saturation.toString(), sidebar, (v) {
+ saturation = v;
+ _apply();
+ });
+
+ _addControl('Brightness', brightness.toString(), sidebar, (v) {
+ brightness = v;
+ _apply();
+ });
+
+ _addControl('Gamma', gamma.toString(), sidebar, (v) {
+ gamma = v;
+ _apply();
+ });
+
+ _addControl('Exposure', exposure.toString(), sidebar, (v) {
+ exposure = v;
+ _apply();
+ });
+
+ _addControl('Hue', hue.toString(), sidebar, (v) {
+ hue = v;
+ _apply();
+ });
+
+ _addControl('Amount', amount.toString(), sidebar, (v) {
+ amount = v;
+ _apply();
+ });
+
+ _apply();
+}
+
+void main() {
+ canvas = Html.document.querySelector('#filter_canvas');
+ logDiv = Html.document.querySelector('#log');
+
+ Html.SelectElement menu = Html.document.querySelector('#FilterType');
+ menu.onChange.listen((e) {
+ if (menu.value == 'Pixelate') {
+ testPixelate();
+ } else if (menu.value == 'Sepia') {
+ testSepia();
+ } else if (menu.value == 'Gaussian') {
+ testGaussian();
+ } else if (menu.value == 'Adjust Color') {
+ testAdjustColor();
+ } else if (menu.value == 'Sobel') {
+ testSobel();
+ } else if (menu.value == 'Vignette') {
+ testVignette();
+ } else if (menu.value == 'Color Offset') {
+ testColorOffset();
+ }
+ });
+
+ Html.ImageElement img = Html.ImageElement();
+ img.src = 'res/big_buck_bunny.jpg';
+ img.onLoad.listen((e) {
+ var c = Html.CanvasElement();
+ c.width = img.width;
+ c.height = img.height;
+ c.context2D.drawImage(img, 0, 0);
+
+ var imageData = c.context2D.getImageData(0, 0, img.width, img.height);
+ origImage = Image.fromBytes(img.width, img.height, imageData.data);
+
+ canvas.width = img.width;
+ canvas.height = img.height;
+ filterImageData = canvas.context2D.createImageData(img.width, img.height);
+
+ testSepia();
+ });
+}
diff --git a/image/web/image_http_input.dart b/image/web/image_http_input.dart
old mode 100644
new mode 100755
index 8a720b4..4b5d991
--- a/image/web/image_http_input.dart
+++ b/image/web/image_http_input.dart
@@ -1,86 +1,86 @@
-import 'dart:html' as Html;
-
-import 'package:image/image.dart';
-import 'dart:convert';
-
-Html.InputElement fileInput;
-
-void main() {
- // There are at least two ways to get a file into an html dart app:
- // using a file Input element, or an AJAX HttpRequest.
-
- // This example demonstrates using a file Input element.
- fileInput = Html.querySelector("#file");
-
- fileInput.addEventListener("change", onFileChanged);
-}
-
-/**
- * Called when the user has selected a file.
- */
-void onFileChanged(Html.Event event) {
- Html.FileList files = fileInput.files;
- var file = files.item(0);
-
- Html.FileReader reader = new Html.FileReader();
- reader.addEventListener("load", onFileLoaded);
- reader.readAsArrayBuffer(file);
-}
-
-/**
- * Called when the file has been read.
- */
-void onFileLoaded(Html.Event event) {
- Html.FileReader reader = event.currentTarget;
-
- var bytes = reader.result;
-
- // Find a decoder that is able to decode the given file contents.
- Decoder decoder = findDecoderForData(bytes);
- if (decoder == null) {
- print('Could not find format decoder for file');
- return;
- }
-
- // If a decoder was found, decode the file contents into an image.
- Image image = decoder.decodeImage(bytes);
-
- // If the image was able to be decoded, we can display it in a couple
- // different ways. We could encode it to a format that can be displayed
- // by an IMG image element (like PNG or JPEG); or we could draw it into
- // a canvas.
- if (image != null) {
- // Add a separator to the html page
- Html.document.body.append(new Html.ParagraphElement());
-
- // Draw the image into a canvas. First create a canvas at the correct
- // resolution.
- var c = new Html.CanvasElement();
- Html.document.body.append(c);
- c.width = image.width;
- c.height = image.height;
-
- // Create a buffer that the canvas can draw.
- Html.ImageData d = c.context2D.createImageData(c.width, c.height);
- // Fill the buffer with our image data.
- d.data.setRange(0, d.data.length, image.getBytes());
- // Draw the buffer onto the canvas.
- c.context2D.putImageData(d, 0, 0);
-
-
- // OR we could use an IMG element to display the image.
- // This requires encoding it to a common format (like PNG), base64 encoding
- // the encoded image, and using a data url for the img src.
-
- var img = new Html.ImageElement();
- Html.document.body.append(img);
- // encode the image to a PNG
- var png = encodePng(image);
- // base64 encode the png
- var png64 = base64Encode(png);
- // set the img src as a data url
- img.src = 'data:image/png;base64,${png64}';
- }
-
- return;
-}
+import 'dart:html' as Html;
+
+import 'package:image/image.dart';
+import 'dart:convert';
+
+Html.InputElement fileInput;
+
+void main() {
+ // There are at least two ways to get a file into an html dart app:
+ // using a file Input element, or an AJAX HttpRequest.
+
+ // This example demonstrates using a file Input element.
+ fileInput = Html.querySelector("#file");
+
+ fileInput.addEventListener("change", onFileChanged);
+}
+
+/**
+ * Called when the user has selected a file.
+ */
+void onFileChanged(Html.Event event) {
+ Html.FileList files = fileInput.files;
+ var file = files.item(0);
+
+ Html.FileReader reader = Html.FileReader();
+ reader.addEventListener("load", onFileLoaded);
+ reader.readAsArrayBuffer(file);
+}
+
+/**
+ * Called when the file has been read.
+ */
+void onFileLoaded(Html.Event event) {
+ Html.FileReader reader = event.currentTarget;
+
+ var bytes = reader.result;
+
+ // Find a decoder that is able to decode the given file contents.
+ Decoder decoder = findDecoderForData(bytes);
+ if (decoder == null) {
+ print('Could not find format decoder for file');
+ return;
+ }
+
+ // If a decoder was found, decode the file contents into an image.
+ Image image = decoder.decodeImage(bytes);
+
+ // If the image was able to be decoded, we can display it in a couple
+ // different ways. We could encode it to a format that can be displayed
+ // by an IMG image element (like PNG or JPEG); or we could draw it into
+ // a canvas.
+ if (image != null) {
+ // Add a separator to the html page
+ Html.document.body.append(new Html.ParagraphElement());
+
+ // Draw the image into a canvas. First create a canvas at the correct
+ // resolution.
+ var c = Html.CanvasElement();
+ Html.document.body.append(c);
+ c.width = image.width;
+ c.height = image.height;
+
+ // Create a buffer that the canvas can draw.
+ Html.ImageData d = c.context2D.createImageData(c.width, c.height);
+ // Fill the buffer with our image data.
+ d.data.setRange(0, d.data.length, image.getBytes());
+ // Draw the buffer onto the canvas.
+ c.context2D.putImageData(d, 0, 0);
+
+
+ // OR we could use an IMG element to display the image.
+ // This requires encoding it to a common format (like PNG), base64 encoding
+ // the encoded image, and using a data url for the img src.
+
+ var img = Html.ImageElement();
+ Html.document.body.append(img);
+ // encode the image to a PNG
+ var png = encodePng(image);
+ // base64 encode the png
+ var png64 = base64Encode(png);
+ // set the img src as a data url
+ img.src = 'data:image/png;base64,${png64}';
+ }
+
+ return;
+}
diff --git a/image/web/image_server.dart b/image/web/image_server.dart
old mode 100644
new mode 100755
index 38c1269..ae1b0ea
--- a/image/web/image_server.dart
+++ b/image/web/image_server.dart
@@ -1,34 +1,34 @@
-import 'dart:io' as Io;
-import 'package:image/image.dart';
-
-void main(List<String> argv) {
- if (argv.length < 1) {
- print('Usage: image_server <image_file>');
- return;
- }
-
- String filename = argv[0];
-
- Io.File file = new Io.File(filename);
- if (!file.existsSync()) {
- print('File does not exist: ${filename}');
- return;
- }
-
- List<int> fileBytes = file.readAsBytesSync();
-
- Decoder decoder = findDecoderForData(fileBytes);
- if (decoder == null) {
- print('Could not find format decoder for: ${filename}');
- return;
- }
-
- Image image = decoder.decodeImage(fileBytes);
-
- // ... do something with image ...
-
- // Save the image as a PNG
- List<int> png = new PngEncoder().encodeImage(image);
- // Write the PNG to disk
- new Io.File(filename + '.png').writeAsBytesSync(png);
-}
+import 'dart:io' as Io;
+import 'package:image/image.dart';
+
+void main(List<String> argv) {
+ if (argv.length < 1) {
+ print('Usage: image_server <image_file>');
+ return;
+ }
+
+ String filename = argv[0];
+
+ Io.File file = Io.File(filename);
+ if (!file.existsSync()) {
+ print('File does not exist: ${filename}');
+ return;
+ }
+
+ List<int> fileBytes = file.readAsBytesSync();
+
+ Decoder decoder = findDecoderForData(fileBytes);
+ if (decoder == null) {
+ print('Could not find format decoder for: ${filename}');
+ return;
+ }
+
+ Image image = decoder.decodeImage(fileBytes);
+
+ // ... do something with image ...
+
+ // Save the image as a PNG
+ List<int> png = PngEncoder().encodeImage(image);
+ // Write the PNG to disk
+ new Io.File(filename + '.png').writeAsBytesSync(png);
+}
diff --git a/image/web/mandelbrot.dart b/image/web/mandelbrot.dart
old mode 100644
new mode 100755
index 25cfde2..5ef0d6c
--- a/image/web/mandelbrot.dart
+++ b/image/web/mandelbrot.dart
@@ -1,72 +1,72 @@
-import 'dart:html' as Html;
-import 'dart:math';
-import 'package:image/image.dart';
-
-double logN(num x, num div) {
- return log(x) / div;
-}
-
-/**
- * Render the Mandelbrot Set into an Image and display it.
- */
-void main() {
- const int width = 1024;
- const int height = 1024;
-
- // Create a canvas to put our decoded image into.
- var c = new Html.CanvasElement(width: width, height: height);
- Html.document.body.append(c);
-
- double zoom = 1.0;
- double moveX = -0.5;
- double moveY = 0.0;
- const int MaxIterations = 255;
- const double radius = 2.0;
- const double radius_squared = radius * radius;
- final double log2 = log(2.0);
- double Log2MaxIterations = logN(MaxIterations, log2);
- const double h_2 = height / 2.0;
- const double w_2 = width / 2.0;
- const double aspect = 0.5;
-
- Image image = new Image(width, height);
- for (int y = 0; y < height; ++y) {
- double pi = (y - h_2) / (0.5 * zoom * aspect * height) + moveY;
-
- for (int x = 0; x < width; ++x) {
- double pr = 1.5 * (x - w_2) / (0.5 * zoom * width) + moveX;
-
- double newRe = 0.0;
- double newIm = 0.0;
- int i = 0;
- for (; i < MaxIterations; i++) {
- double oldRe = newRe;
- double oldIm = newIm;
-
- newRe = oldRe * oldRe - oldIm * oldIm + pr;
- newIm = 2.0 * oldRe * oldIm + pi;
-
- if ((newRe * newRe + newIm * newIm) > radius_squared) {
- break;
- }
- }
-
- if (i == MaxIterations) {
- image.setPixelRGBA(x, y, 0, 0, 0);
- } else {
- double z = sqrt(newRe * newRe + newIm * newIm);
- double b = 256.0 * logN(1.75 + i -
- logN(logN(z, log2), log2), log2) / Log2MaxIterations;
- int brightness = b.toInt();
- image.setPixelRGBA(x, y, brightness, brightness, 255);
- }
- }
- }
-
- // Create a buffer that the canvas can draw.
- Html.ImageData d = c.context2D.createImageData(image.width, image.height);
- // Fill the buffer with our image data.
- d.data.setRange(0, d.data.length, image.getBytes());
- // Draw the buffer onto the canvas.
- c.context2D.putImageData(d, 0, 0);
-}
+import 'dart:html' as Html;
+import 'dart:math';
+import 'package:image/image.dart';
+
+double logN(num x, num div) {
+ return log(x) / div;
+}
+
+/**
+ * Render the Mandelbrot Set into an Image and display it.
+ */
+void main() {
+ const int width = 1024;
+ const int height = 1024;
+
+ // Create a canvas to put our decoded image into.
+ var c = Html.CanvasElement(width: width, height: height);
+ Html.document.body.append(c);
+
+ double zoom = 1.0;
+ double moveX = -0.5;
+ double moveY = 0.0;
+ const int MaxIterations = 255;
+ const double radius = 2.0;
+ const double radius_squared = radius * radius;
+ final double log2 = log(2.0);
+ double Log2MaxIterations = logN(MaxIterations, log2);
+ const double h_2 = height / 2.0;
+ const double w_2 = width / 2.0;
+ const double aspect = 0.5;
+
+ Image image = Image(width, height);
+ for (int y = 0; y < height; ++y) {
+ double pi = (y - h_2) / (0.5 * zoom * aspect * height) + moveY;
+
+ for (int x = 0; x < width; ++x) {
+ double pr = 1.5 * (x - w_2) / (0.5 * zoom * width) + moveX;
+
+ double newRe = 0.0;
+ double newIm = 0.0;
+ int i = 0;
+ for (; i < MaxIterations; i++) {
+ double oldRe = newRe;
+ double oldIm = newIm;
+
+ newRe = oldRe * oldRe - oldIm * oldIm + pr;
+ newIm = 2.0 * oldRe * oldIm + pi;
+
+ if ((newRe * newRe + newIm * newIm) > radius_squared) {
+ break;
+ }
+ }
+
+ if (i == MaxIterations) {
+ image.setPixelRGBA(x, y, 0, 0, 0);
+ } else {
+ double z = sqrt(newRe * newRe + newIm * newIm);
+ double b = 256.0 * logN(1.75 + i -
+ logN(logN(z, log2), log2), log2) / Log2MaxIterations;
+ int brightness = b.toInt();
+ image.setPixelRGBA(x, y, brightness, brightness, 255);
+ }
+ }
+ }
+
+ // Create a buffer that the canvas can draw.
+ Html.ImageData d = c.context2D.createImageData(image.width, image.height);
+ // Fill the buffer with our image data.
+ d.data.setRange(0, d.data.length, image.getBytes());
+ // Draw the buffer onto the canvas.
+ c.context2D.putImageData(d, 0, 0);
+}
diff --git a/image/web/res/1.webp b/image/web/res/1.webp
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/res/1_webp_ll.webp b/image/web/res/1_webp_ll.webp
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/res/3_webp_a.webp b/image/web/res/3_webp_a.webp
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/res/BladeRunner_lossy.webp b/image/web/res/BladeRunner_lossy.webp
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/res/animated.png b/image/web/res/animated.png
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/res/big_buck_bunny.jpg b/image/web/res/big_buck_bunny.jpg
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/res/cars.gif b/image/web/res/cars.gif
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/res/penguins.jpg b/image/web/res/penguins.jpg
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/res/puppies.jpg b/image/web/res/puppies.jpg
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/res/trees.png b/image/web/res/trees.png
old mode 100644
new mode 100755
Binary files differ
diff --git a/image/web/test_formats.dart b/image/web/test_formats.dart
old mode 100644
new mode 100755
index af839f2..7005f75
--- a/image/web/test_formats.dart
+++ b/image/web/test_formats.dart
@@ -1,102 +1,102 @@
-import 'dart:html' as Html;
-import 'dart:async' as Async;
-import 'dart:typed_data';
-import 'package:image/image.dart';
-
-/**
- * Decode and display various image formats. This is used as a visual
- * unit-test to indentify problems that may occur after the translation to
- * javascript.
- */
-void main() {
- // An img on the html page is used to establish the path to the images
- // directory. It's removed after we get the path since we'll be populating
- // the page with our own decoded images.
- Html.ImageElement img = Html.querySelectorAll('img')[0];
- String path = img.src.substring(0, img.src.lastIndexOf('/'));
- img.remove();
-
- // The list of images we'll be decoding, representing a wide range
- // of formats and sub-formats.
- List<String> images = ['penguins.jpg', '1_webp_ll.webp', '1.webp', '3_webp_a.webp',
- 'puppies.jpg', 'cars.gif', 'trees.png',
- 'animated.png', 'BladeRunner_lossy.webp'];
-
- for (String name in images) {
- // Use an http request to get the image file from disk.
- var req = new Html.HttpRequest();
- req.open('GET', path + '/' + name);
- req.responseType = 'arraybuffer';
- req.onLoadEnd.listen((e) {
- if (req.status == 200) {
- // Convert the text to binary byte list.
- List<int> bytes = new Uint8List.view(req.response);
-
- var label = new Html.DivElement();
- Html.document.body.append(label);
- label.text = name;
-
- // Create a canvas to put our decoded image into.
- var c = new Html.CanvasElement();
- Html.document.body.append(c);
-
- // Find the best decoder for the image.
- Decoder decoder = findDecoderForData(bytes);
- if (decoder == null) {
- return;
- }
-
- // Some of the files are animated, so always decode to animation.
- // Single image files will decode to a single framed animation.
- Animation anim = decoder.decodeAnimation(bytes);
- if (anim == null) {
- return;
- }
-
- // If it's a single image, dump the decoded image into the canvas.
- if (anim.length == 1) {
- Image image = anim.frames[0];
-
- //Image newImage = copyResize(image, 2000, -1, CUBIC);
- var newImage = image;
-
- c.width = newImage.width;
- c.height = newImage.height;
-
- // Create a buffer that the canvas can draw.
- Html.ImageData d = c.context2D.createImageData(c.width, c.height);
- // Fill the buffer with our image data.
- d.data.setRange(0, d.data.length, newImage.getBytes());
- // Draw the buffer onto the canvas.
- c.context2D.putImageData(d, 0, 0);
-
- return;
- }
-
- // A multi-frame animation, use a timer to draw frames.
- // TODO this is currently not using the timing information in the
- // [Animation], and using a hard-coded delay instead.
-
- // Setup the canvas size to the size of the first image.
- c.width = anim.frames[0].width;
- c.height = anim.frames[0].height;
- // Create a buffer that the canvas can draw.
- Html.ImageData d = c.context2D.createImageData(c.width, c.height);
-
- int frame = 0;
- new Async.Timer.periodic(new Duration(milliseconds: 40), (t) {
- Image image = anim.frames[frame++];
- if (frame >= anim.numFrames) {
- frame = 0;
- }
-
- // Fill the buffer with our image data.
- d.data.setRange(0, d.data.length, image.getBytes());
- // Draw the buffer onto the canvas.
- c.context2D.putImageData(d, 0, 0);
- });
- }
- });
- req.send('');
- }
-}
+import 'dart:html' as Html;
+import 'dart:async' as Async;
+import 'dart:typed_data';
+import 'package:image/image.dart';
+
+/**
+ * Decode and display various image formats. This is used as a visual
+ * unit-test to indentify problems that may occur after the translation to
+ * javascript.
+ */
+void main() {
+ // An img on the html page is used to establish the path to the images
+ // directory. It's removed after we get the path since we'll be populating
+ // the page with our own decoded images.
+ Html.ImageElement img = Html.querySelectorAll('img')[0];
+ String path = img.src.substring(0, img.src.lastIndexOf('/'));
+ img.remove();
+
+ // The list of images we'll be decoding, representing a wide range
+ // of formats and sub-formats.
+ List<String> images = ['penguins.jpg', '1_webp_ll.webp', '1.webp', '3_webp_a.webp',
+ 'puppies.jpg', 'cars.gif', 'trees.png',
+ 'animated.png', 'BladeRunner_lossy.webp'];
+
+ for (String name in images) {
+ // Use an http request to get the image file from disk.
+ var req = Html.HttpRequest();
+ req.open('GET', path + '/' + name);
+ req.responseType = 'arraybuffer';
+ req.onLoadEnd.listen((e) {
+ if (req.status == 200) {
+ // Convert the text to binary byte list.
+ List<int> bytes = Uint8List.view(req.response);
+
+ var label = Html.DivElement();
+ Html.document.body.append(label);
+ label.text = name;
+
+ // Create a canvas to put our decoded image into.
+ var c = Html.CanvasElement();
+ Html.document.body.append(c);
+
+ // Find the best decoder for the image.
+ Decoder decoder = findDecoderForData(bytes);
+ if (decoder == null) {
+ return;
+ }
+
+ // Some of the files are animated, so always decode to animation.
+ // Single image files will decode to a single framed animation.
+ Animation anim = decoder.decodeAnimation(bytes);
+ if (anim == null) {
+ return;
+ }
+
+ // If it's a single image, dump the decoded image into the canvas.
+ if (anim.length == 1) {
+ Image image = anim.frames[0];
+
+ //Image newImage = copyResize(image, 2000, -1, CUBIC);
+ var newImage = image;
+
+ c.width = newImage.width;
+ c.height = newImage.height;
+
+ // Create a buffer that the canvas can draw.
+ Html.ImageData d = c.context2D.createImageData(c.width, c.height);
+ // Fill the buffer with our image data.
+ d.data.setRange(0, d.data.length, newImage.getBytes());
+ // Draw the buffer onto the canvas.
+ c.context2D.putImageData(d, 0, 0);
+
+ return;
+ }
+
+ // A multi-frame animation, use a timer to draw frames.
+ // TODO this is currently not using the timing information in the
+ // [Animation], and using a hard-coded delay instead.
+
+ // Setup the canvas size to the size of the first image.
+ c.width = anim.frames[0].width;
+ c.height = anim.frames[0].height;
+ // Create a buffer that the canvas can draw.
+ Html.ImageData d = c.context2D.createImageData(c.width, c.height);
+
+ int frame = 0;
+ new Async.Timer.periodic(new Duration(milliseconds: 40), (t) {
+ Image image = anim.frames[frame++];
+ if (frame >= anim.numFrames) {
+ frame = 0;
+ }
+
+ // Fill the buffer with our image data.
+ d.data.setRange(0, d.data.length, image.getBytes());
+ // Draw the buffer onto the canvas.
+ c.context2D.putImageData(d, 0, 0);
+ });
+ }
+ });
+ req.send('');
+ }
+}
diff --git a/image/web/test_jpeg_encoder.dart b/image/web/test_jpeg_encoder.dart
old mode 100644
new mode 100755
index ed11261..fdad554
--- a/image/web/test_jpeg_encoder.dart
+++ b/image/web/test_jpeg_encoder.dart
@@ -1,24 +1,24 @@
-import 'dart:html' as Html;
-import 'dart:convert';
-import 'package:image/image.dart';
-
-void main() {
- var theImg = Html.document.getElementById('testimage') as Html.ImageElement;
- var cvs = Html.document.createElement('canvas') as Html.CanvasElement;
- cvs.width = theImg.width;
- cvs.height = theImg.height;
-
- var ctx = cvs.getContext("2d") as Html.CanvasRenderingContext2D;
-
- ctx.drawImage(theImg,0,0);
-
- var bytes = ctx.getImageData(0, 0, cvs.width, cvs.height).data;
- Image image = new Image.fromBytes(cvs.width, cvs.height, bytes);
-
- var jpg = encodeJpg(image, quality: 25);
-
- var jpg64 = base64Encode(jpg);
- var img = Html.document.createElement('img') as Html.ImageElement;
- img.src = 'data:image/png;base64,${jpg64}';
- Html.document.body.append(img);
-}
+import 'dart:html' as Html;
+import 'dart:convert';
+import 'package:image/image.dart';
+
+void main() {
+ var theImg = Html.document.getElementById('testimage') as Html.ImageElement;
+ var cvs = Html.document.createElement('canvas') as Html.CanvasElement;
+ cvs.width = theImg.width;
+ cvs.height = theImg.height;
+
+ var ctx = cvs.getContext("2d") as Html.CanvasRenderingContext2D;
+
+ ctx.drawImage(theImg,0,0);
+
+ var bytes = ctx.getImageData(0, 0, cvs.width, cvs.height).data;
+ Image image = Image.fromBytes(cvs.width, cvs.height, bytes);
+
+ var jpg = encodeJpg(image, quality: 25);
+
+ var jpg64 = base64Encode(jpg);
+ var img = Html.document.createElement('img') as Html.ImageElement;
+ img.src = 'data:image/png;base64,${jpg64}';
+ Html.document.body.append(img);
+}
diff --git a/image/web/webp_viewer.dart b/image/web/webp_viewer.dart
old mode 100644
new mode 100755
index aec6f60..26aa389
--- a/image/web/webp_viewer.dart
+++ b/image/web/webp_viewer.dart
@@ -1,34 +1,34 @@
-import 'dart:html' as Html;
-import 'dart:convert';
-import 'package:image/image.dart';
-
-/**
- * Convert all .webp IMG elements on the page to PNG so that they can be viewed
- * by browsers like FireFox and IE.
- */
-void main() {
- var images = Html.querySelectorAll('img');
-
- for (var _img in images) {
- var img = _img as Html.ImageElement;
- if (img.src.toLowerCase().endsWith('.webp')) {
- var req = new Html.HttpRequest();
- req.open('GET', img.src);
- req.overrideMimeType('text\/plain; charset=x-user-defined');
- req.onLoadEnd.listen((e) {
- if (req.status == 200) {
- var bytes = req.responseText.split('').map((e){
- return new String.fromCharCode(e.codeUnitAt(0) & 0xff);
- }).join('').codeUnits;
-
- Image image = decodeWebP(bytes);
- List<int> png = encodePng(image);
-
- var png64 = base64Encode(png);
- img.src = 'data:image/png;base64,${png64}';
- }
- });
- req.send('');
- }
- }
-}
+import 'dart:html' as Html;
+import 'dart:convert';
+import 'package:image/image.dart';
+
+/**
+ * Convert all .webp IMG elements on the page to PNG so that they can be viewed
+ * by browsers like FireFox and IE.
+ */
+void main() {
+ var images = Html.querySelectorAll('img');
+
+ for (var _img in images) {
+ var img = _img as Html.ImageElement;
+ if (img.src.toLowerCase().endsWith('.webp')) {
+ var req = Html.HttpRequest();
+ req.open('GET', img.src);
+ req.overrideMimeType('text\/plain; charset=x-user-defined');
+ req.onLoadEnd.listen((e) {
+ if (req.status == 200) {
+ var bytes = req.responseText.split('').map((e){
+ return new String.fromCharCode(e.codeUnitAt(0) & 0xff);
+ }).join('').codeUnits;
+
+ Image image = decodeWebP(bytes);
+ List<int> png = encodePng(image);
+
+ var png64 = base64Encode(png);
+ img.src = 'data:image/png;base64,${png64}';
+ }
+ });
+ req.send('');
+ }
+ }
+}