Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jajbshjahavahh
GitHub Repository: jajbshjahavahh/Gojo-Satoru
Path: blob/master/node_modules/bmp-js/lib/decoder.js
2591 views
1
/**
2
* @author shaozilee
3
*
4
* Bmp format decoder,support 1bit 4bit 8bit 24bit bmp
5
*
6
*/
7
8
function BmpDecoder(buffer,is_with_alpha) {
9
this.pos = 0;
10
this.buffer = buffer;
11
this.is_with_alpha = !!is_with_alpha;
12
this.bottom_up = true;
13
this.flag = this.buffer.toString("utf-8", 0, this.pos += 2);
14
if (this.flag != "BM") throw new Error("Invalid BMP File");
15
this.parseHeader();
16
this.parseRGBA();
17
}
18
19
BmpDecoder.prototype.parseHeader = function() {
20
this.fileSize = this.buffer.readUInt32LE(this.pos);
21
this.pos += 4;
22
this.reserved = this.buffer.readUInt32LE(this.pos);
23
this.pos += 4;
24
this.offset = this.buffer.readUInt32LE(this.pos);
25
this.pos += 4;
26
this.headerSize = this.buffer.readUInt32LE(this.pos);
27
this.pos += 4;
28
this.width = this.buffer.readUInt32LE(this.pos);
29
this.pos += 4;
30
this.height = this.buffer.readInt32LE(this.pos);
31
this.pos += 4;
32
this.planes = this.buffer.readUInt16LE(this.pos);
33
this.pos += 2;
34
this.bitPP = this.buffer.readUInt16LE(this.pos);
35
this.pos += 2;
36
this.compress = this.buffer.readUInt32LE(this.pos);
37
this.pos += 4;
38
this.rawSize = this.buffer.readUInt32LE(this.pos);
39
this.pos += 4;
40
this.hr = this.buffer.readUInt32LE(this.pos);
41
this.pos += 4;
42
this.vr = this.buffer.readUInt32LE(this.pos);
43
this.pos += 4;
44
this.colors = this.buffer.readUInt32LE(this.pos);
45
this.pos += 4;
46
this.importantColors = this.buffer.readUInt32LE(this.pos);
47
this.pos += 4;
48
49
if(this.bitPP === 16 && this.is_with_alpha){
50
this.bitPP = 15
51
}
52
if (this.bitPP < 15) {
53
var len = this.colors === 0 ? 1 << this.bitPP : this.colors;
54
this.palette = new Array(len);
55
for (var i = 0; i < len; i++) {
56
var blue = this.buffer.readUInt8(this.pos++);
57
var green = this.buffer.readUInt8(this.pos++);
58
var red = this.buffer.readUInt8(this.pos++);
59
var quad = this.buffer.readUInt8(this.pos++);
60
this.palette[i] = {
61
red: red,
62
green: green,
63
blue: blue,
64
quad: quad
65
};
66
}
67
}
68
if(this.height < 0) {
69
this.height *= -1;
70
this.bottom_up = false;
71
}
72
73
}
74
75
BmpDecoder.prototype.parseRGBA = function() {
76
var bitn = "bit" + this.bitPP;
77
var len = this.width * this.height * 4;
78
this.data = new Buffer(len);
79
this[bitn]();
80
};
81
82
BmpDecoder.prototype.bit1 = function() {
83
var xlen = Math.ceil(this.width / 8);
84
var mode = xlen%4;
85
var y = this.height >= 0 ? this.height - 1 : -this.height
86
for (var y = this.height - 1; y >= 0; y--) {
87
var line = this.bottom_up ? y : this.height - 1 - y
88
for (var x = 0; x < xlen; x++) {
89
var b = this.buffer.readUInt8(this.pos++);
90
var location = line * this.width * 4 + x*8*4;
91
for (var i = 0; i < 8; i++) {
92
if(x*8+i<this.width){
93
var rgb = this.palette[((b>>(7-i))&0x1)];
94
95
this.data[location+i*4] = 0;
96
this.data[location+i*4 + 1] = rgb.blue;
97
this.data[location+i*4 + 2] = rgb.green;
98
this.data[location+i*4 + 3] = rgb.red;
99
100
}else{
101
break;
102
}
103
}
104
}
105
106
if (mode != 0){
107
this.pos+=(4 - mode);
108
}
109
}
110
};
111
112
BmpDecoder.prototype.bit4 = function() {
113
//RLE-4
114
if(this.compress == 2){
115
this.data.fill(0xff);
116
117
var location = 0;
118
var lines = this.bottom_up?this.height-1:0;
119
var low_nibble = false;//for all count of pixel
120
121
while(location<this.data.length){
122
var a = this.buffer.readUInt8(this.pos++);
123
var b = this.buffer.readUInt8(this.pos++);
124
//absolute mode
125
if(a == 0){
126
if(b == 0){//line end
127
if(this.bottom_up){
128
lines--;
129
}else{
130
lines++;
131
}
132
location = lines*this.width*4;
133
low_nibble = false;
134
continue;
135
}else if(b == 1){//image end
136
break;
137
}else if(b ==2){
138
//offset x,y
139
var x = this.buffer.readUInt8(this.pos++);
140
var y = this.buffer.readUInt8(this.pos++);
141
if(this.bottom_up){
142
lines-=y;
143
}else{
144
lines+=y;
145
}
146
147
location +=(y*this.width*4+x*4);
148
}else{
149
var c = this.buffer.readUInt8(this.pos++);
150
for(var i=0;i<b;i++){
151
if (low_nibble) {
152
setPixelData.call(this, (c & 0x0f));
153
} else {
154
setPixelData.call(this, (c & 0xf0)>>4);
155
}
156
157
if ((i & 1) && (i+1 < b)){
158
c = this.buffer.readUInt8(this.pos++);
159
}
160
161
low_nibble = !low_nibble;
162
}
163
164
if ((((b+1) >> 1) & 1 ) == 1){
165
this.pos++
166
}
167
}
168
169
}else{//encoded mode
170
for (var i = 0; i < a; i++) {
171
if (low_nibble) {
172
setPixelData.call(this, (b & 0x0f));
173
} else {
174
setPixelData.call(this, (b & 0xf0)>>4);
175
}
176
low_nibble = !low_nibble;
177
}
178
}
179
180
}
181
182
183
184
185
function setPixelData(rgbIndex){
186
var rgb = this.palette[rgbIndex];
187
this.data[location] = 0;
188
this.data[location + 1] = rgb.blue;
189
this.data[location + 2] = rgb.green;
190
this.data[location + 3] = rgb.red;
191
location+=4;
192
}
193
}else{
194
195
var xlen = Math.ceil(this.width/2);
196
var mode = xlen%4;
197
for (var y = this.height - 1; y >= 0; y--) {
198
var line = this.bottom_up ? y : this.height - 1 - y
199
for (var x = 0; x < xlen; x++) {
200
var b = this.buffer.readUInt8(this.pos++);
201
var location = line * this.width * 4 + x*2*4;
202
203
var before = b>>4;
204
var after = b&0x0F;
205
206
var rgb = this.palette[before];
207
this.data[location] = 0;
208
this.data[location + 1] = rgb.blue;
209
this.data[location + 2] = rgb.green;
210
this.data[location + 3] = rgb.red;
211
212
213
if(x*2+1>=this.width)break;
214
215
rgb = this.palette[after];
216
217
this.data[location+4] = 0;
218
this.data[location+4 + 1] = rgb.blue;
219
this.data[location+4 + 2] = rgb.green;
220
this.data[location+4 + 3] = rgb.red;
221
222
}
223
224
if (mode != 0){
225
this.pos+=(4 - mode);
226
}
227
}
228
229
}
230
231
};
232
233
BmpDecoder.prototype.bit8 = function() {
234
//RLE-8
235
if(this.compress == 1){
236
this.data.fill(0xff);
237
238
var location = 0;
239
var lines = this.bottom_up?this.height-1:0;
240
241
while(location<this.data.length){
242
var a = this.buffer.readUInt8(this.pos++);
243
var b = this.buffer.readUInt8(this.pos++);
244
//absolute mode
245
if(a == 0){
246
if(b == 0){//line end
247
if(this.bottom_up){
248
lines--;
249
}else{
250
lines++;
251
}
252
location = lines*this.width*4;
253
continue;
254
}else if(b == 1){//image end
255
break;
256
}else if(b ==2){
257
//offset x,y
258
var x = this.buffer.readUInt8(this.pos++);
259
var y = this.buffer.readUInt8(this.pos++);
260
if(this.bottom_up){
261
lines-=y;
262
}else{
263
lines+=y;
264
}
265
266
location +=(y*this.width*4+x*4);
267
}else{
268
for(var i=0;i<b;i++){
269
var c = this.buffer.readUInt8(this.pos++);
270
setPixelData.call(this, c);
271
}
272
if(b&1 == 1){
273
this.pos++;
274
}
275
276
}
277
278
}else{//encoded mode
279
for (var i = 0; i < a; i++) {
280
setPixelData.call(this, b);
281
}
282
}
283
284
}
285
286
287
288
289
function setPixelData(rgbIndex){
290
var rgb = this.palette[rgbIndex];
291
this.data[location] = 0;
292
this.data[location + 1] = rgb.blue;
293
this.data[location + 2] = rgb.green;
294
this.data[location + 3] = rgb.red;
295
location+=4;
296
}
297
}else {
298
var mode = this.width % 4;
299
for (var y = this.height - 1; y >= 0; y--) {
300
var line = this.bottom_up ? y : this.height - 1 - y
301
for (var x = 0; x < this.width; x++) {
302
var b = this.buffer.readUInt8(this.pos++);
303
var location = line * this.width * 4 + x * 4;
304
if (b < this.palette.length) {
305
var rgb = this.palette[b];
306
307
this.data[location] = 0;
308
this.data[location + 1] = rgb.blue;
309
this.data[location + 2] = rgb.green;
310
this.data[location + 3] = rgb.red;
311
312
} else {
313
this.data[location] = 0;
314
this.data[location + 1] = 0xFF;
315
this.data[location + 2] = 0xFF;
316
this.data[location + 3] = 0xFF;
317
}
318
}
319
if (mode != 0) {
320
this.pos += (4 - mode);
321
}
322
}
323
}
324
};
325
326
BmpDecoder.prototype.bit15 = function() {
327
var dif_w =this.width % 3;
328
var _11111 = parseInt("11111", 2),_1_5 = _11111;
329
for (var y = this.height - 1; y >= 0; y--) {
330
var line = this.bottom_up ? y : this.height - 1 - y
331
for (var x = 0; x < this.width; x++) {
332
333
var B = this.buffer.readUInt16LE(this.pos);
334
this.pos+=2;
335
var blue = (B & _1_5) / _1_5 * 255 | 0;
336
var green = (B >> 5 & _1_5 ) / _1_5 * 255 | 0;
337
var red = (B >> 10 & _1_5) / _1_5 * 255 | 0;
338
var alpha = (B>>15)?0xFF:0x00;
339
340
var location = line * this.width * 4 + x * 4;
341
342
this.data[location] = alpha;
343
this.data[location + 1] = blue;
344
this.data[location + 2] = green;
345
this.data[location + 3] = red;
346
}
347
//skip extra bytes
348
this.pos += dif_w;
349
}
350
};
351
352
BmpDecoder.prototype.bit16 = function() {
353
var dif_w =(this.width % 2)*2;
354
//default xrgb555
355
this.maskRed = 0x7C00;
356
this.maskGreen = 0x3E0;
357
this.maskBlue =0x1F;
358
this.mask0 = 0;
359
360
if(this.compress == 3){
361
this.maskRed = this.buffer.readUInt32LE(this.pos);
362
this.pos+=4;
363
this.maskGreen = this.buffer.readUInt32LE(this.pos);
364
this.pos+=4;
365
this.maskBlue = this.buffer.readUInt32LE(this.pos);
366
this.pos+=4;
367
this.mask0 = this.buffer.readUInt32LE(this.pos);
368
this.pos+=4;
369
}
370
371
372
var ns=[0,0,0];
373
for (var i=0;i<16;i++){
374
if ((this.maskRed>>i)&0x01) ns[0]++;
375
if ((this.maskGreen>>i)&0x01) ns[1]++;
376
if ((this.maskBlue>>i)&0x01) ns[2]++;
377
}
378
ns[1]+=ns[0]; ns[2]+=ns[1]; ns[0]=8-ns[0]; ns[1]-=8; ns[2]-=8;
379
380
for (var y = this.height - 1; y >= 0; y--) {
381
var line = this.bottom_up ? y : this.height - 1 - y;
382
for (var x = 0; x < this.width; x++) {
383
384
var B = this.buffer.readUInt16LE(this.pos);
385
this.pos+=2;
386
387
var blue = (B&this.maskBlue)<<ns[0];
388
var green = (B&this.maskGreen)>>ns[1];
389
var red = (B&this.maskRed)>>ns[2];
390
391
var location = line * this.width * 4 + x * 4;
392
393
this.data[location] = 0;
394
this.data[location + 1] = blue;
395
this.data[location + 2] = green;
396
this.data[location + 3] = red;
397
}
398
//skip extra bytes
399
this.pos += dif_w;
400
}
401
};
402
403
BmpDecoder.prototype.bit24 = function() {
404
for (var y = this.height - 1; y >= 0; y--) {
405
var line = this.bottom_up ? y : this.height - 1 - y
406
for (var x = 0; x < this.width; x++) {
407
//Little Endian rgb
408
var blue = this.buffer.readUInt8(this.pos++);
409
var green = this.buffer.readUInt8(this.pos++);
410
var red = this.buffer.readUInt8(this.pos++);
411
var location = line * this.width * 4 + x * 4;
412
this.data[location] = 0;
413
this.data[location + 1] = blue;
414
this.data[location + 2] = green;
415
this.data[location + 3] = red;
416
}
417
//skip extra bytes
418
this.pos += (this.width % 4);
419
}
420
421
};
422
423
/**
424
* add 32bit decode func
425
* @author soubok
426
*/
427
BmpDecoder.prototype.bit32 = function() {
428
//BI_BITFIELDS
429
if(this.compress == 3){
430
this.maskRed = this.buffer.readUInt32LE(this.pos);
431
this.pos+=4;
432
this.maskGreen = this.buffer.readUInt32LE(this.pos);
433
this.pos+=4;
434
this.maskBlue = this.buffer.readUInt32LE(this.pos);
435
this.pos+=4;
436
this.mask0 = this.buffer.readUInt32LE(this.pos);
437
this.pos+=4;
438
for (var y = this.height - 1; y >= 0; y--) {
439
var line = this.bottom_up ? y : this.height - 1 - y;
440
for (var x = 0; x < this.width; x++) {
441
//Little Endian rgba
442
var alpha = this.buffer.readUInt8(this.pos++);
443
var blue = this.buffer.readUInt8(this.pos++);
444
var green = this.buffer.readUInt8(this.pos++);
445
var red = this.buffer.readUInt8(this.pos++);
446
var location = line * this.width * 4 + x * 4;
447
this.data[location] = alpha;
448
this.data[location + 1] = blue;
449
this.data[location + 2] = green;
450
this.data[location + 3] = red;
451
}
452
}
453
454
}else{
455
for (var y = this.height - 1; y >= 0; y--) {
456
var line = this.bottom_up ? y : this.height - 1 - y;
457
for (var x = 0; x < this.width; x++) {
458
//Little Endian argb
459
var blue = this.buffer.readUInt8(this.pos++);
460
var green = this.buffer.readUInt8(this.pos++);
461
var red = this.buffer.readUInt8(this.pos++);
462
var alpha = this.buffer.readUInt8(this.pos++);
463
var location = line * this.width * 4 + x * 4;
464
this.data[location] = alpha;
465
this.data[location + 1] = blue;
466
this.data[location + 2] = green;
467
this.data[location + 3] = red;
468
}
469
}
470
471
}
472
473
474
475
476
};
477
478
BmpDecoder.prototype.getData = function() {
479
return this.data;
480
};
481
482
module.exports = function(bmpData) {
483
var decoder = new BmpDecoder(bmpData);
484
return decoder;
485
};
486
487