Compare commits

...

62 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
45 changed files with 6202 additions and 1767 deletions

2
.eslintignore Normal file
View file

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

View file

@ -1,183 +1,19 @@
{ {
"ecmaFeatures": {}, "parserOptions": { "ecmaVersion": 2018 },
"rules": { "extends": ["eslint:recommended", "prettier"],
"no-alert": 2,
"no-array-constructor": 0,
"no-bitwise": 0,
"no-caller": 2,
"no-catch-shadow": 2,
"no-class-assign": 2,
"no-cond-assign": 2,
"no-console": 2,
"no-const-assign": 2,
"no-constant-condition": 2,
"no-continue": 0,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-div-regex": 0,
"no-dupe-keys": 2,
"no-dupe-args": 2,
"no-duplicate-case": 2,
"no-else-return": 1,
"no-empty": 2,
"no-empty-character-class": 2,
"no-eq-null": 0,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 1,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 2,
"no-fallthrough": 2,
"no-floating-decimal": 1,
"no-func-assign": 2,
"no-implicit-coercion": 1,
"no-implied-eval": 1,
"no-inline-comments": 0,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-invalid-this": 1,
"no-irregular-whitespace": 2,
"no-iterator": 1,
"no-label-var": 1,
"no-labels": 1,
"no-lone-blocks": 1,
"no-lonely-if": 1,
"no-loop-func": 1,
"no-mixed-requires": [1, false],
"no-mixed-spaces-and-tabs": [2, false],
"linebreak-style": [0, "unix"],
"no-multi-spaces": 1,
"no-multi-str": 0,
"no-multiple-empty-lines": [1, {"max": 2}],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-nested-ternary": 1,
"no-new": 1,
"no-new-func": 1,
"no-new-object": 1,
"no-new-require": 1,
"no-new-wrappers": 1,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 0,
"no-param-reassign": 1,
"no-path-concat": 0,
"no-plusplus": 0,
"no-process-env": 0,
"no-process-exit": 0,
"no-proto": 0,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-restricted-modules": 0,
"no-return-assign": 1,
"no-script-url": 0,
"no-self-compare": 1,
"no-sequences": 1,
"no-shadow": 1,
"no-shadow-restricted-names": 1,
"no-spaced-func": 1,
"no-sparse-arrays": 2,
"no-sync": 0,
"no-ternary": 0,
"no-trailing-spaces": 1,
"no-this-before-super": 1,
"no-throw-literal": 1,
"no-undef": 2,
"no-undef-init": 1,
"no-undefined": 0,
"no-unexpected-multiline": 1,
"no-underscore-dangle": 0,
"no-unneeded-ternary": 0,
"no-unreachable": 2,
"no-unused-expressions": 1,
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
"no-use-before-define": 2,
"no-useless-call": 2,
"no-void": 0,
"no-var": 0,
"no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
"no-with": 1,
"array-bracket-spacing": [1, "never"],
"arrow-parens": 1,
"arrow-spacing": 1,
"accessor-pairs": 1,
"block-scoped-var": 0,
"brace-style": [1, "stroustrup"],
"callback-return": [2, ["callback", "cb", "next"]],
"camelcase": [2, {"properties": "always"}],
"comma-dangle": [2, "never"],
"comma-spacing": 2,
"comma-style": 1,
"complexity": [1, 10],
"computed-property-spacing": [0, "never"],
"consistent-return": 0,
"consistent-this": [0, "that"],
"constructor-super": 1,
"curly": [1, "all"],
"default-case": 1,
"dot-location": [1, "property"],
"dot-notation": [1, { "allowKeywords": true }],
"eol-last": 0,
"eqeqeq": 1,
"func-names": 0,
"func-style": [0, "declaration"],
"generator-star-spacing": 0,
"guard-for-in": 1,
"handle-callback-err": 2,
"id-length": [2, {"min": 3, "max": 25, "exceptions":["x", "y", "i", "j", "ex", "up"]}],
"indent": [1, 2, {"SwitchCase": 1}],
"init-declarations": 0,
"key-spacing": [1, { "beforeColon": false, "afterColon": true }],
"keyword-spacing": 1,
"lines-around-comment": 0,
"max-depth": [1, 4],
"max-len": [1, 160, 2],
"max-nested-callbacks": [1, 2],
"max-params": [1, 5],
"max-statements": [1, 30],
"new-cap": 1,
"new-parens": 1,
"newline-after-var": 0,
"object-curly-spacing": [1, "always"],
"object-shorthand": 0,
"one-var": [1, {
"initialized": "never"
}],
"operator-assignment": [0, "always"],
"operator-linebreak": [1, "after"],
"padded-blocks": 0,
"prefer-const": 0,
"prefer-spread": 0,
"prefer-reflect": 0,
"quote-props": 0,
"quotes": [1, "single"],
"radix": 0,
"id-match": 0,
"require-yield": 0,
"semi": [1, "always"],
"semi-spacing": [1, {"before": false, "after": true}],
"sort-vars": 0,
"space-before-blocks": [1, "always"],
"space-before-function-paren": [1, "never"],
"space-in-parens": [1, "never"],
"space-infix-ops": 1,
"space-unary-ops": [1, { "words": true, "nonwords": false }],
"spaced-comment": 0,
"strict": [2, "global"],
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"vars-on-top": 0,
"wrap-iife": 0,
"wrap-regex": 0,
"yoda": [0, "never"]
},
"env": { "env": {
"node": true, "node": true,
"es6": true "es6": true
},
"rules": {
"no-var": "error"
},
"overrides": [
{
"files": ["test/*.js", "examples/*.js"],
"rules": {
"no-console": "off"
} }
}
]
} }

2
.gitignore vendored
View file

@ -2,3 +2,5 @@ node_modules
.idea .idea
coverage coverage
examples/*.png examples/*.png
browser\.js
.nyc_output

3
.prettierignore Normal file
View file

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

View file

@ -1,10 +1,9 @@
language: node_js language: node_js
before_script:
- if [ "$TRAVIS_NODE_VERSION" = "0.10" ]; then npm i node-zlib-backport; fi
after_success: after_success:
- if [ "$TRAVIS_NODE_VERSION" = "5" ]; then npm run coverage && npm i coveralls@2 && npm run coveralls; fi - if [ "$TRAVIS_NODE_VERSION" = "10" ]; then yarn coverage && yarn codecov; fi
node_js: node_js:
- "0.10" - "10"
- "0.12" - "12"
- "4" branches:
- "5" only:
- master

321
README.md
View file

@ -1,52 +1,57 @@
[![Build Status](https://travis-ci.org/lukeapage/pngjs.svg?branch=master)](https://travis-ci.org/lukeapage/pngjs) [![Build status](https://ci.appveyor.com/api/projects/status/tb8418jql1trkntd/branch/master?svg=true)](https://ci.appveyor.com/project/lukeapage/pngjs2/branch/master) [![Coverage Status](https://coveralls.io/repos/lukeapage/pngjs2/badge.svg?branch=master&service=github)](https://coveralls.io/github/lukeapage/pngjs2?branch=master) [![npm version](https://badge.fury.io/js/pngjs.svg)](http://badge.fury.io/js/pngjs) [![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)
pngjs # pngjs
========
Simple PNG encoder/decoder for Node.js with no dependencies. *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. 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 1,2,4 & 16 bit files
* Support for reading interlace files - Support for reading interlace files
* Support for reading `tTRNS` transparent colours - Support for reading `tTRNS` transparent colours
* Support for writing colortype 2 (RGB) and colortype 6 (RGBA) - Support for writing colortype 0 (grayscale), colortype 2 (RGB), colortype 4 (grayscale alpha) and colortype 6 (RGBA)
* Sync interface as well as async - Sync interface as well as async
* API compatible with pngjs and node-pngjs - API compatible with pngjs and node-pngjs
Known lack of support for: Known lack of support for:
* Extended PNG e.g. Animation - Extended PNG e.g. Animation
* Writing in different formats, colortype 0 (greyscale), colortype 3 (indexed color), colortype 4 (greyscale with alpha) - Writing in colortype 3 (indexed color)
* Synchronous write
Requirements # Table of Contents
============
* Async - Node.js 0.10 / 0.12 / IO.js - [Requirements](#requirements)
* Sync - Node.js 0.12 / IO.js (0.10 with [node-zlib-backport](https://www.npmjs.com/package/node-zlib-backport) dependency) - [Comparison Table](#comparison-table)
- [Tests](#tests)
- [Installation](#installation)
- [Browser](#browser)
- [Example](#example)
- [Async API](#async-api)
- [Sync API](#sync-api)
- [Changelog](#changelog)
Comparison Table # 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
| 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: Native C++ node decoders:
* png
* png-sync (sync version of above)
* pixel-png
* png-img
Tests - 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. 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.
@ -54,72 +59,84 @@ To run the tests, fetch the repo (tests are not distributed via npm) and install
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. 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.
In addition we use a tolerance of 3 for 16 bit images in PhantomJS because PhantomJS seems to have non-compliant rules for downscaling 16 bit images. # Installation
Installation
===============
``` ```
$ npm install pngjs --save $ 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')
.pipe(new PNG({
filterType: 4
}))
.on('parsed', function() {
fs.createReadStream("in.png")
.pipe(
new PNG({
filterType: 4,
})
)
.on("parsed", function () {
for (var y = 0; y < this.height; y++) { for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) { for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x) << 2; var idx = (this.width * y + x) << 2;
// invert color // invert color
this.data[idx] = 255 - this.data[idx]; this.data[idx] = 255 - this.data[idx];
this.data[idx+1] = 255 - this.data[idx+1]; this.data[idx + 1] = 255 - this.data[idx + 1];
this.data[idx+2] = 255 - this.data[idx+2]; this.data[idx + 2] = 255 - this.data[idx + 2];
// and reduce opacity // and reduce opacity
this.data[idx+3] = this.data[idx+3] >> 1; this.data[idx + 3] = this.data[idx + 3] >> 1;
} }
} }
this.pack().pipe(fs.createWriteStream('out.png')); this.pack().pipe(fs.createWriteStream("out.png"));
}); });
``` ```
For more examples see `examples` folder. For more examples see `examples` folder.
Async API # 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.
## 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`) - `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. 2 = color, no alpha, 6 = color & alpha. Default currently 6, but in the future may calculate best mode. - `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.
- `inputHasAlpha` - whether the input bitmap has 4 bits per pixel (rgb and alpha) or 3 (rgb - no alpha). - `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 - `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) 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
@ -127,17 +144,17 @@ Image's header has been parsed, metadata contains this information:
- `alpha` image contains alpha channel - `alpha` image contains alpha channel
- `interlace` image is interlaced - `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. Can be `String` or `Buffer`. 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
@ -146,36 +163,39 @@ two arguments `(err, data)`.
Returns `this` for method chaining. Returns `this` for method chaining.
#### Example #### Example
```js ```js
new PNG({ filterType:4 }).parse( imageData, function(error, data) new PNG({ filterType: 4 }).parse(imageData, function (error, data) {
{ console.log(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 a rectangle of pixels from current (i.e. the source) 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`: For example, the following code copies the top-left 100x50 px of `in.png` into dst and writes it to `out.png`:
```js ```js
var dst = new PNG({width: 100, height: 50}); var dst = new PNG({ width: 100, height: 50 });
fs.createReadStream('in.png') fs.createReadStream("in.png")
.pipe(new PNG()) .pipe(new PNG())
.on('parsed', function() { .on("parsed", function () {
this.bitblt(dst, 0, 0, 100, 50, 0, 0); this.bitblt(dst, 0, 0, 100, 50, 0, 0);
dst.pack().pipe(fs.createWriteStream('out.png')); dst.pack().pipe(fs.createWriteStream("out.png"));
}); });
``` ```
### Property: adjustGamma() ### 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. 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. In tests against PNG suite it compared 100% with chrome on all 8 bit and below images. On IE there were some differences.
@ -183,50 +203,53 @@ In tests against PNG suite it compared 100% with chrome on all 8 bit and below i
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. 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 ```js
fs.createReadStream('in.png') fs.createReadStream("in.png")
.pipe(new PNG()) .pipe(new PNG())
.on('parsed', function() { .on("parsed", function () {
this.adjustGamma(); this.adjustGamma();
this.pack().pipe(fs.createWriteStream('out.png')); 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)
## Packing a PNG and removing alpga (RGBA to RGB) ## Packing a PNG and removing alpha (RGBA to RGB)
When removing the alpha channel from an image, there needs to be a background color to correctly When removing the alpha channel from an image, there needs to be a background color to correctly
convert each pixel's transparency to the appropriate RGB value. By default, pngjs will flatten 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: the image against a white background. You can override this in the options:
```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(
new PNG({
colorType: 2, colorType: 2,
bgColor: { bgColor: {
red: 0, red: 0,
green: 255, green: 255,
blue: 0 blue: 0,
} },
})) })
.on('parsed', function() { )
this.pack().pipe(fs.createWriteStream('out.png')); .on("parsed", function () {
this.pack().pipe(fs.createWriteStream("out.png"));
}); });
``` ```
@ -250,7 +273,8 @@ Take a PNG image and returns a buffer. The properties on the image include the m
``` ```
var data = fs.readFileSync('in.png'); var data = fs.readFileSync('in.png');
var png = PNG.sync.read(data); var png = PNG.sync.read(data);
var buffer = PNG.sync.write(png); var options = { colorType: 6 };
var buffer = PNG.sync.write(png, options);
fs.writeFileSync('out.png', buffer); fs.writeFileSync('out.png', buffer);
``` ```
@ -264,58 +288,121 @@ var png = PNG.sync.read(data);
PNG.adjustGamma(png); PNG.adjustGamma(png);
``` ```
# Changelog
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 ### 2.3.0 - 22/04/2016
- Support for sync in node 0.10
- Support for sync in node 0.10
### 2.2.0 - 04/12/2015 ### 2.2.0 - 04/12/2015
- Add sync write api
- Fix newfile example - Add sync write api
- Correct comparison table - Fix newfile example
- Correct comparison table
### 2.1.0 - 28/10/2015 ### 2.1.0 - 28/10/2015
- rename package to pngjs
- added 'bgColor' option - rename package to pngjs
- added 'bgColor' option
### 2.0.0 - 08/10/2015 ### 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 - 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 ### 1.2.0 - 13/09/2015
- support passing colorType to write PNG's and writing bitmaps without alpha information
- support passing colorType to write PNG's and writing bitmaps without alpha information
### 1.1.0 - 07/09/2015 ### 1.1.0 - 07/09/2015
- support passing a deflate factory for controlled compression
- support passing a deflate factory for controlled compression
### 1.0.2 - 22/08/2015 ### 1.0.2 - 22/08/2015
- Expose all PNG creation info
- Expose all PNG creation info
### 1.0.1 - 21/08/2015 ### 1.0.1 - 21/08/2015
- Fix non square interlaced files
- Fix non square interlaced files
### 1.0.0 - 08/08/2015 ### 1.0.0 - 08/08/2015
- More tests
- source linted - More tests
- maintainability refactorings - source linted
- async API - exceptions in reading now emit warnings - maintainability refactorings
- documentation improvement - sync api now documented, adjustGamma documented - async API - exceptions in reading now emit warnings
- breaking change - gamma chunk is now written. previously a read then write would destroy gamma information, now it is persisted. - 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 ### 0.0.3 - 03/08/2015
- Error handling fixes
- ignore files for smaller npm footprint - Error handling fixes
- ignore files for smaller npm footprint
### 0.0.2 - 02/08/2015 ### 0.0.2 - 02/08/2015
- Bugfixes to interlacing, support for transparent colours
- Bugfixes to interlacing, support for transparent colours
### 0.0.1 - 02/08/2015 ### 0.0.1 - 02/08/2015
- Initial release, see pngjs for older changelog.
License - Initial release, see pngjs for older changelog.
=========
# License
(The MIT License) (The MIT License)

View file

@ -4,8 +4,8 @@ clone_depth: 10
environment: environment:
matrix: matrix:
- nodejs_version: '4' - nodejs_version: "10"
- nodejs_version: '' - nodejs_version: "12"
install: install:
- ps: Install-Product node $env:nodejs_version - ps: Install-Product node $env:nodejs_version
@ -22,3 +22,7 @@ cache:
matrix: matrix:
fast_finish: true 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,25 +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('../lib/png').PNG; // note require('pngjs') outside this project .pipe(new PNG({}))
.on("parsed", function () {
fs.createReadStream('test/in/basi0g01.png') for (let y = 0; y < this.height; y++) {
.pipe(new PNG({ for (let x = 0; x < this.width; x++) {
})) let idx = (this.width * y + x) << 2;
.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;
// invert color // invert color
this.data[idx] = 255 - this.data[idx]; this.data[idx] = 255 - this.data[idx];
this.data[idx+1] = 255 - this.data[idx+1]; this.data[idx + 1] = 255 - this.data[idx + 1];
this.data[idx+2] = 255 - this.data[idx+2]; this.data[idx + 2] = 255 - this.data[idx + 2];
// and reduce opacity // and reduce opacity
this.data[idx+3] = this.data[idx+3] >> 1; this.data[idx + 3] = this.data[idx + 3] >> 1;
} }
} }
this.pack().pipe(fs.createWriteStream('out.png')); this.pack().pipe(fs.createWriteStream("out.png"));
}); });

View file

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

View file

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

View file

@ -1,26 +1,24 @@
#!/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({
var png = new PNG({ filterType: -1,
filterType: -1
}), }),
src = fs.createReadStream(process.argv[2]), src = fs.createReadStream(process.argv[2]),
dst = fs.createWriteStream(process.argv[3] || 'out.png'); dst = fs.createWriteStream(process.argv[3] || "out.png");
png.on("parsed", function () {
for (let y = 0; y < png.height; y++) {
for (let x = 0; x < png.width; x++) {
let idx = (png.width * y + x) << 2;
png.on('parsed', function() { if (
Math.abs(png.data[idx] - png.data[idx + 1]) <= 1 &&
for (var y = 0; y < png.height; y++) { Math.abs(png.data[idx + 1] - png.data[idx + 2]) <= 1
for (var x = 0; x < png.width; x++) { )
var idx = (png.width * y + x) << 2; png.data[idx] = png.data[idx + 1] = png.data[idx + 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];
} }
} }

View file

@ -1,19 +1,18 @@
#!/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 srcFname = process.argv[2],
var srcFname = process.argv[2], dstFname = process.argv[3] || "out.png";
dstFname = process.argv[3] || 'out.png';
// Read a PNG file // Read a PNG file
var data = fs.readFileSync(srcFname); let data = fs.readFileSync(srcFname);
// Parse it // Parse it
var png = PNG.sync.read(data, { let png = PNG.sync.read(data, {
filterType: -1 filterType: -1,
}); });
// Pack it back into a PNG data // Pack it back into a PNG data
var buff = PNG.sync.write(png); let buff = PNG.sync.write(png);
// Write a PNG file // Write a PNG file
fs.writeFileSync(dstFname, buff); fs.writeFileSync(dstFname, buff);

View file

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

View file

@ -1,58 +1,150 @@
'use strict'; import constants from "./constants.js";
var constants = require('./constants'); export default function (dataIn, width, height, options) {
let outHasAlpha =
module.exports = function(data, width, height, options) { [constants.COLORTYPE_COLOR_ALPHA, constants.COLORTYPE_ALPHA].indexOf(
var outHasAlpha = options.colorType === constants.COLORTYPE_COLOR_ALPHA; options.colorType
if (options.inputHasAlpha && outHasAlpha) { ) !== -1;
return data; 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;
} }
if (!options.inputHasAlpha && !outHasAlpha) {
return data;
} }
var outBpp = outHasAlpha ? 4 : 3; // map to a UInt16 array if data is 16bit, fix endianness below
var outData = new Buffer(width * height * outBpp); let data = options.bitDepth !== 16 ? dataIn : new Uint16Array(dataIn.buffer);
var inBpp = options.inputHasAlpha ? 4 : 3;
var inIndex = 0;
var outIndex = 0;
var bgColor = options.bgColor || {}; 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) { if (bgColor.red === undefined) {
bgColor.red = 255; bgColor.red = maxValue;
} }
if (bgColor.green === undefined) { if (bgColor.green === undefined) {
bgColor.green = 255; bgColor.green = maxValue;
} }
if (bgColor.blue === undefined) { if (bgColor.blue === undefined) {
bgColor.blue = 255; bgColor.blue = maxValue;
} }
for (var y = 0; y < height; y++) { function getRGBA() {
for (var x = 0; x < width; x++) { let red;
var red = data[inIndex]; let green;
var green = data[inIndex + 1]; let blue;
var blue = data[inIndex + 2]; let alpha = maxValue;
switch (options.inputColorType) {
var alpha; case constants.COLORTYPE_COLOR_ALPHA:
if (options.inputHasAlpha) {
alpha = data[inIndex + 3]; alpha = data[inIndex + 3];
if (!outHasAlpha) { red = data[inIndex];
alpha /= 255; green = data[inIndex + 1];
red = Math.min(Math.max(Math.round((1 - alpha) * bgColor.red + alpha * red), 0), 255); blue = data[inIndex + 2];
green = Math.min(Math.max(Math.round((1 - alpha) * bgColor.green + alpha * green), 0), 255); break;
blue = Math.min(Math.max(Math.round((1 - alpha) * bgColor.blue + alpha * blue), 0), 255); case constants.COLORTYPE_COLOR:
} red = data[inIndex];
} green = data[inIndex + 1];
else { blue = data[inIndex + 2];
alpha = 255; 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"
);
} }
outData[outIndex] = red; if (options.inputHasAlpha) {
outData[outIndex + 1] = green; if (!outHasAlpha) {
outData[outIndex + 2] = blue; 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) { if (outHasAlpha) {
outData[outIndex + 3] = alpha; 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; inIndex += inBpp;

View file

@ -1,11 +1,7 @@
'use strict'; let util = require("util");
let Stream = require("stream");
let ChunkStream = function () {
var util = require('util');
var Stream = require('stream');
var ChunkStream = module.exports = function() {
Stream.call(this); Stream.call(this);
this._buffers = []; this._buffers = [];
@ -14,45 +10,46 @@ var ChunkStream = module.exports = function() {
this._reads = []; this._reads = [];
this._paused = false; this._paused = false;
this._encoding = 'utf8'; this._encoding = "utf8";
this.writable = true; this.writable = true;
}; };
export default ChunkStream;
util.inherits(ChunkStream, Stream); util.inherits(ChunkStream, Stream);
ChunkStream.prototype.read = function (length, callback) {
ChunkStream.prototype.read = function(length, callback) {
this._reads.push({ this._reads.push({
length: Math.abs(length), // if length < 0 then at most this length length: Math.abs(length), // if length < 0 then at most this length
allowLess: length < 0, allowLess: length < 0,
func: callback func: callback,
}); });
process.nextTick(function() { 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) { if (!this.writable) {
this.emit('error', new Error('Stream not writable')); this.emit("error", new Error("Stream not writable"));
return false; return false;
} }
var dataBuffer; let dataBuffer;
if (Buffer.isBuffer(data)) { if (Buffer.isBuffer(data)) {
dataBuffer = data; dataBuffer = data;
} } else {
else { dataBuffer = Buffer.from(data, encoding || this._encoding);
dataBuffer = new Buffer(data, encoding || this._encoding);
} }
this._buffers.push(dataBuffer); this._buffers.push(dataBuffer);
@ -68,8 +65,7 @@ ChunkStream.prototype.write = function(data, encoding) {
return this.writable && !this._paused; return this.writable && !this._paused;
}; };
ChunkStream.prototype.end = function(data, encoding) { ChunkStream.prototype.end = function (data, encoding) {
if (data) { if (data) {
this.write(data, encoding); this.write(data, encoding);
} }
@ -84,8 +80,7 @@ ChunkStream.prototype.end = function(data, encoding) {
// enqueue or handle end // enqueue or handle end
if (this._buffers.length === 0) { if (this._buffers.length === 0) {
this._end(); this._end();
} } else {
else {
this._buffers.push(null); this._buffers.push(null);
this._process(); this._process();
} }
@ -93,19 +88,15 @@ ChunkStream.prototype.end = function(data, encoding) {
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) { if (this._reads.length > 0) {
this.emit('error', this.emit("error", new Error("Unexpected end of input"));
new Error('There are some read requests waitng on finished stream')
);
} }
this.destroy(); this.destroy();
}; };
ChunkStream.prototype.destroy = function() { ChunkStream.prototype.destroy = function () {
if (!this._buffers) { if (!this._buffers) {
return; return;
} }
@ -114,26 +105,23 @@ ChunkStream.prototype.destroy = function() {
this._reads = null; this._reads = null;
this._buffers = null; this._buffers = null;
this.emit('close'); this.emit("close");
}; };
ChunkStream.prototype._processReadAllowingLess = function(read) { ChunkStream.prototype._processReadAllowingLess = function (read) {
// ok there is any data so that we can satisfy this request // ok there is any data so that we can satisfy this request
this._reads.shift(); // == read this._reads.shift(); // == read
// first we need to peek into first buffer // first we need to peek into first buffer
var smallerBuf = this._buffers[0]; let smallerBuf = this._buffers[0];
// ok there is more data than we need // ok there is more data than we need
if (smallerBuf.length > read.length) { if (smallerBuf.length > read.length) {
this._buffered -= read.length; this._buffered -= read.length;
this._buffers[0] = smallerBuf.slice(read.length); this._buffers[0] = smallerBuf.slice(read.length);
read.func.call(this, smallerBuf.slice(0, read.length)); read.func.call(this, smallerBuf.slice(0, read.length));
} else {
}
else {
// ok this is less than maximum length so use it all // ok this is less than maximum length so use it all
this._buffered -= smallerBuf.length; this._buffered -= smallerBuf.length;
this._buffers.shift(); // == smallerBuf this._buffers.shift(); // == smallerBuf
@ -142,18 +130,17 @@ ChunkStream.prototype._processReadAllowingLess = function(read) {
} }
}; };
ChunkStream.prototype._processRead = function(read) { ChunkStream.prototype._processRead = function (read) {
this._reads.shift(); // == read this._reads.shift(); // == read
var pos = 0; let pos = 0;
var count = 0; let count = 0;
var data = new Buffer(read.length); let data = Buffer.alloc(read.length);
// create buffer for all data // create buffer for all data
while (pos < read.length) { while (pos < read.length) {
let buf = this._buffers[count++];
var buf = this._buffers[count++]; let len = Math.min(buf.length, read.length - pos);
var len = Math.min(buf.length, read.length - pos);
buf.copy(data, pos, 0, len); buf.copy(data, pos, 0, len);
pos += len; pos += len;
@ -174,36 +161,30 @@ ChunkStream.prototype._processRead = function(read) {
read.func.call(this, data); read.func.call(this, data);
}; };
ChunkStream.prototype._process = function() { ChunkStream.prototype._process = function () {
try { try {
// 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) // read any data (but no more than length)
if (read.allowLess) { if (read.allowLess) {
this._processReadAllowingLess(read); this._processReadAllowingLess(read);
} else if (this._buffered >= read.length) {
}
else if (this._buffered >= read.length) {
// ok we can meet some expectations // ok we can meet some expectations
this._processRead(read); this._processRead(read);
} } else {
else {
// not enought data to satisfy first request in queue // not enought data to satisfy first request in queue
// so we need to wait for more // so we need to wait for more
break; break;
} }
} }
if (this._buffers && this._buffers.length > 0 && this._buffers[0] === null) { if (this._buffers && !this.writable) {
this._end(); this._end();
} }
} } catch (ex) {
catch (ex) { this.emit("error", ex);
this.emit('error', ex);
} }
}; };

View file

@ -1,8 +1,4 @@
'use strict'; export default {
module.exports = {
PNG_SIGNATURE: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], PNG_SIGNATURE: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
TYPE_IHDR: 0x49484452, TYPE_IHDR: 0x49484452,
@ -27,8 +23,8 @@ module.exports = {
2: 3, 2: 3,
3: 1, 3: 1,
4: 2, 4: 2,
6: 4 6: 4,
}, },
GAMMA_DIVISION: 100000 GAMMA_DIVISION: 100000,
}; };

View file

@ -1,43 +1,39 @@
'use strict'; let crcTable = [];
var crcTable = []; (function () {
for (let i = 0; i < 256; i++) {
(function() { let currentCrc = i;
for (var i = 0; i < 256; i++) { for (let j = 0; j < 8; j++) {
var currentCrc = i;
for (var j = 0; j < 8; j++) {
if (currentCrc & 1) { if (currentCrc & 1) {
currentCrc = 0xedb88320 ^ (currentCrc >>> 1); currentCrc = 0xedb88320 ^ (currentCrc >>> 1);
} } else {
else {
currentCrc = currentCrc >>> 1; currentCrc = currentCrc >>> 1;
} }
} }
crcTable[i] = currentCrc; crcTable[i] = currentCrc;
} }
}()); })();
var CrcCalculator = module.exports = function() { let CrcCalculator = function () {
this._crc = -1; this._crc = -1;
}; };
CrcCalculator.prototype.write = function(data) { export default CrcCalculator;
for (var i = 0; i < data.length; i++) { CrcCalculator.prototype.write = function (data) {
for (let i = 0; i < data.length; i++) {
this._crc = crcTable[(this._crc ^ data[i]) & 0xff] ^ (this._crc >>> 8); this._crc = crcTable[(this._crc ^ data[i]) & 0xff] ^ (this._crc >>> 8);
} }
return true; return true;
}; };
CrcCalculator.prototype.crc32 = function() { CrcCalculator.prototype.crc32 = function () {
return this._crc ^ -1; return this._crc ^ -1;
}; };
CrcCalculator.crc32 = function (buf) {
CrcCalculator.crc32 = function(buf) { let crc = -1;
for (let i = 0; i < buf.length; i++) {
var crc = -1;
for (var i = 0; i < buf.length; i++) {
crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8); crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
} }
return crc ^ -1; return crc ^ -1;

View file

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

View file

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

View file

@ -1,20 +1,15 @@
'use strict'; import SyncReader from "./sync-reader.js";
import Filter from "./filter-parse.js";
var SyncReader = require('./sync-reader'); export default function (inBuffer, bitmapInfo) {
var Filter = require('./filter-parse'); let outBuffers = [];
let reader = new SyncReader(inBuffer);
let filter = new Filter(bitmapInfo, {
exports.process = function(inBuffer, bitmapInfo) {
var outBuffers = [];
var reader = new SyncReader(inBuffer);
var filter = new Filter(bitmapInfo, {
read: reader.read.bind(reader), read: reader.read.bind(reader),
write: function(bufferPart) { write: function (bufferPart) {
outBuffers.push(bufferPart); outBuffers.push(bufferPart);
}, },
complete: function() { complete: function () {},
}
}); });
filter.start(); filter.start();

View file

@ -1,23 +1,20 @@
'use strict'; import interlaceUtils from "./interlace.js";
import paethPredictor from "./paeth-predictor.js";
var interlaceUtils = require('./interlace');
var paethPredictor = require('./paeth-predictor');
function getByteWidth(width, bpp, depth) { function getByteWidth(width, bpp, depth) {
var byteWidth = width * bpp; let byteWidth = width * bpp;
if (depth !== 8) { if (depth !== 8) {
byteWidth = Math.ceil(byteWidth / (8 / depth)); byteWidth = Math.ceil(byteWidth / (8 / depth));
} }
return byteWidth; return byteWidth;
} }
var Filter = module.exports = function(bitmapInfo, dependencies) { let Filter = function (bitmapInfo, dependencies) {
let width = bitmapInfo.width;
var width = bitmapInfo.width; let height = bitmapInfo.height;
var height = bitmapInfo.height; let interlace = bitmapInfo.interlace;
var interlace = bitmapInfo.interlace; let bpp = bitmapInfo.bpp;
var bpp = bitmapInfo.bpp; let depth = bitmapInfo.depth;
var depth = bitmapInfo.depth;
this.read = dependencies.read; this.read = dependencies.read;
this.write = dependencies.write; this.write = dependencies.write;
@ -26,20 +23,19 @@ var Filter = module.exports = function(bitmapInfo, dependencies) {
this._imageIndex = 0; this._imageIndex = 0;
this._images = []; this._images = [];
if (interlace) { if (interlace) {
var passes = interlaceUtils.getImagePasses(width, height); let passes = interlaceUtils.getImagePasses(width, height);
for (var i = 0; i < passes.length; i++) { for (let i = 0; i < passes.length; i++) {
this._images.push({ this._images.push({
byteWidth: getByteWidth(passes[i].width, bpp, depth), byteWidth: getByteWidth(passes[i].width, bpp, depth),
height: passes[i].height, height: passes[i].height,
lineIndex: 0 lineIndex: 0,
}); });
} }
} } else {
else {
this._images.push({ this._images.push({
byteWidth: getByteWidth(width, bpp, depth), byteWidth: getByteWidth(width, bpp, depth),
height: height, height: height,
lineIndex: 0 lineIndex: 0,
}); });
} }
@ -49,86 +45,98 @@ var Filter = module.exports = function(bitmapInfo, dependencies) {
// a pixel rather than just a different byte part. However if we are sub byte, we ignore. // a pixel rather than just a different byte part. However if we are sub byte, we ignore.
if (depth === 8) { if (depth === 8) {
this._xComparison = bpp; this._xComparison = bpp;
} } else if (depth === 16) {
else if (depth === 16) {
this._xComparison = bpp * 2; this._xComparison = bpp * 2;
} } else {
else {
this._xComparison = 1; this._xComparison = 1;
} }
}; };
Filter.prototype.start = function() { export default Filter;
this.read(this._images[this._imageIndex].byteWidth + 1, this._reverseFilterLine.bind(this));
Filter.prototype.start = function () {
this.read(
this._images[this._imageIndex].byteWidth + 1,
this._reverseFilterLine.bind(this)
);
}; };
Filter.prototype._unFilterType1 = function(rawData, unfilteredLine, byteWidth) { Filter.prototype._unFilterType1 = function (
rawData,
unfilteredLine,
byteWidth
) {
let xComparison = this._xComparison;
let xBiggerThan = xComparison - 1;
var xComparison = this._xComparison; for (let x = 0; x < byteWidth; x++) {
var xBiggerThan = xComparison - 1; let rawByte = rawData[1 + x];
let f1Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
for (var x = 0; x < byteWidth; x++) {
var rawByte = rawData[1 + x];
var f1Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
unfilteredLine[x] = rawByte + f1Left; unfilteredLine[x] = rawByte + f1Left;
} }
}; };
Filter.prototype._unFilterType2 = function(rawData, unfilteredLine, byteWidth) { Filter.prototype._unFilterType2 = function (
rawData,
unfilteredLine,
byteWidth
) {
let lastLine = this._lastLine;
var lastLine = this._lastLine; for (let x = 0; x < byteWidth; x++) {
let rawByte = rawData[1 + x];
for (var x = 0; x < byteWidth; x++) { let f2Up = lastLine ? lastLine[x] : 0;
var rawByte = rawData[1 + x];
var f2Up = lastLine ? lastLine[x] : 0;
unfilteredLine[x] = rawByte + f2Up; unfilteredLine[x] = rawByte + f2Up;
} }
}; };
Filter.prototype._unFilterType3 = function(rawData, unfilteredLine, byteWidth) { Filter.prototype._unFilterType3 = function (
rawData,
unfilteredLine,
byteWidth
) {
let xComparison = this._xComparison;
let xBiggerThan = xComparison - 1;
let lastLine = this._lastLine;
var xComparison = this._xComparison; for (let x = 0; x < byteWidth; x++) {
var xBiggerThan = xComparison - 1; let rawByte = rawData[1 + x];
var lastLine = this._lastLine; let f3Up = lastLine ? lastLine[x] : 0;
let f3Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
for (var x = 0; x < byteWidth; x++) { let f3Add = Math.floor((f3Left + f3Up) / 2);
var rawByte = rawData[1 + x];
var f3Up = lastLine ? lastLine[x] : 0;
var f3Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
var f3Add = Math.floor((f3Left + f3Up) / 2);
unfilteredLine[x] = rawByte + f3Add; unfilteredLine[x] = rawByte + f3Add;
} }
}; };
Filter.prototype._unFilterType4 = function(rawData, unfilteredLine, byteWidth) { Filter.prototype._unFilterType4 = function (
rawData,
unfilteredLine,
byteWidth
) {
let xComparison = this._xComparison;
let xBiggerThan = xComparison - 1;
let lastLine = this._lastLine;
var xComparison = this._xComparison; for (let x = 0; x < byteWidth; x++) {
var xBiggerThan = xComparison - 1; let rawByte = rawData[1 + x];
var lastLine = this._lastLine; let f4Up = lastLine ? lastLine[x] : 0;
let f4Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
for (var x = 0; x < byteWidth; x++) { let f4UpLeft = x > xBiggerThan && lastLine ? lastLine[x - xComparison] : 0;
var rawByte = rawData[1 + x]; let f4Add = paethPredictor(f4Left, f4Up, f4UpLeft);
var f4Up = lastLine ? lastLine[x] : 0;
var f4Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
var f4UpLeft = x > xBiggerThan && lastLine ? lastLine[x - xComparison] : 0;
var f4Add = paethPredictor(f4Left, f4Up, f4UpLeft);
unfilteredLine[x] = rawByte + f4Add; unfilteredLine[x] = rawByte + f4Add;
} }
}; };
Filter.prototype._reverseFilterLine = function(rawData) { Filter.prototype._reverseFilterLine = function (rawData) {
let filter = rawData[0];
var filter = rawData[0]; let unfilteredLine;
var unfilteredLine; let currentImage = this._images[this._imageIndex];
var currentImage = this._images[this._imageIndex]; let byteWidth = currentImage.byteWidth;
var byteWidth = currentImage.byteWidth;
if (filter === 0) { if (filter === 0) {
unfilteredLine = rawData.slice(1, byteWidth + 1); unfilteredLine = rawData.slice(1, byteWidth + 1);
} } else {
else { unfilteredLine = Buffer.alloc(byteWidth);
unfilteredLine = new Buffer(byteWidth);
switch (filter) { switch (filter) {
case 1: case 1:
@ -144,7 +152,7 @@ Filter.prototype._reverseFilterLine = function(rawData) {
this._unFilterType4(rawData, unfilteredLine, byteWidth); this._unFilterType4(rawData, unfilteredLine, byteWidth);
break; break;
default: default:
throw new Error('Unrecognised filter type - ' + filter); throw new Error("Unrecognised filter type - " + filter);
} }
} }
@ -155,16 +163,14 @@ Filter.prototype._reverseFilterLine = function(rawData) {
this._lastLine = null; this._lastLine = null;
this._imageIndex++; this._imageIndex++;
currentImage = this._images[this._imageIndex]; currentImage = this._images[this._imageIndex];
} } else {
else {
this._lastLine = unfilteredLine; this._lastLine = unfilteredLine;
} }
if (currentImage) { if (currentImage) {
// read, using the byte width that may be from the new current image // read, using the byte width that may be from the new current image
this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this)); this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this));
} } else {
else {
this._lastLine = null; this._lastLine = null;
this.complete(); this.complete();
} }

View file

@ -1,17 +1,15 @@
'use strict';
function dePalette(indata, outdata, width, height, palette) { function dePalette(indata, outdata, width, height, palette) {
var pxPos = 0; let pxPos = 0;
// use values from palette // use values from palette
for (var y = 0; y < height; y++) { for (let y = 0; y < height; y++) {
for (var x = 0; x < width; x++) { for (let x = 0; x < width; x++) {
var color = palette[indata[pxPos]]; let color = palette[indata[pxPos]];
if (!color) { if (!color) {
throw new Error('index ' + indata[pxPos] + ' not in palette'); throw new Error("index " + indata[pxPos] + " not in palette");
} }
for (var i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
outdata[pxPos + i] = color[i]; outdata[pxPos + i] = color[i];
} }
pxPos += 4; pxPos += 4;
@ -20,21 +18,24 @@ function dePalette(indata, outdata, width, height, palette) {
} }
function replaceTransparentColor(indata, outdata, width, height, transColor) { function replaceTransparentColor(indata, outdata, width, height, transColor) {
var pxPos = 0; let pxPos = 0;
for (var y = 0; y < height; y++) { for (let y = 0; y < height; y++) {
for (var x = 0; x < width; x++) { for (let x = 0; x < width; x++) {
var makeTrans = false; let makeTrans = false;
if (transColor.length === 1) { if (transColor.length === 1) {
if (transColor[0] === indata[pxPos]) { if (transColor[0] === indata[pxPos]) {
makeTrans = true; makeTrans = true;
} }
} } else if (
else if (transColor[0] === indata[pxPos] && transColor[1] === indata[pxPos + 1] && transColor[2] === indata[pxPos + 2]) { transColor[0] === indata[pxPos] &&
transColor[1] === indata[pxPos + 1] &&
transColor[2] === indata[pxPos + 2]
) {
makeTrans = true; makeTrans = true;
} }
if (makeTrans) { if (makeTrans) {
for (var i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
outdata[pxPos + i] = 0; outdata[pxPos + i] = 0;
} }
} }
@ -44,35 +45,36 @@ function replaceTransparentColor(indata, outdata, width, height, transColor) {
} }
function scaleDepth(indata, outdata, width, height, depth) { function scaleDepth(indata, outdata, width, height, depth) {
var maxOutSample = 255; let maxOutSample = 255;
var maxInSample = Math.pow(2, depth) - 1; let maxInSample = Math.pow(2, depth) - 1;
var pxPos = 0; let pxPos = 0;
for (var y = 0; y < height; y++) { for (let y = 0; y < height; y++) {
for (var x = 0; x < width; x++) { for (let x = 0; x < width; x++) {
for (var i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
outdata[pxPos + i] = Math.floor((indata[pxPos + i] * maxOutSample) / maxInSample + 0.5); outdata[pxPos + i] = Math.floor(
(indata[pxPos + i] * maxOutSample) / maxInSample + 0.5
);
} }
pxPos += 4; pxPos += 4;
} }
} }
} }
module.exports = function(indata, imageData) { 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;
var depth = imageData.depth; let outdata = indata; // only different for 16 bits
var width = imageData.width;
var height = imageData.height;
var colorType = imageData.colorType;
var transColor = imageData.transColor;
var palette = imageData.palette;
var outdata = indata; // only different for 16 bits if (colorType === 3) {
// paletted
if (colorType === 3) { // paletted
dePalette(indata, outdata, width, height, palette); dePalette(indata, outdata, width, height, palette);
} } else {
else {
if (transColor) { if (transColor) {
replaceTransparentColor(indata, outdata, width, height, transColor); replaceTransparentColor(indata, outdata, width, height, transColor);
} }
@ -80,7 +82,7 @@ module.exports = function(indata, imageData) {
if (depth !== 8) { if (depth !== 8) {
// if we need to change the buffer size // if we need to change the buffer size
if (depth === 16) { if (depth === 16) {
outdata = new Buffer(width * height * 4); outdata = Buffer.alloc(width * height * 4);
} }
scaleDepth(indata, outdata, width, height, depth); scaleDepth(indata, outdata, width, height, depth);
} }

View file

@ -1,5 +1,3 @@
'use strict';
// Adam 7 // Adam 7
// 0 1 2 3 4 5 6 7 // 0 1 2 3 4 5 6 7
// 0 x 6 4 6 x 6 4 6 // 0 x 6 4 6 x 6 4 6
@ -11,61 +9,65 @@
// 6 5 6 5 6 5 6 5 6 // 6 5 6 5 6 5 6 5 6
// 7 7 7 7 7 7 7 7 7 // 7 7 7 7 7 7 7 7 7
let imagePasses = [
var imagePasses = [ {
{ // pass 1 - 1px // pass 1 - 1px
x: [0], x: [0],
y: [0] y: [0],
}, },
{ // pass 2 - 1px {
// pass 2 - 1px
x: [4], x: [4],
y: [0] y: [0],
}, },
{ // pass 3 - 2px {
// pass 3 - 2px
x: [0, 4], x: [0, 4],
y: [4] y: [4],
}, },
{ // pass 4 - 4px {
// pass 4 - 4px
x: [2, 6], x: [2, 6],
y: [0, 4] y: [0, 4],
}, },
{ // pass 5 - 8px {
// pass 5 - 8px
x: [0, 2, 4, 6], x: [0, 2, 4, 6],
y: [2, 6] y: [2, 6],
}, },
{ // pass 6 - 16px {
// pass 6 - 16px
x: [1, 3, 5, 7], x: [1, 3, 5, 7],
y: [0, 2, 4, 6] y: [0, 2, 4, 6],
}, },
{ // pass 7 - 32px {
// pass 7 - 32px
x: [0, 1, 2, 3, 4, 5, 6, 7], x: [0, 1, 2, 3, 4, 5, 6, 7],
y: [1, 3, 5, 7] y: [1, 3, 5, 7],
} },
]; ];
exports.getImagePasses = function(width, height) { export var getImagePasses = function (width, height) {
var images = []; let images = [];
var xLeftOver = width % 8; let xLeftOver = width % 8;
var yLeftOver = height % 8; let yLeftOver = height % 8;
var xRepeats = (width - xLeftOver) / 8; let xRepeats = (width - xLeftOver) / 8;
var yRepeats = (height - yLeftOver) / 8; let yRepeats = (height - yLeftOver) / 8;
for (var i = 0; i < imagePasses.length; i++) { for (let i = 0; i < imagePasses.length; i++) {
var pass = imagePasses[i]; let pass = imagePasses[i];
var passWidth = xRepeats * pass.x.length; let passWidth = xRepeats * pass.x.length;
var passHeight = yRepeats * pass.y.length; let passHeight = yRepeats * pass.y.length;
for (var j = 0; j < pass.x.length; j++) { for (let j = 0; j < pass.x.length; j++) {
if (pass.x[j] < xLeftOver) { if (pass.x[j] < xLeftOver) {
passWidth++; passWidth++;
} } else {
else {
break; break;
} }
} }
for (j = 0; j < pass.y.length; j++) { for (let j = 0; j < pass.y.length; j++) {
if (pass.y[j] < yLeftOver) { if (pass.y[j] < yLeftOver) {
passHeight++; passHeight++;
} } else {
else {
break; break;
} }
} }
@ -76,12 +78,21 @@ exports.getImagePasses = function(width, height) {
return images; return images;
}; };
exports.getInterlaceIterator = function(width) { export var getInterlaceIterator = function (width) {
return function(x, y, pass) { return function (x, y, pass) {
var outerXLeftOver = x % imagePasses[pass].x.length; let outerXLeftOver = x % imagePasses[pass].x.length;
var outerX = (((x - outerXLeftOver) / imagePasses[pass].x.length) * 8) + imagePasses[pass].x[outerXLeftOver]; let outerX =
var outerYLeftOver = y % imagePasses[pass].y.length; ((x - outerXLeftOver) / imagePasses[pass].x.length) * 8 +
var outerY = (((y - outerYLeftOver) / imagePasses[pass].y.length) * 8) + imagePasses[pass].y[outerYLeftOver]; imagePasses[pass].x[outerXLeftOver];
return (outerX * 4) + (outerY * width * 4); 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
}

View file

@ -1,14 +1,13 @@
'use strict'; let util = require("util");
let Stream = require("stream");
var util = require('util'); import constants from "./constants.js";
var Stream = require('stream'); import Packer from "./packer.js";
var constants = require('./constants');
var Packer = require('./packer');
var PackerAsync = module.exports = function(opt) { let PackerAsync = function (opt) {
Stream.call(this); Stream.call(this);
var options = opt || {}; let options = opt || {};
this._packer = new Packer(options); this._packer = new Packer(options);
this._deflate = this._packer.createDeflate(); this._deflate = this._packer.createDeflate();
@ -17,29 +16,36 @@ var PackerAsync = module.exports = function(opt) {
}; };
util.inherits(PackerAsync, Stream); util.inherits(PackerAsync, Stream);
export default PackerAsync;
PackerAsync.prototype.pack = function(data, width, height, gamma) { PackerAsync.prototype.pack = function (data, width, height, gamma) {
// Signature // Signature
this.emit('data', new Buffer(constants.PNG_SIGNATURE)); this.emit("data", Buffer.from(constants.PNG_SIGNATURE));
this.emit('data', this._packer.packIHDR(width, height)); this.emit("data", this._packer.packIHDR(width, height));
if (gamma) { if (gamma) {
this.emit('data', this._packer.packGAMA(gamma)); this.emit("data", this._packer.packGAMA(gamma));
} }
var filteredData = this._packer.filterData(data, width, height); let filteredData = this._packer.filterData(data, width, height);
// compress it // compress it
this._deflate.on('error', this.emit.bind(this, 'error')); this._deflate.on("error", this.emit.bind(this, "error"));
this._deflate.on('data', function(compressedData) { this._deflate.on(
this.emit('data', this._packer.packIDAT(compressedData)); "data",
}.bind(this)); function (compressedData) {
this.emit("data", this._packer.packIDAT(compressedData));
}.bind(this)
);
this._deflate.on('end', function() { this._deflate.on(
this.emit('data', this._packer.packIEND()); "end",
this.emit('end'); function () {
}.bind(this)); this.emit("data", this._packer.packIEND());
this.emit("end");
}.bind(this)
);
this._deflate.end(filteredData); this._deflate.end(filteredData);
}; };

View file

@ -1,33 +1,24 @@
'use strict'; import constants from "./constants.js";
import Packer from "./packer.js";
var hasSyncZlib = true; let hasSyncZlib = true;
var zlib = require('zlib'); let zlib = require("zlib");
if (!zlib.deflateSync) {
// Backwards compatibility with 0.10.
try {
zlib = require('node-zlib-backport');
}
catch(ex) {
hasSyncZlib = false;
}
}
var constants = require('./constants');
var Packer = require('./packer');
module.exports = function(metaData, opt) {
export default function (metaData, opt) {
if (!hasSyncZlib) { if (!hasSyncZlib) {
throw new Error('To use the sync capability of this library in old node versions, please also add a dependency on node-zlb-backport'); throw new Error(
"To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
);
} }
var options = opt || {}; let options = opt || {};
var packer = new Packer(options); let packer = new Packer(options);
var chunks = []; let chunks = [];
// Signature // Signature
chunks.push(new Buffer(constants.PNG_SIGNATURE)); chunks.push(Buffer.from(constants.PNG_SIGNATURE));
// Header // Header
chunks.push(packer.packIHDR(metaData.width, metaData.height)); chunks.push(packer.packIHDR(metaData.width, metaData.height));
@ -36,14 +27,21 @@ module.exports = function(metaData, opt) {
chunks.push(packer.packGAMA(metaData.gamma)); chunks.push(packer.packGAMA(metaData.gamma));
} }
var filteredData = packer.filterData(metaData.data, metaData.width, metaData.height); let filteredData = packer.filterData(
metaData.data,
metaData.width,
metaData.height
);
// compress it // compress it
var compressedData = zlib.deflateSync(filteredData, packer.getDeflateOptions()); let compressedData = zlib.deflateSync(
filteredData,
packer.getDeflateOptions()
);
filteredData = null; filteredData = null;
if (!compressedData || !compressedData.length) { if (!compressedData || !compressedData.length) {
throw new Error('bad png - invalid compressed data response'); throw new Error("bad png - invalid compressed data response");
} }
chunks.push(packer.packIDAT(compressedData)); chunks.push(packer.packIDAT(compressedData));

View file

@ -1,56 +1,92 @@
'use strict'; import constants from "./constants.js";
import CrcStream from "./crc.js";
import bitPacker from "./bitpacker.js";
import filter from "./filter-pack.js";
var constants = require('./constants'); let zlib = require("zlib");
var CrcStream = require('./crc');
var bitPacker = require('./bitpacker');
var filter = require('./filter-pack');
var zlib = require('zlib');
var Packer = module.exports = function(options) { let Packer = function (options) {
this._options = options; this._options = options;
options.deflateChunkSize = options.deflateChunkSize || 32 * 1024; options.deflateChunkSize = options.deflateChunkSize || 32 * 1024;
options.deflateLevel = options.deflateLevel != null ? options.deflateLevel : 9; options.deflateLevel =
options.deflateStrategy = options.deflateStrategy != null ? options.deflateStrategy : 3; options.deflateLevel != null ? options.deflateLevel : 9;
options.inputHasAlpha = options.inputHasAlpha != null ? options.inputHasAlpha : true; options.deflateStrategy =
options.deflateStrategy != null ? options.deflateStrategy : 3;
options.inputHasAlpha =
options.inputHasAlpha != null ? options.inputHasAlpha : true;
options.deflateFactory = options.deflateFactory || zlib.createDeflate; options.deflateFactory = options.deflateFactory || zlib.createDeflate;
options.bitDepth = options.bitDepth || 8; options.bitDepth = options.bitDepth || 8;
options.colorType = (typeof options.colorType === 'number') ? options.colorType : constants.COLORTYPE_COLOR_ALPHA; // 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 (options.colorType !== constants.COLORTYPE_COLOR && options.colorType !== constants.COLORTYPE_COLOR_ALPHA) { if (
throw new Error('option color type:' + options.colorType + ' is not supported at present'); [
constants.COLORTYPE_GRAYSCALE,
constants.COLORTYPE_COLOR,
constants.COLORTYPE_COLOR_ALPHA,
constants.COLORTYPE_ALPHA,
].indexOf(options.colorType) === -1
) {
throw new Error(
"option color type:" + options.colorType + " is not supported at present"
);
} }
if (options.bitDepth !== 8) { if (
throw new Error('option bit depth:' + options.bitDepth + ' is not supported at present'); [
constants.COLORTYPE_GRAYSCALE,
constants.COLORTYPE_COLOR,
constants.COLORTYPE_COLOR_ALPHA,
constants.COLORTYPE_ALPHA,
].indexOf(options.inputColorType) === -1
) {
throw new Error(
"option input color type:" +
options.inputColorType +
" is not supported at present"
);
}
if (options.bitDepth !== 8 && options.bitDepth !== 16) {
throw new Error(
"option bit depth:" + options.bitDepth + " is not supported at present"
);
} }
}; };
Packer.prototype.getDeflateOptions = function() { export default Packer;
Packer.prototype.getDeflateOptions = function () {
return { return {
chunkSize: this._options.deflateChunkSize, chunkSize: this._options.deflateChunkSize,
level: this._options.deflateLevel, level: this._options.deflateLevel,
strategy: this._options.deflateStrategy strategy: this._options.deflateStrategy,
}; };
}; };
Packer.prototype.createDeflate = function() { Packer.prototype.createDeflate = function () {
return this._options.deflateFactory(this.getDeflateOptions()); return this._options.deflateFactory(this.getDeflateOptions());
}; };
Packer.prototype.filterData = function(data, width, height) { Packer.prototype.filterData = function (data, width, height) {
// convert to correct format for filtering (e.g. right bpp and bit depth) // convert to correct format for filtering (e.g. right bpp and bit depth)
var packedData = bitPacker(data, width, height, this._options); let packedData = bitPacker(data, width, height, this._options);
// filter pixel data // filter pixel data
var bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType]; let bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType];
var filteredData = filter(packedData, width, height, this._options, bpp); let filteredData = filter(packedData, width, height, this._options, bpp);
return filteredData; return filteredData;
}; };
Packer.prototype._packChunk = function(type, data) { Packer.prototype._packChunk = function (type, data) {
let len = data ? data.length : 0;
var len = (data ? data.length : 0); let buf = Buffer.alloc(len + 12);
var buf = new Buffer(len + 12);
buf.writeUInt32BE(len, 0); buf.writeUInt32BE(len, 0);
buf.writeUInt32BE(type, 4); buf.writeUInt32BE(type, 4);
@ -59,19 +95,21 @@ Packer.prototype._packChunk = function(type, data) {
data.copy(buf, 8); data.copy(buf, 8);
} }
buf.writeInt32BE(CrcStream.crc32(buf.slice(4, buf.length - 4)), buf.length - 4); buf.writeInt32BE(
CrcStream.crc32(buf.slice(4, buf.length - 4)),
buf.length - 4
);
return buf; return buf;
}; };
Packer.prototype.packGAMA = function(gamma) { Packer.prototype.packGAMA = function (gamma) {
var buf = new Buffer(4); let buf = Buffer.alloc(4);
buf.writeUInt32BE(Math.floor(gamma * constants.GAMMA_DIVISION), 0); buf.writeUInt32BE(Math.floor(gamma * constants.GAMMA_DIVISION), 0);
return this._packChunk(constants.TYPE_gAMA, buf); return this._packChunk(constants.TYPE_gAMA, buf);
}; };
Packer.prototype.packIHDR = function(width, height) { Packer.prototype.packIHDR = function (width, height) {
let buf = Buffer.alloc(13);
var buf = new Buffer(13);
buf.writeUInt32BE(width, 0); buf.writeUInt32BE(width, 0);
buf.writeUInt32BE(height, 4); buf.writeUInt32BE(height, 4);
buf[8] = this._options.bitDepth; // Bit depth buf[8] = this._options.bitDepth; // Bit depth
@ -83,10 +121,10 @@ Packer.prototype.packIHDR = function(width, height) {
return this._packChunk(constants.TYPE_IHDR, buf); return this._packChunk(constants.TYPE_IHDR, buf);
}; };
Packer.prototype.packIDAT = function(data) { Packer.prototype.packIDAT = function (data) {
return this._packChunk(constants.TYPE_IDAT, data); return this._packChunk(constants.TYPE_IDAT, data);
}; };
Packer.prototype.packIEND = function() { Packer.prototype.packIEND = function () {
return this._packChunk(constants.TYPE_IEND, null); return this._packChunk(constants.TYPE_IEND, null);
}; };

View file

@ -1,11 +1,8 @@
'use strict'; export default function paethPredictor(left, above, upLeft) {
let paeth = left + above - upLeft;
module.exports = function paethPredictor(left, above, upLeft) { let pLeft = Math.abs(paeth - left);
let pAbove = Math.abs(paeth - above);
var paeth = left + above - upLeft; let pUpLeft = Math.abs(paeth - upLeft);
var pLeft = Math.abs(paeth - left);
var pAbove = Math.abs(paeth - above);
var pUpLeft = Math.abs(paeth - upLeft);
if (pLeft <= pAbove && pLeft <= pUpLeft) { if (pLeft <= pAbove && pLeft <= pUpLeft) {
return left; return left;

View file

@ -1,25 +1,26 @@
'use strict'; 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";
var util = require('util'); let util = require("util");
var zlib = require('zlib'); let zlib = require("zlib");
var ChunkStream = require('./chunkstream');
var FilterAsync = require('./filter-parse-async');
var Parser = require('./parser');
var bitmapper = require('./bitmapper');
var formatNormaliser = require('./format-normaliser');
var ParserAsync = module.exports = function(options) { let ParserAsync = function (options) {
ChunkStream.call(this); ChunkStream.call(this);
this._parser = new Parser(options, { this._parser = new Parser(options, {
read: this.read.bind(this), read: this.read.bind(this),
error: this._handleError.bind(this), error: this._handleError.bind(this),
metadata: this._handleMetaData.bind(this), metadata: this._handleMetaData.bind(this),
gamma: this.emit.bind(this, 'gamma'), gamma: this.emit.bind(this, "gamma"),
palette: this._handlePalette.bind(this), palette: this._handlePalette.bind(this),
transColor: this._handleTransColor.bind(this), transColor: this._handleTransColor.bind(this),
finished: this._finished.bind(this), finished: this._finished.bind(this),
inflateData: this._inflateData.bind(this) inflateData: this._inflateData.bind(this),
simpleTransparency: this._simpleTransparency.bind(this),
headersFinished: this._headersFinished.bind(this),
}); });
this._options = options; this._options = options;
this.writable = true; this.writable = true;
@ -28,10 +29,10 @@ var ParserAsync = module.exports = function(options) {
}; };
util.inherits(ParserAsync, ChunkStream); util.inherits(ParserAsync, ChunkStream);
export default ParserAsync;
ParserAsync.prototype._handleError = function(err) { ParserAsync.prototype._handleError = function (err) {
this.emit("error", err);
this.emit('error', err);
this.writable = false; this.writable = false;
@ -41,70 +42,125 @@ ParserAsync.prototype._handleError = function(err) {
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; this.errord = true;
}; };
ParserAsync.prototype._inflateData = function(data) { ParserAsync.prototype._inflateData = function (data) {
if (!this._inflate) { if (!this._inflate) {
if (this._bitmapInfo.interlace) {
this._inflate = zlib.createInflate(); this._inflate = zlib.createInflate();
this._inflate.on('error', this.emit.bind(this, 'error')); this._inflate.on("error", this.emit.bind(this, "error"));
this._filter.on('complete', this._complete.bind(this)); this._filter.on("complete", this._complete.bind(this));
this._inflate.pipe(this._filter); 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); this._inflate.write(data);
}; };
ParserAsync.prototype._handleMetaData = function(metaData) { ParserAsync.prototype._handleMetaData = function (metaData) {
this._metaData = metaData;
this.emit('metadata', metaData);
this._bitmapInfo = Object.create(metaData); this._bitmapInfo = Object.create(metaData);
this._filter = new FilterAsync(this._bitmapInfo); this._filter = new FilterAsync(this._bitmapInfo);
}; };
ParserAsync.prototype._handleTransColor = function(transColor) { ParserAsync.prototype._handleTransColor = function (transColor) {
this._bitmapInfo.transColor = transColor; this._bitmapInfo.transColor = transColor;
}; };
ParserAsync.prototype._handlePalette = function(palette) { ParserAsync.prototype._handlePalette = function (palette) {
this._bitmapInfo.palette = palette; this._bitmapInfo.palette = palette;
}; };
ParserAsync.prototype._simpleTransparency = function () {
this._metaData.alpha = true;
};
ParserAsync.prototype._finished = function() { 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) { if (this.errord) {
return; return;
} }
if (!this._inflate) { if (!this._inflate) {
this.emit('error', 'No Inflate block'); this.emit("error", "No Inflate block");
} } else {
else {
// no more data to inflate // no more data to inflate
this._inflate.end(); this._inflate.end();
} }
this.destroySoon();
}; };
ParserAsync.prototype._complete = function(filteredData) { ParserAsync.prototype._complete = function (filteredData) {
if (this.errord) { if (this.errord) {
return; return;
} }
try { let normalisedBitmapData;
var bitmapData = bitmapper.dataToBitMap(filteredData, this._bitmapInfo);
var normalisedBitmapData = formatNormaliser(bitmapData, this._bitmapInfo); try {
let bitmapData = bitmapper(filteredData, this._bitmapInfo);
normalisedBitmapData = formatNormaliser(bitmapData, this._bitmapInfo);
bitmapData = null; bitmapData = null;
} } catch (ex) {
catch (ex) {
this._handleError(ex); this._handleError(ex);
return; return;
} }
this.emit('parsed', normalisedBitmapData); this.emit("parsed", normalisedBitmapData);
}; };

View file

@ -1,35 +1,29 @@
'use strict'; 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";
var hasSyncZlib = true; let hasSyncZlib = true;
var zlib = require('zlib'); let zlib = require("zlib");
if (!zlib.deflateSync) { if (!zlib.deflateSync) {
// Backwards compatibility with 0.10.
try {
zlib = require('node-zlib-backport');
}
catch(ex) {
hasSyncZlib = false; hasSyncZlib = false;
}
} }
var SyncReader = require('./sync-reader');
var FilterSync = require('./filter-parse-sync');
var Parser = require('./parser');
var bitmapper = require('./bitmapper');
var formatNormaliser = require('./format-normaliser');
module.exports = function(buffer, options) {
export default function (buffer, options) {
if (!hasSyncZlib) { if (!hasSyncZlib) {
throw new Error('To use the sync capability of this library in old node versions, please also add a dependency on node-zlb-backport'); throw new Error(
"To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
);
} }
var err; let err;
function handleError(_err_) { function handleError(_err_) {
err = _err_; err = _err_;
} }
var metaData; let metaData;
function handleMetaData(_metaData_) { function handleMetaData(_metaData_) {
metaData = _metaData_; metaData = _metaData_;
} }
@ -42,26 +36,31 @@ module.exports = function(buffer, options) {
metaData.palette = palette; metaData.palette = palette;
} }
var gamma; function handleSimpleTransparency() {
metaData.alpha = true;
}
let gamma;
function handleGamma(_gamma_) { function handleGamma(_gamma_) {
gamma = _gamma_; gamma = _gamma_;
} }
var inflateDataList = []; let inflateDataList = [];
function handleInflateData(inflatedData) { function handleInflateData(inflatedData) {
inflateDataList.push(inflatedData); inflateDataList.push(inflatedData);
} }
var reader = new SyncReader(buffer); let reader = new SyncReader(buffer);
var parser = new Parser(options, { let parser = new Parser(options, {
read: reader.read.bind(reader), read: reader.read.bind(reader),
error: handleError, error: handleError,
metadata: handleMetaData, metadata: handleMetaData,
gamma: handleGamma, gamma: handleGamma,
palette: handlePalette, palette: handlePalette,
transColor: handleTransColor, transColor: handleTransColor,
inflateData: handleInflateData inflateData: handleInflateData,
simpleTransparency: handleSimpleTransparency,
}); });
parser.start(); parser.start();
@ -72,23 +71,34 @@ module.exports = function(buffer, options) {
} }
//join together the inflate datas //join together the inflate datas
var inflateData = Buffer.concat(inflateDataList); let inflateData = Buffer.concat(inflateDataList);
inflateDataList.length = 0; inflateDataList.length = 0;
var inflatedData = zlib.inflateSync(inflateData); 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; inflateData = null;
if (!inflatedData || !inflatedData.length) { if (!inflatedData || !inflatedData.length) {
throw new Error('bad png - invalid inflate data response'); throw new Error("bad png - invalid inflate data response");
} }
var unfilteredData = FilterSync.process(inflatedData, metaData); let unfilteredData = FilterSync.process(inflatedData, metaData);
inflateData = null; inflateData = null;
var bitmapData = bitmapper.dataToBitMap(unfilteredData, metaData); let bitmapData = bitmapper.dataToBitMap(unfilteredData, metaData);
unfilteredData = null; unfilteredData = null;
var normalisedBitmapData = formatNormaliser(bitmapData, metaData); let normalisedBitmapData = formatNormaliser(bitmapData, metaData);
metaData.data = normalisedBitmapData; metaData.data = normalisedBitmapData;
metaData.gamma = gamma || 0; metaData.gamma = gamma || 0;

View file

@ -1,16 +1,15 @@
'use strict'; "use strict";
var constants = require('./constants'); let constants = require("./constants");
var CrcCalculator = require('./crc'); let CrcCalculator = require("./crc");
var Parser = module.exports = function(options, dependencies) {
let Parser = (module.exports = function (options, dependencies) {
this._options = options; this._options = options;
options.checkCRC = options.checkCRC !== false; options.checkCRC = options.checkCRC !== false;
this._hasIHDR = false; this._hasIHDR = false;
this._hasIEND = false; this._hasIEND = false;
this._emittedHeadersFinished = false;
// input flags/metadata // input flags/metadata
this._palette = []; this._palette = [];
@ -32,84 +31,80 @@ var Parser = module.exports = function(options, dependencies) {
this.palette = dependencies.palette; this.palette = dependencies.palette;
this.parsed = dependencies.parsed; this.parsed = dependencies.parsed;
this.inflateData = dependencies.inflateData; this.inflateData = dependencies.inflateData;
this.inflateData = dependencies.inflateData;
this.finished = dependencies.finished; this.finished = dependencies.finished;
this.simpleTransparency = dependencies.simpleTransparency;
this.headersFinished = dependencies.headersFinished || function () {};
});
Parser.prototype.start = function () {
this.read(constants.PNG_SIGNATURE.length, this._parseSignature.bind(this));
}; };
Parser.prototype.start = 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++) {
var signature = constants.PNG_SIGNATURE;
for (var i = 0; i < signature.length; i++) {
if (data[i] !== signature[i]) { if (data[i] !== signature[i]) {
this.error(new Error('Invalid file signature')); this.error(new Error("Invalid file signature"));
return; return;
} }
} }
this.read(8, this._parseChunkBegin.bind(this)); this.read(8, this._parseChunkBegin.bind(this));
}; };
Parser.prototype._parseChunkBegin = function(data) { Parser.prototype._parseChunkBegin = function (data) {
// chunk content length // chunk content length
var length = data.readUInt32BE(0); let length = data.readUInt32BE(0);
// chunk type // chunk type
var type = data.readUInt32BE(4); let type = data.readUInt32BE(4);
var name = ''; let name = "";
for (var i = 4; i < 8; i++) { for (let i = 4; i < 8; i++) {
name += String.fromCharCode(data[i]); name += String.fromCharCode(data[i]);
} }
//console.log('chunk ', name, length); //console.log('chunk ', name, length);
// chunk flags // chunk flags
var ancillary = Boolean(data[4] & 0x20); // or critical let ancillary = Boolean(data[4] & 0x20); // or critical
// priv = Boolean(data[5] & 0x20), // or public // priv = Boolean(data[5] & 0x20), // or public
// safeToCopy = Boolean(data[7] & 0x20); // or unsafe // safeToCopy = Boolean(data[7] & 0x20); // or unsafe
if (!this._hasIHDR && type !== constants.TYPE_IHDR) { if (!this._hasIHDR && type !== constants.TYPE_IHDR) {
this.error(new Error('Expected IHDR on beggining')); this.error(new Error("Expected IHDR on beggining"));
return; return;
} }
this._crc = new CrcCalculator(); this._crc = new CrcCalculator();
this._crc.write(new Buffer(name)); this._crc.write(Buffer.from(name));
if (this._chunks[type]) { if (this._chunks[type]) {
return this._chunks[type](length); return this._chunks[type](length);
} }
if (!ancillary) { if (!ancillary) {
this.error(new Error('Unsupported critical chunk type ' + name)); this.error(new Error("Unsupported critical chunk type " + name));
return; return;
} }
this.read(length + 4, this._skipChunk.bind(this)); this.read(length + 4, this._skipChunk.bind(this));
}; };
Parser.prototype._skipChunk = function(/*data*/) { Parser.prototype._skipChunk = function (/*data*/) {
this.read(8, this._parseChunkBegin.bind(this)); this.read(8, this._parseChunkBegin.bind(this));
}; };
Parser.prototype._handleChunkEnd = function() { Parser.prototype._handleChunkEnd = function () {
this.read(4, this._parseChunkEnd.bind(this)); this.read(4, this._parseChunkEnd.bind(this));
}; };
Parser.prototype._parseChunkEnd = function(data) { Parser.prototype._parseChunkEnd = function (data) {
let fileCrc = data.readInt32BE(0);
var fileCrc = data.readInt32BE(0); let calcCrc = this._crc.crc32();
var calcCrc = this._crc.crc32();
// check CRC // check CRC
if (this._options.checkCRC && calcCrc !== fileCrc) { if (this._options.checkCRC && calcCrc !== fileCrc) {
this.error(new Error('Crc error - ' + fileCrc + ' - ' + calcCrc)); this.error(new Error("Crc error - " + fileCrc + " - " + calcCrc));
return; return;
} }
@ -118,50 +113,55 @@ Parser.prototype._parseChunkEnd = function(data) {
} }
}; };
Parser.prototype._handleIHDR = function(length) { Parser.prototype._handleIHDR = function (length) {
this.read(length, this._parseIHDR.bind(this)); this.read(length, this._parseIHDR.bind(this));
}; };
Parser.prototype._parseIHDR = function(data) { Parser.prototype._parseIHDR = function (data) {
this._crc.write(data); this._crc.write(data);
var width = data.readUInt32BE(0); let width = data.readUInt32BE(0);
var height = data.readUInt32BE(4); let height = data.readUInt32BE(4);
var depth = data[8]; let depth = data[8];
var colorType = data[9]; // bits: 1 palette, 2 color, 4 alpha let colorType = data[9]; // bits: 1 palette, 2 color, 4 alpha
var compr = data[10]; let compr = data[10];
var filter = data[11]; let filter = data[11];
var interlace = data[12]; let interlace = data[12];
// console.log(' width', width, 'height', height, // console.log(' width', width, 'height', height,
// 'depth', depth, 'colorType', colorType, // 'depth', depth, 'colorType', colorType,
// 'compr', compr, 'filter', filter, 'interlace', interlace // 'compr', compr, 'filter', filter, 'interlace', interlace
// ); // );
if (depth !== 8 && depth !== 4 && depth !== 2 && depth !== 1 && depth !== 16) { if (
this.error(new Error('Unsupported bit depth ' + depth)); depth !== 8 &&
depth !== 4 &&
depth !== 2 &&
depth !== 1 &&
depth !== 16
) {
this.error(new Error("Unsupported bit depth " + depth));
return; return;
} }
if (!(colorType in constants.COLORTYPE_TO_BPP_MAP)) { if (!(colorType in constants.COLORTYPE_TO_BPP_MAP)) {
this.error(new Error('Unsupported color type')); this.error(new Error("Unsupported color type"));
return; return;
} }
if (compr !== 0) { if (compr !== 0) {
this.error(new Error('Unsupported compression method')); this.error(new Error("Unsupported compression method"));
return; return;
} }
if (filter !== 0) { if (filter !== 0) {
this.error(new Error('Unsupported filter method')); this.error(new Error("Unsupported filter method"));
return; return;
} }
if (interlace !== 0 && interlace !== 1) { if (interlace !== 0 && interlace !== 1) {
this.error(new Error('Unsupported interlace method')); this.error(new Error("Unsupported interlace method"));
return; return;
} }
this._colorType = colorType; this._colorType = colorType;
var bpp = constants.COLORTYPE_TO_BPP_MAP[this._colorType]; let bpp = constants.COLORTYPE_TO_BPP_MAP[this._colorType];
this._hasIHDR = true; this._hasIHDR = true;
@ -174,30 +174,23 @@ Parser.prototype._parseIHDR = function(data) {
color: Boolean(colorType & constants.COLORTYPE_COLOR), color: Boolean(colorType & constants.COLORTYPE_COLOR),
alpha: Boolean(colorType & constants.COLORTYPE_ALPHA), alpha: Boolean(colorType & constants.COLORTYPE_ALPHA),
bpp: bpp, bpp: bpp,
colorType: colorType colorType: colorType,
}); });
this._handleChunkEnd(); this._handleChunkEnd();
}; };
Parser.prototype._handlePLTE = function (length) {
Parser.prototype._handlePLTE = function(length) {
this.read(length, this._parsePLTE.bind(this)); this.read(length, this._parsePLTE.bind(this));
}; };
Parser.prototype._parsePLTE = function(data) { Parser.prototype._parsePLTE = function (data) {
this._crc.write(data); this._crc.write(data);
var entries = Math.floor(data.length / 3); let entries = Math.floor(data.length / 3);
// console.log('Palette:', entries); // console.log('Palette:', entries);
for (var i = 0; i < entries; i++) { for (let i = 0; i < entries; i++) {
this._palette.push([ this._palette.push([data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 0xff]);
data[i * 3],
data[i * 3 + 1],
data[i * 3 + 2],
0xff
]);
} }
this.palette(this._palette); this.palette(this._palette);
@ -205,24 +198,24 @@ Parser.prototype._parsePLTE = function(data) {
this._handleChunkEnd(); this._handleChunkEnd();
}; };
Parser.prototype._handleTRNS = function(length) { Parser.prototype._handleTRNS = function (length) {
this.simpleTransparency();
this.read(length, this._parseTRNS.bind(this)); this.read(length, this._parseTRNS.bind(this));
}; };
Parser.prototype._parseTRNS = function(data) { Parser.prototype._parseTRNS = function (data) {
this._crc.write(data); this._crc.write(data);
// palette // palette
if (this._colorType === constants.COLORTYPE_PALETTE_COLOR) { if (this._colorType === constants.COLORTYPE_PALETTE_COLOR) {
if (this._palette.length === 0) { if (this._palette.length === 0) {
this.error(new Error('Transparency chunk must be after palette')); this.error(new Error("Transparency chunk must be after palette"));
return; return;
} }
if (data.length > this._palette.length) { if (data.length > this._palette.length) {
this.error(new Error('More transparent colors than palette size')); this.error(new Error("More transparent colors than palette size"));
return; return;
} }
for (var i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
this._palette[i][3] = data[i]; this._palette[i][3] = data[i];
} }
this.palette(this._palette); this.palette(this._palette);
@ -235,50 +228,57 @@ Parser.prototype._parseTRNS = function(data) {
this.transColor([data.readUInt16BE(0)]); this.transColor([data.readUInt16BE(0)]);
} }
if (this._colorType === constants.COLORTYPE_COLOR) { if (this._colorType === constants.COLORTYPE_COLOR) {
this.transColor([data.readUInt16BE(0), data.readUInt16BE(2), data.readUInt16BE(4)]); this.transColor([
data.readUInt16BE(0),
data.readUInt16BE(2),
data.readUInt16BE(4),
]);
} }
this._handleChunkEnd(); this._handleChunkEnd();
}; };
Parser.prototype._handleGAMA = function(length) { Parser.prototype._handleGAMA = function (length) {
this.read(length, this._parseGAMA.bind(this)); this.read(length, this._parseGAMA.bind(this));
}; };
Parser.prototype._parseGAMA = function(data) { Parser.prototype._parseGAMA = function (data) {
this._crc.write(data); this._crc.write(data);
this.gamma(data.readUInt32BE(0) / constants.GAMMA_DIVISION); this.gamma(data.readUInt32BE(0) / constants.GAMMA_DIVISION);
this._handleChunkEnd(); this._handleChunkEnd();
}; };
Parser.prototype._handleIDAT = function(length) { Parser.prototype._handleIDAT = function (length) {
if (!this._emittedHeadersFinished) {
this._emittedHeadersFinished = true;
this.headersFinished();
}
this.read(-length, this._parseIDAT.bind(this, length)); this.read(-length, this._parseIDAT.bind(this, length));
}; };
Parser.prototype._parseIDAT = function(length, 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) { if (
throw new Error('Expected palette not found'); this._colorType === constants.COLORTYPE_PALETTE_COLOR &&
this._palette.length === 0
) {
throw new Error("Expected palette not found");
} }
this.inflateData(data); this.inflateData(data);
var leftOverLength = length - data.length; let leftOverLength = length - data.length;
if (leftOverLength > 0) { if (leftOverLength > 0) {
this._handleIDAT(leftOverLength); this._handleIDAT(leftOverLength);
} } else {
else {
this._handleChunkEnd(); this._handleChunkEnd();
} }
}; };
Parser.prototype._handleIEND = function(length) { Parser.prototype._handleIEND = function (length) {
this.read(length, this._parseIEND.bind(this)); this.read(length, this._parseIEND.bind(this));
}; };
Parser.prototype._parseIEND = function(data) { Parser.prototype._parseIEND = function (data) {
this._crc.write(data); this._crc.write(data);
this._hasIEND = true; this._hasIEND = true;

View file

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

View file

@ -1,22 +1,23 @@
'use strict'; import Parser from "./parser-async.js";
import Packer from "./packer-async.js";
import PNGSync from "./png-sync.js";
var util = require('util'); let util = require("util");
var Stream = require('stream'); let Stream = require("stream");
var Parser = require('./parser-async');
var Packer = require('./packer-async');
var PNGSync = require('./png-sync');
let PNG = (exports.PNG = function (options) {
var PNG = exports.PNG = function(options) {
Stream.call(this); Stream.call(this);
options = options || {}; // eslint-disable-line no-param-reassign options = options || {}; // eslint-disable-line no-param-reassign
this.width = options.width || 0; // coerce pixel dimensions to integers (also coerces undefined -> 0):
this.height = options.height || 0; this.width = options.width | 0;
this.height = options.height | 0;
this.data = this.width > 0 && this.height > 0 ? this.data =
new Buffer(4 * this.width * this.height) : null; this.width > 0 && this.height > 0
? Buffer.alloc(4 * this.width * this.height)
: null;
if (options.fill && this.data) { if (options.fill && this.data) {
this.data.fill(0); this.data.fill(0);
@ -27,106 +28,127 @@ var PNG = exports.PNG = function(options) {
this._parser = new Parser(options); this._parser = new Parser(options);
this._parser.on('error', this.emit.bind(this, 'error')); this._parser.on("error", this.emit.bind(this, "error"));
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._parser.on("metadata", this._metadata.bind(this));
this._parser.on('gamma', this._gamma.bind(this)); this._parser.on("gamma", this._gamma.bind(this));
this._parser.on('parsed', function(data) { this._parser.on(
"parsed",
function (data) {
this.data = data; this.data = data;
this.emit('parsed', data); this.emit("parsed", data);
}.bind(this)); }.bind(this)
);
this._packer = new Packer(options); this._packer = new Packer(options);
this._packer.on('data', this.emit.bind(this, 'data')); this._packer.on("data", this.emit.bind(this, "data"));
this._packer.on('end', this.emit.bind(this, 'end')); 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._packer.on('error', this.emit.bind(this, 'error')); this._packer.on("error", this.emit.bind(this, "error"));
});
};
util.inherits(PNG, Stream); util.inherits(PNG, Stream);
PNG.sync = PNGSync; PNG.sync = PNGSync;
PNG.prototype.pack = function() { PNG.prototype.pack = function () {
if (!this.data || !this.data.length) { if (!this.data || !this.data.length) {
this.emit('error', 'No data provided'); this.emit("error", "No data provided");
return this; return this;
} }
process.nextTick(function() { process.nextTick(
function () {
this._packer.pack(this.data, this.width, this.height, this.gamma); this._packer.pack(this.data, this.width, this.height, this.gamma);
}.bind(this)); }.bind(this)
);
return this; return this;
}; };
PNG.prototype.parse = function (data, callback) {
PNG.prototype.parse = function(data, callback) {
if (callback) { if (callback) {
var onParsed, onError; let onParsed, onError;
onParsed = function(parsedData) { onParsed = function (parsedData) {
this.removeListener('error', onError); this.removeListener("error", onError);
this.data = parsedData; this.data = parsedData;
callback(null, this); callback(null, this);
}.bind(this); }.bind(this);
onError = function(err) { onError = function (err) {
this.removeListener('parsed', onParsed); this.removeListener("parsed", onParsed);
callback(err, null); callback(err, null);
}.bind(this); }.bind(this);
this.once('parsed', onParsed); this.once("parsed", onParsed);
this.once('error', onError); this.once("error", onError);
} }
this.end(data); this.end(data);
return this; return this;
}; };
PNG.prototype.write = function(data) { PNG.prototype.write = function (data) {
this._parser.write(data); this._parser.write(data);
return true; return true;
}; };
PNG.prototype.end = function(data) { PNG.prototype.end = function (data) {
this._parser.end(data); this._parser.end(data);
}; };
PNG.prototype._metadata = function(metadata) { PNG.prototype._metadata = function (metadata) {
this.width = metadata.width; this.width = metadata.width;
this.height = metadata.height; this.height = metadata.height;
this.emit('metadata', metadata); this.emit("metadata", metadata);
}; };
PNG.prototype._gamma = function(gamma) { PNG.prototype._gamma = function (gamma) {
this.gamma = gamma; this.gamma = gamma;
}; };
PNG.prototype._handleClose = function() { PNG.prototype._handleClose = function () {
if (!this._parser.writable && !this._packer.readable) { if (!this._parser.writable && !this._packer.readable) {
this.emit('close'); 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 */
PNG.bitblt = function(src, dst, srcX, srcY, width, height, deltaX, deltaY) { // eslint-disable-line max-params if (
srcX > src.width ||
if (srcX > src.width || srcY > src.height || srcX + width > src.width || srcY + height > src.height) { srcY > src.height ||
throw new Error('bitblt reading outside image'); 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) { if (
throw new Error('bitblt writing outside image'); deltaX > dst.width ||
deltaY > dst.height ||
deltaX + width > dst.width ||
deltaY + height > dst.height
) {
throw new Error("bitblt writing outside image");
} }
for (var y = 0; y < height; y++) { for (let y = 0; y < height; y++) {
src.data.copy(dst.data, src.data.copy(
dst.data,
((deltaY + y) * dst.width + deltaX) << 2, ((deltaY + y) * dst.width + deltaX) << 2,
((srcY + y) * src.width + srcX) << 2, ((srcY + y) * src.width + srcX) << 2,
((srcY + y) * src.width + srcX + width) << 2 ((srcY + y) * src.width + srcX + width) << 2
@ -134,21 +156,29 @@ PNG.bitblt = function(src, dst, srcX, srcY, width, height, deltaX, deltaY) { //
} }
}; };
PNG.prototype.bitblt = function (
PNG.prototype.bitblt = function(dst, srcX, srcY, width, height, deltaX, deltaY) { // eslint-disable-line max-params dst,
srcX,
srcY,
width,
height,
deltaX,
deltaY
) {
// eslint-disable-line max-params
PNG.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY); PNG.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY);
return this; return this;
}; };
PNG.adjustGamma = function(src) { PNG.adjustGamma = function (src) {
if (src.gamma) { if (src.gamma) {
for (var y = 0; y < src.height; y++) { for (let y = 0; y < src.height; y++) {
for (var x = 0; x < src.width; x++) { for (let x = 0; x < src.width; x++) {
var idx = (src.width * y + x) << 2; let idx = (src.width * y + x) << 2;
for (var i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
var sample = src.data[idx + i] / 255; let sample = src.data[idx + i] / 255;
sample = Math.pow(sample, 1 / 2.2 / src.gamma); sample = Math.pow(sample, 1 / 2.2 / src.gamma);
src.data[idx + i] = Math.round(sample * 255); src.data[idx + i] = Math.round(sample * 255);
} }
@ -158,6 +188,6 @@ PNG.adjustGamma = function(src) {
} }
}; };
PNG.prototype.adjustGamma = function() { PNG.prototype.adjustGamma = function () {
PNG.adjustGamma(this); PNG.adjustGamma(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;

View file

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

View file

@ -1,6 +1,6 @@
{ {
"name": "pngjs", "name": "pngjs",
"version": "2.3.1", "version": "5.0.0",
"description": "PNG encoder/decoder in pure JS, supporting any bit size & interlace, async & sync with full test suite.", "description": "PNG encoder/decoder in pure JS, supporting any bit size & interlace, async & sync with full test suite.",
"contributors": [ "contributors": [
"Alexandre Paré", "Alexandre Paré",
@ -11,7 +11,10 @@
"Pietajan De Potter", "Pietajan De Potter",
"Steven Sojka", "Steven Sojka",
"liangzeng", "liangzeng",
"Michael Vogt" "Michael Vogt",
"Xin-Xin Wang",
"toriningen",
"Eugene Kulabuhov"
], ],
"homepage": "https://github.com/lukeapage/pngjs", "homepage": "https://github.com/lukeapage/pngjs",
"keywords": [ "keywords": [
@ -27,36 +30,44 @@
"pngjs" "pngjs"
], ],
"engines": { "engines": {
"node": ">=0.10.0", "node": ">=10.13.0"
"iojs": ">= 1.0.0"
}, },
"main": "./lib/png.js", "main": "./lib/png.js",
"directories": { "directories": {
"example": "examples" "lib": "lib",
"example": "examples",
"test": "test"
}, },
"scripts": { "scripts": {
"coverage": "istanbul -- cover node_modules/tape/bin/tape test/*-spec.js nolarge", "build": "yarn prepublish",
"coverage-report": "npm run coverage && istanbul report html", "prepublish": "yarn browserify",
"coveralls": "cat ./coverage/lcov.info | coveralls", "browserify": "browserify lib/png.js --standalone png > browser.js",
"test": "npm run lint && tape test/*-spec.js | tap-dot && node test/run-compare", "coverage": "nyc --reporter=lcov --reporter=text-summary tape test/*-spec.js nolarge",
"lint": "eslint lib" "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/lukeapage/pngjs2.git" "url": "git://github.com/lukeapage/pngjs.git"
}, },
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/lukeapage/pngjs2/issues" "url": "https://github.com/lukeapage/pngjs/issues"
}, },
"devDependencies": { "devDependencies": {
"browserify": "16.5.1",
"buffer-equal": "1.0.0", "buffer-equal": "1.0.0",
"connect": "^3.4.0", "codecov": "3.7.0",
"eslint": "^2.5.0", "connect": "3.7.0",
"istanbul": "^0.4.1", "eslint": "7.0.0",
"phantomjs-prebuilt": "^2.1.7", "eslint-config-prettier": "6.11.0",
"serve-static": "^1.10.0", "nyc": "15.0.1",
"tap-dot": "^1.0.0", "prettier": "2.0.5",
"tape": "^4.0.2" "puppeteer": "3.0.2",
"serve-static": "1.14.1",
"tap-dot": "2.0.0",
"tape": "5.0.0"
} }
} }

View file

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

View file

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

View file

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

View file

@ -355,7 +355,7 @@
callback = callback || Function; callback = callback || Function;
base64Data = canvas.toDataURL().replace(/^data:image\/\w+;base64,/,""); base64Data = canvas.toDataURL().replace(/^data:image\/\w+;base64,/,"");
decodedImage = new Buffer(base64Data, 'base64'); decodedImage = Buffer.from(base64Data, 'base64');
require('fs').writeFile(outputFile, decodedImage, callback); require('fs').writeFile(outputFile, decodedImage, callback);
} }

File diff suppressed because it is too large Load diff

View file

@ -1,58 +0,0 @@
/*global phantom:true*/
'use strict';
var page = require('webpage').create();
var last = new Date();
var timeout = 10000;
setInterval(function () {
var results = page.evaluate(function () {
if (window.isFinished && window.isFinished()) {
return window.results;
}
});
if (results) {
var success = true;
var successes = [],
failures = [];
for (var i = 0; i < results.length; i++) {
var result = results[i];
if (result.success) {
successes.push(result.name);
} else {
failures.push(result.name);
}
success = success && result.success;
}
console.log("Success:", successes.join(", "));
if (failures.length) {
console.log("Failure:", failures.join(", "));
}
phantom.exit(success ? 0 : 1);
return;
}
if (new Date() - last > timeout) {
phantom.exit();
}
}, 100);
page.onConsoleMessage = function (msg, lineNum, sourceId) {
//console.log('CONSOLE: ' + msg);
};
page.onError = function (msg, trace) {
console.log('error.onError', msg, trace);
phantom.exit();
};
phantom.onError = function (msg, trace) {
console.log('error.onError', msg, trace);
phantom.exit();
};
page.open("http://localhost:8000");

View file

@ -1,38 +1,37 @@
var test = require('tape'); let test = require("tape");
var fs = require('fs'); let fs = require("fs");
var path = require('path'); let path = require("path");
var PNG = require('../lib/png').PNG; let PNG = require("../lib/png").PNG;
var stream = require('stream'); let stream = require("stream");
function parseFile(filename, cb) { function parseFile(filename, cb) {
fs.createReadStream(path.join(__dirname, "png-parse-data", filename)) fs.createReadStream(path.join(__dirname, "png-parse-data", filename))
.pipe(new PNG()) .pipe(new PNG())
.on('error', function (e) { .on("error", function (e) {
console.log("error"); console.log("error");
cb(e); cb(e);
}) })
.on('parsed', function () { .on("parsed", function () {
cb(null, this); cb(null, this);
}); });
} }
function parseBuffer(buffer, cb) { function parseBuffer(buffer, cb) {
let bufferStream = new stream.PassThrough();
var bufferStream = new stream.PassThrough();
bufferStream.end(buffer); bufferStream.end(buffer);
bufferStream bufferStream
.pipe(new PNG({})) .pipe(new PNG({}))
.on('error', function (e) { .on("error", function (e) {
cb(e); cb(e);
}) })
.on('parse', function () { .on("parse", function () {
cb(null, this); cb(null, this);
}); });
} }
function getPixel(png, x, y) { function getPixel(png, x, y) {
return png.data.readUInt32BE((x + (y * png.width)) * 4); return png.data.readUInt32BE((x + y * png.width) * 4);
} }
test("should correctly parse an 1-bit colormap png", function (t) { test("should correctly parse an 1-bit colormap png", function (t) {
@ -45,15 +44,17 @@ test("should correctly parse an 1-bit colormap png", function (t) {
t.equal(png.data.length, 1024 * 1024 * 4); t.equal(png.data.length, 1024 * 1024 * 4);
//t.equal(png.trailer.length, 0); //t.equal(png.trailer.length, 0);
var y = 1024, let y = 1024,
x; x;
var isOk = true; let isOk = true;
while (y--) { while (y--) {
x = 1024; x = 1024;
while (x--) while (x--)
if (getPixel(png, x, y) !== 0x000000FF) { if (getPixel(png, x, y) !== 0x000000ff) {
t.fail("pixel does not match - " + getPixel(png, x, y) + " !== 0x000000FF"); t.fail(
"pixel does not match - " + getPixel(png, x, y) + " !== 0x000000FF"
);
isOk = false; isOk = false;
break; break;
} }
@ -73,15 +74,20 @@ test("should correctly parse an 8-bit grayscale png", function (t) {
t.equal(png.data.length, 16 * 16 * 4); t.equal(png.data.length, 16 * 16 * 4);
//t.equal(png.trailer.toString(), "Hello, world!\n"); //t.equal(png.trailer.toString(), "Hello, world!\n");
var y = 16, let y = 16,
x; x;
var isOk = true; let isOk = true;
while (y--) { while (y--) {
x = 16; x = 16;
while (x--) { while (x--) {
if (getPixel(png, x, y) !== (x ^ y) * 286331136 + 255) { if (getPixel(png, x, y) !== (x ^ y) * 286331136 + 255) {
t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + ((x ^ y) * 286331136 + 255)); t.fail(
"pixel does not match - " +
getPixel(png, x, y) +
" !== " +
((x ^ y) * 286331136 + 255)
);
isOk = false; isOk = false;
break; break;
} }
@ -90,7 +96,7 @@ test("should correctly parse an 8-bit grayscale png", function (t) {
t.ok(isOk, "The pixels should match"); t.ok(isOk, "The pixels should match");
t.end(); t.end();
}) });
}); });
test("should correctly parse an 8-bit truecolor png", function (t) { test("should correctly parse an 8-bit truecolor png", function (t) {
@ -102,15 +108,23 @@ test("should correctly parse an 8-bit truecolor png", function (t) {
t.equal(png.data.length, 16 * 16 * 4); t.equal(png.data.length, 16 * 16 * 4);
//t.equal(png.trailer.length, 0); //t.equal(png.trailer.length, 0);
var y = 16, let y = 16,
x; x;
var isOk = true; let isOk = true;
while (y--) { while (y--) {
x = 16; x = 16;
while (x--) { while (x--) {
if (getPixel(png, x, y) !== x * 285212672 + y * 1114112 + (x ^ y) * 4352 + 255) { if (
t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + (x * 285212672 + y * 1114112 + (x ^ y) * 4352 + 255)); getPixel(png, x, y) !==
x * 285212672 + y * 1114112 + (x ^ y) * 4352 + 255
) {
t.fail(
"pixel does not match - " +
getPixel(png, x, y) +
" !== " +
(x * 285212672 + y * 1114112 + (x ^ y) * 4352 + 255)
);
isOk = false; isOk = false;
break; break;
} }
@ -119,8 +133,8 @@ test("should correctly parse an 8-bit truecolor png", function (t) {
t.ok(isOk, "The pixels should match"); t.ok(isOk, "The pixels should match");
t.end(); t.end();
}) });
}) });
test("should correctly parse an 8-bit truecolor png with alpha", function (t) { test("should correctly parse an 8-bit truecolor png with alpha", function (t) {
parseFile("truecoloralpha.png", function (err, png) { parseFile("truecoloralpha.png", function (err, png) {
@ -131,15 +145,23 @@ test("should correctly parse an 8-bit truecolor png with alpha", function (t) {
t.equal(png.data.length, 16 * 16 * 4); t.equal(png.data.length, 16 * 16 * 4);
//t.equal(png.trailer.length, 0); //t.equal(png.trailer.length, 0);
var y = 16, let y = 16,
x; x;
var isOk = true; let isOk = true;
while (y--) { while (y--) {
x = 16; x = 16;
while (x--) { while (x--) {
if (getPixel(png, x, y) !== x * 285212672 + y * 1114112 + (x ^ y) * 17) { if (
t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + (x * 285212672 + y * 1114112 + (x ^ y) * 17)); getPixel(png, x, y) !==
x * 285212672 + y * 1114112 + (x ^ y) * 17
) {
t.fail(
"pixel does not match - " +
getPixel(png, x, y) +
" !== " +
(x * 285212672 + y * 1114112 + (x ^ y) * 17)
);
isOk = false; isOk = false;
break; break;
} }
@ -148,8 +170,8 @@ test("should correctly parse an 8-bit truecolor png with alpha", function (t) {
t.ok(isOk, "The pixels should match"); t.ok(isOk, "The pixels should match");
t.end(); t.end();
}) });
}) });
test("should correctly read image with scanline filter", function (t) { test("should correctly read image with scanline filter", function (t) {
parseFile("accum.png", function (err, png) { parseFile("accum.png", function (err, png) {
@ -160,16 +182,16 @@ test("should correctly read image with scanline filter", function (t) {
t.equal(png.data.length, 1024 * 1024 * 4); t.equal(png.data.length, 1024 * 1024 * 4);
//t.equal(png.trailer.length, 0); //t.equal(png.trailer.length, 0);
t.equal(getPixel(png, 0, 0), 0xFF0000FF); t.equal(getPixel(png, 0, 0), 0xff0000ff);
t.equal(getPixel(png, 1, 0), 0xFF0000FF); t.equal(getPixel(png, 1, 0), 0xff0000ff);
t.equal(getPixel(png, 420, 308), 0xFF0029FF); t.equal(getPixel(png, 420, 308), 0xff0029ff);
t.equal(getPixel(png, 433, 308), 0x0A299DFF); t.equal(getPixel(png, 433, 308), 0x0a299dff);
t.equal(getPixel(png, 513, 308), 0x0066FFFF); t.equal(getPixel(png, 513, 308), 0x0066ffff);
t.equal(getPixel(png, 728, 552), 0xFF0047FF); t.equal(getPixel(png, 728, 552), 0xff0047ff);
t.end(); t.end();
}) });
}) });
test("should correctly read an indexed color image", function (t) { test("should correctly read an indexed color image", function (t) {
parseFile("indexed.png", function (err, png) { parseFile("indexed.png", function (err, png) {
@ -180,29 +202,28 @@ test("should correctly read an indexed color image", function (t) {
t.equal(png.data.length, 16 * 16 * 4); t.equal(png.data.length, 16 * 16 * 4);
//t.equal(png.trailer.length, 0); //t.equal(png.trailer.length, 0);
var y = 16, let y = 16,
x; x;
var isOk = true; let isOk = true;
while (y--) { while (y--) {
x = 16; x = 16;
while (x--) { while (x--) {
var expected; let expected;
if (x + y < 8) { if (x + y < 8) {
expected = 0xFF0000FF; expected = 0xff0000ff;
} else if (x + y < 16) { } else if (x + y < 16) {
expected = 0x00FF00FF; expected = 0x00ff00ff;
} else if (x + y < 24) { } else if (x + y < 24) {
expected = 0x0000FFFF; expected = 0x0000ffff;
} else { } else {
expected = 0x000000FF; expected = 0x000000ff;
} }
if (getPixel(png, x, y) !== expected) { if (getPixel(png, x, y) !== expected) {
t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + expected); t.fail(
"pixel does not match - " + getPixel(png, x, y) + " !== " + expected
);
isOk = false; isOk = false;
break; break;
} }
@ -222,32 +243,30 @@ test("should correctly read an indexed color image with alpha", function (t) {
t.equal(png.data.length, 16 * 16 * 4); t.equal(png.data.length, 16 * 16 * 4);
//t.equal(png.trailer.length, 0); //t.equal(png.trailer.length, 0);
var y = 16, let y = 16,
x; x;
var isOk = true; let isOk = true;
while (y--) { while (y--) {
x = 16; x = 16;
while (x--) { while (x--) {
var expected; let expected;
if (x >= 4 && x < 12) { if (x >= 4 && x < 12) {
expected = 0x00000000; expected = 0x00000000;
} else if (x + y < 8) { } else if (x + y < 8) {
expected = 0xFF0000FF; expected = 0xff0000ff;
} else if (x + y < 16) { } else if (x + y < 16) {
expected = 0x00FF00FF; expected = 0x00ff00ff;
} else if (x + y < 24) { } else if (x + y < 24) {
expected = 0x0000FFFF; expected = 0x0000ffff;
} else { } else {
expected = 0x000000FF; expected = 0x000000ff;
} }
if (getPixel(png, x, y) !== expected) { if (getPixel(png, x, y) !== expected) {
t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + expected); t.fail(
"pixel does not match - " + getPixel(png, x, y) + " !== " + expected
);
isOk = false; isOk = false;
break; break;
} }
@ -267,55 +286,99 @@ test("should correctly support crazily-filtered images", function (t) {
//t.equal(png.bpp, 4); //t.equal(png.bpp, 4);
t.equal(png.data.length, 512 * 512 * 4); t.equal(png.data.length, 512 * 512 * 4);
t.equal(getPixel(png, 0, 0), 0xFF000000) t.equal(getPixel(png, 0, 0), 0xff000000);
t.equal(getPixel(png, 1, 0), 0xFF000000) t.equal(getPixel(png, 1, 0), 0xff000000);
t.equal(getPixel(png, 0, 1), 0xFF000000) t.equal(getPixel(png, 0, 1), 0xff000000);
t.equal(getPixel(png, 2, 2), 0xFF000000) t.equal(getPixel(png, 2, 2), 0xff000000);
t.equal(getPixel(png, 0, 50), 0xFF000000) t.equal(getPixel(png, 0, 50), 0xff000000);
t.equal(getPixel(png, 219, 248), 0xFF000D00) t.equal(getPixel(png, 219, 248), 0xff000d00);
t.equal(getPixel(png, 220, 248), 0xFF000D00) t.equal(getPixel(png, 220, 248), 0xff000d00);
t.equal(getPixel(png, 215, 249), 0xFF000C00) t.equal(getPixel(png, 215, 249), 0xff000c00);
t.equal(getPixel(png, 216, 249), 0xFF000C00) t.equal(getPixel(png, 216, 249), 0xff000c00);
t.equal(getPixel(png, 217, 249), 0xFF000D00) t.equal(getPixel(png, 217, 249), 0xff000d00);
t.equal(getPixel(png, 218, 249), 0xFF000D00) t.equal(getPixel(png, 218, 249), 0xff000d00);
t.equal(getPixel(png, 219, 249), 0xFF000E00) t.equal(getPixel(png, 219, 249), 0xff000e00);
t.equal(getPixel(png, 220, 249), 0xFF000E00) t.equal(getPixel(png, 220, 249), 0xff000e00);
t.equal(getPixel(png, 263, 319), 0xFF002100) t.equal(getPixel(png, 263, 319), 0xff002100);
t.equal(getPixel(png, 145, 318), 0x05535A00) t.equal(getPixel(png, 145, 318), 0x05535a00);
t.equal(getPixel(png, 395, 286), 0x0007FF00) t.equal(getPixel(png, 395, 286), 0x0007ff00);
t.equal(getPixel(png, 152, 167), 0x052C3500) t.equal(getPixel(png, 152, 167), 0x052c3500);
t.equal(getPixel(png, 153, 167), 0x04303600) t.equal(getPixel(png, 153, 167), 0x04303600);
t.equal(getPixel(png, 154, 167), 0x042F3700) t.equal(getPixel(png, 154, 167), 0x042f3700);
t.equal(getPixel(png, 100, 168), 0xFF000400) t.equal(getPixel(png, 100, 168), 0xff000400);
t.equal(getPixel(png, 120, 168), 0xFF000900) t.equal(getPixel(png, 120, 168), 0xff000900);
t.equal(getPixel(png, 140, 168), 0xFF001B00) t.equal(getPixel(png, 140, 168), 0xff001b00);
t.equal(getPixel(png, 150, 168), 0x05313600) t.equal(getPixel(png, 150, 168), 0x05313600);
t.equal(getPixel(png, 152, 168), 0x04343C00) t.equal(getPixel(png, 152, 168), 0x04343c00);
t.equal(getPixel(png, 153, 168), 0x03343F00) t.equal(getPixel(png, 153, 168), 0x03343f00);
t.equal(getPixel(png, 154, 168), 0x03344100) t.equal(getPixel(png, 154, 168), 0x03344100);
t.equal(getPixel(png, 155, 168), 0x02344300) t.equal(getPixel(png, 155, 168), 0x02344300);
t.equal(getPixel(png, 156, 168), 0x02314400) t.equal(getPixel(png, 156, 168), 0x02314400);
t.equal(getPixel(png, 157, 168), 0x02323F00) t.equal(getPixel(png, 157, 168), 0x02323f00);
t.equal(getPixel(png, 158, 168), 0x03313900) t.equal(getPixel(png, 158, 168), 0x03313900);
t.end(); t.end();
}); });
}); });
test("should bail with an error given an invalid PNG", function (t) { test("should bail with an error given an invalid PNG", function (t) {
var buf = new Buffer("I AM NOT ACTUALLY A PNG", "utf8") let buf = Buffer.from("I AM NOT ACTUALLY A PNG", "utf8");
return parseBuffer(buf, function (err) {
t.ok(err instanceof Error, "Error should be received");
t.end();
})
})
test("should return an error if a PNG is normal except for a missing IEND", function (t) {
var buf = new Buffer("89504e470d0a1a0a0000000d49484452000000100000001008000000003a98a0bd000000017352474200aece1ce90000002174455874536f6674776172650047726170686963436f6e7665727465722028496e74656c297787fa190000008849444154789c448e4111c020100363010b58c00216b080052c60010b58c0c259c00216ae4d3b69df99dd0d1062caa5b63ee6b27d1c012996dceae86b6ef38398106acb65ae3e8edbbef780564b5e73743fdb409e1ef2f4803c3de4e901797ac8d3f3f0f490a7077ffffd03f5f507eaeb0fd4d71fa8af3f505f7fa0befe7c7dfdb9000000ffff0300c0fd7f8179301408", "hex")
return parseBuffer(buf, function (err) { return parseBuffer(buf, function (err) {
t.ok(err instanceof Error, "Error should be received"); t.ok(err instanceof Error, "Error should be received");
t.end(); t.end();
}); });
}); });
test("should bail with an error given an empty file", function (t) {
let buf = Buffer.from("");
return parseBuffer(buf, function (err) {
t.ok(err instanceof Error, "Error should be received");
t.end();
});
});
test("should bail with an error given a truncated PNG", function (t) {
let buf = Buffer.from("89504e470d0a1a0a000000", "hex");
return parseBuffer(buf, function (err) {
t.ok(err instanceof Error, "Error should be received");
t.end();
});
});
test("should return an error if a PNG is normal except for a missing IEND", function (t) {
let buf = Buffer.from(
"89504e470d0a1a0a0000000d49484452000000100000001008000000003a98a0bd000000017352474200aece1ce90000002174455874536f6674776172650047726170686963436f6e7665727465722028496e74656c297787fa190000008849444154789c448e4111c020100363010b58c00216b080052c60010b58c0c259c00216ae4d3b69df99dd0d1062caa5b63ee6b27d1c012996dceae86b6ef38398106acb65ae3e8edbbef780564b5e73743fdb409e1ef2f4803c3de4e901797ac8d3f3f0f490a7077ffffd03f5f507eaeb0fd4d71fa8af3f505f7fa0befe7c7dfdb9000000ffff0300c0fd7f8179301408",
"hex"
);
return parseBuffer(buf, function (err) {
t.ok(err instanceof Error, "Error should be received");
t.end();
});
});
test("should set alpha=true in metadata for images with tRNS chunk", function (t) {
fs.createReadStream(path.join(__dirname, "in", "tbbn0g04.png"))
.pipe(new PNG())
.on("metadata", function (metadata) {
t.ok(metadata.alpha, "Image should have alpha=true");
t.end();
});
});
test("Should parse with low highWaterMark", function (t) {
fs.createReadStream(path.join(__dirname, "in", "tbbn0g04.png"), {
highWaterMark: 2,
})
.pipe(new PNG())
.on("parsed", function () {
t.pass("Image should have parsed");
t.end();
})
.on("error", function (e) {
t.error(e, "Should not error");
});
});

View file

@ -1,24 +1,57 @@
require("./http-server"); const closeServer = require("./http-server");
const puppeteer = require("puppeteer");
const URL = "http://localhost:8000";
var path = require('path'); puppeteer
var childProcess = require('child_process'); .launch({
var phantomjs = require('phantomjs-prebuilt'); headless: true,
var binPath = phantomjs.path; args: ["--no-sandbox", "--disable-setuid-sandbox"],
})
var childArgs = [ .then(async (browser) => {
path.join(__dirname, 'phantom-compare.js') const page = await browser.newPage();
]; await page.goto(URL, { waitUntil: "networkidle0" });
const results = await page.evaluate(() => {
try { /* global window:false */
console.log("Comparing in PhantomJS"); try {
if (window.isFinished && window.isFinished()) {
childProcess.execFile(binPath, childArgs, function (err, stdout, stderr) { return window.results;
}
// handle results } catch (err) {
console.log("Comparison Test Results:"); console.log("Failed", err);
console.log(stdout); }
process.exit(err ? 1 : 0); });
console.log("Comparing in Chrome");
console.log("Comparison Test Results:");
await browser.close();
if (results) {
let success = true;
let successes = [],
failures = [];
for (let i = 0; i < results.length; i++) {
let result = results[i];
if (result.success) {
successes.push(result.name);
} else {
failures.push(result.name);
}
success = success && result.success;
}
console.log("Success:", successes.join(", "));
if (failures.length) {
console.log("Failure:", failures.join(", "));
if (failures.length > 10) {
console.error("failures higher than expected");
process.exitCode = 1;
}
}
} else {
process.exitCode = 1;
}
})
.catch((error) => {
console.error(error);
process.exitCode = 1;
})
.finally(() => {
closeServer();
}); });
} catch (e) {
console.log("Error starting phantomjs");
}

3254
yarn.lock Normal file

File diff suppressed because it is too large Load diff