class BGRAPixel {
private raw: Uint8ClampedArray;
constructor(red: number, green: number, blue: number, alpha: number) {
this.raw = new Uint8ClampedArray(4);
this.red = red;
this.green = green;
this.blue = blue;
this.alpha = alpha;
}
public get red() : number {
return this.raw[0];
}
public get green() : number {
return this.raw[1];
}
public get blue() : number {
return this.raw[2];
}
public get alpha() : number {
return this.raw[3];
}
public set red(v : number) {
this.raw[0] = v;
}
public set green(v : number) {
this.raw[1] = v;
}
public set blue(v : number) {
this.raw[2] = v;
}
public set alpha(v : number) {
this.raw[3] = v;
}
toString(): string
{
return "BGRA(" + this.red + "," + this.green + "," + this.blue + "," + this.alpha + ")";
}
Equals(other: BGRAPixel): boolean
{
return other === this || (other.alpha == 0 && this.alpha == 0) ||
(other.red == this.red && other.green == this.green && other.green == this.green && other.blue == this.blue);
}
}
function BGRA(red: number, green: number, blue: number, alpha: number) {
return new BGRAPixel(red, green, blue, alpha == undefined ? 255 : alpha);
}
class Rectangle {
private raw: Int32Array;
constructor(left: number, top: number, right: number, bottom: number) {
this.raw = new Int32Array(4);
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
public get Left() : number {
return this.raw[0];
}
public get Top() : number {
return this.raw[1];
}
public get Right() : number {
return this.raw[2];
}
public get Bottom() : number {
return this.raw[3];
}
public set Left(v : number) {
this.raw[0] = Math.floor(v);
}
public set Top(v : number) {
this.raw[1] = Math.floor(v);
}
public set Right(v : number) {
this.raw[2] = Math.floor(v);
}
public set Bottom(v : number) {
this.raw[3] = Math.floor(v);
}
public get Empty(): boolean
{
return (this.Left == this.Right && this.Top == this.Bottom);
}
Contains(x: number, y: number): boolean
{
return x >= this.Left && y >= this.Top && x < this.Right && y < this.Bottom;
}
toString(): string
{
return "Rect(" + this.Left + "," + this.Top + "," + this.Right + "," + this.Bottom + ")";
}
Clone(): Rectangle
{
return new Rectangle(this.Left,this.Top,this.Right,this.Bottom);
}
Intersect(other: Rectangle): boolean
{
if (other.Left > this.Right ||
other.Right < this.Left ||
other.Top > this.Bottom ||
other.Bottom < this.Top)
{
this.Left = 0;
this.Right = 0;
this.Top = 0;
this.Bottom = 0;
return false;
}
else
{
if (other.Left > this.Left) this.Left = other.Left;
if (other.Top > this.Top) this.Top = other.Top;
if (other.Right < this.Right) this.Right = other.Right;
if (other.Bottom < this.Bottom) this.Bottom = other.Bottom;
return true;
}
}
Equals(other: Rectangle): boolean
{
return other === this || (other.Empty && this.Empty) ||
(other.Left == this.Left && other.Top == this.Top && other.Right == this.Right && other.Bottom == this.Bottom);
}
}
function Rect(left: number, top: number, right: number, bottom: number) {
return new Rectangle(left,top,right,bottom);
}
class BGRABitmap {
private raw: ImageData;
private rawModified: boolean;
private canvas: HTMLCanvasElement;
private canvasModified: boolean;
private clipRect: Rectangle;
private rowStride: number;
constructor(canvas: HTMLCanvasElement, width: number, height: number) {
this.raw = canvas.getContext('2d').createImageData(width == undefined ? canvas.width : width, height == undefined ? canvas.height : height);
this.rawModified = false;
this.canvas = null;
this.canvasModified = false;
this.clipRect = Rect(0,0,this.Width,this.Height);
this.rowStride = this.Width*4;
}
public get Width() : number {
return this.raw.width;
}
public get Height() : number {
return this.raw.height;
}
public get Data() : ImageData {
if (this.canvasModified&&(this.canvas!=null))
{
this.raw = this.canvas.getContext('2d').getImageData(0,0,this.Width,this.Height);
this.canvasModified = false;
}
return this.raw;
}
public get ClipRect(): Rectangle {
return this.clipRect;
}
public set ClipRect(v : Rectangle) {
let newClip = v.Clone();
newClip.Intersect(Rect(0,0,this.Width,this.Height));
if (!newClip.Equals(this.clipRect))
this.clipRect = newClip;
}
NoClip() {
this.ClipRect = Rect(0,0,this.Width,this.Height);
}
GetDataOffset(x,y: number): number
{
return Math.floor(y)*this.rowStride + Math.floor(x)*4;
}
public get CanvasElement(): HTMLCanvasElement {
if (this.canvas==null)
{
this.canvas = document.createElement("canvas");
this.canvas.width = this.Width;
this.canvas.height = this.Height;
}
if (this.rawModified)
{
let ctx = this.canvas.getContext('2d');
ctx.clearRect(0,0,this.Width,this.Height);
ctx.putImageData(this.raw,0,0);
this.rawModified = false;
}
return this.canvas;
}
public get Canvas2D(): CanvasRenderingContext2D {
this.canvasModified = true;
return this.CanvasElement.getContext('2d');
}
Fill(color: BGRAPixel) {
let data = this.Data;
for (var i = 0; i < data.data.length; i+= 4) {
data.data[i] = color.red;
data.data[i + 1] = color.green;
data.data[i + 2] = color.blue;
data.data[i + 3] = color.alpha;
}
this.InvalidateBitmap();
}
SetPixel(x: number, y:number, color: BGRAPixel)
{
if (!this.clipRect.Contains(x,y)) return;
let data = this.Data;
let offset = this.GetDataOffset(x,y);
data.data[offset] = color.red;
data.data[offset+1] = color.green;
data.data[offset+2] = color.blue;
data.data[offset+3] = color.alpha;
this.InvalidateBitmap();
}
SetHorizLine(x: number, y: number, x2: number, color: BGRAPixel)
{
if (y < this.clipRect.Top || y >= this.clipRect.Bottom+1) return;
if (x2<x)
{
let temp = x;
x = x2;
x2 = temp;
}
if (x >= this.clipRect.Right || x2 < this.clipRect.Left) return;
if (x < this.clipRect.Left) x = this.clipRect.Left;
if (x2 > this.clipRect.Right) x2 = this.clipRect.Right;
let data = this.Data;
let offset = this.GetDataOffset(x,y);
let red = color.red, green = color.green, blue = color.blue, alpha = color.alpha;
while (x<=x2)
{
data.data[offset] = red;
data.data[offset+1] = green;
data.data[offset+2] = blue;
data.data[offset+3] = alpha;
offset += 4;
x += 1;
}
}
SetVertLine(x: number, y: number, y2: number, color: BGRAPixel)
{
if (x < this.clipRect.Left || x >= this.clipRect.Right+1) return;
if (y2<y)
{
let temp = y;
y = y2;
y2 = temp;
}
if (y >= this.clipRect.Bottom || y2 < this.clipRect.Top) return;
if (y < this.clipRect.Top) y = this.clipRect.Top;
if (y2 > this.clipRect.Bottom) y2 = this.clipRect.Bottom;
let data = this.Data;
let offset = this.GetDataOffset(x,y);
let red = color.red, green = color.green, blue = color.blue, alpha = color.alpha;
while (y<=y2)
{
data.data[offset] = red;
data.data[offset+1] = green;
data.data[offset+2] = blue;
data.data[offset+3] = alpha;
offset += this.rowStride;
y += 1;
}
}
SetRectangle(x: number, y: number, x2: number, y2: number, color: BGRAPixel)
{
if (y == y2) { this.SetHorizLine(x,y,x2,color); return; }
if (x == x2) { this.SetVertLine(x,y,y2,color); return; }
if (y>y2)
{
let temp = y;
y = y2;
y2 = y;
}
this.SetHorizLine(x,y,x2,color);
this.SetHorizLine(x,y2,x2,color);
this.SetVertLine(x,y+1,y2-1,color);
this.SetVertLine(x2,y+1,y2-1,color);
}
InvalidateBitmap() {
this.rawModified = true;
}
Draw(canvas: HTMLCanvasElement, x: number, y: number) {
canvas.getContext('2d').putImageData(this.Data, x, y);
}
}