1 | ![build](https://github.com/pngjs/pngjs/actions/workflows/ci.yml/badge.svg?branch=main) [![codecov](https://codecov.io/gh/pngjs/pngjs/branch/master/graph/badge.svg)](https://codecov.io/gh/pngjs/pngjs) [![npm version](https://badge.fury.io/js/pngjs.svg)](http://badge.fury.io/js/pngjs)
|
2 |
|
3 | # pngjs
|
4 |
|
5 | Simple PNG encoder/decoder for Node.js with no dependencies.
|
6 |
|
7 | Based on the original [pngjs](https://github.com/niegowski/node-pngjs) with the follow enhancements.
|
8 |
|
9 | - Support for reading 1,2,4 & 16 bit files
|
10 | - Support for reading interlace files
|
11 | - Support for reading `tTRNS` transparent colours
|
12 | - Support for writing colortype 0 (grayscale), colortype 2 (RGB), colortype 4 (grayscale alpha) and colortype 6 (RGBA)
|
13 | - Sync interface as well as async
|
14 | - API compatible with pngjs and node-pngjs
|
15 |
|
16 | Known lack of support for:
|
17 |
|
18 | - Extended PNG e.g. Animation
|
19 | - Writing in colortype 3 (indexed color)
|
20 |
|
21 | # Table of Contents
|
22 |
|
23 | - [Requirements](#requirements)
|
24 | - [Comparison Table](#comparison-table)
|
25 | - [Tests](#tests)
|
26 | - [Installation](#installation)
|
27 | - [Browser](#browser)
|
28 | - [Example](#example)
|
29 | - [Async API](#async-api)
|
30 | - [Sync API](#sync-api)
|
31 | - [Changelog](#changelog)
|
32 |
|
33 | # Comparison Table
|
34 |
|
35 | | Name | Forked From | Sync | Async | 16 Bit | 1/2/4 Bit | Interlace | Gamma | Encodes | Tested |
|
36 | | ------------- | ----------- | ---- | ----- | ------ | --------- | --------- | ------ | ------- | ------ |
|
37 | | pngjs | | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
38 | | node-png | pngjs | No | Yes | No | No | No | Hidden | Yes | Manual |
|
39 | | png-coder | pngjs | No | Yes | Yes | No | No | Hidden | Yes | Manual |
|
40 | | pngparse | | No | Yes | No | Yes | No | No | No | Yes |
|
41 | | pngparse-sync | pngparse | Yes | No | No | Yes | No | No | No | Yes |
|
42 | | png-async | | No | Yes | No | No | No | No | Yes | Yes |
|
43 | | png-js | | No | Yes | No | No | No | No | No | No |
|
44 |
|
45 | Native C++ node decoders:
|
46 |
|
47 | - png
|
48 | - png-sync (sync version of above)
|
49 | - pixel-png
|
50 | - png-img
|
51 |
|
52 | # Tests
|
53 |
|
54 | 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.
|
55 |
|
56 | To run the tests, fetch the repo (tests are not distributed via npm) and install with `npm i`, run `npm test`.
|
57 |
|
58 | 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.
|
59 |
|
60 | # Installation
|
61 |
|
62 | ```
|
63 | $ npm install pngjs --save
|
64 | ```
|
65 |
|
66 | # Browser
|
67 |
|
68 | 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:
|
69 |
|
70 | ```
|
71 | import { PNG } from 'pngjs/browser';
|
72 | ```
|
73 |
|
74 | # Example
|
75 |
|
76 | ```js
|
77 | var fs = require("fs"),
|
78 | PNG = require("pngjs").PNG;
|
79 |
|
80 | fs.createReadStream("in.png")
|
81 | .pipe(
|
82 | new PNG({
|
83 | filterType: 4,
|
84 | })
|
85 | )
|
86 | .on("parsed", function () {
|
87 | for (var y = 0; y < this.height; y++) {
|
88 | for (var x = 0; x < this.width; x++) {
|
89 | var idx = (this.width * y + x) << 2;
|
90 |
|
91 | // invert color
|
92 | this.data[idx] = 255 - this.data[idx];
|
93 | this.data[idx + 1] = 255 - this.data[idx + 1];
|
94 | this.data[idx + 2] = 255 - this.data[idx + 2];
|
95 |
|
96 | // and reduce opacity
|
97 | this.data[idx + 3] = this.data[idx + 3] >> 1;
|
98 | }
|
99 | }
|
100 |
|
101 | this.pack().pipe(fs.createWriteStream("out.png"));
|
102 | });
|
103 | ```
|
104 |
|
105 | For more examples see `examples` folder.
|
106 |
|
107 | # Async API
|
108 |
|
109 | 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.
|
110 |
|
111 | ## Class: PNG
|
112 |
|
113 | `PNG` is readable and writable `Stream`.
|
114 |
|
115 | ### Options
|
116 |
|
117 | - `width` - use this with `height` if you want to create png from scratch
|
118 | - `height` - as above
|
119 | - `checkCRC` - whether parser should be strict about checksums in source stream (default: `true`)
|
120 | - `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)
|
121 | - `deflateLevel` - compression level for deflate (default: 9)
|
122 | - `deflateStrategy` - compression strategy for deflate (default: 3)
|
123 | - `deflateFactory` - deflate stream factory (default: `zlib.createDeflate`)
|
124 | - `filterType` - png filtering method for scanlines (default: -1 => auto, accepts array of numbers 0-4)
|
125 | - `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.
|
126 | - `inputColorType` - the input colorType - see constants. Default is 6 (RGBA)
|
127 | - `bitDepth` - the bitDepth of the output, 8 or 16 bits. Input data is expected to have this bit depth.
|
128 | 16 bit data is expected in the system endianness (Default: 8)
|
129 | - `inputHasAlpha` - whether the input bitmap has 4 bytes per pixel (rgb and alpha) or 3 (rgb - no alpha).
|
130 | - `bgColor` - an object containing red, green, and blue values between 0 and 255
|
131 | that is used when packing a PNG if alpha is not to be included (default: 255,255,255)
|
132 |
|
133 | ### Event "metadata"
|
134 |
|
135 | `function(metadata) { }`
|
136 | Image's header has been parsed, metadata contains this information:
|
137 |
|
138 | - `width` image size in pixels
|
139 | - `height` image size in pixels
|
140 | - `palette` image is paletted
|
141 | - `color` image is not grayscale
|
142 | - `alpha` image contains alpha channel
|
143 | - `interlace` image is interlaced
|
144 |
|
145 | ### Event: "parsed"
|
146 |
|
147 | `function(data) { }`
|
148 | Input image has been completely parsed, `data` is complete and ready for modification.
|
149 |
|
150 | ### Event: "error"
|
151 |
|
152 | `function(error) { }`
|
153 |
|
154 | ### png.parse(data, [callback])
|
155 |
|
156 | Parses PNG file data. Can be `String` or `Buffer`. Alternatively you can stream data to instance of PNG.
|
157 |
|
158 | Optional `callback` is once called on `error` or `parsed`. The callback gets
|
159 | two arguments `(err, data)`.
|
160 |
|
161 | Returns `this` for method chaining.
|
162 |
|
163 | #### Example
|
164 |
|
165 | ```js
|
166 | new PNG({ filterType: 4 }).parse(imageData, function (error, data) {
|
167 | console.log(error, data);
|
168 | });
|
169 | ```
|
170 |
|
171 | ### png.pack()
|
172 |
|
173 | Starts converting data to PNG file Stream.
|
174 |
|
175 | Returns `this` for method chaining.
|
176 |
|
177 | ### png.bitblt(dst, sx, sy, w, h, dx, dy)
|
178 |
|
179 | 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`).
|
180 |
|
181 | Returns `this` for method chaining.
|
182 |
|
183 | For example, the following code copies the top-left 100x50 px of `in.png` into dst and writes it to `out.png`:
|
184 |
|
185 | ```js
|
186 | var dst = new PNG({ width: 100, height: 50 });
|
187 | fs.createReadStream("in.png")
|
188 | .pipe(new PNG())
|
189 | .on("parsed", function () {
|
190 | this.bitblt(dst, 0, 0, 100, 50, 0, 0);
|
191 | dst.pack().pipe(fs.createWriteStream("out.png"));
|
192 | });
|
193 | ```
|
194 |
|
195 | ### Property: adjustGamma()
|
196 |
|
197 | 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.
|
198 |
|
199 | In tests against PNG suite it compared 100% with chrome on all 8 bit and below images. On IE there were some differences.
|
200 |
|
201 | 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.
|
202 |
|
203 | ```js
|
204 | fs.createReadStream("in.png")
|
205 | .pipe(new PNG())
|
206 | .on("parsed", function () {
|
207 | this.adjustGamma();
|
208 | this.pack().pipe(fs.createWriteStream("out.png"));
|
209 | });
|
210 | ```
|
211 |
|
212 | ### Property: width
|
213 |
|
214 | Width of image in pixels
|
215 |
|
216 | ### Property: height
|
217 |
|
218 | Height of image in pixels
|
219 |
|
220 | ### Property: data
|
221 |
|
222 | Buffer of image pixel data. Every pixel consists 4 bytes: R, G, B, A (opacity).
|
223 |
|
224 | ### Property: gamma
|
225 |
|
226 | Gamma of image (0 if not specified)
|
227 |
|
228 | ## Packing a PNG and removing alpha (RGBA to RGB)
|
229 |
|
230 | When removing the alpha channel from an image, there needs to be a background color to correctly
|
231 | convert each pixel's transparency to the appropriate RGB value. By default, pngjs will flatten
|
232 | the image against a white background. You can override this in the options:
|
233 |
|
234 | ```js
|
235 | var fs = require("fs"),
|
236 | PNG = require("pngjs").PNG;
|
237 |
|
238 | fs.createReadStream("in.png")
|
239 | .pipe(
|
240 | new PNG({
|
241 | colorType: 2,
|
242 | bgColor: {
|
243 | red: 0,
|
244 | green: 255,
|
245 | blue: 0,
|
246 | },
|
247 | })
|
248 | )
|
249 | .on("parsed", function () {
|
250 | this.pack().pipe(fs.createWriteStream("out.png"));
|
251 | });
|
252 | ```
|
253 |
|
254 | # Sync API
|
255 |
|
256 | ## PNG.sync
|
257 |
|
258 | ### PNG.sync.read(buffer)
|
259 |
|
260 | Take a buffer and returns a PNG image. The properties on the image include the meta data and `data` as per the async API above.
|
261 |
|
262 | ```
|
263 | var data = fs.readFileSync('in.png');
|
264 | var png = PNG.sync.read(data);
|
265 | ```
|
266 |
|
267 | ### PNG.sync.write(png)
|
268 |
|
269 | Take a PNG image and returns a buffer. The properties on the image include the meta data and `data` as per the async API above.
|
270 |
|
271 | ```
|
272 | var data = fs.readFileSync('in.png');
|
273 | var png = PNG.sync.read(data);
|
274 | var options = { colorType: 6 };
|
275 | var buffer = PNG.sync.write(png, options);
|
276 | fs.writeFileSync('out.png', buffer);
|
277 | ```
|
278 |
|
279 | ### PNG.adjustGamma(src)
|
280 |
|
281 | Adjusts the gamma of a sync image. See the async adjustGamma.
|
282 |
|
283 | ```
|
284 | var data = fs.readFileSync('in.png');
|
285 | var png = PNG.sync.read(data);
|
286 | PNG.adjustGamma(png);
|
287 | ```
|