提交 85477612 编写于 作者: B bae

4893408: JPEGReader throws IllegalArgException when setting the destination to BYTE_GRAY

Reviewed-by: igor, prr
上级 a376a352
......@@ -215,17 +215,21 @@ public class JPEG {
public static class JCS {
public static final ColorSpace sRGB =
ColorSpace.getInstance(ColorSpace.CS_sRGB);
public static final ColorSpace YCC;
static {
ColorSpace cs = null;
try {
cs = ColorSpace.getInstance(ColorSpace.CS_PYCC);
} catch (IllegalArgumentException e) {
// PYCC.pf may not always be installed
} finally {
YCC = cs;
private static ColorSpace YCC = null;
private static boolean yccInited = false;
public static ColorSpace getYCC() {
if (!yccInited) {
try {
YCC = ColorSpace.getInstance(ColorSpace.CS_PYCC);
} catch (IllegalArgumentException e) {
// PYCC.pf may not always be installed
} finally {
yccInited = true;
}
}
return YCC;
}
}
......
......@@ -53,6 +53,7 @@ import java.io.IOException;
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
......@@ -215,51 +216,6 @@ public class JPEGImageReader extends ImageReader {
/** The DisposerRecord that handles the actual disposal of this reader. */
private DisposerRecord disposerRecord;
/**
* Maintain an array of the default image types corresponding to the
* various supported IJG colorspace codes.
*/
private static final ImageTypeSpecifier [] defaultTypes =
new ImageTypeSpecifier [JPEG.NUM_JCS_CODES];
static {
defaultTypes[JPEG.JCS_GRAYSCALE] =
ImageTypeSpecifier.createFromBufferedImageType
(BufferedImage.TYPE_BYTE_GRAY);
defaultTypes[JPEG.JCS_RGB] =
ImageTypeSpecifier.createInterleaved
(JPEG.JCS.sRGB,
JPEG.bOffsRGB,
DataBuffer.TYPE_BYTE,
false,
false);
defaultTypes[JPEG.JCS_RGBA] =
ImageTypeSpecifier.createPacked
(JPEG.JCS.sRGB,
0xff000000,
0x00ff0000,
0x0000ff00,
0x000000ff,
DataBuffer.TYPE_INT,
false);
if (JPEG.JCS.YCC != null) {
defaultTypes[JPEG.JCS_YCC] =
ImageTypeSpecifier.createInterleaved
(JPEG.JCS.YCC,
JPEG.bandOffsets[2],
DataBuffer.TYPE_BYTE,
false,
false);
defaultTypes[JPEG.JCS_YCCA] =
ImageTypeSpecifier.createInterleaved
(JPEG.JCS.YCC,
JPEG.bandOffsets[3],
DataBuffer.TYPE_BYTE,
true,
false);
}
}
/** Sets up static C structures. */
private static native void initReaderIDs(Class iisClass,
Class qTableClass,
......@@ -706,11 +662,11 @@ public class JPEGImageReader extends ImageReader {
* Return an ImageTypeSpecifier corresponding to the given
* color space code, or null if the color space is unsupported.
*/
private ImageTypeSpecifier getImageType(int code) {
ImageTypeSpecifier ret = null;
private ImageTypeProducer getImageType(int code) {
ImageTypeProducer ret = null;
if ((code > 0) && (code < JPEG.NUM_JCS_CODES)) {
ret = defaultTypes[code];
ret = ImageTypeProducer.getTypeProducer(code);
}
return ret;
}
......@@ -724,7 +680,7 @@ public class JPEGImageReader extends ImageReader {
}
// Returns null if it can't be represented
return getImageType(colorSpaceCode);
return getImageType(colorSpaceCode).getType();
} finally {
clearThreadLock();
}
......@@ -758,13 +714,13 @@ public class JPEGImageReader extends ImageReader {
// Get the raw ITS, if there is one. Note that this
// won't always be the same as the default.
ImageTypeSpecifier raw = getImageType(colorSpaceCode);
ImageTypeProducer raw = getImageType(colorSpaceCode);
// Given the encoded colorspace, build a list of ITS's
// representing outputs you could handle starting
// with the default.
ArrayList list = new ArrayList(1);
ArrayList<ImageTypeProducer> list = new ArrayList<ImageTypeProducer>(1);
switch (colorSpaceCode) {
case JPEG.JCS_GRAYSCALE:
......@@ -774,9 +730,7 @@ public class JPEGImageReader extends ImageReader {
case JPEG.JCS_RGB:
list.add(raw);
list.add(getImageType(JPEG.JCS_GRAYSCALE));
if (JPEG.JCS.YCC != null) {
list.add(getImageType(JPEG.JCS_YCC));
}
list.add(getImageType(JPEG.JCS_YCC));
break;
case JPEG.JCS_RGBA:
list.add(raw);
......@@ -801,19 +755,21 @@ public class JPEGImageReader extends ImageReader {
list.add(getImageType(JPEG.JCS_RGB));
if (iccCS != null) {
list.add(ImageTypeSpecifier.createInterleaved
list.add(new ImageTypeProducer() {
protected ImageTypeSpecifier produce() {
return ImageTypeSpecifier.createInterleaved
(iccCS,
JPEG.bOffsRGB, // Assume it's for RGB
DataBuffer.TYPE_BYTE,
false,
false));
false);
}
});
}
list.add(getImageType(JPEG.JCS_GRAYSCALE));
if (JPEG.JCS.YCC != null) { // Might be null if PYCC.pf not installed
list.add(getImageType(JPEG.JCS_YCC));
}
list.add(getImageType(JPEG.JCS_YCC));
break;
case JPEG.JCS_YCbCrA: // Default is to convert to RGBA
// As there is no YCbCr ColorSpace, we can't support
......@@ -822,7 +778,7 @@ public class JPEGImageReader extends ImageReader {
break;
}
return list.iterator();
return new ImageTypeIterator(list.iterator());
}
/**
......@@ -872,6 +828,10 @@ public class JPEGImageReader extends ImageReader {
if (csType == ColorSpace.TYPE_RGB) { // We want RGB
// IJG can do this for us more efficiently
setOutColorSpace(structPointer, JPEG.JCS_RGB);
// Update java state according to changes
// in the native part of decoder.
outColorSpaceCode = JPEG.JCS_RGB;
numComponents = 3;
} else if (csType != ColorSpace.TYPE_GRAY) {
throw new IIOException("Incompatible color conversion");
}
......@@ -881,6 +841,10 @@ public class JPEGImageReader extends ImageReader {
if (colorSpaceCode == JPEG.JCS_YCbCr) {
// If the jpeg space is YCbCr, IJG can do it
setOutColorSpace(structPointer, JPEG.JCS_GRAYSCALE);
// Update java state according to changes
// in the native part of decoder.
outColorSpaceCode = JPEG.JCS_GRAYSCALE;
numComponents = 1;
}
} else if ((iccCS != null) &&
(cm.getNumComponents() == numComponents) &&
......@@ -906,20 +870,26 @@ public class JPEGImageReader extends ImageReader {
}
break;
case JPEG.JCS_YCC:
if (JPEG.JCS.YCC == null) { // We can't do YCC at all
throw new IIOException("Incompatible color conversion");
}
if ((cs != JPEG.JCS.YCC) &&
(cm.getNumComponents() == numComponents)) {
convert = new ColorConvertOp(JPEG.JCS.YCC, cs, null);
{
ColorSpace YCC = JPEG.JCS.getYCC();
if (YCC == null) { // We can't do YCC at all
throw new IIOException("Incompatible color conversion");
}
if ((cs != YCC) &&
(cm.getNumComponents() == numComponents)) {
convert = new ColorConvertOp(YCC, cs, null);
}
}
break;
case JPEG.JCS_YCCA:
// No conversions available; image must be YCCA
if ((JPEG.JCS.YCC == null) || // We can't do YCC at all
(cs != JPEG.JCS.YCC) ||
(cm.getNumComponents() != numComponents)) {
throw new IIOException("Incompatible color conversion");
{
ColorSpace YCC = JPEG.JCS.getYCC();
// No conversions available; image must be YCCA
if ((YCC == null) || // We can't do YCC at all
(cs != YCC) ||
(cm.getNumComponents() != numComponents)) {
throw new IIOException("Incompatible color conversion");
}
}
break;
default:
......@@ -1554,3 +1524,140 @@ public class JPEGImageReader extends ImageReader {
}
}
}
/**
* An internal helper class that wraps producer's iterator
* and extracts specifier instances on demand.
*/
class ImageTypeIterator implements Iterator<ImageTypeSpecifier> {
private Iterator<ImageTypeProducer> producers;
private ImageTypeSpecifier theNext = null;
public ImageTypeIterator(Iterator<ImageTypeProducer> producers) {
this.producers = producers;
}
public boolean hasNext() {
if (theNext != null) {
return true;
}
if (!producers.hasNext()) {
return false;
}
do {
theNext = producers.next().getType();
} while (theNext == null && producers.hasNext());
return (theNext != null);
}
public ImageTypeSpecifier next() {
if (theNext != null || hasNext()) {
ImageTypeSpecifier t = theNext;
theNext = null;
return t;
} else {
throw new NoSuchElementException();
}
}
public void remove() {
producers.remove();
}
}
/**
* An internal helper class that provides means for deferred creation
* of ImageTypeSpecifier instance required to describe available
* destination types.
*
* This implementation only supports standard
* jpeg color spaces (defined by corresponding JCS color space code).
*
* To support other color spaces one can override produce() method to
* return custom instance of ImageTypeSpecifier.
*/
class ImageTypeProducer {
private ImageTypeSpecifier type = null;
boolean failed = false;
private int csCode;
public ImageTypeProducer(int csCode) {
this.csCode = csCode;
}
public ImageTypeProducer() {
csCode = -1; // undefined
}
public synchronized ImageTypeSpecifier getType() {
if (!failed && type == null) {
try {
type = produce();
} catch (Throwable e) {
failed = true;
}
}
return type;
}
private static final ImageTypeProducer [] defaultTypes =
new ImageTypeProducer [JPEG.NUM_JCS_CODES];
public synchronized static ImageTypeProducer getTypeProducer(int csCode) {
if (csCode < 0 || csCode >= JPEG.NUM_JCS_CODES) {
return null;
}
if (defaultTypes[csCode] == null) {
defaultTypes[csCode] = new ImageTypeProducer(csCode);
}
return defaultTypes[csCode];
}
protected ImageTypeSpecifier produce() {
switch (csCode) {
case JPEG.JCS_GRAYSCALE:
return ImageTypeSpecifier.createFromBufferedImageType
(BufferedImage.TYPE_BYTE_GRAY);
case JPEG.JCS_RGB:
return ImageTypeSpecifier.createInterleaved(JPEG.JCS.sRGB,
JPEG.bOffsRGB,
DataBuffer.TYPE_BYTE,
false,
false);
case JPEG.JCS_RGBA:
return ImageTypeSpecifier.createPacked(JPEG.JCS.sRGB,
0xff000000,
0x00ff0000,
0x0000ff00,
0x000000ff,
DataBuffer.TYPE_INT,
false);
case JPEG.JCS_YCC:
if (JPEG.JCS.getYCC() != null) {
return ImageTypeSpecifier.createInterleaved(
JPEG.JCS.getYCC(),
JPEG.bandOffsets[2],
DataBuffer.TYPE_BYTE,
false,
false);
} else {
return null;
}
case JPEG.JCS_YCCA:
if (JPEG.JCS.getYCC() != null) {
return ImageTypeSpecifier.createInterleaved(
JPEG.JCS.getYCC(),
JPEG.bandOffsets[3],
DataBuffer.TYPE_BYTE,
true,
false);
} else {
return null;
}
default:
return null;
}
}
}
......@@ -812,7 +812,7 @@ public class JPEGImageWriter extends ImageWriter {
}
break;
case ColorSpace.TYPE_3CLR:
if (cs == JPEG.JCS.YCC) {
if (cs == JPEG.JCS.getYCC()) {
if (!alpha) {
if (jfif != null) {
convertTosRGB = true;
......@@ -1494,7 +1494,7 @@ public class JPEGImageWriter extends ImageWriter {
}
break;
case ColorSpace.TYPE_3CLR:
if (cs == JPEG.JCS.YCC) {
if (cs == JPEG.JCS.getYCC()) {
if (alpha) {
retval = JPEG.JCS_YCCA;
} else {
......@@ -1533,7 +1533,7 @@ public class JPEGImageWriter extends ImageWriter {
}
break;
case ColorSpace.TYPE_3CLR:
if (cs == JPEG.JCS.YCC) {
if (cs == JPEG.JCS.getYCC()) {
if (alpha) {
retval = JPEG.JCS_YCCA;
} else {
......@@ -1579,7 +1579,7 @@ public class JPEGImageWriter extends ImageWriter {
}
break;
case ColorSpace.TYPE_3CLR:
if (cs == JPEG.JCS.YCC) {
if (cs == JPEG.JCS.getYCC()) {
if (alpha) {
retval = JPEG.JCS_YCCA;
} else {
......
......@@ -490,7 +490,7 @@ public class JPEGMetadata extends IIOMetadata implements Cloneable {
}
break;
case ColorSpace.TYPE_3CLR:
if (cs == JPEG.JCS.YCC) {
if (cs == JPEG.JCS.getYCC()) {
wantJFIF = false;
componentIDs[0] = (byte) 'Y';
componentIDs[1] = (byte) 'C';
......
......@@ -863,11 +863,16 @@ public class ICC_Profile implements Serializable {
case ColorSpace.CS_PYCC:
synchronized(ICC_Profile.class) {
if (PYCCprofile == null) {
ProfileDeferralInfo pInfo =
new ProfileDeferralInfo("PYCC.pf",
ColorSpace.TYPE_3CLR, 3,
CLASS_DISPLAY);
PYCCprofile = getDeferredInstance(pInfo);
if (getProfileFile("PYCC.pf") != null) {
ProfileDeferralInfo pInfo =
new ProfileDeferralInfo("PYCC.pf",
ColorSpace.TYPE_3CLR, 3,
CLASS_DISPLAY);
PYCCprofile = getDeferredInstance(pInfo);
} else {
throw new IllegalArgumentException(
"Can't load standard profile: PYCC.pf");
}
}
thisProfile = PYCCprofile;
}
......@@ -1783,17 +1788,33 @@ public class ICC_Profile implements Serializable {
return (FileInputStream)java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
return privilegedOpenProfile(fileName);
File f = privilegedGetProfileFile(fileName);
if (f != null) {
try {
return new FileInputStream(f);
} catch (FileNotFoundException e) {
}
}
return null;
}
});
}
private static File getProfileFile(final String fileName) {
return (File)java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
return privilegedGetProfileFile(fileName);
}
});
}
/*
* this version is called from doPrivileged in privilegedOpenProfile.
* the whole method is privileged!
* this version is called from doPrivileged in openProfile
* or getProfileFile, so the whole method is privileged!
*/
private static FileInputStream privilegedOpenProfile(String fileName) {
FileInputStream fis = null;
private static File privilegedGetProfileFile(String fileName) {
String path, dir, fullPath;
File f = new File(fileName); /* try absolute file name */
......@@ -1830,12 +1851,9 @@ public class ICC_Profile implements Serializable {
}
if (f.isFile()) {
try {
fis = new FileInputStream(f);
} catch (FileNotFoundException e) {
}
return f;
}
return fis;
return null;
}
......
......@@ -1783,7 +1783,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage
struct jpeg_source_mgr *src;
JSAMPROW scanLinePtr;
JSAMPROW scanLinePtr = NULL;
jint bands[MAX_BANDS];
int i, j;
jint *body;
......@@ -1819,7 +1819,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage
cinfo = (j_decompress_ptr) data->jpegObj;
if ((numBands < 1) || (numBands > cinfo->num_components) ||
if ((numBands < 1) ||
(sourceXStart < 0) || (sourceXStart >= (jint)cinfo->image_width) ||
(sourceYStart < 0) || (sourceYStart >= (jint)cinfo->image_height) ||
(sourceWidth < 1) || (sourceWidth > (jint)cinfo->image_width) ||
......@@ -1877,16 +1877,6 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage
return data->abortFlag; // We already threw an out of memory exception
}
// Allocate a 1-scanline buffer
scanLinePtr = (JSAMPROW)malloc(cinfo->image_width*cinfo->num_components);
if (scanLinePtr == NULL) {
RELEASE_ARRAYS(env, data, src->next_input_byte);
JNU_ThrowByName( env,
"java/lang/OutOfMemoryError",
"Reading JPEG Stream");
return data->abortFlag;
}
/* Establish the setjmp return context for sun_jpeg_error_exit to use. */
jerr = (sun_jpeg_error_ptr) cinfo->err;
......@@ -1900,7 +1890,10 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage
buffer);
JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
}
free(scanLinePtr);
if (scanLinePtr != NULL) {
free(scanLinePtr);
scanLinePtr = NULL;
}
return data->abortFlag;
}
......@@ -1938,6 +1931,23 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage
jpeg_start_decompress(cinfo);
if (numBands != cinfo->output_components) {
JNU_ThrowByName(env, "javax/imageio/IIOException",
"Invalid argument to native readImage");
return data->abortFlag;
}
// Allocate a 1-scanline buffer
scanLinePtr = (JSAMPROW)malloc(cinfo->image_width*cinfo->output_components);
if (scanLinePtr == NULL) {
RELEASE_ARRAYS(env, data, src->next_input_byte);
JNU_ThrowByName( env,
"java/lang/OutOfMemoryError",
"Reading JPEG Stream");
return data->abortFlag;
}
// loop over progressive passes
done = FALSE;
while (!done) {
......@@ -1965,9 +1975,9 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage
scanlineLimit = sourceYStart+sourceHeight;
pixelLimit = scanLinePtr
+(sourceXStart+sourceWidth)*cinfo->num_components;
+(sourceXStart+sourceWidth)*cinfo->output_components;
pixelStride = stepX*cinfo->num_components;
pixelStride = stepX*cinfo->output_components;
targetLine = 0;
while ((data->abortFlag == JNI_FALSE)
......@@ -1982,12 +1992,12 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage
// Optimization: The component bands are ordered sequentially,
// so we can simply use memcpy() to copy the intermediate
// scanline buffer into the raster.
in = scanLinePtr + (sourceXStart * cinfo->num_components);
in = scanLinePtr + (sourceXStart * cinfo->output_components);
if (pixelLimit > in) {
memcpy(out, in, pixelLimit - in);
}
} else {
for (in = scanLinePtr+sourceXStart*cinfo->num_components;
for (in = scanLinePtr+sourceXStart*cinfo->output_components;
in < pixelLimit;
in += pixelStride) {
for (i = 0; i < numBands; i++) {
......
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/**
* @test
* @bug 4893408
*
* @summary Test verifies that Image I/O jpeg reader correctly handles
* destination types if number of color components in destination
* differs from number of color components in the jpeg image.
* Particularly, it verifies reading YCbCr image as a grayscaled
* and reading grayscaled jpeg as a RGB.
*
* @run main ReadAsGrayTest
*/
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.stream.ImageInputStream;
import static java.awt.image.BufferedImage.TYPE_3BYTE_BGR;
import static java.awt.image.BufferedImage.TYPE_BYTE_GRAY;
import static java.awt.color.ColorSpace.TYPE_GRAY;
import static java.awt.color.ColorSpace.CS_sRGB;
public class ReadAsGrayTest {
static Color[] colors = new Color[] {
Color.white, Color.red, Color.green,
Color.blue, Color.black };
static final int dx = 50;
static final int h = 100;
static ColorSpace sRGB = ColorSpace.getInstance(CS_sRGB);
public static void main(String[] args) throws IOException {
System.out.println("Type TYPE_BYTE_GRAY");
doTest(TYPE_BYTE_GRAY);
System.out.println("Type TYPE_3BYTE_BGR");
doTest(TYPE_3BYTE_BGR);
System.out.println("Test PASSED.");
}
private static void doTest(int type) throws IOException {
BufferedImage src = createTestImage(type);
File f = new File("test.jpg");
if (!ImageIO.write(src, "jpg", f)) {
throw new RuntimeException("Failed to write test image.");
}
ImageInputStream iis = ImageIO.createImageInputStream(f);
ImageReader reader = ImageIO.getImageReaders(iis).next();
reader.setInput(iis);
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
ImageTypeSpecifier srgb = null;
ImageTypeSpecifier gray = null;
// look for gray and srgb types
while ((srgb == null || gray == null) && types.hasNext()) {
ImageTypeSpecifier t = types.next();
if (t.getColorModel().getColorSpace().getType() == TYPE_GRAY) {
gray = t;
}
if (t.getColorModel().getColorSpace() == sRGB) {
srgb = t;
}
}
if (gray == null) {
throw new RuntimeException("No gray type available.");
}
if (srgb == null) {
throw new RuntimeException("No srgb type available.");
}
System.out.println("Read as GRAY...");
testType(reader, gray, src);
System.out.println("Read as sRGB...");
testType(reader, srgb, src);
}
private static void testType(ImageReader reader,
ImageTypeSpecifier t,
BufferedImage src)
throws IOException
{
ImageReadParam p = reader.getDefaultReadParam();
p.setDestinationType(t);
BufferedImage dst = reader.read(0, p);
verify(src, dst, t);
}
private static void verify(BufferedImage src,
BufferedImage dst,
ImageTypeSpecifier type)
{
BufferedImage test =
type.createBufferedImage(src.getWidth(), src.getHeight());
Graphics2D g = test.createGraphics();
g.drawImage(src, 0, 0, null);
g.dispose();
for (int i = 0; i < colors.length; i++) {
int x = i * dx + dx / 2;
int y = h / 2;
Color c_test = new Color(test.getRGB(x, y));
Color c_dst = new Color(dst.getRGB(x, y));
if (!compareWithTolerance(c_test, c_dst, 0.01f)) {
String msg = String.format("Invalid color: %x instead of %x",
c_dst.getRGB(), c_test.getRGB());
throw new RuntimeException("Test failed: " + msg);
}
}
System.out.println("Verified.");
}
private static boolean compareWithTolerance(Color a, Color b, float delta) {
float[] a_rgb = new float[3];
a_rgb = a.getRGBColorComponents(a_rgb);
float[] b_rgb = new float[3];
b_rgb = b.getRGBColorComponents(b_rgb);
for (int i = 0; i < 3; i++) {
if (Math.abs(a_rgb[i] - b_rgb[i]) > delta) {
return false;
}
}
return true;
}
private static BufferedImage createTestImage(int type) {
BufferedImage img = new BufferedImage(dx * colors.length, h, type);
Graphics2D g = img.createGraphics();
for (int i = 0; i < colors.length; i++) {
g.setColor(colors[i]);
g.fillRect(i * dx, 0, dx, h);
}
g.dispose();
return img;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册