Compare commits

...

191 commits

Author SHA1 Message Date
Daniel Bulant
31f3472b34 Fix relative import exports 2020-05-29 19:23:47 +02:00
Daniel Bulant
8eabb688fe Remove strict since it's default in deno 2020-05-29 18:58:20 +02:00
Daniel Bulant
c2302025b7 NOTICE 2020-05-29 18:53:56 +02:00
dependabot-preview[bot]
bdcc276351
Bump eslint from 6.8.0 to 7.0.0 (#156)
Bumps [eslint](https://github.com/eslint/eslint) from 6.8.0 to 7.0.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v6.8.0...v7.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-19 07:30:19 +02:00
dependabot-preview[bot]
f90b7c7121
Bump codecov from 3.6.5 to 3.7.0 (#159)
Bumps [codecov](https://github.com/codecov/codecov-node) from 3.6.5 to 3.7.0.
- [Release notes](https://github.com/codecov/codecov-node/releases)
- [Commits](https://github.com/codecov/codecov-node/compare/v3.6.5...v3.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-19 07:29:21 +02:00
dependabot-preview[bot]
738c9649b3
Bump puppeteer from 3.0.1 to 3.0.2 (#153)
Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Commits](https://github.com/puppeteer/puppeteer/compare/v3.0.1...v3.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-29 07:51:09 +02:00
dependabot-preview[bot]
35258f8817
Bump puppeteer from 3.0.0 to 3.0.1 (#151)
Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Commits](https://github.com/puppeteer/puppeteer/compare/v3.0.0...v3.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-27 07:27:20 +02:00
dependabot-preview[bot]
8d4eefe0b5
Bump tape from 4.13.2 to 5.0.0 (#152)
Bumps [tape](https://github.com/substack/tape) from 4.13.2 to 5.0.0.
- [Release notes](https://github.com/substack/tape/releases)
- [Commits](https://github.com/substack/tape/compare/v4.13.2...v5.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-27 07:27:06 +02:00
dependabot-preview[bot]
28a0c02502
Bump eslint-config-prettier from 6.10.1 to 6.11.0 (#149)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 6.10.1 to 6.11.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v6.10.1...v6.11.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-22 09:46:14 +02:00
dependabot-preview[bot]
e5f4fdef88
Bump prettier from 2.0.4 to 2.0.5 (#150)
Bumps [prettier](https://github.com/prettier/prettier) from 2.0.4 to 2.0.5.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.0.4...2.0.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-22 09:45:59 +02:00
dependabot-preview[bot]
e530f459da
Bump puppeteer from 2.1.1 to 3.0.0 (#148)
Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 2.1.1 to 3.0.0.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Commits](https://github.com/puppeteer/puppeteer/compare/v2.1.1...v3.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-17 16:33:03 +02:00
Luke Page
6736cef032
Fix incorrect info in readme 2020-04-15 21:20:41 +02:00
Luke Page
c3a2ba2cc5 v5.0.0 2020-04-15 21:16:02 +02:00
divdavem
792e432c9c
fixes error when trying to access this._reads in chunkstream.js (#147) 2020-04-15 21:04:48 +02:00
Luke Page
2c7b959a73
Update appveyor to only build master (#145) 2020-04-10 14:36:10 +02:00
Luke Page
0704ce06d7
Only build master branch to avoid duplicate builds (#144) 2020-04-10 14:21:52 +02:00
Luke Page
40178cabb2
Fix Travis CI link after migration (#143) 2020-04-10 14:21:22 +02:00
Luke Page
f8cb8eecbc generate a lcov report for codecov 2020-04-10 13:56:08 +02:00
dependabot-preview[bot]
8cd8a5549b
[Security] Bump cached-path-relative from 1.0.1 to 1.0.2 (#141)
Bumps [cached-path-relative](https://github.com/ashaffer/cached-path-relative) from 1.0.1 to 1.0.2. **This update includes security fixes.**
- [Release notes](https://github.com/ashaffer/cached-path-relative/releases)
- [Commits](https://github.com/ashaffer/cached-path-relative/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-10 09:54:28 +02:00
dependabot-preview[bot]
088dbb7b8a
[Security] Bump minimist from 1.2.0 to 1.2.5 (#142)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.0 to 1.2.5. **This update includes security fixes.**
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.0...1.2.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-10 09:54:15 +02:00
Luke Page
f3110ce2e0 switch to codecov 2020-04-10 09:53:05 +02:00
dependabot-preview[bot]
b127fc658d
Bump puppeteer from 1.20.0 to 2.1.1 (#140)
Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 1.20.0 to 2.1.1.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Commits](https://github.com/puppeteer/puppeteer/compare/v1.20.0...v2.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-10 09:30:32 +02:00
Luke Page
7158967dcf
Fix appveyor builds 2020-04-10 09:12:28 +02:00
Luke Page
68b6da32b6
Remove greenkeeper reference 2020-04-10 09:08:59 +02:00
Luke Page
6b93d6f9cc upgrade futher 2020-04-09 21:12:56 +02:00
Luke Page
5e92cc3722 upgrade eslint 2020-04-09 21:06:01 +02:00
Luke Page
32271b56e3 prettier, es6, upgrades 2020-04-09 20:28:27 +02:00
Luke Page
31bedc7bb0 v4.0.0 2020-04-09 15:58:37 +02:00
smallnamespace
5d5f543c41
Fix https://github.com/lukeapage/pngjs/issues/129 (#130) 2020-04-09 15:50:37 +02:00
ossdev07
90e00c07fd
Pngjs:Replace phantomjs dependency with chromium puppeteer (#135)
* pngjs: Replace phantomjs dependency with chromium puppeteer

Signed-off-by: ossdev <ossdev@puresoftware.com>

* pngjs: Replace phantomjs dependency with chromium puppeteer

Signed-off-by: ossdev <ossdev@puresoftware.com>

* Removed Node-v6 and added Node-v12 support from travis
2020-04-09 15:49:20 +02:00
Zachary Holyszko
257a4c972d
fix(buffer): migrate to safe constructor methods (#134)
Co-authored-by: Zachary Holyszko <zholyszko@dmdconnects.com>
2020-04-09 15:48:18 +02:00
greenkeeper[bot]
8cf115b010 Update eslint to the latest version 🚀 (#126)
* chore(package): update eslint to version 5.15.2

* chore(package): update lockfile package-lock.json
2019-03-16 19:08:44 +01:00
Luke Page
9e499c4f92 v3.4.0 2019-03-09 13:59:38 +01:00
greenkeeper[bot]
7e160967c9 Update eslint to the latest version 🚀 (#86) 2019-03-09 13:51:49 +01:00
Luke Page
d6bf8d133a
Update to node 10 in .travis.yml (#124) 2019-03-09 13:35:31 +01:00
Cory Snider
0b5822a4be Emit an error if the image is truncated (#118)
If the image data is truncated on a chunk read boundary, PNGjs would
hang. Now it emits an error event.
2019-03-08 06:31:01 +01:00
Max Gordon
728cc7e265 Added a browserified version (#98) 2019-03-08 06:29:59 +01:00
greenkeeper[bot]
7acfa680b1 chore(package): update tap-dot to version 2.0.0 (#113) 2019-03-08 06:28:27 +01:00
Liam McLoughlin
decc76f686 Return a correct alpha value in the metadata callback when a tRNS chunk is present (#119)
* Fix "metadata" alpha property to respect presence of tRNS chunk

* Emit metadata at first IDAT chunk

* Fix typo
2019-03-08 06:27:54 +01:00
Victor Homyakov
2d78949365 Speed up mapImage8Bit and mapImageCustomBit (#123)
Inner loop with `if()` comparison is eliminated and replaced with
function call.

Synthetic benchmark shows 1.5x increase in speed of mapImage8Bit for big
PNG images (e.g. 4064 x 2338, 8-bit/color RGBA, non-interlaced
screenshots).
2019-03-08 06:25:36 +01:00
Luke Page
ae7b0a1534 v3.3.3 2018-04-19 22:06:27 +02:00
Ryo Kawaguchi
0a6201df87 Support up to node v9 (#110)
* Fix compatibility issues with node 9.

* Fix async tests.
2018-04-19 22:04:14 +02:00
Luke Page
32d0580228 Fix filter none. Fixes #92 2018-02-16 10:03:37 +01:00
Luke Page
f947e3fcfb v3.3.2 2018-02-16 09:56:01 +01:00
Morgan Intrator
f5acfc5855 Kill zlib (#102) 2018-02-16 09:54:16 +01:00
Luke Page
a3d9c92eec v3.3.1 2017-11-15 19:40:12 +01:00
Jesus Nuevo
bdc384e9c3 Fixes for #88 and #93 (#94) 2017-11-15 07:47:10 +01:00
Luke Page
08534acc84 v3.3.0 2017-08-05 10:53:08 +02:00
Jesus Nuevo
2c37ebcbf2 Add writing 16 bit channels and support for grayscale input (#82)
* Add support for writing 16 bit channels

* Fix for quick exit from the bitpacker

* Add example and describe bitDepth option in README
2017-08-05 10:41:47 +02:00
Gergely Hornich
a1d9b0a52d Fix typos in readme (#85) 2017-06-14 19:09:49 +02:00
Luke Page
9c35587467 v3.2.0 2017-04-30 11:48:35 +02:00
Eugene Kulabuhov
f28d2a9e02 Added support for encoding 8-bit grayscale images (colorType = 0 and 4) (#76)
* Added support for encoding 8-bit grayscale images (colorType = 4)

* Fixed unit tests, updated README

* [README.md] Added Browserify and TOC
2017-04-30 11:45:27 +02:00
Luke Page
4c09644993 v3.1.0 2017-04-30 11:41:35 +02:00
toriningen
70f55344de Fix for bug #78 (#79)
* Fixed bug #78.

Implemented support for PNGs with unterminated IDAT stream.
Implemented synchronous inflater with length limit support.

* Removed duplicate line.

* Fixing linter errors.

* Fixing linter errors.

* no finishFlushFlag on Node 4

* It now passes tests on Node 4
2017-04-30 11:32:05 +02:00
yjhuoh
8bb2686bb9 typo in readme (#74) 2017-03-05 06:50:39 +00:00
Luke Page
d64454721c v3.0.1 2017-02-16 07:09:50 +01:00
greenkeeper[bot]
2c5f972566 docs(readme): add Greenkeeper badge (#73)
https://greenkeeper.io/
2017-02-15 18:34:47 +01:00
Xin-Xin Wang
e3bef7d1a6 Fix issue #65 (single pixel png causes an error) (#72) 2017-02-15 18:19:32 +01:00
Martin Segado
3ad37374e3 fix: explicitly coerce all pixel-dimensioned inputs to integers (#66) 2017-01-09 07:12:46 +01:00
Greenkeeper
b495e60323 chore(package): update eslint to version 3.2.2 (#63)
https://greenkeeper.io/
2016-08-03 05:42:32 +02:00
Luke Page
96c67ea08d drop support for node < 4 2016-08-03 05:37:26 +02:00
Balthazar Gronon
f49e99aa28 chore(chunkstream): minor typo (#58) 2016-07-06 18:28:57 +02:00
Luke Page
e5b4faf258 Fix travis file 2016-04-27 05:54:54 +02:00
Luke Page
b914ab4d80 remove node-zlib-backport as a dependency as it causes too many problems. Fixes #48 2016-04-27 05:35:51 +02:00
Luke Page
ebdb3c546c v2.3.0 2016-04-22 07:30:39 +02:00
Mitar
e1c1022162 Backporting to 0.10
* Backporting to 0.10 for Meteor.

* Still use zlib if possible.

* Updated README.

* Try Travis CI for 0.10.
2016-04-21 23:45:11 +02:00
Luke Page
3c121291e8 Merge pull request #44 from lukeapage/greenkeeper-eslint-2.5.0
eslint@2.5.0 breaks build 🚨
2016-03-26 09:00:34 +01:00
Luke Page
e406a7527f Merge pull request #45 from lukeapage/greenkeeper-phantomjs-prebuilt-2.1.7
phantomjs-prebuilt@2.1.7 breaks build 🚨
2016-03-26 08:33:32 +01:00
greenkeeperio-bot
d1f3b0b9b1 chore(package): update phantomjs-prebuilt to version 2.1.7
http://greenkeeper.io/
2016-03-25 22:51:08 +01:00
greenkeeperio-bot
8e20083602 chore(package): update eslint to version 2.5.0
http://greenkeeper.io/
2016-03-25 20:46:44 +01:00
Luke Page
c7d7cbc305 Merge pull request #42 from lukeapage/greenkeeper-eslint-2.1.0
Update eslint to version 2.1.0 🚀
2016-02-16 06:52:44 +01:00
greenkeeperio-bot
4d6b6c16da chore(package): update eslint to version 2.1.0
http://greenkeeper.io/
2016-02-16 06:06:25 +01:00
Luke Page
a3f3ef50df clarification - fixes #36 2016-02-16 05:57:59 +01:00
Luke Page
f5faba0119 Merge pull request #40 from lukeapage/greenkeeper-phantomjs-2.1.3
Update phantomjs to version 2.1.3 🚀
2016-01-28 08:52:12 +01:00
greenkeeperio-bot
785d6c1ab0 chore(package): update phantomjs to version 2.1.3
http://greenkeeper.io/
2016-01-28 05:54:26 +01:00
Luke Page
ff2d83c6d5 Merge pull request #37 from lukeapage/greenkeeper-update-all
Update all dependencies 🌴
2016-01-06 16:52:51 +00:00
greenkeeperio-bot
b23bc321a6 chore(package): update dependencies
http://greenkeeper.io/
2016-01-06 16:50:42 +00:00
Luke Page
596f130230 Correct instructions for testing - related to #36 2015-12-15 13:06:12 +00:00
Luke Page
2ec7f0d50f v2.2.0 2015-12-04 21:27:05 +00:00
Luke Page
1ba93c2af8 Fix newfile example. Fixes #35 2015-12-04 21:21:29 +00:00
Luke Page
5ee7782212 Merge pull request #34 from gusc/master
I created a PNG.sync.write
2015-12-04 21:15:53 +00:00
Gusts Kaksis
c76df92fd4 Moved packer processing options to Packer class. Use zlib deflate options in PackerSync. 2015-12-04 10:35:19 +02:00
Gusts Kaksis
bc271fcf38 Added missing "use strict" and fixed an ESLint no-param-reassign warning 2015-12-03 18:31:56 +02:00
Gusts Kaksis
ac815b87ee Implemented symetric PNG.sync.write to complement PNG.sync.read 2015-12-03 18:00:33 +02:00
Gusts Kaksis
42fd49f0b6 Begin to lay out a symetric packer api for synchronous usage. 2015-11-25 14:36:22 +02:00
Luke Page
a04ccc330a 2.1 release 2015-10-28 19:50:37 +00:00
Luke Page
214838df84 2.1 changelog 2015-10-28 19:50:27 +00:00
Luke Page
db33e31a41 clarify bgColor option 2015-10-28 19:48:26 +00:00
Luke Page
43852cdab6 Update .travis.yml 2015-10-28 19:44:44 +00:00
Luke Page
346bdaa07b test on node 4.3 in travis 2015-10-28 19:43:05 +00:00
Luke Page
f4b394e117 update badge names pngjs2 -> pngjs 2015-10-28 19:41:30 +00:00
Luke Page
e81a7525e5 Merge pull request #32 from cb1kenobi/flatten-bgcolor
Added bgColor support when removing alpha from image.
2015-10-28 19:39:04 +00:00
Chris Barber
76450f7e2d Added bgColor support when removing alpha from image. 2015-10-23 14:05:00 -05:00
Luke Page
717cbb13b4 rename to pngjs 2015-10-20 17:09:09 +01:00
Luke Page
8575154813 Update to pngjs 2015-10-20 17:07:34 +01:00
Luke Page
45033d4031 expand example to create a new file 2015-10-09 07:49:42 +01:00
Luke Page
912df207b5 don't include filter type when parsing 2015-10-09 07:46:45 +01:00
Luke Page
0da750762f Update changelog 2015-10-08 12:17:59 +01:00
Luke Page
7878c860ad Version 2 release 2015-10-08 12:15:01 +01:00
Luke Page
c631e6507c Merge pull request #30 from elisee/patch-3
Fix a couple typos in README.md
2015-10-08 12:14:02 +01:00
Luke Page
b5105432b3 Merge pull request #29 from elisee/patch-2
Remove accidentally-added "src" parameter from PNG.prototype.bitblt
2015-10-08 12:13:42 +01:00
Luke Page
ace6fa505b Merge pull request #28 from elisee/patch-1
Require the correct module in the README example
2015-10-08 12:12:48 +01:00
Elisée
4004ac2cd5 Fix a couple typos in README.md 2015-10-08 09:43:43 +02:00
Elisée
7717852dad Remove "src" parameter from PNG.prototype.bitblt 2015-10-08 09:31:32 +02:00
Elisée
3c3eb6d709 Require the correct module in the README example 2015-10-08 08:22:43 +02:00
Luke Page
b90e811243 1.2.0 release 2015-09-13 19:20:59 +01:00
Luke Page
d7a8638b72 do not use the output format to determine the input one 2015-09-13 19:19:32 +01:00
Luke Page
d08fc30edb Use lowercase BPP consistently 2015-09-13 18:43:44 +01:00
Luke Page
159221c115 move color type constants to constants. Revert change requiring default options on null. 2015-09-13 18:40:53 +01:00
michuNEEO
780a796de3 add example 2015-09-13 14:56:07 +02:00
michuNEEO
4428bba483 update gitignore 2015-09-13 14:55:54 +02:00
michuNEEO
6467ad747b update readme, fix stlye issues 2015-09-13 14:41:40 +02:00
michuNEEO
83d60edeaf poc to support colortype 2 2015-09-13 14:31:22 +02:00
Luke Page
75d2927e75 Remove filter type -1 2015-09-08 08:01:04 +01:00
Luke Page
99c5b43871 1.1.0 release 2015-09-07 13:19:38 +01:00
Luke Page
79409fb535 Merge pull request #22 from gagern/deflateFactory
Help producing reproducible results
2015-09-07 13:19:01 +01:00
Martin von Gagern
a0596a58c4 Introduce deflateFactory option to packer
The main application motivating this addition is the creation of
reproducible compression results.  Since `zlib` is shipped with node.js, we
have no control over its version via npm.  To guarantee reproducible
results, other libraries (like pako) could be used, with a fixed version
number to guarantee consistent behavior.  This modification provides a hook
to allow such usage.
2015-08-28 09:59:28 +02:00
Luke Page
7f456bacda 1.0.2 release 2015-08-22 07:04:03 +01:00
Luke Page
15896644fd Expose all info (palette and transparent color) to the user 2015-08-22 07:03:41 +01:00
Luke Page
33be98c413 v1.0.1 2015-08-21 08:11:38 +01:00
Luke Page
ba44715818 Fix #21 - interlaced png's that were not square failed because of typo 2015-08-21 08:11:10 +01:00
Luke Page
880d1b0814 v1.0.0 release 2015-08-08 16:46:59 +01:00
Luke Page
109c83eba2 add the tests from pngparse - fixes #12 2015-08-08 16:39:29 +01:00
Luke Page
dd5003a518 Documentation of sync api and adjustGamma. Fixes #14 2015-08-08 07:16:25 +01:00
Luke Page
6cf6d787bc catch async exceptions and emit error 2015-08-08 06:53:15 +01:00
Luke Page
3ccd332f5a refactor - shorten method param count 2015-08-08 06:42:22 +01:00
Luke Page
3ddd3f11a3 split out bitmapper 2015-08-07 20:46:28 +01:00
Luke Page
adf5216a49 Tidy up filtering. 2015-08-07 07:59:57 +01:00
Luke Page
6607d71c04 test - no large option the wrong way round 2015-08-07 08:26:27 +01:00
Luke Page
b23964c551 Try to run coveralls only in 0.12 2015-08-07 08:20:52 +01:00
Luke Page
88869809e5 tweaks to CI config 2015-08-07 08:12:46 +01:00
Luke Page
119d403098 Rename createData 2015-08-07 07:31:14 +01:00
Luke Page
2fbf81e598 Finished is now optional 2015-08-06 19:57:39 +01:00
Luke Page
735902ff9e refactor parser-sync 2015-08-06 19:46:30 +01:00
Luke Page
1c817dbb9d pick up nolarge no matter how many spec files there are 2015-08-06 19:45:24 +01:00
Luke Page
79465895af Gamma division constant 2015-08-06 18:50:36 +01:00
Luke Page
b24153494e Even longer timeouts 2015-08-06 18:47:19 +01:00
Luke Page
411d777e22 Large timeouts for slow CI servers 2015-08-06 18:36:06 +01:00
Luke Page
a5b282356b bg spec 2015-08-06 13:23:33 +01:00
Luke Page
5310b0ed5f Code format test code 2015-08-06 13:12:14 +01:00
Luke Page
288a696584 Tap ftw 2015-08-06 13:08:58 +01:00
Luke Page
cdd6f0c5ab tidy examples a little 2015-08-05 23:37:30 +01:00
Luke Page
f84416a523 Merge branch 'master' of github.com:lukeapage/node-pngjs2 2015-08-05 23:21:36 +01:00
Luke Page
71a371d6cd Pull out format normaliser. Fix #18 2015-08-05 23:21:28 +01:00
Luke Page
c450597b09 Merge branch 'master' of github.com:lukeapage/node-pngjs2 2015-08-05 17:00:47 +01:00
Luke Page
ff9d322a36 add ability to write gamma chunks 2015-08-05 17:00:37 +01:00
Luke Page
eabc8ac061 Update package.json 2015-08-04 23:11:37 +01:00
Luke Page
8cf14b9ca2 Update licence 2015-08-04 23:05:56 +01:00
Luke Page
41c5a14950 Remove copyright from individual files. It is included in the project once. 2015-08-04 23:03:38 +01:00
Luke Page
99eae1c711 Resolve remaining style issues 2015-08-04 23:00:10 +01:00
Luke Page
bf5ae3c54e configure the rest of eslint. 2015-08-04 21:32:22 +01:00
Luke Page
8155d300f0 linting and tidy 2015-08-04 19:27:18 +01:00
Luke Page
2398b72c8f Update README.md
Add an extra space for NPM
2015-08-04 08:04:30 +01:00
Luke Page
1183e25413 lint 2015-08-03 23:08:50 +01:00
Luke Page
53bd460f4f v0.0.3 2015-08-03 22:46:14 +01:00
Luke Page
d6b6f66bf4 npm ignore files not needed 2015-08-03 22:43:52 +01:00
Luke Page
a3620b0f02 Coveralls badge 2015-08-03 22:40:03 +01:00
Luke Page
3a3b1ef242 Code coverage 2015-08-03 22:36:36 +01:00
Luke Page
7c6f744795 AppVeyor badge 2015-08-03 17:15:03 +01:00
Luke Page
47c9d09276 appveyor take 2 2015-08-03 17:10:56 +01:00
Luke Page
ced0e4c2e0 appveyor file 2015-08-03 17:07:07 +01:00
Luke Page
132841b3d3 Update README.md 2015-08-03 16:58:03 +01:00
Luke Page
e54257c4bd Update README.md 2015-08-03 16:55:14 +01:00
Luke Page
2291c1816d Add badges 2015-08-03 16:53:46 +01:00
Luke Page
ee3dc328c5 Requires late version of node.js for sync 2015-08-03 16:46:02 +01:00
Luke Page
a5b996a9ef Travis CI 2015-08-03 16:39:07 +01:00
Luke Page
2914659815 Do not process if checksum errors 2015-08-03 16:35:05 +01:00
Luke Page
b89ef0384f tidy up tests 2015-08-03 16:18:01 +01:00
Luke Page
233925190c Automated testing using phantomjs 2015-08-03 13:25:48 +01:00
Luke Page
8933a9048d For now, set a 16 tolerance on 16bit images because of gamma issue and a tolerance of 1 on the 2.2 8bit gamma correction test. 2015-08-03 07:54:05 +01:00
Luke Page
cb346c5685 0.0.2 release 2015-08-02 23:30:43 +01:00
Luke Page
80addf080b Fix issue interlacing small files 2015-08-02 23:25:57 +01:00
Luke Page
b9293eaa2b support for transparent colour blocks without palettes 2015-08-02 23:09:08 +01:00
Luke Page
51365543ae Add automated test to test page 2015-08-02 17:19:55 +01:00
Luke Page
86a114be2f Add interlaced to meta data 2015-08-02 11:50:19 +01:00
Luke Page
d9452afc44 Update package.json 2015-08-02 11:31:28 +01:00
Luke Page
9afd1b2358 Update README.md 2015-08-02 11:30:45 +01:00
Luke Page
4def219e1e Merge remote-tracking branch 'origin/sync' 2015-08-02 11:23:12 +01:00
Luke Page
8c8f205fa4 Update package.json 2015-08-02 11:20:37 +01:00
Luke Page
f7c70a92b4 Interlace support 2015-08-02 11:06:21 +01:00
Luke Page
2d91824d0c Support for 16 bit 2015-08-01 22:53:29 +01:00
Luke Page
d601f60c79 Support for 1,2,4 bits PNG 2015-08-01 13:03:38 +01:00
Luke Page
807ffcd66f Support for synchronous reading of PNG's 2015-08-01 13:03:06 +01:00
Luke Page
b5328d3574 test tweaks and markdown 2015-07-31 06:02:51 +01:00
Luke Page
30e6e38e22 move tests, increase tests 2015-07-30 23:30:30 +01:00
Alexandre Paré
fa9693a42c Fix the falsy 0 options
if you set the options deflateStrategy to 0, it will set 3
2015-07-30 18:38:23 +01:00
Luke Page
4c508c661e Clarified png.parse() (from @stevenvanchon) 2015-07-30 18:35:45 +01:00
Luke Page
abc6f7e789 doodles on sync 2015-07-30 17:50:14 +01:00
Gaurav Mali
dbe7fbe390 Update README.md
The method signature wasn't obvious enough so I came up with an example to show how it can be used.
2015-07-30 16:32:16 +01:00
leogiese
c38a7edf70 update README 2015-01-07 12:07:15 +08:00
brighthas
51cc500a2e update README 2014-06-20 08:26:36 +08:00
243 changed files with 8268 additions and 1365 deletions

2
.eslintignore Normal file
View file

@ -0,0 +1,2 @@
/browser.js
/test/imagediff.js

19
.eslintrc.json Normal file
View file

@ -0,0 +1,19 @@
{
"parserOptions": { "ecmaVersion": 2018 },
"extends": ["eslint:recommended", "prettier"],
"env": {
"node": true,
"es6": true
},
"rules": {
"no-var": "error"
},
"overrides": [
{
"files": ["test/*.js", "examples/*.js"],
"rules": {
"no-console": "off"
}
}
]
}

5
.gitignore vendored
View file

@ -1 +1,6 @@
node_modules node_modules
.idea
coverage
examples/*.png
browser\.js
.nyc_output

5
.npmignore Normal file
View file

@ -0,0 +1,5 @@
test
examples
.travis.yml
appveyor.yml

3
.prettierignore Normal file
View file

@ -0,0 +1,3 @@
/browser.js
/test/imagediff.js
.nyc_output

9
.travis.yml Normal file
View file

@ -0,0 +1,9 @@
language: node_js
after_success:
- if [ "$TRAVIS_NODE_VERSION" = "10" ]; then yarn coverage && yarn codecov; fi
node_js:
- "10"
- "12"
branches:
only:
- master

View file

@ -1,4 +1,5 @@
Copyright (c) 2012 Kuba Niegowski pngjs2 original work Copyright (c) 2015 Luke Page & Original Contributors
pngjs derived work Copyright (c) 2012 Kuba Niegowski
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

399
README.md
View file

@ -1,150 +1,411 @@
About [![Build Status](https://travis-ci.com/lukeapage/pngjs.svg?branch=master)](https://travis-ci.com/lukeapage/pngjs) [![Build status](https://ci.appveyor.com/api/projects/status/qo5x8ayutr028108/branch/master?svg=true)](https://ci.appveyor.com/project/lukeapage/pngjs/branch/master) [![codecov](https://codecov.io/gh/lukeapage/pngjs/branch/master/graph/badge.svg)](https://codecov.io/gh/lukeapage/pngjs) [![npm version](https://badge.fury.io/js/pngjs.svg)](http://badge.fury.io/js/pngjs)
========
Simple PNG encoder/decoder for Node.js with no native dependencies. # pngjs
*This is a fork of pngjs to make it work with deno*
Simple PNG encoder/decoder for ~~Node.js~~ Deno with no dependencies.
Based on the original [pngjs](https://github.com/niegowski/node-pngjs) with the follow enhancements.
- Support for reading 1,2,4 & 16 bit files
- Support for reading interlace files
- Support for reading `tTRNS` transparent colours
- Support for writing colortype 0 (grayscale), colortype 2 (RGB), colortype 4 (grayscale alpha) and colortype 6 (RGBA)
- Sync interface as well as async
- API compatible with pngjs and node-pngjs
Known lack of support for:
- Extended PNG e.g. Animation
- Writing in colortype 3 (indexed color)
# Table of Contents
- [Requirements](#requirements)
- [Comparison Table](#comparison-table)
- [Tests](#tests)
- [Installation](#installation)
- [Browser](#browser)
- [Example](#example)
- [Async API](#async-api)
- [Sync API](#sync-api)
- [Changelog](#changelog)
# Comparison Table
| Name | Forked From | Sync | Async | 16 Bit | 1/2/4 Bit | Interlace | Gamma | Encodes | Tested |
| ------------- | ----------- | ---- | ----- | ------ | --------- | --------- | ------ | ------- | ------ |
| pngjs | | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| node-png | pngjs | No | Yes | No | No | No | Hidden | Yes | Manual |
| png-coder | pngjs | No | Yes | Yes | No | No | Hidden | Yes | Manual |
| pngparse | | No | Yes | No | Yes | No | No | No | Yes |
| pngparse-sync | pngparse | Yes | No | No | Yes | No | No | No | Yes |
| png-async | | No | Yes | No | No | No | No | Yes | Yes |
| png-js | | No | Yes | No | No | No | No | No | No |
Native C++ node decoders:
- png
- png-sync (sync version of above)
- pixel-png
- png-img
# Tests
Tested using [PNG Suite](http://www.schaik.com/pngsuite/). We read every file into pngjs, output it in standard 8bit colour, synchronously and asynchronously, then compare the original with the newly saved images.
To run the tests, fetch the repo (tests are not distributed via npm) and install with `npm i`, run `npm test`.
The only thing not converted is gamma correction - this is because multiple vendors will do gamma correction differently, so the tests will have different results on different browsers.
# Installation
Installation
===============
``` ```
$ npm install node-png $ npm install pngjs --save
``` ```
Example # Browser
==========
The package has been build with a [Browserify](browserify.org) version (`npm run browserify`) and you can use the browser version by including in your code:
```
import { PNG } from 'pngjs/browser';
```
# Example
```js ```js
var fs = require('fs'), var fs = require("fs"),
PNG = require('pngjs').PNG; PNG = require("pngjs").PNG;
fs.createReadStream('in.png') fs.createReadStream("in.png")
.pipe(new PNG({ .pipe(
filterType: 4 new PNG({
})) filterType: 4,
.on('parsed', function() { })
)
.on("parsed", function () {
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x) << 2;
for (var y = 0; y < this.height; y++) { // invert color
for (var x = 0; x < this.width; x++) { this.data[idx] = 255 - this.data[idx];
var idx = (this.width * y + x) << 2; this.data[idx + 1] = 255 - this.data[idx + 1];
this.data[idx + 2] = 255 - this.data[idx + 2];
// invert color // and reduce opacity
this.data[idx] = 255 - this.data[idx]; this.data[idx + 3] = this.data[idx + 3] >> 1;
this.data[idx+1] = 255 - this.data[idx+1]; }
this.data[idx+2] = 255 - this.data[idx+2]; }
// and reduce opacity this.pack().pipe(fs.createWriteStream("out.png"));
this.data[idx+3] = this.data[idx+3] >> 1; });
}
}
this.pack().pipe(fs.createWriteStream('out.png'));
});
``` ```
For more examples see `examples` folder. For more examples see `examples` folder.
Documentation # Async API
================
As input any color type is accepted (grayscale, rgb, palette, grayscale with alpha, rgb with alpha) but 8 bit per sample (channel) is the only supported bit depth. Interlaced mode is not supported. As input any color type is accepted (grayscale, rgb, palette, grayscale with alpha, rgb with alpha) but 8 bit per sample (channel) is the only supported bit depth. Interlaced mode is not supported.
### Supported ancillary chunks
- `gAMA` - gamma,
- `tRNS` - transparency (but only for paletted image)
## Class: PNG ## Class: PNG
`PNG` is readable and writable `Stream`. `PNG` is readable and writable `Stream`.
### Options ### Options
- `width` - use this with `height` if you want to create png from scratch - `width` - use this with `height` if you want to create png from scratch
- `height` - as above - `height` - as above
- `checkCRC` - whether parser should be strict about checksums in source stream (default: `true`) - `checkCRC` - whether parser should be strict about checksums in source stream (default: `true`)
- `deflateChunkSize` - chunk size used for deflating data chunks, this should be power of 2 and must not be less than 256 and more than 32*1024 (default: 32 kB) - `deflateChunkSize` - chunk size used for deflating data chunks, this should be power of 2 and must not be less than 256 and more than 32\*1024 (default: 32 kB)
- `deflateLevel` - compression level for delate (default: 9) - `deflateLevel` - compression level for deflate (default: 9)
- `deflateStrategy` - compression strategy for delate (default: 3) - `deflateStrategy` - compression strategy for deflate (default: 3)
- `deflateFactory` - deflate stream factory (default: `zlib.createDeflate`)
- `filterType` - png filtering method for scanlines (default: -1 => auto, accepts array of numbers 0-4) - `filterType` - png filtering method for scanlines (default: -1 => auto, accepts array of numbers 0-4)
- `colorType` - the output colorType - see constants. 0 = grayscale, no alpha, 2 = color, no alpha, 4 = grayscale & alpha, 6 = color & alpha. Default currently 6, but in the future may calculate best mode.
- `inputColorType` - the input colorType - see constants. Default is 6 (RGBA)
- `bitDepth` - the bitDepth of the output, 8 or 16 bits. Input data is expected to have this bit depth.
16 bit data is expected in the system endianness (Default: 8)
- `inputHasAlpha` - whether the input bitmap has 4 bytes per pixel (rgb and alpha) or 3 (rgb - no alpha).
- `bgColor` - an object containing red, green, and blue values between 0 and 255
that is used when packing a PNG if alpha is not to be included (default: 255,255,255)
### Event "metadata" ### Event "metadata"
`function(metadata) { }` `function(metadata) { }`
Image's header has been parsed, metadata contains this information: Image's header has been parsed, metadata contains this information:
- `width` image size in pixels - `width` image size in pixels
- `height` image size in pixels - `height` image size in pixels
- `palette` image is paletted - `palette` image is paletted
- `color` image is not grayscale - `color` image is not grayscale
- `alpha` image contains alpha channel - `alpha` image contains alpha channel
- `interlace` image is interlaced
### Event: "parsed" ### Event: "parsed"
`function(data) { }`
Input image has been completly parsed, `data` is complete and ready for modification.
`function(data) { }`
Input image has been completely parsed, `data` is complete and ready for modification.
### Event: "error" ### Event: "error"
`function(error) { }` `function(error) { }`
### png.parse(data, [callback]) ### png.parse(data, [callback])
Parses PNG file data. Alternatively you can stream data to instance of PNG.
Parses PNG file data. Can be `String` or `Buffer`. Alternatively you can stream data to instance of PNG.
Optional `callback` is once called on `error` or `parsed`. The callback gets Optional `callback` is once called on `error` or `parsed`. The callback gets
two arguments `(err, data)`. two arguments `(err, data)`.
Returns `this` for method chaining. Returns `this` for method chaining.
#### Example
```js
new PNG({ filterType: 4 }).parse(imageData, function (error, data) {
console.log(error, data);
});
```
### png.pack() ### png.pack()
Starts converting data to PNG file Stream. Starts converting data to PNG file Stream.
Returns `this` for method chaining. Returns `this` for method chaining.
### png.bitblt(dst, sx, sy, w, h, dx, dy) ### png.bitblt(dst, sx, sy, w, h, dx, dy)
Helper for image manipulation, copies rectangle of pixels from current image (`sx`, `sy`, `w`, `h`) to `dst` image (at `dx`, `dy`).
Helper for image manipulation, copies a rectangle of pixels from current (i.e. the source) image (`sx`, `sy`, `w`, `h`) to `dst` image (at `dx`, `dy`).
Returns `this` for method chaining. Returns `this` for method chaining.
For example, the following code copies the top-left 100x50 px of `in.png` into dst and writes it to `out.png`:
```js
var dst = new PNG({ width: 100, height: 50 });
fs.createReadStream("in.png")
.pipe(new PNG())
.on("parsed", function () {
this.bitblt(dst, 0, 0, 100, 50, 0, 0);
dst.pack().pipe(fs.createWriteStream("out.png"));
});
```
### Property: adjustGamma()
Helper that takes data and adjusts it to be gamma corrected. Note that it is not 100% reliable with transparent colours because that requires knowing the background colour the bitmap is rendered on to.
In tests against PNG suite it compared 100% with chrome on all 8 bit and below images. On IE there were some differences.
The following example reads a file, adjusts the gamma (which sets the gamma to 0) and writes it out again, effectively removing any gamma correction from the image.
```js
fs.createReadStream("in.png")
.pipe(new PNG())
.on("parsed", function () {
this.adjustGamma();
this.pack().pipe(fs.createWriteStream("out.png"));
});
```
### Property: width ### Property: width
Width of image in pixels Width of image in pixels
### Property: height ### Property: height
Height of image in pixels Height of image in pixels
### Property: data ### Property: data
Buffer of image pixel data. Every pixel consists 4 bytes: R, G, B, A (opacity). Buffer of image pixel data. Every pixel consists 4 bytes: R, G, B, A (opacity).
### Property: gamma ### Property: gamma
Gamma of image (0 if not specified) Gamma of image (0 if not specified)
Changelog ## Packing a PNG and removing alpha (RGBA to RGB)
============
### 0.4.0 - Jun 05 2013 When removing the alpha channel from an image, there needs to be a background color to correctly
- fixed reading of destroyed input stream convert each pixel's transparency to the appropriate RGB value. By default, pngjs will flatten
the image against a white background. You can override this in the options:
### 0.4.0-alpha - 29 Nov 2012 ```js
- added zlib deflateStrategy option, default to Z_RLE (by pdpotter) var fs = require("fs"),
- added possibility to use multiple filters (by pdpotter, modified by niegowski) PNG = require("pngjs").PNG;
### 0.3.0-alpha - 23 Aug 2012 fs.createReadStream("in.png")
- Processing data as Streams, not complete Buffers of data .pipe(
new PNG({
colorType: 2,
bgColor: {
red: 0,
green: 255,
blue: 0,
},
})
)
.on("parsed", function () {
this.pack().pipe(fs.createWriteStream("out.png"));
});
```
### 0.2.0-alpha - 21 Aug 2012 # Sync API
- Input added palette, grayscale, no alpha support
- Better scanline filter selection
### 0.1.0-alpha - 19 Aug 2012 ## PNG.sync
- First version
License ### PNG.sync.read(buffer)
=========
Take a buffer and returns a PNG image. The properties on the image include the meta data and `data` as per the async API above.
```
var data = fs.readFileSync('in.png');
var png = PNG.sync.read(data);
```
### PNG.sync.write(png)
Take a PNG image and returns a buffer. The properties on the image include the meta data and `data` as per the async API above.
```
var data = fs.readFileSync('in.png');
var png = PNG.sync.read(data);
var options = { colorType: 6 };
var buffer = PNG.sync.write(png, options);
fs.writeFileSync('out.png', buffer);
```
### PNG.adjustGamma(src)
Adjusts the gamma of a sync image. See the async adjustGamma.
```
var data = fs.readFileSync('in.png');
var png = PNG.sync.read(data);
PNG.adjustGamma(png);
```
# Changelog
### 5.0.0 - 15/04/2020
- Drop support for Node 8
- Browserified bundle may now contain ES20(15-20) code if the supported node version supports it. Please run the browserified version through babel if you need to support older browsers.
### 4.0.1 - 15/04/2020
- Fix to possible null reference in nextTick of async method
### 4.0.0 - 09/04/2020
- Fix issue in newer nodes with using Buffer
- Fix async issue with some png files
- Drop support for Node 4 & 6
### 3.4.0 - 09/03/2019
- Include whether the png has alpha in the meta data
- emit an error if the image is truncated instead of hanging
- Add a browserified version
- speed up some mapping functions
### 3.3.3 - 19/04/2018
- Real fix for node 9
### 3.3.2 - 16/02/2018
- Fix for node 9
### 3.3.1 - 15/11/2017
- Bugfixes and removal of es6
### 3.3.0
- Add writing 16 bit channels and support for grayscale input
### 3.2.0 - 30/04/2017
- Support for encoding 8-bit grayscale images
### 3.1.0 - 30/04/2017
- Support for pngs with zlib chunks that are malformed after valid data
### 3.0.1 - 16/02/2017
- Fix single pixel pngs
### 3.0.0 - 03/08/2016
- Drop support for node below v4 and iojs. Pin to 2.3.0 to use with old, unsupported or patched node versions.
### 2.3.0 - 22/04/2016
- Support for sync in node 0.10
### 2.2.0 - 04/12/2015
- Add sync write api
- Fix newfile example
- Correct comparison table
### 2.1.0 - 28/10/2015
- rename package to pngjs
- added 'bgColor' option
### 2.0.0 - 08/10/2015
- fixes to readme
- _breaking change_ - bitblt on the png prototype now doesn't take a unused, unnecessary src first argument
### 1.2.0 - 13/09/2015
- support passing colorType to write PNG's and writing bitmaps without alpha information
### 1.1.0 - 07/09/2015
- support passing a deflate factory for controlled compression
### 1.0.2 - 22/08/2015
- Expose all PNG creation info
### 1.0.1 - 21/08/2015
- Fix non square interlaced files
### 1.0.0 - 08/08/2015
- More tests
- source linted
- maintainability refactorings
- async API - exceptions in reading now emit warnings
- documentation improvement - sync api now documented, adjustGamma documented
- breaking change - gamma chunk is now written. previously a read then write would destroy gamma information, now it is persisted.
### 0.0.3 - 03/08/2015
- Error handling fixes
- ignore files for smaller npm footprint
### 0.0.2 - 02/08/2015
- Bugfixes to interlacing, support for transparent colours
### 0.0.1 - 02/08/2015
- Initial release, see pngjs for older changelog.
# License
(The MIT License) (The MIT License)
Copyright (c) 2012 Kuba Niegowski
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights

28
appveyor.yml Normal file
View file

@ -0,0 +1,28 @@
version: "{build}"
clone_depth: 10
environment:
matrix:
- nodejs_version: "10"
- nodejs_version: "12"
install:
- ps: Install-Product node $env:nodejs_version
- npm install
build: off
test_script:
- node --version && npm --version
- npm test
cache:
- node_modules # local npm modules
matrix:
fast_finish: true
branches:
only:
- master

50
examples/16bit_write.js Normal file
View file

@ -0,0 +1,50 @@
#!/usr/bin/env node
let fs = require("fs");
let PNG = require("../lib/png").PNG;
let w = 32;
let h = 64;
/// RGBA input (color type 6)
let buffer = Buffer.alloc(2 * w * h * 4);
let bitmap = new Uint16Array(buffer.buffer);
for (let i = 0; i < h; i++) {
for (let j = 0; j < w; j++) {
bitmap[i * 4 * w + 4 * j] = (i * 65535) / h;
bitmap[i * 4 * w + 4 * j + 1] = (j * 65535) / w;
bitmap[i * 4 * w + 4 * j + 2] = ((h - i) * 65535) / h;
bitmap[i * 4 * w + 4 * j + 3] = 65535;
}
}
let png = new PNG({
width: w,
height: h,
bitDepth: 16,
colorType: 6,
inputColorType: 6,
inputHasAlpha: true,
});
png.data = buffer;
png.pack().pipe(fs.createWriteStream("colortype6.png"));
//////// Grayscale 16 bits///////
buffer = Buffer.alloc(2 * w * h);
bitmap = new Uint16Array(buffer.buffer);
for (let i = 0; i < h; i++) {
for (let j = 0; j < w; j++) bitmap[i * w + j] = (i * 65535) / h;
}
png = new PNG({
width: w,
height: h,
bitDepth: 16,
colorType: 0,
inputColorType: 0,
inputHasAlpha: false,
});
png.data = buffer;
png.pack().pipe(fs.createWriteStream("colortype0.png"));

View file

@ -1,26 +1,22 @@
let fs = require("fs"),
PNG = require("../lib/png").PNG; // note require('pngjs') outside this project
var fs = require('fs'), fs.createReadStream("test/in/basi0g01.png")
PNG = require('pngjs').PNG; .pipe(new PNG({}))
.on("parsed", function () {
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
let idx = (this.width * y + x) << 2;
fs.createReadStream('in.png') // invert color
.pipe(new PNG({ this.data[idx] = 255 - this.data[idx];
filterType: 4 this.data[idx + 1] = 255 - this.data[idx + 1];
})) this.data[idx + 2] = 255 - this.data[idx + 2];
.on('parsed', function() {
for (var y = 0; y < this.height; y++) { // and reduce opacity
for (var x = 0; x < this.width; x++) { this.data[idx + 3] = this.data[idx + 3] >> 1;
var idx = (this.width * y + x) << 2; }
}
// invert color this.pack().pipe(fs.createWriteStream("out.png"));
this.data[idx] = 255 - this.data[idx]; });
this.data[idx+1] = 255 - this.data[idx+1];
this.data[idx+2] = 255 - this.data[idx+2];
// and reduce opacity
this.data[idx+3] = this.data[idx+3] >> 1;
}
}
this.pack().pipe(fs.createWriteStream('out.png'));
});

25
examples/newfile.js Normal file
View file

@ -0,0 +1,25 @@
let PNG = require("../lib/png").PNG;
let fs = require("fs");
let newfile = new PNG({ width: 10, height: 10 });
for (let y = 0; y < newfile.height; y++) {
for (let x = 0; x < newfile.width; x++) {
let idx = (newfile.width * y + x) << 2;
let col =
(x < newfile.width >> 1) ^ (y < newfile.height >> 1) ? 0xe5 : 0xff;
newfile.data[idx] = col;
newfile.data[idx + 1] = col;
newfile.data[idx + 2] = col;
newfile.data[idx + 3] = 0xff;
}
}
newfile
.pack()
.pipe(fs.createWriteStream(__dirname + "/newfile.png"))
.on("finish", function () {
console.log("Written!");
});

34
examples/set-colortype.js Normal file
View file

@ -0,0 +1,34 @@
let fs = require("fs");
let PNG = require("../lib/png").PNG;
let w = 320;
let h = 200;
let bitmapWithoutAlpha = Buffer.alloc(w * h * 3);
let ofs = 0;
for (let i = 0; i < bitmapWithoutAlpha.length; i += 3) {
bitmapWithoutAlpha[ofs++] = 0xff;
bitmapWithoutAlpha[ofs++] = i % 0xff;
bitmapWithoutAlpha[ofs++] = (i / 3) % 0xff;
}
let png = new PNG({
width: w,
height: h,
bitDepth: 8,
colorType: 2,
inputHasAlpha: false,
});
png.data = bitmapWithoutAlpha;
png.pack().pipe(fs.createWriteStream("colortype2.png"));
png = new PNG({
width: w,
height: h,
bitDepth: 8,
colorType: 6,
inputHasAlpha: false,
});
png.data = bitmapWithoutAlpha;
png.pack().pipe(fs.createWriteStream("colortype6.png"));

View file

@ -1,30 +1,28 @@
#!/usr/bin/env node #!/usr/bin/env node
var fs = require('fs'), let fs = require("fs"),
PNG = require('../lib/png').PNG; PNG = require("../lib/png").PNG;
let png = new PNG({
filterType: -1,
}),
src = fs.createReadStream(process.argv[2]),
dst = fs.createWriteStream(process.argv[3] || "out.png");
var png = new PNG({ png.on("parsed", function () {
filterType: -1 for (let y = 0; y < png.height; y++) {
}), for (let x = 0; x < png.width; x++) {
src = fs.createReadStream(process.argv[2]), let idx = (png.width * y + x) << 2;
dst = fs.createWriteStream(process.argv[3] || 'out.png');
if (
png.on('parsed', function() { Math.abs(png.data[idx] - png.data[idx + 1]) <= 1 &&
Math.abs(png.data[idx + 1] - png.data[idx + 2]) <= 1
for (var y = 0; y < png.height; y++) { )
for (var x = 0; x < png.width; x++) { png.data[idx] = png.data[idx + 1] = png.data[idx + 2];
var idx = (png.width * y + x) << 2;
if (Math.abs(png.data[idx] - png.data[idx+1]) <= 1
&& Math.abs(png.data[idx+1] - png.data[idx+2]) <= 1)
png.data[idx] = png.data[idx+1] = png.data[idx+2];
}
} }
}
png.pack().pipe(dst); png.pack().pipe(dst);
}); });
src.pipe(png); src.pipe(png);

18
examples/sync.js Normal file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env node
let fs = require("fs"),
PNG = require("../lib/png").PNG;
let srcFname = process.argv[2],
dstFname = process.argv[3] || "out.png";
// Read a PNG file
let data = fs.readFileSync(srcFname);
// Parse it
let png = PNG.sync.read(data, {
filterType: -1,
});
// Pack it back into a PNG data
let buff = PNG.sync.write(png);
// Write a PNG file
fs.writeFileSync(dstFname, buff);

View file

@ -1,27 +0,0 @@
#!/usr/bin/env node
var fs = require('fs'),
PNG = require('pngjs').PNG;
var png = new PNG({
width: 10,
height: 10,
filterType: -1
});
for (var y = 0; y < png.height; y++) {
for (var x = 0; x < png.width; x++) {
var idx = (png.width * y + x) << 2;
var col = x < (png.width >> 1) ^ y < (png.height >> 1) ? 0xe5 : 0xff;
png.data[idx ] = col;
png.data[idx+1] = col;
png.data[idx+2] = col;
png.data[idx+3] = 0xff;
}
}
png.pack().pipe(fs.createWriteStream(__dirname + '/bg.png'));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 B

View file

@ -1,38 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>PNG Test</title>
<style>
body { background: #eee; font: 12px Arial; margin: 10px; }
img { margin: 2px; background: url(bg.png); }
h3 { margin: 10px 0; }
</style>
</head>
<body>
<h3>Basic</h3>
<img src="img/basn0g08.png"> <img src="out/basn0g08.png"> grayscale<br>
<img src="img/basn2c08.png"> <img src="out/basn2c08.png"> color<br>
<img src="img/basn3p08.png"> <img src="out/basn3p08.png"> paletted<br>
<img src="img/basn4a08.png"> <img src="out/basn4a08.png"> grayscale + alpha<br>
<img src="img/basn6a08.png"> <img src="out/basn6a08.png"> color + alpha<br>
<h3>Image filtering</h3>
<img src="img/f00n0g08.png"> <img src="out/f00n0g08.png"> grayscale, filter 0<br>
<img src="img/f00n2c08.png"> <img src="out/f00n2c08.png"> color, filter 0<br>
<img src="img/f01n0g08.png"> <img src="out/f01n0g08.png"> grayscale, filter 1<br>
<img src="img/f01n2c08.png"> <img src="out/f01n2c08.png"> color, filter 1<br>
<img src="img/f02n0g08.png"> <img src="out/f02n0g08.png"> grayscale, filter 2<br>
<img src="img/f02n2c08.png"> <img src="out/f02n2c08.png"> color, filter 2<br>
<img src="img/f03n0g08.png"> <img src="out/f03n0g08.png"> grayscale, filter 3<br>
<img src="img/f03n2c08.png"> <img src="out/f03n2c08.png"> color, filter 3<br>
<img src="img/f04n0g08.png"> <img src="out/f04n0g08.png"> grayscale, filter 4<br>
<img src="img/f04n2c08.png"> <img src="out/f04n2c08.png"> color, filter 4<br>
<h3>Transparency</h3>
<img src="img/tp0n0g08.png"> <img src="out/tp0n0g08.png"> grayscale, not transparent<br>
<img src="img/tp0n2c08.png"> <img src="out/tp0n2c08.png"> color, not transparent<br>
<img src="img/tp0n3p08.png"> <img src="out/tp0n3p08.png"> paletted, not transparent<br>
<img src="img/tbrn2c08.png"> <img src="out/tbrn2c08.png"> color, transparent<br>
<img src="img/tp1n3p08.png"> <img src="out/tp1n3p08.png"> paletted, transparent<br>
</body>
</html>

View file

@ -1,38 +0,0 @@
var fs = require('fs'),
PNG = require('pngjs').PNG;
fs.readdir(__dirname + '/img/', function(err, files) {
if (err) throw err;
files.forEach(function(file) {
if (!file.match(/\.png$/i))
return;
fs.createReadStream(__dirname + '/img/' + file)
.pipe(new PNG())
.on('parsed', function() {
if (this.gamma) {
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x) << 2;
for (var i = 0; i < 3; i++) {
var sample = this.data[idx + i] / 255;
sample = Math.pow(sample, 1 / 2.2 / this.gamma);
this.data[idx + i] = Math.round(sample * 255);
}
}
}
}
this.pack()
.pipe(fs.createWriteStream(__dirname + '/out/' + file));
});
});
});

265
lib/bitmapper.js Normal file
View file

@ -0,0 +1,265 @@
import interlaceUtils from "./interlace.js";
let pixelBppMapper = [
// 0 - dummy entry
function () {},
// 1 - L
// 0: 0, 1: 0, 2: 0, 3: 0xff
function (pxData, data, pxPos, rawPos) {
if (rawPos === data.length) {
throw new Error("Ran out of data");
}
let pixel = data[rawPos];
pxData[pxPos] = pixel;
pxData[pxPos + 1] = pixel;
pxData[pxPos + 2] = pixel;
pxData[pxPos + 3] = 0xff;
},
// 2 - LA
// 0: 0, 1: 0, 2: 0, 3: 1
function (pxData, data, pxPos, rawPos) {
if (rawPos + 1 >= data.length) {
throw new Error("Ran out of data");
}
let pixel = data[rawPos];
pxData[pxPos] = pixel;
pxData[pxPos + 1] = pixel;
pxData[pxPos + 2] = pixel;
pxData[pxPos + 3] = data[rawPos + 1];
},
// 3 - RGB
// 0: 0, 1: 1, 2: 2, 3: 0xff
function (pxData, data, pxPos, rawPos) {
if (rawPos + 2 >= data.length) {
throw new Error("Ran out of data");
}
pxData[pxPos] = data[rawPos];
pxData[pxPos + 1] = data[rawPos + 1];
pxData[pxPos + 2] = data[rawPos + 2];
pxData[pxPos + 3] = 0xff;
},
// 4 - RGBA
// 0: 0, 1: 1, 2: 2, 3: 3
function (pxData, data, pxPos, rawPos) {
if (rawPos + 3 >= data.length) {
throw new Error("Ran out of data");
}
pxData[pxPos] = data[rawPos];
pxData[pxPos + 1] = data[rawPos + 1];
pxData[pxPos + 2] = data[rawPos + 2];
pxData[pxPos + 3] = data[rawPos + 3];
},
];
let pixelBppCustomMapper = [
// 0 - dummy entry
function () {},
// 1 - L
// 0: 0, 1: 0, 2: 0, 3: 0xff
function (pxData, pixelData, pxPos, maxBit) {
let pixel = pixelData[0];
pxData[pxPos] = pixel;
pxData[pxPos + 1] = pixel;
pxData[pxPos + 2] = pixel;
pxData[pxPos + 3] = maxBit;
},
// 2 - LA
// 0: 0, 1: 0, 2: 0, 3: 1
function (pxData, pixelData, pxPos) {
let pixel = pixelData[0];
pxData[pxPos] = pixel;
pxData[pxPos + 1] = pixel;
pxData[pxPos + 2] = pixel;
pxData[pxPos + 3] = pixelData[1];
},
// 3 - RGB
// 0: 0, 1: 1, 2: 2, 3: 0xff
function (pxData, pixelData, pxPos, maxBit) {
pxData[pxPos] = pixelData[0];
pxData[pxPos + 1] = pixelData[1];
pxData[pxPos + 2] = pixelData[2];
pxData[pxPos + 3] = maxBit;
},
// 4 - RGBA
// 0: 0, 1: 1, 2: 2, 3: 3
function (pxData, pixelData, pxPos) {
pxData[pxPos] = pixelData[0];
pxData[pxPos + 1] = pixelData[1];
pxData[pxPos + 2] = pixelData[2];
pxData[pxPos + 3] = pixelData[3];
},
];
function bitRetriever(data, depth) {
let leftOver = [];
let i = 0;
function split() {
if (i === data.length) {
throw new Error("Ran out of data");
}
let byte = data[i];
i++;
let byte8, byte7, byte6, byte5, byte4, byte3, byte2, byte1;
switch (depth) {
default:
throw new Error("unrecognised depth");
case 16:
byte2 = data[i];
i++;
leftOver.push((byte << 8) + byte2);
break;
case 4:
byte2 = byte & 0x0f;
byte1 = byte >> 4;
leftOver.push(byte1, byte2);
break;
case 2:
byte4 = byte & 3;
byte3 = (byte >> 2) & 3;
byte2 = (byte >> 4) & 3;
byte1 = (byte >> 6) & 3;
leftOver.push(byte1, byte2, byte3, byte4);
break;
case 1:
byte8 = byte & 1;
byte7 = (byte >> 1) & 1;
byte6 = (byte >> 2) & 1;
byte5 = (byte >> 3) & 1;
byte4 = (byte >> 4) & 1;
byte3 = (byte >> 5) & 1;
byte2 = (byte >> 6) & 1;
byte1 = (byte >> 7) & 1;
leftOver.push(byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8);
break;
}
}
return {
get: function (count) {
while (leftOver.length < count) {
split();
}
let returner = leftOver.slice(0, count);
leftOver = leftOver.slice(count);
return returner;
},
resetAfterLine: function () {
leftOver.length = 0;
},
end: function () {
if (i !== data.length) {
throw new Error("extra data found");
}
},
};
}
function mapImage8Bit(image, pxData, getPxPos, bpp, data, rawPos) {
// eslint-disable-line max-params
let imageWidth = image.width;
let imageHeight = image.height;
let imagePass = image.index;
for (let y = 0; y < imageHeight; y++) {
for (let x = 0; x < imageWidth; x++) {
let pxPos = getPxPos(x, y, imagePass);
pixelBppMapper[bpp](pxData, data, pxPos, rawPos);
rawPos += bpp; //eslint-disable-line no-param-reassign
}
}
return rawPos;
}
function mapImageCustomBit(image, pxData, getPxPos, bpp, bits, maxBit) {
// eslint-disable-line max-params
let imageWidth = image.width;
let imageHeight = image.height;
let imagePass = image.index;
for (let y = 0; y < imageHeight; y++) {
for (let x = 0; x < imageWidth; x++) {
let pixelData = bits.get(bpp);
let pxPos = getPxPos(x, y, imagePass);
pixelBppCustomMapper[bpp](pxData, pixelData, pxPos, maxBit);
}
bits.resetAfterLine();
}
}
export default function (data, bitmapInfo) {
let width = bitmapInfo.width;
let height = bitmapInfo.height;
let depth = bitmapInfo.depth;
let bpp = bitmapInfo.bpp;
let interlace = bitmapInfo.interlace;
let bits;
if (depth !== 8) {
bits = bitRetriever(data, depth);
}
let pxData;
if (depth <= 8) {
pxData = Buffer.alloc(width * height * 4);
} else {
pxData = new Uint16Array(width * height * 4);
}
let maxBit = Math.pow(2, depth) - 1;
let rawPos = 0;
let images;
let getPxPos;
if (interlace) {
images = interlaceUtils.getImagePasses(width, height);
getPxPos = interlaceUtils.getInterlaceIterator(width, height);
} else {
let nonInterlacedPxPos = 0;
getPxPos = function () {
let returner = nonInterlacedPxPos;
nonInterlacedPxPos += 4;
return returner;
};
images = [{ width: width, height: height }];
}
for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
if (depth === 8) {
rawPos = mapImage8Bit(
images[imageIndex],
pxData,
getPxPos,
bpp,
data,
rawPos
);
} else {
mapImageCustomBit(
images[imageIndex],
pxData,
getPxPos,
bpp,
bits,
maxBit
);
}
}
if (depth === 8) {
if (rawPos !== data.length) {
throw new Error("extra data found");
}
} else {
bits.end();
}
return pxData;
};

156
lib/bitpacker.js Normal file
View file

@ -0,0 +1,156 @@
import constants from "./constants.js";
export default function (dataIn, width, height, options) {
let outHasAlpha =
[constants.COLORTYPE_COLOR_ALPHA, constants.COLORTYPE_ALPHA].indexOf(
options.colorType
) !== -1;
if (options.colorType === options.inputColorType) {
let bigEndian = (function () {
let buffer = new ArrayBuffer(2);
new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
// Int16Array uses the platform's endianness.
return new Int16Array(buffer)[0] !== 256;
})();
// If no need to convert to grayscale and alpha is present/absent in both, take a fast route
if (options.bitDepth === 8 || (options.bitDepth === 16 && bigEndian)) {
return dataIn;
}
}
// map to a UInt16 array if data is 16bit, fix endianness below
let data = options.bitDepth !== 16 ? dataIn : new Uint16Array(dataIn.buffer);
let maxValue = 255;
let inBpp = constants.COLORTYPE_TO_BPP_MAP[options.inputColorType];
if (inBpp === 4 && !options.inputHasAlpha) {
inBpp = 3;
}
let outBpp = constants.COLORTYPE_TO_BPP_MAP[options.colorType];
if (options.bitDepth === 16) {
maxValue = 65535;
outBpp *= 2;
}
let outData = Buffer.alloc(width * height * outBpp);
let inIndex = 0;
let outIndex = 0;
let bgColor = options.bgColor || {};
if (bgColor.red === undefined) {
bgColor.red = maxValue;
}
if (bgColor.green === undefined) {
bgColor.green = maxValue;
}
if (bgColor.blue === undefined) {
bgColor.blue = maxValue;
}
function getRGBA() {
let red;
let green;
let blue;
let alpha = maxValue;
switch (options.inputColorType) {
case constants.COLORTYPE_COLOR_ALPHA:
alpha = data[inIndex + 3];
red = data[inIndex];
green = data[inIndex + 1];
blue = data[inIndex + 2];
break;
case constants.COLORTYPE_COLOR:
red = data[inIndex];
green = data[inIndex + 1];
blue = data[inIndex + 2];
break;
case constants.COLORTYPE_ALPHA:
alpha = data[inIndex + 1];
red = data[inIndex];
green = red;
blue = red;
break;
case constants.COLORTYPE_GRAYSCALE:
red = data[inIndex];
green = red;
blue = red;
break;
default:
throw new Error(
"input color type:" +
options.inputColorType +
" is not supported at present"
);
}
if (options.inputHasAlpha) {
if (!outHasAlpha) {
alpha /= maxValue;
red = Math.min(
Math.max(Math.round((1 - alpha) * bgColor.red + alpha * red), 0),
maxValue
);
green = Math.min(
Math.max(Math.round((1 - alpha) * bgColor.green + alpha * green), 0),
maxValue
);
blue = Math.min(
Math.max(Math.round((1 - alpha) * bgColor.blue + alpha * blue), 0),
maxValue
);
}
}
return { red: red, green: green, blue: blue, alpha: alpha };
}
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let rgba = getRGBA(data, inIndex);
switch (options.colorType) {
case constants.COLORTYPE_COLOR_ALPHA:
case constants.COLORTYPE_COLOR:
if (options.bitDepth === 8) {
outData[outIndex] = rgba.red;
outData[outIndex + 1] = rgba.green;
outData[outIndex + 2] = rgba.blue;
if (outHasAlpha) {
outData[outIndex + 3] = rgba.alpha;
}
} else {
outData.writeUInt16BE(rgba.red, outIndex);
outData.writeUInt16BE(rgba.green, outIndex + 2);
outData.writeUInt16BE(rgba.blue, outIndex + 4);
if (outHasAlpha) {
outData.writeUInt16BE(rgba.alpha, outIndex + 6);
}
}
break;
case constants.COLORTYPE_ALPHA:
case constants.COLORTYPE_GRAYSCALE: {
// Convert to grayscale and alpha
let grayscale = (rgba.red + rgba.green + rgba.blue) / 3;
if (options.bitDepth === 8) {
outData[outIndex] = grayscale;
if (outHasAlpha) {
outData[outIndex + 1] = rgba.alpha;
}
} else {
outData.writeUInt16BE(grayscale, outIndex);
if (outHasAlpha) {
outData.writeUInt16BE(rgba.alpha, outIndex + 2);
}
}
break;
}
default:
throw new Error("unrecognised color Type " + options.colorType);
}
inIndex += inBpp;
outIndex += outBpp;
}
}
return outData;
};

316
lib/chunkstream.js Executable file → Normal file
View file

@ -1,200 +1,190 @@
// Copyright (c) 2012 Kuba Niegowski let util = require("util");
// let Stream = require("stream");
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict'; let ChunkStream = function () {
Stream.call(this);
this._buffers = [];
this._buffered = 0;
var util = require('util'), this._reads = [];
Stream = require('stream'); this._paused = false;
this._encoding = "utf8";
var ChunkStream = module.exports = function() { this.writable = true;
Stream.call(this);
this._buffers = [];
this._buffered = 0;
this._reads = [];
this._paused = false;
this._encoding = 'utf8';
this.writable = true;
}; };
export default ChunkStream;
util.inherits(ChunkStream, Stream); util.inherits(ChunkStream, Stream);
ChunkStream.prototype.read = function (length, callback) {
this._reads.push({
length: Math.abs(length), // if length < 0 then at most this length
allowLess: length < 0,
func: callback,
});
ChunkStream.prototype.read = function(length, callback) { process.nextTick(
function () {
this._reads.push({
length: Math.abs(length), // if length < 0 then at most this length
allowLess: length < 0,
func: callback
});
process.nextTick(function() {
this._process(); this._process();
// its paused and there is not enought data then ask for more // its paused and there is not enought data then ask for more
if (this._paused && this._reads.length > 0) { if (this._paused && this._reads && this._reads.length > 0) {
this._paused = false; this._paused = false;
this.emit('drain'); this.emit("drain");
} }
}.bind(this)); }.bind(this)
);
}; };
ChunkStream.prototype.write = function(data, encoding) { ChunkStream.prototype.write = function (data, encoding) {
if (!this.writable) {
this.emit("error", new Error("Stream not writable"));
return false;
}
if (!this.writable) { let dataBuffer;
this.emit('error', new Error('Stream not writable')); if (Buffer.isBuffer(data)) {
return false; dataBuffer = data;
} } else {
dataBuffer = Buffer.from(data, encoding || this._encoding);
}
if (!Buffer.isBuffer(data)) this._buffers.push(dataBuffer);
data = new Buffer(data, encoding || this._encoding); this._buffered += dataBuffer.length;
this._buffers.push(data); this._process();
this._buffered += data.length;
// ok if there are no more read requests
if (this._reads && this._reads.length === 0) {
this._paused = true;
}
return this.writable && !this._paused;
};
ChunkStream.prototype.end = function (data, encoding) {
if (data) {
this.write(data, encoding);
}
this.writable = false;
// already destroyed
if (!this._buffers) {
return;
}
// enqueue or handle end
if (this._buffers.length === 0) {
this._end();
} else {
this._buffers.push(null);
this._process(); this._process();
}
// ok if there are no more read requests
if (this._reads && this._reads.length == 0)
this._paused = true;
return this.writable && !this._paused;
};
ChunkStream.prototype.end = function(data, encoding) {
if (data) this.write(data, encoding);
this.writable = false;
// already destroyed
if (!this._buffers) return;
// enqueue or handle end
if (this._buffers.length == 0) {
this._end();
} else {
this._buffers.push(null);
this._process();
}
}; };
ChunkStream.prototype.destroySoon = ChunkStream.prototype.end; ChunkStream.prototype.destroySoon = ChunkStream.prototype.end;
ChunkStream.prototype._end = function() { ChunkStream.prototype._end = function () {
if (this._reads.length > 0) {
this.emit("error", new Error("Unexpected end of input"));
}
if (this._reads.length > 0) { this.destroy();
this.emit('error', };
new Error('There are some read requests waitng on finished stream')
); ChunkStream.prototype.destroy = function () {
if (!this._buffers) {
return;
}
this.writable = false;
this._reads = null;
this._buffers = null;
this.emit("close");
};
ChunkStream.prototype._processReadAllowingLess = function (read) {
// ok there is any data so that we can satisfy this request
this._reads.shift(); // == read
// first we need to peek into first buffer
let smallerBuf = this._buffers[0];
// ok there is more data than we need
if (smallerBuf.length > read.length) {
this._buffered -= read.length;
this._buffers[0] = smallerBuf.slice(read.length);
read.func.call(this, smallerBuf.slice(0, read.length));
} else {
// ok this is less than maximum length so use it all
this._buffered -= smallerBuf.length;
this._buffers.shift(); // == smallerBuf
read.func.call(this, smallerBuf);
}
};
ChunkStream.prototype._processRead = function (read) {
this._reads.shift(); // == read
let pos = 0;
let count = 0;
let data = Buffer.alloc(read.length);
// create buffer for all data
while (pos < read.length) {
let buf = this._buffers[count++];
let len = Math.min(buf.length, read.length - pos);
buf.copy(data, pos, 0, len);
pos += len;
// last buffer wasn't used all so just slice it and leave
if (len !== buf.length) {
this._buffers[--count] = buf.slice(len);
} }
}
this.destroy(); // remove all used buffers
if (count > 0) {
this._buffers.splice(0, count);
}
this._buffered -= read.length;
read.func.call(this, data);
}; };
ChunkStream.prototype.destroy = function() { ChunkStream.prototype._process = function () {
try {
if (!this._buffers) return;
this.writable = false;
this._reads = null;
this._buffers = null;
this.emit('close');
};
ChunkStream.prototype._process = function() {
// as long as there is any data and read requests // as long as there is any data and read requests
while (this._buffered > 0 && this._reads && this._reads.length > 0) { while (this._buffered > 0 && this._reads && this._reads.length > 0) {
let read = this._reads[0];
var read = this._reads[0]; // read any data (but no more than length)
if (read.allowLess) {
this._processReadAllowingLess(read);
} else if (this._buffered >= read.length) {
// ok we can meet some expectations
// read any data (but no more than length) this._processRead(read);
if (read.allowLess) { } else {
// not enought data to satisfy first request in queue
// ok there is any data so that we can satisfy this request // so we need to wait for more
this._reads.shift(); // == read break;
}
// first we need to peek into first buffer
var buf = this._buffers[0];
// ok there is more data than we need
if (buf.length > read.length) {
this._buffered -= read.length;
this._buffers[0] = buf.slice(read.length);
read.func.call(this, buf.slice(0, read.length));
} else {
// ok this is less than maximum length so use it all
this._buffered -= buf.length;
this._buffers.shift(); // == buf
read.func.call(this, buf);
}
} else if (this._buffered >= read.length) {
// ok we can meet some expectations
this._reads.shift(); // == read
var pos = 0,
count = 0,
data = new Buffer(read.length);
// create buffer for all data
while (pos < read.length) {
var buf = this._buffers[count++],
len = Math.min(buf.length, read.length - pos);
buf.copy(data, pos, 0, len);
pos += len;
// last buffer wasn't used all so just slice it and leave
if (len != buf.length)
this._buffers[--count] = buf.slice(len);
}
// remove all used buffers
if (count > 0)
this._buffers.splice(0, count);
this._buffered -= read.length;
read.func.call(this, data);
} else {
// not enought data to satisfy first request in queue
// so we need to wait for more
break;
}
} }
if (this._buffers && this._buffers.length > 0 && this._buffers[0] == null) { if (this._buffers && !this.writable) {
this._end(); this._end();
} }
} catch (ex) {
this.emit("error", ex);
}
}; };

56
lib/constants.js Executable file → Normal file
View file

@ -1,38 +1,30 @@
// Copyright (c) 2012 Kuba Niegowski export default {
// PNG_SIGNATURE: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict'; TYPE_IHDR: 0x49484452,
TYPE_IEND: 0x49454e44,
TYPE_IDAT: 0x49444154,
TYPE_PLTE: 0x504c5445,
TYPE_tRNS: 0x74524e53, // eslint-disable-line camelcase
TYPE_gAMA: 0x67414d41, // eslint-disable-line camelcase
// color-type bits
COLORTYPE_GRAYSCALE: 0,
COLORTYPE_PALETTE: 1,
COLORTYPE_COLOR: 2,
COLORTYPE_ALPHA: 4, // e.g. grayscale and alpha
module.exports = { // color-type combinations
COLORTYPE_PALETTE_COLOR: 3,
COLORTYPE_COLOR_ALPHA: 6,
PNG_SIGNATURE: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], COLORTYPE_TO_BPP_MAP: {
0: 1,
2: 3,
3: 1,
4: 2,
6: 4,
},
TYPE_IHDR: 0x49484452, GAMMA_DIVISION: 100000,
TYPE_IEND: 0x49454e44,
TYPE_IDAT: 0x49444154,
TYPE_PLTE: 0x504c5445,
TYPE_tRNS: 0x74524e53,
TYPE_gAMA: 0x67414d41,
COLOR_PALETTE: 1,
COLOR_COLOR: 2,
COLOR_ALPHA: 4
}; };

99
lib/crc.js Executable file → Normal file
View file

@ -1,79 +1,40 @@
// Copyright (c) 2012 Kuba Niegowski let crcTable = [];
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict'; (function () {
for (let i = 0; i < 256; i++) {
var util = require('util'), let currentCrc = i;
Stream = require('stream'); for (let j = 0; j < 8; j++) {
if (currentCrc & 1) {
currentCrc = 0xedb88320 ^ (currentCrc >>> 1);
var CrcStream = module.exports = function() { } else {
Stream.call(this); currentCrc = currentCrc >>> 1;
}
this._crc = -1;
this.writable = true;
};
util.inherits(CrcStream, Stream);
CrcStream.prototype.write = function(data) {
for (var i = 0; i < data.length; i++) {
this._crc = crcTable[(this._crc ^ data[i]) & 0xff] ^ (this._crc >>> 8);
} }
return true; crcTable[i] = currentCrc;
}
})();
let CrcCalculator = function () {
this._crc = -1;
}; };
CrcStream.prototype.end = function(data) { export default CrcCalculator;
if (data) this.write(data);
this.emit('crc', this.crc32()); CrcCalculator.prototype.write = function (data) {
for (let i = 0; i < data.length; i++) {
this._crc = crcTable[(this._crc ^ data[i]) & 0xff] ^ (this._crc >>> 8);
}
return true;
}; };
CrcStream.prototype.crc32 = function() { CrcCalculator.prototype.crc32 = function () {
return this._crc ^ -1; return this._crc ^ -1;
}; };
CrcCalculator.crc32 = function (buf) {
CrcStream.crc32 = function(buf) { let crc = -1;
for (let i = 0; i < buf.length; i++) {
var crc = -1; crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
for (var i = 0; i < buf.length; i++) { }
crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8); return crc ^ -1;
}
return crc ^ -1;
}; };
var crcTable = [];
for (var i = 0; i < 256; i++) {
var c = i;
for (var j = 0; j < 8; j++) {
if (c & 1) {
c = 0xedb88320 ^ (c >>> 1);
} else {
c = c >>> 1;
}
}
crcTable[i] = c;
}

169
lib/filter-pack.js Normal file
View file

@ -0,0 +1,169 @@
import paethPredictor from "./paeth-predictor.js";
function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) {
for (let x = 0; x < byteWidth; x++) {
rawData[rawPos + x] = pxData[pxPos + x];
}
}
function filterSumNone(pxData, pxPos, byteWidth) {
let sum = 0;
let length = pxPos + byteWidth;
for (let i = pxPos; i < length; i++) {
sum += Math.abs(pxData[i]);
}
return sum;
}
function filterSub(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
for (let x = 0; x < byteWidth; x++) {
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
let val = pxData[pxPos + x] - left;
rawData[rawPos + x] = val;
}
}
function filterSumSub(pxData, pxPos, byteWidth, bpp) {
let sum = 0;
for (let x = 0; x < byteWidth; x++) {
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
let val = pxData[pxPos + x] - left;
sum += Math.abs(val);
}
return sum;
}
function filterUp(pxData, pxPos, byteWidth, rawData, rawPos) {
for (let x = 0; x < byteWidth; x++) {
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
let val = pxData[pxPos + x] - up;
rawData[rawPos + x] = val;
}
}
function filterSumUp(pxData, pxPos, byteWidth) {
let sum = 0;
let length = pxPos + byteWidth;
for (let x = pxPos; x < length; x++) {
let up = pxPos > 0 ? pxData[x - byteWidth] : 0;
let val = pxData[x] - up;
sum += Math.abs(val);
}
return sum;
}
function filterAvg(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
for (let x = 0; x < byteWidth; x++) {
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
let val = pxData[pxPos + x] - ((left + up) >> 1);
rawData[rawPos + x] = val;
}
}
function filterSumAvg(pxData, pxPos, byteWidth, bpp) {
let sum = 0;
for (let x = 0; x < byteWidth; x++) {
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
let val = pxData[pxPos + x] - ((left + up) >> 1);
sum += Math.abs(val);
}
return sum;
}
function filterPaeth(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
for (let x = 0; x < byteWidth; x++) {
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
let upleft =
pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
let val = pxData[pxPos + x] - paethPredictor(left, up, upleft);
rawData[rawPos + x] = val;
}
}
function filterSumPaeth(pxData, pxPos, byteWidth, bpp) {
let sum = 0;
for (let x = 0; x < byteWidth; x++) {
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
let upleft =
pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
let val = pxData[pxPos + x] - paethPredictor(left, up, upleft);
sum += Math.abs(val);
}
return sum;
}
let filters = {
0: filterNone,
1: filterSub,
2: filterUp,
3: filterAvg,
4: filterPaeth,
};
let filterSums = {
0: filterSumNone,
1: filterSumSub,
2: filterSumUp,
3: filterSumAvg,
4: filterSumPaeth,
};
export default function (pxData, width, height, options, bpp) {
let filterTypes;
if (!("filterType" in options) || options.filterType === -1) {
filterTypes = [0, 1, 2, 3, 4];
} else if (typeof options.filterType === "number") {
filterTypes = [options.filterType];
} else {
throw new Error("unrecognised filter types");
}
if (options.bitDepth === 16) {
bpp *= 2;
}
let byteWidth = width * bpp;
let rawPos = 0;
let pxPos = 0;
let rawData = Buffer.alloc((byteWidth + 1) * height);
let sel = filterTypes[0];
for (let y = 0; y < height; y++) {
if (filterTypes.length > 1) {
// find best filter for this line (with lowest sum of values)
let min = Infinity;
for (let i = 0; i < filterTypes.length; i++) {
let sum = filterSums[filterTypes[i]](pxData, pxPos, byteWidth, bpp);
if (sum < min) {
sel = filterTypes[i];
min = sum;
}
}
}
rawData[rawPos] = sel;
rawPos++;
filters[sel](pxData, pxPos, byteWidth, rawData, rawPos, bpp);
rawPos += byteWidth;
pxPos += byteWidth;
}
return rawData;
};

24
lib/filter-parse-async.js Normal file
View file

@ -0,0 +1,24 @@
import ChunkStream from "./chunkstream.js";
import Filter from "./filter-parse.js";
let util = require("util");
let FilterAsync = function (bitmapInfo) {
ChunkStream.call(this);
let buffers = [];
let that = this;
this._filter = new Filter(bitmapInfo, {
read: this.read.bind(this),
write: function (buffer) {
buffers.push(buffer);
},
complete: function () {
that.emit("complete", Buffer.concat(buffers));
},
});
this._filter.start();
};
util.inherits(FilterAsync, ChunkStream);
export default FilterAsync;

19
lib/filter-parse-sync.js Normal file
View file

@ -0,0 +1,19 @@
import SyncReader from "./sync-reader.js";
import Filter from "./filter-parse.js";
export default function (inBuffer, bitmapInfo) {
let outBuffers = [];
let reader = new SyncReader(inBuffer);
let filter = new Filter(bitmapInfo, {
read: reader.read.bind(reader),
write: function (bufferPart) {
outBuffers.push(bufferPart);
},
complete: function () {},
});
filter.start();
reader.process();
return Buffer.concat(outBuffers);
};

177
lib/filter-parse.js Normal file
View file

@ -0,0 +1,177 @@
import interlaceUtils from "./interlace.js";
import paethPredictor from "./paeth-predictor.js";
function getByteWidth(width, bpp, depth) {
let byteWidth = width * bpp;
if (depth !== 8) {
byteWidth = Math.ceil(byteWidth / (8 / depth));
}
return byteWidth;
}
let Filter = function (bitmapInfo, dependencies) {
let width = bitmapInfo.width;
let height = bitmapInfo.height;
let interlace = bitmapInfo.interlace;
let bpp = bitmapInfo.bpp;
let depth = bitmapInfo.depth;
this.read = dependencies.read;
this.write = dependencies.write;
this.complete = dependencies.complete;
this._imageIndex = 0;
this._images = [];
if (interlace) {
let passes = interlaceUtils.getImagePasses(width, height);
for (let i = 0; i < passes.length; i++) {
this._images.push({
byteWidth: getByteWidth(passes[i].width, bpp, depth),
height: passes[i].height,
lineIndex: 0,
});
}
} else {
this._images.push({
byteWidth: getByteWidth(width, bpp, depth),
height: height,
lineIndex: 0,
});
}
// when filtering the line we look at the pixel to the left
// the spec also says it is done on a byte level regardless of the number of pixels
// so if the depth is byte compatible (8 or 16) we subtract the bpp in order to compare back
// a pixel rather than just a different byte part. However if we are sub byte, we ignore.
if (depth === 8) {
this._xComparison = bpp;
} else if (depth === 16) {
this._xComparison = bpp * 2;
} else {
this._xComparison = 1;
}
};
export default Filter;
Filter.prototype.start = function () {
this.read(
this._images[this._imageIndex].byteWidth + 1,
this._reverseFilterLine.bind(this)
);
};
Filter.prototype._unFilterType1 = function (
rawData,
unfilteredLine,
byteWidth
) {
let xComparison = this._xComparison;
let xBiggerThan = xComparison - 1;
for (let x = 0; x < byteWidth; x++) {
let rawByte = rawData[1 + x];
let f1Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
unfilteredLine[x] = rawByte + f1Left;
}
};
Filter.prototype._unFilterType2 = function (
rawData,
unfilteredLine,
byteWidth
) {
let lastLine = this._lastLine;
for (let x = 0; x < byteWidth; x++) {
let rawByte = rawData[1 + x];
let f2Up = lastLine ? lastLine[x] : 0;
unfilteredLine[x] = rawByte + f2Up;
}
};
Filter.prototype._unFilterType3 = function (
rawData,
unfilteredLine,
byteWidth
) {
let xComparison = this._xComparison;
let xBiggerThan = xComparison - 1;
let lastLine = this._lastLine;
for (let x = 0; x < byteWidth; x++) {
let rawByte = rawData[1 + x];
let f3Up = lastLine ? lastLine[x] : 0;
let f3Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
let f3Add = Math.floor((f3Left + f3Up) / 2);
unfilteredLine[x] = rawByte + f3Add;
}
};
Filter.prototype._unFilterType4 = function (
rawData,
unfilteredLine,
byteWidth
) {
let xComparison = this._xComparison;
let xBiggerThan = xComparison - 1;
let lastLine = this._lastLine;
for (let x = 0; x < byteWidth; x++) {
let rawByte = rawData[1 + x];
let f4Up = lastLine ? lastLine[x] : 0;
let f4Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
let f4UpLeft = x > xBiggerThan && lastLine ? lastLine[x - xComparison] : 0;
let f4Add = paethPredictor(f4Left, f4Up, f4UpLeft);
unfilteredLine[x] = rawByte + f4Add;
}
};
Filter.prototype._reverseFilterLine = function (rawData) {
let filter = rawData[0];
let unfilteredLine;
let currentImage = this._images[this._imageIndex];
let byteWidth = currentImage.byteWidth;
if (filter === 0) {
unfilteredLine = rawData.slice(1, byteWidth + 1);
} else {
unfilteredLine = Buffer.alloc(byteWidth);
switch (filter) {
case 1:
this._unFilterType1(rawData, unfilteredLine, byteWidth);
break;
case 2:
this._unFilterType2(rawData, unfilteredLine, byteWidth);
break;
case 3:
this._unFilterType3(rawData, unfilteredLine, byteWidth);
break;
case 4:
this._unFilterType4(rawData, unfilteredLine, byteWidth);
break;
default:
throw new Error("Unrecognised filter type - " + filter);
}
}
this.write(unfilteredLine);
currentImage.lineIndex++;
if (currentImage.lineIndex >= currentImage.height) {
this._lastLine = null;
this._imageIndex++;
currentImage = this._images[this._imageIndex];
} else {
this._lastLine = unfilteredLine;
}
if (currentImage) {
// read, using the byte width that may be from the new current image
this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this));
} else {
this._lastLine = null;
this.complete();
}
};

View file

@ -1,314 +0,0 @@
// Copyright (c) 2012 Kuba Niegowski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict';
var util = require('util'),
zlib = require('zlib'),
ChunkStream = require('./chunkstream');
var Filter = module.exports = function(width, height, Bpp, data, options) {
ChunkStream.call(this);
this._width = width;
this._height = height;
this._Bpp = Bpp;
this._data = data;
this._options = options;
this._line = 0;
if (!('filterType' in options) || options.filterType == -1) {
options.filterType = [0, 1, 2, 3, 4];
} else if (typeof options.filterType == 'number') {
options.filterType = [options.filterType];
}
this._filters = {
0: this._filterNone.bind(this),
1: this._filterSub.bind(this),
2: this._filterUp.bind(this),
3: this._filterAvg.bind(this),
4: this._filterPaeth.bind(this)
};
this.read(this._width * Bpp + 1, this._reverseFilterLine.bind(this));
};
util.inherits(Filter, ChunkStream);
var pixelBppMap = {
1: { // L
0: 0,
1: 0,
2: 0,
3: 0xff
},
2: { // LA
0: 0,
1: 0,
2: 0,
3: 1
},
3: { // RGB
0: 0,
1: 1,
2: 2,
3: 0xff
},
4: { // RGBA
0: 0,
1: 1,
2: 2,
3: 3
}
};
Filter.prototype._reverseFilterLine = function(rawData) {
var pxData = this._data,
pxLineLength = this._width << 2,
pxRowPos = this._line * pxLineLength,
filter = rawData[0];
if (filter == 0) {
for (var x = 0; x < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i];
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] : 0xff;
}
}
} else if (filter == 1) {
for (var x = 0; x < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i],
left = x > 0 ? pxData[pxPos + i - 4] : 0;
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] + left : 0xff;
}
}
} else if (filter == 2) {
for (var x = 0; x < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i],
up = this._line > 0 ? pxData[pxPos - pxLineLength + i] : 0;
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] + up : 0xff;
}
}
} else if (filter == 3) {
for (var x = 0; x < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i],
left = x > 0 ? pxData[pxPos + i - 4] : 0,
up = this._line > 0 ? pxData[pxPos - pxLineLength + i] : 0,
add = Math.floor((left + up) / 2);
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] + add : 0xff;
}
}
} else if (filter == 4) {
for (var x = 0; x < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i],
left = x > 0 ? pxData[pxPos + i - 4] : 0,
up = this._line > 0 ? pxData[pxPos - pxLineLength + i] : 0,
upLeft = x > 0 && this._line > 0
? pxData[pxPos - pxLineLength + i - 4] : 0,
add = PaethPredictor(left, up, upLeft);
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] + add : 0xff;
}
}
}
this._line++;
if (this._line < this._height)
this.read(this._width * this._Bpp + 1, this._reverseFilterLine.bind(this));
else
this.emit('complete', this._data, this._width, this._height);
};
Filter.prototype.filter = function() {
var pxData = this._data,
rawData = new Buffer(((this._width << 2) + 1) * this._height);
for (var y = 0; y < this._height; y++) {
// find best filter for this line (with lowest sum of values)
var filterTypes = this._options.filterType,
min = Infinity,
sel = 0;
for (var i = 0; i < filterTypes.length; i++) {
var sum = this._filters[filterTypes[i]](pxData, y, null);
if (sum < min) {
sel = filterTypes[i];
min = sum;
}
}
this._filters[sel](pxData, y, rawData);
}
return rawData;
};
Filter.prototype._filterNone = function(pxData, y, rawData) {
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,
sum = 0;
if (!rawData) {
for (var x = 0; x < pxRowLength; x++)
sum += Math.abs(pxData[y * pxRowLength + x]);
} else {
rawData[y * rawRowLength] = 0;
pxData.copy(rawData, rawRowLength * y + 1, pxRowLength * y, pxRowLength * (y + 1));
}
return sum;
};
Filter.prototype._filterSub = function(pxData, y, rawData) {
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,
sum = 0;
if (rawData)
rawData[y * rawRowLength] = 1;
for (var x = 0; x < pxRowLength; x++) {
var left = x >= 4 ? pxData[y * pxRowLength + x - 4] : 0,
val = pxData[y * pxRowLength + x] - left;
if (!rawData) sum += Math.abs(val);
else rawData[y * rawRowLength + 1 + x] = val;
}
return sum;
};
Filter.prototype._filterUp = function(pxData, y, rawData) {
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,
sum = 0;
if (rawData)
rawData[y * rawRowLength] = 2;
for (var x = 0; x < pxRowLength; x++) {
var up = y > 0 ? pxData[(y - 1) * pxRowLength + x] : 0,
val = pxData[y * pxRowLength + x] - up;
if (!rawData) sum += Math.abs(val);
else rawData[y * rawRowLength + 1 + x] = val;
}
return sum;
};
Filter.prototype._filterAvg = function(pxData, y, rawData) {
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,
sum = 0;
if (rawData)
rawData[y * rawRowLength] = 3;
for (var x = 0; x < pxRowLength; x++) {
var left = x >= 4 ? pxData[y * pxRowLength + x - 4] : 0,
up = y > 0 ? pxData[(y - 1) * pxRowLength + x] : 0,
val = pxData[y * pxRowLength + x] - ((left + up) >> 1);
if (!rawData) sum += Math.abs(val);
else rawData[y * rawRowLength + 1 + x] = val;
}
return sum;
};
Filter.prototype._filterPaeth = function(pxData, y, rawData) {
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,
sum = 0;
if (rawData)
rawData[y * rawRowLength] = 4;
for (var x = 0; x < pxRowLength; x++) {
var left = x >= 4 ? pxData[y * pxRowLength + x - 4] : 0,
up = y > 0 ? pxData[(y - 1) * pxRowLength + x] : 0,
upLeft = x >= 4 && y > 0 ? pxData[(y - 1) * pxRowLength + x - 4] : 0,
val = pxData[y * pxRowLength + x] - PaethPredictor(left, up, upLeft);
if (!rawData) sum += Math.abs(val);
else rawData[y * rawRowLength + 1 + x] = val;
}
return sum;
};
var PaethPredictor = function(left, above, upLeft) {
var p = left + above - upLeft,
pLeft = Math.abs(p - left),
pAbove = Math.abs(p - above),
pUpLeft = Math.abs(p - upLeft);
if (pLeft <= pAbove && pLeft <= pUpLeft) return left;
else if (pAbove <= pUpLeft) return above;
else return upLeft;
};

91
lib/format-normaliser.js Normal file
View file

@ -0,0 +1,91 @@
function dePalette(indata, outdata, width, height, palette) {
let pxPos = 0;
// use values from palette
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let color = palette[indata[pxPos]];
if (!color) {
throw new Error("index " + indata[pxPos] + " not in palette");
}
for (let i = 0; i < 4; i++) {
outdata[pxPos + i] = color[i];
}
pxPos += 4;
}
}
}
function replaceTransparentColor(indata, outdata, width, height, transColor) {
let pxPos = 0;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let makeTrans = false;
if (transColor.length === 1) {
if (transColor[0] === indata[pxPos]) {
makeTrans = true;
}
} else if (
transColor[0] === indata[pxPos] &&
transColor[1] === indata[pxPos + 1] &&
transColor[2] === indata[pxPos + 2]
) {
makeTrans = true;
}
if (makeTrans) {
for (let i = 0; i < 4; i++) {
outdata[pxPos + i] = 0;
}
}
pxPos += 4;
}
}
}
function scaleDepth(indata, outdata, width, height, depth) {
let maxOutSample = 255;
let maxInSample = Math.pow(2, depth) - 1;
let pxPos = 0;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
for (let i = 0; i < 4; i++) {
outdata[pxPos + i] = Math.floor(
(indata[pxPos + i] * maxOutSample) / maxInSample + 0.5
);
}
pxPos += 4;
}
}
}
export default function (indata, imageData) {
let depth = imageData.depth;
let width = imageData.width;
let height = imageData.height;
let colorType = imageData.colorType;
let transColor = imageData.transColor;
let palette = imageData.palette;
let outdata = indata; // only different for 16 bits
if (colorType === 3) {
// paletted
dePalette(indata, outdata, width, height, palette);
} else {
if (transColor) {
replaceTransparentColor(indata, outdata, width, height, transColor);
}
// if it needs scaling
if (depth !== 8) {
// if we need to change the buffer size
if (depth === 16) {
outdata = Buffer.alloc(width * height * 4);
}
scaleDepth(indata, outdata, width, height, depth);
}
}
return outdata;
};

98
lib/interlace.js Normal file
View file

@ -0,0 +1,98 @@
// Adam 7
// 0 1 2 3 4 5 6 7
// 0 x 6 4 6 x 6 4 6
// 1 7 7 7 7 7 7 7 7
// 2 5 6 5 6 5 6 5 6
// 3 7 7 7 7 7 7 7 7
// 4 3 6 4 6 3 6 4 6
// 5 7 7 7 7 7 7 7 7
// 6 5 6 5 6 5 6 5 6
// 7 7 7 7 7 7 7 7 7
let imagePasses = [
{
// pass 1 - 1px
x: [0],
y: [0],
},
{
// pass 2 - 1px
x: [4],
y: [0],
},
{
// pass 3 - 2px
x: [0, 4],
y: [4],
},
{
// pass 4 - 4px
x: [2, 6],
y: [0, 4],
},
{
// pass 5 - 8px
x: [0, 2, 4, 6],
y: [2, 6],
},
{
// pass 6 - 16px
x: [1, 3, 5, 7],
y: [0, 2, 4, 6],
},
{
// pass 7 - 32px
x: [0, 1, 2, 3, 4, 5, 6, 7],
y: [1, 3, 5, 7],
},
];
export var getImagePasses = function (width, height) {
let images = [];
let xLeftOver = width % 8;
let yLeftOver = height % 8;
let xRepeats = (width - xLeftOver) / 8;
let yRepeats = (height - yLeftOver) / 8;
for (let i = 0; i < imagePasses.length; i++) {
let pass = imagePasses[i];
let passWidth = xRepeats * pass.x.length;
let passHeight = yRepeats * pass.y.length;
for (let j = 0; j < pass.x.length; j++) {
if (pass.x[j] < xLeftOver) {
passWidth++;
} else {
break;
}
}
for (let j = 0; j < pass.y.length; j++) {
if (pass.y[j] < yLeftOver) {
passHeight++;
} else {
break;
}
}
if (passWidth > 0 && passHeight > 0) {
images.push({ width: passWidth, height: passHeight, index: i });
}
}
return images;
};
export var getInterlaceIterator = function (width) {
return function (x, y, pass) {
let outerXLeftOver = x % imagePasses[pass].x.length;
let outerX =
((x - outerXLeftOver) / imagePasses[pass].x.length) * 8 +
imagePasses[pass].x[outerXLeftOver];
let outerYLeftOver = y % imagePasses[pass].y.length;
let outerY =
((y - outerYLeftOver) / imagePasses[pass].y.length) * 8 +
imagePasses[pass].y[outerYLeftOver];
return outerX * 4 + outerY * width * 4;
};
};
export default {
getImagePasses,
getInterlaceIterator
}

51
lib/packer-async.js Normal file
View file

@ -0,0 +1,51 @@
let util = require("util");
let Stream = require("stream");
import constants from "./constants.js";
import Packer from "./packer.js";
let PackerAsync = function (opt) {
Stream.call(this);
let options = opt || {};
this._packer = new Packer(options);
this._deflate = this._packer.createDeflate();
this.readable = true;
};
util.inherits(PackerAsync, Stream);
export default PackerAsync;
PackerAsync.prototype.pack = function (data, width, height, gamma) {
// Signature
this.emit("data", Buffer.from(constants.PNG_SIGNATURE));
this.emit("data", this._packer.packIHDR(width, height));
if (gamma) {
this.emit("data", this._packer.packGAMA(gamma));
}
let filteredData = this._packer.filterData(data, width, height);
// compress it
this._deflate.on("error", this.emit.bind(this, "error"));
this._deflate.on(
"data",
function (compressedData) {
this.emit("data", this._packer.packIDAT(compressedData));
}.bind(this)
);
this._deflate.on(
"end",
function () {
this.emit("data", this._packer.packIEND());
this.emit("end");
}.bind(this)
);
this._deflate.end(filteredData);
};

52
lib/packer-sync.js Normal file
View file

@ -0,0 +1,52 @@
import constants from "./constants.js";
import Packer from "./packer.js";
let hasSyncZlib = true;
let zlib = require("zlib");
export default function (metaData, opt) {
if (!hasSyncZlib) {
throw new Error(
"To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
);
}
let options = opt || {};
let packer = new Packer(options);
let chunks = [];
// Signature
chunks.push(Buffer.from(constants.PNG_SIGNATURE));
// Header
chunks.push(packer.packIHDR(metaData.width, metaData.height));
if (metaData.gamma) {
chunks.push(packer.packGAMA(metaData.gamma));
}
let filteredData = packer.filterData(
metaData.data,
metaData.width,
metaData.height
);
// compress it
let compressedData = zlib.deflateSync(
filteredData,
packer.getDeflateOptions()
);
filteredData = null;
if (!compressedData || !compressedData.length) {
throw new Error("bad png - invalid compressed data response");
}
chunks.push(packer.packIDAT(compressedData));
// End
chunks.push(packer.packIEND());
return Buffer.concat(chunks);
};

212
lib/packer.js Executable file → Normal file
View file

@ -1,110 +1,130 @@
// Copyright (c) 2012 Kuba Niegowski import constants from "./constants.js";
// import CrcStream from "./crc.js";
// Permission is hereby granted, free of charge, to any person obtaining a copy import bitPacker from "./bitpacker.js";
// of this software and associated documentation files (the "Software"), to deal import filter from "./filter-pack.js";
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict'; let zlib = require("zlib");
let Packer = function (options) {
this._options = options;
var util = require('util'), options.deflateChunkSize = options.deflateChunkSize || 32 * 1024;
Stream = require('stream'), options.deflateLevel =
zlib = require('zlib'), options.deflateLevel != null ? options.deflateLevel : 9;
Filter = require('./filter'), options.deflateStrategy =
CrcStream = require('./crc'), options.deflateStrategy != null ? options.deflateStrategy : 3;
constants = require('./constants'); options.inputHasAlpha =
options.inputHasAlpha != null ? options.inputHasAlpha : true;
options.deflateFactory = options.deflateFactory || zlib.createDeflate;
options.bitDepth = options.bitDepth || 8;
// This is outputColorType
options.colorType =
typeof options.colorType === "number"
? options.colorType
: constants.COLORTYPE_COLOR_ALPHA;
options.inputColorType =
typeof options.inputColorType === "number"
? options.inputColorType
: constants.COLORTYPE_COLOR_ALPHA;
if (
var Packer = module.exports = function(options) { [
Stream.call(this); constants.COLORTYPE_GRAYSCALE,
constants.COLORTYPE_COLOR,
this._options = options; constants.COLORTYPE_COLOR_ALPHA,
constants.COLORTYPE_ALPHA,
options.deflateChunkSize = options.deflateChunkSize || 32 * 1024; ].indexOf(options.colorType) === -1
options.deflateLevel = options.deflateLevel || 9; ) {
options.deflateStrategy = options.deflateStrategy || 3; throw new Error(
"option color type:" + options.colorType + " is not supported at present"
this.readable = true; );
}; }
util.inherits(Packer, Stream); if (
[
constants.COLORTYPE_GRAYSCALE,
Packer.prototype.pack = function(data, width, height) { constants.COLORTYPE_COLOR,
constants.COLORTYPE_COLOR_ALPHA,
// Signature constants.COLORTYPE_ALPHA,
this.emit('data', new Buffer(constants.PNG_SIGNATURE)); ].indexOf(options.inputColorType) === -1
this.emit('data', this._packIHDR(width, height)); ) {
throw new Error(
// filter pixel data "option input color type:" +
var filter = new Filter(width, height, 4, data, this._options); options.inputColorType +
var data = filter.filter(); " is not supported at present"
);
// compress it }
var deflate = zlib.createDeflate({ if (options.bitDepth !== 8 && options.bitDepth !== 16) {
chunkSize: this._options.deflateChunkSize, throw new Error(
level: this._options.deflateLevel, "option bit depth:" + options.bitDepth + " is not supported at present"
strategy: this._options.deflateStrategy );
}); }
deflate.on('error', this.emit.bind(this, 'error'));
deflate.on('data', function(data) {
this.emit('data', this._packIDAT(data));
}.bind(this));
deflate.on('end', function() {
this.emit('data', this._packIEND());
this.emit('end');
}.bind(this));
deflate.end(data);
}; };
Packer.prototype._packChunk = function(type, data) { export default Packer;
var len = (data ? data.length : 0), Packer.prototype.getDeflateOptions = function () {
buf = new Buffer(len + 12); return {
chunkSize: this._options.deflateChunkSize,
buf.writeUInt32BE(len, 0); level: this._options.deflateLevel,
buf.writeUInt32BE(type, 4); strategy: this._options.deflateStrategy,
};
if (data) data.copy(buf, 8);
buf.writeInt32BE(CrcStream.crc32(buf.slice(4, buf.length - 4)), buf.length - 4);
return buf;
}; };
Packer.prototype._packIHDR = function(width, height) { Packer.prototype.createDeflate = function () {
return this._options.deflateFactory(this.getDeflateOptions());
var buf = new Buffer(13);
buf.writeUInt32BE(width, 0);
buf.writeUInt32BE(height, 4);
buf[8] = 8;
buf[9] = 6; // colorType
buf[10] = 0; // compression
buf[11] = 0; // filter
buf[12] = 0; // interlace
return this._packChunk(constants.TYPE_IHDR, buf);
}; };
Packer.prototype._packIDAT = function(data) { Packer.prototype.filterData = function (data, width, height) {
return this._packChunk(constants.TYPE_IDAT, data); // convert to correct format for filtering (e.g. right bpp and bit depth)
let packedData = bitPacker(data, width, height, this._options);
// filter pixel data
let bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType];
let filteredData = filter(packedData, width, height, this._options, bpp);
return filteredData;
}; };
Packer.prototype._packIEND = function() { Packer.prototype._packChunk = function (type, data) {
return this._packChunk(constants.TYPE_IEND, null); let len = data ? data.length : 0;
let buf = Buffer.alloc(len + 12);
buf.writeUInt32BE(len, 0);
buf.writeUInt32BE(type, 4);
if (data) {
data.copy(buf, 8);
}
buf.writeInt32BE(
CrcStream.crc32(buf.slice(4, buf.length - 4)),
buf.length - 4
);
return buf;
};
Packer.prototype.packGAMA = function (gamma) {
let buf = Buffer.alloc(4);
buf.writeUInt32BE(Math.floor(gamma * constants.GAMMA_DIVISION), 0);
return this._packChunk(constants.TYPE_gAMA, buf);
};
Packer.prototype.packIHDR = function (width, height) {
let buf = Buffer.alloc(13);
buf.writeUInt32BE(width, 0);
buf.writeUInt32BE(height, 4);
buf[8] = this._options.bitDepth; // Bit depth
buf[9] = this._options.colorType; // colorType
buf[10] = 0; // compression
buf[11] = 0; // filter
buf[12] = 0; // interlace
return this._packChunk(constants.TYPE_IHDR, buf);
};
Packer.prototype.packIDAT = function (data) {
return this._packChunk(constants.TYPE_IDAT, data);
};
Packer.prototype.packIEND = function () {
return this._packChunk(constants.TYPE_IEND, null);
}; };

14
lib/paeth-predictor.js Normal file
View file

@ -0,0 +1,14 @@
export default function paethPredictor(left, above, upLeft) {
let paeth = left + above - upLeft;
let pLeft = Math.abs(paeth - left);
let pAbove = Math.abs(paeth - above);
let pUpLeft = Math.abs(paeth - upLeft);
if (pLeft <= pAbove && pLeft <= pUpLeft) {
return left;
}
if (pAbove <= pUpLeft) {
return above;
}
return upLeft;
};

166
lib/parser-async.js Normal file
View file

@ -0,0 +1,166 @@
import ChunkStream from "./chunkstream.js";
import FilterAsync from "./filter-parse-async.js";
import Parser from "./parser.js";
import bitmapper from "./bitmapper.js";
import formatNormaliser from "./format-normaliser.js";
let util = require("util");
let zlib = require("zlib");
let ParserAsync = function (options) {
ChunkStream.call(this);
this._parser = new Parser(options, {
read: this.read.bind(this),
error: this._handleError.bind(this),
metadata: this._handleMetaData.bind(this),
gamma: this.emit.bind(this, "gamma"),
palette: this._handlePalette.bind(this),
transColor: this._handleTransColor.bind(this),
finished: this._finished.bind(this),
inflateData: this._inflateData.bind(this),
simpleTransparency: this._simpleTransparency.bind(this),
headersFinished: this._headersFinished.bind(this),
});
this._options = options;
this.writable = true;
this._parser.start();
};
util.inherits(ParserAsync, ChunkStream);
export default ParserAsync;
ParserAsync.prototype._handleError = function (err) {
this.emit("error", err);
this.writable = false;
this.destroy();
if (this._inflate && this._inflate.destroy) {
this._inflate.destroy();
}
if (this._filter) {
this._filter.destroy();
// For backward compatibility with Node 7 and below.
// Suppress errors due to _inflate calling write() even after
// it's destroy()'ed.
this._filter.on("error", function () {});
}
this.errord = true;
};
ParserAsync.prototype._inflateData = function (data) {
if (!this._inflate) {
if (this._bitmapInfo.interlace) {
this._inflate = zlib.createInflate();
this._inflate.on("error", this.emit.bind(this, "error"));
this._filter.on("complete", this._complete.bind(this));
this._inflate.pipe(this._filter);
} else {
let rowSize =
((this._bitmapInfo.width *
this._bitmapInfo.bpp *
this._bitmapInfo.depth +
7) >>
3) +
1;
let imageSize = rowSize * this._bitmapInfo.height;
let chunkSize = Math.max(imageSize, zlib.Z_MIN_CHUNK);
this._inflate = zlib.createInflate({ chunkSize: chunkSize });
let leftToInflate = imageSize;
let emitError = this.emit.bind(this, "error");
this._inflate.on("error", function (err) {
if (!leftToInflate) {
return;
}
emitError(err);
});
this._filter.on("complete", this._complete.bind(this));
let filterWrite = this._filter.write.bind(this._filter);
this._inflate.on("data", function (chunk) {
if (!leftToInflate) {
return;
}
if (chunk.length > leftToInflate) {
chunk = chunk.slice(0, leftToInflate);
}
leftToInflate -= chunk.length;
filterWrite(chunk);
});
this._inflate.on("end", this._filter.end.bind(this._filter));
}
}
this._inflate.write(data);
};
ParserAsync.prototype._handleMetaData = function (metaData) {
this._metaData = metaData;
this._bitmapInfo = Object.create(metaData);
this._filter = new FilterAsync(this._bitmapInfo);
};
ParserAsync.prototype._handleTransColor = function (transColor) {
this._bitmapInfo.transColor = transColor;
};
ParserAsync.prototype._handlePalette = function (palette) {
this._bitmapInfo.palette = palette;
};
ParserAsync.prototype._simpleTransparency = function () {
this._metaData.alpha = true;
};
ParserAsync.prototype._headersFinished = function () {
// Up until this point, we don't know if we have a tRNS chunk (alpha)
// so we can't emit metadata any earlier
this.emit("metadata", this._metaData);
};
ParserAsync.prototype._finished = function () {
if (this.errord) {
return;
}
if (!this._inflate) {
this.emit("error", "No Inflate block");
} else {
// no more data to inflate
this._inflate.end();
}
};
ParserAsync.prototype._complete = function (filteredData) {
if (this.errord) {
return;
}
let normalisedBitmapData;
try {
let bitmapData = bitmapper(filteredData, this._bitmapInfo);
normalisedBitmapData = formatNormaliser(bitmapData, this._bitmapInfo);
bitmapData = null;
} catch (ex) {
this._handleError(ex);
return;
}
this.emit("parsed", normalisedBitmapData);
};

107
lib/parser-sync.js Normal file
View file

@ -0,0 +1,107 @@
import inflateSync from "./sync-inflate.js";
import SyncReader from "./sync-reader.js";
import FilterSync from "./filter-parse-sync.js";
import bitmapper from "./bitmapper.js";
import formatNormaliser from "./format-normaliser.js";
import Parser from "./parser.js";
let hasSyncZlib = true;
let zlib = require("zlib");
if (!zlib.deflateSync) {
hasSyncZlib = false;
}
export default function (buffer, options) {
if (!hasSyncZlib) {
throw new Error(
"To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
);
}
let err;
function handleError(_err_) {
err = _err_;
}
let metaData;
function handleMetaData(_metaData_) {
metaData = _metaData_;
}
function handleTransColor(transColor) {
metaData.transColor = transColor;
}
function handlePalette(palette) {
metaData.palette = palette;
}
function handleSimpleTransparency() {
metaData.alpha = true;
}
let gamma;
function handleGamma(_gamma_) {
gamma = _gamma_;
}
let inflateDataList = [];
function handleInflateData(inflatedData) {
inflateDataList.push(inflatedData);
}
let reader = new SyncReader(buffer);
let parser = new Parser(options, {
read: reader.read.bind(reader),
error: handleError,
metadata: handleMetaData,
gamma: handleGamma,
palette: handlePalette,
transColor: handleTransColor,
inflateData: handleInflateData,
simpleTransparency: handleSimpleTransparency,
});
parser.start();
reader.process();
if (err) {
throw err;
}
//join together the inflate datas
let inflateData = Buffer.concat(inflateDataList);
inflateDataList.length = 0;
let inflatedData;
if (metaData.interlace) {
inflatedData = zlib.inflateSync(inflateData);
} else {
let rowSize =
((metaData.width * metaData.bpp * metaData.depth + 7) >> 3) + 1;
let imageSize = rowSize * metaData.height;
inflatedData = inflateSync(inflateData, {
chunkSize: imageSize,
maxLength: imageSize,
});
}
inflateData = null;
if (!inflatedData || !inflatedData.length) {
throw new Error("bad png - invalid inflate data response");
}
let unfilteredData = FilterSync.process(inflatedData, metaData);
inflateData = null;
let bitmapData = bitmapper.dataToBitMap(unfilteredData, metaData);
unfilteredData = null;
let normalisedBitmapData = formatNormaliser(bitmapData, metaData);
metaData.data = normalisedBitmapData;
metaData.gamma = gamma || 0;
return metaData;
};

565
lib/parser.js Executable file → Normal file
View file

@ -1,359 +1,290 @@
// Copyright (c) 2012 Kuba Niegowski "use strict";
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict'; let constants = require("./constants");
let CrcCalculator = require("./crc");
let Parser = (module.exports = function (options, dependencies) {
this._options = options;
options.checkCRC = options.checkCRC !== false;
var util = require('util'), this._hasIHDR = false;
zlib = require('zlib'), this._hasIEND = false;
CrcStream = require('./crc'), this._emittedHeadersFinished = false;
ChunkStream = require('./chunkstream'),
constants = require('./constants'),
Filter = require('./filter');
// input flags/metadata
this._palette = [];
this._colorType = 0;
var Parser = module.exports = function(options) { this._chunks = {};
ChunkStream.call(this); this._chunks[constants.TYPE_IHDR] = this._handleIHDR.bind(this);
this._chunks[constants.TYPE_IEND] = this._handleIEND.bind(this);
this._chunks[constants.TYPE_IDAT] = this._handleIDAT.bind(this);
this._chunks[constants.TYPE_PLTE] = this._handlePLTE.bind(this);
this._chunks[constants.TYPE_tRNS] = this._handleTRNS.bind(this);
this._chunks[constants.TYPE_gAMA] = this._handleGAMA.bind(this);
this._options = options; this.read = dependencies.read;
options.checkCRC = options.checkCRC !== false; this.error = dependencies.error;
this.metadata = dependencies.metadata;
this.gamma = dependencies.gamma;
this.transColor = dependencies.transColor;
this.palette = dependencies.palette;
this.parsed = dependencies.parsed;
this.inflateData = dependencies.inflateData;
this.finished = dependencies.finished;
this.simpleTransparency = dependencies.simpleTransparency;
this.headersFinished = dependencies.headersFinished || function () {};
});
this._hasIHDR = false; Parser.prototype.start = function () {
this._hasIEND = false; this.read(constants.PNG_SIGNATURE.length, this._parseSignature.bind(this));
this._inflate = null;
this._filter = null;
this._crc = null;
// input flags/metadata
this._palette = [];
this._colorType = 0;
this._chunks = {};
this._chunks[constants.TYPE_IHDR] = this._handleIHDR.bind(this);
this._chunks[constants.TYPE_IEND] = this._handleIEND.bind(this);
this._chunks[constants.TYPE_IDAT] = this._handleIDAT.bind(this);
this._chunks[constants.TYPE_PLTE] = this._handlePLTE.bind(this);
this._chunks[constants.TYPE_tRNS] = this._handleTRNS.bind(this);
this._chunks[constants.TYPE_gAMA] = this._handleGAMA.bind(this);
this.writable = true;
this.on('error', this._handleError.bind(this));
this._handleSignature();
};
util.inherits(Parser, ChunkStream);
Parser.prototype._handleError = function() {
this.writable = false;
this.destroy();
if (this._inflate)
this._inflate.destroy();
}; };
Parser.prototype._handleSignature = function() { Parser.prototype._parseSignature = function (data) {
this.read(constants.PNG_SIGNATURE.length, let signature = constants.PNG_SIGNATURE;
this._parseSignature.bind(this)
);
};
Parser.prototype._parseSignature = function(data) { for (let i = 0; i < signature.length; i++) {
if (data[i] !== signature[i]) {
var signature = constants.PNG_SIGNATURE; this.error(new Error("Invalid file signature"));
return;
for (var i = 0; i < signature.length; i++) {
if (data[i] != signature[i]) {
this.emit('error', new Error('Invalid file signature'));
return;
}
} }
}
this.read(8, this._parseChunkBegin.bind(this));
};
Parser.prototype._parseChunkBegin = function (data) {
// chunk content length
let length = data.readUInt32BE(0);
// chunk type
let type = data.readUInt32BE(4);
let name = "";
for (let i = 4; i < 8; i++) {
name += String.fromCharCode(data[i]);
}
//console.log('chunk ', name, length);
// chunk flags
let ancillary = Boolean(data[4] & 0x20); // or critical
// priv = Boolean(data[5] & 0x20), // or public
// safeToCopy = Boolean(data[7] & 0x20); // or unsafe
if (!this._hasIHDR && type !== constants.TYPE_IHDR) {
this.error(new Error("Expected IHDR on beggining"));
return;
}
this._crc = new CrcCalculator();
this._crc.write(Buffer.from(name));
if (this._chunks[type]) {
return this._chunks[type](length);
}
if (!ancillary) {
this.error(new Error("Unsupported critical chunk type " + name));
return;
}
this.read(length + 4, this._skipChunk.bind(this));
};
Parser.prototype._skipChunk = function (/*data*/) {
this.read(8, this._parseChunkBegin.bind(this));
};
Parser.prototype._handleChunkEnd = function () {
this.read(4, this._parseChunkEnd.bind(this));
};
Parser.prototype._parseChunkEnd = function (data) {
let fileCrc = data.readInt32BE(0);
let calcCrc = this._crc.crc32();
// check CRC
if (this._options.checkCRC && calcCrc !== fileCrc) {
this.error(new Error("Crc error - " + fileCrc + " - " + calcCrc));
return;
}
if (!this._hasIEND) {
this.read(8, this._parseChunkBegin.bind(this)); this.read(8, this._parseChunkBegin.bind(this));
}
}; };
Parser.prototype._parseChunkBegin = function(data) { Parser.prototype._handleIHDR = function (length) {
this.read(length, this._parseIHDR.bind(this));
};
Parser.prototype._parseIHDR = function (data) {
this._crc.write(data);
// chunk content length let width = data.readUInt32BE(0);
var length = data.readUInt32BE(0); let height = data.readUInt32BE(4);
let depth = data[8];
let colorType = data[9]; // bits: 1 palette, 2 color, 4 alpha
let compr = data[10];
let filter = data[11];
let interlace = data[12];
// chunk type // console.log(' width', width, 'height', height,
var type = data.readUInt32BE(4), // 'depth', depth, 'colorType', colorType,
name = ''; // 'compr', compr, 'filter', filter, 'interlace', interlace
for (var i = 4; i < 8; i++) // );
name += String.fromCharCode(data[i]);
// console.log('chunk ', name, length); if (
depth !== 8 &&
depth !== 4 &&
depth !== 2 &&
depth !== 1 &&
depth !== 16
) {
this.error(new Error("Unsupported bit depth " + depth));
return;
}
if (!(colorType in constants.COLORTYPE_TO_BPP_MAP)) {
this.error(new Error("Unsupported color type"));
return;
}
if (compr !== 0) {
this.error(new Error("Unsupported compression method"));
return;
}
if (filter !== 0) {
this.error(new Error("Unsupported filter method"));
return;
}
if (interlace !== 0 && interlace !== 1) {
this.error(new Error("Unsupported interlace method"));
return;
}
// chunk flags this._colorType = colorType;
var ancillary = !!(data[4] & 0x20), // or critical
priv = !!(data[5] & 0x20), // or public
safeToCopy = !!(data[7] & 0x20); // or unsafe
if (!this._hasIHDR && type != constants.TYPE_IHDR) { let bpp = constants.COLORTYPE_TO_BPP_MAP[this._colorType];
this.emit('error', new Error('Expected IHDR on beggining'));
return;
}
this._crc = new CrcStream(); this._hasIHDR = true;
this._crc.write(new Buffer(name));
if (this._chunks[type]) { this.metadata({
return this._chunks[type](length); width: width,
height: height,
depth: depth,
interlace: Boolean(interlace),
palette: Boolean(colorType & constants.COLORTYPE_PALETTE),
color: Boolean(colorType & constants.COLORTYPE_COLOR),
alpha: Boolean(colorType & constants.COLORTYPE_ALPHA),
bpp: bpp,
colorType: colorType,
});
} else if (!ancillary) { this._handleChunkEnd();
this.emit('error', new Error('Unsupported critical chunk type ' + name));
return;
} else {
this.read(length + 4, this._skipChunk.bind(this));
}
}; };
Parser.prototype._skipChunk = function(data) { Parser.prototype._handlePLTE = function (length) {
this.read(8, this._parseChunkBegin.bind(this)); this.read(length, this._parsePLTE.bind(this));
};
Parser.prototype._parsePLTE = function (data) {
this._crc.write(data);
let entries = Math.floor(data.length / 3);
// console.log('Palette:', entries);
for (let i = 0; i < entries; i++) {
this._palette.push([data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 0xff]);
}
this.palette(this._palette);
this._handleChunkEnd();
}; };
Parser.prototype._handleChunkEnd = function() { Parser.prototype._handleTRNS = function (length) {
this.read(4, this._parseChunkEnd.bind(this)); this.simpleTransparency();
this.read(length, this._parseTRNS.bind(this));
};
Parser.prototype._parseTRNS = function (data) {
this._crc.write(data);
// palette
if (this._colorType === constants.COLORTYPE_PALETTE_COLOR) {
if (this._palette.length === 0) {
this.error(new Error("Transparency chunk must be after palette"));
return;
}
if (data.length > this._palette.length) {
this.error(new Error("More transparent colors than palette size"));
return;
}
for (let i = 0; i < data.length; i++) {
this._palette[i][3] = data[i];
}
this.palette(this._palette);
}
// for colorType 0 (grayscale) and 2 (rgb)
// there might be one gray/color defined as transparent
if (this._colorType === constants.COLORTYPE_GRAYSCALE) {
// grey, 2 bytes
this.transColor([data.readUInt16BE(0)]);
}
if (this._colorType === constants.COLORTYPE_COLOR) {
this.transColor([
data.readUInt16BE(0),
data.readUInt16BE(2),
data.readUInt16BE(4),
]);
}
this._handleChunkEnd();
}; };
Parser.prototype._parseChunkEnd = function(data) { Parser.prototype._handleGAMA = function (length) {
this.read(length, this._parseGAMA.bind(this));
};
Parser.prototype._parseGAMA = function (data) {
this._crc.write(data);
this.gamma(data.readUInt32BE(0) / constants.GAMMA_DIVISION);
var fileCrc = data.readInt32BE(0), this._handleChunkEnd();
calcCrc = this._crc.crc32();
// check CRC
if (this._options.checkCRC && calcCrc != fileCrc) {
this.emit('error', new Error('Crc error'));
return;
}
if (this._hasIEND) {
this.destroySoon();
} else {
this.read(8, this._parseChunkBegin.bind(this));
}
}; };
Parser.prototype._handleIDAT = function (length) {
Parser.prototype._handleIHDR = function(length) { if (!this._emittedHeadersFinished) {
this.read(length, this._parseIHDR.bind(this)); this._emittedHeadersFinished = true;
this.headersFinished();
}
this.read(-length, this._parseIDAT.bind(this, length));
}; };
Parser.prototype._parseIHDR = function(data) { Parser.prototype._parseIDAT = function (length, data) {
this._crc.write(data);
this._crc.write(data); if (
this._colorType === constants.COLORTYPE_PALETTE_COLOR &&
this._palette.length === 0
) {
throw new Error("Expected palette not found");
}
var width = data.readUInt32BE(0), this.inflateData(data);
height = data.readUInt32BE(4), let leftOverLength = length - data.length;
depth = data[8],
colorType = data[9], // bits: 1 palette, 2 color, 4 alpha
compr = data[10],
filter = data[11],
interlace = data[12];
// console.log(' width', width, 'height', height,
// 'depth', depth, 'colorType', colorType,
// 'compr', compr, 'filter', filter, 'interlace', interlace
// );
if (depth != 8) {
this.emit('error', new Error('Unsupported bit depth ' + depth));
return;
}
if (!(colorType in colorTypeToBppMap)) {
this.emit('error', new Error('Unsupported color type'));
return;
}
if (compr != 0) {
this.emit('error', new Error('Unsupported compression method'));
return;
}
if (filter != 0) {
this.emit('error', new Error('Unsupported filter method'));
return;
}
if (interlace != 0) {
this.emit('error', new Error('Unsupported interlace method'));
return;
}
this._colorType = colorType;
this._data = new Buffer(width * height * 4);
this._filter = new Filter(
width, height,
colorTypeToBppMap[this._colorType],
this._data,
this._options
);
this._hasIHDR = true;
this.emit('metadata', {
width: width,
height: height,
palette: !!(colorType & constants.COLOR_PALETTE),
color: !!(colorType & constants.COLOR_COLOR),
alpha: !!(colorType & constants.COLOR_ALPHA),
data: this._data
});
if (leftOverLength > 0) {
this._handleIDAT(leftOverLength);
} else {
this._handleChunkEnd(); this._handleChunkEnd();
}
}; };
Parser.prototype._handleIEND = function (length) {
Parser.prototype._handlePLTE = function(length) { this.read(length, this._parseIEND.bind(this));
this.read(length, this._parsePLTE.bind(this));
}; };
Parser.prototype._parsePLTE = function(data) { Parser.prototype._parseIEND = function (data) {
this._crc.write(data);
this._crc.write(data); this._hasIEND = true;
this._handleChunkEnd();
var entries = Math.floor(data.length / 3); if (this.finished) {
// console.log('Palette:', entries); this.finished();
}
for (var i = 0; i < entries; i++) {
this._palette.push([
data.readUInt8(i * 3),
data.readUInt8(i * 3 + 1),
data.readUInt8(i * 3 + 2 ),
0xff
]);
}
this._handleChunkEnd();
};
Parser.prototype._handleTRNS = function(length) {
this.read(length, this._parseTRNS.bind(this));
};
Parser.prototype._parseTRNS = function(data) {
this._crc.write(data);
// palette
if (this._colorType == 3) {
if (this._palette.length == 0) {
this.emit('error', new Error('Transparency chunk must be after palette'));
return;
}
if (data.length > this._palette.length) {
this.emit('error', new Error('More transparent colors than palette size'));
return;
}
for (var i = 0; i < this._palette.length; i++) {
this._palette[i][3] = i < data.length ? data.readUInt8(i) : 0xff;
}
}
// for colorType 0 (grayscale) and 2 (rgb)
// there might be one gray/color defined as transparent
this._handleChunkEnd();
};
Parser.prototype._handleGAMA = function(length) {
this.read(length, this._parseGAMA.bind(this));
};
Parser.prototype._parseGAMA = function(data) {
this._crc.write(data);
this.emit('gamma', data.readUInt32BE(0) / 100000);
this._handleChunkEnd();
};
Parser.prototype._handleIDAT = function(length) {
this.read(-length, this._parseIDAT.bind(this, length));
};
Parser.prototype._parseIDAT = function(length, data) {
this._crc.write(data);
if (this._colorType == 3 && this._palette.length == 0)
throw new Error('Expected palette not found');
if (!this._inflate) {
this._inflate = zlib.createInflate();
this._inflate.on('error', this.emit.bind(this, 'error'));
this._filter.on('complete', this._reverseFiltered.bind(this));
this._inflate.pipe(this._filter);
}
this._inflate.write(data);
length -= data.length;
if (length > 0)
this._handleIDAT(length);
else
this._handleChunkEnd();
};
Parser.prototype._handleIEND = function(length) {
this.read(length, this._parseIEND.bind(this));
};
Parser.prototype._parseIEND = function(data) {
this._crc.write(data);
// no more data to inflate
this._inflate.end();
this._hasIEND = true;
this._handleChunkEnd();
};
var colorTypeToBppMap = {
0: 1,
2: 3,
3: 1,
4: 2,
6: 4
};
Parser.prototype._reverseFiltered = function(data, width, height) {
if (this._colorType == 3) { // paletted
// use values from palette
var pxLineLength = width << 2;
for (var y = 0; y < height; y++) {
var pxRowPos = y * pxLineLength;
for (var x = 0; x < width; x++) {
var pxPos = pxRowPos + (x << 2),
color = this._palette[data[pxPos]];
for (var i = 0; i < 4; i++)
data[pxPos + i] = color[i];
}
}
}
this.emit('parsed', data);
}; };

15
lib/png-sync.js Normal file
View file

@ -0,0 +1,15 @@
import parse from "./parser-sync.js";
import pack from "./packer-sync.js";
export var read = function (buffer, options) {
return parse(buffer, options || {});
};
export var write = function (png, options) {
return pack(png, options);
};
export default {
read,
write
}

288
lib/png.js Executable file → Normal file
View file

@ -1,149 +1,193 @@
// Copyright (c) 2012 Kuba Niegowski import Parser from "./parser-async.js";
// import Packer from "./packer-async.js";
// Permission is hereby granted, free of charge, to any person obtaining a copy import PNGSync from "./png-sync.js";
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict'; let util = require("util");
let Stream = require("stream");
let PNG = (exports.PNG = function (options) {
Stream.call(this);
var util = require('util'), options = options || {}; // eslint-disable-line no-param-reassign
Stream = require('stream'),
Parser = require('./parser'),
Packer = require('./packer');
// coerce pixel dimensions to integers (also coerces undefined -> 0):
this.width = options.width | 0;
this.height = options.height | 0;
var PNG = exports.PNG = function(options) { this.data =
Stream.call(this); this.width > 0 && this.height > 0
? Buffer.alloc(4 * this.width * this.height)
: null;
options = options || {}; if (options.fill && this.data) {
this.data.fill(0);
}
this.width = options.width || 0; this.gamma = 0;
this.height = options.height || 0; this.readable = this.writable = true;
this.data = this.width > 0 && this.height > 0
? new Buffer(4 * this.width * this.height) : null;
if(options.fill && this.data){this.data.fill(0)}; this._parser = new Parser(options);
this.gamma = 0; this._parser.on("error", this.emit.bind(this, "error"));
this.readable = this.writable = true; this._parser.on("close", this._handleClose.bind(this));
this._parser.on("metadata", this._metadata.bind(this));
this._parser.on("gamma", this._gamma.bind(this));
this._parser.on(
"parsed",
function (data) {
this.data = data;
this.emit("parsed", data);
}.bind(this)
);
this._parser = new Parser(options || {}); this._packer = new Packer(options);
this._packer.on("data", this.emit.bind(this, "data"));
this._parser.on('error', this.emit.bind(this, 'error')); this._packer.on("end", this.emit.bind(this, "end"));
this._parser.on('close', this._handleClose.bind(this)); this._parser.on("close", this._handleClose.bind(this));
this._parser.on('metadata', this._metadata.bind(this)); this._packer.on("error", this.emit.bind(this, "error"));
this._parser.on('gamma', this._gamma.bind(this)); });
this._parser.on('parsed', function(data) {
this.data = data;
this.emit('parsed', data);
}.bind(this));
this._packer = new Packer(options);
this._packer.on('data', this.emit.bind(this, 'data'));
this._packer.on('end', this.emit.bind(this, 'end'));
this._parser.on('close', this._handleClose.bind(this));
this._packer.on('error', this.emit.bind(this, 'error'));
};
util.inherits(PNG, Stream); util.inherits(PNG, Stream);
PNG.sync = PNGSync;
PNG.prototype.pack = function() { PNG.prototype.pack = function () {
if (!this.data || !this.data.length) {
process.nextTick(function() { this.emit("error", "No data provided");
this._packer.pack(this.data, this.width, this.height);
}.bind(this));
return this; return this;
}
process.nextTick(
function () {
this._packer.pack(this.data, this.width, this.height, this.gamma);
}.bind(this)
);
return this;
}; };
PNG.prototype.parse = function (data, callback) {
if (callback) {
let onParsed, onError;
PNG.prototype.parse = function(data, callback) { onParsed = function (parsedData) {
this.removeListener("error", onError);
if (callback) { this.data = parsedData;
var onParsed = null, onError = null; callback(null, this);
}.bind(this);
this.once('parsed', onParsed = function(data) { onError = function (err) {
this.removeListener('error', onError); this.removeListener("parsed", onParsed);
this.data = data; callback(err, null);
callback(null, this); }.bind(this);
}.bind(this)); this.once("parsed", onParsed);
this.once("error", onError);
}
this.once('error', onError = function(err) { this.end(data);
this.removeListener('parsed', onParsed); return this;
};
callback(err, null); PNG.prototype.write = function (data) {
}.bind(this)); this._parser.write(data);
return true;
};
PNG.prototype.end = function (data) {
this._parser.end(data);
};
PNG.prototype._metadata = function (metadata) {
this.width = metadata.width;
this.height = metadata.height;
this.emit("metadata", metadata);
};
PNG.prototype._gamma = function (gamma) {
this.gamma = gamma;
};
PNG.prototype._handleClose = function () {
if (!this._parser.writable && !this._packer.readable) {
this.emit("close");
}
};
PNG.bitblt = function (src, dst, srcX, srcY, width, height, deltaX, deltaY) {
// eslint-disable-line max-params
// coerce pixel dimensions to integers (also coerces undefined -> 0):
/* eslint-disable no-param-reassign */
srcX |= 0;
srcY |= 0;
width |= 0;
height |= 0;
deltaX |= 0;
deltaY |= 0;
/* eslint-enable no-param-reassign */
if (
srcX > src.width ||
srcY > src.height ||
srcX + width > src.width ||
srcY + height > src.height
) {
throw new Error("bitblt reading outside image");
}
if (
deltaX > dst.width ||
deltaY > dst.height ||
deltaX + width > dst.width ||
deltaY + height > dst.height
) {
throw new Error("bitblt writing outside image");
}
for (let y = 0; y < height; y++) {
src.data.copy(
dst.data,
((deltaY + y) * dst.width + deltaX) << 2,
((srcY + y) * src.width + srcX) << 2,
((srcY + y) * src.width + srcX + width) << 2
);
}
};
PNG.prototype.bitblt = function (
dst,
srcX,
srcY,
width,
height,
deltaX,
deltaY
) {
// eslint-disable-line max-params
PNG.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY);
return this;
};
PNG.adjustGamma = function (src) {
if (src.gamma) {
for (let y = 0; y < src.height; y++) {
for (let x = 0; x < src.width; x++) {
let idx = (src.width * y + x) << 2;
for (let i = 0; i < 3; i++) {
let sample = src.data[idx + i] / 255;
sample = Math.pow(sample, 1 / 2.2 / src.gamma);
src.data[idx + i] = Math.round(sample * 255);
}
}
} }
src.gamma = 0;
this.end(data); }
return this;
}; };
PNG.prototype.write = function(data) { PNG.prototype.adjustGamma = function () {
this._parser.write(data); PNG.adjustGamma(this);
return true;
};
PNG.prototype.end = function(data) {
this._parser.end(data);
};
PNG.prototype._metadata = function(metadata) {
this.width = metadata.width;
this.height = metadata.height;
this.data = metadata.data;
delete metadata.data;
this.emit('metadata', metadata);
};
PNG.prototype._gamma = function(gamma) {
this.gamma = gamma;
};
PNG.prototype._handleClose = function() {
if (!this._parser.writable && !this._packer.readable)
this.emit('close');
};
PNG.prototype.bitblt = function(dst, sx, sy, w, h, dx, dy) {
var src = this;
if (sx > src.width || sy > src.height
|| sx + w > src.width || sy + h > src.height)
throw new Error('bitblt reading outside image');
if (dx > dst.width || dy > dst.height
|| dx + w > dst.width || dy + h > dst.height)
throw new Error('bitblt writing outside image');
for (var y = 0; y < h; y++) {
src.data.copy(dst.data,
((dy + y) * dst.width + dx) << 2,
((sy + y) * src.width + sx) << 2,
((sy + y) * src.width + sx + w) << 2
);
}
return this;
}; };

163
lib/sync-inflate.js Normal file
View file

@ -0,0 +1,163 @@
let assert = require("assert").ok;
let zlib = require("zlib");
let util = require("util");
let kMaxLength = require("buffer").kMaxLength;
export function Inflate(opts) {
if (!(this instanceof Inflate)) {
return new Inflate(opts);
}
if (opts && opts.chunkSize < zlib.Z_MIN_CHUNK) {
opts.chunkSize = zlib.Z_MIN_CHUNK;
}
zlib.Inflate.call(this, opts);
// Node 8 --> 9 compatibility check
this._offset = this._offset === undefined ? this._outOffset : this._offset;
this._buffer = this._buffer || this._outBuffer;
if (opts && opts.maxLength != null) {
this._maxLength = opts.maxLength;
}
}
export function createInflate(opts) {
return new Inflate(opts);
}
function _close(engine, callback) {
if (callback) {
process.nextTick(callback);
}
// Caller may invoke .close after a zlib error (which will null _handle).
if (!engine._handle) {
return;
}
engine._handle.close();
engine._handle = null;
}
Inflate.prototype._processChunk = function (chunk, flushFlag, asyncCb) {
if (typeof asyncCb === "function") {
return zlib.Inflate._processChunk.call(this, chunk, flushFlag, asyncCb);
}
let self = this;
let availInBefore = chunk && chunk.length;
let availOutBefore = this._chunkSize - this._offset;
let leftToInflate = this._maxLength;
let inOff = 0;
let buffers = [];
let nread = 0;
let error;
this.on("error", function (err) {
error = err;
});
function handleChunk(availInAfter, availOutAfter) {
if (self._hadError) {
return;
}
let have = availOutBefore - availOutAfter;
assert(have >= 0, "have should not go down");
if (have > 0) {
let out = self._buffer.slice(self._offset, self._offset + have);
self._offset += have;
if (out.length > leftToInflate) {
out = out.slice(0, leftToInflate);
}
buffers.push(out);
nread += out.length;
leftToInflate -= out.length;
if (leftToInflate === 0) {
return false;
}
}
if (availOutAfter === 0 || self._offset >= self._chunkSize) {
availOutBefore = self._chunkSize;
self._offset = 0;
self._buffer = Buffer.allocUnsafe(self._chunkSize);
}
if (availOutAfter === 0) {
inOff += availInBefore - availInAfter;
availInBefore = availInAfter;
return true;
}
return false;
}
assert(this._handle, "zlib binding closed");
let res;
do {
res = this._handle.writeSync(
flushFlag,
chunk, // in
inOff, // in_off
availInBefore, // in_len
this._buffer, // out
this._offset, //out_off
availOutBefore
); // out_len
// Node 8 --> 9 compatibility check
res = res || this._writeState;
} while (!this._hadError && handleChunk(res[0], res[1]));
if (this._hadError) {
throw error;
}
if (nread >= kMaxLength) {
_close(this);
throw new RangeError(
"Cannot create final Buffer. It would be larger than 0x" +
kMaxLength.toString(16) +
" bytes"
);
}
let buf = Buffer.concat(buffers, nread);
_close(this);
return buf;
};
util.inherits(Inflate, zlib.Inflate);
function zlibBufferSync(engine, buffer) {
if (typeof buffer === "string") {
buffer = Buffer.from(buffer);
}
if (!(buffer instanceof Buffer)) {
throw new TypeError("Not a string or buffer");
}
let flushFlag = engine._finishFlushFlag;
if (flushFlag == null) {
flushFlag = zlib.Z_FINISH;
}
return engine._processChunk(buffer, flushFlag);
}
export function inflateSync(buffer, opts) {
return zlibBufferSync(new Inflate(opts), buffer);
}
export default inflateSync;

45
lib/sync-reader.js Normal file
View file

@ -0,0 +1,45 @@
let SyncReader = function (buffer) {
this._buffer = buffer;
this._reads = [];
};
export default SyncReader;
SyncReader.prototype.read = function (length, callback) {
this._reads.push({
length: Math.abs(length), // if length < 0 then at most this length
allowLess: length < 0,
func: callback,
});
};
SyncReader.prototype.process = function () {
// as long as there is any data and read requests
while (this._reads.length > 0 && this._buffer.length) {
let read = this._reads[0];
if (
this._buffer.length &&
(this._buffer.length >= read.length || read.allowLess)
) {
// ok there is any data so that we can satisfy this request
this._reads.shift(); // == read
let buf = this._buffer;
this._buffer = buf.slice(read.length);
read.func.call(this, buf.slice(0, read.length));
} else {
break;
}
}
if (this._reads.length > 0) {
return new Error("There are some read requests waitng on finished stream");
}
if (this._buffer.length > 0) {
return new Error("unrecognised content at end of stream");
}
};

View file

@ -1,29 +1,73 @@
{ {
"name": "node-png", "name": "pngjs",
"version": "0.4.3", "version": "5.0.0",
"description": "Simple PNG encoder/decoder", "description": "PNG encoder/decoder in pure JS, supporting any bit size & interlace, async & sync with full test suite.",
"author": "Kuba Niegowski", "contributors": [
"contributors": [], "Alexandre Paré",
"homepage": "https://github.com/niegowski/node-pngjs/", "Gaurav Mali",
"Gusts Kaksis",
"Kuba Niegowski",
"Luke Page",
"Pietajan De Potter",
"Steven Sojka",
"liangzeng",
"Michael Vogt",
"Xin-Xin Wang",
"toriningen",
"Eugene Kulabuhov"
],
"homepage": "https://github.com/lukeapage/pngjs",
"keywords": [ "keywords": [
"png" "PNG",
"decoder",
"encoder",
"js-png",
"node-png",
"parser",
"png",
"png-js",
"png-parse",
"pngjs"
], ],
"engines": { "engines": {
"node": "0.8.x" "node": ">=10.13.0"
}, },
"main": "./lib/png.js", "main": "./lib/png.js",
"directories": { "directories": {
"example": "examples" "lib": "lib",
"example": "examples",
"test": "test"
}, },
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "build": "yarn prepublish",
"prepublish": "yarn browserify",
"browserify": "browserify lib/png.js --standalone png > browser.js",
"coverage": "nyc --reporter=lcov --reporter=text-summary tape test/*-spec.js nolarge",
"test": "yarn lint && yarn prettier:check && tape test/*-spec.js | tap-dot && node test/run-compare",
"lint": "eslint .",
"prettier:write": "prettier --write .",
"prettier:check": "prettier --check ."
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/brighthas/node-pngjs.git" "url": "git://github.com/lukeapage/pngjs.git"
}, },
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/brighthas/node-pngjs/issues" "url": "https://github.com/lukeapage/pngjs/issues"
},
"devDependencies": {
"browserify": "16.5.1",
"buffer-equal": "1.0.0",
"codecov": "3.7.0",
"connect": "3.7.0",
"eslint": "7.0.0",
"eslint-config-prettier": "6.11.0",
"nyc": "15.0.1",
"prettier": "2.0.5",
"puppeteer": "3.0.2",
"serve-static": "1.14.1",
"tap-dot": "2.0.0",
"tape": "5.0.0"
} }
} }

1
test/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
bg.png

BIN
test/bg-ref.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

46
test/bg-spec.js Normal file
View file

@ -0,0 +1,46 @@
#!/usr/bin/env node
let fs = require("fs");
let PNG = require("../lib/png").PNG;
let test = require("tape");
let bufferEqual = require("buffer-equal");
test("outputs background, created from scratch", function (t) {
t.timeoutAfter(1000 * 60 * 5);
let png = new PNG({
width: 10,
height: 10,
filterType: -1,
});
for (let y = 0; y < png.height; y++) {
for (let x = 0; x < png.width; x++) {
let idx = (png.width * y + x) << 2;
let col = (x < png.width >> 1) ^ (y < png.height >> 1) ? 0xe5 : 0xff;
png.data[idx] = col;
png.data[idx + 1] = col;
png.data[idx + 2] = col;
png.data[idx + 3] = 0xff;
}
}
png
.pack()
.pipe(fs.createWriteStream(__dirname + "/bg.png"))
.on("finish", function () {
let out = fs.readFileSync(__dirname + "/bg.png");
let ref = fs.readFileSync(__dirname + "/bg-ref.png");
let isBufferEqual = bufferEqual(out, ref);
t.ok(isBufferEqual, "compares with working file ok");
if (!isBufferEqual) {
console.log(out.length, ref.length);
}
t.end();
});
});

BIN
test/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

105
test/convert-images-spec.js Normal file
View file

@ -0,0 +1,105 @@
let fs = require("fs");
let PNG = require("../lib/png").PNG;
let test = require("tape");
let noLargeOption = process.argv.indexOf("nolarge") >= 0;
fs.readdir(__dirname + "/in/", function (err, files) {
if (err) throw err;
files = files.filter(function (file) {
return (
(!noLargeOption || !file.match(/large/i)) &&
Boolean(file.match(/\.png$/i))
);
});
console.log("Converting images");
files.forEach(function (file) {
let expectedError = false;
if (file.match(/^x/)) {
expectedError = true;
}
test("convert sync - " + file, function (t) {
t.timeoutAfter(1000 * 60 * 5);
let data = fs.readFileSync(__dirname + "/in/" + file);
let png;
try {
png = PNG.sync.read(data);
} catch (e) {
if (!expectedError) {
t.fail(
"Unexpected error parsing.." +
file +
"\n" +
e.message +
"\n" +
e.stack
);
} else {
t.pass("completed");
}
return t.end();
}
if (expectedError) {
t.fail("Sync: Error expected, parsed fine .. - " + file);
return t.end();
}
let outpng = new PNG();
outpng.gamma = png.gamma;
outpng.data = png.data;
outpng.width = png.width;
outpng.height = png.height;
outpng.pack().pipe(
fs
.createWriteStream(__dirname + "/outsync/" + file)
.on("finish", function () {
t.pass("completed");
t.end();
})
);
});
test("convert async - " + file, function (t) {
t.timeoutAfter(1000 * 60 * 5);
fs.createReadStream(__dirname + "/in/" + file)
.pipe(new PNG())
.on("error", function (err) {
if (!expectedError) {
t.fail(
"Async: Unexpected error parsing.." +
file +
"\n" +
err.message +
"\n" +
err.stack
);
} else {
t.pass("completed");
}
t.end();
})
.on("parsed", function () {
if (expectedError) {
t.fail("Async: Error expected, parsed fine .." + file);
return t.end();
}
this.pack().pipe(
fs
.createWriteStream(__dirname + "/out/" + file)
.on("finish", function () {
t.pass("completed");
t.end();
})
);
});
});
});
});

16
test/http-server.js Normal file
View file

@ -0,0 +1,16 @@
let serveStatic = require("serve-static");
let http = require("http");
let connect = require("connect");
let app = connect();
let server = http.createServer(app);
app.use(serveStatic("test"));
server.listen(8000);
module.exports = () => {
server.close();
};
console.log("Tests available at http://localhost:8000/");

409
test/imagediff.js Normal file
View file

@ -0,0 +1,409 @@
// js-imagediff 1.0.3
// (c) 2011-2012 Carl Sutherland, Humble Software
// Distributed under the MIT License
// For original source and documentation visit:
// http://www.github.com/HumbleSoftware/js-imagediff
(function (name, definition) {
var root = this;
if (typeof module !== 'undefined') {
try {
var Canvas = require('canvas');
} catch (e) {
throw new Error(
e.message + '\n' +
'Please see https://github.com/HumbleSoftware/js-imagediff#cannot-find-module-canvas\n'
);
}
module.exports = definition(root, name, Canvas);
} else if (typeof define === 'function' && typeof define.amd === 'object') {
define(definition);
} else {
root[name] = definition(root, name);
}
})('imagediff', function (root, name, Canvas) {
var
TYPE_ARRAY = /\[object Array\]/i,
TYPE_CANVAS = /\[object (Canvas|HTMLCanvasElement)\]/i,
TYPE_CONTEXT = /\[object CanvasRenderingContext2D\]/i,
TYPE_IMAGE = /\[object (Image|HTMLImageElement)\]/i,
TYPE_IMAGE_DATA = /\[object ImageData\]/i,
UNDEFINED = 'undefined',
canvas = getCanvas(),
context = canvas.getContext('2d'),
previous = root[name],
imagediff, jasmine;
// Creation
function getCanvas (width, height) {
var
canvas = Canvas ?
new Canvas() :
document.createElement('canvas');
if (width) canvas.width = width;
if (height) canvas.height = height;
return canvas;
}
function getImageData (width, height) {
canvas.width = width;
canvas.height = height;
context.clearRect(0, 0, width, height);
return context.createImageData(width, height);
}
// expost canvas module
function getCanvasRef() {
return Canvas;
}
// Type Checking
function isImage (object) {
return isType(object, TYPE_IMAGE);
}
function isCanvas (object) {
return isType(object, TYPE_CANVAS);
}
function isContext (object) {
return isType(object, TYPE_CONTEXT);
}
function isImageData (object) {
return !!(object &&
isType(object, TYPE_IMAGE_DATA) &&
typeof(object.width) !== UNDEFINED &&
typeof(object.height) !== UNDEFINED &&
typeof(object.data) !== UNDEFINED);
}
function isImageType (object) {
return (
isImage(object) ||
isCanvas(object) ||
isContext(object) ||
isImageData(object)
);
}
function isType (object, type) {
return typeof (object) === 'object' && !!Object.prototype.toString.apply(object).match(type);
}
// Type Conversion
function copyImageData (imageData) {
var
height = imageData.height,
width = imageData.width,
data = imageData.data,
newImageData, newData, i;
canvas.width = width;
canvas.height = height;
newImageData = context.getImageData(0, 0, width, height);
newData = newImageData.data;
for (i = imageData.data.length; i--;) {
newData[i] = data[i];
}
return newImageData;
}
function toImageData (object) {
if (isImage(object)) { return toImageDataFromImage(object); }
if (isCanvas(object)) { return toImageDataFromCanvas(object); }
if (isContext(object)) { return toImageDataFromContext(object); }
if (isImageData(object)) { return object; }
}
function toImageDataFromImage (image) {
var
height = image.height,
width = image.width;
canvas.width = width;
canvas.height = height;
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0);
return context.getImageData(0, 0, width, height);
}
function toImageDataFromCanvas (canvas) {
var
height = canvas.height,
width = canvas.width,
context = canvas.getContext('2d');
return context.getImageData(0, 0, width, height);
}
function toImageDataFromContext (context) {
var
canvas = context.canvas,
height = canvas.height,
width = canvas.width;
return context.getImageData(0, 0, width, height);
}
function toCanvas (object) {
var
data = toImageData(object),
canvas = getCanvas(data.width, data.height),
context = canvas.getContext('2d');
context.putImageData(data, 0, 0);
return canvas;
}
// ImageData Equality Operators
function equalWidth (a, b) {
return a.width === b.width;
}
function equalHeight (a, b) {
return a.height === b.height;
}
function equalDimensions (a, b) {
return equalHeight(a, b) && equalWidth(a, b);
}
function equal (a, b, tolerance) {
var
aData = a.data,
bData = b.data,
length = aData.length,
i;
tolerance = tolerance || 0;
if (!equalDimensions(a, b)) return false;
for (i = length; i--;) {
if (aData[i] !== bData[i] && Math.abs(aData[i] - bData[i]) > tolerance) {
var x = i % (a.width * 4);
var y = (i - x) / (a.width * 4);
var color = x % 4;
x = (x - color) / 4;
console.log("Difference x", x, "y", y, ["R", "G", "B", "A"][color], " - ", aData[i], " !== ", bData[i]);
return false;
}
}
return true;
}
// Diff
function diff (a, b, options) {
return (equalDimensions(a, b) ? diffEqual : diffUnequal)(a, b, options);
}
function diffEqual (a, b, options) {
var
height = a.height,
width = a.width,
c = getImageData(width, height), // c = a - b
aData = a.data,
bData = b.data,
cData = c.data,
length = cData.length,
row, column,
i, j, k, v;
for (i = 0; i < length; i += 4) {
cData[i] = Math.abs(aData[i] - bData[i]);
cData[i+1] = Math.abs(aData[i+1] - bData[i+1]);
cData[i+2] = Math.abs(aData[i+2] - bData[i+2]);
cData[i+3] = Math.abs(255 - Math.abs(aData[i+3] - bData[i+3]));
}
return c;
}
function diffUnequal (a, b, options) {
var
height = Math.max(a.height, b.height),
width = Math.max(a.width, b.width),
c = getImageData(width, height), // c = a - b
aData = a.data,
bData = b.data,
cData = c.data,
align = options && options.align,
rowOffset,
columnOffset,
row, column,
i, j, k, v;
for (i = cData.length - 1; i > 0; i = i - 4) {
cData[i] = 255;
}
// Add First Image
offsets(a);
for (row = a.height; row--;){
for (column = a.width; column--;) {
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
j = 4 * (row * a.width + column);
cData[i+0] = aData[j+0]; // r
cData[i+1] = aData[j+1]; // g
cData[i+2] = aData[j+2]; // b
// cData[i+3] = aData[j+3]; // a
}
}
// Subtract Second Image
offsets(b);
for (row = b.height; row--;){
for (column = b.width; column--;) {
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
j = 4 * (row * b.width + column);
cData[i+0] = Math.abs(cData[i+0] - bData[j+0]); // r
cData[i+1] = Math.abs(cData[i+1] - bData[j+1]); // g
cData[i+2] = Math.abs(cData[i+2] - bData[j+2]); // b
}
}
// Helpers
function offsets (imageData) {
if (align === 'top') {
rowOffset = 0;
columnOffset = 0;
} else {
rowOffset = Math.floor((height - imageData.height) / 2);
columnOffset = Math.floor((width - imageData.width) / 2);
}
}
return c;
}
// Validation
function checkType () {
var i;
for (i = 0; i < arguments.length; i++) {
if (!isImageType(arguments[i])) {
throw {
name : 'ImageTypeError',
message : 'Submitted object was not an image.'
};
}
}
}
// Jasmine Matchers
function get (element, content) {
element = document.createElement(element);
if (element && content) {
element.innerHTML = content;
}
return element;
}
jasmine = {
toBeImageData : function () {
return imagediff.isImageData(this.actual);
},
toImageDiffEqual : function (expected, tolerance) {
if (typeof (document) !== UNDEFINED) {
this.message = function () {
var
div = get('div'),
a = get('div', '<div>Actual:</div>'),
b = get('div', '<div>Expected:</div>'),
c = get('div', '<div>Diff:</div>'),
diff = imagediff.diff(this.actual, expected),
canvas = getCanvas(),
context;
canvas.height = diff.height;
canvas.width = diff.width;
div.style.overflow = 'hidden';
a.style.float = 'left';
b.style.float = 'left';
c.style.float = 'left';
context = canvas.getContext('2d');
context.putImageData(diff, 0, 0);
a.appendChild(toCanvas(this.actual));
b.appendChild(toCanvas(expected));
c.appendChild(canvas);
div.appendChild(a);
div.appendChild(b);
div.appendChild(c);
return [
div,
"Expected not to be equal."
];
};
}
return imagediff.equal(this.actual, expected, tolerance);
}
};
// Image Output
function imageDataToPNG (imageData, outputFile, callback) {
var
canvas = toCanvas(imageData),
base64Data,
decodedImage;
callback = callback || Function;
base64Data = canvas.toDataURL().replace(/^data:image\/\w+;base64,/,"");
decodedImage = Buffer.from(base64Data, 'base64');
require('fs').writeFile(outputFile, decodedImage, callback);
}
// Definition
imagediff = {
createCanvas : getCanvas,
createImageData : getImageData,
getCanvasRef: getCanvasRef,
isImage : isImage,
isCanvas : isCanvas,
isContext : isContext,
isImageData : isImageData,
isImageType : isImageType,
toImageData : function (object) {
checkType(object);
if (isImageData(object)) { return copyImageData(object); }
return toImageData(object);
},
equal : function (a, b, tolerance) {
checkType(a, b);
a = toImageData(a);
b = toImageData(b);
return equal(a, b, tolerance);
},
diff : function (a, b, options) {
checkType(a, b);
a = toImageData(a);
b = toImageData(b);
return diff(a, b, options);
},
jasmine : jasmine,
// Compatibility
noConflict : function () {
root[name] = previous;
return imagediff;
}
};
if (typeof module !== 'undefined') {
imagediff.imageDataToPNG = imageDataToPNG;
}
return imagediff;
});

View file

@ -1,25 +1,25 @@
PNGSUITE PNGSUITE
---------------- ----------------
testset for PNG-(de)coders testset for PNG-(de)coders
created by Willem van Schaik created by Willem van Schaik
------------------------------------ ------------------------------------
This is a collection of graphics images created to test the png applications This is a collection of graphics images created to test the png applications
like viewers, converters and editors. All (as far as that is possible) like viewers, converters and editors. All (as far as that is possible)
formats supported by the PNG standard are represented. formats supported by the PNG standard are represented.
The suite consists of the following files: The suite consists of the following files:
- PngSuite.README - this file - PngSuite.README - this file
- PngSuite.LICENSE - the PngSuite is freeware - PngSuite.LICENSE - the PngSuite is freeware
- PngSuite.png - image with PngSuite logo - PngSuite.png - image with PngSuite logo
- PngSuite.tgz - archive of all PNG testfiles - PngSuite.tgz - archive of all PNG testfiles
- PngSuite.zip - same in .zip format for PCs - PngSuite.zip - same in .zip format for PCs
-------- --------
(c) Willem van Schaik (c) Willem van Schaik
willem@schaik.com willem@schaik.com
Calgary, April 2011 Calgary, April 2011

BIN
test/in/PngSuite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
test/in/basi0g01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

BIN
test/in/basi0g02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

BIN
test/in/basi0g04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

BIN
test/in/basi0g08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

BIN
test/in/basi0g16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

BIN
test/in/basi2c08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

BIN
test/in/basi2c16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

BIN
test/in/basi3p01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

BIN
test/in/basi3p02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

BIN
test/in/basi3p04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

BIN
test/in/basi3p08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
test/in/basi4a08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

BIN
test/in/basi4a16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
test/in/basi6a08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

BIN
test/in/basi6a16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
test/in/basn0g01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

BIN
test/in/basn0g02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

BIN
test/in/basn0g04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

View file

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 138 B

BIN
test/in/basn0g16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

View file

Before

Width:  |  Height:  |  Size: 145 B

After

Width:  |  Height:  |  Size: 145 B

BIN
test/in/basn2c16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

BIN
test/in/basn3p01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

BIN
test/in/basn3p02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

BIN
test/in/basn3p04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 126 B

After

Width:  |  Height:  |  Size: 126 B

BIN
test/in/basn4a16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 184 B

After

Width:  |  Height:  |  Size: 184 B

BIN
test/in/basn6a16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
test/in/bgai4a08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

BIN
test/in/bgai4a16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
test/in/bgan6a08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

BIN
test/in/bgan6a16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
test/in/bgbn4a08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

BIN
test/in/bggn4a16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
test/in/bgwn6a08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

BIN
test/in/bgyn6a16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
test/in/ccwn2c08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
test/in/ccwn3p08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
test/in/cdfn2c08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

BIN
test/in/cdhn2c08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

BIN
test/in/cdsn2c08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

BIN
test/in/cdun2c08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

BIN
test/in/ch1n3p04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

BIN
test/in/ch2n3p08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
test/in/cm0n0g04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Some files were not shown because too many files have changed in this diff Show more