Merge branch 'pixel-data-structures'
This commit is contained in:
commit
eb12e43d71
5 changed files with 189 additions and 36 deletions
176
lib/Dither.dart
Normal file
176
lib/Dither.dart
Normal file
|
@ -0,0 +1,176 @@
|
|||
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++) {
|
||||
var px = pxArr.getPixel(x, y);
|
||||
px.r = (steps * px.r / 255).round() * (255 ~/ steps);
|
||||
px.g = (steps * px.g / 255).round() * (255 ~/ steps);
|
||||
px.b = (steps * px.b / 255).round() * (255 ~/ steps);
|
||||
}
|
||||
}
|
||||
return pxArr;
|
||||
}
|
||||
|
||||
static PixelArray calcQuantizeError(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;
|
||||
}
|
||||
}
|
||||
|
||||
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<List<Pixel>> 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 < height; 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);
|
||||
print(pixels[0].length.toString() + ","+ pixels.length.toString());
|
||||
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 = new List<List<Pixel>>.from(pixelArray.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;
|
||||
}
|
||||
}
|
3
lib/dither.dart
Normal file
3
lib/dither.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
library dither;
|
||||
|
||||
export 'Dither.dart';
|
|
@ -8,7 +8,6 @@ environment:
|
|||
sdk: '>=1.20.1 <2.0.0'
|
||||
|
||||
dependencies:
|
||||
image: "^1.1.29"
|
||||
# path: ^1.4.1
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
@ -1,56 +1,31 @@
|
|||
import 'dart:html';
|
||||
import 'package:image/image.dart';
|
||||
import 'package:dart_floyd_steinberg_dithering/Dither.dart';
|
||||
|
||||
CanvasElement input;
|
||||
CanvasElement output;
|
||||
|
||||
ImageElement inputImg;
|
||||
ImageElement outputImg;
|
||||
|
||||
void main() {
|
||||
inputImg = new ImageElement(src: 'kitten.jpg', width: 712, height: 470);
|
||||
|
||||
input = document.querySelector('#input');
|
||||
print(input);
|
||||
inputImg.onLoad.listen(imgLoaded);
|
||||
|
||||
output = document.querySelector('#output');
|
||||
|
||||
output.context2D.fillRect(0, 0, output.width, output.height);
|
||||
inputImg.onLoad.listen(imgLoaded);
|
||||
}
|
||||
|
||||
void imgLoaded(Event e) {
|
||||
print("image loaded");
|
||||
input.context2D.drawImage(inputImg, 0, 0);
|
||||
|
||||
Image outputImg = editImage( getImageFromCanvas(input) );
|
||||
PixelArray px = new PixelArray.fromImageData(getImageData(input), 712);
|
||||
px = Pixed.greyscale(px);
|
||||
|
||||
drawImageToCanvas(output, outputImg ) ;
|
||||
output.context2D.putImageData(px.toImageData(), 0, 0);
|
||||
}
|
||||
|
||||
Image getImageFromCanvas(CanvasElement input) {
|
||||
ImageData data = input.context2D.getImageData(0, 0, input.width, input.height);
|
||||
return new Image.fromBytes(input.width, input.height, data.data);
|
||||
}
|
||||
|
||||
void drawImageToCanvas(CanvasElement canvas, Image image) {
|
||||
var imageData = canvas.context2D.createImageData(image.width, image.height);
|
||||
imageData.data.setRange(0, imageData.data.length, image.getBytes());
|
||||
|
||||
canvas.context2D.putImageData(imageData, 0, 0);
|
||||
}
|
||||
|
||||
Image editImage(Image image) {
|
||||
|
||||
|
||||
image = contrast( image, 200);
|
||||
|
||||
for (var y = 0; y < image.height; y++) {
|
||||
for (var x = 0; x < image.width; x++) {
|
||||
// print(image.getPixel(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return image;
|
||||
ImageData getImageData(CanvasElement canvas) {
|
||||
return canvas.context2D.getImageData(0, 0, canvas.width, canvas.height);
|
||||
}
|
|
@ -6,11 +6,11 @@ html, body {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
background: gainsboro;
|
||||
background: black;
|
||||
}
|
||||
|
||||
canvas {
|
||||
padding: 00px;
|
||||
padding: 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue