1 | "use strict";
|
2 |
|
3 | let interlaceUtils = require("./interlace");
|
4 | let paethPredictor = require("./paeth-predictor");
|
5 |
|
6 | function getByteWidth(width, bpp, depth) {
|
7 | let byteWidth = width * bpp;
|
8 | if (depth !== 8) {
|
9 | byteWidth = Math.ceil(byteWidth / (8 / depth));
|
10 | }
|
11 | return byteWidth;
|
12 | }
|
13 |
|
14 | let Filter = (module.exports = function (bitmapInfo, dependencies) {
|
15 | let width = bitmapInfo.width;
|
16 | let height = bitmapInfo.height;
|
17 | let interlace = bitmapInfo.interlace;
|
18 | let bpp = bitmapInfo.bpp;
|
19 | let depth = bitmapInfo.depth;
|
20 |
|
21 | this.read = dependencies.read;
|
22 | this.write = dependencies.write;
|
23 | this.complete = dependencies.complete;
|
24 |
|
25 | this._imageIndex = 0;
|
26 | this._images = [];
|
27 | if (interlace) {
|
28 | let passes = interlaceUtils.getImagePasses(width, height);
|
29 | for (let i = 0; i < passes.length; i++) {
|
30 | this._images.push({
|
31 | byteWidth: getByteWidth(passes[i].width, bpp, depth),
|
32 | height: passes[i].height,
|
33 | lineIndex: 0,
|
34 | });
|
35 | }
|
36 | } else {
|
37 | this._images.push({
|
38 | byteWidth: getByteWidth(width, bpp, depth),
|
39 | height: height,
|
40 | lineIndex: 0,
|
41 | });
|
42 | }
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | if (depth === 8) {
|
49 | this._xComparison = bpp;
|
50 | } else if (depth === 16) {
|
51 | this._xComparison = bpp * 2;
|
52 | } else {
|
53 | this._xComparison = 1;
|
54 | }
|
55 | });
|
56 |
|
57 | Filter.prototype.start = function () {
|
58 | this.read(
|
59 | this._images[this._imageIndex].byteWidth + 1,
|
60 | this._reverseFilterLine.bind(this)
|
61 | );
|
62 | };
|
63 |
|
64 | Filter.prototype._unFilterType1 = function (
|
65 | rawData,
|
66 | unfilteredLine,
|
67 | byteWidth
|
68 | ) {
|
69 | let xComparison = this._xComparison;
|
70 | let xBiggerThan = xComparison - 1;
|
71 |
|
72 | for (let x = 0; x < byteWidth; x++) {
|
73 | let rawByte = rawData[1 + x];
|
74 | let f1Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
|
75 | unfilteredLine[x] = rawByte + f1Left;
|
76 | }
|
77 | };
|
78 |
|
79 | Filter.prototype._unFilterType2 = function (
|
80 | rawData,
|
81 | unfilteredLine,
|
82 | byteWidth
|
83 | ) {
|
84 | let lastLine = this._lastLine;
|
85 |
|
86 | for (let x = 0; x < byteWidth; x++) {
|
87 | let rawByte = rawData[1 + x];
|
88 | let f2Up = lastLine ? lastLine[x] : 0;
|
89 | unfilteredLine[x] = rawByte + f2Up;
|
90 | }
|
91 | };
|
92 |
|
93 | Filter.prototype._unFilterType3 = function (
|
94 | rawData,
|
95 | unfilteredLine,
|
96 | byteWidth
|
97 | ) {
|
98 | let xComparison = this._xComparison;
|
99 | let xBiggerThan = xComparison - 1;
|
100 | let lastLine = this._lastLine;
|
101 |
|
102 | for (let x = 0; x < byteWidth; x++) {
|
103 | let rawByte = rawData[1 + x];
|
104 | let f3Up = lastLine ? lastLine[x] : 0;
|
105 | let f3Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
|
106 | let f3Add = Math.floor((f3Left + f3Up) / 2);
|
107 | unfilteredLine[x] = rawByte + f3Add;
|
108 | }
|
109 | };
|
110 |
|
111 | Filter.prototype._unFilterType4 = function (
|
112 | rawData,
|
113 | unfilteredLine,
|
114 | byteWidth
|
115 | ) {
|
116 | let xComparison = this._xComparison;
|
117 | let xBiggerThan = xComparison - 1;
|
118 | let lastLine = this._lastLine;
|
119 |
|
120 | for (let x = 0; x < byteWidth; x++) {
|
121 | let rawByte = rawData[1 + x];
|
122 | let f4Up = lastLine ? lastLine[x] : 0;
|
123 | let f4Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
|
124 | let f4UpLeft = x > xBiggerThan && lastLine ? lastLine[x - xComparison] : 0;
|
125 | let f4Add = paethPredictor(f4Left, f4Up, f4UpLeft);
|
126 | unfilteredLine[x] = rawByte + f4Add;
|
127 | }
|
128 | };
|
129 |
|
130 | Filter.prototype._reverseFilterLine = function (rawData) {
|
131 | let filter = rawData[0];
|
132 | let unfilteredLine;
|
133 | let currentImage = this._images[this._imageIndex];
|
134 | let byteWidth = currentImage.byteWidth;
|
135 |
|
136 | if (filter === 0) {
|
137 | unfilteredLine = rawData.slice(1, byteWidth + 1);
|
138 | } else {
|
139 | unfilteredLine = Buffer.alloc(byteWidth);
|
140 |
|
141 | switch (filter) {
|
142 | case 1:
|
143 | this._unFilterType1(rawData, unfilteredLine, byteWidth);
|
144 | break;
|
145 | case 2:
|
146 | this._unFilterType2(rawData, unfilteredLine, byteWidth);
|
147 | break;
|
148 | case 3:
|
149 | this._unFilterType3(rawData, unfilteredLine, byteWidth);
|
150 | break;
|
151 | case 4:
|
152 | this._unFilterType4(rawData, unfilteredLine, byteWidth);
|
153 | break;
|
154 | default:
|
155 | throw new Error("Unrecognised filter type - " + filter);
|
156 | }
|
157 | }
|
158 |
|
159 | this.write(unfilteredLine);
|
160 |
|
161 | currentImage.lineIndex++;
|
162 | if (currentImage.lineIndex >= currentImage.height) {
|
163 | this._lastLine = null;
|
164 | this._imageIndex++;
|
165 | currentImage = this._images[this._imageIndex];
|
166 | } else {
|
167 | this._lastLine = unfilteredLine;
|
168 | }
|
169 |
|
170 | if (currentImage) {
|
171 |
|
172 | this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this));
|
173 | } else {
|
174 | this._lastLine = null;
|
175 | this.complete();
|
176 | }
|
177 | };
|