mirror of
https://github.com/openjdk/jdk.git
synced 2026-08-03 06:35:31 +00:00
8377158: Enhance XBM image support
Reviewed-by: rhalade, aivanov, prr, azvegint
This commit is contained in:
parent
24def160f9
commit
88fb149eb0
@ -31,12 +31,8 @@ package sun.awt.image;
|
||||
import java.awt.image.ImageConsumer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.io.IOException;
|
||||
|
||||
import static java.lang.Math.multiplyExact;
|
||||
|
||||
@ -62,9 +58,8 @@ public class XbmImageDecoder extends ImageDecoder {
|
||||
ImageConsumer.SINGLEPASS |
|
||||
ImageConsumer.SINGLEFRAME);
|
||||
|
||||
private static final int MAX_CHAR_LIMIT = 128000;
|
||||
private static final int MAX_XBM_SIZE = 16384;
|
||||
private static final int HEADER_SCAN_LIMIT = 100;
|
||||
|
||||
public XbmImageDecoder(InputStreamImageSource src, InputStream is) {
|
||||
super(src, is);
|
||||
if (!(input instanceof BufferedInputStream)) {
|
||||
@ -86,150 +81,229 @@ public class XbmImageDecoder extends ImageDecoder {
|
||||
* produce an image from the stream.
|
||||
*/
|
||||
public void produceImage() throws IOException, ImageFormatException {
|
||||
char[] nm = new char[80];
|
||||
int c;
|
||||
int i = 0;
|
||||
int state = 0;
|
||||
int H = 0;
|
||||
int W = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int n = 0;
|
||||
int state = 0;
|
||||
boolean consumeWidthValue = false;
|
||||
boolean consumeHeightValue = false;
|
||||
boolean validWidthConsumed = false;
|
||||
boolean validHeightConsumed = false;
|
||||
// number of tokens seen as part of this define statement
|
||||
int defineTokenCount = 0;
|
||||
byte[] raster = null;
|
||||
IndexColorModel model = null;
|
||||
int charCount = 0;
|
||||
|
||||
String matchRegex = "\\s*(0[xX])?((?:(?!,|\\};).)+)(,|\\};)";
|
||||
String replaceRegex = "0[xX]|,|\\s+|\\};";
|
||||
|
||||
String line;
|
||||
int lineNum = 0;
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(input))) {
|
||||
// loop to process XBM header - width, height and create raster
|
||||
while (!aborted && (line = br.readLine()) != null
|
||||
&& lineNum <= HEADER_SCAN_LIMIT) {
|
||||
lineNum++;
|
||||
// process #define stmts
|
||||
if (line.trim().startsWith("#define")) {
|
||||
String[] token = line.split("\\s+");
|
||||
if (token.length != 3) {
|
||||
error("Error while parsing define statement");
|
||||
}
|
||||
try {
|
||||
if (state < 2) {
|
||||
if (token[1].endsWith("h")) {
|
||||
W = Integer.parseInt(token[2]);
|
||||
} else if (token[1].endsWith("ht")) {
|
||||
H = Integer.parseInt(token[2]);
|
||||
}
|
||||
// After the 1st dimension is set, state becomes 1;
|
||||
// after the 2nd dimension is set, state becomes 2
|
||||
++state;
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
// parseInt() can throw NFE
|
||||
error("Error while parsing width or height.");
|
||||
}
|
||||
}
|
||||
|
||||
if (state == 2) {
|
||||
if (W <= 0 || H <= 0) {
|
||||
error("Invalid values for width or height.");
|
||||
}
|
||||
if (multiplyExact(W, H) > MAX_XBM_SIZE) {
|
||||
error("Large XBM file size."
|
||||
+ " Maximum allowed size: " + MAX_XBM_SIZE);
|
||||
}
|
||||
model = new IndexColorModel(8, 2, XbmColormap,
|
||||
0, false, 0);
|
||||
setDimensions(W, H);
|
||||
setColorModel(model);
|
||||
setHints(XbmHints);
|
||||
headerComplete();
|
||||
raster = new byte[W];
|
||||
state = 3;
|
||||
break;
|
||||
}
|
||||
//read header info
|
||||
while (!aborted && (c = input.read()) != -1) {
|
||||
charCount++;
|
||||
if (charCount > MAX_CHAR_LIMIT) {
|
||||
error("Incomplete image after reading "
|
||||
+ "the maximum allowed number of characters: "
|
||||
+ MAX_CHAR_LIMIT);
|
||||
}
|
||||
|
||||
if (state != 3) {
|
||||
error("Width or Height of XBM file not defined");
|
||||
}
|
||||
|
||||
boolean contFlag = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// loop to process image data
|
||||
while (!aborted && (line = br.readLine()) != null) {
|
||||
lineNum++;
|
||||
|
||||
if (!contFlag) {
|
||||
if (line.contains("[]")) {
|
||||
contFlag = true;
|
||||
} else {
|
||||
if ('a' <= c && c <= 'z' ||
|
||||
'A' <= c && c <= 'Z' ||
|
||||
'0' <= c && c <= '9' || c == '#' || c == '_') {
|
||||
if (i < nm.length) {
|
||||
nm[i++] = (char) c;
|
||||
} else {
|
||||
error("XBM header contains literal greater than size 80");
|
||||
}
|
||||
} else if (i > 0) {
|
||||
int nc = i;
|
||||
i = 0;
|
||||
if (defineTokenCount >= 1) {
|
||||
// we are inside a #define line
|
||||
defineTokenCount++;
|
||||
}
|
||||
if (defineTokenCount == 0) {
|
||||
if (nc == 7 &&
|
||||
nm[0] == '#' &&
|
||||
nm[1] == 'd' &&
|
||||
nm[2] == 'e' &&
|
||||
nm[3] == 'f' &&
|
||||
nm[4] == 'i' &&
|
||||
nm[5] == 'n' &&
|
||||
nm[6] == 'e')
|
||||
{
|
||||
defineTokenCount++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (defineTokenCount == 2) {
|
||||
// consume second token in #define line
|
||||
if (state < 2) {
|
||||
if (nm[nc - 1] == 'h' &&
|
||||
!validWidthConsumed) {
|
||||
consumeWidthValue = true;
|
||||
} else if ((nm[nc - 1] == 't' && nc > 1 &&
|
||||
nm[nc - 2] == 'h') &&
|
||||
!validHeightConsumed) {
|
||||
consumeHeightValue = true;
|
||||
}
|
||||
}
|
||||
} else if (defineTokenCount == 3) {
|
||||
defineTokenCount = 0;
|
||||
// consume third token in #define line
|
||||
int n = 0;
|
||||
for (int p = 0; p < nc; p++) {
|
||||
if ('0' <= (c = nm[p]) && c <= '9') {
|
||||
n = n * 10 + c - '0';
|
||||
if (n > MAX_XBM_SIZE) {
|
||||
error("Width/Height cannot be more than: "
|
||||
+ MAX_XBM_SIZE);
|
||||
}
|
||||
} else {
|
||||
error("Invalid width/height value");
|
||||
}
|
||||
}
|
||||
|
||||
int end = line.indexOf(';');
|
||||
if (end >= 0) {
|
||||
sb.append(line, 0, end + 1);
|
||||
break;
|
||||
} else {
|
||||
sb.append(line).append(System.lineSeparator());
|
||||
if (n > 0 && (consumeWidthValue || consumeHeightValue)) {
|
||||
if (consumeWidthValue) {
|
||||
if (!validWidthConsumed) {
|
||||
W = n;
|
||||
validWidthConsumed = true;
|
||||
state++;
|
||||
}
|
||||
consumeWidthValue = false;
|
||||
} else if (consumeHeightValue) {
|
||||
if (!validHeightConsumed) {
|
||||
H = n;
|
||||
validHeightConsumed = true;
|
||||
state++;
|
||||
}
|
||||
consumeHeightValue = false;
|
||||
}
|
||||
}
|
||||
// verify the consumed width & height value and initialize
|
||||
// required constructs
|
||||
if (state == 2) {
|
||||
if (multiplyExact(W, H) > MAX_XBM_SIZE) {
|
||||
error("Large XBM file size."
|
||||
+ " Maximum allowed size: " + MAX_XBM_SIZE);
|
||||
}
|
||||
model = new IndexColorModel(8, 2, XbmColormap,
|
||||
0, false, 0);
|
||||
setDimensions(W, H);
|
||||
setColorModel(model);
|
||||
setHints(XbmHints);
|
||||
headerComplete();
|
||||
raster = new byte[W];
|
||||
state = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String resultLine = sb.toString();
|
||||
int cutOffIndex = resultLine.indexOf('{');
|
||||
resultLine = resultLine.substring(cutOffIndex + 1);
|
||||
|
||||
Matcher matcher = Pattern.compile(matchRegex).matcher(resultLine);
|
||||
while (matcher.find()) {
|
||||
if (y >= H) {
|
||||
error("Scan size of XBM file exceeds"
|
||||
+ " the defined width x height");
|
||||
}
|
||||
|
||||
int startIndex = matcher.start();
|
||||
int endIndex = matcher.end();
|
||||
String hexByte = resultLine.substring(startIndex, endIndex);
|
||||
hexByte = hexByte.replaceAll("^\\s+", "");
|
||||
|
||||
if (!(hexByte.startsWith("0x")
|
||||
|| hexByte.startsWith("0X"))) {
|
||||
error("Invalid hexadecimal number at Ln#:" + lineNum
|
||||
+ " Col#:" + (startIndex + 1));
|
||||
}
|
||||
hexByte = hexByte.replaceAll(replaceRegex, "");
|
||||
if (hexByte.length() != 2) {
|
||||
error("Invalid hexadecimal number at Ln#:" + lineNum
|
||||
+ " Col#:" + (startIndex + 1));
|
||||
}
|
||||
|
||||
try {
|
||||
n = Integer.parseInt(hexByte, 16);
|
||||
} catch (NumberFormatException nfe) {
|
||||
error("Error parsing hexadecimal at Ln#:" + lineNum
|
||||
+ " Col#:" + (startIndex + 1));
|
||||
}
|
||||
for (int mask = 1; mask <= 0x80; mask <<= 1) {
|
||||
if (x < W) {
|
||||
if ((n & mask) != 0)
|
||||
raster[x] = 1;
|
||||
else
|
||||
raster[x] = 0;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
|
||||
if (x >= W) {
|
||||
int result = setPixels(0, y, W, 1, model, raster, 0, W);
|
||||
if (result <= 0) {
|
||||
error("Unexpected error occurred during setPixel()");
|
||||
}
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
imageComplete(ImageConsumer.STATICIMAGEDONE, true);
|
||||
}
|
||||
|
||||
if (state != 3) {
|
||||
error("Width or Height of XBM file not defined");
|
||||
}
|
||||
|
||||
// skip until we find '{'
|
||||
boolean imageDataStarted = false;
|
||||
while (!aborted && (c = input.read()) != -1) {
|
||||
charCount++;
|
||||
if (charCount > MAX_CHAR_LIMIT) {
|
||||
error("Incomplete image after reading "
|
||||
+ "the maximum allowed number of characters: "
|
||||
+ MAX_CHAR_LIMIT);
|
||||
}
|
||||
if (c == '{') {
|
||||
imageDataStarted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!imageDataStarted) {
|
||||
error("Missing '{' at the start of image data");
|
||||
}
|
||||
|
||||
// used to make sure that we have the final delimiter '};',
|
||||
// while parsing the image data
|
||||
int previousChar = '{';
|
||||
// parse image data
|
||||
boolean imageDataTerminated = false;
|
||||
while (!aborted && (c = input.read()) != -1) {
|
||||
charCount++;
|
||||
if (charCount > MAX_CHAR_LIMIT) {
|
||||
error("Incomplete image after reading "
|
||||
+ "the maximum allowed number of characters: "
|
||||
+ MAX_CHAR_LIMIT);
|
||||
}
|
||||
|
||||
if (c == ';') {
|
||||
if (previousChar != '}') {
|
||||
error("Abrupt end of image data without '};' delimiter");
|
||||
}
|
||||
imageDataTerminated = true;
|
||||
break;
|
||||
}
|
||||
if (!Character.isWhitespace(c)) {
|
||||
previousChar = c;
|
||||
}
|
||||
|
||||
if (',' != c && '}' != c &&
|
||||
!Character.isWhitespace(c)) {
|
||||
nm[i++] = (char) c;
|
||||
if (i > 4) {
|
||||
error("Image hex data should be 3 or 4 characters long");
|
||||
}
|
||||
} else if (i == 3 || i == 4) {
|
||||
// consume valid hex image data
|
||||
int n = 0;
|
||||
int nc = i;
|
||||
i = 0;
|
||||
if (nm[0] == '0' &&
|
||||
(nm[1] == 'x' || nm[1] == 'X')) {
|
||||
for (int p = 2; p < nc; p++) {
|
||||
c = nm[p];
|
||||
if ('0' <= c && c <= '9')
|
||||
c = c - '0';
|
||||
else if ('A' <= c && c <= 'F')
|
||||
c = c - 'A' + 10;
|
||||
else if ('a' <= c && c <= 'f')
|
||||
c = c - 'a' + 10;
|
||||
else
|
||||
error("Corrupt hex image data");
|
||||
n = n * 16 + c;
|
||||
}
|
||||
for (int mask = 1; mask <= 0x80; mask <<= 1) {
|
||||
if (x < W) {
|
||||
if ((n & mask) != 0)
|
||||
raster[x] = 1;
|
||||
else
|
||||
raster[x] = 0;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
if (x >= W) {
|
||||
if ((y + 1) > H) {
|
||||
error("Scan size of XBM file exceeds"
|
||||
+ " the defined width x height");
|
||||
}
|
||||
if (setPixels(0, y, W, 1, model, raster, 0, W) == 0) {
|
||||
error("Unexpected error occurred during setPixel()");
|
||||
}
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
} else {
|
||||
error("Corrupt hex image data");
|
||||
}
|
||||
} else if (i == 1 || i == 2) {
|
||||
error("Image hex data should be 3 or 4 characters long");
|
||||
}
|
||||
}
|
||||
if (!imageDataTerminated) {
|
||||
error("Missing terminator ';'");
|
||||
}
|
||||
input.close();
|
||||
imageComplete(ImageConsumer.STATICIMAGEDONE, true);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user