import 'dart:html'; import 'dart:typed_data'; class Pixed { static PixelArray greyscale(PixelArray image) { PixelArray pxArr = new PixelArray.fromPixelArray(image); for (var y = 0; y < pxArr.getHeight(); y++) { for (var x = 0; x < pxArr.getWidth(); x++) { var px = pxArr.getPixel(x, y); int average = ((px.r+px.g+px.b) / 3).round(); px.r = average; px.g = average; px.b = average; } } return pxArr; } static PixelArray quantize(PixelArray image, [int steps=1]) { PixelArray pxArr = new PixelArray.fromPixelArray(image); for (var y = 0; y < pxArr.getHeight(); y++) { for (var x = 0; x < pxArr.getWidth(); x++) { pxArr.setPixel( _quantizePixel(pxArr.getPixel(x, y)) ); } } return pxArr; } static Pixel _quantizePixel(Pixel pixel, [int steps=1]) { return new Pixel( pixel.x, pixel.y, (steps * pixel.r / 255).round() * (255 ~/ steps), (steps * pixel.g / 255).round() * (255 ~/ steps), (steps * pixel.b / 255).round() * (255 ~/ steps), pixel.a); } static PixelArray quantizeError(PixelArray original, PixelArray quantized) { PixelArray pxArr = new PixelArray.fromPixelArray(original); for (var y = 0; y < pxArr.getHeight(); y++) { for (var x = 0; x < pxArr.getWidth(); x++) { Pixel px = pxArr.getPixel(x, y); Pixel quant = quantized.getPixel(x, y); px.r = px.r - quant.r; px.g = px.g - quant.g; px.b = px.b - quant.b; } } return pxArr; } static Pixel _quantizeErrorPixel(Pixel original, Pixel quantized) { return new Pixel(original.x, original.y, original.r - quantized.r, original.g - quantized.g, original.b - quantized.b, original.a); } static PixelArray dither(PixelArray image) { PixelArray pxArr = new PixelArray.fromPixelArray(image); for (var y = 0; y < pxArr.getHeight()-1; y++) { for (var x = 1; x < pxArr.getWidth()-1; x++) { Pixel origPx = pxArr.getPixel(x, y); Pixel quantPx = _quantizePixel(origPx); Pixel errPx = _quantizeErrorPixel(origPx, quantPx); pxArr.setPixel(quantPx); Pixel px = pxArr.getPixel(x+1, y); px.r = (px.r + errPx.r * (7/16)).toInt(); px.g = (px.g + errPx.g * (7/16)).toInt(); px.b = (px.b + errPx.b * (7/16)).toInt(); px = pxArr.getPixel(x-1, y+1); px.r = (px.r + errPx.r * (3/16)).toInt(); px.g = (px.g + errPx.g * (3/16)).toInt(); px.b = (px.b + errPx.b * (3/16)).toInt(); px = pxArr.getPixel(x, y+1); px.r = (px.r + errPx.r * (5/16)).toInt(); px.g = (px.g + errPx.g * (5/16)).toInt(); px.b = (px.b + errPx.b * (5/16)).toInt(); px = pxArr.getPixel(x+1, y+1); px.r = (px.r + errPx.r * (1/16)).toInt(); px.g = (px.g + errPx.g * (1/16)).toInt(); px.b = (px.b + errPx.b * (1/16)).toInt(); } } return pxArr; } } class Pixel { int x; int y; int r; int g; int b; int a; Pixel(this.x, this.y, this.r, this.g, this.b, this.a); int getBytePos(int imageWidth) { return (x+y*imageWidth)*4; } void setRGB(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } @override String toString() { return 'Pixel at $x, $y: {r: $r, g: $g, b: $b, a: $a}'; } } class PixelArray { //TODO Implement iterable List> pixels; PixelArray(int width, int height) { pixels = new List(height); for (int y = 0; y < height; y++) { pixels[y] = new List(width); for (int x = 0; x < width; x++) { pixels[y][x] = new Pixel(x, y, 0, 0, 0, 0); } } } PixelArray.fromByteArray(Uint8ClampedList array, int imageWidth) { pixels = new List(array.length~/imageWidth~/4); int y = 0; int x = 0; pixels[y] = new List(imageWidth); for (var pos = 0; pos < array.length; pos = pos + 4) { if (x >= imageWidth) { x = 0; y++; pixels[y] = new List(imageWidth); } pixels[y][x] = new Pixel(x, y, array[pos], array[pos+1], array[pos+2], array[pos+3]); x++; } } PixelArray.fromImageData(ImageData imagedata, int imageWidth): this.fromByteArray(imagedata.data, imageWidth); PixelArray.fromPixelArray(PixelArray pixelArray) { pixels = pixelArray.clone().pixels; } Uint8ClampedList toByteArray() { //TODO: look for longest array, or each line separately. Only gets width from line 1 currently. Uint8ClampedList result = new Uint8ClampedList(getHeight()*getWidth()*4); int x = 0; int y = 0; for (var i = 0; i < result.length; i = i + 4) { if (x >= getWidth()) { y++; x = 0; } result[i] = pixels[y][x].r; result[i+1] = pixels[y][x].g; result[i+2] = pixels[y][x].b; result[i+3] = pixels[y][x].a; x++; } return result; } ImageData toImageData() { return new ImageData(this.toByteArray(), this.getWidth(), this.getHeight()); } Pixel getPixel(int x, int y) { return pixels[y][x]; } void setPixel(Pixel pixel, [int x, int y]) { if (x != null && y != null && getHeight() < y && getWidth() < x) { pixel.x = x; pixel.y = y; pixels[y][x] = pixel; return; } else if (getHeight() <= pixel.y || getWidth() <= pixel.x) { return; } else { pixels[pixel.y][pixel.x] = pixel; return; } } int getWidth() { //TODO find longest/shortest width not at 0 (support for non square img) return pixels[0].length; } int getHeight() { return pixels.length; } PixelArray clone() { PixelArray pxArr = new PixelArray(getWidth(), getHeight()); for (var y = 0; y < getHeight(); y++) { for (var x = 0; x < getWidth(); x++) { Pixel px = getPixel(x, y); Pixel newPix = new Pixel(x, y, px.r, px.g, px.b, px.a); pxArr.setPixel( newPix, x, y ); } } return pxArr; } }