From d6503bb613c408e7892897fd231ea3b3888942ce Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 15:19:21 +0100 Subject: [PATCH 01/12] Add Basic Data Structures --- lib/Dither.dart | 102 ++++++++++++++++++++++++++++++++++++++++++------ web/main.dart | 1 - 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/lib/Dither.dart b/lib/Dither.dart index 6cc123f..7cc3f6c 100644 --- a/lib/Dither.dart +++ b/lib/Dither.dart @@ -1,18 +1,98 @@ import 'dart:html'; +import 'dart:typed_data'; -class Dither { - static ImageData editImage(ImageData image) { +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); -// 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; + 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 { + List> pixels; + + 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++; + } + } + + 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; + } + + Pixel getPixel(int x, int y) { + return pixels[y][x]; + } + + void setPixel(Pixel pixel, [int x, int y]) { + if (x != null && y != null && pixels[y] != null && pixels[y].length >= x) { + pixel.x = x; + pixel.y = y; + pixels[y][x] = pixel; + return; + } else if (pixels.length < pixel.y || pixels[y].length < pixel.x) { + return; + } else { + pixels[pixel.y][pixel.x] = pixel; + return; + } + } + + int getWidth() { + return pixels[0].length; + } + + int getHeight() { + return pixels.length; + } +} \ No newline at end of file diff --git a/web/main.dart b/web/main.dart index b9f9f47..7e712d9 100644 --- a/web/main.dart +++ b/web/main.dart @@ -20,7 +20,6 @@ void imgLoaded(Event e) { print("image loaded"); input.context2D.drawImage(inputImg, 0, 0); - output.context2D.putImageData( Dither.editImage( getImageData(input)), 0, 0); } ImageData getImageData(CanvasElement canvas) { From fb7776feac38b7e04640bac1113968e0de22d035 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 15:27:33 +0100 Subject: [PATCH 02/12] PixelArray from ImageData constructor --- lib/Dither.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Dither.dart b/lib/Dither.dart index 7cc3f6c..839fe79 100644 --- a/lib/Dither.dart +++ b/lib/Dither.dart @@ -49,6 +49,8 @@ class PixelArray { } } + PixelArray.fromImageData(ImageData imagedata, int imageWidth): this.fromByteArray(imagedata.data, imageWidth); + 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); From 881da038251566b2dbbedcc19c18e3dc6a2ce2ab Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 15:54:28 +0100 Subject: [PATCH 03/12] Add Helper Functions create PixelArray from another PixelArray create empty PixelArray convert PixelArray to ImageData --- lib/Dither.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/Dither.dart b/lib/Dither.dart index 839fe79..ce0032c 100644 --- a/lib/Dither.dart +++ b/lib/Dither.dart @@ -32,6 +32,10 @@ class Pixel { class PixelArray { List> pixels; + PixelArray(int width, int height) { + pixels = new List.filled(height, new List(width)); + } + PixelArray.fromByteArray(Uint8ClampedList array, int imageWidth) { pixels = new List(array.length~/imageWidth~/4); int y = 0; @@ -51,6 +55,10 @@ class PixelArray { PixelArray.fromImageData(ImageData imagedata, int imageWidth): this.fromByteArray(imagedata.data, imageWidth); + PixelArray.fromPixelArray(PixelArray pixelArray) { + pixels = new List>.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); @@ -72,6 +80,10 @@ class PixelArray { return result; } + ImageData toImageData() { + return new ImageData(this.toByteArray(), this.getWidth(), this.getHeight()); + } + Pixel getPixel(int x, int y) { return pixels[y][x]; } From 33e07962bd18ba31fcc0cc2ddc46f92ffe868e40 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 15:56:55 +0100 Subject: [PATCH 04/12] Add Greyscale Function --- lib/Dither.dart | 18 ++++++++++++++++++ web/main.dart | 4 ++++ web/styles.css | 4 ++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/Dither.dart b/lib/Dither.dart index ce0032c..940c068 100644 --- a/lib/Dither.dart +++ b/lib/Dither.dart @@ -1,6 +1,24 @@ 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; + } +} + class Pixel { int x; int y; diff --git a/web/main.dart b/web/main.dart index 7e712d9..ef830c7 100644 --- a/web/main.dart +++ b/web/main.dart @@ -20,6 +20,10 @@ void imgLoaded(Event e) { print("image loaded"); input.context2D.drawImage(inputImg, 0, 0); + PixelArray px = new PixelArray.fromImageData(getImageData(input), 712); + px = Pixed.greyscale(px); + + output.context2D.putImageData(px.toImageData(), 0, 0); } ImageData getImageData(CanvasElement canvas) { diff --git a/web/styles.css b/web/styles.css index 6a88d3b..b8742bc 100644 --- a/web/styles.css +++ b/web/styles.css @@ -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; } From 2f0de10848e8498a11a3abfc8a7728a12b12e8a4 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 16:44:56 +0100 Subject: [PATCH 05/12] Add PixelArray clone function --- lib/Dither.dart | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/Dither.dart b/lib/Dither.dart index 940c068..8579376 100644 --- a/lib/Dither.dart +++ b/lib/Dither.dart @@ -48,10 +48,17 @@ class Pixel { } class PixelArray { + //TODO Implement iterable List> pixels; PixelArray(int width, int height) { - pixels = new List.filled(height, new List(width)); + 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) { @@ -107,12 +114,12 @@ class PixelArray { } void setPixel(Pixel pixel, [int x, int y]) { - if (x != null && y != null && pixels[y] != null && pixels[y].length >= x) { + if (x != null && y != null && getHeight() < y && getWidth() < x) { pixel.x = x; pixel.y = y; pixels[y][x] = pixel; return; - } else if (pixels.length < pixel.y || pixels[y].length < pixel.x) { + } else if (getHeight() <= pixel.y || getWidth() <= pixel.x) { return; } else { pixels[pixel.y][pixel.x] = pixel; @@ -121,10 +128,23 @@ class PixelArray { } 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; + } } \ No newline at end of file From 4a341451ba9f1a9db517fa5a39b3285b09aa5034 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 16:45:43 +0100 Subject: [PATCH 06/12] Add quantize Function --- lib/Dither.dart | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/Dither.dart b/lib/Dither.dart index 8579376..7f1cdb9 100644 --- a/lib/Dither.dart +++ b/lib/Dither.dart @@ -14,7 +14,33 @@ class Pixed { 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; } } From 56994771f03b6003b023c6e35b391064cdb726bf Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 17:58:37 +0100 Subject: [PATCH 07/12] Fix PixelArray Creation Width --- lib/Dither.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Dither.dart b/lib/Dither.dart index 7f1cdb9..b51e2ea 100644 --- a/lib/Dither.dart +++ b/lib/Dither.dart @@ -81,7 +81,7 @@ class PixelArray { pixels = new List(height); for (int y = 0; y < height; y++) { pixels[y] = new List(width); - for (int x = 0; x < height; x++) { + for (int x = 0; x < width; x++) { pixels[y][x] = new Pixel(x, y, 0, 0, 0, 0); } } From 0cc15e9b4f67d0641ee9f29dfae36f9e97f4302a Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 18:00:13 +0100 Subject: [PATCH 08/12] Add per Pixel Quantize Methods --- lib/Dither.dart | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/Dither.dart b/lib/Dither.dart index b51e2ea..1201655 100644 --- a/lib/Dither.dart +++ b/lib/Dither.dart @@ -21,16 +21,23 @@ class Pixed { 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); + pxArr.setPixel( _quantizePixel(pxArr.getPixel(x, y)) ); } } return pxArr; } - static PixelArray calcQuantizeError(PixelArray original, PixelArray quantized) { + 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++) { @@ -43,6 +50,14 @@ class Pixed { } 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); + } } class Pixel { @@ -92,7 +107,6 @@ class PixelArray { 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; @@ -107,7 +121,7 @@ class PixelArray { PixelArray.fromImageData(ImageData imagedata, int imageWidth): this.fromByteArray(imagedata.data, imageWidth); PixelArray.fromPixelArray(PixelArray pixelArray) { - pixels = new List>.from(pixelArray.pixels); + pixels = pixelArray.clone().pixels; } Uint8ClampedList toByteArray() { From c5a68ea94b6e80a98b3fe3c1abe71e33cd7814ea Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 18:00:25 +0100 Subject: [PATCH 09/12] Add Dither Function --- lib/Dither.dart | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/Dither.dart b/lib/Dither.dart index 1201655..89aee36 100644 --- a/lib/Dither.dart +++ b/lib/Dither.dart @@ -58,6 +58,39 @@ class Pixed { 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 { From 763512a3b16afcc4ee0f332015a0a050637292cf Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 18:00:57 +0100 Subject: [PATCH 10/12] Add Dither Effect to Example Image --- web/index.html | 4 ++-- web/main.dart | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/web/index.html b/web/index.html index 1a09a60..7dde92e 100644 --- a/web/index.html +++ b/web/index.html @@ -14,8 +14,8 @@ - + - + diff --git a/web/main.dart b/web/main.dart index ef830c7..a4c027a 100644 --- a/web/main.dart +++ b/web/main.dart @@ -8,11 +8,15 @@ ImageElement inputImg; ImageElement outputImg; void main() { - inputImg = new ImageElement(src: 'kitten.jpg', width: 712, height: 470); + inputImg = new ImageElement(src: 'kitten.jpg'); input = document.querySelector('#input'); output = document.querySelector('#output'); + input.width = inputImg.width; + input.height = inputImg.height; + output.width = inputImg.width; + output.height = inputImg.height; inputImg.onLoad.listen(imgLoaded); } @@ -20,9 +24,9 @@ void imgLoaded(Event e) { print("image loaded"); input.context2D.drawImage(inputImg, 0, 0); - PixelArray px = new PixelArray.fromImageData(getImageData(input), 712); + PixelArray px = new PixelArray.fromImageData(getImageData(input), input.width); px = Pixed.greyscale(px); - + px = Pixed.dither(px); output.context2D.putImageData(px.toImageData(), 0, 0); } From 494e335890b73696a3a3cffb755f026c78f73c12 Mon Sep 17 00:00:00 2001 From: Marty Date: Wed, 14 Feb 2018 17:19:47 +0000 Subject: [PATCH 11/12] Add new file --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a3188b1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Marty Oehme + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From dad9d12e2ece598524be7cd4178ad34e2974654f Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 14 Feb 2018 18:31:00 +0100 Subject: [PATCH 12/12] Remove Simple Copyright --- LICENSE.md | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 86a595d..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,2 +0,0 @@ - -Copyright (C) 2018