Code clean-up, no functional changes.

This commit is contained in:
Harald Kuhr 2011-12-20 15:38:06 +01:00
parent 7435c12a80
commit 6ba32b657a
5 changed files with 133 additions and 164 deletions

View File

@ -36,34 +36,36 @@ import java.util.Random;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/DiffusionDither.java#1 $ * @version $Id: DiffusionDither.java#1 $
* *
*/ */
public class DiffusionDither implements BufferedImageOp, RasterOp { public class DiffusionDither implements BufferedImageOp, RasterOp {
protected IndexColorModel indexColorModel = null;
private boolean alternateScans = true;
private static final int FS_SCALE = 1 << 8; private static final int FS_SCALE = 1 << 8;
private static final Random RANDOM = new Random(); private static final Random RANDOM = new Random();
protected final IndexColorModel indexColorModel;
private boolean alternateScans = true;
/** /**
* Creates a {@code DiffusionDither}, using the given * Creates a {@code DiffusionDither}, using the given
* {@code IndexColorModel} for dithering into. * {@code IndexColorModel} for dithering into.
* *
* @param pICM an IndexColorModel. * @param pICM an IndexColorModel.
*/ */
public DiffusionDither(IndexColorModel pICM) { public DiffusionDither(final IndexColorModel pICM) {
// Store colormodel // Store color model
indexColorModel = pICM; indexColorModel = pICM;
} }
/** /**
* Creates a {@code DiffusionDither}, with no fixed * Creates a {@code DiffusionDither}, with no fixed
* {@code IndexColorModel}. The colormodel will be generated for each * {@code IndexColorModel}. The color model will be generated for each
* filtering, unless the dest image allready has an * filtering, unless the destination image already has an
* {@code IndexColorModel}. * {@code IndexColorModel}.
*/ */
public DiffusionDither() { public DiffusionDither() {
this(null);
} }
/** /**
@ -86,8 +88,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or * @throws ImageFilterException if {@code pDestCM} is not {@code null} or
* an instance of {@code IndexColorModel}. * an instance of {@code IndexColorModel}.
*/ */
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
ColorModel pDestCM) {
if (pDestCM == null) { if (pDestCM == null) {
return new BufferedImage(pSource.getWidth(), pSource.getHeight(), return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
BufferedImage.TYPE_BYTE_INDEXED, BufferedImage.TYPE_BYTE_INDEXED,
@ -107,7 +108,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
* Creates a compatible {@code Raster} to dither into. * Creates a compatible {@code Raster} to dither into.
* Only {@code IndexColorModel} allowed. * Only {@code IndexColorModel} allowed.
* *
* @param pSrc * @param pSrc the source raster
* *
* @return a {@code WritableRaster} * @return a {@code WritableRaster}
*/ */
@ -115,14 +116,16 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
return createCompatibleDestRaster(pSrc, getICM(pSrc)); return createCompatibleDestRaster(pSrc, getICM(pSrc));
} }
public final WritableRaster createCompatibleDestRaster(Raster pSrc, /**
IndexColorModel pIndexColorModel) { * Creates a compatible {@code Raster} to dither into.
*
* @param pSrc the source raster.
* @param pIndexColorModel the index color model used to create a {@code Raster}.
*
* @return a {@code WritableRaster}
*/
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight()); return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
/*
return new BufferedImage(pSrc.getWidth(), pSrc.getHeight(),
BufferedImage.TYPE_BYTE_INDEXED,
pIndexColorModel).getRaster();
*/
} }
@ -221,8 +224,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
* @return the destination image, or a new image, if {@code pDest} was * @return the destination image, or a new image, if {@code pDest} was
* {@code null}. * {@code null}.
*/ */
public final BufferedImage filter(BufferedImage pSource, public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
BufferedImage pDest) {
// Create destination image, if none provided // Create destination image, if none provided
if (pDest == null) { if (pDest == null) {
pDest = createCompatibleDestImage(pSource, getICM(pSource)); pDest = createCompatibleDestImage(pSource, getICM(pSource));
@ -265,8 +267,6 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK); return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK);
} }
/** /**
* Performs a single-input/single-output dither operation, applying basic * Performs a single-input/single-output dither operation, applying basic
* Floyd-Steinberg error-diffusion to the image. * Floyd-Steinberg error-diffusion to the image.
@ -278,8 +278,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
* @return the destination raster, or a new raster, if {@code pDest} was * @return the destination raster, or a new raster, if {@code pDest} was
* {@code null}. * {@code null}.
*/ */
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
IndexColorModel pColorModel) {
int width = pSource.getWidth(); int width = pSource.getWidth();
int height = pSource.getHeight(); int height = pSource.getHeight();
@ -460,6 +459,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
forward = !forward; forward = !forward;
} }
} }
return pDest; return pDest;
} }
} }

View File

@ -89,6 +89,8 @@ import java.awt.image.BufferedImage;
import java.awt.image.ColorModel; import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel; import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -127,7 +129,7 @@ import java.util.List;
* @author <A href="mailto:deweese@apache.org">Thomas DeWeese</A> * @author <A href="mailto:deweese@apache.org">Thomas DeWeese</A>
* @author <A href="mailto:jun@oop-reserch.com">Jun Inamori</A> * @author <A href="mailto:jun@oop-reserch.com">Jun Inamori</A>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/IndexImage.java#1 $ * @version $Id: IndexImage.java#1 $
* @see DiffusionDither * @see DiffusionDither
*/ */
class IndexImage { class IndexImage {
@ -237,19 +239,22 @@ class IndexImage {
if (this.val != val) { if (this.val != val) {
return false; return false;
} }
count++; count++;
return true; return true;
} }
} }
/** /**
* Used to define a cube of the colorspace. The cube can be split * Used to define a cube of the color space. The cube can be split
* approximagely in half to generate two cubes. * approximately in half to generate two cubes.
*/ */
private static class Cube { private static class Cube {
int[] min = {0, 0, 0}, max = {255, 255, 255}; int[] min = {0, 0, 0};
int[] max = {255, 255, 255};
boolean done = false; boolean done = false;
List[] colors = null; List<Counter>[] colors = null;
int count = 0; int count = 0;
static final int RED = 0; static final int RED = 0;
static final int GRN = 1; static final int GRN = 1;
@ -261,7 +266,7 @@ class IndexImage {
* @param colors contains the 3D color histogram to be subdivided * @param colors contains the 3D color histogram to be subdivided
* @param count the total number of pixels in the 3D histogram. * @param count the total number of pixels in the 3D histogram.
*/ */
public Cube(List[] colors, int count) { public Cube(List<Counter>[] colors, int count) {
this.colors = colors; this.colors = colors;
this.count = count; this.count = count;
} }
@ -312,20 +317,27 @@ class IndexImage {
c0 = RED; c0 = RED;
c1 = GRN; c1 = GRN;
} }
Cube ret; Cube ret;
ret = splitChannel(splitChannel, c0, c1); ret = splitChannel(splitChannel, c0, c1);
if (ret != null) { if (ret != null) {
return ret; return ret;
} }
ret = splitChannel(c0, splitChannel, c1); ret = splitChannel(c0, splitChannel, c1);
if (ret != null) { if (ret != null) {
return ret; return ret;
} }
ret = splitChannel(c1, splitChannel, c0); ret = splitChannel(c1, splitChannel, c0);
if (ret != null) { if (ret != null) {
return ret; return ret;
} }
done = true; done = true;
return null; return null;
@ -381,16 +393,13 @@ class IndexImage {
for (int k = minIdx[c1]; k <= maxIdx[c1]; k++) { for (int k = minIdx[c1]; k <= maxIdx[c1]; k++) {
int idx = idx2 | (k << c1Sh4); int idx = idx2 | (k << c1Sh4);
List v = colors[idx]; List<Counter> v = colors[idx];
if (v == null) { if (v == null) {
continue; continue;
} }
Iterator itr = v.iterator();
Counter c; for (Counter c : v) {
while (itr.hasNext()) {
c = (Counter) itr.next();
val = c.val; val = c.val;
vals[0] = (val & 0xFF0000) >> 16; vals[0] = (val & 0xFF0000) >> 16;
vals[1] = (val & 0xFF00) >> 8; vals[1] = (val & 0xFF00) >> 8;
@ -425,7 +434,6 @@ class IndexImage {
int c = counts[i]; int c = counts[i];
if (c == 0) { if (c == 0) {
// No counts below this so move up bottom of cube. // No counts below this so move up bottom of cube.
if ((tcount == 0) && (i < max[splitChannel])) { if ((tcount == 0) && (i < max[splitChannel])) {
this.min[splitChannel] = i + 1; this.min[splitChannel] = i + 1;
@ -438,10 +446,8 @@ class IndexImage {
continue; continue;
} }
if ((half - tcount) <= ((tcount + c) - half)) { if ((half - tcount) <= ((tcount + c) - half)) {
// Then lastAdd is a better top idx for this then i. // Then lastAdd is a better top idx for this then i.
if (lastAdd == -1) { if (lastAdd == -1) {
// No lower place to break. // No lower place to break.
if (c == this.count) { if (c == this.count) {
@ -465,13 +471,11 @@ class IndexImage {
else { else {
if (i == this.max[splitChannel]) { if (i == this.max[splitChannel]) {
if (c == this.count) { if (c == this.count) {
// would move min up but that should // would move min up but that should
// have happened already. // have happened already.
return null;// no split to make. return null;// no split to make.
} }
else { else {
// Would like to break between i and i+1 // Would like to break between i and i+1
// but no i+1 so use lastAdd and i; // but no i+1 so use lastAdd and i;
splitLo = lastAdd; splitLo = lastAdd;
@ -503,6 +507,7 @@ class IndexImage {
ret.max[c0] = this.max[c0]; ret.max[c0] = this.max[c0];
ret.min[c1] = this.min[c1]; ret.min[c1] = this.min[c1];
ret.max[c1] = this.max[c1]; ret.max[c1] = this.max[c1];
return ret; return ret;
} }
@ -515,6 +520,7 @@ class IndexImage {
if (this.count == 0) { if (this.count == 0) {
return 0; return 0;
} }
float red = 0, grn = 0, blu = 0; float red = 0, grn = 0, blu = 0;
int minR = min[0], minG = min[1], minB = min[2]; int minR = min[0], minG = min[1], minB = min[2];
int maxR = max[0], maxG = max[1], maxB = max[2]; int maxR = max[0], maxG = max[1], maxB = max[2];
@ -531,20 +537,18 @@ class IndexImage {
for (int k = minIdx[2]; k <= maxIdx[2]; k++) { for (int k = minIdx[2]; k <= maxIdx[2]; k++) {
int idx = idx2 | k; int idx = idx2 | k;
List v = colors[idx]; List<Counter> v = colors[idx];
if (v == null) { if (v == null) {
continue; continue;
} }
Iterator itr = v.iterator();
Counter c;
while (itr.hasNext()) { for (Counter c : v) {
c = (Counter) itr.next();
val = c.val; val = c.val;
ired = (val & 0xFF0000) >> 16; ired = (val & 0xFF0000) >> 16;
igrn = (val & 0x00FF00) >> 8; igrn = (val & 0x00FF00) >> 8;
iblu = (val & 0x0000FF); iblu = (val & 0x0000FF);
if (((ired >= minR) && (ired <= maxR)) && ((igrn >= minG) && (igrn <= maxG)) && ((iblu >= minB) && (iblu <= maxB))) { if (((ired >= minR) && (ired <= maxR)) && ((igrn >= minG) && (igrn <= maxG)) && ((iblu >= minB) && (iblu <= maxB))) {
weight = (c.count / (float) this.count); weight = (c.count / (float) this.count);
red += ((float) ired) * weight; red += ((float) ired) * weight;
@ -579,10 +583,7 @@ class IndexImage {
* This version will be removed in a later version of the API. * This version will be removed in a later version of the API.
*/ */
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) { public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY);
return getIndexColorModel(pImage, pNumberOfColors, pFast
? COLOR_SELECTION_FAST
: COLOR_SELECTION_QUALITY);
} }
/** /**
@ -636,17 +637,12 @@ class IndexImage {
// We now have at least a buffered image, create model from it // We now have at least a buffered image, create model from it
if (icm == null) { if (icm == null) {
icm = createIndexColorModel(ImageUtil.toBuffered(image), pNumberOfColors, pHints); icm = createIndexColorModel(ImageUtil.toBuffered(image), pNumberOfColors, pHints);
}
//System.out.println("IndexColorModel created from colors.");
}
else if (!(icm instanceof InverseColorMapIndexColorModel)) { else if (!(icm instanceof InverseColorMapIndexColorModel)) {
// If possible, use faster code // If possible, use faster code
//System.out.println("Wrappimg IndexColorModel in InverseColorMapIndexColorModel");
icm = new InverseColorMapIndexColorModel(icm); icm = new InverseColorMapIndexColorModel(icm);
} }
//else {
//System.out.println("Allredy InverseColorMapIndexColorModel");
//}
return icm; return icm;
} }
@ -674,7 +670,8 @@ class IndexImage {
int height = pImage.getHeight(); int height = pImage.getHeight();
// Using 4 bits from R, G & B. // Using 4 bits from R, G & B.
List[] colors = new List[1 << 12];// [4096] @SuppressWarnings("unchecked")
List<Counter>[] colors = new List[1 << 12];// [4096]
// Speedup, doesn't decrease image quality much // Speedup, doesn't decrease image quality much
int step = 1; int step = 1;
@ -739,13 +736,16 @@ class IndexImage {
while (numberOfCubes < pNumberOfColors) { while (numberOfCubes < pNumberOfColors) {
while (cubes[fCube].isDone()) { while (cubes[fCube].isDone()) {
fCube++; fCube++;
if (fCube == numberOfCubes) { if (fCube == numberOfCubes) {
break; break;
} }
} }
if (fCube == numberOfCubes) { if (fCube == numberOfCubes) {
break; break;
} }
Cube cube = cubes[fCube]; Cube cube = cubes[fCube];
Cube newCube = cube.split(); Cube newCube = cube.split();
@ -756,6 +756,7 @@ class IndexImage {
cube = newCube; cube = newCube;
newCube = tmp; newCube = tmp;
} }
int j = fCube; int j = fCube;
int count = cube.count; int count = cube.count;
@ -765,17 +766,19 @@ class IndexImage {
} }
cubes[j++] = cubes[i]; cubes[j++] = cubes[i];
} }
cubes[j++] = cube; cubes[j++] = cube;
count = newCube.count; count = newCube.count;
while (j < numberOfCubes) { while (j < numberOfCubes) {
if (cubes[j].count < count) { if (cubes[j].count < count) {
break; break;
} }
j++; j++;
} }
for (int i = numberOfCubes; i > j; i--) {
cubes[i] = cubes[i - 1]; System.arraycopy(cubes, j, cubes, j + 1, numberOfCubes - j);
}
cubes[j/*++*/] = newCube; cubes[j/*++*/] = newCube;
numberOfCubes++; numberOfCubes++;
} }
@ -803,15 +806,13 @@ class IndexImage {
// - transparency added to all totally black colors? // - transparency added to all totally black colors?
int numOfBits = 8; int numOfBits = 8;
// -- haraldK, 20021024, as suggested by Thomas E Deweese // -- haraldK, 20021024, as suggested by Thomas E. Deweese
// plus adding a transparent pixel // plus adding a transparent pixel
IndexColorModel icm; IndexColorModel icm;
if (useTransparency) { if (useTransparency) {
//icm = new IndexColorModel(numOfBits, r.length, r, g, b, r.length - 1);
icm = new InverseColorMapIndexColorModel(numOfBits, r.length, r, g, b, r.length - 1); icm = new InverseColorMapIndexColorModel(numOfBits, r.length, r, g, b, r.length - 1);
} }
else { else {
//icm = new IndexColorModel(numOfBits, r.length, r, g, b);
icm = new InverseColorMapIndexColorModel(numOfBits, r.length, r, g, b); icm = new InverseColorMapIndexColorModel(numOfBits, r.length, r, g, b);
} }
return icm; return icm;
@ -925,7 +926,7 @@ class IndexImage {
* @see IndexColorModel * @see IndexColorModel
*/ */
public static BufferedImage getIndexedImage(BufferedImage pImage, int pNumberOfColors, Color pMatte, int pHints) { public static BufferedImage getIndexedImage(BufferedImage pImage, int pNumberOfColors, Color pMatte, int pHints) {
// NOTE: We need to apply matte before creating colormodel, otherwise we // NOTE: We need to apply matte before creating color model, otherwise we
// won't have colors for potential faded transitions // won't have colors for potential faded transitions
IndexColorModel icm; IndexColorModel icm;
@ -985,15 +986,16 @@ class IndexImage {
final int width = pImage.getWidth(); final int width = pImage.getWidth();
final int height = pImage.getHeight(); final int height = pImage.getHeight();
// Support transparancy? // Support transparency?
boolean transparency = isTransparent(pHints) && (pImage.getColorModel().getTransparency() != Transparency.OPAQUE) && (pColors.getTransparency() != Transparency.OPAQUE); boolean transparency = isTransparent(pHints) && (pImage.getColorModel().getTransparency() != Transparency.OPAQUE) && (pColors.getTransparency() != Transparency.OPAQUE);
// Create image with solid background // Create image with solid background
BufferedImage solid = pImage; BufferedImage solid = pImage;
if (pMatte != null) {// transparency doesn't really matter if (pMatte != null) { // transparency doesn't really matter
solid = createSolid(pImage, pMatte); solid = createSolid(pImage, pMatte);
} }
BufferedImage indexed; BufferedImage indexed;
// Support TYPE_BYTE_BINARY, but only for 2 bit images, as the default // Support TYPE_BYTE_BINARY, but only for 2 bit images, as the default
@ -1044,12 +1046,12 @@ class IndexImage {
finally { finally {
g2d.dispose(); g2d.dispose();
} }
break; break;
} }
// Transparency support, this approach seems lame, but it's the only // Transparency support, this approach seems lame, but it's the only
// solution I've found until now (that actually works). // solution I've found until now (that actually works).
// Got anything to do with isPremultiplied? Hmm...
if (transparency) { if (transparency) {
// Re-apply the alpha-channel of the original image // Re-apply the alpha-channel of the original image
applyAlpha(indexed, pImage); applyAlpha(indexed, pImage);
@ -1183,14 +1185,14 @@ class IndexImage {
* @param pAlpha the image containing the alpha * @param pAlpha the image containing the alpha
*/ */
private static void applyAlpha(BufferedImage pImage, BufferedImage pAlpha) { private static void applyAlpha(BufferedImage pImage, BufferedImage pAlpha) {
// Apply alpha as transparancy, using threshold of 25% // Apply alpha as transparency, using threshold of 25%
for (int y = 0; y < pAlpha.getHeight(); y++) { for (int y = 0; y < pAlpha.getHeight(); y++) {
for (int x = 0; x < pAlpha.getWidth(); x++) { for (int x = 0; x < pAlpha.getWidth(); x++) {
// Get alpha component of pixel, if less than 25% opaque // Get alpha component of pixel, if less than 25% opaque
// (0x40 = 64 => 25% of 256), the pixel will be transparent // (0x40 = 64 => 25% of 256), the pixel will be transparent
if (((pAlpha.getRGB(x, y) >> 24) & 0xFF) < 0x40) { if (((pAlpha.getRGB(x, y) >> 24) & 0xFF) < 0x40) {
pImage.setRGB(x, y, 0x00FFFFFF);// 100% transparent pImage.setRGB(x, y, 0x00FFFFFF); // 100% transparent
} }
} }
} }
@ -1200,7 +1202,6 @@ class IndexImage {
* This class is also a command-line utility. * This class is also a command-line utility.
*/ */
public static void main(String pArgs[]) { public static void main(String pArgs[]) {
// Defaults // Defaults
int argIdx = 0; int argIdx = 0;
int speedTest = -1; int speedTest = -1;
@ -1237,14 +1238,13 @@ class IndexImage {
speedTest = 10; speedTest = 10;
} }
} }
else else if ((pArgs[argIdx].charAt(1) == 'w') || pArgs[argIdx].equals("--overwrite")) {
if ((pArgs[argIdx].charAt(1) == 'w') || pArgs[argIdx].equals("--overwrite")) {
overWrite = true; overWrite = true;
argIdx++; argIdx++;
} }
else else if ((pArgs[argIdx].charAt(1) == 'c') || pArgs[argIdx].equals("--colors")) {
if ((pArgs[argIdx].charAt(1) == 'c') || pArgs[argIdx].equals("--colors")) {
argIdx++; argIdx++;
try { try {
numColors = Integer.parseInt(pArgs[argIdx++]); numColors = Integer.parseInt(pArgs[argIdx++]);
} }
@ -1253,34 +1253,28 @@ class IndexImage {
break; break;
} }
} }
else else if ((pArgs[argIdx].charAt(1) == 'g') || pArgs[argIdx].equals("--grayscale")) {
if ((pArgs[argIdx].charAt(1) == 'g') || pArgs[argIdx].equals("--grayscale")) {
argIdx++; argIdx++;
gray = true; gray = true;
} }
else else if ((pArgs[argIdx].charAt(1) == 'm') || pArgs[argIdx].equals("--monochrome")) {
if ((pArgs[argIdx].charAt(1) == 'm') || pArgs[argIdx].equals("--monochrome")) {
argIdx++; argIdx++;
numColors = 2; numColors = 2;
monochrome = true; monochrome = true;
} }
else else if ((pArgs[argIdx].charAt(1) == 'd') || pArgs[argIdx].equals("--dither")) {
if ((pArgs[argIdx].charAt(1) == 'd') || pArgs[argIdx].equals("--dither")) {
argIdx++; argIdx++;
dither = pArgs[argIdx++]; dither = pArgs[argIdx++];
} }
else else if ((pArgs[argIdx].charAt(1) == 'p') || pArgs[argIdx].equals("--palette")) {
if ((pArgs[argIdx].charAt(1) == 'p') || pArgs[argIdx].equals("--palette")) {
argIdx++; argIdx++;
paletteFileName = pArgs[argIdx++]; paletteFileName = pArgs[argIdx++];
} }
else else if ((pArgs[argIdx].charAt(1) == 'q') || pArgs[argIdx].equals("--quality")) {
if ((pArgs[argIdx].charAt(1) == 'q') || pArgs[argIdx].equals("--quality")) {
argIdx++; argIdx++;
quality = pArgs[argIdx++]; quality = pArgs[argIdx++];
} }
else else if ((pArgs[argIdx].charAt(1) == 'b') || pArgs[argIdx].equals("--bgcolor")) {
if ((pArgs[argIdx].charAt(1) == 'b') || pArgs[argIdx].equals("--bgcolor")) {
argIdx++; argIdx++;
try { try {
background = StringUtil.toColor(pArgs[argIdx++]); background = StringUtil.toColor(pArgs[argIdx++]);
@ -1290,18 +1284,15 @@ class IndexImage {
break; break;
} }
} }
else else if ((pArgs[argIdx].charAt(1) == 't') || pArgs[argIdx].equals("--transparency")) {
if ((pArgs[argIdx].charAt(1) == 't') || pArgs[argIdx].equals("--transparency")) {
argIdx++; argIdx++;
transparency = true; transparency = true;
} }
else else if ((pArgs[argIdx].charAt(1) == 'f') || pArgs[argIdx].equals("--outputformat")) {
if ((pArgs[argIdx].charAt(1) == 'f') || pArgs[argIdx].equals("--outputformat")) {
argIdx++; argIdx++;
format = StringUtil.toLowerCase(pArgs[argIdx++]); format = StringUtil.toLowerCase(pArgs[argIdx++]);
} }
else else if ((pArgs[argIdx].charAt(1) == 'h') || pArgs[argIdx].equals("--help")) {
if ((pArgs[argIdx].charAt(1) == 'h') || pArgs[argIdx].equals("--help")) {
argIdx++; argIdx++;
// Setting errArgs to true, to print usage // Setting errArgs to true, to print usage
@ -1321,6 +1312,7 @@ class IndexImage {
? ", " ? ", "
: "\n")); : "\n"));
} }
System.err.print("Output format names: "); System.err.print("Output format names: ");
String[] writers = ImageIO.getWriterFormatNames(); String[] writers = ImageIO.getWriterFormatNames();
@ -1333,7 +1325,7 @@ class IndexImage {
} }
// Read in image // Read in image
java.io.File in = new java.io.File(pArgs[argIdx++]); File in = new File(pArgs[argIdx++]);
if (!in.exists()) { if (!in.exists()) {
System.err.println("File \"" + in.getAbsolutePath() + "\" does not exist!"); System.err.println("File \"" + in.getAbsolutePath() + "\" does not exist!");
@ -1341,10 +1333,10 @@ class IndexImage {
} }
// Read palette if needed // Read palette if needed
java.io.File paletteFile = null; File paletteFile = null;
if (paletteFileName != null) { if (paletteFileName != null) {
paletteFile = new java.io.File(paletteFileName); paletteFile = new File(paletteFileName);
if (!paletteFile.exists()) { if (!paletteFile.exists()) {
System.err.println("File \"" + in.getAbsolutePath() + "\" does not exist!"); System.err.println("File \"" + in.getAbsolutePath() + "\" does not exist!");
System.exit(5); System.exit(5);
@ -1352,10 +1344,10 @@ class IndexImage {
} }
// Make sure we can write // Make sure we can write
java.io.File out; File out;
if (argIdx < pArgs.length) { if (argIdx < pArgs.length) {
out = new java.io.File(pArgs[argIdx/*++*/]); out = new File(pArgs[argIdx/*++*/]);
// Get format from file extension // Get format from file extension
if (format == null) { if (format == null) {
@ -1363,7 +1355,6 @@ class IndexImage {
} }
} }
else { else {
// Create new file in current dir, same name + format extension // Create new file in current dir, same name + format extension
String baseName = FileUtil.getBasename(in); String baseName = FileUtil.getBasename(in);
@ -1371,8 +1362,9 @@ class IndexImage {
if (format == null) { if (format == null) {
format = "png"; format = "png";
} }
out = new java.io.File(baseName + '.' + format); out = new File(baseName + '.' + format);
} }
if (!overWrite && out.exists()) { if (!overWrite && out.exists()) {
System.err.println("The file \"" + out.getAbsolutePath() + "\" allready exists!"); System.err.println("The file \"" + out.getAbsolutePath() + "\" allready exists!");
System.exit(5); System.exit(5);
@ -1396,7 +1388,7 @@ class IndexImage {
} }
} }
} }
catch (java.io.IOException ioe) { catch (IOException ioe) {
ioe.printStackTrace(System.err); ioe.printStackTrace(System.err);
System.exit(5); System.exit(5);
} }
@ -1441,16 +1433,14 @@ class IndexImage {
/////////////////////////////// ///////////////////////////////
// Index // Index
long start = 0; long start = 0;
long end;
if (speedTest > 0) { if (speedTest > 0) {
// SPEED TESTING // SPEED TESTING
System.out.println("Measuring speed!"); System.out.println("Measuring speed!");
start = System.currentTimeMillis(); start = System.currentTimeMillis();
// END SPEED TESTING // END SPEED TESTING
} }
BufferedImage indexed; BufferedImage indexed;
IndexColorModel colors; IndexColorModel colors;
@ -1459,7 +1449,6 @@ class IndexImage {
colors = MonochromeColorModel.getInstance(); colors = MonochromeColorModel.getInstance();
} }
else if (gray) { else if (gray) {
//indexed = ImageUtil.toBuffered(ImageUtil.grayscale(image), BufferedImage.TYPE_BYTE_GRAY); //indexed = ImageUtil.toBuffered(ImageUtil.grayscale(image), BufferedImage.TYPE_BYTE_GRAY);
image = ImageUtil.toBuffered(ImageUtil.grayscale(image)); image = ImageUtil.toBuffered(ImageUtil.grayscale(image));
indexed = getIndexedImage(image, colors = getIndexColorModel(image, numColors, hints), background, hints); indexed = getIndexedImage(image, colors = getIndexColorModel(image, numColors, hints), background, hints);
@ -1470,7 +1459,6 @@ class IndexImage {
} }
} }
else if (paletteImg != null) { else if (paletteImg != null) {
// Get palette from image // Get palette from image
indexed = getIndexedImage(ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB), indexed = getIndexedImage(ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB),
colors = getIndexColorModel(paletteImg, numColors, hints), background, hints); colors = getIndexColorModel(paletteImg, numColors, hints), background, hints);
@ -1479,12 +1467,10 @@ class IndexImage {
image = ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB); image = ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB);
indexed = getIndexedImage(image, colors = getIndexColorModel(image, numColors, hints), background, hints); indexed = getIndexedImage(image, colors = getIndexColorModel(image, numColors, hints), background, hints);
} }
if (speedTest > 0) { if (speedTest > 0) {
// SPEED TESTING // SPEED TESTING
end = System.currentTimeMillis(); System.out.println("Color selection + dither: " + (System.currentTimeMillis() - start) + " ms");
System.out.println("Color selection + dither: " + (end - start) + " ms");
// END SPEED TESTING // END SPEED TESTING
} }
@ -1494,11 +1480,11 @@ class IndexImage {
System.err.println("No writer for format: \"" + format + "\"!"); System.err.println("No writer for format: \"" + format + "\"!");
} }
} }
catch (java.io.IOException ioe) { catch (IOException ioe) {
ioe.printStackTrace(System.err); ioe.printStackTrace(System.err);
} }
if (speedTest > 0) {
if (speedTest > 0) {
// SPEED TESTING // SPEED TESTING
System.out.println("Measuring speed!"); System.out.println("Measuring speed!");
@ -1513,17 +1499,16 @@ class IndexImage {
for (int i = 0; i < speedTest; i++) { for (int i = 0; i < speedTest; i++) {
start = System.currentTimeMillis(); start = System.currentTimeMillis();
getIndexedImage(image, colors, background, hints); getIndexedImage(image, colors, background, hints);
end = System.currentTimeMillis(); time += (System.currentTimeMillis() - start);
time += (end - start);
System.out.print('.'); System.out.print('.');
if ((i + 1) % 10 == 0) { if ((i + 1) % 10 == 0) {
System.out.println("\nAverage (after " + (i + 1) + " iterations): " + (time / (i + 1)) + "ms"); System.out.println("\nAverage (after " + (i + 1) + " iterations): " + (time / (i + 1)) + "ms");
} }
} }
System.out.println("\nDither only:"); System.out.println("\nDither only:");
System.out.println("Total time (" + speedTest + " invocations): " + time + "ms"); System.out.println("Total time (" + speedTest + " invocations): " + time + "ms");
System.out.println("Average: " + time / speedTest + "ms"); System.out.println("Average: " + time / speedTest + "ms");
// END SPEED TESTING // END SPEED TESTING
} }
} }

View File

@ -39,7 +39,7 @@ import java.io.ByteArrayInputStream;
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FastByteArrayOutputStream.java#2 $ * @version $Id: FastByteArrayOutputStream.java#2 $
*/ */
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block // TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
public final class FastByteArrayOutputStream extends ByteArrayOutputStream { public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
@ -78,23 +78,24 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
else if (pLength == 0) { else if (pLength == 0) {
return; return;
} }
int newcount = count + pLength;
growIfNeeded(newcount); int newCount = count + pLength;
growIfNeeded(newCount);
System.arraycopy(pBytes, pOffset, buf, count, pLength); System.arraycopy(pBytes, pOffset, buf, count, pLength);
count = newcount; count = newCount;
} }
@Override @Override
public synchronized void write(int pByte) { public synchronized void write(int pByte) {
int newcount = count + 1; int newCount = count + 1;
growIfNeeded(newcount); growIfNeeded(newCount);
buf[count] = (byte) pByte; buf[count] = (byte) pByte;
count = newcount; count = newCount;
} }
private void growIfNeeded(int pNewcount) { private void growIfNeeded(int pNewCount) {
if (pNewcount > buf.length) { if (pNewCount > buf.length) {
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewcount); int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
byte newBuf[] = new byte[newSize]; byte newBuf[] = new byte[newSize];
System.arraycopy(buf, 0, newBuf, 0, count); System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf; buf = newBuf;
@ -110,9 +111,10 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
// Non-synchronized version of toByteArray // Non-synchronized version of toByteArray
@Override @Override
public byte[] toByteArray() { public byte[] toByteArray() {
byte newbuf[] = new byte[count]; byte newBuf[] = new byte[count];
System.arraycopy(buf, 0, newbuf, 0, count); System.arraycopy(buf, 0, newBuf, 0, count);
return newbuf;
return newBuf;
} }
/** /**

View File

@ -38,7 +38,7 @@ import java.io.InputStreamReader;
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileSystem.java#1 $ * @version $Id: FileSystem.java#1 $
*/ */
abstract class FileSystem { abstract class FileSystem {
abstract long getFreeSpace(File pPath); abstract long getFreeSpace(File pPath);
@ -57,21 +57,21 @@ abstract class FileSystem {
//System.out.println("os = " + os); //System.out.println("os = " + os);
os = os.toLowerCase(); os = os.toLowerCase();
if (os.indexOf("windows") != -1) { if (os.contains("windows")) {
return new Win32FileSystem(); return new Win32FileSystem();
} }
else if (os.indexOf("linux") != -1 || else if (os.contains("linux") ||
os.indexOf("sun os") != -1 || os.contains("sun os") ||
os.indexOf("sunos") != -1 || os.contains("sunos") ||
os.indexOf("solaris") != -1 || os.contains("solaris") ||
os.indexOf("mpe/ix") != -1 || os.contains("mpe/ix") ||
os.indexOf("hp-ux") != -1 || os.contains("hp-ux") ||
os.indexOf("aix") != -1 || os.contains("aix") ||
os.indexOf("freebsd") != -1 || os.contains("freebsd") ||
os.indexOf("irix") != -1 || os.contains("irix") ||
os.indexOf("digital unix") != -1 || os.contains("digital unix") ||
os.indexOf("unix") != -1 || os.contains("unix") ||
os.indexOf("mac os x") != -1) { os.contains("mac os x")) {
return new UnixFileSystem(); return new UnixFileSystem();
} }
else { else {

View File

@ -186,6 +186,7 @@ public final class FileUtil {
if (!pOverWrite && pToFile.exists()) { if (!pOverWrite && pToFile.exists()) {
return false; return false;
} }
InputStream in = null; InputStream in = null;
OutputStream out = null; OutputStream out = null;
@ -202,6 +203,7 @@ public final class FileUtil {
close(in); close(in);
close(out); close(out);
} }
return true; // If we got here, everything's probably okay.. ;-) return true; // If we got here, everything's probably okay.. ;-)
} }
@ -307,6 +309,8 @@ public final class FileUtil {
Validate.notNull(pFrom, "from"); Validate.notNull(pFrom, "from");
Validate.notNull(pTo, "to"); Validate.notNull(pTo, "to");
// TODO: Consider using file channels for faster copy where possible
// Use buffer size two times byte array, to avoid i/o bottleneck // Use buffer size two times byte array, to avoid i/o bottleneck
// TODO: Consider letting the client decide as this is sometimes not a good thing! // TODO: Consider letting the client decide as this is sometimes not a good thing!
InputStream in = new BufferedInputStream(pFrom, BUF_SIZE * 2); InputStream in = new BufferedInputStream(pFrom, BUF_SIZE * 2);
@ -322,31 +326,9 @@ public final class FileUtil {
// Flush out stream, to write any remaining buffered data // Flush out stream, to write any remaining buffered data
out.flush(); out.flush();
return true; // If we got here, everything's probably okay.. ;-) return true; // If we got here, everything is probably okay.. ;-)
} }
/*
// Consider using the example from
// http://developer.java.sun.com/developer/Books/performance/ch04.pdf
// Test if this is really faster. And what about a lot of concurrence?
// Have a pool of buffers? :-)
static final int BUFF_SIZE = 100000;
static final byte[] buffer = new byte[BUFF_SIZE];
public static void copy(InputStream in, OutputStream out) throws IOException {
while (true) {
synchronized (buffer) {
int amountRead = in.read(buffer);
if (amountRead == -1) {
break;
}
out.write(buffer, 0, amountRead);
}
}
}
*/
/** /**
* Gets the file (type) extension of the given file. * Gets the file (type) extension of the given file.
* A file extension is the part of the filename, after the last occurence * A file extension is the part of the filename, after the last occurence