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

View file

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

View file

@ -1702,17 +1702,23 @@ public class HQSixelEncoder implements SixelEncoder {
* Emit the sixel palette. * Emit the sixel palette.
* *
* @param sb the StringBuilder to append to * @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++) { for (int i = 0; i < sixelColors.size(); i++) {
int sixelColor = sixelColors.get(i); int sixelColor = sixelColors.get(i);
sb.append(String.format("#%d;2;%d;%d;%d", i, int red = ((sixelColor >>> 16) & 0xFF);
((sixelColor >>> 16) & 0xFF), int green = ((sixelColor >>> 8) & 0xFF);
((sixelColor >>> 8) & 0xFF), int blue = ( sixelColor & 0xFF);
( 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; package jexer.bits;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import jexer.backend.Backend; import jexer.backend.Backend;
import jexer.backend.GlyphMaker; import jexer.backend.GlyphMaker;
import jexer.backend.SwingTerminal; import jexer.backend.SwingTerminal;
@ -227,8 +228,8 @@ public class Cell extends CellAttributes {
int textWidth = image.getWidth(); int textWidth = image.getWidth();
int textHeight = image.getHeight(); int textHeight = image.getHeight();
BufferedImage newImage = new BufferedImage(textWidth, BufferedImage newImage = ImageUtils.createImage(image, textWidth,
textHeight, BufferedImage.TYPE_INT_ARGB); textHeight);
java.awt.Graphics gr = newImage.getGraphics(); java.awt.Graphics gr = newImage.getGraphics();
if (invertedImage != null) { if (invertedImage != null) {
assert (image != null); assert (image != null);
@ -315,8 +316,12 @@ public class Cell extends CellAttributes {
int textWidth = image.getWidth(); int textWidth = image.getWidth();
int textHeight = image.getHeight(); int textHeight = image.getHeight();
/*
BufferedImage newImage = new BufferedImage(textWidth, BufferedImage newImage = new BufferedImage(textWidth,
textHeight, BufferedImage.TYPE_INT_ARGB); textHeight, BufferedImage.TYPE_INT_ARGB);
*/
BufferedImage newImage = ImageUtils.createImage(image,
textWidth, textHeight);
java.awt.Graphics gr = newImage.getGraphics(); java.awt.Graphics gr = newImage.getGraphics();
if (backend != null) { if (backend != null) {
gr.setColor(backend.attrToBackgroundColor(this)); gr.setColor(backend.attrToBackgroundColor(this));
@ -356,8 +361,12 @@ public class Cell extends CellAttributes {
int textWidth = image.getWidth(); int textWidth = image.getWidth();
int textHeight = image.getHeight(); int textHeight = image.getHeight();
/*
BufferedImage newImage = new BufferedImage(textWidth, BufferedImage newImage = new BufferedImage(textWidth,
textHeight, BufferedImage.TYPE_INT_ARGB); textHeight, BufferedImage.TYPE_INT_ARGB);
*/
BufferedImage newImage = ImageUtils.createImage(image,
textWidth, textHeight);
java.awt.Graphics gr = newImage.getGraphics(); java.awt.Graphics gr = newImage.getGraphics();
gr.setColor(background); gr.setColor(background);
@ -690,6 +699,15 @@ public class Cell extends CellAttributes {
if ((invertedImage != null) && (that.invertedImage == null)) { if ((invertedImage != null) && (that.invertedImage == null)) {
return false; 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. // Either both objects have their image inverted, or neither do.
if ((imageId != 0) && (that.imageId != 0)) { if ((imageId != 0) && (that.imageId != 0)) {
return (imageId == that.imageId); return (imageId == that.imageId);

View file

@ -30,6 +30,10 @@ package jexer.bits;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage; 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.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
@ -509,4 +513,31 @@ public class ImageUtils {
return (rgbRed << 16) | (rgbGreen << 8) | rgbBlue; 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);
}
} }