1 | "use strict";
|
2 |
|
3 | let util = require("util");
|
4 | let Stream = require("stream");
|
5 | let Parser = require("./parser-async");
|
6 | let Packer = require("./packer-async");
|
7 | let PNGSync = require("./png-sync");
|
8 |
|
9 | let PNG = (exports.PNG = function (options) {
|
10 | Stream.call(this);
|
11 |
|
12 | options = options || {};
|
13 |
|
14 |
|
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 | });
|
50 | util.inherits(PNG, Stream);
|
51 |
|
52 | PNG.sync = PNGSync;
|
53 |
|
54 | PNG.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 |
|
69 | PNG.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 |
|
94 | PNG.prototype.write = function (data) {
|
95 | this._parser.write(data);
|
96 | return true;
|
97 | };
|
98 |
|
99 | PNG.prototype.end = function (data) {
|
100 | this._parser.end(data);
|
101 | };
|
102 |
|
103 | PNG.prototype._metadata = function (metadata) {
|
104 | this.width = metadata.width;
|
105 | this.height = metadata.height;
|
106 |
|
107 | this.emit("metadata", metadata);
|
108 | };
|
109 |
|
110 | PNG.prototype._gamma = function (gamma) {
|
111 | this.gamma = gamma;
|
112 | };
|
113 |
|
114 | PNG.prototype._handleClose = function () {
|
115 | if (!this._parser.writable && !this._packer.readable) {
|
116 | this.emit("close");
|
117 | }
|
118 | };
|
119 |
|
120 | PNG.bitblt = function (src, dst, srcX, srcY, width, height, deltaX, deltaY) {
|
121 |
|
122 |
|
123 |
|
124 | srcX |= 0;
|
125 | srcY |= 0;
|
126 | width |= 0;
|
127 | height |= 0;
|
128 | deltaX |= 0;
|
129 | deltaY |= 0;
|
130 |
|
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 |
|
160 | PNG.prototype.bitblt = function (
|
161 | dst,
|
162 | srcX,
|
163 | srcY,
|
164 | width,
|
165 | height,
|
166 | deltaX,
|
167 | deltaY
|
168 | ) {
|
169 |
|
170 |
|
171 | PNG.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY);
|
172 | return this;
|
173 | };
|
174 |
|
175 | PNG.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 |
|
192 | PNG.prototype.adjustGamma = function () {
|
193 | PNG.adjustGamma(this);
|
194 | };
|