#102 Pass indexed from TImage to HQSixelEncoder

This commit is contained in:
Autumn Lamonte 2022-01-31 14:29:54 -06:00
parent 9e824d90b7
commit a427368227
5 changed files with 110 additions and 33 deletions

View file

@ -99,6 +99,11 @@ public class TImage extends TWidget implements EditMenuUser {
*/
private BufferedImage image;
/**
* If false, the image is fully opaque.
*/
private boolean maybeTransparent = true;
/**
* The original image from construction time.
*/
@ -463,6 +468,7 @@ public class TImage extends TWidget implements EditMenuUser {
* @param always if true, always resize the cells
*/
private void sizeToImage(final boolean always) {
scaleBackColor = getApplication().getBackend().attrToBackgroundColor(getWindow().getBackground());
int textWidth = getScreen().getTextWidth();
@ -513,20 +519,22 @@ public class TImage extends TWidget implements EditMenuUser {
cell.setTo(getWindow().getBackground());
// Render over a full-cell-size image.
BufferedImage newImage = new BufferedImage(textWidth,
textHeight, BufferedImage.TYPE_INT_ARGB);
BufferedImage newImage = ImageUtils.createImage(image,
textWidth, textHeight);
Graphics gr = newImage.getGraphics();
BufferedImage subImage = image.getSubimage(x * textWidth,
y * textHeight, width, height);
gr.drawImage(subImage, 0, 0, null, null);
gr.dispose();
if (!ImageUtils.isFullyTransparent(newImage)) {
cell.setImage(newImage);
cell.setImage(newImage);
if (!maybeTransparent) {
cell.setOpaqueImage();
} else if (!ImageUtils.isFullyTransparent(newImage)) {
cell.flattenImage(false, getApplication().getBackend());
imageId++;
cell.setImageId(imageId & 0x7FFFFFFF);
}
imageId++;
cell.setImageId(imageId & 0x7FFFFFFF);
cells[x][y] = cell;
}
}
@ -649,7 +657,20 @@ public class TImage extends TWidget implements EditMenuUser {
* @param image the new image
*/
public void setImage(final BufferedImage image) {
setImage(image, true);
}
/**
* Set the raw image, and reprocess to make the visible image.
*
* @param image the new image
* @param maybeTransparent if false, the image is fully opaque
*/
public void setImage(final BufferedImage image,
final boolean maybeTransparent) {
this.originalImage = image;
this.maybeTransparent = maybeTransparent;
this.image = null;
sizeToImage(true);
if (animation != null) {
@ -807,14 +828,13 @@ public class TImage extends TWidget implements EditMenuUser {
case NONE:
destWidth = (int) (image.getWidth() * factor);
destHeight = (int) (image.getHeight() * factor);
newImage = new BufferedImage(Math.max(1, destWidth),
Math.max(1, destHeight), BufferedImage.TYPE_INT_ARGB);
newImage = ImageUtils.createImage(image,
Math.max(1, destWidth), Math.max(1, destHeight));
break;
case STRETCH:
destWidth = Math.max(1, width) * textWidth;
destHeight = Math.max(1, height) * textHeight;
newImage = new BufferedImage(destWidth, destHeight,
BufferedImage.TYPE_INT_ARGB);
newImage = ImageUtils.createImage(image, destWidth, destHeight);
break;
case SCALE:
double a = (double) image.getWidth() / image.getHeight();
@ -851,8 +871,9 @@ public class TImage extends TWidget implements EditMenuUser {
"x" + destHeight + ", X offset " + x);
*/
}
newImage = new BufferedImage(Math.max(1, width) * textWidth,
Math.max(1, height) * textHeight, BufferedImage.TYPE_INT_ARGB);
newImage = ImageUtils.createImage(image,
Math.max(1, width) * textWidth,
Math.max(1, height) * textHeight);
break;
}
@ -896,8 +917,8 @@ public class TImage extends TWidget implements EditMenuUser {
if (clockwise % 4 == 1) {
// 90 degrees clockwise
newImage = new BufferedImage(image.getHeight(), image.getWidth(),
BufferedImage.TYPE_INT_ARGB);
newImage = ImageUtils.createImage(image,
image.getHeight(), image.getWidth());
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
newImage.setRGB(y, x,
@ -906,8 +927,8 @@ public class TImage extends TWidget implements EditMenuUser {
}
} else if (clockwise % 4 == 2) {
// 180 degrees clockwise
newImage = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
newImage = ImageUtils.createImage(image,
image.getWidth(), image.getHeight());
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
newImage.setRGB(x, y,
@ -917,8 +938,8 @@ public class TImage extends TWidget implements EditMenuUser {
}
} else if (clockwise % 4 == 3) {
// 270 degrees clockwise
newImage = new BufferedImage(image.getHeight(), image.getWidth(),
BufferedImage.TYPE_INT_ARGB);
newImage = ImageUtils.createImage(image,
image.getHeight(), image.getWidth());
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
newImage.setRGB(y, x,

View file

@ -59,6 +59,7 @@ import javax.imageio.ImageIO;
import jexer.bits.Cell;
import jexer.bits.CellAttributes;
import jexer.bits.Color;
import jexer.bits.ImageUtils;
import jexer.bits.StringUtils;
import jexer.event.TCommandEvent;
import jexer.event.TInputEvent;
@ -3629,8 +3630,8 @@ public class ECMA48Terminal extends LogicalScreen
// The final image will be 1000 x 1000 or less.
BufferedImage cellsImage = cellsToImage(cells);
BufferedImage fullImage = new BufferedImage(maxPixelX,
maxPixelY, BufferedImage.TYPE_INT_ARGB);
BufferedImage fullImage = ImageUtils.createImage(cellsImage,
maxPixelX, maxPixelY);
Graphics gr = fullImage.getGraphics();
gr.drawImage(cellsImage, pixelX, pixelY, null);
gr.dispose();
@ -3673,8 +3674,8 @@ public class ECMA48Terminal extends LogicalScreen
totalWidth += cells.get(i).getImage().getWidth();
}
BufferedImage image = new BufferedImage(fullWidth,
fullHeight, BufferedImage.TYPE_INT_ARGB);
BufferedImage image = ImageUtils.createImage(cells.get(0).getImage(),
fullWidth, fullHeight);
int [] rgbArray;
for (int i = 0; i < cells.size() - 1; i++) {
@ -3780,8 +3781,8 @@ public class ECMA48Terminal extends LogicalScreen
) {
// Rescale the image to fit the text cells it is going into.
BufferedImage newImage;
newImage = new BufferedImage(cells.size() * getTextWidth(),
getTextHeight(), BufferedImage.TYPE_INT_ARGB);
newImage = ImageUtils.createImage(image,
cells.size() * getTextWidth(), getTextHeight());
Graphics gr = newImage.getGraphics();
if (gr instanceof Graphics2D) {

View file

@ -1702,17 +1702,23 @@ public class HQSixelEncoder implements SixelEncoder {
* Emit the sixel palette.
*
* @param sb the StringBuilder to append to
* @return the string to emit to an ANSI / ECMA-style terminal
*/
public String emitPalette(final StringBuilder sb) {
public void emitPalette(final StringBuilder sb) {
for (int i = 0; i < sixelColors.size(); i++) {
int sixelColor = sixelColors.get(i);
sb.append(String.format("#%d;2;%d;%d;%d", i,
((sixelColor >>> 16) & 0xFF),
((sixelColor >>> 8) & 0xFF),
( sixelColor & 0xFF)));
int red = ((sixelColor >>> 16) & 0xFF);
int green = ((sixelColor >>> 8) & 0xFF);
int blue = ( sixelColor & 0xFF);
sb.append("#");
sb.append(Integer.toString(i));
sb.append(";2;");
sb.append(Integer.toString(red));
sb.append(";");
sb.append(Integer.toString(green));
sb.append(";");
sb.append(Integer.toString(blue));
}
return sb.toString();
}
}

View file

@ -29,6 +29,7 @@
package jexer.bits;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import jexer.backend.Backend;
import jexer.backend.GlyphMaker;
import jexer.backend.SwingTerminal;
@ -227,8 +228,8 @@ public class Cell extends CellAttributes {
int textWidth = image.getWidth();
int textHeight = image.getHeight();
BufferedImage newImage = new BufferedImage(textWidth,
textHeight, BufferedImage.TYPE_INT_ARGB);
BufferedImage newImage = ImageUtils.createImage(image, textWidth,
textHeight);
java.awt.Graphics gr = newImage.getGraphics();
if (invertedImage != null) {
assert (image != null);
@ -315,8 +316,12 @@ public class Cell extends CellAttributes {
int textWidth = image.getWidth();
int textHeight = image.getHeight();
/*
BufferedImage newImage = new BufferedImage(textWidth,
textHeight, BufferedImage.TYPE_INT_ARGB);
*/
BufferedImage newImage = ImageUtils.createImage(image,
textWidth, textHeight);
java.awt.Graphics gr = newImage.getGraphics();
if (backend != null) {
gr.setColor(backend.attrToBackgroundColor(this));
@ -356,8 +361,12 @@ public class Cell extends CellAttributes {
int textWidth = image.getWidth();
int textHeight = image.getHeight();
/*
BufferedImage newImage = new BufferedImage(textWidth,
textHeight, BufferedImage.TYPE_INT_ARGB);
*/
BufferedImage newImage = ImageUtils.createImage(image,
textWidth, textHeight);
java.awt.Graphics gr = newImage.getGraphics();
gr.setColor(background);
@ -690,6 +699,15 @@ public class Cell extends CellAttributes {
if ((invertedImage != null) && (that.invertedImage == null)) {
return false;
}
if (image.getType() != that.image.getType()) {
return false;
}
if ((image.getColorModel() instanceof IndexColorModel)
&& (that.image.getColorModel() instanceof IndexColorModel)
&& image.getColorModel() != that.image.getColorModel()
) {
return false;
}
// Either both objects have their image inverted, or neither do.
if ((imageId != 0) && (that.imageId != 0)) {
return (imageId == that.imageId);

View file

@ -30,6 +30,10 @@ package jexer.bits;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
// import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
// import java.awt.image.Raster;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
@ -509,4 +513,31 @@ public class ImageUtils {
return (rgbRed << 16) | (rgbGreen << 8) | rgbBlue;
}
/**
* Create a BufferedImage using the same color model as another image.
*
* @param image the original image
* @param width the width of the new image
* @param height the height of the new image
* @return the new image
*/
public static BufferedImage createImage(final BufferedImage image,
final int width, final int height) {
if (image.getType() == BufferedImage.TYPE_INT_ARGB) {
return new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
}
ColorModel colorModel = image.getColorModel();
if (colorModel instanceof IndexColorModel) {
IndexColorModel indexModel = (IndexColorModel) colorModel;
return new BufferedImage(width, height, image.getType(),
indexModel);
}
// Fallback: ARGB
return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
}
}