UNPKG

4.42 kBJavaScriptView Raw
1"use strict";
2
3let util = require("util");
4let Stream = require("stream");
5let Parser = require("./parser-async");
6let Packer = require("./packer-async");
7let PNGSync = require("./png-sync");
8
9let PNG = (exports.PNG = function (options) {
10 Stream.call(this);
11
12 options = options || {}; // eslint-disable-line no-param-reassign
13
14 // coerce pixel dimensions to integers (also coerces undefined -> 0):
15 this.width = options.width | 0;
16 this.height = options.height | 0;
17
18 this.data =
19 this.width > 0 && this.height > 0
20 ? Buffer.alloc(4 * this.width * this.height)
21 : null;
22
23 if (options.fill && this.data) {
24 this.data.fill(0);
25 }
26
27 this.gamma = 0;
28 this.readable = this.writable = true;
29
30 this._parser = new Parser(options);
31
32 this._parser.on("error", this.emit.bind(this, "error"));
33 this._parser.on("close", this._handleClose.bind(this));
34 this._parser.on("metadata", this._metadata.bind(this));
35 this._parser.on("gamma", this._gamma.bind(this));
36 this._parser.on(
37 "parsed",
38 function (data) {
39 this.data = data;
40 this.emit("parsed", data);
41 }.bind(this)
42 );
43
44 this._packer = new Packer(options);
45 this._packer.on("data", this.emit.bind(this, "data"));
46 this._packer.on("end", this.emit.bind(this, "end"));
47 this._parser.on("close", this._handleClose.bind(this));
48 this._packer.on("error", this.emit.bind(this, "error"));
49});
50util.inherits(PNG, Stream);
51
52PNG.sync = PNGSync;
53
54PNG.prototype.pack = function () {
55 if (!this.data || !this.data.length) {
56 this.emit("error", "No data provided");
57 return this;
58 }
59
60 process.nextTick(
61 function () {
62 this._packer.pack(this.data, this.width, this.height, this.gamma);
63 }.bind(this)
64 );
65
66 return this;
67};
68
69PNG.prototype.parse = function (data, callback) {
70 if (callback) {
71 let onParsed, onError;
72
73 onParsed = function (parsedData) {
74 this.removeListener("error", onError);
75
76 this.data = parsedData;
77 callback(null, this);
78 }.bind(this);
79
80 onError = function (err) {
81 this.removeListener("parsed", onParsed);
82
83 callback(err, null);
84 }.bind(this);
85
86 this.once("parsed", onParsed);
87 this.once("error", onError);
88 }
89
90 this.end(data);
91 return this;
92};
93
94PNG.prototype.write = function (data) {
95 this._parser.write(data);
96 return true;
97};
98
99PNG.prototype.end = function (data) {
100 this._parser.end(data);
101};
102
103PNG.prototype._metadata = function (metadata) {
104 this.width = metadata.width;
105 this.height = metadata.height;
106
107 this.emit("metadata", metadata);
108};
109
110PNG.prototype._gamma = function (gamma) {
111 this.gamma = gamma;
112};
113
114PNG.prototype._handleClose = function () {
115 if (!this._parser.writable && !this._packer.readable) {
116 this.emit("close");
117 }
118};
119
120PNG.bitblt = function (src, dst, srcX, srcY, width, height, deltaX, deltaY) {
121 // eslint-disable-line max-params
122 // coerce pixel dimensions to integers (also coerces undefined -> 0):
123 /* eslint-disable no-param-reassign */
124 srcX |= 0;
125 srcY |= 0;
126 width |= 0;
127 height |= 0;
128 deltaX |= 0;
129 deltaY |= 0;
130 /* eslint-enable no-param-reassign */
131
132 if (
133 srcX > src.width ||
134 srcY > src.height ||
135 srcX + width > src.width ||
136 srcY + height > src.height
137 ) {
138 throw new Error("bitblt reading outside image");
139 }
140
141 if (
142 deltaX > dst.width ||
143 deltaY > dst.height ||
144 deltaX + width > dst.width ||
145 deltaY + height > dst.height
146 ) {
147 throw new Error("bitblt writing outside image");
148 }
149
150 for (let y = 0; y < height; y++) {
151 src.data.copy(
152 dst.data,
153 ((deltaY + y) * dst.width + deltaX) << 2,
154 ((srcY + y) * src.width + srcX) << 2,
155 ((srcY + y) * src.width + srcX + width) << 2
156 );
157 }
158};
159
160PNG.prototype.bitblt = function (
161 dst,
162 srcX,
163 srcY,
164 width,
165 height,
166 deltaX,
167 deltaY
168) {
169 // eslint-disable-line max-params
170
171 PNG.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY);
172 return this;
173};
174
175PNG.adjustGamma = function (src) {
176 if (src.gamma) {
177 for (let y = 0; y < src.height; y++) {
178 for (let x = 0; x < src.width; x++) {
179 let idx = (src.width * y + x) << 2;
180
181 for (let i = 0; i < 3; i++) {
182 let sample = src.data[idx + i] / 255;
183 sample = Math.pow(sample, 1 / 2.2 / src.gamma);
184 src.data[idx + i] = Math.round(sample * 255);
185 }
186 }
187 }
188 src.gamma = 0;
189 }
190};
191
192PNG.prototype.adjustGamma = function () {
193 PNG.adjustGamma(this);
194};