Spaces:
Sleeping
Sleeping
File size: 4,116 Bytes
11811dc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | // @flow
import assert from 'assert';
import {register} from './web_worker_transfer';
export type Size = {
width: number,
height: number
};
type Point = {
x: number,
y: number
};
function createImage(image: *, {width, height}: Size, channels: number, data?: Uint8Array | Uint8ClampedArray) {
if (!data) {
data = new Uint8Array(width * height * channels);
} else if (data instanceof Uint8ClampedArray) {
data = new Uint8Array(data.buffer);
} else if (data.length !== width * height * channels) {
throw new RangeError('mismatched image size');
}
image.width = width;
image.height = height;
image.data = data;
return image;
}
function resizeImage(image: *, {width, height}: Size, channels: number) {
if (width === image.width && height === image.height) {
return;
}
const newImage = createImage({}, {width, height}, channels);
copyImage(image, newImage, {x: 0, y: 0}, {x: 0, y: 0}, {
width: Math.min(image.width, width),
height: Math.min(image.height, height)
}, channels);
image.width = width;
image.height = height;
image.data = newImage.data;
}
function copyImage(srcImg: *, dstImg: *, srcPt: Point, dstPt: Point, size: Size, channels: number) {
if (size.width === 0 || size.height === 0) {
return dstImg;
}
if (size.width > srcImg.width ||
size.height > srcImg.height ||
srcPt.x > srcImg.width - size.width ||
srcPt.y > srcImg.height - size.height) {
throw new RangeError('out of range source coordinates for image copy');
}
if (size.width > dstImg.width ||
size.height > dstImg.height ||
dstPt.x > dstImg.width - size.width ||
dstPt.y > dstImg.height - size.height) {
throw new RangeError('out of range destination coordinates for image copy');
}
const srcData = srcImg.data;
const dstData = dstImg.data;
assert(srcData !== dstData);
for (let y = 0; y < size.height; y++) {
const srcOffset = ((srcPt.y + y) * srcImg.width + srcPt.x) * channels;
const dstOffset = ((dstPt.y + y) * dstImg.width + dstPt.x) * channels;
for (let i = 0; i < size.width * channels; i++) {
dstData[dstOffset + i] = srcData[srcOffset + i];
}
}
return dstImg;
}
export class AlphaImage {
width: number;
height: number;
data: Uint8Array;
constructor(size: Size, data?: Uint8Array | Uint8ClampedArray) {
createImage(this, size, 1, data);
}
resize(size: Size) {
resizeImage(this, size, 1);
}
clone() {
return new AlphaImage({width: this.width, height: this.height}, new Uint8Array(this.data));
}
static copy(srcImg: AlphaImage, dstImg: AlphaImage, srcPt: Point, dstPt: Point, size: Size) {
copyImage(srcImg, dstImg, srcPt, dstPt, size, 1);
}
}
// Not premultiplied, because ImageData is not premultiplied.
// UNPACK_PREMULTIPLY_ALPHA_WEBGL must be used when uploading to a texture.
export class RGBAImage {
width: number;
height: number;
// data must be a Uint8Array instead of Uint8ClampedArray because texImage2D does not
// support Uint8ClampedArray in all browsers
data: Uint8Array;
constructor(size: Size, data?: Uint8Array | Uint8ClampedArray) {
createImage(this, size, 4, data);
}
resize(size: Size) {
resizeImage(this, size, 4);
}
replace(data: Uint8Array | Uint8ClampedArray, copy?: boolean) {
if (copy) {
this.data.set(data);
} else if (data instanceof Uint8ClampedArray) {
this.data = new Uint8Array(data.buffer);
} else {
this.data = data;
}
}
clone() {
return new RGBAImage({width: this.width, height: this.height}, new Uint8Array(this.data));
}
static copy(srcImg: RGBAImage | ImageData, dstImg: RGBAImage, srcPt: Point, dstPt: Point, size: Size) {
copyImage(srcImg, dstImg, srcPt, dstPt, size, 4);
}
}
register('AlphaImage', AlphaImage);
register('RGBAImage', RGBAImage);
|