Compare commits

..

No commits in common. "master" and "v2.3.0" have entirely different histories.

45 changed files with 1747 additions and 6202 deletions

View file

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

View file

@ -1,19 +1,183 @@
{ {
"parserOptions": { "ecmaVersion": 2018 }, "ecmaFeatures": {},
"extends": ["eslint:recommended", "prettier"], "rules": {
"env": { "no-alert": 2,
"node": true, "no-array-constructor": 0,
"es6": true "no-bitwise": 0,
}, "no-caller": 2,
"rules": { "no-catch-shadow": 2,
"no-var": "error" "no-class-assign": 2,
}, "no-cond-assign": 2,
"overrides": [ "no-console": 2,
{ "no-const-assign": 2,
"files": ["test/*.js", "examples/*.js"], "no-constant-condition": 2,
"rules": { "no-continue": 0,
"no-console": "off" "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": {
"node": true,
"es6": true
} }
]
} }

2
.gitignore vendored
View file

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

View file

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

View file

@ -1,9 +1,7 @@
language: node_js language: node_js
after_success: after_success:
- if [ "$TRAVIS_NODE_VERSION" = "10" ]; then yarn coverage && yarn codecov; fi - test $TRAVIS_NODE_VERSION = '4.3' && npm run coverage && npm install coveralls@2 && npm run coveralls
node_js: node_js:
- "10" - "0.10"
- "12" - "0.12"
branches: - "4.1"
only:
- master

361
README.md
View file

@ -1,57 +1,52 @@
[![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) [![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)
# pngjs pngjs
========
*This is a fork of pngjs to make it work with deno* Simple PNG encoder/decoder for Node.js with no dependencies.
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 0 (grayscale), colortype 2 (RGB), colortype 4 (grayscale alpha) and colortype 6 (RGBA) * Support for writing colortype 2 (RGB) 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 colortype 3 (indexed color) * Writing in different formats, colortype 0 (greyscale), colortype 3 (indexed color), colortype 4 (greyscale with alpha)
* Synchronous write
# Table of Contents Requirements
============
- [Requirements](#requirements) * Async - Node.js 0.10 / 0.12 / IO.js
- [Comparison Table](#comparison-table) * Sync - Node.js 0.12 / IO.js (0.10 with [node-zlib-backport](https://www.npmjs.com/package/node-zlib-backport) dependency)
- [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
- png Tests
- 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.
@ -59,84 +54,72 @@ 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.
# Installation 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
===============
``` ```
$ npm install pngjs --save $ npm install pngjs --save
``` ```
# Browser Example
==========
The package has been build with a [Browserify](browserify.org) version (`npm run browserify`) and you can use the browser version by including in your code:
```
import { PNG } from 'pngjs/browser';
```
# Example
```js ```js
var fs = require("fs"), var fs = require('fs'),
PNG = require("pngjs").PNG; PNG = require('pngjs').PNG;
fs.createReadStream("in.png") fs.createReadStream('in.png')
.pipe( .pipe(new PNG({
new PNG({ filterType: 4
filterType: 4, }))
}) .on('parsed', function() {
)
.on("parsed", function () {
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x) << 2;
// invert color for (var y = 0; y < this.height; y++) {
this.data[idx] = 255 - this.data[idx]; for (var x = 0; x < this.width; x++) {
this.data[idx + 1] = 255 - this.data[idx + 1]; var idx = (this.width * y + x) << 2;
this.data[idx + 2] = 255 - this.data[idx + 2];
// and reduce opacity // invert color
this.data[idx + 3] = this.data[idx + 3] >> 1; this.data[idx] = 255 - this.data[idx];
} this.data[idx+1] = 255 - this.data[idx+1];
} this.data[idx+2] = 255 - this.data[idx+2];
this.pack().pipe(fs.createWriteStream("out.png")); // and reduce opacity
}); this.data[idx+3] = this.data[idx+3] >> 1;
}
}
this.pack().pipe(fs.createWriteStream('out.png'));
});
``` ```
For more examples see `examples` folder. For more examples see `examples` folder.
# 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 deflate (default: 9) - `deflateLevel` - compression level for delate (default: 9)
- `deflateStrategy` - compression strategy for deflate (default: 3) - `deflateStrategy` - compression strategy for delate (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. 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. - `colorType` - the output colorType - see constants. 2 = color, no alpha, 6 = color & alpha. Default currently 6, but in the future may calculate best mode.
- `inputColorType` - the input colorType - see constants. Default is 6 (RGBA) - `inputHasAlpha` - whether the input bitmap has 4 bits per pixel (rgb and alpha) or 3 (rgb - no alpha).
- `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
@ -144,17 +127,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) { }` `function(data) { }`
Input image has been completely parsed, `data` is complete and ready for modification. Input image has been completly 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
@ -163,39 +146,36 @@ 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.
@ -203,54 +183,51 @@ 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 alpha (RGBA to RGB) ## Packing a PNG and removing alpga (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( .pipe(new PNG({
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"));
});
``` ```
# Sync API # Sync API
@ -273,8 +250,7 @@ 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 options = { colorType: 6 }; var buffer = PNG.sync.write(png);
var buffer = PNG.sync.write(png, options);
fs.writeFileSync('out.png', buffer); fs.writeFileSync('out.png', buffer);
``` ```
@ -288,121 +264,58 @@ var png = PNG.sync.read(data);
PNG.adjustGamma(png); PNG.adjustGamma(png);
``` ```
# Changelog
### 5.0.0 - 15/04/2020 Changelog
============
- 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
- Add sync write api - Fix newfile example
- Fix newfile example - Correct comparison table
- Correct comparison table
### 2.1.0 - 28/10/2015 ### 2.1.0 - 28/10/2015
- rename package to pngjs
- rename package to pngjs - added 'bgColor' option
- added 'bgColor' option
### 2.0.0 - 08/10/2015 ### 2.0.0 - 08/10/2015
- fixes to readme
- fixes to readme - *breaking change* - bitblt on the png prototype now doesn't take a unused, unnecessary src first argument
- _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
- More tests - source linted
- source linted - maintainability refactorings
- maintainability refactorings - async API - exceptions in reading now emit warnings
- async API - exceptions in reading now emit warnings - documentation improvement - sync api now documented, adjustGamma documented
- 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.
- 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
- Error handling fixes - ignore files for smaller npm footprint
- 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.
- Initial release, see pngjs for older changelog. License
=========
# License
(The MIT License) (The MIT License)

View file

@ -4,8 +4,8 @@ clone_depth: 10
environment: environment:
matrix: matrix:
- nodejs_version: "10" - nodejs_version: '0.12'
- nodejs_version: "12" - nodejs_version: ''
install: install:
- ps: Install-Product node $env:nodejs_version - ps: Install-Product node $env:nodejs_version
@ -18,11 +18,7 @@ test_script:
- npm test - npm test
cache: cache:
- node_modules # local npm modules - node_modules # local npm modules
matrix: matrix:
fast_finish: true fast_finish: true
branches:
only:
- master

View file

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

View file

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

View file

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

View file

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

View file

@ -1,125 +1,53 @@
import interlaceUtils from "./interlace.js"; 'use strict';
let pixelBppMapper = [ var interlaceUtils = require('./interlace');
// 0 - dummy entry
function () {},
// 1 - L var pixelBppMap = {
// 0: 0, 1: 0, 2: 0, 3: 0xff 1: { // L
function (pxData, data, pxPos, rawPos) { 0: 0,
if (rawPos === data.length) { 1: 0,
throw new Error("Ran out of data"); 2: 0,
} 3: 0xff
let pixel = data[rawPos];
pxData[pxPos] = pixel;
pxData[pxPos + 1] = pixel;
pxData[pxPos + 2] = pixel;
pxData[pxPos + 3] = 0xff;
}, },
2: { // LA
// 2 - LA 0: 0,
// 0: 0, 1: 0, 2: 0, 3: 1 1: 0,
function (pxData, data, pxPos, rawPos) { 2: 0,
if (rawPos + 1 >= data.length) { 3: 1
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
// 3 - RGB 0: 0,
// 0: 0, 1: 1, 2: 2, 3: 0xff 1: 1,
function (pxData, data, pxPos, rawPos) { 2: 2,
if (rawPos + 2 >= data.length) { 3: 0xff
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
// 4 - RGBA 0: 0,
// 0: 0, 1: 1, 2: 2, 3: 3 1: 1,
function (pxData, data, pxPos, rawPos) { 2: 2,
if (rawPos + 3 >= data.length) { 3: 3
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 = [];
let i = 0; var leftOver = [];
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');
} }
let byte = data[i]; var byte = data[i];
i++; i++;
let byte8, byte7, byte6, byte5, byte4, byte3, byte2, byte1; var 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;
@ -128,136 +56,135 @@ 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();
} }
let returner = leftOver.slice(0, count); var 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) { function mapImage8Bit(image, pxData, getPxPos, bpp, data, rawPos) { // eslint-disable-line max-params
// eslint-disable-line max-params var imageWidth = image.width;
let imageWidth = image.width; var imageHeight = image.height;
let imageHeight = image.height; var imagePass = image.index;
let imagePass = image.index; for (var y = 0; y < imageHeight; y++) {
for (let y = 0; y < imageHeight; y++) { for (var x = 0; x < imageWidth; x++) {
for (let x = 0; x < imageWidth; x++) { var pxPos = getPxPos(x, y, imagePass);
let pxPos = getPxPos(x, y, imagePass);
pixelBppMapper[bpp](pxData, data, pxPos, rawPos); for (var i = 0; i < 4; i++) {
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) { function mapImageCustomBit(image, pxData, getPxPos, bpp, bits, maxBit) { // eslint-disable-line max-params
// eslint-disable-line max-params var imageWidth = image.width;
let imageWidth = image.width; var imageHeight = image.height;
let imageHeight = image.height; var imagePass = image.index;
let imagePass = image.index; for (var y = 0; y < imageHeight; y++) {
for (let y = 0; y < imageHeight; y++) { for (var x = 0; x < imageWidth; x++) {
for (let x = 0; x < imageWidth; x++) { var pixelData = bits.get(bpp);
let pixelData = bits.get(bpp); var pxPos = getPxPos(x, y, imagePass);
let pxPos = getPxPos(x, y, imagePass);
pixelBppCustomMapper[bpp](pxData, pixelData, pxPos, maxBit); for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[bpp][i];
pxData[pxPos + i] = idx !== 0xff ? pixelData[idx] : maxBit;
}
} }
bits.resetAfterLine(); bits.resetAfterLine();
} }
} }
export default function (data, bitmapInfo) { exports.dataToBitMap = function(data, bitmapInfo) {
let width = bitmapInfo.width;
let height = bitmapInfo.height; var width = bitmapInfo.width;
let depth = bitmapInfo.depth; var height = bitmapInfo.height;
let bpp = bitmapInfo.bpp; var depth = bitmapInfo.depth;
let interlace = bitmapInfo.interlace; var bpp = bitmapInfo.bpp;
let bits; var interlace = bitmapInfo.interlace;
if (depth !== 8) { if (depth !== 8) {
bits = bitRetriever(data, depth); var bits = bitRetriever(data, depth);
} }
let pxData; var pxData;
if (depth <= 8) { if (depth <= 8) {
pxData = Buffer.alloc(width * height * 4); pxData = new Buffer(width * height * 4);
} else { }
else {
pxData = new Uint16Array(width * height * 4); pxData = new Uint16Array(width * height * 4);
} }
let maxBit = Math.pow(2, depth) - 1; var maxBit = Math.pow(2, depth) - 1;
let rawPos = 0; var rawPos = 0;
let images; var images;
let getPxPos; var 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 { }
let nonInterlacedPxPos = 0; else {
getPxPos = function () { var nonInterlacedPxPos = 0;
let returner = nonInterlacedPxPos; getPxPos = function() {
var returner = nonInterlacedPxPos;
nonInterlacedPxPos += 4; nonInterlacedPxPos += 4;
return returner; return returner;
}; };
images = [{ width: width, height: height }]; images = [{ width: width, height: height }];
} }
for (let imageIndex = 0; imageIndex < images.length; imageIndex++) { for (var imageIndex = 0; imageIndex < images.length; imageIndex++) {
if (depth === 8) { if (depth === 8) {
rawPos = mapImage8Bit( rawPos = mapImage8Bit(images[imageIndex], pxData, getPxPos, bpp, data, rawPos);
images[imageIndex], }
pxData, else {
getPxPos, mapImageCustomBit(images[imageIndex], pxData, getPxPos, bpp, bits, maxBit);
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,150 +1,58 @@
import constants from "./constants.js"; 'use strict';
export default function (dataIn, width, height, options) { var constants = require('./constants');
let outHasAlpha =
[constants.COLORTYPE_COLOR_ALPHA, constants.COLORTYPE_ALPHA].indexOf( module.exports = function(data, width, height, options) {
options.colorType var outHasAlpha = options.colorType === constants.COLORTYPE_COLOR_ALPHA;
) !== -1; if (options.inputHasAlpha && outHasAlpha) {
if (options.colorType === options.inputColorType) { return data;
let bigEndian = (function () { }
let buffer = new ArrayBuffer(2); if (!options.inputHasAlpha && !outHasAlpha) {
new DataView(buffer).setInt16(0, 256, true /* littleEndian */); return data;
// Int16Array uses the platform's endianness.
return new Int16Array(buffer)[0] !== 256;
})();
// If no need to convert to grayscale and alpha is present/absent in both, take a fast route
if (options.bitDepth === 8 || (options.bitDepth === 16 && bigEndian)) {
return dataIn;
}
} }
// map to a UInt16 array if data is 16bit, fix endianness below var outBpp = outHasAlpha ? 4 : 3;
let data = options.bitDepth !== 16 ? dataIn : new Uint16Array(dataIn.buffer); var outData = new Buffer(width * height * outBpp);
var inBpp = options.inputHasAlpha ? 4 : 3;
var inIndex = 0;
var outIndex = 0;
let maxValue = 255; var bgColor = options.bgColor || {};
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 = maxValue; bgColor.red = 255;
} }
if (bgColor.green === undefined) { if (bgColor.green === undefined) {
bgColor.green = maxValue; bgColor.green = 255;
} }
if (bgColor.blue === undefined) { if (bgColor.blue === undefined) {
bgColor.blue = maxValue; bgColor.blue = 255;
} }
function getRGBA() { for (var y = 0; y < height; y++) {
let red; for (var x = 0; x < width; x++) {
let green; var red = data[inIndex];
let blue; var green = data[inIndex + 1];
let alpha = maxValue; var blue = data[inIndex + 2];
switch (options.inputColorType) {
case constants.COLORTYPE_COLOR_ALPHA: var alpha;
if (options.inputHasAlpha) {
alpha = data[inIndex + 3]; alpha = data[inIndex + 3];
red = data[inIndex]; if (!outHasAlpha) {
green = data[inIndex + 1]; alpha /= 255;
blue = data[inIndex + 2]; red = Math.min(Math.max(Math.round((1 - alpha) * bgColor.red + alpha * red), 0), 255);
break; green = Math.min(Math.max(Math.round((1 - alpha) * bgColor.green + alpha * green), 0), 255);
case constants.COLORTYPE_COLOR: blue = Math.min(Math.max(Math.round((1 - alpha) * bgColor.blue + alpha * blue), 0), 255);
red = data[inIndex];
green = data[inIndex + 1];
blue = data[inIndex + 2];
break;
case constants.COLORTYPE_ALPHA:
alpha = data[inIndex + 1];
red = data[inIndex];
green = red;
blue = red;
break;
case constants.COLORTYPE_GRAYSCALE:
red = data[inIndex];
green = red;
blue = red;
break;
default:
throw new Error(
"input color type:" +
options.inputColorType +
" is not supported at present"
);
}
if (options.inputHasAlpha) {
if (!outHasAlpha) {
alpha /= maxValue;
red = Math.min(
Math.max(Math.round((1 - alpha) * bgColor.red + alpha * red), 0),
maxValue
);
green = Math.min(
Math.max(Math.round((1 - alpha) * bgColor.green + alpha * green), 0),
maxValue
);
blue = Math.min(
Math.max(Math.round((1 - alpha) * bgColor.blue + alpha * blue), 0),
maxValue
);
}
}
return { red: red, green: green, blue: blue, alpha: alpha };
}
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let rgba = getRGBA(data, inIndex);
switch (options.colorType) {
case constants.COLORTYPE_COLOR_ALPHA:
case constants.COLORTYPE_COLOR:
if (options.bitDepth === 8) {
outData[outIndex] = rgba.red;
outData[outIndex + 1] = rgba.green;
outData[outIndex + 2] = rgba.blue;
if (outHasAlpha) {
outData[outIndex + 3] = rgba.alpha;
}
} else {
outData.writeUInt16BE(rgba.red, outIndex);
outData.writeUInt16BE(rgba.green, outIndex + 2);
outData.writeUInt16BE(rgba.blue, outIndex + 4);
if (outHasAlpha) {
outData.writeUInt16BE(rgba.alpha, outIndex + 6);
}
}
break;
case constants.COLORTYPE_ALPHA:
case constants.COLORTYPE_GRAYSCALE: {
// Convert to grayscale and alpha
let grayscale = (rgba.red + rgba.green + rgba.blue) / 3;
if (options.bitDepth === 8) {
outData[outIndex] = grayscale;
if (outHasAlpha) {
outData[outIndex + 1] = rgba.alpha;
}
} else {
outData.writeUInt16BE(grayscale, outIndex);
if (outHasAlpha) {
outData.writeUInt16BE(rgba.alpha, outIndex + 2);
}
}
break;
} }
default: }
throw new Error("unrecognised color Type " + options.colorType); else {
alpha = 255;
}
outData[outIndex] = red;
outData[outIndex + 1] = green;
outData[outIndex + 2] = blue;
if (outHasAlpha) {
outData[outIndex + 3] = alpha;
} }
inIndex += inBpp; inIndex += inBpp;

View file

@ -1,7 +1,11 @@
let util = require("util"); 'use strict';
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 = [];
@ -10,46 +14,45 @@ let ChunkStream = 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( process.nextTick(function() {
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 && this._reads.length > 0) { if (this._paused && 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;
} }
let dataBuffer; var dataBuffer;
if (Buffer.isBuffer(data)) { if (Buffer.isBuffer(data)) {
dataBuffer = data; dataBuffer = data;
} else { }
dataBuffer = Buffer.from(data, encoding || this._encoding); else {
dataBuffer = new Buffer(data, encoding || this._encoding);
} }
this._buffers.push(dataBuffer); this._buffers.push(dataBuffer);
@ -65,7 +68,8 @@ 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);
} }
@ -80,7 +84,8 @@ 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();
} }
@ -88,15 +93,19 @@ 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", new Error("Unexpected end of input")); this.emit('error',
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;
} }
@ -105,23 +114,26 @@ 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
let smallerBuf = this._buffers[0]; var 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
@ -130,17 +142,18 @@ ChunkStream.prototype._processReadAllowingLess = function (read) {
} }
}; };
ChunkStream.prototype._processRead = function (read) { ChunkStream.prototype._processRead = function(read) {
this._reads.shift(); // == read this._reads.shift(); // == read
let pos = 0; var pos = 0;
let count = 0; var count = 0;
let data = Buffer.alloc(read.length); var data = new Buffer(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++];
let len = Math.min(buf.length, read.length - pos); var buf = this._buffers[count++];
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;
@ -161,30 +174,36 @@ 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.writable) { if (this._buffers && this._buffers.length > 0 && this._buffers[0] === null) {
this._end(); this._end();
} }
} catch (ex) { }
this.emit("error", ex); catch (ex) {
this.emit('error', ex);
} }
}; };

View file

@ -1,4 +1,8 @@
export default { 'use strict';
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,
@ -23,8 +27,8 @@ export default {
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,39 +1,43 @@
let crcTable = []; 'use strict';
(function () { var crcTable = [];
for (let i = 0; i < 256; i++) {
let currentCrc = i; (function() {
for (let j = 0; j < 8; j++) { for (var i = 0; i < 256; i++) {
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;
} }
})(); }());
let CrcCalculator = function () { var CrcCalculator = module.exports = function() {
this._crc = -1; this._crc = -1;
}; };
export default CrcCalculator; CrcCalculator.prototype.write = function(data) {
CrcCalculator.prototype.write = function (data) { for (var i = 0; i < data.length; i++) {
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) {
let crc = -1; CrcCalculator.crc32 = function(buf) {
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,35 +1,40 @@
import paethPredictor from "./paeth-predictor.js"; 'use strict';
var paethPredictor = require('./paeth-predictor');
function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) { function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) {
for (let x = 0; x < byteWidth; x++) { pxData.copy(rawData, rawPos, pxPos, pxPos + byteWidth);
rawData[rawPos + x] = pxData[pxPos + x];
}
} }
function filterSumNone(pxData, pxPos, byteWidth) { function filterSumNone(pxData, pxPos, byteWidth) {
let sum = 0;
let length = pxPos + byteWidth;
for (let i = pxPos; i < length; i++) { var sum = 0;
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++) {
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0; for (var x = 0; x < byteWidth; x++) {
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;
for (let x = 0; x < byteWidth; x++) { var sum = 0;
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0; for (var x = 0; x < byteWidth; x++) {
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);
} }
@ -38,20 +43,24 @@ 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++) {
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0; for (var x = 0; x < byteWidth; x++) {
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;
let length = pxPos + byteWidth; var sum = 0;
for (let x = pxPos; x < length; x++) { var length = pxPos + byteWidth;
let up = pxPos > 0 ? pxData[x - byteWidth] : 0; for (var x = pxPos; x < length; x++) {
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);
} }
@ -60,21 +69,25 @@ 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++) {
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0; for (var x = 0; x < byteWidth; x++) {
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
let val = pxData[pxPos + x] - ((left + up) >> 1); var left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
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;
for (let x = 0; x < byteWidth; x++) { var sum = 0;
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0; for (var x = 0; x < byteWidth; x++) {
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
let val = pxData[pxPos + x] - ((left + up) >> 1); var left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
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);
} }
@ -83,25 +96,26 @@ 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++) {
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0; for (var x = 0; x < byteWidth; x++) {
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
let upleft = var left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0; var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
let val = pxData[pxPos + x] - paethPredictor(left, up, upleft); var upleft = pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
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) {
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 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);
sum += Math.abs(val); sum += Math.abs(val);
} }
@ -109,49 +123,49 @@ function filterSumPaeth(pxData, pxPos, byteWidth, bpp) {
return sum; return sum;
} }
let filters = { var filters = {
0: filterNone, 0: filterNone,
1: filterSub, 1: filterSub,
2: filterUp, 2: filterUp,
3: filterAvg, 3: filterAvg,
4: filterPaeth, 4: filterPaeth
}; };
let filterSums = { var filterSums = {
0: filterSumNone, 0: filterSumNone,
1: filterSumSub, 1: filterSumSub,
2: filterSumUp, 2: filterSumUp,
3: filterSumAvg, 3: filterSumAvg,
4: filterSumPaeth, 4: filterSumPaeth
}; };
export default function (pxData, width, height, options, bpp) { module.exports = function(pxData, width, height, options, bpp) {
let filterTypes;
if (!("filterType" in options) || options.filterType === -1) { var filterTypes;
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 { }
throw new Error("unrecognised filter types"); else {
throw new Error('unrecognised filter types');
} }
if (options.bitDepth === 16) { var byteWidth = width * bpp;
bpp *= 2; var rawPos = 0;
} var pxPos = 0;
let byteWidth = width * bpp; var rawData = new Buffer((byteWidth + 1) * height);
let rawPos = 0; var sel = filterTypes[0];
let pxPos = 0;
let rawData = Buffer.alloc((byteWidth + 1) * height);
let sel = filterTypes[0]; for (var y = 0; y < height; y++) {
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)
let min = Infinity; var min = Infinity;
for (let i = 0; i < filterTypes.length; i++) { for (var i = 0; i < filterTypes.length; i++) {
let sum = filterSums[filterTypes[i]](pxData, pxPos, byteWidth, bpp); var 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,24 +1,25 @@
import ChunkStream from "./chunkstream.js"; 'use strict';
import Filter from "./filter-parse.js";
let util = require("util");
let FilterAsync = function (bitmapInfo) { var util = require('util');
var ChunkStream = require('./chunkstream');
var Filter = require('./filter-parse');
var FilterAsync = module.exports = function(bitmapInfo) {
ChunkStream.call(this); ChunkStream.call(this);
let buffers = []; var buffers = [];
let that = this; var 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,19 +1,24 @@
import SyncReader from "./sync-reader.js"; 'use strict';
import Filter from "./filter-parse.js";
export default function (inBuffer, bitmapInfo) { var SyncReader = require('./sync-reader');
let outBuffers = []; var Filter = require('./filter-parse');
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();
reader.process(); reader.process();
return Buffer.concat(outBuffers); return Buffer.concat(outBuffers);
}; };

View file

@ -1,20 +1,23 @@
import interlaceUtils from "./interlace.js"; 'use strict';
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) {
let byteWidth = width * bpp; var 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;
} }
let Filter = function (bitmapInfo, dependencies) { var Filter = module.exports = function(bitmapInfo, dependencies) {
let width = bitmapInfo.width;
let height = bitmapInfo.height; var width = bitmapInfo.width;
let interlace = bitmapInfo.interlace; var height = bitmapInfo.height;
let bpp = bitmapInfo.bpp; var interlace = bitmapInfo.interlace;
let depth = bitmapInfo.depth; var bpp = bitmapInfo.bpp;
var depth = bitmapInfo.depth;
this.read = dependencies.read; this.read = dependencies.read;
this.write = dependencies.write; this.write = dependencies.write;
@ -23,19 +26,20 @@ let Filter = function (bitmapInfo, dependencies) {
this._imageIndex = 0; this._imageIndex = 0;
this._images = []; this._images = [];
if (interlace) { if (interlace) {
let passes = interlaceUtils.getImagePasses(width, height); var passes = interlaceUtils.getImagePasses(width, height);
for (let i = 0; i < passes.length; i++) { for (var 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
}); });
} }
@ -45,98 +49,86 @@ let Filter = 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;
} }
}; };
export default Filter; Filter.prototype.start = function() {
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 ( Filter.prototype._unFilterType1 = function(rawData, unfilteredLine, byteWidth) {
rawData,
unfilteredLine,
byteWidth
) {
let xComparison = this._xComparison;
let xBiggerThan = xComparison - 1;
for (let x = 0; x < byteWidth; x++) { var xComparison = this._xComparison;
let rawByte = rawData[1 + x]; var xBiggerThan = xComparison - 1;
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 ( Filter.prototype._unFilterType2 = function(rawData, unfilteredLine, byteWidth) {
rawData,
unfilteredLine,
byteWidth
) {
let lastLine = this._lastLine;
for (let x = 0; x < byteWidth; x++) { var lastLine = this._lastLine;
let rawByte = rawData[1 + x];
let f2Up = lastLine ? lastLine[x] : 0; for (var x = 0; x < byteWidth; x++) {
var rawByte = rawData[1 + x];
var f2Up = lastLine ? lastLine[x] : 0;
unfilteredLine[x] = rawByte + f2Up; unfilteredLine[x] = rawByte + f2Up;
} }
}; };
Filter.prototype._unFilterType3 = function ( Filter.prototype._unFilterType3 = function(rawData, unfilteredLine, byteWidth) {
rawData,
unfilteredLine,
byteWidth
) {
let xComparison = this._xComparison;
let xBiggerThan = xComparison - 1;
let lastLine = this._lastLine;
for (let x = 0; x < byteWidth; x++) { var xComparison = this._xComparison;
let rawByte = rawData[1 + x]; var xBiggerThan = xComparison - 1;
let f3Up = lastLine ? lastLine[x] : 0; var lastLine = this._lastLine;
let f3Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
let f3Add = Math.floor((f3Left + f3Up) / 2); for (var x = 0; x < byteWidth; x++) {
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 ( Filter.prototype._unFilterType4 = function(rawData, unfilteredLine, byteWidth) {
rawData,
unfilteredLine,
byteWidth
) {
let xComparison = this._xComparison;
let xBiggerThan = xComparison - 1;
let lastLine = this._lastLine;
for (let x = 0; x < byteWidth; x++) { var xComparison = this._xComparison;
let rawByte = rawData[1 + x]; var xBiggerThan = xComparison - 1;
let f4Up = lastLine ? lastLine[x] : 0; var lastLine = this._lastLine;
let f4Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
let f4UpLeft = x > xBiggerThan && lastLine ? lastLine[x - xComparison] : 0; for (var x = 0; x < byteWidth; x++) {
let f4Add = paethPredictor(f4Left, f4Up, f4UpLeft); var rawByte = rawData[1 + x];
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];
let unfilteredLine; var filter = rawData[0];
let currentImage = this._images[this._imageIndex]; var unfilteredLine;
let byteWidth = currentImage.byteWidth; var currentImage = this._images[this._imageIndex];
var byteWidth = currentImage.byteWidth;
if (filter === 0) { if (filter === 0) {
unfilteredLine = rawData.slice(1, byteWidth + 1); unfilteredLine = rawData.slice(1, byteWidth + 1);
} else { }
unfilteredLine = Buffer.alloc(byteWidth); else {
unfilteredLine = new Buffer(byteWidth);
switch (filter) { switch (filter) {
case 1: case 1:
@ -152,7 +144,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);
} }
} }
@ -163,14 +155,16 @@ 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,15 +1,17 @@
'use strict';
function dePalette(indata, outdata, width, height, palette) { function dePalette(indata, outdata, width, height, palette) {
let pxPos = 0; var pxPos = 0;
// use values from palette // use values from palette
for (let y = 0; y < height; y++) { for (var y = 0; y < height; y++) {
for (let x = 0; x < width; x++) { for (var x = 0; x < width; x++) {
let color = palette[indata[pxPos]]; var 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 (let i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
outdata[pxPos + i] = color[i]; outdata[pxPos + i] = color[i];
} }
pxPos += 4; pxPos += 4;
@ -18,24 +20,21 @@ function dePalette(indata, outdata, width, height, palette) {
} }
function replaceTransparentColor(indata, outdata, width, height, transColor) { function replaceTransparentColor(indata, outdata, width, height, transColor) {
let pxPos = 0; var pxPos = 0;
for (let y = 0; y < height; y++) { for (var y = 0; y < height; y++) {
for (let x = 0; x < width; x++) { for (var x = 0; x < width; x++) {
let makeTrans = false; var 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 ( }
transColor[0] === indata[pxPos] && else if (transColor[0] === indata[pxPos] && transColor[1] === indata[pxPos + 1] && transColor[2] === indata[pxPos + 2]) {
transColor[1] === indata[pxPos + 1] &&
transColor[2] === indata[pxPos + 2]
) {
makeTrans = true; makeTrans = true;
} }
if (makeTrans) { if (makeTrans) {
for (let i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
outdata[pxPos + i] = 0; outdata[pxPos + i] = 0;
} }
} }
@ -45,36 +44,35 @@ function replaceTransparentColor(indata, outdata, width, height, transColor) {
} }
function scaleDepth(indata, outdata, width, height, depth) { function scaleDepth(indata, outdata, width, height, depth) {
let maxOutSample = 255; var maxOutSample = 255;
let maxInSample = Math.pow(2, depth) - 1; var maxInSample = Math.pow(2, depth) - 1;
let pxPos = 0; var pxPos = 0;
for (let y = 0; y < height; y++) { for (var y = 0; y < height; y++) {
for (let x = 0; x < width; x++) { for (var x = 0; x < width; x++) {
for (let i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
outdata[pxPos + i] = Math.floor( outdata[pxPos + i] = Math.floor((indata[pxPos + i] * maxOutSample) / maxInSample + 0.5);
(indata[pxPos + i] * maxOutSample) / maxInSample + 0.5
);
} }
pxPos += 4; pxPos += 4;
} }
} }
} }
export default function (indata, imageData) { module.exports = function(indata, imageData) {
let depth = imageData.depth;
let width = imageData.width;
let height = imageData.height;
let colorType = imageData.colorType;
let transColor = imageData.transColor;
let palette = imageData.palette;
let outdata = indata; // only different for 16 bits var depth = imageData.depth;
var width = imageData.width;
var height = imageData.height;
var colorType = imageData.colorType;
var transColor = imageData.transColor;
var palette = imageData.palette;
if (colorType === 3) { var outdata = indata; // only different for 16 bits
// 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);
} }
@ -82,7 +80,7 @@ export default 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 = Buffer.alloc(width * height * 4); outdata = new Buffer(width * height * 4);
} }
scaleDepth(indata, outdata, width, height, depth); scaleDepth(indata, outdata, width, height, depth);
} }

View file

@ -1,3 +1,5 @@
'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
@ -9,65 +11,61 @@
// 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]
}, }
]; ];
export var getImagePasses = function (width, height) { exports.getImagePasses = function(width, height) {
let images = []; var images = [];
let xLeftOver = width % 8; var xLeftOver = width % 8;
let yLeftOver = height % 8; var yLeftOver = height % 8;
let xRepeats = (width - xLeftOver) / 8; var xRepeats = (width - xLeftOver) / 8;
let yRepeats = (height - yLeftOver) / 8; var yRepeats = (height - yLeftOver) / 8;
for (let i = 0; i < imagePasses.length; i++) { for (var i = 0; i < imagePasses.length; i++) {
let pass = imagePasses[i]; var pass = imagePasses[i];
let passWidth = xRepeats * pass.x.length; var passWidth = xRepeats * pass.x.length;
let passHeight = yRepeats * pass.y.length; var passHeight = yRepeats * pass.y.length;
for (let j = 0; j < pass.x.length; j++) { for (var j = 0; j < pass.x.length; j++) {
if (pass.x[j] < xLeftOver) { if (pass.x[j] < xLeftOver) {
passWidth++; passWidth++;
} else { }
else {
break; break;
} }
} }
for (let j = 0; j < pass.y.length; j++) { for (j = 0; j < pass.y.length; j++) {
if (pass.y[j] < yLeftOver) { if (pass.y[j] < yLeftOver) {
passHeight++; passHeight++;
} else { }
else {
break; break;
} }
} }
@ -78,21 +76,12 @@ export var getImagePasses = function (width, height) {
return images; return images;
}; };
export var getInterlaceIterator = function (width) { exports.getInterlaceIterator = function(width) {
return function (x, y, pass) { return function(x, y, pass) {
let outerXLeftOver = x % imagePasses[pass].x.length; var outerXLeftOver = x % imagePasses[pass].x.length;
let outerX = var outerX = (((x - outerXLeftOver) / imagePasses[pass].x.length) * 8) + imagePasses[pass].x[outerXLeftOver];
((x - outerXLeftOver) / imagePasses[pass].x.length) * 8 + var outerYLeftOver = y % imagePasses[pass].y.length;
imagePasses[pass].x[outerXLeftOver]; var outerY = (((y - outerYLeftOver) / imagePasses[pass].y.length) * 8) + imagePasses[pass].y[outerYLeftOver];
let outerYLeftOver = y % imagePasses[pass].y.length; return (outerX * 4) + (outerY * width * 4);
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,13 +1,14 @@
let util = require("util"); 'use strict';
let Stream = require("stream");
import constants from "./constants.js"; var util = require('util');
import Packer from "./packer.js"; var Stream = require('stream');
var constants = require('./constants');
var Packer = require('./packer');
let PackerAsync = function (opt) { var PackerAsync = module.exports = function(opt) {
Stream.call(this); Stream.call(this);
let options = opt || {}; var options = opt || {};
this._packer = new Packer(options); this._packer = new Packer(options);
this._deflate = this._packer.createDeflate(); this._deflate = this._packer.createDeflate();
@ -16,36 +17,29 @@ let PackerAsync = 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", Buffer.from(constants.PNG_SIGNATURE)); this.emit('data', new Buffer(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));
} }
let filteredData = this._packer.filterData(data, width, height); var 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( this._deflate.on('data', function(compressedData) {
"data", this.emit('data', this._packer.packIDAT(compressedData));
function (compressedData) { }.bind(this));
this.emit("data", this._packer.packIDAT(compressedData));
}.bind(this)
);
this._deflate.on( this._deflate.on('end', function() {
"end", this.emit('data', this._packer.packIEND());
function () { this.emit('end');
this.emit("data", this._packer.packIEND()); }.bind(this));
this.emit("end");
}.bind(this)
);
this._deflate.end(filteredData); this._deflate.end(filteredData);
}; };

View file

@ -1,24 +1,22 @@
import constants from "./constants.js"; 'use strict';
import Packer from "./packer.js";
let hasSyncZlib = true; var zlib = require('zlib');
let zlib = require("zlib"); if (!zlib.deflateSync) {
// Backwards compatibility with 0.10.
zlib = require('node-zlib-backport');
}
var constants = require('./constants');
var Packer = require('./packer');
export default function (metaData, opt) { module.exports = function(metaData, opt) {
if (!hasSyncZlib) { var options = opt || {};
throw new Error(
"To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
);
}
let options = opt || {}; var packer = new Packer(options);
let packer = new Packer(options); var chunks = [];
let chunks = [];
// Signature // Signature
chunks.push(Buffer.from(constants.PNG_SIGNATURE)); chunks.push(new Buffer(constants.PNG_SIGNATURE));
// Header // Header
chunks.push(packer.packIHDR(metaData.width, metaData.height)); chunks.push(packer.packIHDR(metaData.width, metaData.height));
@ -27,21 +25,14 @@ export default function (metaData, opt) {
chunks.push(packer.packGAMA(metaData.gamma)); chunks.push(packer.packGAMA(metaData.gamma));
} }
let filteredData = packer.filterData( var filteredData = packer.filterData(metaData.data, metaData.width, metaData.height);
metaData.data,
metaData.width,
metaData.height
);
// compress it // compress it
let compressedData = zlib.deflateSync( var compressedData = zlib.deflateSync(filteredData, packer.getDeflateOptions());
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,92 +1,56 @@
import constants from "./constants.js"; 'use strict';
import CrcStream from "./crc.js";
import bitPacker from "./bitpacker.js";
import filter from "./filter-pack.js";
let zlib = require("zlib"); var constants = require('./constants');
var CrcStream = require('./crc');
var bitPacker = require('./bitpacker');
var filter = require('./filter-pack');
var zlib = require('zlib');
let Packer = function (options) { var Packer = module.exports = function(options) {
this._options = options; this._options = options;
options.deflateChunkSize = options.deflateChunkSize || 32 * 1024; options.deflateChunkSize = options.deflateChunkSize || 32 * 1024;
options.deflateLevel = options.deflateLevel = options.deflateLevel != null ? options.deflateLevel : 9;
options.deflateLevel != null ? options.deflateLevel : 9; options.deflateStrategy = options.deflateStrategy != null ? options.deflateStrategy : 3;
options.deflateStrategy = options.inputHasAlpha = options.inputHasAlpha != null ? options.inputHasAlpha : true;
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;
// This is outputColorType options.colorType = (typeof options.colorType === 'number') ? options.colorType : constants.COLORTYPE_COLOR_ALPHA;
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 ( if (options.colorType !== constants.COLORTYPE_COLOR && options.colorType !== constants.COLORTYPE_COLOR_ALPHA) {
[ 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 ( if (options.bitDepth !== 8) {
[ 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"
);
} }
}; };
export default Packer; Packer.prototype.getDeflateOptions = function() {
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)
let packedData = bitPacker(data, width, height, this._options); var packedData = bitPacker(data, width, height, this._options);
// filter pixel data // filter pixel data
let bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType]; var bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType];
let filteredData = filter(packedData, width, height, this._options, bpp); var 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;
let buf = Buffer.alloc(len + 12); var len = (data ? data.length : 0);
var buf = new Buffer(len + 12);
buf.writeUInt32BE(len, 0); buf.writeUInt32BE(len, 0);
buf.writeUInt32BE(type, 4); buf.writeUInt32BE(type, 4);
@ -95,24 +59,22 @@ Packer.prototype._packChunk = function (type, data) {
data.copy(buf, 8); data.copy(buf, 8);
} }
buf.writeInt32BE( buf.writeInt32BE(CrcStream.crc32(buf.slice(4, buf.length - 4)), buf.length - 4);
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) {
let buf = Buffer.alloc(4); var buf = new Buffer(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
buf[9] = this._options.colorType; // colorType buf[9] = this._options.colorType; // colorType
buf[10] = 0; // compression buf[10] = 0; // compression
buf[11] = 0; // filter buf[11] = 0; // filter
@ -121,10 +83,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,14 +1,17 @@
export default function paethPredictor(left, above, upLeft) { 'use strict';
let paeth = left + above - upLeft;
let pLeft = Math.abs(paeth - left); module.exports = function paethPredictor(left, above, upLeft) {
let pAbove = Math.abs(paeth - above);
let pUpLeft = Math.abs(paeth - upLeft); var paeth = left + above - upLeft;
var pLeft = Math.abs(paeth - left);
if (pLeft <= pAbove && pLeft <= pUpLeft) { var pAbove = Math.abs(paeth - above);
return left; var pUpLeft = Math.abs(paeth - upLeft);
}
if (pAbove <= pUpLeft) { if (pLeft <= pAbove && pLeft <= pUpLeft) {
return above; return left;
} }
return upLeft; if (pAbove <= pUpLeft) {
}; return above;
}
return upLeft;
};

View file

@ -1,26 +1,25 @@
import ChunkStream from "./chunkstream.js"; 'use strict';
import FilterAsync from "./filter-parse-async.js";
import Parser from "./parser.js";
import bitmapper from "./bitmapper.js";
import formatNormaliser from "./format-normaliser.js";
let util = require("util"); var util = require('util');
let zlib = require("zlib"); var 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');
let ParserAsync = function (options) { var ParserAsync = module.exports = 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;
@ -29,10 +28,10 @@ let ParserAsync = 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;
@ -42,125 +41,70 @@ 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._headersFinished = function () { ParserAsync.prototype._finished = 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;
} }
let normalisedBitmapData;
try { try {
let bitmapData = bitmapper(filteredData, this._bitmapInfo); var bitmapData = bitmapper.dataToBitMap(filteredData, this._bitmapInfo);
normalisedBitmapData = formatNormaliser(bitmapData, this._bitmapInfo); var 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,29 +1,25 @@
import inflateSync from "./sync-inflate.js"; 'use strict';
import SyncReader from "./sync-reader.js";
import FilterSync from "./filter-parse-sync.js";
import bitmapper from "./bitmapper.js";
import formatNormaliser from "./format-normaliser.js";
import Parser from "./parser.js";
let hasSyncZlib = true; var zlib = require('zlib');
let zlib = require("zlib"); if (!zlib.inflateSync) {
if (!zlib.deflateSync) { // Backwards compatibility with 0.10.
hasSyncZlib = false; zlib = require('node-zlib-backport');
} }
var SyncReader = require('./sync-reader');
var FilterSync = require('./filter-parse-sync');
var Parser = require('./parser');
var bitmapper = require('./bitmapper');
var formatNormaliser = require('./format-normaliser');
export default function (buffer, options) {
if (!hasSyncZlib) {
throw new Error(
"To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
);
}
let err; module.exports = function(buffer, options) {
var err;
function handleError(_err_) { function handleError(_err_) {
err = _err_; err = _err_;
} }
let metaData; var metaData;
function handleMetaData(_metaData_) { function handleMetaData(_metaData_) {
metaData = _metaData_; metaData = _metaData_;
} }
@ -36,31 +32,26 @@ export default function (buffer, options) {
metaData.palette = palette; metaData.palette = palette;
} }
function handleSimpleTransparency() { var gamma;
metaData.alpha = true;
}
let gamma;
function handleGamma(_gamma_) { function handleGamma(_gamma_) {
gamma = _gamma_; gamma = _gamma_;
} }
let inflateDataList = []; var inflateDataList = [];
function handleInflateData(inflatedData) { function handleInflateData(inflatedData) {
inflateDataList.push(inflatedData); inflateDataList.push(inflatedData);
} }
let reader = new SyncReader(buffer); var reader = new SyncReader(buffer);
let parser = new Parser(options, { var 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();
@ -71,37 +62,27 @@ export default function (buffer, options) {
} }
//join together the inflate datas //join together the inflate datas
let inflateData = Buffer.concat(inflateDataList); var inflateData = Buffer.concat(inflateDataList);
inflateDataList.length = 0; inflateDataList.length = 0;
let inflatedData; var inflatedData = zlib.inflateSync(inflateData);
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');
} }
let unfilteredData = FilterSync.process(inflatedData, metaData); var unfilteredData = FilterSync.process(inflatedData, metaData);
inflateData = null; inflateData = null;
let bitmapData = bitmapper.dataToBitMap(unfilteredData, metaData); var bitmapData = bitmapper.dataToBitMap(unfilteredData, metaData);
unfilteredData = null; unfilteredData = null;
let normalisedBitmapData = formatNormaliser(bitmapData, metaData); var normalisedBitmapData = formatNormaliser(bitmapData, metaData);
metaData.data = normalisedBitmapData; metaData.data = normalisedBitmapData;
metaData.gamma = gamma || 0; metaData.gamma = gamma || 0;
return metaData; return metaData;
}; };

View file

@ -1,15 +1,16 @@
"use strict"; 'use strict';
let constants = require("./constants"); var constants = require('./constants');
let CrcCalculator = require("./crc"); var 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 = [];
@ -31,80 +32,84 @@ let 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._parseSignature = function (data) { Parser.prototype.start = function() {
let signature = constants.PNG_SIGNATURE; this.read(constants.PNG_SIGNATURE.length,
this._parseSignature.bind(this)
);
};
for (let i = 0; i < signature.length; i++) { Parser.prototype._parseSignature = function(data) {
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
let length = data.readUInt32BE(0); var length = data.readUInt32BE(0);
// chunk type // chunk type
let type = data.readUInt32BE(4); var type = data.readUInt32BE(4);
let name = ""; var name = '';
for (let i = 4; i < 8; i++) { for (var 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
let ancillary = Boolean(data[4] & 0x20); // or critical var 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(Buffer.from(name)); this._crc.write(new Buffer(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);
let calcCrc = this._crc.crc32(); var fileCrc = data.readInt32BE(0);
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;
} }
@ -113,55 +118,50 @@ 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);
let width = data.readUInt32BE(0); var width = data.readUInt32BE(0);
let height = data.readUInt32BE(4); var height = data.readUInt32BE(4);
let depth = data[8]; var depth = data[8];
let colorType = data[9]; // bits: 1 palette, 2 color, 4 alpha var colorType = data[9]; // bits: 1 palette, 2 color, 4 alpha
let compr = data[10]; var compr = data[10];
let filter = data[11]; var filter = data[11];
let interlace = data[12]; var 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 ( if (depth !== 8 && depth !== 4 && depth !== 2 && depth !== 1 && depth !== 16) {
depth !== 8 && this.error(new Error('Unsupported bit depth ' + depth));
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;
let bpp = constants.COLORTYPE_TO_BPP_MAP[this._colorType]; var bpp = constants.COLORTYPE_TO_BPP_MAP[this._colorType];
this._hasIHDR = true; this._hasIHDR = true;
@ -174,23 +174,30 @@ 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);
let entries = Math.floor(data.length / 3); var entries = Math.floor(data.length / 3);
// console.log('Palette:', entries); // console.log('Palette:', entries);
for (let i = 0; i < entries; i++) { for (var i = 0; i < entries; i++) {
this._palette.push([data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 0xff]); this._palette.push([
data[i * 3],
data[i * 3 + 1],
data[i * 3 + 2],
0xff
]);
} }
this.palette(this._palette); this.palette(this._palette);
@ -198,24 +205,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 (let i = 0; i < data.length; i++) { for (var 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);
@ -228,57 +235,50 @@ 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([ this.transColor([data.readUInt16BE(0), data.readUInt16BE(2), data.readUInt16BE(4)]);
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 ( if (this._colorType === constants.COLORTYPE_PALETTE_COLOR && this._palette.length === 0) {
this._colorType === constants.COLORTYPE_PALETTE_COLOR && throw new Error('Expected palette not found');
this._palette.length === 0
) {
throw new Error("Expected palette not found");
} }
this.inflateData(data); this.inflateData(data);
let leftOverLength = length - data.length; var 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,15 +1,16 @@
import parse from "./parser-sync.js"; 'use strict';
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 || {});
}; };
export var write = function (png, options) { exports.write = function(png) {
return pack(png, options);
};
export default { return pack(png);
read, };
write
}

View file

@ -1,23 +1,22 @@
import Parser from "./parser-async.js"; 'use strict';
import Packer from "./packer-async.js";
import PNGSync from "./png-sync.js";
let util = require("util"); var util = require('util');
let Stream = require("stream"); var 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
// coerce pixel dimensions to integers (also coerces undefined -> 0): this.width = options.width || 0;
this.width = options.width | 0; this.height = options.height || 0;
this.height = options.height | 0;
this.data = this.data = this.width > 0 && this.height > 0 ?
this.width > 0 && this.height > 0 new Buffer(4 * this.width * this.height) : null;
? 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);
@ -28,127 +27,106 @@ let 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( this._parser.on('parsed', function(data) {
"parsed", this.data = data;
function (data) { this.emit('parsed', data);
this.data = data; }.bind(this));
this.emit("parsed", data);
}.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( process.nextTick(function() {
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) {
if (callback) {
let onParsed, onError;
onParsed = function (parsedData) { PNG.prototype.parse = function(data, callback) {
this.removeListener("error", onError);
if (callback) {
var onParsed, onError;
onParsed = function(parsedData) {
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 */
if ( PNG.bitblt = function(src, dst, srcX, srcY, width, height, deltaX, deltaY) { // eslint-disable-line max-params
srcX > src.width ||
srcY > src.height || if (srcX > src.width || srcY > src.height || srcX + width > src.width || srcY + height > src.height) {
srcX + width > src.width || throw new Error('bitblt reading outside image');
srcY + height > src.height
) {
throw new Error("bitblt reading outside image");
} }
if ( if (deltaX > dst.width || deltaY > dst.height || deltaX + width > dst.width || deltaY + height > dst.height) {
deltaX > dst.width || throw new Error('bitblt writing outside image');
deltaY > dst.height ||
deltaX + width > dst.width ||
deltaY + height > dst.height
) {
throw new Error("bitblt writing outside image");
} }
for (let y = 0; y < height; y++) { for (var y = 0; y < height; y++) {
src.data.copy( src.data.copy(dst.data,
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
@ -156,29 +134,21 @@ PNG.bitblt = function (src, dst, srcX, srcY, width, height, deltaX, deltaY) {
} }
}; };
PNG.prototype.bitblt = function (
dst, PNG.prototype.bitblt = function(dst, srcX, srcY, width, height, deltaX, deltaY) { // eslint-disable-line max-params
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 (let y = 0; y < src.height; y++) { for (var y = 0; y < src.height; y++) {
for (let x = 0; x < src.width; x++) { for (var x = 0; x < src.width; x++) {
let idx = (src.width * y + x) << 2; var idx = (src.width * y + x) << 2;
for (let i = 0; i < 3; i++) { for (var i = 0; i < 3; i++) {
let sample = src.data[idx + i] / 255; var 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);
} }
@ -188,6 +158,6 @@ PNG.adjustGamma = function (src) {
} }
}; };
PNG.prototype.adjustGamma = function () { PNG.prototype.adjustGamma = function() {
PNG.adjustGamma(this); PNG.adjustGamma(this);
}; };

View file

@ -1,163 +0,0 @@
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,45 +1,51 @@
let SyncReader = function (buffer) { 'use strict';
var SyncReader = module.exports = function(buffer) {
this._buffer = buffer; this._buffer = buffer;
this._reads = []; this._reads = [];
}; };
export default SyncReader; SyncReader.prototype.read = function(length, callback) {
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];
if ( var read = this._reads[0];
this._buffer.length &&
(this._buffer.length >= read.length || read.allowLess) if (this._buffer.length && (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
let buf = this._buffer; var 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": "5.0.0", "version": "2.3.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,10 +11,7 @@
"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": [
@ -30,44 +27,39 @@
"pngjs" "pngjs"
], ],
"engines": { "engines": {
"node": ">=10.13.0" "node": ">=0.10.0",
"iojs": ">= 1.0.0"
}, },
"main": "./lib/png.js", "main": "./lib/png.js",
"directories": { "directories": {
"lib": "lib", "example": "examples"
"example": "examples",
"test": "test"
}, },
"scripts": { "scripts": {
"build": "yarn prepublish", "coverage": "istanbul -- cover node_modules/tape/bin/tape test/*-spec.js nolarge",
"prepublish": "yarn browserify", "coverage-report": "npm run coverage && istanbul report html",
"browserify": "browserify lib/png.js --standalone png > browser.js", "coveralls": "cat ./coverage/lcov.info | coveralls",
"coverage": "nyc --reporter=lcov --reporter=text-summary tape test/*-spec.js nolarge", "test": "npm run lint && tape test/*-spec.js | tap-dot && node test/run-compare",
"test": "yarn lint && yarn prettier:check && tape test/*-spec.js | tap-dot && node test/run-compare", "lint": "eslint lib"
"lint": "eslint .",
"prettier:write": "prettier --write .",
"prettier:check": "prettier --check ."
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/lukeapage/pngjs.git" "url": "git://github.com/lukeapage/pngjs2.git"
}, },
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/lukeapage/pngjs/issues" "url": "https://github.com/lukeapage/pngjs2/issues"
},
"optionalDependencies": {
"node-zlib-backport": "^0.11.15"
}, },
"devDependencies": { "devDependencies": {
"browserify": "16.5.1",
"buffer-equal": "1.0.0", "buffer-equal": "1.0.0",
"codecov": "3.7.0", "connect": "^3.4.0",
"connect": "3.7.0", "eslint": "^2.5.0",
"eslint": "7.0.0", "istanbul": "^0.4.1",
"eslint-config-prettier": "6.11.0", "phantomjs-prebuilt": "^2.1.7",
"nyc": "15.0.1", "serve-static": "^1.10.0",
"prettier": "2.0.5", "tap-dot": "^1.0.0",
"puppeteer": "3.0.2", "tape": "^4.0.2"
"serve-static": "1.14.1",
"tap-dot": "2.0.0",
"tape": "5.0.0"
} }
} }

View file

@ -1,24 +1,26 @@
#!/usr/bin/env node #!/usr/bin/env node
let fs = require("fs"); var fs = require('fs');
let PNG = require("../lib/png").PNG; var PNG = require('../lib/png').PNG;
let test = require("tape"); var test = require('tape');
let bufferEqual = require("buffer-equal"); var 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);
let png = new PNG({ var 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;
let col = (x < png.width >> 1) ^ (y < png.height >> 1) ? 0xe5 : 0xff; for (var y = 0; y < png.height; y++) {
for (var x = 0; x < png.width; x++) {
var idx = (png.width * y + x) << 2;
var col = x < (png.width >> 1) ^ y < (png.height >> 1) ? 0xe5 : 0xff;
png.data[idx] = col; png.data[idx] = col;
png.data[idx + 1] = col; png.data[idx + 1] = col;
@ -27,14 +29,13 @@ test("outputs background, created from scratch", function (t) {
} }
} }
png png.pack().pipe(fs.createWriteStream(__dirname + '/bg.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");
let isBufferEqual = bufferEqual(out, ref); var out = fs.readFileSync(__dirname + '/bg.png');
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,44 +1,35 @@
let fs = require("fs"); var fs = require('fs');
let PNG = require("../lib/png").PNG; var PNG = require('../lib/png').PNG;
let test = require("tape"); var test = require('tape');
let noLargeOption = process.argv.indexOf("nolarge") >= 0; var 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 ( return (!noLargeOption || !file.match(/large/i)) && Boolean(file.match(/\.png$/i));
(!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);
let data = fs.readFileSync(__dirname + "/in/" + file); var data = fs.readFileSync(__dirname + '/in/' + file);
let png;
try { try {
png = PNG.sync.read(data); var png = PNG.sync.read(data);
} catch (e) { } catch (e) {
if (!expectedError) { if (!expectedError) {
t.fail( t.fail('Unexpected error parsing..' + file + '\n' + e.message + "\n" + e.stack);
"Unexpected error parsing.." +
file +
"\n" +
e.message +
"\n" +
e.stack
);
} else { } else {
t.pass("completed"); t.pass("completed");
} }
@ -50,55 +41,47 @@ fs.readdir(__dirname + "/in/", function (err, files) {
return t.end(); return t.end();
} }
let outpng = new PNG(); var 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().pipe( outpng.pack()
fs .pipe(fs.createWriteStream(__dirname + '/outsync/' + file)
.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( t.fail("Async: Unexpected error parsing.." + file + '\n' + err.message + '\n' + err.stack);
"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().pipe( this.pack()
fs .pipe(
.createWriteStream(__dirname + "/out/" + file) fs.createWriteStream(__dirname + '/out/' + file)
.on("finish", function () { .on("finish", function () {
t.pass("completed"); t.pass("completed");
t.end(); t.end();
}) }));
);
}); });
}); });
}); });

View file

@ -1,16 +1,14 @@
let serveStatic = require("serve-static"); var serveStatic = require('serve-static');
let http = require("http"); //var serveIndex = require('serve-index');
let connect = require("connect"); var http = require('http');
var connect = require('connect');
let app = connect(); var app = connect();
let server = http.createServer(app); 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 = Buffer.from(base64Data, 'base64'); decodedImage = new Buffer(base64Data, 'base64');
require('fs').writeFile(outputFile, decodedImage, callback); require('fs').writeFile(outputFile, decodedImage, callback);
} }

File diff suppressed because it is too large Load diff

58
test/phantom-compare.js Normal file
View file

@ -0,0 +1,58 @@
/*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,37 +1,38 @@
let test = require("tape"); var test = require('tape');
let fs = require("fs"); var fs = require('fs');
let path = require("path"); var path = require('path');
let PNG = require("../lib/png").PNG; var PNG = require('../lib/png').PNG;
let stream = require("stream"); var 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) {
@ -44,17 +45,15 @@ 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);
let y = 1024, var y = 1024,
x; x;
let isOk = true; var 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( t.fail("pixel does not match - " + getPixel(png, x, y) + " !== 0x000000FF");
"pixel does not match - " + getPixel(png, x, y) + " !== 0x000000FF"
);
isOk = false; isOk = false;
break; break;
} }
@ -74,20 +73,15 @@ 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");
let y = 16, var y = 16,
x; x;
let isOk = true; var 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( t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + ((x ^ y) * 286331136 + 255));
"pixel does not match - " +
getPixel(png, x, y) +
" !== " +
((x ^ y) * 286331136 + 255)
);
isOk = false; isOk = false;
break; break;
} }
@ -96,7 +90,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) {
@ -108,23 +102,15 @@ 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);
let y = 16, var y = 16,
x; x;
let isOk = true; var isOk = true;
while (y--) { while (y--) {
x = 16; x = 16;
while (x--) { while (x--) {
if ( if (getPixel(png, x, y) !== x * 285212672 + y * 1114112 + (x ^ y) * 4352 + 255) {
getPixel(png, x, y) !== t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + (x * 285212672 + y * 1114112 + (x ^ y) * 4352 + 255));
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;
} }
@ -133,8 +119,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) {
@ -145,23 +131,15 @@ 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);
let y = 16, var y = 16,
x; x;
let isOk = true; var isOk = true;
while (y--) { while (y--) {
x = 16; x = 16;
while (x--) { while (x--) {
if ( if (getPixel(png, x, y) !== x * 285212672 + y * 1114112 + (x ^ y) * 17) {
getPixel(png, x, y) !== t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + (x * 285212672 + y * 1114112 + (x ^ y) * 17));
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;
} }
@ -170,8 +148,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) {
@ -182,16 +160,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) {
@ -202,28 +180,29 @@ 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);
let y = 16, var y = 16,
x; x;
let isOk = true; var isOk = true;
while (y--) { while (y--) {
x = 16; x = 16;
while (x--) { while (x--) {
let expected; var 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( t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + expected);
"pixel does not match - " + getPixel(png, x, y) + " !== " + expected
);
isOk = false; isOk = false;
break; break;
} }
@ -243,30 +222,32 @@ 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);
let y = 16, var y = 16,
x; x;
let isOk = true; var isOk = true;
while (y--) { while (y--) {
x = 16; x = 16;
while (x--) { while (x--) {
let expected; var 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( t.fail("pixel does not match - " + getPixel(png, x, y) + " !== " + expected);
"pixel does not match - " + getPixel(png, x, y) + " !== " + expected
);
isOk = false; isOk = false;
break; break;
} }
@ -286,99 +267,55 @@ 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) {
let buf = Buffer.from("I AM NOT ACTUALLY A PNG", "utf8"); var buf = new Buffer("I AM NOT ACTUALLY A PNG", "utf8")
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) { test("should return an error if a PNG is normal except for a missing IEND", function (t) {
let buf = Buffer.from( var buf = new Buffer("89504e470d0a1a0a0000000d49484452000000100000001008000000003a98a0bd000000017352474200aece1ce90000002174455874536f6674776172650047726170686963436f6e7665727465722028496e74656c297787fa190000008849444154789c448e4111c020100363010b58c00216b080052c60010b58c0c259c00216ae4d3b69df99dd0d1062caa5b63ee6b27d1c012996dceae86b6ef38398106acb65ae3e8edbbef780564b5e73743fdb409e1ef2f4803c3de4e901797ac8d3f3f0f490a7077ffffd03f5f507eaeb0fd4d71fa8af3f505f7fa0befe7c7dfdb9000000ffff0300c0fd7f8179301408", "hex")
"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 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,57 +1,24 @@
const closeServer = require("./http-server"); require("./http-server");
const puppeteer = require("puppeteer");
const URL = "http://localhost:8000"; var path = require('path');
var childProcess = require('child_process');
puppeteer var phantomjs = require('phantomjs-prebuilt');
.launch({ var binPath = phantomjs.path;
headless: true,
args: ["--no-sandbox", "--disable-setuid-sandbox"], var childArgs = [
}) path.join(__dirname, 'phantom-compare.js')
.then(async (browser) => { ];
const page = await browser.newPage();
await page.goto(URL, { waitUntil: "networkidle0" }); try {
const results = await page.evaluate(() => { console.log("Comparing in PhantomJS");
/* global window:false */
try { childProcess.execFile(binPath, childArgs, function (err, stdout, stderr) {
if (window.isFinished && window.isFinished()) {
return window.results; // handle results
} console.log("Comparison Test Results:");
} catch (err) { console.log(stdout);
console.log("Failed", err); process.exit(err ? 1 : 0);
} });
}); } catch (e) {
console.log("Comparing in Chrome"); console.log("Error starting phantomjs");
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();
});

3254
yarn.lock

File diff suppressed because it is too large Load diff