*/
private enum ScanState {
GROUND,
- QUOTE,
+ RASTER,
COLOR,
REPEAT,
}
*/
private static int HEIGHT_INCREASE = 400;
+ /**
+ * Maximum width in pixels.
+ */
+ private static int MAX_WIDTH = 1000;
+
+ /**
+ * Maximum height in pixels.
+ */
+ private static int MAX_HEIGHT = 1000;
+
/**
* Current scanning state.
*/
private ScanState scanState = ScanState.GROUND;
/**
- * Parameter characters being collected.
+ * Parameters being collected.
+ */
+ private int [] params = new int[5];
+
+ /**
+ * Current parameter being collected.
*/
- private ArrayList<Integer> colorParams;
+ private int paramsI = 0;
/**
* The sixel palette colors specified.
*/
private int height = 0;
+ /**
+ * The width of image provided in the raster attribute.
+ */
+ private int rasterWidth = 0;
+
+ /**
+ * The height of image provided in the raster attribute.
+ */
+ private int rasterHeight = 0;
+
/**
* The repeat count.
*/
*/
private Color color = Color.BLACK;
+ /**
+ * If set, abort processing this image.
+ */
+ private boolean abort = false;
+
// ------------------------------------------------------------------------
// Constructors -----------------------------------------------------------
// ------------------------------------------------------------------------
* Public constructor.
*
* @param buffer the sixel data to parse
+ * @param palette palette to use, or null for a private palette
*/
- public Sixel(final String buffer) {
+ public Sixel(final String buffer, final HashMap<Integer, Color> palette) {
this.buffer = buffer;
- colorParams = new ArrayList<Integer>();
- palette = new HashMap<Integer, Color>();
- image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB);
- for (int i = 0; i < buffer.length(); i++) {
- consume(buffer.charAt(i));
+ if (palette == null) {
+ this.palette = new HashMap<Integer, Color>();
+ } else {
+ this.palette = palette;
}
}
* @return the sixel data as an image.
*/
public BufferedImage getImage() {
- if ((width > 0) && (height > 0)) {
+ if (buffer != null) {
+ for (int i = 0; (i < buffer.length()) && (abort == false); i++) {
+ consume(buffer.charAt(i));
+ }
+ buffer = null;
+ }
+ if (abort == true) {
+ return null;
+ }
+
+ if ((width > 0) && (height > 0) && (image != null)) {
+ /*
+ System.err.println(String.format("%d %d %d %d", width, y + 1,
+ rasterWidth, rasterHeight));
+ */
+
+ if ((rasterWidth > width) || (rasterHeight > y + 1)) {
+ resizeImage(Math.max(width, rasterWidth),
+ Math.max(y + 1, rasterHeight));
+ }
return image.getSubimage(0, 0, width, y + 1);
}
return null;
BufferedImage newImage = new BufferedImage(newWidth, newHeight,
BufferedImage.TYPE_INT_ARGB);
+ if (image == null) {
+ image = newImage;
+ return;
+ }
+
if (DEBUG) {
System.err.println("resizeImage(); old " + image.getWidth() + "x" +
image.getHeight() + " new " + newWidth + "x" + newHeight);
* Clear the parameters and flags.
*/
private void toGround() {
- colorParams.clear();
+ paramsI = 0;
+ for (int i = 0; i < params.length; i++) {
+ params[i] = 0;
+ }
scanState = ScanState.GROUND;
repeatCount = -1;
}
- /**
- * Save a byte into the color parameters buffer.
- *
- * @param ch byte to save
- */
- private void param(final byte ch) {
- if (colorParams.size() == 0) {
- colorParams.add(Integer.valueOf(0));
- }
- Integer n = colorParams.get(colorParams.size() - 1);
- if ((ch >= '0') && (ch <= '9')) {
- n *= 10;
- n += (ch - '0');
- colorParams.set(colorParams.size() - 1, n);
- }
-
- if ((ch == ';') && (colorParams.size() < 16)) {
- colorParams.add(Integer.valueOf(0));
- }
- }
-
/**
* Get a color parameter value, with a default.
*
* @param defaultValue value to use if colorParams[position] doesn't exist
* @return parameter value
*/
- private int getColorParam(final int position, final int defaultValue) {
- if (colorParams.size() < position + 1) {
+ private int getParam(final int position, final int defaultValue) {
+ if (position > paramsI) {
return defaultValue;
}
- return colorParams.get(position).intValue();
+ return params[position];
}
/**
* @param maxValue maximum value inclusive
* @return parameter value
*/
- private int getColorParam(final int position, final int defaultValue,
+ private int getParam(final int position, final int defaultValue,
final int minValue, final int maxValue) {
assert (minValue <= maxValue);
- int value = getColorParam(position, defaultValue);
+ int value = getParam(position, defaultValue);
if (value < minValue) {
value = minValue;
}
assert (n >= 0);
+ if (image == null) {
+ // The raster attributes was not provided.
+ resizeImage(WIDTH_INCREASE, HEIGHT_INCREASE);
+ }
+
if (x + rep > image.getWidth()) {
// Resize the image, give us another max(rep, WIDTH_INCREASE)
// pixels of horizontal length.
if (x > width) {
width = x;
}
+ if (width > MAX_WIDTH) {
+ abort = true;
+ }
return;
}
+ int dy = 0;
for (int i = 0; i < rep; i++) {
if ((n & 0x01) != 0) {
- image.setRGB(x, height + 0, rgb);
- y = Math.max(y, height);
+ dy = 0;
+ image.setRGB(x, height + dy, rgb);
}
if ((n & 0x02) != 0) {
- image.setRGB(x, height + 1, rgb);
- y = Math.max(y, height + 1);
+ dy = 1;
+ image.setRGB(x, height + dy, rgb);
}
if ((n & 0x04) != 0) {
- image.setRGB(x, height + 2, rgb);
- y = Math.max(y, height + 2);
+ dy = 2;
+ image.setRGB(x, height + dy, rgb);
}
if ((n & 0x08) != 0) {
- image.setRGB(x, height + 3, rgb);
- y = Math.max(y, height + 3);
+ dy = 3;
+ image.setRGB(x, height + dy, rgb);
}
if ((n & 0x10) != 0) {
- image.setRGB(x, height + 4, rgb);
- y = Math.max(y, height + 4);
+ dy = 4;
+ image.setRGB(x, height + dy, rgb);
}
if ((n & 0x20) != 0) {
- image.setRGB(x, height + 5, rgb);
- y = Math.max(y, height + 5);
+ dy = 5;
+ image.setRGB(x, height + dy, rgb);
}
- x++;
- if (x > width) {
- width++;
- assert (x == width);
+ if (height + dy > y) {
+ y = height + dy;
}
+ x++;
+ }
+ if (x > width) {
+ width = x;
+ }
+ if (width > MAX_WIDTH) {
+ abort = true;
+ }
+ if (y + 1 > MAX_HEIGHT) {
+ abort = true;
}
}
* Process a color palette change.
*/
private void setPalette() {
- int idx = getColorParam(0, 0);
+ int idx = getParam(0, 0);
- if (colorParams.size() == 1) {
+ if (paramsI == 0) {
Color newColor = palette.get(idx);
if (newColor != null) {
color = newColor;
return;
}
- int type = getColorParam(1, 0);
- float red = (float) (getColorParam(2, 0, 0, 100) / 100.0);
- float green = (float) (getColorParam(3, 0, 0, 100) / 100.0);
- float blue = (float) (getColorParam(4, 0, 0, 100) / 100.0);
+ int type = getParam(1, 0);
+ float red = (float) (getParam(2, 0, 0, 100) / 100.0);
+ float green = (float) (getParam(3, 0, 0, 100) / 100.0);
+ float blue = (float) (getParam(4, 0, 0, 100) / 100.0);
if (type == 2) {
Color newColor = new Color(red, green, blue);
}
}
+ /**
+ * Parse the raster attributes.
+ */
+ private void parseRaster() {
+ int pan = getParam(0, 0); // Aspect ratio numerator
+ int pad = getParam(1, 0); // Aspect ratio denominator
+ int pah = getParam(2, 0); // Horizontal width
+ int pav = getParam(3, 0); // Vertical height
+
+ if ((pan == pad) && (pah > 0) && (pav > 0)) {
+ rasterWidth = pah;
+ rasterHeight = pav;
+ if ((rasterWidth <= MAX_WIDTH) && (rasterHeight <= MAX_HEIGHT)) {
+ resizeImage(rasterWidth, rasterHeight);
+ } else {
+ abort = true;
+ }
+ } else {
+ abort = true;
+ }
+ }
+
/**
* Run this input character through the sixel state machine.
*
if ((ch >= 63) && (ch < 127)) {
if (scanState == ScanState.COLOR) {
setPalette();
+ }
+ if (scanState == ScanState.RASTER) {
+ parseRaster();
toGround();
}
addSixel(ch);
setPalette();
toGround();
}
+ if (scanState == ScanState.RASTER) {
+ parseRaster();
+ toGround();
+ }
scanState = ScanState.COLOR;
return;
}
setPalette();
toGround();
}
+ if (scanState == ScanState.RASTER) {
+ parseRaster();
+ toGround();
+ }
scanState = ScanState.REPEAT;
repeatCount = 0;
return;
setPalette();
toGround();
}
+ if (scanState == ScanState.RASTER) {
+ parseRaster();
+ toGround();
+ }
height += 6;
x = 0;
setPalette();
toGround();
}
+ if (scanState == ScanState.RASTER) {
+ parseRaster();
+ toGround();
+ }
x = 0;
return;
}
setPalette();
toGround();
}
- scanState = ScanState.QUOTE;
+ scanState = ScanState.RASTER;
return;
}
}
return;
- case QUOTE:
- // Ignore everything else in the quote header.
+ case RASTER:
+ // 30-39, 3B --> param
+ if ((ch >= '0') && (ch <= '9')) {
+ params[paramsI] *= 10;
+ params[paramsI] += (ch - '0');
+ }
+ if (ch == ';') {
+ if (paramsI < params.length - 1) {
+ paramsI++;
+ }
+ }
return;
case COLOR:
// 30-39, 3B --> param
if ((ch >= '0') && (ch <= '9')) {
- param((byte) ch);
+ params[paramsI] *= 10;
+ params[paramsI] += (ch - '0');
}
if (ch == ';') {
- param((byte) ch);
+ if (paramsI < params.length - 1) {
+ paramsI++;
+ }
}
return;