@awayjs/stage
Version:
Stage for AwayJS
237 lines (194 loc) • 6.66 kB
text/typescript
import { Matrix, Point } from '@awayjs/core';
export class BitmapImageUtils {
public static drawBitmap(
source: Uint8ClampedArray,
offsetX: number,
offsetY: number,
width: number,
height: number,
canvas: Uint8ClampedArray,
canvasOffsetX: number,
canvasOffsetY: number,
canvasImageWidth: number,
canvasImageHeight: number,
matrix: Matrix = null): void {
if (matrix || (canvasImageWidth != width || canvasImageHeight != height)) {
if (!matrix) {
matrix = new Matrix();
matrix.scale(canvasImageWidth / width, canvasImageHeight / height);
}
const scaleX: number = Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b);
const scaleY: number = Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d);
const canvasWidth = width * scaleX;
const canvasHeight = height * scaleY;
matrix.tx += canvasOffsetX;
matrix.ty += canvasOffsetY;
canvasOffsetX = Math.floor(matrix.tx);
canvasOffsetY = Math.floor(matrix.ty);
matrix.invert();
if (scaleX >= 1 || scaleY >= 1) {
let p: Point = new Point();
for (let i = canvasOffsetX; i < canvasOffsetX + canvasWidth; i++) {
for (let j = canvasOffsetY; j < canvasOffsetY + canvasHeight; j++) {
p.x = i;
p.y = j;
p = matrix.transformPoint(p);
const color: number[] = BitmapImageUtils.sampleBilinear(p.x, p.y, source, width, height);
BitmapImageUtils.applyPixel32(canvas, canvasImageWidth, canvasImageHeight, i, j, color);
}
}
} else {
//decimate
let p1: Point = new Point();
let p2: Point = new Point();
for (let i = canvasOffsetX; i < canvasOffsetX + canvasWidth; i++) {
for (let j = canvasOffsetY; j < canvasOffsetY + canvasHeight; j++) {
p1.x = i;
p1.y = j;
p1 = matrix.transformPoint(p1);
p2.x = i + 1;
p2.y = j + 1;
p2 = matrix.transformPoint(p2);
const color: number[] = BitmapImageUtils.sampleBox(
p1.x + offsetX,
p1.y + offsetY,
p2.x + offsetX,
p2.y + offsetY,
source,
width,
height);
BitmapImageUtils.applyPixel32(canvas, canvasImageWidth, canvasImageHeight, i, j, color);
}
}
}
matrix.invert();
} else {
for (let i = canvasOffsetX; i < canvasOffsetX + width; i++) {
for (let j = canvasOffsetY; j < canvasOffsetY + height; j++) {
const color: number[] = BitmapImageUtils.sample(
i - canvasOffsetX + offsetX,
j - canvasOffsetY + offsetY,
source,
width,
height);
BitmapImageUtils.applyPixel32(canvas, canvasImageWidth, canvasImageHeight, i, j, color);
}
}
}
}
private static applyPixel32(
data: Uint8ClampedArray, width: number, height: number, x: number, y: number, color: number[]) {
//todo: blending support
x = Math.floor(x);
y = Math.floor(y);
if (x < 0 || x >= width || y >= height || y < 0)
return;
const index: number = (x + y * width) * 4;
//var alpha:number = color[3] / 255;
// target.data[index] += color[0];
// target.data[index + 1] += color[1];
// target.data[index + 2] += color[2];
// target.data[index + 3] += color[3];
data[index] = color[0];
data[index + 1] = color[1];
data[index + 2] = color[2];
data[index + 3] = color[3];
data[index] = data[index] & 0xFF;
data[index + 1] = data[index + 1] & 0xFF;
data[index + 2] = data[index + 2] & 0xFF;
data[index + 3] = data[index + 3] & 0xFF;
}
public static sampleBilinear(
u: number, v: number,
data: Uint8ClampedArray,
width: number, height: number,
texelSizeX: number = 1, texelSizeY: number = 1): number[] {
const color00: number[] = BitmapImageUtils.sample(u, v, data, width, height);
const color10: number[] = BitmapImageUtils.sample(u + texelSizeX, v, data, width, height);
const color01: number[] = BitmapImageUtils.sample(u, v + texelSizeY, data, width, height);
const color11: number[] = BitmapImageUtils.sample(u + texelSizeX, v + texelSizeY, data, width, height);
let a: number = u;
a = a - Math.floor(a);
const interColor0: number[] = BitmapImageUtils.interpolateColor(color00, color10, a);
const interColor1: number[] = BitmapImageUtils.interpolateColor(color01, color11, a);
let b: number = v;
b = b - Math.floor(b);
return BitmapImageUtils.interpolateColor(interColor0, interColor1, b);
}
public static sample(u: number, v: number, data: Uint8ClampedArray, width: number, height: number): number[] {
u = Math.floor(u);
v = Math.floor(v);
const result: number[] = [0, 0, 0, 0];
if (u < 0 || u >= width || v < 0 || v >= height) {
return result;
}
const index: number = (u + v * width) * 4;
result[0] = data[index];
result[1] = data[index + 1];
result[2] = data[index + 2];
result[3] = data[index + 3];
return result;
}
public static sampleBox(
x0: number,y0: number,
x1: number, y1: number,
data: Uint8ClampedArray,
width: number,
height: number): number[] {
let area: number = 0;// -- total area accumulated in pixels
const result: number[] = [0, 0, 0, 0];
let x: number;
let y: number;
let xsize: number;
let ysize: number;
let fromY: number = Math.floor(y0);
let toY: number = Math.ceil(y1);
fromY = Math.max(Math.min(fromY, height - 1), 0);
toY = Math.max(Math.min(toY, height - 1), 0);
for (y = fromY; y < toY; y++) {
ysize = 1;
if (y < y0) {
ysize = ysize * (1.0 - (y0 - y));
}
if (y > y1) {
ysize = ysize * (1.0 - (y - y1));
}
let fromX: number = Math.floor(x0);
let toX: number = Math.ceil(x1);
fromX = Math.max(Math.min(fromX, width - 1), 0);
toX = Math.max(Math.min(toX, width - 1), 0);
for (x = fromX; x < toX; x++) {
xsize = ysize;
const color: number[] = BitmapImageUtils.sample(x, y, data, width, height);
if (x < x0) {
xsize = xsize * (1.0 - (x0 - x));
}
if (x > x1) {
xsize = xsize * (1.0 - (x - x1));
}
result[0] += color[0] * xsize;
result[1] += color[1] * xsize;
result[2] += color[2] * xsize;
result[3] += color[3] * xsize;
area = area + xsize;
}
}
result[0] /= area;
result[1] /= area;
result[2] /= area;
result[3] /= area;
result[0] = result[0] & 0xFF;
result[1] = result[1] & 0xFF;
result[2] = result[2] & 0xFF;
result[3] = result[3] & 0xFF;
return result;
}
private static interpolateColor(source: number[], target: number[], a: number): number[] {
const result: number[] = [];
result[0] = source[0] + (target[0] - source[0]) * a;
result[1] = source[1] + (target[1] - source[1]) * a;
result[2] = source[2] + (target[2] - source[2]) * a;
result[3] = source[3] + (target[3] - source[3]) * a;
return result;
}
}