mirror of
https://gitlab.com/AutumnMeowMeow/jexer
synced 2024-09-19 03:40:20 -06:00
#99 - show PCA screenshot and chafa reference on README
This commit is contained in:
parent
63055fa744
commit
b90084576b
3 changed files with 59 additions and 37 deletions
|
@ -28,9 +28,11 @@ stock xterm, with a custom mouse icon, and SGR-Pixel mode active:
|
|||
![Xterm SGR-Pixel Mouse](/screenshots/xterm_pixel_mouse.gif?raw=true "Xterm SGR-Pixel Mouse")
|
||||
|
||||
A new sixel encoder is in the works for Xterm, which should look a lot
|
||||
better:
|
||||
better. This encoder is inspired in part by
|
||||
[chafa's](https://hpjansson.org/chafa/) high-performance principal
|
||||
component analysis based sixel encoder.
|
||||
|
||||
![Xterm Snake Image](/screenshots/snake_xterm_hq.png?raw=true "Xterm Snake Image - HQ Encoder")
|
||||
![PCA color matching with 128-color palette and translucent windows](/screenshots/pca_match.png?raw=true "PCA color matching with 128-color palette and translucent windows")
|
||||
|
||||
Jexer can be run inside its own terminal window, with support for all
|
||||
of its features including images and mouse, and more terminals:
|
||||
|
|
BIN
screenshots/pca_match.png
Normal file
BIN
screenshots/pca_match.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 289 KiB |
|
@ -25,6 +25,12 @@
|
|||
*
|
||||
* @author Autumn Lamonte ⚧ Trans Liberation Now
|
||||
* @version 1
|
||||
*
|
||||
* Portions of this encoder were inspired / influenced by Hans Petter
|
||||
* Jansson's chafa project: https://hpjansson.org/chafa/ . Please refer to
|
||||
* chafa's high-performance sixel encoder for far more advanced
|
||||
* implementations of principal component analysis color mapping, and sixel
|
||||
* row encoding.
|
||||
*/
|
||||
package jexer.backend;
|
||||
|
||||
|
@ -36,6 +42,7 @@ import java.awt.image.IndexColorModel;
|
|||
import java.awt.image.Raster;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
@ -48,6 +55,14 @@ import jexer.bits.MathUtils;
|
|||
* HQSixelEncoder turns a BufferedImage into String of sixel image data,
|
||||
* using several strategies to produce a reasonably high quality image within
|
||||
* sixel's ~19.97 bit (101^3) color depth.
|
||||
*
|
||||
* <p>
|
||||
* Portions of this encoder were inspired / influenced by Hans Petter
|
||||
* Jansson's chafa project: https://hpjansson.org/chafa/ . Please refer to
|
||||
* chafa's high-performance sixel encoder for far more advanced
|
||||
* implementations of principal component analysis color mapping, and sixel
|
||||
* row encoding.
|
||||
* </p>
|
||||
*/
|
||||
public class HQSixelEncoder implements SixelEncoder {
|
||||
|
||||
|
@ -485,6 +500,25 @@ public class HQSixelEncoder implements SixelEncoder {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata regarding one sixel row.
|
||||
*/
|
||||
private class SixelRow {
|
||||
|
||||
/**
|
||||
* A set of colors that are present in this row.
|
||||
*/
|
||||
private BitSet colors;
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*/
|
||||
public SixelRow() {
|
||||
colors = new BitSet(sixelColors.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of colors in this palette.
|
||||
*/
|
||||
|
@ -581,6 +615,11 @@ public class HQSixelEncoder implements SixelEncoder {
|
|||
*/
|
||||
private ArrayList<Bucket> buckets;
|
||||
|
||||
/**
|
||||
* The sixel rows of this image.
|
||||
*/
|
||||
private SixelRow [] sixelRows;
|
||||
|
||||
/**
|
||||
* If true, quantization is done.
|
||||
*/
|
||||
|
@ -620,6 +659,10 @@ public class HQSixelEncoder implements SixelEncoder {
|
|||
numColors = Math.min(paletteSize, FAST_AND_DIRTY);
|
||||
}
|
||||
sixelColors = new ArrayList<Integer>(numColors);
|
||||
sixelRows = new SixelRow[(image.getHeight() / 6) + 1];
|
||||
for (int i = 0; i < sixelRows.length; i++) {
|
||||
sixelRows[i] = new SixelRow();
|
||||
}
|
||||
|
||||
if (image.getTransparency() == Transparency.TRANSLUCENT) {
|
||||
// PNG like images where transparency is carried in alpha.
|
||||
|
@ -1226,7 +1269,8 @@ public class HQSixelEncoder implements SixelEncoder {
|
|||
* . The palette colors have been sorted by their principal
|
||||
* component (see principal component analysis), such that a binary
|
||||
* search can quickly find the region where the closest matching
|
||||
* color resides.
|
||||
* color resides. One can then search forward and backward to find
|
||||
* all nearby colors.
|
||||
*
|
||||
* @param red the red component, from 0-100
|
||||
* @param green the green component, from 0-100
|
||||
|
@ -1457,7 +1501,9 @@ public class HQSixelEncoder implements SixelEncoder {
|
|||
|
||||
int height = sixelImageHeight;
|
||||
int width = sixelImageWidth;
|
||||
SixelRow sixelRow;
|
||||
for (int imageY = 0; imageY < height; imageY++) {
|
||||
sixelRow = sixelRows[imageY / 6];
|
||||
for (int imageX = 0; imageX < width; imageX++) {
|
||||
int oldPixel = rgbArray[imageX + (width * imageY)];
|
||||
if ((oldPixel & 0xFF000000) != 0xFF000000) {
|
||||
|
@ -1478,6 +1524,7 @@ public class HQSixelEncoder implements SixelEncoder {
|
|||
assert (colorIdx < sixelColors.size());
|
||||
int newPixel = sixelColors.get(colorIdx);
|
||||
rgbArray[imageX + (width * imageY)] = colorIdx;
|
||||
sixelRow.colors.set(colorIdx);
|
||||
|
||||
if (quantizationType == 0) {
|
||||
// For direct map, every possible color is already in
|
||||
|
@ -1763,41 +1810,11 @@ public class HQSixelEncoder implements SixelEncoder {
|
|||
// Render the entire row of cells.
|
||||
int width = bitmap.getWidth();
|
||||
|
||||
// TODO: sixels[][] shouldn't be necessary, and both of these loops
|
||||
// should be able to be combined.
|
||||
int [][] sixels = new int[width][6];
|
||||
for (int currentRow = 0; currentRow < fullHeight; currentRow += 6) {
|
||||
Palette.SixelRow sixelRow = palette.sixelRows[currentRow / 6];
|
||||
|
||||
// See which colors are actually used in this band of sixels.
|
||||
boolean [] usedColors = new boolean[palette.sixelColors.size()];
|
||||
for (int imageX = 0; imageX < width; imageX++) {
|
||||
for (int imageY = 0;
|
||||
(imageY < 6) && (imageY + currentRow < fullHeight);
|
||||
imageY++) {
|
||||
|
||||
int colorIdx = rgbArray[imageX +
|
||||
(width * (imageY + currentRow))];
|
||||
|
||||
if (allowTransparent && (colorIdx == -1)) {
|
||||
sixels[imageX][imageY] = colorIdx;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
if (!palette.noDither) {
|
||||
assert (colorIdx >= 0);
|
||||
assert (colorIdx < palette.sixelColors.size());
|
||||
}
|
||||
if (!allowTransparent) {
|
||||
assert (colorIdx != -1);
|
||||
}
|
||||
*/
|
||||
sixels[imageX][imageY] = colorIdx;
|
||||
usedColors[colorIdx] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < usedColors.length; i++) {
|
||||
if (!usedColors[i]) {
|
||||
for (int i = 0; i < paletteSize; i++) {
|
||||
if (!sixelRow.colors.get(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1811,12 +1828,15 @@ public class HQSixelEncoder implements SixelEncoder {
|
|||
for (int imageX = 0; imageX < width; imageX++) {
|
||||
|
||||
// Add up all the pixels that match this color.
|
||||
|
||||
// TODO: Make this scan horizontally first to build the
|
||||
// row, so as to reduce all the random access.
|
||||
int data = 0;
|
||||
for (int j = 0;
|
||||
(j < 6) && (currentRow + j < fullHeight);
|
||||
j++) {
|
||||
|
||||
if (sixels[imageX][j] == i) {
|
||||
if (rgbArray[imageX + (width * (currentRow + j))] == i) {
|
||||
switch (j) {
|
||||
case 0:
|
||||
data += 1;
|
||||
|
|
Loading…
Reference in a new issue