1 | "use strict";
|
2 |
|
3 | let paethPredictor = require("./paeth-predictor");
|
4 |
|
5 | function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) {
|
6 | for (let x = 0; x < byteWidth; x++) {
|
7 | rawData[rawPos + x] = pxData[pxPos + x];
|
8 | }
|
9 | }
|
10 |
|
11 | function filterSumNone(pxData, pxPos, byteWidth) {
|
12 | let sum = 0;
|
13 | let length = pxPos + byteWidth;
|
14 |
|
15 | for (let i = pxPos; i < length; i++) {
|
16 | sum += Math.abs(pxData[i]);
|
17 | }
|
18 | return sum;
|
19 | }
|
20 |
|
21 | function filterSub(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
|
22 | for (let x = 0; x < byteWidth; x++) {
|
23 | let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
24 | let val = pxData[pxPos + x] - left;
|
25 |
|
26 | rawData[rawPos + x] = val;
|
27 | }
|
28 | }
|
29 |
|
30 | function filterSumSub(pxData, pxPos, byteWidth, bpp) {
|
31 | let sum = 0;
|
32 | for (let x = 0; x < byteWidth; x++) {
|
33 | let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
34 | let val = pxData[pxPos + x] - left;
|
35 |
|
36 | sum += Math.abs(val);
|
37 | }
|
38 |
|
39 | return sum;
|
40 | }
|
41 |
|
42 | function filterUp(pxData, pxPos, byteWidth, rawData, rawPos) {
|
43 | for (let x = 0; x < byteWidth; x++) {
|
44 | let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
45 | let val = pxData[pxPos + x] - up;
|
46 |
|
47 | rawData[rawPos + x] = val;
|
48 | }
|
49 | }
|
50 |
|
51 | function filterSumUp(pxData, pxPos, byteWidth) {
|
52 | let sum = 0;
|
53 | let length = pxPos + byteWidth;
|
54 | for (let x = pxPos; x < length; x++) {
|
55 | let up = pxPos > 0 ? pxData[x - byteWidth] : 0;
|
56 | let val = pxData[x] - up;
|
57 |
|
58 | sum += Math.abs(val);
|
59 | }
|
60 |
|
61 | return sum;
|
62 | }
|
63 |
|
64 | function filterAvg(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
|
65 | for (let x = 0; x < byteWidth; x++) {
|
66 | let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
67 | let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
68 | let val = pxData[pxPos + x] - ((left + up) >> 1);
|
69 |
|
70 | rawData[rawPos + x] = val;
|
71 | }
|
72 | }
|
73 |
|
74 | function filterSumAvg(pxData, pxPos, byteWidth, bpp) {
|
75 | let sum = 0;
|
76 | for (let x = 0; x < byteWidth; x++) {
|
77 | let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
78 | let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
79 | let val = pxData[pxPos + x] - ((left + up) >> 1);
|
80 |
|
81 | sum += Math.abs(val);
|
82 | }
|
83 |
|
84 | return sum;
|
85 | }
|
86 |
|
87 | function filterPaeth(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
|
88 | for (let x = 0; x < byteWidth; x++) {
|
89 | let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
90 | let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
91 | let upleft =
|
92 | pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
|
93 | let val = pxData[pxPos + x] - paethPredictor(left, up, upleft);
|
94 |
|
95 | rawData[rawPos + x] = val;
|
96 | }
|
97 | }
|
98 |
|
99 | function filterSumPaeth(pxData, pxPos, byteWidth, bpp) {
|
100 | let sum = 0;
|
101 | for (let x = 0; x < byteWidth; x++) {
|
102 | let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
103 | let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
104 | let upleft =
|
105 | pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
|
106 | let val = pxData[pxPos + x] - paethPredictor(left, up, upleft);
|
107 |
|
108 | sum += Math.abs(val);
|
109 | }
|
110 |
|
111 | return sum;
|
112 | }
|
113 |
|
114 | let filters = {
|
115 | 0: filterNone,
|
116 | 1: filterSub,
|
117 | 2: filterUp,
|
118 | 3: filterAvg,
|
119 | 4: filterPaeth,
|
120 | };
|
121 |
|
122 | let filterSums = {
|
123 | 0: filterSumNone,
|
124 | 1: filterSumSub,
|
125 | 2: filterSumUp,
|
126 | 3: filterSumAvg,
|
127 | 4: filterSumPaeth,
|
128 | };
|
129 |
|
130 | module.exports = function (pxData, width, height, options, bpp) {
|
131 | let filterTypes;
|
132 | if (!("filterType" in options) || options.filterType === -1) {
|
133 | filterTypes = [0, 1, 2, 3, 4];
|
134 | } else if (typeof options.filterType === "number") {
|
135 | filterTypes = [options.filterType];
|
136 | } else {
|
137 | throw new Error("unrecognised filter types");
|
138 | }
|
139 |
|
140 | if (options.bitDepth === 16) {
|
141 | bpp *= 2;
|
142 | }
|
143 | let byteWidth = width * bpp;
|
144 | let rawPos = 0;
|
145 | let pxPos = 0;
|
146 | let rawData = Buffer.alloc((byteWidth + 1) * height);
|
147 |
|
148 | let sel = filterTypes[0];
|
149 |
|
150 | for (let y = 0; y < height; y++) {
|
151 | if (filterTypes.length > 1) {
|
152 |
|
153 | let min = Infinity;
|
154 |
|
155 | for (let i = 0; i < filterTypes.length; i++) {
|
156 | let sum = filterSums[filterTypes[i]](pxData, pxPos, byteWidth, bpp);
|
157 | if (sum < min) {
|
158 | sel = filterTypes[i];
|
159 | min = sum;
|
160 | }
|
161 | }
|
162 | }
|
163 |
|
164 | rawData[rawPos] = sel;
|
165 | rawPos++;
|
166 | filters[sel](pxData, pxPos, byteWidth, rawData, rawPos, bpp);
|
167 | rawPos += byteWidth;
|
168 | pxPos += byteWidth;
|
169 | }
|
170 | return rawData;
|
171 | };
|