UNPKG

4.8 kBJavaScriptView Raw
1"use strict";
2
3let interlaceUtils = require("./interlace");
4let paethPredictor = require("./paeth-predictor");
5
6function 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
14let 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 // when filtering the line we look at the pixel to the left
45 // the spec also says it is done on a byte level regardless of the number of pixels
46 // so if the depth is byte compatible (8 or 16) we subtract the bpp in order to compare back
47 // a pixel rather than just a different byte part. However if we are sub byte, we ignore.
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
57Filter.prototype.start = function () {
58 this.read(
59 this._images[this._imageIndex].byteWidth + 1,
60 this._reverseFilterLine.bind(this)
61 );
62};
63
64Filter.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
79Filter.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
93Filter.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
111Filter.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
130Filter.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 // read, using the byte width that may be from the new current image
172 this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this));
173 } else {
174 this._lastLine = null;
175 this.complete();
176 }
177};