Skip to content

Commit 481ef3e

Browse files
committed
cleanup things, test reencoding with image2sixel in demo
1 parent 3144741 commit 481ef3e

File tree

8 files changed

+62
-30
lines changed

8 files changed

+62
-30
lines changed

.npmignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
testfiles
22
dist
33
*.png
4+
*.jpg
45
node_example*
56
index.html
67
sixel_textcursor.sh

img2sixel.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,18 @@ async function processImage(filename, palLimit) {
3535
console.log(`${filename}:`);
3636
console.log(image2sixel(data, img.width, img.height, palLimit, BACKGROUND_SELECT));
3737

38-
//// alternatively use custom quantizer library
39-
//const RgbQuant = require('rgbquant');
40-
//const q = new RgbQuant({colors: palLimit, dithKern: 'FloydSteinberg', dithSerp: true});
41-
//q.sample(canvas);
42-
//const palette = q.palette(true);
43-
//const quantizedData = q.reduce(canvas);
44-
//console.log(`${filename}:`);
45-
//console.log([
46-
// introducer(BACKGROUND_SELECT),
47-
// sixelEncode(quantizedData, img.width, img.height, palette),
48-
// FINALIZER
49-
//].join(''));
38+
// alternatively use custom quantizer library
39+
// const RgbQuant = require('rgbquant');
40+
// const q = new RgbQuant({colors: palLimit, dithKern: 'FloydSteinberg', dithSerp: true});
41+
// q.sample(canvas);
42+
// const palette = q.palette(true);
43+
// const quantizedData = q.reduce(canvas);
44+
// console.log(`${filename}:`);
45+
// console.log([
46+
// introducer(BACKGROUND_SELECT),
47+
// sixelEncode(quantizedData, img.width, img.height, palette),
48+
// FINALIZER
49+
// ].join(''));
5050
}
5151

5252
async function main() {

index.html

+15
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
<span id="stats"></span>
5353
<br><br>
5454
<canvas id="output" style="border: 1px solid black"></canvas>
55+
<br><br>
56+
Re-encoded with img2sixel:<br>
57+
<canvas id="output2" style="border: 1px solid black"></canvas>
5558
<script src="/dist/bundle.js"></script>
5659
<script id="sampsa" type="application/json"></script>
5760
<script>
@@ -85,6 +88,18 @@
8588
// fill color
8689
sixel.toRGBA8888(...hexColorToRGB(document.getElementById('fillColor').value)));
8790
ctx.putImageData(target, 0, 0);
91+
92+
// test encoding by re-encoding the output above
93+
const reEncoded = sixel.image2sixel(target.data, img.width, img.height);
94+
const six2 = new sixel.SixelDecoder();
95+
six2.decodeString(reEncoded.slice(7, -2)); // strip off enclosing escape sequence
96+
const canvas2 = document.getElementById('output2');
97+
canvas2.width = six2.width;
98+
canvas2.height = six2.height;
99+
const ctx2 = canvas2.getContext('2d');
100+
const target2 = ctx.getImageData(0, 0, six2.width, six2.height);
101+
six2.toPixelData(target2.data, six2.width, six2.height);
102+
ctx2.putImageData(target2, 0, 0);
88103
}
89104

90105
function hexColorToRGB(color) {

src/Quantizer.ts

+18-14
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55
* Parts taken from UPNG:
66
* MIT License, Copyright (c) 2017 Photopea
77
*/
8-
import { red, green, blue, fromRGBA8888 } from "./Colors";
9-
import { IQuantResult, RGBColor, RGBA8888 } from "./Types";
10-
const UPNGQuantize = require('../upng').quantize;
8+
import { red, green, blue, fromRGBA8888 } from './Colors';
9+
import { IQuantResult, RGBColor, RGBA8888 } from './Types';
10+
import { quantize as UPNGQuantize } from './upng';
1111

1212

1313
function clamp8Bit(value: number): number {
1414
return value >= 255 ? 255 : value < 0 ? 0 : value;
1515
}
1616

1717

18-
function applyError(value: number, r:number, g:number, b:number): number {
18+
function applyError(value: number, r: number, g: number, b: number): number {
1919
return ((0xFF00 | clamp8Bit(blue(value) + b))
20-
<< 8 | clamp8Bit(green(value) + g))
21-
<< 8 | clamp8Bit(red(value) + r);
20+
<< 8 | clamp8Bit(green(value) + g))
21+
<< 8 | clamp8Bit(red(value) + r);
2222
}
2323

2424

@@ -49,7 +49,7 @@ export function reduce(
4949

5050
const indices = new Uint16Array(data32.length);
5151
const len = data32.length;
52-
for(let i = 0; i < len; ++i) {
52+
for (let i = 0; i < len; ++i) {
5353
const v = data32[i];
5454
const r = red(v);
5555
const g = green(v);
@@ -65,9 +65,9 @@ export function reduce(
6565
let eg = (g - green(vp)) >> 2;
6666
let eb = (b - blue(vp)) >> 2;
6767

68-
//FIXME: respect idx overflow / left and right border
69-
data32[i + 1] = applyError(data32[i + 1], er, eg, eb)
70-
data32[i + width] = applyError(data32[i + width], er, eg, eb)
68+
// FIXME: respect idx overflow / left and right border
69+
data32[i + 1] = applyError(data32[i + 1], er, eg, eb);
70+
data32[i + width] = applyError(data32[i + width], er, eg, eb);
7171

7272
er >>= 1;
7373
eg >>= 1;
@@ -83,8 +83,9 @@ export function reduce(
8383
* Class to do nearest palette color matching with 16x16x16 boxes.
8484
*/
8585
class ColorMatcher {
86-
private _boxes: {[key: number]: number[]} = {};
87-
private _boxes2: {[key: number]: number[]} = {};
86+
private _boxes: { [key: number]: number[] } = {};
87+
private _boxes2: { [key: number]: number[] } = {};
88+
8889
constructor(public palette: RGBColor[], radius: number = 14, radius2: number = 42) {
8990
// limit: search sphere to add palette points from
9091
// limit2: outer search sphere for uncertain area
@@ -96,10 +97,11 @@ class ColorMatcher {
9697
const x = i >> 8;
9798
const y = i >> 4 & 15;
9899
const z = i & 15;
99-
this._nearestPoints(i, (x<<4) + 8, (y<<4) + 8, (z<<4) + 8, limit, limit2);
100+
this._nearestPoints(i, (x << 4) + 8, (y << 4) + 8, (z << 4) + 8, limit, limit2);
100101
}
101102
}
102-
private _nearestPoints(box: number, r: number, g: number, b: number, limit: number, limit2: number): void {
103+
104+
private _nearestPoints(box: number, r: number, g: number, b: number, limit: number, limit2: number): void {
103105
let min = Number.MAX_SAFE_INTEGER;
104106
let idx = -1;
105107
const pointIndices: number[] = [];
@@ -123,12 +125,14 @@ class ColorMatcher {
123125
this._boxes[box] = pointIndices;
124126
this._boxes2[box] = pointIndices2;
125127
}
128+
126129
private _distance(r1: number, g1: number, b1: number, r2: number, g2: number, b2: number): number {
127130
const dr = r1 - r2;
128131
const dg = g1 - g2;
129132
const db = b1 - b2;
130133
return dr * dr + dg * dg + db * db;
131134
}
135+
132136
public nearest(color: RGBA8888): number {
133137
const r = red(color);
134138
const g = green(color);

src/SixelEncoder.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -401,20 +401,20 @@ export function sixelEncodeIndexed(
401401
* @param data pixel data
402402
* @param width width of the image
403403
* @param height height of the image
404-
* @param max_colors max colors of the created palette
404+
* @param maxColors max colors of the created palette
405405
* @param fast whether to use fast color approximation
406406
* @param backgroundSelect background select behavior for transparent pixels
407407
*/
408408
export function image2sixel(
409409
data: Uint8Array | Uint8ClampedArray,
410410
width: number,
411411
height: number,
412-
max_colors: number = 256,
412+
maxColors: number = 256,
413413
backgroundSelect: 0 | 1 | 2 = 0): string
414414
{
415415
// FIXME: sixelEncodeIndexed does not yet handle transparent pixels
416416
// FIXME: dithering in reduce does not yet respect image width/height
417-
const { indices, palette } = reduce(data, width, max_colors);
417+
const { indices, palette } = reduce(data, width, maxColors);
418418
const sixelData = sixelEncodeIndexed(indices, width, height, palette);
419419
return [introducer(backgroundSelect), sixelData, FINALIZER].join('');
420420
}

src/Types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@ export interface IQuantResult {
3232
/** image data as palette indices (max. 2^16 colors supported) */
3333
indices: Uint16Array;
3434
/** array with quantized colors */
35-
palette: number[]
35+
palette: number[];
3636
}

src/upng.d.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare module 'upng' {
2+
// export function quantize(data: Uint8Array | Uint8ClampedArray, colors: number): any;
3+
interface IQuantizer {
4+
getKDtree(data: Uint8Array | Uint8ClampedArray, colors: number): any;
5+
}
6+
const quantize: IQuantizer;
7+
}

src/upng.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
interface IQuantizer {
2+
getKDtree(data: Uint8Array | Uint8ClampedArray, colors: number): any;
3+
}
4+
5+
export const quantize: IQuantizer = require('../upng').quantize;

0 commit comments

Comments
 (0)