/// ///
import {Injectable} from '@angular/core';
declare var base64js: any /** TODO #9100 */;
// Temporary fix for Typescript issue #4220 (https://github.com/Microsoft/TypeScript/issues/4220)
// var _ImageData: (width: number, height: number) => void = postMessage;
var _ImageData: {
prototype: ImageData, new (width: number, height: number): ImageData;
}
= ImageData;
// This class is based on the Bitmap examples at:
// http://www.i-programmer.info/projects/36-web/6234-reading-a-bmp-file-in-javascript.html
// and
// http://www.worldwidewhat.net/2012/07/how-to-draw-bitmaps-using-javascript/
@Injectable()
export class BitmapService {
convertToImageData(buffer: ArrayBuffer): ImageData {
var bmp = this._getBMP(buffer);
return this._BMPToImageData(bmp);
}
applySepia(imageData: ImageData): ImageData {
var buffer = imageData.data;
for (var i = 0; i < buffer.length; i += 4) {
var r = buffer[i];
var g = buffer[i + 1];
var b = buffer[i + 2];
buffer[i] = (r * .393) + (g * .769) + (b * .189);
buffer[i + 1] = (r * .349) + (g * .686) + (b * .168);
buffer[i + 2] = (r * .272) + (g * .534) + (b * .131);
}
return imageData;
}
toDataUri(imageData: ImageData): string {
var header = this._createBMPHeader(imageData);
imageData = this._imageDataToBMP(imageData);
return 'data:image/bmp;base64,' + btoa(header) + base64js.fromByteArray(imageData.data);
}
// converts a .bmp file ArrayBuffer to a dataURI
arrayBufferToDataUri(data: Uint8Array): string {
return 'data:image/bmp;base64,' + base64js.fromByteArray(data);
}
// returns a UInt8Array in BMP order (starting from the bottom)
private _imageDataToBMP(imageData: ImageData): ImageData {
var width = imageData.width;
var height = imageData.height;
var data = imageData.data;
for (var y = 0; y < height / 2; ++y) {
var topIndex = y * width * 4;
var bottomIndex = (height - y) * width * 4;
for (var i = 0; i < width * 4; i++) {
this._swap(data, topIndex, bottomIndex);
topIndex++;
bottomIndex++;
}
}
return imageData;
}
private _swap(data: Uint8Array | Uint8ClampedArray | number[], index1: number, index2: number) {
var temp = data[index1];
data[index1] = data[index2];
data[index2] = temp;
}
// Based on example from
// http://www.worldwidewhat.net/2012/07/how-to-draw-bitmaps-using-javascript/
private _createBMPHeader(imageData: ImageData): string {
var numFileBytes = this._getLittleEndianHex(imageData.width * imageData.height);
var w = this._getLittleEndianHex(imageData.width);
var h = this._getLittleEndianHex(imageData.height);
return 'BM' + // Signature
numFileBytes + // size of the file (bytes)*
'\x00\x00' + // reserved
'\x00\x00' + // reserved
'\x36\x00\x00\x00' + // offset of where BMP data lives (54 bytes)
'\x28\x00\x00\x00' + // number of remaining bytes in header from here (40 bytes)
w + // the width of the bitmap in pixels*
h + // the height of the bitmap in pixels*
'\x01\x00' + // the number of color planes (1)
'\x20\x00' + // 32 bits / pixel
'\x00\x00\x00\x00' + // No compression (0)
'\x00\x00\x00\x00' + // size of the BMP data (bytes)*
'\x13\x0B\x00\x00' + // 2835 pixels/meter - horizontal resolution
'\x13\x0B\x00\x00' + // 2835 pixels/meter - the vertical resolution
'\x00\x00\x00\x00' + // Number of colors in the palette (keep 0 for 32-bit)
'\x00\x00\x00\x00'; // 0 important colors (means all colors are important)
}
private _BMPToImageData(bmp: BitmapFile): ImageData {
var width = bmp.infoHeader.biWidth;
var height = bmp.infoHeader.biHeight;
var imageData = new _ImageData(width, height);
var data = imageData.data;
var bmpData = bmp.pixels;
var stride = bmp.stride;
for (var y = 0; y < height; ++y) {
for (var x = 0; x < width; ++x) {
var index1 = (x + width * (height - y)) * 4;
var index2 = x * 3 + stride * y;
data[index1] = bmpData[index2 + 2];
data[index1 + 1] = bmpData[index2 + 1];
data[index1 + 2] = bmpData[index2];
data[index1 + 3] = 255;
}
}
return imageData;
}
private _getBMP(buffer: ArrayBuffer): BitmapFile {
var datav = new DataView(buffer);
var bitmap: BitmapFile = {
fileHeader: {
bfType: datav.getUint16(0, true),
bfSize: datav.getUint32(2, true),
bfReserved1: datav.getUint16(6, true),
bfReserved2: datav.getUint16(8, true),
bfOffBits: datav.getUint32(10, true),
},
infoHeader: {
biSize: datav.getUint32(14, true),
biWidth: datav.getUint32(18, true),
biHeight: datav.getUint32(22, true),
biPlanes: datav.getUint16(26, true),
biBitCount: datav.getUint16(28, true),
biCompression: datav.getUint32(30, true),
biSizeImage: datav.getUint32(34, true),
biXPelsPerMeter: datav.getUint32(38, true),
biYPelsPerMeter: datav.getUint32(42, true),
biClrUsed: datav.getUint32(46, true),
biClrImportant: datav.getUint32(50, true)
},
stride: null,
pixels: null
};
var start = bitmap.fileHeader.bfOffBits;
bitmap.stride =
Math.floor((bitmap.infoHeader.biBitCount * bitmap.infoHeader.biWidth + 31) / 32) * 4;
bitmap.pixels = new Uint8Array(datav.buffer, start);
return bitmap;
}
// Based on example from
// http://www.worldwidewhat.net/2012/07/how-to-draw-bitmaps-using-javascript/
private _getLittleEndianHex(value: number): string {
var result: any[] /** TODO #9100 */ = [];
for (var bytes = 4; bytes > 0; bytes--) {
result.push(String.fromCharCode(value & 255));
value >>= 8;
}
return result.join('');
}
}
interface BitmapFile {
fileHeader: {
bfType: number;
bfSize: number;
bfReserved1: number;
bfReserved2: number;
bfOffBits: number;
};
infoHeader: {
biSize: number;
biWidth: number;
biHeight: number;
biPlanes: number;
biBitCount: number;
biCompression: number;
biSizeImage: number;
biXPelsPerMeter: number;
biYPelsPerMeter: number;
biClrUsed: number;
biClrImportant: number
};
stride: number;
pixels: Uint8Array;
}