java-imaging-utilities-0.14.2+3.orig/0000775000000000000000000000000004547516600014135 5ustar java-imaging-utilities-0.14.2+3.orig/net/0000775000000000000000000000000010546531760014723 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/0000775000000000000000000000000010546532046017244 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/0000775000000000000000000000000010546532052020030 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/0000775000000000000000000000000010546532076021154 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/io/0000775000000000000000000000000010546532076021563 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/io/MatrixSerialization.java0000664000000000000000000000261710324333312026420 0ustar /* * MatrixSerialization * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.color.io; import java.io.PrintStream; import net.sourceforge.jiu.color.data.CoOccurrenceMatrix; import net.sourceforge.jiu.color.data.CoOccurrenceFrequencyMatrix; /** * Write co-occurrence and co-occurrence frequency matrices to text files. * * @author Marco Schmidt * @since 0.6.0 */ public class MatrixSerialization { private MatrixSerialization() { } public static void save(CoOccurrenceMatrix matrix, PrintStream out) { if (matrix == null || out == null) { return; } int dim = matrix.getDimension(); out.println(Integer.toString(dim)); for (int i = 0; i < dim; i++) { StringBuffer sb = new StringBuffer(); for (int j = 0; j < dim; j++) { sb.append(matrix.getValue(i, j)); sb.append(' '); } out.println(sb.toString()); } } public static void save(CoOccurrenceFrequencyMatrix matrix, PrintStream out) { if (matrix == null || out == null) { return; } int dim = matrix.getDimension(); out.println(Integer.toString(dim)); for (int i = 0; i < dim; i++) { StringBuffer sb = new StringBuffer(); for (int j = 0; j < dim; j++) { sb.append(matrix.getValue(i, j)); sb.append(' '); } out.println(sb.toString()); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/io/package.html0000664000000000000000000000035007741250132024033 0ustar

Classes to read and write color-related data from and to files. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/io/HistogramSerialization.java0000664000000000000000000000663410324333256027123 0ustar /* * HistogramSerialization * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.color.io; import java.io.PrintStream; import net.sourceforge.jiu.color.data.Histogram1D; import net.sourceforge.jiu.color.data.Histogram3D; /** * This class has static methods for saving histograms. * Text files (actually, any PrintStream, so you could write to standard output * using {@link java.lang.System#out}) are used to store the histogram information. * Hint: When creating a {@link java.io.PrintStream} object yourself, set the autoFlush * argument of the constructor to false. * You should also wrap your {@link java.io.OutputStream} object into a {@link java.io.BufferedOutputStream} object. * That may speed things up. *

* A simple format is used for storing the histograms. * The first line holds the number of components. * This would be 3 for a three-dimensional histogram, e.g.for RGB color images, * or 1 for a one-dimensional histogram as used for a grayscale image. *

* Next, as many lines as dimensions follow. * Each line holds the maximum value allowed for that component. * The minimum value is always zero. * Typically, the maximum values are all the same, e.g. 255 for each * component of a 24 bit RGB truecolor image. *

* Following these header lines is the actual histogram. * Each line holds a non-zero counter value for one pixel. * The counter is always the last integer value in the line. *

* Example: *

 * 34 0 55 4033
 * 
* For the histogram of an RGB24Image, this would mean that the pixel * red=34, green=0, blue=55 occurs 4033 times. *
 * 0 2
 * 
* For the histogram of any one channel image, this means that the value 0 occurs twice. */ public class HistogramSerialization { private HistogramSerialization() { } /** * Saves a one-dimensional histogram to a text output stream. * * @param hist the histogram that will be written to a stream * @param out the stream that will be written to */ public static void save(Histogram1D hist, PrintStream out) { if (hist == null || out == null) { return; } int max = hist.getMaxValue(); out.println("1"); out.println(max); for (int i = 0; i <= max; i++) { int counter = hist.getEntry(i); if (counter != 0) { out.print(i); out.print(' '); out.println(counter); } } out.flush(); } /** * Saves a three-dimensional histogram to a text output stream. * * @param hist the histogram to be saved * @param out the output stream where the histogram will be saved to */ public static void save(Histogram3D hist, PrintStream out) { if (hist == null || out == null) { return; } int max1 = hist.getMaxValue(0); int max2 = hist.getMaxValue(1); int max3 = hist.getMaxValue(2); out.println("3"); out.println(max1); out.println(max2); out.println(max3); for (int i = 0; i <= max1; i++) { for (int j = 0; j <= max2; j++) { for (int k = 0; k <= max3; k++) { int counter = hist.getEntry(i, j, k); if (counter != 0) { out.print(i); out.print(' '); out.print(j); out.print(' '); out.print(k); out.print(' '); out.println(counter); } } } } out.flush(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/io/PaletteSerialization.java0000664000000000000000000000672407741250132026564 0ustar /* * PaletteSerialization * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.io; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.Vector; import net.sourceforge.jiu.codecs.ImageLoader; import net.sourceforge.jiu.codecs.PNMCodec; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.OperationFailedException; /** * This class loads and saves palettes. * Loading is done using the {@link ImageLoader} class - an image * is loaded which is supposed to have no more than 256 pixels, the palette entries. * When saving, the {@link PNMCodec} is used to store palettes as .ppm files. * * @author Marco Schmidt * @since 0.5.0 */ public class PaletteSerialization implements RGBIndex { private PaletteSerialization() { } /** * Create a palette from the pixels of the argument image. */ public static Palette convertImageToPalette(RGB24Image image) { if (image == null) { return null; } int numPixels = image.getWidth() * image.getHeight(); if (numPixels > 256) { // too many pixels return null; } Palette result = new Palette(numPixels, 255); int index = 0; for (int y = 0; y < image.getHeight(); y++) { for (int x = 0; x < image.getWidth(); x++) { result.put(index++, image.getSample(INDEX_RED, x, y), image.getSample(INDEX_GREEN, x, y), image.getSample(INDEX_BLUE, x, y)); } } return result; } /** * Creates an RGB24Image from the palette entries, each entry * becomes a pixel in an image of width 1 and height * palette.getNumEntries(). */ public static RGB24Image convertPaletteToImage(Palette palette) { RGB24Image result = new MemoryRGB24Image(1, palette.getNumEntries()); for (int index = 0; index < palette.getNumEntries(); index++) { result.putSample(INDEX_RED, 0, index, palette.getSample(INDEX_RED, index)); result.putSample(INDEX_GREEN, 0, index, palette.getSample(INDEX_GREEN, index)); result.putSample(INDEX_BLUE, 0, index, palette.getSample(INDEX_BLUE, index)); } return result; } /** * Loads a palette from the argument file. * Uses {@link net.sourceforge.jiu.codecs.ImageLoader} to load an * image from the argument file, then calls {@link #convertImageToPalette} * and returns the palette created that way. */ public static Palette load(File paletteFile) { PixelImage image; try { image = ImageLoader.load(paletteFile, (Vector)null); } catch (Exception e) { return null; } if (!(image instanceof RGB24Image)) { return null; } return convertImageToPalette((RGB24Image)image); } /** * Saves the palette to the given file as a PPM image file. * Uses {@link net.sourceforge.jiu.codecs.PNMCodec}. */ public static void save(Palette palette, File paletteFile) throws IOException { RGB24Image image = convertPaletteToImage(palette); PNMCodec codec = new PNMCodec(); codec.setOutputStream(new FileOutputStream(paletteFile)); codec.setAscii(true); codec.setImage(image); try { codec.process(); } catch(OperationFailedException ofe) { throw new IOException("I/O error: " + ofe.toString()); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/0000775000000000000000000000000010546532076023702 5ustar ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/PopularityQuantizer.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/PopularityQuantizer.java0000664000000000000000000001320610421160643030607 0ustar /* * PopularityQuantizer * * Copyright (c) 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.color.analysis.Histogram3DCreator; import net.sourceforge.jiu.color.data.Histogram3D; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Performs the popularity color quantization algorithm that maps an image to * the colors occurring most frequently in the input image. * The number of colors in the palette can be defined by the user of this * operation with {@link #setPaletteSize(int)}. *

Supported image types

* The input image must implement {@link net.sourceforge.jiu.data.RGB24Image}, * the output image must be of type {@link net.sourceforge.jiu.data.Paletted8Image}. *

Usage example

* The following code snippet uses the default settings with a palette of 256 entries. *
 * PopularityQuantizer quantizer = new PopularityQuantizer();
 * quantizer.setInputImage(image);
 * quantizer.setPaletteSize(256);
 * quantizer.process();
 * PixelImage quantizedImage = quantizer.getOutputImage();
 * 
* @author Marco Schmidt * @since 0.11.0 * @see ArbitraryPaletteQuantizer */ public class PopularityQuantizer extends ImageToImageOperation implements RGBIndex, RGBQuantizer { private ArbitraryPaletteQuantizer arbQuantizer; private int paletteSize; private Palette palette; private boolean doNotMap; public Palette createPalette() { if (palette == null) { try { palette = determinePalette(); return (Palette)palette.clone(); } catch (OperationFailedException ofe) { return null; } } else { return (Palette)palette.clone(); } } private Palette determinePalette() throws OperationFailedException { Histogram3DCreator hc = new Histogram3DCreator(); hc.setImage((IntegerImage)getInputImage(), RGBIndex.INDEX_RED, RGBIndex.INDEX_GREEN, RGBIndex.INDEX_BLUE); hc.process(); Histogram3D hist = hc.getHistogram(); if (hist == null) { throw new OperationFailedException("Could not create histogram from input image."); } int numUniqueColors = hist.getNumUsedEntries(); if (numUniqueColors <= paletteSize) { paletteSize = numUniqueColors; } RGBColorList list = new RGBColorList(hist); list.sortByCounter(0, list.getNumEntries() - 1); Palette result = new Palette(paletteSize); int paletteIndex = paletteSize - 1; int listIndex = list.getNumEntries() - 1; while (paletteIndex >= 0) { RGBColor color = list.getColor(listIndex--); result.put(paletteIndex--, color.getSample(RGBIndex.INDEX_RED), color.getSample(RGBIndex.INDEX_GREEN), color.getSample(RGBIndex.INDEX_BLUE) ); } return result; } /** * Returns the number of colors in the destination image. * If output is paletted, this is also the number of entries * in the palette. * @return number of colors in the destination * @see #setPaletteSize(int) */ public int getPaletteSize() { return paletteSize; } public int map(int[] origRgb, int[] quantizedRgb) { return arbQuantizer.map(origRgb, quantizedRgb); } public void process() throws MissingParameterException, OperationFailedException, WrongParameterException { ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); if (!(in instanceof RGB24Image)) { throw new WrongParameterException("Input image must implement RGB24Image."); } Histogram3DCreator hc = new Histogram3DCreator(); hc.setImage((IntegerImage)in, RGBIndex.INDEX_RED, RGBIndex.INDEX_GREEN, RGBIndex.INDEX_BLUE); hc.process(); Histogram3D hist = hc.getHistogram(); if (hist == null) { throw new OperationFailedException("Could not create histogram from input image."); } int numUniqueColors = hist.getNumUsedEntries(); if (numUniqueColors <= paletteSize) { paletteSize = numUniqueColors; } arbQuantizer = new ArbitraryPaletteQuantizer(createPalette()); if (!doNotMap) { arbQuantizer.setInputImage(in); arbQuantizer.setOutputImage(getOutputImage()); arbQuantizer.process(); // TODO: copy ProgressListeners to arbQuantizer setOutputImage(arbQuantizer.getOutputImage()); } } /** * Specifies whether this operation will map the image to the * new palette (true) or not (false). * The latter may be interesting if only the palette is required. * By default, this operation does map. * @param newValue map to new image (true) or just search palette (false) */ public void setMapping(boolean newValue) { doNotMap = !newValue; } /** * Sets the number of colors that this operations is supposed to reduce * the original image to. * @param newPaletteSize the number of colors * @throws IllegalArgumentException if the argument is smaller than 1 or larger than 256 * @see #getPaletteSize */ public void setPaletteSize(int newPaletteSize) { if (newPaletteSize < 1) { throw new IllegalArgumentException("Palette size must be 1 or larger."); } if (newPaletteSize > 256) { throw new IllegalArgumentException("Palette size must be at most 256."); } paletteSize = newPaletteSize; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/RGBColor.java0000664000000000000000000000554207741250132026155 0ustar /* * RGBColor * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.data.RGBIndex; /** * Encapsulates a single color from RGB (red, green, blue) color space plus a frequency counter. * Each of the three RGB samples is of type int. * Also stores a counter of type int. * @author Marco Schmidt */ public class RGBColor implements RGBIndex { /** The intensity values that make up the color. */ private int[] samples; /** Stores how many times this colors appears in a certain image. */ private int counter; /** * Creates an instance of this class and initializes it to the given * intensity values. * The internal color counter is set to zero. */ public RGBColor(int red, int green, int blue) { this(red, green, blue, 0); } /** * Creates an instance of this class and initializes it to the given * intensity values. * Also sets the internal color counter to the given parameter. */ public RGBColor(int red, int green, int blue, int counter) { samples = new int[3]; samples[INDEX_RED] = red; samples[INDEX_GREEN] = green; samples[INDEX_BLUE] = blue; this.counter = counter; } /** * Compares this color to the argument color, using the sortOrder argument (which is one of the * three index values defined in {@link RGBIndex}. * That way, the two sample values for one component (e.g. red if sortOrder == INDEX_RED) are * compared. * * @param c the color to which this color is compared * @param sortOrder the component used for the comparison * @return relation between this color and the argument color */ public int compareTo(RGBColor c, int sortOrder) { int s1 = samples[sortOrder]; int s2 = c.samples[sortOrder]; if (s1 < s2) { return -1; } else if (s1 == s2) { return 0; } else { return 1; } } /** * For two RGB triplets (r1, g1, b1) and (r2, g2, b2) this will return * the distance between those colors in RGB color space. */ public static double computeDistance(int r1, int g1, int b1, int r2, int g2, int b2) { int r = r1 - r2; int g = g1 - g2; int b = b1 - b2; return Math.sqrt(r * r + g * g + b * b); } /** * Compares this color with another instance of RGBColor and returns true * if all intensity values are equal, false otherwise. */ public boolean equals(Object obj) { RGBColor c = (RGBColor)obj; return (samples[0] == c.samples[0] && samples[1] == c.samples[1] && samples[2] == c.samples[2]); } public int getCounter() { return counter; } public int getSample(int index) { return samples[index]; } public String toString() { return "(" + samples[INDEX_RED] + ", " + samples[INDEX_GREEN] + ", " + samples[INDEX_BLUE] + ")"; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/MedianCutContourRemoval.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/MedianCutContourRemoval.0000664000000000000000000004634007741250132030454 0ustar /* * MedianCutContourRemoval * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import java.util.Hashtable; import java.util.Vector; import net.sourceforge.jiu.codecs.BMPCodec; import net.sourceforge.jiu.codecs.CodecMode; import net.sourceforge.jiu.codecs.ImageLoader; import net.sourceforge.jiu.color.analysis.MatrixCreator; import net.sourceforge.jiu.color.analysis.MeanDifference; import net.sourceforge.jiu.color.data.CoOccurrenceFrequencyMatrix; import net.sourceforge.jiu.color.data.CoOccurrenceMatrix; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; import net.sourceforge.jiu.util.ComparatorInterface; import net.sourceforge.jiu.util.Sort; import net.sourceforge.jiu.util.Statistics; /** * A data structure for storing the index values of a pair of * contouring colors plus their respective self co-occurrence * frequency values. * @author Marco Schmidt * @see MedianCutContourRemoval */ class ContouringColorPair implements ComparatorInterface { private int index1; private int index2; private double scof1; private double scof2; /** * Creates a new object of this class. */ public ContouringColorPair() { } /** * Creates a new object of this class. * @param i1 palette index of first color * @param i2 palette index of second color * @param sf1 self co-occurrence frequency value of first color * @param sf2 self co-occurrence frequency value of second color */ public ContouringColorPair(int i1, int i2, double sf1, double sf2) { index1 = i1; index2 = i2; scof1 = sf1; scof2 = sf2; } public int compare(Object o1, Object o2) { ContouringColorPair p1 = (ContouringColorPair)o1; ContouringColorPair p2 = (ContouringColorPair)o2; double sum1 = p1.scof1 + p1.scof2; double sum2 = p2.scof1 + p2.scof2; if (sum1 < sum2) { return -1; } else if (sum1 > sum2) { return 1; } else { return 0; } } public int getColorIndex(boolean smaller) { if (smaller) { return scof1 < scof2 ? index1 : index2; } else { return scof1 < scof2 ? index2 : index1; } } } /** * Performs the Median Cut color quantization algorithm in combination with * a contour removal algorithm. *

* Quantization is an operation that reduces the number of colors in * an image while trying to remain as close to the original image * as possible. * Standard Median Cut quantization is implemented in the * {@link net.sourceforge.jiu.color.quantization.MedianCutQuantizer} * class. *

* This class implements an algorithm that improves the standard * implementation. * It repeatedly calls the original quantizer and adjusts the palette * in order to reduce the amount of contouring errors. *

Image types

* This operation requires an {@link net.sourceforge.jiu.data.RGB24Image} * object as input and produces a {@link net.sourceforge.jiu.data.Paletted8Image} * as output. *

Usage example

*
 * RGB24Image inputImage = ...; // image to be processed, from a file etc. 
 * MedianCutQuantizer quantizer = new MedianCutQuantizer();
 * quantizer.setPaletteSize(256);
 * MedianCutContourRemoval removal = new MedianCutContourRemoval();
 * removal.setQuantizer(quantizer);
 * removal.setInputImage(inputImage);
 * removal.setTau(11.0);
 * removal.setNumPasses(3);
 * removal.process();
 * PixelImage outputImage = removal.getOutputImage();
 * 
*

Rationale - why an extension to Median Cut?

* Quantization without dithering can lead to contouring (banding) in the output image. * The contours introduced that way are not only ugly but they may lead to erroneous * results when processing that quantized image. * Dithering, an alternative group of algorithms used in combination with quantizers * to improve output quality, leads to output which is more pleasant to the human eye. * However, it introduces noise that may not be acceptable when the output image * is to be further processed by image processing algorithms. * Instead, this algorithm attempts to adjust the palette found by the Median * Cut algorithm. * The adjustments aim at reducing the amount of contouring caused by a * palette found in a previous Median Cut operation. *

How the contour removal algorithm works

* * For an in-depth description of the algorithm see the journal article mentioned in the * Credits section below. *

Credits

* The algorithm was developed by * Jefferey * Shufelt and described in * his article * Texture Analysis for Enhanced Color Image Quantization. * CVGIP: Graphical Model and Image Processing 59(3): 149-163 (1997). * @see MedianCutQuantizer * @author Marco Schmidt */ public class MedianCutContourRemoval extends ImageToImageOperation { /** * The default tau value, used if none is specified * with {@link #setTau(double)}. * Check the class documentation to find out more about * the meaning of tau: {@link MedianCutContourRemoval}. */ public static final double DEFAULT_TAU = 12; /** * The default number of passes, used if they are not specified * with {@link #setNumPasses(int)}. * Check the class documentation to find out more about * the meaning of that number of passes: {@link MedianCutContourRemoval}. */ public static final int DEFAULT_NUM_PASSES = 8; private Vector compressibleNodes; private Vector contouringPairs; private MedianCutNode[] leaves; private double[] meanC; private double meanS; private int numPasses = DEFAULT_NUM_PASSES; private Palette palette; private MedianCutQuantizer quantizer; private double stdDevS; private double[] stdDevC; private double sumMeanStdDevS; private double tau = DEFAULT_TAU; private double computeDistance(int index1, int index2) { return RGBColor.computeDistance( palette.getSample(RGBIndex.INDEX_RED, index1), palette.getSample(RGBIndex.INDEX_GREEN, index1), palette.getSample(RGBIndex.INDEX_BLUE, index1), palette.getSample(RGBIndex.INDEX_RED, index2), palette.getSample(RGBIndex.INDEX_GREEN, index2), palette.getSample(RGBIndex.INDEX_BLUE, index2) ); } /** * Computes the mean and standard deviation (stddev) values and * from the argument matrix and initializes the mean / stddev * fields of this class with them. * @param matrix */ private void computeStatistics(CoOccurrenceFrequencyMatrix matrix) { final int N = quantizer.getPaletteSize(); double values[] = new double[N]; // compute mean of self co-occurrence frequencies for (int i = 0; i < N; i++) { values[i] = matrix.getValue(i, i); } meanS = Statistics.computeMean(values); // compute mean of self co-occurrence frequencies stdDevS = Statistics.computeStandardDeviation(values, meanS); sumMeanStdDevS = meanS + stdDevS; // compute mean and standard deviation of co-occurrence frequencies meanC = new double[N]; stdDevC = new double[N]; for (int j = 0; j < N; j++) { for (int i = 0; i < N; i++) { values[i] = matrix.getValue(i, j); } meanC[j] = Statistics.computeMean(values); stdDevC[j] = Statistics.computeStandardDeviation(values, meanC[j]); } } /** * Takes * @return */ private Vector createContouringIndexList() { Hashtable table = new Hashtable(contouringPairs.size() * 2); Vector indexes = new Vector(); Object[] contouringPairArray = toArray(contouringPairs); Sort.sort(contouringPairArray, new ContouringColorPair()); for (int i = contouringPairArray.length - 1; i >= 0; i--) { ContouringColorPair pair = (ContouringColorPair)contouringPairArray[i]; Integer index = new Integer(pair.getColorIndex(false)); if (table.get(index) == null) { table.put(index, index); indexes.addElement(index); } index = new Integer(pair.getColorIndex(true)); if (table.get(index) == null) { table.put(index, index); indexes.addElement(index); } } return indexes; } private void findColorPairs(CoOccurrenceFrequencyMatrix matrix, final CoOccurrenceMatrix A) { compressibleNodes = new Vector(); contouringPairs = new Vector(); final int N = quantizer.getPaletteSize(); for (int i = 0; i < N; i++) { final double SI = matrix.getValue(i); for (int j = i + 1; j < N; j++) { final double SJ = matrix.getValue(j); if (SI > sumMeanStdDevS && SJ > sumMeanStdDevS) { // potential contouring pair if (matrix.getValue(i, j) > meanC[j] + stdDevC[j] && matrix.getValue(j, i) > meanC[i] + stdDevC[i] && computeDistance(i, j) <= tau) { contouringPairs.addElement(new ContouringColorPair(i, j, SI, SJ)); } } else if (SI < meanS && SJ < meanS) { MedianCutNode parentI = leaves[i].getParentNode(); MedianCutNode parentJ = leaves[j].getParentNode(); // potential compressible pair if (parentI == parentJ && A.getValue(i, j) == 0 && parentI.getNumColors() > 1) { System.out.println("compressible: " + i + "/" + j); compressibleNodes.addElement(parentI); } } } } } /** * Small command line application that performs a contour removal * on an image. * The first and only argument must be the name of image file from * which the image to be quantized is loaded. * @param args program arguments; must have length one, the only argument being the input image file name * @throws Exception */ public static void main(String[] args) throws Exception { PixelImage inputImage = ImageLoader.load(args[0]); if (inputImage == null) { System.err.println("Could not load image from " + args[0]); return; } MedianCutQuantizer quantizer = new MedianCutQuantizer(); quantizer.setPaletteSize(256); MedianCutContourRemoval removal = new MedianCutContourRemoval(); removal.setQuantizer(quantizer); removal.setInputImage(inputImage); removal.process(); BMPCodec codec = new BMPCodec(); codec.setImage(removal.getOutputImage()); codec.setFile(args[1], CodecMode.SAVE); codec.process(); codec.close(); MeanDifference diff = new MeanDifference(); diff.setImages(inputImage, removal.getOutputImage()); diff.process(); System.out.println("Mean difference: " + diff.getDifference()); } private void mergeAndSplit() { Vector contouringIndexes = createContouringIndexList(); final int ITERATIONS = Math.min(contouringIndexes.size(), compressibleNodes.size()); int index = 0; do { // make the node a leaf by setting its two successors (which are leaves) to null MedianCutNode compressibleNode = (MedianCutNode)compressibleNodes.elementAt(index); compressibleNode.setSuccessors(null, null); // split the contouring color into two Integer contouringIndex = (Integer)contouringIndexes.elementAt(index); MedianCutNode contouringNode = leaves[contouringIndex.intValue()]; quantizer.splitNode(contouringNode); index++; } while (index < ITERATIONS); } public void process() throws MissingParameterException, OperationFailedException, WrongParameterException { if (quantizer == null) { throw new MissingParameterException("No MedianCutQuantizer object was specified."); } ensureInputImageIsAvailable(); PixelImage pixelImage = getInputImage(); if (!(pixelImage instanceof RGB24Image)) { throw new WrongParameterException("Input image must implement RGB24Image."); } RGB24Image originalImage = (RGB24Image)pixelImage; quantizer.setInputImage(originalImage); quantizer.setMapping(true); // we want the quantizer to create an output image quantizer.setTruecolorOutput(false); // that output image must be paletted quantizer.process(); for (int currentPass = 0; currentPass < numPasses; currentPass++) { Paletted8Image palImage = (Paletted8Image)quantizer.getOutputImage(); palette = palImage.getPalette(); // create co-occurrence matrix for paletted image CoOccurrenceMatrix com = MatrixCreator.createCoOccurrenceMatrix(palImage); // create co-occurrence frequency matrix for co-occurrence matrix CoOccurrenceFrequencyMatrix cofm = MatrixCreator.createCoOccurrenceFrequencyMatrix(com); // compute certain statistics from the co-occurrence frequency matrix computeStatistics(cofm); // find pairs of contouring and compressible colors leaves = quantizer.createLeafList(); findColorPairs(cofm, com); if (compressibleNodes.size() == 0 || contouringPairs.size() == 0) { //System.out.println("Compressible=" + compressibleNodes.size() + contouring=" + contouringPairs.size() + " in iteration " + currentPass); break; } // adjust Median-Cut-specific data structures: // merge compressible and split contouring nodes System.out.println((currentPass + 1) + " " + compressibleNodes.size() + " " + contouringPairs.size()); mergeAndSplit(); // create a new version of the paletted image: // (1) reassign palette index values for the nodes quantizer.setAllPaletteIndexValues(); // (2) make it recompute the representative colors quantizer.findAllRepresentativeColors(); // (3) create a new Palette object palette = quantizer.createPalette(); // (4) give that to the paletted image Paletted8Image out = (Paletted8Image)quantizer.getOutputImage(); out.setPalette(palette); // (5) map original to that new palette quantizer.mapImage(originalImage, out); } setOutputImage(quantizer.getOutputImage()); } /** * Set the * {@link net.sourceforge.jiu.color.quantization.MedianCutQuantizer} * object to be used with this contour removal operation. * This is a mandatory parameter. * If process gets called before the quantizer object was specified, * a {@link MissingParameterException} is thrown. * @param medianCutQuantizer the quantizer object that will get used by this operation */ public void setQuantizer(MedianCutQuantizer medianCutQuantizer) { quantizer = medianCutQuantizer; } /** * Specify the number of contour removal passes to be performed. * Check out the section How the contour removal algorithm works * to learn more about the meaning of this value. * If this method is not called the default value {@link #DEFAULT_NUM_PASSES} * is used. * @param newValue number of passes, 1 or higher * @throws IllegalArgumentException if the argument is 0 or smaller */ public void setNumPasses(int newValue) { if (newValue < 1) { throw new IllegalArgumentException("Number of passes must be 1 or larger."); } numPasses = newValue; } /** * Specify the tau value to be used by this operation. * Check out the section How the contour removal algorithm works * to learn more about the meaning of this value. * If this method is not called the default value {@link #DEFAULT_TAU} * is used. * @param newValue tau value, 0.0 or higher * @throws IllegalArgumentException if the argument is smaller than 0.0 */ public void setTau(double newValue) { if (newValue < 0.0) { throw new IllegalArgumentException("Tau value must be 0.0 or larger."); } tau = newValue; } /** * Converts a Vector to an Object array. * Since Java 1.2 Vector has a toArray method, but we cannot rely * on 1.2 being available. * @param list Vector with objects * @return Object array with elements from list, in the same order */ private Object[] toArray(Vector list) { Object[] result = new Object[list.size()]; for (int i = 0; i < list.size(); i++) { result[i] = list.elementAt(i); } return result; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/MedianCutQuantizer.java0000664000000000000000000005112107741250132030312 0ustar /* * MedianCutQuantizer * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.color.analysis.Histogram3DCreator; import net.sourceforge.jiu.color.data.Histogram3D; import net.sourceforge.jiu.color.quantization.MedianCutNode; import net.sourceforge.jiu.color.quantization.RGBColor; import net.sourceforge.jiu.color.quantization.RGBColorList; import net.sourceforge.jiu.color.quantization.RGBQuantizer; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Performs the Median Cut color quantization algorithm * for a given list of colors. *

Supported image types

* The input image must implement {@link net.sourceforge.jiu.data.RGB24Image}. *

Usage example

* The following code snippet uses the default settings with a palette of 256 entries. *
 * MedianCutQuantizer quantizer = new MedianCutQuantizer();
 * quantizer.setInputImage(image);
 * quantizer.setPaletteSize(256);
 * quantizer.process();
 * PixelImage quantizedImage = quantizer.getOutputImage();
 * 
* If you want to combine Median Cut quantization with error diffusion dithering to * improve the visual quality of the output, try the * {@link net.sourceforge.jiu.color.dithering.ErrorDiffusionDithering} class. * However, note that noise is introduced into the image with dithering methods so * that the resulting image may not be suitable for automatic processing. *

Credits

* The Median Cut algorithm was designed by * Paul Heckbert. * He described it in the article Color image quantization for frame * buffer display. Comput. Graphics 16(3), 1982, 297 - 304. * CiteSeer * page of the article. * @author Marco Schmidt * @see MedianCutContourRemoval * @see net.sourceforge.jiu.color.dithering.ErrorDiffusionDithering */ public class MedianCutQuantizer extends ImageToImageOperation implements RGBIndex, RGBQuantizer { /** * Constant value for a method of determining the representative color * for a set of colors by computing the average of all samples for each * of the three components red, green and blue. * #getMethodToDetermineRepresentativeColors * #setMethodToDetermineRepresentativeColors */ public static final int METHOD_REPR_COLOR_AVERAGE = 0; /** * Constant value for a method of determining the representative color * for a set of colors by computing the weighted average of all samples for each * of the three components red, green and blue. * Weighted means that each color is multiplied by the number of times it occurs * in the input image. * The values of samples multiplied by their frequency are then divided by the total * number of times the colors appear in the image. * #getMethodToDetermineRepresentativeColors * #setMethodToDetermineRepresentativeColors */ public static final int METHOD_REPR_COLOR_WEIGHTED_AVERAGE = 1; /** * Constant value for a method of determining the representative color * for a set of colors by picking the median value of all samples for each * of the three components red, green and blue. * #getMethodToDetermineRepresentativeColors * #setMethodToDetermineRepresentativeColors */ public static final int METHOD_REPR_COLOR_MEDIAN = 2; /** * The default method to determine the representative color * from a list of colors. * Will be used if none is set by the user of this class via * {@link #setMethodToDetermineRepresentativeColors}. */ public static final int DEFAULT_METHOD_REPR_COLOR = METHOD_REPR_COLOR_MEDIAN; private boolean doNotMap; private RGBColorList list; private int maxValue; private int method; private boolean outputTruecolor; private int paletteSize; private MedianCutNode root; /** * Creates a MedianCutQuantizer object and * initializes its fields to default values. */ public MedianCutQuantizer() { doNotMap = false; maxValue = -1; method = METHOD_REPR_COLOR_AVERAGE; maxValue = 255; outputTruecolor = false; paletteSize = 256; } private void addNodes(MedianCutNode[] nodeList, MedianCutNode node) { if (node == null) { return; } if (node.isLeaf()) { int index = node.getPaletteIndex(); if (index >= 0 && index < nodeList.length) { nodeList[index] = node; } else { // ERROR ILLEGAL STATE throw new IllegalStateException("A node's index is invalid."); } } else { addNodes(nodeList, node.getLeftSuccessor()); addNodes(nodeList, node.getRightSuccessor()); } } private RGBColorList createColorList(RGB24Image image) throws OperationFailedException { Histogram3DCreator hc = new Histogram3DCreator(); hc.setImage(image, RGBIndex.INDEX_RED, RGBIndex.INDEX_GREEN, RGBIndex.INDEX_BLUE); hc.process(); Histogram3D hist = hc.getHistogram(); if (hist == null) { throw new OperationFailedException("Could not create histogram from input image."); } int numUniqueColors = hist.getNumUsedEntries(); if (numUniqueColors <= paletteSize) { throw new WrongParameterException("Input image has only " + numUniqueColors + " unique color(s), so it cannot be reduced to " + paletteSize + " color(s)."); } return new RGBColorList(hist); } /** * Creates a linear list of leaf nodes. * Assumes that {@link #findPalette()} was successfully run before. */ public MedianCutNode[] createLeafList() { MedianCutNode[] result = new MedianCutNode[paletteSize]; addNodes(result, root); return result; } /** * Creates a palette with the representative colors of all leaf nodes. * Assumes that {@link #findPalette()} was successfully run before. * @return palette with all representative colors */ public Palette createPalette() { MedianCutNode[] leaves = createLeafList(); Palette result = new Palette(leaves.length); for (int i = 0; i < leaves.length; i++) { int[] reprColor = leaves[i].getRepresentativeColor(); result.putSample(INDEX_RED, i, reprColor[INDEX_RED]); result.putSample(INDEX_GREEN, i, reprColor[INDEX_GREEN]); result.putSample(INDEX_BLUE, i, reprColor[INDEX_BLUE]); } return result; } /** * Traverses tree given by argument node and returns leaf with largest distribution * of samples for any of its three components. */ private MedianCutNode findLeafToBeSplit(MedianCutNode node) { if (node == null) { return null; } if (node.canBeSplit()) { if (!node.isAxisDetermined()) { int[] pairAxisDiff = list.findExtrema(node.getLeftIndex(), node.getRightIndex()); if (pairAxisDiff == null) { return null; } node.setLargestDistribution(pairAxisDiff[0], pairAxisDiff[1]); // axis, difference } return node; } else { MedianCutNode node1 = findLeafToBeSplit(node.getLeftSuccessor()); boolean canSplit1 = (node1 != null && node1.canBeSplit()); MedianCutNode node2 = findLeafToBeSplit(node.getRightSuccessor()); boolean canSplit2 = (node2 != null && node2.canBeSplit()); if (canSplit1) { if (canSplit2) { // case 1 of 4: both nodes can be split; find out which one has the largest distribution // of samples for one of the three RGB channels if (node1.getDifferenceOfLargestDistribution() >= node2.getDifferenceOfLargestDistribution()) { return node1; } else { return node2; } } else { // case 2 of 4: node1 can be split, node2 can't => take node1 return node1; } } else { if (canSplit2) { // case 3 of 4: node2 can be split, node1 can't => take node2 return node2; } else { // case 4 of 4: both nodes cannot be split => return null return null; } } } } /** * For a given RGB value, searches the node in the internal node tree whose * representative color is closest to this color. * @param rgb the color for which a match is searched; the array must have at least * three entries; {@link RGBIndex} constants are used to address the samples * @return node with best match */ public MedianCutNode findNearestNeighbor(int[] rgb) { MedianCutNode result = root; while (!result.isLeaf()) { result = result.getSuccessor(rgb); } return result; } /** * For each node in the argument array computes the distance between the * representative color of that node and the color given by the three * argument samples. * @return index of the node with the smallest distance or -1 if the array has a length of 0 */ public int findNearestNeighbor(MedianCutNode[] nodes, int red, int green, int blue) { int index = -1; double distance = 1000000; for (int i = 0; i < nodes.length; i++) { MedianCutNode node = nodes[i]; int[] reprColor = node.getRepresentativeColor(); double d = RGBColor.computeDistance(red, green, blue, reprColor[INDEX_RED], reprColor[INDEX_GREEN], reprColor[INDEX_BLUE]); if (d < distance) { distance = d; index = i; } } return index; } public void findPalette() { int colorsLeft = paletteSize - 1; while (colorsLeft > 0) { // find leaf with largest sample difference MedianCutNode node = findLeafToBeSplit(root); splitNode(node); colorsLeft--; } findRepresentativeColors(root); setAllPaletteIndexValues(); } public void findAllRepresentativeColors() { findRepresentativeColors(root); } /** * Computes a representative color for a set of colors in the color list. * Returns the color as a length three int array of sample values (which can be accessed using the * index constants from {@link RGBIndex}. * The method of determining the color (the REPR_xxx constants from this class) * has been given to the constructor. */ private int[] findRepresentativeColor(int index1, int index2) { int[] result = new int[3]; long[] temp = new long[3]; temp[0] = 0; temp[1] = 0; temp[2] = 0; switch(method) { case(METHOD_REPR_COLOR_AVERAGE): { int num = index2 - index1 + 1; for (int i = index1; i <= index2; i++) { RGBColor color = list.getColor(i); temp[0] += color.getSample(0); temp[1] += color.getSample(1); temp[2] += color.getSample(2); } result[0] = (int)(temp[0] / num); result[1] = (int)(temp[1] / num); result[2] = (int)(temp[2] / num); return result; } case(METHOD_REPR_COLOR_WEIGHTED_AVERAGE): { long num = 0; for (int i = index1; i <= index2; i++) { RGBColor color = list.getColor(i); int counter = color.getCounter(); temp[0] += color.getSample(0) * counter; temp[1] += color.getSample(1) * counter; temp[2] += color.getSample(2) * counter; num += counter; } if (num == 0) { //System.out.println("ERROR IN FINDREPRESENTATIVECOLOR (WEIGHTED AVERAGE): ZERO COUNTER"); return null; } result[0] = (int)(temp[0] / num); result[1] = (int)(temp[1] / num); result[2] = (int)(temp[2] / num); return result; } case(METHOD_REPR_COLOR_MEDIAN): { RGBColor color = list.getColor((index1 + index2) / 2); result[0] = color.getSample(0); result[1] = color.getSample(1); result[2] = color.getSample(2); return result; } default: throw new IllegalStateException("Unknown method for determining a representative color."); } } /** * Calls findRepresentativeColor with node if node is a leaf. * Otherwise, recursively calls itself with both successor nodes. */ private void findRepresentativeColors(MedianCutNode node) { if (node == null) { return; } if (node.isLeaf()) { node.setRepresentativeColor(findRepresentativeColor(node.getLeftIndex(), node.getRightIndex())); } else { findRepresentativeColors(node.getLeftSuccessor()); findRepresentativeColors(node.getRightSuccessor()); } } /** * Returns the method (to be) used to determine the representative * color for the list of colors of a node. * Default is {@link #DEFAULT_METHOD_REPR_COLOR}. * @return the method, one of the METHOD_xyz constants */ public int getMethodToDetermineRepresentativeColors() { return method; } /** * Returns the number of colors in the destination image. * If output is paletted, this is also the number of entries * in the palette. * @return number of colors in the destination */ public int getPaletteSize() { return paletteSize; } /** * Returns if this operation is supposed to generate truecolor or * paletted output. * @return if truecolor images are to be generated * @see #setTruecolorOutput(boolean) */ public boolean getTruecolorOutput() { return outputTruecolor; } public int map(int[] origRgb, int[] quantizedRgb) { MedianCutNode node = findNearestNeighbor(origRgb); int[] reprColor = node.getRepresentativeColor(); quantizedRgb[INDEX_RED] = reprColor[INDEX_RED]; quantizedRgb[INDEX_GREEN] = reprColor[INDEX_GREEN]; quantizedRgb[INDEX_BLUE] = reprColor[INDEX_BLUE]; return node.getPaletteIndex(); } public void mapImage(RGB24Image in, RGB24Image out) { int[] rgb = new int[3]; for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { rgb[INDEX_RED] = in.getSample(INDEX_RED, x, y); rgb[INDEX_GREEN] = in.getSample(INDEX_GREEN, x, y); rgb[INDEX_BLUE] = in.getSample(INDEX_BLUE, x, y); MedianCutNode node = findNearestNeighbor(rgb); int[] reprColor = node.getRepresentativeColor(); out.putSample(INDEX_RED, x, y, reprColor[INDEX_RED]); out.putSample(INDEX_GREEN, x, y, reprColor[INDEX_GREEN]); out.putSample(INDEX_BLUE, x, y, reprColor[INDEX_BLUE]); } } } public void mapImage(RGB24Image in, Paletted8Image out) { int[] rgb = new int[3]; for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { rgb[INDEX_RED] = in.getSample(INDEX_RED, x, y); rgb[INDEX_GREEN] = in.getSample(INDEX_GREEN, x, y); rgb[INDEX_BLUE] = in.getSample(INDEX_BLUE, x, y); MedianCutNode node = findNearestNeighbor(rgb); out.putSample(0, x, y, node.getPaletteIndex()); } } } public void process() throws MissingParameterException, OperationFailedException, WrongParameterException { ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); if (in instanceof RGB24Image) { list = createColorList((RGB24Image)in); } else { throw new WrongParameterException("Input image must implement RGB24Image."); } root = new MedianCutNode(null, 0, list.getNumEntries() - 1); root.setMinColor(0, 0, 0); root.setMaxColor(maxValue, maxValue, maxValue); findPalette(); if (doNotMap) { return; } PixelImage out = getOutputImage(); if (getTruecolorOutput()) { if (out == null) { out = in.createCompatibleImage(in.getWidth(), in.getHeight()); setOutputImage(out); } else { if (!(out instanceof RGB24Image)) { throw new WrongParameterException("Output image must implement RGB24Image."); } } mapImage((RGB24Image)in, (RGB24Image)out); } else { Palette palette = createPalette(); if (out == null) { out = new MemoryPaletted8Image(in.getWidth(), in.getHeight(), palette); setOutputImage(out); } else { if (out instanceof Paletted8Image) { ((Paletted8Image)out).setPalette(palette); } else { throw new WrongParameterException("Output image must implement Paletted8Image."); } } mapImage((RGB24Image)in, (Paletted8Image)out); } } public void setAllPaletteIndexValues() { int paletteEntriesAssigned = setPaletteIndexValues(root, 0); if (paletteEntriesAssigned != paletteSize) { throw new IllegalStateException("Assigning palette values did not result in correct number of entries."); } } /** * Defines whether process will map the input image to an output image. * If not, only the palette is determined. */ public void setMapping(boolean doMap) { doNotMap = !doMap; } /** * Sets the method to determine the representative color * for a list of colors. * After the algorithm has determined sets of colors that lie * closely together in color space and will be * mapped to the same color in the destination image, * the algorithm will determine that color * @param newMethod the new method, one of the METHOD_xyz constants in this class */ public void setMethodToDetermineRepresentativeColors(int newMethod) { if (newMethod != METHOD_REPR_COLOR_AVERAGE && newMethod != METHOD_REPR_COLOR_WEIGHTED_AVERAGE && newMethod != METHOD_REPR_COLOR_MEDIAN) { throw new IllegalArgumentException("Method must be one of the METHOD_xyz constants."); } method = newMethod; } /** * Recursively visits node and its descendants, assigning ascending * palette index values to leaves via MedianCutNode.setPaletteIndex(int). * If this method is called with root and 0 as parameters, all leaves * will get a unique palette index. */ private int setPaletteIndexValues(MedianCutNode node, int index) { if (node.isLeaf()) { node.setPaletteIndex(index); index++; return index; } else { index = setPaletteIndexValues(node.getLeftSuccessor(), index); return setPaletteIndexValues(node.getRightSuccessor(), index); } } /** * Sets the number of colors that this operations is supposed to reduce * the original image to. * @param newPaletteSize the number of colors * @throws IllegalArgumentException if the argument is smaller than 1 or larger than 256 * @see #getPaletteSize */ public void setPaletteSize(int newPaletteSize) { if (newPaletteSize < 1) { throw new IllegalArgumentException("Palette size must be 1 or larger."); } if (newPaletteSize > 256) { throw new IllegalArgumentException("Palette size must be at most 256."); } paletteSize = newPaletteSize; } /** * Lets the user specify if the output image is to be truecolor * (argument useTruecolor is true) or paletted * (argument useTruecolor is false). * If the color type is to be changed afterwards, use PromoteToRgb24 * to convert from paletted to truecolor. * Reducing a truecolor image that uses only 256 or less colors to * a paletted image can be done with AutoDetectColorType. * @param useTruecolor */ public void setTruecolorOutput(boolean useTruecolor) { outputTruecolor = useTruecolor; } public void splitNode(MedianCutNode node) { if (!node.isAxisDetermined()) { int[] pairAxisDiff = list.findExtrema(node.getLeftIndex(), node.getRightIndex()); node.setLargestDistribution(pairAxisDiff[0], pairAxisDiff[1]); // axis, difference } list.sortByAxis(node.getLeftIndex(), node.getRightIndex(), node.getAxisOfLargestDistribution()); int middleIndex = node.getMiddleIndex(); int leftIndex = node.getLeftIndex(); int rightIndex = node.getRightIndex(); RGBColor color = list.getColor(middleIndex); int axis = node.getAxisOfLargestDistribution(); int medianValue = color.getSample(axis); node.setMedianValue(medianValue); if (leftIndex == rightIndex) { throw new IllegalArgumentException("Cannot split leaf that only holds one color. This should never happen."); } MedianCutNode left = new MedianCutNode(node, leftIndex, middleIndex); MedianCutNode right = new MedianCutNode(node, middleIndex + 1, rightIndex); node.setSuccessors(left, right); for (int i = 0; i < 3; i++) { int max = node.getMaxColorSample(i); left.setMaxColorSample(i, max); right.setMaxColorSample(i, max); int min = node.getMinColorSample(i); left.setMinColorSample(i, min); right.setMinColorSample(i, min); } left.setMaxColorSample(axis, medianValue); right.setMinColorSample(axis, medianValue + 1); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/OctreeNode.java0000664000000000000000000001360607741250132026573 0ustar /* * OctreeNode * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.util.ComparatorInterface; /** * A single node in an octree. * @author Marco Schmidt * @since 0.6.0 * @see OctreeColorQuantizer */ public class OctreeNode implements ComparatorInterface, RGBIndex { private int paletteIndex; private int pixelCount; private int redSum; private int greenSum; private int blueSum; private int red; private int green; private int blue; private OctreeNode[] children; /** * Add a color red-green-blue to the octree, given by its root node. * This methods follows the octree down to the bitsPerSample'th level, * creating nodes as necessary. * Increases the pixelCount of a leaf node (if the node already exists) * or initializes a newly-created leaf. * @param root root node of the octree * @param red the red intensity value of the color to be added * @param green the green intensity value of the color to be added * @param blue the blue intensity value of the color to be added * @param bitsPerSample */ public static boolean add(OctreeNode root, int red, int green, int blue, int bitsPerSample) { OctreeNode node = root; boolean newColor = false; int shift = bitsPerSample - 1; do { if (shift >= 0) { // not a leaf OctreeNode[] children = node.children; if (children == null) { children = new OctreeNode[8]; node.children = children; } int index = computeIndex(red, green, blue, shift); node = children[index]; if (node == null) { node = new OctreeNode(); children[index] = node; newColor = true; } shift--; } else { // leaf; update its red/green/blue/pixel count and leave node.update(red, green, blue); return newColor; } } while (true); } public int compare(Object o1, Object o2) { OctreeNode n1 = (OctreeNode)o1; OctreeNode n2 = (OctreeNode)o2; int pc1 = n1.pixelCount; int pc2 = n2.pixelCount; if (pc1 < pc2) { return -1; } else if (pc1 == pc2) { return 0; } else { return 1; } } private static int computeIndex(int red, int green, int blue, int shift) { return (((red >> shift) & 1) << 2) | (((green >> shift) & 1) << 1) | ((blue >> shift) & 1); } /** * Adds the sums for red, green and blue values and * the pixel count values of all child nodes and * stores the results in this node. * Does nothing if this is a leaf. * Otherwise, recursively calls itself with all * non-null child nodes and adds their sums for red, * green and blue and the number of pixel values. * Then stores these values in this node. * They will be used when the octree is pruned to have * a certain number of leaves. */ public void copyChildSums() { if (children == null) { return; } redSum = 0; greenSum = 0; blueSum = 0; pixelCount = 0; for (int i = 0; i < children.length; i++) { OctreeNode child = children[i]; if (child != null) { child.copyChildSums(); redSum += child.redSum; greenSum += child.greenSum; blueSum += child.blueSum; pixelCount += child.pixelCount; } } } public void determineRepresentativeColor() { if (pixelCount > 0) { red = redSum / pixelCount; green = greenSum / pixelCount; blue = blueSum / pixelCount; } } /*public static OctreeNode findMinimumNode(OctreeNode node, OctreeNode currentMinimumNode, int minimumNodePixelCount) { OctreeNode[] children = node.getChildren(); if (children == null) { return currentMinimumNode; } int sum = 0; boolean hasOnlyLeafChildren = true; for (int i = 0; i < children.length; i++) { OctreeNode child = children[i]; if (child != null) { if (child.isLeaf()) { sum += child.pixelCount; } else { hasOnlyLeafChildren = false; //findMinimumNode(child, currentMinimumNode, minimumNodePixelCount); } } } return currentMinimumNode; }*/ public int getBlue() { return blue; } public OctreeNode[] getChildren() { return children; } public int getGreen() { return green; } public int getNumChildren() { int result = 0; if (children != null) { for (int i = 0; i < children.length; i++) { if (children[i] != null) { result++; } } } return result; } public int getPaletteIndex() { return paletteIndex; } public int getRed() { return red; } public boolean isLeaf() { return children == null; } /** * Returns the index of the best match for origRgb in the palette or * -1 if the best match could not be determined. * If there was a best match, quantizedRgb is filled with the quantized color's * RGB values. */ public int map(int[] origRgb, int[] quantizedRgb) { return map(origRgb[INDEX_RED], origRgb[INDEX_GREEN], origRgb[INDEX_BLUE], 7, quantizedRgb); } private final int map(final int r, final int g, final int b, final int shift, final int[] quantizedRgb) { if (children == null) { quantizedRgb[INDEX_RED] = red; quantizedRgb[INDEX_GREEN] = green; quantizedRgb[INDEX_BLUE] = blue; return paletteIndex; } int index = computeIndex(r, g, b, shift); OctreeNode node = children[index]; if (node == null) { return -1; } return node.map(r, g, b, shift - 1, quantizedRgb); } public void setChildren(OctreeNode[] newChildren) { children = newChildren; } public void setPaletteIndex(int index) { paletteIndex = index; } private void update(int red, int green, int blue) { redSum += red; greenSum += green; blueSum += blue; pixelCount++; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/UniformPaletteQuantizer.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/UniformPaletteQuantizer.0000664000000000000000000001114507741250132030537 0ustar /* * UniformPaletteQuantizer * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.color.quantization.RGBQuantizer; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; /** * A color quantizer that maps to a palette which is equidistantly distributed * in the RGB color cube. * Equidistantly distributed only within each channel. * @author Marco Schmidt */ public class UniformPaletteQuantizer extends ImageToImageOperation implements RGBIndex, RGBQuantizer { private final int RED_BITS; private final int RED_LEFT_SHIFT; private final int RED_RIGHT_SHIFT; private final int[] RED_VALUES; private final int GREEN_BITS; private final int GREEN_LEFT_SHIFT; private final int GREEN_RIGHT_SHIFT; private final int[] GREEN_VALUES; private final int BLUE_BITS; private final int BLUE_RIGHT_SHIFT; private final int[] BLUE_VALUES; private final int TOTAL_BITS; private int[] PALETTE_RED; private int[] PALETTE_GREEN; private int[] PALETTE_BLUE; public UniformPaletteQuantizer(int redBits, int greenBits, int blueBits) { if (redBits < 1) { throw new IllegalArgumentException("Must have at least 1 bit for red."); } if (greenBits < 1) { throw new IllegalArgumentException("Must have at least 1 bit for green."); } if (blueBits < 1) { throw new IllegalArgumentException("Must have at least 1 bit for blue."); } BLUE_BITS = blueBits; BLUE_RIGHT_SHIFT = 8 - BLUE_BITS; BLUE_VALUES = new int[1 << BLUE_BITS]; for (int i = 0; i < BLUE_VALUES.length; i++) BLUE_VALUES[i] = i * 255 / (BLUE_VALUES.length - 1); GREEN_BITS = greenBits; GREEN_RIGHT_SHIFT = 8 - GREEN_BITS; GREEN_LEFT_SHIFT = BLUE_BITS; GREEN_VALUES = new int[1 << GREEN_BITS]; for (int i = 0; i < GREEN_VALUES.length; i++) GREEN_VALUES[i] = i * 255 / (GREEN_VALUES.length - 1); RED_BITS = redBits; RED_RIGHT_SHIFT = 8 - RED_BITS; RED_LEFT_SHIFT = GREEN_BITS + BLUE_BITS; RED_VALUES = new int[1 << RED_BITS]; for (int i = 0; i < RED_VALUES.length; i++) RED_VALUES[i] = i * 255 / (RED_VALUES.length - 1); TOTAL_BITS = RED_BITS + GREEN_BITS + BLUE_BITS; if (TOTAL_BITS > 8) { throw new IllegalArgumentException("Sum of red / green / blue bits must not exceed 8."); } } public Palette createPalette() { int numEntries = 1 << TOTAL_BITS; Palette result = new Palette(numEntries, 255); PALETTE_RED = new int[numEntries]; PALETTE_GREEN = new int[numEntries]; PALETTE_BLUE = new int[numEntries]; int index = 0; for (int r = 0; r < (1 << RED_BITS); r++) { for (int g = 0; g < (1 << GREEN_BITS); g++) { for (int b = 0; b < (1 << BLUE_BITS); b++) { //System.out.println(index + ":" + r + ", " + g + ", " + b); result.putSample(INDEX_RED, index, RED_VALUES[r]); PALETTE_RED[index] = RED_VALUES[r]; result.putSample(INDEX_GREEN, index, GREEN_VALUES[g]); PALETTE_GREEN[index] = GREEN_VALUES[g]; result.putSample(INDEX_BLUE, index, BLUE_VALUES[b]); PALETTE_BLUE[index] = BLUE_VALUES[b]; index++; } } } return result; } public int map(int[] origRgb, int[] quantizedRgb) { int index = mapToIndex(origRgb[INDEX_RED], origRgb[INDEX_GREEN], origRgb[INDEX_BLUE]); quantizedRgb[INDEX_RED] = PALETTE_RED[index]; quantizedRgb[INDEX_GREEN] = PALETTE_GREEN[index]; quantizedRgb[INDEX_BLUE] = PALETTE_BLUE[index]; return index; } public final int mapToIndex(int red, int green, int blue) { return ((red >> RED_RIGHT_SHIFT) << RED_LEFT_SHIFT) | ((green >> GREEN_RIGHT_SHIFT) << GREEN_LEFT_SHIFT) | (blue >> BLUE_RIGHT_SHIFT); } private void process(RGB24Image in, Paletted8Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); if (out == null) { out = new MemoryPaletted8Image(WIDTH, HEIGHT, createPalette()); } for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int r = in.getSample(INDEX_RED, x, y); int g = in.getSample(INDEX_GREEN, x, y); int b = in.getSample(INDEX_BLUE, x, y); out.putSample(0, x, y, mapToIndex(r, g, b)); } setProgress(y, HEIGHT); } setOutputImage(out); } public void process() { process((RGB24Image)getInputImage(), (Paletted8Image)getOutputImage()); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/RGBColorList.java0000664000000000000000000001140607741250132027005 0ustar /* * RGBColorList * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.color.data.Histogram3D; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.util.ComparatorInterface; import net.sourceforge.jiu.util.Sort; /** * Holds an array of {@link RGBColor} objects. * @author Marco Schmidt */ public class RGBColorList implements RGBIndex { private RGBColor[] list; private final int numEntries; /** * Creates a color list with room for a fixed number of entries. * @param numberOfEntries the number of entries in the new list (must be larger than zero) * @throws IllegalArgumentException if the argument is smaller than one */ private RGBColorList(final int NUM_ENTRIES) { if (NUM_ENTRIES < 1) { throw new IllegalArgumentException("RGBColorList must have at least one entry; got " + NUM_ENTRIES); } numEntries = NUM_ENTRIES; list = new RGBColor[NUM_ENTRIES]; } /** * Creates a new list and initializes it with the argument histogram. * All values from the histogram with a counter larger than zero will * be added to the list (which will include all colors that appear at least * once in the image on which the histogram was created). * @param hist the histogram from which the list will be initialized * @throws IllegalArgumentException thrown if no histogram entry has a non-zero counter */ public RGBColorList(Histogram3D hist) { this(hist.getNumUsedEntries()); int i = 0; final int MAX_RED = hist.getMaxValue(INDEX_RED); final int MAX_GREEN = hist.getMaxValue(INDEX_GREEN); final int MAX_BLUE = hist.getMaxValue(INDEX_BLUE); for (int r = 0; r <= MAX_RED; r++) { for (int g = 0; g <= MAX_GREEN; g++) { for (int b = 0; b <= MAX_BLUE; b++) { int counter = hist.getEntry(r, g, b); if (counter > 0) { list[i++] = new RGBColor(r, g, b, counter); } } } } } /** * In a given interval of the list this method searches for the color axis * that has the largest distribution of values. * Returns a pair of int values; * the first value is the component (0, 1 or 2), * the second value is the difference between the minimum and maximum value found in the list. * Only checks colors from index i1 to i2 of the list. */ public int[] findExtrema(int i1, int i2) { if (i1 < 0 || i1 >= numEntries || i2 < 0 || i2 >= numEntries || i1 > i2) { return null; } int[] max = new int[3]; int[] min = new int[3]; RGBColor c = list[i1]; for (int i = 0; i < 3; i++) { min[i] = max[i] = c.getSample(i); } int i = i1 + 1; while (i < i2) { c = list[i++]; for (int j = 0; j < 3; j++) { int cSample = c.getSample(j); if (cSample < min[j]) { min[j] = cSample; } else { if (cSample > max[j]) { max[j] = cSample; } } } } // first value: sample index (0 - 2); second value: difference int[] result = new int[2]; result[0] = result[1] = -1; for (i = 0; i < 3; i++) { int newDiff = max[i] - min[i]; if (newDiff > result[1]) { result[0] = i; result[1] = newDiff; } } return result; } /** * Returns an {@link RGBColor} object from this list, given by its zero-based * index value. * @param index zero-based index into the list; must be smaller than {@link #getNumEntries()} * @return the color object */ public RGBColor getColor(int index) { return list[index]; } /** * Returns the number of color objects in this list. * @return number of colors in the list */ public int getNumEntries() { return list.length; } /** * Sorts an interval of the array of colors by one of the three components (RGB). * @param index1 the index of the first element in the interval * @param index2 the index of the last element in the interval * @param axis the color component by which the interval is to be sorted, {@link #INDEX_RED}, {@link #INDEX_GREEN} or {@link #INDEX_BLUE} */ public void sortByAxis(int index1, int index2, int axis) { Sort.sort(list, index1, index2, new RGBColorComparator(axis)); } /** * Sorts an interval of the array of colors by their counters. * @param index1 the index of the first element in the interval * @param index2 the index of the last element in the interval */ public void sortByCounter(int index1, int index2) { Sort.sort(list, index1, index2, new ComparatorInterface() { public int compare(Object obj1, Object obj2) { RGBColor col1 = (RGBColor)obj1; RGBColor col2 = (RGBColor)obj2; return col1.getCounter() - col2.getCounter(); } }); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/MedianCutNode.java0000664000000000000000000001626507741250132027227 0ustar /* * MedianCutNode * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.color.quantization.RGBColor; import net.sourceforge.jiu.data.RGBIndex; /** * An instance of this node class represents a cuboid part * of the color cube representing the three-dimensional RGB color space. * @author Marco Schmidt * @see MedianCutQuantizer */ public class MedianCutNode implements RGBIndex { private int axis; private boolean axisDetermined; private int diff; private int index1; private int index2; private MedianCutNode leftSuccessor; private int[] max; private int medianValue; private int middleIndex; private int[] min; private int paletteIndex; private MedianCutNode parent; private int[] reprColor; private MedianCutNode rightSuccessor; /** * Creates a node for a Median Cut tree of nodes with index values for * some external color array and the parent node. * This parent is null for the root node. * @param parent the parent node of this new node, should be null only for the root node * @param index1 the index value of the first element of colors in the color list * @param index2 the index value of the last element of colors in the color list; must be larger than or equal to index1 * @throws IllegalArgumentException if index1 is larger than index2 */ public MedianCutNode(MedianCutNode parent, int index1, int index2) { if (index1 > index2) { throw new IllegalArgumentException("MedianCutNode constructor, index1 must be smaller than or equal to index2."); } this.parent = parent; this.index1 = index1; this.index2 = index2; determineMiddleIndex(); leftSuccessor = null; rightSuccessor = null; diff = 0; axisDetermined = false; max = new int[3]; min = new int[3]; paletteIndex = -1; } /** * Returns if this node can be split into two. * This is true if and only if this is a leaf and if the color * list index values represent an interval of at least length 2. * @return if this node can be split into two nodes */ public boolean canBeSplit() { return isLeaf() && index1 != index2; } /** * Computes the distance in RGB color space between the representative color of this node and the * argument node and returns it as non-negative value. */ public double computeRgbDistance(MedianCutNode node) { int[] c = node.reprColor; return RGBColor.computeDistance(reprColor[INDEX_RED], reprColor[INDEX_GREEN], reprColor[INDEX_BLUE], c[INDEX_RED], c[INDEX_GREEN], c[INDEX_BLUE]); } /** * Computes the middle index value of this node. * It uses the index values given to this node's constructor, index1 and index2. */ private void determineMiddleIndex() { if (index1 == index2) { middleIndex = index1; } else { middleIndex = (index1 + index2) / 2; } } /** * Returns the axis of the channel whose samples are most widely * distributed among the colors that belong to this node. * @return index of axis, one of the {@link RGBIndex} constants * @throws IllegalArgumentException if that axis has not been determined */ public int getAxisOfLargestDistribution() { if (axisDetermined) { return axis; } else { throw new IllegalArgumentException("The axis has not been determined and can thus not be returned."); } } public int getDifferenceOfLargestDistribution() { if (axisDetermined) { return diff; } else { throw new IllegalArgumentException("The axis has not been determined and can thus not be returned."); } } public int getLeftIndex() { return index1; } /** * Returns left successor node (or null if this node is a leaf). */ public MedianCutNode getLeftSuccessor() { return leftSuccessor; } public int getMaxColorSample(int index) { return max[index]; } public int getMedianValue() { return medianValue; } public int getMiddleIndex() { return middleIndex; } public int getMinColorSample(int index) { return min[index]; } public int getNumColors() { return index2 - index1 + 1; } public int getPaletteIndex() { return paletteIndex; } /** * Returns parent node (or null if this node is the root node). */ public MedianCutNode getParentNode() { return parent; } public int[] getRepresentativeColor() { return reprColor; } public int getRightIndex() { return index2; } /** * Returns right successor node (or null if this node is a leaf). */ public MedianCutNode getRightSuccessor() { return rightSuccessor; } public MedianCutNode getSuccessor(int[] rgb) { if (rgb[axis] <= medianValue) { return leftSuccessor; } else { return rightSuccessor; } } public boolean isAxisDetermined() { return axisDetermined; } /** * Returns if this node is a leaf by checking if both successors are null. * Note that the case of one successor being null and the other non-null * should never happen. * @return if this node is a leaf (true) */ public boolean isLeaf() { return (leftSuccessor == null && rightSuccessor == null); } public void setLargestDistribution(int newAxis, int newDifference) { if (newAxis != INDEX_RED && newAxis != INDEX_GREEN && newAxis != INDEX_BLUE) { throw new IllegalArgumentException("Axis must be either INDEX_RED, INDEX_GREEN or INDEX_BLUE."); } axis = newAxis; diff = newDifference; axisDetermined = true; } public void setMaxColor(int red, int green, int blue) { max[INDEX_RED] = red; max[INDEX_GREEN] = green; max[INDEX_BLUE] = blue; } public void setMaxColorSample(int index, int value) { max[index] = value; } public void setMedianValue(int newMedianValue) { medianValue = newMedianValue; } public void setMinColor(int red, int green, int blue) { min[INDEX_RED] = red; min[INDEX_GREEN] = green; min[INDEX_BLUE] = blue; } public void setMinColorSample(int index, int value) { min[index] = value; } public void setPaletteIndex(int newPaletteIndex) { paletteIndex = newPaletteIndex; } public void setRepresentativeColor(int[] aRepresentativeColor) { if (aRepresentativeColor != null && aRepresentativeColor.length != 3) { throw new IllegalArgumentException("Representative color array argument must have a length of 3."); } reprColor = aRepresentativeColor; } /** * Sets the successor nodes for this node. * The successors must be either both null or both initialized. * They must not be equal. * @param left the left successor node * @param right the left successor node */ public void setSuccessors(MedianCutNode left, MedianCutNode right) { if ((left == null && right != null) || (left != null && right == null)) { throw new IllegalArgumentException("The successor nodes must be either both null or both initialized ."); } if (left != null && left == right) { throw new IllegalArgumentException("The successor nodes must not be the same."); } leftSuccessor = left; rightSuccessor = right; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/RGBColorComparator.java0000664000000000000000000000207010324333347030177 0ustar /* * RGBColorComparator * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.util.ComparatorInterface; /** * Compares two {@link RGBColor} objects. * @author Marco Schmidt */ public class RGBColorComparator implements ComparatorInterface, RGBIndex { private int sortOrder; public RGBColorComparator(int aSortOrder) { setSortOrder(aSortOrder); } public int compare(Object o1, Object o2) { return ((RGBColor)o1).compareTo((RGBColor)o2, sortOrder); } /** * Sets the internal sort order (it is sorted by one of the three * RGB components) to the parameter. */ public void setSortOrder(int aSortOrder) { if (aSortOrder != INDEX_RED && aSortOrder != INDEX_GREEN && aSortOrder != INDEX_BLUE) { throw new IllegalArgumentException("The sort order argument must be either INDEX_RED, INDEX_GREEN or INDEX_BLUE."); } sortOrder = aSortOrder; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/package.html0000664000000000000000000000171007741250132026153 0ustar

Classes to perform color image quantization, the reduction of the number of unique colors in an image. This is a lossy operation. Usually a number of colors in the destination image is specified by the user (e.g. 256), then the quantization algorithm creates a copy of the input image that has no more than that number of colors. The goal is to be as close to the original as possible. Depending on the actual number of colors specified and the image content this can lead to varying results.

Quantization is usually done to reduce the amount of data necessary to represent the image. The cost for this reduction is a loss of information.

See the dithering package for algorithms that improve the result of color image quantization algorithms. ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/OctreeColorQuantizer.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/OctreeColorQuantizer.jav0000664000000000000000000002455110572434210030524 0ustar /* * OctreeColorQuantizer * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.color.quantization.RGBQuantizer; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; import net.sourceforge.jiu.util.Sort; /** * Performs the octree color quantization algorithm for a given RGB truecolor image. * The quality is usually somewhat inferior to the results of {@link MedianCutQuantizer}. * Note that you can improve the quality by applying a dithering algorithm. * See {@link net.sourceforge.jiu.color.dithering.ErrorDiffusionDithering}. * *

Usage example

* This reduces some RGB24Image image to a 16 color paletted image: *
 * MemoryRGB24Image image = ...; // initialize
 * OctreeColorQuantizer ocq = new OctreeColorQuantizer();
 * ocq.setInputImage(image);
 * ocq.setPaletteSize(16);
 * ocq.process();
 * PixelImage quantizedImage = ocq.getOutputImage();
 * 
* *

Credits

* * @author Marco Schmidt * @since 0.6.0 */ public class OctreeColorQuantizer extends ImageToImageOperation implements RGBIndex, RGBQuantizer { /** * The default number of colors in the palette. * Will be used when no other value is specified via {@link #setPaletteSize}. */ public static final int DEFAULT_PALETTE_SIZE = 256; private int paletteSize = DEFAULT_PALETTE_SIZE; private OctreeNode root; private Palette palette; private int[] redValues; private int[] greenValues; private int[] blueValues; /** * If node is a leaf node, this method assigns palette index values * and determines the representative color, otherwise it simply * recursively calls itself for all child nodes. * The updated index value is returned. * It is increased whenever a leaf is assigned that index value. * * @param node the node of the octree that will itself (and its children) be processed * @param index the current index in the palette index assignment procedure * @return updated index value; may have been increased while node or its child(ren) - * were assigned index values */ private int assignPaletteIndexValues(OctreeNode node, int index) { if (node == null) { return index; } if (node.isLeaf()) { node.setPaletteIndex(index); node.determineRepresentativeColor(); return index + 1; } else { OctreeNode[] children = node.getChildren(); if (children != null) { for (int i = 0; i < 8; i++) { if (children[i] != null) { index = assignPaletteIndexValues(children[i], index); } } } return index; } } public Palette createPalette() { if (palette == null) { int numValues = assignPaletteIndexValues(root, 0); palette = new Palette(numValues); initPalette(root, palette); redValues = new int[numValues]; greenValues = new int[numValues]; blueValues = new int[numValues]; for (int i = 0; i < numValues; i++) { redValues[i] = palette.getSample(INDEX_RED, i); greenValues[i] = palette.getSample(INDEX_GREEN, i); blueValues[i] = palette.getSample(INDEX_BLUE, i); } return palette; } else { return (Palette)palette.clone(); } } /** * Creates an octree and prepares this quantizer so that colors can be mapped to * palette index values. * If you use {@link #process()} you must not call this method. * On the other hand, if you want to do the mapping yourself - maybe if you * want to do mapping and dithering interchangeably - call this method first, * then do the mapping yourself. * @throws MissingParameterException if parameters like the input image are missing * @throws WrongParameterException if parameters exist but are of the wrong type */ public void init() throws MissingParameterException, WrongParameterException { PixelImage pi = getInputImage(); if (pi == null) { throw new MissingParameterException("Input image needed."); } if (!(pi instanceof RGB24Image)) { throw new WrongParameterException("Input image must be of type RGB24Image."); } initOctree(); pruneOctree(); } private int initOctree() { int numUniqueColors = 0; root = new OctreeNode(); RGB24Image in = (RGB24Image)getInputImage(); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { int red = in.getSample(INDEX_RED, x, y); int green = in.getSample(INDEX_GREEN, x, y); int blue = in.getSample(INDEX_BLUE, x, y); if (OctreeNode.add(root, red, green, blue, 8)) { numUniqueColors++; } } } root.copyChildSums(); return numUniqueColors; } private void initPalette(OctreeNode node, Palette palette) { if (node == null) { return; } if (node.isLeaf()) { int index = node.getPaletteIndex(); palette.put(index, node.getRed(), node.getGreen(), node.getBlue()); return; } OctreeNode[] children = node.getChildren(); if (children == null) { return; } for (int i = 0; i < children.length; i++) { OctreeNode child = children[i]; if (child != null) { initPalette(child, palette); } } } /** * Maps an RGB color origRgb to one of the colors in the * color map; that color will be written to quantizedRgb * and its palette index will be returned. * @param origRgb the color to be mapped to the best-possible counterpart in the * palette; the array is indexed by the constants from {@link RGBIndex} * @param quantizedRgb the resulting color from the palette will be written * to this array; it is also indexed by the constants from {@link RGBIndex} * @return index of the found color in the palette */ public int map(int[] origRgb, int[] quantizedRgb) { int result = root.map(origRgb, quantizedRgb); if (result == -1) { int minIndex = 0; int minDistance = Integer.MAX_VALUE; int i = 0; int red = origRgb[INDEX_RED]; int green = origRgb[INDEX_GREEN]; int blue = origRgb[INDEX_BLUE]; while (i < redValues.length) { int v = (redValues[i] - red); int sum = v * v; v = (greenValues[i] - green); sum += v * v; v = (blueValues[i] - blue); sum += v * v; if (sum < minDistance) { minIndex = i; minDistance = sum; } i++; } quantizedRgb[INDEX_RED] = redValues[minIndex]; quantizedRgb[INDEX_GREEN] = greenValues[minIndex]; quantizedRgb[INDEX_BLUE] = blueValues[minIndex]; return minIndex; } else { // quantizedRgb was filled with values in root.map return result; } } private void mapImage() { RGB24Image in = (RGB24Image)getInputImage(); Palette palette = createPalette(); Paletted8Image out = new MemoryPaletted8Image(in.getWidth(), in.getHeight(), palette); int[] origRgb = new int[3]; int[] quantizedRgb = new int[3]; for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { origRgb[INDEX_RED] = in.getSample(INDEX_RED, x, y); origRgb[INDEX_GREEN] = in.getSample(INDEX_GREEN, x, y); origRgb[INDEX_BLUE] = in.getSample(INDEX_BLUE, x, y); int index = map(origRgb, quantizedRgb); out.putSample(0, x, y, index); } } setOutputImage(out); } /** * Initializes an octree, reduces it have as many leaves (or less) as * the desired palette size and maps the original image to the newly-created * palette. */ public void process() throws MissingParameterException, WrongParameterException { init(); mapImage(); } /** * Reduces the octree until it has as many leaves (or less) than specified * by the paletteSize argument in the constructor * {@link #OctreeColorQuantizer(int)}. */ private void pruneOctree() { // create an array for as many palette entries as specified by paletteSize OctreeNode[] a = new OctreeNode[paletteSize]; // initial length is 1, the only element is the root a[0] = root; int length = 1; // create a comparator that will be used to sort the nodes by their pixel count, // in ascending order OctreeNode comparator = new OctreeNode(); // loop and split leaves as long as the number of nodes (length) is smaller // than the desired palette size while (length < paletteSize) { Sort.sort(a, 0, length - 1, comparator); int index = length - 1; while (index >= 0) { OctreeNode node = a[index]; int numChildren = node.getNumChildren(); // check if current length minus the node which may be split // plus the number of its children does not exceed the desired palette size if (numChildren > 0 && length - 1 + numChildren < paletteSize) { // child nodes fit in here if (index < length - 1) { System.arraycopy(a, index + 1, a, index, length - index - 1); } length--; OctreeNode[] children = node.getChildren(); for (int i = 0; i < children.length; i++) { if (children[i] != null) { a[length++] = children[i]; } } break; } else { index--; } } if (index == -1) { // we could not find a node to be split break; } } // in some cases it is not possible to get exactly paletteSize leaves; // adjust paletteSize to be equal to the number of leaves // note that length will never be larger than paletteSize, only smaller // in some cases paletteSize = length; // make all found nodes leaves by setting their child nodes to null for (int i = 0; i < length; i++) { a[i].setChildren(null); } } public void setPaletteSize(int newPaletteSize) { if (newPaletteSize < 1) { throw new IllegalArgumentException("Palette size must be 1 or larger."); } if (newPaletteSize > 256) { throw new IllegalArgumentException("Palette size must be 256 or smaller."); } paletteSize = newPaletteSize; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/RGBQuantizer.java0000664000000000000000000001000207741250132027044 0ustar /* * RGBQuantizer * * Copyright (c) 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.data.Palette; /** * An interface for an RGB color quantizer. * Color quantizers take an input image and produce an output image * that looks like the original but uses less colors. * Keeping the error (the difference between input and output image) as small * as possible is important - the more similar input and output are, the better. *

* Similarity between to pixels (or, more accurately, the colors of two pixels) * can be defined by their distance in color space. * Imagine two colors given by (r1, g1, b1) * and * (r2, g2, b2). * The distance can then be defined as * sqrt((r1 - r2)2 + * (g1 - g2)2 + (b1 - b2)2) * (with sqrt being the square root). *

* A quantizer has two main tasks: *

    *
  1. Find a palette. * Some quantizers create custom palettes for a * given input (e.g. {@link MedianCutQuantizer} or {@link OctreeColorQuantizer}, * other quantizers use fixed palettes (e.g. {@link UniformPaletteQuantizer}). * Using a custom palette typically results in a better output image * (it is more similar because it takes into consideration the content * of the input). * However, using fixed palettes requires less CPU time and memory and * is sufficient in many cases from a point of view of output quality. *

    * If a quantizer does use a fixed palette, this first step obviously is not * so much about finding the palette but about specifying it. *

  2. *
  3. Map the input image to the palette. * For each pixel in the truecolor input image the mapping procedure must * find the color in the palette that is closest to that input pixel * so that the difference between source and destination pixel * is as small as possible. *

    * The code that does the mapping from the original to any given palette * could be shared among quantizers - after all, the goal is always the same, * picking the palette entry with the smallest distance in color space * to the original pixel. * However, sometimes the data structures built while finding the palette * can be reused for faster mapping from the original to output. * This is the case for both the MedianCutQuantizer and the OctreeColorQuantizer. *

  4. *
*

* Dithering methods like error diffusion dithering * may be used to increase * the quality of the output. * Note however that dithering introduces noise that makes the quantized image harder to * compress and in some cases unusable for post-processing (the noise may be an obstacle for * image processing algorithms). *

* This quantizer interface was designed with JIU's error diffusion dithering operation * {@link net.sourceforge.jiu.color.dithering.ErrorDiffusionDithering} in mind. * * * @author Marco Schmidt */ public interface RGBQuantizer { /** * Return a Palette object with the list of colors to be used in the quantization * process. * That palette may be fixed or created specifically for a given input image. * @return Palette object for destination image */ Palette createPalette(); /** * This method maps a triplet of intensity values to its quantized counterpart * and returns the palette index of that quantized color. * The index values for the two arrays are taken from RGBIndex. * @param origRgb the three samples red, green and blue for which a good match is searched in the palette * @param quantizedRgb will hold the three samples found to be closest to origRgb after the call to this method * @return int index in the palette of the match quantizedRgb */ int map(int[] origRgb, int[] quantizedRgb); } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/ArbitraryPaletteQuantizer.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/quantization/ArbitraryPaletteQuantize0000664000000000000000000001444507741250132030625 0ustar /* * ArbitraryPaletteQuantizer * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.quantization; import net.sourceforge.jiu.color.quantization.RGBQuantizer; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * A color quantizer that maps an {@link net.sourceforge.jiu.data.RGBImage} * to any given palette. * This operation is restricted to RGB24Image and palettes with up to 256 colors. * It picks the color from the palette which is closest to the * color to be quantized (with the minimum distance). * This is a rather naive implementation which, for any given color * to be quantized, computes the distance between it and each color * in the palette (read: this operation is rather slow with a large palette and input image). *

* It uses Manhattan distance (L1) * instead of Euclidean distance (L2). * This saves a square root operation per distance computation. *

* There are more sophisticated nearest * neighbor algorithms available, left for future extensions. * *

Usage example

*

This example maps an RGB truecolor image to some palette * we create.

*
 * RGB24Image image = ...; // initialize this
 * // create some Palette object that you want to map the image to
 * Palette palette = new Palette(3); // create palette with three entries
 * palette.put(0, 33, 00, 244); // set first color
 * palette.put(1, 0, 240, 193); // set second color
 * palette.put(2, 245, 126, 136); // set third color
 * ArbitraryPaletteQuantizer quantizer = new ArbitraryPaletteQuantizer(palette);
 * quantizer.setInputImage(image);
 * quantizer.process();
 * PixelImage quantizedImage = quantizer.getOutputImage();
 * 
* * @author Marco Schmidt * @since 0.5.0 */ public class ArbitraryPaletteQuantizer extends ImageToImageOperation implements RGBIndex, RGBQuantizer { private final int[] RED; private final int[] GREEN; private final int[] BLUE; private Palette palette; private int numEntries; /** * Creates a quantizer that will be able to map pixels (or a complete image) * to the argument palette. */ public ArbitraryPaletteQuantizer(Palette palette) { this.palette = palette; numEntries = palette.getNumEntries(); // create 1D lookup tables for the three components and fill them with // the palette entry data RED = new int[numEntries]; GREEN = new int[numEntries]; BLUE = new int[numEntries]; for (int i = 0; i < numEntries; i++) { RED[i] = palette.getSample(INDEX_RED, i); GREEN[i] = palette.getSample(INDEX_GREEN, i); BLUE[i] = palette.getSample(INDEX_BLUE, i); } } /** * Returns a copy of the palette that was given to the * constructor of this class. * @return new copy of the palette this quantizer works on */ public Palette createPalette() { return (Palette)palette.clone(); } public int map(int[] origRgb, int[] quantizedRgb) { int r = origRgb[INDEX_RED]; int g = origRgb[INDEX_GREEN]; int b = origRgb[INDEX_BLUE]; int minIndex = 0; int minDistance = Integer.MAX_VALUE; for (int index = 0; index < numEntries; index++) { int v = r - RED[index]; int distance = v * v; v = g - GREEN[index]; distance += v * v; v = b - BLUE[index]; distance += v * v; if (distance < minDistance) { minDistance = distance; minIndex = index; } } quantizedRgb[INDEX_RED] = RED[minIndex]; quantizedRgb[INDEX_GREEN] = GREEN[minIndex]; quantizedRgb[INDEX_BLUE] = BLUE[minIndex]; return minIndex; } /** * Finds the best match for the argument color in the palette and returns * its index. * Similar to {@link #map(int[], int[])}, the quantized color is not required * and thus a few assignemnts are saved by this method. * @param red red sample of the pixel to be quantized * @param green green sample of the pixel to be quantized * @param blue blue sample of the pixel to be quantized * @return index of the color in the palette that is closest to the argument color */ public int map(int red, int green, int blue) { int minIndex = 0; int minDistance = Integer.MAX_VALUE; for (int index = 0; index < numEntries; index++) { int v = red - RED[index]; int distance = v * v; v = green - GREEN[index]; distance += v * v; v = blue - BLUE[index]; distance += v * v; if (distance < minDistance) { minDistance = distance; minIndex = index; } } return minIndex; } private void process(RGB24Image in, Paletted8Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); if (out == null) { out = new MemoryPaletted8Image(WIDTH, HEIGHT, createPalette()); } for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { out.putSample(0, x, y, map(in.getSample(INDEX_RED, x, y), in.getSample(INDEX_GREEN, x, y), in.getSample(INDEX_BLUE, x, y))); } setProgress(y, HEIGHT); } setOutputImage(out); } /** * Maps the input image to an output image, using the palette given to the constructor. */ public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); PixelImage in = getInputImage(); if (!(in instanceof RGB24Image)) { throw new WrongParameterException("Input image must be of type RGB24Image."); } PixelImage out = getOutputImage(); if (out != null && !(out instanceof Paletted8Image)) { throw new WrongParameterException("Output image must be of type Paletted8Image."); } process((RGB24Image)in, (Paletted8Image)out); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/adjustment/0000775000000000000000000000000010546532076023332 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/adjustment/Contrast.java0000664000000000000000000001267610546773064026012 0ustar /* * Contrast * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.adjustment; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.LookupTableOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Adjusts the contrast of an image. * The amount of adjustment is given to the constructor as a percentage value between -100 and 100. * -100 will make the resulting image middle-gray, 0 will leave it unchanged, 100 will map it to * the eight corners of the color cube. *

Usage examples

* Both examples increase contrast by 30 percent. *

* If all you want is to create a new image with adjusted contrast from the image * data of an existing image, simply use the static helper method: *

PixelImage adjustedImage = Contrast.adjust(inputImage, 30);
* This leaves the original image inputImage unchanged and allocates a second * image object which is here assigned to the variable adjustedImage. *

* If you want more control over parameters, create your own Contrast object. * You can then reuse image objects, e.g. to write the adjusted image data * to the original image object: *

 * Contrast op = new Contrast();
 * op.setInputImage(image);
 * op.setOutputImage(image);
 * op.setContrast(30);
 * op.process();
 * // at this point, image will contain the adjusted image data,
 * // the original data wil be overwritten 
 * 
* @author Marco Schmidt */ public class Contrast extends LookupTableOperation { private int contrast; /** * This static helper method is more simple to use when all * you need are the standard options. * @param input the image to work on * @param percentage contrast modification, from -100 to 100 * @return a new image with adjusted contrast */ public static PixelImage adjust(PixelImage input, int percentage) { try { Contrast op = new Contrast(); op.setInputImage(input); op.setContrast(percentage); op.process(); return op.getOutputImage(); } catch (Exception exc) { return null; } } private int[] createLookupTable(int numSamples, int contrast) { int[] result = new int[numSamples]; final int MAX = numSamples - 1; final float MID = MAX / 2.0f; for (int i = 0; i < numSamples; i++) { if (contrast < 0) { if (i < MID) { result[i] = (int)(i + (MID - i) * (- contrast) / 100.0f); } else { result[i] = (int)(MID + (i - MID) * (100.0f + contrast) / 100.0f); } } else { if (i < MID) { result[i] = (int)(i * (100.0f - contrast) / 100.0f); } else { result[i] = (int)(i + (MAX - i) * contrast / 100.0f); } } } return result; } /** * Returns the contrast adjustment value associated with this opperation. * The value lies between -100 and 100 (including both values). * @return contrast adjustment * @see #setContrast */ public int getContrast() { return contrast; } private void process(Paletted8Image in, Paletted8Image out) { if (out == null) { out = (Paletted8Image)in.createCompatibleImage(in.getWidth(), in.getHeight()); } Palette palette = out.getPalette(); int numSamples = palette.getMaxValue() + 1; final int[] LUT = createLookupTable(numSamples, contrast); for (int c = 0; c < 3; c++) { for (int i = 0; i < palette.getNumEntries(); i++) { palette.putSample(c, i, LUT[palette.getSample(c, i)]); } } for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { out.putSample(0, x, y, in.getSample(0, x, y)); } setProgress(y, in.getHeight()); } setOutputImage(out); } public void process() throws MissingParameterException, WrongParameterException { prepareImages(); IntegerImage in = (IntegerImage)getInputImage(); if (in instanceof GrayIntegerImage || in instanceof RGBIntegerImage) { setNumTables(in.getNumChannels()); for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) { setTable(channelIndex, createLookupTable(in.getMaxSample(channelIndex) + 1, getContrast())); } super.process(); } else if (in instanceof Paletted8Image) { process((Paletted8Image)in, (Paletted8Image)getOutputImage()); } else { throw new WrongParameterException("Contrast operation cannot operate on input image type: " + in.getClass()); } } /** * Sets the value for contrast adjustment to be used within this operation. * @param newContrast new contrast, between -100 and 100 (including both values) * @throws IllegalArgumentException if the new contrast value is not in the above mentioned interval * @see #getContrast */ public void setContrast(int newContrast) { if (newContrast < -100) { throw new IllegalArgumentException("Contrast must be at least -100: " + newContrast); } if (newContrast > 100) { throw new IllegalArgumentException("Contrast must be at most 100: " + newContrast); } contrast = newContrast; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/adjustment/EqualizeHistogram.java0000664000000000000000000000360007741250131027621 0ustar /* * EqualizeHistogram * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.adjustment; import net.sourceforge.jiu.color.analysis.Histogram1DCreator; import net.sourceforge.jiu.color.data.Histogram1D; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.LookupTableOperation; import net.sourceforge.jiu.ops.OperationFailedException; /** * Equalize the image using histogram information separately for each channel. * Works for intensity-based image types like {@link net.sourceforge.jiu.data.Gray8Image} or * {@link net.sourceforge.jiu.data.RGB24Image}. * * @author Marco Schmidt * @since 0.6.0 */ public class EqualizeHistogram extends LookupTableOperation { /** * Creates an object of this class and initializes the lookup * tables with the argument input image. * @param in the input image */ public EqualizeHistogram(IntegerImage in) throws OperationFailedException { super(in.getNumChannels()); setInputImage(in); initTables(in); } private void initTables(IntegerImage in) throws OperationFailedException { for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) { Histogram1DCreator hc = new Histogram1DCreator(); hc.setImage(in, channelIndex); hc.process(); Histogram1D hist = hc.getHistogram(); final int MAX_SAMPLE = in.getMaxSample(channelIndex); int[] data = new int[MAX_SAMPLE + 1]; int NUM_PIXELS = in.getWidth() * in.getHeight(); long sum = 0; for (int i = 0; i < data.length; i++) { sum += hist.getEntry(i); long result = sum * MAX_SAMPLE / NUM_PIXELS; if (result > (long)Integer.MAX_VALUE) { throw new IllegalStateException("Result does not fit into an int."); } data[i] = (int)result; } setTable(channelIndex, data); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/adjustment/package.html0000664000000000000000000000036407741250131025606 0ustar

Contains operations that modify pixel colors independent from other pixels. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/adjustment/HueSaturationValue.java0000664000000000000000000002022010546533516027761 0ustar /* * HueSaturationValue * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.color.adjustment; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Adjusts saturation and value of a color image, optionally hue as well. *

* Supported image types: {@link RGBIntegerImage}, {@link Paletted8Image}. * @author Marco Schmidt * @since 0.5.0 */ public class HueSaturationValue extends ImageToImageOperation implements RGBIndex { private float hue; private boolean modifyHue; private float sMult; private boolean sNegative; private float vMult; private boolean vNegative; private final void adjust(int[] orig, int[] adjusted, final float maxSample) { // get r-g-b as values from 0.0f to 1.0f float r = orig[INDEX_RED] / maxSample; float g = orig[INDEX_GREEN] / maxSample; float b = orig[INDEX_BLUE] / maxSample; // (1) compute h-s-v float max = Math.max(Math.max(r, g), b); float min = Math.min(Math.min(r, g), b); float v = max; float s; if (max != 0.0f) { s = (max - min) / max; } else { s = 0.0f; } float h; if (s == 0.0f) { h = Float.NaN; } else { float delta = max - min; if (r == max) { h = (g - b) / delta; } else if (g == max) { h = 2.0f + (b - r) / delta; } else { h = 4.0f + (r - g) / delta; } h *= 60.0f; if (h < 0.0f) { h += 360.0f; } } // (2) adjust h-s-v if (modifyHue) { h = hue; } if (sNegative) { s *= sMult; } else { s += (1.0f - s) * sMult; } if (vNegative) { v *= vMult; } else { v += (1.0f - v) * vMult; } // (3) convert back to r-g-b if (s == 0.0f) { if (h == Float.NaN) { int value = (int)(v * maxSample); adjusted[INDEX_RED] = value; adjusted[INDEX_GREEN] = value; adjusted[INDEX_BLUE] = value; return; } else { return; } } if (h == 360.0f) { h = 0.0f; } h /= 60.0f; int i = (int)Math.floor(h); float f = h - i; float p = v * (1 - s); float q = v * (1 - (s * f)); float t = v * (1 - (s * (1 - f))); switch(i) { case(0): { adjusted[INDEX_RED] = (int)(v * maxSample); adjusted[INDEX_GREEN] = (int)(t * maxSample); adjusted[INDEX_BLUE] = (int)(p * maxSample); break; } case(1): { adjusted[INDEX_RED] = (int)(q * maxSample); adjusted[INDEX_GREEN] = (int)(v * maxSample); adjusted[INDEX_BLUE] = (int)(p * maxSample); break; } case(2): { adjusted[INDEX_RED] = (int)(p * maxSample); adjusted[INDEX_GREEN] = (int)(v * maxSample); adjusted[INDEX_BLUE] = (int)(t * maxSample); break; } case(3): { adjusted[INDEX_RED] = (int)(p * maxSample); adjusted[INDEX_GREEN] = (int)(q * maxSample); adjusted[INDEX_BLUE] = (int)(v * maxSample); break; } case(4): { adjusted[INDEX_RED] = (int)(t * maxSample); adjusted[INDEX_GREEN] = (int)(p * maxSample); adjusted[INDEX_BLUE] = (int)(v * maxSample); break; } case(5): { adjusted[INDEX_RED] = (int)(v * maxSample); adjusted[INDEX_GREEN] = (int)(p * maxSample); adjusted[INDEX_BLUE] = (int)(q * maxSample); break; } } } private void process(Paletted8Image in, Paletted8Image out) { Palette inPal = in.getPalette(); Palette outPal = out.getPalette(); int[] orig = new int[3]; int[] adjusted = new int[3]; final int MAX = inPal.getMaxValue(); final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int i = 0; i < inPal.getNumEntries(); i++) { orig[INDEX_RED] = inPal.getSample(INDEX_RED, i); orig[INDEX_GREEN] = inPal.getSample(INDEX_GREEN, i); orig[INDEX_BLUE] = inPal.getSample(INDEX_BLUE, i); adjust(orig, adjusted, MAX); outPal.putSample(INDEX_RED, i, adjusted[INDEX_RED]); outPal.putSample(INDEX_GREEN, i, adjusted[INDEX_GREEN]); outPal.putSample(INDEX_BLUE, i, adjusted[INDEX_BLUE]); } for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { out.putSample(0, x, y, in.getSample(0, x, y)); } setProgress(y, HEIGHT); } } private void process(RGBIntegerImage in, RGBIntegerImage out) { final int MAX = in.getMaxSample(0); final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); int[] orig = new int[3]; int[] adjusted = new int[3]; for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { orig[INDEX_RED] = in.getSample(INDEX_RED, x, y); orig[INDEX_GREEN] = in.getSample(INDEX_GREEN, x, y); orig[INDEX_BLUE] = in.getSample(INDEX_BLUE, x, y); adjust(orig, adjusted, MAX); out.putSample(INDEX_RED, x, y, adjusted[INDEX_RED]); out.putSample(INDEX_GREEN, x, y, adjusted[INDEX_GREEN]); out.putSample(INDEX_BLUE, x, y, adjusted[INDEX_BLUE]); } setProgress(y, HEIGHT); } } public void process() throws MissingParameterException, WrongParameterException { PixelImage in = getInputImage(); if (in == null) { throw new MissingParameterException("Input image missing."); } PixelImage out = getOutputImage(); if (out == null) { out = in.createCompatibleImage(in.getWidth(), in.getHeight()); setOutputImage(out); } if (in instanceof RGBIntegerImage) { process((RGBIntegerImage)in, (RGBIntegerImage)out); } else if (in instanceof Paletted8Image) { process((Paletted8Image)in, (Paletted8Image)out); } else { throw new WrongParameterException("Input image type not supported."); } } /** * Set the values for the adjustment of hue, saturation and value (brightness). * Saturation and value must be from the interval -100 to 100 (also see {@link #setSaturationValue(int, int)}). * Hue must be from the interval 0 to 359. * @param hue the hue to be used for the complete image, between 0 and 359 * @param saturation change of saturation, between -100 and 100 * @param value change of saturation, between -100 and 100 * @throws IllegalArgumentException if one of the arguments does not stay within * the valid interval */ public void setHueSaturationValue(int hue, int saturation, int value) { if (hue < 0 || hue >= 360) { throw new IllegalArgumentException("Hue must be from 0..359; got " + hue); } modifyHue = true; this.hue = hue; setSv(saturation, value); } /** * Set the amount of change to saturation and value (brightness) for this operation, * between -100 and 100. * Calling this method also tells the operation not to modify the hue of the image. * @param saturation change of saturation, between -100 and 100 * @param value change of saturation, between -100 and 100 * @throws IllegalArgumentException if one of the two arguments does not stay within * the -100 .. 100 interval */ public void setSaturationValue(int saturation, int value) { modifyHue = false; setSv(saturation, value); } private void setSv(int saturation, int value) { if (saturation < -100 || saturation > 100) { throw new IllegalArgumentException("Saturation must be from -100..100; got " + saturation); } sNegative = (saturation < 0); if (sNegative) { sMult = (100.0f + saturation) / 100.0f; } else if (saturation > 0) { sMult = ((float)saturation) / 100.0f; } else { sMult = 0.0f; } if (value < -100 || value > 100) { throw new IllegalArgumentException("Saturation must be from -100..100; got " + value); } vNegative = (value < 0); if (vNegative) { vMult = (100.0f + value) / 100.0f; } else if (value > 0) { vMult = ((float)value) / 100.0f; } else { vMult = 0.0f; } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/adjustment/NormalizeHistogram.java0000664000000000000000000000344107741250131030005 0ustar /* * NormalizeHistogram * * Copyright (c) 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.adjustment; import net.sourceforge.jiu.color.analysis.Histogram1DCreator; import net.sourceforge.jiu.color.data.Histogram1D; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.LookupTableOperation; import net.sourceforge.jiu.ops.OperationFailedException; /** * Normalize the image using histogram information, separately for each * channel. * Works for intensity-based image types like {@link net.sourceforge.jiu.data.Gray8Image} or * {@link net.sourceforge.jiu.data.RGB24Image}. * * @author Marco Schmidt * @since 0.6.0 */ public class NormalizeHistogram extends LookupTableOperation { /** * Creates an object of this class and initializes the lookup * tables with the argument input image. */ public NormalizeHistogram(IntegerImage in) throws OperationFailedException { super(in.getNumChannels()); setInputImage(in); initTables(in); } private void initTables(IntegerImage in) throws OperationFailedException { for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) { Histogram1DCreator hc = new Histogram1DCreator(); hc.setImage(in, channelIndex); hc.process(); Histogram1D hist = hc.getHistogram(); int min = 0; while (hist.getEntry(min) == 0) { min++; } int maxSample = in.getMaxSample(channelIndex); int max = maxSample; while (hist.getEntry(max) == 0) { max--; } int[] data = new int[maxSample + 1]; int usedInterval = max - min + 1; for (int i = 0; i < data.length; i++) { data[i] = min + usedInterval * i / data.length; } setTable(channelIndex, data); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/adjustment/Brightness.java0000664000000000000000000001261610541052231026274 0ustar /* * Brightness * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.adjustment; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.LookupTableOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Adjusts the brightness of an image. *

* The brightness change is specified as a percentage value between -100 and 100. * -100 will make the resulting image black, 0 will leave it unchanged, 100 will make it white. *

Supported image types

* *

Usage examples

* Both examples make the input image 30 percent brighter. *

* If all you want is to create a new image with adjusted brightness from the image * data of an existing image, simply use the static helper method: *

PixelImage adjustedImage = Brightness.adjust(inputImage, 30);
* This leaves the original image inputImage unchanged and allocates a second * image object which is here assigned to the variable adjustedImage. *

* If you want more control over parameters, create your own Brightness object. * You can then reuse image objects, e.g. to write the adjusted image data * to the original image object: *

 * Brightness brightness = new Brightness();
 * brightness.setInputImage(image);
 * brightness.setOutputImage(image);
 * brightness.setBrightness(30);
 * brightness.process();
 * // at this point, image will contain the adjusted image data,
 * // the original data wil be overwritten 
 * 
*

* @author Marco Schmidt */ public class Brightness extends LookupTableOperation { private int brightness; /** * This static helper method is more simple to use when all * you need are the standard options. * @param input the image to work on * @param percentage brightness modification, from -100 to 100 * @return a new image with adjusted brightness */ public static PixelImage adjust(PixelImage input, int percentage) { try { Brightness brightness = new Brightness(); brightness.setInputImage(input); brightness.setBrightness(percentage); brightness.process(); return brightness.getOutputImage(); } catch (Exception exc) { return null; } } /** * Creates a lookup table that holds all new values for samples 0 to * numSamples - 1. */ private int[] createLookupTable(int numSamples, int brightness) { if (brightness < -100 || brightness > 100) { return null; } int[] result = new int[numSamples]; final int MAX = numSamples - 1; for (int i = 0; i < numSamples; i++) { if (brightness < 0) { result[i] = (int)((float)i * (100.0f + brightness) / 100.0f); } else { result[i] = (int)(i + (MAX - i) * brightness / 100.0f); } } return result; } private void process(Paletted8Image in, Paletted8Image out) { if (out == null) { out = (Paletted8Image)in.createCompatibleImage(in.getWidth(), in.getHeight()); } Palette palette = out.getPalette(); int numSamples = palette.getNumEntries(); final int[] LUT = createLookupTable(numSamples, brightness); for (int c = 0; c < 3; c++) { for (int i = 0; i < palette.getNumEntries(); i++) { palette.putSample(c, i, LUT[palette.getSample(c, i)]); } } for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { out.putSample(0, x, y, in.getSample(0, x, y)); } setProgress(y, in.getHeight()); } setOutputImage(out); } public void process() throws MissingParameterException, WrongParameterException { prepareImages(); IntegerImage in = (IntegerImage)getInputImage(); if (in instanceof GrayIntegerImage || in instanceof RGBIntegerImage) { setNumTables(in.getNumChannels()); for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) { setTable(channelIndex, createLookupTable(in.getMaxSample(channelIndex) + 1, brightness)); } super.process(); } else if (in instanceof Paletted8Image) { process((Paletted8Image)in, (Paletted8Image)getOutputImage()); } else { throw new WrongParameterException("Brightness operation cannot operate on input image type: " + in.getClass()); } } /** * Sets the brightness adjustment value in percent (between -100 and 100). * @param newBrightness the amount of change to be applied to the brightness of the input image * @throws IllegalArgumentException if the argument is smaller than -100 or larger than 100 */ public void setBrightness(int newBrightness) { if (newBrightness < -100) { throw new IllegalArgumentException("Brightness must be at least -100: " + newBrightness); } if (newBrightness > 100) { throw new IllegalArgumentException("Brightness must be at most 100: " + newBrightness); } brightness = newBrightness; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/adjustment/GammaCorrection.java0000664000000000000000000001177207741250131027247 0ustar /* * GammaCorrection * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.adjustment; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.LookupTableOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Corrects the gamma of an image. * Works with {@link net.sourceforge.jiu.data.GrayIntegerImage}, * {@link net.sourceforge.jiu.data.RGBIntegerImage} and * {@link net.sourceforge.jiu.data.Paletted8Image}. * Only the palette is manipulated for paletted images. *

* Changes intensity values by applying the formula * f(x) = MAX * (x / MAX)(1 / gamma) to each * x from [0 ; MAX] to them. * The MAX value is the maximum value allowed for an intensity value of the * corresponding channel. * It is determined by calling {@link net.sourceforge.jiu.data.IntegerImage#getMaxSample} on * the input image. * The gamma parameter must be given to a GammaCorrection operation * before the call to {@link #process} is made. * The valid interval for gamma is (0.0 ; {@link #MAX_GAMMA}] * (so 0.0 is not a valid value). * Gamma values smaller than 1 will make the image darker, values * larger than 1 will make it brighter. *

Usage example

*
 * GammaCorrection gamma = new GammaCorrection();
 * gamma.setInputImage(image);
 * gamma.setGamma(2.2);
 * gamma.process();
 * PixelImage correctedImage = gamma.getOutputImage();
 * 
* @author Marco Schmidt */ public class GammaCorrection extends LookupTableOperation { /** * The maximum allowed value for gamma. */ public static final double MAX_GAMMA = 10.0; private double gamma; /** * Creates a lookup table that holds all new values for samples 0 to * numSamples - 1. */ private final int[] createLookupTable(int numSamples) { if (numSamples < 1) { throw new IllegalArgumentException("Number of samples argument must be one or larger."); } double g = 1.0 / gamma; int[] result = new int[numSamples]; final int MAX_SAMPLE = numSamples - 1; final double MAX = MAX_SAMPLE; for (int i = 0; i < numSamples; i++) { result[i] = (int)Math.round((MAX * Math.pow((i / MAX), g))); if (result[i] < 0) { result[i] = 0; } if (result[i] > MAX_SAMPLE) { result[i] = MAX_SAMPLE; } } return result; } /** * Returns the gamma value to be used for this operation. * @return gamma value between 0 (not included) and {@link #MAX_GAMMA} */ public double getGamma() { return gamma; } private void process(Paletted8Image in, Paletted8Image out) { if (out == null) { out = (Paletted8Image)in.createCompatibleImage(in.getWidth(), in.getHeight()); setOutputImage(out); } Palette palette = out.getPalette(); int numSamples = palette.getMaxValue() + 1; final int[] LUT = createLookupTable(numSamples); for (int c = 0; c < 3; c++) { for (int i = 0; i < palette.getNumEntries(); i++) { palette.putSample(c, i, LUT[palette.getSample(c, i)]); } } for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { out.putSample(x, y, in.getSample(x, y)); } setProgress(y, in.getHeight()); } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); PixelImage in = getInputImage(); if (in instanceof Paletted8Image) { process((Paletted8Image)getInputImage(), (Paletted8Image)getOutputImage()); } else if (in instanceof GrayIntegerImage || in instanceof RGBIntegerImage) { setNumTables(in.getNumChannels()); IntegerImage ii = (IntegerImage)in; for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) { int numSamples = ii.getMaxSample(channelIndex) + 1; int[] table = createLookupTable(numSamples); setTable(channelIndex, table); } super.process(); } else { throw new WrongParameterException("Unsupported image type: " + in.getClass().getName()); } } /** * Sets a new gamma value to be used in this operation. * @param newGamma the new gamma value must be > 0.0 and <= MAX_GAMMA * @throws IllegalArgumentException if the argument is not in the described interval */ public void setGamma(double newGamma) { if (newGamma <= 0.0) { throw new IllegalArgumentException("Gamma must be larger than 0.0; got " + newGamma); } if (newGamma > MAX_GAMMA) { throw new IllegalArgumentException("Gamma must be at most " + MAX_GAMMA + "; got " + newGamma); } gamma = newGamma; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/promotion/0000775000000000000000000000000010546532076023202 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/promotion/PromotionGray16.java0000664000000000000000000000614007741250132027017 0ustar /* * PromotionGray16 * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.promotion; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryGray16Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Converts BilevelImage and Gray8Image objects to Gray16Image objects. * Promotion is a lossless operation that will lead to an output image * that holds the same image in a way that demands more memory. * @author Marco Schmidt * @since 0.11.0 */ public class PromotionGray16 extends ImageToImageOperation { private void prepare(PixelImage in) throws MissingParameterException, WrongParameterException { if (in == null) { throw new MissingParameterException("Missing input image."); } if (! ( (in instanceof BilevelImage) || (in instanceof Gray8Image) ) ) { throw new WrongParameterException("Unsupported input image type: " + in.getClass().getName()); } PixelImage out = getOutputImage(); if (out == null) { setOutputImage(new MemoryGray16Image(in.getWidth(), in.getHeight())); } else { if (!(out instanceof Gray16Image)) { throw new WrongParameterException("Specified output image type must be of class Gray16Image; got " + in.getClass().getName()); } if (in.getWidth() != out.getWidth()) { throw new WrongParameterException("Specified output image must have same width as input image."); } if (in.getHeight() != out.getHeight()) { throw new WrongParameterException("Specified output image must have same height as input image."); } } } private void process(BilevelImage in, Gray16Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); final short MAX = (short)out.getMaxSample(0); final short MIN = 0; for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { if (in.isBlack(x, y)) { out.putShortSample(0, x, y, MIN); } else { out.putShortSample(0, x, y, MAX); } } setProgress(y, HEIGHT); } } private void process(Gray8Image in, Gray16Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int sample = in.getSample(x, y); out.putSample(x, y, sample | (sample << 8)); } setProgress(y, HEIGHT); } } public void process() throws MissingParameterException, WrongParameterException { PixelImage in = getInputImage(); prepare(in); Gray16Image out = (Gray16Image)getOutputImage(); if (in instanceof BilevelImage) { process((BilevelImage)in, out); } else if (in instanceof Gray8Image) { process((Gray8Image)in, out); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/promotion/package.html0000664000000000000000000000147207741250132025460 0ustar

Classes to convert JIU image objects to other image types that require more memory. This is a lossless operation, all information is kept. However, the new image object typically requires more memory to store the same information. A promotion operation is necessary if a certain operation cannot be performed on a smaller image type, but once the image has been promoted to a larger type, the operation becomes possible.

Example: Convolution kernel filtering cannot be applied to paletted images. They require intensity information on each pixel. Once a paletted image has been promoted to be an RGB truecolor image, filtering is possible. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/promotion/PromotionRGB24.java0000664000000000000000000001111610377270770026536 0ustar /* * PromotionRGB24 * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.promotion; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Converts several image types to RGB. * Promoting is a lossless operation that will only lead to an output image * that holds the same image in a way that demands more memory. *

* If you give an image implementing RGB24Image to this operation, a * WrongParameterException will be thrown. * This operation could also return the input image, but this might lead * to the wrong impression that a copy of the input was produced which * can be modified without changing the original. * * @author Marco Schmidt */ public class PromotionRGB24 extends ImageToImageOperation { private void prepare(PixelImage in) throws MissingParameterException, WrongParameterException { if (in == null) { throw new MissingParameterException("Missing input image."); } if (! ( (in instanceof BilevelImage) || (in instanceof Paletted8Image) || (in instanceof Gray8Image) ) ) { throw new WrongParameterException("Unsupported input image type: " + in.getClass().getName()); } PixelImage out = getOutputImage(); if (out == null) { setOutputImage(new MemoryRGB24Image(in.getWidth(), in.getHeight())); } else { if (!(out instanceof RGB24Image)) { throw new WrongParameterException("Specified output image type must be of class RGB24Image; got " + in.getClass().getName()); } if (in.getWidth() != out.getWidth()) { throw new WrongParameterException("Specified output image must have same width as input image."); } if (in.getHeight() != out.getHeight()) { throw new WrongParameterException("Specified output image must have same height as input image."); } } } private void process(BilevelImage in, RGB24Image out) { final int HEIGHT = in.getHeight(); final byte MAX = (byte)255; final byte MIN = (byte)0; for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < in.getWidth(); x++) { if (in.isBlack(x, y)) { out.putByteSample(RGBIndex.INDEX_RED, x, y, MIN); out.putByteSample(RGBIndex.INDEX_GREEN, x, y, MIN); out.putByteSample(RGBIndex.INDEX_BLUE, x, y, MIN); } else { out.putByteSample(RGBIndex.INDEX_RED, x, y, MAX); out.putByteSample(RGBIndex.INDEX_GREEN, x, y, MAX); out.putByteSample(RGBIndex.INDEX_BLUE, x, y, MAX); } } setProgress(y, HEIGHT); } } private void process(Paletted8Image in, RGB24Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); final Palette PAL = in.getPalette(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int value = in.getSample(0, x, y); out.putSample(RGBIndex.INDEX_RED, x, y, PAL.getSample(RGBIndex.INDEX_RED, value)); out.putSample(RGBIndex.INDEX_GREEN, x, y, PAL.getSample(RGBIndex.INDEX_GREEN, value)); out.putSample(RGBIndex.INDEX_BLUE, x, y, PAL.getSample(RGBIndex.INDEX_BLUE, value)); } setProgress(y, HEIGHT); } } private void process(Gray8Image in, RGB24Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int value = in.getSample(0, x, y); out.putSample(RGBIndex.INDEX_RED, x, y, value); out.putSample(RGBIndex.INDEX_GREEN, x, y, value); out.putSample(RGBIndex.INDEX_BLUE, x, y, value); } setProgress(y, HEIGHT); } } public void process() throws MissingParameterException, WrongParameterException { PixelImage in = getInputImage(); prepare(in); RGB24Image out = (RGB24Image)getOutputImage(); if (in instanceof BilevelImage) { process((BilevelImage)in, out); } else if (in instanceof Gray8Image) { process((Gray8Image)in, out); } else if (in instanceof Paletted8Image) { process((Paletted8Image)in, out); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/promotion/PromotionPaletted8.java0000664000000000000000000000655207741250132027607 0ustar /* * PromotionPaletted8 * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.promotion; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Converts {@link BilevelImage} and {@link Gray8Image} objects to * {@link Paletted8Image} objects. * This lossless operation will only lead to an output image * that holds the input image in a way that demands more memory. * * @author Marco Schmidt * @since 0.8.0 */ public class PromotionPaletted8 extends ImageToImageOperation { private void prepare(PixelImage in) throws MissingParameterException, WrongParameterException { if (in == null) { throw new MissingParameterException("Missing input image."); } Palette palette = null; if (in instanceof BilevelImage) { palette = new Palette(2, 255); palette.put(0, 0, 0, 0); palette.put(1, 255, 255, 255); } else if (in instanceof Gray8Image) { palette = new Palette(256, 255); for (int i = 0; i < 256; i++) { palette.put(i, i, i, i); } } else { throw new WrongParameterException("Unsupported input image type: " + in.getClass().getName()); } PixelImage out = getOutputImage(); if (out == null) { setOutputImage(new MemoryPaletted8Image(in.getWidth(), in.getHeight(), palette)); } else { if (!(out instanceof Paletted8Image)) { throw new WrongParameterException("Specified output image type must be of class Paletted8Image; got " + in.getClass().getName()); } if (in.getWidth() != out.getWidth()) { throw new WrongParameterException("Specified output image must have same width as input image."); } if (in.getHeight() != out.getHeight()) { throw new WrongParameterException("Specified output image must have same height as input image."); } } } private void process(BilevelImage in, Paletted8Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { if (in.isBlack(x, y)) { out.putByteSample(0, x, y, (byte)0); } else { out.putByteSample(0, x, y, (byte)1); } } setProgress(y, HEIGHT); } } private void process(Gray8Image in, Paletted8Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); // simple copy for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { out.putSample(0, x, y, in.getSample(0, x, y)); } setProgress(y, HEIGHT); } } public void process() throws MissingParameterException, WrongParameterException { PixelImage in = getInputImage(); prepare(in); Paletted8Image out = (Paletted8Image)getOutputImage(); if (in instanceof BilevelImage) { process((BilevelImage)in, out); } else if (in instanceof Gray8Image) { process((Gray8Image)in, out); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/promotion/PromotionGray8.java0000664000000000000000000000510007741250132026733 0ustar /* * PromotionGray8 * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.promotion; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Converts BilevelImage objects to Gray8Image objects. * Promotion is a lossless operation that will lead to an output image * that holds the same image in a way that demands more memory. * * @author Marco Schmidt * @since 0.8.0 */ public class PromotionGray8 extends ImageToImageOperation { private void prepare(PixelImage in) throws MissingParameterException, WrongParameterException { if (in == null) { throw new MissingParameterException("Missing input image."); } if (! ( (in instanceof BilevelImage) ) ) { throw new WrongParameterException("Unsupported input image type: " + in.getClass().getName()); } PixelImage out = getOutputImage(); if (out == null) { setOutputImage(new MemoryGray8Image(in.getWidth(), in.getHeight())); } else { if (!(out instanceof Gray8Image)) { throw new WrongParameterException("Specified output image type must be of class Gray8Image; got " + in.getClass().getName()); } if (in.getWidth() != out.getWidth()) { throw new WrongParameterException("Specified output image must have same width as input image."); } if (in.getHeight() != out.getHeight()) { throw new WrongParameterException("Specified output image must have same height as input image."); } } } private void process(BilevelImage in, Gray8Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); final byte MAX = (byte)255; final byte MIN = (byte)0; for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { if (in.isBlack(x, y)) { out.putByteSample(0, x, y, MIN); } else { out.putByteSample(0, x, y, MAX); } } setProgress(y, HEIGHT); } } public void process() throws MissingParameterException, WrongParameterException { PixelImage in = getInputImage(); prepare(in); Gray8Image out = (Gray8Image)getOutputImage(); if (in instanceof BilevelImage) { process((BilevelImage)in, out); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/promotion/PromotionRGB48.java0000664000000000000000000001361110377273656026554 0ustar /* * PromotionRGB48 * * Copyright (c) 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.promotion; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryRGB48Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGB48Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Converts several image types to {@link net.sourceforge.jiu.data.RGB48Image}. * Promotion is a lossless operation that will only lead to an output image * that holds the same image in a way that demands more memory. *

* If you give an image implementing RGB24Image to this operation, a * WrongParameterException will be thrown. * This operation could also return the input image, but this might lead * to the wrong impression that a copy of the input was produced which * can be modified without changing the original. * @author Marco Schmidt * @since 0.12.0 */ public class PromotionRGB48 extends ImageToImageOperation { private void prepare(PixelImage in) throws MissingParameterException, WrongParameterException { if (! ( (in instanceof BilevelImage) || (in instanceof Paletted8Image) || (in instanceof Gray16Image)|| (in instanceof Gray8Image) || (in instanceof RGB24Image) ) ) { throw new WrongParameterException("Unsupported input image type: " + in.getClass().getName()); } PixelImage out = getOutputImage(); if (out == null) { setOutputImage(new MemoryRGB48Image(in.getWidth(), in.getHeight())); } else { if (!(out instanceof RGB48Image)) { throw new WrongParameterException("Specified output image type " + "must be of class RGB48Image; got " + in.getClass().getName()); } ensureImagesHaveSameResolution(); } } private void process(BilevelImage in, RGB48Image out) { final int HEIGHT = in.getHeight(); final short MAX = (short)65535; final short MIN = (short)0; for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < in.getWidth(); x++) { if (in.isBlack(x, y)) { out.putShortSample(RGBIndex.INDEX_RED, x, y, MIN); out.putShortSample(RGBIndex.INDEX_GREEN, x, y, MIN); out.putShortSample(RGBIndex.INDEX_BLUE, x, y, MIN); } else { out.putShortSample(RGBIndex.INDEX_RED, x, y, MAX); out.putShortSample(RGBIndex.INDEX_GREEN, x, y, MAX); out.putShortSample(RGBIndex.INDEX_BLUE, x, y, MAX); } } setProgress(y, HEIGHT); } } private void process(Paletted8Image in, RGB48Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); final Palette PAL = in.getPalette(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int value = in.getSample(0, x, y); int red = PAL.getSample(RGBIndex.INDEX_RED, value); int green = PAL.getSample(RGBIndex.INDEX_GREEN, value); int blue = PAL.getSample(RGBIndex.INDEX_BLUE, value); out.putSample(RGBIndex.INDEX_RED, x, y, red | red << 8); out.putSample(RGBIndex.INDEX_GREEN, x, y, green | green << 8); out.putSample(RGBIndex.INDEX_BLUE, x, y, blue | blue << 8); } setProgress(y, HEIGHT); } } private void process(Gray16Image in, RGB48Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int value = in.getSample(0, x, y); out.putSample(RGBIndex.INDEX_RED, x, y, value); out.putSample(RGBIndex.INDEX_GREEN, x, y, value); out.putSample(RGBIndex.INDEX_BLUE, x, y, value); } setProgress(y, HEIGHT); } } private void process(Gray8Image in, RGB48Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int value = in.getSample(0, x, y); value = value | value << 8; out.putSample(RGBIndex.INDEX_RED, x, y, value); out.putSample(RGBIndex.INDEX_GREEN, x, y, value); out.putSample(RGBIndex.INDEX_BLUE, x, y, value); } setProgress(y, HEIGHT); } } private void process(RGB24Image in, RGB48Image out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y); red = red << 8 | red; int green = in.getSample(RGBIndex.INDEX_GREEN, x, y); green = green << 8 | green; int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y); blue = blue << 8 | blue; out.putSample(RGBIndex.INDEX_RED, x, y, red); out.putSample(RGBIndex.INDEX_GREEN, x, y, green); out.putSample(RGBIndex.INDEX_BLUE, x, y, blue); } setProgress(y, HEIGHT); } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); PixelImage in = getInputImage(); prepare(in); RGB48Image out = (RGB48Image)getOutputImage(); if (in instanceof BilevelImage) { process((BilevelImage)in, out); } else if (in instanceof Gray16Image) { process((Gray16Image)in, out); } else if (in instanceof Gray8Image) { process((Gray8Image)in, out); } else if (in instanceof Paletted8Image) { process((Paletted8Image)in, out); } else if (in instanceof RGB24Image) { process((RGB24Image)in, out); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/conversion/0000775000000000000000000000000010546532076023341 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/conversion/LogLuvConversion.java0000664000000000000000000002655307741250131027465 0ustar /* * LogLuvConversion * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.conversion; /** * Convert from LogLuv color representation to RGB color space and * from LogL to grayscale. *

* This implementation is based on the file tif_luv.c which * is part of the TIFF library libtiff. * The original implementation was written by Greg W. Larson. *

* Learn more about the color type and its encoding on Greg's page * LogLuv * Encoding for TIFF Images. * @author Marco Schmidt * @since 0.10.0 */ public class LogLuvConversion { private static final double M_LN2 = 0.69314718055994530942; private static final double UVSCALE = 410.0; // constants from libtiff's uv_decode.h private static final float UV_SQSIZ = 0.003500f; private static final int UV_NDIVS = 16289; private static final float UV_VSTART = 0.016940f; private static final int UV_NVS = 163; private static final double U_NEU = 0.210526316; private static final double V_NEU = 0.473684211; private static final double[] USTART = { 0.247663, 0.243779, 0.241684, 0.237874, 0.235906, 0.232153, 0.228352, 0.226259, 0.222371, 0.220410, 0.214710, 0.212714, 0.210721, 0.204976, 0.202986, 0.199245, 0.195525, 0.193560, 0.189878, 0.186216, 0.186216, 0.182592, 0.179003, 0.175466, 0.172001, 0.172001, 0.168612, 0.168612, 0.163575, 0.158642, 0.158642, 0.158642, 0.153815, 0.153815, 0.149097, 0.149097, 0.142746, 0.142746, 0.142746, 0.138270, 0.138270, 0.138270, 0.132166, 0.132166, 0.126204, 0.126204, 0.126204, 0.120381, 0.120381, 0.120381, 0.120381, 0.112962, 0.112962, 0.112962, 0.107450, 0.107450, 0.107450, 0.107450, 0.100343, 0.100343, 0.100343, 0.095126, 0.095126, 0.095126, 0.095126, 0.088276, 0.088276, 0.088276, 0.088276, 0.081523, 0.081523, 0.081523, 0.081523, 0.074861, 0.074861, 0.074861, 0.074861, 0.068290, 0.068290, 0.068290, 0.068290, 0.063573, 0.063573, 0.063573, 0.063573, 0.057219, 0.057219, 0.057219, 0.057219, 0.050985, 0.050985, 0.050985, 0.050985, 0.050985, 0.044859, 0.044859, 0.044859, 0.044859, 0.040571, 0.040571, 0.040571, 0.040571, 0.036339, 0.036339, 0.036339, 0.036339, 0.032139, 0.032139, 0.032139, 0.032139, 0.027947, 0.027947, 0.027947, 0.023739, 0.023739, 0.023739, 0.023739, 0.019504, 0.019504, 0.019504, 0.016976, 0.016976, 0.016976, 0.016976, 0.012639, 0.012639, 0.012639, 0.009991, 0.009991, 0.009991, 0.009016, 0.009016, 0.009016, 0.006217, 0.006217, 0.005097, 0.005097, 0.005097, 0.003909, 0.003909, 0.002340, 0.002389, 0.001068, 0.001653, 0.000717, 0.001614, 0.000270, 0.000484, 0.001103, 0.001242, 0.001188, 0.001011, 0.000709, 0.000301, 0.002416, 0.003251, 0.003246, 0.004141, 0.005963, 0.008839, 0.010490, 0.016994, 0.023659, }; private static final short[] NCUM = { 0, 4, 10, 17, 26, 36, 48, 62, 77, 94, 112, 133, 155, 178, 204, 231, 260, 291, 323, 357, 393, 429, 467, 507, 549, 593, 637, 683, 729, 778, 830, 882, 934, 989, 1044, 1102, 1160, 1222, 1284, 1346, 1411, 1476, 1541, 1610, 1679, 1752, 1825, 1898, 1975, 2052, 2129, 2206, 2288, 2370, 2452, 2538, 2624, 2710, 2796, 2887, 2978, 3069, 3164, 3259, 3354, 3449, 3549, 3649, 3749, 3849, 3954, 4059, 4164, 4269, 4379, 4489, 4599, 4709, 4824, 4939, 5054, 5169, 5288, 5407, 5526, 5645, 5769, 5893, 6017, 6141, 6270, 6399, 6528, 6657, 6786, 6920, 7054, 7188, 7322, 7460, 7598, 7736, 7874, 8016, 8158, 8300, 8442, 8588, 8734, 8880, 9026, 9176, 9326, 9476, 9630, 9784, 9938, 10092, 10250, 10408, 10566, 10727, 10888, 11049, 11210, 11375, 11540, 11705, 11873, 12041, 12209, 12379, 12549, 12719, 12892, 13065, 13240, 13415, 13590, 13767, 13944, 14121, 14291, 14455, 14612, 14762, 14905, 15041, 15170, 15293, 15408, 15517, 15620, 15717, 15806, 15888, 15964, 16033, 16095, 16150, 16197, 16237, 16268, }; private LogLuvConversion() { } /** * Converts an unsigned 10 bit value (the argument must lie in the * interval 0 to 1023) to a double luminance * (brightness) value between 0.0 and 1.0. * This conversion is needed by both LogLuv to XYZ and LogL to grayscale. * @param p10 input LogL value * @return double value with luminance, between 0 and 1 */ public static double convertLogL10toY(int p10) { if (p10 == 0) { return 0.0; } else { return Math.exp(M_LN2 / 64.0 * (p10 + 0.5) - M_LN2 * 12.0); } } /** * Converts a signed 16 bit value (the argument must lie in the * interval -32768 to 32767) to a double luminance * (brightness) value between 0.0 and 1.0. * This conversion is needed by both LogLuv to XYZ and LogL to grayscale. * @param p16 input LogL value * @return double value with luminance, between 0 and 1 */ public static double convertLogL16toY(int p16) { int Le = p16 & 0x7fff; if (Le == 0) { return 0.0; } double Y = Math.exp(M_LN2 / 256.0 * (Le + 0.5) - M_LN2 * 64.0); if ((p16 & 0x8000) == 0) { return Y; } else { return -Y; } } private static byte convertDoubleToByte(double d) { if (d <= 0.0) { return 0; } else if (d >= 1.0) { return (byte)255; } else { double result = 255.0 * Math.sqrt(d); return (byte)result; } } /** * Converts a number of 24 bit LogLuv pixels to 24 bit RGB pixels. * Each LogLuv pixel is stored as three consecutive bytes in the logluv byte array. * The first byte and the top two bits of the second are the LogL value, the remaining * 14 bits are an index that encodes u and v. * @param logluv byte array with LogLuv data, must be at least num * 3 bytes large * @param red the byte samples for the red channel will be written to this array * @param green the byte samples for the green channel will be written to this array * @param blue the byte samples for the blue channel will be written to this array * @param num number of pixels to be converted */ public static void convertLogLuv24InterleavedtoRGB24Planar(byte[] logluv, byte[] red, byte[] green, byte[] blue, int num) { int srcOffs = 0; int destOffs = 0; while (num-- != 0) { // convert from LogLuv24 to XYZ float X = 0.0f; float Y = 0.0f; float Z = 0.0f; // first byte and top two bits of second make 10 bit value L10 int v1 = logluv[srcOffs++] & 0xff; int v2 = logluv[srcOffs++] & 0xff; int v3 = logluv[srcOffs++] & 0xff; double L = convertLogL10toY(v1 << 2 | ((v2 >> 6) & 0x03)); if (L > 0.0) { int c = ((v2 & 0x3f) << 8) | v3; double u = U_NEU; double v = V_NEU; // uv_decode in tif_luv.c int upper = UV_NVS; int lower = 0; if (c >= 0 && c < UV_NDIVS) { lower = 0; upper = UV_NVS; int ui = 0; int vi = 0; while (upper - lower > 1) { vi = (lower + upper) >> 1; ui = c - NCUM[vi]; if (ui > 0) { lower = vi; } else if (ui < 0) { upper = vi; } else { lower = vi; break; } } vi = lower; ui = c - NCUM[vi]; u = USTART[vi] + (ui + 0.5) * UV_SQSIZ; v = UV_VSTART + (vi + 0.5) * UV_SQSIZ; } double s = 1.0 / (6.0 * u - 16.0 * v + 12.0); double x = 9.0 * u * s; double y = 4.0 * v * s; X = (float)(x / y * L); Y = (float)L; Z = (float)((1.0 - x - y) / y * L); } // convert from XYZ to RGB double r = 2.690 * X + -1.276 * Y + -0.414 * Z; double g = -1.022 * X + 1.978 * Y + 0.044 * Z; double b = 0.061 * X + -0.224 * Y + 1.163 * Z; red[destOffs] = convertDoubleToByte(r); green[destOffs] = convertDoubleToByte(g); blue[destOffs] = convertDoubleToByte(b); destOffs++; } } /** * Converts a number of 32 bit LogLuv pixels to 24 bit RGB pixels. * Each LogLuv pixel is stored as four consecutive bytes in the logluv byte array. * The first two bytes represent the LogL value (most significant bytefirst), followed * by the u value and then the v value. * @param logluv byte array with LogLuv data, must be at least num * 4 bytes large * @param red the byte samples for the red channel will be written to this array * @param green the byte samples for the green channel will be written to this array * @param blue the byte samples for the blue channel will be written to this array * @param num number of pixels to be converted */ public static void convertLogLuv32InterleavedtoRGB24Planar(byte[] logluv, byte[] red, byte[] green, byte[] blue, int num) { int srcOffs = 0; int destOffs = 0; while (num-- != 0) { // convert from LogLuv32 to XYZ float X = 0.0f; float Y = 0.0f; float Z = 0.0f; int v1 = logluv[srcOffs++] & 0xff; int v2 = logluv[srcOffs++] & 0xff; double L = convertLogL16toY((short)((v1 << 8) | v2)); if (L > 0.0) { // decode color double u = 1.0 / UVSCALE * ((logluv[srcOffs++] & 0xff) + 0.5); double v = 1.0 / UVSCALE * ((logluv[srcOffs++] & 0xff) + 0.5); double s = 1.0 / (6.0 * u - 16.0 * v + 12.0); double x = 9.0 * u * s; double y = 4.0 * v * s; X = (float)(x / y * L); Y = (float)L; Z = (float)((1.0 - x - y) / y * L); } // convert from XYZ to RGB double r = 2.690 * X + -1.276 * Y + -0.414 * Z; double g = -1.022 * X + 1.978 * Y + 0.044 * Z; double b = 0.061 * X + -0.224 * Y + 1.163 * Z; red[destOffs] = convertDoubleToByte(r); green[destOffs] = convertDoubleToByte(g); blue[destOffs] = convertDoubleToByte(b); destOffs++; } } /** * Converts a number of 16 bit LogL samples to 8 bit grayscale samples. * @param logl byte array with LogL samples, each 16 bit sample is stored as * two consecutive bytes in order most-significant-byte least-significant-byte (network byte order); * the array must be at least num * 2 entries large * @param gray the byte array to which the converted samples will be written * @param num the number of samples to be converted */ public static void convertLogL16toGray8(byte[] logl, byte[] gray, int num) { int srcOffs = 0; int destOffs = 0; while (num-- != 0) { int v1 = logl[srcOffs++] & 0xff; int v2 = logl[srcOffs++] & 0xff; double L = convertLogL16toY((short)((v1 << 8) | v2)); gray[destOffs++] = convertDoubleToByte(L); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/conversion/PCDYCbCrConversion.java0000664000000000000000000001264107741250132027540 0ustar /* * PCDYCbCrConversion * * Copyright (c) 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.conversion; import net.sourceforge.jiu.color.YCbCrIndex; import net.sourceforge.jiu.data.RGBIndex; /** * Convert from YCbCr color space (as used in Kodak PCD files) to * RGB. Only works for 24 bits per pixel (8 bits per channel) image * data. * * @author Marco Schmidt */ public class PCDYCbCrConversion implements RGBIndex, YCbCrIndex { // color conversion coefficients (YCbCr to RGB) private static final float c11 = 0.0054980f * 256; private static final float c12 = 0.0000000f * 256; private static final float c13 = 0.0051681f * 256; private static final float c21 = 0.0054980f * 256; private static final float c22 = -0.0015446f * 256; private static final float c23 = -0.0026325f * 256; private static final float c31 = 0.0054980f * 256; private static final float c32 = 0.0079533f * 256; private static final float c33 = 0.0000000f * 256; private PCDYCbCrConversion() { } private static byte floatToByte(float f) { if (f <= 0.0) { return 0; } if (f >= 255.0) { return (byte)255; } return (byte)((int)f); } /* * Converts the color given by (y, cb, cr) to RGB color space. * The three int variables y, cb and cr must be from the * interval 0 to 255 (this is not checked). * The rgb array will get the resulting RGB color, so it must * have at least three entries. * The three entries in that array will also be from 0 to 255 each. * public static void convertYCbCrToRgb(int y, int cb, int cr, int[] rgb) { int cr137 = cr - 137; int cb156 = cb - 156; rgb[INDEX_RED] = floatToInt(c11 * y + c12 * (cb156) + c13 * (cr137)); rgb[INDEX_GREEN] = floatToInt(c21 * y + c22 * (cb156) + c23 * (cr137)); rgb[INDEX_BLUE] = floatToInt(c31 * y + c32 * (cb156) + c33 * (cr137)); } public static int[] convertToRgb(byte[][] data, int width, int height) throws IllegalArgumentException { if (width < 1 || height < 1) { throw new IllegalArgumentException("Error -- width and height must be larger " + "than 0 (width=" + width + ", height=" + height); } if (data == null) { throw new IllegalArgumentException("Error -- data array must not be null."); } if (data.length < 3) { throw new IllegalArgumentException("Error -- data array must have at least " + "three items (has " + data.length); } int numPixels = width * height; int[] result = new int[numPixels]; int[] rgb = new int[3]; for (int i = 0; i < numPixels; i++) { int gray = data[INDEX_Y][i] & 0xff; int cb = data[INDEX_CB][i] & 0xff; int cr = data[INDEX_CR][i] & 0xff; convertYCbCrToRgb(gray, cb, cr, rgb); result[i] = 0xff000000 | rgb[0] << 16 | (rgb[1] << 8) | (rgb[2]); } return result; }*/ private static void checkArray(byte[] data, int offset, int num) throws IllegalArgumentException { if (data == null) { throw new IllegalArgumentException("Data array must be initialized."); } if (offset < 0 || offset + num > data.length) { throw new IllegalArgumentException("Invalid combination of " + "offset, number and array length: offset=" + offset + ", num=" + num + ", data.length=" + data.length); } } /** * Converts pixels from YCbCr to RGB color space. * Input pixels are given as three byte arrays for luminance and the * two chroma components. * Same for output pixels, three other arrays for red, green and blue. * Offset values can be specified separately for the YCbCr and the RGB * arrays. * @param y the array of gray source samples * @param cb the array of chroma blue source samples * @param cr the array of chroma red source samples * @param yccOffset offset value into the arrays y, cb and cr; color * conversion will be started at the yccOffset'th value of each array * @param r the array of red destination samples * @param g the array of green destination samples * @param b the array of blue destination samples * @param rgbOffset offset value into the arrays r, g and b; destination samples * will be written to the three arrays starting at the rgbOffset'th value of each array * @param num the number of pixels to be converted * @throws IllegalArgumentException if one of the int values is negative or one * of the arrays is null or too small */ public static void convertYccToRgb( byte[] y, byte[] cb, byte[] cr, int yccOffset, byte[] r, byte[] g, byte[] b, int rgbOffset, int num) throws IllegalArgumentException { if (num < 0) { throw new IllegalArgumentException("Negative number of pixels " + "to be converted is invalid: " + num); } checkArray(y, yccOffset, num); checkArray(cb, yccOffset, num); checkArray(cr, yccOffset, num); checkArray(r, rgbOffset, num); checkArray(g, rgbOffset, num); checkArray(b, rgbOffset, num); while (num-- > 0) { int gray = y[yccOffset] & 0xff; int chromaBlue = cb[yccOffset] & 0xff; int chromaRed = cr[yccOffset++] & 0xff; int cr137 = chromaRed - 137; int cb156 = chromaBlue - 156; r[rgbOffset] = floatToByte(c11 * gray + c12 * (cb156) + c13 * (cr137)); g[rgbOffset] = floatToByte(c21 * gray + c22 * (cb156) + c23 * (cr137)); b[rgbOffset++] = floatToByte(c31 * gray + c32 * (cb156) + c33 * (cr137)); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/conversion/CMYKConversion.java0000664000000000000000000000750507741250131027014 0ustar /* * CMYKConversion * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.conversion; import net.sourceforge.jiu.data.RGBIndex; /** * Convert from CMYK color space to RGB color space. * @author Marco Schmidt * @since 0.10.0 */ public class CMYKConversion { private CMYKConversion() { } private static int convertToByte(int value) { if (value <= 0) { return 0; } else if (value >= 255) { return 255; } else { return value; } } /** * Converts a 32 bit CMYK pixel to a 24 bit RGB pixel. * Red, green and blue sample will be written at the indexes that {@link net.sourceforge.jiu.data.RGBIndex} defines for them. * @param cyan the cyan sample, must lie in the interval 0 to 255 * @param magenta the magenta sample, must lie in the interval 0 to 255 * @param yellow the yellow sample, must lie in the interval 0 to 255 * @param black the black sample, must lie in the interval 0 to 255 * @param rgb byte array for the destination R-G-B pixel, must have length 3 or larger, will be accessed using RGBIndex, each sample will lie in the interval 0 to 255 */ public static void convertCMYK32ToRGB24(int cyan, int magenta, int yellow, int black, int[] rgb) { int red = 255 - cyan; int green = 255 - magenta; int blue = 255 - yellow; red -= black; green -= black; blue -= black; rgb[RGBIndex.INDEX_RED] = convertToByte(red); rgb[RGBIndex.INDEX_GREEN] = convertToByte(green); rgb[RGBIndex.INDEX_BLUE] = convertToByte(blue); } /** * Converts a number of CMYK pixels stored in interleaved order (all samples of one pixel * together: CMYKCMYKCMYK...) to RGB pixels which are stored as planes (all red samples * together, etc.). * @param cmyk a byte array with numPixels times four samples stored in order C-M-Y-K * @param cmykOffset the index of the first byte that is to be accessed * @param red the byte array to which the red samples will be written by this method * @param redOffset the offset into the red array of the first sample to be written * @param green the byte array to which the green samples will be written by this method * @param greenOffset the offset into the green array of the first sample to be written * @param blue the byte array to which the blue samples will be written by this method * @param blueOffset the offset into the blue array of the first sample to be written */ public static void convertCMYK32InterleavedToRGB24Planar( byte[] cmyk, int cmykOffset, byte[] red, int redOffset, byte[] green, int greenOffset, byte[] blue, int blueOffset, int numPixels) { int[] rgb = new int[3]; while (numPixels-- != 0) { convertCMYK32ToRGB24( cmyk[cmykOffset] & 0xff, cmyk[cmykOffset + 1] & 0xff, cmyk[cmykOffset + 2] & 0xff, cmyk[cmykOffset + 3] & 0xff, rgb); cmykOffset += 4; red[redOffset++] = (byte)rgb[RGBIndex.INDEX_RED]; green[greenOffset++] = (byte)rgb[RGBIndex.INDEX_GREEN]; blue[blueOffset++] = (byte)rgb[RGBIndex.INDEX_BLUE]; } } public static void convertCMYK32PlanarToRGB24Planar( byte[] cyan, int cyanOffset, byte[] magenta, int magentaOffset, byte[] yellow, int yellowOffset, byte[] black, int blackOffset, byte[] red, int redOffset, byte[] green, int greenOffset, byte[] blue, int blueOffset, int numPixels) { int[] rgb = new int[3]; while (numPixels-- != 0) { convertCMYK32ToRGB24( cyan[cyanOffset++] & 0xff, magenta[magentaOffset++] & 0xff, yellow[yellowOffset++] & 0xff, black[blackOffset++] & 0xff, rgb); red[redOffset++] = (byte)rgb[RGBIndex.INDEX_RED]; green[greenOffset++] = (byte)rgb[RGBIndex.INDEX_GREEN]; blue[blueOffset++] = (byte)rgb[RGBIndex.INDEX_BLUE]; } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/conversion/package.html0000664000000000000000000000065307741250131025616 0ustar

Classes to improve the results of color reduction algorithms. Some algorithms work by modifying pixel values before they are reduced, others compute the error of a pixel reduction and distributes that error over neighboring pixels that are yet to be reduced. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/reduction/0000775000000000000000000000000010546532076023150 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/reduction/ReduceRGB.java0000664000000000000000000001116510104713600025541 0ustar /* * ReduceRGB * * Copyright (c) 2003, 2004 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.reduction; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.MemoryRGB48Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGB48Image; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Reduces the color depth of RGB truecolor images. * This class uses a simple approach, it just drops some of the * lowest bits and scales the value back to eight or sixteen bits per sample. *

Supported image classes

* This class works with {@link net.sourceforge.jiu.data.RGB24Image} * and {@link net.sourceforge.jiu.data.RGB48Image}. *

Usage example

* Reduce a 24 or 48 bits per pixel RGB image to 15 bits per pixel: *
 * PixelImage inputImage = ...; // initialize
 * ReduceRGB reduce = new ReduceRGB();
 * reduce.setBitsPerSample(5);
 * reduce.setInputImage(inputImage);
 * reduce.process();
 * PixelImage reducedImage = reduce.getOutputImage();
 * 
* @author Marco Schmidt * @since 0.12.0 * @see net.sourceforge.jiu.color.reduction.ReduceShadesOfGray */ public class ReduceRGB extends ImageToImageOperation { /** * Number of significant bits per channel in the destination RGB image. */ private Integer destBits; /*private byte[] createByteLut(int inDepth) { int outDepth = destBits.intValue(); lut = new int[1 << inDepth]; final int SHIFT = inDepth - outDepth; final int MAX_IN_VALUE = (1 << inDepth) - 1; final int MAX_OUT_VALUE = (1 << outDepth) - 1; for (int i = 0; i < lut.length; i++) { int value = i >> SHIFT; lut[i] = (value * MAX_IN_VALUE) / MAX_OUT_VALUE; } }*/ public void process() throws MissingParameterException, WrongParameterException { if (destBits == null) { throw new MissingParameterException( "The number of destination bits has not been specified."); } int bits = destBits.intValue(); ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); boolean rgb24 = in instanceof RGB24Image; boolean rgb48 = in instanceof RGB48Image; if (!(rgb24 || rgb48)) { throw new WrongParameterException( "Input image must be either RGB24Image or RGB48Image."); } if (rgb24 && bits >= 8) { throw new WrongParameterException( "Number of output bits per sample must be 7 or lower for RGB24Image."); } PixelImage out = getOutputImage(); int inDepth = 0; if (rgb24) { inDepth = 8; } if (rgb48) { inDepth = 16; } int maxOutputValue = 1; int maxShiftedValue = (1 << bits) - 1; if (bits <= 8) { if (out == null) { out = new MemoryRGB24Image(in.getWidth(), in.getHeight()); } else { if (!(out instanceof RGB24Image)) { throw new WrongParameterException( "Output image must be of type RGB24Image."); } } maxOutputValue = 255; } else if (bits <= 16) { if (out == null) { out = new MemoryRGB48Image(in.getWidth(), in.getHeight()); } else { if (!(out instanceof RGB48Image)) { throw new WrongParameterException( "Output image must be of type RGB48Image."); } } maxOutputValue = 65535; } else { throw new WrongParameterException("Can only process up to 16 bits per sample."); } final int SHIFT = inDepth - bits; IntegerImage ii = (IntegerImage)in; IntegerImage oo = (IntegerImage)out; for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { for (int c = 0; c < 3; c++) { int inputSample = ii.getSample(c, x, y); int outputSample = ((inputSample >> SHIFT) * maxOutputValue) / maxShiftedValue; oo.putSample(c, x, y, outputSample); } } setProgress(y, in.getHeight()); } } /** * Specifies the number of bits per sample in the output image. * @param bits number of bits in output image, from 1 to 15 * @throws IllegalArgumentException if bits is smaller than 1 or larger than 15 */ public void setBitsPerSample(int bits) { if (bits < 1) { throw new IllegalArgumentException("Number of bits must be 1 or larger."); } if (bits > 15) { throw new IllegalArgumentException("Number of bits must be 15 or smaller."); } destBits = new Integer(bits); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/reduction/AutoDetectColorType.java0000664000000000000000000004312010546763021027711 0ustar /* * AutoDetectColorType * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.reduction; import net.sourceforge.jiu.color.data.Histogram3D; import net.sourceforge.jiu.color.data.OnDemandHistogram3D; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGB48Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.Operation; import net.sourceforge.jiu.ops.WrongParameterException; /** * Detects the minimum (in terms of memory) color type of an image. * Can convert the original image to that new input type on demand. *

* Input parameters: image to be examined, boolean that specifies whether * conversion will be performed (default is true, conversion is performed). * Output parameters: converted image, boolean that expresses whether * a conversion was possible. *

* Supported types for input image: RGB24Image, Gray8Image, Paletted8Image. *

* BilevelImage is not supported because there is no smaller image type, * so bilevel images cannot be reduced. *

* This operation is not a {@link net.sourceforge.jiu.ops.ImageToImageOperation} because this * class need not necessarily produce a new image * (with {@link #setConversion}(false)). *

Usage example

* This code snippet loads an image and attempts to reduce it to the * minimum color type that will hold it. *
 * PixelImage image = ImageLoader.load("test.bmp");
 * AutoDetectColorType op = new AutoDetectColorType();
 * op.setInputImage(image);
 * op.process();
 * if (op.isReducible())
 * {
 *   image = op.getOutputImage();
 * }
 * 
* * @author Marco Schmidt */ public class AutoDetectColorType extends Operation { public static final int TYPE_UNKNOWN = -1; public static final int TYPE_BILEVEL = 0; public static final int TYPE_GRAY16 = 4; public static final int TYPE_GRAY8 = 1; public static final int TYPE_PALETTED8 = 2; public static final int TYPE_RGB24 = 3; public static final int TYPE_RGB48 = 5; private PixelImage inputImage; private PixelImage outputImage; private boolean doConvert; private int type; private Histogram3D hist; public AutoDetectColorType() { doConvert = true; type = TYPE_UNKNOWN; } /** * Creates a bilevel image from any grayscale (or RGB) image * that has been checked to be bilevel. */ private void createBilevelFromGrayOrRgb(IntegerImage in) { MemoryBilevelImage out = new MemoryBilevelImage(in.getWidth(), in.getHeight()); out.clear(BilevelImage.BLACK); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { if (in.getSample(0, x, y) != 0) { out.putWhite(x, y); } } setProgress(y, in.getHeight()); } outputImage = out; } private void createBilevelFromPaletted(Paletted8Image in) { Palette palette = in.getPalette(); MemoryBilevelImage out = new MemoryBilevelImage(in.getWidth(), in.getHeight()); out.clear(BilevelImage.BLACK); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { if (palette.getSample(RGBIndex.INDEX_RED, in.getSample(0, x, y)) != 0) { out.putWhite(x, y); } } setProgress(y, in.getHeight()); } outputImage = out; } // works for RGB24 and RGB48 input image, assumed that // a matching output image type was chosen (Gray8 for RGB24, Gray16 for // RGB48) private void createGrayFromRgb(IntegerImage in, IntegerImage out) { for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { out.putSample(0, x, y, in.getSample(0, x, y)); } setProgress(y, in.getHeight()); } outputImage = out; } private void createGray8FromGray16(Gray16Image in) { Gray8Image out = new MemoryGray8Image(in.getWidth(), in.getHeight()); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { out.putSample(0, x, y, in.getSample(0, x, y) & 0xff); } setProgress(y, in.getHeight()); } outputImage = out; } // assumes that in fact has a palette with gray colors only private void createGray8FromPaletted8(Paletted8Image in, Gray8Image out) { Palette palette = in.getPalette(); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { out.putSample(0, x, y, palette.getSample(0, in.getSample(0, x, y))); } setProgress(y, in.getHeight()); } outputImage = out; } private void createPaletted8FromRgb24(RGB24Image in) { // create palette from histogram int uniqueColors = hist.getNumUsedEntries(); Palette palette = new Palette(uniqueColors, 255); int index = 0; for (int r = 0; r < 256; r++) { for (int g = 0; g < 256; g++) { for (int b = 0; b < 256; b++) { if (hist.getEntry(r, g, b) != 0) { hist.setEntry(r, g, b, index); palette.putSample(RGBIndex.INDEX_RED, index, r); palette.putSample(RGBIndex.INDEX_GREEN, index, g); palette.putSample(RGBIndex.INDEX_BLUE, index, b); index++; } } } } Paletted8Image out = new MemoryPaletted8Image(in.getWidth(), in.getHeight(), palette); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y); int green = in.getSample(RGBIndex.INDEX_GREEN, x, y); int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y); out.putSample(0, x, y, hist.getEntry(red, green, blue)); } setProgress(y, in.getHeight()); } outputImage = out; } private void createPaletted8FromRgb48(RGB48Image in) { // create palette from histogram int uniqueColors = hist.getNumUsedEntries(); Palette palette = new Palette(uniqueColors, 255); int index = 0; for (int r = 0; r < 256; r++) { for (int g = 0; g < 256; g++) { for (int b = 0; b < 256; b++) { if (hist.getEntry(r, g, b) != 0) { hist.setEntry(r, g, b, index); palette.putSample(RGBIndex.INDEX_RED, index, r); palette.putSample(RGBIndex.INDEX_GREEN, index, g); palette.putSample(RGBIndex.INDEX_BLUE, index, b); index++; } } } } Paletted8Image out = new MemoryPaletted8Image(in.getWidth(), in.getHeight(), palette); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y) >> 8; int green = in.getSample(RGBIndex.INDEX_GREEN, x, y) >> 8; int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y) >> 8; out.putSample(0, x, y, hist.getEntry(red, green, blue)); } setProgress(y, in.getHeight()); } outputImage = out; } private void createRgb24FromRgb48(RGB48Image in, RGB24Image out) { for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { out.putSample(RGBIndex.INDEX_RED, x, y, in.getSample(RGBIndex.INDEX_RED, x, y) >> 8); out.putSample(RGBIndex.INDEX_GREEN, x, y, in.getSample(RGBIndex.INDEX_GREEN, x, y) >> 8); out.putSample(RGBIndex.INDEX_BLUE, x, y, in.getSample(RGBIndex.INDEX_BLUE, x, y) >> 8); } setProgress(y, in.getHeight()); } outputImage = out; } /** * Returns the reduced output image if one was created in {@link #process()}. * @return newly-created output image */ public PixelImage getOutputImage() { return outputImage; } /** * Returns the type of the minimum image type found (one of the TYPE_xyz constants * of this class). * Can only be called after a successful call to process. */ public int getType() { return type; } /** * This method can be called after {@link #process()} to find out if the input * image in fact can be reduced to a "smaller" image type. * If this method returns true and if conversion was desired by the * user (can be specified via {@link #setConversion}), the reduced image can * be retrieved via {@link #getOutputImage()}. * @return if image was found to be reducible in process() */ public boolean isReducible() { return type != TYPE_UNKNOWN; } // works for Gray8 and Gray16 private boolean isGrayBilevel(IntegerImage in) { final int HEIGHT = in.getHeight(); final int MAX = in.getMaxSample(0); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < in.getWidth(); x++) { int value = in.getSample(0, x, y); if (value != 0 && value != MAX) { return false; // not a grayscale image } } } return true; } private boolean isGray16Gray8(Gray16Image in) { final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int value = in.getSample(0, x, y); int lsb = value & 0xff; int msb = (value >> 8) & 0xff; if (lsb != msb) { return false; } } } return true; } private boolean isRgb48Gray8(RGB48Image in) { final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < in.getWidth(); x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y); int green = in.getSample(RGBIndex.INDEX_GREEN, x, y); int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y); if (red != green || green != blue) { return false; } int lsb = red & 0xff; int msb = red >> 8; if (lsb != msb) { return false; } } } return true; } /** * Assumes that it has already been verified that the input 48 bpp * RGB image is also a 24 bpp RGB image. * @param in input image to be checked * @return if this image can be losslessly converted to a Paletted8Image */ private boolean isRgb48Paletted8(RGB48Image in) { int uniqueColors = 0; hist = new OnDemandHistogram3D(255, 255, 255); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y) >> 8; int green = in.getSample(RGBIndex.INDEX_GREEN, x, y) >> 8; int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y) >> 8; if (hist.getEntry(red, green, blue) == 0) { hist.increaseEntry(red, green, blue); uniqueColors++; if (uniqueColors > 256) { return false; } } } } return true; } private boolean isRgb48Rgb24(RGB48Image in) { final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < in.getWidth(); x++) { for (int channel = 0; channel < 3; channel++) { int sample = in.getSample(channel, x, y); if ((sample & 0xff) != ((sample & 0xff00) >> 8)) { return false; } } } } return true; } // works for RGB24 and RGB48 private boolean isRgbBilevel(IntegerImage in) { final int HEIGHT = in.getHeight(); final int MAX = in.getMaxSample(0); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < in.getWidth(); x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y); int green = in.getSample(RGBIndex.INDEX_GREEN, x, y); int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y); if (red != green || green != blue || (blue != 0 && blue != MAX)) { return false; } } } return true; } /** * Returns if the input RGB image can be losslessly converted to * a grayscale image. * @param in RGB image to be checked * @return true if input is gray, false otherwise */ private boolean isRgbGray(RGBIntegerImage in) { final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < in.getWidth(); x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y); int green = in.getSample(RGBIndex.INDEX_GREEN, x, y); int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y); if (red != green || green != blue) { return false; } } } return true; } private boolean isRgb24Paletted8(RGB24Image in) { int uniqueColors = 0; hist = new OnDemandHistogram3D(255, 255, 255); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y); int green = in.getSample(RGBIndex.INDEX_GREEN, x, y); int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y); if (hist.getEntry(red, green, blue) == 0) { hist.increaseEntry(red, green, blue); uniqueColors++; if (uniqueColors > 256) { return false; } } } } return true; } public void process() throws MissingParameterException, WrongParameterException { if (inputImage == null) { throw new MissingParameterException("No input image available"); } // GRAY8 if (inputImage instanceof Gray8Image) { if (isGrayBilevel((Gray8Image)inputImage)) { type = TYPE_BILEVEL; if (doConvert) { createBilevelFromGrayOrRgb((Gray8Image)inputImage); } } } else // GRAY16 if (inputImage instanceof Gray16Image) { if (isGrayBilevel((Gray16Image)inputImage)) { type = TYPE_BILEVEL; if (doConvert) { createBilevelFromGrayOrRgb((Gray16Image)inputImage); } } else if (isGray16Gray8((Gray16Image)inputImage)) { type = TYPE_GRAY16; if (doConvert) { createGray8FromGray16((Gray16Image)inputImage); } } } else // RGB24 if (inputImage instanceof RGB24Image) { if (isRgbBilevel((RGB24Image)inputImage)) { type = TYPE_BILEVEL; if (doConvert) { createBilevelFromGrayOrRgb((RGB24Image)inputImage); } } else if (isRgbGray((RGB24Image)inputImage)) { type = TYPE_GRAY8; if (doConvert) { outputImage = new MemoryGray8Image(inputImage.getWidth(), inputImage.getHeight()); createGrayFromRgb((RGB24Image)inputImage, (Gray8Image)outputImage); } } else if (isRgb24Paletted8((RGB24Image)inputImage)) { type = TYPE_PALETTED8; if (doConvert) { createPaletted8FromRgb24((RGB24Image)inputImage); } } } else // RGB48 if (inputImage instanceof RGB48Image) { if (isRgbBilevel((RGB48Image)inputImage)) { type = TYPE_BILEVEL; if (doConvert) { createBilevelFromGrayOrRgb((RGB48Image)inputImage); } } else if (isRgb48Gray8((RGB48Image)inputImage)) { type = TYPE_GRAY8; if (doConvert) { outputImage = new MemoryGray8Image(inputImage.getWidth(), inputImage.getHeight()); // this create method works because it works with int and the least significant 8 // bits are equal to the most significant 8 bits if isRgb48Gray8 returned true createGrayFromRgb((RGB48Image)inputImage, (Gray8Image)outputImage); } } else if (isRgbGray((RGB48Image)inputImage)) { type = TYPE_GRAY16; if (doConvert) { outputImage = new MemoryGray8Image(inputImage.getWidth(), inputImage.getHeight()); createGrayFromRgb((RGB24Image)inputImage, (Gray8Image)outputImage); } } else if (isRgb48Rgb24((RGB48Image)inputImage)) { // RGB48 input is RGB24; is it also Paletted8? if (isRgb48Paletted8((RGB48Image)inputImage)) { type = TYPE_PALETTED8; if (doConvert) { createPaletted8FromRgb48((RGB48Image)inputImage); } } else { type = TYPE_RGB24; if (doConvert) { outputImage = new MemoryRGB24Image(inputImage.getWidth(), inputImage.getHeight()); createRgb24FromRgb48((RGB48Image)inputImage, (RGB24Image)outputImage); } } } } else // PALETTED8 if (inputImage instanceof Paletted8Image) { Paletted8Image in = (Paletted8Image)inputImage; Palette palette = in.getPalette(); if (palette.isBlackAndWhite()) { type = TYPE_BILEVEL; if (doConvert) { createBilevelFromPaletted(in); } } else if (palette.isGray()) { type = TYPE_GRAY8; if (doConvert) { Gray8Image out = new MemoryGray8Image(in.getWidth(), in.getHeight()); createGray8FromPaletted8(in, out); } } } else { throw new WrongParameterException("Not a supported or reducible image type: " + inputImage.toString()); } } /** * This method can be used to specify whether the input image is to be converted * to the minimum image type if it is clear that such a conversion is possible. * The default value is true. * If this is set to false, it can still be * @param convert if true, the conversion will be performed */ public void setConversion(boolean convert) { doConvert = convert; } /** * This method must be used to specify the mandatory input image. * @param image PixelImage object to be examined */ public void setInputImage(PixelImage image) { inputImage = image; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/reduction/package.html0000664000000000000000000000115007741250132025417 0ustar

Classes to convert images to a lower color type. Usually, that conversion is lossy (e.g. from RGB truecolor to grayscale, or from shades of gray to black and white).

However, the {@link net.sourceforge.jiu.color.reduction.AutoDetectColorType} class reduces only if an image can be converted to a lower color type without losing information. This is the inverse operation to color promotion, provided by another package. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/reduction/ReduceShadesOfGray.java0000664000000000000000000001265110377271207027465 0ustar /* * ReduceShadesOfGray * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.reduction; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.MemoryGray16Image; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Reduces the number of shades of gray of a grayscale image. * This class uses the most simple possible algorithm. * Only the most significant N bits are kept (where N is the * number specified with {@link #setBits}), the others are dropped * and the result is scaled back to either 8 or 16 bits to fit * into the two grayscale image types. *

Supported image classes

* This class works with {@link net.sourceforge.jiu.data.Gray8Image} * and {@link net.sourceforge.jiu.data.Gray16Image}. *

Usage example

* Reduce a grayscale image to 3 bit (23 = 8 shades of gray): *
 * ReduceShadesOfGray reduce = new ReduceShadesOfGray();
 * reduce.setBits(3);
 * reduce.setInputImage(image); // some Gray8Image or Gray16Image
 * reduce.process();
 * PixelImage reducedImage = reduce.getOutputImage();
 * 
* @author Marco Schmidt * @since 0.3.0 */ public class ReduceShadesOfGray extends ImageToImageOperation { /** * Number of significant bits in the destination grayscale image. */ private Integer destBits; /** * Lookup table, for each possible input sample stores the * corresponding output sample. */ private int[] lut; private void createLut(int inDepth) { int outDepth = destBits.intValue(); lut = new int[1 << inDepth]; final int SHIFT = inDepth - outDepth; final int MAX_IN_VALUE = (1 << inDepth) - 1; final int MAX_OUT_VALUE = (1 << outDepth) - 1; for (int i = 0; i < lut.length; i++) { int value = i >> SHIFT; lut[i] = (value * MAX_IN_VALUE) / MAX_OUT_VALUE; } } private void process(GrayIntegerImage in, final int MASK, BilevelImage out) { if (out == null) { out = new MemoryBilevelImage(in.getWidth(), in.getHeight()); } out.clear(BilevelImage.BLACK); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { if ((in.getSample(x, y) & MASK) != 0) { out.putWhite(x, y); } } setProgress(y, in.getHeight()); } setOutputImage(out); } private void process(GrayIntegerImage in, GrayIntegerImage out) { //int bits = destBits.intValue(); for (int y = 0; y < in.getHeight(); y++) { for (int x = 0; x < in.getWidth(); x++) { out.putSample(x, y, lut[in.getSample(0, x, y)]); } setProgress(y, in.getHeight()); } setOutputImage(out); } public void process() throws MissingParameterException, WrongParameterException { if (destBits == null) { throw new MissingParameterException("The number of destination bits has not been specified."); } ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); boolean gray8 = in instanceof Gray8Image; boolean gray16 = in instanceof Gray16Image; if (!(gray8 || gray16)) { throw new WrongParameterException("Input image must be either Gray8Image or Gray16Image."); } if (destBits.intValue() == 1) { process((GrayIntegerImage)in, gray8 ? 0x80 : 0x8000, (BilevelImage)getOutputImage()); } else if (gray8) { if (destBits.intValue() > 7) { throw new WrongParameterException("For a Gray8Image destination bits must be 7 or less."); } PixelImage out = getOutputImage(); if (out == null) { out = new MemoryGray8Image(in.getWidth(), in.getHeight()); } else { if (!(out instanceof Gray8Image)) { throw new WrongParameterException("For this input image, output image must be a Gray8Image."); } } createLut(8); process((GrayIntegerImage)in, (GrayIntegerImage)out); } else if (gray16) { PixelImage out = getOutputImage(); if (out == null) { out = new MemoryGray16Image(in.getWidth(), in.getHeight()); } else { if (destBits.intValue() <= 8 && !(out instanceof Gray8Image)) { throw new WrongParameterException("For this input image, output image must be a Gray8Image."); } if (destBits.intValue() <= 15 && !(out instanceof Gray16Image)) { throw new WrongParameterException("For this input image, output image must be a Gray16Image."); } } createLut(16); process((GrayIntegerImage)in, (GrayIntegerImage)out); } } /** * Specifies the number of bits the output image is supposed to have. * @param bits number of bits in output image, from 1 to 15 * @throws IllegalArgumentException if bits is smaller than 1 or larger than 15 */ public void setBits(int bits) { if (bits < 1) { throw new IllegalArgumentException("Number of bits must be 1 or larger."); } if (bits > 15) { throw new IllegalArgumentException("Number of bits must be 15 or smaller."); } destBits = new Integer(bits); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/reduction/ReduceToBilevelThreshold.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/reduction/ReduceToBilevelThreshold.ja0000664000000000000000000000744007741250132030354 0ustar /* * ReduceToBilevelThreshold * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.reduction; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Reduces a {@link net.sourceforge.jiu.data.GrayIntegerImage} to a * {@link net.sourceforge.jiu.data.BilevelImage} by setting all values below * a certain threshold value to black and everything else to white. *

Default value

* If no threshold is specified via {@link #setThreshold(int)}, this operation * uses a default value of ({@link net.sourceforge.jiu.data.IntegerImage#getMaxSample(int)} + 1) / 2. *

Usage example

* This example sets all values below 33 percent luminance to black, * everything else to white. *
 * GrayIntegerImage image = ...;
 * ReduceToBilevelThreshold red = new ReduceToBilevelThreshold();
 * red.setInputImage(image);
 * red.setThreshold(image.getMaxSample(0) / 3);
 * red.process();
 * BilevelImage reducedImage= (BilevelImage)red.getOutputImage();
 * 
* @author Marco Schmidt */ public class ReduceToBilevelThreshold extends ImageToImageOperation { private Integer threshold; /** * Returns the current threshold value, or null if * none was specified and the operation's process method was not * run yet. * @return threshold value */ public Integer getThreshold() { return threshold; } private void process(GrayIntegerImage in, BilevelImage out) throws WrongParameterException { final int MAX_SAMPLE = in.getMaxSample(0); if (threshold == null) { threshold = new Integer((MAX_SAMPLE + 1) / 2); } final int THRESHOLD = threshold.intValue(); if (THRESHOLD > MAX_SAMPLE) { throw new WrongParameterException("Threshold must be smaller than or equal to the maximum sample of the input image."); } final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); out.clear(BilevelImage.BLACK); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { if (in.getSample(0, x, y) >= THRESHOLD) { out.putWhite(x, y); } } setProgress(y, HEIGHT); } } public void process() throws MissingParameterException, WrongParameterException { PixelImage in = getInputImage(); if (in == null) { throw new MissingParameterException("Input image missing."); } if (!(in instanceof GrayIntegerImage)) { throw new WrongParameterException("Input image must implement GrayIntegerImage."); } PixelImage out = getOutputImage(); if (out == null) { out = new MemoryBilevelImage(in.getWidth(), in.getHeight()); setOutputImage(out); } if (out != null && !(out instanceof BilevelImage)) { throw new WrongParameterException("Output image must implement BilevelImage."); } if (out != null && (in.getWidth() != out.getWidth() || in.getHeight() != out.getHeight())) { throw new WrongParameterException("Input and output images must have the same resolution."); } process((GrayIntegerImage)in, (BilevelImage)out); } /** * Sets a new threshold value. * @param newThreshold the new threshold value to be used for this operation * @throws IllegalArgumentException if the threshold value is negative */ public void setThreshold(int newThreshold) { if (newThreshold < 0) { throw new IllegalArgumentException("New threshold value must be 0 or larger."); } threshold = new Integer(newThreshold); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/reduction/RGBToGrayConversion.java0000664000000000000000000002164610611702335027620 0ustar /* * RGBToGrayConversion * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.reduction; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.MemoryGray16Image; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGB48Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Converts RGB color images (both truecolor and paletted) to grayscale images. * The weights to be used with the three base colors red, green and blue can be * modified with a call to * {@link #setColorWeights(float, float, float)}. *

Supported image types

* {@link RGB24Image} and {@link Paletted8Image} can be used as input image types. * A {@link Gray8Image} be will be created from them. *

* Could be optimized to use int multiplication instead of float multiplication. *

Usage example

* Convert some PixelImage rgbImage to grayscale: *
PixelImage grayImg = RGBToGrayConversion.convert(rgbImage);
* Using your own color weights can be done like this. * You may also want to specify an output grayscale image if you have * one to reuse. *
 * RGBToGrayConversion rgbtogray = new RGBToGrayConversion();
 * rgbtogray.setInputImage(image);
 * rgbtogray.setColorWeights(0.3f, 0.3f, 0.4f);
 * rgbtogray.process();
 * PixelImage grayImage = rgbtogray.getOutputImage();
 * 
* @author Marco Schmidt */ public class RGBToGrayConversion extends ImageToImageOperation { /** * The default weight for red samples in the conversion, 0.3f. */ public static final float DEFAULT_RED_WEIGHT = 0.3f; /** * The default weight for green samples in the conversion, 0.59f. */ public static final float DEFAULT_GREEN_WEIGHT = 0.59f; /** * The default weight for blue samples in the conversion, 0.11f. */ public static final float DEFAULT_BLUE_WEIGHT = 0.11f; private float redWeight = DEFAULT_RED_WEIGHT; private float greenWeight = DEFAULT_GREEN_WEIGHT; private float blueWeight = DEFAULT_BLUE_WEIGHT; /** * Static convenience method to convert an RGB image to a grayscale image. * @param rgbImage input RGB image to be converted * @return a new grayscale image, created from the RGB input image * @throws MissingParameterException rgbImage is null * @throws WrongParameterException rgbImage's type is unsupported * @since 0.14.2 */ public static PixelImage convert(PixelImage rgbImage) throws MissingParameterException, WrongParameterException { RGBToGrayConversion op = new RGBToGrayConversion(); op.setInputImage(rgbImage); op.process(); return op.getOutputImage(); } private void process(RGBIntegerImage in, GrayIntegerImage out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y); int green = in.getSample(RGBIndex.INDEX_GREEN, x, y); int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y); out.putSample(x, y, (int)(red * redWeight + green * greenWeight + blue * blueWeight)); } setProgress(y, HEIGHT); } setOutputImage(out); } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); PixelImage in = getInputImage(); if (in instanceof RGB24Image) { process((RGB24Image)in); } else if (in instanceof RGB48Image) { process((RGB48Image)in); } else if (in instanceof Paletted8Image) { process((Paletted8Image)in); } else { throw new WrongParameterException("Type of input image unsupported: " + in.getImageType().getName()); } } private void process(Paletted8Image in) throws MissingParameterException, WrongParameterException { PixelImage image = getOutputImage(); Gray8Image out = null; if (image == null) { out = new MemoryGray8Image(in.getWidth(), in.getHeight()); } else { if (!(image instanceof Gray8Image)) { throw new WrongParameterException("Specified output image must be of type Gray8Image for input image of type Paletted8Image."); } out = (Gray8Image)image; ensureImagesHaveSameResolution(); } Palette palette = in.getPalette(); int[] lut = new int[palette.getNumEntries()]; for (int i = 0; i < lut.length; i++) { int red = palette.getSample(RGBIndex.INDEX_RED, i); int green = palette.getSample(RGBIndex.INDEX_GREEN, i); int blue = palette.getSample(RGBIndex.INDEX_BLUE, i); lut[i] = (int)(red * redWeight + green * greenWeight + blue * blueWeight); } final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { try { out.putSample(0, x, y, lut[in.getSample(0, x, y)]); } catch (ArrayIndexOutOfBoundsException aioobe) { } } setProgress(y, HEIGHT); } setOutputImage(out); } private void process(RGB24Image in) throws WrongParameterException { PixelImage out = getOutputImage(); if (out == null) { out = new MemoryGray8Image(in.getWidth(), in.getHeight()); } else { if (!(out instanceof Gray8Image)) { throw new WrongParameterException("Specified output image must be of type Gray8Image for input image of type RGB24Image."); } ensureImagesHaveSameResolution(); } process(in, (GrayIntegerImage)out); } private void process(RGB48Image in) throws WrongParameterException { PixelImage out = getOutputImage(); if (out == null) { out = new MemoryGray16Image(in.getWidth(), in.getHeight()); } else { if (!(out instanceof Gray16Image)) { throw new WrongParameterException("Specified output image must be of type Gray16Image for input image of type RGB48Image."); } ensureImagesHaveSameResolution(); } process(in, (GrayIntegerImage)out); } /** * Sets the weights for the three colors red, green and blue used in the conversion procedure. * For each RGB value (r, g, b) to be converted (whether in a truecolor * image or in the palette), the formula is gray = r * red + g * green + b * blue. * The default values for these weights are {@link #DEFAULT_RED_WEIGHT}, * {@link #DEFAULT_GREEN_WEIGHT} and {@link #DEFAULT_BLUE_WEIGHT}. * This method lets the user change these values. * Each of these arguments must be >= 0.0f and <= 1.0f. * The sum of the three must be <= 1.0f. * For any resulting gray value to be spread over the complete scale from 0.0f to 1.0f it is * preferable for the sum to be equal to or at least close to 1.0f. * However, this is not checked. * The smaller the sum of the weights is, the darker the resulting gray image will become. * @param red weight of the red sample in the conversion, between 0.0f and 1.0f * @param green weight of the green sample in the conversion, between 0.0f and 1.0f * @param blue weight of the blue sample in the conversion, between 0.0f and 1.0f * @throws IllegalArgumentException if any one of the above mentioned constraints for the arguments is not met */ public void setColorWeights(float red, float green, float blue) { if (red < 0.0f) { throw new IllegalArgumentException("The red weight must be larger than or equal to 0; got " + red); } if (green < 0.0f) { throw new IllegalArgumentException("The green weight must be larger than or equal to 0; got " + green); } if (blue < 0.0f) { throw new IllegalArgumentException("The blue weight must be larger than or equal to 0; got " + blue); } if (red > 1.0f) { throw new IllegalArgumentException("The red weight must be smaller than or equal to 1.0f; got " + red); } if (green > 1.0f) { throw new IllegalArgumentException("The green weight must be smaller than or equal to 1.0f; got " + green); } if (blue > 1.0f) { throw new IllegalArgumentException("The blue weight must be smaller than or equal to 1.0f; got " + blue); } float sum = red + green + blue; if (sum > 1.0f) { throw new IllegalArgumentException("The sum of the three weights must be smaller than or equal to 1.0f; got " + sum); } redWeight = red; greenWeight = green; blueWeight = blue; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/Invert.java0000664000000000000000000001243210522413415023255 0ustar /* * Invert * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Creates an inverted (negated) version of an image. * This is done by subtracting each sample value of a channel * from the maximum sample for that channel. * The maximum sample for a channel is given by * {@link net.sourceforge.jiu.data.IntegerImage#getMaxSample}. * For paletted images, just the palette is treated that way. * Supported image types: {@link net.sourceforge.jiu.data.IntegerImage}. * Input and output image can be the same object. *

Usage

* There are two ways of using this class. * Either create an Invert object and set all paramters yourself. * This lets you reuse image objects or add a progress listener. *
 * Invert invert = new Invert();
 * invert.setInputImage(image);
 * invert.addProgressListener(listener); // this is optional
 * invert.process();
 * PixelImage invertedImage = invert.getOutputImage();
 * 
* The other method is by sing the static convenience method *
 * PixelImage invertedImage = Invert.invert(someImage);
 * 
* You will have to catch the potential exceptions in both cases. * @author Marco Schmidt */ public class Invert extends ImageToImageOperation { /** * Helper method to return an inverted image from the argument image. * @param inputImage image to be inverted * @return new image object with inverted image * @throws OperationFailedException on operation errors */ public static PixelImage invert(PixelImage inputImage) throws OperationFailedException { Invert invert = new Invert(); invert.setInputImage(inputImage); invert.process(); return invert.getOutputImage(); } private void prepare(PixelImage in) throws MissingParameterException, WrongParameterException { if (in == null) { throw new MissingParameterException("Missing input image."); } PixelImage out = getOutputImage(); if (out == null) { setOutputImage(in.createCompatibleImage(in.getWidth(), in.getHeight())); } else { if (in.getClass() != out.getClass()) { throw new WrongParameterException("Specified output image type must be the same as input image type."); } if (in.getWidth() != out.getWidth()) { throw new WrongParameterException("Specified output image must have same width as input image."); } if (in.getHeight() != out.getHeight()) { throw new WrongParameterException("Specified output image must have same height as input image."); } } } private void process(Paletted8Image in) { // prepare(PixelImage) has made sure that we have a compatible output image Paletted8Image out = (Paletted8Image)getOutputImage(); // invert palette of output image Palette pal = out.getPalette(); final int MAX = pal.getMaxValue(); for (int entryIndex = 0; entryIndex < pal.getNumEntries(); entryIndex++) { for (int channelIndex = 0; channelIndex < 3; channelIndex++) { pal.putSample(channelIndex, entryIndex, MAX - pal.getSample(channelIndex, entryIndex)); } } // copy image content final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { out.putSample(0, x, y, in.getSample(0, x, y)); } setProgress(y, HEIGHT); } } private void process(IntegerImage in) { IntegerImage out = (IntegerImage)getOutputImage(); final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); final int CHANNELS = in.getNumChannels(); final int TOTAL_ITEMS = CHANNELS * HEIGHT; int processedItems = 0; for (int channel = 0; channel < CHANNELS; channel++) { final int MAX = in.getMaxSample(channel); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { out.putSample(channel, x, y, MAX - in.getSample(channel, x, y)); } setProgress(processedItems++, TOTAL_ITEMS); } } } /** * Inverts the input image, reusing an output image if one has been specified. * For paletted images, inverts the palette. * For all other types, subtracts each sample of each channel from the maximum * value of that channel. * @throws MissingParameterException if the input image is missing * @throws WrongParameterException if any of the specified image parameters are unsupported or of the wrong width or height */ public void process() throws MissingParameterException, WrongParameterException { PixelImage in = getInputImage(); prepare(in); if (in instanceof Paletted8Image) { process((Paletted8Image)in); } else if (in instanceof IntegerImage) { process((IntegerImage)in); } else { throw new WrongParameterException("Input image type unsupported: " + in.toString()); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/0000775000000000000000000000000010546532076023131 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/LineSpotFunction.java0000664000000000000000000000106107741250132027226 0ustar /* * LineSpotFunction * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.dithering; import net.sourceforge.jiu.color.dithering.SpotFunction; /** * A line spot function. * @author Marco Schmidt * @since 0.9.0 * @see ClusteredDotDither */ public class LineSpotFunction implements SpotFunction { public double compute(double x, double y) { if (y < 0) { return -y; } else { return y; } } public boolean isBalanced() { return true; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/ClusteredDotDither.java0000664000000000000000000002242207741250132027530 0ustar /* * ClusteredDotDither * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.dithering; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; import net.sourceforge.jiu.util.ComparatorInterface; import net.sourceforge.jiu.util.Sort; /** * Apply a clustered dot ordered dither to a grayscale image, converting * it to a bilevel image in the process. * Works with {@link net.sourceforge.jiu.data.GrayIntegerImage} objects * as input and {@link net.sourceforge.jiu.data.BilevelImage} objects * as output. * Resolution of both must be the same. * Use one of the predefined dither matrices or have one compiled from * a spot function (given by an object of a class implementing * {@link net.sourceforge.jiu.color.dithering.SpotFunction}). * There are a couple of classes implementing that spot function interface * in the dithering package. * If no matrix is specified, {@link #process()} creates a default matrix * by calling {@link #setOrder3DitherMatrix()}. *

Usage example

*
 * ClusteredDotDither dither = new ClusteredDotDither();
 * dither.setInputImage(image); // some GrayIntegerImage
 * dither.setDitherMatrix(8, 8, new DiamondSpotFunction());
 * dither.process();
 * PixelImage ditheredImage = dither.getOutputImage();
 * 
* *

Credits

* The predefined dither matrices were taken from * Netpbm's * pgmtopbm program (the matrices are stored in the * file dithers.h). *

* I learned about spot functions and their use from Austin Donnelly's * GIMP plugin newsprint - it has extensive comments, and the * newsprint website * explains the * theoretical background. * @author Marco Schmidt * @since 0.9.0 */ public class ClusteredDotDither extends ImageToImageOperation { private int ditherHeight; private int ditherWidth; private int[] ditherData; public void process() throws MissingParameterException, WrongParameterException { if (ditherData == null) { setDefaults(); } ensureInputImageIsAvailable(); PixelImage in = getInputImage(); if (!(in instanceof GrayIntegerImage)) { throw new WrongParameterException("Input image must implement GrayIntegerImage."); } PixelImage out = getOutputImage(); if (out == null) { out = new MemoryBilevelImage(in.getWidth(), in.getHeight()); setOutputImage(out); } else { if (!(out instanceof BilevelImage)) { throw new WrongParameterException("Output image must implement BilevelImage."); } ensureOutputImageResolution(in.getWidth(), in.getHeight()); } process((GrayIntegerImage)in, (BilevelImage)out); } private void process(GrayIntegerImage in, BilevelImage out) { // find maximum entry in dither matrix int maxTableValue = 1; for (int i = 0; i < ditherData.length; i++) { if (ditherData[i] > maxTableValue) { maxTableValue = ditherData[i]; } } maxTableValue++; // create adjusted dither matrix data final int MAX_SAMPLE = in.getMaxSample(0) + 1; final int[] data = new int[ditherData.length]; for (int i = 0; i < data.length; i++) { data[i] = ditherData[i] * MAX_SAMPLE / maxTableValue; } // do the actual dithering final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); for (int y = 0; y < HEIGHT; y++) { int ditherOffset = (y % ditherHeight) * ditherWidth; int samplesLeft = ditherWidth; for (int x = 0; x < WIDTH; x++) { if (in.getSample(0, x, y) >= data[ditherOffset++]) { out.putWhite(x, y); } else { out.putBlack(x, y); } if (--samplesLeft == 0) { samplesLeft = ditherWidth; ditherOffset -= ditherWidth; } } setProgress(y, HEIGHT); } } private void setDefaults() { setOrder3DitherMatrix(); } /** * Sets the dither matrix to be used in this operation. * @param width number of entries in horizontal direction * @param height number of entries in vertical direction * @param data matrix entries, in order top to bottom, and in each row left to right * @throws IllegalArgumentException if width or height are smaller than one or data * is null or has not at least width times height entries */ public void setDitherMatrix(int width, int height, int[] data) { if (width < 1) { throw new IllegalArgumentException("Width must be one or larger."); } if (height < 1) { throw new IllegalArgumentException("Height must be one or larger."); } if (data == null) { throw new IllegalArgumentException("Data must not be null."); } if (data.length < width * height) { throw new IllegalArgumentException("Data must have at least width times height entries."); } ditherWidth = width; ditherHeight = height; ditherData = data; } /** * Creates and sets a dither matrix of user-defined size and * compiles it from a spot function. * Creates a matrix from the spot function and calls {@link #setDitherMatrix(int, int, int[])}. * @param width width of matrix, must be one or larger * @param height height of matrix, must be one or larger * @param f the spot function to be used for compiling the matrix */ public void setDitherMatrix(int width, int height, SpotFunction f) { class MatrixElement implements ComparatorInterface { int index; double value; public int compare(Object o1, Object o2) { MatrixElement e1 = (MatrixElement)o1; MatrixElement e2 = (MatrixElement)o2; if (e1.value < e2.value) { return -1; } else if (e1.value == e2.value) { return 0; } else { return 1; } } } int[] data = new int[width * height]; MatrixElement[] matrixElements = new MatrixElement[data.length]; for (int i = 0; i < data.length; i++) { matrixElements[i] = new MatrixElement(); matrixElements[i].index = i; } int index = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { double sx = ((double)x / (width - 1) - 0.5) * 2; double sy = ((double)y / (height - 1) - 0.5) * 2; double value = f.compute(sx, sy); if (value < -1.0) { value = -1.0; } else if (value > 1.0) { value = 1.0; } matrixElements[index++].value = value; } } boolean balanced = f.isBalanced(); if (!balanced) { Sort.sort(matrixElements, matrixElements[0]); } for (int i = 0; i < data.length; i++) { MatrixElement elem = matrixElements[i]; if (balanced) { data[elem.index] = (int)(elem.value * 254); } else { data[elem.index] = i * 255 / data.length; } } setDitherMatrix(width, height, data); } /** * Sets a 6 times 6 elements matrix to be used for dithering. */ public void setOrder3DitherMatrix() { setDitherMatrix(6, 6, new int[] { 9, 11, 10, 8, 6, 7, 12, 17, 16, 5, 0, 1, 13, 14, 15, 4, 3, 2, 8, 6, 7, 9, 11, 10, 5, 0, 1, 12, 17, 16, 4, 3, 2, 13, 14, 15}); } /** * Sets an 8 times 8 elements matrix to be used for dithering. */ public void setOrder4DitherMatrix() { setDitherMatrix(8, 8, new int[] {18,20,19,16,13,11,12,15, 27,28,29,22, 4, 3, 2, 9, 26,31,30,21, 5, 0, 1,10, 23,25,24,17, 8, 6, 7,14, 13,11,12,15,18,20,19,16, 4, 3, 2, 9,27,28,29,22, 5, 0, 1,10,26,31,30,21, 8, 6, 7,14,23,25,24,17}); } /** * Sets a 16 times 16 elements matrix to be used for dithering. */ public void setOrder8DitherMatrix() { setDitherMatrix(16, 16, new int[] { 64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60, 70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52, 78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44, 88,110,123,124,125,118,107, 85, 39, 17, 4, 3, 2, 9, 20, 42, 89,111,122,127,126,117,106, 84, 38, 16, 5, 0, 1, 10, 21, 43, 79,102,119,121,120,113, 97, 82, 48, 25, 8, 6, 7, 14, 30, 45, 71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53, 65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61, 63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67, 57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75, 49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83, 39, 17, 4, 3, 2, 9, 20, 42, 88,110,123,124,125,118,107, 85, 38, 16, 5, 0, 1, 10, 21, 43, 89,111,122,127,126,117,106, 84, 48, 25, 8, 6, 7, 14, 30, 45, 79,102,119,121,120,113, 97, 82, 56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74, 62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66}); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/DiamondSpotFunction.java0000664000000000000000000000125307741250132027715 0ustar /* * DiamondSpotFunction * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.dithering; import net.sourceforge.jiu.color.dithering.SpotFunction; /** * A diamond spot function. * @author Marco Schmidt * @since 0.9.0 * @see ClusteredDotDither */ public class DiamondSpotFunction implements SpotFunction { public double compute(double x, double y) { double xy = Math.abs(x) + Math.abs(y); if (xy <= 1) { return 0.5 * xy * xy; } else { double xy1 = xy - 1; return (2 * xy * xy - 4 * xy1 * xy1) / 4; } } public boolean isBalanced() { return false; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/SpotFunction.java0000664000000000000000000000140507741250132026420 0ustar /* * SpotFunction * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.dithering; /** * An interface for spot functions to be used for clustered dot dithering. * @author Marco Schmidt * @since 0.9.0 * @see ClusteredDotDither */ public interface SpotFunction { /** * Compute the spot intensity at the given position. * @param x horizontal position, must be between -1.0 and 1.0 (including both) * @param y vertical position, must be between -1.0 and 1.0 (including both) * @return the function value, must be between 0.0 and 1.0 (including both) */ double compute(double x, double y); /** * Returns if this spot function is balanced. */ boolean isBalanced(); } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/ErrorDiffusionDithering.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/ErrorDiffusionDithering.jav0000664000000000000000000007020410546765207030440 0ustar /* * ErrorDiffusionDithering * * Copyright (c) 2001, 2002, 2003, 2004 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.dithering; import net.sourceforge.jiu.color.quantization.RGBQuantizer; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * This class is used to apply error diffusion dithering to images that are being reduced in their color depth. * Works with {@link net.sourceforge.jiu.data.GrayIntegerImage} and * {@link net.sourceforge.jiu.data.RGBIntegerImage} objects. * For RGB images, a quantizer must be specified via {@link #setQuantizer}. * That quantizer must have been initialized (it must have searched for / given a palette that it can map to). *

* This class offers six predefined types of error diffusion dithering. * In addition, user-defined types can be integrated by providing a * information on how the error is to be distributed; see the * description of {@link #setTemplateData}. * *

Usage examples

*

Color

* This small program maps some RGB24Image object to a Paletted8Image * with 120 entries in its palette, using Stucki error * diffusion dithering in combination with an octree color quantizer. *
 * MemoryRGB24Image image = ...; // some RGB image
 * OctreeColorQuantizer quantizer = new OctreeColorQuantizer();
 * quantizer.setInputImage(image);
 * quantizer.setPaletteSize(120);
 * quantizer.init();
 * ErrorDiffusionDithering edd = new ErrorDiffusionDithering();
 * edd.setTemplateType(ErrorDiffusionDithering.TYPE_STUCKI);
 * edd.setQuantizer(quantizer);
 * edd.setInputImage(image);
 * edd.process();
 * PixelImage quantizedImage = edd.getOutputImage();
 * 
*

Grayscale to black and white

* In this example, a {@link net.sourceforge.jiu.data.Gray8Image} object * is reduced to black and white using Floyd-Steinberg dithering. *
 * Gray8Image image = ...; // some grayscale image
 * ErrorDiffusionDithering edd = new ErrorDiffusionDithering();
 * edd.setGrayscaleOutputBits(1);
 * edd.setInputImage(image);
 * edd.process();
 * PixelImage ditheredImage = edd.getOutputImage();
 * // if you need something more specific than PixelImage: 
 * BilevelImage output = null;
 * // ditheredImage should be a BilevelImage...
 * if (ditheredImage instanceof BilevelImage
 * {
 *   // ... and it is!
 *   output = (BilevelImage)ditheredImage;
 * }
 * 
*

TODO

* Adjust this class to be able to process 16 bits per sample. *

Theoretical background

* The predefined templates were taken from the book Bit-mapped * graphics (2nd edition) by Steve Rimmer, published by * Windcrest / McGraw-Hill, ISBN 0-8306-4208-0. * The part on error diffusion dithering starts on page 375. *

* Several sources recommend Robert Ulichney's book * Digital * Halftoning for this topic (published by The MIT Press, ISBN 0-262-21009-6). * Unfortunately, I wasn't able to get a copy (or the CD-ROM version published by * Dr. Dobb's Journal). * * @since 0.5.0 * @author Marco Schmidt */ public class ErrorDiffusionDithering extends ImageToImageOperation implements RGBIndex { /** * Constant for Floyd-Steinberg error diffusion. * The quantization error is distributed to four neighboring pixels. */ public static final int TYPE_FLOYD_STEINBERG = 0; /** * Constant for Stucki error diffusion. * The quantization error is distributed to twelve neighboring pixels. */ public static final int TYPE_STUCKI = 1; /** * Constant for Burkes error diffusion. * The quantization error is distributed to seven neighboring pixels. */ public static final int TYPE_BURKES = 2; /** * Constant for Burkes error diffusion. * The quantization error is distributed to ten neighboring pixels. */ public static final int TYPE_SIERRA = 3; /** * Constant for Burkes error diffusion. * The quantization error is distributed to twelve neighboring pixels. */ public static final int TYPE_JARVIS_JUDICE_NINKE= 4; /** * Constant for Burkes error diffusion. * The quantization error is distributed to twelve neighboring pixels. */ public static final int TYPE_STEVENSON_ARCE = 5; /** * The default error diffusion type, to be used if none is specified by the user: * (@link #TYPE_FLOYD_STEINBERG}. */ public static final int DEFAULT_TYPE = TYPE_FLOYD_STEINBERG; /** * The index for the horizontal position of a neighbor pixel. * For a description, see the constructor {@link #setTemplateData}. */ public static final int INDEX_X_POS = 0; /** * The index for the vertical position of a neighbor pixel. * For a description, see the constructor {@link #setTemplateData}. */ public static final int INDEX_Y_POS = 1; /** * The index of the numerator of the relative part of the error of a neighbor pixel. * For a description, see the constructor {@link #setTemplateData}. */ public static final int INDEX_ERROR_NUMERATOR = 2; /** * The index of the denominator of the relative part of the error of a neighbor pixel. * For a description, see the constructor {@link #setTemplateData}. */ public static final int INDEX_ERROR_DENOMINATOR = 3; private static final int[][] FLOYD_STEINBERG_DATA = {{ 1, 0, 7, 16}, {-1, 1, 3, 16}, { 0, 1, 5, 16}, { 1, 1, 1, 16}}; private static final int[][] STUCKI_DATA = {{ 1, 0, 8, 42}, { 2, 0, 4, 42}, {-2, 1, 2, 42}, {-1, 1, 4, 42}, { 0, 1, 8, 42}, { 1, 1, 4, 42}, { 2, 1, 2, 42}, {-2, 2, 1, 42}, {-1, 2, 2, 42}, { 0, 2, 4, 42}, { 1, 2, 2, 42}, { 2, 2, 1, 42}}; private static final int[][] BURKES_DATA = {{ 1, 0, 8, 32}, { 2, 0, 4, 32}, {-2, 1, 2, 32}, {-1, 1, 4, 32}, { 0, 1, 8, 32}, { 1, 1, 4, 32}, { 2, 1, 2, 32}}; private static final int[][] SIERRA_DATA = {{ 1, 0, 5, 32}, { 2, 1, 3, 32}, {-2, 1, 2, 32}, {-1, 1, 4, 32}, { 0, 1, 5, 32}, { 1, 1, 4, 32}, { 2, 1, 2, 32}, {-1, 2, 2, 32}, { 0, 2, 3, 32}, { 1, 2, 2, 32}}; private static final int[][] JARVIS_JUDICE_NINKE_DATA = {{ 1, 0, 7, 48}, { 2, 0, 5, 48}, {-2, 1, 3, 48}, {-1, 1, 5, 48}, { 0, 1, 7, 48}, { 1, 1, 5, 48}, { 2, 1, 3, 48}, {-2, 2, 1, 48}, {-1, 2, 3, 48}, { 0, 2, 5, 48}, { 1, 2, 3, 48}, { 2, 2, 1, 48}}; private static final int[][] STEVENSON_ARCE_DATA = {{ 2, 0, 32, 200}, {-3, 1, 12, 200}, {-1, 1, 26, 200}, { 1, 1, 30, 200}, { 3, 1, 16, 200}, {-2, 2, 12, 200}, { 0, 2, 26, 200}, { 2, 2, 12, 200}, {-3, 3, 5, 200}, {-1, 3, 12, 200}, { 1, 3, 12, 200}, { 3, 3, 5, 200}}; private int grayBits; private int imageWidth; private int leftColumns; private int rightColumns; private int newWidth; private int numRows; private int[][] templateData; private int[] errorNum; private int[] errorDen; private int[] indexLut; private RGBQuantizer quantizer; private boolean useTruecolorOutput; /** * Creates a new object of this class and set the dithering type to * {@link #DEFAULT_TYPE}. */ public ErrorDiffusionDithering() { setTemplateType(DEFAULT_TYPE); } /** * Clamps the argument value to interval 0..max. * @param value the value to be adjusted * @param max the maximum allowed value (minimum is always 0) * @return the adjusted value */ private static int adjust(int value, int max) { if (value <= 0) { return 0; } else if (value >= max) { return max; } else { return value; } } /** * Copies data from input image to argument buffer. * @param channelIndex index of the channel of the input image from which data is to be copied * @param rowIndex index of the row of the input image from which data is to be copied * @param dest the array to which data is to be copied * @param destOffset index of the first element in the dest array to which data will be copied */ private void fillBuffer(int channelIndex, int rowIndex, int[] dest, int destOffset) { IntegerImage in = (IntegerImage)getInputImage(); final int LAST = destOffset + imageWidth; int x = 0; while (destOffset != LAST) { dest[destOffset++] = in.getSample(channelIndex, x++, rowIndex); } } private void init(int[][] data, int imageWidth) { if (data == null) { throw new IllegalArgumentException("Data must not be null."); } if (imageWidth < 1) { throw new IllegalArgumentException("Image width must be larger than 0."); } this.imageWidth = imageWidth; leftColumns = 0; rightColumns = 0; numRows = 1; errorNum = new int[data.length]; errorDen = new int[data.length]; for (int i = 0; i < data.length; i++) { if (data[i] == null) { throw new IllegalArgumentException("Each int[] array of data must be initialized; array #" + i + " is not."); } if (data[i].length != 4) { throw new IllegalArgumentException("Each int[] array of data must be of length 4; array #" + i + " has length " + data[i].length + "."); } int x = data[i][INDEX_X_POS]; if (x < 0) { x = - x; if (x > leftColumns) { leftColumns = x; } } else if (x > 0) { if (x > rightColumns) { rightColumns = x; } } int y = data[i][INDEX_Y_POS]; if (y < 0) { throw new IllegalArgumentException("The y values must be >= 0; that is not true for array index #" + i + "."); } if (y > numRows - 1) { numRows = y + 1; } if (x <= 0 && y == 0) { throw new IllegalArgumentException("If y is equal to 0, x must not be <= 0; this is true for array index #" + i + "."); } if (data[i][INDEX_ERROR_NUMERATOR] == 0 || data[i][INDEX_ERROR_DENOMINATOR] == 0) { throw new IllegalArgumentException("Neither numerator nor denominator can be 0; this is the case for array index #" + i + "."); } errorNum[i] = data[i][INDEX_ERROR_NUMERATOR]; errorDen[i] = data[i][INDEX_ERROR_DENOMINATOR]; } newWidth = imageWidth + leftColumns + rightColumns; //System.out.println("new width=" + newWidth); indexLut = new int[data.length]; for (int i = 0; i < indexLut.length; i++) { indexLut[i] = data[i][INDEX_Y_POS] * newWidth + data[i][INDEX_X_POS]; //System.out.println("lut i=" + i + "=" + indexLut[i]); } } /** * Quantizes the input image, distributing quantization errors to neighboring * pixels. * Works for {@link Gray8Image} (then {@link #setGrayscaleOutputBits(int)} * must have been called to set a number of output bits between 1 and 7) objects and * {@link RGB24Image} (then a quantizer must be specified using * {@link #setQuantizer(RGBQuantizer)}) objects. */ public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); PixelImage out = getOutputImage(); if (in instanceof Gray8Image) { init(templateData, in.getWidth()); if (grayBits == 1) { process((Gray8Image)in, (BilevelImage)out); } else if (grayBits > 1 && grayBits < 8) { process((Gray8Image)in, (Gray8Image)out); } else { throw new WrongParameterException("Cannot handle gray bits other than 1..7."); } } else if (in instanceof RGB24Image) { init(templateData, in.getWidth()); if (quantizer == null) { throw new MissingParameterException("No quantizer was specified."); } if (useTruecolorOutput) { process((RGB24Image)in, (RGB24Image)out); } else { process((RGB24Image)in, (Paletted8Image)out); } } else { throw new WrongParameterException("Cannot handle this image: " + in.toString()); } } private void process(Gray8Image in, BilevelImage out) { final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); if (out == null) { out = new MemoryBilevelImage(WIDTH, HEIGHT); } final int NUM_ERROR_PIXELS = errorNum.length; // create buffer int[] buffer = new int[newWidth * numRows]; //System.out.println("buffer length=" + buffer.length); // fill buffer with numRows (or HEIGHT, whatever is smaller) rows of data int n = Math.min(numRows, HEIGHT); int offset = leftColumns; int bufferYIndex = 0; while (n-- > 0) { fillBuffer(0, bufferYIndex++, buffer, offset); offset += newWidth; } int bufferLastRowOffset = offset - newWidth; // set complete output image to black out.clear(BilevelImage.BLACK); for (int y = 0; y < HEIGHT; y++) { int bufferIndex = leftColumns; for (int x = 0; x < WIDTH; x++) { int value = buffer[bufferIndex]; if (value < 0) { value = 0; } else if (value > 255) { value = 255; } int error; if ((value & 0x80) == 0) { // black pixel need not be written to output image // because all of its pixels have initially been set // to that color error = value; } else { // white out.putWhite(x, y); error = value - 255; } for (int i = 0; i < NUM_ERROR_PIXELS; i++) { int errorPart = error * errorNum[i] / errorDen[i]; buffer[bufferIndex + indexLut[i]] += errorPart; } bufferIndex++; } for (int i = 0, j = newWidth; j < buffer.length; i++, j++) { buffer[i] = buffer[j]; } if (bufferYIndex < HEIGHT) { fillBuffer(0, bufferYIndex++, buffer, bufferLastRowOffset); } setProgress(y, HEIGHT); } setOutputImage(out); } private void process(Gray8Image in, Gray8Image out) { final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); final int RIGHT_SHIFT = 8 - grayBits; final int[] GRAY_LUT = new int[1 << grayBits]; for (int i = 0; i < GRAY_LUT.length; i++) { GRAY_LUT[i] = i * 255 / (GRAY_LUT.length - 1); } if (out == null) { out = new MemoryGray8Image(WIDTH, HEIGHT); } final int NUM_ERROR_PIXELS = errorNum.length; // create buffer int[] buffer = new int[newWidth * numRows]; // fill buffer with numRows (or HEIGHT, whatever is smaller) rows of data int n = Math.min(numRows, HEIGHT); int offset = leftColumns; int bufferYIndex = 0; while (n-- > 0) { fillBuffer(0, bufferYIndex++, buffer, offset); offset += newWidth; } int bufferLastRowOffset = offset - newWidth; for (int y = 0; y < HEIGHT; y++) { int bufferIndex = leftColumns; for (int x = 0; x < WIDTH; x++) { int value = buffer[bufferIndex]; if (value < 0) { value = 0; } else if (value > 255) { value = 255; } int quantized = GRAY_LUT[value >> RIGHT_SHIFT]; out.putSample(0, x, y, quantized); int error = value - quantized; for (int i = 0; i < NUM_ERROR_PIXELS; i++) { int errorPart = error * errorNum[i] / errorDen[i]; buffer[bufferIndex + indexLut[i]] += errorPart; } bufferIndex++; } for (int i = 0, j = newWidth; j < buffer.length; i++, j++) { buffer[i] = buffer[j]; } if (bufferYIndex < HEIGHT) { fillBuffer(0, bufferYIndex++, buffer, bufferLastRowOffset); } setProgress(y, HEIGHT); } setOutputImage(out); } private void process(RGB24Image in, Paletted8Image out) { final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); final int MAX = 255; if (out == null) { out = new MemoryPaletted8Image(WIDTH, HEIGHT, quantizer.createPalette()); } final int NUM_ERROR_PIXELS = errorNum.length; // create buffers int[] redBuffer = new int[newWidth * numRows]; int[] greenBuffer = new int[newWidth * numRows]; int[] blueBuffer = new int[newWidth * numRows]; //System.out.println("buffer length=" + buffer.length); // fill buffer with numRows (or HEIGHT, whatever is smaller) rows of data int n = Math.min(numRows, HEIGHT); int offset = leftColumns; int bufferYIndex = 0; while (n-- > 0) { fillBuffer(INDEX_RED, bufferYIndex, redBuffer, offset); fillBuffer(INDEX_GREEN, bufferYIndex, greenBuffer, offset); fillBuffer(INDEX_BLUE, bufferYIndex++, blueBuffer, offset); offset += newWidth; } int bufferLastRowOffset = offset - newWidth; int[] originalRgb = new int[3]; int[] quantizedRgb = new int[3]; for (int y = 0; y < HEIGHT; y++) { int bufferIndex = leftColumns; for (int x = 0; x < WIDTH; x++) { originalRgb[INDEX_RED] = adjust(redBuffer[bufferIndex], MAX); originalRgb[INDEX_GREEN] = adjust(greenBuffer[bufferIndex], MAX); originalRgb[INDEX_BLUE] = adjust(blueBuffer[bufferIndex], MAX); int paletteIndex = quantizer.map(originalRgb, quantizedRgb); out.putSample(0, x, y, paletteIndex); // red int error = originalRgb[INDEX_RED] - quantizedRgb[INDEX_RED]; for (int i = 0; i < NUM_ERROR_PIXELS; i++) { int errorPart = error * errorNum[i] / errorDen[i]; redBuffer[bufferIndex + indexLut[i]] += errorPart; } // green error = originalRgb[INDEX_GREEN] - quantizedRgb[INDEX_GREEN]; for (int i = 0; i < NUM_ERROR_PIXELS; i++) { int errorPart = error * errorNum[i] / errorDen[i]; greenBuffer[bufferIndex + indexLut[i]] += errorPart; } // blue error = originalRgb[INDEX_BLUE] - quantizedRgb[INDEX_BLUE]; for (int i = 0; i < NUM_ERROR_PIXELS; i++) { int errorPart = error * errorNum[i] / errorDen[i]; blueBuffer[bufferIndex + indexLut[i]] += errorPart; } bufferIndex++; } System.arraycopy(redBuffer, newWidth, redBuffer, 0, redBuffer.length - newWidth); System.arraycopy(greenBuffer, newWidth, greenBuffer, 0, greenBuffer.length - newWidth); System.arraycopy(blueBuffer, newWidth, blueBuffer, 0, blueBuffer.length - newWidth); if (bufferYIndex < HEIGHT) { fillBuffer(INDEX_RED, bufferYIndex, redBuffer, bufferLastRowOffset); fillBuffer(INDEX_GREEN, bufferYIndex, greenBuffer, bufferLastRowOffset); fillBuffer(INDEX_BLUE, bufferYIndex++, blueBuffer, bufferLastRowOffset); } setProgress(y, HEIGHT); } setOutputImage(out); } private void process(RGB24Image in, RGB24Image out) { final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); final int MAX = 255; if (out == null) { out = (RGB24Image)in.createCompatibleImage(WIDTH, HEIGHT); } final int NUM_ERROR_PIXELS = errorNum.length; // create buffers int[] redBuffer = new int[newWidth * numRows]; int[] greenBuffer = new int[newWidth * numRows]; int[] blueBuffer = new int[newWidth * numRows]; // fill buffer with numRows (or HEIGHT, whatever is smaller) rows of data int n = Math.min(numRows, HEIGHT); int offset = leftColumns; int bufferYIndex = 0; while (n-- > 0) { fillBuffer(INDEX_RED, bufferYIndex, redBuffer, offset); fillBuffer(INDEX_GREEN, bufferYIndex, greenBuffer, offset); fillBuffer(INDEX_BLUE, bufferYIndex++, blueBuffer, offset); offset += newWidth; } int bufferLastRowOffset = offset - newWidth; int[] originalRgb = new int[3]; int[] quantizedRgb = new int[3]; for (int y = 0; y < HEIGHT; y++) { int bufferIndex = leftColumns; for (int x = 0; x < WIDTH; x++) { originalRgb[INDEX_RED] = adjust(redBuffer[bufferIndex], MAX); originalRgb[INDEX_GREEN] = adjust(greenBuffer[bufferIndex], MAX); originalRgb[INDEX_BLUE] = adjust(blueBuffer[bufferIndex], MAX); /*int paletteIndex = quantizer.map(originalRgb, quantizedRgb); out.putSample(0, x, y, paletteIndex);*/ out.putSample(INDEX_RED, x, y, quantizedRgb[INDEX_RED]); out.putSample(INDEX_GREEN, x, y, quantizedRgb[INDEX_GREEN]); out.putSample(INDEX_BLUE, x, y, quantizedRgb[INDEX_BLUE]); // red int error = originalRgb[INDEX_RED] - quantizedRgb[INDEX_RED]; for (int i = 0; i < NUM_ERROR_PIXELS; i++) { int errorPart = error * errorNum[i] / errorDen[i]; redBuffer[bufferIndex + indexLut[i]] += errorPart; } // green error = originalRgb[INDEX_GREEN] - quantizedRgb[INDEX_GREEN]; for (int i = 0; i < NUM_ERROR_PIXELS; i++) { int errorPart = error * errorNum[i] / errorDen[i]; greenBuffer[bufferIndex + indexLut[i]] += errorPart; } // blue error = originalRgb[INDEX_BLUE] - quantizedRgb[INDEX_BLUE]; for (int i = 0; i < NUM_ERROR_PIXELS; i++) { int errorPart = error * errorNum[i] / errorDen[i]; blueBuffer[bufferIndex + indexLut[i]] += errorPart; } bufferIndex++; } /*for (int i = 0, j = newWidth; j < buffer.length; i++, j++) { buffer[i] = buffer[j]; }*/ System.arraycopy(redBuffer, newWidth, redBuffer, 0, redBuffer.length - newWidth); System.arraycopy(greenBuffer, newWidth, greenBuffer, 0, greenBuffer.length - newWidth); System.arraycopy(blueBuffer, newWidth, blueBuffer, 0, blueBuffer.length - newWidth); if (bufferYIndex < HEIGHT) { fillBuffer(INDEX_RED, bufferYIndex, redBuffer, bufferLastRowOffset); fillBuffer(INDEX_GREEN, bufferYIndex, greenBuffer, bufferLastRowOffset); fillBuffer(INDEX_BLUE, bufferYIndex++, blueBuffer, bufferLastRowOffset); } setProgress(y, HEIGHT); } setOutputImage(out); } /** * Sets the number of bits to be in the output image when a grayscale image * is quantized. * If the input image is of type {@link Gray8Image}, only values between 1 and 7 * are valid. * @param numBits the number of bits in the output image */ public void setGrayscaleOutputBits(int numBits) { grayBits = numBits; } /** * Sets the color quantizer to be used (if the input image is * a truecolor image). * @param q an object of a class implementing the RGBQuantizer interface */ public void setQuantizer(RGBQuantizer q) { quantizer = q; } /** * Set information on how errors are to be distributed by this error diffusion * dithering operation. *

* Error diffusion dithering works by quantizing each pixel and distributing the * resulting error to neighboring pixels. * Quantizing maps a pixel to another pixel. * Each pixel is made up of one or more samples (as an example, three samples * rorig, gorig and borig for the * original pixel of an RGB image and rquant, gquant and * bquant for the quantized pixel). *

* The process of quantization attempts to find a quantized pixel that is as * close to the original as possible. * In the ideal case, the difference between original and quantized pixel is * zero for each sample. * Otherwise, this quantization error is non-zero, positive or negative. * Example: original pixel (12, 43, 33), quantized pixel (10, 47, 40); the * error is (12 - 10, 43 - 47, 40 - 33) = (2, -4, 7). * The error (2, -4, 7) is to be distributed to neighboring pixels. *

* The data argument of this constructor describes how to do that. * It is a two-dimensional array of int values. * Each of the one-dimensional int arrays of data describe * one neighboring pixel and the relative amount of the error that it gets. * That is why data.length specifies the number of neighboring * pixels involved in distributing the error. * Let's call the pixel that was just quantized the current pixel. * It is at image position (x, y). *

* Each of the one-dimensional arrays that are part of data * must have a length of 4. * The meaning of these four values is now described. * The values can be accessed by the INDEX_xyz constants of this class. * These four values describe the position of one neighboring pixel and * the relative amount of the error that will be added to or subtracted * from it. *

* Example: the predefined dithering type Floyd-Steinberg. * It has the following data array: *
	 * int[][] FLOYD_STEINBERG = {{ 1,  0, 7, 16},
	 *   {-1,  1, 3, 16},
	 *   { 0,  1, 5, 16},
	 *   { 1,  1, 1, 16}};
	 * 
* Each of the one-dimensional arrays is of length 4. * Accidentally, there are also four one-dimensional arrays. * The number of arrays is up to the designer. * The first array {1, 0, 7, 16} is interpreted as follows--go to * the pixel with a horizontal difference of 1 and a vertical difference of 0 * (so, the pixel to the right of the current pixel) and add 7 / 16th of the * quantization error to it. * Then go to the pixel at position (-1, 1) (one to the left, one row below the * current row) and add 3 / 16th of the error to it. * The other two one-dimensional arrays are processed just like that. *

* As you can see, the four relative errors 1/16, 3/16, 5/16 and 7/16 sum up to * 1 (or 16/16); this is in a precondition to make sure that the error * is distributed completely. * * @param data contains a description of how the error is to be distributed */ public void setTemplateData(int[][] data) { templateData = data; } /** * When dithering an RGB input image, this method specifies whether the * output will be an {@link net.sourceforge.jiu.data.RGBIntegerImage} * (true) or a {@link net.sourceforge.jiu.data.Paletted8Image} (false). * @param truecolor true if truecolor output is wanted */ public void setTruecolorOutput(boolean truecolor) { useTruecolorOutput = truecolor; } /** * Sets a new template type. * The argument must be one of the TYPE_xyz constants of this class. * @param type int value, one of the TYPE_xyz constants of this class * @throws IllegalArgumentException if the argument is not of the TYPE_xyz constants */ public void setTemplateType(int type) { switch(type) { case(TYPE_FLOYD_STEINBERG): { templateData = FLOYD_STEINBERG_DATA; break; } case(TYPE_STUCKI): { templateData = STUCKI_DATA; break; } case(TYPE_BURKES): { templateData = BURKES_DATA; break; } case(TYPE_SIERRA): { templateData = SIERRA_DATA; break; } case(TYPE_JARVIS_JUDICE_NINKE): { templateData = JARVIS_JUDICE_NINKE_DATA; break; } case(TYPE_STEVENSON_ARCE): { templateData = STEVENSON_ARCE_DATA; break; } default: { throw new IllegalArgumentException("Unknown template type: " + type + "."); } } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/RoundSpotFunction.java0000664000000000000000000000101507741250132027425 0ustar /* * RoundSpotFunction * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.dithering; import net.sourceforge.jiu.color.dithering.SpotFunction; /** * A round spot function. * @author Marco Schmidt * @since 0.9.0 * @see ClusteredDotDither */ public class RoundSpotFunction implements SpotFunction { public double compute(double x, double y) { return 1.0 - x * x - y * y; } public boolean isBalanced() { return false; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/package.html0000664000000000000000000000045507741250132025407 0ustar

Classes for conversion between color spaces. Right now, this mostly includes conversion to the RGB (red / green / blue) color space. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/dithering/OrderedDither.java0000664000000000000000000003567510572434070026532 0ustar /* * OrderedDither * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.dithering; import net.sourceforge.jiu.color.quantization.UniformPaletteQuantizer; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * This operation reduces the color depth of RGB truecolor images and grayscale images * by applying ordered dithering. * A relatively nice output image (for a human observer) will be created at the * cost of introducing noise into the output image. * The algorithm is relatively fast, but its quality is usually inferior to what * can be reached by using {@link ErrorDiffusionDithering} or similar other methods. *

Supported conversions

* *

Usage example

* The following code snippet demonstrates how to create a paletted * image with 256 colors, using three bits for red and green and two * bits for blue, from an RGB truecolor image. *
 * OrderedDither od = new OrderedDither();
 * od.setRgbBits(3, 3, 2);
 * od.setInputImage(image);
 * od.process();
 * Paletted8Image ditheredImage = (Paletted8Image)od.getOutputImage();
 * 
* @author Marco Schmidt */ public class OrderedDither extends ImageToImageOperation implements RGBIndex { private int[] values; private int valueWidth; private int valueHeight; private int grayBits = 3; private int redBits = 3; private int greenBits = 3; private int blueBits = 2; private void process(Gray8Image in, Gray8Image out) { if (out == null) { out = new MemoryGray8Image(in.getWidth(), in.getHeight()); setOutputImage(out); } int D1 = 4; int D2 = 4; int D1D2 = D1 * D2; final int[] DITHER_MATRIX = {0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5}; final int SPACE = 255 / ((1 << grayBits) - 1); final int SHIFT = 8 - grayBits; final int NUM_VALUES = 1 << grayBits; final byte[] OUTPUT_SAMPLES = new byte[NUM_VALUES]; for (int i = 0; i < OUTPUT_SAMPLES.length; i++) { OUTPUT_SAMPLES[i] = (byte)((i * 255) / NUM_VALUES); } final int[] DITHER_SIGNAL = new int[D1D2]; for (int i = 0; i < D1D2; i++) { DITHER_SIGNAL[i] = (2 * DITHER_MATRIX[i] - D1D2 + 1) * SPACE / (2 * D1D2); } final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); int rowOffset = 0; for (int y = 0; y < HEIGHT; y++) { int offset = rowOffset; final int MAX_OFFSET = rowOffset + D1; for (int x = 0; x < WIDTH; x++) { int sample = in.getSample(0, x, y); sample = sample + DITHER_SIGNAL[ offset++ ]; if (offset == MAX_OFFSET) { offset = rowOffset; } if (sample < 0) { sample = 0; } else if (sample > 255) { sample = 255; } out.putByteSample(0, x, y, OUTPUT_SAMPLES[sample >> SHIFT]); } rowOffset += D1; if (rowOffset >= DITHER_SIGNAL.length) { rowOffset = 0; } setProgress(y, HEIGHT); } } private void process(Gray8Image in, BilevelImage out) { if (out == null) { out = new MemoryBilevelImage(in.getWidth(), in.getHeight()); setOutputImage(out); } if (values == null) { setStandardThresholdValues(); } out.clear(BilevelImage.BLACK); int rowOffset = 0; final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { int offset = rowOffset; final int MAX_OFFSET = rowOffset + valueWidth; for (int x = 0; x < in.getWidth(); x++) { if (in.getSample(x, y) >= values[offset++]) { out.putWhite(x, y); } if (offset == MAX_OFFSET) { offset = rowOffset; } } setProgress(y, HEIGHT); rowOffset += valueWidth; if (rowOffset >= values.length) { rowOffset = 0; } } } private void process(RGB24Image in, Paletted8Image out) { UniformPaletteQuantizer upq = new UniformPaletteQuantizer(redBits, greenBits, blueBits); if (out == null) { out = new MemoryPaletted8Image(in.getWidth(), in.getHeight(), upq.createPalette()); setOutputImage(out); } int D1 = 4; int D2 = 4; int D1D2 = D1 * D2; final int[] DITHER_MATRIX = {0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5}; final int RED_SPACE = 255 / ((1 << redBits) - 1); final int GREEN_SPACE = 255 / ((1 << greenBits) - 1); final int BLUE_SPACE = 255 / ((1 << blueBits) - 1); /*final int RED_SHIFT = 8 - redBits; final int GREEN_SHIFT = 8 - redBits; final int BLUE_SHIFT = 8 - redBits; final int NUM_RED_VALUES = 1 << redBits; final int NUM_GREEN_VALUES = 1 << greenBits; final int NUM_BLUE_VALUES = 1 << blueBits;*/ final int[] RED_DITHER_SIGNAL = new int[D1D2]; final int[] GREEN_DITHER_SIGNAL = new int[D1D2]; final int[] BLUE_DITHER_SIGNAL = new int[D1D2]; for (int i = 0; i < D1D2; i++) { RED_DITHER_SIGNAL[i] = (2 * DITHER_MATRIX[i] - D1D2 + 1) * RED_SPACE / (2 * D1D2); GREEN_DITHER_SIGNAL[i] = (2 * DITHER_MATRIX[i] - D1D2 + 1) * GREEN_SPACE / (2 * D1D2); BLUE_DITHER_SIGNAL[i] = (2 * DITHER_MATRIX[i] - D1D2 + 1) * BLUE_SPACE / (2 * D1D2); } final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); int rowOffset = 0; for (int y = 0; y < HEIGHT; y++) { int offset = rowOffset; final int MAX_OFFSET = rowOffset + D1; for (int x = 0; x < WIDTH; x++) { int redSample = in.getSample(INDEX_RED, x, y); redSample = redSample + RED_DITHER_SIGNAL[ offset ]; if (redSample < 0) { redSample = 0; } else if (redSample > 255) { redSample = 255; } int greenSample = in.getSample(INDEX_GREEN, x, y); greenSample = greenSample + GREEN_DITHER_SIGNAL[ offset ]; if (greenSample < 0) { greenSample = 0; } else if (greenSample > 255) { greenSample = 255; } int blueSample = in.getSample(INDEX_BLUE, x, y); blueSample = blueSample + BLUE_DITHER_SIGNAL[ offset ]; if (blueSample < 0) { blueSample = 0; } else if (blueSample > 255) { blueSample = 255; } out.putSample(0, x, y, upq.mapToIndex(redSample, greenSample, blueSample)); offset++; if (offset == MAX_OFFSET) { offset = rowOffset; } } rowOffset += D1; if (rowOffset >= DITHER_MATRIX.length) { rowOffset = 0; } setProgress(y, HEIGHT); } } private void process(RGB24Image in, RGB24Image out) { //UniformPaletteQuantizer upq = new UniformPaletteQuantizer(redBits, greenBits, blueBits); //System.out.println("RGB=>RGB, r=" + redBits+ ", g=" + greenBits + ", b=" + blueBits); if (out == null) { out = new MemoryRGB24Image(in.getWidth(), in.getHeight()); setOutputImage(out); } int D1 = 4; int D2 = 4; int D1D2 = D1 * D2; final int[] DITHER_MATRIX = {0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5}; final int RED_SPACE = 255 / ((1 << redBits) - 1); final int GREEN_SPACE = 255 / ((1 << greenBits) - 1); final int BLUE_SPACE = 255 / ((1 << blueBits) - 1); final int RED_SHIFT = 8 - redBits; final int GREEN_SHIFT = 8 - greenBits; final int BLUE_SHIFT = 8 - blueBits; final int MAX_RED = (1 << redBits) - 1; final int MAX_GREEN = (1 << greenBits) - 1; final int MAX_BLUE = (1 << blueBits) - 1; /*final int NUM_RED_VALUES = 1 << redBits; final int NUM_GREEN_VALUES = 1 << greenBits; final int NUM_BLUE_VALUES = 1 << blueBits;*/ final int[] RED_DITHER_SIGNAL = new int[D1D2]; final int[] GREEN_DITHER_SIGNAL = new int[D1D2]; final int[] BLUE_DITHER_SIGNAL = new int[D1D2]; for (int i = 0; i < D1D2; i++) { RED_DITHER_SIGNAL[i] = (2 * DITHER_MATRIX[i] - D1D2 + 1) * RED_SPACE / (2 * D1D2); GREEN_DITHER_SIGNAL[i] = (2 * DITHER_MATRIX[i] - D1D2 + 1) * GREEN_SPACE / (2 * D1D2); BLUE_DITHER_SIGNAL[i] = (2 * DITHER_MATRIX[i] - D1D2 + 1) * BLUE_SPACE / (2 * D1D2); } final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); int rowOffset = 0; for (int y = 0; y < HEIGHT; y++) { int offset = rowOffset; final int MAX_OFFSET = rowOffset + D1; for (int x = 0; x < WIDTH; x++) { // RED int redSample = in.getSample(INDEX_RED, x, y); redSample = redSample + RED_DITHER_SIGNAL[ offset ]; if (redSample < 0) { redSample = 0; } else if (redSample > 255) { redSample = 255; } redSample >>= RED_SHIFT; out.putSample(RGBIndex.INDEX_RED, x, y, redSample * 255 / MAX_RED); // GREEN int greenSample = in.getSample(INDEX_GREEN, x, y); greenSample = greenSample + GREEN_DITHER_SIGNAL[ offset ]; if (greenSample < 0) { greenSample = 0; } else if (greenSample > 255) { greenSample = 255; } greenSample >>= GREEN_SHIFT; out.putSample(RGBIndex.INDEX_GREEN, x, y, greenSample * 255 / MAX_GREEN); // BLUE int blueSample = in.getSample(INDEX_BLUE, x, y); blueSample = blueSample + BLUE_DITHER_SIGNAL[offset]; if (blueSample < 0) { blueSample = 0; } else if (blueSample > 255) { blueSample = 255; } blueSample >>= BLUE_SHIFT; out.putSample(RGBIndex.INDEX_BLUE, x, y, blueSample * 255 / MAX_BLUE); //out.putSample(0, x, y, upq.mapToIndex(redSample, greenSample, blueSample)); offset++; if (offset == MAX_OFFSET) { offset = rowOffset; } } rowOffset += D1; if (rowOffset >= DITHER_MATRIX.length) { rowOffset = 0; } setProgress(y, HEIGHT); } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); PixelImage out = getOutputImage(); if (in instanceof RGB24Image) { int sum = redBits + greenBits + blueBits; if (sum > 8) { process((RGB24Image)in, (RGB24Image)out); } else { process((RGB24Image)in, (Paletted8Image)out); } } else if (grayBits == 1) { process((Gray8Image)in, (BilevelImage)out); } else if (grayBits >= 2 && grayBits <= 7) { process((Gray8Image)in, (Gray8Image)out); } } public void setOutputBits(int bits) { if (bits >= 1 && bits <= 7) { grayBits = bits; } else { throw new IllegalArgumentException("Grayscale output bits must be from 1..7; got " + bits); } } /** * Sets the number of bits to be used for each RGB component in the output image. * Each argument must be one or larger. * The values defined by this method are only used if the input image implements * RGBIntegerImage. * Later, in {@link #process}, these values are checked against * the actual number of bits per component in the input image. * If any of the arguments of this method is equal to or larger * than those actual bits per channel values, a WrongParameterException * will then be thrown. * Right now, there is no way how this can be checked, because * the input image may not have been defined yet. * @param red number of bits for the red channel in the output image * @param green number of bits for the green channel in the output image * @param blue number of bits for the blue channel in the output image * @throws IllegalArgumentException if at least one argument is smaller than 1 */ public void setRgbBits(int red, int green, int blue) { if (red > 0 && green > 0 && blue > 0) { redBits = red; greenBits = green; blueBits = blue; } else { throw new IllegalArgumentException("All parameters must be 1 or larger."); } } /** * Calls {@link #setThresholdValues} with a 16 x 16 matrix. */ public void setStandardThresholdValues() { final int[] VALUES = { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255, 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127, 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223, 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95, 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247, 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119, 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215, 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87, 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253, 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125, 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221, 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93, 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245, 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117, 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213, 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }; setThresholdValues(VALUES, 16, 16); } /** * Defines a matrix of threshold values that will be used for grayscale * dithering. * @param values the int values to use for comparing * @param valueWidth * @param valueHeight */ public void setThresholdValues(int[] values, int valueWidth, int valueHeight) { if (values == null) { throw new IllegalArgumentException("The value array must be non-null."); } if (valueWidth < 1) { throw new IllegalArgumentException("The width argument must be at least 1."); } if (valueHeight < 1) { throw new IllegalArgumentException("The height argument must be at least 1."); } this.values = values; this.valueWidth = valueWidth; this.valueHeight = valueHeight; if (this.valueHeight * this.valueWidth < this.values.length) { throw new IllegalArgumentException("The array must have at least valuesWidth * valuesHeight elements.."); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/analysis/0000775000000000000000000000000010546532076022777 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/analysis/TextureAnalysis.java0000664000000000000000000001673610611674621027017 0ustar /* * TextureAnalysis * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.analysis; import net.sourceforge.jiu.color.data.CoOccurrenceMatrix; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.Operation; /** * This class determines a number of properties for a given co-occurrence matrix. * The only input parameter is a mandatory co-occurrence matrix object * to be specified using {@link #setMatrix}. * Then {@link #process} must be called. * After that, the various properties can be retrieved using the * corresponding get methods, e.g. {@link #getContrast}, * {@link #getEnergy} etc. *

* Static convenience methods {@link #compute(CoOccurrenceMatrix)} and * {@link #compute(IntegerImage, int)} exist for simplified usage. * You may need to cast your {@link net.sourceforge.jiu.data.PixelImage} to * {@link net.sourceforge.jiu.data.IntegerImage} first. *

Usage example

* Use like that: *
GrayIntegerImage img = ...;
TextureAnalysis op = TextureAnalysis.compute(img);
System.out.println("Entropy=" + op.getEntropy());
* Use the compute method that takes a {@link net.sourceforge.jiu.color.data.CoOccurrenceFrequencyMatrix} * if you want to reuse a matrix object. *

*

Caveat: memory consumption

*

* Will not work with 16 bit image objects because {@link net.sourceforge.jiu.color.data.MemoryCoOccurrenceMatrix} * is rather unsophisticated, trying to allocate 4 * 65536 * 65536 bytes (16 GB). * Solution is an implementation of {@link net.sourceforge.jiu.color.data.CoOccurrenceMatrix} * which creates element counters on demand. * * The following resources were helpful when creating this class: *

* @since 0.7.0 * * @author Marco Schmidt */ public class TextureAnalysis extends Operation { private CoOccurrenceMatrix matrix; private int contrast; private double correlation; private int dissimilarity; private int energy; private double entropy; private double homogeneity; private int sum; private boolean symmetry; /** * Create a TextureAnalysis operation with the results computed from a * given matrix. * @param matrix co-occurrence matrix to use for texture analysis * @return TextureAnalysis object with all attributes computed * @throws MissingParameterException if matrix is null * @since 0.14.2 */ public static TextureAnalysis compute(CoOccurrenceMatrix matrix) throws MissingParameterException { TextureAnalysis op = new TextureAnalysis(); op.setMatrix(matrix); op.process(); return op; } /** * For one channel of the argument image, * create a TextureAnalysis operation with all attributes * @param image the IntegerImage for which the texture analysis is being done * @param channelIndex zero-based index of channel to work on * @return TextureAnalysis object with all attributes computed * @throws MissingParameterException if matrix is null * @since 0.14.2 */ public static TextureAnalysis compute(IntegerImage image, int channelIndex) throws MissingParameterException { CoOccurrenceMatrix matrix = MatrixCreator.createCoOccurrenceMatrix(image, channelIndex); return compute(matrix); } /** * Returns the contrast value determined in {@link #process}. * Also called inertia. */ public int getContrast() { return contrast; } /** * Returns the correlation determined in {@link #process}. */ public double getCorrelation() { return correlation; } /** * Returns the dissimilarity value determined in {@link #process}. */ public int getDissimilarity() { return dissimilarity; } /** * Returns the energy value determined in {@link #process}. */ public int getEnergy() { return energy; } /** * Returns the entropy value determined in {@link #process}. */ public double getEntropy() { return entropy; } /** * Returns the homogeneity value determined in {@link #process}. * Also called inverse difference moment. */ public double getHomogeneity() { return homogeneity; } /** * Returns the sum of all entries in the matrix. */ public int getSum() { return sum; } public boolean isSymmetrical() { return symmetry; } /** * Run over the input matrix and determine contrast, energy, entropy and homogeneity * of that matrix. * @throws MissingParameterException if no co-occurrence matrix was provided using * {@link #setMatrix} */ public void process() throws MissingParameterException { if (matrix == null) { throw new MissingParameterException("No input co-occurrence matrix was provided."); } final int DIMENSION = matrix.getDimension(); int items = 0; final int TOTAL_ITEMS = DIMENSION * 3; // initialize mu_i and mu_j double[] muI = new double[DIMENSION]; double[] muJ = new double[DIMENSION]; for (int k = 0; k < DIMENSION; k++) { muI[k] = 0.0; muJ[k] = 0.0; for (int i = 0; i < DIMENSION; i++) { for (int j = 0; j < DIMENSION; j++) { int value = matrix.getValue(i, j); muI[k] += i * value; muJ[k] += j * value; } } setProgress(items++, TOTAL_ITEMS); } // initialize sigma_i and sigma_j double[] sigmaI = new double[DIMENSION]; double[] sigmaJ = new double[DIMENSION]; for (int k = 0; k < DIMENSION; k++) { sigmaI[k] = 0.0; sigmaJ[k] = 0.0; for (int i = 0; i < DIMENSION; i++) { for (int j = 0; j < DIMENSION; j++) { int value = matrix.getValue(i, j); double a = (i - muI[i]); sigmaI[k] += value * a * a; double b = (j - muJ[j]); sigmaJ[k] += value * b * b; } } setProgress(items++, TOTAL_ITEMS); } contrast = 0; dissimilarity = 0; energy = 0; entropy = 0; homogeneity = 0; sum = 0; symmetry = true; for (int i = 0; i < DIMENSION; i++) { for (int j = 0; j < DIMENSION; j++) { int value = matrix.getValue(i, j); symmetry = symmetry && value == matrix.getValue(j, i); sum += value; energy += value * value; int diffAbs = (i - j); if (diffAbs < 0) { diffAbs = - diffAbs; } dissimilarity += diffAbs * value; contrast += diffAbs * diffAbs * value; if (value != 0) // log not defined for 0 { entropy += value * Math.log(value); } homogeneity += value / (1.0 + diffAbs); double a = sigmaI[i] * sigmaJ[j]; if (a != 0.0) { correlation += (value * (i - muI[i]) * (j - muJ[j])) / Math.sqrt(a); } } setProgress(items++, TOTAL_ITEMS); } } /** * Sets the matrix to be used by this operation to the argument value. * @param m the matrix for which the various properties will be computed */ public void setMatrix(CoOccurrenceMatrix m) { matrix = m; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/analysis/MeanDifference.java0000664000000000000000000001511410546743753026505 0ustar /* * MeanDifference * * Copyright (c) 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.analysis; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.Operation; import net.sourceforge.jiu.ops.WrongParameterException; /** * This operation determines the mean difference between two images. * It requires two images of the same resolution and adds the absolute difference * of all samples. * Then it divides by the number of samples in the image (width times height times * number of channels). *

Supported combinations of image types

* *

Usage example

*
Double meanDifference = MeanDifference.compute(image1, image2);
* @author Marco Schmidt * @since 0.11.0 */ public class MeanDifference extends Operation { private double diff; private PixelImage image1; private PixelImage image2; /** * Compute the mean difference between two images. * @param image1 first image to be examined * @param image2 second image to be examined * @return sum of all differences divided by number of pixels * as Double or null on failure (image types * are incompatible) * @since 0.15.0 */ public static Double compute(PixelImage image1, PixelImage image2) { MeanDifference diff = new MeanDifference(); diff.setImages(image1, image2); try { diff.process(); return new Double(diff.getDifference()); } catch (Exception e) { return null; } } /** * Returns abs(a - b). * @param a first number * @param b second number * @return abs(a - b) */ private static int computeDiff(int a, int b) { // code is equal to Math.abs(a - b); int diff = a - b; if (diff < 0) { return -diff; } else { return diff; } } /** * After a call to process, returns the determined mean difference value. * @return difference value, 0.0 or larger */ public double getDifference() { return diff; } public void process() throws MissingParameterException, WrongParameterException { if (image1 == null) { throw new MissingParameterException("You must specify images using setImages."); } boolean sameType = image1.getImageType() == image2.getImageType(); if (image1 instanceof RGB24Image && image2 instanceof Paletted8Image) { process((RGB24Image)image1, (Paletted8Image)image2); } else if (image2 instanceof RGB24Image && image1 instanceof Paletted8Image) { process((RGB24Image)image2, (Paletted8Image)image1); } else if (sameType && image1 instanceof RGBIntegerImage) { process((RGBIntegerImage)image1, (RGBIntegerImage)image2); } else if (sameType && image1 instanceof GrayIntegerImage) { process((GrayIntegerImage)image1, (GrayIntegerImage)image2); } else { throw new WrongParameterException("Not a supported image type combination."); } } private void process(GrayIntegerImage image1, GrayIntegerImage image2) { final int HEIGHT = image1.getHeight(); final int WIDTH = image1.getWidth(); long sum = 0; for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { sum += computeDiff(image1.getSample(x, y), image2.getSample(x, y)); } setProgress(y, HEIGHT); } setDifference((double)sum / (WIDTH * HEIGHT)); } private void process(RGB24Image image1, Paletted8Image image2) { final int HEIGHT = image1.getHeight(); final int WIDTH = image1.getWidth(); long sum = 0; Palette pal = image2.getPalette(); int[] red = pal.getSamples(RGBIndex.INDEX_RED); int[] green = pal.getSamples(RGBIndex.INDEX_GREEN); int[] blue = pal.getSamples(RGBIndex.INDEX_BLUE); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { int palSample = image2.getSample(x, y); sum += computeDiff(image1.getSample(RGBIndex.INDEX_RED, x, y), red[palSample]); sum += computeDiff(image1.getSample(RGBIndex.INDEX_GREEN, x, y), green[palSample]); sum += computeDiff(image1.getSample(RGBIndex.INDEX_BLUE, x, y), blue[palSample]); } setProgress(y, HEIGHT); } setDifference((double)sum / (WIDTH * HEIGHT * 3)); } private void process(RGBIntegerImage image1, RGBIntegerImage image2) { final int HEIGHT = image1.getHeight(); final int WIDTH = image1.getWidth(); long sum = 0; for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { sum += computeDiff(image1.getSample(RGBIndex.INDEX_RED, x, y), image2.getSample(RGBIndex.INDEX_RED, x, y)); sum += computeDiff(image1.getSample(RGBIndex.INDEX_GREEN, x, y), image2.getSample(RGBIndex.INDEX_GREEN, x, y)); sum += computeDiff(image1.getSample(RGBIndex.INDEX_BLUE, x, y), image2.getSample(RGBIndex.INDEX_BLUE, x, y)); } setProgress(y, HEIGHT); } setDifference((double)sum / (WIDTH * HEIGHT * 3)); } private void setDifference(double newValue) { diff = newValue; } /** * Sets the two images for which the mean difference is to be * determined. * @param firstImage first image * @param secondImage second image * @throws IllegalArgumentException if either of the images is null, * if their resolution is different or if their types are not supported * by this operation */ public void setImages(PixelImage firstImage, PixelImage secondImage) { if (firstImage == null || secondImage == null) { throw new IllegalArgumentException("Both image arguments must be non-null."); } if (firstImage.getWidth() != secondImage.getWidth()) { throw new IllegalArgumentException("The images must have the same width."); } if (firstImage.getHeight() != secondImage.getHeight()) { throw new IllegalArgumentException("The images must have the same height."); } image1 = firstImage; image2 = secondImage; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/analysis/package.html0000664000000000000000000000041207741250131025245 0ustar

Contains classes that analyze pixel or sample values and thus determine certain image properties. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/analysis/MatrixCreator.java0000664000000000000000000001554310546742753026443 0ustar /* * MatrixCreator * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.analysis; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.color.data.CoOccurrenceMatrix; import net.sourceforge.jiu.color.data.CoOccurrenceFrequencyMatrix; import net.sourceforge.jiu.color.data.MemoryCoOccurrenceMatrix; import net.sourceforge.jiu.color.data.MemoryCoOccurrenceFrequencyMatrix; /** * This class creates and initializes co-occurrence matrices and co-occurrence * frequency matrices. * @author Marco Schmidt */ public class MatrixCreator { private MatrixCreator() { // to prevent instantiation } /** * Create a co-occurrence matrix for a 16 bit grayscale image. * @param image the image for which the matrix is created * @return the resulting matrix * @since 0.15.0 */ public static CoOccurrenceMatrix createCoOccurrenceMatrix(Gray16Image image) { return createCoOccurrenceMatrix(image, 0); } public static CoOccurrenceMatrix createCoOccurrenceMatrix(Gray8Image image) { return createCoOccurrenceMatrix(image, 0); } public static CoOccurrenceMatrix createCoOccurrenceMatrix(Paletted8Image image) { return createCoOccurrenceMatrix(image, 0); } public static CoOccurrenceMatrix createCoOccurrenceMatrix(IntegerImage image, int channelIndex) { if (image == null) { throw new IllegalArgumentException("The image must non-null."); } if (channelIndex < 0) { throw new IllegalArgumentException("The channel index must be zero or larger."); } if (channelIndex >= image.getNumChannels()) { throw new IllegalArgumentException("The channel index must be smaller than the number of channels in the image (" + image.getNumChannels() + ")."); } int dim = image.getMaxSample(channelIndex) + 1; MemoryCoOccurrenceMatrix matrix = new MemoryCoOccurrenceMatrix(dim); initCoOccurrenceMatrix(image, channelIndex, matrix); return matrix; } /** * Initializes a co-occurrence matrix from the input image, using the direct * four neighbor pixels. * The number of entries in the palette of the argument image must be equal to the dimension * of the argument matrix. * @param image the image that will be used to initialize the matrix * @param matrix the matrix that will first be cleared and then initialized from the image * @throws IllegalArgumentException if at least one of the arguments is null or if the * palette size is not equal to the matrix dimension */ public static void initCoOccurrenceMatrix(IntegerImage image, int channelIndex, CoOccurrenceMatrix matrix) { if (image == null) { throw new IllegalArgumentException("The image must non-null."); } if (channelIndex < 0) { throw new IllegalArgumentException("The channel index must be zero or larger."); } if (channelIndex >= image.getNumChannels()) { throw new IllegalArgumentException("The channel index must be smaller than the number of channels in the image (" + image.getNumChannels() + ")."); } int dim = image.getMaxSample(channelIndex) + 1; if (matrix == null) { throw new IllegalArgumentException("The matrix must non-null."); } if (matrix.getDimension() != dim) { throw new IllegalArgumentException("Dimension of matrix (" + matrix.getDimension() + " must be exactly one larger than " + "maximum sample value (" + (dim - 1) + ")."); } matrix.clear(); int maxX = image.getWidth() - 1; int maxY = image.getHeight() - 1; for (int y = 0; y <= maxY; y++) { for (int x = 0; x <= maxX; x++) { int index = image.getSample(channelIndex, x, y); if (x > 0) { int leftNeighbor = image.getSample(channelIndex, x - 1, y); matrix.incValue(index, leftNeighbor); } if (x < maxX) { int rightNeighbor = image.getSample(channelIndex, x + 1, y); matrix.incValue(index, rightNeighbor); } if (y > 0) { int topNeighbor = image.getSample(channelIndex, x, y - 1); matrix.incValue(index, topNeighbor); } if (y < maxY) { int bottomNeighbor = image.getSample(channelIndex, x, y + 1); matrix.incValue(index, bottomNeighbor); } } } } /** * Creates a new co-occurrence frequency with the same dimension as the argument co-occurrence * matrix, calls {@link #initCoOccurrenceFrequencyMatrix} with them to initialize the new matrix, * then returns it. * A {@link MemoryCoOccurrenceFrequencyMatrix} is created. * @param A the co-occurrence matrix from which the resulting matrix will be initialized * @return the newly-created co-occurrence frequency matrix * @throws IllegalArgumentException if the argument matrix is null */ public static CoOccurrenceFrequencyMatrix createCoOccurrenceFrequencyMatrix(final CoOccurrenceMatrix A) { if (A == null) { throw new IllegalArgumentException("Matrix argument must be non-null."); } int dimension = A.getDimension(); MemoryCoOccurrenceFrequencyMatrix matrix = new MemoryCoOccurrenceFrequencyMatrix(dimension); initCoOccurrenceFrequencyMatrix(A, matrix); return matrix; } /** * Initializes a co-occurrence frequency matrix from a co-occurrence matrix. * The two argument matrices must be non-null and have the same dimension. * @param A co-occurrence matrix used as input * @param cofm co-occurrence matrix, will be initialized by this method * @throws IllegalArgumentException if either matrix is null or if the dimensions are not equal */ public static void initCoOccurrenceFrequencyMatrix(final CoOccurrenceMatrix A, CoOccurrenceFrequencyMatrix cofm) { if (A == null) { throw new IllegalArgumentException("Co-occurrence matrix A argument must not be null."); } if (cofm == null) { throw new IllegalArgumentException("Co-occurrence frequency matrix cofm argument must not be null."); } final int DIMENSION = A.getDimension(); if (DIMENSION != cofm.getDimension()) { throw new IllegalArgumentException("Dimension of matrices A (" + DIMENSION + ") and cofm (" + cofm.getDimension() + ") must " + "be equal."); } cofm.clear(); double totalSum = 0.0; for (int i = 0; i < DIMENSION; i++) { // first sum up A[i, k], k = 0..dimension-1 double sum = 0.0; for (int k = 0; k < DIMENSION; k++) { sum += A.getValue(i, k); } totalSum += sum; for (int j = 0; j < DIMENSION; j++) { double value = A.getValue(i, j); double result; if (sum == 0.0) { result = 0.0; } else { result = value / sum; } cofm.setValue(i, j, result); } } cofm.computeStatistics(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/analysis/Histogram3DCreator.java0000664000000000000000000001175410546523562027316 0ustar /* * Histogram3DCreator * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.analysis; import net.sourceforge.jiu.color.data.Histogram3D; import net.sourceforge.jiu.color.data.NaiveHistogram3D; import net.sourceforge.jiu.color.data.OnDemandHistogram3D; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.Operation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * This class creates three-dimensional histograms for images with integer samples. * Only {@link net.sourceforge.jiu.data.IntegerImage} is supported. * Existing histogram objects can be given to this operation to be reused. *

* Note: Before JIU 0.10.0 there was a single HistogramCreator class. * @author Marco Schmidt * @since 0.10.0 */ public class Histogram3DCreator extends Operation { private Histogram3D hist; private IntegerImage image; private int index1; private int index2; private int index3; private boolean naive; /** * Static convenience method to count the number of colors in * any three channel {@link IntegerImage} object. * @param image the IntegerImage whose number of used colors is to be determined * @return the number of colors or null on failure (image does not have three * channels, etc.) * @since 0.15.0 */ public static Integer count(IntegerImage image) { if (image.getNumChannels() == 3) { try { Histogram3DCreator hc = new Histogram3DCreator(); hc.setImage((RGBIntegerImage)image); hc.process(); Histogram3D hist = hc.getHistogram(); return new Integer(hist.getNumUsedEntries()); } catch (Exception e) { return null; } } else { return null; } } private void createHistogramIfNecessary() { if (hist == null) { if (naive) { hist = new NaiveHistogram3D(image.getMaxSample(index1) + 1, image.getMaxSample(index2) + 1, image.getMaxSample(index3) + 1); } else { hist = new OnDemandHistogram3D(image.getMaxSample(index1) + 1, image.getMaxSample(index2) + 1, image.getMaxSample(index3) + 1); } } } /** * Returns the histogram initialized in this operation. */ public Histogram3D getHistogram() { return hist; } public void process() throws MissingParameterException, WrongParameterException { if (image == null) { throw new MissingParameterException("Image parameter missing."); } createHistogramIfNecessary(); if (hist.getMaxValue(0) < image.getMaxSample(index1) || hist.getMaxValue(1) < image.getMaxSample(index2) || hist.getMaxValue(2) < image.getMaxSample(index3)) { throw new WrongParameterException("Histogram is not large enough for image (hist max value / image max samples)."); } hist.clear(); final int WIDTH = image.getWidth(); final int HEIGHT = image.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { hist.increaseEntry( image.getSample(index1, x, y), image.getSample(index2, x, y), image.getSample(index3, x, y)); } setProgress(y, HEIGHT); } } /** * Sets the histogram object to be reused for this operation. * If this method is not called, a new histogram will be created. * @param histogram the histogram object to be used in this operation */ public void setHistogram3D(Histogram3D histogram) { hist = histogram; } /** * The image for which a histogram will be initialized. * Simply calls {@link #setImage(IntegerImage, int, int, int)} * with 0, 1 and 2 as parameters. * @param newImage the image for the histogram initialization */ public void setImage(IntegerImage newImage) { setImage(newImage, 0, 1, 2); } /** * The image for which a histogram will be initialized. * Simply calls {@link #setImage(IntegerImage, int, int, int)} * with 0, 1 and 2 as parameters. * @param newImage */ public void setImage(IntegerImage newImage, int channelIndex1, int channelIndex2, int channelIndex3) { if (newImage == null) { throw new IllegalArgumentException("Image argument must not be null."); } if (channelIndex1 < 0 || channelIndex1 >= newImage.getNumChannels() || channelIndex2 < 0 || channelIndex2 >= newImage.getNumChannels() || channelIndex3 < 0 || channelIndex3 >= newImage.getNumChannels()) { throw new IllegalArgumentException("The three index arguments must be >= 0 and < the number of channels."); } if (channelIndex1 == channelIndex2 || channelIndex2 == channelIndex3 || channelIndex1 == channelIndex3) { throw new IllegalArgumentException("The three index arguments must be different from each other."); } image = newImage; index1 = channelIndex1; index2 = channelIndex2; index3 = channelIndex3; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/analysis/Histogram1DCreator.java0000664000000000000000000001301010546742235027277 0ustar /* * Histogram1DCreator * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.analysis; import net.sourceforge.jiu.color.data.ArrayHistogram1D; import net.sourceforge.jiu.color.data.Histogram1D; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.Operation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * This class creates one-dimensional histograms for images with integer samples. * Only {@link net.sourceforge.jiu.data.IntegerImage} objects are supported. *

* Existing histogram objects can be given to this operation to be reused. * Give an existing {@link net.sourceforge.jiu.color.data.Histogram1D} object to this operation via * {@link #setHistogram(Histogram1D)}. *

* The histogram can be created for any channel of an IntegerImage. * The first channel (index 0) is the default channel. * Use {@link #setImage(IntegerImage, int)} to specify another one. *

* Note: Before JIU 0.10.0 there was a single HistogramCreator class. *

Usage example

* Creates a histogram for the third channel of an image, whose index is two * because channels are zero-based: *
 * Histogram1DCreator hc = new Histogram1DCreator();
 * hc.setImage(image, 2);
 * hc.process();
 * Histogram1D hist = hc.getHistogram();
 * 
* If you just want to learn the number of different samples in an image, * there's a static convenience method count for that. Again, we use the * third channel: *
Integer numUsedSamples = Histogram1DCreator.count(image, 2);
* @author Marco Schmidt * @since 0.10.0 */ public class Histogram1DCreator extends Operation { private Histogram1D hist; private int channelIndex; private IntegerImage image; /** * Static convenience method to count the number of different samples * in the first channel of the argument image. * @param image the IntegerImage to be examined * @return the number of different values or null on failure * (image is null, histogram data structure could not * be allocated) * @since 0.15.0 */ public static Integer count(IntegerImage image) { return count(image, 0); } /** * Static convenience method to count the number of different samples * in one of the channels of the argument image. * @param image the IntegerImage to be examined * @param channelIndex the zero-based index of the channel to use * @return the number of different values or null on failure * (image is null, channel index is invalid, * histogram data structure could not be allocated) * @since 0.15.0 */ public static Integer count(IntegerImage image, int channelIndex) { if (image != null && channelIndex >= 0 && channelIndex < image.getNumChannels()) { try { Histogram1DCreator hc = new Histogram1DCreator(); hc.setImage((RGBIntegerImage)image); hc.process(); Histogram1D hist = hc.getHistogram(); return new Integer(hist.getNumUsedEntries()); } catch (Exception e) { return null; } } else { return null; } } private void createHistogramIfNecessary() { if (hist == null) { hist = new ArrayHistogram1D(image.getMaxSample(0) + 1); } } /** * Returns the histogram used in this operation. * @return histogram object, newly-created or reused one * @see #setHistogram */ public Histogram1D getHistogram() { return hist; } public void process() throws MissingParameterException, WrongParameterException { if (image == null) { throw new MissingParameterException("Image parameter missing."); } createHistogramIfNecessary(); if (hist.getMaxValue() < image.getMaxSample(channelIndex)) { throw new WrongParameterException("Histogram does not have enough entries."); } hist.clear(); final int WIDTH = image.getWidth(); final int HEIGHT = image.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { hist.increaseEntry(image.getSample(channelIndex, x, y)); } setProgress(y, HEIGHT); } } /** * Sets a histogram object to be used for this operation. * Within {@link #process} it will be checked if this histogram is large enough * for the image. * @see #getHistogram */ public void setHistogram(Histogram1D histogram) { hist = histogram; } /** * Set the image for which the histogram is to be initialized. * The first channel (index 0) is used by default. * @param newImage image object to be used * @see #setImage(IntegerImage, int) */ public void setImage(IntegerImage newImage) { setImage(newImage, 0); } /** * Set the image and the channel index for which the histogram is to be initialized. * @param newImage image object to be used * @param imageChannelIndex must not be negative and must be smaller than newImage.getNumChannels() * @see #setImage(IntegerImage) */ public void setImage(IntegerImage newImage, int imageChannelIndex) { if (newImage == null) { throw new IllegalArgumentException("Image argument must be non-null."); } if (imageChannelIndex < 0 || imageChannelIndex >= newImage.getNumChannels()) { throw new IllegalArgumentException("Invalid channel for given image."); } image = newImage; channelIndex = imageChannelIndex; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/YCbCrIndex.java0000664000000000000000000000154607741250131023747 0ustar /* * YCbCrIndex * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color; /** * This interface simply provides three integer constants as index * values for the three channels of an YCbCr image: gray, * blue chrominance and red chrominance. * The three values are guaranteed to lie in the interval 0 to 2. * Furthermore, all three values are different from each other, so * that the complete interval from 0 to 2 is used. * @see net.sourceforge.jiu.data.RGBIndex * @author Marco Schmidt */ public interface YCbCrIndex { /** * Index value for the luminance (gray) component. */ int INDEX_Y = 0; /** * Index value for the blue chrominance component. */ int INDEX_CB = 1; /** * Index value for the red chrominance component. */ int INDEX_CR = 2; } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/package.html0000664000000000000000000000037007741250131023425 0ustar

Contains color-related operations that did not fit into one of the subpackages. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/0000775000000000000000000000000010546757500022066 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/OnDemandHistogram3D.java0000664000000000000000000001060110546764333026463 0ustar /* * OnDemandHistogram3D * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.color.data; import java.util.Hashtable; import net.sourceforge.jiu.color.data.Histogram3D; /** * Internal data class for the on demand histogram. Stores * one triplet of int values plus an int counter. * Implements {@link java.lang.Object#hashCode()} and * {@link java.lang.Object#equals(java.lang.Object)} to * be used with a hash table. * * @author Marco Schmidt */ final class Histogram3DNode { private final int sample1; private final int sample2; private final int sample3; private int counter; public Histogram3DNode(int s1, int s2, int s3) { sample1 = s1; sample2 = s2; sample3 = s3; } public boolean equals(Object obj) { Histogram3DNode node = (Histogram3DNode)obj; return sample1 == node.sample1 && sample2 == node.sample2 && sample3 == node.sample3; } public int getCounter() { return counter; } public int getSample1() { return sample1; } public int getSample2() { return sample2; } public int getSample3() { return sample3; } public int hashCode() { return sample1 + sample2 + sample3; } public void increase() { setCounter(getCounter() + 1); } public void setCounter(int newValue) { counter = newValue; } } /** * A data class for a three-dimensional histogram, creating counters on demand only, * not allocating counters for all possible entries at the beginning. * The creation on demand happens to save space. *

* Note: * Rewrote from scratch for version 0.15.0 to use hash tables * instead of int arrays. New version creates and * throws away a lot of objects, which had been a problem * with early JVMs but should be OK these days. * * @author Marco Schmidt */ public class OnDemandHistogram3D implements Histogram3D { private Hashtable hash; private int numUniqueValues; private final int maxValue1; private final int maxValue2; private final int maxValue3; /** * Creates a new histogram, internally creates the hash table * for triplet values. */ public OnDemandHistogram3D(int max1, int max2, int max3) { if (max1 < 1 || max2 < 1 || max3 < 1) { throw new IllegalArgumentException("All max arguments must be 1 or larger."); } maxValue1 = max1; maxValue2 = max2; maxValue3 = max3; clear(); } public void clear() { if (hash == null) { hash = new Hashtable(); } else { hash.clear(); } numUniqueValues = 0; } private Histogram3DNode createNode(int v1, int v2, int v3) { if (v1 >= 0 && v2 >= 0 && v3 >= 0 && v1 <= maxValue1 && v2 <= maxValue2 && v3 <= maxValue3) { return new Histogram3DNode(v1, v2, v3); } else { throw new IllegalArgumentException("At least one of the arguments was not in its valid 0..max range."); } } public int getEntry(int index1, int index2, int index3) { Histogram3DNode searchNode = createNode(index1, index2, index3); Histogram3DNode counter = (Histogram3DNode)hash.get(searchNode); if (counter == null) { return 0; } else { return counter.getCounter(); } } public int getMaxValue(int index) throws IllegalArgumentException { switch(index) { case(0): return maxValue1; case(1): return maxValue2; case(2): return maxValue3; default: throw new IllegalArgumentException("Not a valid index, must be from 0 to 2: " + index); } } public int getNumUsedEntries() { return numUniqueValues; } public void increaseEntry(int index1, int index2, int index3) { Histogram3DNode searchNode = createNode(index1, index2, index3); Histogram3DNode counter = (Histogram3DNode)hash.get(searchNode); if (counter == null) { searchNode.setCounter(1); hash.put(searchNode, searchNode); numUniqueValues++; } else { counter.increase(); } } public void setEntry(int index1, int index2, int index3, int newValue) { Histogram3DNode searchNode = createNode(index1, index2, index3); Histogram3DNode counter = (Histogram3DNode)hash.get(searchNode); if (counter == null) { searchNode.setCounter(newValue); hash.put(searchNode, searchNode); numUniqueValues++; } else { counter.setCounter(newValue); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/NaiveHistogram3D.java0000664000000000000000000001327210541050241026024 0ustar /* * NaiveHistogram3D * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.color.data; import net.sourceforge.jiu.color.data.Histogram3D; /** * A class for a three-dimensional histogram that allocates one int value * per counter at construction time. * This means that a histogram with 8 bits for each channel will have * 28 + 8 + 8 = 224 = 16,777,216 int values, * making it 64 MB large. * * @author Marco Schmidt */ public class NaiveHistogram3D implements Histogram3D { private int[][][] data; private int[] values; /** * Creates a histogram */ public NaiveHistogram3D(int numValuesLevel1, int numValuesLevel2, int numValuesLevel3) throws IllegalArgumentException, OutOfMemoryError { if (numValuesLevel1 < 1) { throw new IllegalArgumentException("The number of values for " + "level 1 must be at least 1; got " + numValuesLevel1); } if (numValuesLevel2 < 1) { throw new IllegalArgumentException("The number of values for " + "level 2 must be at least 1; got " + numValuesLevel2); } if (numValuesLevel3 < 1) { throw new IllegalArgumentException("The number of values for " + "level 3 must be at least 1; got " + numValuesLevel3); } values = new int[3]; values[0] = numValuesLevel1; values[1] = numValuesLevel2; values[2] = numValuesLevel3; data = new int[values[0]][][]; for (int i1 = 0; i1 < values[0]; i1++) { data[i1] = new int[values[1]][]; for (int i2 = 0; i2 < values[1]; i1++) { data[i1][i2] = new int[values[2]]; } } clear(); } /** * Creates a histogram with the same number of values for all three dimensions. * Calls {@link NaiveHistogram3D (int, int, int)}, repeating numValues * three times. * @param numValues the number of levels for all three dimensions */ public NaiveHistogram3D(int numValues) throws IllegalArgumentException, OutOfMemoryError { this(numValues, numValues, numValues); } /** * Sets all counters to zero. */ public void clear() { for (int i1 = 0; i1 < values[0]; i1++) for (int i2 = 0; i2 < values[1]; i2++) for (int i3 = 0; i3 < values[2]; i3++) data[i1][i2][i3] = 0; } /** * Returns the counter value of (index1, index2, index3). * @param index1 first of the three values forming the threedimensional index * @param index2 second of the three values forming the threedimensional index * @param index3 three of the three values forming the threedimensional index * @return the counter value of the desired index * @throws IllegalArgumentException could be (read: need not necessarily) be * thrown if the index formed by the arguments is invalid */ public int getEntry(int index1, int index2, int index3) throws IllegalArgumentException { try { return data[index1][index2][index3]; } catch (ArrayIndexOutOfBoundsException aioobe) { throw new IllegalArgumentException("Not a valid index (" + index1 + ", " + index2 + ", " + index3 + ")"); } } public int getMaxValue(int index) throws IllegalArgumentException { if (index >= 0 && index <= 2) { return values[index] - 1; } else { throw new IllegalArgumentException("The index argument must be " + "from 0 to 2; got " + index); } } /** * Returns the number of used entries (those entries with * a counter value larger than zero). * @return number of non-zero counter values */ public int getNumUsedEntries() { int result = 0; for (int i1 = 0; i1 < values[0]; i1++) for (int i2 = 0; i2 < values[1]; i2++) for (int i3 = 0; i3 < values[2]; i3++) if (data[i1][i2][i3] > 0) result++; return result; } /** * Increases the counter value of (index1, index2, index3) by one. * This method can easily be implemented by the one-liner * setEntry(index1, index2, index3, getEntry(index1, index2, index3) + 1); * However, this method is expected to be faster in some contexts. * * @param index1 first of the three values forming the threedimensional index * @param index2 second of the three values forming the threedimensional index * @param index3 three of the three values forming the threedimensional index * @throws IllegalArgumentException could be (read: need not necessarily) be * thrown if the index formed by the arguments is invalid */ public void increaseEntry(int index1, int index2, int index3) throws IllegalArgumentException { try { data[index1][index2][index3]++; } catch (ArrayIndexOutOfBoundsException aioobe) { throw new IllegalArgumentException("Not a valid index (" + index1 + ", " + index2 + ", " + index3 + ")"); } } /** * Sets the counter value of (index1, index2, index3) to newValue. * * @param index1 first of the three values forming the threedimensional index * @param index2 second of the three values forming the threedimensional index * @param index3 three of the three values forming the threedimensional index * @param newValue the counter value that is assigned to the argument index * @throws IllegalArgumentException could be (read: need not necessarily) be * thrown if the index formed by the first three arguments is invalid */ public void setEntry(int index1, int index2, int index3, int newValue) throws IllegalArgumentException { try { data[index1][index2][index3] = newValue; } catch (ArrayIndexOutOfBoundsException aioobe) { throw new IllegalArgumentException("Not a valid index (" + index1 + ", " + index2 + ", " + index3 + ")"); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/CoOccurrenceMatrix.java0000664000000000000000000000323207741250132026460 0ustar /* * CoOccurrenceMatrix * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.data; /** * An interface for co-occurrence matrices. * An implementing class stores int counter values for pairs of pixels. * These counters represent the number of times two pixels are direct * neighbors in an image. * @author Marco Schmidt */ public interface CoOccurrenceMatrix { /** * Sets all counters to zero. */ void clear(); /** * Returns the dimension of this matrix. * This is the number of rows and columns. * @return matrix dimension (larger than zero) */ int getDimension(); /** * Returns the matrix value at a given position. * @param i column index, from 0 to {@link #getDimension} - 1 * @param j row index, from 0 to {@link #getDimension} - 1 * @throws IllegalArgumentException for invalid index pairs (i, j) */ int getValue(int i, int j); /** * Increases the counter for pair (i, j) by one. * This method can be implemented by the call * setValue(i, j, getValue(i, j) + 1);. * @param i column index, from 0 to {@link #getDimension} - 1 * @param j row index, from 0 to {@link #getDimension} - 1 * @throws IllegalArgumentException for invalid index pairs (i, j) */ void incValue(int i, int j); /** * Sets the counter for pair (i, j) to a new value. * @param i column index, from 0 to {@link #getDimension} - 1 * @param j row index, from 0 to {@link #getDimension} - 1 * @throws IllegalArgumentException for invalid index pairs (i, j) */ void setValue(int i, int j, int newValue); } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/MemoryCoOccurrenceFrequencyMatrix.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/MemoryCoOccurrenceFrequencyMatri0000664000000000000000000000576207741250132030435 0ustar /* * MemoryCoOccurrenceFrequencyMatrix * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.data; import net.sourceforge.jiu.color.data.BaseCoOccurrenceFrequencyMatrix; /** * Implements the {@link CoOccurrenceFrequencyMatrix} interface by using a large array * of values in memory. * @author Marco Schmidt */ public class MemoryCoOccurrenceFrequencyMatrix extends BaseCoOccurrenceFrequencyMatrix { /** will be initialized in constructor and never changed */ private final int dimension; /** total number of values, equals dimension * dimension and data.length */ private int numValues; /** co occurrence frequency values */ private double[] data; /** * Creates a co-occurrence frequency matrix of given dimension; * allocates dimension times dimension double values for * internal array; * does not call clear() to set everything to zero, must be * done by user (or automatically in init). * Dimension should be number of colors in palette. * @throws IllegalArgumentException if dimension is smaller than 1 */ public MemoryCoOccurrenceFrequencyMatrix(int dimension) { if (dimension < 1) { throw new IllegalArgumentException("Dimension of co-occurrence frequency matrix must be >= 1."); } this.dimension = dimension; this.numValues = dimension * dimension; data = new double[numValues]; } /** * Sets all values of this matrix to zero. */ public void clear() { if (data == null) { return; } for (int i = 0; i < numValues; i++) { data[i] = 0.0; } } public int getDimension() { return dimension; } /** * Returns the value of this matrix at row i, column i. * Argument is zero-based, so make sure that * 0 <= i < getDimension(). * Other values will raise an IllegalArgumentException. * Simply calls getValue(i, i). */ public double getValue(int i) throws IllegalArgumentException { return getValue(i, i); } /** * Returns the value of this matrix at row j, column i. * Both arguments are zero-based, so make sure that * 0 <= i, j < getDimension(). * Other values will raise an IllegalArgumentException. */ public double getValue(int i, int j) throws IllegalArgumentException { if (i < 0 || i >= dimension || j < 0 || j >= dimension) { throw new IllegalArgumentException( "i/j arguments out of bounds: " + i + "/" + j); } return data[j * dimension + i]; } /** * Sets value at row j, column i to newValue. * Both arguments are zero-based, so make sure that * 0 <= i, j < getDimension(). * Other values will raise an IllegalArgumentException. */ public void setValue(int i, int j, double newValue) throws IllegalArgumentException { if (i < 0 || i >= dimension || j < 0 || j >= dimension) { throw new IllegalArgumentException("i/j coordinate out of bounds: " + i + "/" + j); } data[j * dimension + i] = newValue; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/MemoryCoOccurrenceMatrix.java0000664000000000000000000000524210611674432027657 0ustar /* * MemoryCoOccurrenceMatrix * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.data; import net.sourceforge.jiu.color.data.CoOccurrenceMatrix; /** * This class stores a co-occurrence matrix, a two-dimensional array of int counters. * The dimension is given to the constructor which allocates a corresponding array. *

Caveat

* Does not (realistically) work with 16 bit channels because it allocates * dimension times dimension int values, resulting in an attempt to allocate 16 GB * with 16 bit images (dimension=65,536). TODO: Implement more sophisticated class, * creating counters on-demand. * @author Marco Schmidt */ public class MemoryCoOccurrenceMatrix implements CoOccurrenceMatrix { private final int dimension; private final int dimSquare; private final int[] data; /** * Creates a new matrix that stores dimension times dimension int values in memory. * Given that array index values are of type int, this limits dimension to about 46000 * (sqrt(Integer.MAX_VALUE). * In practice, dimension leads to dimension times dimenstion times 4 bytes being * allocated, so that memory available to the JVM may become a decisive factor. * @param dimension the matrix' dimension, which is both the number of rows and columns */ public MemoryCoOccurrenceMatrix(int dimension) { if (dimension < 1) { throw new IllegalArgumentException("Dimension of co-occurrence matrix must be >= 1."); } this.dimension = dimension; long longDimSquare = (long)dimension * (long)dimension; if (longDimSquare > Integer.MAX_VALUE) { throw new IllegalArgumentException("Dimension " + dimension + " leads to an array exceeding the maximum size of 2^31 entries."); } dimSquare = dimension * dimension; data = new int[dimSquare]; } public void clear() { for (int i = 0; i < dimSquare; i++) { data[i] = 0; } } public int getDimension() { return dimension; } public int getValue(int i, int j) { if (i < 0 || i >= dimension || j < 0 || j >= dimension) { throw new IllegalArgumentException("co-occ matrix i/j arguments out of bounds: " + i + "/" + j); } return data[j * dimension + i]; } public void incValue(int i, int j) throws IllegalArgumentException { data[j * dimension + i]++; } public void setValue(int i, int j, int newValue) { if (i < 0 || i >= dimension || j < 0 || j >= dimension) { throw new IllegalArgumentException("co-occ matrix setValue, i/j coordinate out of bounds: " + i + "/" + j); } data[j * dimension + i] = newValue; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/package.html0000664000000000000000000000323407741250132024341 0ustar

Provides classes to store images and data directly related to them.

Package Specification

The base interface for image data in JIU is {@link net.sourceforge.jiu.data.PixelImage}. The concept of a pixel image includes the following properties:

The interface {@link net.sourceforge.jiu.data.IntegerImage} extends the {@link net.sourceforge.jiu.data.PixelImage} interface. All sample values belonging to an object of a class implementing IntegerImage are supposed to be integer values that can be stored in an int value (a signed 32 bit value).

Related Documentation

java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/ArrayHistogram1D.java0000664000000000000000000000426210324333124026040 0ustar /* * ArrayHistogram1D * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.color.data; import net.sourceforge.jiu.color.data.Histogram1D; /** * A one-dimensional histogram data class that stores its counters in memory. * Counters are stored in an int array of length * {@link #getMaxValue()} + 1 so that k * values will require k * 4 bytes. * @author Marco Schmidt */ public class ArrayHistogram1D implements Histogram1D { private int[] data; /** * Creates a histogram with the argument's number of values, from * 0 to numValues - 1. * * @param numValues the number of counters in the histogram; must be one or larger * @throws IllegalArgumentException if the argument is smaller than one */ public ArrayHistogram1D(int numValues) { if (numValues < 1) { throw new IllegalArgumentException("Must have at least one entry; numValues=" + numValues); } data = new int[numValues]; } public void clear() { // OPTIMIZE // we could use java.util.Arrays.fill, but that would require Java 1.2+: // Arrays.fill(data, 0); for (int i = 0; i < data.length; i++) { data[i] = 0; } } public int getEntry(int index) { try { return data[index]; } catch (ArrayIndexOutOfBoundsException aioobe) { throw new IllegalArgumentException("Not a valid index: " + index); } } public int getMaxValue() { return data.length - 1; } public int getNumUsedEntries() { int result = 0; for (int i = 0; i < data.length; i++) { if (data[i] > 0) { result++; } } return result; } public void increaseEntry(int index) { try { data[index]++; } catch (ArrayIndexOutOfBoundsException aioobe) { throw new IllegalArgumentException("Not a valid index: " + index); } } public void setEntry(int index, int newValue) { try { data[index] = newValue; } catch (ArrayIndexOutOfBoundsException aioobe) { throw new IllegalArgumentException("Not a valid index: " + index); } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/BaseCoOccurrenceFrequencyMatrix.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/BaseCoOccurrenceFrequencyMatrix.0000664000000000000000000001170510377273422030305 0ustar /* * BaseCoOccurrenceFrequencyMatrix * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.data; /** * This abstract class encapsulates all data of a co-occurrence * frequency matrix except for the frequency values. * The method computeStatistics is implemented. * Any class extending this class only has to * deal with storing the frequency values ({@link MemoryCoOccurrenceFrequencyMatrix} * does this by using a one-dimensional array internally). * @author Marco Schmidt */ public abstract class BaseCoOccurrenceFrequencyMatrix implements CoOccurrenceFrequencyMatrix { /** co-occurrence frequency mean $\mu_{C(j)}$ */ private double[] cofMean; /** co-occurrence frequency standard deviation $\sigma_{C(j)}$ */ private double[] cofStddev; /** self co-occurrence frequency mean $\mu_S$ */ private double scofMean; /** self co-occurrence frequency standard deviation $\sigma_S$ */ private double scofStddev; /** equals scofMean + scofStddev */ private double scofSum; private void computeCoOccurrenceFrequencyMeanValues() { cofMean = new double[getDimension()]; for (int j = 0; j < getDimension(); j++) { double result = 0.0; for (int i = 0; i < getDimension(); i++) { result += getValue(i, j); } cofMean[j] = result / ((double)getDimension()); } //System.out.println("DEBUG: done computing cofm mean values"); } private void computeCoOccurrenceFrequencyStandardDeviationValues() { cofStddev = new double[getDimension()]; for (int j = 0; j < getDimension(); j++) { double result = 0.0; for (int i = 0; i < getDimension(); i++) { double value = getValue(i, j) - cofMean[j]; result += (value * value); } cofStddev[j] = Math.sqrt(result); } //System.out.println("DEBUG: done computing cofm stddev values"); } private void computeSelfCoOccurrenceFrequencyMeanValue() { double sum = 0.0; for (int i = 0; i < getDimension(); i++) { sum += getValue(i, i); } scofMean = sum / (getDimension()); //System.out.println("DEBUG: scof mean=" + scofMean); } private void computeSelfCoOccurrenceFrequencyStandardDeviationValue() { double result = 0.0; for (int i = 0; i < getDimension(); i++) { double value = getValue(i, i) - getScofMean(); result += (value * value); } scofStddev = Math.sqrt(result); //System.out.println("DEBUG: scof stddev=" + scofStddev); } /** * Assumes that the co-occurrence frequency values have been initialized. * Computes mean and standard deviation for co-occurrence and self co-occurrence * frequency values. */ public void computeStatistics() { // we must keep this order because stddev needs mean! computeSelfCoOccurrenceFrequencyMeanValue(); computeSelfCoOccurrenceFrequencyStandardDeviationValue(); scofSum = getScofMean() + getScofStddev(); //System.out.println("DEBUG: scof sum=" + scofSum); computeCoOccurrenceFrequencyMeanValues(); computeCoOccurrenceFrequencyStandardDeviationValues(); } /** * Prints co-occurrence frequency values to standard output, one line * per matrix row j. * Calls getValue(i, j) with each column i for each row j. */ /*private void dump() { for (int j = 0; j < getDimension(); j++) { for (int i = 0; i < getDimension(); i++) { System.out.print(getValue(i, j) + " "); } System.out.println(""); } }*/ /** * Prints self co-occurrence frequency values to standard output, eight * values per row. * Calls getValue(i, i) with each value i from 0 to getDimension() - 1. */ /* private void dumpScofValues() { for (int j = 0; j < getDimension(); j++) { System.out.print(getValue(j) + " "); if (j % 8 == 0 && j > 0) { System.out.println(""); } } System.out.println(""); }*/ /** * Returns the mean of the co-occurrence frequency values. */ public double getMean(int index) { return cofMean[index]; } public double getStddev(int index) { return cofStddev[index]; } /** * Returns the mean of all self co-occurrence frequency values. * This value is called $\mu_S$ in Shufelt's paper. * This value is determined once within computeStatistics(). */ public double getScofMean() { return scofMean; } /** * Returns the standard deviation of all self co-occurrence frequency * values. * This value is called $\sigma_S$ in Shufelt's paper. * This value is determined once within a call to computeStatistics(). */ public double getScofStddev() { return scofStddev; } /** * Return the sum of mean and standard deviation of the self * co-occurrence frequency values. * Assumes that {@link #computeStatistics} has been called already. * @return sum of mean and standard deviation of the self co-occurrence * frequency values */ public double getScofSum() { return scofSum; } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/CoOccurrenceFrequencyMatrix.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/CoOccurrenceFrequencyMatrix.java0000664000000000000000000000424207741250132030344 0ustar /* * CoOccurrenceFrequencyMatrix * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.data; /** * An interface for a co-occurrence frequency matrix. * Also provides access to some statistical data. * This class is not a pure data type for it also demands a method {@link #computeStatistics} * which takes the matrix coefficients and computes mean, standard deviation and * other properties from it. * @author Marco Schmidt */ public interface CoOccurrenceFrequencyMatrix { /** * Sets all frequency values in this matrix to 0.0. */ void clear(); /** * Computes mean, standard deviation and the sum of those two * so that these values can be queried by the appropriate * get methods. */ void computeStatistics(); /** * Returns the sum of mean and standard deviation for all pairs (index, x), with x running from 0 to getDimension() - 1. * The result is equal to {@link #getMean} + {@link #getStddev} */ double getScofMean(); /** * Returns the mean for all pairs (index, i), with i running from 0 to {@link #getDimension()} - 1. */ double getMean(int index); /** * Returns the standard deviation of the values getValue(index, i) * with i running from 0 to {@link #getDimension()} - 1. * @param index first argument to all calls of getValue used to determine the standard deviation */ double getStddev(int index); /** * Returns the standard deviation for all pairs (i, i), with i running from 0 to getDimension() - 1. * @return standard deviation for pairs */ double getScofStddev(); double getScofSum(); /** * Returns the dimension of this matrix. */ int getDimension(); /** * Returns the value for the self co-occurrence frequency of i (i being from * 0 to {@link #getDimension()} - 1). * The result is the same as a call to getValue(i, i). * @param i index into the matrix, must be larger than or equal to 0 and smaller than {@link #getDimension()} */ double getValue(int i); double getValue(int i, int j); void setValue(int i, int j, double newValue); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/Histogram1D.java0000664000000000000000000000277010541050173025044 0ustar /* * Histogram1D * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color.data; /** * An interface for a one-dimensional histogram. * * @author Marco Schmidt * @see Histogram3D */ public interface Histogram1D { /** * Sets all counters to zero. */ void clear(); /** * Returns the counter value for the given index. * @param index the zero-based index of the desired counter value * @return the counter value * @throws IllegalArgumentException if the argument is not a valid index */ int getEntry(int index); /** * Returns the maximum allowed index. * The minimum is always 0. * @return the maximum index value */ int getMaxValue(); /** * Returns the number of used entries (those entries with * a counter value larger than zero). * @return number of non-zero counter values */ int getNumUsedEntries(); /** * Increases the counter value of the given index by one. * Same semantics as * setEntry(index, getEntry(index) + 1); * @param index index into the histogram * @throws IllegalArgumentException if the argument index is invalid */ void increaseEntry(int index); /** * Sets one counter to a new value. * @param index index of the counter to be changed * @param newValue new value for that counter * @throws IllegalArgumentException if the index is invalid */ void setEntry(int index, int newValue); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/data/Histogram3D.java0000664000000000000000000000542710546764741025072 0ustar /* * Histogram3D * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.color.data; /** * An interface for classes that store three-dimensional histograms. * Histograms count the occurrence of values, so a three-dimensional * histogram has counters for three-dimensional values. * The 3D histograms are used (as an example) to count the occurrence of * each color in an RGB image. * * @author Marco Schmidt * @see Histogram1D */ public interface Histogram3D { /** * Sets all counters to zero. */ void clear(); /** * Returns the counter value of (index1, index2, index3). * @param index1 first of the three values forming the threedimensional index * @param index2 second of the three values forming the threedimensional index * @param index3 three of the three values forming the threedimensional index * @return the counter value of the desired index * @throws IllegalArgumentException if the index formed by the arguments is invalid */ int getEntry(int index1, int index2, int index3); /** * Returns the maximum index value for one of the three indexes. * @throws IllegalArgumentException if the index formed by the arguments is invalid */ int getMaxValue(int index); /** * Returns the number of used entries (those entries with * a counter value larger than zero). * @return number of non-zero counter values */ int getNumUsedEntries(); /** * Increases the counter value of (index1, index2, index3) by one. * This method can be implemented by the one-liner * setEntry(index1, index2, index3, getEntry(index1, index2, index3) + 1); * However, implementations of this method may take advantage of * implementation details to provide a more efficient approach. * @param index1 first of the three values forming the threedimensional index * @param index2 second of the three values forming the threedimensional index * @param index3 three of the three values forming the threedimensional index * @throws IllegalArgumentException if the index formed by the arguments is invalid */ void increaseEntry(int index1, int index2, int index3); /** * Sets the counter value of (index1, index2, index3) to newValue. * * @param index1 first of the three values forming the threedimensional index * @param index2 second of the three values forming the threedimensional index * @param index3 three of the three values forming the threedimensional index * @param newValue the counter value that is assigned to the argument index * @throws IllegalArgumentException if the index formed by the first three arguments is invalid */ void setEntry(int index1, int index2, int index3, int newValue); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/color/WebsafePaletteCreator.java0000664000000000000000000000326707741250131026232 0ustar /* * WebsafePaletteCreator * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.color; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.RGBIndex; /** * This class creates {@link net.sourceforge.jiu.data.Palette} objects that * contain the so-called websafe palette. * This palette has 216 entries which are uniformly spread over the RGB color * cube. * Each component (red / green / blue) takes each of the six values 0, 51, 101, * 153, 204 and 255 (note that the difference is almost equal between two consecutive * values, between 50 and 52). * Therefore, the palette will have 63 = 6 * 6 * 6 = 216 entries. *

* This palette was designed with computer systems in mind that can only display * 256 colors at a time. * With the 216 colors that are uniformly spread over RGB color space, there is * at least a somewhat similar match for each possible input color. * * @author Marco Schmidt * @since 0.5.0 */ public class WebsafePaletteCreator implements RGBIndex { private static final int[] SAMPLES = {0x00, 0x33, 0x66, 0x99, 0xcc, 0xff}; private WebsafePaletteCreator() { // private so that this class cannot be instantiated } /** * Creates a new palette with the 216 websafe colors. * @return new palette object */ public static Palette create() { Palette result = new Palette(216, 255); int index = 0; for (int r = 0; r < 6; r++) { for (int g = 0; g < 6; g++) { for (int b = 0; b < 6; b++) { result.put(index++, SAMPLES[r], SAMPLES[g], SAMPLES[b]); } } } return result; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/0000775000000000000000000000000010546532075021012 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/ArrayRotation.java0000664000000000000000000003050710572433350024453 0ustar /* * ArrayRotation * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.util; /** * Provides static methods to rotate (in steps of 90 degrees), flip and mirror array elements. * The image data is expected to be available as an array of integer values, being stored as rows top-to-bottom. * Within each row, the data is laid out from left to right. * This class may also been useful for transposing matrices. *

* The rotation by 90 and 270 degrees in-place (i.e., without using a * second array to copy to) is based on ideas and code developed * by others. * See Rotation of arrays * by Thomas W. Christopher. *

* I also got very useful advice from Hans-Bernhard Broeker and others in * comp.graphics.algorithms. * There is a thread titled In-place rotation of pixel images starting Oct 11, 2000. *

* Note: This class should be adjusted if Java ever supports genericity. * Then rotation functionality could be provided for all kinds of arrays. * @author Hans-Bernhard Broeker * @author Thomas W. Christopher * @author Marco Schmidt */ public class ArrayRotation { private ArrayRotation() { } /** * This method checks several properties of the arguments. * If any of the properties is not fulfilled, an explaining * {@link java.lang.IllegalArgumentException} is thrown. * Otherwise, nothing happens. * This method is supposed to be called at the beginning of several * other methods in this class. * Properties checked: *

*/ public static void checkPixelArray(int[] pixels, int width, int height) { if (pixels == null) { throw new IllegalArgumentException("Error -- the pixel array must be initialized."); } if (width < 1 || height < 1) { throw new IllegalArgumentException("Error -- width and height must be >= 1 (width=" + width + ", height=" + height + ")."); } if (pixels.length < width * height) { throw new IllegalArgumentException("Error -- pixel array must have at least width * height pixels, has only " + pixels.length + " pixels."); } } /** * Flips the argument image, i.e., the top line becomes the bottom line * and vice versa, etc. * This method first checks the validity of the arguments that define the image * by a call to {@link checkPixelArray}. * Then the image data is flipped in place, no additional memory is required. * Note that after applying this operation twice you will get the original * image back. * * @param pixels the array of pixels that form the image to be flipped * @param width the horizontal resolution of the image; must be larger than 0 * @param height the vertical resolution of the image; must be larger than 0 * @exception IllegalArgumentException if the arguments are invalid */ private static final void flipInPlace(int[] pixels, int width, int height) { checkPixelArray(pixels, width, height); int y1 = 0; int y2 = height - 1; while (y1 < y2) { int offset1 = y1 * width; int offset2 = y2 * width; for (int x = 0; x < width; x++) { int temp = pixels[offset1]; pixels[offset1++] = pixels[offset2]; pixels[offset2++] = temp; } } } private static final int[] flip(int[] pixels, int width, int height) { checkPixelArray(pixels, width, height); int[] result = new int[width * height]; for (int y1 = 0, y2 = height - 1; y1 < height; y1++, y2--) { int offset1 = y1 * width; int offset2 = y2 * width; for (int x = 0; x < width; x++) { result[offset2++] = pixels[offset1++]; } } return result; } /** * Flips the image given by the arguments. * The inPlace argument determines if the pixels array is modified or not. * If inPlace is true, no additional array is allocated. * Otherwise, an array of width times height items is allocated and the * flipped image will be stored in this array. * @param inPlace if true all work is done on the pixels array; * otherwise, a second array is allocated and the pixels array * remains unmodified * @param pixels the array of pixels that form the image to be flipped * @param width the horizontal resolution of the image; must be larger than 0 * @param height the vertical resolution of the image; must be larger than 0 * @return the flipped image as int array; equals pixels if * inPlace is true * @exception IllegalArgumentException if the pixel resolution * is invalid or the pixels array is not initialized or its length smaller * than width times height */ public static final int[] flip(boolean inPlace, int[] pixels, int width, int height) { if (inPlace) { flipInPlace(pixels, width, height); return pixels; } else { return flip(pixels, width, height); } } /** * Mirrors the image given by the arguments. * For each row, pixels are swapped, leftmost and rightmost, * second-leftmost and second-rightmost, and so on. * The inPlace argument determines if the pixels array is modified or not. * If inPlace is true, no additional array is used. * Otherwise, an array of width times height items is allocated and the * mirrored image will be stored in this array. * @param inPlace if true all work is done on the pixels array; * otherwise, a second array is allocated and the pixels array * remains unmodified * @param pixels the array of pixels that form the image to be flipped * @param width the horizontal resolution of the image; must be larger than 0 * @param height the vertical resolution of the image; must be larger than 0 * @return the flipped image as int array; equals pixels if * inPlace is true * @exception IllegalArgumentException if the pixel resolution * is invalid or the pixels array is not initialized or its length smaller * than width times height */ /*public static int[] mirror(boolean inPlace, int[] pixels, int width, int height) { if (inPlace) { //flipInPlace(pixels, width, height); return pixels; } else { return pixels;//flip(pixels, width, height); } }*/ /** * Rotates the argument image by 180 degrees. * The resulting image will have exactly the same pixel resolution. * Note that this operation is the same as two consecutive 90 degree * rotations in the same direction. * Another way of implementing a 180 degree rotation is first flipping * and then mirroring the original image (or vice versa). *

* If inPlace is true, the rotation is done on the * argument pixels array. * Otherwise a new array of sufficient length is allocated and the * rotated image will be stored in this new array, not modifying the * content of the pixels array. *

* @param inPlace determines whether the rotated image is written to the argument array * @param pixels the array of pixels that form the image to be rotated * @param width the horizontal resolution of the image; must be larger than 0 * @param height the vertical resolution of the image; must be larger than 0 * @return the flipped image as int array; equals pixels if * inPlace is true * @exception IllegalArgumentException if the pixel resolution * is invalid or the pixels array is not initialized or its length smaller * than width times height */ public static int[] rotate180(boolean inPlace, int[] pixels, int width, int height) { if (inPlace) { rotateInPlace180(pixels, width, height); return pixels; } else { return pixels;//rotateToCopy180(pixels, width, height); } } /*private static int[] rotate180(int[] pixels, int width, int height) { checkPixelArray(pixels, width, height); int numPixels = width * height; int[] result = new int[numPixels]; int x1 = 0; int x2 = numPixels - 1; while (x1 < x2) { int temp = pixels[x1]; pixels[x1++] = pixels[x2]; pixels[x2--] = temp; } return result; }*/ private static void rotateInPlace180(int[] pixels, int width, int height) { checkPixelArray(pixels, width, height); int x1 = 0; int x2 = width * height - 1; while (x1 < x2) { int temp = pixels[x1]; pixels[x1++] = pixels[x2]; pixels[x2--] = temp; } } /*private static void rotateToArray90Left(int[] src, int[] dest, int width, int height) { checkPixelArray(src, width, height); checkPixelArray(dest, width, height); if (src == dest) { throw new IllegalArgumentException("rotate90Left assumes that the argument arrays are not the same."); } int x_ = -1; int y_ = -1; try { int offset = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { x_ = x; y_ = y; dest[(width - x - 1) * height + y] = src[offset++]; } } } catch (ArrayIndexOutOfBoundsException ae) { System.out.println("bounds; x=" + x_ + " y=" + y_); } }*/ /*private static int pred(int k, int width, int height) { return (k % height) * width + k / height; } private static void rotateInPlace90Left(int[] pixels, int width, int height) { int LxM = height * width; int i, j, k, stillToMove; for (i = 0, stillToMove = LxM; stillToMove > 0; i++) { for (j = pred(i, width, height); j > i; j = pred(j, width, height)) ; if (j < i) { continue; } for (k = i, j = pred(i, width, height); j != i; k = j, j = pred(j, width, height)) { //exchange(k,j); int temp = pixels[k]; pixels[k] = pixels[j]; pixels[j] = temp; --stillToMove; } --stillToMove; } }*/ public static void rotate90Left(int width, int height, byte[] src, int srcOffset, byte[] dest, int destOffset) { int offset = srcOffset; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { dest[destOffset + (width - x - 1) * height + y] = src[offset++]; } } } public static void rotate90Right(int width, int height, byte[] src, int srcOffset, byte[] dest, int destOffset) { int offset = srcOffset; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { dest[destOffset + x * height + (height - 1 - y)] = src[offset++]; } } } public static void rotate180(int width, int height, byte[] src, int srcOffset, byte[] dest, int destOffset) { int n = width * height; destOffset = destOffset + n - 1; while (n-- > 0) { dest[destOffset--] = src[srcOffset++]; } } /* public static int[] rotate90Left(boolean inPlace, int[] pixels, int width, int height) throws IllegalArgumentException { if (inPlace) { rotateInPlace90Left(pixels, width, height); return pixels; } else { int[] dest = new int[width * height]; rotateToArray90Left(pixels, dest, width, height); return dest; } }*/ private static void rotateInPlace90Right(int[] pixels, int width, int height) { throw new IllegalArgumentException("This method not implemented yet."); } private static int[] rotate90Right(int[] pixels, int width, int height) { int[] result = new int[width * height]; int offset = 0; for (int y = 0; y < height; y++) { int srcOffset = height - 1 - y; for (int x = 0; x < width; x++) { result[srcOffset] = pixels[offset++]; srcOffset += height; } } return result; } public static int[] rotate90Right(boolean inPlace, int[] pixels, int width, int height) { if (inPlace) { rotateInPlace90Right(pixels, width, height); return pixels; } else { return rotate90Right(pixels, width, height); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/Sort.java0000664000000000000000000001222707741250135022605 0ustar /* * Sort * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.util; import net.sourceforge.jiu.util.ComparatorInterface; /** * Provides sorting of an Object array. * @author Marco Schmidt */ public class Sort { /** * This class is supposed to have static methods only. * To hide any constructor, we define an empty private one. */ private Sort() { } /** * Sorts some (or all) elements of an Object array according to a specified comparator. * This method does exactly the same as java.util.Arrays.sort(Object[], int, int, Comparator). * Unfortunately, this method is not available in Java 1.1, so it must be provided here. *

* As for the implementation of this method, it is taken from Arrays.java as found in * Classpath 0.0.2 (2001-01-06). * Go to www.classpath.org to learn more * about the project, which implements the Java core libraries under the GPL. * * @param a the array which is to be sorted * @param from the index value of the first element of the interval to be sorted * @param to the index value of the last element of the interval to be sorted * @param c the comparator used to query the relation between two objects */ public static void sort(Object[] a, int from, int to, ComparatorInterface c) { if (a == null) { throw new IllegalArgumentException("The object array to be sorted must be non-null."); } if (from > to) { throw new IllegalArgumentException("The from parameter (" + from + ") must be smaller than or equal to the to parameter (" + to + ")."); } if (to >= a.length) { throw new IllegalArgumentException("The to parameter (" + to + ") must be smaller than the array length (" + a.length + ")."); } if (c == null) { throw new IllegalArgumentException("The comparator parameter must be non-null."); } // First presort the array in chunks of length 6 with insertion sort. // mergesort would give too much overhead for this length. for (int chunk = from; chunk < to; chunk += 6) { int end = Math.min(chunk + 6, to); for (int i = chunk + 1; i < end; i++) { if (c.compare(a[i - 1], a[i]) > 0) { // not already sorted int j = i; Object elem = a[j]; do { a[j] = a[j - 1]; j--; } while (j > chunk && c.compare(a[j - 1], elem) > 0); a[j] = elem; } } } int len = to - from; // If length is smaller or equal 6 we are done. if (len <= 6) return; Object[]src = a; Object[]dest = new Object[len]; Object[]t = null; // t is used for swapping src and dest // The difference of the fromIndex of the src and dest array. int srcDestDiff = -from; // The merges are done in this loop for (int size = 6; size < len; size <<= 1) { for (int start = from; start < to; start += size << 1) { // mid ist the start of the second sublist; // end the start of the next sublist (or end of array). int mid = start + size; int end = Math.min(to, mid + size); // The second list is empty or the elements are already in // order - no need to merge if (mid >= end || c.compare(src[mid - 1], src[mid]) <= 0) { System.arraycopy(src, start, dest, start + srcDestDiff, end - start); // The two halves just need swapping - no need to merge } else if (c.compare(src[start], src[end - 1]) > 0) { System.arraycopy(src, start, dest, end - size + srcDestDiff, size); System.arraycopy(src, mid, dest, start + srcDestDiff, end - mid); } else { // Declare a lot of variables to save repeating // calculations. Hopefully a decent JIT will put these // in registers and make this fast int p1 = start; int p2 = mid; int i = start + srcDestDiff; // The main merge loop; terminates as soon as either // half is ended while (p1 < mid && p2 < end) { dest[i++] = src[c.compare(src[p1], src[p2]) <= 0 ? p1++ : p2++]; } // Finish up by copying the remainder of whichever half // wasn't finished. if (p1 < mid) System.arraycopy(src, p1, dest, i, mid - p1); else System.arraycopy(src, p2, dest, i, end - p2); } } // swap src and dest ready for the next merge t = src; src = dest; dest = t; from += srcDestDiff; to += srcDestDiff; srcDestDiff = -srcDestDiff; } // make sure the result ends up back in the right place. Note // that src and dest may have been swapped above, so src // contains the sorted array. if (src != a) { // Note that from == 0. System.arraycopy(src, 0, a, srcDestDiff, to); } } /** * Sort the complete argument array according to the argument comparator. * Simply calls sort(a, 0, a.length - 1, comparator); * @param a array to be sorted * @param comparator the comparator used to compare to array entries */ public static void sort(Object[] a, ComparatorInterface comparator) { sort(a, 0, a.length - 1, comparator); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/Statistics.java0000664000000000000000000001522307741250135024007 0ustar /* * Statistics * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.util; /** * A number of static methods to compute statistical properties of an * array of double values. * Implements the computation of mean, variance and standard deviation * for double values. * @author Marco Schmidt * @since 0.11.0 */ public class Statistics { private Statistics() { } /** * Computes the mean value for the argument array. * Adds all values and divides them by the number of array elements. * @param values double array on which the mean is to be determined * @return computed mean value * @throws IllegalArgumentException if the array has not at least one element */ public static double computeMean(double[] values) { return computeMean(values, 0, values.length); } /** * Computes the mean value for some elements of the argument array. * Adds all values and divides them by the number of array elements. * @param values array from which elements are read * @param offset index of the first element to be used * @param number number of elements to be used * @return computed mean value * @throws IllegalArgumentException if the array has not at least one element */ public static double computeMean(double[] values, int offset, int number) { if (number < 1) { throw new IllegalArgumentException("The number of values to process must be one or larger."); } double sum = 0; final int UNTIL = offset + number; do { sum += values[offset++]; } while (offset != UNTIL); return sum / number; } /** * Computes the standard deviation for the argument array of values. * @param values array from which elements are read * @return computed standard deviation * @throws IllegalArgumentException if the array has not at least two elements */ public static double computeStandardDeviation(double[] values) { return computeStandardDeviation(values, 0, values.length); } /** * Computes the standard deviation for the argument array of values. * Reuses the mean value for that argument which must have been computed before. * @param values array from which elements are read * @param mean the mean value for the array, possibly computed with a * call to {@link #computeMean(double[])}. * @return computed standard deviation * @throws IllegalArgumentException if the array has not at least two elements */ public static double computeStandardDeviation(double[] values, double mean) { return computeStandardDeviation(values, 0, values.length, mean); } /** * Computes the standard deviation for some of the argument array's values. * If you already have computed a mean value using {@link #computeMean(double[], int, int)}, * better call {@link #computeStandardDeviation(double[], int, int, double)}. * Otherwise, this method has to compute mean again. * @param values array from which elements are read * @param offset first element to be used * @param number number of elements used starting at values[offset] * @return computed standard deviation * @throws IllegalArgumentException if the array has not at least two elements */ public static double computeStandardDeviation(double[] values, int offset, int number) { double mean = computeMean(values, offset, number); return computeStandardDeviation(values, 0, values.length, mean); } /** * Computes the standard deviation for some of the argument array's values. * Use this version of the method if you already have a mean value, * otherwise this method must be computed again. * @param values array from which elements are read * @param offset first element to be used * @param number number of elements used starting at values[offset] * @param mean value of the elements * @return computed standard deviation * @throws IllegalArgumentException if the array has not at least two elements */ public static double computeStandardDeviation(double[] values, int offset, int number, double mean) { return Math.sqrt(computeVariance(values, offset, number, mean)); } /** * Computes the variance for the argument array. * @param values array from which elements are read * @return variance for the array elements * @throws IllegalArgumentException if the array has not at least two elements */ public static double computeVariance(final double[] values) { return computeVariance(values, 0, values.length); } /** * Computes the variance for some of the argument array's values. * @param values array from which elements are read * @param mean the mean for the array elements * @return variance for the array elements * @throws IllegalArgumentException if the array has not at least two elements */ public static double computeVariance(final double[] values, final double mean) { return computeVariance(values, 0, values.length, mean); } /** * Computes the variance for some of the argument array's values. * If you already have computed a mean value using {@link #computeMean(double[], int, int)}, * better call {@link #computeVariance(double[], int, int, double)}. * Otherwise, this method has to compute mean again. * @param values array from which elements are read * @param offset first element to be used * @param number number of elements used starting at values[offset] * @return computed variance * @throws IllegalArgumentException if the array has not at least two elements */ public static double computeVariance(final double[] values, int offset, final int number) { double mean = computeMean(values, offset, number); return computeVariance(values, 0, values.length, mean); } /** * Computes the variance for some of the argument array's values. * Use this version of the method in case mean has already been * computed. * @param values array from which elements are read * @param offset first element to be used * @param number number of elements used starting at values[offset] * @param mean the mean for the array elements * @return computed variance * @throws IllegalArgumentException if the array has not at least two elements */ public static double computeVariance(final double[] values, int offset, final int number, final double mean) { if (number < 2) { throw new IllegalArgumentException("The number of values to process must be two or larger."); } double sum = 0; final int UNTIL = offset + number; do { double diff = values[offset++] - mean; sum += diff * diff; } while (offset != UNTIL); return sum / (number - 1); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/SeekableByteArrayOutputStream.java0000664000000000000000000001434707741250135027616 0ustar /* * SeekableByteArrayOutputStream * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.util; import java.io.IOException; import java.io.OutputStream; /** * An extension of {@link java.io.OutputStream} that writes data to an internal * byte array, resizing it when necessary. * Similar to {@link java.io.ByteArrayOutputStream}, but also enables seeking and truncating. * * @author Marco Schmidt * @since 0.10.0 */ public class SeekableByteArrayOutputStream extends OutputStream { private byte[] buffer; private boolean closed; private int incrementStep; private int offset; private int size; /** * Creates a new object of this class, setting initial capacity and increment size * to default values. */ public SeekableByteArrayOutputStream() { this(1024, 1024); } /** * Creates a new object of this class, setting initial capacity to the argument * value. * The increment size is set to the initial capacity as well if that value is larger * than 0. * Otherwise it is set to a default value. * @param initialCapacity the number of bytes that are allocated in this constructor (0 or larger) */ public SeekableByteArrayOutputStream(int initialCapacity) { this(initialCapacity, initialCapacity == 0 ? 1024 : initialCapacity); } /** * Creates a new object of this class, setting initial capacity and increment * to the argument values. * @param initialCapacity the number of bytes that are allocated in this constructor (0 or larger) * @param increment the number of bytes by which the internal byte array is increased if it is full (1 or larger) */ public SeekableByteArrayOutputStream(int initialCapacity, int increment) { if (initialCapacity < 0) { throw new IllegalArgumentException("Value for initial capacity must not be negative."); } if (increment < 1) { throw new IllegalArgumentException("Value for increment must be 1 or larger."); } buffer = new byte[initialCapacity]; incrementStep = increment; offset = 0; } /** * Closes this output stream. * After a call to this method, all write attempts will result in an exception. */ public void close() throws IOException { closed = true; } private void ensureSpace(int numBytes) throws IOException { if (closed) { throw new IOException("Stream was closed already. Cannot write to closed stream."); } if (numBytes < 0) { throw new IllegalArgumentException("Cannot write negative number of bytes (" + numBytes + ")."); } if (buffer.length - offset < numBytes) { increaseBuffer(Math.max(buffer.length + incrementStep, offset + numBytes)); } } /** * Returns the current offset in the output stream. * Larger than or equal to 0 and smaller than or equal to {@link #getSize}. * @return current position in the output stream, 0-based */ public int getPosition() { return offset; } /** * Returns the current size of the output stream. * @return size of the output stream in bytes (0 or larger) */ public int getSize() { return size; } private void increaseBuffer(int newLength) { if (newLength <= buffer.length) { return; } byte[] temp = new byte[newLength]; System.arraycopy(buffer, 0, temp, 0, offset); buffer = temp; } /** * Sets the current position in the output stream to the argument. * @param newOffset new offset into the file, must be >= 0 and <= {@link #getSize} * @throws IOException if the argument is invalid */ public void seek(int newOffset) throws IOException { if (newOffset < 0) { throw new IOException("Cannot seek to negative offset (" + newOffset + ")."); } if (newOffset > size) { throw new IOException("Cannot seek to offset " + newOffset + ", stream has only " + size + " byte(s)."); } offset = newOffset; } /** * Allocates a new byte[] object, copies {@link #getSize} bytes * from the internal byte array to that new array and returns the array. * @return a copy of the byte[] data stored internally */ public byte[] toByteArray() { byte[] result = new byte[size]; System.arraycopy(buffer, 0, result, 0, size); return result; } /** * Removes all bytes after the current position. * After a call to this method, {@link #getSize} is equal to {@link #getPosition}. */ public void truncate() { size = offset; } /** * Writes the least significant eight bits of the argument int to the internal array. * @param b int variable that stores the byte value to be written */ public void write(int b) throws IOException { ensureSpace(1); buffer[offset++] = (byte)b; if (offset > size) { size = offset; } } /** * Write the complete argument array to this stream. * Copies the data to the internal byte array. * Simply calls write(data, 0, data.length);. * @param data array to be copied to this stream */ public void write(byte[] data) throws IOException { write(data, 0, data.length); } /** * Write some bytes from the argument array to this stream. * Copies num bytes starting at src[srcOffset] to this stream. * @param src the array from which data is copied * @param srcOffset int index into that array pointing to the first byte to be copied * @param num number of bytes to be copied */ public void write(byte[] src, int srcOffset, int num) throws IOException { ensureSpace(num); System.arraycopy(src, srcOffset, buffer, offset, num); offset += num; if (offset > size) { size = offset; } } /** * Writes the bytes in the internal byte array to the argument output stream. * A call to this method has the same effect as *

	 * byte[] copy = toByteArray();
	 * out.write(copy, 0, copy.length);
	 * 
* However, you with this method you save the allocation of an additional byte array * and the copying to that new array. * @param out the output stream to which this stream's content is copied * @throws IOException if out has a problem writing the bytes */ public void writeTo(OutputStream out) throws IOException { out.write(buffer, 0, size); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/ArrayScaling.java0000664000000000000000000000670110572433337024240 0ustar /* * ArrayScaling * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.util; import java.lang.IllegalArgumentException; /** * This class currently only scales up an image given as a one-dimensional array * of values. *

* Note: This class should be adjusted if Java ever supports genericity. * It could then work on all kinds of arrays. * * @author Marco Schmidt */ public class ArrayScaling { private ArrayScaling() { } /** * Scales up the argument image by factor 2 in both directions. * It is assumed that the first width times * height values of data contain an image * (or image channel). * The pixels (or samples) are assumed to be laid out rows top-to-bottom, * within each row left-to-right. * It is further assumed that the length of the data array is * at least 4 times width times height. * This method scales up the image in data so that after the call to this * method data can be treated as an image (a channel) that has a horizontal * resolution of width * 2 and a vertical resolution of * height * 2. * * @param data the array of pixels that form the image to be flipped * @param width the horizontal resolution of the image; must be larger than 0 * @param height the vertical resolution of the image; must be larger than 0 * @exception IllegalArgumentException if the arguments are invalid */ public static final void scaleUp200Percent(byte[] data, int width, int height) throws IllegalArgumentException { if (data == null) { throw new IllegalArgumentException("Error -- data must be non-null."); } if (width < 1 || height < 1) { throw new IllegalArgumentException("Error -- both width and " + "height must be larger than zero (width=" + width + ", height=" + height + ")."); } if (width * height * 4 > data.length) { throw new IllegalArgumentException("Error -- data array must hold " + "at least width times height times 4 values."); } int newWidth = width * 2; int newHeight = height * 2; // (1) scale up each row in horizontal direction and copy it to its destination // at the same time int y1 = height - 1; int y2 = newHeight - 1; while (y1 >= 0) { int x = width - 1; int offset1 = (y1 + 1) * width - 1; int offset2 = (y2 + 1) * newWidth - 1; while (x > 0) { int v1 = data[offset1--] & 0xff; int v2 = data[offset1] & 0xff; data[offset2--] = (byte)v1; data[offset2--] = (byte)((v1 + v2) >> 1); x--; } byte v = data[offset1]; data[offset2--] = v; data[offset2] = v; y1--; y2 -= 2; } // (2) take two already-copied rows from scaled image and // interpolate the row between them int y = newHeight - 1; while (y > 1) { int offset1 = (y - 2) * newWidth; int offset2 = offset1 + newWidth; int offset3 = offset2 + newWidth; for (int x = 0; x < newWidth; x++) { int v1 = data[offset1++] & 0xff; int v2 = data[offset3++] & 0xff; data[offset2++] = (byte)((v1 + v2) >> 1); } y -= 2; } // (3) copy second row of scaled image to first row int x1 = 0; int x2 = newWidth; while (x1 < newWidth) { data[x1++] = data[x2++]; } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/ArrayConverter.java0000664000000000000000000003754610104716177024637 0ustar /* * ArrayConverter * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.util; /** * Helper class with static methods to convert between byte arrays and primitive types. * Useful for serialization. * @author Marco Schmidt * @since 0.9.0 */ public class ArrayConverter { private static final int SHORT_SIZE = 2; private static final int INT_SIZE = 4; private ArrayConverter() { } /** * Makes sure that the arguments define a valid (existing) array interval. * This includes: *

*/ private static void checkArray(byte[] array, int offset, int length) throws IllegalArgumentException { if (array == null) { throw new IllegalArgumentException("Array must not be null."); } if (offset < 0) { throw new IllegalArgumentException("Array index must not be negative."); } if (length < 1) { throw new IllegalArgumentException("Length of value must not be smaller than one."); } if (offset >= array.length) { throw new IllegalArgumentException("Offset " + offset + " is invalid, must be smaller than array length " + array.length + "."); } if (offset + length > array.length) { throw new IllegalArgumentException("Value of length " + length + " does not fit at offset " + offset + " into an array of length " + array.length + "."); } } /*public static void convertPacked2BitIntensityTo8BitA(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes) { final byte[] DEST_VALUES = {0, 85, (byte)170, (byte)255}; while (numPackedBytes-- > 0) { int srcValue = src[srcOffset++] & 0xff; dest[destOffset++] = DEST_VALUES[(srcValue >> 6) & 3]; dest[destOffset++] = DEST_VALUES[(srcValue >> 4) & 3]; dest[destOffset++] = DEST_VALUES[(srcValue >> 2) & 3]; dest[destOffset++] = DEST_VALUES[srcValue & 3]; } }*/ /** * Converts bytes with two four-bit-intensity samples to 8 byte intensity * values, each stored in one byte. * Two-bit values can be 0, 1, 2 or 3. * These values will be scaled to the full [0;255] range so that 0 remains 0, * 1 becomes 85, 2 becomes 170 and 3 becomes 255. *

* A little discussion on how to implement this method * was held in the German Java newsgroup * de.comp.lang.java. * The message I wrote to start the thread has the ID * 1ef7du4vfqsd2pskb6jukut6pnhn87htt2@4ax.com. * Read the * thread * at Google Groups. * @param src byte array, each byte stores four two-bit intensity values * @param srcOffset index into src * @param dest byte array, each byte stores an eight-bit intensity values * @param destOffset index into dest * @param numPackedBytes number of bytes in src to be decoded */ public static void convertPacked2BitIntensityTo8Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes) { while (numPackedBytes-- > 0) { int srcValue = src[srcOffset++] & 0xff; dest[destOffset++] = (byte)(((srcValue >> 6) & 3) * 85); dest[destOffset++] = (byte)(((srcValue >> 4) & 3) * 85); dest[destOffset++] = (byte)(((srcValue >> 2) & 3) * 85); dest[destOffset++] = (byte)((srcValue & 3) * 85); } } /** * Converts bytes with four two-bit-intensity samples to byte-sized intensity values. * Four-bit values can be from 0 to 15. * These values will be scaled to the full [0;255] range so that 0 remains 0, * 1 becomes 17, 2 becomes 34, ..., and 15 becomes 255. * The most significant four bits in a byte become the left, the least significant * four bits the right pixel. * @param src byte array, each byte stores two four-bit intensity values * @param srcOffset index into src * @param dest byte array, each byte stores an eight-bit intensity values * @param destOffset index into dest * @param numPackedBytes number of bytes in src to be decoded * @since 0.12.0 */ public static void convertPacked4BitIntensityTo8Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes) { while (numPackedBytes-- > 0) { int srcValue = src[srcOffset++] & 0xff; dest[destOffset++] = (byte)((srcValue & 0xf0) | ((srcValue & 0xf0) >> 4)); dest[destOffset++] = (byte)((srcValue & 0x0f) | ((srcValue & 0x0f) << 4)); } } /** * Copies a number of bit values from one byte array to another. * @param src array from which is copied * @param srcOffset index into the src array of the first byte from which is copied * @param srcBitOffset first bit within src[srcOffset] from which is copied (0 is left-most, 1 is second left-most, 7 is right-most) * @param dest array to which is copied * @param destOffset index into the dest array of the first byte to which is copied * @param destBitOffset first bit within dest[destOffset] to which is copied (0 is left-most, 1 is second left-most, 7 is right-most) * @param numSamples number of bits to be copied */ public static void copyPackedBytes(byte[] src, int srcOffset, int srcBitOffset, byte[] dest, int destOffset, int destBitOffset, int numSamples) { if (numSamples < 0) { throw new IllegalArgumentException("Number of samples to be copied must be 0 or larger."); } if (srcBitOffset == 0 && destBitOffset == 0 && numSamples > 7) { int bytes = numSamples >> 3; System.arraycopy(src, srcOffset, dest, destOffset, bytes); srcOffset += bytes; destOffset += bytes; numSamples &= 7; } int srcMask = 1 << (7 - srcBitOffset); int destMask = 1 << (7 - destBitOffset); while (numSamples-- != 0) { if ((src[srcOffset] & srcMask) == 0) { dest[destOffset] &= (byte)(255 - destMask); } else { dest[destOffset] |= (byte)destMask; } if (srcMask == 1) { srcMask = 128; srcOffset++; } else { srcMask >>= 1; } if (destMask == 1) { destMask = 128; destOffset++; } else { destMask >>= 1; } } } public static void decodePacked1Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes) { while (numPackedBytes-- != 0) { int srcValue = src[srcOffset++] & 0xff; dest[destOffset++] = (byte)((srcValue >> 7) & 0x01); dest[destOffset++] = (byte)((srcValue >> 6) & 0x01); dest[destOffset++] = (byte)((srcValue >> 5) & 0x01); dest[destOffset++] = (byte)((srcValue >> 4) & 0x01); dest[destOffset++] = (byte)((srcValue >> 3) & 0x01); dest[destOffset++] = (byte)((srcValue >> 2) & 0x01); dest[destOffset++] = (byte)((srcValue >> 1) & 0x01); dest[destOffset++] = (byte)(srcValue & 0x01); } } /** * Decodes bytes with four two-bit samples to single bytes. * The two most significant bits of a source byte become the first value, * the two least significant bits the fourth value. * The method expects numPackedBytes bytes at src[srcOffset] * (these will be read and interpreted) and * numPackedBytes * 4 at dest[destOffset] (where the decoded * byte values will be stored. *

* @param src byte array, each byte stores four two-bit values * @param srcOffset index into src * @param dest byte array, each byte stores a single decoded value (from 0 to 3) * @param destOffset index into dest * @param numPackedBytes number of bytes in src to be decoded * @since 0.10.0 */ public static void decodePacked2Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes) { while (numPackedBytes-- != 0) { int srcValue = src[srcOffset++] & 0xff; dest[destOffset++] = (byte)(srcValue >> 6); dest[destOffset++] = (byte)((srcValue >> 4) & 0x03); dest[destOffset++] = (byte)((srcValue >> 2) & 0x03); dest[destOffset++] = (byte)(srcValue & 0x03); } } /** * Decodes bytes with two four-bit samples to single bytes. * The four most significant bits of a source byte become the first value, * the least significant four bits the second value. * The method expects numPackedBytes bytes at src[srcOffset] * (these will be read and interpreted) and * numPackedBytes * 2 at dest[destOffset] (where the decoded * byte values will be stored. *

* @param src byte array, each byte stores two four-bit values * @param srcOffset index into src * @param dest byte array, each byte stores a single decoded value * @param destOffset index into dest * @param numPackedBytes number of bytes in src to be decoded */ public static void decodePacked4Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes) { while (numPackedBytes-- > 0) { int srcValue = src[srcOffset++] & 0xff; dest[destOffset++] = (byte)(srcValue >> 4); dest[destOffset++] = (byte)(srcValue & 0x0f); } } /** * Convert 16 bit RGB samples stored in big endian (BE) byte order * with 5 bits for red and blue and 6 bits for green to 24 * bit RGB byte samples. * @since 0.10.0 */ public static void decodePackedRGB565BigEndianToRGB24(byte[] src, int srcOffset, byte[] red, int redOffset, byte[] green, int greenOffset, byte[] blue, int blueOffset, int numPixels) { while (numPixels-- != 0) { int pixel = ((src[srcOffset] & 0xff) << 8) | (src[srcOffset + 1] & 0xff); srcOffset += 2; int r = (pixel >> 11) & 0x1f; int g = (pixel >> 5) & 0x3f; int b = pixel & 0x1f; red[redOffset++] = (byte)((r << 3) | ((r >> 2) & 0x07)); green[greenOffset++] = (byte)((g << 2) | ((g >> 4) & 0x03)); blue[blueOffset++] = (byte)((b << 3) | ((b >>2) & 0x07)); } } public static void encodePacked2Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numSamples) { int numBytes = numSamples / 4; while (numBytes-- != 0) { int b1 = src[srcOffset++] & 3; int b2 = src[srcOffset++] & 3; int b3 = src[srcOffset++] & 3; int b4 = src[srcOffset++] & 3; dest[destOffset++] = (byte)(b1 << 6 | b2 << 4 | b3 << 2 | b4); } numSamples = numSamples % 4; if (numSamples > 0) { int value = 0; int mask = 6; while (numSamples-- != 0) { value |= ((src[srcOffset++] & 3) << mask); mask -= 2; } dest[destOffset] = (byte)value; } } public static void encodePacked4Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numSamples) { int numBytes = numSamples / 2; while (numBytes-- != 0) { int b1 = src[srcOffset++] & 15; int b2 = src[srcOffset++] & 15; dest[destOffset++] = (byte)(b1 << 4 | b2); } if ((numSamples % 2) == 1) { dest[destOffset] = (byte)((src[srcOffset] & 15) << 4); } } /** * Convert 24 bit RGB pixels to 16 bit pixels stored in big endian (BE) byte order * with 5 bits for red and blue and 6 bits for green. * @since 0.10.0 */ public static void encodeRGB24ToPackedRGB565BigEndian( byte[] red, int redOffset, byte[] green, int greenOffset, byte[] blue, int blueOffset, byte[] dest, int destOffset, int numPixels) { while (numPixels-- != 0) { int r = (red[redOffset++] & 0xff) >> 3; int g = (green[greenOffset++] & 0xff) >> 2; int b = (blue[blueOffset++] & 0xff) >> 3; int pixel = r << 11 | g << 5 | b; dest[destOffset++] = (byte)(pixel >> 8); dest[destOffset++] = (byte)(pixel & 0xff); } } /** * Reads four consecutive bytes from the given array at the * given position in big endian order and returns them as * an int. * @param src the array from which bytes are read * @param srcOffset the index into the array from which the bytes are read * @return int value taken from the array */ public static int getIntBE(byte[] src, int srcOffset) { checkArray(src, srcOffset, INT_SIZE); return (src[srcOffset + 3] & 0xff) | ((src[srcOffset + 2] & 0xff) << 8) | ((src[srcOffset + 1] & 0xff) << 16) | ((src[srcOffset] & 0xff) << 24); } /** * Reads four consecutive bytes from the given array at the * given position in little endian order and returns them as * an int. * @param src the array from which bytes are read * @param srcOffset the index into the array from which the bytes are read * @return short value taken from the array */ public static int getIntLE(byte[] src, int srcOffset) { checkArray(src, srcOffset, INT_SIZE); return (src[srcOffset] & 0xff) | ((src[srcOffset + 1] & 0xff) << 8) | ((src[srcOffset + 2] & 0xff) << 16) | ((src[srcOffset + 3] & 0xff) << 24); } /** * Reads two consecutive bytes from the given array at the * given position in big endian order and returns them as * a short. * @param src the array from which two bytes are read * @param srcOffset the index into the array from which the two bytes are read * @return short value taken from the array */ public static short getShortBE(byte[] src, int srcOffset) { checkArray(src, srcOffset, SHORT_SIZE); return (short) (((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff)); } public static int getShortBEAsInt(byte[] src, int srcOffset) { checkArray(src, srcOffset, SHORT_SIZE); return ((src[srcOffset++] & 0xff) << 8) | (src[srcOffset++] & 0xff); } /** * Reads two consecutive bytes from the given array at the * given position in little endian order and returns them as * a short. * @param src the array from which two bytes are read * @param srcOffset the index into the array from which the two bytes are read * @return short value taken from the array */ public static short getShortLE(byte[] src, int srcOffset) { checkArray(src, srcOffset, SHORT_SIZE); return (short) ((src[srcOffset++] & 0xff) | ((src[srcOffset++] & 0xff) << 8)); } /** * Writes an int value into four consecutive bytes of a byte array, * in big endian (network) byte order. * @param dest the array to which bytes are written * @param destOffset index of the array to which the first byte is written * @param newValue the int value to be written to the array */ public static void setIntBE(byte[] dest, int destOffset, int newValue) { checkArray(dest, destOffset, INT_SIZE); dest[destOffset] = (byte)((newValue >> 24)& 0xff); dest[destOffset + 1] = (byte)((newValue >> 16)& 0xff); dest[destOffset + 2] = (byte)((newValue >> 8)& 0xff); dest[destOffset + 3] = (byte)(newValue & 0xff); } /** * Writes an int value into four consecutive bytes of a byte array, * in little endian (Intel) byte order. * @param dest the array to which bytes are written * @param destOffset index of the array to which the first byte is written * @param newValue the int value to be written to the array */ public static void setIntLE(byte[] dest, int destOffset, int newValue) { checkArray(dest, destOffset, INT_SIZE); dest[destOffset] = (byte)(newValue & 0xff); dest[destOffset + 1] = (byte)((newValue >> 8)& 0xff); dest[destOffset + 2] = (byte)((newValue >> 16)& 0xff); dest[destOffset + 3] = (byte)((newValue >> 24)& 0xff); } public static void setShortBE(byte[] dest, int destOffset, short newValue) { checkArray(dest, destOffset, SHORT_SIZE); dest[destOffset] = (byte)((newValue >> 8) & 0xff); dest[destOffset + 1] = (byte)(newValue & 0xff); } public static void setShortLE(byte[] dest, int destOffset, short newValue) { checkArray(dest, destOffset, SHORT_SIZE); dest[destOffset + 1] = (byte)((newValue >> 8) & 0xff); dest[destOffset] = (byte)(newValue & 0xff); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/package.html0000664000000000000000000000036307741250135023272 0ustar

Various helper classes with functionality not directly related to imaging. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/ComparatorInterface.java0000664000000000000000000000156310324334215025600 0ustar /* * ComparatorInterface * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.util; /** * To be able to do sorting in Java 1.1 as defined in java.util.Arrays (which * is only available in Java 1.2 and higher), we offer a java.util.Comparator * clone under a different name: ComparatorInterface. * Sorting will be provided by the {@link Sort} class of this package. */ public interface ComparatorInterface { /** * Compares the two argument objects and returns their relation. * Returns *

*/ int compare(Object o1, Object o2); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/SystemInfo.java0000664000000000000000000000556207741250135023762 0ustar /* * SystemInfo * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.util; import net.sourceforge.jiu.apps.StringIndexConstants; import net.sourceforge.jiu.apps.Strings; /** * Class to retrieve system information in a human-readable form. * @author Marco Schmidt */ public class SystemInfo implements StringIndexConstants { private SystemInfo() { } public static String getMemoryInfo(Strings strings) { Runtime runtime = Runtime.getRuntime(); long freeMemory = runtime.freeMemory(); long totalMemory = runtime.totalMemory(); long usedMemory = totalMemory - freeMemory; return strings.get(StringIndexConstants.FREE_MEMORY) + "=" + freeMemory + "\n" + strings.get(StringIndexConstants.USED_MEMORY) + "=" + usedMemory + "\n" + strings.get(StringIndexConstants.TOTAL_MEMORY) + "=" + totalMemory + "\n"; } /** * Returns a multiple-line text with information on the Java Virtual Machine, * the path settings and the operating system used, regarding the current * language by using a {@link Strings} resource. * @return system information as String * @see java.lang.System#getProperty(String) */ public static String getSystemInfo(Strings strings) { final String[] PROPERTIES = {"java.version", "java.vendor", "java.vendor.url", "java.home", "java.vm.specification.version", "java.vm.specification.vendor", "java.vm.specification.name", "java.vm.version", "java.vm.vendor", "java.vm.name", "java.specification.version", "java.specification.vendor", "java.specification.name", "java.class.version", "java.class.path", "os.name", "os.arch", "os.version", "sun.cpu.endian", "sun.cpu.isalist", }; final int[] STRING_INDEX_VALUES = { PROPERTY_JAVA_VERSION, PROPERTY_JAVA_VENDOR, PROPERTY_JAVA_VENDOR_URL, PROPERTY_JAVA_HOME, PROPERTY_JAVA_VM_SPECIFICATION_VERSION, PROPERTY_JAVA_VM_SPECIFICATION_VENDOR, PROPERTY_JAVA_VM_SPECIFICATION_NAME, PROPERTY_JAVA_VM_VERSION, PROPERTY_JAVA_VM_VENDOR, PROPERTY_JAVA_VM_NAME, PROPERTY_JAVA_SPECIFICATION_VERSION, PROPERTY_JAVA_SPECIFICATION_VENDOR, PROPERTY_JAVA_SPECIFICATION_NAME, PROPERTY_JAVA_CLASS_VERSION, PROPERTY_JAVA_CLASS_PATH, PROPERTY_OS_NAME, PROPERTY_OS_ARCH, PROPERTY_OS_VERSION, CPU_ENDIANNESS, CPU_ISALIST }; StringBuffer sb = new StringBuffer(); for (int i = 0; i < PROPERTIES.length; i++) { try { String result = System.getProperty(PROPERTIES[i]); if (result != null) { if (sb.length() > 0) { sb.append("\n"); } sb.append(strings.get(STRING_INDEX_VALUES[i])); sb.append('='); sb.append(result); } } catch (Exception e) { } } return sb.toString(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/util/Median.java0000664000000000000000000000635107741250135023054 0ustar /* * Median * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.util; /** * Pick the median value from an array (or an interval of an array). * @author Marco Schmidt * @since 0.5.0 */ public class Median { /** * This class is supposed to have static methods only. * To hide any constructor, we define an empty private one. */ private Median() { } /** * Exchange two elements in the argument array. * A temporary variable is used so that a[i1] will * hold the value that was previously stored at a[i2] * and vice versa. * * @param a the array in which two elements are swapped * @param i1 index of the first element * @param i2 index of the second element * @throws ArrayIndexOutOfBoundsException if either i1 or i2 are * not valid index values into a (from 0 to a.length - 1) */ public static void swap(int[] a, int i1, int i2) { int temp = a[i1]; a[i1] = a[i2]; a[i2] = temp; } /** * Find the median value of the specified interval of the argument array. * The interval starts at index from and goes to * to; the values at these positions are included. * Note that the array will be modified while searching, so you might want * to backup your data. *

* This implementation is a port of the C function from * quickselect.c, provided at http://ndevilla.free.fr/median/. * The page is a good resource for various median value algorithms, including * implementations and benchmarks. *

* The original code on which this class is based was written in C++ * by Martin Leese. * It was ported to C and optimized by Nicolas Devillard (author of the * above mentioned page). * The algorithm is from Numerical recipes in C, Second Edition, * Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5. *

* * @param a the array * @param from the index of the start of the interval in which the median value will be searched * @param to the index of the end of the interval in which the median value will be searched * @return the median value */ public static int find(int[] a, int from, int to) { int low = from; int high = to; int median = (low + high) / 2; do { if (high <= low) { return a[median]; } if (high == low + 1) { if (a[low] > a[high]) { swap(a, low, high); } return a[median]; } int middle = (low + high) / 2; if (a[middle] > a[high]) { swap(a, middle, high); } if (a[low] > a[high]) { swap(a, low, high); } if (a[middle] > a[low]) { swap(a, middle, low); } swap(a, middle, low + 1); int ll = low + 1; int hh = high; do { do { ll++; } while(a[low] > a[ll]); do { hh--; } while(a[hh] > a[low]); if (hh < ll) { break; } swap(a, ll, hh); } while(true); swap(a, low, hh); if (hh <= median) { low = ll; } if (hh >= median) { high = hh - 1; } } while(true); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/0000775000000000000000000000000010612172112020762 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/MenuWrapper.java0000664000000000000000000002354210404065334024106 0ustar /* * MenuWrapper * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; /** * Abstract menu wrapper. * A menu consists of a number of menu elements, each of which have a text, * an enabled status and an int constant from MenuIndexConstants associated with it. * @author Marco Schmidt */ public abstract class MenuWrapper { /** * Attempts to find the index of a given object that represents a menu element. * @param o some object representing part of the menu * @return corresponding index value from {@link MenuIndexConstants} on success * or -1 on failure */ public abstract int findIndex(Object o); /** * For one of the values in {@link MenuIndexConstants}, returns the corresponding * constant in {@link StringIndexConstants}. * @param menuIndex int value from the MenuIndexConstants interface * @return int value from the StringIndexConstants interface */ public int getStringIndex(int menuIndex) { switch(menuIndex) { case(MenuIndexConstants.FILE): return StringIndexConstants.FILE; case(MenuIndexConstants.FILE_OPEN): return StringIndexConstants.OPEN; case(MenuIndexConstants.FILE_SAVEAS): return StringIndexConstants.SAVE_AS; case(MenuIndexConstants.FILE_SAVEAS_GIF): return StringIndexConstants.GIF; case(MenuIndexConstants.FILE_SAVEAS_PALM): return StringIndexConstants.PALM; case(MenuIndexConstants.FILE_SAVEAS_PBM): return StringIndexConstants.PORTABLE_BITMAP; case(MenuIndexConstants.FILE_SAVEAS_PGM): return StringIndexConstants.PORTABLE_GRAYMAP; case(MenuIndexConstants.FILE_SAVEAS_PNG): return StringIndexConstants.PORTABLE_NETWORK_GRAPHICS; case(MenuIndexConstants.FILE_SAVEAS_PPM): return StringIndexConstants.PORTABLE_PIXMAP; case(MenuIndexConstants.FILE_SAVEAS_SUNRASTER): return StringIndexConstants.SUN_RASTER; case(MenuIndexConstants.FILE_SAVEAS_WINDOWSBMP): return StringIndexConstants.WINDOWS_BITMAP; case(MenuIndexConstants.FILE_IMAGE_1): return StringIndexConstants.IMAGE_1; case(MenuIndexConstants.FILE_CLOSE): return StringIndexConstants.CLOSE; case(MenuIndexConstants.FILE_EXIT): return StringIndexConstants.EXIT; case(MenuIndexConstants.EDIT): return StringIndexConstants.EDIT; case(MenuIndexConstants.EDIT_UNDO): return StringIndexConstants.EDIT_UNDO; case(MenuIndexConstants.EDIT_REDO): return StringIndexConstants.EDIT_REDO; case(MenuIndexConstants.COLOR): return StringIndexConstants.COLOR; case(MenuIndexConstants.COLOR_ADJUST): return StringIndexConstants.ADJUST; case(MenuIndexConstants.COLOR_ADJUST_BRIGHTNESS): return StringIndexConstants.BRIGHTNESS_MENU_ITEM; case(MenuIndexConstants.COLOR_ADJUST_CONTRAST): return StringIndexConstants.CONTRAST_MENU_ITEM; case(MenuIndexConstants.COLOR_ADJUST_GAMMA): return StringIndexConstants.GAMMA_MENU_ITEM; case(MenuIndexConstants.COLOR_ADJUST_HUESATURATIONVALUE): return StringIndexConstants.HUE_SATURATION_VALUE_MENU_ITEM; case(MenuIndexConstants.COLOR_HISTOGRAM): return StringIndexConstants.HISTOGRAM; case(MenuIndexConstants.COLOR_HISTOGRAM_COUNTCOLORSUSED): return StringIndexConstants.COUNT_COLORS_USED; case(MenuIndexConstants.COLOR_HISTOGRAM_EQUALIZE): return StringIndexConstants.EQUALIZE_HISTOGRAM_MENU_ITEM; case(MenuIndexConstants.COLOR_HISTOGRAM_NORMALIZE): return StringIndexConstants.NORMALIZE_HISTOGRAM_MENU_ITEM; case(MenuIndexConstants.COLOR_HISTOGRAM_TEXTUREPROPERTIES): return StringIndexConstants.TEXTURE_PROPERTIES_MENU_ITEM; case(MenuIndexConstants.COLOR_HISTOGRAM_SAVEHISTOGRAMAS): return StringIndexConstants.SAVE_HISTOGRAM_AS_MENU_ITEM; case(MenuIndexConstants.COLOR_HISTOGRAM_SAVECOOCCURRENCEMATRIXAS): return StringIndexConstants.SAVE_COOCCURRENCE_MATRIX_MENU_ITEM; case(MenuIndexConstants.COLOR_HISTOGRAM_SAVECOOCCURRENCEFREQUENCYMATRIXAS): return StringIndexConstants.SAVE_COOCCURRENCE_FREQUENCY_MATRIX_MENU_ITEM; case(MenuIndexConstants.COLOR_PALETTE): return StringIndexConstants.PALETTE_MENU_ITEM; case(MenuIndexConstants.COLOR_PALETTE_SAVEAS): return StringIndexConstants.PALETTE_SAVE_AS_MENU_ITEM; case(MenuIndexConstants.COLOR_PROMOTE): return StringIndexConstants.PROMOTE; case(MenuIndexConstants.COLOR_PROMOTE_PROMOTETOPALETTED): return StringIndexConstants.PROMOTE_TO_PALETTED; case(MenuIndexConstants.COLOR_PROMOTE_PROMOTETOGRAY8): return StringIndexConstants.PROMOTE_TO_GRAY8; case(MenuIndexConstants.COLOR_PROMOTE_PROMOTETOGRAY16): return StringIndexConstants.PROMOTE_TO_GRAY16; case(MenuIndexConstants.COLOR_PROMOTE_PROMOTETORGB24): return StringIndexConstants.PROMOTE_TO_RGB24; case(MenuIndexConstants.COLOR_PROMOTE_PROMOTETORGB48): return StringIndexConstants.PROMOTE_TO_RGB48; case(MenuIndexConstants.COLOR_REDUCE): return StringIndexConstants.REDUCE; case(MenuIndexConstants.COLOR_REDUCE_REDUCETOBILEVELTHRESHOLD): return StringIndexConstants.REDUCE_TO_BILEVEL_THRESHOLD_MENU_ITEM; case(MenuIndexConstants.COLOR_REDUCE_REDUCENUMBEROFSHADESOFGRAY): return StringIndexConstants.REDUCE_NUMBER_OF_SHADES_OF_GRAY_MENU_ITEM; case(MenuIndexConstants.COLOR_REDUCE_CONVERTTOGRAYSCALE): return StringIndexConstants.CONVERT_TO_GRAYSCALE; case(MenuIndexConstants.COLOR_REDUCE_MEDIANCUT): return StringIndexConstants.MEDIAN_CUT; case(MenuIndexConstants.COLOR_REDUCE_OCTREE): return StringIndexConstants.OCTREE_COLOR_QUANTIZATION_MENU_ITEM; case(MenuIndexConstants.COLOR_REDUCE_UNIFORMPALETTE): return StringIndexConstants.UNIFORM_PALETTE_COLOR_QUANTIZATION_MENU_ITEM; case(MenuIndexConstants.COLOR_REDUCE_MAPTOARBITRARYPALETTE): return StringIndexConstants.MAP_TO_ARBITRARY_PALETTE_MENU_ITEM; case(MenuIndexConstants.COLOR_INVERT): return StringIndexConstants.INVERT; case(MenuIndexConstants.COLOR_CONVERTTOMINIMUMCOLORTYPE): return StringIndexConstants.CONVERT_TO_MINIMUM_COLOR_TYPE_MENU_ITEM; case(MenuIndexConstants.TRANSFORMATIONS): return StringIndexConstants.TRANSFORMATIONS; case(MenuIndexConstants.TRANSFORMATIONS_FLIP): return StringIndexConstants.FLIP; case(MenuIndexConstants.TRANSFORMATIONS_MIRROR): return StringIndexConstants.MIRROR; case(MenuIndexConstants.TRANSFORMATIONS_ROTATELEFT90): return StringIndexConstants.ROTATE_90_LEFT; case(MenuIndexConstants.TRANSFORMATIONS_ROTATERIGHT90): return StringIndexConstants.ROTATE_90_RIGHT; case(MenuIndexConstants.TRANSFORMATIONS_ROTATE180): return StringIndexConstants.ROTATE_180; case(MenuIndexConstants.TRANSFORMATIONS_CROP): return StringIndexConstants.CROP_MENU_ITEM; case(MenuIndexConstants.TRANSFORMATIONS_SHEAR): return StringIndexConstants.SHEAR_MENU_ITEM; case(MenuIndexConstants.TRANSFORMATIONS_SCALE): return StringIndexConstants.SCALE; case(MenuIndexConstants.FILTERS): return StringIndexConstants.FILTERS; case(MenuIndexConstants.FILTERS_BLUR): return StringIndexConstants.BLUR; case(MenuIndexConstants.FILTERS_SHARPEN): return StringIndexConstants.SHARPEN; case(MenuIndexConstants.FILTERS_EDGEDETECTION): return StringIndexConstants.EDGE_DETECTION; case(MenuIndexConstants.FILTERS_EMBOSS): return StringIndexConstants.EMBOSS; case(MenuIndexConstants.FILTERS_PSYCHEDELICDISTILLATION): return StringIndexConstants.PSYCHEDELIC_DISTILLATION; case(MenuIndexConstants.FILTERS_LITHOGRAPH): return StringIndexConstants.LITHOGRAPH; case(MenuIndexConstants.FILTERS_HORIZONTALSOBEL): return StringIndexConstants.HORIZONTAL_SOBEL; case(MenuIndexConstants.FILTERS_VERTICALSOBEL): return StringIndexConstants.VERTICAL_SOBEL; case(MenuIndexConstants.FILTERS_HORIZONTALPREWITT): return StringIndexConstants.HORIZONTAL_PREWITT; case(MenuIndexConstants.FILTERS_VERTICALPREWITT): return StringIndexConstants.VERTICAL_PREWITT; case(MenuIndexConstants.FILTERS_MINIMUM): return StringIndexConstants.MINIMUM_FILTER_MENU_ITEM; case(MenuIndexConstants.FILTERS_MAXIMUM): return StringIndexConstants.MAXIMUM_FILTER_MENU_ITEM; case(MenuIndexConstants.FILTERS_MEDIAN): return StringIndexConstants.MEDIAN_FILTER_MENU_ITEM; case(MenuIndexConstants.FILTERS_MEAN): return StringIndexConstants.MEAN_FILTER_MENU_ITEM; case(MenuIndexConstants.FILTERS_OIL): return StringIndexConstants.OIL_FILTER_MENU_ITEM; case(MenuIndexConstants.VIEW): return StringIndexConstants.VIEW; case(MenuIndexConstants.VIEW_ZOOMIN): return StringIndexConstants.VIEW_ZOOMIN; case(MenuIndexConstants.VIEW_ZOOMOUT): return StringIndexConstants.VIEW_ZOOMOUT; case(MenuIndexConstants.VIEW_SETORIGINALSIZE): return StringIndexConstants.VIEW_SETORIGINALSIZE; case(MenuIndexConstants.VIEW_INTERPOLATIONTYPE): return StringIndexConstants.VIEW_INTERPOLATIONTYPE; case(MenuIndexConstants.VIEW_INTERPOLATIONTYPE_NEARESTNEIGHBOR): return StringIndexConstants.VIEW_INTERPOLATIONTYPE_NEARESTNEIGHBOR; case(MenuIndexConstants.VIEW_INTERPOLATIONTYPE_BILINEAR): return StringIndexConstants.VIEW_INTERPOLATIONTYPE_BILINEAR; case(MenuIndexConstants.VIEW_INTERPOLATIONTYPE_BICUBIC): return StringIndexConstants.VIEW_INTERPOLATIONTYPE_BICUBIC; case(MenuIndexConstants.HELP): return StringIndexConstants.HELP; case(MenuIndexConstants.HELP_ABOUT): return StringIndexConstants.ABOUT; case(MenuIndexConstants.HELP_SYSTEMINFORMATION): return StringIndexConstants.SYSTEM_INFORMATION; default: return -1; } } /** * Sets the enabled status of one of the menu items to either * true or false. * @param index menu index of the component whose status is to be reset * @param enabled boolean with the new value */ public abstract void setEnabled(int index, boolean enabled); /** * Sets the text of one of the menu elements to a new value. * This method is usually called when the language settings have changed and * new words have to be assigned. * @param index integer index of the menu element * @param text new text value to be used for this element */ public abstract void setLabel(int index, String text); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/Strings.java0000664000000000000000000001177110121567174023300 0ustar /* * Strings * * Copyright (c) 2001, 2002, 2003, 2004 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.util.Hashtable; import java.util.Locale; /** * String resource for the various apps. * Each index value from {@link StringIndexConstants} has a corresponding String value * for all supported natural languages. * @author Marco Schmidt */ public class Strings implements StringIndexConstants { /** * Constant int value for the natural language English. * */ public static final Integer LANG_ENGLISH = new Integer(0); /** * Constant int value for the natural language German. */ public static final Integer LANG_GERMAN = new Integer(1); /** * Constant int value for the natural language Spanish. */ public static final Integer LANG_SPANISH = new Integer(2); /** * Constant int value for the natural language French. */ public static final Integer LANG_FRENCH = new Integer(3); /** * Constant of the default language, {@link #LANG_ENGLISH}. */ public static final Integer DEFAULT_LANGUAGE = LANG_ENGLISH; /** * ISO 639 two-letter country codes for the supported languages, lower case. */ private static final String[] ISO_639_LANGUAGE_CODES = { "en", "de", "es", "fr", }; private static final Integer[] LANGUAGE_CONSTANTS = { LANG_ENGLISH, LANG_GERMAN, LANG_SPANISH, LANG_FRENCH, }; /** * The ISO 639 code for the default language {@link #DEFAULT_LANGUAGE}. */ public static final String DEFAULT_LANGUAGE_ISO_639_CODE = ISO_639_LANGUAGE_CODES[DEFAULT_LANGUAGE.intValue()]; /** * A hashtable that maps from ISO 639 country codes to Integer * objects with the corresponding LANG_xyz constant for that language. */ private static Hashtable isoToConstant; static { isoToConstant = new Hashtable(ISO_639_LANGUAGE_CODES.length); for (int i = 0; i < ISO_639_LANGUAGE_CODES.length; i++) { isoToConstant.put(ISO_639_LANGUAGE_CODES[i], LANGUAGE_CONSTANTS[i]); } } private String[] data; private Integer language; /** * Create a new String object for the given language and fill it * with the String array. */ public Strings(Integer languageConstant, String[] stringValues) { set(languageConstant, stringValues); } /** * Determines an ISO 639 code of a language suitable for the environment * in which the JVM is currently running. * First calls {@link #determineIsoCodeFromDefaultLocale()}. * If that yields null, the ISO code for {@link #DEFAULT_LANGUAGE} is returned. * So different from {@link #determineIsoCodeFromDefaultLocale()} * this method always returns a non-null value. * @return String with ISO 639 code of a language that fits the JVM environment, * or the default language as fallback solution */ public static String determineSuitableIsoCode() { String code = determineIsoCodeFromDefaultLocale(); if (code != null && findLanguageCode(code) != null) { return code; } else { return ISO_639_LANGUAGE_CODES[DEFAULT_LANGUAGE.intValue()]; } } public static String determineIsoCodeFromDefaultLocale() { Locale locale = Locale.getDefault(); if (locale == null) { return null; } return locale.getLanguage(); } public static Integer findLanguageCode(String iso639LanguageCode) { if (iso639LanguageCode == null) { return null; } String code = iso639LanguageCode.toLowerCase(); return (Integer)isoToConstant.get(code); } /** * Gets the String denoted by the argument index. * This index must be one of the int constants defined in {@link StringIndexConstants}. * @return String with given index in the current language * @throws IllegalArgumentException is not a valid index from {@link StringIndexConstants} */ public String get(int index) { try { return data[index]; } catch (ArrayIndexOutOfBoundsException aioobe) { throw new IllegalArgumentException("Not a valid String index: " + index); } } /** * Returns the language of this object as one of the LANG_xyz * constants of this class. */ public Integer getLanguage() { return language; } public static String getFileName(int languageCode) { if (languageCode >= 0 && languageCode < ISO_639_LANGUAGE_CODES.length) { return ISO_639_LANGUAGE_CODES[languageCode] + ".txt"; } else { return null; } } public void set(Integer languageConstant, String[] values) { if (languageConstant == null || languageConstant.intValue() < 0 || languageConstant.intValue() >= ISO_639_LANGUAGE_CODES.length) { throw new IllegalArgumentException("Not a valid language constant: " + languageConstant); } if (values == null || values.length < 1) { throw new IllegalArgumentException("The values array argument must be non-null and have at least one element."); } language = languageConstant; data = values; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/OperationProcessor.java0000664000000000000000000004553610404072156025510 0ustar /* * OperationProcessor * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGB48Image; /** * Abstract base class for performing JIU operations in combination * with an {@link EditorState}. * @author Marco Schmidt */ public abstract class OperationProcessor implements MenuIndexConstants { private EditorState state; /** * Create an object of this class, storing the state argument for later use. * @param editorState EditorState object to be used for processing */ public OperationProcessor(EditorState editorState) { state = editorState; } /** * Adjust the brightness of the current image. */ public abstract void colorAdjustBrightness(); /** * Adjust the contrast of the current image. */ public abstract void colorAdjustContrast(); /** * Adjust the gamma value of the current image. */ public abstract void colorAdjustGamma(); /** * Adjust hue, saturation and value of the current image. */ public abstract void colorAdjustHueSaturationValue(); /** * Count the number of colors used in the current image. */ public abstract void colorHistogramCountColorsUsed(); public abstract void colorHistogramEqualize(); public abstract void colorHistogramNormalize(); public abstract void colorHistogramTextureProperties(); public abstract void colorHistogramSaveHistogramAs(); public abstract void colorHistogramSaveCoOccurrenceMatrixAs(); public abstract void colorHistogramSaveCoOccurrenceFrequencyMatrixAs(); public abstract void colorPaletteSaveAs(); public abstract void colorPromotePromoteToPaletted(); public abstract void colorPromotePromoteToGray8(); public abstract void colorPromotePromoteToGray16(); public abstract void colorPromotePromoteToRgb24(); public abstract void colorPromotePromoteToRgb48(); public abstract void colorReduceReduceNumberOfShadesOfGray(); public abstract void colorReduceConvertToGrayscale(); public abstract void colorReduceMedianCut(); public abstract void colorReduceOctree(); public abstract void colorReduceReduceToBilevelThreshold(); public abstract void colorReduceUniformPalette(); public abstract void colorReduceMapToArbitraryPalette(); public abstract void colorConvertToMinimumColorType(); public abstract void colorInvert(); public abstract void editRedo(); public abstract void editUndo(); public abstract void filtersBlur(); public abstract void filtersSharpen(); public abstract void filtersEdgeDetection(); public abstract void filtersEmboss(); public abstract void filtersPsychedelicDistillation(); public abstract void filtersLithograph(); public abstract void filtersHorizontalSobel(); public abstract void filtersVerticalSobel(); public abstract void filtersHorizontalPrewitt(); public abstract void filtersVerticalPrewitt(); public abstract void filtersMaximum(); public abstract void filtersMedian(); public abstract void filtersMean(); public abstract void filtersMinimum(); public abstract void filtersOil(); public abstract void transformationsFlip(); public abstract void transformationsMirror(); public abstract void transformationsRotate90Left(); public abstract void transformationsRotate90Right(); public abstract void transformationsRotate180(); public abstract void transformationsCrop(); public abstract void transformationsShear(); public abstract void transformationsScale(); public abstract void viewInterpolationTypeBicubic(); public abstract void viewInterpolationTypeBilinear(); public abstract void viewInterpolationTypeNearestNeighbor(); public abstract void viewZoomIn(); public abstract void viewZoomOut(); public abstract void viewSetOriginalSize(); /** * If there is an image loaded in the application, remove the image. */ public abstract void fileClose(); /** * Terminate the application. * If changes were not saved, the user should be asked whether these changes * should be discarded. */ public abstract void fileExit(); /** * Load an image in the application. */ public abstract void fileOpen(String uri); /** * Save the current image as a Windows BMP file. */ public abstract void fileSaveAsBmp(); /** * Save the current image as a GIF file. */ public abstract void fileSaveAsGif(); /** * Save the current image as a Palm image file. */ public abstract void fileSaveAsPalm(); /** * Save the current image as a Portable Bitmap file. */ public abstract void fileSaveAsPbm(); /** * Save the current image as a Portable Graymap file. */ public abstract void fileSaveAsPgm(); /** * Save the current image as a Portable Network Graphics file. */ public abstract void fileSaveAsPng(); /** * Save the current image as a Portable Pixmap file. */ public abstract void fileSaveAsPpm(); /** * Save the current image as a Sun Raster file. */ public abstract void fileSaveAsRas(); /** * Returns the EditorState object given to this object's constructor. * @return EditorState object used by this processor */ public EditorState getEditorState() { return state; } /** * Display information about the application: * name, version, feedback email address, website. */ public abstract void helpAbout(); /** * Display information on the system this application * is currently running on. */ public abstract void helpSystemInformation(); /** * Returns if the operation given by the menu index (from {@link MenuIndexConstants} * is available regarding the current editor state. * This method is used to update the enabled status of menu items so that * they reflect what can be done in the current state of an application. * Thus, things that cannot be done cannot be chosen in the menu * because they are disabled. * Example: the File | Save as... items are disabled as long as there is no image loaded, * simply because there is nothing to save. * @param menuIndex index of menu item to be checked * @return whether the operation is available (if true, menu item * should be enabled) */ public boolean isAvailable(int menuIndex) { boolean hasImage = state.hasImage(); PixelImage image = state.getImage(); boolean bilevel = hasImage && image instanceof BilevelImage; boolean gray8 = hasImage && image instanceof Gray8Image; boolean gray16 = hasImage && image instanceof Gray16Image; //boolean gray = gray8 || gray16; boolean pal8 = hasImage && image instanceof Paletted8Image; boolean rgb24 = hasImage && image instanceof RGB24Image; boolean rgb48 = hasImage && image instanceof RGB48Image; boolean rgb = rgb24 || rgb48; switch(menuIndex) { case(FILE): { return true; } case(FILE_OPEN): { return true; } case(FILE_CLOSE): { return hasImage; } case(FILE_SAVEAS): { return hasImage; } case(FILE_SAVEAS_GIF): { return bilevel || gray8 || pal8; } case(FILE_SAVEAS_PALM): { return bilevel || gray8 || pal8 || rgb24; } case(FILE_SAVEAS_PBM): { return bilevel; } case(FILE_SAVEAS_PGM): { return gray8 || gray16; } case(FILE_SAVEAS_PNG): { return hasImage; } case(FILE_SAVEAS_PPM): { return rgb24 || rgb48; } case(FILE_SAVEAS_SUNRASTER): { return pal8; } case(FILE_SAVEAS_WINDOWSBMP): { return bilevel || gray8 || pal8 || rgb24; } case(FILE_IMAGE_1): { return true; } case(FILE_EXIT): { return true; } case(EDIT): { return state.canUndo() || state.canRedo(); } case(EDIT_UNDO): { return state.canUndo(); } case(EDIT_REDO): { return state.canRedo(); } case(COLOR): { return hasImage; } case(COLOR_ADJUST): { return !bilevel; } case(COLOR_ADJUST_BRIGHTNESS): { return gray8 || gray16 || pal8 || rgb24 || rgb48; } case(COLOR_ADJUST_CONTRAST): { return gray8 || gray16 || pal8 || rgb24 || rgb48; } case(COLOR_ADJUST_GAMMA): { return gray8 || gray16 || pal8 || rgb24 || rgb48; } case(COLOR_HISTOGRAM): { return hasImage; } case(COLOR_HISTOGRAM_COUNTCOLORSUSED): { return hasImage; } case(COLOR_HISTOGRAM_EQUALIZE): { return gray8 || pal8 || rgb24 || rgb48; } case(COLOR_HISTOGRAM_NORMALIZE): { return gray8 || pal8 || rgb24 || rgb48; } case(COLOR_HISTOGRAM_TEXTUREPROPERTIES): { return gray8; } case(COLOR_HISTOGRAM_SAVEHISTOGRAMAS): { return hasImage; } case(COLOR_HISTOGRAM_SAVECOOCCURRENCEMATRIXAS): { return pal8 || gray8 || bilevel; } case(COLOR_HISTOGRAM_SAVECOOCCURRENCEFREQUENCYMATRIXAS): { return pal8 || gray8 || bilevel; } case(COLOR_PALETTE): { return pal8; } case(COLOR_PALETTE_SAVEAS): { return pal8; } case(COLOR_PROMOTE): { return !rgb48; } case(COLOR_PROMOTE_PROMOTETOGRAY8): { return bilevel; } case(COLOR_PROMOTE_PROMOTETOGRAY16): { return bilevel || gray8; } case(COLOR_PROMOTE_PROMOTETORGB24): { return bilevel || gray8 || pal8; } case(COLOR_PROMOTE_PROMOTETORGB48): { return bilevel || gray8 || gray16 || pal8 || rgb24; } case(COLOR_PROMOTE_PROMOTETOPALETTED): { return bilevel || gray8; } case(COLOR_ADJUST_HUESATURATIONVALUE): { return pal8 || rgb24; } case(COLOR_REDUCE): { return !bilevel; } case(COLOR_REDUCE_CONVERTTOGRAYSCALE): { return pal8 || rgb; } case(COLOR_REDUCE_REDUCENUMBEROFSHADESOFGRAY): { return gray8 || gray16; } case(COLOR_REDUCE_REDUCETOBILEVELTHRESHOLD): { return gray8 || gray16; } case(COLOR_REDUCE_MEDIANCUT): { return rgb24; } case(COLOR_REDUCE_OCTREE): { return rgb24; } case(COLOR_REDUCE_UNIFORMPALETTE): { return rgb24; } case(COLOR_REDUCE_MAPTOARBITRARYPALETTE): { return rgb24; } case(COLOR_INVERT): { return hasImage; } case(COLOR_CONVERTTOMINIMUMCOLORTYPE): { return !bilevel; } case(TRANSFORMATIONS): { return hasImage; } case(TRANSFORMATIONS_FLIP): { return hasImage; } case(TRANSFORMATIONS_MIRROR): { return hasImage; } case(TRANSFORMATIONS_ROTATELEFT90): { return hasImage; } case(TRANSFORMATIONS_ROTATERIGHT90): { return hasImage; } case(TRANSFORMATIONS_ROTATE180): { return hasImage; } case(TRANSFORMATIONS_SHEAR): { return hasImage; } case(TRANSFORMATIONS_SCALE): { return hasImage; } case(TRANSFORMATIONS_CROP): { return hasImage; } case(FILTERS): { return hasImage && !pal8; } case(FILTERS_BLUR): case(FILTERS_SHARPEN): case(FILTERS_EDGEDETECTION): case(FILTERS_EMBOSS): case(FILTERS_PSYCHEDELICDISTILLATION): case(FILTERS_LITHOGRAPH): case(FILTERS_HORIZONTALSOBEL): case(FILTERS_VERTICALSOBEL): case(FILTERS_HORIZONTALPREWITT): case(FILTERS_VERTICALPREWITT): case(FILTERS_MEAN): case(FILTERS_OIL): { return gray16 || gray8 || rgb24 || rgb48; } case(FILTERS_MAXIMUM): case(FILTERS_MINIMUM): case(FILTERS_MEDIAN): { return gray16 || gray8 || rgb24 || bilevel || rgb48; } case(VIEW): case(VIEW_INTERPOLATIONTYPE): case(VIEW_INTERPOLATIONTYPE_NEARESTNEIGHBOR): case(VIEW_INTERPOLATIONTYPE_BILINEAR): case(VIEW_INTERPOLATIONTYPE_BICUBIC): { return hasImage; } case(VIEW_SETORIGINALSIZE): { return hasImage && !state.isZoomOriginalSize(); } case(VIEW_ZOOMIN): { return hasImage && !state.isMaximumZoom(); } case(VIEW_ZOOMOUT): { return hasImage && !state.isMinimumZoom(); } case(HELP): { return true; } case(HELP_ABOUT): { return true; } case(HELP_SYSTEMINFORMATION): { return true; } default: { throw new IllegalArgumentException("Not a valid menu index: " + menuIndex); } } } public void process(int menuIndex) { switch (menuIndex) { case(FILE_OPEN): { fileOpen(null); break; } case(FILE_SAVEAS_GIF): { fileSaveAsGif(); break; } case(FILE_SAVEAS_PALM): { fileSaveAsPalm(); break; } case(FILE_SAVEAS_PBM): { fileSaveAsPbm(); break; } case(FILE_SAVEAS_PGM): { fileSaveAsPgm(); break; } case(FILE_SAVEAS_PNG): { fileSaveAsPng(); break; } case(FILE_SAVEAS_PPM): { fileSaveAsPpm(); break; } case(FILE_SAVEAS_SUNRASTER): { fileSaveAsRas(); break; } case(FILE_SAVEAS_WINDOWSBMP): { fileSaveAsBmp(); break; } case(FILE_IMAGE_1): { fileOpen("/resources/images/image1.jpg"); break; } case(FILE_CLOSE): { fileClose(); break; } case(FILE_EXIT): { fileExit(); break; } case(EDIT_UNDO): { editUndo(); break; } case(EDIT_REDO): { editRedo(); break; } case(COLOR_ADJUST_BRIGHTNESS): { colorAdjustBrightness(); break; } case(COLOR_ADJUST_CONTRAST): { colorAdjustContrast(); break; } case(COLOR_ADJUST_GAMMA): { colorAdjustGamma(); break; } case(COLOR_ADJUST_HUESATURATIONVALUE): { colorAdjustHueSaturationValue(); break; } case(COLOR_HISTOGRAM_COUNTCOLORSUSED): { colorHistogramCountColorsUsed(); break; } case(COLOR_HISTOGRAM_EQUALIZE): { colorHistogramEqualize(); break; } case(COLOR_HISTOGRAM_NORMALIZE): { colorHistogramNormalize(); break; } case(COLOR_HISTOGRAM_TEXTUREPROPERTIES): { colorHistogramTextureProperties(); break; } case(COLOR_HISTOGRAM_SAVEHISTOGRAMAS): { colorHistogramSaveHistogramAs(); break; } case(COLOR_HISTOGRAM_SAVECOOCCURRENCEMATRIXAS): { colorHistogramSaveCoOccurrenceMatrixAs(); break; } case(COLOR_HISTOGRAM_SAVECOOCCURRENCEFREQUENCYMATRIXAS): { colorHistogramSaveCoOccurrenceFrequencyMatrixAs(); break; } case(COLOR_PALETTE_SAVEAS): { colorPaletteSaveAs(); break; } case(COLOR_PROMOTE_PROMOTETOPALETTED): { colorPromotePromoteToPaletted(); break; } case(COLOR_PROMOTE_PROMOTETOGRAY8): { colorPromotePromoteToGray8(); break; } case(COLOR_PROMOTE_PROMOTETOGRAY16): { colorPromotePromoteToGray16(); break; } case(COLOR_PROMOTE_PROMOTETORGB24): { colorPromotePromoteToRgb24(); break; } case(COLOR_PROMOTE_PROMOTETORGB48): { colorPromotePromoteToRgb48(); break; } case(COLOR_REDUCE_REDUCETOBILEVELTHRESHOLD): { colorReduceReduceToBilevelThreshold(); break; } case(COLOR_REDUCE_REDUCENUMBEROFSHADESOFGRAY): { colorReduceReduceNumberOfShadesOfGray(); break; } case(COLOR_REDUCE_CONVERTTOGRAYSCALE): { colorReduceConvertToGrayscale(); break; } case(COLOR_REDUCE_MEDIANCUT): { colorReduceMedianCut(); break; } case(COLOR_REDUCE_OCTREE): { colorReduceOctree(); break; } case(COLOR_REDUCE_UNIFORMPALETTE): { colorReduceUniformPalette(); break; } case(COLOR_REDUCE_MAPTOARBITRARYPALETTE): { colorReduceMapToArbitraryPalette(); break; } case(COLOR_INVERT): { colorInvert(); break; } case(COLOR_CONVERTTOMINIMUMCOLORTYPE): { colorConvertToMinimumColorType(); break; } case(TRANSFORMATIONS_FLIP): { transformationsFlip(); break; } case(TRANSFORMATIONS_MIRROR): { transformationsMirror(); break; } case(TRANSFORMATIONS_ROTATELEFT90): { transformationsRotate90Left(); break; } case(TRANSFORMATIONS_ROTATERIGHT90): { transformationsRotate90Right(); break; } case(TRANSFORMATIONS_ROTATE180): { transformationsRotate180(); break; } case(TRANSFORMATIONS_CROP): { transformationsCrop(); break; } case(TRANSFORMATIONS_SCALE): { transformationsScale(); break; } case(TRANSFORMATIONS_SHEAR): { transformationsShear(); break; } case(FILTERS_BLUR): { filtersBlur(); break; } case(FILTERS_SHARPEN): { filtersSharpen(); break; } case(FILTERS_EDGEDETECTION): { filtersEdgeDetection(); break; } case(FILTERS_EMBOSS): { filtersEmboss(); break; } case(FILTERS_PSYCHEDELICDISTILLATION): { filtersPsychedelicDistillation(); break; } case(FILTERS_LITHOGRAPH): { filtersLithograph(); break; } case(FILTERS_HORIZONTALSOBEL): { filtersHorizontalSobel(); break; } case(FILTERS_VERTICALSOBEL): { filtersVerticalSobel(); break; } case(FILTERS_HORIZONTALPREWITT): { filtersHorizontalPrewitt(); break; } case(FILTERS_VERTICALPREWITT): { filtersVerticalPrewitt(); break; } case(FILTERS_MINIMUM): { filtersMinimum(); break; } case(FILTERS_MAXIMUM): { filtersMaximum(); break; } case(FILTERS_MEDIAN): { filtersMedian(); break; } case(FILTERS_MEAN): { filtersMean(); break; } case(FILTERS_OIL): { filtersOil(); break; } case(VIEW_ZOOMIN): { viewZoomIn(); break; } case(VIEW_ZOOMOUT): { viewZoomOut(); break; } case(VIEW_SETORIGINALSIZE): { viewSetOriginalSize(); break; } case(VIEW_INTERPOLATIONTYPE_NEARESTNEIGHBOR): { viewInterpolationTypeNearestNeighbor(); break; } case(VIEW_INTERPOLATIONTYPE_BILINEAR): { viewInterpolationTypeBilinear(); break; } case(VIEW_INTERPOLATIONTYPE_BICUBIC): { viewInterpolationTypeBicubic(); break; } case(HELP_ABOUT): { helpAbout(); break; } case(HELP_SYSTEMINFORMATION): { helpSystemInformation(); break; } default: { // error? break; } } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/MenuIndexConstants.java0000664000000000000000000000624410404065314025430 0ustar /* * MenuIndexConstants * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; /** * Constant int values for all menu items. * Some of these menu items denote operations which can be performed by * {@link OperationProcessor}. * * @author Marco Schmidt */ public interface MenuIndexConstants { int FILE = 0; int FILE_OPEN = 1; int FILE_SAVEAS = 2; int FILE_SAVEAS_GIF = 83; int FILE_SAVEAS_PALM = 81; int FILE_SAVEAS_PBM = 3; int FILE_SAVEAS_PGM = 4; int FILE_SAVEAS_PNG = 82; int FILE_SAVEAS_PPM = 5; int FILE_SAVEAS_SUNRASTER = 6; int FILE_SAVEAS_WINDOWSBMP = 7; int FILE_IMAGE_1 = 84; int FILE_CLOSE = 8; int FILE_EXIT = 9; int EDIT = 67; int EDIT_UNDO = 68; int EDIT_REDO = 69; int COLOR = 10; int COLOR_ADJUST = 11; int COLOR_ADJUST_BRIGHTNESS = 12; int COLOR_ADJUST_CONTRAST = 13; int COLOR_ADJUST_GAMMA = 14; int COLOR_ADJUST_HUESATURATIONVALUE = 15; int COLOR_HISTOGRAM = 16; int COLOR_HISTOGRAM_COUNTCOLORSUSED = 17; int COLOR_HISTOGRAM_EQUALIZE = 18; int COLOR_HISTOGRAM_NORMALIZE = 19; int COLOR_HISTOGRAM_TEXTUREPROPERTIES = 70; int COLOR_HISTOGRAM_SAVEHISTOGRAMAS = 66; int COLOR_HISTOGRAM_SAVECOOCCURRENCEMATRIXAS = 20; int COLOR_HISTOGRAM_SAVECOOCCURRENCEFREQUENCYMATRIXAS = 21; int COLOR_PALETTE = 22; int COLOR_PALETTE_SAVEAS = 23; int COLOR_PROMOTE = 24; int COLOR_PROMOTE_PROMOTETOPALETTED = 25; int COLOR_PROMOTE_PROMOTETOGRAY8 = 26; int COLOR_PROMOTE_PROMOTETOGRAY16 = 27; int COLOR_PROMOTE_PROMOTETORGB24 = 28; int COLOR_PROMOTE_PROMOTETORGB48 = 29; int COLOR_REDUCE = 30; int COLOR_REDUCE_REDUCETOBILEVELTHRESHOLD = 31; int COLOR_REDUCE_REDUCENUMBEROFSHADESOFGRAY = 32; int COLOR_REDUCE_CONVERTTOGRAYSCALE = 33; int COLOR_REDUCE_MEDIANCUT = 34; int COLOR_REDUCE_OCTREE = 35; int COLOR_REDUCE_UNIFORMPALETTE = 36; int COLOR_REDUCE_MAPTOARBITRARYPALETTE = 37; int COLOR_INVERT = 38; int COLOR_CONVERTTOMINIMUMCOLORTYPE = 39; int TRANSFORMATIONS = 40; int TRANSFORMATIONS_FLIP = 41; int TRANSFORMATIONS_MIRROR = 42; int TRANSFORMATIONS_ROTATELEFT90 = 43; int TRANSFORMATIONS_ROTATERIGHT90 = 44; int TRANSFORMATIONS_ROTATE180 = 45; int TRANSFORMATIONS_CROP = 46; int TRANSFORMATIONS_SCALE = 47; int TRANSFORMATIONS_SHEAR = 48; int FILTERS = 49; int FILTERS_BLUR = 50; int FILTERS_SHARPEN = 51; int FILTERS_EDGEDETECTION = 52; int FILTERS_EMBOSS = 53; int FILTERS_PSYCHEDELICDISTILLATION = 54; int FILTERS_LITHOGRAPH = 55; int FILTERS_HORIZONTALSOBEL = 56; int FILTERS_VERTICALSOBEL = 57; int FILTERS_HORIZONTALPREWITT = 58; int FILTERS_VERTICALPREWITT = 59; int FILTERS_MINIMUM = 71; int FILTERS_MAXIMUM = 72; int FILTERS_MEDIAN = 60; int FILTERS_MEAN = 61; int FILTERS_OIL = 62; int VIEW = 73; int VIEW_ZOOMIN = 74; int VIEW_ZOOMOUT = 75; int VIEW_SETORIGINALSIZE = 76; int VIEW_INTERPOLATIONTYPE = 77; int VIEW_INTERPOLATIONTYPE_NEARESTNEIGHBOR = 78; int VIEW_INTERPOLATIONTYPE_BILINEAR = 79; int VIEW_INTERPOLATIONTYPE_BICUBIC = 80; int HELP = 63; int HELP_ABOUT = 64; int HELP_SYSTEMINFORMATION = 65; int NUM_CONSTANTS = 85; } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/EditorState.java0000664000000000000000000003234607741250130024072 0ustar /* * EditorState * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.io.IOException; import java.util.Locale; import java.util.Vector; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.Operation; import net.sourceforge.jiu.ops.ProgressListener; /** * Represents the state of the editor, including image(s), modified flag, * current file name and directories and more. * This class must not know GUI-specific information like Frame or JFrame objects. * These GUI classes (more precisely, the JIU classes that extend them) will have to * know EditorState and update according to the information they retrieve from an * EditorState object associated with them. * EditorState is a pure data container. * @author Marco Schmidt */ public class EditorState implements MenuIndexConstants { /** * The default number of undo steps possible. */ public static final int DEFAULT_MAX_UNDO_IMAGES = 2; /** * The default number of redo steps possible. */ public static final int DEFAULT_MAX_REDO_IMAGES = DEFAULT_MAX_UNDO_IMAGES; /** * All allowed zoom levels, as percentage values in ascending order. */ public static final int[] ZOOM_LEVELS = {5, 7, 10, 15, 20, 30, 50, 70, 100, 150, 200, 300, 500, 700, 1000, 2000, 3000, 5000}; /** * The index into the {@link #ZOOM_LEVELS} array that holds the original size zoom level (100 percent). * So, ZOOM_LEVELS[ORIGINAL_SIZE_ZOOM_INDEX] must be equal to 100. */ public static final int ORIGINAL_SIZE_ZOOM_INDEX = 8; /** * Integer constant for nearest neighbor interpolation. * A fast but ugly method. */ public static final int INTERPOLATION_NEAREST_NEIGHBOR = 0; /** * Integer constant for bilinear neighbor interpolation. * A slow but nice method. */ public static final int INTERPOLATION_BILINEAR = 1; /** * Integer constant for bicubic interpolation. * A very slow method, but with the nicest output of the three supported interpolation types. */ public static final int INTERPOLATION_BICUBIC = 2; /** * The default interpolation type, one of the three INTERPOLATION_xyz constants. */ public static final int DEFAULT_INTERPOLATION = INTERPOLATION_NEAREST_NEIGHBOR; private String currentDirectory; private String fileName; private PixelImage currentImage; private int interpolation; private Locale locale; private int maxRedoImages; private int maxUndoImages; private boolean modified; private Vector progressListeners; private Vector redoImages; private Vector redoModified; private String startupImageName; private Strings strings; private Vector undoImages; private Vector undoModified; private int zoomIndex = ORIGINAL_SIZE_ZOOM_INDEX; private double zoomFactorX; private double zoomFactorY; private boolean zoomToFit; /** * Create new EditorState object and initialize its private fields * to default values. */ public EditorState() { locale = Locale.getDefault(); setStrings(null); progressListeners = new Vector(); maxRedoImages = DEFAULT_MAX_REDO_IMAGES; maxUndoImages = DEFAULT_MAX_UNDO_IMAGES; redoImages = new Vector(maxRedoImages); redoModified = new Vector(maxRedoImages); undoImages = new Vector(maxUndoImages); undoModified = new Vector(maxUndoImages); zoomFactorX = 1.0; zoomFactorY = 1.0; zoomToFit = false; } private void addImageToRedo(PixelImage image, boolean modifiedState) { if (maxRedoImages < 1) { return; } if (redoImages.size() == maxRedoImages) { redoImages.setElementAt(null, 0); redoImages.removeElementAt(0); redoModified.removeElementAt(0); } redoImages.addElement(image); redoModified.addElement(new Boolean(modifiedState)); } private void addImageToUndo(PixelImage image, boolean modifiedState) { if (maxUndoImages < 1) { return; } if (undoImages.size() == maxUndoImages) { undoImages.setElementAt(null, 0); undoImages.removeElementAt(0); undoModified.removeElementAt(0); } undoImages.addElement(image); undoModified.addElement(new Boolean(modifiedState)); } /** * Adds the argument progress listener to the internal list of progress * listeners to be notified by progress updates. * @param pl object implementing ProgressListener to be added */ public void addProgressListener(ProgressListener pl) { progressListeners.addElement(pl); } /** * Returns if a redo operation is possible right now. */ public boolean canRedo() { return (redoImages.size() > 0); } /** * Returns if an undo operation is possible right now. */ public boolean canUndo() { return (undoImages.size() > 0); } public void clearRedo() { int index = 0; while (index < redoImages.size()) { redoImages.setElementAt(null, index++); } redoImages.setSize(0); redoModified.setSize(0); } public void clearUndo() { int index = 0; while (index < undoImages.size()) { undoImages.setElementAt(null, index++); } undoImages.setSize(0); undoModified.setSize(0); } public void ensureStringsAvailable() { if (getStrings() == null) { setStrings(Strings.DEFAULT_LANGUAGE_ISO_639_CODE); } } /** * Returns the current directory. * This directory will be used when file dialogs are opened. */ public String getCurrentDirectory() { return currentDirectory; } /** * Returns the name of the file from which the current image was loaded. */ public String getFileName() { return fileName; } /** * Returns the image object currently loaded. */ public PixelImage getImage() { return currentImage; } /** * Returns the current interpolation type, one of the INTERPOLATION_xyz constants. */ public int getInterpolation() { return interpolation; } /** * Returns the Locale object currently used. */ public Locale getLocale() { return locale; } /** * Returns the current modified state (true if image was modified and not saved * after modification, false otherwise). */ public boolean getModified() { return modified; } /** * Returns the internal list of progress listeners. */ public Vector getProgressListeners() { return progressListeners; } public String getStartupImageName() { return startupImageName; } /** * Returns the Strings object currently in use. */ public Strings getStrings() { return strings; } /** * Returns the current zoom factor in horizontal direction. * The value 1.0 means that the image is displayed at its * original size. * Anything smaller means that the image is scaled down, * anything larger means that the image is scaled up. * The value must not be smaller than or equal to 0.0. * @return zoom factor in horizontal direction * @see #getZoomFactorY */ public double getZoomFactorX() { return zoomFactorX; } /** * Returns the current zoom factor in vertical direction. * The value 1.0 means that the image is displayed at its * original size. * Anything smaller means that the image is scaled down, * anything larger means that the image is scaled up. * The value must not be smaller than or equal to 0.0. * @return zoom factor in vertical direction * @see #getZoomFactorX */ public double getZoomFactorY() { return zoomFactorY; } /** * Returns if image display is currently set to "zoom to fit" * Zoom to fit means that the image is always zoomed to fit exactly into the window. */ public boolean getZoomToFit() { return zoomToFit; } /** * Returns if this state encapsulates an image object. */ public boolean hasImage() { return (currentImage != null); } /** * Adds all ProgressListener objects from the internal list of listeners to * the argument operation. */ public void installProgressListeners(Operation op) { if (op == null) { return; } // cannot use Iterator because it's 1.2+ int index = 0; while (index < progressListeners.size()) { ProgressListener pl = (ProgressListener)progressListeners.elementAt(index++); op.addProgressListener(pl); } } /** * Returns if the image is displayed at maximum zoom level. */ public boolean isMaximumZoom() { return zoomIndex == ZOOM_LEVELS.length - 1; } /** * Returns if the image is displayed at minimum zoom level. */ public boolean isMinimumZoom() { return zoomIndex == 0; } /** * Returns if the current zoom level is set to original size * (each image pixel is displayed as one pixel). */ public boolean isZoomOriginalSize() { return zoomIndex == ORIGINAL_SIZE_ZOOM_INDEX; } /** * Perform a redo operation, restore the state before the last undo operation. * Before that is done, save the current state for an undo. */ public void redo() { if (redoImages.size() < 1) { return; } addImageToUndo(currentImage, modified); int redoIndex = redoImages.size() - 1; currentImage = (PixelImage)redoImages.elementAt(redoIndex); redoImages.setElementAt(null, redoIndex); redoImages.setSize(redoIndex); modified = ((Boolean)redoModified.elementAt(redoIndex)).booleanValue(); redoModified.setSize(redoIndex); } public void resetZoomFactors() { setZoomFactors(1.0, 1.0); } /** * Sets a new current directory. * @param newCurrentDirectory the directory to be used as current directory from now on */ public void setCurrentDirectory(String newCurrentDirectory) { currentDirectory = newCurrentDirectory; } /** * Sets a new file name. * This is used mostly after a new image was loaded from a file or * if the current image is closed (then a null value would be given to this method). * @param newFileName new name of the current file */ public void setFileName(String newFileName) { fileName = newFileName; } /** * Sets image and modified state to argument values. * @param image new current image * @param newModifiedState new state of modified flag */ public void setImage(PixelImage image, boolean newModifiedState) { if (hasImage()) { addImageToUndo(currentImage, modified); } currentImage = image; modified = newModifiedState; clearRedo(); } public void setStartupImageName(String name) { startupImageName = name; } /** * Sets a new interpolation type to be used for display. * @param newInterpolation an int for the interpolation type, must be one of the INTERPOLATION_xyz constants */ public void setInterpolation(int newInterpolation) { if (newInterpolation == INTERPOLATION_NEAREST_NEIGHBOR || newInterpolation == INTERPOLATION_BILINEAR || newInterpolation == INTERPOLATION_BICUBIC) { interpolation = newInterpolation; } } /** * Defines a new Locale to be used. * @param newLocale Locale object used from now on * @see #setStrings */ public void setLocale(Locale newLocale) { locale = newLocale; } /*public void setModified(boolean modifiedState) { modified = modifiedState; }*/ /** * Set new Strings resource. * @param iso639Code language of the new Strings resource */ public void setStrings(String iso639Code) { Strings newStrings = null; try { StringLoader loader; if (iso639Code == null) { loader = new StringLoader(); } else { loader = new StringLoader(iso639Code); } newStrings = loader.load(); } catch (IOException ioe) { } if (newStrings != null) { strings = newStrings; } } /** * Sets the zoom factors to the argument values. */ public void setZoomFactors(double zoomX, double zoomY) { zoomFactorX = zoomX; zoomFactorY = zoomY; } /** * Perform an undo step - the previous state will be set, the * current state will be saved for a redo operation * @see #redo */ public void undo() { if (undoImages.size() < 1) { return; } addImageToRedo(currentImage, modified); int undoIndex = undoImages.size() - 1; currentImage = (PixelImage)undoImages.elementAt(undoIndex); undoImages.setElementAt(null, undoIndex); undoImages.setSize(undoIndex); modified = ((Boolean)undoModified.elementAt(undoIndex)).booleanValue(); undoModified.setSize(undoIndex); } /** * Increase the zoom level by one. * @see #zoomOut * @see #zoomSetOriginalSize */ public void zoomIn() { if (zoomIndex + 1 == ZOOM_LEVELS.length) { return; } zoomIndex++; zoomFactorX = 1.0 * ZOOM_LEVELS[zoomIndex] / 100; zoomFactorY = zoomFactorX; } /** * Decrease the zoom level by one. * @see #zoomIn * @see #zoomSetOriginalSize */ public void zoomOut() { if (zoomIndex == 0) { return; } zoomIndex--; zoomFactorX = 1.0 * ZOOM_LEVELS[zoomIndex] / 100; zoomFactorY = zoomFactorX; } /** * Set the zoom level to 100 percent (1:1). * Each image pixel will be displayed as one pixel * @see #zoomIn * @see #zoomOut */ public void zoomSetOriginalSize() { zoomIndex = ORIGINAL_SIZE_ZOOM_INDEX; zoomFactorX = 1.0; zoomFactorY = 1.0; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/JiuBlur.java0000664000000000000000000000215610612170500023204 0ustar /* * JiuBlur * * Copyright (c) 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import net.sourceforge.jiu.color.adjustment.Contrast; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.filters.ConvolutionKernelFilter; import net.sourceforge.jiu.gui.awt.ImageCreator; import net.sourceforge.jiu.gui.awt.ToolkitLoader; /** * Small example command line program which loads a JPEG file, * applies a blur filter, increases its contrast and saves it * back to another JPEG file. * @author Marco Schmidt * @since 0.14.2 */ public class JiuBlur { public static void main(String[] args) throws Exception { PixelImage image = ToolkitLoader.loadAsRgb24Image("resources/images/image1.jpg"); image = Contrast.adjust(image, 20); image = ConvolutionKernelFilter.filter(image, ConvolutionKernelFilter.TYPE_BLUR); BufferedImage awtImage = ImageCreator.convertToAwtBufferedImage(image); ImageIO.write(awtImage, "jpg", new File("out-image1.jpg")); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/JiuHelloWorld.java0000664000000000000000000000275110611710711024357 0ustar /* * JiuHelloWorld * * Copyright (c) 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import net.sourceforge.jiu.codecs.CodecMode; import net.sourceforge.jiu.codecs.PNGCodec; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.gui.awt.ImageCreator; /** * Small example command line program which creates a new image, * prints the text Hello World! into it and saves it as * a PNG file. * @author Marco Schmidt * @since 0.14.2 */ public class JiuHelloWorld { public static void main(String[] args) throws Exception { // AWT image creation BufferedImage awtImage = new BufferedImage(100, 30, BufferedImage.TYPE_INT_RGB); Graphics graphics = awtImage.getGraphics(); // fill image with red graphics.setColor(Color.RED); graphics.fillRect(0, 0, awtImage.getWidth(), awtImage.getHeight()); // draw on it in white graphics.setColor(Color.WHITE); graphics.drawString("Hello World!", 5, 15); // conversion AWT image to JIU image RGB24Image jiuImage = ImageCreator.convertImageToRGB24Image(awtImage); // saving JIU image to file PNGCodec codec = new PNGCodec(); codec.setImage(jiuImage); codec.appendComment("Hello World! as a text comment; " + "see http://schmidt.devlib.org/jiu/introduction.html"); codec.setFile("jiu-hello-world.png", CodecMode.SAVE); codec.process(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/StringLoader.java0000664000000000000000000000322010212515720024221 0ustar /* * StringLoader * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Vector; /** * This class loads a {@link Strings} resource from a text file. * The text file must contain one * @author Marco Schmidt * @since 0.12.0 */ public class StringLoader { private BufferedReader in; private Integer langCode; /** * The directory of language resource files, default: * /resources/lang/. */ public static String resourceDirectory = "/resources/lang/"; public StringLoader() { this(Strings.determineSuitableIsoCode()); } public StringLoader(String iso639Code) { InputStream input = getClass().getResourceAsStream(resourceDirectory + iso639Code + ".txt"); if (input == null && !"en".equals(iso639Code)) { input = getClass().getResourceAsStream(resourceDirectory + "en.txt"); } if (input == null) { return; } in = new BufferedReader(new InputStreamReader(input)); langCode = Strings.findLanguageCode(iso639Code); } public Strings load() throws IOException { if (in == null) { return null; } Vector list = new Vector(); String line; while ((line = in.readLine()) != null) { list.addElement(line); } if (list.size() < 1) { return null; } String[] data = new String[list.size()]; for (int i = 0; i < list.size(); i++) { data[i] = (String)list.elementAt(i); } in.close(); return new Strings(langCode, data); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/jiuawtapplet.java0000664000000000000000000000161510421163206024343 0ustar /* * jiuawtapplet * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.applet.Applet; import net.sourceforge.jiu.gui.awt.JiuAwtFrame; /** * Applet version of jiuawt. * Not really useful because no images can be loaded. * @author Marco Schmidt */ public class jiuawtapplet extends Applet implements JiuInfo { private static final long serialVersionUID = 93423883004L; public String getAppletInfo() { return "jiuawtapplet" + "; demo applet for the Java Imaging Utilities" + "; feedback address: " + JIU_FEEDBACK_ADDRESS + "; homepage: " + JIU_HOMEPAGE; } public void init() { EditorState state = new EditorState(); state.setStrings(null); new JiuAwtFrame(state); } public void start() { } public void stop() { System.exit(0); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/package.html0000664000000000000000000000035307741250130023252 0ustar

Smaller and larger applications demonstrating how to use JIU.

java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/ImageDescriptionCreator.java0000664000000000000000000000647310523755041026416 0ustar /* * ImageDescriptionCreator * * Copyright (c) 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.text.NumberFormat; import java.util.Locale; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.GrayImage; import net.sourceforge.jiu.data.PalettedImage; import net.sourceforge.jiu.data.RGBImage; /** * Returns textual descriptions of the properties of JIU image objects. * @author Marco Schmidt * @since 0.9.0 */ public class ImageDescriptionCreator { /*private static final int TYPE = 0; private static final int PIXELS = 1; private static final int IMAGE_TYPE_BILEVEL = 2; private static final int IMAGE_TYPE_GRAYSCALE = 3; private static final int IMAGE_TYPE_PALETTED = 4; private static final int IMAGE_TYPE_RGB_TRUECOLOR = 5; private static final int IMAGE_TYPE_UNKNOWN = 6; private static final int BITS_PER_PIXEL = 7; private static final int MEMORY = 8; private static final int DISK_SPACE = 9;*/ private ImageDescriptionCreator() { } private static String formatNumber(long value, Locale locale) { if (locale == null) { return Long.toString(value); } else { return NumberFormat.getInstance(locale).format(value); } } /** * Returns a description of the argument image using the default locale. */ /*public static String getDescription(PixelImage image) { return getDescription(image, Locale.getDefault()); }*/ /** * Returns a description of the argument image using the language * as specified by the argument locale's two-letter language code. * @param image the image for which a textual description is to be returned * @param locale the Locale storing the natural language to be used for formatting * @return a textual description of the image */ public static String getDescription(PixelImage image, Locale locale, Strings strings) { StringBuffer result = new StringBuffer(); result.append(strings.get(StringIndexConstants.IMAGE_TYPE)); result.append(": "); result.append(strings.get(getImageType(image))); result.append(", "); result.append(strings.get(StringIndexConstants.PIXELS)); result.append(": "); int width = image.getWidth(); int height = image.getHeight(); result.append(formatNumber(width, locale)); result.append(" x "); result.append(formatNumber(height, locale)); result.append(" ("); result.append(formatNumber(width * height, locale)); result.append("), "); result.append(strings.get(StringIndexConstants.BITS_PER_PIXEL)); result.append(": "); result.append(formatNumber(image.getBitsPerPixel(), locale)); return result.toString(); } private static int getImageType(PixelImage image) { int stringIndex = StringIndexConstants.IMAGE_TYPE_UNKNOWN; if (image instanceof BilevelImage) { return stringIndex = StringIndexConstants.BILEVEL; } else if (image instanceof GrayImage) { return stringIndex = StringIndexConstants.GRAYSCALE; } else if (image instanceof PalettedImage) { return stringIndex = StringIndexConstants.PALETTED; } else if (image instanceof RGBImage) { return stringIndex = StringIndexConstants.RGB_TRUECOLOR; } return stringIndex; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/ImageLoadTester.java0000664000000000000000000000740207741250130024647 0ustar /* * ImageLoadTester * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.io.File; import net.sourceforge.jiu.codecs.ImageLoader; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.gui.awt.ToolkitLoader; import net.sourceforge.jiu.ops.BatchProcessorOperation; /** * Command line program that tries to load images from files, * thus testing the built-in and / or Toolkit's codecs. * Start the program with file names or directory names as arguments. * Directory names will lead to the inclusion of that complete directory tree * in the list of files to test. * Add the argument --notoolkit in order to keep this * program from trying to load images via java.awt.Toolkit. * The program will process all files and print one line per file * that tells the result of the loading process. * At the end, three lines with statistics (failed / successful / total * loading attempts) are printed. * Note that you may want to give the JVM more memory if large images * are stored in those files you want to test. * Example: *
java -mx300m net.sourceforge.jiu.apps.ImageLoadTester *.jpg
* This gives 300 MB to the JVM. * @author Marco Schmidt * @since 0.11.0 */ public class ImageLoadTester extends BatchProcessorOperation { private boolean useToolkit; private int numFailed; private int numSuccess; /** * Main method of this command line program. * @param args program arguments, provided by the Java Virtual Machine, must be file or directory names */ public static void main(String[] args) throws Exception { ImageLoadTester tester = new ImageLoadTester(); boolean useToolkit = true; for (int i = 0; i < args.length; i++) { String name = args[i]; if ("--notoolkit".equals(name)) { useToolkit = false; } else { File file = new File(name); if (file.isFile()) { tester.addInputFileName(name); } else if (file.isDirectory()) { tester.addDirectoryTree(name); } } } tester.setUseToolkit(useToolkit); tester.process(); int total = (tester.numFailed + tester.numSuccess); System.out.println("OK: " + tester.numSuccess + " (" + tester.numSuccess * 100.0 / total + " %)"); System.out.println("Failed: " + tester.numFailed + " (" + tester.numFailed * 100.0 / total + " %)"); System.out.println("Total: " + total + " (100.0 %)"); } /** * Tries to load an image from a file. * Prints a message to standard output and increases certain internal counters * for statistics. * @param inputDirectory directory where the file resides * @param inputFileName name of file * @param outputDirectory not used, this argument is demanded by the parent class+ */ public void processFile(String inputDirectory, String inputFileName, String outputDirectory) { File file = new File(inputDirectory, inputFileName); String name = file.getAbsolutePath(); System.out.print(name); PixelImage image; try { if (useToolkit) { image = ToolkitLoader.loadViaToolkitOrCodecs(name, true, null); } else { image = ImageLoader.load(name); } } catch (Exception e) { image = null; } if (image == null) { numFailed++; System.out.println(" Failed."); } else { numSuccess++; System.out.println(" OK. Width=" + image.getWidth() + ", height=" + image.getHeight() + " pixels."); } } /** * Specifies whether java.awt.Toolkit is supposed to be used when * trying to load an image. * @param newValue boolean, true if Toolkit is to be used, false otherwise */ public void setUseToolkit(boolean newValue) { useToolkit = newValue; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/JiuCountColors.java0000664000000000000000000000152310612171071024553 0ustar /* * JiuCountColors * * Copyright (c) 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import net.sourceforge.jiu.color.analysis.Histogram3DCreator; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.gui.awt.ToolkitLoader; /** * Small example command line program to count the number of * unique colors in image files. * @author Marco Schmidt * @since 0.14.2 */ public class JiuCountColors { public static void main(String[] args) throws Exception { String[] FILE_NAMES = {"jiu-hello-world.png", "resources/images/image1.jpg", "out-image1.jpg"}; for (int i = 0; i < FILE_NAMES.length; i++) { RGB24Image image = ToolkitLoader.loadAsRgb24Image(FILE_NAMES[i]); System.out.println(FILE_NAMES[i] + "\t" + Histogram3DCreator.count(image)); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/ColorIndexer.java0000664000000000000000000001317210523754542024244 0ustar /* * ColorIndexer * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.io.File; import java.text.DecimalFormat; import java.text.NumberFormat; import net.sourceforge.jiu.color.adjustment.Contrast; import net.sourceforge.jiu.color.promotion.PromotionRGB24; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.geometry.Resample; import net.sourceforge.jiu.gui.awt.ToolkitLoader; import net.sourceforge.jiu.ops.BatchProcessorOperation; import net.sourceforge.jiu.ops.OperationFailedException; /** * Loads image files and generates color index information for them. * @author Marco Schmidt * @since 0.12.0 */ public class ColorIndexer extends BatchProcessorOperation { private int maxLength = 256; private int contrastChange = 100; private NumberFormat formatter = new DecimalFormat("#.##"); public static final int BLACK = 0; public static final int RED = 4; public static final int GREEN = 2; public static final int BLUE = 1; public static final int YELLOW = 6; public static final int MAGENTA = 5; public static final int CYAN = 3; public static final int WHITE = 7; public static final String[] COLOR_NAMES = {"black", "blue", "green", "cyan", "red", "magenta", "yellow", "white"}; public static void main(String[] args) { ColorIndexer indexer = new ColorIndexer(); for (int i = 0; i < args.length; i++) { String name = args[i]; File file = new File(name); if (file.isFile()) { indexer.addInputFileName(name); } else if (file.isDirectory()) { indexer.addDirectoryTree(name); } } indexer.process(); System.out.println("Done."); } private PixelImage convertToRgb24(PixelImage in) { if (in == null) { return null; } if (in instanceof RGB24Image) { return in; } try { PromotionRGB24 pr = new PromotionRGB24(); pr.setInputImage(in); pr.process(); return pr.getOutputImage(); } catch (OperationFailedException ofe) { return null; } } private PixelImage adjustColor(PixelImage img) { if (img == null || contrastChange == 0) { return img; } try { Contrast con = new Contrast(); con.setInputImage(img); con.setContrast(contrastChange); con.process(); return con.getOutputImage(); /* HueSaturationValue hsv = new HueSaturationValue(); hsv.setInputImage(img); hsv.setSaturationValue(30, 0); hsv.process(); return hsv.getOutputImage();*/ } catch (OperationFailedException ofe) { return null; } } private PixelImage scale(PixelImage in) { if (in == null) { return null; } if (in.getWidth() <= maxLength && in.getHeight() <= maxLength) { return in; } try { Resample res = new Resample(); res.setFilter(Resample.FILTER_TYPE_LANCZOS3); res.setInputImage(in); float thumbRatio = 1.0f; float imageRatio = (float)in.getWidth() / (float)in.getHeight(); int width = maxLength; int height = maxLength; if (thumbRatio < imageRatio) { height = (int)(maxLength / imageRatio); } else { width = (int)(maxLength * imageRatio); } //float x = (float)in.getWidth() / maxLength; //float y = (float)in.getHeight() / maxLength; res.setSize(width, height); res.process(); return res.getOutputImage(); } catch (OperationFailedException ofe) { return null; } } private int[] count(PixelImage image) { if (image == null) { return null; } RGB24Image in = (RGB24Image)image; int[] result = new int[8]; for (int y = 0; y < image.getHeight(); y++) { for (int x = 0; x < image.getWidth(); x++) { int red = in.getSample(RGBIndex.INDEX_RED, x, y) >> 7; int green = in.getSample(RGBIndex.INDEX_GREEN, x, y) >> 7; int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y) >> 7; int index = (red << 2) | (green << 1) | blue; result[index]++; } } return result; } /*private void save(PixelImage img, String fileName) { PNGCodec codec = new PNGCodec(); try { codec.setImage(img); codec.setFile(fileName, CodecMode.SAVE); codec.process(); codec.close(); } catch (OperationFailedException ofe) { } catch (IOException ioe) { } }*/ private void store(String name, int[] occ) { if (name == null || occ == null) { return; } int sum = 0; for (int i = 0; i < occ.length; i++) { sum += occ[i]; } System.out.print(name); System.out.print(';'); for (int i = 0; i < occ.length; i++) { float rel = (float)occ[i] / sum; if (rel < 0.01f) { continue; } System.out.print(COLOR_NAMES[i] + " = " + formatter.format(rel) + ";"); } System.out.println(); } public void processFile(String inputDirectory, String inputFileName, String outputDirectory) { File dir = new File(inputDirectory); File file = new File(dir, inputFileName); String name = file.getAbsolutePath(); // load image PixelImage image = ToolkitLoader.loadViaToolkitOrCodecs(name); // convert to RGB24 if necessary image = convertToRgb24(image); // scale down if necessary image = scale(image); // increase contrast image = adjustColor(image); // save the modified image, for testing purposes /*File outFile = new File("f:\\", "th_" + inputFileName + ".png"); save(image, outFile.getAbsolutePath());*/ // determine occurrence of colors int[] occur = count(image); // store store(name, occur); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/jiuawt.java0000664000000000000000000000743507741250130023147 0ustar /* * jiuawt * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import net.sourceforge.jiu.apps.EditorState; import net.sourceforge.jiu.gui.awt.JiuAwtFrame; import net.sourceforge.jiu.util.SystemInfo; /** * Graphical user interface application based on the AWT (Abstract * Windowing Toolkit, part of Java's standard runtime library since 1.0) * that demonstrates features of JIU. *

*

Memory shortage

* One of the errors experienced most frequently with jiuawt is the * 'out of memory' error. * Note that only whoever starts jiuawt can give it more memory by * giving more memory to the Java virtual machine. * Example: *
java -mx300m jiu.jar
* starts jiuawt and provides it with 300 MB of memory. * *

Command line switches

*
*
--dir DIRECTORY *
set working directory to DIRECTORY *
--help *
print help screen to standard output and exit *
--lang LANGUAGE *
set language to LANGUAGE, where en is English, de is German and es is Spanish *
--system *
print system information to standard output and exit *
--version *
print version information to standard output and exit *
* @author Marco Schmidt */ public class jiuawt { private jiuawt() { } /** * Creates a {@link JiuAwtFrame} object. * @param args program arguments, call jiuawt with --help as single argument to get a help screen */ public static void main(String[] args) { EditorState state = new EditorState(); int index = 0; while (index < args.length) { String s = args[index++]; if ("--dir".equals(s)) { if (index == args.length) { throw new IllegalArgumentException("Directory switch must be followed by a directory name."); } state.setCurrentDirectory(args[index++]); } else if ("--help".equals(s)) { printVersion(); System.out.println(); System.out.println("Usage: jiuawt [OPTIONS] [FILE]"); System.out.println("\tFILE is the name of an image file to be loaded after start-up"); System.out.println("\t--dir DIRECTORY set working directory to DIRECTORY"); System.out.println("\t--help print this help screen and exit"); System.out.println("\t--lang LANGCODE set language to LANGCODE (de=German, en=English, es=Spanish)"); System.out.println("\t--system print system info and exit"); System.out.println("\t--version print version information and exit"); System.exit(0); } else if ("--lang".equals(s)) { if (index == args.length) { throw new IllegalArgumentException("Language switch must be followed by language code."); } state.setStrings(args[index++]); } else if ("--system".equals(s)) { String info = SystemInfo.getSystemInfo(state.getStrings()); System.out.println(info); System.exit(0); } else if ("--version".equals(s)) { printVersion(); System.exit(0); } else { if (s.startsWith("-")) { throw new IllegalArgumentException("Unknown switch: " + s); } else { state.setStartupImageName(s); } } } state.ensureStringsAvailable(); new JiuAwtFrame(state); } private static void printVersion() { System.out.println("jiuawt " + JiuInfo.JIU_VERSION); System.out.println("An image editing program as a demo for the JIU image processing library."); System.out.println("Written by Marco Schmidt."); System.out.println("Visit the JIU website at <" + JiuInfo.JIU_HOMEPAGE + ">."); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/JiuInfo.java0000664000000000000000000000154310541044123023174 0ustar /* * JiuInfo * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; /** * Contains several constants with information on JIU. * @author Marco Schmidt */ public interface JiuInfo { /** * Three int values for the JIU version, in order (major, minor, patch). */ int[] JIU_NUMERICAL_VERSION = {0, 15, 0}; /** * Version as String, created from {@link #JIU_NUMERICAL_VERSION}. */ String JIU_VERSION = JIU_NUMERICAL_VERSION[0] + "." + JIU_NUMERICAL_VERSION[1] + "." + JIU_NUMERICAL_VERSION[2]; /** * URL of the homepage of the JIU project. */ String JIU_HOMEPAGE = "http://schmidt.devlib.org/jiu/"; /** * Address for feedback on JIU. */ String JIU_FEEDBACK_ADDRESS = "http://schmidt.devlib.org/jiu/feedback.html"; } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/StringIndexConstants.java0000664000000000000000000001661310404064612025773 0ustar /* * StringIndexConstants * * Copyright (c) 2001, 2002, 2003, 2004 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; /** * Interface of integer index values to be used with {@link Strings}. * @author Marco Schmidt */ public interface StringIndexConstants { int ERROR_LOADING_IMAGE = 0; int FILE_FORMAT_UNKNOWN = 1; int LOAD_IMAGE_FILE = 2; int SCREEN_RESOLUTION = 3; int COULD_NOT_CREATE_HISTOGRAM = 4; int NUMBER_OF_USED_COLORS = 5; int COUNT_COLORS_USED = 6; int CLOSE = 7; int HELP = 8; int ABOUT = 9; int SYSTEM = 10; int SYSTEM_INFORMATION = 11; int COLOR = 12; int INVERT = 13; int CONVERT_TO_GRAYSCALE = 14; int FILE = 15; int OPEN = 16; int SAVEAS = 17; int EXIT = 18; int PROPERTY_JAVA_VERSION = 19; int PROPERTY_JAVA_VENDOR = 20; int PROPERTY_JAVA_VENDOR_URL = 21; int PROPERTY_JAVA_HOME = 22; int PROPERTY_JAVA_VM_SPECIFICATION_VERSION = 23; int PROPERTY_JAVA_VM_SPECIFICATION_VENDOR = 24; int PROPERTY_JAVA_VM_SPECIFICATION_NAME = 25; int PROPERTY_JAVA_VM_VERSION = 26; int PROPERTY_JAVA_VM_VENDOR = 27; int PROPERTY_JAVA_VM_NAME = 28; int PROPERTY_JAVA_SPECIFICATION_VERSION = 29; int PROPERTY_JAVA_SPECIFICATION_VENDOR = 30; int PROPERTY_JAVA_SPECIFICATION_NAME = 31; int PROPERTY_JAVA_CLASS_VERSION = 32; int PROPERTY_JAVA_CLASS_PATH = 33; int PROPERTY_OS_NAME = 34; int PROPERTY_OS_ARCH = 35; int PROPERTY_OS_VERSION = 36; int HOMEPAGE = 37; int FEEDBACK = 38; int MEDIAN_CUT = 39; int ERROR_MESSAGE = 40; int NUMBER_OF_COLORS_SMALL_ENOUGH = 41; int CPU_ENDIANNESS = 42; int CPU_ISALIST = 43; int FREE_MEMORY = 44; int USED_MEMORY = 45; int TOTAL_MEMORY = 46; int SAVE_AS = 47; int TRANSFORMATIONS = 48; int FLIP = 49; int MIRROR = 50; int MEDIAN_CUT_CONTOUR_REMOVAL = 51; int PROMOTE_TO_RGB = 52; int REDUCE_TO_BILEVEL_THRESHOLD_MENU_ITEM = 53; int REDUCE_TO_BILEVEL_ORDERED_DITHERING = 54; int ROTATE_90_LEFT = 55; int ROTATE_90_RIGHT = 56; int ROTATE_180 = 57; int ROTATE_OTHER = 58; int SCALE = 59; int SCALE_IMAGE = 60; int NEW_WIDTH = 61; int NEW_HEIGHT = 62; int MAINTAIN_ASPECT_RATIO = 63; int OK = 64; int CANCEL = 65; int NUM_COLORS = 66; int MEDIAN_CUT_COLOR_QUANTIZATION = 67; int OUTPUT_COLOR_TYPE = 68; int METHOD_REPR_COLOR_AVERAGE = 69; int METHOD_REPR_COLOR_WEIGHTED_AVERAGE = 70; int METHOD_REPR_COLOR_MEDIAN = 71; int OUTPUT_COLOR_TYPE_PALETTED = 72; int OUTPUT_COLOR_TYPE_RGB = 73; int METHOD_REPR_COLOR = 74; int CONTOUR_REMOVAL = 75; int METHOD = 76; int FILTERS = 77; int SHARPEN = 78; int BLUR = 79; int EMBOSS = 80; int PSYCHEDELIC_DISTILLATION = 81; int LITHOGRAPH = 82; int MAXIMUM_COLOR_DISTANCE = 83; int PORTABLE_BITMAP = 84; int PORTABLE_GRAYMAP = 85; int PORTABLE_PIXMAP = 86; int UNIFORM_PALETTE_COLOR_QUANTIZATION_MENU_ITEM = 87; int NUMBER_OF_BITS_RED = 88; int NUMBER_OF_BITS_GREEN = 89; int NUMBER_OF_BITS_BLUE = 90; int ORDERED_DITHERING = 91; int DITHERING_NONE = 92; int DITHERING_METHOD = 93; int UNIFORM_PALETTE_COLOR_QUANTIZATION = 94; int SAVE_IMAGE_AS = 95; int ERROR_NO_MORE_THAN_8_BITS = 96; int TOTAL_NUMBER_OF_BITS_AND_COLORS = 97; int EDGE_DETECTION = 98; int REDUCE_NUMBER_OF_SHADES_OF_GRAY = 99; int REDUCE_NUMBER_OF_SHADES_OF_GRAY_MENU_ITEM = 100; int NUMBER_OF_BITS = 101; int NUMBER_OF_SHADES_OF_GRAY = 102; int SUN_RASTER = 103; int ADJUST = 104; int CONTRAST_MENU_ITEM = 105; int BRIGHTNESS_MENU_ITEM = 106; int GAMMA_MENU_ITEM = 107; int ADJUST_CONTRAST = 108; int ENTER_CONTRAST_VALUE = 109; int ADJUST_BRIGHTNESS = 110; int ENTER_BRIGHTNESS_VALUE = 111; int ADJUST_GAMMA = 112; int ENTER_GAMMA_VALUE = 113; int CROP_IMAGE = 114; int LEFT_COLUMN = 115; int TOP_ROW = 116; int RIGHT_COLUMN = 117; int BOTTOM_ROW = 118; int CROP_MENU_ITEM = 119; int CONVERT_TO_MINIMUM_COLOR_TYPE_MENU_ITEM = 120; int HISTOGRAM = 121; int REDUCE_TO_BILEVEL_THRESHOLD = 122; int ENTER_THRESHOLD_VALUE = 123; int FLOYD_STEINBERG_ERROR_DIFFUSION = 124; int STUCKI_ERROR_DIFFUSION = 125; int BURKES_ERROR_DIFFUSION = 126; int SIERRA_ERROR_DIFFUSION = 127; int JARVIS_JUDICE_NINKE_ERROR_DIFFUSION = 128; int STEVENSON_ARCE_ERROR_DIFFUSION = 129; int OUTPUT_QUALITY_IMPROVEMENT_ALGORITHM = 130; int ALGORITHMS_NONE = 131; int ERROR_DIFFUSION = 132; int COLOR_IMAGE_QUANTIZATION = 133; int HORIZONTAL_SOBEL = 134; int VERTICAL_SOBEL = 135; int HORIZONTAL_PREWITT = 136; int VERTICAL_PREWITT = 137; int SHEAR_MENU_ITEM = 138; int SHEAR_IMAGE = 139; int SHEAR_ENTER_ANGLE = 140; int HUE_SATURATION_VALUE_MENU_ITEM = 141; int ADJUST_HUE_SATURATION_AND_VALUE = 142; int SET_HUE = 143; int HUE = 144; int SATURATION = 145; int VALUE = 146; int MEAN_FILTER_MENU_ITEM = 147; int MEDIAN_FILTER_MENU_ITEM = 148; int OIL_FILTER_MENU_ITEM = 149; int APPLY_MEAN_FILTER = 150; int APPLY_MEDIAN_FILTER = 151; int APPLY_OIL_FILTER = 152; int WINDOW_WIDTH = 153; int WINDOW_HEIGHT = 154; int ENTER_WINDOW_SIZE = 155; int CONTOUR_REMOVAL_NUM_PASSES = 156; int CONTOUR_REMOVAL_TAU = 157; int PALETTE_MENU_ITEM = 158; int PALETTE_SAVE_AS_MENU_ITEM = 159; int MAP_TO_ARBITRARY_PALETTE_MENU_ITEM = 160; int SAVE_PALETTE = 161; int LOAD_PALETTE = 162; int CHOOSE_DITHERING_METHOD = 163; int WEBSAFE_PALETTE = 164; int PALETTE_FROM_FILE = 165; int CHOOSE_PALETTE_TYPE = 166; int MAP_TO_ARBITRARY_PALETTE = 167; int EQUALIZE_HISTOGRAM_MENU_ITEM = 168; int NORMALIZE_HISTOGRAM_MENU_ITEM = 169; int OCTREE_COLOR_QUANTIZATION_MENU_ITEM = 170; int OCTREE_COLOR_QUANTIZATION = 171; int SAVE_COOCCURRENCE_MATRIX = 172; int SAVE_COOCCURRENCE_MATRIX_MENU_ITEM = 173; int SAVE_COOCCURRENCE_FREQUENCY_MATRIX = 174; int SAVE_COOCCURRENCE_FREQUENCY_MATRIX_MENU_ITEM = 175; int WINDOWS_BITMAP = 176; int PROMOTE = 177; int PROMOTE_TO_PALETTED = 178; int PROMOTE_TO_GRAY8 = 179; int PROMOTE_TO_GRAY16 = 180; int PROMOTE_TO_RGB24 = 181; int PROMOTE_TO_RGB48 = 182; int REDUCE = 183; int SAVE_HISTOGRAM_AS_MENU_ITEM = 184; int SAVE_HISTOGRAM_AS = 185; int EDIT = 186; int EDIT_UNDO = 187; int EDIT_REDO = 188; int TEXTURE_PROPERTIES_MENU_ITEM = 189; int CONTRAST = 190; int ENERGY = 191; int ENTROPY = 192; int HOMOGENEITY = 193; int TEXTURE_PROPERTIES = 194; int CORRELATION = 195; int DISSIMILARITY = 196; int MINIMUM_FILTER_MENU_ITEM = 197; int MAXIMUM_FILTER_MENU_ITEM = 198; int APPLY_MINIMUM_FILTER = 199; int APPLY_MAXIMUM_FILTER = 200; int VIEW = 201; int VIEW_ZOOMIN = 202; int VIEW_ZOOMOUT = 203; int VIEW_SETORIGINALSIZE = 204; int VIEW_INTERPOLATIONTYPE = 205; int VIEW_INTERPOLATIONTYPE_NEARESTNEIGHBOR = 206; int VIEW_INTERPOLATIONTYPE_BILINEAR = 207; int VIEW_INTERPOLATIONTYPE_BICUBIC = 208; int PALM = 209; int PALETTE_PALM_256_COLORS = 210; int PALETTE_PALM_16_COLORS = 211; int PALETTE_PALM_16_GRAY = 212; int PALETTE_PALM_4_GRAY = 213; int DO_YOU_REALLY_WANT_TO_QUIT_WITHOUT_SAVING = 214; int QUIT_PROGRAM = 215; int YES = 216; int NO = 217; int DO_YOU_REALLY_WANT_TO_CLOSE_WITHOUT_SAVING = 218; int CLOSE_FILE = 219; int BITS_PER_PIXEL = 220; int BILEVEL = 221; int GRAYSCALE = 222; int RGB_TRUECOLOR = 223; int IMAGE_TYPE_UNKNOWN = 224; int IMAGE_TYPE = 225; int PALETTED = 226; int PIXELS = 227; int PORTABLE_NETWORK_GRAPHICS = 228; int MEMORY = 229; int DISK_SPACE = 230; int GIF = 231; int IMAGE_1 = 232; } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/dumpcodecs.java0000664000000000000000000000150707741250130023764 0ustar /* * dumpcodecs * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import net.sourceforge.jiu.codecs.*; /** * Command line program that lists all codecs registered with ImageLoader. * All program arguments are ignored. * @author Marco Schmidt * @since 0.10.0 */ public class dumpcodecs { public static void main(String[] args) throws Exception { System.out.println("Codecs known to net.sourceforge.jiu.codecs.ImageLoader"); for (int i = 0; i < ImageLoader.getNumCodecs(); i++) { ImageCodec codec = ImageLoader.createCodec(i); System.out.println("(" + (i + 1) + ") " + codec.getClass().getName() + " / " + codec.getFormatName() + " / " + "Saving supported=" + (codec.isSavingSupported() ? "yes" : "no")); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/apps/jiuconvert.java0000664000000000000000000003455310377272210024037 0ustar /* * jiuconvert * * Copyright (c) 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.apps; import java.io.File; import java.io.IOException; import java.util.Hashtable; import java.util.Vector; import net.sourceforge.jiu.codecs.BMPCodec; import net.sourceforge.jiu.codecs.CodecMode; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.ImageLoader; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.InvalidImageIndexException; import net.sourceforge.jiu.codecs.PalmCodec; import net.sourceforge.jiu.codecs.PNMCodec; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.OperationFailedException; class JiuConvertSettings { static final int VERBOSITY_QUIET = 0; static final int VERBOSITY_NORMAL = 1; static final int VERBOSITY_HIGH = 2; static final int FORMAT_UNKNOWN = -1; static final int FORMAT_BMP = 0; static final int FORMAT_PNM = 1; static final int FORMAT_PALM = 2; Vector inputFileNames = new Vector(); File destinationDirectory; int fileFormat; boolean noAwtLoading; boolean overwrite; boolean testOnly; long time1; long time2; int verbosity; } abstract class Switch { void check(JiuConvertSettings settings) { } abstract String getDescription(); int getMinParameters() { return 0; } String getParameters() { return ""; } abstract String[] getValues(); abstract int init(String[] args, int index, JiuConvertSettings settings); void setDefaults(JiuConvertSettings settings) { } } class BMPSwitch extends Switch { void check(JiuConvertSettings settings) { if (settings.fileFormat == JiuConvertSettings.FORMAT_UNKNOWN && !settings.testOnly) { System.err.println("ERROR: You must either use test mode or provide an output file format switch."); System.exit(1); } } String[] getValues() { return new String[] {"-b", "--bmp"}; } String getDescription() { return "write Windows BMP output"; } int init(String[] args, int index, JiuConvertSettings settings) { settings.fileFormat = JiuConvertSettings.FORMAT_BMP; return index; } void setDefaults(JiuConvertSettings settings) { settings.fileFormat = JiuConvertSettings.FORMAT_UNKNOWN; } } class DestinationDirectorySwitch extends Switch { int getMinParameters() { return 1; } String getDescription() { return "write output files to directory DIR"; } String getParameters() { return "DIR"; } String[] getValues() { return new String[] {"-d", "--destdir"}; } int init(String[] args, int index, JiuConvertSettings settings) { String name = args[index++]; File dir = new File(name); if (!dir.exists()) { System.err.println("Directory " + name + " does not exist."); System.exit(1); } if (!dir.isDirectory()) { System.err.println(name + " does not seem to be a directory."); System.exit(1); } settings.destinationDirectory = dir; return index; } void setDefaults(JiuConvertSettings settings) { settings.destinationDirectory = new File("."); } } class NoAwtLoadingSwitch extends Switch { String[] getValues() { return new String[] {"--noawtloading"}; } String getDescription() { return "never use AWT Toolkit to load images"; } int init(String[] args, int index, JiuConvertSettings settings) { settings.noAwtLoading = true; return index; } void setDefaults(JiuConvertSettings settings) { settings.noAwtLoading = false; } } class OverwriteSwitch extends Switch { String[] getValues() { return new String[] {"-o", "--overwrite"}; } String getDescription() { return "overwrite existing files"; } int init(String[] args, int index, JiuConvertSettings settings) { settings.overwrite = true; return index; } void setDefaults(JiuConvertSettings settings) { settings.overwrite = false; } } class PalmSwitch extends Switch { String[] getValues() { return new String[] {"-P", "--palm"}; } String getDescription() { return "write Palm native image output"; } int init(String[] args, int index, JiuConvertSettings settings) { settings.fileFormat = JiuConvertSettings.FORMAT_PALM; return index; } } class PNMSwitch extends Switch { String[] getValues() { return new String[] {"-p", "--pnm"}; } String getDescription() { return "write Portable Anymap (PNM/PBM/PGM/PPM) output"; } int init(String[] args, int index, JiuConvertSettings settings) { settings.fileFormat = JiuConvertSettings.FORMAT_PNM; return index; } } class PrintHelpSwitch extends Switch { static Vector switches; String[] getValues() { return new String[] {"-H", "--help"}; } String getDescription() { return "print help text to stdout and terminate"; } int init(String[] args, int index, JiuConvertSettings settings) { System.out.println("Usage: java jiuconvert [OPTIONS] [FILEs]"); System.out.println(""); for (int i = 0; i < switches.size(); i++) { Switch sw = (Switch)switches.elementAt(i); System.out.print("\t"); String[] values = sw.getValues(); int chars = 0; for (int j = 0; j < values.length; j++) { if (j > 0) { System.out.print(", "); chars += 2; } System.out.print(values[j]); chars += values[j].length(); } String params = sw.getParameters(); System.out.print(" " + params); chars += params.length() + 1; while (chars++ < 24) { System.out.print(" "); } System.out.println(sw.getDescription()); } System.exit(0); return 0; // compiler doesn't know that System.exit(0) prevents execution ever getting here } } class PrintVersionSwitch extends Switch { String[] getValues() { return new String[] {"-V", "--version"}; } String getDescription() { return "print version to stdout and terminate"; } int init(String[] args, int index, JiuConvertSettings settings) { System.out.println("jiuconvert " + JiuInfo.JIU_VERSION); System.out.println("Written by Marco Schmidt."); System.out.println("Copyright (C) 2002, 2003, 2004, 2005 Marco Schmidt."); System.out.println("This is free software; see the source for copying conditions. There is NO"); System.out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."); System.exit(0); return 0; // compiler doesn't know that System.exit(0) prevents execution ever getting here } } class QuietSwitch extends Switch { String[] getValues() { return new String[] {"-q", "--quiet"}; } String getDescription() { return "print only error messages"; } int init(String[] args, int index, JiuConvertSettings settings) { settings.verbosity = JiuConvertSettings.VERBOSITY_QUIET; return index; } void setDefaults(JiuConvertSettings settings) { settings.verbosity = JiuConvertSettings.VERBOSITY_NORMAL; } } class TestSwitch extends Switch { String[] getValues() { return new String[] {"-t", "--test"}; } String getDescription() { return "test loading, do not write any output files"; } int init(String[] args, int index, JiuConvertSettings settings) { settings.testOnly = true; return index; } void setDefaults(JiuConvertSettings settings) { settings.testOnly = false; } } class VerbositySwitch extends Switch { String[] getValues() { return new String[] {"-v", "--verbose"}; } String getDescription() { return "print extensive information to stdout"; } int init(String[] args, int index, JiuConvertSettings settings) { settings.verbosity = JiuConvertSettings.VERBOSITY_HIGH; return index; } } /** * A command line program to convert between file formats. * @author Marco Schmidt * @since 0.10.0 */ public class jiuconvert { // to prevent instances of this class private jiuconvert() { } private static Vector createSwitches() { Vector switches = new Vector(); // note that the order in which the switches are added is the order in which they are displayed switches.addElement(new BMPSwitch()); switches.addElement(new PalmSwitch()); switches.addElement(new PNMSwitch()); switches.addElement(new TestSwitch()); switches.addElement(new NoAwtLoadingSwitch()); switches.addElement(new OverwriteSwitch()); switches.addElement(new DestinationDirectorySwitch()); switches.addElement(new QuietSwitch()); switches.addElement(new VerbositySwitch()); switches.addElement(new PrintHelpSwitch()); switches.addElement(new PrintVersionSwitch()); return switches; } private static void exit(JiuConvertSettings settings, int exitCode) { settings.time2 = System.currentTimeMillis(); println(JiuConvertSettings.VERBOSITY_NORMAL, settings, "Time: " + ((settings.time2 - settings.time1) / 1000L) + " s."); System.exit(exitCode); } private static JiuConvertSettings initFromArguments(String[] args) { // create switch objects Vector switches = createSwitches(); PrintHelpSwitch.switches = switches; // set defaults JiuConvertSettings settings = new JiuConvertSettings(); settings.time1 = System.currentTimeMillis(); Hashtable switchHash = new Hashtable(); for (int i = 0; i < switches.size(); i++) { Switch sw = (Switch)switches.elementAt(i); sw.setDefaults(settings); String[] values = sw.getValues(); int j = 0; while (j < values.length) { String value = values[j++]; if (switchHash.get(value) != null) { System.err.println("FATAL INTERNAL ERROR: Switch " + value + " is used more than once."); System.exit(1); } switchHash.put(value, sw); } } // process arguments int index = 0; while (index < args.length) { String arg = args[index++]; Switch sw = (Switch)switchHash.get(arg); if (sw == null) { // maybe a switch that does not exist? if (arg.charAt(0) == '-') { System.err.println("Error: Unknown switch " + arg); System.exit(1); } // there is no switch of that name => must be a file File file = new File(arg); if (!file.exists() || !file.isFile()) { System.err.println("Error: There is no file \"" + arg + "\"."); System.exit(1); } settings.inputFileNames.addElement(arg); } else { int minParams = sw.getMinParameters(); if (index + minParams > args.length) { System.err.println("Error: switch " + arg + " needs at least " + minParams + " parameter(s)."); System.exit(1); } index = sw.init(args, index, settings); } } // now call check() on each switch for (int i = 0; i < switches.size(); i++) { Switch sw = (Switch)switches.elementAt(i); sw.check(settings); } // other checks if (settings.inputFileNames.size() < 1) { System.err.println("Error: You must provide at least one input file name."); System.exit(1); } return settings; } private static void print(int minVerbosityLevel, JiuConvertSettings settings, String message) { if (settings.verbosity >= minVerbosityLevel) { System.out.print(message); } } private static void println(int minVerbosityLevel, JiuConvertSettings settings, String message) { if (settings.verbosity >= minVerbosityLevel) { System.out.println(message); } } private static void run(JiuConvertSettings settings, String inputFileName) { String message = null; PixelImage image = null; try { image = ImageLoader.load(inputFileName, (Vector)null); } catch (InvalidImageIndexException iiie) { message = "Failed: " + iiie.toString(); } catch (InvalidFileStructureException ifse) { message = "Failed: " + ifse.toString(); } catch (UnsupportedTypeException ute) { message = "Failed: " + ute.toString(); } catch (IOException ioe) { message = "Failed: " + ioe.toString(); } if (message == null && image == null) { message = "Failed."; } if (message != null) { println(JiuConvertSettings.VERBOSITY_QUIET, settings, "\"" + inputFileName + "\" " + message); return; } else { print(JiuConvertSettings.VERBOSITY_NORMAL, settings, "\"" + inputFileName + "\" "); print(JiuConvertSettings.VERBOSITY_NORMAL, settings, "Loaded (" + //ImageDescriptionCreator.getDescription(image, Locale.US, state) + ")."); } if (settings.testOnly) { println(JiuConvertSettings.VERBOSITY_NORMAL, settings, ""); return; } String outputFileName = inputFileName; String sep = System.getProperty("file.separator"); int index = outputFileName.lastIndexOf(sep); if (index != -1) { outputFileName = outputFileName.substring(index + sep.length()); } index = outputFileName.lastIndexOf("."); if (index != -1) { outputFileName = outputFileName.substring(0, index); } ImageCodec codec = null; switch(settings.fileFormat) { case(JiuConvertSettings.FORMAT_BMP): { codec = new BMPCodec(); break; } case(JiuConvertSettings.FORMAT_PALM): { codec = new PalmCodec(); break; } case(JiuConvertSettings.FORMAT_PNM): { codec = new PNMCodec(); break; } } String ext = codec.suggestFileExtension(image); if (ext != null) { outputFileName += ext; } File outputFile = new File(settings.destinationDirectory, outputFileName); outputFileName = outputFile.getAbsolutePath(); if (outputFile.exists() && !settings.overwrite) { println(JiuConvertSettings.VERBOSITY_NORMAL, settings, " File \"" + outputFileName + "\" already exists, skipping."); } codec.setImage(image); try { codec.setFile(outputFileName, CodecMode.SAVE); codec.process(); codec.close(); println(JiuConvertSettings.VERBOSITY_NORMAL, settings, " Wrote \"" + outputFileName + "\"."); } catch (IOException ioe) { println(JiuConvertSettings.VERBOSITY_HIGH, settings, " I/O error writing \"" + outputFileName + "\": " + ioe.toString()); } catch (OperationFailedException ofe) { println(JiuConvertSettings.VERBOSITY_HIGH, settings, " Error writing \"" + outputFileName + "\": " + ofe.toString()); } } private static void run(JiuConvertSettings settings) { int index = 0; while (index < settings.inputFileNames.size()) { String fileName = (String)settings.inputFileNames.elementAt(index++); run(settings, fileName); } exit(settings, 0); } public static void main(String[] args) { JiuConvertSettings settings = initFromArguments(args); run(settings); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/0000775000000000000000000000000010546532075020621 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/0000775000000000000000000000000010572433573021416 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/0000775000000000000000000000000010546532075023036 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/ReduceGrayscaleDialog.java0000664000000000000000000001214207741250134030057 0ustar /* * ReduceGrayscaleDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Choice; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.Rectangle; import java.awt.Scrollbar; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import net.sourceforge.jiu.apps.Strings; /** * A dialog to enter the parameters for a grayscale reduction operation. * @author Marco Schmidt */ public class ReduceGrayscaleDialog extends Dialog implements ActionListener, AdjustmentListener { public static final int TYPE_DITHERING_NONE = 0; public static final int TYPE_ORDERED_DITHERING = 1; public static final int TYPE_FLOYD_STEINBERG_ERROR_DIFFUSION = 2; public static final int TYPE_STUCKI_ERROR_DIFFUSION = 3; public static final int TYPE_BURKES_ERROR_DIFFUSION = 4; public static final int TYPE_SIERRA_ERROR_DIFFUSION = 5; public static final int TYPE_JARVIS_JUDICE_NINKE_ERROR_DIFFUSION = 6; public static final int TYPE_STEVENSON_ARCE_ERROR_DIFFUSION = 7; public final int[][] DITHERING_METHODS = { { TYPE_DITHERING_NONE, TYPE_ORDERED_DITHERING, TYPE_FLOYD_STEINBERG_ERROR_DIFFUSION, TYPE_STUCKI_ERROR_DIFFUSION, TYPE_BURKES_ERROR_DIFFUSION, TYPE_SIERRA_ERROR_DIFFUSION, TYPE_JARVIS_JUDICE_NINKE_ERROR_DIFFUSION, TYPE_STEVENSON_ARCE_ERROR_DIFFUSION }, { Strings.DITHERING_NONE, Strings.ORDERED_DITHERING, Strings.FLOYD_STEINBERG_ERROR_DIFFUSION, Strings.STUCKI_ERROR_DIFFUSION, Strings.BURKES_ERROR_DIFFUSION, Strings.SIERRA_ERROR_DIFFUSION, Strings.JARVIS_JUDICE_NINKE_ERROR_DIFFUSION, Strings.STEVENSON_ARCE_ERROR_DIFFUSION } }; private Strings strings; private Button ok; private Button cancel; private Scrollbar scrollbar; private Choice ditheringMethod; private Label bitLabel; private Label shadesLabel; private boolean pressedOk; /** * Creates a modal dialog to enter the parameters. * @param owner the parent of this modal dialog * @param strings an object to get String constants in the current language * @param bits initial number of bits to be shown in the dialog * @param maxBits maximum allowed number of bits * @param ditheringMethodSelection initial selection of dithering method */ public ReduceGrayscaleDialog(Frame owner, Strings strings, int bits, int maxBits, int ditheringMethodSelection) { super(owner, strings.get(Strings.REDUCE_NUMBER_OF_SHADES_OF_GRAY), true); pressedOk = false; this.strings = strings; Panel panel = new Panel(); panel.setLayout(new GridLayout(0, 2)); bitLabel = new Label(); panel.add(bitLabel); scrollbar = new Scrollbar(Scrollbar.HORIZONTAL, bits, 1, 1, maxBits + 1); scrollbar.addAdjustmentListener(this); panel.add(scrollbar); panel.add(new Label(strings.get(Strings.NUMBER_OF_SHADES_OF_GRAY) + ": ")); shadesLabel = new Label(); panel.add(shadesLabel); panel.add(new Label(strings.get(Strings.DITHERING_METHOD))); ditheringMethod = new Choice(); for (int i = 0; i < DITHERING_METHODS[0].length; i++) { ditheringMethod.add(strings.get(DITHERING_METHODS[1][i])); if (ditheringMethodSelection == i) { ditheringMethod.select(i); } } panel.add(ditheringMethod); add(panel, BorderLayout.CENTER); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel = new Panel(); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); updateLabels(); pack(); center(); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } public void adjustmentValueChanged(AdjustmentEvent e) { updateLabels(); } /** * Centers the dialog on screen. */ public void center() { Rectangle rect = getBounds(); int width = rect.width; int height = rect.height; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((screenSize.width / 2) - (width / 2), (screenSize.height / 2) - (height / 2)); } public int getDitheringMethod() { return DITHERING_METHODS[0][ditheringMethod.getSelectedIndex()]; } public int getNumBits() { return scrollbar.getValue(); } public boolean hasPressedOk() { return pressedOk; } private void updateLabels() { int numBits = getNumBits(); bitLabel.setText(strings.get(Strings.NUMBER_OF_BITS) + ": " + numBits); shadesLabel.setText(Integer.toString(1 << numBits)); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/UniformPaletteQuantizerDialog.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/UniformPaletteQuantizerDial0000664000000000000000000001642507741250134030420 0ustar /* * UniformPaletteQuantizerDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Choice; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.Rectangle; import java.awt.Scrollbar; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import net.sourceforge.jiu.apps.Strings; /** * An AWT dialog to enter the parameters for a uniform palette color quantization operation. * @author Marco Schmidt */ public class UniformPaletteQuantizerDialog extends Dialog implements ActionListener, AdjustmentListener, ItemListener { public static final int TYPE_DITHERING_NONE = 0; public static final int TYPE_ORDERED_DITHERING = 1; public static final int TYPE_FLOYD_STEINBERG_ERROR_DIFFUSION = 2; public static final int TYPE_STUCKI_ERROR_DIFFUSION = 3; public static final int TYPE_BURKES_ERROR_DIFFUSION = 4; public static final int TYPE_SIERRA_ERROR_DIFFUSION = 5; public static final int TYPE_JARVIS_JUDICE_NINKE_ERROR_DIFFUSION = 6; public static final int TYPE_STEVENSON_ARCE_ERROR_DIFFUSION = 7; public final int[][] DITHERING_METHODS = { { TYPE_DITHERING_NONE, TYPE_ORDERED_DITHERING, TYPE_FLOYD_STEINBERG_ERROR_DIFFUSION, TYPE_STUCKI_ERROR_DIFFUSION, TYPE_BURKES_ERROR_DIFFUSION, TYPE_SIERRA_ERROR_DIFFUSION, TYPE_JARVIS_JUDICE_NINKE_ERROR_DIFFUSION, TYPE_STEVENSON_ARCE_ERROR_DIFFUSION }, { Strings.DITHERING_NONE, Strings.ORDERED_DITHERING, Strings.FLOYD_STEINBERG_ERROR_DIFFUSION, Strings.STUCKI_ERROR_DIFFUSION, Strings.BURKES_ERROR_DIFFUSION, Strings.SIERRA_ERROR_DIFFUSION, Strings.JARVIS_JUDICE_NINKE_ERROR_DIFFUSION, Strings.STEVENSON_ARCE_ERROR_DIFFUSION } }; private Strings strings; private Button ok; private Button cancel; private Scrollbar redScrollbar; private Scrollbar greenScrollbar; private Scrollbar blueScrollbar; private Choice ditheringMethod; private Label infoLabel1; private Label infoLabel2; private Label redLabel; private Label greenLabel; private Label blueLabel; private boolean pressedOk; /** * Creates a modal dialog to enter the parameter. * @param owner the parent of this modal dialog * @param strings an object to get String constants in the current language * @param redBits the initial selection of the number of bits for the red channel * @param greenBits the initial selection of the number of bits for the green channel * @param blueBits the initial selection of the number of bits for the blue channel * @param ditheringMethodSelection initial selection for dithering method */ public UniformPaletteQuantizerDialog(Frame owner, Strings strings, int redBits, int greenBits, int blueBits, int ditheringMethodSelection) { super(owner, strings.get(Strings.UNIFORM_PALETTE_COLOR_QUANTIZATION), true); pressedOk = false; this.strings = strings; Panel panel = new Panel(); panel.setLayout(new GridLayout(0, 2)); redLabel = new Label(strings.get(Strings.NUMBER_OF_BITS_RED)); panel.add(redLabel); redScrollbar = new Scrollbar(Scrollbar.HORIZONTAL, redBits, 1, 1, 7); redScrollbar.addAdjustmentListener(this); panel.add(redScrollbar); greenLabel = new Label(strings.get(Strings.NUMBER_OF_BITS_GREEN)); panel.add(greenLabel); greenScrollbar = new Scrollbar(Scrollbar.HORIZONTAL, greenBits, 1, 1, 7); greenScrollbar.addAdjustmentListener(this); panel.add(greenScrollbar); blueLabel = new Label(strings.get(Strings.NUMBER_OF_BITS_BLUE)); panel.add(blueLabel); blueScrollbar = new Scrollbar(Scrollbar.HORIZONTAL, blueBits, 1, 1, 7); blueScrollbar.addAdjustmentListener(this); panel.add(blueScrollbar); infoLabel1 = new Label(); infoLabel2 = new Label(); panel.add(infoLabel1); panel.add(infoLabel2); panel.add(new Label(strings.get(Strings.DITHERING_METHOD))); ditheringMethod = new Choice(); for (int i = 0; i < DITHERING_METHODS[0].length; i++) { ditheringMethod.add(strings.get(DITHERING_METHODS[1][i])); if (ditheringMethodSelection == i) { ditheringMethod.select(i); } } ditheringMethod.addItemListener(this); panel.add(ditheringMethod); add(panel, BorderLayout.CENTER); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel = new Panel(); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); updateLabels(); pack(); center(); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } public void adjustmentValueChanged(AdjustmentEvent e) { updateLabels(); } /** * Centers the dialog on screen. */ public void center() { Rectangle rect = getBounds(); int width = rect.width; int height = rect.height; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((screenSize.width / 2) - (width / 2), (screenSize.height / 2) - (height / 2)); } /*public int getNumColors() { return getValue(numColorsField); }*/ public int getDitheringMethod() { return DITHERING_METHODS[0][ditheringMethod.getSelectedIndex()]; } public int getBlueBits() { return blueScrollbar.getValue(); } public int getGreenBits() { return greenScrollbar.getValue(); } public int getRedBits() { return redScrollbar.getValue(); } public boolean hasPressedOk() { return pressedOk; } public boolean isSelectionValid() { int r = getRedBits(); int g = getGreenBits(); int b = getBlueBits(); int sum = r + g + b; if (getDitheringMethod() == TYPE_DITHERING_NONE) { return (sum < 9); } else { return (sum < 24); } } public void itemStateChanged(ItemEvent e) { if (e.getSource() == ditheringMethod) { updateLabels(); } } private void updateLabels() { redLabel.setText(strings.get(Strings.NUMBER_OF_BITS_RED) + " (" + getRedBits() + ")"); greenLabel.setText(strings.get(Strings.NUMBER_OF_BITS_GREEN) + " (" + getGreenBits() + ")"); blueLabel.setText(strings.get(Strings.NUMBER_OF_BITS_BLUE) + " (" + getBlueBits() + ")"); boolean valid = isSelectionValid(); ok.setEnabled(valid); if (valid) { int totalBits = getRedBits() + getGreenBits() + getBlueBits(); int totalColors = 1 << totalBits; infoLabel1.setText(strings.get(Strings.TOTAL_NUMBER_OF_BITS_AND_COLORS)); infoLabel2.setText(Integer.toString(totalBits) + ", " + Integer.toString(totalColors)); } else { infoLabel1.setText(strings.get(Strings.ERROR_NO_MORE_THAN_8_BITS)); infoLabel2.setText(""); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/WindowSizeDialog.java0000664000000000000000000000671707741250134027132 0ustar /* * WindowSizeDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Dialog; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.TextComponent; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import net.sourceforge.jiu.apps.Strings; /** * A dialog to enter values for the width and height of a window (typically * for a spatial filter like median or mean. * * @author Marco Schmidt */ public class WindowSizeDialog extends Dialog implements ActionListener, KeyListener { private Button ok; private Button cancel; private TextField width; private TextField height; private boolean pressedOk; /** * @param owner the Frame this dialog will belong to */ public WindowSizeDialog(Frame owner, Strings strings, int titleIndex, int initialWidth, int initialHeight) { super(owner, strings.get(titleIndex), true); pressedOk = false; Panel panel = new Panel(); panel.setLayout(new GridLayout(0, 2)); panel.add(new Label(strings.get(Strings.ENTER_WINDOW_SIZE))); panel.add(new Label("")); panel.add(new Label(strings.get(Strings.WINDOW_WIDTH))); width = new TextField(Integer.toString(initialWidth)); width.addKeyListener(this); panel.add(width); panel.add(new Label(strings.get(Strings.WINDOW_HEIGHT))); height = new TextField(Integer.toString(initialHeight)); height.addKeyListener(this); panel.add(height); add(panel, BorderLayout.CENTER); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel = new Panel(); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); updateOkButton(); pack(); Dialogs.center(this); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } public int getHeightValue() { return getValue(height); } public int getWidthValue() { return getValue(width); } /** * Attempts to convert the content of the argument text component * to an int; if successful, returns that int, otherwise * -1000 is returned. * @param textField the text component that is supposed to hold an int value * @return int representation of the text component's data */ private int getValue(TextComponent textField) { try { return Integer.parseInt(textField.getText()); } catch (NumberFormatException nfe) { return -1000; } } public boolean hasPressedOk() { return pressedOk; } private void updateOkButton() { int w = getWidthValue(); int h = getHeightValue(); ok.setEnabled(w >= 1 && h >= 1 && ((w % 2) == 1) && ((h % 2) == 1)); } public void keyPressed(KeyEvent e) { updateOkButton(); } public void keyReleased(KeyEvent e) { updateOkButton(); } public void keyTyped(KeyEvent e) { updateOkButton(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/CropDialog.java0000664000000000000000000001330207741250134025717 0ustar /* * CropDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Dialog; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.TextComponent; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import net.sourceforge.jiu.apps.Strings; /** * A dialog to enter the parameters for an image crop operation. * @author Marco Schmidt */ public class CropDialog extends Dialog implements ActionListener, KeyListener { private Button ok; private Button cancel; private TextField x1; private TextField y1; private TextField x2; private TextField y2; private int width; private int height; private Label newWidth; private Label newHeight; private boolean pressedOk; /** * @param owner the Frame this dialog will belong to * @param strings Strings resource to be used for text messages * @param width width of the original image, before cropping; this is used to * determine the valid values for left and right column, from 0 to width - 1 * @param height height of the original image, before cropping; this is used to * determine the valid values for top and bottom row, from 0 to height - 1 */ public CropDialog(Frame owner, Strings strings, int width, int height) { super(owner, strings.get(Strings.CROP_IMAGE) + " (" + width + " x " + height + ")", true); pressedOk = false; this.width = width; this.height = height; Panel panel = new Panel(); panel.setLayout(new GridLayout(0, 6)); panel.add(new Label(strings.get(Strings.LEFT_COLUMN))); x1 = new TextField("0"); x1.addKeyListener(this); panel.add(x1); panel.add(new Label(strings.get(Strings.RIGHT_COLUMN))); x2 = new TextField(Integer.toString(width - 1)); x2.addKeyListener(this); panel.add(x2); panel.add(new Label(strings.get(Strings.NEW_WIDTH))); newWidth = new Label(); panel.add(newWidth); panel.add(new Label(strings.get(Strings.TOP_ROW))); y1 = new TextField("0"); y1.addKeyListener(this); panel.add(y1); panel.add(new Label(strings.get(Strings.BOTTOM_ROW))); y2 = new TextField(Integer.toString(height - 1)); y2.addKeyListener(this); panel.add(y2); panel.add(new Label(strings.get(Strings.NEW_HEIGHT))); newHeight = new Label(); panel.add(newHeight); add(panel, BorderLayout.CENTER); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel = new Panel(); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); updateLabels(); pack(); Dialogs.center(this); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } public int getHeight() { int y1 = getY1(); int y2 = getY2(); if (y1 != -1 && y2 != -1 && y1 >= 0 && y2 >= y1 && y2 < height && y1 <= y2) { return y2 - y1 + 1; } else { return -1; } } /** * Attempts to convert the content of the argument text component * to an int; if successful, returns that int, otherwise * -1 is returned. * @param textField the text component that is supposed to hold an int value * @return int representation of the text component's data */ private int getValue(TextComponent textField) { try { return Integer.parseInt(textField.getText()); } catch (NumberFormatException nfe) { return -1; } } /** * Returns the width of the to-be-cropped image as given by the * current values in the text fields for left column and right column. * Computes the width from those values and returns it or returns -1 * if the data in the text fields is not valid for some reason. * @return width of cropped image or -1 if information is invalid */ public int getWidth() { int x1 = getX1(); int x2 = getX2(); if (x1 != -1 && x2 != -1 && x1 >= 0 && x2 >= x1 && x2 < width && x1 <= x2) { return x2 - x1 + 1; } else { return -1; } } public int getX1() { return getValue(x1); } public int getX2() { return getValue(x2); } public int getY1() { return getValue(y1); } public int getY2() { return getValue(y2); } public boolean hasPressedOk() { return pressedOk; } /** * Computes width and height of new image and updates the * corresponding labels. * The labels will either display width and height or a single * dash if the data in the text fields is invalid. */ private void updateLabels() { String text; boolean enabled = true; int valueWidth = getWidth(); if (valueWidth == -1) { text = "-"; enabled = false; } else { text = Integer.toString(valueWidth); } newWidth.setText(text); int valueHeight = getHeight(); if (valueHeight == -1) { text = "-"; enabled = false; } else { text = Integer.toString(valueHeight); } newHeight.setText(text); ok.setEnabled(enabled); } public void keyPressed(KeyEvent e) { updateLabels(); } public void keyReleased(KeyEvent e) { updateLabels(); } public void keyTyped(KeyEvent e) { updateLabels(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/GammaCorrectionDialog.java0000664000000000000000000000610107741250134030065 0ustar /* * GammaCorrectionDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Dialog; import java.awt.Frame; import java.awt.Label; import java.awt.Panel; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import net.sourceforge.jiu.apps.Strings; /** * A dialog to enter the parameters for a gamma correction operation. * @author Marco Schmidt */ public class GammaCorrectionDialog extends Dialog implements ActionListener, KeyListener { private Button ok; private Button cancel; private TextField gammaTextField; private boolean pressedOk; private Double result; private final double MAX_GAMMA; /** * Creates a GammaCorrectionDialog. * @param owner the Frame this dialog will belong to * @param strings Strings resource used for text messages * @param initialValue the value to be set when the dialog pops up * @param maxGamma the maximum allowed gamma value */ public GammaCorrectionDialog(Frame owner, Strings strings, double initialValue, double maxGamma) { super(owner, strings.get(Strings.ADJUST_GAMMA), true); MAX_GAMMA = maxGamma; Panel panel = new Panel(new BorderLayout()); panel.add(new Label(strings.get(Strings.ENTER_GAMMA_VALUE)), BorderLayout.CENTER); gammaTextField = new TextField(Double.toString(initialValue)); gammaTextField.addKeyListener(this); panel.add(gammaTextField, BorderLayout.EAST); add(panel, BorderLayout.CENTER); panel = new Panel(); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); pack(); Dialogs.center(this); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; result = getValue(gammaTextField); setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } private Double getValue(TextField tf) { if (tf == null) { return null; } double d; try { d = (Double.valueOf(tf.getText())).doubleValue(); } catch(NumberFormatException nfe) { return null; } if (d <= 0.0 || d > MAX_GAMMA) { return null; } return new Double(d); } public Double getValue() { return result; } public boolean hasPressedOk() { return pressedOk; } public void handleKeys(KeyEvent e) { ok.setEnabled(getValue(gammaTextField) != null); } public void keyPressed(KeyEvent e) { handleKeys(e); } public void keyReleased(KeyEvent e) { handleKeys(e); } public void keyTyped(KeyEvent e) { handleKeys(e); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/IntegerDialog.java0000664000000000000000000001002610377273166026422 0ustar /* * IntegerDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Dialog; import java.awt.Frame; import java.awt.Label; import java.awt.Panel; import java.awt.Scrollbar; import java.awt.TextComponent; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; /** * An AWT dialog to select an int value from a given interval. * * @author Marco Schmidt */ public class IntegerDialog extends Dialog implements ActionListener, AdjustmentListener, KeyListener { private Button cancel; private int maxValue; private int minValue; private Button ok; private Integer result; private Scrollbar scrollbar; private TextComponent valueTextField; /** * Creates an IntegerDialog, a modal dialog that lets the user select one int * value from a given interval. * * @param owner the {@link java.awt.Frame} this dialog will belong to * @param title the text that will be displayed in the title bar of the dialog * @param message the message text that will be displayed in the upper part of the dialog * @param minValue the minimum allowed int value to be selected by the user * @param initialValue the int value that is selected when the dialog first pops up * @param maxValue the maximum allowed int value to be selected by the user * @param okText the value for OK (just "OK" in English * programs, may be different for other natural languages * @param cancelText the value for Cancel (just "Cancel" in English * programs, may be different for other natural languages */ public IntegerDialog(Frame owner, String title, String message, int minValue, int initialValue, int maxValue, String okText, String cancelText) { super(owner, title, true); this.minValue = minValue; this.maxValue = maxValue; add(new Label(message), BorderLayout.NORTH); Panel panel = new Panel(new BorderLayout()); scrollbar = new Scrollbar(Scrollbar.HORIZONTAL, initialValue, 1, minValue, maxValue + 1); scrollbar.addAdjustmentListener(this); panel.add(scrollbar, BorderLayout.CENTER); valueTextField = new TextField(Integer.toString(initialValue), 6); valueTextField.addKeyListener(this); panel.add(valueTextField, BorderLayout.EAST); add(panel, BorderLayout.CENTER); panel = new Panel(); ok = new Button(okText); ok.addActionListener(this); cancel = new Button(cancelText); cancel.addActionListener(this); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); pack(); Dialogs.center(this); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { result = new Integer(scrollbar.getValue()); setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } public void adjustmentValueChanged(AdjustmentEvent e) { valueTextField.setText(Integer.toString(scrollbar.getValue())); } public Integer getValue() { return result; } public void handleKeys(KeyEvent e) { setScrollbarFromTextField(); } public void keyPressed(KeyEvent e) { handleKeys(e); } public void keyReleased(KeyEvent e) { handleKeys(e); } public void keyTyped(KeyEvent e) { handleKeys(e); } private void setScrollbarFromTextField() { String text = valueTextField.getText().trim(); int number; try { number = Integer.parseInt(text); } catch (NumberFormatException nfe) { return; } if (number < minValue || number > maxValue) { return; } scrollbar.setValue(number); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/ScaleDialog.java0000664000000000000000000001341407741250134026047 0ustar /* * ScaleDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Checkbox; import java.awt.Choice; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.Rectangle; import java.awt.TextComponent; import java.awt.TextField; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import net.sourceforge.jiu.apps.Strings; /** * A dialog to enter the parameters for an image scaling operation. * @author Marco Schmidt */ public class ScaleDialog extends Dialog implements ActionListener, KeyListener { private Button ok; private Button cancel; private TextComponent heightTextField; private TextComponent widthTextField; private Checkbox maintainAspectRatio; private Choice types; private boolean pressedOk; private String oldWidthString; private String oldHeightString; private int oldWidth; private int oldHeight; private int type; /** * Creates an InfoDialog, a modal dialog to display a text message, centered on the desktop. * @param owner the Frame this dialog will belong to * @param strings the Strings resource used for text messages * @param width the current width of the image * @param height the current height of the image * @param pickType determines whether the will be a Choice box for picking the type of scaling algorithm * @param typeNames names of the image scaling algorithms * @param initialType algorithm type to be initially selected */ public ScaleDialog(Frame owner, Strings strings, int width, int height, boolean pickType, String[] typeNames, int initialType) { super(owner, strings.get(Strings.SCALE_IMAGE), true); pressedOk = false; oldWidth = width; oldWidthString = Integer.toString(oldWidth); oldHeight = height; oldHeightString = Integer.toString(oldHeight); Panel panel = new Panel(); panel.setLayout(new GridLayout(0, 2)); Label widthLabel = new Label(strings.get(Strings.NEW_WIDTH)); widthTextField = new TextField(Integer.toString(width), 6); widthTextField.addKeyListener(this); Label heightLabel = new Label(strings.get(Strings.NEW_HEIGHT)); heightTextField = new TextField(Integer.toString(height), 6); heightTextField.addKeyListener(this); panel.add(widthLabel); panel.add(widthTextField); panel.add(heightLabel); panel.add(heightTextField); panel.add(new Label("")); maintainAspectRatio = new Checkbox(strings.get(Strings.MAINTAIN_ASPECT_RATIO), true); panel.add(maintainAspectRatio); type = initialType; if (pickType) { panel.add(new Label(strings.get(Strings.METHOD))); types = new Choice(); for (int i = 0; i < typeNames.length; i++) { types.add(typeNames[i]); } types.select(initialType); panel.add(types); } add(panel, BorderLayout.CENTER); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel = new Panel(); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); pack(); center(); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } /** * Centers the dialog on screen. */ public void center() { Rectangle rect = getBounds(); int width = rect.width; int height = rect.height; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((screenSize.width / 2) - (width / 2), (screenSize.height / 2) - (height / 2)); } public int getHeightValue() { return getValue(heightTextField); } public int getType() { if (types == null) { return type; } else { return types.getSelectedIndex(); } } private int getValue(TextComponent textField) { try { return Integer.parseInt(textField.getText()); } catch (NumberFormatException nfe) { return -1; } } public int getWidthValue() { return getValue(widthTextField); } public boolean hasPressedOk() { return pressedOk; } public void handleKeys(KeyEvent e) { if (e.getSource() == widthTextField) { String text = widthTextField.getText(); if (maintainAspectRatio.getState() && (!text.equals(oldWidthString))) { // compute height from current width int w = getValue(widthTextField); if (w > 0) { oldHeightString = Integer.toString((int)(w * (float)oldHeight / (float)oldWidth)); heightTextField.setText(oldHeightString); } } } else if (e.getSource() == heightTextField) { String text = heightTextField.getText(); if (maintainAspectRatio.getState() && (!text.equals(oldHeightString))) { // compute width from current height int h = getValue(heightTextField); if (h > 0) { oldWidthString = Integer.toString((int)(h * (float)oldWidth / (float)oldHeight)); widthTextField.setText(oldWidthString); } } } oldWidthString = widthTextField.getText(); oldHeightString = heightTextField.getText(); } public void keyPressed(KeyEvent e) { handleKeys(e); } public void keyReleased(KeyEvent e) { handleKeys(e); } public void keyTyped(KeyEvent e) { handleKeys(e); } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/MapToArbitraryPaletteDialog.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/MapToArbitraryPaletteDialog0000664000000000000000000001273407741250134030323 0ustar /* * MapToArbitraryPaletteDialog * * Copyright (c) 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Checkbox; import java.awt.CheckboxGroup; import java.awt.Choice; import java.awt.Dialog; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import net.sourceforge.jiu.apps.Strings; import net.sourceforge.jiu.color.dithering.ErrorDiffusionDithering; /** * A dialog to enter the parameters for an operation to map an RGB truecolor * image to any given palette. * * @since 0.5.0 * @author Marco Schmidt * @see net.sourceforge.jiu.color.quantization.ArbitraryPaletteQuantizer */ public class MapToArbitraryPaletteDialog extends Dialog implements ActionListener { public static final int PALETTE_FILE = 0; public static final int PALETTE_WEBSAFE = 1; public static final int PALETTE_PALM_256_COLORS = 2; public static final int PALETTE_PALM_16_COLORS = 3; public static final int PALETTE_PALM_16_GRAY = 4; public static final int PALETTE_PALM_4_GRAY = 5; public static final int NUM_PALETTE_TYPES = 6; private static final int[] PALETTE_STRING_CONSTANTS = { Strings.PALETTE_FROM_FILE, Strings.WEBSAFE_PALETTE, Strings.PALETTE_PALM_256_COLORS, Strings.PALETTE_PALM_16_COLORS, Strings.PALETTE_PALM_16_GRAY, Strings.PALETTE_PALM_4_GRAY, }; private static final int[] DITHERING_STRING_CONSTANTS = { Strings.DITHERING_NONE, Strings.FLOYD_STEINBERG_ERROR_DIFFUSION, Strings.STUCKI_ERROR_DIFFUSION, Strings.BURKES_ERROR_DIFFUSION, Strings.SIERRA_ERROR_DIFFUSION, Strings.JARVIS_JUDICE_NINKE_ERROR_DIFFUSION, Strings.STEVENSON_ARCE_ERROR_DIFFUSION }; private static final int[] ERROR_DIFFUSION_TYPES = { ErrorDiffusionDithering.TYPE_FLOYD_STEINBERG, ErrorDiffusionDithering.TYPE_STUCKI, ErrorDiffusionDithering.TYPE_BURKES, ErrorDiffusionDithering.TYPE_SIERRA, ErrorDiffusionDithering.TYPE_JARVIS_JUDICE_NINKE, ErrorDiffusionDithering.TYPE_STEVENSON_ARCE }; private Button ok; private Button cancel; private Checkbox[] checkboxes; private CheckboxGroup paletteType; private Choice dithering; private boolean pressedOk; /** * @param owner the Frame this dialog will belong to */ public MapToArbitraryPaletteDialog(Frame owner, Strings strings) { super(owner, strings.get(Strings.MAP_TO_ARBITRARY_PALETTE), true); pressedOk = false; // 1 (CENTER) main panel with components for the various options Panel mainPanel = new Panel(new GridLayout(0, 1)); // 1.1 Label with message text Panel panel = new Panel(new GridLayout(0, 1)); panel.add(new Label(strings.get(Strings.CHOOSE_PALETTE_TYPE))); mainPanel.add(panel); // 1.2 radio buttons (CheckboxGroup) with palette type panel = new Panel(new GridLayout(0, 1)); paletteType = new CheckboxGroup(); checkboxes = new Checkbox[PALETTE_STRING_CONSTANTS.length]; boolean selected = true; for (int i = 0; i < NUM_PALETTE_TYPES; i++) { checkboxes[i] = new Checkbox(strings.get(PALETTE_STRING_CONSTANTS[i]), paletteType, selected); selected = false; panel.add(checkboxes[i]); } mainPanel.add(panel); // 1.3 Choice with dithering types panel = new Panel(); panel.add(new Label(strings.get(Strings.DITHERING_METHOD))); dithering = new Choice(); for (int i = 0; i < DITHERING_STRING_CONSTANTS.length; i++) { dithering.add(strings.get(DITHERING_STRING_CONSTANTS[i])); } dithering.select(1); panel.add(dithering); mainPanel.add(panel); add(mainPanel, BorderLayout.CENTER); // 2 (SOUTH) buttons OK and Cancel panel = new Panel(); // 2.1 OK button ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); panel.add(ok); // 2.2 Cancel button cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel.add(cancel); add(panel, BorderLayout.SOUTH); pack(); Dialogs.center(this); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } /** * If the use of error diffusion was selected, this method * returns on of the ErrorDiffusionDithering TYPE constants */ public int getErrorDiffusionType() { int sel = dithering.getSelectedIndex(); if (sel > 0 && sel <= ERROR_DIFFUSION_TYPES.length) { return ERROR_DIFFUSION_TYPES[sel - 1]; } else { return -1; } } /** * Return the palette type (one of the PALETTE_xyz constants of this class) * that is currently selected in the dialog. */ public int getPaletteType() { for (int i = 0; i < checkboxes.length; i++) { if (checkboxes[i].getState()) { return i; } } return -1; } /** * Returns true if the OK button was pressed, false if * it was the Cancel button. */ public boolean hasPressedOk() { return pressedOk; } /** * Returns whether the use of one of the error diffusion * algorithms is selected in the dialog. */ public boolean useErrorDiffusion() { return dithering.getSelectedIndex() > 0; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/package.html0000664000000000000000000000033507741250134025314 0ustar

AWT dialogs that are used in the jiuawt application. ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/HueSaturationValueDialog.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/HueSaturationValueDialog.ja0000664000000000000000000001162707741250134030265 0ustar /* * HueSaturationValueDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Checkbox; import java.awt.Color; import java.awt.Dialog; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.TextComponent; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import net.sourceforge.jiu.apps.Strings; /** * A dialog to enter the parameters for an hue/saturation/value adjustment operation. * Saturation and value are specified as percentage values between -100 and 100, * where 0 means no change. * Hue can be specified optionally (a Choice component must be checked so * that the hue value will be used); it is a value between 0 and 359. * * @since 0.5.0 * @author Marco Schmidt * @see net.sourceforge.jiu.color.adjustment.HueSaturationValue */ public class HueSaturationValueDialog extends Dialog implements ActionListener, ItemListener, KeyListener { private Button ok; private Button cancel; private Panel colorPanel; private TextField hue; private TextField saturation; private TextField value; private Checkbox setHue; private boolean pressedOk; /** * @param owner the Frame this dialog will belong to */ public HueSaturationValueDialog(Frame owner, Strings strings, boolean initialSetHue, int h, int s, int v) { super(owner, strings.get(Strings.ADJUST_HUE_SATURATION_AND_VALUE), true); pressedOk = false; Panel panel = new Panel(); panel.setLayout(new GridLayout(0, 2)); setHue = new Checkbox(strings.get(Strings.SET_HUE), initialSetHue); setHue.addItemListener(this); panel.add(setHue); colorPanel = new Panel(); panel.add(colorPanel); panel.add(new Label(strings.get(Strings.HUE))); hue = new TextField(Integer.toString(h)); hue.addKeyListener(this); panel.add(hue); panel.add(new Label(strings.get(Strings.SATURATION))); saturation = new TextField(Integer.toString(s)); saturation.addKeyListener(this); panel.add(saturation); panel.add(new Label(strings.get(Strings.VALUE))); value = new TextField(Integer.toString(v)); value.addKeyListener(this); panel.add(value); add(panel, BorderLayout.CENTER); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel = new Panel(); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); updateTextFields(); pack(); Dialogs.center(this); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } public int getHue() { return getValue(hue); } public int getSaturation() { return getValue(saturation); } public int getValue() { return getValue(value); } /** * Attempts to convert the content of the argument text component * to an int; if successful, returns that int, otherwise * -1000 is returned. * @param textField the text component that is supposed to hold an int value * @return int representation of the text component's data */ private int getValue(TextComponent textField) { try { return Integer.parseInt(textField.getText()); } catch (NumberFormatException nfe) { return -1000; } } public boolean hasPressedOk() { return pressedOk; } public void itemStateChanged(ItemEvent e) { updateTextFields(); } public boolean isHueSet() { return setHue.getState(); } /** * Computes width and height of new image and updates the * corresponding labels. * The labels will either display width and height or a single * dash if the data in the text fields is invalid. */ private void updateTextFields() { hue.setEnabled(setHue.getState()); int h = getValue(hue); int s = getValue(saturation); int v = getValue(value); boolean enabled = s >= -100 && s <= 100 && v >= -100 && v <= 100; if (setHue.getState()) { enabled = enabled && h >= 0 && h <= 359; } ok.setEnabled(enabled); Color color = new Color(Color.HSBtoRGB(h / 360f, 1.0f, 1.0f)); colorPanel.setBackground(color); } public void keyPressed(KeyEvent e) { updateTextFields(); } public void keyReleased(KeyEvent e) { updateTextFields(); } public void keyTyped(KeyEvent e) { updateTextFields(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/ShearDialog.java0000664000000000000000000000706007741250134026062 0ustar /* * ShearDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Dialog; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import net.sourceforge.jiu.apps.Strings; import net.sourceforge.jiu.geometry.Shear; /** * An AWT dialog to enter the angle for a shearing operation. * @author Marco Schmidt */ public class ShearDialog extends Dialog implements ActionListener, KeyListener { private Button ok; private Button cancel; private TextField angleTextField; private boolean pressedOk; private Double result; private Label newWidthLabel; private int imageWidth; private int imageHeight; /** * Creates a ShearDialog. * @param owner the Frame this dialog will belong to */ public ShearDialog(Frame owner, Strings strings, double initialValue, int imageWidth, int imageHeight) { super(owner, strings.get(Strings.SHEAR_IMAGE) + " (" + imageWidth + " x " + imageHeight + ")", true); this.imageWidth = imageWidth; this.imageHeight = imageHeight; Panel panel = new Panel(new GridLayout(0, 2)); panel.add(new Label(strings.get(Strings.SHEAR_ENTER_ANGLE))); angleTextField = new TextField(Double.toString(initialValue)); angleTextField.addKeyListener(this); panel.add(angleTextField); panel.add(new Label(strings.get(Strings.NEW_WIDTH))); newWidthLabel = new Label(""); panel.add(newWidthLabel); add(panel, BorderLayout.CENTER); panel = new Panel(); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); handleKeys(null); pack(); Dialogs.center(this); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; result = getValue(angleTextField); setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } private Double getValue(TextField tf) { if (tf == null) { return null; } double d; try { d = (Double.valueOf(tf.getText())).doubleValue(); } catch(NumberFormatException nfe) { return null; } if (d <= -90.0 || d >= 90.0) { return null; } return new Double(d); } public Double getValue() { return result; } public boolean hasPressedOk() { return pressedOk; } public void handleKeys(KeyEvent e) { Double d = getValue(angleTextField); double angle = -90.0; if (d != null) { angle = d.doubleValue(); } String labelText; if (angle > -90.0 && angle < 90.0) { ok.setEnabled(true); int newWidth = Shear.computeNewImageWidth(imageWidth, imageHeight, angle); labelText = Integer.toString(newWidth); } else { ok.setEnabled(false); labelText = "-"; } newWidthLabel.setText(labelText); } public void keyPressed(KeyEvent e) { handleKeys(e); } public void keyReleased(KeyEvent e) { handleKeys(e); } public void keyTyped(KeyEvent e) { handleKeys(e); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/OctreeDialog.java0000664000000000000000000001246007741250134026241 0ustar /* * OctreeDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Choice; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.Rectangle; import java.awt.TextField; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import net.sourceforge.jiu.apps.Strings; import net.sourceforge.jiu.color.dithering.ErrorDiffusionDithering; /** * A dialog to enter the parameters for an Octree color quantization operation. * It also allows to enter the optional algorithms that can be applied in combination with Octree. * * @author Marco Schmidt * @since 0.6.0 * @see MedianCutDialog */ public class OctreeDialog extends Dialog implements ActionListener, KeyListener { public final int[] DITHERING_STRINGS = { Strings.DITHERING_NONE, Strings.FLOYD_STEINBERG_ERROR_DIFFUSION, Strings.STUCKI_ERROR_DIFFUSION, Strings.BURKES_ERROR_DIFFUSION, Strings.SIERRA_ERROR_DIFFUSION, Strings.JARVIS_JUDICE_NINKE_ERROR_DIFFUSION, Strings.STEVENSON_ARCE_ERROR_DIFFUSION }; public final int[] DITHERING_TYPES = { 0, ErrorDiffusionDithering.TYPE_FLOYD_STEINBERG, ErrorDiffusionDithering.TYPE_STUCKI, ErrorDiffusionDithering.TYPE_BURKES, ErrorDiffusionDithering.TYPE_SIERRA, ErrorDiffusionDithering.TYPE_JARVIS_JUDICE_NINKE, ErrorDiffusionDithering.TYPE_STEVENSON_ARCE }; private Button ok; private Button cancel; private TextField numColorsField; private Choice outputColorType; private Choice dithering; private boolean pressedOk; /** * Creates a modal dialog to enter the parameter. * @param owner the parent of this modal dialog * @param strings an object to get String constants in the current language * @param numColors the number of colors in the resulting image * @param paletted if true, the output image will be paletted, otherwise truecolor */ public OctreeDialog(Frame owner, Strings strings, int numColors, boolean paletted) { super(owner, strings.get(Strings.OCTREE_COLOR_QUANTIZATION), true); pressedOk = false; Panel panel = new Panel(); panel.setLayout(new GridLayout(0, 2)); panel.add(new Label(strings.get(Strings.NUM_COLORS))); numColorsField = new TextField(Integer.toString(numColors), 6); numColorsField.addKeyListener(this); panel.add(numColorsField); /*panel.add(new Label(strings.get(Strings.OUTPUT_COLOR_TYPE))); outputColorType = new Choice(); outputColorType.add(strings.get(Strings.OUTPUT_COLOR_TYPE_PALETTED)); outputColorType.add(strings.get(Strings.OUTPUT_COLOR_TYPE_RGB)); outputColorType.select(paletted ? 0 : 1); panel.add(outputColorType);*/ panel.add(new Label(strings.get(Strings.DITHERING_METHOD))); dithering = new Choice(); for (int i = 0; i < DITHERING_STRINGS.length; i++) { dithering.add(strings.get(DITHERING_STRINGS[i])); } dithering.select(1); panel.add(dithering); add(panel, BorderLayout.CENTER); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); panel = new Panel(); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); pack(); center(); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } /** * Centers the dialog on screen. */ public void center() { Rectangle rect = getBounds(); int width = rect.width; int height = rect.height; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((screenSize.width / 2) - (width / 2), (screenSize.height / 2) - (height / 2)); } public int getErrorDiffusion() { int sel = dithering.getSelectedIndex(); if (sel > 0) { return DITHERING_TYPES[sel]; } else { return -1; } } private int getIntValue(TextField textField) { try { return Integer.parseInt(textField.getText()); } catch (NumberFormatException nfe) { return -1; } } public int getNumColors() { return getIntValue(numColorsField); } public boolean hasPressedOk() { return pressedOk; } public boolean isOutputTypePaletted() { return outputColorType.getSelectedIndex() == 0; } public void keyPressed(KeyEvent e) { updateOkButton(); } public void keyReleased(KeyEvent e) { updateOkButton(); } public void keyTyped(KeyEvent e) { updateOkButton(); } private void updateOkButton() { int nc = getNumColors(); boolean enabled = nc >= 1 && nc <= 256; ok.setEnabled(enabled); } public boolean useErrorDiffusion() { return dithering.getSelectedIndex() > 0; } public boolean useNoDithering() { return dithering.getSelectedIndex() == 0; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/Dialogs.java0000664000000000000000000000371410572433445025270 0ustar /* * Dialogs * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.Dimension; import java.awt.Frame; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Window; /** * Convenience class that provides a number of static helper methods to deal with dialogs. * @author Marco Schmidt */ public class Dialogs { private Dialogs() { } /** * Centers the argument window on screen. */ public static void center(Window window) { if (window == null) { return; } Rectangle rect = window.getBounds(); int width = rect.width; int height = rect.height; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); window.setLocation((screenSize.width / 2) - (width / 2), (screenSize.height / 2) - (height / 2)); } /** * Creates a new IntegerDialog, displays it and returns the Integer * value specified by the user (or null if the dialog was canceled). * @param owner frame from which the dialog is spawned * @param title text for the title bar of the dialog * @param message message displayed in the dialog * @param minValue minimal allowed integer value to be entered by the user * @param initialValue initial integer value shown in the dialog * @param maxValue maximal allowed integer value to be entered by the user * @param okText the text for the OK button * @param cancelText the text for the cancel button * @return the specified integer value or null if the Cancel button was pressed */ public static Integer getInteger(Frame owner, String title, String message, int minValue, int initialValue, int maxValue, String okText, String cancelText) { IntegerDialog dialog = new IntegerDialog(owner, title, message, minValue, initialValue, maxValue, okText, cancelText); dialog.setVisible(true); return dialog.getValue(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/MedianCutDialog.java0000664000000000000000000002050307741250134026666 0ustar /* * MedianCutDialog * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Choice; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.Rectangle; import java.awt.TextField; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import net.sourceforge.jiu.apps.Strings; import net.sourceforge.jiu.color.dithering.ErrorDiffusionDithering; import net.sourceforge.jiu.color.quantization.MedianCutQuantizer; /** * A dialog to enter the parameters for a Median Cut color quantization operation. * It also allows to enter the optional algorithms that can be applied in combination with Median Cut. * @author Marco Schmidt */ public class MedianCutDialog extends Dialog implements ActionListener, ItemListener, KeyListener { public final int[][] METHODS = { { MedianCutQuantizer.METHOD_REPR_COLOR_WEIGHTED_AVERAGE, MedianCutQuantizer.METHOD_REPR_COLOR_AVERAGE, MedianCutQuantizer.METHOD_REPR_COLOR_MEDIAN }, { Strings.METHOD_REPR_COLOR_WEIGHTED_AVERAGE, Strings.METHOD_REPR_COLOR_AVERAGE, Strings.METHOD_REPR_COLOR_MEDIAN } }; public final int[] ERROR_DIFFUSION_STRINGS = { Strings.FLOYD_STEINBERG_ERROR_DIFFUSION, Strings.STUCKI_ERROR_DIFFUSION, Strings.BURKES_ERROR_DIFFUSION, Strings.SIERRA_ERROR_DIFFUSION, Strings.JARVIS_JUDICE_NINKE_ERROR_DIFFUSION, Strings.STEVENSON_ARCE_ERROR_DIFFUSION }; public final int[] ERROR_DIFFUSION_TYPES = { ErrorDiffusionDithering.TYPE_FLOYD_STEINBERG, ErrorDiffusionDithering.TYPE_STUCKI, ErrorDiffusionDithering.TYPE_BURKES, ErrorDiffusionDithering.TYPE_SIERRA, ErrorDiffusionDithering.TYPE_JARVIS_JUDICE_NINKE, ErrorDiffusionDithering.TYPE_STEVENSON_ARCE }; private Button ok; private Button cancel; private TextField numColorsField; private Choice outputColorType; private Choice reprColorMethod; private Choice algorithms; private Choice errorDiffusion; private TextField numPassesField; private TextField tauField; private boolean pressedOk; /** * Creates a modal dialog to enter the parameter. * @param owner the parent of this modal dialog * @param strings an object to get String constants in the current language * @param numColors the number of colors in the resulting image * @param representativeColorMethod the method to determine the representative color from a set of colors * @param paletted if true, the output image will be paletted, otherwise truecolor * @param numPasses number of contour removal iterations * @param initialTau maximum distance for two colors to be considered similar in contour removal */ public MedianCutDialog(Frame owner, Strings strings, int numColors, int representativeColorMethod, boolean paletted, int numPasses, double initialTau) { super(owner, strings.get(Strings.MEDIAN_CUT_COLOR_QUANTIZATION), true); pressedOk = false; Panel panel = new Panel(); panel.setLayout(new GridLayout(0, 2)); panel.add(new Label(strings.get(Strings.NUM_COLORS))); numColorsField = new TextField(Integer.toString(numColors), 6); numColorsField.addKeyListener(this); panel.add(numColorsField); panel.add(new Label(strings.get(Strings.OUTPUT_COLOR_TYPE))); outputColorType = new Choice(); outputColorType.add(strings.get(Strings.OUTPUT_COLOR_TYPE_PALETTED)); outputColorType.add(strings.get(Strings.OUTPUT_COLOR_TYPE_RGB)); outputColorType.select(paletted ? 0 : 1); panel.add(outputColorType); panel.add(new Label(strings.get(Strings.METHOD_REPR_COLOR))); reprColorMethod = new Choice(); for (int i = 0; i < METHODS[0].length; i++) { reprColorMethod.add(strings.get(METHODS[1][i])); if (representativeColorMethod == METHODS[0][i]) { reprColorMethod.select(i); } } panel.add(reprColorMethod); panel.add(new Label(strings.get(Strings.OUTPUT_QUALITY_IMPROVEMENT_ALGORITHM))); algorithms = new Choice(); algorithms.add(strings.get(Strings.ALGORITHMS_NONE)); algorithms.add(strings.get(Strings.ERROR_DIFFUSION)); algorithms.add(strings.get(Strings.CONTOUR_REMOVAL)); algorithms.select(1); algorithms.addItemListener(this); panel.add(algorithms); panel.add(new Label(strings.get(Strings.ERROR_DIFFUSION))); errorDiffusion = new Choice(); for (int i = 0; i < ERROR_DIFFUSION_STRINGS.length; i++) { errorDiffusion.add(strings.get(ERROR_DIFFUSION_STRINGS[i])); } errorDiffusion.select(0); panel.add(errorDiffusion); panel.add(new Label(strings.get(Strings.CONTOUR_REMOVAL_NUM_PASSES))); numPassesField = new TextField(Integer.toString(numPasses)); numPassesField.addKeyListener(this); panel.add(numPassesField); panel.add(new Label(strings.get(Strings.CONTOUR_REMOVAL_TAU))); tauField = new TextField(Double.toString(initialTau)); tauField.addKeyListener(this); panel.add(tauField); add(panel, BorderLayout.CENTER); ok = new Button(strings.get(Strings.OK)); ok.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); updateStates(); panel = new Panel(); panel.add(ok); panel.add(cancel); add(panel, BorderLayout.SOUTH); pack(); center(); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { pressedOk = true; setVisible(false); } else if (e.getSource() == cancel) { setVisible(false); } } /** * Centers the dialog on screen. */ public void center() { Rectangle rect = getBounds(); int width = rect.width; int height = rect.height; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((screenSize.width / 2) - (width / 2), (screenSize.height / 2) - (height / 2)); } public int getErrorDiffusion() { if (algorithms.getSelectedIndex() == 1) { return ERROR_DIFFUSION_TYPES[errorDiffusion.getSelectedIndex()]; } else { return -1; } } private double getDoubleValue(TextField textField) { try { Double d = new Double(textField.getText()); return d.doubleValue(); } catch (NumberFormatException nfe) { return Double.NaN; } } private int getIntValue(TextField textField) { try { return Integer.parseInt(textField.getText()); } catch (NumberFormatException nfe) { return -1; } } public int getNumColors() { return getIntValue(numColorsField); } public int getNumPasses() { return getIntValue(numPassesField); } public int getReprColorMethod() { return METHODS[0][reprColorMethod.getSelectedIndex()]; } public double getTau() { return getDoubleValue(tauField); } public boolean hasPressedOk() { return pressedOk; } public boolean isOutputTypePaletted() { return outputColorType.getSelectedIndex() == 0; } public void itemStateChanged(ItemEvent event) { if (event.getSource() == algorithms) { updateStates(); } } public void keyPressed(KeyEvent e) { updateOkButton(); } public void keyReleased(KeyEvent e) { updateOkButton(); } public void keyTyped(KeyEvent e) { updateOkButton(); } private void updateOkButton() { int nc = getNumColors(); boolean enabled = nc >= 1 && nc <= 256; if (enabled && algorithms.getSelectedIndex() == 2) { enabled = getTau() >= 0.0 && getNumPasses() >= 1; } ok.setEnabled(enabled); } private void updateStates() { int algorithmSelection = algorithms.getSelectedIndex(); boolean ed = algorithmSelection == 1; errorDiffusion.setEnabled(ed); ed = algorithmSelection == 2; tauField.setEnabled(ed); numPassesField.setEnabled(ed); } public boolean useContourRemoval() { return algorithms.getSelectedIndex() == 2; } public boolean useErrorDiffusion() { return algorithms.getSelectedIndex() == 1; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/YesNoDialog.java0000664000000000000000000000552707741250134026063 0ustar /* * YesNoDialog * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Dialog; import java.awt.Frame; import java.awt.Label; import java.awt.Panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import net.sourceforge.jiu.apps.Strings; /** * A dialog that asks a question and offers a Yes and a No button * (and optionally a Cancel button). * @author Marco Schmidt * @since 0.11.0 */ public class YesNoDialog extends Dialog implements ActionListener { /** * Will be returned in {@link #getResult} if the YES button was chosen. */ public static final int RESULT_YES = 0; /** * Will be returned in {@link #getResult} if the NO button was chosen. */ public static final int RESULT_NO = 1; /** * Will be returned in {@link #getResult} if the CANCEL button was chosen. */ public static final int RESULT_CANCEL = 2; private Button yes; private Button no; private Button cancel; private int result; /** * Creates a new YesNoDialog object and shows it centered on the screen. * @param owner the frame that owns this modal dialog * @param strings the String resources * @param titleIndex the index into the String resource of the title text * @param questionIndex the index into the String resource of the question text * @param includeCancel determines whether a third button 'Cancel' will be included */ public YesNoDialog(Frame owner, Strings strings, int titleIndex, int questionIndex, boolean includeCancel) { super(owner, strings.get(titleIndex), true); add(new Label(strings.get(questionIndex)), BorderLayout.CENTER); yes = new Button(strings.get(Strings.YES)); yes.addActionListener(this); no = new Button(strings.get(Strings.NO)); no.addActionListener(this); cancel = new Button(strings.get(Strings.CANCEL)); cancel.addActionListener(this); Panel panel = new Panel(); panel.add(yes); panel.add(no); if (includeCancel) { panel.add(cancel); } add(panel, BorderLayout.SOUTH); pack(); Dialogs.center(this); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == yes) { result = RESULT_YES; setVisible(false); } else if (e.getSource() == no) { result = RESULT_NO; setVisible(false); } else if (e.getSource() == cancel) { result = RESULT_CANCEL; setVisible(false); } } /** * Returns one of the RESULT_xyz constants of this class. * @return the RESULT constant of the button which the user has chosen */ public int getResult() { return result; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/dialogs/InfoDialog.java0000664000000000000000000000434207741250134025713 0ustar /* * InfoDialog * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt.dialogs; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.Panel; import java.awt.Rectangle; import java.awt.TextArea; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * A modal AWT dialog that displays text in a non-editable text area component * (so that it can be selected and easily copied to the system's clipboard). * Provides an OK button so that user can remove the dialog. * @author Marco Schmidt */ public class InfoDialog extends Dialog implements ActionListener { private Button ok; private TextArea textArea; /** * Creates an InfoDialog, a modal dialog to display a text message, centered on the desktop. * @param owner the Frame this dialog will belong to * @param title the text that will be displayed in the title bar of the dialog * @param text the message text that will be displayed in the main part of the dialog */ public InfoDialog(Frame owner, String title, String text) { super(owner, title, true); ok = new Button("OK"); ok.addActionListener(this); Panel panel = new Panel(); panel.add(ok); add(panel, BorderLayout.SOUTH); textArea = new TextArea(text); textArea.setEditable(false); textArea.setSize(textArea.getMinimumSize()); //ScrollPane scrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); //scrollPane.add(textArea); add(textArea); pack(); center(); } /** * Hides (closes) this dialog if the OK button was source of the action event * (e.g. if the button was pressed). */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { setVisible(false); } } /** * Centers the dialog on screen. */ public void center() { Rectangle rect = getBounds(); int width = rect.width; int height = rect.height; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((screenSize.width / 2) - (width / 2), (screenSize.height / 2) - (height / 2)); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/BufferedRGB24Image.java0000664000000000000000000002366707741250134025457 0ustar /* * BufferedRGB24Image * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt; import java.awt.image.BufferedImage; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; /** * A bridge class to use {@link java.awt.image.BufferedImage} objects (class defined * in the standard runtime library, package java.awt.image) as * {@link net.sourceforge.jiu.data.RGB24Image} objects within JIU. * This class encapsulates a single {@link java.awt.image.BufferedImage} object. * It enables reusing existing BufferedImage objects as input or * output of JIU operations, * removing the necessity for the conversion step from java.awt.Image * to net.sourceforge.jiu.data.PixelImage (or vice versa) * and thus reducing memory consumption. * The name of this class is a combination of BufferedImage (the class of the object * that is encapsulated) and RGB24Image (the JIU image data interface). *

* Internally, this class uses {@link java.awt.image.BufferedImage}'s getRGB and * setRGB methods to access image data. * This approach is slower than working directly on the BufferedImage's data * buffers. * However, using getRGB and setRGB, this class will work with all types of BufferedImage objects. *

* Note that while the abstract java.awt.Image class existed from the very * beginning (version 1.0) of the Java runtime library, java.awt.image.BufferedImage * has not been added until version 1.2. *

Usage example

* This code snippet demonstrates to how combine functionality from Java's runtime * library with JIU by using this class. * Requires Java 1.4 or higher. * Obviously, BufferedRGB24Image objects can only be used with operations that * work on classes implementing RGB24Image. *
 *  import java.awt.image.BufferedImage;
 *  import java.io.File;
 *  import javax.imageio.ImageIO;
 *  import net.sourceforge.jiu.color.Invert;
 *  import net.sourceforge.jiu.data.PixelImage;
 *  import net.sourceforge.jiu.gui.awt.BufferedRGB24Image;
 *  ...
 *  BufferedImage bufferedImage = ImageIO.read(new File("image.jpg"));
 *  BufferedRGB24Image image = new BufferedRGB24Image(bufferedImage);
 *  Invert invert = new Invert();
 *  invert.setInputImage(image);
 *  invert.process();
 *  PixelImage outputImage = invert.getOutputImage();
 * 
* If you can be sure that an image object can be input and output * image at the same time (as is the case with some operations), you * can even work with only one BufferedRGB24Image object. * Invert is one of these operations, so the following would work: *
 *  Invert invert = new Invert();
 *  invert.setInputImage(image);
 *  invert.setOutputImage(image);
 *  invert.process();
 *  // image now is inverted
 * 
* * @author Marco Schmidt * @since 0.10.0 */ public class BufferedRGB24Image implements RGB24Image { private static final int RED_SHIFT = 16; private static final int GREEN_SHIFT = 8; private static final int BLUE_SHIFT = 0; /** * Masks for the three RGB channels. * RGB_CLEAR[i] is an int with all bits on, except for those occupied by the channel i. * RGB_CLEAR[i] can thus be used to bitwise AND an ARGB value so that the sample for channel i will be cleared. */ private static final int[] RGB_CLEAR = new int[3]; private static final int[] RGB_SHIFT = new int[3]; static { RGB_SHIFT[INDEX_RED] = RED_SHIFT; RGB_SHIFT[INDEX_GREEN] = GREEN_SHIFT; RGB_SHIFT[INDEX_BLUE] = BLUE_SHIFT; RGB_CLEAR[INDEX_RED] = 0xff00ffff; RGB_CLEAR[INDEX_GREEN] = 0xffff00ff; RGB_CLEAR[INDEX_BLUE] = 0xffffff00; } private final int HEIGHT; private final BufferedImage image; private final int WIDTH; /** * Creates a new BufferedRGB24Image object, storing the argument * BufferedImage object internally. * All image data access will be delegated to that BufferedImage object's methods. * @param bufferedImage the underlying BufferedImage object for this BufferedRGB24Image object */ public BufferedRGB24Image(BufferedImage bufferedImage) { image = bufferedImage; if (image == null) { throw new IllegalArgumentException("Argument image object must not be null."); } WIDTH = image.getWidth(); HEIGHT = image.getHeight(); } /** * Sets all the RGB samples in this image to the argument, keeping * the alpha value. * @param newValue all samples in the image will be set to this value */ public void clear(byte newValue) { final int RGB = (newValue & 0xff) | (newValue & 0xff) << 8 | (newValue & 0xff) << 16; for (int y = 0; y < getHeight(); y++) { for (int x = 0; x < getWidth(); x++) { int rgba = image.getRGB(x, y); rgba = (rgba & 0xff000000) | RGB; image.setRGB(x, y, rgba); } } } public void clear(int newValue) { clear((byte)newValue); } public void clear(int channelIndex, byte newValue) { final int MASK = RGB_CLEAR[channelIndex]; final int SAMPLE = (newValue & 0xff) << RGB_SHIFT[channelIndex]; for (int y = 0; y < getHeight(); y++) { for (int x = 0; x < getWidth(); x++) { int rgba = image.getRGB(x, y); rgba = (rgba & MASK) | SAMPLE; image.setRGB(x, y, rgba); } } } public void clear(int channelIndex, int newValue) { clear(channelIndex, (byte)newValue); } public PixelImage createCompatibleImage(int width, int height) { BufferedImage newBufferedImage = new BufferedImage(width, height, image.getType()); return new BufferedRGB24Image(newBufferedImage); } public PixelImage createCopy() { BufferedImage newBufferedImage = new BufferedImage(getWidth(), getHeight(), image.getType()); image.copyData(newBufferedImage.getRaster()); return new BufferedRGB24Image(newBufferedImage); } public long getAllocatedMemory() { /* actually, number of pixels times 4 is just a guess, BufferedImage allows for all kinds of data buffers; for a more accurate approximation these data buffers must be examined */ return 4L * (long)getWidth() * (long)getHeight(); } public int getBitsPerPixel() { return 24; } public byte getByteSample(int x, int y) { return getByteSample(0, x, y); } public byte getByteSample(int channelIndex, int x, int y) { return (byte)((image.getRGB(x, y) >> RGB_SHIFT[channelIndex]) & 0xff); } public void getByteSamples(int channelIndex, int x, int y, int w, int h, byte[] dest, int destOffset) { final int SHIFT = RGB_SHIFT[channelIndex]; int[] row = new int[w]; while (h-- > 0) { image.getRGB(x, y++, w, 1, row, 0, w); int columns = w; int rowIndex = 0; while (columns-- > 0) { dest[destOffset++] = (byte)((row[rowIndex++] >> SHIFT) & 0xff); } } } public void getByteSamples(int x, int y, int w, int h, byte[] dest, int destOffset) { getByteSamples(0, x, y, w, h, dest, destOffset); } public int getHeight() { return HEIGHT; } public Class getImageType() { return RGB24Image.class; } public int getMaxSample(int channel) { if (channel == INDEX_BLUE || channel == INDEX_RED || channel == INDEX_GREEN) { return 255; } else { throw new IllegalArgumentException("Not a valid channel index: " + channel); } } public int getNumChannels() { return 3; } public int getSample(int x, int y) { return getSample(0, x, y); } public int getSample(int channelIndex, int x, int y) { return (image.getRGB(x, y) >> RGB_SHIFT[channelIndex]) & 0xff; } public void getSamples(int x, int y, int w, int h, int[] dest, int destOffs) { getSamples(0, x, y, w, h, dest, destOffs); } public void getSamples(int channelIndex, int x, int y, int w, int h, int[] dest, int destOffs) { final int SHIFT = RGB_SHIFT[channelIndex]; int[] row = new int[w]; while (h-- > 0) { image.getRGB(x, y++, w, 1, row, 0, w); int columns = w; int rowIndex = 0; while (columns-- > 0) { dest[destOffs++] = (row[rowIndex++] >> SHIFT) & 0xff; } } } public int getWidth() { return WIDTH; } public void putByteSample(int channelIndex, int x, int y, byte newValue) { int argb = image.getRGB(x, y) & RGB_CLEAR[channelIndex]; image.setRGB(x, y, argb | ((newValue & 0xff) << RGB_SHIFT[channelIndex])); } public void putByteSample(int x, int y, byte newValue) { putByteSample(0, x, y, newValue); } public void putByteSamples(int channelIndex, int x, int y, int w, int h, byte[] src, int srcOffset) { final int SHIFT = RGB_SHIFT[channelIndex]; final int MASK = RGB_CLEAR[channelIndex]; int[] row = new int[w]; while (h-- > 0) { image.getRGB(x, y, w, 1, row, 0, w); int columns = w; int rowIndex = 0; while (columns-- > 0) { int argb = row[rowIndex] & MASK; row[rowIndex++] = argb | ((src[srcOffset++] & 0xff) << SHIFT); } image.setRGB(x, y++, w, 1, row, 0, w); } } public void putByteSamples(int x, int y, int w, int h, byte[] src, int srcOffset) { putByteSamples(0, x, y, w, h, src, srcOffset); } public void putSample(int x, int y, int newValue) { putSample(0, x, y, newValue); } public void putSample(int channelIndex, int x, int y, int newValue) { int argb = image.getRGB(x, y) & RGB_CLEAR[channelIndex]; image.setRGB(x, y, argb | (newValue << RGB_SHIFT[channelIndex])); } public void putSamples(int channelIndex, int x, int y, int w, int h, int[] src, int srcOffset) { final int SHIFT = RGB_SHIFT[channelIndex]; final int MASK = RGB_CLEAR[channelIndex]; int[] row = new int[w]; while (h-- > 0) { image.getRGB(x, y, w, 1, row, 0, w); int columns = w; int rowIndex = 0; while (columns-- > 0) { int argb = row[rowIndex] & MASK; row[rowIndex++] = argb | (src[srcOffset++] << SHIFT); } image.setRGB(x, y++, w, 1, row, 0, w); } } }java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/JiuAwtFrame.java0000664000000000000000000002355410572433573024450 0ustar /* * JiuAwtFrame * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Frame; import java.awt.Image; import java.awt.Label; import java.awt.ScrollPane; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentListener; import java.awt.event.ComponentEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import net.sourceforge.jiu.apps.EditorState; import net.sourceforge.jiu.apps.ImageDescriptionCreator; import net.sourceforge.jiu.apps.JiuInfo; import net.sourceforge.jiu.apps.StringIndexConstants; import net.sourceforge.jiu.apps.Strings; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.gui.awt.AwtMenuWrapper; import net.sourceforge.jiu.gui.awt.ImageCanvas; import net.sourceforge.jiu.gui.awt.dialogs.InfoDialog; import net.sourceforge.jiu.ops.ProgressListener; /** * The frame class for the AWT demo program {@link net.sourceforge.jiu.apps.jiuawt}. * @author Marco Schmidt * @since 0.8.0 */ public class JiuAwtFrame extends Frame implements ActionListener, ComponentListener, JiuInfo, ProgressListener { /** * The name of this application, jiuawt, plus the version number taken * from {@link JiuInfo}. * Example: jiuawt 0.8.0. * Will be displayed in the title bar of this frame. */ public static final String APP_NAME = "jiuawt " + JiuInfo.JIU_VERSION; static final long serialVersionUID = 2592450425245L; private EditorState editor; private AwtMenuWrapper menuWrapper; private AwtOperationProcessor processor; private Label statusBar; private ScrollPane scrollPane; private ImageCanvas canvas; /** * Create an object of this class, using the argument editor * state. * String resources to initialize the menu etc. will be taken * from the EditorState object's Strings variable * @param editorState EditorState object used by this frame */ public JiuAwtFrame(EditorState editorState) { super(APP_NAME); processor = new AwtOperationProcessor(editorState, this); editor = editorState; editor.addProgressListener(this); addComponentListener(this); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { processor.fileExit(); } }); // MENU menuWrapper = new AwtMenuWrapper(editor.getStrings(), this); setMenuBar(menuWrapper.getMenuBar()); menuWrapper.updateEnabled(processor); // IMAGE CANVAS // STATUS BAR statusBar = new Label(""); add(statusBar, BorderLayout.SOUTH); maximize(); //pack(); repaint(); setVisible(true); if (editor.getStartupImageName() != null) { processor.fileOpen(null); } } /** * Processes event objects that get created when menu items are * picked. * Determines the {@link net.sourceforge.jiu.apps.MenuIndexConstants} value for a given * event object and calls the internal {@link AwtOperationProcessor} * object's process method with the menu value. * The operation will then be performed. * @param e the ActionEvent object */ public void actionPerformed(ActionEvent e) { Object source = e.getSource(); int index = menuWrapper.findIndex(source); if (index != -1) { processor.process(index); } } public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentResized(ComponentEvent e) { if (scrollPane != null) { canvas.computeZoomToFitSize(); scrollPane.doLayout(); } } public void componentShown(ComponentEvent e) { } /** * Maximize the frame on the desktop. * There is no such function in the 1.1 AWT (was added in 1.4), so * this class determines the screen size and sets the frame to be * a little smaller than that (to make up for task bars etc.). * So this is just a heuristical approach. */ public void maximize() { /* The following line: setExtendedState(getExtendedState() | MAXIMIZED_BOTH); does a nice maximization, but works only with Java 1.4+ */ Toolkit toolkit = Toolkit.getDefaultToolkit(); if (toolkit == null) { return; } Dimension screenSize = toolkit.getScreenSize(); if (screenSize == null) { return; } int w = screenSize.width; int h = screenSize.height; int x = 20; int y = 80; setLocation(x / 2, y / 2); setSize(w - x, h - y); } /** * Displays the argument text in a message box with * error in the title bar. * @param text the error message to be displayed */ public void showError(String text) { Strings strings = editor.getStrings(); showInfo(strings.get(StringIndexConstants.ERROR_MESSAGE), text); } /** * Sets the current cursor to be {@link java.awt.Cursor#DEFAULT_CURSOR}. */ public void setDefaultCursor() { Cursor cursor = new Cursor(Cursor.DEFAULT_CURSOR); setCursor(cursor); } /** * If an image is currently loaded, */ public void setOriginalSize() { if (canvas != null && !editor.isZoomOriginalSize()) { editor.zoomSetOriginalSize(); canvas.setZoomFactors(editor.getZoomFactorX(), editor.getZoomFactorY()); updateTitle(); menuWrapper.updateEnabled(processor); } } public void setProgress(int zeroBasedIndex, int totalItems) { if (totalItems < 1) { throw new IllegalArgumentException("Total number of items (second parameter) must be larger than zero."); } if (zeroBasedIndex < 0) { throw new IllegalArgumentException("Zero-based index must be at least zero."); } if (zeroBasedIndex >= totalItems) { throw new IllegalArgumentException("Zero-based index must be smaller than total " + "number of items; zeroBasedIndex=" + zeroBasedIndex + ", totalItems=" + totalItems); } setProgress((float)(zeroBasedIndex + 1) / (float)totalItems); } /** * Set a new progress status. * @param progress float from 0.0f to 1.0f, indicating the progress between 0 and 100 percent */ public void setProgress(float progress) { if (progress >= 0.0f && progress <= 1.0f) { setStatusBar(" " + Math.round(progress * 100.0f) + "%"); } } public void setStatusBar(String text) { statusBar.setText(text); } public void setWaitCursor() { Cursor cursor = new Cursor(Cursor.WAIT_CURSOR); setCursor(cursor); } /** * Shows a modal dialog with given title bar and message text. * @param title will be displayed in the dialog's title bar * @param text will be displayed in the dialog's center part */ public void showInfo(String title, String text) { InfoDialog d = new InfoDialog(this, title, text); d.setVisible(true); } /** * If there is an image loaded, forces a canvas redraw by * calling repaint. */ public void updateCanvas() { if (canvas != null) { canvas.setInterpolation(editor.getInterpolation()); //canvas.revalidate(); canvas.repaint(); } } /** * Removes the current canvas from the frame (if there * is an image loaded) and creates a new canvas for the * current image. */ public void updateImage() { PixelImage image = editor.getImage(); if (scrollPane != null) { remove(scrollPane); } if (image != null) { //editor.zoomSetOriginalSize(); Image awtImage = ImageCreator.convertToAwtImage(image, RGBA.DEFAULT_ALPHA); scrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); canvas = new ImageCanvas(scrollPane); canvas.setInterpolation(editor.getInterpolation()); canvas.setZoomToFit(editor.getZoomToFit()); canvas.setImage(awtImage); canvas.setZoomFactors(editor.getZoomFactorX(), editor.getZoomFactorY()); //canvas.computeZoomToFitSize(); scrollPane.add(canvas); add(scrollPane); } updateStatusBar(); updateTitle(); validate(); menuWrapper.updateEnabled(processor); } /** * Creates a description string for the current image and sets the * status bar to that text. */ public void updateStatusBar() { PixelImage image = editor.getImage(); String statusBarText; if (image == null) { statusBarText = ""; } else { statusBarText = ImageDescriptionCreator.getDescription(image, editor.getLocale(), editor.getStrings()); } setStatusBar(statusBarText); } /** * Sets the frame's title bar to the application name, plus the file name of * the currently loaded image file, plus the current zoom factor, plus an * optional asterisk in case the image was modified but not yet saved. */ public void updateTitle() { StringBuffer sb = new StringBuffer(APP_NAME); String fileName = editor.getFileName(); if (fileName != null && fileName.length() > 0) { sb.append(" ["); sb.append(fileName); if (editor.getModified()) { sb.append('*'); } sb.append(']'); } if (editor.getImage() != null) { double zoom = editor.getZoomFactorX(); int percent = (int)(zoom * 100.0); sb.append(' '); sb.append(Integer.toString(percent)); sb.append('%'); } setTitle(sb.toString()); } /** * If an image is currently displayed, zoom in one level. */ public void zoomIn() { if (canvas != null && !editor.isMaximumZoom()) { editor.zoomIn(); canvas.setZoomFactors(editor.getZoomFactorX(), editor.getZoomFactorY()); updateTitle(); menuWrapper.updateEnabled(processor); } } /** * If an image is currently displayed, zoom out one level. */ public void zoomOut() { if (canvas != null && !editor.isMinimumZoom()) { editor.zoomOut(); canvas.setZoomFactors(editor.getZoomFactorX(), editor.getZoomFactorY()); updateTitle(); menuWrapper.updateEnabled(processor); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/AwtOperationProcessor.java0000664000000000000000000012063110572433001026562 0ustar /* * AwtOperationProcessor * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt; import java.awt.*; import java.io.*; import net.sourceforge.jiu.apps.*; import net.sourceforge.jiu.color.*; import net.sourceforge.jiu.color.adjustment.*; import net.sourceforge.jiu.color.analysis.*; import net.sourceforge.jiu.color.data.*; import net.sourceforge.jiu.color.dithering.*; import net.sourceforge.jiu.color.io.*; import net.sourceforge.jiu.color.promotion.*; import net.sourceforge.jiu.color.quantization.*; import net.sourceforge.jiu.color.reduction.*; import net.sourceforge.jiu.gui.awt.dialogs.*; import net.sourceforge.jiu.codecs.*; import net.sourceforge.jiu.data.*; import net.sourceforge.jiu.filters.*; import net.sourceforge.jiu.geometry.*; import net.sourceforge.jiu.ops.*; import net.sourceforge.jiu.util.*; /** * Performs operations specified by parent class {@link OperationProcessor}, * uses various AWT dialogs to get parameters from user in a GUI application. * @author Marco Schmidt * @since 0.8.0 */ public class AwtOperationProcessor extends OperationProcessor { private JiuAwtFrame frame; public AwtOperationProcessor(EditorState editorState, JiuAwtFrame awtFrame) { super(editorState); frame = awtFrame; } public void colorAdjustBrightness() { EditorState state = getEditorState(); Strings strings = state.getStrings(); Integer value = Dialogs.getInteger(frame, strings.get(StringIndexConstants.ADJUST_BRIGHTNESS), strings.get(StringIndexConstants.ENTER_BRIGHTNESS_VALUE), -100, 0, 100, strings.get(StringIndexConstants.OK), strings.get(StringIndexConstants.CANCEL)); if (value == null || value.intValue() == 0) { return; } Brightness brightness = new Brightness(); brightness.setBrightness(value.intValue()); process(brightness); } public void colorAdjustContrast() { EditorState state = getEditorState(); Strings strings = state.getStrings(); Integer value = Dialogs.getInteger(frame, strings.get(StringIndexConstants.ADJUST_CONTRAST), strings.get(StringIndexConstants.ENTER_CONTRAST_VALUE), -100, 0, 100, strings.get(StringIndexConstants.OK), strings.get(StringIndexConstants.CANCEL)); if (value == null || value.intValue() == 0) { return; } Contrast contrast = new Contrast(); contrast.setContrast(value.intValue()); process(contrast); } public void colorAdjustGamma() { EditorState state = getEditorState(); Strings strings = state.getStrings(); GammaCorrectionDialog gcd = new GammaCorrectionDialog(frame, strings, 2.2, GammaCorrection.MAX_GAMMA); gcd.setVisible(true); Double result = gcd.getValue(); if (result == null) { return; } GammaCorrection gc = new GammaCorrection(); gc.setGamma(result.doubleValue()); process(gc); } public void colorAdjustHueSaturationValue() { EditorState state = getEditorState(); Strings strings = state.getStrings(); HueSaturationValueDialog hsvDialog = new HueSaturationValueDialog(frame, strings, false, 0, 0, 0); hsvDialog.setVisible(true); if (!hsvDialog.hasPressedOk()) { return; } boolean setHue = hsvDialog.isHueSet(); int hue = hsvDialog.getHue(); int saturation = hsvDialog.getSaturation(); int value = hsvDialog.getValue(); HueSaturationValue hsv = new HueSaturationValue(); if (setHue) { hsv.setHueSaturationValue(hue, saturation, value); } else { hsv.setSaturationValue(saturation, value); } process(hsv); } public void colorHistogramCountColorsUsed() { EditorState state = getEditorState(); Strings strings = state.getStrings(); PixelImage image = state.getImage(); int numColors = 0; frame.setWaitCursor(); try { if (image instanceof RGBIntegerImage) { Histogram3DCreator hc = new Histogram3DCreator(); hc.setImage((RGBIntegerImage)image); hc.addProgressListeners(state.getProgressListeners()); hc.process(); Histogram3D hist = hc.getHistogram(); numColors = hist.getNumUsedEntries(); } else if (image instanceof IntegerImage && image.getNumChannels() == 1) { Histogram1DCreator hc = new Histogram1DCreator(); hc.setImage((IntegerImage)image); hc.addProgressListeners(state.getProgressListeners()); hc.process(); Histogram1D hist = hc.getHistogram(); numColors = hist.getNumUsedEntries(); } else { throw new UnsupportedTypeException("Not a supported image type for counting colors: " + image.getImageType().getName()); } } catch (OperationFailedException ofe) { frame.setDefaultCursor(); frame.updateStatusBar(); frame.showError(ofe.toString()); return; } frame.setDefaultCursor(); frame.updateStatusBar(); frame.showInfo( strings.get(StringIndexConstants.COUNT_COLORS_USED), strings.get(StringIndexConstants.NUMBER_OF_USED_COLORS) + ": " + numColors); } public void colorHistogramEqualize() { EditorState state = getEditorState(); try { process(new EqualizeHistogram((IntegerImage)state.getImage())); } catch (OperationFailedException ofe) { frame.showError(ofe.toString()); } } public void colorHistogramNormalize() { EditorState state = getEditorState(); try { process(new NormalizeHistogram((IntegerImage)state.getImage())); } catch (OperationFailedException ofe) { frame.showError(ofe.toString()); } } public void colorHistogramTextureProperties() { EditorState state = getEditorState(); Strings strings = state.getStrings(); PixelImage img = state.getImage(); if (img == null || (!(img instanceof Gray8Image))) { return; } Gray8Image image = (Gray8Image)img; frame.setWaitCursor(); CoOccurrenceMatrix matrix = MatrixCreator.createCoOccurrenceMatrix(image, 0); TextureAnalysis ta = new TextureAnalysis(); ta.setMatrix(matrix); ta.addProgressListeners(state.getProgressListeners()); try { ta.process(); } catch (MissingParameterException mpe) { } StringBuffer text = new StringBuffer(); text.append(strings.get(StringIndexConstants.CONTRAST) + "=" + ta.getContrast() + "\n"); text.append(strings.get(StringIndexConstants.CORRELATION) + "=" + ta.getCorrelation() + "\n"); text.append(strings.get(StringIndexConstants.DISSIMILARITY) + "=" + ta.getDissimilarity() + "\n"); text.append(strings.get(StringIndexConstants.ENTROPY) + "=" + ta.getEntropy() + "\n"); text.append(strings.get(StringIndexConstants.ENERGY) + "=" + ta.getEnergy() + "\n"); text.append(strings.get(StringIndexConstants.HOMOGENEITY) + "=" + ta.getHomogeneity()); frame.setDefaultCursor(); frame.updateStatusBar(); frame.showInfo( strings.get(StringIndexConstants.TEXTURE_PROPERTIES), text.toString()); } public void colorHistogramSaveHistogramAs() { EditorState state = getEditorState(); PixelImage pi = state.getImage(); if (pi == null || !(pi instanceof IntegerImage)) { return; } String textFileName = getUserSaveAsFileName(".txt", StringIndexConstants.SAVE_HISTOGRAM_AS); if (textFileName == null) { return; } PrintStream out = null; try { out = new PrintStream(new BufferedOutputStream(new FileOutputStream(textFileName)), false); } catch (IOException ioe) { frame.showError(ioe.toString()); return; } IntegerImage image = (IntegerImage)pi; frame.setWaitCursor(); int numChannels = image.getNumChannels(); if (numChannels == 1) { Histogram1D hist = null; try { Histogram1DCreator hc = new Histogram1DCreator(); hc.setImage(image); hc.addProgressListeners(state.getProgressListeners()); hc.process(); hist = hc.getHistogram(); } catch(OperationFailedException ofe) { frame.showError(ofe.toString()); frame.updateStatusBar(); return; } HistogramSerialization.save(hist, out); } else if (numChannels == 3) { Histogram3D hist = null; try { Histogram3DCreator hc = new Histogram3DCreator(); hc.setImage(image, RGBIndex.INDEX_RED, RGBIndex.INDEX_GREEN, RGBIndex.INDEX_BLUE); hc.addProgressListeners(state.getProgressListeners()); hc.process(); hist = hc.getHistogram(); } catch(OperationFailedException ofe) { frame.showError(ofe.toString()); frame.updateStatusBar(); return; } HistogramSerialization.save(hist, out); } out.close(); frame.setDefaultCursor(); frame.updateStatusBar(); } public void colorHistogramSaveCoOccurrenceMatrixAs() { EditorState state = getEditorState(); PixelImage image = state.getImage(); String textFileName = getUserSaveAsFileName(".txt", StringIndexConstants.SAVE_COOCCURRENCE_MATRIX); if (textFileName == null) { return; } CoOccurrenceMatrix matrix = MatrixCreator.createCoOccurrenceMatrix((IntegerImage)image, 0); File textFile = new File(textFileName); try { PrintStream out = new PrintStream(new FileOutputStream(textFile)); MatrixSerialization.save(matrix, out); out.close(); } catch(IOException ioe) { frame.showError(ioe.toString()); } } public void colorHistogramSaveCoOccurrenceFrequencyMatrixAs() { EditorState state = getEditorState(); PixelImage image = state.getImage(); String textFileName = getUserSaveAsFileName(".txt", StringIndexConstants.SAVE_COOCCURRENCE_FREQUENCY_MATRIX); if (textFileName == null) { return; } CoOccurrenceMatrix com = MatrixCreator.createCoOccurrenceMatrix((IntegerImage)image, 0); CoOccurrenceFrequencyMatrix matrix = MatrixCreator.createCoOccurrenceFrequencyMatrix(com); File textFile = new File(textFileName); try { PrintStream out = new PrintStream(new FileOutputStream(textFile)); MatrixSerialization.save(matrix, out); out.close(); } catch(IOException ioe) { frame.showError(ioe.toString()); } } public void colorPaletteSaveAs() { EditorState state = getEditorState(); PalettedImage image = (PalettedImage)state.getImage(); Palette palette = image.getPalette(); String paletteFileName = getUserSaveAsFileName(".ppm", StringIndexConstants.SAVE_PALETTE); if (paletteFileName == null) { return; } File paletteFile = new File(paletteFileName); try { PaletteSerialization.save(palette, paletteFile); } catch(IOException ioe) { frame.showError(ioe.toString()); } } public void colorPromotePromoteToPaletted() { process(new PromotionPaletted8()); } public void colorPromotePromoteToGray8() { process(new PromotionGray8()); } public void colorPromotePromoteToGray16() { process(new PromotionGray16()); } public void colorPromotePromoteToRgb24() { process(new PromotionRGB24()); } public void colorPromotePromoteToRgb48() { process(new PromotionRGB48()); } public void colorReduceReduceNumberOfShadesOfGray() { EditorState state = getEditorState(); Strings strings = state.getStrings(); PixelImage image = state.getImage(); int maxBits = image.getBitsPerPixel() - 1; ReduceGrayscaleDialog rgd = new ReduceGrayscaleDialog(frame, strings, 1, maxBits, ReduceGrayscaleDialog.TYPE_FLOYD_STEINBERG_ERROR_DIFFUSION); rgd.setVisible(true); if (!rgd.hasPressedOk()) { return; } int numBits = rgd.getNumBits(); ImageToImageOperation op = null; switch (rgd.getDitheringMethod()) { case(ReduceGrayscaleDialog.TYPE_DITHERING_NONE): { ReduceShadesOfGray rsog; rsog = new ReduceShadesOfGray(); rsog.setBits(numBits); op = rsog; break; } case(ReduceGrayscaleDialog.TYPE_ORDERED_DITHERING): { OrderedDither od = new OrderedDither(); od.setOutputBits(numBits); op = od; break; } case(ReduceGrayscaleDialog.TYPE_FLOYD_STEINBERG_ERROR_DIFFUSION): { ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(ErrorDiffusionDithering.TYPE_FLOYD_STEINBERG); ed.setGrayscaleOutputBits(numBits); op = ed; break; } case(ReduceGrayscaleDialog.TYPE_STUCKI_ERROR_DIFFUSION): { ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(ErrorDiffusionDithering.TYPE_STUCKI); ed.setGrayscaleOutputBits(numBits); op = ed; break; } case(ReduceGrayscaleDialog.TYPE_BURKES_ERROR_DIFFUSION): { ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(ErrorDiffusionDithering.TYPE_BURKES); ed.setGrayscaleOutputBits(numBits); op = ed; break; } case(ReduceGrayscaleDialog.TYPE_SIERRA_ERROR_DIFFUSION): { ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(ErrorDiffusionDithering.TYPE_SIERRA); ed.setGrayscaleOutputBits(numBits); op = ed; break; } case(ReduceGrayscaleDialog.TYPE_JARVIS_JUDICE_NINKE_ERROR_DIFFUSION): { ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(ErrorDiffusionDithering.TYPE_JARVIS_JUDICE_NINKE); ed.setGrayscaleOutputBits(numBits); op = ed; break; } case(ReduceGrayscaleDialog.TYPE_STEVENSON_ARCE_ERROR_DIFFUSION): { ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(ErrorDiffusionDithering.TYPE_STEVENSON_ARCE); ed.setGrayscaleOutputBits(numBits); op = ed; break; } default: { return; } } op.setInputImage(image); op.addProgressListeners(state.getProgressListeners()); process(op); } public void colorReduceConvertToGrayscale() { process(new RGBToGrayConversion()); } public void colorReduceMedianCut() { EditorState state = getEditorState(); Strings strings = state.getStrings(); PixelImage image = state.getImage(); MedianCutDialog mcd = new MedianCutDialog( frame, strings, 256, MedianCutQuantizer.METHOD_REPR_COLOR_WEIGHTED_AVERAGE, true, MedianCutContourRemoval.DEFAULT_NUM_PASSES, MedianCutContourRemoval.DEFAULT_TAU); mcd.setVisible(true); if (!mcd.hasPressedOk()) { return; } int numColors = mcd.getNumColors(); int method = mcd.getReprColorMethod(); boolean palettedOutput = mcd.isOutputTypePaletted(); //int numBitsToBeCleared = 0; ImageToImageOperation op = null; if (mcd.useContourRemoval()) { MedianCutQuantizer quantizer = new MedianCutQuantizer(); quantizer.setInputImage(image); quantizer.setPaletteSize(numColors); quantizer.setMethodToDetermineRepresentativeColors(method); MedianCutContourRemoval removal = new MedianCutContourRemoval(); removal.setQuantizer(quantizer); removal.setTau(mcd.getTau()); removal.setNumPasses(mcd.getNumPasses()); op = removal; } else if (mcd.useErrorDiffusion()) { MedianCutQuantizer medianCut = new MedianCutQuantizer(); medianCut.setInputImage(image); medianCut.setPaletteSize(numColors); medianCut.setMethodToDetermineRepresentativeColors(method); medianCut.setMapping(false); try { medianCut.process(); } catch (OperationFailedException ofe) { frame.showError(ofe.toString()); return; } ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(mcd.getErrorDiffusion()); ed.setQuantizer(medianCut); op = ed; } else { MedianCutQuantizer medianCut = new MedianCutQuantizer(); medianCut.setInputImage(image); medianCut.setPaletteSize(numColors); medianCut.setTruecolorOutput(!palettedOutput); medianCut.setMethodToDetermineRepresentativeColors(method); op = medianCut; } process(op); } public void colorInvert() { process(new Invert()); } public void colorConvertToMinimumColorType() { EditorState state = getEditorState(); PixelImage image = state.getImage(); AutoDetectColorType adct = new AutoDetectColorType(); adct.setInputImage(image); adct.addProgressListeners(state.getProgressListeners()); try { frame.setWaitCursor(); adct.process(); } catch (MissingParameterException mpe) { frame.setDefaultCursor(); return; } catch (WrongParameterException mpe) { frame.setDefaultCursor(); return; } if (!adct.isReducible()) { frame.setDefaultCursor(); return; } frame.setDefaultCursor(); setImage(adct.getOutputImage(), true); frame.updateImage(); } public void colorReduceOctree() { EditorState state = getEditorState(); Strings strings = state.getStrings(); PixelImage image = state.getImage(); OctreeDialog od = new OctreeDialog(frame, strings, 256, true); od.setVisible(true); if (!(od.hasPressedOk())) { return; } OctreeColorQuantizer quantizer = new OctreeColorQuantizer(); quantizer.setPaletteSize(od.getNumColors()); quantizer.setInputImage(image); if (od.useNoDithering()) { quantizer.addProgressListeners(state.getProgressListeners()); try { quantizer.process(); } catch(WrongParameterException wpe) { } catch(MissingParameterException mpe) { } image = quantizer.getOutputImage(); } else if (od.useErrorDiffusion()) { try { quantizer.init(); } catch(WrongParameterException wpe) { } catch(MissingParameterException mpe) { } ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(od.getErrorDiffusion()); ed.setQuantizer(quantizer); process(ed); return; } setImage(image, true); frame.updateImage(); } public void colorReduceReduceToBilevelThreshold() { EditorState state = getEditorState(); Strings strings = state.getStrings(); IntegerImage image = (IntegerImage)state.getImage(); final int MAX = image.getMaxSample(0); Integer value = Dialogs.getInteger(frame, strings.get(StringIndexConstants.REDUCE_TO_BILEVEL_THRESHOLD), strings.get(Strings.ENTER_THRESHOLD_VALUE), 0, MAX / 2, MAX, strings.get(Strings.OK), strings.get(Strings.CANCEL)); if (value == null) { return; } ReduceToBilevelThreshold red = new ReduceToBilevelThreshold(); red.setThreshold(value.intValue()); process(red); } private int convertUniformToErrorDiffusion(int utype) { switch(utype) { case(UniformPaletteQuantizerDialog.TYPE_FLOYD_STEINBERG_ERROR_DIFFUSION): return ErrorDiffusionDithering.TYPE_FLOYD_STEINBERG; case(UniformPaletteQuantizerDialog.TYPE_BURKES_ERROR_DIFFUSION): return ErrorDiffusionDithering.TYPE_BURKES; case(UniformPaletteQuantizerDialog.TYPE_STUCKI_ERROR_DIFFUSION): return ErrorDiffusionDithering.TYPE_STUCKI; case(UniformPaletteQuantizerDialog.TYPE_SIERRA_ERROR_DIFFUSION): return ErrorDiffusionDithering.TYPE_SIERRA; case(UniformPaletteQuantizerDialog.TYPE_JARVIS_JUDICE_NINKE_ERROR_DIFFUSION): return ErrorDiffusionDithering.TYPE_JARVIS_JUDICE_NINKE; case(UniformPaletteQuantizerDialog.TYPE_STEVENSON_ARCE_ERROR_DIFFUSION): return ErrorDiffusionDithering.TYPE_STEVENSON_ARCE; default: return -1; } } public void colorReduceUniformPalette() { EditorState state = getEditorState(); Strings strings = state.getStrings(); UniformPaletteQuantizerDialog upqd = new UniformPaletteQuantizerDialog (frame, strings, 3, 3, 2, UniformPaletteQuantizerDialog.TYPE_FLOYD_STEINBERG_ERROR_DIFFUSION); upqd.setVisible(true); if (!upqd.hasPressedOk()) { return; } int redBits = upqd.getRedBits(); int greenBits = upqd.getGreenBits(); int blueBits = upqd.getBlueBits(); int sum = redBits + greenBits + blueBits; switch (upqd.getDitheringMethod()) { case(UniformPaletteQuantizerDialog.TYPE_DITHERING_NONE): { UniformPaletteQuantizer upq = new UniformPaletteQuantizer(redBits, greenBits, blueBits); process(upq); return; } case(UniformPaletteQuantizerDialog.TYPE_ORDERED_DITHERING): { OrderedDither od = new OrderedDither(); od.setRgbBits(redBits, greenBits, blueBits); process(od); return; } case(UniformPaletteQuantizerDialog.TYPE_FLOYD_STEINBERG_ERROR_DIFFUSION): case(UniformPaletteQuantizerDialog.TYPE_BURKES_ERROR_DIFFUSION): case(UniformPaletteQuantizerDialog.TYPE_STUCKI_ERROR_DIFFUSION): case(UniformPaletteQuantizerDialog.TYPE_SIERRA_ERROR_DIFFUSION): case(UniformPaletteQuantizerDialog.TYPE_JARVIS_JUDICE_NINKE_ERROR_DIFFUSION): case(UniformPaletteQuantizerDialog.TYPE_STEVENSON_ARCE_ERROR_DIFFUSION): { ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(convertUniformToErrorDiffusion(upqd.getDitheringMethod())); UniformPaletteQuantizer upq = new UniformPaletteQuantizer(redBits, greenBits, blueBits); ed.setQuantizer(upq); ed.setTruecolorOutput(sum > 8); process(ed); return; } default: { return; } } } public void colorReduceMapToArbitraryPalette() { EditorState state = getEditorState(); Strings strings = state.getStrings(); PixelImage image = state.getImage(); MapToArbitraryPaletteDialog d = new MapToArbitraryPaletteDialog(frame, strings); d.setVisible(true); if (!d.hasPressedOk()) { return; } int paletteType = d.getPaletteType(); if (paletteType < 0) { return; } // load palette from file Palette palette; switch(paletteType) { case(MapToArbitraryPaletteDialog.PALETTE_FILE): { String name = getUserFileName(null, StringIndexConstants.LOAD_PALETTE, FileDialog.LOAD); if (name == null) { return; } File file = new File(name); palette = PaletteSerialization.load(file); break; } case(MapToArbitraryPaletteDialog.PALETTE_WEBSAFE): { palette = WebsafePaletteCreator.create(); break; } case(MapToArbitraryPaletteDialog.PALETTE_PALM_256_COLORS): { palette = PalmCodec.createSystem8BitPalette(); break; } case(MapToArbitraryPaletteDialog.PALETTE_PALM_16_COLORS): { palette = PalmCodec.createSystem4BitColorPalette(); break; } case(MapToArbitraryPaletteDialog.PALETTE_PALM_16_GRAY): { palette = PalmCodec.createSystem4BitGrayscalePalette(); break; } case(MapToArbitraryPaletteDialog.PALETTE_PALM_4_GRAY): { palette = PalmCodec.createSystem2BitGrayscalePalette(); break; } default: { return; } } ArbitraryPaletteQuantizer apq = new ArbitraryPaletteQuantizer(palette); if (palette == null) { return; } if (d.useErrorDiffusion()) { // error diffusion dithering ErrorDiffusionDithering ed = new ErrorDiffusionDithering(); ed.setTemplateType(d.getErrorDiffusionType()); ed.setQuantizer(apq); process(ed); return; } else { // no dithering apq.setInputImage(image); apq.addProgressListeners(state.getProgressListeners()); try { apq.process(); } catch (OperationFailedException ofe) { return; } image = apq.getOutputImage(); } setImage(image, true); frame.updateImage(); } public void editRedo() { EditorState state = getEditorState(); if (!state.canRedo()) { return; } state.redo(); frame.updateImage(); } public void editUndo() { EditorState state = getEditorState(); if (!state.canUndo()) { return; } state.undo(); frame.updateImage(); } public void fileClose() { EditorState state = getEditorState(); if (state.getModified()) { YesNoDialog dialog = new YesNoDialog(frame, state.getStrings(), StringIndexConstants.CLOSE_FILE, StringIndexConstants.DO_YOU_REALLY_WANT_TO_CLOSE_WITHOUT_SAVING, false); dialog.setVisible(true); if (dialog.getResult() == YesNoDialog.RESULT_NO) { return; } } setImage(null, false); state.resetZoomFactors(); state.setFileName(""); state.clearRedo(); state.clearUndo(); frame.updateImage(); } public void fileExit() { EditorState state = getEditorState(); if (state.getModified()) { YesNoDialog dialog = new YesNoDialog(frame, state.getStrings(), StringIndexConstants.QUIT_PROGRAM, StringIndexConstants.DO_YOU_REALLY_WANT_TO_QUIT_WITHOUT_SAVING, false); dialog.setVisible(true); if (dialog.getResult() == YesNoDialog.RESULT_NO) { return; } } frame.setVisible(false); System.exit(0); } public void fileOpen(String uri) { EditorState state = getEditorState(); Strings strings = state.getStrings(); if (state.getModified()) { YesNoDialog dialog = new YesNoDialog(frame, state.getStrings(), StringIndexConstants.CLOSE_FILE, StringIndexConstants.DO_YOU_REALLY_WANT_TO_CLOSE_WITHOUT_SAVING, false); dialog.setVisible(true); if (dialog.getResult() == YesNoDialog.RESULT_NO) { return; } } File file = null; if (uri != null) { } else if (state.getStartupImageName() != null) { file = new File(state.getStartupImageName()); state.setStartupImageName(null); } else { FileDialog fd = new FileDialog(frame, strings.get(StringIndexConstants.LOAD_IMAGE_FILE), FileDialog.LOAD); String dir = state.getCurrentDirectory(); if (dir != null) { fd.setDirectory(dir); } //fd.setFilenameFilter(ImageLoader.createFilenameFilter()); fd.setVisible(true); fd.setMode(FileDialog.LOAD); String fn = fd.getFile(); String dn = fd.getDirectory(); if (fn == null || dn == null) { return; } state.setCurrentDirectory(dn); file = new File(dn, fn); } PixelImage image = null; String fullName = uri; try { if (uri != null) { image = ImageLoader.loadToolkitImageUri(uri); } else { image = ImageLoader.load(file, state.getProgressListeners()); } } catch (Exception e) { frame.showInfo("Error loading image", e.toString()); e.printStackTrace(); return; } if (file != null) { fullName = file.getAbsolutePath(); if (image == null) { image = ToolkitLoader.loadAsRgb24Image(fullName); } } if (image == null) { frame.showInfo(strings.get(StringIndexConstants.ERROR_LOADING_IMAGE), strings.get(StringIndexConstants.FILE_FORMAT_UNKNOWN)); return; } setImage(image, false); state.setFileName(fullName); frame.updateImage(); } public void fileSaveAsBmp() { EditorState editor = getEditorState(); PixelImage image = editor.getImage(); if (image == null) { return; } BMPCodec codec = new BMPCodec(); String name = getUserSaveAsFileName(codec.suggestFileExtension(image), StringIndexConstants.SAVE_IMAGE_AS); if (name == null) { return; } codec.addProgressListeners(editor.getProgressListeners()); try { codec.setOutputStream(new BufferedOutputStream(new FileOutputStream(name))); codec.setImage(image); codec.process(); } catch (Exception e) { frame.showError(e.toString()); return; } editor.setFileName(name); setImage(image, false); frame.updateImage(); } public void fileSaveAsGif() { EditorState editor = getEditorState(); PixelImage image = editor.getImage(); if (image == null) { return; } GIFCodec codec = new GIFCodec(); String name = getUserSaveAsFileName(codec.suggestFileExtension(image), StringIndexConstants.SAVE_IMAGE_AS); if (name == null) { return; } codec.addProgressListeners(editor.getProgressListeners()); try { codec.setFile(name, CodecMode.SAVE); codec.setImage(image); codec.process(); } catch (Exception e) { frame.showError(e.toString()); return; } editor.setFileName(name); setImage(image, false); frame.updateImage(); } public void fileSaveAsPalm() { EditorState editor = getEditorState(); PixelImage image = editor.getImage(); if (image == null) { return; } PalmCodec codec = new PalmCodec(); String name = getUserSaveAsFileName(codec.suggestFileExtension(image), StringIndexConstants.SAVE_IMAGE_AS); if (name == null) { return; } codec.setCompression(PalmCodec.COMPRESSION_SCANLINE); codec.addProgressListeners(editor.getProgressListeners()); try { codec.setFile(name, CodecMode.SAVE); codec.setImage(image); codec.process(); } catch (Exception e) { frame.showError(e.toString()); return; } editor.setFileName(name); setImage(image, false); frame.updateImage(); } public void fileSaveAsPbm() { fileSaveAsPnm(); } public void fileSaveAsPgm() { fileSaveAsPnm(); } public void fileSaveAsPng() { EditorState editor = getEditorState(); PixelImage image = editor.getImage(); if (image == null) { return; } PNGCodec codec = new PNGCodec(); String name = getUserSaveAsFileName(codec.suggestFileExtension(image), StringIndexConstants.SAVE_IMAGE_AS); if (name == null) { return; } codec.addProgressListeners(editor.getProgressListeners()); try { codec.setFile(name, CodecMode.SAVE); codec.setImage(image); codec.process(); } catch (Exception e) { frame.showError(e.toString()); return; } editor.setFileName(name); setImage(image, false); frame.updateImage(); } private void fileSaveAsPnm() { EditorState editor = getEditorState(); PixelImage image = editor.getImage(); if (image == null) { return; } PNMCodec codec = new PNMCodec(); String name = getUserSaveAsFileName(codec.suggestFileExtension(image), StringIndexConstants.SAVE_IMAGE_AS); if (name == null) { return; } codec.addProgressListeners(editor.getProgressListeners()); try { codec.setOutputStream(new BufferedOutputStream(new FileOutputStream(name))); codec.setImage(image); codec.process(); } catch (Exception e) { frame.showError(e.toString()); return; } editor.setFileName(name); setImage(image, false); frame.updateImage(); } public void fileSaveAsPpm() { fileSaveAsPnm(); } public void fileSaveAsRas() { EditorState editor = getEditorState(); PixelImage image = editor.getImage(); if (image == null) { return; } RASCodec codec = new RASCodec(); String name = getUserSaveAsFileName(codec.suggestFileExtension(image), StringIndexConstants.SAVE_IMAGE_AS); if (name == null) { return; } codec.addProgressListeners(editor.getProgressListeners()); try { codec.setOutputStream(new BufferedOutputStream(new FileOutputStream(name))); codec.setImage(image); codec.process(); } catch (Exception e) { frame.showError(e.toString()); return; } editor.setFileName(name); setImage(image, false); frame.updateImage(); } public void filterConvolutionFilter(int type) { ConvolutionKernelFilter ckf = new ConvolutionKernelFilter(); ckf.setKernel(type); process(ckf); } public void filtersBlur() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_BLUR); } public void filtersSharpen() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_SHARPEN); } public void filtersEdgeDetection() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_EDGE_DETECTION); } public void filtersEmboss() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_EMBOSS); } public void filtersPsychedelicDistillation() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_PSYCHEDELIC_DISTILLATION); } public void filtersLithograph() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_LITHOGRAPH); } public void filtersHorizontalSobel() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_HORIZONTAL_SOBEL); } public void filtersVerticalSobel() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_VERTICAL_SOBEL); } public void filtersHorizontalPrewitt() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_HORIZONTAL_PREWITT); } public void filtersVerticalPrewitt() { filterConvolutionFilter(ConvolutionKernelFilter.TYPE_VERTICAL_PREWITT); } public void filtersMaximum() { EditorState state = getEditorState(); Strings strings = state.getStrings(); WindowSizeDialog wsd = new WindowSizeDialog(frame, strings, StringIndexConstants.APPLY_MAXIMUM_FILTER, 3, 3); wsd.setVisible(true); if (!wsd.hasPressedOk()) { return; } MaximumFilter mf = new MaximumFilter(); mf.setArea(wsd.getWidthValue(), wsd.getHeightValue()); process(mf); } public void filtersMedian() { EditorState state = getEditorState(); Strings strings = state.getStrings(); WindowSizeDialog wsd = new WindowSizeDialog(frame, strings, StringIndexConstants.APPLY_MEDIAN_FILTER, 3, 3); wsd.setVisible(true); if (!wsd.hasPressedOk()) { return; } MedianFilter mf = new MedianFilter(); mf.setArea(wsd.getWidthValue(), wsd.getHeightValue()); process(mf); } public void filtersMean() { EditorState state = getEditorState(); Strings strings = state.getStrings(); WindowSizeDialog wsd = new WindowSizeDialog(frame, strings, StringIndexConstants.APPLY_MEAN_FILTER, 3, 3); wsd.setVisible(true); if (!wsd.hasPressedOk()) { return; } MeanFilter mf = new MeanFilter(); mf.setArea(wsd.getWidthValue(), wsd.getHeightValue()); process(mf); } public void filtersMinimum() { EditorState state = getEditorState(); Strings strings = state.getStrings(); WindowSizeDialog wsd = new WindowSizeDialog(frame, strings, StringIndexConstants.APPLY_MINIMUM_FILTER, 3, 3); wsd.setVisible(true); if (!wsd.hasPressedOk()) { return; } MinimumFilter mf = new MinimumFilter(); mf.setArea(wsd.getWidthValue(), wsd.getHeightValue()); process(mf); } public void filtersOil() { EditorState state = getEditorState(); Strings strings = state.getStrings(); WindowSizeDialog wsd = new WindowSizeDialog(frame, strings, StringIndexConstants.APPLY_OIL_FILTER, 3, 3); wsd.setVisible(true); if (!wsd.hasPressedOk()) { return; } OilFilter of = new OilFilter(); of.setArea(wsd.getWidthValue(), wsd.getHeightValue()); process(of); } public String getUserFileName(String extension, int titleIndex, int fileDialogType) { EditorState editor = getEditorState(); Strings strings = editor.getStrings(); FileDialog fd = new FileDialog(frame, strings.get(titleIndex), fileDialogType); String currentDirectory = editor.getCurrentDirectory(); if (currentDirectory != null) { fd.setDirectory(currentDirectory); } String fileName = editor.getFileName(); if (fileDialogType == FileDialog.SAVE && fileName != null && extension != null) { File existingFile = new File(fileName); String name = existingFile.getName(); if (name != null) { int dotIndex = name.lastIndexOf("."); if (dotIndex != -1) { name = name.substring(0, dotIndex); name += extension; } } fd.setFile(name); } fd.setVisible(true); String fn = fd.getFile(); String dn = fd.getDirectory(); if (fn == null || dn == null) { return null; } File file = new File(dn, fn); return file.getAbsolutePath(); } public String getUserSaveAsFileName(String extension, int titleIndex) { return getUserFileName(extension, titleIndex, FileDialog.SAVE); } public void helpAbout() { EditorState state = getEditorState(); Strings strings = state.getStrings(); String message = JiuAwtFrame.APP_NAME + "\n" + strings.get(StringIndexConstants.HOMEPAGE) + "=" + JiuInfo.JIU_HOMEPAGE + "\n" + strings.get(StringIndexConstants.FEEDBACK) + "=" + JiuInfo.JIU_FEEDBACK_ADDRESS; frame.showInfo(strings.get(StringIndexConstants.ABOUT), message); } public void helpSystemInformation() { EditorState state = getEditorState(); Strings strings = state.getStrings(); frame.showInfo(strings.get(StringIndexConstants.SYSTEM_INFORMATION), SystemInfo.getSystemInfo(strings) + "\n" + AwtInfo.getAwtInfo(strings) + "\n" + SystemInfo.getMemoryInfo(strings)); } /** * This method can be called for ImageToImageOperation objects. */ public void process(ImageToImageOperation op) { EditorState state = getEditorState(); PixelImage image = state.getImage(); if (image == null) { return; } frame.setWaitCursor(); op.setInputImage(image); op.addProgressListeners(state.getProgressListeners()); try { op.process(); frame.setDefaultCursor(); } catch (OperationFailedException ofe) { frame.setDefaultCursor(); frame.showError(ofe.toString()); return; } setImage(op.getOutputImage(), true); frame.updateImage(); } public void setImage(PixelImage newImage, boolean newModified) { EditorState state = getEditorState(); state.setImage(newImage, newModified); } public void transformationsFlip() { process(new Flip()); } public void transformationsMirror() { process(new Mirror()); } public void transformationsRotate90Left() { process(new Rotate90Left()); } public void transformationsRotate90Right() { process(new Rotate90Right()); } public void transformationsRotate180() { Rotate180 rot = new Rotate180(); EditorState state = getEditorState(); rot.setInputImage(state.getImage()); process(rot); } public void transformationsCrop() { EditorState state = getEditorState(); Strings strings = state.getStrings(); PixelImage image = state.getImage(); CropDialog cd = new CropDialog(frame, strings, image.getWidth(), image.getHeight()); cd.setVisible(true); if (!cd.hasPressedOk()) { return; } int x1 = cd.getX1(); int x2 = cd.getX2(); int y1 = cd.getY1(); int y2 = cd.getY2(); Crop crop = new Crop(); crop.setBounds(x1, y1, x2, y2); process(crop); } public void transformationsShear() { EditorState state = getEditorState(); PixelImage image = state.getImage(); Strings strings = state.getStrings(); ShearDialog sd = new ShearDialog(frame, strings, 45.0, image.getWidth(), image.getHeight()); sd.setVisible(true); if (!sd.hasPressedOk()) { return; } Double angle = sd.getValue(); if (angle == null || angle.doubleValue() == 0.0) { return; } Shear shear = new Shear(); shear.setAngle(angle.doubleValue()); process(shear); } public void transformationsScale() { EditorState state = getEditorState(); PixelImage image = state.getImage(); Strings strings = state.getStrings(); // a type can be chosen by the user if rgb or gray image boolean pickType = image instanceof RGB24Image || image instanceof Gray8Image; int initialType; if (pickType) { initialType = Resample.FILTER_TYPE_B_SPLINE; } else { initialType = Resample.FILTER_TYPE_BOX; } ScaleDialog sd = new ScaleDialog(frame, strings, image.getWidth(), image.getHeight(), pickType, Resample.getFilterNames(), initialType); sd.setVisible(true); if (sd.hasPressedOk()) { int newWidth = sd.getWidthValue(); int newHeight = sd.getHeightValue(); if (newWidth < 1 || newHeight < 1 || (newWidth == image.getWidth() && newHeight == image.getHeight())) { return; } if (pickType) { Resample resample = new Resample(); resample.setFilter(sd.getType()); ResampleFilter filter = resample.getFilter(); filter.setSamplingRadius(filter.getRecommendedSamplingRadius() * 50); resample.setSize(newWidth, newHeight); process(resample); } else { ScaleReplication sc = new ScaleReplication(); sc.setSize(newWidth, newHeight); process(sc); } } } public void updateFrame(PixelImage image) { EditorState state = getEditorState(); state.setImage(image, true); frame.setDefaultCursor(); frame.updateImage(); } public void viewInterpolationTypeBicubic() { EditorState state = getEditorState(); state.setInterpolation(EditorState.INTERPOLATION_BICUBIC); frame.updateCanvas(); } public void viewInterpolationTypeBilinear() { EditorState state = getEditorState(); state.setInterpolation(EditorState.INTERPOLATION_BILINEAR); frame.updateCanvas(); } public void viewInterpolationTypeNearestNeighbor() { EditorState state = getEditorState(); state.setInterpolation(EditorState.INTERPOLATION_NEAREST_NEIGHBOR); frame.updateCanvas(); } public void viewZoomIn() { frame.zoomIn(); } public void viewZoomOut() { frame.zoomOut(); } public void viewSetOriginalSize() { frame.setOriginalSize(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/RGBA.java0000664000000000000000000001712507741250134022774 0ustar /* * RGBA * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt; /** * This class converts between the 32 bit RGBA int values (used throughout the AWT) and * various standard pixel formats like 24 bits RGB, 8 bits gray, 16 bits gray, 1 bit black and white. *

* The conversion is done in a number of static methods. * This class is not supposed to be instantiated. *

* The method names of this class can be interpreted as follows. * If they contain * fromXYZ (where XYZ is a pixel format type like Gray8, RGB24 etc.), * a conversion from another pixel format to RGBA is done. * If the names contains toXYZ, a conversion from RGBA to that pixel * format will be performed. *

* Not all conversions are lossless or well-defined. * If 48 bpp RGB truecolor is used as source, only the top eight bits of each * 16 bit sample will be used (thus, the procedure is lossy). * If RGBA data is to be converted to bilevel (black and white), the conversion * is undefined if there are input RGBA pixels that are neither black nor white. * * @author Marco Schmidt */ public class RGBA { /** * The default value for the alpha part of RGBA. * The alpha value is eight bits long left-shifted by 24. * This default value is no transparency - the underlying image * cannot be seen: 0xff000000. */ public static final int DEFAULT_ALPHA = 0xff000000; private RGBA() { } /** * Converts pixels from bilevel packed bytes to RGBA format. * A byte is supposed to store eight pixels, the most significant bit being the leftmost pixel. * @param src the array with the packed bytes * @param srcOffset the index of the first byte to be converted from src * @param alpha the alpha value to be used for the destination RGBA values * @param dest the array where the destination RGBA pixels will be stored * @param destOffset the index of the first destination pixel in the dest array; * that array must be at least destOffset + ((num + 7) / 8) large * @param num the number of pixels (not bytes) to be converted */ public static void convertFromPackedBilevel(byte[] src, int srcOffset, int alpha, int[] dest, int destOffset, int num) { final int BLACK = alpha; final int WHITE = alpha | 0x00ffffff; int mask = 1; int value = 0; // 0 will never be used; value will be assigned a value in the first pass of the loop while (num-- > 0) { if (mask == 1) { mask = 128; value = src[srcOffset++] & 0xff; } else { mask >>= 1; } if ((value & mask) == 0) { dest[destOffset++] = BLACK; } else { dest[destOffset++] = WHITE; } } } /** * Convert a number of 8 bit grayscale pixels, shades of gray between 0 (for black) * and 255 (for white), given as bytes, to RGBA type int pixels, adding the given * alpha value. * @param src array with grayscale pixels * @param srcOffset index of first entry of src to be converted * @param alpha transparency value to be used in resulting RGBA array (only top eight bits can be set) * @param dest array to store resulting RGBA pixels * @param destOffset index of first entry in dest to be used * @param num number of pixels to be converted */ public static void convertFromGray8(byte[] src, int srcOffset, int alpha, int[] dest, int destOffset, int num) { while (num-- > 0) { int grayValue = src[srcOffset++] & 0xff; dest[destOffset++] = alpha | grayValue | (grayValue << 8) | (grayValue << 16); } } /** * Convert a number of 16 bit grayscale pixels to RGBA type int pixels, adding the given * alpha value. * Note that the lower 8 bits of each grayscale value are dropped. * @param src array with grayscale pixels * @param srcOffset index of first entry of src to be converted * @param alpha transparency value to be used in resulting RGBA array (only top eight bits can be set) * @param dest array to store resulting RGBA pixels * @param destOffset index of first entry in dest to be used * @param num number of pixels to be converted */ public static void convertFromGray16(short[] src, int srcOffset, int alpha, int[] dest, int destOffset, int num) { while (num-- > 0) { int grayValue = (src[srcOffset++] & 0xffff) >> 8; dest[destOffset++] = alpha | grayValue | (grayValue << 8) | (grayValue << 16); } } /** * Converts a byte array of palette index values to an array of RGBA values, * using palette color data. * @param src the byte array with the palette index values * @param srcOffset index of the first entry of src to be used * @param alpha transparency value to be used (only top eight bits should be set) * @param red the red palette values * @param green the green palette values * @param blue the blue palette values * @param dest the destination array to store the RGBA values * @param destOffset the first entry of dest to be used * @param num the number of pixels to be converted */ public static void convertFromPaletted8(byte[] src, int srcOffset, int alpha, int[] red, int[] green, int[] blue, int[] dest, int destOffset, int num) { while (num-- > 0) { int index = src[srcOffset++] & 0xff; dest[destOffset++] = alpha | (blue[index]) | (green[index] << 8) | (red[index] << 16); } } /** * Converts 24 bit RGB truecolor data to RGBA int values. * @param srcRed the red pixel values * @param srcRedOffset the first entry of srcRed to be used * @param srcGreen the green pixel values * @param srcGreenOffset the first entry of srcGreen to be used * @param srcBlue the blue pixel values * @param srcBlueOffset the first entry of srcBlue to be used * @param alpha the transpancy value to be used in the destination RGBA array (only top 8 bits should be set) * @param dest array to store RGBA pixel values * @param destOffset first entry of dest to be used * @param num number of pixels to be converted */ public static void convertFromRGB24(byte[] srcRed, int srcRedOffset, byte[] srcGreen, int srcGreenOffset, byte[] srcBlue, int srcBlueOffset, int alpha, int[] dest, int destOffset, int num) { while (num-- > 0) { dest[destOffset++] = alpha | (srcBlue[srcBlueOffset++] & 0xff) | ((srcGreen[srcGreenOffset++] & 0xff) << 8) | ((srcRed[srcRedOffset++] & 0xff) << 16); } } /** * Converts 48 bit RGB truecolor data to RGBA int values, dropping the least * significant eight bits of each short sample. * @param srcRed the red pixel values * @param srcRedOffset the first entry of srcRed to be used * @param srcGreen the green pixel values * @param srcGreenOffset the first entry of srcGreen to be used * @param srcBlue the blue pixel values * @param srcBlueOffset the first entry of srcBlue to be used * @param alpha the transpancy value to be used in the destination RGBA array (only top 8 bits should be set) * @param dest array to store RGBA pixel values * @param destOffset first entry of dest to be used * @param num number of pixels to be converted * @since 0.12.0 */ public static void convertFromRGB48(short[] srcRed, int srcRedOffset, short[] srcGreen, int srcGreenOffset, short[] srcBlue, int srcBlueOffset, int alpha, int[] dest, int destOffset, int num) { while (num-- > 0) { dest[destOffset++] = alpha | ((srcBlue[srcBlueOffset++] & 0xff00) >> 8) | ((srcGreen[srcGreenOffset++] & 0xff00)) | ((srcRed[srcRedOffset++] & 0xff00) << 8); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/AwtMenuWrapper.java0000664000000000000000000003156010404065410025170 0ustar /* * AwtMenuWrapper * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt; import java.awt.Menu; import java.awt.MenuBar; import java.awt.MenuItem; import java.awt.MenuShortcut; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import net.sourceforge.jiu.apps.MenuIndexConstants; import net.sourceforge.jiu.apps.MenuWrapper; import net.sourceforge.jiu.apps.OperationProcessor; import net.sourceforge.jiu.apps.StringIndexConstants; import net.sourceforge.jiu.apps.Strings; /** * A wrapper around an AWT MenuBar object. * @author Marco Schmidt * @since 0.8.0 */ public class AwtMenuWrapper extends MenuWrapper { private ActionListener listener; private MenuItem[] items; private MenuBar menuBar; /** * Internally creates a MenuBar object and provides methods to update that * menu bar. * @param strings String resource used to initialize menu items * @param actionListener a listener which will be registered with all menu items */ public AwtMenuWrapper(Strings strings, ActionListener actionListener) { items = new MenuItem[MenuIndexConstants.NUM_CONSTANTS]; listener = actionListener; init(strings); } private Menu createMenu(Strings strings, int stringIndex) { String labelText = strings.get(stringIndex); Menu result = new Menu(labelText); return result; } private MenuShortcut createMenuShortcut(int menuIndex) { switch(menuIndex) { case(MenuIndexConstants.FILE_OPEN): return new MenuShortcut(KeyEvent.VK_O); case(MenuIndexConstants.FILE_EXIT): return new MenuShortcut(KeyEvent.VK_Q); case(MenuIndexConstants.EDIT_UNDO): return new MenuShortcut(KeyEvent.VK_Z); case(MenuIndexConstants.EDIT_REDO): return new MenuShortcut(KeyEvent.VK_Y); case(MenuIndexConstants.VIEW_ZOOMIN): return new MenuShortcut(KeyEvent.VK_ADD); case(MenuIndexConstants.VIEW_ZOOMOUT): return new MenuShortcut(KeyEvent.VK_SUBTRACT); case(MenuIndexConstants.VIEW_SETORIGINALSIZE): return new MenuShortcut(KeyEvent.VK_SEPARATER); default: return null; } } /** * Attempts to find one of the menu items in the internal list. * Returns its index or -1 if it is not one of the items. */ public int findIndex(Object o) { if (o != null && items != null) { for (int i = 0; i < items.length; i++) { if (o == items[i]) { return i; } } } return -1; } /** * Returns the encapsulated MenuBar object. */ public MenuBar getMenuBar() { return menuBar; } /** * Initializes an object of type MenuBar. */ private void init(Strings strings) { // by default, create all items as MenuItem objects for (int i = 0; i < items.length; i++) { int stringIndex = getStringIndex(i); if (stringIndex == -1) { continue; } String labelText = strings.get(stringIndex); items[i] = new MenuItem(labelText); } menuBar = new MenuBar(); // FILE - SAVE AS Menu fileSaveAsMenu = createMenu(strings, StringIndexConstants.SAVEAS); items[MenuIndexConstants.FILE_SAVEAS] = fileSaveAsMenu; fileSaveAsMenu.add(items[MenuIndexConstants.FILE_SAVEAS_GIF]); fileSaveAsMenu.add(items[MenuIndexConstants.FILE_SAVEAS_PALM]); fileSaveAsMenu.add(items[MenuIndexConstants.FILE_SAVEAS_PBM]); fileSaveAsMenu.add(items[MenuIndexConstants.FILE_SAVEAS_PGM]); fileSaveAsMenu.add(items[MenuIndexConstants.FILE_SAVEAS_PNG]); fileSaveAsMenu.add(items[MenuIndexConstants.FILE_SAVEAS_PPM]); fileSaveAsMenu.add(items[MenuIndexConstants.FILE_SAVEAS_SUNRASTER]); fileSaveAsMenu.add(items[MenuIndexConstants.FILE_SAVEAS_WINDOWSBMP]); // FILE Menu fileMenu = createMenu(strings, StringIndexConstants.FILE); items[MenuIndexConstants.FILE] = fileMenu; fileMenu.add(items[MenuIndexConstants.FILE_OPEN]); fileMenu.add(fileSaveAsMenu); fileMenu.add(items[MenuIndexConstants.FILE_CLOSE]); fileMenu.addSeparator(); fileMenu.add(items[MenuIndexConstants.FILE_IMAGE_1]); fileMenu.addSeparator(); fileMenu.add(items[MenuIndexConstants.FILE_EXIT]); menuBar.add(fileMenu); // EDIT Menu editMenu = createMenu(strings, StringIndexConstants.EDIT); items[MenuIndexConstants.EDIT] = editMenu; editMenu.add(items[MenuIndexConstants.EDIT_UNDO]); editMenu.add(items[MenuIndexConstants.EDIT_REDO]); menuBar.add(editMenu); // COLOR - ADJUST Menu colorAdjustMenu = createMenu(strings, StringIndexConstants.ADJUST); items[MenuIndexConstants.COLOR_ADJUST] = colorAdjustMenu; colorAdjustMenu.add(items[MenuIndexConstants.COLOR_ADJUST_BRIGHTNESS]); colorAdjustMenu.add(items[MenuIndexConstants.COLOR_ADJUST_CONTRAST]); colorAdjustMenu.add(items[MenuIndexConstants.COLOR_ADJUST_GAMMA]); colorAdjustMenu.add(items[MenuIndexConstants.COLOR_ADJUST_HUESATURATIONVALUE]); // COLOR - HISTOGRAM Menu colorHistogramMenu = createMenu(strings, StringIndexConstants.HISTOGRAM); items[MenuIndexConstants.COLOR_HISTOGRAM] = colorHistogramMenu; colorHistogramMenu.add(items[MenuIndexConstants.COLOR_HISTOGRAM_COUNTCOLORSUSED]); colorHistogramMenu.add(items[MenuIndexConstants.COLOR_HISTOGRAM_EQUALIZE]); colorHistogramMenu.add(items[MenuIndexConstants.COLOR_HISTOGRAM_NORMALIZE]); colorHistogramMenu.add(items[MenuIndexConstants.COLOR_HISTOGRAM_TEXTUREPROPERTIES]); colorHistogramMenu.add(items[MenuIndexConstants.COLOR_HISTOGRAM_SAVEHISTOGRAMAS]); colorHistogramMenu.add(items[MenuIndexConstants.COLOR_HISTOGRAM_SAVECOOCCURRENCEMATRIXAS]); colorHistogramMenu.add(items[MenuIndexConstants.COLOR_HISTOGRAM_SAVECOOCCURRENCEFREQUENCYMATRIXAS]); // COLOR - PALETTE Menu colorPaletteMenu = createMenu(strings, StringIndexConstants.PALETTE_MENU_ITEM); items[MenuIndexConstants.COLOR_PALETTE] = colorPaletteMenu; colorPaletteMenu.add(items[MenuIndexConstants.COLOR_PALETTE_SAVEAS]); // COLOR - PROMOTE Menu colorPromoteMenu = createMenu(strings, StringIndexConstants.PROMOTE); items[MenuIndexConstants.COLOR_PROMOTE] = colorPromoteMenu; colorPromoteMenu.add(items[MenuIndexConstants.COLOR_PROMOTE_PROMOTETOPALETTED]); colorPromoteMenu.add(items[MenuIndexConstants.COLOR_PROMOTE_PROMOTETOGRAY8]); colorPromoteMenu.add(items[MenuIndexConstants.COLOR_PROMOTE_PROMOTETOGRAY16]); colorPromoteMenu.add(items[MenuIndexConstants.COLOR_PROMOTE_PROMOTETORGB24]); colorPromoteMenu.add(items[MenuIndexConstants.COLOR_PROMOTE_PROMOTETORGB48]); // COLOR - REDUCE Menu colorReduceMenu = createMenu(strings, StringIndexConstants.REDUCE); items[MenuIndexConstants.COLOR_REDUCE] = colorReduceMenu; colorReduceMenu.add(items[MenuIndexConstants.COLOR_REDUCE_REDUCETOBILEVELTHRESHOLD]); colorReduceMenu.add(items[MenuIndexConstants.COLOR_REDUCE_REDUCENUMBEROFSHADESOFGRAY]); colorReduceMenu.add(items[MenuIndexConstants.COLOR_REDUCE_CONVERTTOGRAYSCALE]); colorReduceMenu.add(items[MenuIndexConstants.COLOR_REDUCE_MEDIANCUT]); colorReduceMenu.add(items[MenuIndexConstants.COLOR_REDUCE_OCTREE]); colorReduceMenu.add(items[MenuIndexConstants.COLOR_REDUCE_UNIFORMPALETTE]); colorReduceMenu.add(items[MenuIndexConstants.COLOR_REDUCE_MAPTOARBITRARYPALETTE]); // COLOR Menu colorMenu = createMenu(strings, StringIndexConstants.COLOR); items[MenuIndexConstants.COLOR] = colorMenu; colorMenu.add(colorAdjustMenu); colorMenu.add(colorHistogramMenu); colorMenu.add(colorPaletteMenu); colorMenu.add(colorPromoteMenu); colorMenu.add(colorReduceMenu); colorMenu.add(items[MenuIndexConstants.COLOR_INVERT]); colorMenu.add(items[MenuIndexConstants.COLOR_CONVERTTOMINIMUMCOLORTYPE]); menuBar.add(colorMenu); // TRANSFORMATIONS Menu transformationsMenu = createMenu(strings, StringIndexConstants.TRANSFORMATIONS); items[MenuIndexConstants.TRANSFORMATIONS] = transformationsMenu; transformationsMenu.add(items[MenuIndexConstants.TRANSFORMATIONS_MIRROR]); transformationsMenu.add(items[MenuIndexConstants.TRANSFORMATIONS_FLIP]); transformationsMenu.addSeparator(); transformationsMenu.add(items[MenuIndexConstants.TRANSFORMATIONS_ROTATELEFT90]); transformationsMenu.add(items[MenuIndexConstants.TRANSFORMATIONS_ROTATERIGHT90]); transformationsMenu.add(items[MenuIndexConstants.TRANSFORMATIONS_ROTATE180]); transformationsMenu.addSeparator(); transformationsMenu.add(items[MenuIndexConstants.TRANSFORMATIONS_CROP]); transformationsMenu.add(items[MenuIndexConstants.TRANSFORMATIONS_SCALE]); transformationsMenu.add(items[MenuIndexConstants.TRANSFORMATIONS_SHEAR]); menuBar.add(transformationsMenu); // FILTERS Menu filtersMenu = createMenu(strings, StringIndexConstants.FILTERS); items[MenuIndexConstants.FILTERS] = filtersMenu; menuBar.add(filtersMenu); filtersMenu.add(items[MenuIndexConstants.FILTERS_BLUR]); filtersMenu.add(items[MenuIndexConstants.FILTERS_SHARPEN]); filtersMenu.add(items[MenuIndexConstants.FILTERS_EDGEDETECTION]); filtersMenu.add(items[MenuIndexConstants.FILTERS_EMBOSS]); filtersMenu.add(items[MenuIndexConstants.FILTERS_PSYCHEDELICDISTILLATION]); filtersMenu.add(items[MenuIndexConstants.FILTERS_LITHOGRAPH]); filtersMenu.add(items[MenuIndexConstants.FILTERS_HORIZONTALSOBEL]); filtersMenu.add(items[MenuIndexConstants.FILTERS_VERTICALSOBEL]); filtersMenu.add(items[MenuIndexConstants.FILTERS_HORIZONTALPREWITT]); filtersMenu.add(items[MenuIndexConstants.FILTERS_VERTICALPREWITT]); filtersMenu.addSeparator(); filtersMenu.add(items[MenuIndexConstants.FILTERS_MINIMUM]); filtersMenu.add(items[MenuIndexConstants.FILTERS_MAXIMUM]); filtersMenu.add(items[MenuIndexConstants.FILTERS_MEDIAN]); filtersMenu.add(items[MenuIndexConstants.FILTERS_MEAN]); filtersMenu.add(items[MenuIndexConstants.FILTERS_OIL]); // VIEW Menu viewMenu = createMenu(strings, StringIndexConstants.VIEW); items[MenuIndexConstants.VIEW] = viewMenu; menuBar.add(viewMenu); viewMenu.add(items[MenuIndexConstants.VIEW_ZOOMIN]); viewMenu.add(items[MenuIndexConstants.VIEW_ZOOMOUT]); viewMenu.add(items[MenuIndexConstants.VIEW_SETORIGINALSIZE]); // VIEW - INTERPOLATION TYPE Menu viewInterpolationMenu = createMenu(strings, StringIndexConstants.VIEW_INTERPOLATIONTYPE); items[MenuIndexConstants.VIEW_INTERPOLATIONTYPE] = viewInterpolationMenu; //viewMenu.add(viewInterpolationMenu); /*CheckboxGroup checkboxGroup = new CheckboxGroup(); int stringIndex = getStringIndex(MenuIndexConstants.VIEW_INTERPOLATIONTYPE_NEARESTNEIGHBOR); items[MenuIndexConstants.VIEW_INTERPOLATIONTYPE_NEARESTNEIGHBOR] = new CheckboxMenuItem(strings.get(stringIndex), true); stringIndex = getStringIndex(MenuIndexConstants.VIEW_INTERPOLATIONTYPE_BILINEAR); items[MenuIndexConstants.VIEW_INTERPOLATIONTYPE_BILINEAR] = new CheckboxMenuItem(strings.get(stringIndex), false); stringIndex = getStringIndex(MenuIndexConstants.VIEW_INTERPOLATIONTYPE_BICUBIC); items[MenuIndexConstants.VIEW_INTERPOLATIONTYPE_BICUBIC] = new CheckboxMenuItem(strings.get(stringIndex), false);*/ viewInterpolationMenu.add(items[MenuIndexConstants.VIEW_INTERPOLATIONTYPE_NEARESTNEIGHBOR]); viewInterpolationMenu.add(items[MenuIndexConstants.VIEW_INTERPOLATIONTYPE_BILINEAR]); viewInterpolationMenu.add(items[MenuIndexConstants.VIEW_INTERPOLATIONTYPE_BICUBIC]); // HELP Menu helpMenu = createMenu(strings, StringIndexConstants.HELP); items[MenuIndexConstants.HELP] = helpMenu; menuBar.add(helpMenu); helpMenu.add(items[MenuIndexConstants.HELP_ABOUT]); helpMenu.add(items[MenuIndexConstants.HELP_SYSTEMINFORMATION]); // add the listener to all items for (int i = 0; i < items.length; i++) { if (items[i] != null) { MenuShortcut shortcut = createMenuShortcut(i); if (shortcut != null) { items[i].setShortcut(shortcut); } items[i].addActionListener(listener); } } } /** * Changes the enabled status of one of the MenuItem objects, * given by its index. */ public void setEnabled(int index, boolean enabled) { if (index >= 0 && index < items.length && items[index] != null) { items[index].setEnabled(enabled); } } /** * Changes the label text of one of the MenuItem objects, * given by its index. */ public void setLabel(int index, String text) { if (index >= 0 && index < items.length && items[index] != null) { items[index].setLabel(text); } } /** * Changes the enabled status of all MenuItem objects * using the argument OperationProcessor object (more * precisely, its isAvailable(int) method). */ public void updateEnabled(OperationProcessor op) { for (int i = 0; i < items.length; i++) { setEnabled(i, op.isAvailable(i)); } } /** * Sets the label text of all MenuItem objects to * new values using the argument Strings information. */ public void updateLabels(Strings strings) { for (int i = 0; i < items.length; i++) { int stringIndex = getStringIndex(i); String text = strings.get(stringIndex); setLabel(i, text); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/AwtInfo.java0000664000000000000000000000263307741250134023626 0ustar /* * AwtInfo * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.image.ColorModel; import net.sourceforge.jiu.apps.StringIndexConstants; import net.sourceforge.jiu.apps.Strings; /** * Retrieve some information on the current graphical environment. * @author Marco Schmidt * @since 0.8.0 */ public class AwtInfo { private AwtInfo() { } /** * Returns information on the current AWT settings, regarding the current * language by using a {@link Strings} resource. * Right now, only returns the screen resolution. * All textual information is taken from the strings argument. * @param strings String resources * @return AWT information */ public static String getAwtInfo(Strings strings) { Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension screen = toolkit.getScreenSize(); StringBuffer result = new StringBuffer(); result.append(strings.get(StringIndexConstants.SCREEN_RESOLUTION) + "=" + screen.width + " x " + screen.height + "\n"); ColorModel model = toolkit.getColorModel(); if (model != null) { /* only in Java 1.2+ result.append("# components=" + model.getNumComponents() + "\n"); */ result.append("# bits per pixel=" + model.getPixelSize() + "\n"); } return result.toString(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/package.html0000664000000000000000000000053607741250134023675 0ustar

Classes to interoperate with Java's first GUI toolkit, the AWT (Abstract Windowing Toolkit). This includes GUI components and conversion from JIU's image types to AWT's image types. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/ToolkitLoader.java0000664000000000000000000001330207741250134025026 0ustar /* * ToolkitLoader * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt; import java.awt.Frame; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Toolkit; import java.io.IOException; import java.util.Vector; import net.sourceforge.jiu.codecs.ImageLoader; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.gui.awt.ImageCreator; import net.sourceforge.jiu.ops.OperationFailedException; /** * This class loads an instance of {@link java.awt.Image} using * {@link java.awt.Toolkit}'s built-in loading capabilities and * converts it to {@link net.sourceforge.jiu.data.RGB24Image} using * {@link net.sourceforge.jiu.gui.awt.ImageCreator}. *

* Supported file formats are JPEG and GIF. * PNG is supported since Java 1.3. * I have heard that XBM are supposedly loaded as well. * I don't know that format and haven't tested this functionality. *

* In addition, this class can also use JIU's built-in codecs from * this class. *

Usage examples

* Load an image using Java's own {@link java.awt.Toolkit} class: *
 * RGB24Image rgbImage = ToolkitLoader.loadAsRgb24Image("flower.jpg");
 * 
* This will only load images from files in formats that are supported * by Toolkit - normally, that only includes JPEG, GIF and since Java 1.3 PNG. * A potential problem of this approach is that Toolkit always delivers RGB * data, even if the image file only contains a black and white image. * In order to get an image object of the "real" type, try * JIU's {@link net.sourceforge.jiu.color.reduction.AutoDetectColorType} with * rgbImage (if you follow the link you will get a usage example * for that class as well). * *

Known issues

* If you are using this class to load JPEGs, GIFs or PNGs, * an AWT background thread is started (as for a normal AWT GUI application). * Before Java 1.4 there was a bug that kept the thread running although * an application had reached the end of its execution (by getting to the * end of the main(String[]) method). * If you experience this problem, either update to a 1.4+ JDK or * follow the advice given at jguru.com * and call System.exit(0);. * @author Marco Schmidt */ public class ToolkitLoader { private static Frame frame = null; /** * This class has only static methods and fields, so there is no need to instantiate it. * That's why the empty constructor is hidden here. */ private ToolkitLoader() { } /** * Loads an image from a file using the AWT's built-in loader. * Returns that image as an AWT {@link java.awt.Image} object. * This method does nothing more than call {@link java.awt.Toolkit#getImage(String)}, * wait for it using a {@link java.awt.MediaTracker} and return * the resulting image. * * @param fileName name of the image file * @return the image as AWT image object */ public static Image load(String fileName) { Toolkit toolkit = Toolkit.getDefaultToolkit(); Image image = toolkit.getImage(fileName); if (frame == null) { frame = new Frame(); } MediaTracker mt = new MediaTracker(frame); mt.addImage(image, 0); try { mt.waitForID(0); } catch (InterruptedException e) { return null; } return image; } /** * Loads an image from a file using the AWT's built-in loader and * converts the image to a {@link net.sourceforge.jiu.data.RGB24Image} * object. * First calls {@link #load} with the filename, then converts * the loaded image using {@link ImageCreator#convertImageToRGB24Image}. * @param fileName name of the file from which the image is to be loaded * @return loaded image as {@link net.sourceforge.jiu.data.RGB24Image} */ public static RGB24Image loadAsRgb24Image(String fileName) { return ImageCreator.convertImageToRGB24Image(load(fileName)); } /** * Attempts to load an image from a file given by its name, * using both the JIU codecs and the image loading functionality in * java.awt.Toolkit. * First tries JIU's codecs, then java.awt.Toolkit. * Simply calls loadViaToolkitOrCodecs(fileName, false);. * @param fileName name of the image file * @return image object or null on failure */ public static PixelImage loadViaToolkitOrCodecs(String fileName) { return loadViaToolkitOrCodecs(fileName, false, null); } /** * Attempts to load an image from a file given by its name, * using both the JIU codecs and the image loading functionality in * java.awt.Toolkit. * The second argument determines which method is tried first, * Toolkit (true) or the JIU codecs (false). * Uses {@link #loadAsRgb24Image} from this class for Toolkit loading * and {@link net.sourceforge.jiu.codecs.ImageLoader} for JIU's codecs. * @param fileName name of the image file * @return image object or null on failure */ public static PixelImage loadViaToolkitOrCodecs(String fileName, boolean preferToolkit, Vector progressListeners) { PixelImage result = null; try { if (preferToolkit) { result = loadAsRgb24Image(fileName); if (result == null) { result = ImageLoader.load(fileName, progressListeners); } } else { result = ImageLoader.load(fileName, progressListeners); if (result == null) { result = loadAsRgb24Image(fileName); } } } catch (OperationFailedException ofe) { } catch (IOException ioe) { } return result; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/ImageCanvas.java0000664000000000000000000001230710377272462024443 0ustar /* * ImageCanvas * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt; import java.awt.Canvas; import java.awt.Dimension; import java.awt.Graphics; //import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; //import java.awt.RenderingHints; import java.awt.ScrollPane; import net.sourceforge.jiu.apps.EditorState; /** * An AWT canvas that displays an {@link java.awt.Image} object. * Capable to display at arbitrary zooming levels. * Does not use rendering hints because they require Java 1.2 or higher * (although bilinear and bicubic interpolation usually improve display quality * when zooming at the cost of slowing down image drawing). * * @author Marco Schmidt */ public class ImageCanvas extends Canvas { private Image image; private int width; private int height; private int scaledWidth; private int scaledHeight; private double zoomFactorX = 1.0; private double zoomFactorY = 1.0; private boolean zoomToFit; private ScrollPane myScrollPane; public ImageCanvas(ScrollPane scrollPane) { myScrollPane = scrollPane; //interpolation = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; } public void computeZoomToFitSize() { if (!zoomToFit || myScrollPane == null) { return; } Dimension scrollPaneSize = myScrollPane.getSize(); int maxWidth = scrollPaneSize.width; int maxHeight = scrollPaneSize.height; double paneRatio = (double)maxWidth / (double)maxHeight; double imageRatio = (double)width / (double)height; if (paneRatio < imageRatio) { scaledWidth = maxWidth; scaledHeight = (int)(scaledWidth * imageRatio); } else { scaledHeight = maxHeight; scaledWidth = (int)(scaledHeight * imageRatio); } scaledHeight--; scaledWidth--; zoomFactorX = (double)scaledWidth / (double)width; zoomFactorY = zoomFactorX; } public int getZoomPercentageX() { return (int)(zoomFactorX * 100.0); } public int getZoomPercentageY() { return (int)(zoomFactorY * 100.0); } public Dimension getPreferredSize() { return new Dimension(scaledWidth, scaledHeight); } /** * Draws image to upper left corner. */ public void paint(Graphics g) { if (image == null) { super.paint(g); } else { Rectangle rect = getBounds(); int canvasWidth = rect.width; int canvasHeight = rect.height; int x1 = 0; int y1 = 0; if (canvasWidth > scaledWidth) { x1 = (canvasWidth - scaledWidth) / 2; } if (canvasHeight > scaledHeight) { y1 = (canvasHeight - scaledHeight) / 2; } if (canvasHeight > canvasWidth || canvasHeight > scaledHeight) { super.paint(g); } /* commented because Graphics2D requires Java 1.2+ if (g instanceof Graphics2D) { ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation); } */ g.drawImage(image, x1, y1, scaledWidth, scaledHeight, this); } } /** * Specifies a new Image object to be displayed in this canvas. * @param newImage the new Image object, potentially null */ public void setImage(Image newImage) { image = newImage; width = image.getWidth(this); height = image.getHeight(this); scaledWidth = (int)(width * zoomFactorX); scaledHeight = (int)(height * zoomFactorY); /*zoomFactorX = 1.0; zoomFactorY = 1.0;*/ setSize(scaledWidth, scaledHeight); validate(); } /** * Sets both zoom factors to 1.0. */ public void setOriginalSize() { setZoomFactor(1.0); } public double getZoomFactorX() { return zoomFactorX; } public double getZoomFactorY() { return zoomFactorY; } /** * Sets the interpolation type used for drawing to the argument * (must be one of the * INTERPOLATION_xyz constants of EditorState), but does not * do a redraw. */ public void setInterpolation(int newType) { switch(newType) { case(EditorState.INTERPOLATION_BICUBIC): { //interpolation = RenderingHints.VALUE_INTERPOLATION_BICUBIC; break; } case(EditorState.INTERPOLATION_BILINEAR): { //interpolation = RenderingHints.VALUE_INTERPOLATION_BILINEAR; break; } case(EditorState.INTERPOLATION_NEAREST_NEIGHBOR): { //interpolation = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; break; } } } public void setZoomFactor(double newZoomFactor) { setZoomFactors(newZoomFactor, newZoomFactor); } public void setZoomFactors(double newZoomFactorX, double newZoomFactorY) { if (newZoomFactorX <= 0.0 || newZoomFactorY <= 0.0) { throw new IllegalArgumentException("Zoom factors must be larger than 0.0."); } zoomFactorX = newZoomFactorX; zoomFactorY = newZoomFactorY; scaledWidth = (int)(width * zoomFactorX); scaledHeight = (int)(height * zoomFactorY); setSize(scaledWidth, scaledHeight); myScrollPane.validate(); } public void setZoomToFit(boolean newValue) { zoomToFit = newValue; validate(); } /** * Simply calls {@link #paint(Graphics)} with the argument. * @param g Graphics context */ public void update(Graphics g) { paint(g); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/gui/awt/ImageCreator.java0000664000000000000000000003162110612167015024614 0ustar /* * ImageCreator * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.gui.awt; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGB48Image; import net.sourceforge.jiu.data.RGBIndex; import java.awt.Frame; import java.awt.Image; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; import java.awt.image.MemoryImageSource; import java.awt.image.PixelGrabber; /** * A class to create {@link java.awt.Image} objects from various JIU image data types * and vice versa. * java.awt.Image objects can be used with the AWT and Swing GUI environments. * * @author Marco Schmidt */ public class ImageCreator { /** * The default transparency value to be used: full opacity. */ public static final int DEFAULT_ALPHA = 0xff000000; private static Frame frame; private ImageCreator() { } /** * Creates a {@link java.awt.Image} object from a pixel array. * Internally, a {@link java.awt.Frame} object is used to call its * {@link java.awt.Frame#createImage} method * with a {@link java.awt.image.MemoryImageSource} object. * * @param pixels the image pixel data in the typical RGBA 32-bit format, one int per pixel * @param width the horizontal resolution in pixels of the image to be created * @param height the vertical resolution in pixels of the image to be created */ public static Image createImage(int[] pixels, int width, int height) { if (width < 1 || height < 1) { throw new IllegalArgumentException("Error -- width and height " + "must both be larger than zero."); } if (pixels == null) { throw new IllegalArgumentException("Error -- the pixel array " + "must be non-null."); } if (pixels.length < width * height) { throw new IllegalArgumentException("Error -- the pixel array " + "must contain at least width times height items."); } if (frame == null) { frame = new Frame(); } return frame.createImage(new MemoryImageSource(width, height, pixels, 0, width)); } public static BufferedImage convertToAwtBufferedImage(PixelImage image) { if (image == null) { return null; } if (image instanceof RGB24Image) { return convertToAwtBufferedImage((RGB24Image)image); } else { throw new IllegalArgumentException("Unsupported input image type: " + image.getImageType()); } } /** * Convert a JIU {@link RGB24Image} to a {@link BufferedImage} with the * given alpha value (use {@link RGBA#DEFAULT_ALPHA} as default). * @param image JIU image to be converted * @param alpha alpha value to be used with each pixel * @return a new BufferedImage * @since 0.14.2 */ public static BufferedImage convertToAwtBufferedImage(RGB24Image image) { if (image == null) { return null; } final int WIDTH = image.getWidth(); final int HEIGHT = image.getHeight(); BufferedImage out = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); int outBuffer[] = new int[WIDTH]; byte red[] = new byte[WIDTH]; byte green[] = new byte[WIDTH]; byte blue[] = new byte[WIDTH]; for (int y = 0; y < HEIGHT; y++) { image.getByteSamples(RGBIndex.INDEX_RED, 0, y, WIDTH, 1, red, 0); image.getByteSamples(RGBIndex.INDEX_GREEN, 0, y, WIDTH, 1, green, 0); image.getByteSamples(RGBIndex.INDEX_BLUE, 0, y, WIDTH, 1, blue, 0); for (int x = 0; x < WIDTH; x++) { outBuffer[x] = //0xff000000 | ((red[x] & 0xff) << 16) | ((green[x] & 0xff) << 8) | ((blue[x] & 0xff)); } out.setRGB(0, y, WIDTH,1, outBuffer, 0, WIDTH); } return out; } /** * Creates an instance of {@link java.awt.Image} from an instance of * {@link RGB24Image}. * This will require image.getWidth() * image.getHeight() * 4 * bytes of free memory. * This method checks the type of the argument image via instanceof * and the calls the right convertToAwtImage method of this class. * @param image the RGB24Image to be converted * @return newly-created AWT image instance */ public static Image convertToAwtImage(PixelImage image, int alpha) { if (image == null) { return null; } if (image instanceof RGB24Image) { return convertToAwtImage((RGB24Image)image, alpha); } else if (image instanceof RGB48Image) { return convertToAwtImage((RGB48Image)image, alpha); } else if (image instanceof Gray8Image) { return convertToAwtImage((Gray8Image)image, alpha); } else if (image instanceof Gray16Image) { return convertToAwtImage((Gray16Image)image, alpha); } else if (image instanceof Paletted8Image) { return convertToAwtImage((Paletted8Image)image, alpha); } else if (image instanceof BilevelImage) { return convertToAwtImage((BilevelImage)image, alpha); } else { return null; } } /** * Convert a BilevelImage object to an AWT image object. * @param image the image to be converted * @param alpha the transparency value to be written to each * pixel in the resulting image * @return newly-created AWT image */ public static Image convertToAwtImage(BilevelImage image, int alpha) { if (image == null) { return null; } Toolkit toolkit = Toolkit.getDefaultToolkit(); if (toolkit == null) { return null; } int width = image.getWidth(); int height = image.getHeight(); if (width < 1 || height < 1) { return null; } int bytesPerRow = (width + 7) / 8; int[] pixels = new int[width * height]; byte[] row = new byte[bytesPerRow]; int destOffset = 0; for (int y = 0; y < height; y++) { image.getPackedBytes(0, y, width, row, 0, 0); RGBA.convertFromPackedBilevel(row, 0, alpha, pixels, destOffset, width); destOffset += width; } return toolkit.createImage(new MemoryImageSource(width, height, pixels, 0, width)); } /** * Creates an AWT Image object from a Gray16Image object and an alpha value. * This is done by allocating a new int array with image.getWidth() times * image.getHeight() elements, copying the data to those ints (using transparency * information from the top eight bits of the alpha argument) and calling * Toolkit.createImage with a MemoryImageSource of those int[] pixels. * @param image the grayscale image to be converted * @param alpha the alpha value, bits must only be set in the top eight bits * @return AWT image created from the argument input image */ public static Image convertToAwtImage(Gray16Image image, int alpha) { if (image == null) { return null; } Toolkit toolkit = Toolkit.getDefaultToolkit(); if (toolkit == null) { return null; } int width = image.getWidth(); int height = image.getHeight(); if (width < 1 || height < 1) { return null; } int[] pixels = new int[width * height]; short[] gray = new short[width]; int destOffset = 0; for (int y = 0; y < height; y++) { image.getShortSamples(0, 0, y, width, 1, gray, 0); RGBA.convertFromGray16(gray, 0, alpha, pixels, destOffset, width); destOffset += width; } return toolkit.createImage(new MemoryImageSource(width, height, pixels, 0, width)); } /** * Creates an AWT Image object from a Gray8Image object and an alpha value. * This is done by allocating a new int array with image.getWidth() times * image.getHeight() elements, copying the data to those ints (using transparency * information from the top eight bits of the alpha argument) and calling * Toolkit.createImage with a MemoryImageSource of those int[] pixels. * * @param image the grayscale image to be converted * @param alpha the alpha value, bits must only be set in the top eight bits * @return AWT image created from the argument input image */ public static Image convertToAwtImage(Gray8Image image, int alpha) { if (image == null) { return null; } Toolkit toolkit = Toolkit.getDefaultToolkit(); if (toolkit == null) { return null; } int width = image.getWidth(); int height = image.getHeight(); if (width < 1 || height < 1) { return null; } int[] pixels = new int[width * height]; byte[] gray = new byte[width]; int destOffset = 0; for (int y = 0; y < height; y++) { image.getByteSamples(0, 0, y, width, 1, gray, 0); RGBA.convertFromGray8(gray, 0, alpha, pixels, destOffset, width); destOffset += width; } return toolkit.createImage(new MemoryImageSource(width, height, pixels, 0, width)); } public static Image convertToAwtImage(Paletted8Image image, int alpha) { if (image == null) { return null; } Toolkit toolkit = Toolkit.getDefaultToolkit(); if (toolkit == null) { return null; } int width = image.getWidth(); int height = image.getHeight(); Palette palette = image.getPalette(); if (width < 1 || height < 1 || palette == null) { return null; } int[] red = new int[palette.getNumEntries()]; int[] green = new int[palette.getNumEntries()]; int[] blue = new int[palette.getNumEntries()]; for (int i = 0; i < palette.getNumEntries(); i++) { red[i] = palette.getSample(RGBIndex.INDEX_RED, i); green[i] = palette.getSample(RGBIndex.INDEX_GREEN, i); blue[i] = palette.getSample(RGBIndex.INDEX_BLUE, i); } int[] pixels = new int[width * height]; byte[] data = new byte[width]; int destOffset = 0; for (int y = 0; y < height; y++) { image.getByteSamples(0, 0, y, width, 1, data, 0); RGBA.convertFromPaletted8(data, 0, alpha, red, green, blue, pixels, destOffset, width); destOffset += width; } return toolkit.createImage(new MemoryImageSource(width, height, pixels, 0, width)); } public static Image convertToAwtImage(RGB24Image image, int alpha) { if (image == null) { return null; } Toolkit toolkit = Toolkit.getDefaultToolkit(); if (toolkit == null) { return null; } int width = image.getWidth(); int height = image.getHeight(); if (width < 1 || height < 1) { return null; } int[] pixels = new int[width * height]; byte[] red = new byte[width]; byte[] green = new byte[width]; byte[] blue = new byte[width]; int destOffset = 0; for (int y = 0; y < height; y++) { image.getByteSamples(RGBIndex.INDEX_RED, 0, y, width, 1, red, 0); image.getByteSamples(RGBIndex.INDEX_GREEN, 0, y, width, 1, green, 0); image.getByteSamples(RGBIndex.INDEX_BLUE, 0, y, width, 1, blue, 0); RGBA.convertFromRGB24(red, 0, green, 0, blue, 0, alpha, pixels, destOffset, width); destOffset += width; } return toolkit.createImage(new MemoryImageSource(width, height, pixels, 0, width)); } public static Image convertToAwtImage(RGB48Image image, int alpha) { if (image == null) { return null; } Toolkit toolkit = Toolkit.getDefaultToolkit(); if (toolkit == null) { return null; } int width = image.getWidth(); int height = image.getHeight(); if (width < 1 || height < 1) { return null; } int[] pixels = new int[width * height]; short[] red = new short[width]; short[] green = new short[width]; short[] blue = new short[width]; int destOffset = 0; for (int y = 0; y < height; y++) { image.getShortSamples(RGBIndex.INDEX_RED, 0, y, width, 1, red, 0); image.getShortSamples(RGBIndex.INDEX_GREEN, 0, y, width, 1, green, 0); image.getShortSamples(RGBIndex.INDEX_BLUE, 0, y, width, 1, blue, 0); RGBA.convertFromRGB48(red, 0, green, 0, blue, 0, alpha, pixels, destOffset, width); destOffset += width; } return toolkit.createImage(new MemoryImageSource(width, height, pixels, 0, width)); } /** * Creates an {@link RGB24Image} from the argument AWT image instance. * @param image AWT image object to be converted to a {@link RGB24Image} * @return a {@link RGB24Image} object holding the image data from the argument image */ public static RGB24Image convertImageToRGB24Image(Image image) { if (image == null) { return null; } int width = image.getWidth(null); int height = image.getHeight(null); if (width < 1 || height < 1) { return null; } int[] pixels = new int[width * height]; PixelGrabber pg = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width); try { pg.grabPixels(); } catch (InterruptedException e) { return null; } if ((pg.getStatus() & ImageObserver.ABORT) != 0) { return null; } RGB24Image result = new MemoryRGB24Image(width, height); int offset = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int pixel = pixels[offset++] & 0xffffff; // TODO: store alpha value; requires some sort of // transparency channel data type yet to be implemented result.putSample(RGBIndex.INDEX_RED, x, y, pixel >> 16); result.putSample(RGBIndex.INDEX_GREEN, x, y, (pixel >> 8) & 0xff); result.putSample(RGBIndex.INDEX_BLUE, x, y, pixel & 0xff); } } return result; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/0000775000000000000000000000000010546532075021505 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/UnsharpMaskKernel.java0000664000000000000000000000206110572431506025740 0ustar /* * UnsharpMaskKernel * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; /** * An unsharp mask kernel to be used with {@link ConvolutionKernelFilter}. * * @author Marco Schmidt * @author Niels Donvil * @since 0.10.0 */ public class UnsharpMaskKernel extends ConvolutionKernelData { /** * Creates a new unsharp mask kernel. * @param level adjusts the amount of 'unsharpness', must be from 1 to 50 */ public UnsharpMaskKernel(int level) { super("Unsharp mask", new int[] {1}, 1, 1, 1, 0); if (level < 1 || level > 50) { throw new IllegalArgumentException("The level argument must be >= 1 and <= 50."); } level = ((51 - level) * 4 ) + 20; setDiv(level); int[] data = { 0, 0, -1, 0, 0, 0, -8, -21, -8, 0, -1, -21, level + 120, -21, -1, 0, -8, -21, -8, 0, 0, 0, -1, 0, 0 }; setData(data); setHeight(5); setWidth(5); check(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/MedianFilter.java0000664000000000000000000000231307741250133024705 0ustar /* * MedianFilter * * Copyright (c) 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; import net.sourceforge.jiu.filters.AreaFilterOperation; import net.sourceforge.jiu.util.Median; /** * Applies a Median filter that replaces each pixel by the median of * itself and its neighbors. * The number of neighbors can be defined with the setArea methods. *

* Can be used as despeckle filter, but the image will lose sharpness. * The larger the area becomes, the less noise and the less sharpness will remain, * and the longer it will take. *

* Uses {@link net.sourceforge.jiu.util.Median} to do the search for the median value. *

Usage example

*
 * PixelImage image = ...; // some GrayIntegerImage or RGBIntegerImage
 * MedianFilter filter = new MedianFilter();
 * filter.setArea(5, 5);
 * filter.setInputImage(image);
 * filter.process();
 * PixelImage filteredImage = filter.getOutputImage();
 * 
* @author Marco Schmidt */ public class MedianFilter extends AreaFilterOperation { public final int computeSample(int[] samples, int numSamples) { return Median.find(samples, 0, numSamples - 1); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/MaximumFilter.java0000664000000000000000000000213707741250133025131 0ustar /* * MaximumFilter * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; import net.sourceforge.jiu.filters.AreaFilterOperation; /** * Filter operation that replaces each sample by the maximum value of itself * and its neighbor samples. *

* Note that this is not the maximum operation that takes two input images * and, for each position, takes the maximum sample value and writes it * to output. * *

Usage example

*
 * MaximumFilter filter = new MaximumFilter();
 * filter.setArea(7, 5);
 * filter.setInputImage(image);
 * filter.process();
 * PixelImage filteredImage = filter.getOutputImage();
 * 
* @author Marco Schmidt * @since 0.9.0 * @see MinimumFilter */ public class MaximumFilter extends AreaFilterOperation { public final int computeSample(int[] samples, int numSamples) { int max = samples[0]; int index = 1; while (index < numSamples) { int value = samples[index++]; if (value > max) { max = value; } } return max; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/ConvolutionKernelData.java0000664000000000000000000001002710572432153026615 0ustar /* * ConvolutionKernelData * * Copyright (c) 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; /** * This class encapsulates the information for a specific convolution kernel filter. * An object of this class is used in combination with {@link ConvolutionKernelFilter}. * Several kernel data objects are predefined in that class. * * @author Marco Schmidt * @see ConvolutionKernelFilter */ public class ConvolutionKernelData { private int[] data; private int width; private int height; private int div; private int bias; private String name; /** * Creates a new kernel from the arguments. * Calls the various set methods to actually store these arguments. */ public ConvolutionKernelData(String name, int[] data, int width, int height, int div, int bias) { setName(name); setData(data); setWidth(width); setHeight(height); setDiv(div); setBias(bias); check(); } /** * Checks if this kernel's data is valid and throws an IllegalArgumentException if anything * is wrong. * Otherwise, does nothing. */ public void check() { if (data.length < width * height) { throw new IllegalArgumentException("Kernel data array must have at least width * height elements."); } } /** * Returns this kernel's bias value. * See {@link ConvolutionKernelFilter} for an explanation of this and other kernel properties. * @see #setBias */ public int getBias() { return bias; } /** * Returns this kernel's div value. * Must not be 0. * See {@link ConvolutionKernelFilter} for an explanation of this and other kernel properties. * @see #setDiv */ public int getDiv() { return div; } /** * Returns the kernel data. * See {@link ConvolutionKernelFilter} for an explanation of this and other kernel properties. * @see #setData */ public int[] getData() { return data; } /** * Returns this kernel's height, an odd positive number. * See {@link ConvolutionKernelFilter} for an explanation of this and other kernel properties. */ public int getHeight() { return height; } /** * Returns this kernel's name. */ public String getName() { return name; } /** * Returns this kernel's width, an odd positive number. * See {@link ConvolutionKernelFilter} for an explanation of this and other kernel properties. */ public int getWidth() { return width; } /** * Set new bias value. * See {@link ConvolutionKernelFilter} for an explanation of this and other kernel properties. */ public void setBias(int newBias) { bias = newBias; } /** * Sets the data array to be used in this kernel. * Must have at least getWidth() times getHeight() elements - however, * this constraint is not checked in this method (setting * width and height may happen later). * Call {@link #check} * @param newData */ public void setData(int[] newData) { if (newData == null) { throw new IllegalArgumentException("The data array must not be null."); } if (newData.length < 1) { throw new IllegalArgumentException("The data array must have a length of at least 1."); } data = newData; } public void setDiv(int newDiv) { if (newDiv == 0) { throw new IllegalArgumentException("Div value must not be 0."); } div = newDiv; } public void setHeight(int newHeight) { if (newHeight < 1) { throw new IllegalArgumentException("Height must be 1 or larger."); } if ((newHeight % 2) == 0) { throw new IllegalArgumentException("Height must not be an even number."); } height = newHeight; } public void setName(String newName) { name = newName; } public void setWidth(int newWidth) { if (newWidth < 1) { throw new IllegalArgumentException("Width must be 1 or larger."); } if ((newWidth % 2) == 0) { throw new IllegalArgumentException("Width must not be an even number."); } width = newWidth; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/AreaFilterOperation.java0000664000000000000000000001775007741250133026254 0ustar /* * AreaFilterOperation * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Base class for operations that convert images to images and determine * an output sample by doing calculations on the input sample at the same * position plus some neighboring samples. *

* Override {@link #computeSample} and the operation will work. * @since 0.9.0 * @author Marco Schmidt */ public abstract class AreaFilterOperation extends ImageToImageOperation { private int areaWidth; private int areaHeight; /** * Checks if the argument is a valid area height value. * The default implementation requires the argument to be odd and larger than zero. * Override this method if your extension of AreaFilterOperation requires different heights. * @throws IllegalArgumentException if the argument is not valid */ public void checkAreaHeight(int height) { if (height < 1) { throw new IllegalArgumentException("Height must be larger than 0."); } if ((height & 1) == 0) { throw new IllegalArgumentException("Height must be odd."); } } /** * Checks if the argument is a valid area width value. * The default implementation requires the argument to be odd and larger than zero. * Override this method if your extension of AreaFilterOperation requires different widths. * @throws IllegalArgumentException if the argument is not valid */ public void checkAreaWidth(int width) { if (width < 1) { throw new IllegalArgumentException("Width must be larger than 0."); } if ((width & 1) == 0) { throw new IllegalArgumentException("Width must be odd."); } } /** * Determine the resulting sample for an array with the source sample * and zero or more of its neighbors. * This abstract method must be implemented by classes extending this operation. * The array will hold numSamples samples, which will be stored * starting at offset 0. *

* Normally, numSamples is equal to {@link #getAreaWidth} times {@link #getAreaHeight}. * Near the border of the image you may get less samples. * Example: the top left sample of an image has only three neighbors (east, south-east and south), * so you will only get four samples (three neighbors and the sample itself). * @param samples the array holding the sample(s) * @param numSamples number of samples in the array * @return sample to be written to the output image */ public abstract int computeSample(int[] samples, int numSamples); /** * Returns the current area height. * @return height of area window in pixels * @see #setAreaHeight(int) */ public int getAreaHeight() { return areaHeight; } /** * Returns the current area width. * @return width of area window in pixels * @see #setAreaWidth(int) */ public int getAreaWidth() { return areaWidth; } /** * Applies the filter to one of the channels of an image. */ private void process(int channelIndex, IntegerImage in, IntegerImage out) { processBorders(channelIndex, in, out); processCenter(channelIndex, in, out); /* final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); final int H_2 = areaWidth / 2; final int V_2 = areaHeight / 2; int processedItems = channelIndex * HEIGHT; final int TOTAL_ITEMS = in.getNumChannels() * HEIGHT; int[] samples = new int[areaWidth * areaHeight]; for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { // collect samples from area int numSamples = 0; for (int v = y - V_2; v <= y + V_2; v++) { if (v >= 0 && v < HEIGHT) { for (int u = x - H_2; u <= x + H_2; u++) { if (u >= 0 && u < WIDTH) { samples[numSamples++] = in.getSample(channelIndex, u, v); } } } } // determine and set output sample out.putSample(channelIndex, x, y, computeSample(samples, numSamples)); } setProgress(processedItems++, TOTAL_ITEMS); } */ } private void process(IntegerImage in, IntegerImage out) { if (out == null) { out = (IntegerImage)in.createCompatibleImage(in.getWidth(), in.getHeight()); setOutputImage(out); } for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) { process(channelIndex, in, out); } } public void process() throws MissingParameterException, WrongParameterException { if (areaWidth == 0) { throw new MissingParameterException("Area width has not been initialized."); } if (areaHeight == 0) { throw new MissingParameterException("Area height has not been initialized."); } ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); PixelImage out = getOutputImage(); if (in instanceof GrayIntegerImage || in instanceof RGBIntegerImage) { process((IntegerImage)in, (IntegerImage)out); } else { throw new WrongParameterException("Input image must implement GrayIntegerImage or RGBIntegerImage."); } } private void processBorders(int channelIndex, IntegerImage in, IntegerImage out) { /*processBorderNorthWest(channelIndex, in, out); processBorderNorth(channelIndex, in, out); processBorderNorthEast(channelIndex, in, out); processBorderEast(channelIndex, in, out); processBorderSouthEast(channelIndex, in, out); processBorderSouth(channelIndex, in, out); processBorderSouthWest(channelIndex, in, out); processBorderWest(channelIndex, in, out);*/ } private void processCenter(int channelIndex, IntegerImage in, IntegerImage out) { final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); final int AREA_WIDTH = getAreaWidth(); final int H_2 = AREA_WIDTH / 2; final int AREA_HEIGHT = getAreaHeight(); final int V_2 = AREA_HEIGHT / 2; if (WIDTH < AREA_WIDTH || HEIGHT < AREA_HEIGHT) { return; } final int NUM_SAMPLES = AREA_WIDTH * AREA_HEIGHT; final int TOTAL_ITEMS = in.getNumChannels() * HEIGHT; int processedItems = channelIndex * HEIGHT + AREA_HEIGHT / 2; int[] samples = new int[AREA_WIDTH * AREA_HEIGHT]; for (int y1 = 0, y2 = V_2; y2 < HEIGHT - V_2; y1++, y2++) { for (int x1 = 0, x2 = H_2; x2 < WIDTH - H_2; x1++, x2++) { in.getSamples(channelIndex, x1, y1, areaWidth, areaHeight, samples, 0); out.putSample(channelIndex, x2, y2, computeSample(samples, NUM_SAMPLES)); } setProgress(processedItems++, TOTAL_ITEMS); } } /** * Sets the area of the window to be used to determine each pixel's mean to * the argument width and height. * @param width width of window, must be 1 or larger * @param height height of window, must be 1 or larger * @see #setAreaHeight * @see #setAreaWidth */ public void setArea(int width, int height) { setAreaWidth(width); setAreaHeight(height); } /** * Sets the height of the area of the window to be used to determine each pixel's mean to * the argument value. * @param height height of window, must be odd and 1 or larger * @see #getAreaHeight * @see #setArea * @see #setAreaWidth */ public void setAreaHeight(int height) { checkAreaHeight(height); areaHeight = height; } /** * Sets the width of the area of the window to be used to determine each pixel's mean to * the argument value. * @param width width of window, must be odd and 1 or larger * @see #getAreaWidth * @see #setArea * @see #setAreaHeight */ public void setAreaWidth(int width) { checkAreaWidth(width); areaWidth = width; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/package.html0000664000000000000000000000053107741250133023760 0ustar

Various image filters that produce an output image from an input image, mostly reading a pixel and its neighbors in the input image to determine the pixel in the output image. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/OilFilter.java0000664000000000000000000000633207741250133024240 0ustar /* * OilFilter * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.filters.AreaFilterOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Applies a filter that makes the image look like an oil painting. * This is accomplished by creating a histogram of the neighboring samples * for each input sample and storing the value that occurs most often * in the output image. * If two or more samples occur an equal number of times, the lowest * sample value is picked. *

Supported image types

* Can process both {@link net.sourceforge.jiu.data.GrayIntegerImage} and * {@link net.sourceforge.jiu.data.RGBIntegerImage}. * Note that this operation becomes very slow with 16 bits per sample * because a lot of runs over a 65536 element array are necessary. *

Usage example

*
 * PixelImage image = ...; // some GrayIntegerImage or RGBIntegerImage
 * OilFilter filter = new OilFilter();
 * filter.setArea(5, 5);
 * filter.setInputImage(image);
 * filter.process();
 * PixelImage filteredImage = filter.getOutputImage();
 * 
*

Credits

* Idea taken from the * Oil class of Jef Poskanzer's ACME package. * @author Marco Schmidt */ public class OilFilter extends AreaFilterOperation { private int[] hist; private int[] zeroes; public final int computeSample(int[] samples, int numSamples) { /* for each sample find the neighbor that occurs most often in the area surrounding that pixel specified by getAreaWidth x getAreaHeight */ // clear histogram System.arraycopy(zeroes, 0, hist, 0, hist.length); // initialize histogram int index = numSamples; do { hist[samples[--index]]++; } while (index != 0); // now find the value that occurs most frequently int maxIndex = 0; int maxValue = hist[0]; index = 1; final int HIST_LENGTH = hist.length; while (index != HIST_LENGTH) { int value = hist[index]; if (value > maxValue) { maxIndex = index; maxValue = value; } index++; } // return value that occurs most frequently // if several samples occur most frequently, the smallest is returned return maxIndex; } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); PixelImage image = getInputImage(); if (image instanceof IntegerImage) { IntegerImage ii = (IntegerImage)image; int max = ii.getMaxSample(0); int index = 1; while (index < ii.getNumChannels()) { int maxSample = ii.getMaxSample(index++); if (maxSample > max) { max = maxSample; } } hist = new int[max + 1]; zeroes = new int[hist.length]; for (int i = 0; i < zeroes.length; i++) { zeroes[i] = 0; } } super.process(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/MeanFilter.java0000664000000000000000000000224007741250133024367 0ustar /* * MeanFilter * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; import net.sourceforge.jiu.filters.AreaFilterOperation; /** * Applies a mean filter that replaces each pixel by the mean of itself and its neighbors. * The number of neighbors can be defined by the setArea methods. * This filter only works with intensity-based image types. * More precisely, only {@link net.sourceforge.jiu.data.GrayIntegerImage} and * {@link net.sourceforge.jiu.data.RGBIntegerImage} will work. *

Usage example

*
 * PixelImage image = ...; // some GrayIntegerImage or RGBIntegerImage
 * MeanFilter filter = new MeanFilter();
 * filter.setArea(5, 5);
 * filter.setInputImage(image);
 * filter.process();
 * PixelImage filteredImage = filter.getOutputImage();
 * 
* @since 0.5.0 * @author Marco Schmidt */ public class MeanFilter extends AreaFilterOperation { public int computeSample(int[] samples, int numSamples) { int sum = 0; int index = numSamples; do { sum += samples[--index]; } while (index != 0); return sum / numSamples; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/BorderSampleGenerator.java0000664000000000000000000000776310541050265026602 0ustar /* * BorderSampleGenerator * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; import net.sourceforge.jiu.data.IntegerImage; /** * Abstract base class for classes that fill an int array with samples * from a rectangular region of an image's channel by * (1) copying int samples from an {@link net.sourceforge.jiu.data.IntegerImage} object * and by (2) generating samples that lie outside of the image. * To be used by {@link ConvolutionKernelFilter} and other operations * that require rectangular parts of an image that may not lie fully * inside of the image. * @author Marco Schmidt * @since 0.10.0 */ public abstract class BorderSampleGenerator { private int areaWidth; private int areaHeight; private int channelIndex; private IntegerImage image; /** * Initialize width and height of the area to be covered in every call to * {@link #fill}, also provides the image to be used for data copying. * The current channel is set to 0. * @param integerImage the image from which samples will be copied * @param areaWidth number of columns of the area to be covered in {@link #fill} * @param areaHeight number of rows of the area to be covered in {@link #fill} */ public BorderSampleGenerator(IntegerImage integerImage, int areaWidth, int areaHeight) { image = integerImage; if (image == null) { throw new IllegalArgumentException("The image argument must be non-null."); } this.areaWidth = areaWidth; if (areaWidth < 1 || (areaWidth % 2) == 0) { throw new IllegalArgumentException("Area width must be a positive odd number."); } this.areaHeight = areaHeight; if (areaHeight < 1 || (areaHeight % 2) == 0) { throw new IllegalArgumentException("Area height must be a positive odd number."); } } /** * Fills the argument array with samples from the current channel of the image * given to the constructor, generating samples that lie outside of the image. * The samples are copied (or generated) from the row y to row y + areaHeight - 1, * and within each row from column x to x + areaWidth - 1. *

* The implementation of this method is left to the child classes. * There are different ways to generate new samples, and each child class * is supposed to implement another way. * Obviously, the child classes also must copy samples from the image. * @param x leftmost column to be copied or generated * @param y top row to be copied or generated * @param samples array to which samples will be written; must have at least * {@link #getAreaWidth} times {@link #getAreaHeight} elements */ public abstract void fill(int x, int y, int[] samples); /** * Returns the number of rows from which data is copied or generated * with every call to {@link #fill}. * @return number or rows of a fill area */ public int getAreaHeight() { return areaHeight; } /** * Returns the number of columns from which data is copied or generated * with every call to {@link #fill}. * @return number or columns of a fill area */ public int getAreaWidth() { return areaWidth; } /** * Returns the index of the channel of the image from which data is copied. * @see #setChannelIndex * @return number or rows */ public int getChannelIndex() { return channelIndex; } /** * Returns the image from which data is copied. * @return image object */ public IntegerImage getImage() { return image; } /** * Sets the channel from which data is copied in {@link #fill}. * @see #getChannelIndex */ public void setChannelIndex(int newChannelIndex) { if (newChannelIndex < 0 || newChannelIndex >= image.getNumChannels()) { throw new IllegalArgumentException("Illegal channel index: " + newChannelIndex + " (must be from 0 to " + (image.getNumChannels() - 1) + ")."); } else { channelIndex = newChannelIndex; } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/MinimumFilter.java0000664000000000000000000000131707741250133025126 0ustar /* * MinimumFilter * * Copyright (c) 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; import net.sourceforge.jiu.filters.AreaFilterOperation; /** * Filter operation that replaces each sample by the minimum value of itself * and its neighbors. * See {@link MaximumFilter} for a usage example. * @author Marco Schmidt * @since 0.9.0 */ public class MinimumFilter extends AreaFilterOperation { public final int computeSample(int[] samples, int numSamples) { int min = samples[--numSamples]; while (numSamples != 0) { int value = samples[--numSamples]; if (value < min) { min = value; } } return min; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/filters/ConvolutionKernelFilter.java0000664000000000000000000003132110377272653027203 0ustar /* * ConvolutionKernelFilter * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.filters; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Applies a convolution kernel filter to an image. *

Supported image types

* Only image types that store intensity samples are supported. * Right now, this only includes {@link net.sourceforge.jiu.data.GrayIntegerImage} and * {@link net.sourceforge.jiu.data.RGBIntegerImage}. *

Usage example

* Standard approach (set up everything yourself): *
 * ConvolutionKernelFilter filter = new ConvolutionKernelFilter();
 * filter.setKernel(ConvolutionKernelFilter.TYPE_SHARPEN);
 * filter.setInputImage(image);
 * filter.process();
 * PixelImage sharpenedImage = filter.getOutputImage();
* Use static convenience method on image img: *
 * PixelImage filteredImage = ConvolutionKernelFilter.filter(img, ConvolutionKernelFilter.TYPE_BLUR);
 * 
*

Credits

* The implementation of the filter was created by members of the Java newsgroup * de.comp.lang.java and adapted to the JIU * framework by Marco Schmidt. * As it was done in a contest style where people improved other people's work, and even * more people suggested ideas, tested results and discussed the contest it is (1) * hard to tell who won the contest and (2) only fair to list all persons involved. *

* The resulting implementation is significantly faster than the * reference implementation. * The contest was started by the posting [JPEC#3] Vorschläge to de.comp.lang.java * by Marco Schmidt (2001-02-18) and was ended by the posting [JPEC#3] Ergebnisse * (2001-03-07). * A Usenet archive like Google Groups should be * able to provide the postings. * * @author Bernd Eckenfels * @author Carl Rosenberger * @author Dietmar Münzenberger * @author Karsten Schulz * @author Marco Kaiser * @author Marco Schmidt * @author Peter Luschny * @author Peter Schneider * @author Ramin Sadre * @author Roland Dieterich * @author Thilo Schwidurski */ public class ConvolutionKernelFilter extends ImageToImageOperation { public static final int TYPE_BLUR = 0; public static final int TYPE_SHARPEN = 1; public static final int TYPE_EDGE_DETECTION = 2; public static final int TYPE_EMBOSS = 3; public static final int TYPE_PSYCHEDELIC_DISTILLATION = 4; public static final int TYPE_LITHOGRAPH = 5; public static final int TYPE_HORIZONTAL_SOBEL = 6; public static final int TYPE_VERTICAL_SOBEL = 7; public static final int TYPE_HORIZONTAL_PREWITT = 8; public static final int TYPE_VERTICAL_PREWITT = 9; private static final int[] BLUR_DATA = {1, 1, 1, 1, 1, 1, 1, 1, 1}; private static final int[] SHARPEN_DATA = {0, -1, 0, -1, 5, -1, 0, -1, 0}; private static final int[] EDGE_DETECTION_DATA = {-1, -1, -1, -1, 8, -1, -1, -1, -1}; private static final int[] EMBOSS_DATA = {1, 1, 0, 1, 0, -1, 0, -1, -1}; private static final int[] PSYCHEDELIC_DISTILLATION_DATA = {0, -1, -2, -3, -4, 0, -1, 3, 2, 1, 0, -1, 10, 2, 1, 0, -1, 3, 2, 1, 0, -1, -2, -3, -4}; private static final int[] LITHOGRAPH_DATA = {-1, -1, -1, -1, -1, -1,-10,-10,-10, -1, -1,-10, 98,-10, -1, -1,-10,-10,-10, -1, -1, -1, -1, -1, -1}; private static final int[] HORIZONTAL_SOBEL_DATA = {-1, 0, 1, -2, 0, 2, -1, 0, 1}; private static final int[] VERTICAL_SOBEL_DATA = {-1, -2, -1, 0, 0, 0, 1, 2, 1}; private static final int[] HORIZONTAL_PREWITT_DATA = {-1, 0, 1, -1, 0, 1, -1, 0, 1}; private static final int[] VERTICAL_PREWITT_DATA = {-1, -1, -1, 0, 0, 0, 1, 1, 1}; private static ConvolutionKernelData[] PREDEFINED_KERNELS = { new ConvolutionKernelData("Blur", BLUR_DATA, 3, 3, 9, 0), new ConvolutionKernelData("Sharpen", SHARPEN_DATA, 3, 3, 1, 0), new ConvolutionKernelData("Edge detection", EDGE_DETECTION_DATA, 3, 3, 1, 0), new ConvolutionKernelData("Emboss", EMBOSS_DATA, 3, 3, 1, 128), new ConvolutionKernelData("Psychedelic Distillation", PSYCHEDELIC_DISTILLATION_DATA, 5, 5, 1, 0), new ConvolutionKernelData("Lithograph", LITHOGRAPH_DATA, 5, 5, 1, 0), new ConvolutionKernelData("Horizontal Sobel", HORIZONTAL_SOBEL_DATA, 3, 3, 1, 0), new ConvolutionKernelData("Vertical Sobel", VERTICAL_SOBEL_DATA, 3, 3, 1, 0), new ConvolutionKernelData("Horizontal Prewitt", HORIZONTAL_PREWITT_DATA, 3, 3, 1, 0), new ConvolutionKernelData("Vertical Prewitt", VERTICAL_PREWITT_DATA, 3, 3, 1, 0) }; private int kernelBias; private int[] kernelData; private int kernelDiv; private int kernelHeight; private int kernelWidth; /** * Copies row data from input image to buffer and replicates * samples at the left and right border. */ private void copyRow(IntegerImage srcImage, int srcChannelIndex, int rowIndex, int[] dest, int destOffset, int numBorderColumns) { /* row has a width of N + 1 samples at positions 0 to N X X 0 1 ... N Y Y copy byte at 0 to all X positions copy byte at N to all Y positions */ final int WIDTH = srcImage.getWidth(); srcImage.getSamples(srcChannelIndex, 0, rowIndex, WIDTH, 1, dest, destOffset + numBorderColumns); // copy leftmost sample to X X positions int srcOffset = destOffset + numBorderColumns; int offset = numBorderColumns - 1; while (offset >= 0) { dest[offset--] = dest[srcOffset]; } // copy rightmost sample to Y Y positions srcOffset = destOffset + numBorderColumns + WIDTH - 1; offset = srcOffset + 1; int n = numBorderColumns; while (n-- > 0) { dest[offset++] = dest[srcOffset]; } } /** * Filters argument image with argument kernel type and returns output image. * Static convenience method to do filtering with one line of code: *

PixelImage blurredImage = ConvolutionKernelFilter.filter(in, ConvolutionKernelFilter.TYPE_BLUR);
*/ public static PixelImage filter(PixelImage input, int kernelType) { return filter(input, PREDEFINED_KERNELS[kernelType]); } public static PixelImage filter(PixelImage input, ConvolutionKernelData data) { ConvolutionKernelFilter op = new ConvolutionKernelFilter(); op.setKernel(data); op.setInputImage(input); try { op.process(); return op.getOutputImage(); } catch (OperationFailedException ofe) { return null; } } /** * Applies the kernel to one of the channels of an image. * @param channelIndex index of the channel to be filtered, must be from 0 to ByteChannelImage.getNumChannels() - 1 */ private void process(int channelIndex, IntegerImage in, IntegerImage out) { final int H_DIM = kernelWidth; final int H_DIM_2 = (H_DIM / 2); final int V_DIM = kernelHeight; final int V_DIM_2 = (V_DIM / 2); final int HEIGHT = in.getHeight(); final int WIDTH = in.getWidth(); final int NEW_WIDTH = WIDTH + 2 * H_DIM_2; final int NEW_HEIGHT = HEIGHT + 2 * V_DIM_2; final int MAX = in.getMaxSample(channelIndex); int processedItems = channelIndex * HEIGHT; final int TOTAL_ITEMS = in.getNumChannels() * HEIGHT; int[] src = new int[NEW_WIDTH * NEW_HEIGHT]; // fill src with data for (int y = 0, offs = V_DIM_2 * NEW_WIDTH; y < HEIGHT; y++, offs += NEW_WIDTH) { copyRow(in, channelIndex, y, src, offs, H_DIM_2); } // copy row H_DIM_2 to 0 .. H_DIM_2 - 1 int srcOffset = V_DIM_2 * NEW_WIDTH; for (int y = 0; y < V_DIM_2; y++) { System.arraycopy(src, srcOffset, src, y * NEW_WIDTH, NEW_WIDTH); } // copy row H_DIM_2 + HEIGHT - 1 to H_DIM_2 + HEIGHT .. 2 * H_DIM_2 + HEIGHT - 1 srcOffset = (HEIGHT + V_DIM_2 - 1) * NEW_WIDTH; for (int y = V_DIM_2 + HEIGHT; y < NEW_HEIGHT; y++) { System.arraycopy(src, srcOffset, src, y * NEW_WIDTH, NEW_WIDTH); } // do the filtering int count = H_DIM * V_DIM; final int[] kernelLine = new int[count]; final int[] kernelD = new int[count]; int p; count = 0; final int j = H_DIM - 1; for (int x = H_DIM; x-- > 0;) { for (int y = V_DIM; y-- > 0;) { int index = y * H_DIM + x; if (kernelData[index] != 0) { kernelLine[count] = kernelData[index]; kernelD[count] = y * NEW_WIDTH + x; count++; } } } // all kernel elements are zero => nothing to do, resulting channel will be full of zeroes if (count == 0) { setProgress(channelIndex, in.getNumChannels()); return; } p = (HEIGHT - 1) * NEW_WIDTH + (WIDTH - 1); int[] dest = new int[WIDTH]; for (int y = HEIGHT; y-- > 0;) { for (int x = WIDTH; x-- > 0;) { int sum = 0; for (int i = count; i-- > 0;) { sum += (src[p + kernelD[i]] & MAX) * kernelLine[i]; } sum = (sum / kernelDiv) + kernelBias; if (sum <= 0) { dest[x] = 0; } else if (sum >= MAX) { dest[x] = MAX; } else { dest[x] = sum; } // (byte)(((0xFFFFFF00 & sum) == 0) ? sum : ((sum >>> 31) - 1)); p--; } out.putSamples(channelIndex, 0, y, WIDTH, 1, dest, 0); p -= j; setProgress(processedItems++, TOTAL_ITEMS); } } private void process(IntegerImage in, IntegerImage out) { for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) { process(channelIndex, in, out); } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); if (in instanceof GrayIntegerImage || in instanceof RGBIntegerImage) { PixelImage out = getOutputImage(); if (out == null) { out = (IntegerImage)in.createCompatibleImage(in.getWidth(), in.getHeight()); setOutputImage(out); } process((IntegerImage)in, (IntegerImage)out); } else { throw new WrongParameterException("Input image must implement GrayIntegerImage or RGBIntegerImage."); } } /** * Sets properties of the kernel to be used in this operation. * @param data the kernel coefficients; this one-dimensional array stores * them in order top-to-bottom, left-to-right; the length of this * array must be at least width times height * @param width the width of the kernel; must not be even * @param height the height of the kernel; must not be even * @param div the result is divided by this value after the addition of value * (so this value must not be zero) * @param bias this value is added to the result before the division */ public void setKernel(int[] data, int width, int height, int div, int bias) { if (data == null) { throw new IllegalArgumentException("Kernel data must be non-null."); } if (width < 1) { throw new IllegalArgumentException("Kernel width must be at least 1."); } if (width % 2 != 1) { throw new IllegalArgumentException("Kernel width must not be even."); } if (height < 1) { throw new IllegalArgumentException("Kernel height must be at least 1."); } if (height % 2 != 1) { throw new IllegalArgumentException("Kernel width must not be even."); } if (data.length < width * height) { throw new IllegalArgumentException("Kernel data must have a length >= " + (width * height) + " to hold " + width + " times " + height + " elements."); } if (div == 0) { throw new IllegalArgumentException("The div parameter must not be zero."); } kernelData = data; kernelWidth = width; kernelHeight = height; kernelDiv = div; kernelBias = bias; } /** * Sets kernel data to be used for filtering. * @param ckd all information necessary for filtering */ public void setKernel(ConvolutionKernelData ckd) { setKernel(ckd.getData(), ckd.getWidth(), ckd.getHeight(), ckd.getDiv(), ckd.getBias()); } /** * Sets one of the predefined kernel types to be used for filtering. * @param type one of the TYPE_xyz constants of this class * @throws IllegalArgumentException if the argument is not a valid TYPE_xyz constant */ public void setKernel(int type) { if (type < 0 || type >= PREDEFINED_KERNELS.length) { throw new IllegalArgumentException("Not a valid type index for predefined kernels: " + type); } else { setKernel(PREDEFINED_KERNELS[type]); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/0000775000000000000000000000000010546532075020746 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/Gray16Image.java0000664000000000000000000000145607741250133023626 0ustar /* * Gray16Image * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.ShortChannelImage; import net.sourceforge.jiu.data.GrayIntegerImage; /** * Interface for grayscale images using integer samples that are sixteen bits large. * Valid sample values must lie in the interval 0 to 65535 (including both of those values). * Like all grayscale images, implementations of this class are supposed to have one channel only. * Simply merges the two interfaces GrayIntegerImage and ShortChannelImage without adding methods of its own. * @author Marco Schmidt * @since 0.11.0 * @see ShortChannelImage * @see GrayIntegerImage */ public interface Gray16Image extends GrayIntegerImage, ShortChannelImage { } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/RGB24Image.java0000664000000000000000000000076007741250133023332 0ustar /* * RGB24Image * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.ByteChannelImage; import net.sourceforge.jiu.data.RGBIntegerImage; /** * An empty interface for RGB truecolor images with integer samples * that are each eight bits large (thus, 24 bits per pixel). * @author Marco Schmidt */ public interface RGB24Image extends ByteChannelImage, RGBIntegerImage { } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/RGBImage.java0000664000000000000000000000066407741250133023167 0ustar /* * RGBImage * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.RGBIndex; /** * An interface for RGB truecolor images. * This is an empty interface, useful for labeling an image type * as being an RGB image type. * @author Marco Schmidt * @since 0.9.0 */ public interface RGBImage extends PixelImage, RGBIndex { } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/MemoryRGB48Image.java0000664000000000000000000000174107741250133024531 0ustar /* * MemoryRGB48Image * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.MemoryShortChannelImage; import net.sourceforge.jiu.data.RGB48Image; /** * A class to store 48 bit RGB truecolor images in memory. * @author Marco Schmidt * @since 0.12.0 * @see RGB24Image */ public class MemoryRGB48Image extends MemoryShortChannelImage implements RGB48Image { /** * Creates a new object of this class, with width and height as * specified by the arguments. * @param width the horizontal resolution of the new image in pixels * @param height the vertical resolution of the new image in pixels */ public MemoryRGB48Image(int width, int height) { super(3, width, height); } public PixelImage createCompatibleImage(int width, int height) { return new MemoryRGB48Image(width, height); } public Class getImageType() { return RGB48Image.class; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/MemoryPaletted8Image.java0000664000000000000000000000625407741250133025601 0ustar /* * Paletted8Image * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.PixelImage; /** * This class stores a paletted image with one byte per sample in memory. * * @author Marco Schmidt * @see net.sourceforge.jiu.data.ByteChannelImage * @see net.sourceforge.jiu.data.IntegerImage * @see net.sourceforge.jiu.data.Palette */ public class MemoryPaletted8Image extends MemoryByteChannelImage implements Paletted8Image { /** * This image's palette. */ private Palette palette; private int maxSampleValue; /** * Create an image of byte channels. * Image data will be completely in memory, so memory requirements are * width * height * numChannels bytes. * Note that the data will not be initialized, so you should not assume * anything about its content. * @param width the horizontal resolution, must be non-zero and positive * @param height the vertical resolution, must be non-zero and positive * @throws IllegalArgumentException if any of the parameters are smaller than 1 */ public MemoryPaletted8Image(int width, int height) { super(1, width, height); palette = null; maxSampleValue = 255; } public MemoryPaletted8Image(int width, int height, Palette palette) { this(width, height); setPalette(palette); } public static void checkPalette(Palette palette) { if (palette == null) { throw new IllegalArgumentException("Palette must be non-null."); } else { int numEntries = palette.getNumEntries(); if (numEntries < 1 || numEntries > 256) { throw new IllegalArgumentException("Number of entries must " + "be from 1..256 for a Paletted8Image; got: " + numEntries); } } } public PixelImage createCompatibleImage(int width, int height) { Palette newPalette = null; Palette myPalette = getPalette(); if (myPalette != null) { newPalette = (Palette)myPalette.clone(); } return new MemoryPaletted8Image(width, height, newPalette); } public long getAllocatedMemory() { long result = super.getAllocatedMemory(); Palette myPalette = getPalette(); if (myPalette != null) { result += myPalette.getAllocatedMemory(); } return result; } public Class getImageType() { return Paletted8Image.class; } public int getMaxSample(int channel) { return maxSampleValue; } /** * Returns this image's palette. * @see #setPalette */ public Palette getPalette() { return palette; } public String getTypeDescription() { return "Paletted image, 8 bits per pixel"; } /** * Sets this image's palette to a new value. * @see #getPalette */ public void setPalette(Palette palette) { if (palette != null && palette.getNumEntries() > 256) { throw new IllegalArgumentException("Cannot use palette with more " + "than 256 entries in a Paletted8Image."); } this.palette = palette; if (palette == null) { maxSampleValue = 255; } else { maxSampleValue = palette.getNumEntries() - 1; } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/RGBIntegerImage.java0000664000000000000000000000103607741250133024477 0ustar /* * RGBIntegerImage * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.RGBImage; /** * An interface for RGB truecolor images that have integer samples. * A combination of {@link net.sourceforge.jiu.data.IntegerImage} and * {@link net.sourceforge.jiu.data.RGBImage}. * * @author Marco Schmidt * @since 0.9.0 */ public interface RGBIntegerImage extends IntegerImage, RGBImage { } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/TransparencyInformation.java0000664000000000000000000000323507741250133026466 0ustar /* * TransparencyInformation * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.IntegerImage; /** * An interface that represents transparency information which may be * available for a pixel image. * Transparency information describes how an image is supposed to be * drawn on a pixel background (e.g. another image). * That way, irregularly shaped images can easily be handled by excluding * those pixels of a rectangular image that are not part of the image. * @author Marco Schmidt */ public interface TransparencyInformation { /** * Returns an image object that contains an alpha channel. * The first channel of that image is supposed to be the alpha channel. * @return the alpha channel image object * @see #setAlphaChannelImage */ IntegerImage getAlphaChannelImage(); /** * If there is a transparency index, this method returns it. * Otherwise, the return value is undefined. * @return transparency index * @see #setTransparencyIndex */ Integer getTransparencyIndex(); /** * Set a new alpha channel image object. * @see #getAlphaChannelImage */ void setAlphaChannelImage(IntegerImage newImage); /** * Set a new transparency value. * Can be null. * However, if the value is non-null, it must encapsulate an * integer number which is 0 or larger. * @param newValue new transparency index * @see #getAlphaChannelImage * @throws IllegalArgumentException if the argument is non-null and contains a negative value */ void setTransparencyIndex(Integer newValue); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/RGB48Image.java0000664000000000000000000000076407741250133023344 0ustar /* * RGB48Image * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.data.ShortChannelImage; /** * An empty interface for RGB truecolor images with integer samples * that are each sixteen bits large (thus, 48 bits per pixel). * @author Marco Schmidt * @since 0.12.0 */ public interface RGB48Image extends ShortChannelImage, RGBIntegerImage { } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/PixelImage.java0000664000000000000000000000606307741250133023635 0ustar /* * PixelImage * * Copyright (c) 2000, 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; /** * The base interface for all image data types in JIU. * These image data classes and interfaces share the following properties: * * * @author Marco Schmidt */ public interface PixelImage { /** * Creates an instance of the same class as this one, with width and height * given by the arguments. * @param width the horizontal resolution of the new image * @param height the vertical resolution of the new image * @return the new image * @throws IllegalArgumentException if width or height are smaller than one */ PixelImage createCompatibleImage(int width, int height); /** * Creates an new image object that will be of the same type as this one, * with the same image data, using entirely new resources. * @return the new image object */ PixelImage createCopy(); /** * Returns the number of bytes that were dynamically allocated for * this image object. * @return allocated memory in bytes */ long getAllocatedMemory(); /** * Returns the number of bits per pixel of this image. * That is the number of bits per sample for all channels of this image. * Does not include any transparency channels. */ int getBitsPerPixel(); /** * Returns the vertical resolution of the image in pixels. * Must be one or larger. * @return height in pixels */ int getHeight(); /** * If there is a single interface or class that describes the image data type * of this class, the {@link java.lang.Class} object associated with that * interface (or class) is returned (or null otherwise). * This {@link java.lang.Class} object, if available for two image objects, * can be used to find out if they are compatible. * Example: {@link net.sourceforge.jiu.data.MemoryGray8Image} returns * net.sourceforge.jiu.data.Gray8Image.class. */ Class getImageType(); /** * Returns the number of channels in this image. * Must be one or larger. * @return the number of channels */ int getNumChannels(); /** * Returns the horizontal resolution of the image in pixels. * Must be one or larger. * @return width in pixels */ int getWidth(); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/ShortChannelImage.java0000664000000000000000000001175707741250133025152 0ustar /* * ShortChannelImage * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; /** * An extension of the {@link IntegerImage} interface that restricts the image to * short samples. * The minimum sample value for all channels is 0, * the maximum sample value 65535 (216 - 1). *

* Number of channels and resolution must be given to the constructor * and cannot be changed after creation. *

* Each channel of the image is made up of short values. * Note that shorts in Java are signed, they can take values from -32768 to 32767. * If you use {@link IntegerImage}'s getSample and putSample methods * you don't have to deal with this, you always get int samples * that are in the 0 .. 65535 interval. *

* To manually convert a Java short value to an int value * in the range of 0 to 65535, do the following: *

 * short s = ...; // initialize short value
 * int i = s & 0xffff; 
 * // i now is a value between 0 and 65535
 * 
* @author Marco Schmidt * @since 0.11.0 */ public interface ShortChannelImage extends IntegerImage { /** * Sets all samples of the first channel to the argument short value. * Equal to clear(0, newValue);. * @param newValue all samples in the first channel are set to this value * @see #clear(int, short) * @see #clear(int) * @see #clear(int, int) */ void clear(short newValue); /** * Sets all samples of one channel to a new value. * @param channelIndex zero-based index of the channel to be cleared (must be smaller than {@link #getNumChannels()} * @param newValue all samples in the channel will be set to this value */ void clear(int channelIndex, short newValue); /** * Returns a single short sample from the first channel and the specified position. * A call to this method is the same as getShortSample(0, x, y). * @param x horizontal position of the sample to be returned (must be between 0 and {@link #getWidth()} - 1 * @param y vertical position of the sample to be returned (must be between 0 and {@link #getHeight()} - 1 * @return the requested short sample */ short getShortSample(int x, int y); /** * Returns a single short sample from the image. * When possible, try copying several samples at a time for higher speed ({@link #getShortSamples}). * @param channel the number of the channel of the sample; must be from 0 to {@link #getNumChannels()} - 1 * @param x the column of the sample to be returned; must be from 0 to {@link #getWidth()} - 1 * @param y the row of the sample; must be from 0 to {@link #getHeight()} - 1 * @return the sample, a single short value * @throws IllegalArgumentException if the arguments hurt one of the preconditions above * @see #getShortSamples */ short getShortSample(int channel, int x, int y); /** * Copies samples from this image to a short array. * Copies num samples in row y of channel * channel, starting at horizontal offset x. * Data will be written to the dest array, starting at * offset destOffset. * Data will be copied from one row only, so a maximum of * getWidth() * samples can be copied with a call to this method. * * @param channelIndex the index of the channel to be copied from; must be * from 0 to getNumChannels() - 1 * @param x the horizontal offset where copying will start; must be from * 0 to getWidth() - 1 * @param y the row from which will be copied; must be from * 0 to getHeight() - 1 * @param w number of columns to be copied * @param h number of rows to be copied * @param dest the array where the data will be copied to; must have a * length of at least destOffset + num * @param destOffset the offset into dest where this method * will start copying data * @throws IllegalArgumentException if the arguments hurt one of the many * preconditions above */ void getShortSamples(int channelIndex, int x, int y, int w, int h, short[] dest, int destOffset); /** * Sets one short sample in one channel to a new value. */ void putShortSample(int channel, int x, int y, short newValue); /** * Sets one short sample in the first channel (index 0) to a new value. * Result is equal to putShortSample(0, x, y, newValue);. */ void putShortSample(int x, int y, short newValue); /** * Copies a number of samples from the argument array to this image. */ void putShortSamples(int channel, int x, int y, int w, int h, short[] src, int srcOffset); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/MemoryShortChannelImage.java0000664000000000000000000002150007741250133026326 0ustar /* * MemoryShortChannelImage * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; /** * An implementation of {@link ShortChannelImage} that stores image channels as * short[] arrays in memory. * An image can have an arbitrary number of channels. *

* This class is abstract because it is merely a data container. * It takes a subclass like {@link MemoryGray16Image} to give meaning to the values. * * @author Marco Schmidt * @since 0.11.0 */ public abstract class MemoryShortChannelImage implements ShortChannelImage { private final short[][] data; private final short[] firstChannel; // == data[0] private final int numChannels; // == data.length private final int width; private final int height; private final int numPixels; // == width * height /** * Create an image of short channels. * Image data will be completely in memory, so memory requirements are * width * height * numChannels * 2 bytes. * @param numChannels the number of channels in this image, must be * larger than zero * @param width the horizontal resolution, must be larger than zero * @param height the vertical resolution, must be larger than zero */ public MemoryShortChannelImage(int numChannels, int width, int height) { if (width < 1) { throw new IllegalArgumentException("Width must be larger than " + "0: " + width); } if (height < 1) { throw new IllegalArgumentException("Height must be larger than" + " 0: " + height); } if (numChannels < 1) { throw new IllegalArgumentException("Number of channels must be " + "larger than 0: " + numChannels); } this.width = width; this.height = height; this.numChannels = numChannels; numPixels = width * height; data = new short[numChannels][]; for (int i = 0; i < numChannels; i++) { data[i] = new short[numPixels]; } firstChannel = data[0]; } /** * Throws an exception if the arguments do not form a valid horizontal * sequence of samples. * To be valid, all of the following requirements must be met: */ protected void checkPositionAndNumber(int channel, int x, int y, int w, int h) { if (channel < 0 || channel >= numChannels) { throw new IllegalArgumentException("Illegal channel index value: " + channel + ". Must be from 0 to " + (numChannels - 1) + "."); } if (x < 0 || x >= getWidth()) { throw new IllegalArgumentException("The value for x is invalid: " + x + "."); } if (w < 1) { throw new IllegalArgumentException("The value for w is invalid: " + w + "."); } if (x + w > getWidth()) { throw new IllegalArgumentException("The values x + w exceed the " + "width of this image; x=" + x + ", w=" + w + ", width=" + getWidth()); } if (h < 1) { throw new IllegalArgumentException("The value for h is invalid: " + h + "."); } if (y < 0 || y >= getHeight()) { throw new IllegalArgumentException("The value for y is invalid: " + y + "."); } if (y + h > getHeight()) { throw new IllegalArgumentException("The values y + h exceed the " + "height of this image; y=" + y + ", h=" + h + ", height=" + getHeight()); } } public void clear(short newValue) { clear(0, newValue); } public void clear(int channelIndex, short newValue) { // check the validity of the channel index checkPositionAndNumber(channelIndex, 0, 0, 1, 1); // get the correct channel as short[] final short[] CHANNEL = data[channelIndex]; // fill channel with the argument value final short VALUE = (short)newValue; final int LENGTH = CHANNEL.length; for (int i = 0; i < LENGTH; i++) { CHANNEL[i] = VALUE; } } public void clear(int newValue) { clear(0, (short)newValue); } public void clear(int channelIndex, int newValue) { clear(channelIndex, (short)newValue); } public abstract PixelImage createCompatibleImage(int width, int height); public PixelImage createCopy() { PixelImage copy = createCompatibleImage(getWidth(), getHeight()); MemoryShortChannelImage result = (MemoryShortChannelImage)copy; for (int channelIndex = 0; channelIndex < getNumChannels(); channelIndex++) { System.arraycopy(data[channelIndex], 0, result.data[channelIndex], 0, data[channelIndex].length); } return result; } public long getAllocatedMemory() { long result = 0; if (data != null) { int channelIndex = 0; while (channelIndex < data.length) { short[] array = data[channelIndex++]; if (array != null) { result += array.length * 2; } } } return result; } public int getBitsPerPixel() { return numChannels * 16; } public short getShortSample(int channel, int x, int y) { /* advantage of the following approach: we don't check arguments before we access the data (too costly); instead, we have the VM throw an array index out of bounds exception and then determine which of the arguments was wrong; that's better than checking before access all of the time => the VM checks anyway we then throw a meaningful IllegalArgumentException (in checkPositionAndNumber) disadvantage: some erroneous arguments aren't noticed, example: width=100, height=20, x=100, y=0 will not result in an error (because only 0..99 are valid for x) but in the return of sample(0/1) */ try { return data[channel][y * width + x]; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(channel, x, y, 1, 1); return -1; } } public short getShortSample(int x, int y) { return getShortSample(0, x, y); } public void getShortSamples(int channel, int x, int y, int w, int h, short[] dest, int destOffset) { checkPositionAndNumber(channel, x, y, w, h); short[] src = data[channel]; try { int srcOffset = y * width + x; while (h-- > 0) { java.lang.System.arraycopy(src, srcOffset, dest, destOffset, w); srcOffset += width; destOffset += w; } } catch (ArrayIndexOutOfBoundsException aioobe) { } } public final int getHeight() { return height; } public int getMaxSample(int channel) { return 65535; } public int getNumChannels() { return numChannels; } public final int getSample(int x, int y) { try { return firstChannel[y * width + x] & 0xffff; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(0, x, y, 1, 1); return -1; } } public final int getSample(int channel, int x, int y) { try { return data[channel][y * width + x] & 0xffff; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(channel, x, y, 1, 1); return -1; } } public void getSamples(int channel, int x, int y, int w, int h, int[] dest, int destOffs) { if (w < 1 || h < 1) { return; } short[] src = data[channel]; int srcOffs = y * width + x; while (h-- != 0) { int loop = w; int from = srcOffs; while (loop-- != 0) { dest[destOffs++] = src[from++] & 0xffff; } srcOffs += width; } } public final int getWidth() { return width; } public final void putShortSample(int channel, int x, int y, short newValue) { checkPositionAndNumber(channel, x, y, 1, 1); try { data[channel][y * width + x] = newValue; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(channel, x, y, 1, 1); } } public final void putShortSample(int x, int y, short newValue) { checkPositionAndNumber(0, x, y, 1, 1); try { firstChannel[y * width + x] = newValue; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(0, x, y, 1, 1); } } public void putShortSamples(int channel, int x, int y, int w, int h, short[] src, int srcOffset) { checkPositionAndNumber(channel, x, y, w, h); short[] dest = data[channel]; int destOffset = y * width + x; while (h-- > 0) { java.lang.System.arraycopy(src, srcOffset, dest, destOffset, w); srcOffset += w; destOffset += width; } } public void putSamples(int channel, int x, int y, int w, int h, int[] src, int srcOffs) { checkPositionAndNumber(channel, x, y, w, h); short[] dest = data[channel]; int destOffs = y * width + x; while (h-- != 0) { int loop = w; int to = destOffs; while (loop-- != 0) { dest[to++] = (short)src[srcOffs++]; } destOffs += width; } } public final void putSample(int x, int y, int newValue) { putShortSample(0, x, y, (short)newValue); } public final void putSample(int channel, int x, int y, int newValue) { putShortSample(channel, x, y, (short)newValue); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/MemoryBilevelImage.java0000664000000000000000000002034407741250133025325 0ustar /* * MemoryBilevelImage * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.util.ArrayConverter; /** * An implementation of the {@link BilevelImage} interface that stores image * data in a byte array in memory. * An image of width times height pixels will require * (width + 7) / 8 * height bytes of memory. * @author Marco Schmidt */ public class MemoryBilevelImage implements BilevelImage { private final int BYTES_PER_ROW; private final byte[] data; private final int HEIGHT; private final int WIDTH; /** * Create a new MemoryBilevelImage object with the specified resolution. * @param width the horizontal resolution of the new image, must be larger than zero * @param height the vertical resolution of the new image, must be larger than zero * @throws IllegalArgumentException if any of the two parameters is smaller than one */ public MemoryBilevelImage(int width, int height) { if (width < 1) { throw new IllegalArgumentException("Width must be larger than zero; got " + width); } if (height < 1) { throw new IllegalArgumentException("Height must be larger than zero; got " + height); } BYTES_PER_ROW = (width + 7) / 8; WIDTH = width; HEIGHT = height; data = new byte[BYTES_PER_ROW * HEIGHT]; } private void checkBitOffset(int bitOffset) { if (bitOffset < 0 || bitOffset > 7) { throw new IllegalArgumentException("A bit offset value must be from the interval 0..7."); } } private void checkPositionAndNumber(int x, int y, int w, int h) { if (w < 0) { throw new IllegalArgumentException("Negative number of samples " + "to be copied: " + w); } if (h < 0) { throw new IllegalArgumentException("Negative number of rows " + "to be copied: " + h); } if (x < 0 || x >= getWidth()) { throw new IllegalArgumentException("The value for x is invalid: " + x + "."); } if (y < 0 || y >= getHeight()) { throw new IllegalArgumentException("The value for y is invalid: " + y + "."); } if (x + w > getWidth()) { throw new IllegalArgumentException("Cannot copy " + w + " values starting at " + "offset " + x + " (width is only " + getWidth() + ")."); } if (y + h > getHeight()) { throw new IllegalArgumentException("Cannot copy " + h + " rows starting at " + y + " (height is only " + getHeight() + ")."); } } private void checkValue(int value) { if (value != WHITE && value != BLACK) { throw new IllegalArgumentException("Sample value must be either BilevelImage.BLACK or BilevelImage.WHITE."); } } public void clear(int newValue) { clear(0, newValue); } public void clear(int channelIndex, int newValue) { if (channelIndex != 0) { throw new IllegalArgumentException("Invalid channel index; " + "bilevel images have only one channel, so 0 is the only " + "valid argument; got " + channelIndex); } checkValue(newValue); byte value; if (newValue == BLACK) { value = 0; } else { value = (byte)0xff; } for (int i = 0; i < data.length; i++) { data[i] = value; } } public PixelImage createCompatibleImage(int width, int height) { return new MemoryBilevelImage(width, height); } public PixelImage createCopy() { PixelImage copy = createCompatibleImage(getWidth(), getHeight()); MemoryBilevelImage result = (MemoryBilevelImage)copy; System.arraycopy(data, 0, result.data, 0, data.length); return result; } public long getAllocatedMemory() { return data.length; } public int getBitsPerPixel() { return 1; } public int getHeight() { return HEIGHT; } public Class getImageType() { return BilevelImage.class; } public int getMaxSample(int channelIndex) { return 1; } public int getNumChannels() { return 1; } public void getPackedBytes(int x, int y, int numSamples, byte[] dest, int destOffset, int destBitOffset) { checkPositionAndNumber(x, y, numSamples, 1); checkBitOffset(destBitOffset); int srcOffset = y * BYTES_PER_ROW + (x >> 3); int srcBitOffset = x & 7; ArrayConverter.copyPackedBytes(data, srcOffset, srcBitOffset, dest, destOffset, destBitOffset, numSamples); } public int getSample(int x, int y) { if (isBlack(x, y)) { return BLACK; } else { return WHITE; } } public int getSample(int channelIndex, int x, int y) { if (channelIndex == 0) { if (isBlack(x, y)) { return BLACK; } else { return WHITE; } } else { throw new IllegalArgumentException("The channelIndex argument must be 0 for bilevel images; got " + channelIndex); } } public void getSamples(int channelIndex, int x, int y, int w, int h, int[] dest, int destOffset) { final int INITIAL_MASK = 1 << (7 - (x % 8)); int offset = y * BYTES_PER_ROW + x / 8; while (h-- > 0) { int mask = INITIAL_MASK; int srcOffset = offset; int remainingColumns = w; int srcValue = data[srcOffset++] & 0xff; while (remainingColumns-- != 0) { if ((srcValue & mask) == 0) { dest[destOffset++] = BLACK; } else { dest[destOffset++] = WHITE; } if (mask == 1) { mask = 128; srcValue = data[srcOffset++] & 0xff; } else { mask >>= 1; } } offset += BYTES_PER_ROW; } } public int getWidth() { return WIDTH; } public boolean isBlack(int x, int y) { try { int offset = y * BYTES_PER_ROW + (x >> 3); return (data[offset] & (byte)(1 << (7 - (x & 7)))) == 0; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(x, y, 1, 1); } return true; } public boolean isWhite(int x, int y) { try { int offset = y * BYTES_PER_ROW + (x >> 3); return (data[offset] & (byte)(1 << (7 - (x & 7)))) != 0; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(x, y, 1, 1); } return true; } public void putBlack(int x, int y) { try { int offset = y * BYTES_PER_ROW + (x >> 3); data[offset] &= (byte)(255 - (1 << (7 - (x & 7)))); } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(x, y, 1, 1); } } public void putPackedBytes(int x, int y, int numSamples, byte[] src, int srcOffset, int srcBitOffset) { checkPositionAndNumber(x, y, numSamples, 1); checkBitOffset(srcBitOffset); int destOffset = y * BYTES_PER_ROW + (x >> 3); int destBitOffset = x & 7; ArrayConverter.copyPackedBytes(src, srcOffset, srcBitOffset, data, destOffset, destBitOffset, numSamples); } public void putSample(int x, int y, int newValue) { putSample(0, x, y, newValue); } public void putSample(int channelIndex, int x, int y, int newValue) { checkValue(newValue); try { int offset = y * BYTES_PER_ROW + (x >> 3); if (newValue == BLACK) { data[offset] &= (byte)(255 - (1 << (7 - (x & 7)))); } else { data[offset] |= (byte)(1 << (7 - (x & 7))); } } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(x, y, 1, 1); } } public void putSamples(int channelIndex, int x, int y, int w, int h, int[] src, int srcOffset) { checkPositionAndNumber(x, y, w, h); int INITIAL_ROW_MASK = 1 << (7 - (x & 7)); int initialDestOffset = y * BYTES_PER_ROW + (x >> 3); while (h-- > 0) { int mask = INITIAL_ROW_MASK; int destOffset = initialDestOffset; int pixelsLeft = w; while (pixelsLeft-- > 0) { if (src[srcOffset++] == BLACK) { data[destOffset] &= (byte)(255 - mask); } else { data[destOffset] |= (byte)mask; } if (mask == 1) { mask = 128; destOffset++; } } initialDestOffset += BYTES_PER_ROW; } } public void putWhite(int x, int y) { try { int offset = y * BYTES_PER_ROW + (x >> 3); data[offset] |= (byte)(1 << (7 - (x & 7))); } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(x, y, 1, 1); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/Gray8Image.java0000664000000000000000000000142107741250133023537 0ustar /* * Gray8Image * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.ByteChannelImage; import net.sourceforge.jiu.data.GrayIntegerImage; /** * Interface for grayscale images using integer samples that are eight bits large. * Valid sample values must lie in the interval 0 to 255 (including both of those values). * Like all grayscale images, implementations of this class are supposed to have one channel only. * Simply merges the two interfaces GrayIntegerImage and ByteChannelImage without adding new methods. * @author Marco Schmidt * @see ByteChannelImage * @see GrayIntegerImage */ public interface Gray8Image extends GrayIntegerImage, ByteChannelImage { } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/MemoryByteChannelImage.java0000664000000000000000000002212607741250133026137 0ustar /* * MemoryByteChannelImage * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; /** * An implementation of {@link ByteChannelImage} that stores image channels as * byte[] arrays in memory. * An image can have an arbitrary number of channels. *

* This class is abstract because it is merely a data container. * It takes a subclass like {@link MemoryGray8Image} to give meaning to the values. * * @author Marco Schmidt */ public abstract class MemoryByteChannelImage implements ByteChannelImage { private final byte[][] data; private final byte[] firstChannel; // == data[0] private final int numChannels; // == data.length private final int width; private final int height; private final int numPixels; // == width * height /** * Create an image of byte channels. * Image data will be completely in memory, so memory requirements are * width * height * numChannels bytes. * Note that the data will not be initialized, so you should not assume * anything about its content. * * @param numChannels the number of channels in this image, must be * non-zero and positive * @param width the horizontal resolution, must be non-zero and positive * @param height the vertical resolution, must be non-zero and positive * @throws IllegalArgumentException if any of the parameters are invalid * or if width times height exceeds two GB * @throws OutOfMemoryException if there is not enough free memory for * the specified resolution */ public MemoryByteChannelImage(int numChannels, int width, int height) { if (width < 1) { throw new IllegalArgumentException("Width must be larger than " + "0: " + width); } if (height < 1) { throw new IllegalArgumentException("Height must be larger than" + " 0: " + height); } if (numChannels < 1) { throw new IllegalArgumentException("Number of channels must be " + "larger than 0: " + numChannels); } this.width = width; this.height = height; this.numChannels = numChannels; numPixels = width * height; data = new byte[numChannels][]; for (int i = 0; i < numChannels; i++) { data[i] = new byte[numPixels]; } firstChannel = data[0]; } /** * Throws an exception if the arguments do not form a valid horizontal * sequence of samples. * To be valid, all of the following requirements must be met: */ protected void checkPositionAndNumber(int channel, int x, int y, int w, int h) { if (channel < 0 || channel >= numChannels) { throw new IllegalArgumentException("Illegal channel index value: " + channel + ". Must be from 0 to " + (numChannels - 1) + "."); } if (x < 0 || x >= getWidth()) { throw new IllegalArgumentException("The value for x is invalid: " + x + "."); } if (w < 1) { throw new IllegalArgumentException("The value for w is invalid: " + w + "."); } if (x + w > getWidth()) { throw new IllegalArgumentException("The values x + w exceed the " + "width of this image; x=" + x + ", w=" + w + ", width=" + getWidth()); } if (h < 1) { throw new IllegalArgumentException("The value for h is invalid: " + h + "."); } if (y < 0 || y >= getHeight()) { throw new IllegalArgumentException("The value for y is invalid: " + y + "."); } if (y + h > getHeight()) { throw new IllegalArgumentException("The values y + h exceed the " + "height of this image; y=" + y + ", h=" + h + ", height=" + getHeight()); } } public void clear(byte newValue) { clear(0, newValue); } public void clear(int channelIndex, byte newValue) { // check the validity of the channel index checkPositionAndNumber(channelIndex, 0, 0, 1, 1); // get the correct channel as byte[] final byte[] CHANNEL = data[channelIndex]; // fill channel with the argument value final byte VALUE = (byte)newValue; final int LENGTH = CHANNEL.length; for (int i = 0; i < LENGTH; i++) { CHANNEL[i] = VALUE; } } public void clear(int newValue) { clear(0, (byte)newValue); } public void clear(int channelIndex, int newValue) { clear(channelIndex, (byte)newValue); } public abstract PixelImage createCompatibleImage(int width, int height); public PixelImage createCopy() { PixelImage copy = createCompatibleImage(getWidth(), getHeight()); MemoryByteChannelImage result = (MemoryByteChannelImage)copy; for (int channelIndex = 0; channelIndex < getNumChannels(); channelIndex++) { System.arraycopy(data[channelIndex], 0, result.data[channelIndex], 0, data[channelIndex].length); } return result; } public long getAllocatedMemory() { long result = 0; if (data != null) { int channelIndex = 0; while (channelIndex < data.length) { byte[] array = data[channelIndex++]; if (array != null) { result += array.length; } } } return result; } public int getBitsPerPixel() { return numChannels * 8; } public byte getByteSample(int channel, int x, int y) { /* advantage of the following approach: we don't check arguments before we access the data (too costly); instead, we have the VM throw an array index out of bounds exception and then determine which of the arguments was wrong; that's better than checking before access all of the time => the VM checks anyway we then throw a meaningful IllegalArgumentException (in checkPositionAndNumber) disadvantage: some erroneous arguments aren't noticed, example: width=100, height=20, x=100, y=0 will not result in an error (because only 0..99 are valid for x) but in the return of sample(0/1) */ try { return data[channel][y * width + x]; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(channel, x, y, 1, 1); return -1; } } public byte getByteSample(int x, int y) { return getByteSample(0, x, y); } public void getByteSamples(int channel, int x, int y, int w, int h, byte[] dest, int destOffset) { checkPositionAndNumber(channel, x, y, w, h); byte[] src = data[channel]; try { int srcOffset = y * width + x; while (h-- > 0) { java.lang.System.arraycopy(src, srcOffset, dest, destOffset, w); srcOffset += width; destOffset += w; } } catch (ArrayIndexOutOfBoundsException aioobe) { } } public final int getHeight() { return height; } public int getMaxSample(int channel) { return 255; } public int getNumChannels() { return numChannels; } public final int getSample(int x, int y) { try { return firstChannel[y * width + x] & 0xff; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(0, x, y, 1, 1); return -1; } } public final int getSample(int channel, int x, int y) { try { return data[channel][y * width + x] & 0xff; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(channel, x, y, 1, 1); return -1; } } public void getSamples(int channel, int x, int y, int w, int h, int[] dest, int destOffs) { if (w < 1 || h < 1) { return; } byte[] src = data[channel]; int srcOffs = y * width + x; while (h-- != 0) { int loop = w; int from = srcOffs; while (loop-- != 0) { dest[destOffs++] = src[from++] & 0xff; } srcOffs += width; } } public final int getWidth() { return width; } public final void putByteSample(int channel, int x, int y, byte newValue) { checkPositionAndNumber(channel, x, y, 1, 1); try { data[channel][y * width + x] = newValue; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(channel, x, y, 1, 1); } } public final void putByteSample(int x, int y, byte newValue) { checkPositionAndNumber(0, x, y, 1, 1); try { firstChannel[y * width + x] = newValue; } catch (ArrayIndexOutOfBoundsException aioobe) { checkPositionAndNumber(0, x, y, 1, 1); } } public void putByteSamples(int channel, int x, int y, int w, int h, byte[] src, int srcOffset) { checkPositionAndNumber(channel, x, y, w, h); byte[] dest = data[channel]; int destOffset = y * width + x; while (h-- > 0) { java.lang.System.arraycopy(src, srcOffset, dest, destOffset, w); srcOffset += w; destOffset += width; } } public void putSamples(int channel, int x, int y, int w, int h, int[] src, int srcOffs) { checkPositionAndNumber(channel, x, y, w, h); byte[] dest = data[channel]; int destOffs = y * width + x; while (h-- != 0) { int loop = w; int to = destOffs; while (loop-- != 0) { dest[to++] = (byte)src[srcOffs++]; } destOffs += width; } } public final void putSample(int x, int y, int newValue) { putByteSample(0, x, y, (byte)newValue); } public final void putSample(int channel, int x, int y, int newValue) { putByteSample(channel, x, y, (byte)newValue); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/BilevelImage.java0000664000000000000000000001062707741250133024137 0ustar /* * BilevelImage * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.GrayIntegerImage; /** * An interface for bilevel pixel image data classes. * Each pixel in a bilevel image can have two possible values, {@link #BLACK} and {@link #WHITE}. * Those two constants are guaranteed to be 0 and 1, although * you should not make any assumptions about what value any of the two constants has. * This is the type of image used for faxes. * Black and white photos are usually stored as grayscale images. *

* Apart from implementing {@link PixelImage} - like all image data classes in JIU - * this interface is also an {@link IntegerImage} (each sample is either 0 or 1) and * a {@link GrayImage} (black and white are both grayscale values). * The combination of {@link IntegerImage} and {@link GrayImage} is {@link GrayIntegerImage}, * which is the direct superinterface of this interface. * *

Packed bytes

* * There are methods to get and put packed bytes in this interface. * A packed byte is a byte value that stores eight horizontally neighbored bilevel * pixels in it (pixel and sample can be used interchangeably in the context of bilevel images * because there is only one sample per pixel). * The most significant bit of such a packed bit is defined to be the leftmost of the * eight pixels, the second-most significant bit is the pixel to the right of that leftmost pixel, * and so on. The least significant bit is the rightmost pixel. * If a bit is set, the corresponing pixel value is supposed to be white, otherwise black. * *

Usage examples

* * Here are some code examples that demonstrate how to access image data with this class. *
 * BilevelImage image = new MemoryBilevelImage(2000, 500);
 * // now set all pixels in the first row to white
 * for (int x = 0; x < image.getWidth(); x++)
 * {
 *   image.putWhite(x, 0);
 * }
 * // put vertical stripes on the rest
 * for (int y = 1; y < image.getHeight(); y++)
 * {
 *   for (int x = 0; x < image.getWidth(); x++)
 *   {
 *     int sample;
 *     if ((x % 2) == 0)
 *     {
 *       sample = BilevelImage.BLACK;
 *     }
 *     else
 *     {
 *       sample = BilevelImage.WHITE;
 *     }
 *     image.putSample(x, y, sample);
 *   }
 * }
 * 
* * @author Marco Schmidt */ public interface BilevelImage extends GrayIntegerImage { /** * The value for a black pixel. * To be used with all methods that require int arguments for sample values. * You can rely on this value being either 0 or 1 (that way you can safely store it * in a byte or short). */ int BLACK = 0; /** * The value for a white pixel. * To be used with all methods that require int arguments for sample values. * You can rely on this value being either 0 or 1 (that way you can safely store it * in a byte or short). */ int WHITE = 1; /** * Sets a number of samples in the argument array from this image. * @param x horizontal position of first sample of this image to read * @param y vertical position of samples to be read from this image * @param numSamples number of samples to be set * @param dest array with packed pixels to which samples are copied * @param destOffset index into dest array of the first byte value to write sample values to * @param destBitOffset index of first bit of dest[destOffset] to write a sample to (0 is leftmost, 1 is second-leftmost up to 7, which is the rightmost) */ void getPackedBytes(int x, int y, int numSamples, byte[] dest, int destOffset, int destBitOffset); /** * Sets a number of samples in the image from the argument array data. * @param x horizontal position of first sample to be set * @param y vertical position of samples to be set * @param numSamples number of samples to be set * @param src array with packed pixels to be set * @param srcOffset index into src array of the first byte value to read sample values from * @param srcBitOffset index of first bit of src[srcOffset] to * read a sample from (0 is leftmost, 1 is second-leftmost up to 7, which is the rightmost) */ void putPackedBytes(int x, int y, int numSamples, byte[] src, int srcOffset, int srcBitOffset); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/Palette.java0000664000000000000000000001507507741250133023212 0ustar /* * Palette * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.RGBIndex; /** * This class represents a palette, a list of RGB colors. * An RGB color here has three int values for its red, green and blue * intensity. * Each intensity value must be larger than or equal to zero and * smaller than or equal to the maximum intensity value that can be * given to the constructor {@link #Palette(int, int)}. * This maximum value is typically 255. * Note that the number of entries in a palette is restricted only * by the element index type int so that palettes with * more than 256 entries are no problem. * When accessing (reading or writing) samples of this palette, use * the constants {@link #INDEX_RED}, {@link #INDEX_GREEN} and {@link #INDEX_BLUE} of * this class to define a color channel. * @author Marco Schmidt * @see net.sourceforge.jiu.data.PalettedImage */ public class Palette implements RGBIndex { private int[][] data; private int numEntries; private int maxValue; /** * Create a palette with the given number of entries and a maximum value * for each sample. * @param numEntries the number of entries to be accessible in this palette * @param maxValue the maximum value to be allowed for each sample */ public Palette(int numEntries, int maxValue) { if (numEntries < 1) { throw new IllegalArgumentException("Error -- numEntries must be larger than 0."); } this.numEntries = numEntries; this.maxValue = maxValue; data = new int[3][]; for (int i = 0; i < 3; i++) { data[i] = new int[numEntries]; } } /** * Create a palette with the given number of entries and a maximum value * of 255. * @param numEntries the number of entries to be accessible in this palette */ public Palette(int numEntries) { this(numEntries, 255); } /** * Creates a copy of this palette, allocating a new Palette object * and copying each RGB triplet to the new palette. * Then returns the new palette. * Thus, a "deep" copy of this Palette object is created, * not a "shallow" one. * * @return newly-created palette */ public Object clone() { Palette result = new Palette(getNumEntries(), getMaxValue()); for (int i = 0; i < getNumEntries(); i++) { result.putSample(INDEX_RED, i, getSample(INDEX_RED, i)); result.putSample(INDEX_GREEN, i, getSample(INDEX_GREEN, i)); result.putSample(INDEX_BLUE, i, getSample(INDEX_BLUE, i)); } return result; } /** * Returns the amount of memory in bytes allocated for this palette. * */ public long getAllocatedMemory() { long result = 0; if (data != null) { for (int i = 0; i < data.length; i++) { if (data[i] != null) { result += data[i].length; } } } return result; } /** * Returns the maximum value allowed for a sample. * @return the maximum sample value */ public int getMaxValue() { return maxValue; } /** * Returns the number of entries in this palette. * @return the number of entries in this palette */ public int getNumEntries() { return numEntries; } /** * Returns one of the samples of this palette. * @param entryIndex the index of the color to be addressed, must be from * 0 to getNumEntries() - 1 * @param channelIndex one of the three channels; must be {@link #INDEX_RED}, * {@link #INDEX_GREEN} or {@link #INDEX_BLUE} * @return the requested sample */ public int getSample(int channelIndex, int entryIndex) { try { return data[channelIndex][entryIndex]; } catch (ArrayIndexOutOfBoundsException aioobe) { throw new IllegalArgumentException("Entry must be from 0 to " + (numEntries - 1) + ", channel from 0 to 2."); } } /** * Returns all samples of one channel as an int array. * @param channelIndex index of the channel, one of the {@link RGBIndex} constants * @return array with samples */ public int[] getSamples(int channelIndex) { if (channelIndex < 0 || channelIndex > 2) { throw new IllegalArgumentException("Invalid channel index, must be from 0 to 2."); } int[] result = new int[data[channelIndex].length]; System.arraycopy(data[channelIndex], 0, result, 0, result.length); return result; } /** * Checks if all entries of this palette are either black or white. * An entry is black if all three intensitites (red, green and blue) are * 0, it is white if they are all equal to * {@link #getMaxValue()}. * No particular order of entries (e.g. first color black, second white) * is demanded and no specific number of entries (e.g. 2). * This means that a palette is black and white if it contains ten entries * that are all black. * * @return if the palette contains only the colors black and white */ public boolean isBlackAndWhite() { int i = 0; while (i < numEntries) { if (data[INDEX_RED][i] != data[INDEX_GREEN][i] || data[INDEX_GREEN][i] != data[INDEX_BLUE][i] || (data[INDEX_BLUE][i] != 0 && data[INDEX_BLUE][i] != maxValue)) { return false; } i++; } return true; } /** * Checks if this palette is gray, i.e., checks if all entries are * gray. This is the case if for all entries red, green and blue * have the same intensity. * @return if the palette contains only shades of gray */ public boolean isGray() { int i = 0; while (i < numEntries) { if (data[INDEX_RED][i] != data[INDEX_GREEN][i] || data[INDEX_GREEN][i] != data[INDEX_BLUE][i]) { return false; } i++; } return true; } public void put(int entryIndex, int red, int green, int blue) { putSample(INDEX_RED, entryIndex, red); putSample(INDEX_GREEN, entryIndex, green); putSample(INDEX_BLUE, entryIndex, blue); } /** * Sets one sample of one color entry in the palette to a new value. * @param channelIndex */ public void putSample(int channelIndex, int entryIndex, int newValue) { if (newValue < 0 || newValue > maxValue) { throw new IllegalArgumentException("Value must be from 0 to " + maxValue + "; argument is " + newValue + "."); } try { data[channelIndex][entryIndex] = newValue; } catch (ArrayIndexOutOfBoundsException aioobe) { throw new IllegalArgumentException("Entry must be from 0 to " + (numEntries - 1) + ", channel from 0 to 2."); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/Paletted8Image.java0000664000000000000000000000111507741250133024377 0ustar /* * Paletted8Image * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.ByteChannelImage; import net.sourceforge.jiu.data.PalettedIntegerImage; /** * An interface for classes that store paletted images with * eight bit integers for each pixel. * @author Marco Schmidt * @see net.sourceforge.jiu.data.ByteChannelImage * @see net.sourceforge.jiu.data.PalettedIntegerImage */ public interface Paletted8Image extends ByteChannelImage, PalettedIntegerImage { } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/RGBIndex.java0000664000000000000000000000141407741250133023206 0ustar /* * RGBIndex * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; /** * This interface provides three int constants as index * values for the three channels of an RGB image: red, green and blue. * The three values are guaranteed to lie in the interval 0 to 2. * Furthermore, all three values are different from each other, so * that the complete interval from 0 to 2 is used. * @author Marco Schmidt */ public interface RGBIndex { /** * The index value for the red channel. */ int INDEX_RED = 0; /** * The index value for the green channel. */ int INDEX_GREEN = 1; /** * The index value for the blue channel. */ int INDEX_BLUE = 2; } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/GrayIntegerImage.java0000664000000000000000000000115607741250133024772 0ustar /* * GrayIntegerImage * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; /** * An empty interface for grayscale images which have integer values * of up to 32 bits (int or smaller) as samples. * An interface composed of {@link GrayImage} and {@link IntegerImage}. *

* Like all extensions of {@link GrayImage}, this image data class supports * only one channel. * @author Marco Schmidt * @since 0.9.0 * @see GrayImage * @see IntegerImage */ public interface GrayIntegerImage extends GrayImage, IntegerImage { } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/PalettedIntegerImage.java0000664000000000000000000000071607741250133025633 0ustar /* * PalettedIntegerImage * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.PalettedImage; /** * An empty interface for a paletted image type that uses * integer values as samples. * @author Marco Schmidt * @since 0.11.0 */ public interface PalettedIntegerImage extends PalettedImage, IntegerImage { } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/ByteChannelImage.java0000664000000000000000000001165207741250133024750 0ustar /* * ByteChannelImage * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; /** * An extension of the {@link IntegerImage} interface that restricts the image to * byte samples. * The minimum sample value for all channels is 0, * the maximum sample value 255. *

* Number of channels and resolution must be given to the constructor * and cannot be changed after creation. *

* Each channel of the image is made up of byte values. * Note that bytes in Java are signed, they can take values from -128 to 127. * If you use {@link IntegerImage}'s getSample and putSample methods * you don't have to deal with this, you always get int samples * that are in the 0 .. 255 interval. *

* To manually convert a Java byte value to an int value * in the range of 0 to 255, do the following: *

 * byte b = ...; // initialize byte value
 * int i = b & 0xff; 
 * // i now is a value between 0 and 255
 * 
* @author Marco Schmidt */ public interface ByteChannelImage extends IntegerImage { /** * Sets all samples of the first channel to the argument byte value. * Equal to clear(0, newValue);. * @param newValue all samples in the first channel are set to this value * @see #clear(int, byte) * @see #clear(int) * @see #clear(int, int) */ void clear(byte newValue); /** * Sets all samples of one channel to a new value. * @param channelIndex zero-based index of the channel to be cleared (must be smaller than {@link #getNumChannels()} * @param newValue all samples in the channel will be set to this value */ void clear(int channelIndex, byte newValue); /** * Returns a single byte sample from the first channel and the specified position. * A call to this method is the same as getByteSample(0, x, y). * @param x horizontal position of the sample to be returned (must be between 0 and {@link #getWidth()} - 1 * @param y vertical position of the sample to be returned (must be between 0 and {@link #getHeight()} - 1 * @return the requested byte sample */ byte getByteSample(int x, int y); /** * Returns a single byte sample from the image. * When possible, try copying several samples at a time for * higher speed ({@link #getByteSamples}). * @param channel the number of the channel of the sample; must be from 0 to {@link #getNumChannels()} - 1 * @param x the column of the sample to be returned; must be from 0 to {@link #getWidth()} - 1 * @param y the row of the sample; must be from 0 to {@link #getHeight()} - 1 * @return the sample, a single byte value * @throws IllegalArgumentException if the arguments hurt one of the preconditions above * @see #getByteSamples */ byte getByteSample(int channel, int x, int y); /** * Copies samples from this image to a byte array. * Copies num samples in row y of channel * channel, starting at horizontal offset x. * Data will be written to the dest array, starting at * offset destOffset. * Data will be copied from one row only, so a maximum of * getWidth() * samples can be copied with a call to this method. * * @param channelIndex the index of the channel to be copied from; must be * from 0 to getNumChannels() - 1 * @param x the horizontal offset where copying will start; must be from * 0 to getWidth() - 1 * @param y the row from which will be copied; must be from * 0 to getHeight() - 1 * @param w the number of columns to be copied * @param h the number of rows to be copied * @param dest the array where the data will be copied to; must have a * length of at least destOffset + num * @param destOffset the offset into dest where this method * will start copying data * @throws IllegalArgumentException if the arguments hurt one of the many * preconditions above */ void getByteSamples(int channelIndex, int x, int y, int w, int h, byte[] dest, int destOffset); /** * Sets one byte sample in one channel to a new value. */ void putByteSample(int channel, int x, int y, byte newValue); /** * Sets one byte sample in the first channel (index 0) to a new value. * Result is equal to putByteSample(0, x, y, newValue);. */ void putByteSample(int x, int y, byte newValue); /** * Copies a number of samples from the argument array to this image. */ void putByteSamples(int channel, int x, int y, int w, int h, byte[] src, int srcOffset); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/MemoryGray8Image.java0000664000000000000000000000313607741250133024735 0ustar /* * MemoryGray8Image * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryByteChannelImage; import net.sourceforge.jiu.data.PixelImage; /** * An implementation of {@link Gray8Image} that keeps the complete image in memory. * This class inherits most of its functionality from its parent class * {@link MemoryByteChannelImage}, using one byte channel. * * @author Marco Schmidt */ public class MemoryGray8Image extends MemoryByteChannelImage implements Gray8Image { /** * Creates a new MemoryGray8Image object with the specified resolution. * Simply gives 1 (for one channel) and the two resolution arguments * to the super constructor (of the parent class {@link MemoryByteChannelImage}). * @param width the horizontal resolution, must be non-zero and positive * @param height the vertical resolution, must be non-zero and positive */ public MemoryGray8Image(int width, int height) { super(1, width, height); } public PixelImage createCompatibleImage(int width, int height) { return new MemoryGray8Image(width, height); } public Class getImageType() { return Gray8Image.class; } public boolean isBlack(int x, int y) { return getByteSample(x, y) == 0; } public boolean isWhite(int x, int y) { return getByteSample(x, y) == (byte)255; } public void putBlack(int x, int y) { putSample(x, y, 0); } public void putWhite(int x, int y) { putSample(x, y, 255); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/package.html0000664000000000000000000000316207741250133023224 0ustar

Provides classes to store images and data directly related to them.

Package Specification

The base interface for image data in JIU is {@link net.sourceforge.jiu.data.PixelImage}. The concept of a pixel image includes the following properties:

The interface {@link net.sourceforge.jiu.data.IntegerImage} extends the {@link net.sourceforge.jiu.data.PixelImage} interface. All sample values belonging to an object of a class implementing IntegerImage are supposed to be integer values that can be stored in an int value (a signed 32 bit value).

java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/PalettedImage.java0000664000000000000000000000151007741250133024306 0ustar /* * PalettedImage * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.Palette; /** * This interface defines methods for paletted images. * The image data of paletted images are usually integer numbers. * Those numbers are index values into a list of colors called * the palette or color map. * This way, for images with few colors relatively small integers * can be used as samples. */ public interface PalettedImage { /** * Gets the palette of this image. * @return palette object */ Palette getPalette(); /** * Sets the palette of this image to the argument palette object. * @param palette the new palette for this image */ void setPalette(Palette palette); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/GrayImage.java0000664000000000000000000000305710324333515023453 0ustar /* * GrayImage * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.data; /** * An interface for grayscale images. * Grayscale images have only one channel. * Each sample is a shade of gray, an intensity value between black (zero) and white (maximum value). * Black and white photos are really grayscale photos. * For images that only use black and white, see {@link BilevelImage}. * * @author Marco Schmidt * @since 0.8.0 */ public interface GrayImage { /** * Returns if the pixel specified by the location in the arguments is black. * @param x the horizontal location of the pixel * @param y the vertical location of the pixel * @throws IllegalArgumentException if any of the parameters are invalid */ boolean isBlack(int x, int y); /** * Returns if the pixel specified by the location in the arguments is white. * @param x the horizontal location of the pixel * @param y the vertical location of the pixel * @throws IllegalArgumentException if any of the parameters are invalid */ boolean isWhite(int x, int y); /** * Sets a pixel to black (minimum intensity value). * @param x horizontal position of the pixel's location * @param y vertical position of the pixel's location */ void putBlack(int x, int y); /** * Sets a pixel to white (maximum intensity value). * @param x horizontal position of the pixel's location * @param y vertical position of the pixel's location */ void putWhite(int x, int y); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/IntegerImage.java0000664000000000000000000001300610324333535024143 0ustar /* * IntegerImage * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.data; /** * Extends the {@link PixelImage} interface to access image data as int values. * Image types based on byte, char, short and int will work with this interface. * long will not. *

* Using this interface provides a nice way of accessing a large variety of * image types, but for performance reasons it might be preferable to use * one of the class-specific access methods that get or put several values * at a time, e.g. getByteSamples in {@link ByteChannelImage}. * * @author Marco Schmidt */ public interface IntegerImage extends PixelImage { /** * Sets all samples in the first channel to the argument value. * Equal to clear(0, newValue);: */ void clear(int newValue); /** * Sets all samples of the channelIndex'th channel to newValue. */ void clear(int channelIndex, int newValue); /** * Returns the maximum value for one of the image's channels. * The minimum value is always 0. * @param channel zero-based index of the channel, from 0 to {@link #getNumChannels()} - 1 * @return maximum allowed sample value */ int getMaxSample(int channel); /** * Returns one sample of the first channel (index 0). * A call to this method must have the same result as the call getSample(0, x, y);. * @param x the horizontal position of the sample, from 0 to {@link #getWidth} - 1 * @param y the vertical position of the sample, from 0 to {@link #getHeight} - 1 * @return the desired sample */ int getSample(int x, int y); /** * Returns one sample, specified by its channel index and location. * @param channel the number of the channel, from 0 to {@link #getNumChannels} - 1 * @param x the horizontal position of the sample, from 0 to {@link #getWidth} - 1 * @param y the vertical position of the sample, from 0 to {@link #getHeight} - 1 * @return the desired sample */ int getSample(int channel, int x, int y); /** * Copies a number of samples from this image to an int[] object. * A rectangular part of one channel is copied. * The channel index is given by - the upper left corner of * that rectangle is given by the point x / y. * Width and height of that rectangle are given by w and h. * Each sample will be stored as one int value dest, * starting at index destOffs. * @param channelIndex zero-based index of the channel from which data is to be copied (valid values: 0 to {@link #getNumChannels()} - 1) * @param x horizontal position of upper left corner of the rectangle to be copied * @param y vertical position of upper left corner of the rectangle to be copied * @param w width of rectangle to be copied * @param h height of rectangle to be copied * @param dest int array to which the samples will be copied * @param destOffs int index into the dest array for the position to which the samples will be copied */ void getSamples(int channelIndex, int x, int y, int w, int h, int[] dest, int destOffs); /** * This method sets one sample of the first channel (index 0) to a new value. * This call must have the same result as the call putSample(0, x, y). * The sample location is given by the spatial coordinates, x and y. * @param x the horizontal position of the sample, from 0 to {@link #getWidth} - 1 * @param y the vertical position of the sample, from 0 to {@link #getHeight} - 1 * @param newValue the new value of the sample */ void putSample(int x, int y, int newValue); /** * This method sets one sample to a new value. * The sample location is given by the channel index and the spatial coordinates, x and y. * @param channel the number of the channel, from 0 to {@link #getNumChannels} - 1 * @param x the horizontal position of the sample, from 0 to {@link #getWidth} - 1 * @param y the vertical position of the sample, from 0 to {@link #getHeight} - 1 * @param newValue the new value of the sample */ void putSample(int channel, int x, int y, int newValue); /** * Copies a number of samples from an int[] array to this image. * A rectangular part of one channel is copied - the upper left corner of * that rectangle is given by the point x / y. * Width and height of that rectangle are given by w and h. * Each sample will be stored as one int value src, * starting at index srcOffset. * @param channel int (from 0 to getNumChannels() - 1) to indicate the channel to which data is copied * @param x horizontal position of upper left corner of the rectangle to be copied * @param y vertical position of upper left corner of the rectangle to be copied * @param w width of rectangle to be copied * @param h height of rectangle to be copied * @param src int array from which the samples will be copied * @param srcOffset int index into the src array for the position from which the samples will be copied */ void putSamples(int channel, int x, int y, int w, int h, int[] src, int srcOffset); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/MemoryRGB24Image.java0000664000000000000000000000172307741250133024523 0ustar /* * MemoryRGB24Image * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.MemoryByteChannelImage; import net.sourceforge.jiu.data.RGB24Image; /** * A class to store 24 bit RGB truecolor images in memory. * @author Marco Schmidt * @see RGB24Image */ public class MemoryRGB24Image extends MemoryByteChannelImage implements RGB24Image { /** * Creates a new object of this class, with width and height as * specified by the arguments. * @param width the horizontal resolution of the new image in pixels * @param height the vertical resolution of the new image in pixels */ public MemoryRGB24Image(int width, int height) { super(3, width, height); } public PixelImage createCompatibleImage(int width, int height) { return new MemoryRGB24Image(width, height); } public Class getImageType() { return RGB24Image.class; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/data/MemoryGray16Image.java0000664000000000000000000000320407741250133025010 0ustar /* * MemoryGray16Image * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.data; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.MemoryShortChannelImage; import net.sourceforge.jiu.data.PixelImage; /** * An implementation of {@link Gray16Image} that keeps the complete image in memory. * This class inherits most of its functionality from its parent class * {@link MemoryShortChannelImage}, using one short channel. * @since 0.11.0 * @author Marco Schmidt */ public class MemoryGray16Image extends MemoryShortChannelImage implements Gray16Image { /** * Creates a new MemoryGray16Image object with the specified resolution. * Simply gives 1 (for one channel) and the two resolution arguments * to the super constructor (of the parent class {@link MemoryShortChannelImage}). * @param width the horizontal resolution, must be larger than zero * @param height the vertical resolution, must be larger than zero */ public MemoryGray16Image(int width, int height) { super(1, width, height); } public PixelImage createCompatibleImage(int width, int height) { return new MemoryGray16Image(width, height); } public Class getImageType() { return Gray16Image.class; } public boolean isBlack(int x, int y) { return getShortSample(x, y) == 0; } public boolean isWhite(int x, int y) { return getShortSample(x, y) == (short)65535; } public void putBlack(int x, int y) { putSample(x, y, 0); } public void putWhite(int x, int y) { putSample(x, y, 65535); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/0000775000000000000000000000000010607465453021673 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/TriangleFilter.java0000664000000000000000000000120107741250134025434 0ustar /* * TriangleFilter * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; /** * A triangle filter (also known as linear or bilinear filter). * @author Marco Schmidt * @since 0.10.0 */ public class TriangleFilter extends ResampleFilter { public float apply(float value) { if (value < 0.0f) { value = -value; } if (value < 1.0f) { return 1.0f - value; } else { return 0.0f; } } public String getName() { return "Triangle (bilinear)"; } public float getRecommendedSamplingRadius() { return 1.0f; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Resample.java.old0000664000000000000000000005225210332232063025051 0ustar /* * Resample * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /* This is the beginning of resample.pas, the Unit on which this class is based: // ----------------------------------------------------------------------------- // Project: bitmap resampler // Module: resample // Description: Interpolated Bitmap Resampling using filters. // Version: 01.03 // Release: 4 // Date: 29-JUN-1999 // Target: Win32, Delphi 2, 3 & 4 // Author(s): anme: Anders Melander, anders@melander.dk // Copyright (c) 1997-99 by Anders Melander // Formatting: 2 space indent, 8 space tabs, 80 columns. // ----------------------------------------------------------------------------- // This software is copyrighted as noted above. It may be freely copied, // modified, and redistributed, provided that the copyright notice(s) is // preserved on all copies. // // There is no warranty or other guarantee of fitness for this software, // it is provided solely "as is". Bug reports or fixes may be sent // to the author, who may or may not act on them as he desires. // // You may not include this software in a program or other software product // without supplying the source, or without informing the end-user that the // source is available for no extra charge. // // If you modify this software, you should include a notice in the "Revision // history" section giving the name of the person performing the modification, // the date of modification, and the reason for such modification. // ----------------------------------------------------------------------------- // Here's some additional copyrights for you: // // From filter.c: // The authors and the publisher hold no copyright restrictions // on any of these files; this source code is public domain, and // is freely available to the entire computer graphics community // for study, use, and modification. We do request that the // comment at the top of each file, identifying the original // author and its original publication in the book Graphics // Gems, be retained in all programs that use these files. // // ----------------------------------------------------------------------------- // Revision history: // // 0100 110997 anme - Adapted from Dale Schumacher's fzoom v0.20. // // 0101 110198 anme - Added Lanczos3 and Mitchell filters. // - Fixed range bug. // Min value was not checked on conversion from Single to // byte. // - Numerous optimizations. // - Added TImage stretch on form resize. // - Added support for Delphi 2 via TCanvas.Pixels. // - Renamed module from stretch to resample. // - Moved demo code to separate module. // // 0102 150398 anme - Fixed a problem that caused all pixels to be shifted // 1/2 pixel down and to the right (in source // coordinates). Thanks to David Ullrich for the // solution. // // 0103 170898 anme - Fixed typo: Renamed Strecth function to Stretch. // Thanks to Graham Stratford for spotting it. // Sorry about that. // 081298 anme - Added check for too small destination bitmap. // Thanks to Jeppe Oland for bringing this problem to my // attention. // 260399 anme - Fixed a problem with resampling of very narrow // bitmaps. Thanks to Holger Dors for bringing the // problem to my attention. // - Removed dependency of math unit. // 290699 jobe - Subsampling improvements by Josha Beukema. // // ----------------------------------------------------------------------------- // Credits: // The algorithms and methods used in this library are based on the article // "General Filtered Image Rescaling" by Dale Schumacher which appeared in the // book Graphics Gems III, published by Academic Press, Inc. // // The edge offset problem was fixed by: // * David Ullrich // // The subsampling problem was fixed by: // * Josha Beukema // ----------------------------------------------------------------------------- // To do (in rough order of priority): // * Implement Dale Schumacher's "Optimized Bitmap Scaling Routines". // * Optimize to use integer math instead of floating point where possible. // ----------------------------------------------------------------------------- */ /** * Resizes grayscale and truecolor images using filters. * For other image types (including paletted or bilevel images), you might * want to use the {@link ScaleReplication} class or convert the images to * grayscale (or RGB truecolor) first and then use this class. * Several algorithms for resampling are implemented, they differ in resulting image quality * and computational complexity. * *

Usage example

* This will scale image to 150 percent of its original size * in both directions, using the Lanczos3 filter type: *
 * Resample resample = new Resample();
 * resample.setInputImage(image);
 * resample.setSize(image.getWidth() * 3 / 2, image.getHeight() * 3 / 2);
 * resample.setFilter(Resample.FILTER_TYPE_LANCZOS3);
 * resample.process();
 * PixelImage scaledImage = resample.getOutputImage();
 * 
* *

Known problems

* * *

Origin

* This code is a port of Anders Melander's * Object Pascal (Delphi) unit resample.pas to Java. * The Delphi code is an adaptation (with some improvements) of * Dale Schumacher's fzoom C code. * Check out the homepage for the Delphi resample code, a demo application * to compare the different filtering algorithms is also provided: * http://www.melander.dk/delphi/resampler/index.html. * You will also find the original C code there. * The site seems to have gone for good. * *

Theory

* The theoretical background for all implementations is Dale Schumacher's article * General Filtered Image Rescaling * in Graphics Gems III, editor David Kirk, Academic Press, pages 8-16, 1994. *

* The Graphics Gems Repository can be found at * http://www.acm.org/tog/GraphicsGems/. * It also includes information on the books and how to order them. * * @author Marco Schmidt */ public class Resample extends ImageToImageOperation { /** * Constant for the Box filter (also known as Nearest Neighbor filter). */ public static final int FILTER_TYPE_BOX = 0; /** * Constant for the Triangle filter (also known as Linear filter or Bilinear filter). */ public static final int FILTER_TYPE_TRIANGLE = 1; /** * Constant for the Hermite filter. */ public static final int FILTER_TYPE_HERMITE = 2; /** * Constant for the Bell filter. */ public static final int FILTER_TYPE_BELL = 3; /** * Constant for the B-Spline filter. */ public static final int FILTER_TYPE_B_SPLINE = 4; /** * Constant for the Lanczos3 filter. */ public static final int FILTER_TYPE_LANCZOS3 = 5; /** * Constant for the Mitchell filter. */ public static final int FILTER_TYPE_MITCHELL = 6; class Contributor { int pixel; // Source pixel float weight; // Pixel weight } class CList { int n; Contributor[] p; } private Integer outWidth; private Integer outHeight; private ResampleFilter filter; private static ResampleFilter createFilter(int filterType) { switch(filterType) { case(FILTER_TYPE_BOX): return new BoxFilter(); case(FILTER_TYPE_TRIANGLE): return new TriangleFilter(); case(FILTER_TYPE_HERMITE): return new HermiteFilter(); case(FILTER_TYPE_BELL): return new BellFilter(); case(FILTER_TYPE_B_SPLINE): return new BSplineFilter(); case(FILTER_TYPE_LANCZOS3): return new Lanczos3Filter(); case(FILTER_TYPE_MITCHELL): return new MitchellFilter(); default: { throw new IllegalArgumentException("Unknown filter type in Resample: " + filterType); } } } /** * Returns the filter to be used in this operation. * @return ResampleFilter object or null if none was defined yet */ public ResampleFilter getFilter() { return filter; } /** * Returns the names of all predefined filters. * Each FILTER_TYPE_xyz constant can be used as an index into the array that is returned. * Names are retrieved by creating an object of each predefined filter class and calling its * getName method. * @return String array with filter names */ public static String[] getFilterNames() { String[] result = new String[getNumFilters()]; for (int i = 0; i < getNumFilters(); i++) { ResampleFilter filter = createFilter(i); result[i] = filter.getName(); } return result; } /** * Returns the number of predefined filters. * @return number of filters */ public static int getNumFilters() { return 7; } /** * This method does the actual work of rescaling an image. */ private void process(IntegerImage in, IntegerImage out) { if (out == null) { out = (IntegerImage)in.createCompatibleImage(outWidth.intValue(), outHeight.intValue()); setOutputImage(out); } if (filter == null) { filter = new TriangleFilter(); } float fwidth = filter.getSamplingRadius(); final int dstWidth = outWidth.intValue(); final int dstHeight = outHeight.intValue(); final int srcWidth = in.getWidth(); final int srcHeight = in.getHeight(); /* if (SrcWidth < 1) or (SrcHeight < 1) then raise Exception.Create('Source bitmap too small');*/ // Create intermediate image to hold horizontal zoom IntegerImage work = (IntegerImage)in.createCompatibleImage(dstWidth, srcHeight); float xscale; float yscale; if (srcWidth == 1) { xscale = (float)dstWidth / (float)srcWidth; } else { xscale = (float)(dstWidth - 1) / (float)(srcWidth - 1); } if (srcHeight == 1) { yscale = (float)dstHeight / (float)srcHeight; } else { yscale = (float)(dstHeight - 1) / (float)(srcHeight - 1); } /* Marco: the following two variables are used for progress notification */ int processedItems = 0; int totalItems = /*dstWidth +*/ srcHeight + /*dstHeight +*/ dstWidth; // -------------------------------------------- // Pre-calculate filter contributions for a row // ----------------------------------------------- CList[] contrib = new CList[dstWidth]; for (int i = 0; i < contrib.length; i++) { contrib[i] = new CList(); } // Horizontal sub-sampling // Scales from bigger to smaller width if (xscale < 1.0f) { float width = fwidth / xscale; float fscale = 1.0f / xscale; int numPixels = (int)(width * 2.0f + 1); for (int i = 0; i < dstWidth; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[numPixels]; for (int j = 0; j < contrib[i].p.length; j++) { contrib[i].p[j] = new Contributor(); } float center = i / xscale; int left = (int)Math.floor(center - width); int right = (int)Math.ceil(center + width); for (int j = left; j <= right; j++) { float weight = filter.apply((center - j) / fscale) / fscale; if (weight == 0.0f) { continue; } int n; if (j < 0) { n = -j; } else if (j >= srcWidth) { n = srcWidth - j + srcWidth - 1; } else { n = j; } int k = contrib[i].n; contrib[i].n = contrib[i].n + 1; contrib[i].p[k].pixel = n; contrib[i].p[k].weight = weight; } //setProgress(processedItems++, totalItems); } } else // Horizontal super-sampling // Scales from smaller to bigger width { int numPixels = (int)(fwidth * 2.0f + 1); for (int i = 0; i < dstWidth; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[numPixels]; for (int j = 0; j < contrib[i].p.length; j++) { contrib[i].p[j] = new Contributor(); } float center = i / xscale; int left = (int)Math.floor(center - fwidth); int right = (int)Math.ceil(center + fwidth); for (int j = left; j <= right; j++) { float weight = filter.apply(center - j); if (weight == 0.0f) { continue; } int n; if (j < 0) { n = -j; } else if (j >= srcWidth) { n = srcWidth - j + srcWidth - 1; } else { n = j; } int k = contrib[i].n; if (n < 0 || n >= srcWidth) { weight = 0.0f; } contrib[i].n = contrib[i].n + 1; contrib[i].p[k].pixel = n; contrib[i].p[k].weight = weight; } //setProgress(processedItems++, totalItems); } } // ---------------------------------------------------- // Apply filter to sample horizontally from Src to Work // ---------------------------------------------------- // start of Java-specific code // Marco: adjusted code to work with multi-channel images // where each channel can have a different maximum sample value (not only 255) final int NUM_CHANNELS = work.getNumChannels(); final int[] MAX = new int[NUM_CHANNELS]; for (int k = 0; k < NUM_CHANNELS; k++) { MAX[k] = work.getMaxSample(k); } // end of Java-specific code for (int k = 0; k < srcHeight; k++) { for (int i = 0; i < dstWidth; i++) { for (int channel = 0; channel < NUM_CHANNELS; channel++) { float sample = 0.0f; for (int j = 0; j < contrib[i].n; j++) { float weight = contrib[i].p[j].weight; if (weight == 0.0f) { continue; } int color = in.getSample(channel, contrib[i].p[j].pixel, k); sample = sample + color * weight; } // Marco: procedure BoundRound included directly int result = (int)sample; if (result < 0) { result = 0; } else if (result > MAX[channel]) { result = MAX[channel]; } work.putSample(channel, i, k, result); } } setProgress(processedItems++, totalItems); } /* Marco: no need for "free memory" code as Java has garbage collection: // Free the memory allocated for horizontal filter weights for i := 0 to DstWidth-1 do FreeMem(contrib^[i].p); FreeMem(contrib); */ // ----------------------------------------------- // Pre-calculate filter contributions for a column // ----------------------------------------------- /*GetMem(contrib, DstHeight* sizeof(TCList));*/ contrib = new CList[dstHeight]; for (int i = 0; i < contrib.length; i++) { contrib[i] = new CList(); } // Vertical sub-sampling // Scales from bigger to smaller height if (yscale < 1.0f) { float width = fwidth / yscale; float fscale = 1.0f / yscale; int numContributors = (int)(width * 2.0f + 1); for (int i = 0; i < dstHeight; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[numContributors]; for (int j = 0; j < contrib[i].p.length; j++) { contrib[i].p[j] = new Contributor(); } float center = i / yscale; int left = (int)Math.floor(center - width); int right = (int)Math.ceil(center + width); for (int j = left; j <= right; j++) { float weight = filter.apply((center - j) / fscale) / fscale; // change suggested by Mike Dillon; not thoroughly tested; // old version: // float weight = filter.apply(center - j); if (weight == 0.0f) { continue; } int n; if (j < 0) { n = -j; } else if (j >= srcHeight) { n = srcHeight - j + srcHeight - 1; } else { n = j; } int k = contrib[i].n; contrib[i].n = contrib[i].n + 1; if (n < 0 || n >= srcHeight) { weight = 0.0f;// Flag that cell should not be used } contrib[i].p[k].pixel = n; contrib[i].p[k].weight = weight; } //setProgress(processedItems++, totalItems); } } else // Vertical super-sampling // Scales from smaller to bigger height { int numContributors = (int)(fwidth * 2.0f + 1); for (int i = 0; i < dstHeight; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[numContributors]; for (int j = 0; j < contrib[i].p.length; j++) { contrib[i].p[j] = new Contributor(); } float center = i / yscale; int left = (int)Math.floor(center - fwidth); int right = (int)Math.ceil(center + fwidth); for (int j = left; j <= right; j++) { float weight = filter.apply(center - j); if (weight == 0.0f) { continue; } int n; if (j < 0) { n = -j; } else if (j >= srcHeight) { n = srcHeight - j + srcHeight - 1; } else { n = j; } int k = contrib[i].n; contrib[i].n = contrib[i].n + 1; if (n < 0 || n >= srcHeight) { weight = 0.0f;// Flag that cell should not be used } contrib[i].p[k].pixel = n; contrib[i].p[k].weight = weight; } //setProgress(processedItems++, totalItems); } } // -------------------------------------------------- // Apply filter to sample vertically from Work to Dst // -------------------------------------------------- for (int k = 0; k < dstWidth; k++) { for (int i = 0; i < dstHeight; i++) { for (int channel = 0; channel < NUM_CHANNELS; channel++) { float sample = 0.0f; for (int j = 0; j < contrib[i].n; j++) { float weight = contrib[i].p[j].weight; if (weight == 0.0f) { continue; } float color = work.getSample(channel, k, contrib[i].p[j].pixel); sample = sample + color * weight; int result = (int)sample; if (result < 0) { result = 0; } else if (result > MAX[channel]) { result = MAX[channel]; } out.putSample(channel, k, i, result); } } } setProgress(processedItems++, totalItems); } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); if (outWidth == null && outHeight == null && getOutputImage() != null) { PixelImage out = getOutputImage(); outWidth = new Integer(out.getWidth()); outHeight = new Integer(out.getHeight()); } if (outWidth == null) { throw new MissingParameterException("Output width has not been initialized"); } if (outHeight == null) { throw new MissingParameterException("Output height has not been initialized"); } PixelImage image = getInputImage(); if (image.getWidth() == outWidth.intValue() && image.getHeight() == outHeight.intValue()) { throw new WrongParameterException("Input image already has the size specified by setSize."); } ensureOutputImageResolution(outWidth.intValue(), outHeight.intValue()); if (image instanceof IntegerImage) { process((IntegerImage)image, (IntegerImage)getOutputImage()); } else { throw new WrongParameterException("Input image must implement IntegerImage."); } } /** * Set the pixel resolution of the output image. * @param width the horizontal resolution of the output image * @param height the vertical resolution of the output image */ public void setSize(int width, int height) { outWidth = new Integer(width); outHeight = new Integer(height); } /** * Set a new filter object to be used with this operation. * @param newFilter a resample filter to be used for scaling */ public void setFilter(ResampleFilter newFilter) { filter = newFilter; } /** * Sets a new filter type, using the default sampling radius of that filter. * @param filterType the new filter type, one of the FILTER_TYPE_xyz constants of this class */ public void setFilter(int filterType) { setFilter(createFilter(filterType)); } /** * Sets a new filter type with a user-defined sampling radius. * @param filterType the new filter type, one of the FILTER_TYPE_xyz constants of this class * @param samplingRadius the sampling radius to be used with that filter, must be larger than 0.0f */ public void setFilter(int filterType, float samplingRadius) { ResampleFilter newFilter = createFilter(filterType); newFilter.setSamplingRadius(samplingRadius); setFilter(newFilter); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/BellFilter.java0000664000000000000000000000133707741250133024556 0ustar /* * BellFilter * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; /** * A Bell resample filter. * @author Marco Schmidt * @since 0.10.0 * @see Resample * @see ResampleFilter */ public class BellFilter extends ResampleFilter { public float apply(float value) { if (value < 0.0f) { value = - value; } if (value < 0.5f) { return 0.75f - (value * value); } else if (value < 1.5f) { value = value - 1.5f; return 0.5f * (value * value); } else { return 0.0f; } } public String getName() { return "Bell"; } public float getRecommendedSamplingRadius() { return 1.5f; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Mirror.java0000664000000000000000000000363107741250133024003 0ustar /* * Mirror * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Mirrors images (leftmost column becomes rightmost column and vice versa, and so on). *

* Supported image types: {@link IntegerImage}. *

Usage example

*
 * PixelImage image = ...; // something implementing IntegerImage
 * Mirror mirror = new Mirror();
 * mirror.setInputImage(image);
 * mirror.process();
 * PixelImage mirroredImage = mirror.getOutputImage();
 * 
* @author Marco Schmidt */ public class Mirror extends ImageToImageOperation { private void process(IntegerImage in, IntegerImage out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); if (out == null) { out = (IntegerImage)in.createCompatibleImage(WIDTH, HEIGHT); setOutputImage(out); } int totalItems = in.getNumChannels() * WIDTH; int processedItems = 0; for (int c = 0; c < in.getNumChannels(); c++) { for (int x1 = 0, x2 = WIDTH - 1; x1 < WIDTH; x1++, x2--) { for (int y = 0; y < HEIGHT; y++) { out.putSample(c, x2, y, in.getSample(c, x1, y)); } setProgress(processedItems++, totalItems); } } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); if (in instanceof IntegerImage) { process((IntegerImage)in, (IntegerImage)getOutputImage()); } else { throw new WrongParameterException("Input image must be of type IntegerImage."); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/ResampleFilter.java0000664000000000000000000000374007741250133025450 0ustar /* * ResampleFilter * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; /** * Abstract base class for filters to be used with * the {@link Resample} operation. * @author Marco Schmidt * @since 0.10.0 */ public abstract class ResampleFilter { private float samplingRadius; /** * This empty constructor sets the sampling radius to the * recommended sampling radius as provided by * {@link #getRecommendedSamplingRadius()}. */ public ResampleFilter() { setSamplingRadius(getRecommendedSamplingRadius()); } /** * Returns the weight of the sample at the distance given * by the argument value. */ public abstract float apply(float value); /** * Return the name of this filter. * Should avoid natural language words if possible. * @return String with filter name */ public abstract String getName(); /** * Returns a recommendation for the sampling radius to * be used with this filter. * This recommendation value will be the default value * for the sampling radius of objects of this class. * You can modify it with a call to {@link #setSamplingRadius}. * @return the recommended sampling radius to be used with this filter */ public abstract float getRecommendedSamplingRadius(); /** * Returns the sampling radius of this object. * @see #getRecommendedSamplingRadius * @see #setSamplingRadius */ public float getSamplingRadius() { return samplingRadius; } /** * Sets the sampling radius to a new value. * Call this method if you do not want to use the default * radius as provided by {@link #getRecommendedSamplingRadius}. * @param newValue new sampling radius to be used with this object */ public void setSamplingRadius(float newValue) { if (newValue <= 0.0f) { throw new IllegalArgumentException("Sampling radius must be larger than 0.0f."); } samplingRadius = newValue; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Resample.java0000664000000000000000000005277210611670474024317 0ustar /* * Resample * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; // 2007-04-19 optimization results //* 10393 original speed in ms //* 6053 innerloop fix //* 5688 if .. continue loop break //* 5485 replaced contrib[] array access by variable //* 5173 doing the same on first pass (except innerloop fix which was ok there) // global gain is 2x faster /* This is the beginning of resample.pas, the Unit on which this class is based: // ----------------------------------------------------------------------------- // Project: bitmap resampler // Module: resample // Description: Interpolated Bitmap Resampling using filters. // Version: 01.03 // Release: 4 // Date: 29-JUN-1999 // Target: Win32, Delphi 2, 3 & 4 // Author(s): anme: Anders Melander, anders@melander.dk // Copyright (c) 1997-99 by Anders Melander // Formatting: 2 space indent, 8 space tabs, 80 columns. // ----------------------------------------------------------------------------- // This software is copyrighted as noted above. It may be freely copied, // modified, and redistributed, provided that the copyright notice(s) is // preserved on all copies. // // There is no warranty or other guarantee of fitness for this software, // it is provided solely "as is". Bug reports or fixes may be sent // to the author, who may or may not act on them as he desires. // // You may not include this software in a program or other software product // without supplying the source, or without informing the end-user that the // source is available for no extra charge. // // If you modify this software, you should include a notice in the "Revision // history" section giving the name of the person performing the modification, // the date of modification, and the reason for such modification. // ----------------------------------------------------------------------------- // Here's some additional copyrights for you: // // From filter.c: // The authors and the publisher hold no copyright restrictions // on any of these files; this source code is public domain, and // is freely available to the entire computer graphics community // for study, use, and modification. We do request that the // comment at the top of each file, identifying the original // author and its original publication in the book Graphics // Gems, be retained in all programs that use these files. // // ----------------------------------------------------------------------------- // Revision history: // // 0100 110997 anme - Adapted from Dale Schumacher's fzoom v0.20. // // 0101 110198 anme - Added Lanczos3 and Mitchell filters. // - Fixed range bug. // Min value was not checked on conversion from Single to // byte. // - Numerous optimizations. // - Added TImage stretch on form resize. // - Added support for Delphi 2 via TCanvas.Pixels. // - Renamed module from stretch to resample. // - Moved demo code to separate module. // // 0102 150398 anme - Fixed a problem that caused all pixels to be shifted // 1/2 pixel down and to the right (in source // coordinates). Thanks to David Ullrich for the // solution. // // 0103 170898 anme - Fixed typo: Renamed Strecth function to Stretch. // Thanks to Graham Stratford for spotting it. // Sorry about that. // 081298 anme - Added check for too small destination bitmap. // Thanks to Jeppe Oland for bringing this problem to my // attention. // 260399 anme - Fixed a problem with resampling of very narrow // bitmaps. Thanks to Holger Dors for bringing the // problem to my attention. // - Removed dependency of math unit. // 290699 jobe - Subsampling improvements by Josha Beukema. // // ----------------------------------------------------------------------------- // Credits: // The algorithms and methods used in this library are based on the article // "General Filtered Image Rescaling" by Dale Schumacher which appeared in the // book Graphics Gems III, published by Academic Press, Inc. // // The edge offset problem was fixed by: // * David Ullrich // // The subsampling problem was fixed by: // * Josha Beukema // ----------------------------------------------------------------------------- // To do (in rough order of priority): // * Implement Dale Schumacher's "Optimized Bitmap Scaling Routines". // * Optimize to use integer math instead of floating point where possible. // ----------------------------------------------------------------------------- */ /** * Resizes grayscale and truecolor images using filters. * For other image types (including paletted or bilevel images), you might * want to use the {@link ScaleReplication} class or convert the images to * grayscale (or RGB truecolor) first and then use this class. * Several algorithms for resampling are implemented, they differ in resulting image quality * and computational complexity. * *

Usage example

* This will scale image to 150 percent of its original size * in both directions, using the Lanczos3 filter type: *
 * Resample resample = new Resample();
 * resample.setInputImage(image);
 * resample.setSize(image.getWidth() * 3 / 2, image.getHeight() * 3 / 2);
 * resample.setFilter(Resample.FILTER_TYPE_LANCZOS3);
 * resample.process();
 * PixelImage scaledImage = resample.getOutputImage();
 * 
* *

Known problems

* * *

Origin

* This code is a port of Anders Melander's * Object Pascal (Delphi) unit resample.pas to Java. * The Delphi code is an adaptation (with some improvements) of * Dale Schumacher's fzoom C code. * Check out the homepage for the Delphi resample code, a demo application * to compare the different filtering algorithms is also provided: * http://www.melander.dk/delphi/resampler/index.html. * You will also find the original C code there. * The site seems to have gone for good. * *

Theory

* The theoretical background for all implementations is Dale Schumacher's article * General Filtered Image Rescaling * in Graphics Gems III, editor David Kirk, Academic Press, pages 8-16, 1994. *

* The Graphics Gems Repository can be found at * http://www.acm.org/tog/GraphicsGems/. * It also includes information on the books and how to order them. * * @author Marco Schmidt */ public class Resample extends ImageToImageOperation { /** * Constant for the Box filter (also known as Nearest Neighbor filter). */ public static final int FILTER_TYPE_BOX = 0; /** * Constant for the Triangle filter (also known as Linear filter or Bilinear filter). */ public static final int FILTER_TYPE_TRIANGLE = 1; /** * Constant for the Hermite filter. */ public static final int FILTER_TYPE_HERMITE = 2; /** * Constant for the Bell filter. */ public static final int FILTER_TYPE_BELL = 3; /** * Constant for the B-Spline filter. */ public static final int FILTER_TYPE_B_SPLINE = 4; /** * Constant for the Lanczos3 filter. */ public static final int FILTER_TYPE_LANCZOS3 = 5; /** * Constant for the Mitchell filter. */ public static final int FILTER_TYPE_MITCHELL = 6; class Contributor { int pixel; // Source pixel float weight; // Pixel weight } class CList { int n; Contributor[] p; } private Integer outWidth; private Integer outHeight; private ResampleFilter filter; private static ResampleFilter createFilter(int filterType) { switch(filterType) { case(FILTER_TYPE_BOX): return new BoxFilter(); case(FILTER_TYPE_TRIANGLE): return new TriangleFilter(); case(FILTER_TYPE_HERMITE): return new HermiteFilter(); case(FILTER_TYPE_BELL): return new BellFilter(); case(FILTER_TYPE_B_SPLINE): return new BSplineFilter(); case(FILTER_TYPE_LANCZOS3): return new Lanczos3Filter(); case(FILTER_TYPE_MITCHELL): return new MitchellFilter(); default: { throw new IllegalArgumentException("Unknown filter type in Resample: " + filterType); } } } /** * Returns the filter to be used in this operation. * @return ResampleFilter object or null if none was defined yet */ public ResampleFilter getFilter() { return filter; } /** * Returns the names of all predefined filters. * Each FILTER_TYPE_xyz constant can be used as an index into the array that is returned. * Names are retrieved by creating an object of each predefined filter class and calling its * getName method. * @return String array with filter names */ public static String[] getFilterNames() { String[] result = new String[getNumFilters()]; for (int i = 0; i < getNumFilters(); i++) { ResampleFilter filter = createFilter(i); result[i] = filter.getName(); } return result; } /** * Returns the number of predefined filters. * @return number of filters */ public static int getNumFilters() { return 7; } /** * This method does the actual work of rescaling an image. */ private void process(IntegerImage in, IntegerImage out) { if (out == null) { out = (IntegerImage)in.createCompatibleImage(outWidth.intValue(), outHeight.intValue()); setOutputImage(out); } if (filter == null) { filter = new TriangleFilter(); } float fwidth = filter.getSamplingRadius(); final int dstWidth = outWidth.intValue(); final int dstHeight = outHeight.intValue(); final int srcWidth = in.getWidth(); final int srcHeight = in.getHeight(); /* if (SrcWidth < 1) or (SrcHeight < 1) then raise Exception.Create('Source bitmap too small');*/ // Create intermediate image to hold horizontal zoom IntegerImage work = (IntegerImage)in.createCompatibleImage(dstWidth, srcHeight); float xscale; float yscale; if (srcWidth == 1) { xscale = (float)dstWidth / (float)srcWidth; } else { xscale = (float)(dstWidth - 1) / (float)(srcWidth - 1); } if (srcHeight == 1) { yscale = (float)dstHeight / (float)srcHeight; } else { yscale = (float)(dstHeight - 1) / (float)(srcHeight - 1); } /* Marco: the following two variables are used for progress notification */ int processedItems = 0; int totalItems = /*dstWidth +*/ srcHeight + /*dstHeight +*/ dstWidth; // -------------------------------------------- // Pre-calculate filter contributions for a row // ----------------------------------------------- CList[] contrib = new CList[dstWidth]; for (int i = 0; i < contrib.length; i++) { contrib[i] = new CList(); } // Horizontal sub-sampling // Scales from bigger to smaller width if (xscale < 1.0f) { float width = fwidth / xscale; float fscale = 1.0f / xscale; int numPixels = (int)(width * 2.0f + 1); for (int i = 0; i < dstWidth; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[numPixels]; for (int j = 0; j < contrib[i].p.length; j++) { contrib[i].p[j] = new Contributor(); } float center = i / xscale; int left = (int)Math.floor(center - width); int right = (int)Math.ceil(center + width); for (int j = left; j <= right; j++) { float weight = filter.apply((center - j) / fscale) / fscale; if (weight == 0.0f) { continue; } int n; if (j < 0) { n = -j; } else if (j >= srcWidth) { n = srcWidth - j + srcWidth - 1; } else { n = j; } int k = contrib[i].n; contrib[i].n = contrib[i].n + 1; contrib[i].p[k].pixel = n; contrib[i].p[k].weight = weight; } //setProgress(processedItems++, totalItems); } } else // Horizontal super-sampling // Scales from smaller to bigger width { int numPixels = (int)(fwidth * 2.0f + 1); for (int i = 0; i < dstWidth; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[numPixels]; for (int j = 0; j < contrib[i].p.length; j++) { contrib[i].p[j] = new Contributor(); } float center = i / xscale; int left = (int)Math.floor(center - fwidth); int right = (int)Math.ceil(center + fwidth); for (int j = left; j <= right; j++) { float weight = filter.apply(center - j); if (weight == 0.0f) { continue; } int n; if (j < 0) { n = -j; } else if (j >= srcWidth) { n = srcWidth - j + srcWidth - 1; } else { n = j; } int k = contrib[i].n; if (n < 0 || n >= srcWidth) { weight = 0.0f; } contrib[i].n = contrib[i].n + 1; contrib[i].p[k].pixel = n; contrib[i].p[k].weight = weight; } //setProgress(processedItems++, totalItems); } } // ---------------------------------------------------- // Apply filter to sample horizontally from Src to Work // ---------------------------------------------------- // start of Java-specific code // Marco: adjusted code to work with multi-channel images // where each channel can have a different maximum sample value (not only 255) final int NUM_CHANNELS = work.getNumChannels(); final int[] MAX = new int[NUM_CHANNELS]; for (int k = 0; k < NUM_CHANNELS; k++) { MAX[k] = work.getMaxSample(k); } // end of Java-specific code for (int k = 0; k < srcHeight; k++) { for (int i = 0; i < dstWidth; i++) { for (int channel = 0; channel < NUM_CHANNELS; channel++) { CList c=contrib[i]; float sample = 0.0f; int max=c.n; for (int j = 0; j < max; j++) { sample+=in.getSample(channel, c.p[j].pixel, k) * c.p[j].weight; } // Marco: procedure BoundRound included directly int result = (int)sample; if (result < 0) { result = 0; } else if (result > MAX[channel]) { result = MAX[channel]; } work.putSample(channel, i, k, result); } } setProgress(processedItems++, totalItems); } /* Marco: no need for "free memory" code as Java has garbage collection: // Free the memory allocated for horizontal filter weights for i := 0 to DstWidth-1 do FreeMem(contrib^[i].p); FreeMem(contrib); */ // ----------------------------------------------- // Pre-calculate filter contributions for a column // ----------------------------------------------- /*GetMem(contrib, DstHeight* sizeof(TCList));*/ contrib = new CList[dstHeight]; for (int i = 0; i < contrib.length; i++) { contrib[i] = new CList(); } // Vertical sub-sampling // Scales from bigger to smaller height if (yscale < 1.0f) { float width = fwidth / yscale; float fscale = 1.0f / yscale; int numContributors = (int)(width * 2.0f + 1); for (int i = 0; i < dstHeight; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[numContributors]; for (int j = 0; j < contrib[i].p.length; j++) { contrib[i].p[j] = new Contributor(); } float center = i / yscale; int left = (int)Math.floor(center - width); int right = (int)Math.ceil(center + width); for (int j = left; j <= right; j++) { float weight = filter.apply((center - j) / fscale) / fscale; // change suggested by Mike Dillon; not thoroughly tested; // old version: // float weight = filter.apply(center - j); if (weight == 0.0f) { continue; } int n; if (j < 0) { n = -j; } else if (j >= srcHeight) { n = srcHeight - j + srcHeight - 1; } else { n = j; } int k = contrib[i].n; contrib[i].n = contrib[i].n + 1; if (n < 0 || n >= srcHeight) { weight = 0.0f;// Flag that cell should not be used } contrib[i].p[k].pixel = n; contrib[i].p[k].weight = weight; } //setProgress(processedItems++, totalItems); } } else // Vertical super-sampling // Scales from smaller to bigger height { int numContributors = (int)(fwidth * 2.0f + 1); for (int i = 0; i < dstHeight; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[numContributors]; for (int j = 0; j < contrib[i].p.length; j++) { contrib[i].p[j] = new Contributor(); } float center = i / yscale; int left = (int)Math.floor(center - fwidth); int right = (int)Math.ceil(center + fwidth); for (int j = left; j <= right; j++) { float weight = filter.apply(center - j); if (weight == 0.0f) { continue; } int n; if (j < 0) { n = -j; } else if (j >= srcHeight) { n = srcHeight - j + srcHeight - 1; } else { n = j; } int k = contrib[i].n; contrib[i].n = contrib[i].n + 1; if (n < 0 || n >= srcHeight) { weight = 0.0f;// Flag that cell should not be used } contrib[i].p[k].pixel = n; contrib[i].p[k].weight = weight; } //setProgress(processedItems++, totalItems); } } // -------------------------------------------------- // Apply filter to sample vertically from Work to Dst // -------------------------------------------------- for (int k = 0; k < dstWidth; k++) { for (int i = 0; i < dstHeight; i++) { for (int channel = 0; channel < NUM_CHANNELS; channel++) { float sample = 0.0f; CList c=contrib[i]; int max=c.n; for (int j = 0; j < max; j++) { sample += work.getSample(channel, k, c.p[j].pixel) * c.p[j].weight; } int result = (int)sample; if (result < 0) { result = 0; } else if (result > MAX[channel]) { result = MAX[channel]; } out.putSample(channel, k, i, result); } } setProgress(processedItems++, totalItems); } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); if (outWidth == null && outHeight == null && getOutputImage() != null) { PixelImage out = getOutputImage(); outWidth = new Integer(out.getWidth()); outHeight = new Integer(out.getHeight()); } if (outWidth == null) { throw new MissingParameterException("Output width has not been initialized"); } if (outHeight == null) { throw new MissingParameterException("Output height has not been initialized"); } PixelImage image = getInputImage(); if (image.getWidth() == outWidth.intValue() && image.getHeight() == outHeight.intValue()) { throw new WrongParameterException("Input image already has the size specified by setSize."); } ensureOutputImageResolution(outWidth.intValue(), outHeight.intValue()); if (image instanceof IntegerImage) { process((IntegerImage)image, (IntegerImage)getOutputImage()); } else { throw new WrongParameterException("Input image must implement IntegerImage."); } } /** * Set the pixel resolution of the output image. * @param width the horizontal resolution of the output image * @param height the vertical resolution of the output image */ public void setSize(int width, int height) { outWidth = new Integer(width); outHeight = new Integer(height); } /** * Set a new filter object to be used with this operation. * @param newFilter a resample filter to be used for scaling */ public void setFilter(ResampleFilter newFilter) { filter = newFilter; } /** * Sets a new filter type, using the default sampling radius of that filter. * @param filterType the new filter type, one of the FILTER_TYPE_xyz constants of this class */ public void setFilter(int filterType) { setFilter(createFilter(filterType)); } /** * Sets a new filter type with a user-defined sampling radius. * @param filterType the new filter type, one of the FILTER_TYPE_xyz constants of this class * @param samplingRadius the sampling radius to be used with that filter, must be larger than 0.0f */ public void setFilter(int filterType, float samplingRadius) { ResampleFilter newFilter = createFilter(filterType); newFilter.setSamplingRadius(samplingRadius); setFilter(newFilter); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/BoxFilter.java0000664000000000000000000000113207741250133024421 0ustar /* * BoxFilter * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; /** * A box filter (also known as nearest neighbor). * @author Marco Schmidt * @since 0.10.0 * @see Resample * @see ResampleFilter */ public class BoxFilter extends ResampleFilter { public float apply(float value) { if (value > -0.5f && value <= 0.5f) { return 1.0f; } else { return 0.0f; } } public String getName() { return "Box"; } public float getRecommendedSamplingRadius() { return 0.5f; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/HermiteFilter.java0000664000000000000000000000123607741250133025273 0ustar /* * HermiteFilter * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; /** * A Hermite resampling filter. * @author Marco Schmidt * @since 0.10.0 * @see Resample * @see ResampleFilter */ public class HermiteFilter extends ResampleFilter { public float apply(float value) { if (value < 0.0f) { value = - value; } if (value < 1.0f) { return (2.0f * value - 3.0f) * value * value + 1.0f; } else { return 0.0f; } } public String getName() { return "Hermite"; } public float getRecommendedSamplingRadius() { return 1.0f; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/ScaleReplication.java0000664000000000000000000000647110377272144025764 0ustar /* * ScaleReplication * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Changes the pixel resolution of an image by replicating (or dropping) pixels. * A fast but low quality scaling algorithm that works with all kinds * of image types. * {@link Resample} provides better quality, but is slower and works with * intensity-based image data types only. * *

Usage example

* * The input image will be scaled to an image that is twice as wide as * itself and three times as high. * *
 * ScaleReplication scale = new ScaleReplication();
 * scale.setInputImage(image); // something implementing IntegerImage
 * scale.setSize(image.getWidth() * 2, image.getHeight() * 2);
 * scale.process();
 * PixelImage scaledImage = scale.getOutputImage();
 * 
* @author Marco Schmidt */ public class ScaleReplication extends ImageToImageOperation { private Integer outWidth; private Integer outHeight; private void process(IntegerImage in, IntegerImage out) { if (out == null) { out = (IntegerImage)in.createCompatibleImage(outWidth.intValue(), outHeight.intValue()); setOutputImage(out); } int IN_MAX_X = in.getWidth() - 1; int IN_MAX_Y = in.getHeight() - 1; int OUT_WIDTH = outWidth.intValue(); int OUT_HEIGHT = outHeight.intValue(); for (int y = 0; y < OUT_HEIGHT; y++) { final int SRC_Y = (int)(IN_MAX_Y * (y + 1) / OUT_HEIGHT); for (int x = 0; x < OUT_WIDTH; x++) { final int SRC_X = (int)(IN_MAX_X * (x + 1) / OUT_WIDTH); for (int c = 0; c < in.getNumChannels(); c++) { out.putSample(c, x, y, in.getSample(c, SRC_X, SRC_Y)); } } setProgress(y, OUT_HEIGHT); } } public void process() throws MissingParameterException, WrongParameterException { PixelImage pin = getInputImage(); if (pin == null) { throw new MissingParameterException("Input image object missing."); } if (!(pin instanceof IntegerImage)) { throw new WrongParameterException("ScaleReplication only works on IntegerImage objects."); } if (outWidth == null) { throw new MissingParameterException("Output width value missing."); } if (outHeight == null) { throw new MissingParameterException("Output height value missing."); } ensureImagesHaveSameResolution(); process((IntegerImage)pin, (IntegerImage)getOutputImage()); } /** * Specify the resolution to be used for the image to be created. * @param width horizontal resolution of the new image * @param height vertical resolution of the new image * @throws IllegalArgumentException if any of the arguments is smaller than 1 */ public void setSize(int width, int height) { if (width < 1) { throw new IllegalArgumentException("Output width must be larger than 0."); } if (height < 1) { throw new IllegalArgumentException("Output height must be larger than 0."); } outWidth = new Integer(width); outHeight = new Integer(height); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/MitchellFilter.java0000664000000000000000000000212307741250133025433 0ustar /* * MitchellFilter * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; /** * The Mitchell resample filter. * @author Marco Schmidt * @since 0.10.0 * @see Resample * @see ResampleFilter */ public class MitchellFilter extends ResampleFilter { private final float B = 1.0f / 3.0f; private final float C = 1.0f / 3.0f; public float apply(float value) { if (value < 0.0f) { value = -value; } float tt = value * value; if (value < 1.0f) { value = (((12.0f - 9.0f * B - 6.0f * C) * (value * tt)) + ((-18.0f + 12.0f * B + 6.0f * C) * tt) + (6.0f - 2f * B)); return value / 6.0f; } else if (value < 2.0f) { value = (((-1.0f * B - 6.0f * C) * (value * tt)) + ((6.0f * B + 30.0f * C) * tt) + ((-12.0f * B - 48.0f * C) * value) + (8.0f * B + 24 * C)); return value / 6.0f; } else { return 0.0f; } } public String getName() { return "Mitchell"; } public float getRecommendedSamplingRadius() { return 2.0f; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Flip.java0000664000000000000000000000354407741250133023426 0ustar /* * Flip * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Flips images (top row becomes bottom row and vice versa, and so on). *

* Supported image types: {@link IntegerImage}. *

* Usage example: *

 * Flip flip = new Flip();
 * flip.setInputImage(image); // image is some IntegerImage object
 * flip.process();
 * PixelImage flippedImage = flip.getOutputImage();
 * 
* @author Marco Schmidt */ public class Flip extends ImageToImageOperation { private void process(IntegerImage in, IntegerImage out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); final int TOTAL_ITEMS = in.getNumChannels() * HEIGHT; if (out == null) { out = (IntegerImage)in.createCompatibleImage(WIDTH, HEIGHT); setOutputImage(out); } int processedItems = 0; for (int c = 0; c < in.getNumChannels(); c++) { for (int y1 = 0, y2 = HEIGHT - 1; y1 < HEIGHT; y1++, y2--) { for (int x = 0; x < WIDTH; x++) { out.putSample(c, x, y2, in.getSample(c, x, y1)); } setProgress(processedItems++, TOTAL_ITEMS); } } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage in = getInputImage(); if (in instanceof IntegerImage) { process((IntegerImage)in, (IntegerImage)getOutputImage()); } else { throw new WrongParameterException("Input image must be of type IntegerImage."); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Crop.java0000664000000000000000000001206307741250133023433 0ustar /* * Crop * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Copies a rectangular area of one image to another image that is exactly as large * as that rectangular area. * Works with all image data classes implementing {@link net.sourceforge.jiu.data.IntegerImage}. * Make sure to use zero-based parameters when defining the bounds with * {@link #setBounds}! *

Usage example

* In this example we assume that the input image is larger than 20 pixels in both directions. * Ten pixels will be removed from any of its four borders. *
 * PixelImage image = ...; // something implementing IntegerImage
 * Crop crop = new Crop();
 * crop.setInputImage(image);
 * crop.setBounds(10, 10, image.getWidth() - 9, image.getHeight() - 9);
 * crop.process();
 * PixelImage croppedImage = crop.getOutputImage();
 * 
* @author Marco Schmidt */ public class Crop extends ImageToImageOperation { private int x1; private int y1; private int x2; private int y2; private void checkBounds() throws WrongParameterException { PixelImage in = getInputImage(); if (x1 >= in.getWidth()) { throw new WrongParameterException("x1 must be smaller than input image width."); } if (x2 >= in.getWidth()) { throw new WrongParameterException("x2 must be smaller than input image width."); } if (y1 >= in.getHeight()) { throw new WrongParameterException("y1 must be smaller than input image height."); } if (y2 >= in.getHeight()) { throw new WrongParameterException("y2 must be smaller than input image height."); } } private void process(IntegerImage in, IntegerImage out) { final int OUT_WIDTH = x2 - x1 + 1; final int OUT_HEIGHT = y2 - y1 + 1; if (out == null) { out = (IntegerImage)in.createCompatibleImage(OUT_WIDTH, OUT_HEIGHT); setOutputImage(out); } int totalItems = in.getNumChannels() * OUT_HEIGHT; int processedItems = 0; for (int c = 0; c < in.getNumChannels(); c++) { for (int yfrom = y1, yto = 0; yto < OUT_HEIGHT; yfrom++, yto++) { for (int xfrom = x1, xto = 0; xto < OUT_WIDTH; xfrom++, xto++) { out.putSample(c, xto, yto, in.getSample(c, xfrom, yfrom)); } setProgress(processedItems++, totalItems); } } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); checkBounds(); ensureOutputImageResolution(x2 - x1 + 1, y2 - y1 + 1); if (getInputImage() instanceof IntegerImage) { process((IntegerImage)getInputImage(), (IntegerImage)getOutputImage()); } else { throw new WrongParameterException("Input image must implement IntegerImage."); } } /** * Specify the rectangular section of the original image that is to be * copied to the output image by this operation. * Note that the arguments are not checked directly against any input image that may have * been provided to this Crop object, that checking is done later in {@link #process()}. * If any of the arguments provided here are outside of the input image's resolution * (e.g. x1 == 100 although the input image's width is only 60), a * {@link net.sourceforge.jiu.ops.WrongParameterException} will be thrown from * within {@link #process()}. *

* Note that the arguments to this method are zero-based, so the first column and row * are 0, the second 1, the third 2, and so on. * If you have a image that is 200 pixels wide and 100 pixels high, * values from 0 to 199 are valid for the x arguments, and values from 0 to 99 are valid * for the vertical direction. * @param x1 horizontal position of upper left corner of the rectangle * @param y1 vertical position of upper left corner of the rectangle * @param x2 horizontal position of lower right corner of the rectangle * @param y2 vertical position of lower right corner of the rectangle * @throws IllegalArgumentException if any of the arguments is negative or x1 larger than x2 or y1 larger than y2 */ public void setBounds(int x1, int y1, int x2, int y2) throws IllegalArgumentException { if (x1 < 0) { throw new IllegalArgumentException("x1 must not be negative."); } if (y1 < 0) { throw new IllegalArgumentException("y1 must not be negative."); } if (x2 < 0) { throw new IllegalArgumentException("x2 must not be negative."); } if (y2 < 0) { throw new IllegalArgumentException("y2 must not be negative."); } if (x1 > x2) { throw new IllegalArgumentException("x1 must not be larger than x2."); } if (y1 > y2) { throw new IllegalArgumentException("y1 must not be larger than y2."); } this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Rotate180.java0000664000000000000000000000377507741250133024231 0ustar /* * Rotate180 * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Rotates images by 180 degrees. * The result is the same as a a {@link Flip} operation followed by a {@link Mirror} operation (or vice versa). * Input image must implement {@link net.sourceforge.jiu.data.IntegerImage}. *

Usage example

*
 * Rotate180 rotate = new Rotate180();
 * rotate.setInputImage(image); // something implementing IntegerImage
 * rotate.process();
 * PixelImage rotatedImage = rotate.getOutputImage();
 * 
* @author Marco Schmidt */ public class Rotate180 extends ImageToImageOperation { private void process(IntegerImage in, IntegerImage out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); if (out == null) { out = (IntegerImage)in.createCompatibleImage(WIDTH, HEIGHT); setOutputImage(out); } int totalItems = in.getNumChannels() * HEIGHT; int processedItems = 0; for (int c = 0; c < in.getNumChannels(); c++) { for (int y1 = 0, y2 = HEIGHT - 1; y1 < HEIGHT; y1++, y2--) { for (int x1 = 0, x2 = WIDTH - 1; x1 < WIDTH; x1++, x2--) { out.putSample(c, x2, y2, in.getSample(c, x1, y1)); } setProgress(processedItems++, totalItems); } } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); ensureImagesHaveSameResolution(); PixelImage image = getInputImage(); if (image instanceof IntegerImage) { process((IntegerImage)image, (IntegerImage)getOutputImage()); } else { throw new WrongParameterException("Input image must implement IntegerImage."); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/BSplineFilter.java0000664000000000000000000000145107741250133025231 0ustar /* * BSplineFilter * * Copyright (c) 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; /** * A B-spline resample filter. * @author Marco Schmidt * @since 0.10.0 * @see Resample * @see ResampleFilter */ public class BSplineFilter extends ResampleFilter { public float apply(float value) { if (value < 0.0f) { value = - value; } if (value < 1.0f) { float tt = value * value; return 0.5f * tt * value - tt + (2.0f / 3.0f); } else if (value < 2.0f) { value = 2.0f - value; return (1.0f / 6.0f) * value * value * value; } else { return 0.0f; } } public String getName() { return "B-Spline"; } public float getRecommendedSamplingRadius() { return 2.0f; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/package.html0000664000000000000000000000044407741250133024146 0ustar

Operations to change the geometry of an image, mirroring it horizontally and vertically, shearing, scaling and rotating it. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Lanczos3Filter.java0000664000000000000000000000152507741250133025373 0ustar /* * Lanczos3Filter * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; /** * The Lanczos 3 resample filter. * @author Marco Schmidt * @since 0.10.0 * @see Resample * @see ResampleFilter */ public class Lanczos3Filter extends ResampleFilter { private double sinc(double value) { if (value != 0.0f) { value = value * Math.PI; return Math.sin(value) / value; } else { return 1.0; } } public float apply(float value) { if (value < 0.0f) { value = -value; } if (value < 3.0f) { return (float)(sinc(value) * sinc(value / 3.0)); } else { return 0.0f; } } public String getName() { return "Lanczos3"; } public float getRecommendedSamplingRadius() { return 3.0f; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Shear.java0000664000000000000000000001066710324333614023577 0ustar /* * Shear * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Shears an image by a given angle. * The angle must be larger than -90 and smaller than 90 degrees. * Shearing works with all image types that implement {@link net.sourceforge.jiu.data.IntegerImage}. *

Usage example

*
 * Shear shear = new Shear();
 * shear.setInputImage(image); // some IntegerImage
 * shear.setAngle(5.0);
 * shear.process();
 * PixelImage shearedImage = shear.getOutputImage();
 * 
*

* This is an adjusted version of Jef Poskanzer's shearing code from his ACME * package; see the * API * documentation page of ACME's Shear class. * * @author Jef Poskanzer * @author Marco Schmidt */ public class Shear extends ImageToImageOperation { private double angle; /** * For a given image width and shearing angle this method * computes the width of the resulting image. * This method is static so that it can be called easily from a GUI dialog * or other objects that want to present the width of a sheared image. * @param oldImageWidth horizontal resolution of the image to be sheared * @param height height of the image to be sheared * @param angle the angle to be used in the shearing operation * @return width of the sheared image */ public static int computeNewImageWidth(int oldImageWidth, int height, double angle) { double shearfac = Math.tan(angle * Math.PI / 180.0); if (shearfac < 0.0) { shearfac = -shearfac; } return (int)(height * shearfac + oldImageWidth + 0.999999); } /** * Returns the angle associated with this shearing operation object. * @return shearing angle, between -90 and 90 * @see #setAngle */ public double getAngle() { return angle; } private void process(IntegerImage in, IntegerImage out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); int totalItems = in.getNumChannels() * HEIGHT; int processedItems = 0; double angle = getAngle() * Math.PI / 180.0; double shearfac = Math.tan(angle); if (shearfac < 0.0) { shearfac = -shearfac; } int NEW_WIDTH = (int)(HEIGHT * shearfac + WIDTH + 0.999999); if (out == null) { out = (IntegerImage)in.createCompatibleImage(NEW_WIDTH, HEIGHT); setOutputImage(out); } for (int c = 0; c < in.getNumChannels(); c++) { for (int y = 0; y < HEIGHT; y++) { double new0; if (angle > 0.0) { new0 = y * shearfac; } else { new0 = (HEIGHT - y) * shearfac; } int intnew0 = (int)new0; double fracnew0 = new0 - intnew0; double omfracnew0 = 1.0 - fracnew0; int prev = 0; for (int x = 0; x < WIDTH; x++) { int value = in.getSample(c, x, y); out.putSample(c, intnew0 + x, y, (int)(fracnew0 * prev + omfracnew0 * value)); prev = value; } out.putSample(c, intnew0 + WIDTH, y, (int)(fracnew0 * prev)); setProgress(processedItems++, totalItems); } } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); PixelImage in = getInputImage(); ensureOutputImageResolution(computeNewImageWidth(in.getWidth(), in.getHeight(), getAngle()), in.getHeight()); if (in instanceof IntegerImage) { process((IntegerImage)in, (IntegerImage)getOutputImage()); } else { throw new WrongParameterException("Input image must implement IntegerImage."); } } /** * Sets the angle to be used in the shearing operation to the argument value. * The angle must be larger than -90.0 and smaller than 90.0. * @param newAngle the angle to be used in this operation * @throws IllegalArgumentException if the argument is not in the above mentioned interval * @see #getAngle */ public void setAngle(double newAngle) { if (newAngle <= -90.0 || newAngle >= 90.0) { throw new IllegalArgumentException("Angle must be > -90 and < 90; got " + newAngle); } else { angle = newAngle; } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Rotate90Left.java0000664000000000000000000000357007741250133024755 0ustar /* * Rotate90Left * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Rotates images by 90 degrees counter-clockwise (to the left). * Input image must implement {@link net.sourceforge.jiu.data.IntegerImage}. *

Usage example

*
 * Rotate90Left rotate = new Rotate90Left();
 * rotate.setInputImage(image);
 * rotate.process();
 * PixelImage rotatedImage = rotate.getOutputImage();
 * 
* @author Marco Schmidt */ public class Rotate90Left extends ImageToImageOperation { private void process(IntegerImage in, IntegerImage out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); if (out == null) { out = (IntegerImage)in.createCompatibleImage(HEIGHT, WIDTH); setOutputImage(out); } int totalItems = in.getNumChannels() * HEIGHT; int processedItems = 0; for (int c = 0; c < in.getNumChannels(); c++) { for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { out.putSample(c, y, (WIDTH - x - 1), in.getSample(c, x, y)); } setProgress(processedItems++, totalItems); } } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); PixelImage in = getInputImage(); ensureOutputImageResolution(in.getHeight(), in.getWidth()); if (in instanceof IntegerImage) { process((IntegerImage)in, (IntegerImage)getOutputImage()); } else { throw new WrongParameterException("Input image must implement IntegerImage."); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/geometry/Rotate90Right.java0000664000000000000000000000356607741250134025146 0ustar /* * Rotate90Right * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.geometry; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * Rotates images by 90 degrees clockwise (to the right). * Input image must implement {@link net.sourceforge.jiu.data.IntegerImage}. *

Usage example

*
 * Rotate90Right rotate = new Rotate90Right();
 * rotate.setInputImage(image);
 * rotate.process();
 * PixelImage rotatedImage = rotate.getOutputImage();
 * 
* @author Marco Schmidt */ public class Rotate90Right extends ImageToImageOperation { private void process(IntegerImage in, IntegerImage out) { final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); if (out == null) { out = (IntegerImage)in.createCompatibleImage(HEIGHT, WIDTH); setOutputImage(out); } int totalItems = in.getNumChannels() * HEIGHT; int processedItems = 0; for (int c = 0; c < in.getNumChannels(); c++) { for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { out.putSample(c, (HEIGHT - y - 1), x, in.getSample(c, x, y)); } setProgress(processedItems++, totalItems); } } } public void process() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); PixelImage in = getInputImage(); ensureOutputImageResolution(in.getHeight(), in.getWidth()); if (in instanceof IntegerImage) { process((IntegerImage)in, (IntegerImage)getOutputImage()); } else { throw new WrongParameterException("Input image must implement IntegerImage."); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/0000775000000000000000000000000010546532076021276 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/PCDCodec.java0000664000000000000000000004074410523755460023515 0ustar /* * PCDCodec * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.IOException; import java.io.RandomAccessFile; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.codecs.WrongFileFormatException; import net.sourceforge.jiu.color.YCbCrIndex; import net.sourceforge.jiu.color.conversion.PCDYCbCrConversion; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.util.ArrayRotation; import net.sourceforge.jiu.util.ArrayScaling; /** * A codec to read Kodak Photo-CD (image pac) image files. * Typical file extension is .pcd. * PCD is designed to store the same image in several resolutions. * Not all resolutions are always present in a file. * Typically, the first five resolutions are available and the file size * is between four and six megabytes. * Lossless compression (Huffman encoding) is used to store the higher resolution images. * All images are in 24 bit YCbCr colorspace, with a component subsampling of 4:1:1 (Y:Cb:Cr) * in both horizontal and vertical direction. *

Limitations

* Only the lowest three resolutions are supported by this codec. *

Sample PCD files

* You can download sample PCD image files from * Kodak's * website. * * @author Marco Schmidt */ public class PCDCodec extends ImageCodec implements YCbCrIndex { /** * Base/16, the minimum pixel resolution, 192 x 128 pixels. */ public static final int PCD_RESOLUTION_1 = 0; /** * Base/4, the second pixel resolution, 384 x 256 pixels. */ public static final int PCD_RESOLUTION_2 = 1; /** * Base, the third pixel resolution, 768 x 512 pixels. */ public static final int PCD_RESOLUTION_3 = 2; /** * Base*4, the fourth pixel resolution, 1536 x 1024 pixels. Unsupported */ public static final int PCD_RESOLUTION_4 = 3; /** * Base*16, the fifth pixel resolution, 3072 x 2048 pixels. Unsupported */ public static final int PCD_RESOLUTION_5 = 4; /** * Base*64, the sixth pixel resolution, 6144 x 4096 pixels. Unsupported */ public static final int PCD_RESOLUTION_6 = 5; /** * Index for the default resolution , Base ({@link #PCD_RESOLUTION_3}). */ public static final int PCD_RESOLUTION_DEFAULT = PCD_RESOLUTION_3; /** * This two-dimensional int array holds all possible pixel resolutions for * a PCD file. Use one of the PCD resolution constants (e.g. * {@link #PCD_RESOLUTION_3} as first index. * The second index must be 0 or 1 and leads to either width or * height. * Example: PCD_RESOLUTION[PCD_RESOLUTION_3][1] will evalute * as 512, which can be width or height, depending on the image being * in landscape or portrait mode. * You may want to use these resolution values in your program * to prompt the user which resolution to load from the file. */ public static final int[][] PCD_RESOLUTIONS = {{192, 128}, {384, 256}, {768, 512}, {1536, 1024}, {3072, 2048}, {6144, 4096}}; // offsets into the file for the three uncompressed resolutions private static final long[] PCD_FILE_OFFSETS = {0x2000, 0xb800, 0x30000}; /*private static final long[] PCD_BASE_LENGTH = {0x2000, 0xb800, 0x30000};*/ // some constants to understand the orientation of an image private static final int NO_ROTATION = 0; private static final int ROTATE_90_LEFT = 1; private static final int ROTATE_180 = 2; private static final int ROTATE_90_RIGHT = 3; // 2048 bytes private static final int SECTOR_SIZE = 0x800; // "PCD_IPI" private static final byte[] MAGIC = {0x50, 0x43, 0x44, 0x5f, 0x49, 0x50, 0x49}; private boolean performColorConversion; private boolean monochrome; private int numChannels; private int resolutionIndex; private RandomAccessFile in; private byte[][] data; /** * This constructor chooses the default settings for PCD image loading: * */ public PCDCodec() { super(); setColorConversion(true); setMonochrome(false); setResolutionIndex(PCD_RESOLUTION_DEFAULT); } private byte[][] allocateMemory() { int numPixels = PCD_RESOLUTIONS[resolutionIndex][0] * PCD_RESOLUTIONS[resolutionIndex][1]; byte[][] result = new byte[numChannels][]; for (int i = 0; i < numChannels; i++) { result[i] = new byte[numPixels]; } return result; } /*private void checkByteArray( byte[][] data, int numPixels) throws IllegalArgumentException { // check if array is non-null if (data == null) { throw new IllegalArgumentException("Error: Image channel array is not initialized."); } // check if array has enough entries int channels; if (monochrome) { channels = 1; if (data.length < 1) { throw new IllegalArgumentException("Error: Image channel " + "array must have at least one channel for monochrome " + "images."); } } else { channels = 3; if (data.length < 3) { throw new IllegalArgumentException("Error: Image channel " + "array must have at least three channels for color images."); } } // check if each channel has enough entries for the samples for (int i = 0; i < channels; i++) { if (data[i].length < numPixels) { throw new IllegalArgumentException("Error: Image channel #" + i + " is not large enough (" + numPixels + " entries required, " + data[i].length + " found)."); } } }*/ private void convertToRgb(int width, int height) { byte[] red = new byte[width]; byte[] green = new byte[width]; byte[] blue = new byte[width]; int offset = 0; for (int y = 0; y < height; y++) { PCDYCbCrConversion.convertYccToRgb( data[INDEX_Y], data[INDEX_CB], data[INDEX_CR], offset, red, green, blue, 0, width); System.arraycopy(red, 0, data[0], offset, width); System.arraycopy(green, 0, data[1], offset, width); System.arraycopy(blue, 0, data[2], offset, width); offset += width; } } private IntegerImage createImage(int width, int height) { if (monochrome) { Gray8Image image = new MemoryGray8Image(width, height); int offset = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { image.putByteSample(0, x, y, data[0][offset++]); } } return image; } else if (performColorConversion) { RGB24Image image = new MemoryRGB24Image(width, height); int offset = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { image.putByteSample(RGB24Image.INDEX_RED, x, y, data[0][offset]); image.putByteSample(RGB24Image.INDEX_GREEN, x, y, data[1][offset]); image.putByteSample(RGB24Image.INDEX_BLUE, x, y, data[2][offset]); offset++; } } return image; } else { return null; } } public String[] getFileExtensions() { return new String[] {".pcd"}; } public String getFormatName() { return "Kodak Photo-CD (PCD)"; } public String[] getMimeTypes() { return new String[] {"image/x-pcd"}; } public boolean isLoadingSupported() { return true; } public boolean isSavingSupported() { return false; } /** * Attempts to load an image. * The codec must have been given an input stream, all other * parameters (do not convert color to RGB, load monochrome channel * only, load other resolution than default) can optionally be * chosen by calling the corresponding methods. * * @return loaded image * @throws IOException if there were reading errors * @throws OutOfMemoryException if there was not enough free memory * available * @throws InvalidFileStructureException if the file seems to be a PCD * stream but has logical errors in it * @throws WrongFileFormatException if this is not a PCD file */ private void load() throws InvalidFileStructureException, IOException, UnsupportedTypeException, WrongFileFormatException { if (resolutionIndex != PCD_RESOLUTION_1 && resolutionIndex != PCD_RESOLUTION_2 && resolutionIndex != PCD_RESOLUTION_3) { throw new UnsupportedTypeException("Error reading PCD input " + "stream. Only the three lowest resolutions are supported."); } if (in == null) { throw new IllegalArgumentException("Input file is missing " + "(use PCDCodec.setInput(RandomAccessFile)."); } if (in.length() < 16 * 1024) { throw new WrongFileFormatException("Not a PCD file."); } byte[] sector = new byte[SECTOR_SIZE]; // read first sector; first 7 bytes must be 0xff in.readFully(sector); for (int i = 0; i < 7; i++) { if (sector[i] != -1) { throw new WrongFileFormatException("Input is not a valid PCD " + "file (wrong magic byte sequence)."); } } // read second sector and check more magic bytes in.readFully(sector); for (int i = 0; i < MAGIC.length; i++) { if (sector[i] != MAGIC[i]) { throw new WrongFileFormatException("Input is not a valid PCD " + "file (wrong magic byte sequence)."); } } // get image orientation and resolution int rotationAngle = sector[0x602] & 0x03; int width = PCD_RESOLUTIONS[resolutionIndex][0]; int height = PCD_RESOLUTIONS[resolutionIndex][1]; int realWidth = width; int realHeight = height; if (rotationAngle == ROTATE_90_LEFT || rotationAngle == ROTATE_90_RIGHT) { realWidth = height; realHeight = width; } if (!hasBounds()) { setBounds(0, 0, realWidth - 1, realHeight - 1); } // determine which uncompressed image will be loaded int uncompressedResolution = resolutionIndex; if (resolutionIndex > PCD_RESOLUTION_3) { uncompressedResolution = PCD_RESOLUTION_3; } // load uncompressed image data = allocateMemory(); loadUncompressedImage(uncompressedResolution); // reverse color subsampling if necessary if (!monochrome) { ArrayScaling.scaleUp200Percent(data[INDEX_CB], PCD_RESOLUTIONS[uncompressedResolution][0] / 2, PCD_RESOLUTIONS[uncompressedResolution][1] / 2); ArrayScaling.scaleUp200Percent(data[INDEX_CR], PCD_RESOLUTIONS[uncompressedResolution][0] / 2, PCD_RESOLUTIONS[uncompressedResolution][1] / 2); } // TODO load higher resolution by decoding differences to uncompressed image // ... // convert to RGB color space if possible and desired if ((!monochrome) && performColorConversion) { convertToRgb(width, height); } // rotate the image if necessary rotateArrays(rotationAngle, width, height); // adjust width and height if (rotationAngle == ROTATE_90_LEFT || rotationAngle == ROTATE_90_RIGHT) { int temp = width; width = height; height = temp; } setImage(createImage(width, height)); } /** * Loads one of the three lowest resolution images from the file. * First skips as many bytes as there are between the current * stream offset and the offset of the image in the PCD file * (first three images are at fixed positions). * Then reads the pixels from in to data. *

* Note that there are width times height * samples for Y, but only one fourth that many samples for each Cb and Cr * (because of the 4:1:1 subsampling of the two chroma components). *

* @param resolution one of PCD_RESOLUTION_1, PCD_RESOLUTION_2 or PCD_RESOLUTION_3 * @throws an IOException if there were any reading errors */ private void loadUncompressedImage(int resolution) throws IllegalArgumentException, IOException { if (resolution != PCD_RESOLUTION_1 && resolution != PCD_RESOLUTION_2 && resolution != PCD_RESOLUTION_3) { throw new IllegalArgumentException("Error loading " + "PCD image, only the lowest three resolutions are " + "uncompressed."); } in.seek(PCD_FILE_OFFSETS[resolution]); int fullWidth = PCD_RESOLUTIONS[resolution][0]; int fullHeight = PCD_RESOLUTIONS[resolution][1]; int halfWidth = fullWidth / 2; int halfHeight = fullHeight / 2; int offset1 = 0; int offset2 = 0; for (int y = 0; y < halfHeight; y++) { // read two luminance rows in.readFully(data[INDEX_Y], offset1, fullWidth * 2); offset1 += (fullWidth * 2); if (monochrome) { if (in.skipBytes(fullWidth) != fullWidth) { throw new IOException("Could not skip " + fullWidth + " bytes."); } } else { // read one row for each cb and cr in.readFully(data[INDEX_CB], offset2, halfWidth); in.readFully(data[INDEX_CR], offset2, halfWidth); offset2 += halfWidth; } } } /** * Checks the parameter and loads an image. */ public void process() throws InvalidFileStructureException, MissingParameterException, OperationFailedException, UnsupportedTypeException, WrongFileFormatException { in = getRandomAccessFile(); if (in == null) { throw new MissingParameterException("RandomAccessFile object needed in PCDCodec."); } if (getMode() != CodecMode.LOAD) { throw new UnsupportedTypeException("PCDCodec can only load images."); } try { load(); } catch (IOException ioe) { throw new OperationFailedException("I/O error: " + ioe.toString()); } } private void rotateArrays(int rotationAngle, int width, int height) { if (rotationAngle == NO_ROTATION) { return; } int numPixels = width * height; for (int i = 0; i < numChannels; i++) { byte[] dest = new byte[numPixels]; switch(rotationAngle) { case(ROTATE_90_LEFT): { ArrayRotation.rotate90Left(width, height, data[i], 0, dest, 0); break; } case(ROTATE_90_RIGHT): { ArrayRotation.rotate90Right(width, height, data[i], 0, dest, 0); break; } case(ROTATE_180): { ArrayRotation.rotate180(width, height, data[i], 0, dest, 0); break; } } System.arraycopy(dest, 0, data[i], 0, numPixels); } } /*private void scaleUp(int currentResolution) { int width = PCD_RESOLUTIONS[currentResolution][0]; int height = PCD_RESOLUTIONS[currentResolution][1]; ArrayScaling.scaleUp200Percent(data[INDEX_Y], width, height); if (!monochrome) { ArrayScaling.scaleUp200Percent(data[INDEX_CB], width, height); ArrayScaling.scaleUp200Percent(data[INDEX_CR], width, height); } }*/ /** * Specify whether color is converted from PCD's YCbCr color space to * RGB color space. * The default is true, and you should only change this * if you really know what you are doing. * If you simply want the luminance (gray) channel, use * {@link #setMonochrome(boolean)} with true as parameter. * @param performColorConversion boolean that determines whether color conversion is applied */ public void setColorConversion(boolean performColorConversion) { this.performColorConversion = performColorConversion; } public void setFile(String fileName, CodecMode codecMode) throws IOException, UnsupportedCodecModeException { if (codecMode == CodecMode.LOAD) { setRandomAccessFile(new RandomAccessFile(fileName, "r"), CodecMode.LOAD); } else { throw new UnsupportedCodecModeException("This PCD codec can only load images."); } } /** * Specifies whether the image is to be loaded as gray or color image. * If argument is true, only the gray channel is loaded. */ public void setMonochrome(boolean monochrome) { this.monochrome = monochrome; if (monochrome) { numChannels = 1; } else { numChannels = 3; } } public void setResolutionIndex(int resolutionIndex) { this.resolutionIndex = resolutionIndex; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/PNGCodec.java0000664000000000000000000016127710523756402023535 0ustar /* * PNGCodec * * Copyright (c) 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.DataOutput; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.SimpleTimeZone; import java.util.zip.CheckedInputStream; import java.util.zip.Deflater; import java.util.zip.InflaterInputStream; import java.util.zip.CRC32; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.MemoryGray16Image; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.MemoryRGB48Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGB48Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.util.ArrayConverter; /** * An input stream that reads from an underlying stream of PNG * IDAT chunks and skips all header information. * PNG uses one or more IDAT chunks to store image data. * The resulting stream looks like that: * IDAT [chunk size] [compressed data] [checksum] * IDAT [chunk size] [compressed data] [checksum] ... * This stream class expects an input stream where the first IDAT chunk name and chunk * size have been read already, the stream is thus pointing to the * first byte of the first [compressed data] section. * The size of that section is given to the constructor. * This class then returns calls to read(), counts the bytes it has given * away and, whenever a compressed data section has been consumed, it reads * the IDAT chunk and stores its size, using it to determine when the * next compressed data section will end. * That way, for the caller the stream appears to be one large compressed * section. *

* According to the PNG specs the reason for multiple IDAT chunks is as * follows: *

* (Multiple IDAT chunks are allowed so that encoders can work in a fixed * amount of memory; typically the chunk size will correspond to the encoder's * buffer size.) *
* 4.1.3. IDAT Image data *

* If there is a more elegant approach to read multiple IDAT chunks, please * let me know. * However, reading everything into memory is not an option. * @author Marco Schmidt * @since 0.12.0 */ class PngIdatInputStream extends InputStream { private static final int IDAT = 0x49444154; private DataInputStream in; private long bytesLeft; public PngIdatInputStream(DataInputStream input, long bytes) { in = input; bytesLeft = bytes; } public int read() throws IOException { if (bytesLeft == 0) { skipHeaders(); } bytesLeft--; return in.read(); } private void skipHeaders() throws IOException { do { //int crc = in.readInt(); in.readInt(); // skip CRC bytesLeft = in.readInt() & 0xffffffffL; int type = in.readInt(); if (IDAT != type) { throw new IOException("Expected IDAT chunk type, got " + Integer.toHexString(type)); } } while (bytesLeft == 0); } } /** * A codec for the Portable Network Graphics (PNG) format. * Supports both loading and saving of images. *

Usage examples

*

Load an image

* The following example code loads an image from a PNG file. * Note that you could also use {@link ImageLoader} or {@link net.sourceforge.jiu.gui.awt.ToolkitLoader} * which require only a single line of code and can load all formats * supported by JIU, including PNG. *
  PNGCodec codec = new PNGCodec();
 *  codec.setFile("image.png", CodecMode.LOAD);
 *  codec.process();
 *  PixelImage image = codec.getImage();
*

Save an image

*
  PNGCodec codec = new PNGCodec();
 *  codec.setFile("out.png", CodecMode.SAVE);
 *  codec.setImage(image);
 *  codec.setCompressionLevel(Deflater.BEST_COMPRESSION);
 *  codec.appendComment("Copyright (c) 1992 John Doe");
 *  // sets last modification time to current time
 *  codec.setModification(new GregorianCalendar(
 *   new SimpleTimeZone(0, "UTC")));
 *  codec.process();
*

Supported storage order types

*

Loading

* This codec reads both non-interlaced and Adam7 interlaced PNG files. *

Saving

* This codec only writes non-interlaced PNG files. *

Supported color types

*

Loading

* *

Saving

* *

Transparency information

* PNG allows to store different types of transparency information. * Full alpha channels, transparent index values, and more. * Right now, this JIU codec does not make use of this information and simply * skips over it when encountered. *

Bounds

* This codec regards the bounds concept. * If bounds are specified with {@link #setBounds}, the codec will only load or save * part of an image. *

Metadata

*

Loading

* *

Saving

* *

Implementation details

* This class relies heavily on the Java runtime library for decompression and * checksum creation. *

Background

* To learn more about the PNG file format, visit its * official homepage. * There you can find a detailed specification, * test images and existing PNG libraries and PNG-aware applications. * The book PNG - The Definitive Guide by Greg Roelofs, published by O'Reilly, 1999, * ISBN 1-56592-542-4 is a valuable source of information on PNG. * It is out of print, but it can be viewed online and downloaded for offline reading * in its entirety from the site. * @author Marco Schmidt * @since 0.12.0 */ public class PNGCodec extends ImageCodec { private final int CHUNK_CRC32_IEND = 0xae426082; private final int CHUNK_SIZE_IHDR = 0x0000000d; private final int CHUNK_TYPE_IDAT = 0x49444154; private final int CHUNK_TYPE_IEND = 0x49454e44; private final int CHUNK_TYPE_IHDR = 0x49484452; private final int CHUNK_TYPE_PHYS = 0x70485973; private final int CHUNK_TYPE_PLTE = 0x504c5445; private final int CHUNK_TYPE_TEXT = 0x74455874; private final int CHUNK_TYPE_TIME = 0x74494d45; private final int COLOR_TYPE_GRAY = 0; private final int COLOR_TYPE_GRAY_ALPHA = 4; private final int COLOR_TYPE_INDEXED = 3; private final int COLOR_TYPE_RGB = 2; private final int COLOR_TYPE_RGB_ALPHA = 6; private final int COLOR_TYPE_ALPHA = 4; private final int FILTER_TYPE_NONE = 0; private final int FILTER_TYPE_SUB = 1; private final int FILTER_TYPE_UP = 2; private final int FILTER_TYPE_AVERAGE = 3; private final int FILTER_TYPE_PAETH = 4; private final int COMPRESSION_DEFLATE = 0; private final int INTERLACING_NONE = 0; private final int INTERLACING_ADAM7 = 1; private final int FILTERING_ADAPTIVE = 0; private final int MAX_TEXT_SIZE = 512; private final int ADAM7_NUM_PASSES = 7; private final int DEFAULT_ENCODING_MIN_IDAT_SIZE = 32 * 1024; private final int[] ADAM7_COLUMN_INCREMENT = {8, 8, 4, 4, 2, 2, 1}; private final int[] ADAM7_FIRST_COLUMN = {0, 4, 0, 2, 0, 1, 0}; private final int[] ADAM7_FIRST_ROW = {0, 0, 4, 0, 2, 0, 1}; private final int[] ADAM7_ROW_INCREMENT = {8, 8, 8, 4, 4, 2, 2}; private final byte[] MAGIC_BYTES = {(byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47, (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a}; private boolean alpha; private byte[][] buffers; private int bpp; private CRC32 checksum; private CheckedInputStream checkedIn; private int chunkCounter; private int colorType; private int compressionType; private int currentBufferIndex; private int deflateLevel = Deflater.DEFAULT_COMPRESSION; private int deflateStrategy = Deflater.DEFAULT_STRATEGY; private int encodingMinIdatSize = DEFAULT_ENCODING_MIN_IDAT_SIZE; private int filterType; private boolean hasIhdr; private int height; private IntegerImage image; private DataInputStream in; private InflaterInputStream infl; private int interlaceType; private Calendar modification; private int numChannels; private DataOutput out; private Palette palette; private int precision; private int previousBufferIndex; private int width; /** * Allocates the right image to private field image, * taking into consideration the fields width, height, precision and colorType. * Assumes that an IHDR chunk has been read and the above mentioned * fields have been initialized and checked for their validity. */ private void allocateImage() throws InvalidFileStructureException, UnsupportedTypeException { setBoundsIfNecessary(width, height); int w = getBoundsWidth(); int h = getBoundsHeight(); if (colorType == COLOR_TYPE_GRAY || colorType == COLOR_TYPE_GRAY_ALPHA) { if (precision == 1) { image = new MemoryBilevelImage(w, h); } else if (precision <= 8) { image = new MemoryGray8Image(w, h); } else if (precision == 16) { image = new MemoryGray16Image(w, h); } } else if (colorType == COLOR_TYPE_INDEXED) { if (palette == null) { throw new InvalidFileStructureException("No palette found when trying to load indexed image."); } image = new MemoryPaletted8Image(w, h, palette); } else if (colorType == COLOR_TYPE_RGB || colorType == COLOR_TYPE_RGB_ALPHA) { if (precision == 8) { image = new MemoryRGB24Image(w, h); } else { image = new MemoryRGB48Image(w, h); } } else { throw new UnsupportedTypeException("Unsupported image type encountered"); } } /** * Checks values {@link #precision} and {@link #colorType}. * A lot of combinations possibly found in an IHDR chunk * are invalid. * Also initializes {@link #alpha} and {@link #numChannels}. * @throws UnsupportedTypeException if an invalid combination * of precision and colorType is found */ private void checkColorTypeAndPrecision() throws UnsupportedTypeException { if (colorType != COLOR_TYPE_GRAY && colorType != COLOR_TYPE_RGB && colorType != COLOR_TYPE_INDEXED && colorType != COLOR_TYPE_GRAY_ALPHA && colorType != COLOR_TYPE_RGB_ALPHA) { throw new UnsupportedTypeException("Not a valid color type: " + colorType); } if (precision != 1 && precision != 2 && precision != 4 && precision != 8 && precision != 16) { throw new UnsupportedTypeException("Invalid precision value: " + precision); } if (colorType == COLOR_TYPE_INDEXED && precision > 8) { throw new UnsupportedTypeException("More than eight bits of precision are not allowed for indexed images."); } if (colorType == COLOR_TYPE_RGB && precision < 8) { throw new UnsupportedTypeException("Less than eight bits of precision are not allowed for RGB images."); } alpha = (colorType & COLOR_TYPE_ALPHA) != 0; if (colorType == COLOR_TYPE_RGB || colorType == COLOR_TYPE_RGB_ALPHA) { numChannels = 3; } else { numChannels = 1; } bpp = computeBytesPerRow(1); } /** * Computes a number of bytes for a given number of pixels, * regarding precision and availability of an alpha channel. * @param numPixels the number of pixels for which the number * of bytes necessary to store them is to be computed * @return number of bytes */ private int computeBytesPerRow(int numPixels) { if (precision < 8) { return (numPixels + ((8 / precision) - 1)) / (8 / precision); } else { return (numChannels + (alpha ? 1 : 0)) * (precision / 8) * numPixels; } } private int computeColumnsAdam7(int pass) { switch(pass) { case(0): return (width + 7) / 8; case(1): return (width + 3) / 8; case(2): return (width + 3) / 4; case(3): return (width + 1) / 4; case(4): return (width + 1) / 2; case(5): return width / 2; case(6): return width; default: throw new IllegalArgumentException("Not a valid pass index: " + pass); } } private void fillRowBuffer(int y, byte[] row, int offs) { PixelImage image = getImage(); int x1 = getBoundsX1(); int w = getBoundsWidth(); if (image instanceof BilevelImage) { BilevelImage bilevelImage = (BilevelImage)image; bilevelImage.getPackedBytes(x1, y, w, row, offs, 0); } else if (image instanceof Gray16Image) { Gray16Image grayImage = (Gray16Image)image; while (w-- > 0) { short sample = grayImage.getShortSample(x1++, y); ArrayConverter.setShortBE(row, offs, sample); offs += 2; } } else if (image instanceof Gray8Image) { Gray8Image grayImage = (Gray8Image)image; grayImage.getByteSamples(0, getBoundsX1(), y, getBoundsWidth(), 1, row, offs); } else if (image instanceof Paletted8Image) { Paletted8Image palImage = (Paletted8Image)image; palImage.getByteSamples(0, getBoundsX1(), y, getBoundsWidth(), 1, row, offs); } else if (image instanceof RGB24Image) { RGB24Image rgbImage = (RGB24Image)image; while (w-- > 0) { row[offs++] = rgbImage.getByteSample(RGBIndex.INDEX_RED, x1, y); row[offs++] = rgbImage.getByteSample(RGBIndex.INDEX_GREEN, x1, y); row[offs++] = rgbImage.getByteSample(RGBIndex.INDEX_BLUE, x1, y); x1++; } } else if (image instanceof RGB48Image) { RGB48Image rgbImage = (RGB48Image)image; while (w-- > 0) { short sample = rgbImage.getShortSample(RGBIndex.INDEX_RED, x1, y); ArrayConverter.setShortBE(row, offs, sample); offs += 2; sample = rgbImage.getShortSample(RGBIndex.INDEX_GREEN, x1, y); ArrayConverter.setShortBE(row, offs, sample); offs += 2; sample = rgbImage.getShortSample(RGBIndex.INDEX_BLUE, x1, y); ArrayConverter.setShortBE(row, offs, sample); offs += 2; x1++; } } } /** * Creates a four-letter String from the parameter, an int * value, supposed to be storing a chunk name. * @return the chunk name */ private static String getChunkName(int chunk) { StringBuffer result = new StringBuffer(4); for (int i = 24; i >= 0; i -= 8) { result.append((char)((chunk >> i) & 0xff)); } return result.toString(); } public String getFormatName() { return "Portable Network Graphics (PNG)"; } public String[] getMimeTypes() { return new String[] {"image/png"}; } private static int getPaeth(byte l, byte u, byte nw) { int a = l & 0xff; int b = u & 0xff; int c = nw & 0xff; int p = a + b - c; int pa = p - a; if (pa < 0) { pa = -pa; } int pb = p - b; if (pb < 0) { pb = -pb; } int pc = p - c; if (pc < 0) { pc = -pc; } if (pa <= pb && pa <= pc) { return a; } if (pb <= pc) { return b; } return c; } private void inflateBytes(byte[] buffer, int numBytes) throws InvalidFileStructureException, IOException { int offset = 0; do { try { int toRead = numBytes - offset; int numRead = infl.read(buffer, offset, toRead); if (numRead < 0) { throw new InvalidFileStructureException("Cannot fill buffer"); } offset += numRead; } catch (IOException ioe) { throw new InvalidFileStructureException("Stopped decompressing " + ioe.toString()); } } while (offset != numBytes); } public boolean isLoadingSupported() { return true; } public boolean isSavingSupported() { return true; } private void load() throws InvalidFileStructureException, IOException, UnsupportedTypeException, WrongFileFormatException { byte[] magic = new byte[MAGIC_BYTES.length]; in.readFully(magic); for (int i = 0; i < MAGIC_BYTES.length; i++) { if (magic[i] != MAGIC_BYTES[i]) { throw new WrongFileFormatException("Not a valid PNG input " + "stream, wrong magic byte sequence."); } } chunkCounter = 0; do { loadChunk(); chunkCounter++; } while (image == null); close(); setImage(image); } private void loadChunk() throws InvalidFileStructureException, IOException, UnsupportedTypeException { /* * read chunk size; according to the PNG specs, the size value must not be larger * than 2^31 - 1; to be safe, we treat the value as an unsigned * 32 bit value anyway */ long chunkSize = in.readInt() & 0xffffffffL; checksum.reset(); int chunkName = in.readInt(); // first chunk must be IHDR if (chunkCounter == 0 && chunkName != CHUNK_TYPE_IHDR) { throw new InvalidFileStructureException("First chunk was not IHDR but " + getChunkName(chunkName)); } switch (chunkName) { // image data chunk case(CHUNK_TYPE_IDAT): { loadImage(chunkSize); break; } // end of image chunk case(CHUNK_TYPE_IEND): { throw new InvalidFileStructureException("Reached IEND chunk but could not load image."); } case(CHUNK_TYPE_IHDR): { if (hasIhdr) { throw new InvalidFileStructureException("More than one IHDR chunk found."); } if (chunkCounter != 0) { throw new InvalidFileStructureException("IHDR chunk must be first; found to be chunk #" + (chunkCounter + 1)); } if (chunkSize != CHUNK_SIZE_IHDR) { throw new InvalidFileStructureException("Expected PNG " + "IHDR chunk length to be " + CHUNK_SIZE_IHDR + ", got " + chunkSize + "."); } hasIhdr = true; loadImageHeader(); break; } case(CHUNK_TYPE_PHYS): { if (chunkSize == 9) { byte[] phys = new byte[9]; in.readFully(phys); int x = ArrayConverter.getIntBE(phys, 0); int y = ArrayConverter.getIntBE(phys, 4); if (phys[8] == 1) { // unit is meters final double INCHES_PER_METER = 100 / 2.54; setDpi((int)(x / INCHES_PER_METER), (int)(y / INCHES_PER_METER)); } } else { skip(chunkSize); } break; } case(CHUNK_TYPE_PLTE): { if ((chunkSize % 3) != 0) { throw new InvalidFileStructureException("Not a valid palette chunk size: " + chunkSize); } loadPalette(chunkSize / 3); break; } case(CHUNK_TYPE_TEXT): { if (chunkSize == 0) { } else if (chunkSize > MAX_TEXT_SIZE) { skip(chunkSize); } else { StringBuffer text = new StringBuffer((int)chunkSize); int i = 0; char c; do { c = (char)in.read(); if (c == 0) { skip(chunkSize - i - 1); break; } text.append(c); i++; } while (i < chunkSize); //System.out.println("text=\"" + text.toString() + "\""); } break; } default: { skip(chunkSize); } } int createdChecksum = (int)checksum.getValue(); if (image == null) { // this code doesn't work anymore if we have just read an image int chunkChecksum = in.readInt(); if (createdChecksum != chunkChecksum) { throw new InvalidFileStructureException("Checksum created on chunk " + getChunkName(chunkName) + " " + Integer.toHexString(createdChecksum) + " is not equal to checksum read from stream " + Integer.toHexString(chunkChecksum) + "; file is corrupted."); } } } /** * Load an image from the current position in the file. * Assumes the last things read from input are an IDAT chunk type and * its size, which is the sole argument of this method. * @param chunkSize size of the IDAT chunk that was just read * @throws InvalidFileStructureException if there are values in the PNG stream that make it invalid * @throws IOException if there were I/O errors when reading * @throws UnsupportedTypeException if something was encountered in the stream that is valid but not supported by this codec */ private void loadImage(long chunkSize) throws InvalidFileStructureException, IOException, UnsupportedTypeException { // allocate two byte buffers for current and previous row buffers = new byte[2][]; int numBytes = computeBytesPerRow(width); currentBufferIndex = 0; previousBufferIndex = 1; buffers[currentBufferIndex] = new byte[numBytes]; buffers[previousBufferIndex] = new byte[numBytes]; for (int i = 0; i < buffers[previousBufferIndex].length; i++) { buffers[previousBufferIndex][i] = (byte)0; } // allocate the correct type of image object for the image type read in the IHDR chunk allocateImage(); // create a PngIdatInputStream which will skip header information when // multiple IDAT chunks are in the input stream infl = new InflaterInputStream(new PngIdatInputStream(in, chunkSize)); switch(interlaceType) { case(INTERLACING_NONE): { loadImageNonInterlaced(); break; } case(INTERLACING_ADAM7): { loadImageInterlacedAdam7(); break; } } } /** * Reads data from an IHDR chunk and initializes private fields with it. * Does a lot of checking if read values are valid and supported by this class. * @throws IOException * @throws InvalidFileStructureException * @throws UnsupportedTypeException */ private void loadImageHeader() throws IOException, InvalidFileStructureException, UnsupportedTypeException { // WIDTH -- horizontal resolution width = in.readInt(); if (width < 1) { throw new InvalidFileStructureException("Width must be larger than 0; got " + width); } // HEIGHT -- vertical resolution height = in.readInt(); if (height < 1) { throw new InvalidFileStructureException("Height must be larger than 0; got " + height); } // PRECISION -- bits per sample precision = in.read(); // COLOR TYPE -- indexed, paletted, grayscale, optionally alpha colorType = in.read(); // check for invalid combinations of color type and precision // and initialize alpha and numChannels checkColorTypeAndPrecision(); // COMPRESSION TYPE -- only Deflate is defined compressionType = in.read(); if (compressionType != COMPRESSION_DEFLATE) { throw new UnsupportedTypeException("Unsupported compression type: " + compressionType + "."); } // FILTER TYPE -- only Adaptive is defined filterType = in.read(); if (filterType != FILTERING_ADAPTIVE) { throw new UnsupportedTypeException("Only 'adaptive filtering' is supported right now; got " + filterType); } // INTERLACE TYPE -- order of storage of image data interlaceType = in.read(); if (interlaceType != INTERLACING_NONE && interlaceType != INTERLACING_ADAM7) { throw new UnsupportedTypeException("Only 'no interlacing' and 'Adam7 interlacing' are supported; got " + interlaceType); } } private void loadImageInterlacedAdam7() throws InvalidFileStructureException, IOException, UnsupportedTypeException { final int TOTAL_LINES = ADAM7_NUM_PASSES * height; for (int pass = 0; pass < ADAM7_NUM_PASSES; pass++) { currentBufferIndex = 0; previousBufferIndex = 1; byte[] previousBuffer = buffers[previousBufferIndex]; for (int x = 0; x < previousBuffer.length; x++) { previousBuffer[x] = 0; } int y = ADAM7_FIRST_ROW[pass]; int destY = y - getBoundsY1(); int numColumns = computeColumnsAdam7(pass); if (numColumns == 0) { // this pass contains no data; skip to next pass setProgress((pass + 1) * height, TOTAL_LINES); continue; } int numBytes = computeBytesPerRow(numColumns); while (y < height) { previousBuffer = buffers[previousBufferIndex]; byte[] currentBuffer = buffers[currentBufferIndex]; int rowFilterType = readFilterType(); inflateBytes(currentBuffer, numBytes); reverseFilter(rowFilterType, currentBuffer, previousBuffer, numBytes); if (isRowRequired(y)) { storeInterlacedAdam7(pass, destY, currentBuffer); } int progressY = y; if (pass > 0) { progressY += pass * height; } setProgress(progressY, TOTAL_LINES); y += ADAM7_ROW_INCREMENT[pass]; destY += ADAM7_ROW_INCREMENT[pass]; currentBufferIndex = 1 - currentBufferIndex; previousBufferIndex = 1 - previousBufferIndex; } } } private void loadImageNonInterlaced() throws InvalidFileStructureException, IOException, UnsupportedTypeException { int linesToRead = getBoundsY2() + 1; int rowLength = computeBytesPerRow(width); for (int y = 0, destY = - getBoundsY1(); y <= getBoundsY2(); y++, destY++) { byte[] currentBuffer = buffers[currentBufferIndex]; byte[] previousBuffer = buffers[previousBufferIndex]; int rowFilterType = readFilterType(); inflateBytes(currentBuffer, rowLength); reverseFilter(rowFilterType, currentBuffer, previousBuffer, rowLength); if (isRowRequired(y)) { storeNonInterlaced(destY, currentBuffer); } setProgress(y, linesToRead); previousBufferIndex = 1 - previousBufferIndex; currentBufferIndex = 1 - currentBufferIndex; } } private void loadPalette(long numEntries) throws InvalidFileStructureException, IOException { if (palette != null) { throw new InvalidFileStructureException("More than one palette in input stream."); } if (numEntries < 1) { throw new InvalidFileStructureException("Number of palette entries must be at least 1."); } if (numEntries > 256) { throw new InvalidFileStructureException("Number of palette entries larger than 256: " + numEntries); } palette = new Palette((int)numEntries); int index = 0; do { palette.putSample(Palette.INDEX_RED, index, in.read() & 0xff); palette.putSample(Palette.INDEX_GREEN, index, in.read() & 0xff); palette.putSample(Palette.INDEX_BLUE, index, in.read() & 0xff); index++; } while (index != numEntries); } public static void main(String[] args) throws Exception { PNGCodec codec = new PNGCodec(); codec.setFile(args[0], CodecMode.LOAD); codec.process(); codec.close(); PixelImage image = codec.getImage(); codec = new PNGCodec(); codec.setFile(args[1], CodecMode.SAVE); codec.setImage(image); codec.setDpi(300, 300); codec.appendComment("Test comment #1."); codec.appendComment("And test comment #2."); codec.setModification(new GregorianCalendar(new SimpleTimeZone(0, "UTC"))); codec.process(); codec.close(); } public void process() throws InvalidFileStructureException, MissingParameterException, OperationFailedException, UnsupportedTypeException, WrongFileFormatException { initModeFromIOObjects(); if (getMode() == CodecMode.LOAD) { try { if (getImageIndex() != 0) { throw new InvalidImageIndexException("PNG streams can only store one image; " + "index " + getImageIndex() + " is thus not valid."); } InputStream input = getInputStream(); if (input == null) { throw new MissingParameterException("InputStream object missing."); } checksum = new CRC32(); checkedIn = new CheckedInputStream(input, checksum); in = new DataInputStream(checkedIn); load(); } catch (IOException ioe) { throw new OperationFailedException("I/O failure: " + ioe.toString()); } } else if (getMode() == CodecMode.SAVE) { try { PixelImage image = getImage(); if (image == null) { throw new MissingParameterException("Need image for saving."); } out = getOutputAsDataOutput(); if (out == null) { throw new MissingParameterException("Could not retrieve non-null DataOutput object for saving."); } setBoundsIfNecessary(image.getWidth(), image.getHeight()); save(); } catch (IOException ioe) { throw new OperationFailedException("I/O failure: " + ioe.toString()); } } else { throw new OperationFailedException("Unknown codec mode: " + getMode()); } } private int readFilterType() throws InvalidFileStructureException, IOException { int filterType = infl.read(); if (filterType >= 0 && filterType <= 4) { return filterType; } else { throw new InvalidFileStructureException("Valid filter types are from 0 to 4; got " + filterType); } } private void reverseFilter(int rowFilterType, byte[] buffer, byte[] prev, int numBytes) throws UnsupportedTypeException { switch(rowFilterType) { case(FILTER_TYPE_NONE): { break; } case(FILTER_TYPE_SUB): { for (int x = 0, px = -bpp; x < numBytes; x++, px++) { byte currXMinusBpp; if (px < 0) { currXMinusBpp = 0; } else { currXMinusBpp = buffer[px]; } buffer[x] = (byte)(buffer[x] + currXMinusBpp); } break; } case(FILTER_TYPE_UP): { for (int x = 0; x < numBytes; x++) { buffer[x] = (byte)(buffer[x] + prev[x]); } break; } case(FILTER_TYPE_AVERAGE): { for (int x = 0, px = -bpp; x < numBytes; x++, px++) { int currX = buffer[x] & 0xff; int currXMinus1; if (px < 0) { currXMinus1 = 0; } else { currXMinus1 = buffer[px] & 0xff; } int prevX = prev[x] & 0xff; int result = currX + ((currXMinus1 + prevX) / 2); byte byteResult = (byte)result; buffer[x] = byteResult; } break; } case(FILTER_TYPE_PAETH): { for (int x = 0, px = -bpp; x < numBytes; x++, px++) { byte currXMinusBpp; byte prevXMinusBpp; if (px < 0) { currXMinusBpp = 0; prevXMinusBpp = 0; } else { currXMinusBpp = buffer[px]; prevXMinusBpp = prev[px]; } buffer[x] = (byte)(buffer[x] + getPaeth(currXMinusBpp, prev[x], prevXMinusBpp)); } break; } default: { throw new UnsupportedTypeException("Unknown filter type: " + rowFilterType); } } } private void save() throws IOException { // write 8 byte PNG signature out.write(MAGIC_BYTES); // write IHDR (image header) chunk saveIhdrChunk(); // write pHYs chunk (physical resolution) if data is available savePhysChunk(); // write tEXt chunks if comments are available saveTextChunks(); // write tIME chunk if modification time was set saveTimeChunk(); // write PLTE chunk if necessary savePlteChunk(); // write IDAT chunk saveImage(); // write IEND chunk saveIendChunk(); close(); } private void saveChunk(int chunkType, int chunkSize, byte[] data) throws IOException { // set up array with chunk size and type byte[] intArray = new byte[8]; ArrayConverter.setIntBE(intArray, 0, chunkSize); ArrayConverter.setIntBE(intArray, 4, chunkType); // write chunk size, type and data out.write(intArray, 0, 8); out.write(data, 0, chunkSize); // create checksum on type and data CRC32 checksum = new CRC32(); checksum.reset(); checksum.update(intArray, 4, 4); checksum.update(data, 0, chunkSize); // put checksum into byte array ArrayConverter.setIntBE(intArray, 0, (int)checksum.getValue()); // and write it to output out.write(intArray, 0, 4); } private void saveIendChunk() throws IOException { out.writeInt(0); out.writeInt(CHUNK_TYPE_IEND); out.writeInt(CHUNK_CRC32_IEND); } private void saveIhdrChunk() throws IOException { byte[] buffer = new byte[CHUNK_SIZE_IHDR]; width = getBoundsWidth(); ArrayConverter.setIntBE(buffer, 0, width); height = getBoundsHeight(); ArrayConverter.setIntBE(buffer, 4, height); PixelImage image = getImage(); alpha = false; numChannels = 1; if (image instanceof BilevelImage) { precision = 1; colorType = COLOR_TYPE_GRAY; } else if (image instanceof Gray16Image) { precision = 16; colorType = COLOR_TYPE_GRAY; } else if (image instanceof Gray8Image) { precision = 8; colorType = COLOR_TYPE_GRAY; } else if (image instanceof Paletted8Image) { precision = 8; colorType = COLOR_TYPE_INDEXED; } else if (image instanceof RGB24Image) { numChannels = 3; precision = 8; colorType = COLOR_TYPE_RGB; } else if (image instanceof RGB48Image) { numChannels = 3; precision = 16; colorType = COLOR_TYPE_RGB; } buffer[8] = (byte)precision; buffer[9] = (byte)colorType; compressionType = COMPRESSION_DEFLATE; buffer[10] = (byte)compressionType; filterType = FILTERING_ADAPTIVE; buffer[11] = (byte)filterType; interlaceType = INTERLACING_NONE; buffer[12] = (byte)interlaceType; saveChunk(CHUNK_TYPE_IHDR, CHUNK_SIZE_IHDR, buffer); } private void saveImage() throws IOException { switch(interlaceType) { case(INTERLACING_NONE): { saveImageNonInterlaced(); break; } } } private void saveImageNonInterlaced() throws IOException { int bytesPerRow = computeBytesPerRow(getBoundsWidth()); byte[] rowBuffer = new byte[bytesPerRow + 1]; byte[] outBuffer = new byte[Math.max(encodingMinIdatSize, bytesPerRow + 1)]; int outOffset = 0; int numDeflated; Deflater defl = new Deflater(deflateLevel); defl.setStrategy(deflateStrategy); for (int y = getBoundsY1(); y <= getBoundsY2(); y++) { // fill row buffer rowBuffer[0] = (byte)FILTER_TYPE_NONE; fillRowBuffer(y, rowBuffer, 1); // give it to compressor defl.setInput(rowBuffer); // store compressed data in outBuffer do { numDeflated = defl.deflate(outBuffer, outOffset, outBuffer.length - outOffset); outOffset += numDeflated; if (outOffset == outBuffer.length) { saveChunk(CHUNK_TYPE_IDAT, outOffset, outBuffer); outOffset = 0; } } while (numDeflated > 0); setProgress(y - getBoundsY1(), getBoundsHeight()); } // tell Deflater that it got all the input defl.finish(); // retrieve remaining compressed data from defl to outBuffer do { numDeflated = defl.deflate(outBuffer, outOffset, outBuffer.length - outOffset); outOffset += numDeflated; if (outOffset == outBuffer.length) { saveChunk(CHUNK_TYPE_IDAT, outOffset, outBuffer); outOffset = 0; } } while (numDeflated > 0); // write final IDAT chunk if necessary if (outOffset > 0) { saveChunk(CHUNK_TYPE_IDAT, outOffset, outBuffer); } } private void savePhysChunk() throws IOException { int dpiX = getDpiX(); int dpiY = getDpiY(); if (dpiX < 1 || dpiY < 1) { return; } byte[] data = new byte[9]; int ppuX = (int)(dpiX * (100 / 2.54)); int ppuY = (int)(dpiY * (100 / 2.54)); ArrayConverter.setIntBE(data, 0, ppuX); ArrayConverter.setIntBE(data, 4, ppuY); data[8] = 1; // unit is the meter saveChunk(CHUNK_TYPE_PHYS, data.length, data); } private void savePlteChunk() throws IOException { if (colorType != COLOR_TYPE_INDEXED) { return; } Paletted8Image image = (Paletted8Image)getImage(); Palette pal = image.getPalette(); int numEntries = pal.getNumEntries(); byte[] data = new byte[numEntries * 3]; for (int i = 0, j = 0; i < numEntries; i++, j += 3) { data[j] = (byte)pal.getSample(RGBIndex.INDEX_RED, i); data[j + 1] = (byte)pal.getSample(RGBIndex.INDEX_GREEN, i); data[j + 2] = (byte)pal.getSample(RGBIndex.INDEX_BLUE, i); } saveChunk(CHUNK_TYPE_PLTE, data.length, data); } private void saveTextChunks() throws IOException { int index = 0; while (index < getNumComments()) { String comment = getComment(index++); comment = "Comment\000" + comment; byte[] data = comment.getBytes("ISO-8859-1"); saveChunk(CHUNK_TYPE_TEXT, data.length, data); } } private void saveTimeChunk() throws IOException { if (modification == null) { return; } byte[] data = new byte[7]; ArrayConverter.setShortBE(data, 0, (short)modification.get(Calendar.YEAR)); data[2] = (byte)(modification.get(Calendar.MONTH) + 1); data[3] = (byte)modification.get(Calendar.DAY_OF_MONTH); data[4] = (byte)modification.get(Calendar.HOUR_OF_DAY); data[5] = (byte)modification.get(Calendar.MINUTE); data[6] = (byte)modification.get(Calendar.SECOND); saveChunk(CHUNK_TYPE_TIME, data.length, data); } /** * Sets the compression level to be used with the underlying * {@link java.util.zip.Deflater} object which does the compression. * If no value is specified, {@link java.util.zip.Deflater#DEFAULT_COMPRESSION} * is used. * @param newLevel compression level, from 0 to 9, 0 being fastest * and compressing worst and 9 offering highest compression and taking * the most time */ public void setCompressionLevel(int newLevel) { if (newLevel >= 0 && newLevel <= 9) { deflateLevel = newLevel; } else { throw new IllegalArgumentException("Compression level must be from 0..9; got " + newLevel); } } /** * Sets the compression strategy to be used with the underlying * {@link java.util.zip.Deflater} object which does the compression. * If no value is specified, {@link java.util.zip.Deflater#DEFAULT_STRATEGY} * is used. * @param newStrategy one of Deflater's strategy values: * {@link java.util.zip.Deflater#DEFAULT_STRATEGY}, * {@link java.util.zip.Deflater#FILTERED}, * {@link java.util.zip.Deflater#HUFFMAN_ONLY} */ public void setCompressionStrategy(int newStrategy) { if (newStrategy == Deflater.FILTERED || newStrategy == Deflater.DEFAULT_STRATEGY || newStrategy == Deflater.HUFFMAN_ONLY) { deflateStrategy = newStrategy; } else { throw new IllegalArgumentException("Unknown compression strategy: " + newStrategy); } } /** * Sets the size of IDAT chunks generated when encoding. * If this method is never called, a default value of 32768 bytes (32 KB) is used. * Note that a byte array of the size of the value you specify here is allocated, * so make sure that you keep the value small enough to stay within a * system's memory. *

* Compressed image data is spread over several IDAT chunks by this codec. * The length of the compressed data of a complete image is known only after the complete image * has been encoded. * With PNG, that length value has to be stored before the compressed data as a chunk size value. * This codec is supposed to work with {@link java.io.OutputStream} objects, * so seeking back to adjust the chunk size value of an IDAT chunk is not * possible. * That's why all data of a chunk is compressed into a memory buffer. * Whenever the buffer gets full, it is written to output as an IDAT chunk. *

* Note that the last IDAT chunk may be smaller than the size defined here. * @param newSize size of encoding compressed data buffer */ public void setEncodingIdatSize(int newSize) { if (newSize < 1) { throw new IllegalArgumentException("Minimum IDAT chunk size must be 1 or larger."); } encodingMinIdatSize = newSize; } public void setFile(String fileName, CodecMode codecMode) throws IOException, UnsupportedCodecModeException { if (codecMode == CodecMode.LOAD) { setInputStream(new BufferedInputStream(new FileInputStream(fileName))); } else { super.setFile(fileName, codecMode); } } /** * Sets date and time of last modification of the image to be stored in a PNG stream * when saving. * Make sure the argument object has UTC as time zone * (as * demanded by the PNG specs). * If you want the current time and date, use * new GregorianCalendar(new SimpleTimeZone(0, "UTC")) * as parameter for this method. * @param time time of last modification of the image */ public void setModification(Calendar time) { modification = time; } /** * Skips a number of bytes in the input stream. * @param num number of bytes to be skipped * @throws IOException if there were I/O errors */ private void skip(long num) throws IOException { while (num > 0) { long numSkipped = in.skip(num); if (numSkipped > 0) { num -= numSkipped; } } } private void storeInterlacedAdam7(int pass, int y, byte[] buffer) { switch(colorType) { case(COLOR_TYPE_GRAY): { storeInterlacedAdam7Gray(pass, y, buffer); break; } case(COLOR_TYPE_RGB): { storeInterlacedAdam7Rgb(pass, y, buffer); break; } case(COLOR_TYPE_RGB_ALPHA): { storeInterlacedAdam7RgbAlpha(pass, y, buffer); break; } case(COLOR_TYPE_GRAY_ALPHA): { storeInterlacedAdam7GrayAlpha(pass, y, buffer); break; } case(COLOR_TYPE_INDEXED): { storeInterlacedAdam7Indexed(pass, y, buffer); break; } } } private void storeInterlacedAdam7Gray(int pass, int y, byte[] buffer) { int x = ADAM7_FIRST_COLUMN[pass]; final int incr = ADAM7_COLUMN_INCREMENT[pass]; final int x1 = getBoundsX1(); final int x2 = getBoundsX2(); int offset = 0; int numColumns = computeColumnsAdam7(pass); int numPackedBytes = computeBytesPerRow(numColumns); byte[] dest = new byte[numColumns + 7]; switch(precision) { case(1): { BilevelImage bilevelImage = (BilevelImage)image; ArrayConverter.decodePacked1Bit(buffer, 0, dest, 0, numPackedBytes); while (x <= x2) { if (x >= x1) { if (dest[offset] == 0) { bilevelImage.putBlack(x - x1, y); } else { bilevelImage.putWhite(x - x1, y); } } x += incr; offset++; } break; } case(2): { Gray8Image grayImage = (Gray8Image)image; ArrayConverter.convertPacked2BitIntensityTo8Bit(buffer, 0, dest, 0, numPackedBytes); while (x <= x2) { if (x >= x1) { grayImage.putByteSample(x - x1, y, dest[offset]); } x += incr; offset++; } break; } case(4): { Gray8Image grayImage = (Gray8Image)image; ArrayConverter.convertPacked4BitIntensityTo8Bit(buffer, 0, dest, 0, numPackedBytes); while (x <= x2) { if (x >= x1) { grayImage.putByteSample(x - x1, y, dest[offset]); } x += incr; offset++; } break; } case(8): { Gray8Image grayImage = (Gray8Image)image; while (x <= x2) { if (x >= x1) { grayImage.putSample(x - x1, y, buffer[offset]); } x += incr; offset++; } break; } case(16): { Gray16Image grayImage = (Gray16Image)image; while (x <= x2) { if (x >= x1) { int sample = (buffer[offset] & 0xff) << 8; sample |= (buffer[offset + 1] & 0xff); grayImage.putSample(x, y, sample); } x += incr; offset += 2; } break; } } } private void storeInterlacedAdam7GrayAlpha(int pass, int y, byte[] buffer) { int x = ADAM7_FIRST_COLUMN[pass]; final int incr = ADAM7_COLUMN_INCREMENT[pass]; final int x1 = getBoundsX1(); final int x2 = getBoundsX2(); int offset = 0; switch(precision) { case(8): { Gray8Image grayImage = (Gray8Image)image; while (x <= x2) { if (x >= x1) { grayImage.putSample(x - x1, y, buffer[offset]); // alpha } x += incr; offset += 2; } break; } case(16): { Gray16Image grayImage = (Gray16Image)image; while (x <= x2) { if (x >= x1) { int sample = (buffer[offset] & 0xff) << 8; sample |= (buffer[offset + 1] & 0xff); grayImage.putSample(x, y, sample); // store alpha } x += incr; offset += 4; } break; } } } private void storeInterlacedAdam7Indexed(int pass, int y, byte[] buffer) { Paletted8Image palImage = (Paletted8Image)image; int x = ADAM7_FIRST_COLUMN[pass]; final int incr = ADAM7_COLUMN_INCREMENT[pass]; final int x1 = getBoundsX1(); final int x2 = getBoundsX2(); int offset = 0; int numColumns = computeColumnsAdam7(pass); int numPackedBytes = computeBytesPerRow(numColumns); byte[] dest = new byte[numColumns + 7]; switch(precision) { case(1): { ArrayConverter.decodePacked1Bit(buffer, 0, dest, 0, numPackedBytes); while (x <= x2) { if (x >= x1) { palImage.putByteSample(x - x1, y, dest[offset]); } x += incr; offset++; } break; } case(2): { ArrayConverter.decodePacked2Bit(buffer, 0, dest, 0, numPackedBytes); while (x <= x2) { if (x >= x1) { palImage.putByteSample(x - x1, y, dest[offset]); } x += incr; offset++; } break; } case(4): { ArrayConverter.decodePacked4Bit(buffer, 0, dest, 0, numPackedBytes); while (x <= x2) { if (x >= x1) { palImage.putByteSample(x - x1, y, dest[offset]); } x += incr; offset++; } break; } case(8): { while (x <= x2) { if (x >= x1) { palImage.putSample(x - x1, y, buffer[offset]); } x += incr; offset++; } break; } } } private void storeInterlacedAdam7Rgb(int pass, int y, byte[] buffer) { int x = ADAM7_FIRST_COLUMN[pass]; final int x1 = getBoundsX1(); final int x2 = getBoundsX2(); final int incr = ADAM7_COLUMN_INCREMENT[pass]; int offset = 0; if (precision == 8) { RGB24Image rgbImage = (RGB24Image)image; while (x <= x2) { if (x >= x1) { rgbImage.putSample(RGB24Image.INDEX_RED, x, y, buffer[offset]); rgbImage.putSample(RGB24Image.INDEX_GREEN, x, y, buffer[offset + 1]); rgbImage.putSample(RGB24Image.INDEX_BLUE, x, y, buffer[offset + 2]); } x += incr; offset += 3; } } else if (precision == 16) { RGB48Image rgbImage = (RGB48Image)image; while (x <= x2) { if (x >= x1) { int red = (buffer[offset] & 0xff) << 8; red |= buffer[offset + 1] & 0xff; rgbImage.putSample(RGB24Image.INDEX_RED, x, y, red); int green = (buffer[offset + 2] & 0xff) << 8; green |= buffer[offset + 3] & 0xff; rgbImage.putSample(RGB24Image.INDEX_GREEN, x, y, green); int blue = (buffer[offset + 4] & 0xff) << 8; blue |= buffer[offset + 5] & 0xff; rgbImage.putSample(RGB24Image.INDEX_BLUE, x, y, blue); } x += incr; offset += 6; } } } private void storeInterlacedAdam7RgbAlpha(int pass, int y, byte[] buffer) { int x = ADAM7_FIRST_COLUMN[pass]; final int x1 = getBoundsX1(); final int x2 = getBoundsX2(); final int incr = ADAM7_COLUMN_INCREMENT[pass]; int offset = 0; if (precision == 8) { RGB24Image rgbImage = (RGB24Image)image; while (x <= x2) { if (x >= x1) { rgbImage.putSample(RGB24Image.INDEX_RED, x, y, buffer[offset]); rgbImage.putSample(RGB24Image.INDEX_GREEN, x, y, buffer[offset + 1]); rgbImage.putSample(RGB24Image.INDEX_BLUE, x, y, buffer[offset + 2]); // store alpha } x += incr; offset += 4; } } else if (precision == 16) { RGB48Image rgbImage = (RGB48Image)image; while (x <= x2) { if (x >= x1) { int red = (buffer[offset] & 0xff) << 8; red |= buffer[offset + 1] & 0xff; rgbImage.putSample(RGB24Image.INDEX_RED, x, y, red); int green = (buffer[offset + 2] & 0xff) << 8; green |= buffer[offset + 3] & 0xff; rgbImage.putSample(RGB24Image.INDEX_GREEN, x, y, green); int blue = (buffer[offset + 4] & 0xff) << 8; blue |= buffer[offset + 5] & 0xff; rgbImage.putSample(RGB24Image.INDEX_BLUE, x, y, blue); // store alpha } x += incr; offset += 8; } } } private void storeNonInterlaced(int y, byte[] buffer) { switch(colorType) { case(COLOR_TYPE_GRAY): { storeNonInterlacedGray(y, buffer); break; } case(COLOR_TYPE_GRAY_ALPHA): { storeNonInterlacedGrayAlpha(y, buffer); break; } case(COLOR_TYPE_INDEXED): { storeNonInterlacedIndexed(y, buffer); break; } case(COLOR_TYPE_RGB): { storeNonInterlacedRgb(y, buffer); break; } case(COLOR_TYPE_RGB_ALPHA): { storeNonInterlacedRgbAlpha(y, buffer); break; } } } private void storeNonInterlacedGray(int y, byte[] buffer) { switch(precision) { case(1): { BilevelImage bilevelImage = (BilevelImage)image; int x1 = getBoundsX1(); bilevelImage.putPackedBytes(0, y, getBoundsWidth(), buffer, x1 / 8, x1 % 8); break; } case(2): { Gray8Image grayImage = (Gray8Image)image; byte[] dest = new byte[width + 3]; ArrayConverter.convertPacked2BitIntensityTo8Bit(buffer, 0, dest, 0, buffer.length); grayImage.putByteSamples(0, 0, y, getBoundsWidth(), 1, dest, getBoundsX1()); break; } case(4): { Gray8Image grayImage = (Gray8Image)image; byte[] dest = new byte[width + 1]; ArrayConverter.convertPacked4BitIntensityTo8Bit(buffer, 0, dest, 0, buffer.length); grayImage.putByteSamples(0, 0, y, getBoundsWidth(), 1, dest, getBoundsX1()); break; } case(8): { Gray8Image grayImage = (Gray8Image)image; int offset = getBoundsX1(); int x = 0; int k = getBoundsWidth(); while (k > 0) { grayImage.putSample(0, x++, y, buffer[offset++]); k--; } break; } case(16): { Gray16Image grayImage = (Gray16Image)image; int offset = getBoundsX1(); int x = 0; int k = getBoundsWidth(); while (k > 0) { int sample = (buffer[offset++] & 0xff) << 8; sample |= (buffer[offset++] & 0xff); grayImage.putSample(x++, y, sample); k--; } break; } } } private void storeNonInterlacedGrayAlpha(int y, byte[] buffer) { switch(precision) { case(8): { Gray8Image grayImage = (Gray8Image)image; int offset = getBoundsX1(); int x = 0; int k = getBoundsWidth(); while (k > 0) { grayImage.putSample(0, x++, y, buffer[offset++]); offset++; // skip alpha; should be stored in a TransparencyInformation object k--; } break; } case(16): { Gray16Image grayImage = (Gray16Image)image; int offset = getBoundsX1(); int x = 0; int k = getBoundsWidth(); while (k > 0) { int sample = (buffer[offset++] & 0xff) << 8; sample |= (buffer[offset++] & 0xff); grayImage.putSample(x++, y, sample); offset += 2; // skip alpha; TODO: store in TransparencyInformation object k--; } break; } } } private void storeNonInterlacedIndexed(int y, byte[] buffer) { Paletted8Image palImage = (Paletted8Image)image; switch(precision) { case(1): { byte[] dest = new byte[width + 7]; ArrayConverter.decodePacked1Bit(buffer, 0, dest, 0, buffer.length); palImage.putByteSamples(0, 0, y, getBoundsWidth(), 1, dest, getBoundsX1()); break; } case(2): { byte[] dest = new byte[width + 3]; ArrayConverter.decodePacked2Bit(buffer, 0, dest, 0, buffer.length); palImage.putByteSamples(0, 0, y, getBoundsWidth(), 1, dest, getBoundsX1()); break; } case(4): { byte[] dest = new byte[width + 1]; ArrayConverter.decodePacked4Bit(buffer, 0, dest, 0, buffer.length); palImage.putByteSamples(0, 0, y, getBoundsWidth(), 1, dest, getBoundsX1()); break; } case(8): { int offset = getBoundsX1(); int x = 0; int k = getBoundsWidth(); while (k > 0) { palImage.putSample(0, x++, y, buffer[offset++]); k--; } break; } } } private void storeNonInterlacedRgb(int y, byte[] buffer) { if (precision == 8) { RGB24Image rgbImage = (RGB24Image)image; int offset = getBoundsX1() * 3; int x = 0; int k = getBoundsWidth(); while (k > 0) { rgbImage.putSample(RGB24Image.INDEX_RED, x, y, buffer[offset++]); rgbImage.putSample(RGB24Image.INDEX_GREEN, x, y, buffer[offset++]); rgbImage.putSample(RGB24Image.INDEX_BLUE, x, y, buffer[offset++]); x++; k--; } } else if (precision == 16) { RGB48Image rgbImage = (RGB48Image)image; int offset = getBoundsX1() * 6; int x = 0; int k = getBoundsWidth(); while (k > 0) { int red = (buffer[offset++] & 0xff) << 8; red |= buffer[offset++] & 0xff; rgbImage.putSample(RGB24Image.INDEX_RED, x, y, red); int green = (buffer[offset++] & 0xff) << 8; green |= buffer[offset++] & 0xff; rgbImage.putSample(RGB24Image.INDEX_GREEN, x, y, green); int blue = (buffer[offset++] & 0xff) << 8; blue |= buffer[offset++] & 0xff; rgbImage.putSample(RGB24Image.INDEX_BLUE, x, y, blue); x++; k--; } } } private void storeNonInterlacedRgbAlpha(int y, byte[] buffer) { switch(precision) { case(8): { RGB24Image rgbImage = (RGB24Image)image; int offset = getBoundsX1() * 3; int x = 0; int k = getBoundsWidth(); while (k > 0) { rgbImage.putSample(RGB24Image.INDEX_RED, x, y, buffer[offset++]); rgbImage.putSample(RGB24Image.INDEX_GREEN, x, y, buffer[offset++]); rgbImage.putSample(RGB24Image.INDEX_BLUE, x, y, buffer[offset++]); offset++; // skip alpha; TODO: store in TransparencyInformation object x++; k--; } break; } case(16): { RGB48Image rgbImage = (RGB48Image)image; int offset = getBoundsX1() * 8; int x = 0; int k = getBoundsWidth(); while (k > 0) { int red = (buffer[offset++] & 0xff) << 8; red |= buffer[offset++] & 0xff; rgbImage.putSample(RGB24Image.INDEX_RED, x, y, red); int green = (buffer[offset++] & 0xff) << 8; green |= buffer[offset++] & 0xff; rgbImage.putSample(RGB24Image.INDEX_GREEN, x, y, green); int blue = (buffer[offset++] & 0xff) << 8; blue |= buffer[offset++] & 0xff; rgbImage.putSample(RGB24Image.INDEX_BLUE, x, y, blue); offset += 2; // skip alpha; TODO: store in TransparencyInformation object x++; k--; } break; } } } public String suggestFileExtension(PixelImage image) { return ".png"; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/CodecMode.java0000664000000000000000000000164707741250131023763 0ustar /* * CodecMode * * Copyright (c) 2000, 2001 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; /** * This class is an enumeration type for the two modes that an image codec can be used in, * {@link #LOAD} and {@link #SAVE}. * These values are used as arguments in some of the methods of {@link ImageCodec}. * * @author Marco Schmidt * @since 0.7.0 */ public final class CodecMode { private CodecMode() { } /** * Codec mode load, one of the two possible values of CodecMode. * To be used with a codec to indicate that an image is to be read from a stream. */ public static final CodecMode LOAD = new CodecMode(); /** * Codec mode save, one of the two possible values of CodecMode. * To be used with a codec to indicate that an image is to be written to a stream. */ public static final CodecMode SAVE = new CodecMode(); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/IFFCodec.java0000664000000000000000000005177010377273050023511 0ustar /* * IFFCodec * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.DataInput; import java.io.IOException; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.codecs.WrongFileFormatException; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; /** * A codec to read Amiga IFF image files. * IFF (Interchange File Format) is an Amiga wrapper file format for texts, images, animations, sound and other kinds of data. * This codec only deals with image IFF files. * Typical file extensions for IFF image files are .lbm and .iff. *

Loading / saving

* Only loading is supported by this codec. *

Supported file types

* Both uncompressed and run-length encoded files are read. * *

Usage example

*
 * IFFCodec codec = new IFFCodec();
 * codec.setFile("image.iff", CodecMode.LOAD);
 * codec.process();
 * PixelImage image = codec.getImage();
 * 
* @author Marco Schmidt * @since 0.3.0 */ public class IFFCodec extends ImageCodec { private final static int MAGIC_BMHD = 0x424d4844; private final static int MAGIC_BODY = 0x424f4459; private final static int MAGIC_CMAP = 0x434d4150; private final static int MAGIC_CAMG = 0x43414d47; private final static int MAGIC_FORM = 0x464f524d; private final static int MAGIC_ILBM = 0x494c424d; private final static int MAGIC_PBM = 0x50424d20; private final static int SIZE_BMHD = 0x00000014; private final static byte COMPRESSION_NONE = 0x00; private final static byte COMPRESSION_RLE = 0x01; private int camg; private byte compression; private boolean ehb; private boolean ham; private boolean ham6; private boolean ham8; private int height; private int numPlanes; private Palette palette; private boolean rgb24; private int type; private int width; private void checkAndLoad() throws InvalidFileStructureException, IOException, MissingParameterException, UnsupportedTypeException, WrongFileFormatException, WrongParameterException { DataInput in = getInputAsDataInput(); if (in == null) { throw new MissingParameterException("InputStream / DataInput object is missing."); } int formMagic = in.readInt(); if (formMagic != MAGIC_FORM) { throw new WrongFileFormatException("Cannot load image. The " + "input stream is not a valid IFF file (wrong magic byte " + "sequence)."); } in.readInt(); // read and discard "file size" field type = in.readInt(); if (type != MAGIC_ILBM && type != MAGIC_PBM) { throw new UnsupportedTypeException("Cannot load image. The " + "input stream is an IFF file, but not of type ILBM or PBM" + " (" + getChunkName(type) + ")"); } PixelImage result = null; boolean hasBMHD = false; boolean hasCAMG = false; do { int magic = in.readInt(); //System.out.println(chunkNameToString(magic)); int size = in.readInt(); // chunks must always have an even number of bytes if ((size & 1) == 1) { size++; } //System.out.println("Chunk " + getChunkName(magic) + ", size=" + size); switch(magic) { case(MAGIC_BMHD): // main header with width, height, bit depth { if (hasBMHD) { throw new InvalidFileStructureException("Error in " + "IFF file: more than one BMHD chunk."); } if (size != SIZE_BMHD) { throw new InvalidFileStructureException("Cannot " + "load image. The bitmap header chunk does not " + "have the expected size."); } // image resolution in pixels width = in.readShort(); height = in.readShort(); if (width < 1 || height < 1) { throw new InvalidFileStructureException("Cannot " + "load image. The IFF file's bitmap header " + "contains invalid width and height values: " + width + ", " + height); } // next four bytes don't matter in.skipBytes(4); // color depth, 1..8 or 24 numPlanes = in.readByte(); if ((numPlanes != 24) && (numPlanes < 1 || numPlanes > 8)) { throw new UnsupportedTypeException("Cannot load " + "image, unsupported number of bits per pixel: " + numPlanes); } //System.out.println("\nnum planes=" + numPlanes); in.readByte(); // discard "masking" value // compression type, must be 0 or 1 compression = in.readByte(); if (compression != COMPRESSION_NONE && compression != COMPRESSION_RLE) { throw new UnsupportedTypeException("Cannot load " + "image, unsupported compression type: " + compression); } //System.out.println(getCompressionName(compression)); in.skipBytes(9); hasBMHD = true; break; } case(MAGIC_BODY): { if (!hasBMHD) { // width still has its initialization value -1; no // bitmap chunk was encountered throw new InvalidFileStructureException("Cannot load image. Error in " + "IFF input stream: No bitmap header chunk " + "encountered before image body chunk."); } if (palette == null && (!rgb24)) { // a missing color map is allowed only for truecolor images throw new InvalidFileStructureException("Cannot load image. Error in " + "IFF input stream: No colormap chunk " + "encountered before image body chunk."); } result = loadImage(in); break; } case(MAGIC_CAMG): { if (hasCAMG) { throw new InvalidFileStructureException("Cannot load image. Error in " + "IFF input stream: More than one CAMG chunk."); } hasCAMG = true; if (size < 4) { throw new InvalidFileStructureException("Cannot load" + " image. CAMG must be at least four bytes large; " + "found: " + size); } camg = in.readInt(); ham = (camg & 0x800) != 0; ehb = (camg & 0x80) != 0; //System.out.println("ham=" + ham); in.skipBytes(size - 4); break; } case(MAGIC_CMAP): // palette (color map) { if (palette != null) { throw new InvalidFileStructureException("Cannot " + "load image. Error in IFF " + "input stream: More than one palette."); } if (size < 3 || (size % 3) != 0) { throw new InvalidFileStructureException("Cannot " + "load image. The size of the colormap is " + "invalid: " + size); } int numColors = size / 3; palette = new Palette(numColors, 255); for (int i = 0; i < numColors; i++) { palette.putSample(Palette.INDEX_RED, i, in.readByte() & 0xff); palette.putSample(Palette.INDEX_GREEN, i, in.readByte() & 0xff); palette.putSample(Palette.INDEX_BLUE, i, in.readByte() & 0xff); } break; } default: { if (in.skipBytes(size) != size) { throw new IOException("Error skipping " + size + " bytes of input stream."); } break; } } } while(result == null); setImage(result); } /** * Converts input planes to index or truecolor output values. * Exact interpretation depends on the type of ILBM image storage: * * @param sourcePlanes * @param dest */ private void convertRow(byte[][] sourcePlaneData, byte[][] dest) { int sourceMask = 0x80; int sourceIndex = 0; int lastRed = 0; int lastGreen = 0; int lastBlue = 0; for (int x = 0; x < width; x++) { int destMask = 1; int index = 0; for (int p = 0; p < sourcePlaneData.length; p++) { if ((sourcePlaneData[p][sourceIndex] & sourceMask) != 0) { index |= destMask; } destMask <<= 1; } if ((x & 7) == 7) { sourceIndex++; } if (sourceMask == 0x01) { sourceMask = 0x80; } else { sourceMask >>= 1; } if (ham6) { //System.out.println("enter ham6"); int paletteIndex = index & 0x0f; //System.out.println("palette index=" + paletteIndex); switch((index >> 4) & 0x03) { case(0): // HOLD { lastRed = palette.getSample(Palette.INDEX_RED, paletteIndex); lastGreen = palette.getSample(Palette.INDEX_GREEN, paletteIndex); lastBlue = palette.getSample(Palette.INDEX_BLUE, paletteIndex); break; } case(1): // MODIFY BLUE { lastBlue = (lastBlue & 0x0f) | (paletteIndex << 4); break; } case(2): // MODIFY RED { lastRed = (lastRed & 0x0f) | (paletteIndex << 4); break; } case(3): // MODIFY GREEN { lastGreen = (lastGreen & 0x0f) | (paletteIndex << 4); break; } } dest[0][x] = (byte)lastRed; dest[1][x] = (byte)lastGreen; dest[2][x] = (byte)lastBlue; } else if (ham8) { int paletteIndex = index & 0x3f; //System.out.println("palette index=" + paletteIndex); switch((index >> 6) & 0x03) { case(0): // HOLD { lastRed = palette.getSample(Palette.INDEX_RED, paletteIndex); lastGreen = palette.getSample(Palette.INDEX_GREEN, paletteIndex); lastBlue = palette.getSample(Palette.INDEX_BLUE, paletteIndex); break; } case(1): // MODIFY BLUE { lastBlue = (lastBlue & 0x03) | (paletteIndex << 2); break; } case(2): // MODIFY RED { lastRed = (lastRed & 0x03) | (paletteIndex << 2); break; } case(3): // MODIFY GREEN { lastGreen = (lastGreen & 0x03) | (paletteIndex << 2); break; } } dest[0][x] = (byte)lastRed; dest[1][x] = (byte)lastGreen; dest[2][x] = (byte)lastBlue; } else if (rgb24) { dest[2][x] = (byte)(index >> 16); dest[1][x] = (byte)(index >> 8); dest[0][x] = (byte)index; } else { /* the value is an index into the lookup table */ //destRgbData[destOffset++] = rgbLookup[index]; dest[0][x] = (byte)index; } } } private void createExtraHalfbritePalette() { if (palette == null) { return; } int numPaletteEntries = palette.getNumEntries(); Palette tempPalette = new Palette(numPaletteEntries * 2, 255); for (int i = 0; i < numPaletteEntries; i++) { int red = palette.getSample(Palette.INDEX_RED, i); tempPalette.putSample(Palette.INDEX_RED, numPaletteEntries + i, red); tempPalette.putSample(Palette.INDEX_RED, i, (red / 2) & 0xf0); int green = palette.getSample(Palette.INDEX_GREEN, i); tempPalette.putSample(Palette.INDEX_GREEN, numPaletteEntries + i, red); tempPalette.putSample(Palette.INDEX_GREEN, i, (green / 2) & 0xf0); int blue = palette.getSample(Palette.INDEX_BLUE, i); tempPalette.putSample(Palette.INDEX_BLUE, numPaletteEntries + i, blue); tempPalette.putSample(Palette.INDEX_BLUE, i, (blue / 2) & 0xf0); } palette = tempPalette; } private static String getChunkName(int name) { StringBuffer sb = new StringBuffer(4); sb.setLength(4); sb.setCharAt(0, (char)((name >> 24) & 0xff)); sb.setCharAt(1, (char)((name >> 16) & 0xff)); sb.setCharAt(2, (char)((name >> 8) & 0xff)); sb.setCharAt(3, (char)((name & 0xff))); return new String(sb); } /*private static String getCompressionName(byte method) { switch(method) { case(COMPRESSION_NONE): return "Uncompressed"; case(COMPRESSION_RLE): return "RLE"; default: return "Unknown method (" + (method & 0xff) + ")"; } }*/ public String[] getFileExtensions() { return new String[] {".lbm", ".iff"}; } public String getFormatName() { return "Amiga Interchange File Format (IFF, LBM)"; } public String[] getMimeTypes() { return new String[] {"image/x-iff"}; } public boolean isLoadingSupported() { return true; } public boolean isSavingSupported() { return false; } /** * Loads data.length bytes from the input stream to the data array, * regarding the compression type. * COMPRESSION_NONE will make this method load data.length bytes from * the input stream. * COMPRESSION_RLE will make this method decompress data.length bytes * from input. */ private void loadBytes(DataInput in, byte[] data, int num, int y) throws InvalidFileStructureException, IOException { switch(compression) { case(COMPRESSION_NONE): { in.readFully(data, 0, num); break; } case(COMPRESSION_RLE): { int x = 0; while (x < num) { int n = in.readByte() & 0xff; //System.out.println("value=" + n); boolean compressed = false; int count = -1; try { if (n < 128) { // copy next n + 1 bytes literally n++; in.readFully(data, x, n); x += n; } else { // if n == -128, nothing happens if (n > 128) { compressed = true; // otherwise, compute counter count = 257 - n; // read another byte byte value = in.readByte(); // write this byte counter times to output while (count-- > 0) { data[x++] = value; } } } } catch (ArrayIndexOutOfBoundsException ioobe) { //System.out.println("Loading error"); /* if the encoder did anything wrong, the above code could potentially write beyond array boundaries (e.g. if runs of data exceed line boundaries); this would result in an ArrayIndexOutOfBoundsException thrown by the virtual machine; to give a more understandable error message to the user, this exception is caught here and a explanatory InvalidFileStructureException is thrown */ throw new InvalidFileStructureException("Error: " + "RLE-compressed image " + "file seems to be corrupt (compressed=" + compressed + ", x=" + x + ", y=" + y + ", count=" + (compressed ? (-((int)n) + 1) : n) + ", array length=" + data.length + ")."); } } break; } default: { throw new InvalidFileStructureException("Error loading " + "image; unknown compression type (" + compression + ")"); } } } /** * Loads an image from given input stream in, regarding the compression * type. The image will have 1 to 8 or 24 planes, a resolution given by * the dimension width times height. The color map data will be used to * convert index values to RGB pixels. * Returns the resulting image. * Will throw an IOException if either there were errors reading from the * input stream or if the file does not exactly match the file format. */ private PixelImage loadImage(DataInput in) throws InvalidFileStructureException, IOException, UnsupportedTypeException, WrongParameterException { setBoundsIfNecessary(width, height); checkImageResolution(); if (ham) { if (numPlanes == 6) { ham6 = true; } else if (numPlanes == 8) { ham8 = true; } else { throw new UnsupportedTypeException("Cannot handle " + "IFF ILBM HAM image file with number of planes " + "other than 6 or 8 (got " + numPlanes + ")."); } if (palette == null) { throw new InvalidFileStructureException("Invalid IFF ILBM " + "file: HAM (Hold And Modify) image without a palette."); } int numPaletteEntries = palette.getNumEntries(); if (ham6 && numPaletteEntries < 16) { throw new InvalidFileStructureException("Invalid IFF ILBM " + "file: HAM (Hold And Modify) 6 bit image with a " + "number of palette entries less than 16 (" + numPaletteEntries + ")."); } if (ham8 && numPaletteEntries < 64) { throw new InvalidFileStructureException("Invalid IFF ILBM " + "file: HAM (Hold And Modify) 8 bit image with a " + "number of palette entries less than 64 (" + numPaletteEntries + ")."); } } if (ehb) { createExtraHalfbritePalette(); } int numBytesPerPlane = (width + 7) / 8; PixelImage image = null; Paletted8Image palettedImage = null; RGB24Image rgbImage = null; if (numPlanes == 24 || ham) { rgbImage = new MemoryRGB24Image(getBoundsWidth(), getBoundsHeight()); image = rgbImage; } else { palettedImage = new MemoryPaletted8Image(getBoundsWidth(), getBoundsHeight(), palette); image = palettedImage; } /* only matters for uncompressed files; will be true if the number of bytes is odd; is computed differently for PBM and ILBM types */ boolean oddBytesPerRow = (((numBytesPerPlane * numPlanes) % 2) != 0); if (type == MAGIC_PBM) { oddBytesPerRow = ((width % 2) == 1); } // plane data will have numPlanes planes for ILBM and 1 plane for PBM byte[][] planes = null; int numChannels = 1; if (type == MAGIC_ILBM) { int allocBytes = numBytesPerPlane; if ((numBytesPerPlane % 2) == 1) { allocBytes++; } // allocate numPlanes byte arrays planes = new byte[numPlanes][]; if (rgb24 || ham) { numChannels = 3; } // for each of these byte arrays allocate numBytesPerPlane bytes for (int i = 0; i < numPlanes; i++) { planes[i] = new byte[allocBytes]; } } else { // only one plane, but each plane has width bytes instead of // numBytesPerPlane planes = new byte[1][]; planes[0] = new byte[width]; } byte[][] dest = new byte[numChannels][]; for (int i = 0; i < numChannels; i++) { dest[i] = new byte[width]; } for (int y = 0, destY = 0 - getBoundsY1(); y <= getBoundsY2(); y++, destY++) { // load one row, different approach for PBM and ILBM if (type == MAGIC_ILBM) { // decode all planes for a complete row for (int p = 0; p < numPlanes; p++) { loadBytes(in, planes[p], numBytesPerPlane, y); } } else if (type == MAGIC_PBM) { loadBytes(in, planes[0], numBytesPerPlane, y); } /* all uncompressed rows must have an even number of bytes so in case the number of bytes per row is odd, one byte is read and dropped */ if (compression == COMPRESSION_NONE && oddBytesPerRow) { in.readByte(); } setProgress(y, getBoundsY2() + 1); // if we do not need the row we just loaded we continue loading // the next row if (!isRowRequired(y)) { continue; } //System.out.println("storing row " + y + " as " + destY + ", numPlanes="+ numPlanes + ",type=" + type); // compute offset into pixel data array if (type == MAGIC_ILBM) { convertRow(planes, dest); if (rgb24 || ham) { rgbImage.putByteSamples(RGB24Image.INDEX_RED, 0, destY, getBoundsWidth(), 1, dest[0], getBoundsX1()); rgbImage.putByteSamples(RGB24Image.INDEX_GREEN, 0, destY, getBoundsWidth(), 1, dest[1], getBoundsX1()); rgbImage.putByteSamples(RGB24Image.INDEX_BLUE, 0, destY, getBoundsWidth(), 1, dest[2], getBoundsX1()); } else { palettedImage.putByteSamples(0, 0, destY, getBoundsWidth(), 1, dest[0], getBoundsX1()); } } else if (type == MAGIC_PBM) { palettedImage.putByteSamples(0, 0, destY, getBoundsWidth(), 1, planes[0], getBoundsX1()); } } return image; } public void process() throws InvalidFileStructureException, MissingParameterException, OperationFailedException, UnsupportedTypeException, WrongFileFormatException { initModeFromIOObjects(); if (getMode() == CodecMode.LOAD) { try { checkAndLoad(); } catch (IOException ioe) { throw new InvalidFileStructureException("I/O error while loading: " + ioe.toString()); } } else { throw new OperationFailedException("Only loading from IFF is supported."); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/GIFCodec.java0000664000000000000000000004205610542177143023507 0ustar /* * GIFCodec * * Copyright (c) 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.DataOutput; import java.io.IOException; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; /** * A codec to write Compuserve GIF (Graphics Interchange Format) files. *

* Only writing GIF files is supported right now. * Reading GIF files with JIU can be done with the {@link net.sourceforge.jiu.gui.awt.ToolkitLoader} * class which uses the image reader built into the Java runtime library ({@link java.awt.Toolkit} class). * That reader has supported GIF since Java 1.0. *

Supported image types

* When saving, classes implementing the following image data interfaces * are supported: * {@link net.sourceforge.jiu.data.BilevelImage}, * {@link net.sourceforge.jiu.data.Gray8Image} and * {@link net.sourceforge.jiu.data.Paletted8Image}. * GIF only supports up to 256 colors in an image, so * you will have to use one of the quantization classes to reduce * a truecolor image to 256 or less colors before you can save it * with this codec. *

Supported I/O classes

* This codec supports {@link java.io.OutputStream}, {@link java.io.DataOutput} * and {@link java.io.RandomAccessFile}. *

Bounds

* {@link net.sourceforge.jiu.codecs.ImageCodec}'s bounds concept is supported. * A user of this codec can specify a rectangular part of the input image * that will be saved instead of the complete image. *

Comments

* GIF - at least in its 89a version - allows for the inclusion of textual * comments. * When saving an image to a GIF file, each comment given to a codec * will be stored in a comment extension block of its own. *

Usage example

* Save an image using this codec: *
 * GIFCodec codec = new GIFCodec();
 * codec.appendComment("Bob and Susan at the Munich airport (2002-06-13).");
 * codec.setImage(image); // BilevelImage, Gray8Image or Paletted8Image
 * codec.setInterlacing(true);
 * codec.setFile("output.gif", CodecMode.SAVE);
 * codec.process();
 * codec.close();
 * 
*

Interlaced storage

* This codec allows creating interlaced and non-interlaced GIF files. * The default is non-interlaced storage. * Non-interlaced files store the rows top-to-bottom. *

* Interlaced files store the image in four passes, progressively adding * rows until the complete image is stored. * When decoding, the progressive display of interlaced files makes it * supposedly quicker to find out what's displayed in the image. *

* On the other hand, transmission typically takes longer, because interlacing * often leads to slightly larger files. * When using interlaced mode, lines that get stored one after another * have some room between them in the image, so there are less similarities * between consecutive lines, which worsens compression ratio (compression * works better with a lot of similarities in the data to be compressed). *

GIF versions

* There are two versions of GIF, 87a and 89a. * In 89a, several things were added to the file format specification. * From the 89a features this codec only uses the possibility of storing textual comments * in GIF files. * Thus, the version used for writing depends on the return value of * {@link #getNumComments()}. * If there is at least one comment to be written to the file, version 89a * will be used, 87a otherwise. *

Licensing of the LZW algorithm

* Unisys Corp. had a patent in several countries on the LZW algorithm used within GIF. * However, this patent has expired (Japan being the last country * where the patent expired, on July 7th 2004) so that LZW can be used freely. *

Licensing of the file format

* GIF was defined by Compuserve. * In a technical document file called Gif89a.txt that I found * somewhere on the Net they grant a royalty-free license for use of the * format to anyone - in order to improve the popularity of the format, I guess. * I don't think that it should be possible to put a file format under a copyright, * but all that Compuserve asks for in exchange for freely using the format * is the inclusion of a message. * So, here is that message: *
* "The Graphics Interchange Format(c) is the Copyright property of * CompuServe Incorporated. GIF(sm) is a Service Mark property of * CompuServe Incorporated." *
*

Animated GIFs

* GIF allows for animations to be stored. This codec only supports storing * a single image, though. *

File format background

* I've compiled a web page with * technical * information on GIF. * @author Marco Schmidt */ public class GIFCodec extends ImageCodec { private static final int CODE_ARRAY_LENGTH = 5020; private static final int[] INTERLACING_FIRST_ROW = {0, 4, 2, 1}; private static final int[] INTERLACING_INCREMENT = {8, 8, 4, 2}; private static final int NUM_INTERLACING_PASSES = 4; private static final byte[] MAGIC_GIF87A = {71, 73, 70, 56, 55, (byte)97}; private static final byte[] MAGIC_GIF89A = {71, 73, 70, 56, 57, (byte)97}; private int backgroundColor; private byte[] block; private int bitOffset; private int bitsPerPixel; private int blockLength; private int clearCode; private int codeSize; private int[] currentCode; private int currentColumn; private int currentInterlacingPass; private int currentRow; private int endOfInformationCode; private boolean notFinished; private int freeCode; private IntegerImage imageToBeSaved; private int initialCodeSize; private boolean interlaced; private int height; private int maxCode; private int[] newCode; private int[] oldCode; private DataOutput out; private int processedRows; private int width; /** * Returns the index of the background color. * @return int value with the color (index into the palette) of the background color * @see #setBackgroundColor */ public int getBackgroundColor() { return backgroundColor; } public String[] getFileExtensions() { return new String[] {".gif"}; } public String getFormatName() { return "Compuserve GIF"; } public String[] getMimeTypes() { return new String[] {"image/gif"}; } private int getNextSample() { int result = imageToBeSaved.getSample(currentColumn++, currentRow); if (currentColumn > getBoundsX2()) { setProgress(processedRows++, getBoundsHeight()); currentColumn = getBoundsX1(); if (isInterlaced()) { currentRow += INTERLACING_INCREMENT[currentInterlacingPass]; boolean done; do { if (currentRow > getBoundsY2()) { currentInterlacingPass++; if (currentInterlacingPass < NUM_INTERLACING_PASSES) { currentRow = getBoundsY1() + INTERLACING_FIRST_ROW[currentInterlacingPass]; } } done = currentRow <= getBoundsY2() || currentInterlacingPass > NUM_INTERLACING_PASSES; } while (!done); } else { currentRow++; } notFinished = processedRows < getBoundsHeight(); } return result; } private void initEncoding() throws IOException { imageToBeSaved = (IntegerImage)getImage(); currentColumn = getBoundsX1(); currentRow = getBoundsY1(); processedRows = 0; currentInterlacingPass = 0; notFinished = true; block = new byte[255]; currentCode = new int[CODE_ARRAY_LENGTH]; newCode = new int[CODE_ARRAY_LENGTH]; oldCode = new int[CODE_ARRAY_LENGTH]; if (bitsPerPixel == 1) { initialCodeSize = 2; } else { initialCodeSize = bitsPerPixel; } } /** * Returns if the image will be stored in interlaced (true) * or non-interlaced mode (false). * @return interlacing mode * @see #setInterlacing */ public boolean isInterlaced() { return interlaced; } public boolean isLoadingSupported() { return false; } public boolean isSavingSupported() { return true; } public void process() throws MissingParameterException, OperationFailedException { initModeFromIOObjects(); if (getMode() == CodecMode.LOAD) { throw new OperationFailedException("Loading is not supported."); } else { save(); } } private void resetBlock() { for (int i = 0; i < block.length; i++) { block[i] = 0; } blockLength = 0; bitOffset = 0; } private void resetEncoder() { codeSize = initialCodeSize + 1; clearCode = 1 << initialCodeSize; endOfInformationCode = clearCode + 1; freeCode = endOfInformationCode + 1; maxCode = (1 << codeSize) - 1; for (int i = 0; i < currentCode.length; i++) { currentCode[i] = 0; } } private void save() throws MissingParameterException, OperationFailedException, UnsupportedTypeException, WrongParameterException { PixelImage image = getImage(); if (image == null) { throw new MissingParameterException("No image available for saving."); } width = image.getWidth(); height = image.getHeight(); setBoundsIfNecessary(width, height); width = getBoundsWidth(); height = getBoundsHeight(); if (image instanceof Paletted8Image) { Palette palette = ((Paletted8Image)image).getPalette(); int numEntries = palette.getNumEntries(); if (numEntries < 1 || numEntries > 256) { throw new WrongParameterException("Palette of image to be saved must have 1..256 entries."); } bitsPerPixel = 8; // determine minimum number of bits per pixel necessary to store image for (int i = 1; i <= 8; i++) { if ((1 << i) >= numEntries) { bitsPerPixel = i; break; } } } else if (image instanceof Gray8Image) { bitsPerPixel = 8; } else if (image instanceof BilevelImage) { bitsPerPixel = 1; } else { throw new UnsupportedTypeException("Unsupported image type: " + image.getClass().getName()); } out = getOutputAsDataOutput(); if (out == null) { throw new MissingParameterException("Output stream / random access file parameter missing."); } // now write the output stream try { writeStream(); } catch (IOException ioe) { throw new OperationFailedException("I/O failure: " + ioe.toString()); } } /** * Specify the value of the background color. * Default is 0. * @param colorIndex int value with the color (index into the palette) of the background color * @see #getBackgroundColor */ public void setBackgroundColor(int colorIndex) { backgroundColor = colorIndex; } /** * Specifies whether the image will be stored in interlaced mode * (true) or non-interlaced mode (false). * @param useInterlacing boolean, if true interlaced mode, otherwise non-interlaced mode * @see #isInterlaced() */ public void setInterlacing(boolean useInterlacing) { interlaced = useInterlacing; } private void writeBlock() throws IOException { if (bitOffset > 0) { blockLength++; } if (blockLength == 0) { return; } out.write(blockLength); out.write(block, 0, blockLength); resetBlock(); } private void writeCode(int code) throws IOException { int remainingBits = codeSize; do { int bitsFree = 8 - bitOffset; int bits; /* bits => number of bits to be copied from "code" to "block[blockLength]" in this loop iteration */ if (bitsFree < remainingBits) { bits = bitsFree; } else { bits = remainingBits; } int value = block[blockLength] & 0xff; value += (code & ((1 << bits) - 1)) << bitOffset; block[blockLength] = (byte)value; bitOffset += bits; if (bitOffset == 8) { blockLength++; bitOffset = 0; if (blockLength == 255) { writeBlock(); } } code >>= bits; remainingBits -= bits; } while (remainingBits != 0); } private void writeComments() throws IOException { if (getNumComments() < 1) { return; } for (int commentIndex = 0; commentIndex < getNumComments(); commentIndex++) { String comment = getComment(commentIndex); byte[] data = comment.getBytes(); out.write(0x21); // extension introducer out.write(0xfe); // comment label int offset = 0; while (offset < data.length) { int number = Math.min(data.length - offset, 255); out.write(number); out.write(data, offset, number); offset += number; } out.write(0); // zero-length block } } /** * Writes a global header, a global palette and * an image descriptor to output. */ private void writeHeader() throws IOException { // pick a GIF version; stay with 87a if possible (no comments included // which require 89a) byte[] magic; if (getNumComments() > 0) { magic = MAGIC_GIF89A; } else { magic = MAGIC_GIF87A; } // global header out.write(magic); writeShort(width); writeShort(height); int depth = bitsPerPixel - 1; /* meaning of packed byte 128 => there is a global palette following (depth << 4) => the number of bits used for encoding depth - 1 => the number of bits in the global palette (same as for encoding) */ int packed = 128 | (depth << 4) | depth; out.write(packed); out.write(backgroundColor); int pixelAspectRatio = 0; out.write(pixelAspectRatio); // global palette writePalette(); // write textual comments (if any) to file as extension blocks writeComments(); // image descriptor out.write(44); // comma writeShort(0); // x1 writeShort(0); // y1 writeShort(width); // width writeShort(height); // height packed = 0; if (isInterlaced()) { packed |= 64; } out.write(packed); // flags } private void writeImage() throws IOException { out.write(initialCodeSize); resetBlock(); resetEncoder(); writeCode(clearCode); int suffixChar = getNextSample(); int prefixCode = suffixChar; do { suffixChar = getNextSample(); int d = 1; int hashIndex = (prefixCode ^ (suffixChar << 5)) % 5003; boolean endInnerLoop; do { if (currentCode[hashIndex] == 0) { writeCode(prefixCode); d = freeCode; if (freeCode <= 4095) { oldCode[hashIndex] = prefixCode; newCode[hashIndex] = suffixChar; currentCode[hashIndex] = freeCode; freeCode++; } if (d > maxCode) { if (codeSize < 12) { codeSize++; maxCode = (1 << codeSize) - 1; } else { writeCode(clearCode); resetEncoder(); } } prefixCode = suffixChar; break; } if (oldCode[hashIndex] == prefixCode && newCode[hashIndex] == suffixChar) { prefixCode= currentCode[hashIndex]; endInnerLoop = true; } else { hashIndex += d; d += 2; if (hashIndex > 5003) { hashIndex -= 5003; } endInnerLoop = false; } } while (!endInnerLoop); } while (notFinished); writeCode(prefixCode); writeCode(endOfInformationCode); writeBlock(); } private void writePalette() throws IOException { PixelImage image = getImage(); if (image instanceof Paletted8Image) { Palette palette = ((Paletted8Image)image).getPalette(); int numEntries = 1 << bitsPerPixel; for (int i = 0; i < numEntries; i++) { if (i < palette.getNumEntries()) { out.write(palette.getSample(RGBIndex.INDEX_RED, i)); out.write(palette.getSample(RGBIndex.INDEX_GREEN, i)); out.write(palette.getSample(RGBIndex.INDEX_BLUE, i)); } else { out.write(0); out.write(0); out.write(0); } } } else if (image instanceof Gray8Image) { for (int i = 0; i < 256; i++) { out.write(i); out.write(i); out.write(i); } } else if (image instanceof BilevelImage) { out.write(0); out.write(0); out.write(0); out.write(255); out.write(255); out.write(255); } } private void writeShort(int value) throws IOException { out.write(value & 0xff); out.write((value >> 8) & 0xff); } private void writeStream() throws IOException { initEncoding(); writeHeader(); writeImage(); writeTrailer(); } private void writeTrailer() throws IOException { out.write(0); // zero-length block out.write(59); // semicolon } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/ImageLoader.java0000664000000000000000000002543710421160275024313 0ustar /* * ImageLoader * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.awt.Frame; import java.awt.MediaTracker; import java.awt.Toolkit; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.util.Vector; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.InvalidImageIndexException; import net.sourceforge.jiu.codecs.WrongFileFormatException; import net.sourceforge.jiu.codecs.BMPCodec; import net.sourceforge.jiu.codecs.IFFCodec; import net.sourceforge.jiu.codecs.PCDCodec; import net.sourceforge.jiu.codecs.PNGCodec; import net.sourceforge.jiu.codecs.PNMCodec; import net.sourceforge.jiu.codecs.PSDCodec; import net.sourceforge.jiu.codecs.RASCodec; import net.sourceforge.jiu.codecs.tiff.TIFFCodec; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.gui.awt.ImageCreator; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; /** * A convenience class with static methods to load images from files using JIU codecs. * The load methods of this class try to load an image with all codecs registered with this class. * This includes almost every codec that resides in the net.sourceforge.jiu.codecs package. * You can register additional codecs with {@link #registerCodecClass} or remove the usage * of codecs with {@link #removeCodecClass}. *

* A Codec that cannot safely identify a file to be in the format that it supports must not be used with ImageLoader. * The failure to identify typically comes from the lack of magic byte sequences defined for the format. * In order to load such a file, use the codec manually. * Example: {@link PalmCodec}. *

* In order to load an image via {@link java.awt.Toolkit} (JPEG, PNG or GIF), use * {@link net.sourceforge.jiu.gui.awt.ToolkitLoader}. * It combines the loading features of java.awt.Toolkit and JIU's ImageLoader. *

Usage example

*
 * PixelImage image = null;
 * try
 * {
 *   image = ImageLoader.load("image.tif");
 * }
 * catch (Exception e)
 * {
 *   // handle exception
 * }
 * 
* @author Marco Schmidt */ public class ImageLoader { // all elements of class String private static Vector fileExtensions; private static Vector imageCodecClasses; static { imageCodecClasses = new Vector(); registerCodecClass(new BMPCodec()); registerCodecClass(new IFFCodec()); registerCodecClass(new PCDCodec()); registerCodecClass(new PNGCodec()); registerCodecClass(new PNMCodec()); registerCodecClass(new PSDCodec()); registerCodecClass(new RASCodec()); registerCodecClass(new TIFFCodec()); } private ImageLoader() { } /** * Creates an instance of one of the codec classes known to ImageLoader. * @param index 0-based index of codec number, maximum value is {@link #getNumCodecs} - 1 * @return new codec object or null if no object could be instantiated */ public static ImageCodec createCodec(int index) { ImageCodec result = null; if (index >= 0 && index < getNumCodecs()) { Class c = (Class)imageCodecClasses.elementAt(index); try { Object obj = c.newInstance(); if (obj != null && obj instanceof ImageCodec) { result = (ImageCodec)obj; } } catch (IllegalAccessException iae) { // ignore } catch (InstantiationException ie) { // ignore } } return result; } /** * Returns a filename filter ({@link java.io.FilenameFilter}) that accepts files * with name extensions typical for the image file formats known to ImageLoader. * The filter could then be used in an file dialog like {@link java.awt.FileDialog}. *

* Note that this filter does not include file formats supported by the AWT * {@link java.awt.Toolkit} (GIF and JPEG, also PNG since Java 1.3). * @return filter for image file names */ public static FilenameFilter createFilenameFilter() { return new FilenameFilter() { public boolean accept(File dir, String name) { if (name == null) { return false; } if (fileExtensions == null) { updateFileExtensions(); } name = name.toLowerCase(); int index = 0; while (index < fileExtensions.size()) { String ext = (String)fileExtensions.elementAt(index++); if (name.endsWith(ext)) { return true; } } return false; } }; } /** * Returns the number of codec classes currently known to ImageLoader. * This number can be changed by registering additional codecs * ({@link #registerCodecClass}) * or by removing codec classes ({@link #removeCodecClass}). * @return number of known codec classes */ public static int getNumCodecs() { return imageCodecClasses.size(); } /** * Attempts to load an image from a file. * @param file the file from which an image is to be loaded * @return the image on success or null on failure */ public static PixelImage load(File file) throws IOException, InvalidFileStructureException, InvalidImageIndexException, UnsupportedTypeException { return load(file, null); } /** * Attempts to load an image from a file, notifying the * argument progress listeners. * @param file the file to load an image from * @param listeners a Vector of ProgressListener objects to be notified * @return an instance of a class implementing {@link PixelImage} */ public static PixelImage load(File file, Vector listeners) throws IOException, InvalidFileStructureException, InvalidImageIndexException, UnsupportedTypeException { for (int i = 0; i < getNumCodecs(); i++) { PixelImage result = null; ImageCodec codec = null; try { codec = createCodec(i); codec.setFile(file, CodecMode.LOAD); codec.addProgressListeners(listeners); codec.process(); result = codec.getImage(); if (result != null) { return result; } } catch (MissingParameterException mpe) { // ignore } catch (WrongFileFormatException wffe) { // ignore } catch (IOException ioe) { // ignore } catch (OperationFailedException ofe) { // ignore //System.out.println("codec: " + ofe); } finally { if (codec != null) { codec.close(); } } } return null; } /** * Load an image from a file given by its name. * Simply calls load(fileName, null). * @param fileName name of the file from which an image is to be loaded * @return the loaded image on success, null on failure */ public static PixelImage load(String fileName) throws IOException, InvalidFileStructureException, InvalidImageIndexException, UnsupportedTypeException { return load(fileName, null); } /** * Attempts to load an image from the file with the given name, * using the given list of progress listeners. * @param fileName name of the file from which an image is to be loaded * @param listeners a list of objects implementing ProgressListener * @return the loaded image */ public static PixelImage load(String fileName, Vector listeners) throws IOException, InvalidFileStructureException, InvalidImageIndexException, UnsupportedTypeException { return load(new File(fileName), listeners); } public static PixelImage loadToolkitImageUri(String uri) throws IOException, InvalidFileStructureException, InvalidImageIndexException, UnsupportedTypeException { try { ImageLoader loader = new ImageLoader(); InputStream in = loader.getClass().getResourceAsStream(uri); if (in == null) { return null; } ByteArrayOutputStream out = new ByteArrayOutputStream(); int b; while ((b = in.read()) != -1) { out.write(b); } in.close(); byte[] data = out.toByteArray(); java.awt.Image awtImage = Toolkit.getDefaultToolkit().createImage(data); MediaTracker mediaTracker = new MediaTracker(new Frame()); mediaTracker.addImage(awtImage, 0); try { mediaTracker.waitForID(0); } catch (InterruptedException ie) { return null; } PixelImage image = ImageCreator.convertImageToRGB24Image(awtImage); return image; } catch (Exception e) { e.printStackTrace(); return null; } } /** * Registers a codec class with ImageLoader. * The argument is an instance of the class to be registered. * Note that the codec class must have an empty constructor. *

* Example: let's say you have written a new codec called ACMEImageCodec. * Your codec supports loading images. * Then you could register it like that: *

	 * ImageLoader.registerCodecClass(new ACMEImageCodec());
	 * 
*

* @param codec an instance of the codec class to be registered */ public static void registerCodecClass(ImageCodec codec) { if (codec == null) { return; } if (imageCodecClasses.contains(codec.getClass())) { return; } if (!codec.isLoadingSupported()) { throw new IllegalArgumentException("Codec does not support loading."); } imageCodecClasses.addElement(codec.getClass()); updateFileExtensions(); } /** * Removes all codec classes from the internal list of codec classes. * After a call to this method, ImageLoader will not load anything unless * new codecs get registered. */ public static void removeAllCodecClasses() { imageCodecClasses = new Vector(); updateFileExtensions(); } /** * Removes a codec class from the internal list of codec classes. * @param codec an instance of the codec class to be removed */ public static void removeCodecClass(ImageCodec codec) { if (codec == null) { return; } int index = imageCodecClasses.indexOf(codec.getClass()); if (index != -1) { imageCodecClasses.remove(index); updateFileExtensions(); } else { throw new IllegalArgumentException("The argument codec's class " + "could not be found in the internal list of codec classes."); } } private static void updateFileExtensions() { fileExtensions = new Vector(); int index = 0; while (index < getNumCodecs()) { try { ImageCodec codec = createCodec(index++); String[] extArray = codec.getFileExtensions(); if (extArray != null && extArray.length > 0) { for (int i = 0; i < extArray.length; i++) { fileExtensions.addElement(extArray[i].toLowerCase()); } } } catch (Exception e) { } } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/UnsupportedTypeException.java0000664000000000000000000000177007741250131027207 0ustar /* * UnsupportedTypeException * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import net.sourceforge.jiu.ops.OperationFailedException; /** * This exception is thrown during image loading. * If a codec recognizes the file format but does not support the * exact subtype it encounters (the compression type is unknown or * the color depth unsupported), an instance of this exception class is * created. *

* If the format is not recognized at all, a {@link WrongFileFormatException} * should be thrown. *

* If there were errors during loading because of file corruption, an * {@link InvalidFileStructureException} must be thrown. * * @see InvalidFileStructureException * @see WrongFileFormatException * * @author Marco Schmidt */ public class UnsupportedTypeException extends OperationFailedException { public UnsupportedTypeException(String message) { super(message); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/InvalidImageIndexException.java0000664000000000000000000000157207741250131027336 0ustar /* * InvalidImageIndexException * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import net.sourceforge.jiu.ops.OperationFailedException; /** * This exception is thrown when the caller has defined an image * index that specifies the image to be loaded in a multiple-image * file and that index is unavailable. * Example: user has specified an image index of 5, which is the * sixth image in the file (counting starts at 0), * but only three images are available. * @author Marco Schmidt */ public class InvalidImageIndexException extends OperationFailedException { /** * Creates new exception object with a given error message. * @param message String with text describing the exact problem */ public InvalidImageIndexException(String message) { super(message); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/PNMCodec.java0000664000000000000000000007541210523756512023540 0ustar /* * PNMCodec * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.DataOutput; import java.io.InputStream; import java.io.IOException; import java.io.PushbackInputStream; import java.util.NoSuchElementException; import java.util.StringTokenizer; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.GrayImage; import net.sourceforge.jiu.data.Gray16Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.GrayIntegerImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.MemoryGray16Image; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.MemoryRGB48Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGB48Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.data.RGBIntegerImage; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; /** * A codec to read and write Portable Anymap (PNM) image files. * This format includes three file types well-known in the Unix world: *

*

* *

Compression

* The file format only allows for uncompressed storage. * *

ASCII mode / binary mode

* PNM streams can be stored in binary mode or ASCII mode. * ASCII mode files are text files with numbers representing the pixels. * They become larger than their binary counterparts, but as they are * very redundant they can be compressed well with archive programs. * ASCII PGM and PPM files can have all kinds of maximum sample values, * thus allowing for arbitrary precision. * They are not restricted by byte limits. * PBM streams always have two colors, no matter if they are ASCII or binary. * *

Color depth for PGM / PPM

*

* The header of a PGM and PPM file stores a maximum sample value * (such a value is not stored for PBM, where the maximum value is always 1). * When in binary mode, PGM and PPM typically have a maximum sample value of 255, * which makes PGM 8 bits per pixel and PPM 24 bits per pixel large. * One sample will be stored as a single byte. * However, there also exist binary PGM files with a maximum sample value larger than * 255 and smaller than 65536. * These files use two bytes per sample, in network byte order (big endian). * I have yet to see PPM files with that property, but they are of course imagineable. * 16 bpp *

* *

DPI values

* PNM files cannot store the physical resolution in DPI. * *

Number of images

* Only one image can be stored in a PNM file. * *

Usage example - load an image from a PNM file

*
 * PNMCodec codec = new PNMCodec();
 * codec.setFile("test.ppm", CodecMode.LOAD);
 * codec.process();
 * codec.close();
 * PixelImage image = codec.getImage();
 * 
* *

Usage example - save an image to a PNM file

*
 * PNMCodec codec = new PNMCodec();
 * BilevelImage myFax = ...; // initialize
 * codec.setImage(myFax);
 * codec.setFile("out.pbm", CodecMode.SAVE);
 * codec.process();
 * codec.close();
 * 
* * @author Marco Schmidt */ public class PNMCodec extends ImageCodec { /** * Image type constant for images of unknown type. */ public static final int IMAGE_TYPE_UNKNOWN = -1; /** * Image type constant for bilevel images, stored in PBM files. */ public static final int IMAGE_TYPE_BILEVEL = 0; /** * Image type constant for grayscale images, stored in PGM files. */ public static final int IMAGE_TYPE_GRAY = 1; /** * Image type constant for RGB truecolor images, stored in PPM files. */ public static final int IMAGE_TYPE_COLOR = 2; private static final String[] IMAGE_TYPE_FILE_EXTENSIONS = {".pbm", ".pgm", ".ppm"}; private Boolean ascii; private int columns; private int imageType; private PushbackInputStream in; private DataOutput out; private int height; private int maxSample; private int width; /** * Attempts to find the appropriate image type by looking at a file's name. * Ignores case when comparing. * Returns {@link #IMAGE_TYPE_BILEVEL} for .pbm, * {@link #IMAGE_TYPE_GRAY} for .pgm and * {@link #IMAGE_TYPE_COLOR} for .ppm. * Otherwise, {@link #IMAGE_TYPE_UNKNOWN} is returned. * To get a file extension given that you have an image type, use * {@link #getTypicalFileExtension}. * * @param fileName the file name to be examined * @return one of the IMAGE_TYPE_xxx constants of this class */ public static int determineImageTypeFromFileName(String fileName) { if (fileName == null || fileName.length() < 4) { return IMAGE_TYPE_UNKNOWN; } String ext = fileName.substring(fileName.length() - 3); ext = ext.toLowerCase(); for (int i = 0; i < IMAGE_TYPE_FILE_EXTENSIONS.length; i++) { if (IMAGE_TYPE_FILE_EXTENSIONS[i].equals(ext)) { return i; } } return IMAGE_TYPE_UNKNOWN; } /** * Returns if ASCII mode was used for loading an image or will * be used to store an image. * @return true for ASCII mode, false for binary mode, null if that information is not available * @see #setAscii */ public Boolean getAscii() { return ascii; } public String getFormatName() { return "Portable Anymap (PBM, PGM, PPM)"; } public String[] getMimeTypes() { return new String[] {"image/x-ppm", "image/x-pgm", "image/x-pbm", "image/x-pnm", "image/x-portable-pixmap", "image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-anymap"}; } /** * Returns the typical file extension (including leading dot) for an * image type. * Returns null for {@link #IMAGE_TYPE_UNKNOWN}. * To get the image type given that you have a file name, use * {@link #determineImageTypeFromFileName}. * * @param imageType the image type for which the extension is required * @return the file extension or null */ public static String getTypicalFileExtension(int imageType) { if (imageType >= 0 && imageType < IMAGE_TYPE_FILE_EXTENSIONS.length) { return IMAGE_TYPE_FILE_EXTENSIONS[imageType]; } else { return null; } } public boolean isLoadingSupported() { return true; } public boolean isSavingSupported() { return true; } /** * Loads an image from a PNM input stream. * It is assumed that a stream was given to this codec using {@link #setInputStream(InputStream)}. * * @return the image as an instance of a class that implements {@link IntegerImage} * @throws InvalidFileStructureException if the input stream is not a valid PNM stream (or unsupported) * @throws java.io.IOException if there were problems reading from the input stream */ private void load() throws InvalidFileStructureException, IOException, MissingParameterException, UnsupportedTypeException, WrongFileFormatException, WrongParameterException { InputStream is = getInputStream(); if (is != null) { if (is instanceof PushbackInputStream) { in = (PushbackInputStream)is; } else { in = new PushbackInputStream(is); } } else { throw new MissingParameterException("InputStream object required for loading."); } loadType(); String resolutionLine = loadTextLine(); setResolution(resolutionLine); setBoundsIfNecessary(width, height); if (imageType == IMAGE_TYPE_BILEVEL) { maxSample = 1; } else { // load maximum value String maxSampleLine = loadTextLine(); setMaximumSample(maxSampleLine); } if (maxSample > 65535) { throw new UnsupportedTypeException("Cannot deal with samples larger than 65535."); } checkImageResolution(); switch (imageType) { case(IMAGE_TYPE_BILEVEL): { loadBilevelImage(); break; } case(IMAGE_TYPE_COLOR): { loadColorImage(); break; } case(IMAGE_TYPE_GRAY): { loadGrayImage(); break; } default: { throw new UnsupportedTypeException("Cannot deal with image type."); } } } private int loadAsciiNumber() throws InvalidFileStructureException, IOException { boolean hasDigit = false; int result = -1; do { int b = in.read(); if (b >= 48 && b <= 57) { // decimal digit if (hasDigit) { result = result * 10 + (b - 48); } else { hasDigit = true; result = b - 48; } } else if (b == 32 || b == 10 || b == 13 || b == 9) { // whitespace if (hasDigit) { if (result > maxSample) { throw new InvalidFileStructureException("Read number " + "from PNM stream that is larger than allowed " + "maximum sample value " + maxSample + " (" + result + ")."); } return result; } // ignore whitespace } else if (b == 35) { // the # character, indicating a comment row if (hasDigit) { in.unread(b); if (result > maxSample) { throw new InvalidFileStructureException("Read " + "number from PNM stream that is larger than " + "allowed maximum sample value " + maxSample + " (" + result + ")."); } return result; } do { b = in.read(); } while (b != -1 && b != 10 && b != 13); if (b == 13) { } // put it into the comment list } else if (b == -1) { // the end of file character if (hasDigit) { if (result > maxSample) { throw new InvalidFileStructureException("Read number from PNM stream that is larger than allowed maximum sample value " + maxSample + " (" + result + ")"); } return result; } throw new InvalidFileStructureException("Unexpected end of file while reading ASCII number from PNM stream."); } else { throw new InvalidFileStructureException("Read invalid character from PNM stream: " + b + " dec."); } } while(true); } private void loadBilevelImage() throws InvalidFileStructureException, IOException, WrongParameterException { PixelImage image = getImage(); if (image == null) { setImage(new MemoryBilevelImage(getBoundsWidth(), getBoundsHeight())); } else { if (!(image instanceof BilevelImage)) { throw new WrongParameterException("Specified input image must implement BilevelImage for this image type."); } } if (getAscii().booleanValue()) { loadBilevelImageAscii(); } else { loadBilevelImageBinary(); } } private void loadBilevelImageAscii() throws InvalidFileStructureException, IOException { BilevelImage image = (BilevelImage)getImage(); // skip the pixels of the first getBoundsY1() rows int pixelsToSkip = width * getBoundsY1(); for (int i = 0; i < pixelsToSkip; i++) { loadAsciiNumber(); } final int NUM_ROWS = getBoundsHeight(); final int COLUMNS = getBoundsWidth(); final int X1 = getBoundsX1(); int[] row = new int[width]; // now read and store getBoundsHeight() rows for (int y = 0; y < NUM_ROWS; y++) { for (int x = 0; x < width; x++) { int value = loadAsciiNumber(); if (value == 0) { row[x] = BilevelImage.WHITE; } else if (value == 1) { row[x] = BilevelImage.BLACK; } else { throw new InvalidFileStructureException("Loaded " + "number for position x=" + x + ", y=" + (y + getBoundsY1()) + " is neither 0 nor 1 in PBM stream: " + value); } } image.putSamples(0, 0, y, COLUMNS, 1, row, X1); setProgress(y, NUM_ROWS); } } private void loadBilevelImageBinary() throws InvalidFileStructureException, IOException { BilevelImage image = (BilevelImage)getImage(); int bytesPerRow = (width + 7) / 8; // skip the first getBoundsY1() rows long bytesToSkip = (long)getBoundsY1() * (long)bytesPerRow; // Note: // removed in.skip(bytesToSkip) because that was only available in Java 1.2 // instead the following while loop is used while (bytesToSkip-- > 0) { in.read(); } // allocate buffer large enough for a complete row byte[] row = new byte[bytesPerRow]; final int numRows = getBoundsHeight(); // read and store the next getBoundsHeight() rows for (int y = 0; y < numRows; y++) { // read bytesPerRow bytes into row int bytesToRead = bytesPerRow; int index = 0; while (bytesToRead > 0) { int result = in.read(row, index, bytesToRead); if (result >= 0) { index += result; bytesToRead -= result; } else { throw new InvalidFileStructureException("Unexpected end of input stream while reading."); } } // invert values for (int x = 0; x < row.length; x++) { row[x] = (byte)~row[x]; } //image.putPackedBytes(0, y, bytesPerRow, buffer, 0); if (isRowRequired(y)) { image.putPackedBytes(0, y - getBoundsY1(), getBoundsWidth(), row, getBoundsX1() >> 3, getBoundsX1() & 7); } setProgress(y, numRows); } } private void loadColorImage() throws InvalidFileStructureException, IOException { RGBIntegerImage image = null; RGB24Image image24 = null; if (maxSample <= 255) { image24 = new MemoryRGB24Image(width, height); image = image24; setImage(image); } else { image = new MemoryRGB48Image(width, height); setImage(image); } for (int y = 0, destY = - getBoundsY1(); y < height; y++, destY++) { if (getAscii().booleanValue()) { for (int x = 0; x < width; x++) { int red = loadAsciiNumber(); if (red < 0 || red > maxSample) { throw new InvalidFileStructureException("Invalid " + "sample value " + red + " for red sample at " + "(x=" + x + ", y=" + y + ")."); } image.putSample(RGBIndex.INDEX_RED, x, y, red); int green = loadAsciiNumber(); if (green < 0 || green > maxSample) { throw new InvalidFileStructureException("Invalid " + "sample value " + green + " for green sample at " + "(x=" + x + ", y=" + y + ")."); } image.putSample(RGBIndex.INDEX_GREEN, x, y, green); int blue = loadAsciiNumber(); if (blue < 0 || blue > maxSample) { throw new InvalidFileStructureException("Invalid " + "sample value " + blue + " for blue sample at " + "(x=" + x + ", y=" + y + ")."); } image.putSample(RGBIndex.INDEX_BLUE, x, y, blue); } } else { if (image24 != null) { for (int x = 0; x < width; x++) { int red = in.read(); if (red == -1) { throw new InvalidFileStructureException("Unexpected " + "end of file while reading red sample for pixel " + "x=" + x + ", y=" + y + "."); } image24.putByteSample(RGBIndex.INDEX_RED, x, y, (byte)(red & 0xff)); int green = in.read(); if (green == -1) { throw new InvalidFileStructureException("Unexpected " + "end of file while reading green sample for pixel " + "x=" + x + ", y=" + y + "."); } image24.putByteSample(RGBIndex.INDEX_GREEN, x, y, (byte)(green & 0xff)); int blue = in.read(); if (blue == -1) { throw new InvalidFileStructureException("Unexpected " + "end of file while reading blue sample for pixel " + "x=" + x + ", y=" + y + "."); } image24.putByteSample(RGBIndex.INDEX_BLUE, x, y, (byte)(blue & 0xff)); } } } setProgress(y, getBoundsHeight()); } } private void loadGrayImage() throws InvalidFileStructureException, IOException, UnsupportedTypeException { final int WIDTH = getBoundsWidth(); final int HEIGHT = getBoundsHeight(); PixelImage pimage = getImage(); if (pimage == null) { if (maxSample < 256) { pimage = new MemoryGray8Image(WIDTH, HEIGHT); } else if (maxSample < 65536) { pimage = new MemoryGray16Image(WIDTH, HEIGHT); } else { throw new UnsupportedTypeException("Gray images with more than 16 bits per pixel are not supported."); } setImage(pimage); } else { } GrayIntegerImage image = (GrayIntegerImage)pimage; int[] buffer = new int[width]; for (int y = 0, destY = -getBoundsY1(); destY < getBoundsHeight(); y++, destY++) { if (getAscii().booleanValue()) { for (int x = 0; x < width; x++) { buffer[x] = loadAsciiNumber(); } } else { if (maxSample < 256) { for (int x = 0; x < width; x++) { buffer[x] = in.read(); } } else { for (int x = 0; x < width; x++) { int msb = in.read(); int lsb = in.read(); buffer[x] = (msb << 8) | lsb; } } } if (destY >= 0 && destY < getBoundsHeight()) { image.putSamples(0, 0, destY, getBoundsWidth(), 1, buffer, getBoundsX1()); } setProgress(y, getBoundsY2() + 1); } } private String loadTextLine() throws InvalidFileStructureException, IOException { // load text lines until // 1) a normal text line is found // 2) an error occurs // any comment lines starting with # are added to the // comments Vector boolean isComment; StringBuffer sb; do { sb = new StringBuffer(); int b; boolean crOrLf; do { b = in.read(); if (b == -1) { throw new InvalidFileStructureException("Unexpected end of file in PNM stream."); } crOrLf = (b == 0x0a || b == 0x0d); if (!crOrLf) { sb.append((char)b); } } while (!crOrLf); if (b == 0x0d) { b = in.read(); if (b != 0x0a) { throw new InvalidFileStructureException("Unexpected end of file in PNM stream."); } } isComment = (sb.length() > 0 && sb.charAt(0) == '#'); if (isComment) { //sb.deleteCharAt(0); //sb.delete(0, 1); StringBuffer result = new StringBuffer(sb.length() - 1); int i = 1; while (i < sb.length()) { result.append(sb.charAt(i++)); } appendComment(result.toString()); } } while (isComment); return sb.toString(); } /** * Loads the first two characters (which are expected to be a capital P * followed by a decimal digit between 1 and 6, inclusively) and skips * following LF and CR characters. * This method not only checks the two bytes, it also initializes internal fields * for storage mode (ASCII or binary) and image type. * * @throws WrongFileFormatException if the input stream is not a PNM stream * @throws InvalidFileStructureException if the format that * is described above was not encountered * @throws java.io.IOException if there were errors reading data * @throws java.lang.IllegalArgumentException if the input stream was not given to this codec */ private void loadType() throws InvalidFileStructureException, IOException, WrongFileFormatException { // read two bytes int v1 = in.read(); int v2 = in.read(); // check if first byte is P if (v1 != 0x50) { throw new WrongFileFormatException("Not a PNM stream. First byte " + "in PNM stream is expected to be 0x50 ('P'); found: " + v1 + " (dec)."); } // check if second byte is ASCII of digit from 1 to 6 if (v2 < 0x31 || v2 > 0x36) { throw new WrongFileFormatException("Not a PNM stream. Second byte " + "in PNM stream is expected to be the ASCII value of decimal " + "digit between 1 and 6 (49 dec to 54 dec); found " + v2 + " dec."); } // determine mode (ASCII or binary) from second byte ascii = new Boolean(v2 < 0x34); // determine image type from second byte v2 = v2 - 0x30; imageType = (v2 - 1) % 3; // skip LF and CR int b; do { b = in.read(); } while (b == 0x0a || b == 0x0d || b == ' '); if (b == -1) { throw new InvalidFileStructureException("Read type (" + v2 + "). Unexpected end of file in input PNM stream."); } in.unread(b); } public void process() throws MissingParameterException, OperationFailedException { initModeFromIOObjects(); try { if (getMode() == CodecMode.LOAD) { load(); } else { save(); } } catch (IOException ioe) { throw new OperationFailedException("I/O error: " + ioe.toString()); } } private void save() throws IOException, MissingParameterException, WrongParameterException { out = getOutputAsDataOutput(); if (out == null) { throw new WrongParameterException("Cannot get a DataOutput object to use for saving."); } PixelImage pi = getImage(); if (pi == null) { throw new MissingParameterException("Input image missing."); } if (!(pi instanceof IntegerImage)) { throw new WrongParameterException("Input image must implement IntegerImage."); } IntegerImage image = (IntegerImage)pi; width = image.getWidth(); height = image.getHeight(); setBoundsIfNecessary(width, height); if (image instanceof RGB24Image) { imageType = IMAGE_TYPE_COLOR; maxSample = 255; save((RGB24Image)image); } else if (image instanceof RGB48Image) { imageType = IMAGE_TYPE_COLOR; maxSample = 65535; save((RGB48Image)image); } else if (image instanceof BilevelImage) { imageType = IMAGE_TYPE_BILEVEL; maxSample = 1; save((BilevelImage)image); } else if (image instanceof Gray8Image) { imageType = IMAGE_TYPE_GRAY; maxSample = 255; save((Gray8Image)image); } else if (image instanceof Gray16Image) { imageType = IMAGE_TYPE_GRAY; maxSample = 65535; save((Gray16Image)image); } else { throw new WrongParameterException("Unsupported input image type: " + image.getClass().getName()); } close(); } private void save(BilevelImage image) throws IOException { saveHeader(); final int WIDTH = getBoundsWidth(); final int HEIGHT = getBoundsHeight(); final int BYTES_PER_ROW = (WIDTH + 7) / 8; byte[] buffer = new byte[BYTES_PER_ROW]; for (int y = 0, srcY = getBoundsY1(); y < HEIGHT; y++, srcY++) { if (getAscii().booleanValue()) { for (int x = 0, srcX = getBoundsX1(); x < WIDTH; x++, srcX++) { if (image.isBlack(srcX, srcY)) { out.write(49); // 1 } else { out.write(48); // 0 } columns ++; if (columns > 70) { columns = 0; out.write(10); } else { out.write(32); columns++; } } } else { image.getPackedBytes(getBoundsX1(), srcY, WIDTH, buffer, 0, 0); for (int x = 0; x < buffer.length; x++) { buffer[x] = (byte)(~buffer[x]); } out.write(buffer); } setProgress(y, HEIGHT); } } private void save(Gray8Image image) throws IOException { saveHeader(); final int HEIGHT = getBoundsHeight(); final int WIDTH = getBoundsWidth(); final int X1 = getBoundsX1(); System.out.println(WIDTH + " " + HEIGHT + " " + X1); byte[] buffer = new byte[WIDTH]; for (int y = 0, srcY = getBoundsY1(); y < HEIGHT; y++, srcY++) { image.getByteSamples(0, X1, srcY, WIDTH, 1, buffer, 0); if (getAscii().booleanValue()) { for (int x = 0; x < WIDTH; x++) { saveAsciiNumber(buffer[x] & 0xff); out.write(32); columns += 2; if (columns > 70) { columns = 0; out.write(10); } else { out.write(32); columns++; } } } else { out.write(buffer); } setProgress(y, HEIGHT); } } private void save(Gray16Image image) throws IOException { saveHeader(); final int HEIGHT = getBoundsHeight(); final int WIDTH = getBoundsWidth(); final int X1 = getBoundsX1(); short[] buffer = new short[WIDTH]; for (int y = 0, srcY = getBoundsY1(); y < HEIGHT; y++, srcY++) { image.getShortSamples(0, X1, srcY, WIDTH, 1, buffer, 0); if (getAscii().booleanValue()) { for (int x = 0; x < WIDTH; x++) { saveAsciiNumber(buffer[x] & 0xffff); out.write(32); columns += 4; if (columns > 70) { columns = 0; out.write(10); } else { out.write(32); columns++; } } } else { for (int x = 0; x < WIDTH; x++) { int sample = buffer[x] & 0xffff; out.write((sample >> 8) & 0xff); out.write(sample & 0xff); } } setProgress(y, HEIGHT); } } private void save(RGB24Image image) throws IOException { saveHeader(); final int WIDTH = getBoundsWidth(); final int HEIGHT = getBoundsHeight(); for (int y = 0, srcY = getBoundsY1(); y < HEIGHT; y++, srcY++) { if (getAscii().booleanValue()) { for (int x = 0, srcX = getBoundsX1(); x < WIDTH; x++, srcX++) { int red = image.getSample(RGBIndex.INDEX_RED, srcX, srcY); int green = image.getSample(RGBIndex.INDEX_GREEN, srcX, srcY); int blue = image.getSample(RGBIndex.INDEX_BLUE, srcX, srcY); saveAsciiNumber(red); out.write(32); saveAsciiNumber(green); out.write(32); saveAsciiNumber(blue); columns += 11; if (columns > 80) { columns = 0; out.write(10); } else { out.write(32); columns++; } } } else { for (int x = 0, srcX = getBoundsX1(); x < WIDTH; x++, srcX++) { out.write(image.getSample(RGBIndex.INDEX_RED, srcX, srcY)); out.write(image.getSample(RGBIndex.INDEX_GREEN, srcX, srcY)); out.write(image.getSample(RGBIndex.INDEX_BLUE, srcX, srcY)); } } setProgress(y, HEIGHT); } } private void save(RGB48Image image) throws IOException { saveHeader(); final int WIDTH = getBoundsWidth(); final int HEIGHT = getBoundsHeight(); for (int y = 0, srcY = getBoundsY1(); y < HEIGHT; y++, srcY++) { if (getAscii().booleanValue()) { for (int x = 0, srcX = getBoundsX1(); x < WIDTH; x++, srcX++) { int red = image.getSample(RGBIndex.INDEX_RED, srcX, srcY); int green = image.getSample(RGBIndex.INDEX_GREEN, srcX, srcY); int blue = image.getSample(RGBIndex.INDEX_BLUE, srcX, srcY); saveAsciiNumber(red); out.write(32); saveAsciiNumber(green); out.write(32); saveAsciiNumber(blue); columns += 13; if (columns > 80) { columns = 0; out.write(10); } else { out.write(32); columns++; } } } else { /* for (int x = 0, srcX = getBoundsX1(); x < WIDTH; x++, srcX++) { out.write(image.getSample(RGBIndex.INDEX_RED, srcX, srcY)); out.write(image.getSample(RGBIndex.INDEX_GREEN, srcX, srcY)); out.write(image.getSample(RGBIndex.INDEX_BLUE, srcX, srcY)); } */ } setProgress(y, HEIGHT); } } private void saveAsciiNumber(int number) throws IOException { String s = Integer.toString(number); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); out.write(c); } columns += s.length(); } private void saveHeader() throws IOException { out.write(80); // 'P' int pnmType = 49 + imageType; if (getAscii() == null) { setAscii(maxSample > 255); } if (!getAscii().booleanValue()) { pnmType += 3; } out.write(pnmType); // '1' .. '6' out.write(10); // line feed saveAsciiNumber(getBoundsWidth()); out.write(32); // space saveAsciiNumber(getBoundsHeight()); out.write(10); // line feed if (imageType != IMAGE_TYPE_BILEVEL) { // bilevel max sample is always 1 and MUST NOT be saved saveAsciiNumber(maxSample); out.write(10);// line feed } } /** * Specify whether ASCII mode is to be used when saving an image. * Default is binary mode. * @param asciiMode if true, ASCII mode is used, binary mode otherwise */ public void setAscii(boolean asciiMode) { ascii = new Boolean(asciiMode); } private void setMaximumSample(String line) throws InvalidFileStructureException { line = line.trim(); try { maxSample = Integer.parseInt(line); } catch (NumberFormatException nfe) { throw new InvalidFileStructureException("Not a valid value for the maximum sample: " + line); } if (maxSample < 0) { throw new InvalidFileStructureException("The value for the maximum sample must not be negative; found " + maxSample); } } /* * Reads resolution from argument String and sets private variables * width and height. */ private void setResolution(String line) throws InvalidFileStructureException { line = line.trim(); StringTokenizer st = new StringTokenizer(line, " "); try { if (!st.hasMoreTokens()) { throw new InvalidFileStructureException("No width value found in line \"" + line + "\"."); } String number = st.nextToken(); try { width = Integer.parseInt(number); } catch (NumberFormatException nfe) { throw new InvalidFileStructureException("Not a valid int value for width: " + number); } if (width < 1) { throw new InvalidFileStructureException("The width value must be larger than " + "zero; found " + width + "."); } if (!st.hasMoreTokens()) { throw new InvalidFileStructureException("No height value found in line \"" + line + "\"."); } number = st.nextToken(); try { height = Integer.parseInt(number); } catch (NumberFormatException nfe) { throw new InvalidFileStructureException("Not a valid int value for height: " + number); } if (height < 1) { throw new InvalidFileStructureException("The height value must be larger than " + "zero; found " + width + "."); } } catch (NoSuchElementException nsee) { // should not happen because we always check if there is a token } } public String suggestFileExtension(PixelImage image) { if (image == null) { return null; } if (image instanceof BilevelImage) { return IMAGE_TYPE_FILE_EXTENSIONS[IMAGE_TYPE_BILEVEL]; } else if (image instanceof GrayImage) { return IMAGE_TYPE_FILE_EXTENSIONS[IMAGE_TYPE_GRAY]; } else if (image instanceof RGB24Image) { return IMAGE_TYPE_FILE_EXTENSIONS[IMAGE_TYPE_COLOR]; } return null; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/RASCodec.java0000664000000000000000000003055507741250131023524 0ustar /* * RASCodec * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.codecs.WrongFileFormatException; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; import net.sourceforge.jiu.util.ArrayConverter; /** * A codec to read and write Sun Raster (RAS) image files. * The typical file extension for this format is .ras. *

Usage example

* This code snippet demonstrate how to read a RAS file. *
 * RASCodec codec = new RASCodec();
 * codec.setFile("image.ras", CodecMode.LOAD);
 * codec.process();
 * PixelImage loadedImage = codec.getImage();
 * 
*

Supported file types when reading

* Only uncompressed RAS files are read. * Only 8 bit (gray and paletted) and 24 bit are supported when reading. *

Supported image types when writing

* Only {@link net.sourceforge.jiu.data.Paletted8Image} / uncompressed is supported when writing. *

Bounds

* The bounds concept of ImageCodec is supported so that you can load or save only part of an image. *

File format documentation

* This file format is documented as a man page rasterfile(5) on Sun Unix systems. * That documentation can also be found online, e.g. at * http://www.doc.ic.ac.uk/~mac/manuals/sunos-manual-pages/sunos4/usr/man/man5/rasterfile.5.html. * A web search for rasterfile(5) * brings up other places as well. * * @author Marco Schmidt */ public class RASCodec extends ImageCodec { private static final int RAS_MAGIC = 0x59a66a95; private static final int COMPRESSION_NONE = 0x00000001; private static final int RAS_HEADER_SIZE = 32; private int width; private int height; private int depth; private int length; private int type; private int mapType; private int mapLength; private int bytesPerRow; private int paddingBytes; private int numColors; private DataInput in; private DataOutput out; private Palette palette; public String getFormatName() { return "Sun Raster (RAS)"; } public String[] getMimeTypes() { return new String[] {"image/x-ras"}; } public boolean isLoadingSupported() { return true; } public boolean isSavingSupported() { return true; } /** * Loads an image from an RAS input stream. * It is assumed that a stream was given to this codec using {@link #setInputStream(InputStream)}. * * @return the image as an instance of a class that implements {@link IntegerImage} * @throws InvalidFileStructureException if the input stream is corrupt * @throws java.io.IOException if there were problems reading from the input stream * @throws UnsupportedTypeException if an unsupported flavor of the RAS format is encountered * @throws WrongFileFormatException if this is not a valid RAS stream */ private void load() throws IOException, OperationFailedException { in = getInputAsDataInput(); readHeader(); readImage(); } public void process() throws OperationFailedException { try { initModeFromIOObjects(); if (getMode() == CodecMode.LOAD) { load(); } else if (getMode() == CodecMode.SAVE) { save(); } else { throw new WrongParameterException("Could find neither objects for loading nor for saving."); } } catch (IOException ioe) { throw new OperationFailedException("I/O error in RAS codec: " + ioe.toString()); } } private void readHeader() throws InvalidFileStructureException, UnsupportedTypeException, WrongFileFormatException, WrongParameterException, java.io.IOException { byte[] header = new byte[RAS_HEADER_SIZE]; in.readFully(header); int magic = ArrayConverter.getIntBE(header, 0); if (magic != RAS_MAGIC) { throw new WrongFileFormatException("This stream is not a valid " + "Sun RAS stream (bad magic: " + Integer.toHexString(magic) + " instead of " + Integer.toHexString(RAS_MAGIC)); } width = ArrayConverter.getIntBE(header, 4); height = ArrayConverter.getIntBE(header, 8); if (width < 1 || height < 1) { throw new InvalidFileStructureException("Width and height must both " + "be larger than zero; found width=" + width + ", height=" + height + "."); } setBoundsIfNecessary(width, height); checkBounds(width, height); depth = ArrayConverter.getIntBE(header, 12); switch (depth) { case(1): { bytesPerRow = (width + 7) / 8; break; } case(8): { bytesPerRow = width; break; } case(24): { bytesPerRow = width * 3; break; } default: { throw new UnsupportedTypeException("Depths other than 1, 8 and 24 " + "unsupported when reading RAS stream; found " + depth); } } paddingBytes = (bytesPerRow % 2); numColors = 1 << depth; //length = ArrayConverter.getIntBE(header, 16); type = ArrayConverter.getIntBE(header, 20); if (type != COMPRESSION_NONE) { throw new UnsupportedTypeException("Only uncompressed " + "RAS streams are read; found " + type); } mapType = ArrayConverter.getIntBE(header, 24); mapLength = ArrayConverter.getIntBE(header, 28); if (mapLength != 0) { if (depth != 8) { throw new UnsupportedTypeException("Cannot handle Sun RAS " + "input streams with color maps and a depth other than " + "8 (found " + depth + ")."); } if (mapLength != 768) { throw new UnsupportedTypeException("Cannot handle Sun RAS " + "input streams with color maps of a length different " + "than 768; found " + mapLength); } if (mapType != 1) { throw new UnsupportedTypeException("Cannot handle Sun RAS " + "input streams with color maps of a type other than " + "1; found " + mapType); } palette = readPalette(); } else { palette = null; } } private IntegerImage readImage() throws InvalidFileStructureException, java.io.IOException { RGB24Image rgb24Image = null; Paletted8Image paletted8Image = null; IntegerImage result = null; int numChannels = 1; int bytesPerRow = 0; switch(depth) { case(8): { paletted8Image = new MemoryPaletted8Image(width, height, palette); result = paletted8Image; numChannels = 1; bytesPerRow = width; break; } case(24): { rgb24Image = new MemoryRGB24Image(width, height); result = rgb24Image; numChannels = 3; bytesPerRow = width; break; } } setImage(result); byte[][] buffer = new byte[numChannels][]; for (int i = 0; i < numChannels; i++) { buffer[i] = new byte[bytesPerRow]; } for (int y = 0, destY = -getBoundsY1(); destY <= getBoundsY2(); y++, destY++) { if (rgb24Image != null) { for (int x = 0; x < width; x++) { buffer[RGBIndex.INDEX_BLUE][x] = in.readByte(); buffer[RGBIndex.INDEX_GREEN][x] = in.readByte(); buffer[RGBIndex.INDEX_RED][x] = in.readByte(); } rgb24Image.putByteSamples(RGBIndex.INDEX_RED, 0, destY, getBoundsWidth(), 1, buffer[0], getBoundsX1()); rgb24Image.putByteSamples(RGBIndex.INDEX_GREEN, 0, destY, getBoundsWidth(), 1, buffer[1], getBoundsX1()); rgb24Image.putByteSamples(RGBIndex.INDEX_BLUE, 0, destY, getBoundsWidth(), 1, buffer[2], getBoundsX1()); } else if (paletted8Image != null) { in.readFully(buffer[0], 0, width); paletted8Image.putByteSamples(0, 0, destY, getBoundsWidth(), 1, buffer[0], getBoundsX1()); } if (in.skipBytes(paddingBytes) != paddingBytes) { throw new InvalidFileStructureException("Could not skip " + "byte after row " + y + "."); } setProgress(y, getBoundsY2() + 1); } return result; } private Palette readPalette() throws InvalidFileStructureException, java.io.IOException { Palette result = new Palette(256, 255); for (int channel = 0; channel < 3; channel++) { int channelIndex = -1; switch(channel) { case(0): { channelIndex = Palette.INDEX_RED; break; } case(1): { channelIndex = Palette.INDEX_GREEN; break; } case(2): { channelIndex = Palette.INDEX_BLUE; break; } } for (int i = 0; i < numColors; i++) { int value = in.readUnsignedByte(); if (value == -1) { throw new InvalidFileStructureException("Unexpected end " + "of file when reading Sun RAS palette."); } result.putSample(channelIndex, i, value); } } return result; } private void save() throws IOException, UnsupportedTypeException, WrongParameterException { PixelImage image = getImage(); if (image == null || (!(image instanceof Paletted8Image))) { throw new UnsupportedTypeException("Must have non-null image that is a Paletted8Image."); } saveHeader(image); if (image instanceof Paletted8Image) { saveData((Paletted8Image)image); } } private void saveData(Paletted8Image image) throws IOException { byte[] row = new byte[getBoundsWidth()]; for (int y1 = 0, y2 = getBoundsY1(); y1 < getBoundsHeight(); y1++, y2++) { image.getByteSamples(0, getBoundsX1(), y2, row.length, 1, row, 0); out.write(row); int num = paddingBytes; while (num-- > 0) { out.write(0); } setProgress(y1, getBoundsHeight()); } } private void saveHeader(PixelImage image) throws IOException, UnsupportedTypeException, WrongParameterException { setBoundsIfNecessary(width, height); checkBounds(width, height); out.writeInt(RAS_MAGIC); int width = getBoundsWidth(); out.writeInt(width); int height = getBoundsHeight(); out.writeInt(height); if (image instanceof BilevelImage) { depth = 1; bytesPerRow = (width + 7) / 8; } else if (image instanceof Gray8Image || image instanceof Paletted8Image) { depth = 8; bytesPerRow = width; } else if (image instanceof RGB24Image) { bytesPerRow = width * 3; depth = 24; } else { throw new UnsupportedTypeException("Cannot store image types " + "other than bilevel, gray8, paletted8 and RGB24."); } out.writeInt(depth); paddingBytes = (bytesPerRow % 2); numColors = 1 << depth; length = bytesPerRow * getBoundsHeight(); out.writeInt(length); // length out.writeInt(COMPRESSION_NONE); // type mapType = 1; mapLength = 0; if (image instanceof Paletted8Image) { mapLength = 768; } out.writeInt(mapType); out.writeInt(mapLength); if (image instanceof Paletted8Image) { Paletted8Image pal = (Paletted8Image)image; savePalette(pal.getPalette()); } } private void savePalette(Palette palette) throws java.io.IOException { int numEntries = palette.getNumEntries(); for (int channel = 0; channel < 3; channel++) { int channelIndex = -1; switch(channel) { case(0): { channelIndex = Palette.INDEX_RED; break; } case(1): { channelIndex = Palette.INDEX_GREEN; break; } case(2): { channelIndex = Palette.INDEX_BLUE; break; } } for (int i = 0; i < 256; i++) { int value = 0; if (i < numEntries) { value = palette.getSample(channelIndex, i); } out.write(value); } } } public String suggestFileExtension(PixelImage image) { return ".ras"; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/ImageCodec.java0000664000000000000000000006427210377274111024127 0ustar /* * ImageCodec * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.util.Vector; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.Operation; import net.sourceforge.jiu.ops.WrongParameterException; import net.sourceforge.jiu.data.PixelImage; /** * The base class for image codecs, operations to read images from or write them to streams. * A codec should support one file format only. * The word codec is derived from enCOder DECoder. * *

Usage

* The codecs differ quite a bit in what they support. * But here are two code snippets that demonstrate how to do loading and saving in general. * *

Load image

*
 * ImageCodec codec = new BMPCodec(); // BMPCodec is just an example
 * codec.setFile("image.bmp", CodecMode.LOAD);
 * codec.process();
 * PixelImage image = codec.getImage();
 * 
* *

Save image

*
 * PixelImage image = ...; // the image to be saved
 * ImageCodec codec = new BMPCodec(); // BMPCodec is just an example
 * codec.setFile("image.bmp", CodecMode.SAVE);
 * codec.setImage(image);
 * codec.process();
 * 
* *

I/O objects

* There are several set and get methods for I/O objects, including * DataInput, DataOutput, InputStream, OutputStream and RandomAccessFile. * If you are just using the codec (and not developing one) make it easier * for yourself and use {@link #setFile(String, CodecMode)}. * That way the picking of the right type of I/O class and the creation of a * buffered stream wrapper is done automatically. *

* Codecs have different requirements concerning I/O objects. * If an image is to be loaded, it's enough for some formats to linearly read * from an {@link java.io.InputStream} to load the image. * However, some formats (like TIFF) require random access. *

* When implementing a codec, take care that as many I/O classes as possible can be used. * If possible, call {@link #getInputAsDataInput} when loading and {@link #getOutputAsDataOutput} * when saving. * That way, input / output streams, RandomAccessFiles and arbitrary DataInput / DataOutput objects * can be used. *

*

Mode

* Codecs can be used to save images or load them, or both. * As was g; by default, no mode (of enumeration type {@link CodecMode}) * is specified and {@link #getMode()} returns null. * Mode only has two possible values, {@link CodecMode#LOAD} and * {@link CodecMode#SAVE}. * In some cases, the codec can find out whether to load or save from the I/O objects * that were given to it; if it has an input stream, something must be loaded, * if it has an output stream, something is to be saved. * If a codec demands a {@link RandomAccessFile}, there is no way to find out * the mode automatically, that is why {@link #setRandomAccessFile} also has an * argument of type {@link CodecMode}. *

* Bounds; to load or save only part of an image. * Defining bounds is optional; by default, the complete image is loaded * or saved (no bounds). * Using {@link #setBounds(int, int, int, int)}, one can specify the * rectangle which will be loaded or saved. *

* PixelImage object; get and set methods for the image which is to be * loaded or saved. * If an image is to be loaded, a PixelImage object can optionally be specified so that the image will * be written to that object; image type and resolution must of course match the image * from input. * Normally, the codec will create the appropriate image object * itself. * If an image is to be saved, an image object must be provided, otherwise there * is nothing to do. *

* Image index; the index of the image that is to be loaded (int value, default * is 0). For image formats that support more than one image in one stream, the index of the * image to be loaded (zero-based) can be specified using {@link #setImageIndex(int)}. * *

Textual comments

* Some file formats allow for the inclusion of textual comments, to * store a description, creator, copyright owner or anything else within the image * file without actually drawing that text on the image itself. * Some codecs support reading and writing of comments. * *

Other methods

*

* Each file format must be able to return its name ({@link #getFormatName()}) and * file extensions that are typical for it ({@link #getFileExtensions()}). *

* A related method suggests a file extension for a given PixelImage object ({@link #suggestFileExtension(PixelImage)}). * That method need not be implemented, the default version returns simply null. * However, it is encouraged that codec implementors provide this method as well. * Most file formats only have one typical extension (e. g. .bmp). * However, for a file format like PNM, the extension depends on the image type (a grayscale * image would end in .pgm, a color image in .ppm). *

* @author Marco Schmidt */ public abstract class ImageCodec extends Operation { private int boundsX1; private int boundsY1; private int boundsX2; private int boundsY2; private boolean boundsAvail; private int boundsWidth; private int boundsHeight; private Vector comments; private int dpiX; private int dpiY; private DataInput din; private DataOutput dout; private PixelImage image; private int imageIndex; private InputStream in; private CodecMode mode; private OutputStream out; private RandomAccessFile raf; /** * This constructor will be called by descendants. * The bounds state is initialized to no bounds. */ public ImageCodec() { super(); comments = new Vector(); removeBounds(); } /** * Appends a comment to the internal list of comments. * If the argument comment is non-null, it will be added to the internal * list of comment strings. * @param comment the comment to be added */ public void appendComment(String comment) { if (comment != null) { comments.addElement(comment); } } /** * If bounds were defined for this codec, this method tests if the * bounds rectangle fits into the rectangle (0, 0) / (width - 1, height - 1). * If the bounds are incorrect, a {@link WrongParameterException} * is thrown, otherwise nothing happens. * To be used within codecs that support the bounds concept. */ public void checkBounds(int width, int height) throws WrongParameterException { if (!hasBounds()) { return; } int x1 = getBoundsX1(); if (x1 >= width) { throw new WrongParameterException("Codec bounds x1 (" + x1 + ") must be smaller than image width (" + width + ")."); } int x2 = getBoundsX2(); if (x2 >= width) { throw new WrongParameterException("Codec bounds x2 (" + x2 + ") must be smaller than image width (" + width + ")."); } int y1 = getBoundsY1(); if (y1 >= height) { throw new WrongParameterException("Codec bounds y1 (" + y1 + ") must be smaller than image height (" + height + ")."); } int y2 = getBoundsY2(); if (y2 >= height) { throw new WrongParameterException("Codec bounds y2 (" + y2 + ") must be smaller than image height (" + height + ")."); } } /** * If an image object was provided to be used for loading via {@link #setImage}, * this method checks if its resolution is the same as the bounds' resolution. * If the two differ, a {@link net.sourceforge.jiu.ops.WrongParameterException} is thrown. * @throws WrongParameterException if image resolution and bounds dimension differ */ public void checkImageResolution() throws WrongParameterException { PixelImage image = getImage(); if (image != null) { if (image.getWidth() != getBoundsWidth()) { throw new WrongParameterException("Specified input image must have width equal to getBoundsWidth()."); } if (image.getHeight() != getBoundsHeight()) { throw new WrongParameterException("Specified input image must have height equal to getBoundsHeight()."); } } } /** * Calls the close method of all input and output I/O objects * that were given to this object. * Catches and ignores any IOException objects that may be * thrown in the process. * Note that not all I/O objects have a close method (e.g. {@link java.io.DataInput} * and {@link java.io.DataOutput} have not). */ public void close() { try { if (in != null) { in.close(); } if (out != null) { out.close(); } if (raf != null) { raf.close(); } } catch (IOException ioe) { } } /** * Returns x coordinate of the upper left corner of the bounds. * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, * otherwise the return value is undefined. * @return x coordinate of the upper left corner of the bounds */ public int getBoundsX1() { return boundsX1; } /** * Returns x coordinate of the lower right corner of the bounds. * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, * otherwise the return value is undefined. * @return x coordinate of the lower right corner of the bounds */ public int getBoundsX2() { return boundsX2; } /** * Returns y coordinate of the upper left corner of the bounds. * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, * otherwise the return value is undefined. * @return y coordinate of the upper left corner of the bounds */ public int getBoundsY1() { return boundsY1; } /** * Returns y coordinate of the lower right corner of the bounds. * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, * otherwise the return value is undefined. * @return y coordinate of the lower right corner of the bounds */ public int getBoundsY2() { return boundsY2; } /** * Returns the height of the rectangle specified by bounds. * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, * otherwise the return value is undefined. * This equals {@link #getBoundsY2()} - {@link #getBoundsY1()} + 1. * @return height of bounds rectangle */ public int getBoundsHeight() { return boundsHeight; } /** * Returns the width of the rectangle specified by bounds. * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, * otherwise the return value is undefined. * This equals {@link #getBoundsX2()} - {@link #getBoundsX1()} + 1. * @return width of bounds rectangle */ public int getBoundsWidth() { return boundsWidth; } /** * Returns a comment from the internal list of comments. * @param index the index of the comment to be returned, must be from * 0 to {@link #getNumComments()} - 1; if this is not * the case, null will be returned * @see #getNumComments * @see #appendComment * @see #removeAllComments */ public String getComment(int index) { if (index >= 0 && index < comments.size()) { return (String)comments.elementAt(index); } else { return null; } } /** * Returns a {@link java.io.DataInput} object if one was provided * via {@link #setDataInput(DataInput)} or null otherwise. * @return the DataInput object */ public DataInput getDataInput() { return din; } /** * Returns a {@link java.io.DataOutput} object if one was provided * via {@link #setDataOutput(DataOutput)} or null otherwise. * @return the DataInput object */ public DataOutput getDataOutput() { return dout; } /** * Returns the horizontal physical resolution of the image associated * with this codec. * This resolution value was either retrieved from an image file or * set via {@link #setDpi(int, int)}. * @return horizontal physical resolution in dpi * @see #getDpiY */ public int getDpiX() { return dpiX; } /** * Returns the vertical physical resolution of the image associated * with this codec. * This resolution value was either retrieved from an image file or * set via {@link #setDpi(int, int)}. * @return horizontal physical resolution in dpi * @see #getDpiX */ public int getDpiY() { return dpiY; } /** * Returns all file extensions that are typical for this file format. * The default implementation in ImageCodec returns null. * The file extension strings should include a leading dot * and are supposed to be lower case (if that is allowed for * the given file format). * Example: {".jpg", ".jpeg"} for the JPEG file format. * @return String array with typical file extensions */ public String[] getFileExtensions() { return null; } /** * Returns the name of the file format supported by this codec. * All classes extending {@link ImageCodec} must override this method. * When overriding, leave out any words in a particular language so * that this format name can be understood by everyone. * Usually it is enough to return the format creator plus a typical * abbreviation, e.g. Microsoft BMP or Portable Anymap (PNM). * @return name of the file format supported by this codec */ public abstract String getFormatName(); /** * Returns the image object stored in this codec. * This is either an image given to this object via * {@link #setImage(PixelImage)} or it was created by the codec * itself during a loading operation. * @return PixelImage object stored in this codec */ public PixelImage getImage() { return image; } /** * Returns the zero-based index of the image to be loaded. * Default is zero. * @return zero-based image index value */ public int getImageIndex() { return imageIndex; } /** * Returns a {@link java.io.DataInput} object if one was specified * using {@link #setDataInput(DataInput)}, * or creates a {@link java.io.DataInputStream} if an * {@link java.io.InputStream} was specified, * or returns a {@link java.io.RandomAccessFile} if one was specified * (RandomAccessFile implements DataInput). * If neither of those has been given to this object, null is returned. * @return DataInput object or null */ public DataInput getInputAsDataInput() { DataInput din = getDataInput(); if (din != null) { return din; } RandomAccessFile raf = getRandomAccessFile(); if (getMode() == CodecMode.LOAD && raf != null) { return raf; } InputStream in = getInputStream(); if (in != null) { if (in instanceof DataInput) { return (DataInput)in; } else { return new DataInputStream(in); } } return null; } /** * Returns an {@link java.io.InputStream} object that was given to * this codec via {@link #setInputStream(InputStream)} * (or null otherwise). * @return InputStream object */ public InputStream getInputStream() { return in; } /** * Return the MIME * (Multipurpose Internet Mail Extensions) type strings for this format, or null * if none are available. * @return MIME type strings or null */ public abstract String[] getMimeTypes(); /** * Returns the mode this codec is in. * Can be null, so that the codec will have to find out * itself what to do. * @return codec mode (load or save) */ public CodecMode getMode() { return mode; } /** * Returns the current number of comments in the internal comment list. * @return number of comments in the internal comment list */ public int getNumComments() { return comments.size(); } /** * Attempts to return an output object as a {@link java.io.DataOutput} object. * @return a DataOutput object or null if that was not possible */ public DataOutput getOutputAsDataOutput() { DataOutput dout = getDataOutput(); if (dout != null) { return dout; } OutputStream out = getOutputStream(); if (out != null) { if (out instanceof DataOutput) { return (DataOutput)out; } else { return new DataOutputStream(out); } } RandomAccessFile raf = getRandomAccessFile(); if (raf != null && getMode() == CodecMode.SAVE) { return raf; } return null; } /** * Returns an {@link java.io.OutputStream} object that was given to * this codec via {@link #setOutputStream(OutputStream)} * (or null otherwise). * @return OutputStream object */ public OutputStream getOutputStream() { return out; } /** * Returns a {@link java.io.RandomAccessFile} object that was given to * this codec via {@link #setRandomAccessFile(RandomAccessFile, CodecMode)} * (or null otherwise). * @return RandomAccessFile object */ public RandomAccessFile getRandomAccessFile() { return raf; } /** * Returns if bounds have been specified. * @return if bounds have been specified * @see #removeBounds() * @see #setBounds(int, int, int, int) */ public boolean hasBounds() { return boundsAvail; } protected void initModeFromIOObjects() throws MissingParameterException { if (getMode() != null) { return; } if (getInputStream() != null || getDataInput() != null) { mode = CodecMode.LOAD; } else if (getOutputStream() != null || getDataOutput() != null) { mode = CodecMode.SAVE; } else { throw new MissingParameterException("No streams or files available."); } } /** * Returns if this codec is able to load images in the file format supported by this codec. * If true is returned this does not necessarily mean that all files in this * format can be read, but at least some. * @return if loading is supported */ public abstract boolean isLoadingSupported(); /** * Returns if this codec is able to save images in the file format supported by this codec. * If true is returned this does not necessarily mean that all types files in this * format can be written, but at least some. * @return if saving is supported */ public abstract boolean isSavingSupported(); /** * Returns if an image row given by its number (zero-based) must be loaded * in the context of the current bounds. *

* Example: if vertical bounds have been set to 34 and 37, image rows 34 to * 37 as arguments to this method would result in true, anything * else (e.g. 12 or 45) would result in false. * * @param row the number of the row to be checked * @return if row must be loaded, regarding the current bounds */ public boolean isRowRequired(int row) { if (hasBounds()) { return (row >= boundsY1 && row <= boundsY2); } else { return (row >= 0 && row < getImage().getHeight()); } } /** * Returns if the tile formed by the argument coordinates * form a rectangle that overlaps with the bounds. * If no bounds were defined, returns true. * @param x1 * @param y1 * @param x2 * @param y2 * @return if the argument tile is required */ public boolean isTileRequired(int x1, int y1, int x2, int y2) { if (hasBounds()) { return ! (getBoundsY2() < y1 || getBoundsY1() > y2 || getBoundsX2() < x1 || getBoundsX1() > x2); } else { return true; } } /** * Removes all entries from the internal list of comments. */ public void removeAllComments() { comments.removeAllElements(); } /** * If bounds were set using {@link #setBounds(int, int, int, int)}, these * bounds are no longer regarded after the call to this method. */ public void removeBounds() { boundsAvail = false; } /** * Sets the bounds of a rectangular part of the image that * is to be loaded or saved, instead of the complete image. */ public void setBounds(int x1, int y1, int x2, int y2) { if (x1 < 0 || y1 < 0 || x2 < x1 || y2 < y1) { throw new IllegalArgumentException("Not a valid bounds rectangle: " + "x1=" + x1 + ", y1=" + y1 + ", x2=" + x2 + ", y2=" + y2); } boundsX1 = x1; boundsY1 = y1; boundsX2 = x2; boundsY2 = y2; boundsAvail = true; boundsWidth = x2 - x1 + 1; boundsHeight = y2 - y1 + 1; } /** * If no bounds have been set ({@link #hasBounds()} returns false), * this method will set the bounds to 0, 0, width - 1, height - 1. * By calling this method somewhere in the codec, no distinction has to * be made for the two cases bounds have been defined and * bounds have not been defined. * @param width width of the image to be loaded or saved * @param height height of the image to be loaded or saved */ public void setBoundsIfNecessary(int width, int height) { if (!hasBounds()) { setBounds(0, 0, width - 1, height - 1); } } /** * Specifies a DataInput object to be used for loading. * @param dataInput DataInput object to be used for loading an image */ public void setDataInput(DataInput dataInput) { din = dataInput; } /** * Sets a {@link java.io.DataOutput} object to be used for saving * an image. * @param dataOutput the object to be used for output */ public void setDataOutput(DataOutput dataOutput) { dout = dataOutput; } /** * Sets the DPI values to be stored in the file to the argument values. * @param horizontalDpi horizontal physical resolution in DPI (dots per inch) * @param verticalDpi vertical physical resolution in DPI (dots per inch) * @see #getDpiX * @see #getDpiY */ public void setDpi(int horizontalDpi, int verticalDpi) { dpiX = horizontalDpi; dpiY = verticalDpi; } /** * Gives a File object and a codec mode to this codec and attempts * to initialize the appropriate I/O objects. * Simply calls {@link #setFile(String, CodecMode)} with the absolute * path of the File object. * @param file File object for the file to be used * @param codecMode defines whether an image is to be loaded from or saved to the file */ public void setFile(File file, CodecMode codecMode) throws IOException, UnsupportedCodecModeException { setFile(file.getAbsolutePath(), codecMode); } /** * Gives a file name and codec mode to the codec which will then * try to create the corresponding I/O object. * The default implementation in ImageCodec creates a DataInputStream object * wrapped around a BufferedInputStream wrapped around a FileInputStream for * CodecMode.LOAD. * Similar for CodecMode.SAVE: a DataOutputStream around a BufferedOutputStream * object around a FileOutputStream object. * Codecs that need different I/O objects must override this method * (some codecs may need random access and thus require a RandomAccessFile object). * @param fileName name of the file to be used for loading or saving * @param codecMode defines whether file is to be used for loading or saving */ public void setFile(String fileName, CodecMode codecMode) throws IOException, UnsupportedCodecModeException { if (codecMode == CodecMode.LOAD) { if (isLoadingSupported()) { setInputStream(new BufferedInputStream(new FileInputStream(fileName))); } else { throw new UnsupportedCodecModeException("Loading is not supported for this codec (" + getFormatName() + ")."); } } else { if (isSavingSupported()) { setOutputStream(new BufferedOutputStream(new FileOutputStream(fileName))); } else { throw new UnsupportedCodecModeException("Saving is not supported for this codec (" + getFormatName() + ")."); } } } /** * Give an image to this codec to be used for loading an image into it * or saving the image. * @param img image object to save or to load data into */ public void setImage(PixelImage img) { image = img; } /** * Sets the index of the image to be loaded to the argument value * (which must be zero or larger). * @param index int index value (zero-based) of the image to be loaded * @throws IllegalArgumentException if the argument is negative */ public void setImageIndex(int index) { if (index < 0) { throw new IllegalArgumentException("The index must be 0 or larger."); } imageIndex = index; } /** * An {@link java.io.InputStream} can be given to this codec using this method. * @param inputStream InputStream object to read from */ public void setInputStream(InputStream inputStream) { in = inputStream; } /** * A method to give an {@link java.io.OutputStream} to this codec to be used * for saving an image. * @param outputStream the output stream to be used by this codec */ public void setOutputStream(OutputStream outputStream) { out = outputStream; } /** * A method to give a {@link java.io.RandomAccessFile} to this codec to be used * for loading or saving an image. * It is not possible to determine from a RandomAccessFile object whether it * was opened in read-only or read-and-write mode. * To let the codec know whether the object is to be used for loading or saving * the second argument is of type CodecMode. * @param randomAccessFile the file to be used for loading or saving * @param codecMode tells the codec whether the file is to be used for loading or saving */ public void setRandomAccessFile(RandomAccessFile randomAccessFile, CodecMode codecMode) { if (randomAccessFile == null) { throw new IllegalArgumentException("Argument RandomAccessFile must be non-null."); } if (codecMode == null) { throw new IllegalArgumentException("Argument codec mode must be non-null."); } raf = randomAccessFile; mode = codecMode; } /** * Attempts to suggest a filename extension. * The type of the argument image will be taken into consideration, * although this will be necessary for some file formats only (as an * example, PNM has different extensions for different image types, see * {@link PNMCodec}). * This default implementation always returns null. * @param image the image that is to be written to a file * @return the file extension, including a leading dot, or null if no file extension can be recommended */ public String suggestFileExtension(PixelImage image) { return null; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/0000775000000000000000000000000010546532262022220 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGCodec.java0000664000000000000000000001467310523756546024571 0ustar /* * JPEGCodec * * Copyright (c) 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.jpeg; import java.io.DataInput; import java.io.File; import java.io.IOException; import net.sourceforge.jiu.codecs.CodecMode; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.codecs.WrongFileFormatException; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.util.ArrayConverter; /** * A codec for the JPEG file format. *

Supported JPEG types

* The codec is still under development. * Nothing can be read with it right now. * Writing JPEG files is not even in development stage. *

Credits

* "JPEG Still Image Data Compression Standard" by William B. Pennebaker and * Joan L. Mitchell. Published 1993 by Van Nostrand Reinhold. * ISBN 0-442-01272-1. * This book is referenced as P&M throughout the source code. * It's an invaluable resource for anything related to JPEG. * @author Marco Schmidt * @since 0.13.0 */ public class JPEGCodec extends ImageCodec { private DataInput in; private void decodeScan(JPEGData jpegData) { JPEGFrame frame = jpegData.getFrame(); int width = frame.getWidth(); int height = frame.getHeight(); /*Gray8Image image = new MemoryGray8Image(width, height); int x = 0; int y = 0;*/ int numMCUs = ((width + 7) / 8) * ((height + 7) / 8); while (numMCUs > 0) { numMCUs--; } } public String[] getFileExtensions() { return new String[] {".jpg", ".jpeg"}; } public String getFormatName() { return "JPEG File Interchange Format"; } public String[] getMimeTypes() { // image/pjpeg for progressive JPEGs is not included // because it's not supported by this codec (yet) return new String[] {"image/jpeg"}; } public boolean isLoadingSupported() { return true; } public boolean isSavingSupported() { return false; } private void load() throws OperationFailedException, WrongFileFormatException { in = getInputAsDataInput(); if (in == null) { throw new MissingParameterException( "Input object missing (could not retrieve via getAsDataInput)."); } try { // read and check the first two bytes byte[] data = new byte[4]; in.readFully(data, 0, 2); int signature = ArrayConverter.getShortBEAsInt(data, 0); if (signature != JPEGConstants.JFIF_SIGNATURE) { throw new WrongFileFormatException( "Not a JFIF file (first two bytes are not 0xff 0xd8)."); } // continuously read markers, updating a JPEGData object JPEGData jpegData = new JPEGData(); while (true) { // read and decode marker type and length in.readFully(data); int marker = ArrayConverter.getShortBEAsInt(data, 0); int length = ArrayConverter.getShortBEAsInt(data, 2); // read the actual marker information readMarker(jpegData, marker, length); } } catch (IOException ioe) { throw new OperationFailedException("Error reading from input."); } finally { close(); } } public static void main(String[] args) throws Exception { if (args.length < 1) { System.err.println("Need JPEG file names as arguments."); System.exit(1); } for (int i = 0; i < args.length; i++) { String fileName = args[i]; JPEGCodec codec = new JPEGCodec(); codec.setFile(new File(fileName), CodecMode.LOAD); codec.process(); //PixelImage image = codec.getImage(); } } public void process() throws MissingParameterException, OperationFailedException, WrongFileFormatException { initModeFromIOObjects(); if (getMode() == CodecMode.LOAD) { load(); } else if (getMode() == CodecMode.SAVE) { throw new OperationFailedException("Saving is not supported."); } else { throw new OperationFailedException("Unsupported codec mode."); } } private void readMarker(JPEGData jpegData, int marker, int length) throws InvalidFileStructureException, IOException, UnsupportedTypeException { // make sure marker is a valid marker if ((marker >> 8) != 0xff) { // TODO: instead of giving up, search for next occurrence of 0xff (error recovery) throw new InvalidFileStructureException("Marker " + marker + " does not have 0xff in its top eight bits."); } // zero out everything but the least significant byte marker &= 0xff; // decrease two bytes for the marker length field length -= 2; // react on marker, possible reactions: // - call corresponding method if available // - throw exception if unsupported // - skip otherwise (= marker is unknown) switch(marker) { case(JPEGConstants.MARKER_DHT): { JPEGMarkerReader.readHuffmanTables(in, jpegData, length); break; } case(JPEGConstants.MARKER_DQT): { JPEGMarkerReader.readQuantizationTables(in, jpegData, length); break; } // Start of frame: Huffman Baseline DCT case(JPEGConstants.MARKER_SOF0): { JPEGMarkerReader.readStartOfFrame(in, jpegData, marker, length); break; } // unsupported frame types case(JPEGConstants.MARKER_SOF1): case(JPEGConstants.MARKER_SOF2): case(JPEGConstants.MARKER_SOF3): case(JPEGConstants.MARKER_SOF5): case(JPEGConstants.MARKER_SOF6): case(JPEGConstants.MARKER_SOF7): case(JPEGConstants.MARKER_SOF9): case(JPEGConstants.MARKER_SOFA): case(JPEGConstants.MARKER_SOFB): case(JPEGConstants.MARKER_SOFD): case(JPEGConstants.MARKER_SOFE): case(JPEGConstants.MARKER_SOFF): { throw new UnsupportedTypeException( "Unsupported JPEG SOF type: " + Integer.toHexString(marker)); } case(JPEGConstants.MARKER_SOS): { JPEGMarkerReader.readStartOfScan(in, jpegData, length); decodeScan(jpegData); break; } default: { System.out.println("Unknown marker: " + Integer.toHexString(marker)); // skip marker data while (length > 0) { int skipped = in.skipBytes(length); if (skipped > 0) { length -= skipped; } } break; } } } public String suggestFileExtension(PixelImage image) { return ".jpg"; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGScan.java0000664000000000000000000000117210332236672024415 0ustar package net.sourceforge.jiu.codecs.jpeg; /** * Data class for information from a JPEG scan header (stored in an SOS marker). * @author Marco Schmidt * @since 0.13.0 */ public class JPEGScan { private int numComponents; private JPEGScanComponentSpecification[] compSpecs; public int getNumComponents() { return numComponents; } public void setNumComponents(int i) { numComponents = i; } public JPEGScanComponentSpecification[] getCompSpecs() { return compSpecs; } public void setCompSpecs(JPEGScanComponentSpecification[] specifications) { compSpecs = specifications; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGFrame.java0000664000000000000000000000340210421163624024554 0ustar /* * JPEGFrame * * Copyright (c) 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.jpeg; /** * Data class to store information on a JPEG frame. * A frame here is JPEG terminology for a complete image * and has nothing to do with GUI components like JFrame objects * in Swing. * @author Marco Schmidt * @since 0.13.0 */ public class JPEGFrame { private JPEGFrameComponent[] components; private int numComponents; private int height; private int samplePrecision; private int width; public JPEGFrameComponent[] getComponents() { return components; } public int getHeight() { return height; } public int getNumComponents() { return numComponents; } public int getSamplePrecision() { return samplePrecision; } public int getWidth() { return width; } public void setComponents(JPEGFrameComponent[] components) { this.components = components; } public void setHeight(int i) { height = i; } public void setNumComponents(int i) { numComponents = i; } public void setSamplePrecision(int i) { samplePrecision = i; } public void setWidth(int i) { width = i; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("#components="); sb.append(numComponents); sb.append("/precision="); sb.append(samplePrecision); sb.append("/width="); sb.append(width); sb.append("/height="); sb.append(height); if (components != null) { for (int i = 0; i < components.length; i++) { JPEGFrameComponent comp = components[i]; if (comp != null) { sb.append("/"); sb.append(comp.toString()); } } } return sb.toString(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGConstants.java0000664000000000000000000000524510522414465025511 0ustar /* * JPEGConstants * * Copyright (c) 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.jpeg; /** * Constants necessary to encode and decode JPEG streams. * @author Marco Schmidt * @since 0.13.0 */ public final class JPEGConstants { /** * Length of sample block edge, in samples (8). */ public static final int BLOCK_EDGE_LENGTH = 8; /** * 16 bit value that denotes the beginning of a JPEG stream (0xffd8). */ public static final int JFIF_SIGNATURE = 0xffd8; /** * DHT (define Huffman table) marker ID value. */ public static final int MARKER_DHT = 0xc4; /** * DQT (define quantization table) marker ID value. */ public static final int MARKER_DQT = 0xdb; /** * SOF0 (start of frame, type 0) marker ID value. */ public static final int MARKER_SOF0 = 0xc0; /** * SOF1 (start of frame, type 1) marker ID value. */ public static final int MARKER_SOF1 = 0xc1; /** * SOF2 (start of frame, type 2) marker ID value. */ public static final int MARKER_SOF2 = 0xc2; /** * SOF3 (start of frame, type 3) marker ID value. */ public static final int MARKER_SOF3 = 0xc3; /** * SOF5 (start of frame, type 5) marker ID value. */ public static final int MARKER_SOF5 = 0xc5; /** * SOF6 (start of frame, type 6) marker ID value. */ public static final int MARKER_SOF6 = 0xc6; /** * SOF7 (start of frame, type 7) marker ID value. */ public static final int MARKER_SOF7 = 0xc7; /** * SOF9 (start of frame, type 9) marker ID value. */ public static final int MARKER_SOF9 = 0xc9; /** * SOFa (start of frame, type a) marker ID value. */ public static final int MARKER_SOFA = 0xca; /** * SOFb (start of frame, type b) marker ID value. */ public static final int MARKER_SOFB = 0xcb; /** * SOFd (start of frame, type d) marker ID value. */ public static final int MARKER_SOFD = 0xcd; /** * SOFe (start of frame, type e) marker ID value. */ public static final int MARKER_SOFE = 0xce; /** * SOFf (start of frame, type f) marker ID value. */ public static final int MARKER_SOFF = 0xcf; /** * SOS (start of scan) marker ID value. */ public static final int MARKER_SOS = 0xda; /** * Maximum length of a Huffman code in bit (16). */ public static final int MAX_HUFFMAN_CODE_LENGTH = 16; /** * Number of samples in a block of samples (64). */ public static final int SAMPLES_PER_BLOCK = BLOCK_EDGE_LENGTH * BLOCK_EDGE_LENGTH; /** * Empty private constructor to prevent instantiation of this class. */ private JPEGConstants() { } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGHuffmanTable.java0000664000000000000000000000502010421163377026061 0ustar /* * JPEGHuffmanTable * * Copyright (c) 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.jpeg; /** * Data class that stores a single Huffman table, including class * (AC or DC), ID and codes for the 16 possible bit lengths from 1 to 16. * @author Marco Schmidt * @since 0.13.0 */ public class JPEGHuffmanTable { public static final int TABLE_CLASS_AC = 1; public static final int TABLE_CLASS_DC = 0; private int id; private int classAcDc; private int[][] codes; private int[] huffCode; private int[] huffSize; private int lastK; public void createDecoderTables() { generateSizeTable(); generateCodeTable(); // TODO: F.15 } /** * Initialize huffCode from huffSize. * P&M figure C.2, p. 406f. */ private void generateCodeTable() { huffCode = new int[257]; int k = 0; int code = 0; int si = huffSize[0]; while (true) { while (true) { huffCode[k] = code; code++; k++; if (huffSize[k] != si) { break; } } if (huffSize[k] == 0) { break; } while (true) { code <<= 1; si++; if (huffSize[k] == si) { break; } } } } /** * Initialize huffSize and lastK from codes. * P&M figure C.1, p. 405f. */ private void generateSizeTable() { huffSize = new int[257]; int i = 1; int j = 1; int k = 0; while (true) { while (true) { if (j > codes[i].length) { break; } huffSize[k] = i; k++; j++; } i++; j = 1; if (i > JPEGConstants.MAX_HUFFMAN_CODE_LENGTH) { break; } } huffSize[k] = 0; lastK = k; } public int getClassAcDc() { return classAcDc; } public int[][] getCodes() { return codes; } public int getId() { return id; } public void setClassAcDc(int i) { classAcDc = i; } public void setCodes(int[][] is) { codes = is; } public void setId(int i) { id = i; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("id="); sb.append(id); sb.append("/class="); sb.append(classAcDc == TABLE_CLASS_AC ? "AC" : "DC"); if (codes != null) { sb.append("/codes(length,number)="); for (int i = 0; i < codes.length; i++) { if (codes[i].length > 0) { sb.append(" "); sb.append((i+1)); sb.append(":"); sb.append(codes[i].length); } } } return sb.toString(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGData.java0000664000000000000000000000257110421163521024375 0ustar /* * JPEGData * * Copyright (c) 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.jpeg; import java.util.Vector; /** * Data for decoding or encoding images from or to * JPEG File Interchange Format (JFIF) files. * @author Marco Schmidt * @since 0.13.0 */ public class JPEGData { private JPEGFrame frame; private Vector huffmanTables = new Vector(); private Vector quantTables = new Vector(); private Vector scans = new Vector(); public void addQuantizationTable(JPEGQuantizationTable table) { quantTables.add(table); } public void addHuffmanTable(JPEGHuffmanTable table) { huffmanTables.add(table); } public void addScan(JPEGScan scan) { scans.add(scan); } public JPEGFrame getFrame() { return frame; } /** * Return a quantization table with a given id or * null on failure to find it. * @param id integer id value of table * @return actual table or null on failure */ public JPEGQuantizationTable getQuantizationTable(int id) { JPEGQuantizationTable table = null; int index = 0; while (index < quantTables.size()) { table = (JPEGQuantizationTable)quantTables.elementAt(index++); if (table.getId() == id) { return table; } } return null; } public void setFrame(JPEGFrame newFrame) { frame = newFrame; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/package.html0000664000000000000000000000043610104434033024467 0ustar

Provides classes to read images from JPEG bitstreams.

Package Specification

Related Documentation

././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGScanComponentSpecification.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGScanComponentSpecification.0000664000000000000000000000127710332236675030150 0ustar package net.sourceforge.jiu.codecs.jpeg; /** * Data class to store information on one component in one scan. * @author Marco Schmidt * @since 0.13.0 */ public class JPEGScanComponentSpecification { private int component; private int acEntropyTable; private int dcEntropyTable; public int getAcEntropyTable() { return acEntropyTable; } public int getComponent() { return component; } public int getDcEntropyTable() { return dcEntropyTable; } public void setAcEntropyTable(int i) { acEntropyTable = i; } public void setComponent(int i) { component = i; } public void setDcEntropyTable(int i) { dcEntropyTable = i; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGMarkerReader.java0000664000000000000000000002177710542200240026073 0ustar /* * JPEGMarkerReader * * Copyright (c) 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.jpeg; import java.io.DataInput; import java.io.IOException; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.util.ArrayConverter; /** * Static helper methods to read various JPEG bitstream headers from a * {@link java.io.DataInput} source into objects of the appropriate * data classes. * Objects are then added to a {@link JPEGData} object. * @author Marco Schmidt * @since 0.14.0 */ public class JPEGMarkerReader { /** * Private constructor to prevent instantiation. */ private JPEGMarkerReader() { } public static void readHuffmanTables(DataInput in, JPEGData jpegData, int length) throws InvalidFileStructureException, IOException { while (length > 0) { if (length < 17) { throw new InvalidFileStructureException("DHT marker - " + "less than 17 bytes left."); } JPEGHuffmanTable table = new JPEGHuffmanTable(); int classId = in.readUnsignedByte(); // class (AC or DC) int tableClass = (classId >> 4) & 0x0f; if (tableClass != JPEGHuffmanTable.TABLE_CLASS_AC && tableClass != JPEGHuffmanTable.TABLE_CLASS_DC) { throw new InvalidFileStructureException( "Table class in DHT marker is neither AC nor DC."); } table.setClassAcDc(tableClass); // ID int tableId = classId & 0x0f; table.setId(tableId); // codes byte[] numCodes = new byte[16]; in.readFully(numCodes); length -= 17; int[][] codes = new int[16][]; for (int codeLength = 1; codeLength <= 16; codeLength++) { int number = numCodes[codeLength - 1] & 0xff; if (length < number) { throw new InvalidFileStructureException( "Not enough data left in DHT marker for codes of " + "length " + codeLength + "."); } codes[codeLength - 1] = new int[number]; for (int codeIndex = 0; codeIndex < number; codeIndex++) { codes[codeLength - 1][codeIndex] = in.readUnsignedByte(); } length -= number; } table.setCodes(codes); System.out.println(table); jpegData.addHuffmanTable(table); } } /** * Read quantization tables from a DQT marker. * P&M 7.8.3, p. 118f. * @param jpegData data object which will store the table(s) * @param length length of marker * @throws InvalidFileStructureException if the DQT contains invalid data * @throws IOException on reading errors */ public static void readQuantizationTables(DataInput in, JPEGData jpegData, int length) throws InvalidFileStructureException, IOException { while (length > 0) { int precId = in.readUnsignedByte(); length--; JPEGQuantizationTable table = new JPEGQuantizationTable(); int elementPrecision = (precId >> 4) & 0x0f; switch(elementPrecision) { case(0): { table.setElementPrecision(8); break; } case(1): { table.setElementPrecision(16); break; } default: { throw new InvalidFileStructureException( "Not a valid value for quantization table element precision: " + elementPrecision + " (expected 0 or 1)."); } } int id = precId & 0x0f; if (id > 3) { throw new InvalidFileStructureException( "Not a valid quantization table id: " + id + " (expected 0 to 3)."); } table.setId(id); // check if there's enough data left for the table elements int tableDataLength = JPEGConstants.SAMPLES_PER_BLOCK * (elementPrecision + 1); if (length < tableDataLength) { throw new InvalidFileStructureException( "Not enough data left in marker for quantization table data: " + length + "byte(s) (expected at least " + tableDataLength + ")."); } int[] data = new int[JPEGConstants.SAMPLES_PER_BLOCK]; for (int i = 0; i < data.length; i++) { switch(elementPrecision) { case(0): { data[i] = in.readUnsignedByte(); break; } case(1): { data[i] = in.readShort() & 0xffff; break; } } } length -= tableDataLength; table.setData(data); jpegData.addQuantizationTable(table); } } public static void readStartOfFrame(DataInput in, JPEGData jpegData, int marker, int length) throws InvalidFileStructureException, IOException, UnsupportedTypeException { if (length < 9) { throw new InvalidFileStructureException( "JPEG SOF header length must be at least nine bytes; got " + length + "."); } byte[] data = new byte[6]; in.readFully(data); JPEGFrame frame = new JPEGFrame(); // sample precision int samplePrecision = data[0] & 0xff; if (samplePrecision != 8) { throw new UnsupportedTypeException("Unsupported JPEG sample precision: " + samplePrecision); } frame.setSamplePrecision(samplePrecision); // height int height = ArrayConverter.getShortBEAsInt(data, 1); if (height < 1) { throw new InvalidFileStructureException( "JPEG SOF height value must be 1 or higher; got " + height + "."); } frame.setHeight(height); // width int width = ArrayConverter.getShortBEAsInt(data, 3); if (width < 1) { throw new InvalidFileStructureException( "JPEG SOF width value must be 1 or higher; got " + width + "."); } frame.setWidth(width); // number of components (= channels) int numComponents = data[5] & 0xff; if (numComponents != 1) { throw new UnsupportedTypeException("Unsupported number of JPEG components: " + numComponents); } frame.setNumComponents(numComponents); if (length - 6 != numComponents * 3) { throw new InvalidFileStructureException( "SOF marker has not expected size for " + numComponents + " component(s); got " + length + " instead of " + (6 + numComponents * 3)); } JPEGFrameComponent[] frameComponents = new JPEGFrameComponent[numComponents]; for (int componentIndex = 0; componentIndex < numComponents; componentIndex++) { in.readFully(data, 0, 3); JPEGFrameComponent frameComponent = new JPEGFrameComponent(); int componentIdentifier = data[0] & 0xff; frameComponent.setComponentId(componentIdentifier); int horizontalSamplingFactor = (data[1] & 0xf0) >> 4; frameComponent.setHorizontalSamplingFactor(horizontalSamplingFactor); int verticalSamplingFactor = data[1] & 0x0f; frameComponent.setVerticalSamplingFactor(verticalSamplingFactor); int quantizationTable = data[2] & 0xff; frameComponent.setQuantizationTableId(quantizationTable); frameComponents[componentIndex] = frameComponent; } frame.setComponents(frameComponents); jpegData.setFrame(frame); System.out.println(frame); } /** * Read an SOS (start of scan) marker. * P&M 7.6, p. 113. * @param in source to read marker information from * @param jpegData {@link JPEGData} object to update with information from the marker * @param length size of marker in bytes * @throws InvalidFileStructureException if encountered data does not follow the JPEG standard * @throws IOException on I/O errors * @throws UnsupportedTypeException if encountered data is valid but unsupported by this package */ public static void readStartOfScan(DataInput in, JPEGData jpegData, int length) throws InvalidFileStructureException, IOException, UnsupportedTypeException { if (length < 6) { throw new InvalidFileStructureException("SOS marker must be at least six bytes large; got " + length); } int numScanComponents = in.readUnsignedByte(); length--; if (numScanComponents < 1) { throw new InvalidFileStructureException("Number of scan components must be one or larger."); } JPEGScan scan = new JPEGScan(); scan.setNumComponents(numScanComponents); JPEGScanComponentSpecification[] specs = new JPEGScanComponentSpecification[numScanComponents]; byte[] data = new byte[2]; for (int i = 0; i < numScanComponents; i++) { in.readFully(data); length -= 2; int componentSelector = data[0] & 0xff; int dcTableSelector = (data[1] & 0xf0) >> 4; int acTableSelector = data[1] & 0x0f; JPEGScanComponentSpecification spec = new JPEGScanComponentSpecification(); spec.setAcEntropyTable(acTableSelector); spec.setDcEntropyTable(dcTableSelector); spec.setComponent(componentSelector); specs[i] = spec; } scan.setCompSpecs(specs); if (length != 3) { throw new InvalidFileStructureException( "Expected exactly three bytes left after scan component specs; got " + length); } data = new byte[3]; in.readFully(data); // TODO: add these as fields to JPEGScan, check their values and call the scan setters to copy the data /* int spectralStart = data[0] & 0xff; int spectralEnd = data[1] & 0xff; int highSuccessiveBit = (data[2] & 0xf0) >> 4; int lowSuccessiveBit = data[2] & 0x0f; */ jpegData.addScan(scan); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGQuantizationTable.java0000664000000000000000000000130310332236666027166 0ustar package net.sourceforge.jiu.codecs.jpeg; /** * Data for a JPEG bitstream quantization table. * Includes quantization data, table id (0 to 3) * and element precision in bits (8 or 16). * @author Marco Schmidt * @since 0.13.0 */ public class JPEGQuantizationTable { private int[] data; private int elementPrecision; private int id; public int[] getData() { return data; } public int getElementPrecision() { return elementPrecision; } public int getId() { return id; } public void setData(int[] is) { data = is; } public void setElementPrecision(int i) { elementPrecision = i; } public void setId(int i) { id = i; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/jpeg/JPEGFrameComponent.java0000664000000000000000000000267610410704642026452 0ustar /* * JPEGFrameComponent * * Copyright (c) 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.jpeg; /** * Data class for information on a JPEG frame component. * @author Marco Schmidt * @since 0.13.0 */ public class JPEGFrameComponent { private int componentId; private int horizontalSamplingFactor; private int verticalSamplingFactor; private int quantizationTableId; public int getComponentId() { return componentId; } public int getHorizontalSamplingFactor() { return horizontalSamplingFactor; } public int getQuantizationTableId() { return quantizationTableId; } public int getVerticalSamplingFactor() { return verticalSamplingFactor; } public void setComponentId(int i) { componentId = i; } public void setHorizontalSamplingFactor(int i) { horizontalSamplingFactor = i; } public void setQuantizationTableId(int i) { quantizationTableId = i; } public void setVerticalSamplingFactor(int i) { verticalSamplingFactor = i; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("component id="); sb.append(componentId); sb.append(", horiz. sampling="); sb.append(horizontalSamplingFactor); sb.append(", vert. sampling="); sb.append(verticalSamplingFactor); sb.append(", quantization table="); sb.append(quantizationTableId); return sb.toString(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/package.html0000664000000000000000000000656710324344522023564 0ustar

Provides classes to read images from and save them to files (or streams) in various file formats. In some cases, it will be sufficient for codecs to use {@link java.io.InputStream} and {@link java.io.OutputStream}. This approach should be picked when possible, as it allows for maximum flexibility--input and output streams can be files, network streams, standard input / output (so that data can be piped on the command line) and more. However, in some cases, it will be necessary for codecs to use {@link java.io.RandomAccessFile} in order to seek to various places in the file.

Note that the codecs are (sometimes more, sometimes less) far from being finished or even stable. Please do not rely on them for important data. Treat the codecs (and the rest of JIU) as beta software.

Package Specification

All image codecs must extend the {@link net.sourceforge.jiu.codecs.ImageCodec} class. They may support only a subset of all possible flavors of an image file format. As an example, they may choose to support only reading or only writing. As {@link net.sourceforge.jiu.codecs.ImageCodec} extends {@link net.sourceforge.jiu.ops.Operation}, the progress notification system can (and should) be used. If the codecs are used in GUI (graphical user interface) applications, users could be shown a progress bar--loading and saving can be time-consuming.

{@link net.sourceforge.jiu.codecs.ImageCodec} provides methods to specify a rectangular part of an image. The information should be used by the codec to read or write only that part of the image. That way, loading only a part of a huge image is typically faster and consumes less memory. When saving a part of a huge image, an additional crop operation becomes unncessary this way.

Related Documentation

Obviously, JIU can only benefit from supporting more file formats. If you want to contribute codecs to JIU (remember that you must provide your code under the GNU General Public License), please contact the maintainer at the JIU website.

However, note that no code will be integrated that uses patented algorithms. For better or worse, algorithms like LZW compression (used optionally in TIFF and mandatory in GIF) or arithmetic entropy coding (used in some parts of JPEG) are patented in several countries. In order to use these algorithms, one has to pay license fees. This is not acceptable for JIU and therefore no code will be integrated that uses such algorithms. In cases like GIF or TIFF/LZW that is very unfortunate because these formats a certain popularity that would make them interesting to support. Read the GIF section of the Open Directory, it contains several links to sites that explain the situation (from different points of view).

File format specifications can be found at the following resources:

java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/WrongFileFormatException.java0000664000000000000000000000220607741250131027055 0ustar /* * WrongFileFormatException * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import net.sourceforge.jiu.ops.OperationFailedException; /** * This exception is thrown during image loading. * If a codec is sure that the file or input stream that was given to * it is not in the format supported by that codec, an instance of * this exception class is thrown. * This is usually the case if the magic byte sequence of that format * is not found at the beginning of the stream. *

* If there were errors during loading because of file corruption, an * {@link InvalidFileStructureException} should be thrown. *

* If the format is recognized but cannot be loaded because the codec * does not fully support the file format, a {@link UnsupportedTypeException} * should be thrown. * * @author Marco Schmidt * @see InvalidFileStructureException * @see UnsupportedTypeException */ public class WrongFileFormatException extends OperationFailedException { public WrongFileFormatException(String message) { super(message); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/UnsupportedCodecModeException.java0000664000000000000000000000123707741250131030106 0ustar /* * UnsupportedCodecModeException * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import net.sourceforge.jiu.ops.OperationFailedException; /** * This exception is thrown when a codec does not support the * codec mode wanted by the user. * Example: A user gives an OutputStream to a codec, indicating that an * image is to be saved, but the codec only supports loading. * @author Marco Schmidt * @since 0.10.0 */ public class UnsupportedCodecModeException extends OperationFailedException { public UnsupportedCodecModeException(String message) { super(message); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/BMPCodec.java0000664000000000000000000006330510377274012023517 0ustar /* * BMPCodec * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.codecs.WrongFileFormatException; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.ByteChannelImage; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.util.ArrayConverter; /** * A codec to read and write Windows BMP image files. *

* Typical file extensions are .bmp and .rle * (the latter is only used for compressed files). *

Bounds

*

* This codec supports the bounds concept for loading and saving. *

*

Supported BMP types when loading

* * There is no support for 16 bpp images or BI_BITFIELDS compression (for lack of test files). *

*

Supported JIU image data classes when saving to BMP

* * There is no support for compressed BMP files when saving. *

*

I/O classes

* BMPCodec works with all input and output classes supported by ImageCodec * ({@link java.io.InputStream}, {@link java.io.OutputStream}, * {@link java.io.DataInput}, {@link java.io.DataOutput}, * {@link java.io.RandomAccessFile}). *

Problems

*

The RLE-compressed BMP files that I could test this codec on seem to * have an end-of-line code at the end of every line instead of relying * on the decoder to know when it has unpacked enough bytes for a line. * Whenever this codec encounters an EOL symbol and has a current column * value of 0, the EOL is ignored. *

Usage examples

* Write an image to a BMP file. *
 * BMPCodec codec = new BMPCodec();
 * codec.setImage(image);
 * codec.setFile("out.bmp", CodecMode.SAVE);
 * codec.process();
 * codec.close();
 * 
* Read an image from a BMP file. *
 * BMPCodec codec = new BMPCodec();
 * codec.setFile("image.bmp", CodecMode.LOAD);
 * codec.process();
 * codec.close();
 * PixelImage image = codec.getImage();
 * 
* @author Marco Schmidt * @since 0.7.0 */ public class BMPCodec extends ImageCodec { private int colorDepth; private int compression; private int dataOffset; private int imageHeight; private int imageWidth; private DataInput in; private DataOutput out; private Palette palette; public String[] getFileExtensions() { return new String[] {".bmp", ".rle"}; } public String getFormatName() { return "Windows BMP"; } public String[] getMimeTypes() { return new String[] {"image/bmp", "image/x-ms-bmp"}; } public boolean isLoadingSupported() { return true; } public boolean isSavingSupported() { return true; } private void load() throws MissingParameterException, OperationFailedException, UnsupportedTypeException, WrongFileFormatException { in = getInputAsDataInput(); if (in == null) { throw new MissingParameterException("Input stream / random access file parameter missing."); } // now write the output stream try { loadHeader(); loadStream(); } catch (IOException ioe) { // wrap any I/O failures in an OperationFailedException throw new OperationFailedException("I/O failure: " + ioe.toString()); } } private void loadCompressedPaletted4Stream() throws IOException { Paletted8Image image = (Paletted8Image)getImage(); int imageBytesPerRow = imageWidth; int bytesPerRow = imageBytesPerRow; int mod = bytesPerRow % 4; if (mod != 0) { bytesPerRow += 4 - mod; } final int COLUMNS = getBoundsWidth(); final int ROWS = imageHeight - getBoundsY1(); final int X1 = getBoundsX1(); int processedRows = 0; byte[] row = new byte[bytesPerRow]; int x = 0; int y = imageHeight - 1; boolean endOfBitmap = false; boolean delta = false; int newX = 0; int newY = 0; while (processedRows < ROWS) { int v1 = in.readUnsignedByte(); int v2 = in.readUnsignedByte(); if (v1 == 0) { switch(v2) { case(0): { // end of line if (x != 0) { x = bytesPerRow; } break; } case(1): { // end of bitmap x = bytesPerRow; endOfBitmap = true; break; } case(2): { // delta delta = true; newX = x + in.readUnsignedByte(); newY = y - in.readUnsignedByte(); x = bytesPerRow; break; } default: { // copy the next v2 (3..255) samples from file to output // two samples are packed into one byte // if the number of bytes used to pack is not a multiple of 2, // an additional padding byte is in the stream and must be skipped boolean paddingByte = (((v2 + 1) / 2) % 2) != 0; while (v2 > 1) { int packed = in.readUnsignedByte(); int sample1 = (packed >> 4) & 0x0f; int sample2 = packed & 0x0f; row[x++] = (byte)sample1; row[x++] = (byte)sample2; v2 -= 2; } if (v2 == 1) { int packed = in.readUnsignedByte(); int sample = (packed >> 4) & 0x0f; row[x++] = (byte)sample; } if (paddingByte) { v2 = in.readUnsignedByte(); } break; } } } else { // rle: replicate the two samples in v2 as many times as v1 says byte sample1 = (byte)((v2 >> 4) & 0x0f); byte sample2 = (byte)(v2 & 0x0f); while (v1 > 1) { row[x++] = sample1; row[x++] = sample2; v1 -= 2; } if (v1 == 1) { row[x++] = sample1; } } // end of line? if (x == bytesPerRow) { if (y <= getBoundsY2()) { image.putByteSamples(0, 0, y - getBoundsY1(), COLUMNS, 1, row, X1); } if (delta) { x = newX; y = newY; } else { x = 0; y--; } if (endOfBitmap) { processedRows = ROWS - 1; } setProgress(processedRows, ROWS); processedRows++; delta = false; } } } private void loadCompressedPaletted8Stream() throws IOException { Paletted8Image image = (Paletted8Image)getImage(); int imageBytesPerRow = imageWidth; int bytesPerRow = imageBytesPerRow; int mod = bytesPerRow % 4; if (mod != 0) { bytesPerRow += 4 - mod; } final int COLUMNS = getBoundsWidth(); final int ROWS = imageHeight - getBoundsY1(); final int X1 = getBoundsX1(); int processedRows = 0; byte[] row = new byte[bytesPerRow]; int x = 0; int y = imageHeight - 1; boolean endOfBitmap = false; boolean delta = false; int newX = 0; int newY = 0; while (processedRows < ROWS) { int v1 = in.readUnsignedByte(); int v2 = in.readUnsignedByte(); if (v1 == 0) { switch(v2) { case(0): { // end of line if (x != 0) { x = bytesPerRow; } break; } case(1): { // end of bitmap x = bytesPerRow; endOfBitmap = true; break; } case(2): { // delta delta = true; newX = x + in.readUnsignedByte(); newY = y - in.readUnsignedByte(); x = bytesPerRow; break; } default: { // copy the next v2 (3..255) bytes from file to output boolean paddingByte = (v2 % 2) != 0; while (v2-- > 0) { row[x++] = (byte)in.readUnsignedByte(); } if (paddingByte) { v2 = in.readUnsignedByte(); } break; } } } else { // rle: replicate v2 as many times as v1 says byte value = (byte)v2; while (v1-- > 0) { row[x++] = value; } } // end of line? if (x == bytesPerRow) { if (y <= getBoundsY2()) { image.putByteSamples(0, 0, y - getBoundsY1(), COLUMNS, 1, row, X1); } if (delta) { x = newX; y = newY; } else { x = 0; y--; } if (endOfBitmap) { processedRows = ROWS - 1; } setProgress(processedRows, ROWS); processedRows++; delta = false; } } } private void loadHeader() throws IOException, MissingParameterException, OperationFailedException, UnsupportedTypeException, WrongFileFormatException { byte[] header = new byte[54]; in.readFully(header); if (header[0] != 'B' || header[1] != 'M') { throw new WrongFileFormatException("Not a BMP file (first two bytes are not 0x42 0x4d)."); } dataOffset = ArrayConverter.getIntLE(header, 0x0a); if (dataOffset < 54) { throw new InvalidFileStructureException("BMP data expected to be 54dec or larger, got " + dataOffset); } imageWidth = ArrayConverter.getIntLE(header, 0x12); imageHeight = ArrayConverter.getIntLE(header, 0x16); if (imageWidth < 1 || imageHeight < 1) { throw new InvalidFileStructureException("BMP image width and height must be larger than 0, got " + imageWidth + " x " + imageHeight); } int planes = ArrayConverter.getShortLE(header, 0x1a); if (planes != 1) { throw new InvalidFileStructureException("Can only handle BMP number of planes = 1, got " + planes); } colorDepth = ArrayConverter.getShortLE(header, 0x1c); if (colorDepth != 1 && colorDepth != 4 && colorDepth != 8 && colorDepth != 24) { // TO DO: add support for 16 bpp BMP reading throw new InvalidFileStructureException("Unsupported BMP color depth: " + colorDepth); } compression = ArrayConverter.getIntLE(header, 0x1e); if (compression != 0 && !(compression == 1 && colorDepth == 8) && !(compression == 2 && colorDepth == 4)) { throw new InvalidFileStructureException("Unsupported BMP compression type / color depth combination: " + compression + " / " + colorDepth); } float dpiXValue = ArrayConverter.getIntLE(header, 0x26) / (100.0f / 2.54f); float dpiYValue = ArrayConverter.getIntLE(header, 0x2a) / (100.0f / 2.54f); setDpi((int)dpiXValue, (int)dpiYValue); } private void loadStream() throws IOException, MissingParameterException, OperationFailedException, UnsupportedTypeException { // 1. check bounds, initialize them if necessary setBoundsIfNecessary(imageWidth, imageHeight); checkBounds(imageWidth, imageHeight); // 2. read palette if the image isn't truecolor (even monochrome BMPs have a palette) int bytesToSkip; if (colorDepth <= 8) { int numPaletteEntries = 1 << colorDepth; int expectedPaletteSize = 4 * numPaletteEntries; int headerSpaceLeft = dataOffset - 54; bytesToSkip = headerSpaceLeft - expectedPaletteSize; if (bytesToSkip < 0) { throw new InvalidFileStructureException("Not enough space in header for palette with " + numPaletteEntries + "entries."); } palette = new Palette(numPaletteEntries); for (int index = 0; index < numPaletteEntries; index++) { int blue = in.readUnsignedByte(); int green = in.readUnsignedByte(); int red = in.readUnsignedByte(); in.readUnsignedByte(); palette.put(index, red, green, blue); } } else { bytesToSkip = dataOffset - 54; } // 3. seek to beginning of image data while (bytesToSkip > 0) { int skipped = in.skipBytes(bytesToSkip); if (skipped > 0) { bytesToSkip -= skipped; } } // 4. check if we have an image object that we are supposed to reuse // if there is one, check if it has the correct type // if there is none, create a new one PixelImage image = getImage(); if (image == null) { switch(colorDepth) { case(1): { setImage(new MemoryBilevelImage(getBoundsWidth(), getBoundsHeight())); break; } case(4): case(8): { setImage(new MemoryPaletted8Image(getBoundsWidth(), getBoundsHeight(), palette)); break; } case(24): { setImage(new MemoryRGB24Image(getBoundsWidth(), getBoundsHeight())); break; } // loadHeader would have thrown an exception for any other color depths } } else { // TODO: check if image is of correct type } // now read actual image data if (compression == 0) { loadUncompressedStream(); } else if (compression == 1) { loadCompressedPaletted8Stream(); } else if (compression == 2) { loadCompressedPaletted4Stream(); } } private void loadUncompressedBilevelStream() throws IOException, OperationFailedException { if ((getBoundsX1() % 8) != 0) { throw new OperationFailedException("When loading bilevel images, horizontal X1 bounds must be a multiple of 8; got " + getBoundsX1()); } BilevelImage image = (BilevelImage)getImage(); int imageBytesPerRow = (imageWidth + 7) / 8; int bytesPerRow = imageBytesPerRow; int mod = bytesPerRow % 4; if (mod != 0) { bytesPerRow += 4 - mod; } int bottomRowsToSkip = imageHeight - 1 - getBoundsY2(); int bytesToSkip = bottomRowsToSkip * bytesPerRow; while (bytesToSkip > 0) { int skipped = in.skipBytes(bytesToSkip); if (skipped > 0) { bytesToSkip -= skipped; } } final int COLUMNS = getBoundsWidth(); final int ROWS = getBoundsHeight(); final int SRC_OFFSET = getBoundsX1() / 8; final int SRC_BIT_OFFSET = getBoundsX1() % 8; int y = image.getHeight() - 1; int processedRows = 0; byte[] row = new byte[bytesPerRow]; while (processedRows < ROWS) { in.readFully(row); image.putPackedBytes(0, y, COLUMNS, row, SRC_OFFSET, SRC_BIT_OFFSET); y--; setProgress(processedRows, ROWS); processedRows++; } } private void loadUncompressedPaletted4Stream() throws IOException { Paletted8Image image = (Paletted8Image)getImage(); int imageBytesPerRow = (imageWidth + 1) / 2; int bytesPerRow = imageBytesPerRow; int mod = bytesPerRow % 4; if (mod != 0) { bytesPerRow += 4 - mod; } int bottomRowsToSkip = imageHeight - 1 - getBoundsY2(); int bytesToSkip = bottomRowsToSkip * bytesPerRow; while (bytesToSkip > 0) { int skipped = in.skipBytes(bytesToSkip); if (skipped > 0) { bytesToSkip -= skipped; } } final int COLUMNS = getBoundsWidth(); final int ROWS = getBoundsHeight(); final int X1 = getBoundsX1(); int y = image.getHeight() - 1; int processedRows = 0; byte[] row = new byte[bytesPerRow]; byte[] samples = new byte[bytesPerRow * 2]; while (processedRows < ROWS) { in.readFully(row); ArrayConverter.decodePacked4Bit(row, 0, samples, 0, row.length); image.putByteSamples(0, 0, y, COLUMNS, 1, samples, X1); y--; setProgress(processedRows, ROWS); processedRows++; } } private void loadUncompressedPaletted8Stream() throws IOException { Paletted8Image image = (Paletted8Image)getImage(); int imageBytesPerRow = imageWidth; int bytesPerRow = imageBytesPerRow; int mod = bytesPerRow % 4; if (mod != 0) { bytesPerRow += 4 - mod; } int bottomRowsToSkip = imageHeight - 1 - getBoundsY2(); int bytesToSkip = bottomRowsToSkip * bytesPerRow; while (bytesToSkip > 0) { int skipped = in.skipBytes(bytesToSkip); if (skipped > 0) { bytesToSkip -= skipped; } } final int COLUMNS = getBoundsWidth(); final int ROWS = getBoundsHeight(); final int X1 = getBoundsX1(); int y = image.getHeight() - 1; int processedRows = 0; byte[] row = new byte[bytesPerRow]; while (processedRows < ROWS) { in.readFully(row); image.putByteSamples(0, 0, y, COLUMNS, 1, row, X1); y--; setProgress(processedRows, ROWS); processedRows++; } } private void loadUncompressedRgb24Stream() throws IOException { RGB24Image image = (RGB24Image)getImage(); int imageBytesPerRow = imageWidth * 3; int bytesPerRow = imageBytesPerRow; int mod = bytesPerRow % 4; if (mod != 0) { bytesPerRow += 4 - mod; } int bottomRowsToSkip = imageHeight - 1 - getBoundsY2(); int bytesToSkip = bottomRowsToSkip * bytesPerRow; while (bytesToSkip > 0) { int skipped = in.skipBytes(bytesToSkip); if (skipped > 0) { bytesToSkip -= skipped; } } final int COLUMNS = getBoundsWidth(); final int ROWS = getBoundsHeight(); final int X1 = getBoundsX1(); int y = image.getHeight() - 1; int processedRows = 0; byte[] row = new byte[bytesPerRow]; byte[] samples = new byte[COLUMNS]; while (processedRows < ROWS) { in.readFully(row); // copy red samples to array samples and store those samples for (int x = X1 * 3 + 2, i = 0; i < COLUMNS; x += 3, i++) { samples[i] = row[x]; } image.putByteSamples(RGBIndex.INDEX_RED, 0, y, COLUMNS, 1, samples, 0); // copy green samples to array samples and store those samples for (int x = X1 * 3 + 1, i = 0; i < COLUMNS; x += 3, i++) { samples[i] = row[x]; } image.putByteSamples(RGBIndex.INDEX_GREEN, 0, y, COLUMNS, 1, samples, 0); // copy blue samples to array samples and store those samples for (int x = X1 * 3, i = 0; i < COLUMNS; x += 3, i++) { samples[i] = row[x]; } image.putByteSamples(RGBIndex.INDEX_BLUE, 0, y, COLUMNS, 1, samples, 0); y--; setProgress(processedRows, ROWS); processedRows++; } } private void loadUncompressedStream() throws IOException, OperationFailedException { switch(colorDepth) { case(1): { loadUncompressedBilevelStream(); break; } case(4): { loadUncompressedPaletted4Stream(); break; } case(8): { loadUncompressedPaletted8Stream(); break; } case(24): { loadUncompressedRgb24Stream(); break; } } } public void process() throws MissingParameterException, OperationFailedException { initModeFromIOObjects(); if (getMode() == CodecMode.LOAD) { load(); } else { save(); } } private void save() throws MissingParameterException, OperationFailedException, UnsupportedTypeException { // check parameters of this operation // 1 image to be saved // 1.1 is it available? PixelImage image = getImage(); if (image == null) { throw new MissingParameterException("No image available."); } // 1.2 is it supported? if (!(image instanceof Paletted8Image || image instanceof Gray8Image || image instanceof BilevelImage || image instanceof RGB24Image)) { throw new UnsupportedTypeException("Unsupported image type: " + image.getClass().getName()); } // 2 is output stream available? out = getOutputAsDataOutput(); if (out == null) { throw new MissingParameterException("Output stream / random access file parameter missing."); } // now write the output stream try { writeStream(); } catch (IOException ioe) { throw new OperationFailedException("I/O failure: " + ioe.toString()); } } public String suggestFileExtension(PixelImage image) { return ".bmp"; } private void writeHeader(PixelImage image, int filesize, int offset, int numBits) throws IOException { out.write(0x42); // 'B' out.write(0x4d); // 'M' writeInt(filesize); writeShort(0); writeShort(0); writeInt(offset); writeInt(40); // BITMAP_INFO header length writeInt(getBoundsWidth()); writeInt(getBoundsHeight()); writeShort(1); // # of planes writeShort(numBits); writeInt(0); // compression (0 = none) writeInt(filesize - offset); // size of image data in bytes writeInt((int)(getDpiX() * (100f / 2.54f))); // horizontal resolution in dpi writeInt((int)(getDpiY() * (100f / 2.54f))); // vertical resolution in dpi writeInt(0); // # of used colors writeInt(0); // # of important colors } // we can't use out.writeInt because we need little endian byte order private void writeInt(int value) throws IOException { out.write(value & 0xff); out.write((value >> 8) & 0xff); out.write((value >> 16) & 0xff); out.write((value >> 24) & 0xff); } /** * Write the palette associated with the image getImage(). * Required not only for image objects that implement PalettedImage * but also for BilevelImage and Grayscale8Image. * For the latter two the palette values must be explicitly written into the file. */ private void writePalette() throws IOException { PixelImage pi = getImage(); if (pi == null) { return; } if (pi instanceof Paletted8Image) { // always write 256 entries; if there aren't enough // in the palette, fill it up to 256 with (0, 0, 0, 0) Palette palette = ((Paletted8Image)pi).getPalette(); for (int i = 0; i < 256; i++) { if (i < palette.getNumEntries()) { out.write(palette.getSample(RGBIndex.INDEX_BLUE, i)); out.write(palette.getSample(RGBIndex.INDEX_GREEN, i)); out.write(palette.getSample(RGBIndex.INDEX_RED, i)); out.write(0); } else { out.writeInt(0); // writes four 0 bytes } } } if (pi instanceof Gray8Image) { for (int i = 0; i < 256; i++) { out.write(i); out.write(i); out.write(i); out.write(0); } } if (pi instanceof BilevelImage) { for (int i = 0; i < 2; i++) { out.write(i * 255); out.write(i * 255); out.write(i * 255); out.write(0); } } } // we can't use out.writeShort because we need little endian byte order private void writeShort(int value) throws IOException { out.write(value & 0xff); out.write((value >> 8) & 0xff); } private void writeStream() throws IOException { PixelImage image = getImage(); setBoundsIfNecessary(image.getWidth(), image.getHeight()); int width = getBoundsWidth(); int height = getBoundsHeight(); ByteChannelImage bcimg = null; BilevelImage bilevelImage = null; RGB24Image rgbimg = null; int bytesPerRow = 0; int offset = 54; int numBits = 0; int numPackedBytes = 0; if (image instanceof Paletted8Image || image instanceof Gray8Image) { bcimg = (ByteChannelImage)image; bytesPerRow = width; offset += 1024; numBits = 8; } else if (image instanceof BilevelImage) { bilevelImage = (BilevelImage)image; numPackedBytes = (width + 7) / 8; bytesPerRow = numPackedBytes; offset += 8; numBits = 1; } else if (image instanceof RGB24Image) { rgbimg = (RGB24Image)image; bytesPerRow = width * 3; numBits = 24; } if ((bytesPerRow % 4) != 0) { bytesPerRow = ((bytesPerRow + 3) / 4) * 4; } int filesize = offset + bytesPerRow * height; writeHeader(image, filesize, offset, numBits); writePalette(); byte[] row = new byte[bytesPerRow]; final int X1 = getBoundsX1(); for (int y = getBoundsY2(), processed = 0; processed < height; y--, processed++) { if (bilevelImage != null) { bilevelImage.getPackedBytes(X1, y, width, row, 0, 0); } else if (bcimg != null) { bcimg.getByteSamples(0, 0, y, width, 1, row, 0); } else if (rgbimg != null) { int offs = 0; for (int x = X1; x < X1 + width; x++) { row[offs++] = rgbimg.getByteSample(RGBIndex.INDEX_BLUE, x, y); row[offs++] = rgbimg.getByteSample(RGBIndex.INDEX_GREEN, x, y); row[offs++] = rgbimg.getByteSample(RGBIndex.INDEX_RED, x, y); } } else { // error } out.write(row); setProgress(processed, height); if (getAbort()) { break; } } close(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/0000775000000000000000000000000010546532076022226 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFRational.java0000664000000000000000000000367707741250131025320 0ustar /* * TIFFRational * * Copyright (c) 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; /** * Data class to store a TIFF rational number. * A TIFF rational number is a fraction given by 32 bit integer numerator and denominator values. * It is one of the data types used in TIFF tags ({@link TIFFTag}). * For more information on TIFF's internals, see {@link TIFFCodec}, which lists a few links * to TIFF specification documents. * @author Marco Schmidt */ public class TIFFRational { private int numerator; private int denominator; /** * Creates a TiffRational object from the arguments. * @param numerator the numerator of the fraction stored in this object * @param denominator the denominator of the fraction stored in this object * @throws IllegalArgumentException if denominator is 0 (division by zero is not allowed) */ public TIFFRational(int numerator, int denominator) { if (denominator == 0) { throw new IllegalArgumentException("A zero denominator is not allowed."); } this.numerator = numerator; this.denominator = denominator; } /** * Returns the denominator value that was given to the constructor. * @return denominator value */ public int getDenominator() { return denominator; } /** * Returns the fraction as a double value. * @return the fraction stored in this object * @see #getAsFloat */ public double getAsDouble() { return (double)numerator / (double)denominator; } /** * Returns the fraction as a float value. * @return the fraction stored in this object * @see #getAsDouble */ public float getAsFloat() { return (float)numerator / (float)denominator; } /** * Returns the numerator value that was given to the constructor. * @return numerator value */ public int getNumerator() { return numerator; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFDecoderLogLuv.java0000664000000000000000000000770207741250131026236 0ustar /* * TIFFDecoderLogLuv * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; import java.io.DataInput; import java.io.IOException; import net.sourceforge.jiu.codecs.tiff.TIFFConstants; import net.sourceforge.jiu.codecs.tiff.TIFFDecoder; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.ops.MissingParameterException; /** * A TIFF decoder for files compressed with the LogLuv RLE method. * This compression algorithm has the value 34676 * ({@link TIFFConstants#COMPRESSION_SGI_LOG_RLE}) * in the compression tag of an image file directory. * Only image data with a photometric interpretation value of * {@link TIFFConstants#PHOTOMETRIC_TRUECOLOR_LOGLUV} can be compressed with this method. *

* This implementation is based on the file tif_luv.c which * is part of the TIFF library libtiff. * The original implementation was written by Greg W. Larson. *

* Learn more about the color type and its encoding on Greg's page * LogLuv * Encoding for TIFF Images. * You will also find numerous sample image files there. * @author Marco Schmidt * @since 0.10.0 */ public class TIFFDecoderLogLuv extends TIFFDecoder { private DataInput in; private int compressedSize; private int tileWidth; private boolean rle; public void decode() throws InvalidFileStructureException, IOException { byte[] row = new byte[getBytesPerRow()]; rle = getImageFileDirectory().getCompression() == TIFFConstants.COMPRESSION_SGI_LOG_RLE; for (int y = getY1(); y <= getY2(); y++) { decodeRow(row); putBytes(row, 0, row.length); } } private void decodeRow(byte[] row) throws InvalidFileStructureException, IOException { if (rle) { decodeRowRLE(row); } else { decodeRowPacked24(row); } } private void decodeRowPacked24(byte[] row) throws InvalidFileStructureException, IOException { int num = getImageFileDirectory().getTileWidth() * 3; in.readFully(row, 0, num); } private void decodeRowRLE(byte[] row) throws InvalidFileStructureException, IOException { final int BYTES_PER_PIXEL; if (getImageFileDirectory().getPhotometricInterpretation() == TIFFConstants.PHOTOMETRIC_LOGL) { BYTES_PER_PIXEL = 2; // LogL } else { BYTES_PER_PIXEL = 4; // LogLuv } for (int initialOffset = 0; initialOffset < BYTES_PER_PIXEL; initialOffset++) { int offset = initialOffset; int numPixels = tileWidth; do { int v1 = in.readUnsignedByte(); if ((v1 & 128) != 0) { // run int runCount = v1 + (2 - 128); numPixels -= runCount; compressedSize -= 2; byte v2 = in.readByte(); while (runCount-- != 0) { row[offset] = v2; offset += BYTES_PER_PIXEL; } } else { // non-run, copy data int runCount = v1; numPixels -= runCount; compressedSize = compressedSize - runCount - 1; while (runCount-- != 0) { row[offset] = in.readByte(); offset += BYTES_PER_PIXEL; } } if (compressedSize < 0) { throw new InvalidFileStructureException("Ran out of compressed input bytes before completing the decoding process."); } } while (numPixels > 0); } } public Integer[] getCompressionTypes() { return new Integer[] {new Integer(TIFFConstants.COMPRESSION_SGI_LOG_RLE), new Integer(TIFFConstants.COMPRESSION_SGI_LOG_24_PACKED)}; } public void initialize() throws IOException, MissingParameterException { super.initialize(); in = getInput(); compressedSize = getImageFileDirectory().getByteCount(getTileIndex()); tileWidth = getImageFileDirectory().getTileWidth(); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFImageFileDirectory.java0000664000000000000000000006503210541055055027247 0ustar /* * TIFFImageFileDirectory * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; import java.util.Vector; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.codecs.tiff.TIFFConstants; import net.sourceforge.jiu.codecs.tiff.TIFFTag; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.RGBIndex; /** * This class encapsulates all data of a TIFF image file directory (IFD). * @author Marco Schmidt */ public class TIFFImageFileDirectory implements TIFFConstants { public static final int TYPE_BILEVEL_PACKED = 0; public static final int TYPE_GRAY4 = 1; public static final int TYPE_GRAY8 = 2; public static final int TYPE_GRAY16 = 3; public static final int TYPE_PALETTED4 = 4; public static final int TYPE_PALETTED8 = 5; public static final int TYPE_RGB24_INTERLEAVED = 6; public static final int TYPE_RGB48_INTERLEAVED = 7; public static final int TYPE_BILEVEL_BYTE = 8; public static final int TYPE_CMYK32_INTERLEAVED = 9; public static final int TYPE_CMYK32_PLANAR = 10; public static final int TYPE_LOGLUV32_INTERLEAVED = 11; public static final int TYPE_LOGL = 12; private String artist; private int[] bitsPerSample; private int bitsPerPixel; private int bitsPerRow; private int bytesBetweenSamples; private int[] bytesPerSample; private int bytesPerRow; private int compression; private String copyright; private Date date; private String dateTime; private int dpiX; private int dpiY; private int[] extraSamples; private int height; private int horizontalTiles; private String hostComputer; private String imageDescription; private int imageType; private boolean invertGraySamples; private String make; private String model; private int numStrips; private int numTiles; private int orientation; private Palette palette; private int pixelsPerRow; private int planarConfiguration; private int photometricInterpretation; private int predictor; private int[] sampleTypes; private int resolutionUnit; private double resolutionX; private double resolutionY; private int rowsPerStrip; private int samplesPerPixel; private String software; private Vector stripByteCounts; private Vector stripOffsets; private int t4Options; private int t6Options; private Vector tags; private Vector tileByteCounts; private Vector tileOffsets; private TimeZone timeZone; private int tileWidth; private int tileHeight; private int verticalTiles; private int width; /** * Initializes all members to null or -1 and creates an internal list for * the tags that will be make up this directory. */ public TIFFImageFileDirectory() { initMembers(); tags = new Vector(); } /** * Adds a tag to the end of the internal list of tags. * @param tag the TIFFTag instance to be appended */ public void append(TIFFTag tag) { tags.addElement(tag); } private void checkContent() throws InvalidFileStructureException, UnsupportedTypeException { if (width < 1) { throw new InvalidFileStructureException("No valid width available."); } if (height < 1) { throw new InvalidFileStructureException("No valid width available."); } if (stripOffsets != null) { pixelsPerRow = width; } else if (tileOffsets != null) { pixelsPerRow = tileWidth; } if (rowsPerStrip == -1 && stripOffsets != null && stripOffsets.size() == 1) { rowsPerStrip = height; } // do more checks based on color type switch (photometricInterpretation) { case(PHOTOMETRIC_BLACK_IS_ZERO): case(PHOTOMETRIC_WHITE_IS_ZERO): { if (bitsPerSample[0] == 1) { imageType = TYPE_BILEVEL_PACKED; } else { if (bitsPerSample[0] == 4) { imageType = TYPE_GRAY4; } else if (bitsPerSample[0] == 8) { imageType = TYPE_GRAY8; } else { throw new UnsupportedTypeException("Only bit depths 1, 4 and 8 are supported for bilevel and grayscale images."); } } break; } case(PHOTOMETRIC_PALETTED): { if (getPalette() == null) { throw new InvalidFileStructureException("No palette found in paletted image."); } break; } case(PHOTOMETRIC_TRUECOLOR_RGB): { if (planarConfiguration != PLANAR_CONFIGURATION_CHUNKY) { throw new UnsupportedTypeException("Cannot handle planar configuration other than chunky for RGB images."); } if (bitsPerSample.length != 3) { throw new UnsupportedTypeException("Found RGB truecolor image, but instead of three " + bitsPerSample.length + " component(s)."); } if (bitsPerPixel == 24) { imageType = TYPE_RGB24_INTERLEAVED; } else if (bitsPerPixel == 48) { imageType = TYPE_RGB48_INTERLEAVED; } else { throw new UnsupportedTypeException("Unsupported RGB truecolor image color depth: " + bitsPerPixel + "."); } break; } case(PHOTOMETRIC_TRUECOLOR_LOGLUV): { if (planarConfiguration == PLANAR_CONFIGURATION_CHUNKY) { imageType = TYPE_LOGLUV32_INTERLEAVED; } else { throw new UnsupportedTypeException("Cannot handle planar configuration other than chunky for RGB images."); } break; } case(PHOTOMETRIC_LOGL): { imageType = TYPE_LOGL; break; } case(PHOTOMETRIC_TRUECOLOR_CMYK): { if (planarConfiguration == PLANAR_CONFIGURATION_CHUNKY) { imageType = TYPE_CMYK32_INTERLEAVED; } /*else if (planarConfiguration == PLANAR_CONFIGURATION_PLANAR) { imageType = TYPE_CMYK32_PLANAR; }*/ else { throw new UnsupportedTypeException("Cannot handle planar configuration other than chunky for CMYK images."); } break; } default: { throw new UnsupportedTypeException("Unsupported color type: " + photometricInterpretation + "."); } } if (compression == COMPRESSION_CCITT_GROUP3_1D_MODIFIED_HUFFMAN) { if (bitsPerPixel != 1) { throw new UnsupportedTypeException("Number of bits per pixel must be 1 for " + "compression type: " + getCompressionName(compression) + "."); } imageType = TYPE_BILEVEL_BYTE; } // TODO more validity checks } /** * TODO: regard extra samples */ public int computeNumBytes(int numPixels) { if (bitsPerPixel == 1) { if (imageType == TYPE_BILEVEL_BYTE) { return numPixels; } else { return (numPixels + 7) / 8; } } else if (bitsPerPixel <= 4) { return (numPixels + 1) / 2; } else if (bitsPerPixel <= 8) { return numPixels; } else if (bitsPerPixel == 16) { return numPixels * 2; } else if (bitsPerPixel == 24) { return numPixels * 3; } else if (bitsPerPixel == 32) { return numPixels * 4; } else if (bitsPerPixel == 48) { return numPixels * 6; } else { return -1; } } /** * Returns information on the person who created the image * (as stored in tag {@link TIFFConstants#TAG_ARTIST}). */ public String getArtist() { return artist; } /** * Returns the number of bits per pixel (not including transparency information). */ public int getBitsPerPixel() { return bitsPerPixel; } /** * Returns the number of compressed byte for a given tile. * Tile index must not be negative and must be smaller than the number of tiles. * @param tileIndex zero-based index of tile or strip for which the number of compressed bytes is to be returned */ public int getByteCount(int tileIndex) { if (stripByteCounts != null) { return ((Number)stripByteCounts.elementAt(tileIndex)).intValue(); } else if (tileByteCounts != null) { return ((Number)tileByteCounts.elementAt(tileIndex)).intValue(); } else { return 0; } } public int getBytesPerRow() { return computeNumBytes(getTileWidth()); } /** * Returns the compression method, encoded as a number as found in * {@link TIFFConstants} (more specifically, the COMPRESSION_xyz constants). * Use {@link #getCompressionName(int)} to get the English name * of this compression method. * @return compression method */ public int getCompression() { return compression; } /** * Returns the name of a TIFF compression method. * If the name is unknown, Unknown method plus * the method number is returned. * This static method can be used in combination with the value from * {@link #getCompression}. * @param method the compression method number * @return the compression method name */ public static String getCompressionName(int method) { switch(method) { case(COMPRESSION_CCITT_GROUP3_1D_MODIFIED_HUFFMAN): return "CCITT Group 3 1D Modified Huffman"; case(COMPRESSION_CCITT_T4): return "CCITT T.4"; case(COMPRESSION_CCITT_T6): return "CCITT T.6"; case(COMPRESSION_DEFLATED_INOFFICIAL): return "Deflated (inofficial, 32496)"; case(COMPRESSION_DEFLATED_OFFICIAL): return "Deflated (official, 8)"; case(COMPRESSION_LZW): return "LZW"; case(COMPRESSION_NONE): return "Uncompressed"; case(COMPRESSION_PACKBITS): return "Packbits"; case(6): return "JPEG (old style)"; case(7):return "JPEG (new style)"; case(103): return "Pegasus IMJ"; case(32766): return "NeXT 2-bit RLE"; case(32771): return "Uncompressed, word-aligned"; case(32809): return "Thunderscan RLE"; case(32895): return "IT8 CT with padding"; case(32896): return "IT8 Linework RLE"; case(32897): return "IT8 Monochrome picture"; case(32898): return "IT8 Binary line art"; case(32908): return "Pixar 10 bit LZW"; case(32909): return "Pixar 11 bit ZIP"; case(32947): return "Kodak DCS"; case(34661): return "ISO JBIG"; case(34676): return "SGI Log Luminance RLE"; case(34677): return "SGI Log 24-bit packed"; default: return "Unknown method (" + method + ")"; } } public String getCopyright() { return copyright; } /** * If a date / time tag was found in this image file directory and * {@link #initFromTags} was called already, it was attempted to * create a {@link java.util.Date} object from it. * This object (or null) is returned. * Use {@link #setTimeZone} to provide a time zone before the date * parsing is done. * @see #getDateTimeString */ public Date getDateTime() { return date; } /** * If there was a date / time tag in this IFD, its String value * is returned. * @see #getDateTime */ public String getDateTimeString() { return dateTime; } public int getDpiX() { return dpiX; } public int getDpiY() { return dpiY; } public int getHeight() { return height; } public String getHostComputer() { return hostComputer; } public String getImageDescription() { return imageDescription; } public int getImageType() { return imageType; } public String getModel() { return model; } public int getNumHorizontalTiles() { return horizontalTiles; } public int getNumStrips() { return numStrips; } public int getNumTiles() { return numTiles; } public int getNumVerticalTiles() { return verticalTiles; } public Palette getPalette() { return palette; } public int getPhotometricInterpretation() { return photometricInterpretation; } public int getPredictor() { return predictor; } public int getRowsPerStrip() { return rowsPerStrip; } public int getSamplesPerPixel() { return samplesPerPixel; } public String getSoftware() { return software; } public Vector getStripOffsets() { return stripOffsets; } public int getT4Options() { return t4Options; } public int getT6Options() { return t6Options; } public int getTileHeight() { return tileHeight; } public long getTileOffset(int tileIndex) { if (stripOffsets != null) { Number number = (Number)stripOffsets.elementAt(tileIndex); return number.longValue(); } else if (tileOffsets != null) { Number number = (Number)tileOffsets.elementAt(tileIndex); return number.longValue(); } else { throw new IllegalArgumentException("Tile index invalid: " + tileIndex); } } public int getTileWidth() { return tileWidth; } public int getTileX1(int tileIndex) { if (tileIndex < 0 || tileIndex >= getNumTiles()) { throw new IllegalArgumentException("Not a valid tile index: " + tileIndex); } else { return (tileIndex % getNumHorizontalTiles()) * getTileWidth(); } } public int getTileX2(int tileIndex) { if (tileIndex < 0 || tileIndex >= getNumTiles()) { throw new IllegalArgumentException("Not a valid tile index: " + tileIndex); } else { return ((tileIndex % getNumHorizontalTiles()) + 1) * getTileWidth() - 1; } } public int getTileY1(int tileIndex) { if (tileIndex < 0 || tileIndex >= getNumTiles()) { throw new IllegalArgumentException("Not a valid tile index: " + tileIndex); } else { return (tileIndex % getNumVerticalTiles()) * getTileHeight(); } } public int getTileY2(int tileIndex) { if (tileIndex < 0 || tileIndex >= getNumTiles()) { throw new IllegalArgumentException("Not a valid tile index: " + tileIndex); } else { int result = ((tileIndex % getNumVerticalTiles()) + 1) * getTileHeight() - 1; if (result >= height) { result = height - 1; } return result; } } public int getWidth() { return width; } public void initMembers() { bitsPerPixel = -1; bitsPerSample = null; compression = -1; height = -1; horizontalTiles = -1; invertGraySamples = false; numStrips = -1; numTiles = -1; orientation = 1; photometricInterpretation = -1; planarConfiguration = -1; resolutionUnit = 2; resolutionX = -1.0; resolutionY = -1.0; rowsPerStrip = -1; stripOffsets = null; tags = null; tileOffsets = null; tileWidth = -1; tileHeight = -1; verticalTiles = -1; width = -1; } public void initFromTags(boolean check) throws InvalidFileStructureException, UnsupportedTypeException { int index = 0; while (index < tags.size()) { TIFFTag tag = (TIFFTag)tags.elementAt(index++); int id = tag.getId(); int count = tag.getCount(); int type = tag.getType(); boolean isNotInt = !tag.isInt(); switch(id) { case(TAG_ARTIST): { artist = tag.getString(); break; } case(TAG_BITS_PER_SAMPLE): { if (isNotInt) { throw new InvalidFileStructureException("Bits per " + "sample value(s) must be byte/short/long; type=" + type); } if (count == 1) { bitsPerSample = new int[1]; bitsPerSample[0] = tag.getOffset(); bitsPerPixel = bitsPerSample[0]; } else { bitsPerPixel = 0; bitsPerSample = new int[count]; for (int i = 0; i < count; i++) { bitsPerSample[i] = tag.getElementAsInt(i); if (bitsPerSample[i] < 1) { throw new InvalidFileStructureException("Bits per " + "sample value #" + i + " is smaller than 1."); } bitsPerPixel += bitsPerSample[i]; } } break; } case(TAG_COLOR_MAP): { if ((count % 3) != 0) { throw new InvalidFileStructureException("Number of palette entries must be divideable by three without rest; " + count); } if (count < 3 || count > 768) { throw new UnsupportedTypeException("Unsupported number of palette entries: " + count + "."); } if (type != TAG_TYPE_SHORT) { throw new UnsupportedTypeException("Unsupported number type for palette entries: " + type); } int numEntries = count / 3; palette = new Palette(numEntries, 255); int vectorIndex = 0; for (int paletteIndex = 0; paletteIndex < numEntries; paletteIndex++) { palette.putSample(RGBIndex.INDEX_RED, paletteIndex, tag.getElementAsInt(vectorIndex++) >> 8); } for (int paletteIndex = 0; paletteIndex < numEntries; paletteIndex++) { palette.putSample(RGBIndex.INDEX_GREEN, paletteIndex, tag.getElementAsInt(vectorIndex++) >> 8); } for (int paletteIndex = 0; paletteIndex < numEntries; paletteIndex++) { palette.putSample(RGBIndex.INDEX_BLUE, paletteIndex, tag.getElementAsInt(vectorIndex++) >> 8); } break; } case(TAG_COMPRESSION): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + " single byte/short/long value for compression " + "(count=" + count + ", type=" + type + ")."); } compression = tag.getOffset(); break; } case(TAG_DATE_TIME): { dateTime = tag.getString(); if (dateTime != null) { dateTime = dateTime.trim(); } if (dateTime != null) { SimpleDateFormat format = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); if (timeZone != null) { format.setCalendar(new GregorianCalendar(timeZone)); } try { date = format.parse(dateTime); } catch (ParseException pe) { date = null; } } break; } case(TAG_HOST_COMPUTER): { hostComputer = tag.getString(); break; } case(TAG_IMAGE_DESCRIPTION): { imageDescription = tag.getString(); break; } case(TAG_IMAGE_WIDTH): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for image width " + "(count=" + count + ", type=" + type + ")."); } width = tag.getOffset(); break; } case(TAG_IMAGE_LENGTH): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for image height " + "(count=" + count + ", type=" + type + ")."); } height = tag.getOffset(); break; } case(TAG_MAKE): { make = tag.getString(); break; } case(TAG_MODEL): { model = tag.getString(); break; } case(TAG_ORIENTATION): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for image height " + "(count=" + count + ", type=" + type + ")."); } orientation = tag.getOffset(); break; } case(TAG_PHOTOMETRIC_INTERPRETATION): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for photometric interpretation."); } photometricInterpretation = tag.getOffset(); break; } case(TAG_RESOLUTION_UNIT): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for planar configuration."); } resolutionUnit = tag.getOffset(); break; } case(TAG_RESOLUTION_X): { if (count != 1 || type != TAG_TYPE_RATIONAL) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for planar configuration."); } Object o = tag.getObject(0); if (o != null && o instanceof TIFFRational) { TIFFRational rational = (TIFFRational)o; resolutionX = rational.getAsDouble(); } break; } case(TAG_RESOLUTION_Y): { if (count != 1 || type != TAG_TYPE_RATIONAL) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for planar configuration."); } Object o = tag.getObject(0); if (o != null && o instanceof TIFFRational) { TIFFRational rational = (TIFFRational)o; resolutionY = rational.getAsDouble(); } break; } case(TAG_PLANAR_CONFIGURATION): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for planar configuration."); } planarConfiguration = tag.getOffset(); break; } case(TAG_ROWS_PER_STRIP): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for image height."); } rowsPerStrip = tag.getOffset(); break; } case(TAG_SAMPLES_PER_PIXEL): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for samples per pixel."); } samplesPerPixel = tag.getOffset(); break; } case(TAG_SOFTWARE): { software = tag.getString(); break; } case(TAG_STRIP_BYTE_COUNTS): { if (count < 1) { throw new InvalidFileStructureException("Need at least one strip offset."); } if (count == 1) { if (isNotInt) { throw new InvalidFileStructureException("There is " + "only one strip offset, but its type is not integer."); } stripByteCounts = new Vector(); stripByteCounts.addElement(new Long(tag.getOffset())); } else { stripByteCounts = tag.getVector(); } break; } case(TAG_STRIP_OFFSETS): { if (count < 1) { throw new InvalidFileStructureException("Need at least one strip offset."); } if (count == 1) { if (isNotInt) { throw new InvalidFileStructureException("There is " + "only one strip offset, but its type is not integer."); } stripOffsets = new Vector(); stripOffsets.addElement(new Long(tag.getOffset())); } else { stripOffsets = tag.getVector(); } numStrips = count; numTiles = count; horizontalTiles = 1; verticalTiles = count; break; } case(TAG_T4_OPTIONS): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for T4 Options."); } t4Options = tag.getOffset(); break; } case(TAG_T6_OPTIONS): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for T6 Options."); } t6Options = tag.getOffset(); break; } case(TAG_TILE_HEIGHT): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for image height " + "(count=" + count + ", type=" + type + ")."); } tileHeight = tag.getOffset(); if (tileHeight < 1) { throw new InvalidFileStructureException("Tile height must be one or larger."); } verticalTiles = height / tileHeight; if ((height % tileHeight) != 0) { verticalTiles++; } break; } case(TAG_TILE_OFFSETS): { if (count < 1) { throw new InvalidFileStructureException("Need at least one tile offset."); } if (count == 1) { if (isNotInt) { throw new InvalidFileStructureException("There is " + "only one tile offset, but its type is not integer."); } tileOffsets = new Vector(); tileOffsets.addElement(new Long(tag.getOffset())); } else { tileOffsets = tag.getVector(); } numStrips = count; numTiles = count; horizontalTiles = 1; verticalTiles = count; break; } case(TAG_TILE_WIDTH): { if (count != 1 || isNotInt) { throw new InvalidFileStructureException("Expected " + "single byte/short/long value for image height " + "(count=" + count + ", type=" + type + ")."); } tileWidth = tag.getOffset(); if (tileWidth < 1) { throw new InvalidFileStructureException("Tile width must be one or larger."); } horizontalTiles = width / tileWidth; if ((width % tileWidth) != 0) { horizontalTiles++; } break; } } } if (planarConfiguration == -1) { planarConfiguration = PLANAR_CONFIGURATION_CHUNKY; } if (photometricInterpretation == TIFFConstants.PHOTOMETRIC_PALETTED) { if (bitsPerPixel == 4) { imageType = TYPE_PALETTED4; } else if (bitsPerPixel == 8) { imageType = TYPE_PALETTED8; } else { throw new UnsupportedTypeException("Only paletted images with 4 or 8 bits per sample are supported."); } } if (resolutionUnit == 2 && resolutionX > 0.0 && resolutionY > 0.0) { dpiX = (int)resolutionX; dpiY = (int)resolutionY; } if (isStriped()) { tileWidth = width; if (numStrips == 1 && rowsPerStrip == -1) { rowsPerStrip = height; } tileHeight = rowsPerStrip; } if (check) { checkContent(); } } public boolean isGrayscale() { return getBitsPerPixel() > 1 && (photometricInterpretation == PHOTOMETRIC_BLACK_IS_ZERO || photometricInterpretation == PHOTOMETRIC_WHITE_IS_ZERO); } public boolean isPaletted() { return (photometricInterpretation == PHOTOMETRIC_PALETTED); } /** * Returns true if the image belonging to this IFD * is stored as strips, false otherwise. * @see #isTiled */ public boolean isStriped() { return (stripOffsets != null); } /** * Returns true if the image belonging to this IFD * is stored as tiles, false otherwise. * @see #isStriped */ public boolean isTiled() { return (tileOffsets != null); } /** * Sets the time zone to be used when trying to interpret dates * found in a {@link #TAG_DATE_TIME} tag. * Example call: * setTimeZone(TimeZone.getTimeZone("America/New_York");. * @param tz TimeZone object */ public void setTimeZone(TimeZone tz) { timeZone = tz; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFDecoderPackbits.java0000664000000000000000000000275007741250131026564 0ustar /* * TIFFDecoderPackbits * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; import java.io.DataInput; import java.io.IOException; import net.sourceforge.jiu.codecs.tiff.TIFFDecoder; import net.sourceforge.jiu.codecs.InvalidFileStructureException; /** * A TIFF decoder for files compressed with the Packbits method. * This compression algorithm has the value 32773 * in the compression tag of an image file directory. * @author Marco Schmidt * @since 0.9.0 */ public class TIFFDecoderPackbits extends TIFFDecoder { public void decode() throws InvalidFileStructureException, IOException { DataInput in = getInput(); byte[] row = new byte[getBytesPerRow()]; for (int y = getY1(); y <= getY2(); y++) { int index = 0; do { byte value = in.readByte(); if (value >= 0) { int numSamples = value + 1; // copy bytes literally in.readFully(row, index, numSamples); index += numSamples; } else if (value != (byte)-128) { int numSamples = - value + 1; // write run byte sample = in.readByte(); while (numSamples-- != 0) { row[index++] = sample; } } } while (index != row.length); putBytes(row, 0, row.length); } } public Integer[] getCompressionTypes() { return new Integer[] {new Integer(TIFFConstants.COMPRESSION_PACKBITS)}; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFDecoderDeflated.java0000664000000000000000000000713010505557641026541 0ustar /* * TIFFDecoderDeflated * * Copyright (c) 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; import java.io.DataInput; import java.io.IOException; import java.util.zip.DataFormatException; import java.util.zip.Inflater; import net.sourceforge.jiu.codecs.tiff.TIFFConstants; import net.sourceforge.jiu.codecs.tiff.TIFFDecoder; import net.sourceforge.jiu.codecs.tiff.TIFFImageFileDirectory; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.ops.MissingParameterException; /** * A TIFF decoder for files compressed with the Deflated method. * This compression algorithm has the values 31946 * ({@link TIFFConstants#COMPRESSION_DEFLATED_INOFFICIAL}) and 8 * ({@link TIFFConstants#COMPRESSION_DEFLATED_OFFICIAL}) * in the compression tag of an image file directory. * All types of image data can be compressed with this method. *

* This decoder makes use of the package java.util.zip which comes with an Inflater * class that does most of the work. * All the decoder has to do is feed the Inflater object with compressed data from * the input file and give decompressed data received from the Inflater to the * putBytes method. * @author Marco Schmidt * @since 0.9.0 */ public class TIFFDecoderDeflated extends TIFFDecoder { private DataInput in; private int compressedSize; public void decode() throws InvalidFileStructureException, IOException { Inflater inflater = new Inflater(); byte[] ioBuffer = new byte[20000]; byte[] data = new byte[getBytesPerRow()]; // determine how many bytes have to be read from inflater int numRows = getY2(); TIFFImageFileDirectory ifd = getImageFileDirectory(); if (numRows > ifd.getHeight() - 1) { numRows = ifd.getHeight() - 1; } numRows -= getY1(); int remainingBytes = numRows * data.length; // now read and decompress as long as there is data left to decompress while (compressedSize > 0 || remainingBytes > 0) { if (inflater.needsInput()) { // read compressed data from input int numBytes; if (compressedSize > ioBuffer.length) { numBytes = ioBuffer.length; } else { numBytes = compressedSize; } in.readFully(ioBuffer, 0, numBytes); // give data to inflater inflater.setInput(ioBuffer, 0, numBytes); compressedSize -= numBytes; } else { // determine how many bytes to decompress in this loop iteration int numBytes; if (remainingBytes > data.length) { numBytes = data.length; } else { numBytes = remainingBytes; } int numInflated; // do the decompression try { numInflated = inflater.inflate(data, 0, numBytes); } catch (DataFormatException dfe) { throw new InvalidFileStructureException("Error in compressed input data: " + dfe.toString()); } // store decompressed data and update number of bytes left to decompress if (numInflated > 0) { putBytes(data, 0, numInflated); remainingBytes -= numInflated; } } } } public Integer[] getCompressionTypes() { return new Integer[] { new Integer(TIFFConstants.COMPRESSION_DEFLATED_INOFFICIAL), new Integer(TIFFConstants.COMPRESSION_DEFLATED_OFFICIAL) }; } public void initialize() throws IOException, MissingParameterException { super.initialize(); in = getInput(); compressedSize = getImageFileDirectory().getByteCount(getTileIndex()); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFDecoderUncompressed.java0000664000000000000000000000142607741250131027472 0ustar /* * TIFFDecoderUncompressed * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; import java.io.DataInput; import java.io.IOException; import net.sourceforge.jiu.codecs.tiff.TIFFDecoder; /** * A TIFF decoder for uncompressed TIFF files. * @author Marco Schmidt * @since 0.9.0 */ public class TIFFDecoderUncompressed extends TIFFDecoder { public void decode() throws IOException { DataInput in = getInput(); byte[] row = new byte[getBytesPerRow()]; for (int y = getY1(); y <= getY2(); y++) { in.readFully(row); putBytes(row, 0, row.length); } } public Integer[] getCompressionTypes() { return new Integer[] {new Integer(TIFFConstants.COMPRESSION_NONE)}; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/package.html0000664000000000000000000000261307741250131024501 0ustar

Classes to handle the Tagged Image File Format (TIFF). See the TIFFCodec documentation for the amount of support that is built into this package.

Package Specification

The most important class is TIFFCodec, extending the base class for image codecs, ImageCodec. TIFFCodec reads the TIFF header, then the image file directory of the image to be loaded (TIFF can store more than one image in a file). The information of an image file directory is put into an object of class TIFFImageFileDirectory. It contains the tags of that directory (each tag is of type TIFFTag), and the most important information of a directory can also be retrieved from the various get methods (e.g. getCompression). TIFF files can be stored using all kinds of compression methods. When reading TIFFs, each supported compression method gets its own class extending TIFFDecoder, which provides basic methods required by all decoders (like storing decompressed data). A TIFFCodec object that is supposed to read an image creates an appropriate TIFFDecoder (e.g. TIFFDecoderUncompressed for compression type 1, no compression) for each strip or tile and lets them do the image loading.

java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFConstants.java0000664000000000000000000000616507741250131025516 0ustar /* * TIFFConstants * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; /** * This class provides a lot of constant values for a TIFF encoder or decoder. * @author Marco Schmidt */ public interface TIFFConstants { int COMPRESSION_NONE = 1; int COMPRESSION_CCITT_GROUP3_1D_MODIFIED_HUFFMAN = 2; int COMPRESSION_CCITT_T4 = 3; int COMPRESSION_CCITT_T6 = 4; int COMPRESSION_LZW = 5; int COMPRESSION_JPEG_6_0 = 6; int COMPRESSION_JPEG_POST_6_0 = 7; int COMPRESSION_DEFLATED_OFFICIAL = 8; int COMPRESSION_NEXT = 32766; int COMPRESSION_NONE_WORD_ALIGNED = 32771; int COMPRESSION_PACKBITS = 32773; int COMPRESSION_THUNDERSCAN = 32809; int COMPRESSION_DEFLATED_INOFFICIAL = 32946; int COMPRESSION_JBIG = 34661; int COMPRESSION_SGI_LOG_RLE = 34676; int COMPRESSION_SGI_LOG_24_PACKED = 34677; int COMPRESSION_JBIG2 = 34715; int PHOTOMETRIC_WHITE_IS_ZERO = 0; int PHOTOMETRIC_BLACK_IS_ZERO = 1; int PHOTOMETRIC_PALETTED = 3; int PHOTOMETRIC_TRUECOLOR_RGB = 2; int PHOTOMETRIC_TRUECOLOR_CMYK = 5; int PHOTOMETRIC_LOGL = 32844; int PHOTOMETRIC_TRUECOLOR_LOGLUV = 32845; int PLANAR_CONFIGURATION_CHUNKY = 1; int PLANAR_CONFIGURATION_PLANAR = 2; /** * Length of a tag (an image file directory entry) in bytes (12). */ int TAG_LENGTH = 12; // tag types int TAG_TYPE_BYTE = 1; int TAG_TYPE_ASCII = 2; int TAG_TYPE_SHORT = 3; int TAG_TYPE_LONG = 4; int TAG_TYPE_RATIONAL = 5; int TAG_TYPE_SBYTE = 6; int TAG_TYPE_UNDEFINED = 7; int TAG_TYPE_SSHORT = 8; int TAG_TYPE_SLONG = 9; int TAG_TYPE_SRATIONAL = 10; int TAG_TYPE_FLOAT = 11; int TAG_TYPE_DOUBLE = 12; // tag IDs int TAG_ARTIST = 315; int TAG_BAD_FAX_LINES = 326; int TAG_BITS_PER_SAMPLE = 258; int TAG_CELL_LENGTH = 265; int TAG_CELL_WIDTH = 264; int TAG_CLEAN_FAX_DATA = 327; int TAG_COLOR_MAP = 320; int TAG_COMPRESSION = 259; int TAG_CONSECUTIVE_BAD_FAX_LINES = 328; int TAG_COPYRIGHT = 33432; int TAG_DATE_TIME = 306; int TAG_DOCUMENT_NAME = 269; int TAG_EXTRA_SAMPLES = 338; int TAG_FILL_ORDER = 266; int TAG_FREE_BYTE_COUNTS = 289; int TAG_FREE_OFFSETS = 288; int TAG_GRAY_RESPONSE_CURVE = 291; int TAG_GRAY_RESPONSE_UNIT = 290; int TAG_HOST_COMPUTER = 316; int TAG_IMAGE_DESCRIPTION = 270; int TAG_IMAGE_LENGTH = 257; int TAG_IMAGE_WIDTH = 256; int TAG_MAKE = 271; int TAG_MAX_SAMPLE_VALUE = 281; int TAG_MIN_SAMPLE_VALUE = 280; int TAG_MODEL = 272; int TAG_NEW_SUBFILE_TYPE = 254; int TAG_ORIENTATION = 274; int TAG_PHOTOMETRIC_INTERPRETATION = 262; int TAG_PHOTOSHOP_IMAGE_RESOURCES = 34377; int TAG_PLANAR_CONFIGURATION = 284; int TAG_PREDICTOR = 317; int TAG_RESOLUTION_UNIT = 296; int TAG_RESOLUTION_X = 282; int TAG_RESOLUTION_Y = 283; int TAG_ROWS_PER_STRIP = 278; int TAG_SAMPLES_PER_PIXEL = 277; int TAG_SOFTWARE = 305; int TAG_STRIP_BYTE_COUNTS = 279; int TAG_STRIP_OFFSETS = 273; int TAG_T4_OPTIONS = 292; int TAG_T6_OPTIONS = 293; int TAG_TILE_BYTE_COUNTS = 325; int TAG_TILE_HEIGHT = 323; int TAG_TILE_OFFSETS = 324; int TAG_TILE_WIDTH = 322; } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFFaxCodes.java0000664000000000000000000001146510324333422025231 0ustar /* * TIFFFaxCodes * * Copyright (c) 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; /** * Information to be used to decode and encode TIFF files in one of the * bilevel compression types Modified Huffman, CCITT Group 3 or CCITT Group 4. * @author Marco Schmidt * @since 0.9.0 */ public class TIFFFaxCodes { /** * Index of the code word in the int[] value pairs. */ public static final int INDEX_CODE_WORD = 0; /** * Index of the code value in the int[] value pairs. */ public static final int INDEX_CODE_VALUE = 1; /** * Minimum code length in bits of black codes. */ public static final int MIN_BLACK_CODE_SIZE = 2; /** * Minimum code length in bits of white codes. */ public static final int MIN_WHITE_CODE_SIZE = 4; /** * The code words and their meanings for black codes. * In ascending order, starting at MIN_BLACK_CODE_SIZE bits, * each int[][] object contains all the code word / code value pairs * for one bit length. */ public static final int[][][] BLACK_CODES = { { // 2 bits {2, 3}, {3, 2}, }, { // 3 bits {2, 1}, {3, 4}, }, { // 4 bits {2, 6}, {3, 5}, }, { // 5 bits {3, 7}, }, { // 6 bits {4, 9}, {5, 8}, }, { // 7 bits {4, 10}, {5, 11}, {7, 12}, }, { // 8 bits {4, 13}, {7, 14}, }, { // 9 bits {24, 15}, }, { // 10 bits {23, 16}, {24, 17}, {55, 0}, {8, 18}, {15, 64}, }, { // 11 bits {23, 24}, {24, 25}, {40, 23}, {55, 22}, {103, 19}, {104, 20}, {108, 21}, {8, 1792}, {12, 1856}, {13, 1920}, }, { // 12 bits {18, 1984}, {19, 2048}, {20, 2112}, {21, 2176}, {22, 2240}, {23, 2304}, {28, 2368}, {29, 2432}, {30, 2496}, {31, 2560}, {36, 52}, {39, 55}, {40, 56}, {43, 59}, {44, 60}, {51, 320}, {52, 384}, {53, 448}, {55, 53}, {56, 54}, {82, 50}, {83, 51}, {84, 44}, {85, 45}, {86, 46}, {87, 47}, {88, 57}, {89, 58}, {90, 61}, {91, 256}, {100, 48}, {101, 49}, {102, 62}, {103, 63}, {104, 30}, {105, 31}, {106, 32}, {107, 33}, {108, 40}, {109, 41}, {200, 128}, {201, 192}, {202, 26}, {203, 27}, {204, 28}, {205, 29}, {210, 34}, {211, 35}, {212, 36}, {213, 37}, {214, 38}, {215, 39}, {218, 42}, {219, 43}, }, { // 13 bits {74, 640}, {75, 704}, {76, 768}, {77, 832}, {82, 1280}, {83, 1344}, {84, 1408}, {85, 1472}, {90, 1536}, {91, 1600}, {100, 1664}, {101, 1728}, {108, 512}, {109, 576}, {114, 896}, {115, 960}, {116, 1024}, {117, 1088}, {118, 1152}, {119, 1216}, } }; /** * The code words and their meanings for white codes. * In ascending order, starting at MIN_WHITE_CODE_SIZE bits, * each int[][] object contains all the code word / code value pairs * for one bit length. */ public static final int[][][] WHITE_CODES = { { // 4 bits {7, 2}, {8, 3}, {11, 4}, {12, 5}, {14, 6}, {15, 7}, }, { // 5 bits {18, 128}, {19, 8}, {20, 9}, {27, 64}, {7, 10}, {8, 11}, }, { // 6 bits {23, 192}, {24, 1664}, {42, 16}, {43, 17}, {3, 13}, {52, 14}, {53, 15}, {7, 1}, {8, 12}, }, { // 7 bits {19, 26}, {23, 21}, {24, 28}, {36, 27}, {39, 18}, {40, 24}, {43, 25}, {3, 22}, {55, 256}, {4, 23}, {8, 20}, {12, 19}, }, { // 8 bits {18, 33}, {19, 34}, {20, 35}, {21, 36}, {22, 37}, {23, 38}, {26, 31}, {27, 32}, {2, 29}, {36, 53}, {37, 54}, {40, 39}, {41, 40}, {42, 41}, {43, 42}, {44, 43}, {45, 44}, {3, 30}, {50, 61}, {51, 62}, {52, 63}, {53, 0}, {54, 320}, {55, 384}, {4, 45}, {74, 59}, {75, 60}, {5, 46}, {82, 49}, {83, 50}, {84, 51}, {85, 52}, {88, 55}, {89, 56}, {90, 57}, {91, 58}, {100, 448}, {101, 512}, {103, 640}, {104, 576}, {10, 47}, {11, 48}, }, { // 9 bits {152, 1472}, {153, 1536}, {154, 1600}, {155, 1728}, {204, 704}, {205, 768}, {210, 832}, {211, 896}, {212, 960}, {213, 1024}, {214, 1088}, {215, 1152}, {216, 1216}, {217, 1280}, {218, 1344}, {219, 1408}, }, { // 10 bits }, { // 11 bits {8, 1792}, {12, 1856}, {13, 1920}, }, { // 12 bits {18, 1984}, {19, 2048}, {20, 2112}, {21, 2176}, {22, 2240}, {23, 2304}, {28, 2368}, {29, 2432}, {30, 2496}, {31, 2560}, } }; } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFDecoderModifiedHuffman.javajava-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFDecoderModifiedHuffman.java0000664000000000000000000001053710377271557030071 0ustar /* * TIFFDecoderModifiedHuffman * * Copyright (c) 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; import java.io.DataInput; import java.io.IOException; import net.sourceforge.jiu.codecs.tiff.TIFFDecoder; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.ops.MissingParameterException; /** * A TIFF decoder for files compresseed with the Modified Huffman method * (also known as CCITT 1D Modified Huffman Run Length Encoding). * This compression algorithm has the value 2 * in the compression tag of an image file directory. * Only bilevel images can be encoded with that method. * @author Marco Schmidt * @since 0.9.0 */ public class TIFFDecoderModifiedHuffman extends TIFFDecoder { private DataInput in; private int bitBuffer; private int numBufferedBits; public void decode() throws InvalidFileStructureException, IOException { byte[] row = new byte[getBytesPerRow()]; for (int y = getY1(); y <= getY2(); y++) { decodeRow(row); putBytes(row, 0, row.length); } } private int decodeBlackRun() throws InvalidFileStructureException, IOException { return decodeRun(TIFFFaxCodes.BLACK_CODES, TIFFFaxCodes.MIN_BLACK_CODE_SIZE); } private void decodeRow(byte[] row) throws InvalidFileStructureException, IOException { reset(); boolean black = false; int index = 0; do { // this will hold the accumulated run length for the current // color at the end of this loop iteration int completeRunLength = 0; // get run lengths regarding current color until one is smaller than 64 int runLength; do { if (black) { runLength = decodeBlackRun(); } else { runLength = decodeWhiteRun(); } completeRunLength += runLength; } while (runLength >= 64); // pick color value for output row byte value; if (black) { value = (byte)BilevelImage.BLACK; } else { value = (byte)BilevelImage.WHITE; } // fill row buffer with value while (completeRunLength-- > 0) { row[index++] = value; } // switch colors (black to white or vice versa) black = !black; } while (index < row.length); } private int decodeRun(int[][][] codes, int minCodeSize) throws InvalidFileStructureException, IOException { int code = readBits(minCodeSize); //int currentCodeSize = minCodeSize; for (int i = 0; i < codes.length; i++) { int[][] data = codes[i]; int j = 0; final int LENGTH = data.length; while (j < LENGTH) { int[] pair = data[j++]; if (pair[TIFFFaxCodes.INDEX_CODE_WORD] == code) { return pair[TIFFFaxCodes.INDEX_CODE_VALUE]; } } code = (code << 1) | readBit(); } throw new InvalidFileStructureException("Could not identify Huffman code in TIFF file."); } private int decodeWhiteRun() throws InvalidFileStructureException, IOException { return decodeRun(TIFFFaxCodes.WHITE_CODES, TIFFFaxCodes.MIN_WHITE_CODE_SIZE); } public Integer[] getCompressionTypes() { return new Integer[] {new Integer(TIFFConstants.COMPRESSION_CCITT_GROUP3_1D_MODIFIED_HUFFMAN)}; } public void initialize() throws IOException, MissingParameterException { super.initialize(); in = getInput(); } private int readBit() throws IOException { int result; if (numBufferedBits == 0) { bitBuffer = in.readUnsignedByte(); if ((bitBuffer & 0x80) == 0) { result = 0; } else { result = 1; } bitBuffer &= 0x7f; numBufferedBits = 7; } else { numBufferedBits--; result = bitBuffer >> numBufferedBits; bitBuffer &= (1 << numBufferedBits) - 1; } return result; } private int readBits(int number) throws IOException { // make sure there are at least number bits while (numBufferedBits < number) { int b = in.readUnsignedByte(); bitBuffer = (bitBuffer << 8) | b; numBufferedBits += 8; } numBufferedBits -= number; int result = bitBuffer >> numBufferedBits; bitBuffer &= (1 << numBufferedBits) - 1; return result; } private void reset() { bitBuffer = 0; numBufferedBits = 0; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFDecoder.java0000664000000000000000000003726307762134364025126 0ustar /* * TIFFDecoder * * Copyright (c) 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; import java.io.DataInput; import java.io.IOException; import java.io.RandomAccessFile; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.color.conversion.CMYKConversion; import net.sourceforge.jiu.color.conversion.LogLuvConversion; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.ByteChannelImage; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.data.ShortChannelImage; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.util.ArrayConverter; /** * The abstract base class for a TIFF decoder, a class that decompresses one tile or * strip of image data and understands one or more compression types. * Each child class implements the decoding of a particular TIFF compression type * in its {@link #decode} method. *

* This class does all the work of storing decompressed data (given as a byte array) * in the image object. * Given the many variants (sample order, color depth, color space etc.) this is * a larger portion of code. * @author Marco Schmidt * @since 0.7.0 */ public abstract class TIFFDecoder { private TIFFCodec codec; private TIFFImageFileDirectory ifd; private int currentRow; private int leftColumn; private int rightColumn; private int topRow; private int bottomRow; private byte[] rowBuffer; private int bufferIndex; private int tileIndex; private int processedTileRows; private int totalTileRows; public TIFFDecoder() { tileIndex = -1; } /** * Decode data from input and write the decompressed pixel data to * the image associated with this decoder. * Child classes must override this method to implement the decoding * for a particular compression type. */ public abstract void decode() throws InvalidFileStructureException, IOException; /** * Returns the number of bytes per row for the strip or tile * that this decoder deals with. * So with a tiled TIFF and an image width of 500 and a tile width of 100, * for an eight bit grayscale image this would return 100 (not 500). * @return number of bytes per row */ public int getBytesPerRow() { return ifd.getBytesPerRow(); } /** * Returns the codec from which this decoder is used. * @return TIFFCodec object using this decoder */ public TIFFCodec getCodec() { return codec; } /** * Returns an array with Integer values of all compression types supported by * this decoder (see the COMPRESSION_xyz constants in {@link TIFFConstants}. * Normally, this is only one value, but some compression types got assigned more than one constant * (e.g. deflated). * Also, a decoder could be capable of dealing with more than one type of compression * if the compression types are similar enough to justify that. * However, typically a decoder can only deal with one type of compression. * @return array with Integer objects of all TIFF compression constants supported by this decoder */ public abstract Integer[] getCompressionTypes(); /** * Returns the IFD for the image this decoder is supposed to uncompress * (partially). * @return IFD object */ public TIFFImageFileDirectory getImageFileDirectory() { return ifd; } /** * Returns the input stream from which this decoder is supposed * to read data. */ public DataInput getInput() { return codec.getRandomAccessFile(); } /** * Returns the zero-based index of the tile or strip this decoder * is supposed to be decompressing. * @return tile index */ public int getTileIndex() { return tileIndex; } /** * Returns the leftmost column of the image strip / tile to be read * by this decoder. */ public int getX1() { return leftColumn; } /** * Returns the rightmost column of the image strip / tile to be read * by this decoder. */ public int getX2() { return rightColumn; } /** * Returns the top row of the image strip / tile to be read * by this decoder. */ public int getY1() { return topRow; } /** * Returns the bottom row of the image strip / tile to be read * by this decoder. */ public int getY2() { return bottomRow; } /** * Check if all necessary parameters have been given to this decoder * and initialize several internal fields from them. * Required parameters are a TIFFCodec object, a TIFFImageFileDirectory object and * a tile index. */ public void initialize() throws IOException, MissingParameterException { if (tileIndex < 0) { throw new MissingParameterException("Tile index was not initialized."); } if (codec == null) { throw new MissingParameterException("No TIFFCodec object was given to this decoder."); } if (ifd == null) { throw new MissingParameterException("No TIFFImageFileDirectory object was given to this decoder."); } RandomAccessFile raf = codec.getRandomAccessFile(); long offset = ifd.getTileOffset(tileIndex) & 0x00000000ffffffffL; raf.seek(offset); leftColumn = ifd.getTileX1(tileIndex); rightColumn = ifd.getTileX2(tileIndex); topRow = ifd.getTileY1(tileIndex); bottomRow = ifd.getTileY2(tileIndex); currentRow = topRow; processedTileRows = tileIndex * ifd.getTileHeight(); totalTileRows = ifd.getTileHeight() * ifd.getNumTiles(); rowBuffer = new byte[ifd.getBytesPerRow()]; } /** * Adds a number of bytes to the internal row buffer. * If the row buffer gets full (a complete line is available) * that data will be copied to the image. * Note that more than one line, exactly one line or only part * of a line can be stored in the number bytes * in data. * @param data byte array with image data that has been decoded * @param offset int index into data where the first byte to be stored is situated * @param number int number of bytes to be stored */ public void putBytes(byte[] data, int offset, int number) { // assert(bufferIndex < rowBuffer.length); while (number > 0) { int remaining = rowBuffer.length - bufferIndex; int numCopy; if (number > remaining) { numCopy = remaining; } else { numCopy = number; } System.arraycopy(data, offset, rowBuffer, bufferIndex, numCopy); number -= numCopy; offset += numCopy; bufferIndex += numCopy; if (bufferIndex == getBytesPerRow()) { storeRow(rowBuffer, 0); bufferIndex = 0; } } } /** * Specify the codec to be used with this decoder. * This is a mandatory parameter - without it, {@link #initialize} * will throw an exception. * @param tiffCodec TIFFCodec object to be used by this decoder * @see #getCodec */ public void setCodec(TIFFCodec tiffCodec) { codec = tiffCodec; } /** * Specify the IFD to be used with this decoder. * This is a mandatory parameter - without it, {@link #initialize} * will throw an exception. * @param tiffIfd object to be used by this decoder * @see #getImageFileDirectory */ public void setImageFileDirectory(TIFFImageFileDirectory tiffIfd) { ifd = tiffIfd; } /** * Specify the zero-based tile index for the tile or strip to be decompressed * by this decoder. * This is a mandatory parameter - without it, {@link #initialize} * will throw an exception. * @param index zero-based tile / strip index * @see #getTileIndex */ public void setTileIndex(int index) { if (index < 0) { throw new IllegalArgumentException("Tile index must be 0 or larger."); } tileIndex = index; } private void storeRow(byte[] data, int offset) { codec.setProgress(processedTileRows++, totalTileRows); // get current row number and increase field currentRow by one int y = currentRow++; // buffer index field is reset to zero so that putBytes will start at the beginning of the buffer next time bufferIndex = 0; // leave if we don't need that row because of bounds if (!codec.isRowRequired(y)) { return; } // adjust y so that it will be in bounds coordinate space y -= codec.getBoundsY1(); // get leftmost and rightmost pixel index of the current tile int x1 = getX1(); int x2 = getX2(); // compute number of pixels, adjust for bounds int numPixels = x2 - x1 + 1; int leftPixels = 0; if (getX1() < codec.getBoundsX1()) { leftPixels = codec.getBoundsX1() - getX1(); } int rightPixels = 0; if (getX2() > codec.getBoundsX2()) { rightPixels = getX2() - codec.getBoundsX2(); } numPixels -= (rightPixels + leftPixels); switch(ifd.getImageType()) { case(TIFFImageFileDirectory.TYPE_BILEVEL_BYTE): { BilevelImage image = (BilevelImage)codec.getImage(); int index = offset + leftPixels; int x = getX1() - codec.getBoundsX1() + leftPixels; while (numPixels-- > 0) { if (data[index++] == (byte)BilevelImage.BLACK) { image.putBlack(x++, y); } else { image.putWhite(x++, y); } } break; } case(TIFFImageFileDirectory.TYPE_BILEVEL_PACKED): { BilevelImage image = (BilevelImage)codec.getImage(); int x = getX1() - codec.getBoundsX1() + leftPixels; image.putPackedBytes(x, y, numPixels, data, offset + (leftPixels / 8), leftPixels % 8); break; } case(TIFFImageFileDirectory.TYPE_GRAY4): { ByteChannelImage image = (ByteChannelImage)codec.getImage(); byte[] dest = new byte[data.length * 2]; ArrayConverter.decodePacked4Bit(data, 0, dest, 0, data.length); for (int i = 0; i < dest.length; i++) { int value = dest[i] & 15; value = (value << 4) | value; dest[i] = (byte)value; } image.putByteSamples(0, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, offset + leftPixels); break; } case(TIFFImageFileDirectory.TYPE_PALETTED4): { ByteChannelImage image = (ByteChannelImage)codec.getImage(); byte[] dest = new byte[data.length * 2]; ArrayConverter.decodePacked4Bit(data, 0, dest, 0, data.length); image.putByteSamples(0, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, offset + leftPixels); break; } case(TIFFImageFileDirectory.TYPE_GRAY8): case(TIFFImageFileDirectory.TYPE_PALETTED8): { ByteChannelImage image = (ByteChannelImage)codec.getImage(); image.putByteSamples(0, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, data, offset + leftPixels); break; } case(TIFFImageFileDirectory.TYPE_CMYK32_INTERLEAVED): { ByteChannelImage image = (ByteChannelImage)codec.getImage(); byte[] dest = new byte[data.length]; int numSamples = ifd.getTileWidth(); CMYKConversion.convertCMYK32InterleavedToRGB24Planar( data, 0, dest, 0, dest, numSamples, dest, numSamples * 2, numSamples); image.putByteSamples(RGBIndex.INDEX_RED, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, leftPixels); image.putByteSamples(RGBIndex.INDEX_GREEN, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, numSamples + leftPixels); image.putByteSamples(RGBIndex.INDEX_BLUE, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, 2 * numSamples + leftPixels); break; } /* case(TIFFImageFileDirectory.TYPE_CMYK32_PLANAR): { ByteChannelImage image = (ByteChannelImage)codec.getImage(); byte[] dest = new byte[data.length]; int numSamples = ifd.getTileWidth(); CMYKConversion.convertCMYK32PlanarToRGB24Planar( data, 0, data, numPixels, data, numPixels * 2, data, numPixels * 3, dest, 0, dest, numSamples, dest, numSamples * 2, numSamples); image.putByteSamples(RGBIndex.INDEX_RED, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, leftPixels); image.putByteSamples(RGBIndex.INDEX_GREEN, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, numSamples + leftPixels); image.putByteSamples(RGBIndex.INDEX_BLUE, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, 2 * numSamples + leftPixels); break; }*/ case(TIFFImageFileDirectory.TYPE_RGB24_INTERLEAVED): { ByteChannelImage image = (ByteChannelImage)codec.getImage(); offset += leftPixels * 3; for (int i = 0, x = getX1() - codec.getBoundsX1() + leftPixels; i < numPixels; i++, x++) { image.putByteSample(RGBIndex.INDEX_RED, x, y, data[offset++]); image.putByteSample(RGBIndex.INDEX_GREEN, x, y, data[offset++]); image.putByteSample(RGBIndex.INDEX_BLUE, x, y, data[offset++]); } break; } case(TIFFImageFileDirectory.TYPE_RGB48_INTERLEAVED): { ShortChannelImage image = (ShortChannelImage)codec.getImage(); offset += leftPixels * 3; short[] triplet = new short[3]; boolean littleEndian = codec.getByteOrder() == TIFFCodec.BYTE_ORDER_INTEL; for (int i = 0, x = getX1() - codec.getBoundsX1() + leftPixels; i < numPixels; i++, x++) { for (int j = 0; j < 3; j++, offset += 2) { if (littleEndian) { triplet[j] = ArrayConverter.getShortLE(data, offset); } else { triplet[j] = ArrayConverter.getShortBE(data, offset); } } image.putShortSample(RGBIndex.INDEX_RED, x, y, triplet[0]); image.putShortSample(RGBIndex.INDEX_GREEN, x, y, triplet[1]); image.putShortSample(RGBIndex.INDEX_BLUE, x, y, triplet[2]); } break; } case(TIFFImageFileDirectory.TYPE_LOGLUV32_INTERLEAVED): { if (getImageFileDirectory().getCompression() == TIFFConstants.COMPRESSION_SGI_LOG_RLE) { ByteChannelImage image = (ByteChannelImage)codec.getImage(); int numSamples = ifd.getTileWidth(); byte[] red = new byte[numSamples]; byte[] green = new byte[numSamples]; byte[] blue = new byte[numSamples]; LogLuvConversion.convertLogLuv32InterleavedtoRGB24Planar(data, red, green, blue, numSamples); image.putByteSamples(RGBIndex.INDEX_RED, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, red, leftPixels); image.putByteSamples(RGBIndex.INDEX_GREEN, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, green, leftPixels); image.putByteSamples(RGBIndex.INDEX_BLUE, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, blue, leftPixels); } else if (getImageFileDirectory().getCompression() == TIFFConstants.COMPRESSION_SGI_LOG_24_PACKED) { ByteChannelImage image = (ByteChannelImage)codec.getImage(); int numSamples = ifd.getTileWidth(); byte[] red = new byte[numSamples]; byte[] green = new byte[numSamples]; byte[] blue = new byte[numSamples]; LogLuvConversion.convertLogLuv24InterleavedtoRGB24Planar(data, red, green, blue, numSamples); image.putByteSamples(RGBIndex.INDEX_RED, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, red, leftPixels); image.putByteSamples(RGBIndex.INDEX_GREEN, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, green, leftPixels); image.putByteSamples(RGBIndex.INDEX_BLUE, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, blue, leftPixels); } break; } case(TIFFImageFileDirectory.TYPE_LOGL): { ByteChannelImage image = (ByteChannelImage)codec.getImage(); int numSamples = ifd.getTileWidth(); byte[] gray = new byte[numSamples]; LogLuvConversion.convertLogL16toGray8(data, gray, numSamples); image.putByteSamples(0, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, gray, leftPixels); break; } } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFCodec.java0000664000000000000000000006714610542176267024577 0ustar /* * TIFFCodec * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Hashtable; import java.util.Vector; import net.sourceforge.jiu.codecs.CodecMode; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedCodecModeException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.codecs.WrongFileFormatException; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.MemoryGray16Image; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.MemoryRGB48Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; /** * A codec to read Tagged Image File Format (TIFF) image files. * *

Usage example

* Load an image from a TIFF file. *
 * TIFFCodec codec = new TIFFCodec();
 * codec.setFile("image.tif", CodecMode.LOAD);
 * codec.process();
 * PixelImage loadedImage = codec.getImage();
 * 
* Saving images is not supported by this codec. * *

Compression types

*

Reading

* The TIFF package supports the following compression types when reading: * *

* Note that you can write your own decoder (extending {@link TIFFDecoder}) for any compression type * you want. *

* *

Image types

*

Reading

* The TIFF package supports the following image / color types when reading: * *

* Note that you can write your own decoder (extending {@link TIFFDecoder}) for any compression type * you want. *

* *

Writing

*

Writing TIFFs is not supported. * I don't know if or when it will be supported.

* *

Strips and tiles

* The early versions of TIFF considered an image to be a sequence of strips. * Each strip was a rectangular part of the image, as wide as the complete image, * and with a certain height defined by the rows per strip tag. * So with a number of rows per strip of 10, and an image height of 200, you would * have to store 20 strips. * It was recommended that a strip should not be larger than 8 KB (RAM was tighter * in those days). * The rule of thumb to define the number of rows per strip was to see how many rows * would fit into 8 KB. *

* Later, the concept of tiles was added to the TIFF specs. * Tiled TIFFs are separated into rectangles that not only had a defineable * height but also a defineable width (tile width and tile height are also stored in * corresponding tags). *

* Obviously, strips are just a special case of tiles, with the tile width being equal * to image width. * That is why JIU internally only deals with tiles. * The only difference: No row padding takes place for strips. * In a tiled image with a tile height of 10 and an image height of 14, * the image is two tiles high. * *

Number of images

* TIFF allows for multiple images in a single file. * This codec regards the image index, queries {@link #getImageIndex} and skips to the * correct image. * *

Bounds

* The bounds concept of JIU is supported by this codec. * So you can specify bounds of a rectangular part of an image that you want to load * instead of loading the complete image. * *

Color spaces

* The following color spaces are understood when reading truecolor TIFF files. * *

Physical resolution

* DPI information can be stored in TIFF files. * If that information is available, this codec retrieves it so that it * can be queried using {@link #getDpiX} and {@link #getDpiY}. * *

Background information on TIFF

* TIFF is an important image file format for DTP (desktop publishing). * The advantages of TIFF include its flexibility, availability of libraries to read * and write TIFF files and its good support in existing software. * The major disadvantage of TIFF is its complexity, which makes it hard for software * to support all possible valid TIFF files. *

* TIFF was created by Aldus and now belongs to Adobe, who offer a specification document: * TIFF * (Tagged Image File Format) 6.0 Specification (updated on Web September, 20 1995, * document dated June, 3 1992) (PDF: 385 KB / 121 pages). *

* Other good references include the homepage * of libtiff, a free C library to read and write TIFF files and * The Unofficial TIFF * homepage by Niles Ritter. * Also see the TIFF section * of the Open Directory. *

* TIFF is used for various specialized tasks. * As an example, see GeoTIFF (geographical * data) or EXIF * (digital camera metadata; this is actually a TIFF directory embedded in a JPEG header). *

* Here's a list of features that make TIFF quite complex: *

* * @author Marco Schmidt */ public class TIFFCodec extends ImageCodec implements TIFFConstants { public static final int BYTE_ORDER_MOTOROLA = 0; public static final int BYTE_ORDER_INTEL = 1; private static final int MAGIC_INTEL = 0x49492a00; private static final int MAGIC_MOTOROLA = 0x4d4d002a; private int byteOrder; private int nextIfdOffset; private static Hashtable decoders; static { decoders = new Hashtable(); registerDecoder(TIFFDecoderDeflated.class); registerDecoder(TIFFDecoderModifiedHuffman.class); registerDecoder(TIFFDecoderPackbits.class); registerDecoder(TIFFDecoderUncompressed.class); registerDecoder(TIFFDecoderLogLuv.class); } /** * If the current byte order is {@link BYTE_ORDER_MOTOROLA} and the type * argument is {@link TiffConstants.TAG_TYPE_BYTE} or * {@link TiffConstants.TAG_TYPE_SHORT}, the value parameter must * be adjusted by some bitshifting. * If the above mentioned criteria are not met, the value argument is * returned without any modifications. *

* Why this is necessary remains a mystery to me. Marco * * @param value the int value which may have to be adjusted * @return the value parameter which may have been modified */ private int adjustInt(int value, int type) { if (getByteOrder() == BYTE_ORDER_MOTOROLA) { if (type == TAG_TYPE_BYTE) { return ((value >> 24) & 0xff); } else if (type == TAG_TYPE_SHORT) { return ((value >> 16) & 0xff) | (((value >> 24) & 0xff) << 8); } else { return value; } } else { return value; } } private static TIFFDecoder createDecoder(TIFFCodec codec, TIFFImageFileDirectory ifd, int tileIndex) throws IOException, UnsupportedTypeException { Integer compression = new Integer(ifd.getCompression()); Class decoderClass = (Class)decoders.get(compression); if (decoderClass == null) { throw new UnsupportedTypeException("Could not create decoder for this compression type: " + compression.intValue()); } Object instance; try { instance = decoderClass.newInstance(); } catch (Exception e) { throw new UnsupportedTypeException("Could not create decoder for this compression type."); } if (instance instanceof TIFFDecoder) { TIFFDecoder decoder = (TIFFDecoder)instance; decoder.setCodec(codec); decoder.setTileIndex(tileIndex); decoder.setImageFileDirectory(ifd); try { decoder.initialize(); } catch (MissingParameterException mpe) { throw new UnsupportedTypeException("Unable to initialize decoder: " + mpe.toString()); } return decoder; } else { throw new UnsupportedTypeException("Could not create decoder for this compression type."); } } /** * Returns the current byte order, either * {@link #BYTE_ORDER_INTEL} or * {@link #BYTE_ORDER_MOTOROLA}. * @return current byte order */ public int getByteOrder() { return byteOrder; } public String getFormatName() { return "Tagged Image File Format (TIFF)"; } public String[] getMimeTypes() { return new String[] {"image/tiff", "image/tif"}; } /** * Returns the name of a tag in English. * @param id of the tag for which a name is to be returned * @return tag name as String or a question mark ? */ public static String getTagName(int id) { switch(id) { case(TAG_ARTIST): return "Artist"; case(TAG_BAD_FAX_LINES): return "Bad fax lines"; case(TAG_BITS_PER_SAMPLE): return "Bits per sample"; case(TAG_CELL_LENGTH): return "Cell length"; case(TAG_CELL_WIDTH): return "Cell width"; case(TAG_CLEAN_FAX_DATA): return "Clean fax data"; case(TAG_COLOR_MAP): return "Color map"; case(TAG_COMPRESSION): return "Compression"; case(TAG_CONSECUTIVE_BAD_FAX_LINES): return "Consecutive bad fax lines"; case(TAG_COPYRIGHT): return "Copyright"; case(TAG_DATE_TIME): return "Date and time"; case(TAG_DOCUMENT_NAME): return "Document name"; case(TAG_EXTRA_SAMPLES): return "Extra samples"; case(TAG_FILL_ORDER): return "Fill order"; case(TAG_FREE_BYTE_COUNTS): return "Free byte counts"; case(TAG_FREE_OFFSETS): return "Free offsets"; case(TAG_GRAY_RESPONSE_CURVE): return "Gray response curve"; case(TAG_GRAY_RESPONSE_UNIT): return "Gray response unit"; case(TAG_HOST_COMPUTER): return "Host computer"; case(TAG_IMAGE_DESCRIPTION): return "Image description"; case(TAG_IMAGE_LENGTH): return "Image length"; case(TAG_IMAGE_WIDTH): return "Image width"; case(TAG_MAKE): return "Make"; case(TAG_MAX_SAMPLE_VALUE): return "Maximum sample value"; case(TAG_MIN_SAMPLE_VALUE): return "Minimum sample value"; case(TAG_MODEL): return "Model"; case(TAG_NEW_SUBFILE_TYPE): return "New subfile type"; case(TAG_ORIENTATION): return "Orientation"; case(TAG_PHOTOMETRIC_INTERPRETATION): return "Photometric interpretation"; case(TAG_PLANAR_CONFIGURATION): return "Planar configuration"; case(TAG_PREDICTOR): return "Predictor"; case(TAG_RESOLUTION_UNIT): return "Resolution unit"; case(TAG_RESOLUTION_X): return "Resolution X"; case(TAG_RESOLUTION_Y): return "Resolution Y"; case(TAG_ROWS_PER_STRIP): return "Rows per strip"; case(TAG_SAMPLES_PER_PIXEL): return "Samples per pixel"; case(TAG_SOFTWARE): return "Software"; case(TAG_STRIP_BYTE_COUNTS): return "Strip byte counts"; case(TAG_STRIP_OFFSETS): return "Strip offsets"; case(TAG_TILE_BYTE_COUNTS): return "Byte counts"; case(TAG_TILE_HEIGHT): return "Tile height"; case(TAG_TILE_OFFSETS): return "Tile offsets"; case(TAG_TILE_WIDTH): return "Tile width"; default: return "?"; } } public boolean isLoadingSupported() { return true; } public boolean isSavingSupported() { return false; } /** * Attempts to load an image from a file in the TIFF format. * Some options can be given to this codec before the call * to this load method. *

* * @return the image if everything was successful * @throws InvalidFileStructureException if the TIFF file was corrupt in some way * @throws IOException if there were errors reading from the input file * @throws UnsupportedTypeException if the flavour of TIFF encountered in the input * file is not supported yet * @throws WrongFileFormatException */ private void load() throws InvalidFileStructureException, IOException, UnsupportedTypeException, WrongFileFormatException, WrongParameterException { readHeader(); skipImageFileDirectories(getImageIndex()); TIFFImageFileDirectory ifd = readImageFileDirectory(); ifd.initFromTags(true); int dpiX = ifd.getDpiX(); int dpiY = ifd.getDpiY(); if (dpiX > 0 && dpiY > 0) { setDpi(dpiX, dpiY); } //ifd.dump(); load(ifd); } private void load(TIFFImageFileDirectory ifd) throws InvalidFileStructureException, IOException, UnsupportedTypeException, WrongFileFormatException, WrongParameterException { setBoundsIfNecessary(ifd.getWidth(), ifd.getHeight()); checkImageResolution(); int width = getBoundsWidth(); int height = getBoundsHeight(); // create image if necessary PixelImage image = getImage(); if (image == null) { int imageType = ifd.getImageType(); switch (imageType) { case(TIFFImageFileDirectory.TYPE_BILEVEL_BYTE): case(TIFFImageFileDirectory.TYPE_BILEVEL_PACKED): { image = new MemoryBilevelImage(width, height); break; } case(TIFFImageFileDirectory.TYPE_GRAY4): case(TIFFImageFileDirectory.TYPE_GRAY8): case(TIFFImageFileDirectory.TYPE_LOGL): { image = new MemoryGray8Image(width, height); break; } case(TIFFImageFileDirectory.TYPE_GRAY16): { image = new MemoryGray16Image(width, height); break; } case(TIFFImageFileDirectory.TYPE_PALETTED4): case(TIFFImageFileDirectory.TYPE_PALETTED8): { image = new MemoryPaletted8Image(width, height, ifd.getPalette()); break; } case(TIFFImageFileDirectory.TYPE_CMYK32_INTERLEAVED): case(TIFFImageFileDirectory.TYPE_CMYK32_PLANAR): case(TIFFImageFileDirectory.TYPE_RGB24_INTERLEAVED): case(TIFFImageFileDirectory.TYPE_LOGLUV32_INTERLEAVED): { image = new MemoryRGB24Image(width, height); break; } case(TIFFImageFileDirectory.TYPE_RGB48_INTERLEAVED): { image = new MemoryRGB48Image(width, height); break; } default: { throw new UnsupportedTypeException("Unsupported image type."); } } setImage(image); } int tileIndex = 0; int numTiles = ifd.getNumTiles(); while (tileIndex < numTiles && !getAbort()) { int x1 = ifd.getTileX1(tileIndex); int y1 = ifd.getTileY1(tileIndex); int x2 = ifd.getTileX2(tileIndex); int y2 = ifd.getTileY2(tileIndex); if (isTileRequired(x1, y1, x2, y2)) { TIFFDecoder decoder = createDecoder(this, ifd, tileIndex); decoder.decode(); } tileIndex++; } } public void process() throws MissingParameterException, OperationFailedException { initModeFromIOObjects(); try { if (getMode() == CodecMode.LOAD && getRandomAccessFile() != null) { load(); } else { throw new MissingParameterException("TIFF codec must have RandomAccessFile object opened for reading."); } } catch (IOException ioe) { close(); throw new OperationFailedException("I/O error occurred: " + ioe.toString()); } } /** * Reads the first eight bytes from the input file, checks if this is a * valid TIFF file and stores byte order and offset of the first image * file directory. * @throws IOException if there were reading errors * @throws WrongFileFormatException if this is not a valid TIFF file */ private void readHeader() throws IOException, WrongFileFormatException { RandomAccessFile in = getRandomAccessFile(); // the argument to in.seek must be changed to a variable in the future for // this codec to be used to read EXIF information from JPEGs; // for some reason, TIFF was chosen for that in.seek(0); // note: this is the only place where we use in.readInt() // directly; afterwards, the detected byte order // is regarded via this class' methods readInt() and readShort() // methods int magic = in.readInt(); if (magic == MAGIC_INTEL) { setByteOrder(BYTE_ORDER_INTEL); } else if (magic == MAGIC_MOTOROLA) { setByteOrder(BYTE_ORDER_MOTOROLA); } else { throw new WrongFileFormatException("Not a TIFF file (does not " + "begin with II or MM followed by 42)."); } nextIfdOffset = readInt(); } /** * Reads a complete TIFF image file directory including all data that is * pointed to using the offset components and returns it. * * @return the image file directory data or null on failure */ private TIFFImageFileDirectory readImageFileDirectory() throws InvalidFileStructureException, IOException { TIFFImageFileDirectory result = new TIFFImageFileDirectory(); RandomAccessFile in = getRandomAccessFile(); in.seek(nextIfdOffset); short numTags = readShort(); if (numTags < 0) { throw new InvalidFileStructureException("Number of tags in IFD " + "smaller than 1 @" + nextIfdOffset + ": " + numTags); } for (int i = 0; i < numTags; i++) { TIFFTag tag = readTag(); if (tag != null) { result.append(tag); } } nextIfdOffset = in.readInt(); return result; } /** * Reads a 32 bit signed integer value, regarding the current byte order. * @return the loaded value * @see #getByteOrder */ private int readInt() throws IOException { RandomAccessFile in = getRandomAccessFile(); int result = in.readInt(); if (getByteOrder() == BYTE_ORDER_INTEL) { int r1 = (result >> 24) & 0xff; int r2 = (result >> 16) & 0xff; int r3 = (result >> 8) & 0xff; int r4 = result & 0xff; return r1 | (r2 << 8) | (r3 << 16) | (r4 << 24); } else { return result; } } /** * Reads a 16 bit signed integer value, regarding the current byte order. * @return the loaded value */ private short readShort() throws IOException { RandomAccessFile in = getRandomAccessFile(); short result = in.readShort(); if (getByteOrder() == BYTE_ORDER_INTEL) { int r1 = (result >> 8) & 0xff; int r2 = result & 0xff; return (short)((r2 << 8) | r1); } else { return result; } } /** * Loads a String of a given length from current position of input file. * Characters are one-byte ASCII. * Non-text characters are dropped. * @param length number of characters in a row to be loaded * @return loaded String * @throws IOException if there were reading errors or an unexpected * end of file */ private String readString(int length) throws IOException { RandomAccessFile in = getRandomAccessFile(); StringBuffer sb = new StringBuffer(length - 1); while (length-- > 0) { int value = in.read(); if (value >= 32 && value < 256) { sb.append((char)value); } } return sb.toString(); } /** * Reads a TIFF tag and all data belonging to it and returns a * TIFFTag object. * The additional data is somewhere in the TIFF file. * The current position will be stored, the method will seek to the offset * position and load the data. * * @return TIFFTag containing information on the tag */ private TIFFTag readTag() throws InvalidFileStructureException, IOException { RandomAccessFile in = getRandomAccessFile(); int id = readShort() & 0xffff; int type = readShort() & 0xffff; int count = readInt(); int offset = readInt(); if (count < 1) { //throw new InvalidFileStructureException("Invalid count value for tag " + id + " (" + count + ")."); return null; } Vector vector = null; // perform weird bitshifting magic if necessary if (count == 1 && (type == TAG_TYPE_BYTE || type == TAG_TYPE_SHORT || type == TAG_TYPE_LONG)) { offset = adjustInt(offset, type); } else if (count <= 4 && type == TAG_TYPE_BYTE) { vector = new Vector(); for (int i = 0; i < count; i++) { byte b = (byte)((offset << (i * 8)) & 0xff); vector.addElement(new Byte(b)); } } else if (count >= 1) { long oldOffset = in.getFilePointer(); in.seek(offset); vector = new Vector(); if (type == TAG_TYPE_ASCII) { vector.addElement(readString(count)); } else if (type == TAG_TYPE_BYTE) { for (int i = 0; i < count; i++) { byte b = in.readByte(); vector.addElement(new Byte(b)); } } else if (type == TAG_TYPE_SHORT) { for (int i = 0; i < count; i++) { int s = readShort(); vector.addElement(new Short((short)s)); } } else if (type == TAG_TYPE_LONG) { for (int i = 0; i < count; i++) { int v = adjustInt(readInt(), type); vector.addElement(new Integer(v)); } } else if (type == TAG_TYPE_RATIONAL) { for (int i = 0; i < count; i++) { int v1 = adjustInt(readInt(), TAG_TYPE_LONG); int v2 = adjustInt(readInt(), TAG_TYPE_LONG); vector.addElement(new TIFFRational(v1, v2)); } } in.seek(oldOffset); } TIFFTag result = new TIFFTag(id, type, count, offset); result.setVector(vector); return result; } /** * Register a {@link TIFFDecoder} class. * TIFF knows many compression types, and JIU only supports some of them. * To register an external TIFFDecoder class with TIFFCodec, call this method * with the class field of your decoder. * As an example, for your TIFFDecoderMyCompression class, * call TIFFCodec.registerDecoder(TIFFDecoderMyCompression.class). * It will be checked if * decoderClass.newInstance() instanceof TIFFDecoder * is true and, if so, the class will be added to an internal list. * Whenever a TIFF file is to be decoded, the correct decoder is determined * (each decoder knows about the compression types it supports via the getCompressionTypes method) * and for each tile or strip such a decoder object will be created. */ public static void registerDecoder(Class decoderClass) { if (decoderClass == null) { return; } Object instance; try { instance = decoderClass.newInstance(); } catch (Exception e) { return; } if (instance instanceof TIFFDecoder) { TIFFDecoder decoder = (TIFFDecoder)instance; Integer[] compressionTypes = decoder.getCompressionTypes(); if (compressionTypes == null) { return; } int index = 0; while (index < compressionTypes.length) { Integer type = compressionTypes[index++]; if (type != null) { decoders.put(type, decoderClass); } } } } /** * Sets the byte order to the argument. * The byte order in a TIFF file is either {@link #BYTE_ORDER_INTEL} or * {@link #BYTE_ORDER_MOTOROLA}. * @param newByteOrder the new byte order to be set * @throws IllegalArgumentException if the argument is not one of the above * mentioned constants */ private void setByteOrder(int newByteOrder) { if (newByteOrder == BYTE_ORDER_INTEL || newByteOrder == BYTE_ORDER_MOTOROLA) { byteOrder = newByteOrder; } else { throw new IllegalArgumentException("Byte order must be either " + "BYTE_ORDER_INTEL or BYTE_ORDER_MOTOROLA."); } } public void setFile(String fileName, CodecMode codecMode) throws IOException, UnsupportedCodecModeException { if (codecMode == CodecMode.LOAD) { setRandomAccessFile(new RandomAccessFile(fileName, "r"), CodecMode.LOAD); } else { throw new UnsupportedCodecModeException("This TIFF codec can only load images."); } } /** * Skips a given number of image file directories in this TIFF files. * Throws an exception if there were errors or not enough image file * directories. * @param numDirectories the number of directories to be skipped, * should be non-negative * @throws IllegalArgumentException if argument is negative * @throws InvalidFileStructureException if there aren't enough image * file directories * @throws IOExceptions if there were errors reading or skipping data */ private void skipImageFileDirectories(int numDirectories) throws InvalidFileStructureException, IOException { RandomAccessFile in = getRandomAccessFile(); if (numDirectories < 0) { throw new IllegalArgumentException("Cannot skip negative number " + "of image file directories: " + numDirectories); } int skipped = 0; while (numDirectories-- > 0) { in.seek(nextIfdOffset); short numTags = readShort(); in.skipBytes(numTags * 12); nextIfdOffset = readInt(); if (nextIfdOffset == 0) { throw new InvalidFileStructureException("Could only skip " + skipped + " image file directories, no more images in file."); } skipped++; } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/tiff/TIFFTag.java0000664000000000000000000001127107741250131024247 0ustar /* * TIFFTag * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs.tiff; import java.util.Vector; import net.sourceforge.jiu.codecs.tiff.TIFFConstants; /** * This encapsulates the data stored in a TIFF tag (a single image file directory entry). * That includes the following items: * * See the TIFF specification manual linked in the description of {@link TIFFCodec} * for more details. * * @author Marco Schmidt * @see TIFFImageFileDirectory */ public class TIFFTag implements TIFFConstants { private int id; private int type; private int count; private int offset; private Vector objects; /** * Creates a new tag with the given ID, type, number of objects / primitives stored in it * and offset value. */ public TIFFTag(int id, int type, int count, int offset) { this.id = id; this.type = type; this.count = count; if (count < 1) { throw new IllegalArgumentException("Tiff tag count value must " + "not be smaller than 1: " + count); } this.offset = offset; objects = null; } public TIFFTag(int id, int type, int count, int offset, Vector vector) { this(id, type, count, offset); objects = vector; } /** * Returns the number of items stored in this tag. */ public int getCount() { return count; } /** * Returns an item stored in this tag an int value. * @param index zero-based index of the integer item to be returned */ public int getElementAsInt(int index) { Object element = getObject(index); if (element == null) { throw new IllegalArgumentException("Tag does not contain a list of values."); } if (element instanceof Short) { return ((Short)element).shortValue() & 0xffff; } if (element instanceof Integer) { return ((Integer)element).intValue(); } if (element instanceof Byte) { return ((Byte)element).byteValue() & 0xff; } throw new IllegalArgumentException("Element #" + index + " is not an integer value."); } /** * Returns the ID of this tag, which may be one of the TAG_xyz constants. */ public int getId() { return id; } /** * Returns an object from this tag's Vector of items, * or null if no such Vector exists. */ public Object getObject(int index) { if (objects == null) { return null; } else { return objects.elementAt(index); } } /** * Returns the offset value stored in this tag. */ public int getOffset() { return offset; } /** * If this tag has a Vector of items and if the first item * is a String, that String is returned, null * otherwise. */ public String getString() { if (objects != null && objects.size() > 0) { Object o = objects.elementAt(0); if (o != null && o instanceof String) { return (String)o; } } return null; } /** * Returns the type of this tag's content as a TAG_TYPE_xyz constant. */ public int getType() { return type; } /** * Returns the Vector encapsulating the items stored in this tag. * @see #setVector */ public Vector getVector() { return objects; } /** * Returns if the value(s) stored in this tag are of type BYTE, SHORT or * LONG. * Note that BYTE and SHORT have the same meaning as in Java (one and two bytes * large) while LONG is a 32-bit-value, just like int in Java. * @return if this tag's contains integer values <= 32 bits */ public boolean isInt() { return (type == TAG_TYPE_BYTE || type == TAG_TYPE_SHORT || type == TAG_TYPE_LONG); } /** * If this tag encapsulates more than one item or a single * item that does not fit into four bytes, this Vector * will store all elements in it. * The size() method called on that Vector object returns * the same value as getCount(). * @param vector the Vector with the items to be encapsulated by this tag * @see #getVector */ public void setVector(Vector vector) { objects = vector; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/PalmCodec.java0000664000000000000000000012534010523755353023775 0ustar /* * PalmCodec * * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.RandomAccessFile; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.codecs.WrongFileFormatException; import net.sourceforge.jiu.data.BilevelImage; import net.sourceforge.jiu.data.ByteChannelImage; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.MemoryBilevelImage; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.RGBIndex; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.WrongParameterException; import net.sourceforge.jiu.util.ArrayConverter; import net.sourceforge.jiu.util.SeekableByteArrayOutputStream; /** * A codec to read and write image files in the native image file format of * Palm OS, * an operating system for handheld devices. * *

Supported file types when loading

* This codec reads uncompressed, scan line compressed and RLE compressed Palm files * with bit depths of 1, 2, 4, 8 and 16 bits per pixel. * Not supported are the Packbits compression algorithm or any color depths other * then the aforementioned. * *

Supported image types when saving

* Compression types Uncompressed, Scan line and RLE are written. * When saving an image as a Palm, the image data classes will be mapped to file types as follows: * * *

I/O objects

* This codec supports all the I/O classes that are considered in ImageCodec. * If you save images and want a correct compressed size field * in the resulting Palm file, make sure to give a RandomAccessFile object to * the codec. * Or simply use {@link #setFile} which does that automatically. * *

File extension

* This codec suggests .palm as file extension for this file format. * This is by no means official, but I find it helpful. * *

Transparency information

* The transparency index in a Palm file is saved and loaded, but a loaded index * is not stored in the image object as there is no support for transparency information of * any kind in PixelImage yet. * The RGB transparency color that is present in a file only in direct color mode * is read but not written. * *

Bounds

* The bounds concept of ImageCodec is supported so that you can load or save * only part of an image. * *

Open questions on the Palm file format

* * *

Known problems

* * *

Usage examples

* Load an image from a Palm image file: *
 * PalmCodec codec = new PalmCodec();
 * codec.setFile("test.palm", CodecMode.LOAD);
 * codec.process();
 * PixelImage image = codec.getImage();
 * codec.close();
 * 
* Save an image to a Palm file using RLE compression: *
 * PalmCodec codec = new PalmCodec();
 * codec.setImage(image);
 * codec.setCompression(PalmCodec.COMPRESSION_RLE);
 * codec.setFile("out.palm", CodecMode.SAVE);
 * codec.process();
 * codec.close();
 * 
* *

Background

* The code is based on: * * I also received helpful feedback and test images from Bill Janssen. * * @author Marco Schmidt */ public class PalmCodec extends ImageCodec { /** * Constant for compression type Uncompressed. */ public static final int COMPRESSION_NONE = 255; /** * Constant for compression type Packbits. */ public static final int COMPRESSION_PACKBITS = 2; /** * Constant for compression type RLE (run length encoding). */ public static final int COMPRESSION_RLE = 1; /** * Constant for compression type Scanline. */ public static final int COMPRESSION_SCANLINE = 0; private static final int FLAG_COMPRESSED = 0x8000; private static final int FLAG_COLOR_TABLE = 0x4000; private static final int FLAG_TRANSPARENCY = 0x2000; //private static final int FLAG_INDIRECT = 0x1000; //private static final int FLAG_FOR_SCREEN = 0x0800; private static final int FLAG_DIRECT_COLOR = 0x0400; //private static final int FLAG_4_BYTE_FIELD = 0x0200; // following the Palm OS default palettes // instead of short we could use byte but that would require converting // all values > 127 to byte representation (-128 .. 128) private static final short[][] PALM_SYSTEM_PALETTE_4_GRAY = new short[][] { { 255, 255, 255}, { 192, 192, 192}, { 128, 128, 128 }, { 0, 0, 0 } }; private static final short[][] PALM_SYSTEM_PALETTE_16_COLOR = new short[][] { { 255, 255, 255}, { 128, 128, 128 }, { 128, 0, 0 }, { 128, 128, 0 }, { 0, 128, 0}, { 0, 128, 128 }, { 0, 0, 128 }, { 128, 0, 128 }, { 255, 0, 255}, { 192, 192, 192 }, { 255, 0, 0 }, { 255, 255, 0 }, { 0, 255, 0}, { 0, 255, 255 }, { 0, 0, 255 }, { 0, 0, 0 } }; private static final short[][] PALM_SYSTEM_PALETTE_16_GRAY = new short[][] { { 255, 255, 255}, { 238, 238, 238 }, { 221, 221, 221 }, { 204, 204, 204 }, { 187, 187, 187}, { 170, 170, 170 }, { 153, 153, 153 }, { 136, 136, 136 }, { 119, 119, 119}, { 102, 102, 102 }, { 85, 85, 85 }, { 68, 68, 68 }, { 51, 51, 51}, { 34, 34, 34 }, { 17, 17, 17 }, { 0, 0, 0 } }; private static final short[][] PALM_SYSTEM_PALETTE_256 = new short[][] { { 255, 255, 255 }, { 255, 204, 255 }, { 255, 153, 255 }, { 255, 102, 255 }, { 255, 51, 255 }, { 255, 0, 255 }, { 255, 255, 204 }, { 255, 204, 204 }, { 255, 153, 204 }, { 255, 102, 204 }, { 255, 51, 204 }, { 255, 0, 204 }, { 255, 255, 153 }, { 255, 204, 153 }, { 255, 153, 153 }, { 255, 102, 153 }, { 255, 51, 153 }, { 255, 0, 153 }, { 204, 255, 255 }, { 204, 204, 255 }, { 204, 153, 255 }, { 204, 102, 255 }, { 204, 51, 255 }, { 204, 0, 255 }, { 204, 255, 204 }, { 204, 204, 204 }, { 204, 153, 204 }, { 204, 102, 204 }, { 204, 51, 204 }, { 204, 0, 204 }, { 204, 255, 153 }, { 204, 204, 153 }, { 204, 153, 153 }, { 204, 102, 153 }, { 204, 51, 153 }, { 204, 0, 153 }, { 153, 255, 255 }, { 153, 204, 255 }, { 153, 153, 255 }, { 153, 102, 255 }, { 153, 51, 255 }, { 153, 0, 255 }, { 153, 255, 204 }, { 153, 204, 204 }, { 153, 153, 204 }, { 153, 102, 204 }, { 153, 51, 204 }, { 153, 0, 204 }, { 153, 255, 153 }, { 153, 204, 153 }, { 153, 153, 153 }, { 153, 102, 153 }, { 153, 51, 153 }, { 153, 0, 153 }, { 102, 255, 255 }, { 102, 204, 255 }, { 102, 153, 255 }, { 102, 102, 255 }, { 102, 51, 255 }, { 102, 0, 255 }, { 102, 255, 204 }, { 102, 204, 204 }, { 102, 153, 204 }, { 102, 102, 204 }, { 102, 51, 204 }, { 102, 0, 204 }, { 102, 255, 153 }, { 102, 204, 153 }, { 102, 153, 153 }, { 102, 102, 153 }, { 102, 51, 153 }, { 102, 0, 153 }, { 51, 255, 255 }, { 51, 204, 255 }, { 51, 153, 255 }, { 51, 102, 255 }, { 51, 51, 255 }, { 51, 0, 255 }, { 51, 255, 204 }, { 51, 204, 204 }, { 51, 153, 204 }, { 51, 102, 204 }, { 51, 51, 204 }, { 51, 0, 204 }, { 51, 255, 153 }, { 51, 204, 153 }, { 51, 153, 153 }, { 51, 102, 153 }, { 51, 51, 153 }, { 51, 0, 153 }, { 0, 255, 255 }, { 0, 204, 255 }, { 0, 153, 255 }, { 0, 102, 255 }, { 0, 51, 255 }, { 0, 0, 255 }, { 0, 255, 204 }, { 0, 204, 204 }, { 0, 153, 204 }, { 0, 102, 204 }, { 0, 51, 204 }, { 0, 0, 204 }, { 0, 255, 153 }, { 0, 204, 153 }, { 0, 153, 153 }, { 0, 102, 153 }, { 0, 51, 153 }, { 0, 0, 153 }, { 255, 255, 102 }, { 255, 204, 102 }, { 255, 153, 102 }, { 255, 102, 102 }, { 255, 51, 102 }, { 255, 0, 102 }, { 255, 255, 51 }, { 255, 204, 51 }, { 255, 153, 51 }, { 255, 102, 51 }, { 255, 51, 51 }, { 255, 0, 51 }, { 255, 255, 0 }, { 255, 204, 0 }, { 255, 153, 0 }, { 255, 102, 0 }, { 255, 51, 0 }, { 255, 0, 0 }, { 204, 255, 102 }, { 204, 204, 102 }, { 204, 153, 102 }, { 204, 102, 102 }, { 204, 51, 102 }, { 204, 0, 102 }, { 204, 255, 51 }, { 204, 204, 51 }, { 204, 153, 51 }, { 204, 102, 51 }, { 204, 51, 51 }, { 204, 0, 51 }, { 204, 255, 0 }, { 204, 204, 0 }, { 204, 153, 0 }, { 204, 102, 0 }, { 204, 51, 0 }, { 204, 0, 0 }, { 153, 255, 102 }, { 153, 204, 102 }, { 153, 153, 102 }, { 153, 102, 102 }, { 153, 51, 102 }, { 153, 0, 102 }, { 153, 255, 51 }, { 153, 204, 51 }, { 153, 153, 51 }, { 153, 102, 51 }, { 153, 51, 51 }, { 153, 0, 51 }, { 153, 255, 0 }, { 153, 204, 0 }, { 153, 153, 0 }, { 153, 102, 0 }, { 153, 51, 0 }, { 153, 0, 0 }, { 102, 255, 102 }, { 102, 204, 102 }, { 102, 153, 102 }, { 102, 102, 102 }, { 102, 51, 102 }, { 102, 0, 102 }, { 102, 255, 51 }, { 102, 204, 51 }, { 102, 153, 51 }, { 102, 102, 51 }, { 102, 51, 51 }, { 102, 0, 51 }, { 102, 255, 0 }, { 102, 204, 0 }, { 102, 153, 0 }, { 102, 102, 0 }, { 102, 51, 0 }, { 102, 0, 0 }, { 51, 255, 102 }, { 51, 204, 102 }, { 51, 153, 102 }, { 51, 102, 102 }, { 51, 51, 102 }, { 51, 0, 102 }, { 51, 255, 51 }, { 51, 204, 51 }, { 51, 153, 51 }, { 51, 102, 51 }, { 51, 51, 51 }, { 51, 0, 51 }, { 51, 255, 0 }, { 51, 204, 0 }, { 51, 153, 0 }, { 51, 102, 0 }, { 51, 51, 0 }, { 51, 0, 0 }, { 0, 255, 102 }, { 0, 204, 102 }, { 0, 153, 102 }, { 0, 102, 102 }, { 0, 51, 102 }, { 0, 0, 102 }, { 0, 255, 51 }, { 0, 204, 51 }, { 0, 153, 51 }, { 0, 102, 51 }, { 0, 51, 51 }, { 0, 0 , 51 }, { 0, 255, 0 }, { 0, 204, 0 }, { 0, 153, 0 }, { 0, 102, 0 }, { 0, 51, 0 }, { 17, 17, 17 }, { 34, 34, 34 }, { 68, 68, 68 }, { 85, 85, 85 }, { 119, 119, 119 }, { 136, 136, 136 }, { 170, 170, 170 }, { 187, 187, 187 }, { 221, 221, 221 }, { 238, 238, 238 }, { 192, 192, 192 }, { 128, 0, 0 }, { 128, 0, 128 }, { 0, 128, 0 }, { 0, 128, 128 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; private int bitsPerPixel; private int blueBits; private int bytesPerRow; private int compression; private long compressedDataOffset; private int flags; private int greenBits; private int height; private Palette palette; //private int nextImageOffset; private int redBits; private byte[] rgb; private byte[] transColor; private int transparencyIndex = -1; private int version; private int width; private static Palette createPalette(short[][] data) { Palette result = new Palette(data.length); for (int i = 0; i < data.length; i++) { result.put(i, data[i][0], data[i][1], data[i][2]); } return result; } /** * Creates the 2 bits per pixel Palm system palette with grayscale values. * This palette is used when no custom palette is defined in a 2 bpp image. * @return Palm's default palette for 2 bits per pixel (grayscale), with 4 entries */ public static Palette createSystem2BitGrayscalePalette() { return createPalette(PALM_SYSTEM_PALETTE_4_GRAY); } /** * Creates the 4 bits per pixel Palm system palette with color values. * This palette (or the 4 bpp grayscale palette) is used when no custom palette is defined in a 4 bpp image. * @return Palm's default palette for 4 bits per pixel (color), with 16 entries */ public static Palette createSystem4BitColorPalette() { return createPalette(PALM_SYSTEM_PALETTE_16_COLOR); } /** * Creates the 4 bits per pixel Palm system palette with grayscale values. * This palette (or the 4 bpp color palette) is used when no custom palette is defined in a 4 bpp image. * @return Palm's default palette for 4 bits per pixel (grayscale), with 16 entries */ public static Palette createSystem4BitGrayscalePalette() { return createPalette(PALM_SYSTEM_PALETTE_16_GRAY); } /** * Creates the 8 bits per pixel Palm system palette. * This palette is used when no custom palette is defined in an 8 bpp image. * @return Palm's default palette for 8 bits per pixel, with 256 entries */ public static Palette createSystem8BitPalette() { return createPalette(PALM_SYSTEM_PALETTE_256); } /** * Returns the Palm compression method. * This should be one of the COMPRESSION_xyz constants of this class. * @return integer value with the compression method (found in a file when * loading or to be used for saving) * @see #setCompression */ public int getCompression() { return compression; } public String getFormatName() { return "Palm image file format"; } public String[] getMimeTypes() { return null; } /** * Returns the transpareny index if one is available ({@link #hasTransparencyIndex} * returns true) or an undefined value otherwise. * @see #hasTransparencyIndex * @see #removeTransparencyIndex * @see #setTransparencyIndex */ public int getTransparencyIndex() { return transparencyIndex; } /** * Returns whether a transpareny index is available and can be * retrieved via {@link #getTransparencyIndex}. * @return transparency index, a positive value that is a valid index into the palette * @see #getTransparencyIndex * @see #removeTransparencyIndex * @see #setTransparencyIndex */ public boolean hasTransparencyIndex() { return transparencyIndex >= 0; } private void invertBilevelData(byte[] row) { if (row != null) { for (int i = 0; i < row.length; i++) { row[i] = (byte)~row[i]; } } } private static boolean isEqualPalette(Palette palette, short[][] data) { if (palette == null || data == null) { return false; } if (palette.getNumEntries() != data.length) { return false; } for (int i = 0; i < data.length; i++) { int red = palette.getSample(RGBIndex.INDEX_RED, i); int green = palette.getSample(RGBIndex.INDEX_GREEN, i); int blue = palette.getSample(RGBIndex.INDEX_BLUE, i); short[] color = data[i]; if (color[0] != red || color[1] != green || color[2] != blue) { return false; } } return true; } public boolean isLoadingSupported() { return true; } /** * Returns if the argument palette is the Palm system grayscale palette * with 4 entries. * @param palette to be checked * @see #createSystem2BitGrayscalePalette */ public static boolean isPalmSystemPaletteGray4(Palette palette) { return isEqualPalette(palette, PALM_SYSTEM_PALETTE_4_GRAY); } /** * Returns if the argument palette is the Palm system grayscale palette * with 16 entries. * @param palette to be checked * @see #createSystem4BitGrayscalePalette */ public static boolean isPalmSystemPaletteGray16(Palette palette) { return isEqualPalette(palette, PALM_SYSTEM_PALETTE_16_GRAY); } /** * Returns if the argument palette is the Palm system color palette * with 16 entries. * @param palette to be checked * @see #createSystem4BitColorPalette */ public static boolean isPalmSystemPaletteColor16(Palette palette) { return isEqualPalette(palette, PALM_SYSTEM_PALETTE_16_COLOR); } /** * Returns if the argument palette is the Palm system palette * with 256 colors. * @param palette to be checked * @see #createSystem8BitPalette * @return if the argument is an 8 bits per pixel Palm system palette */ public static boolean isPalmSystemPalette256(Palette palette) { return isEqualPalette(palette, PALM_SYSTEM_PALETTE_256); } public boolean isSavingSupported() { return true; } private void load() throws InvalidFileStructureException, IOException, OperationFailedException, UnsupportedTypeException, WrongFileFormatException { DataInput in = getInputAsDataInput(); loadHeader(in); loadPalette(in); loadImage(in); } private void loadHeader(DataInput in) throws InvalidFileStructureException, IOException, UnsupportedTypeException, WrongFileFormatException { width = in.readShort() & 0xffff; height = in.readShort() & 0xffff; bytesPerRow = in.readShort() & 0xffff; flags = in.readShort() & 0xffff; bitsPerPixel = in.readUnsignedByte(); version = in.readUnsignedByte(); //nextImageOffset = in.readShort() & 0xffff; in.readShort(); transparencyIndex = in.readUnsignedByte() & 0xffff; compression = in.readUnsignedByte() & 0xffff; in.skipBytes(2); // reserved if ((flags & FLAG_COMPRESSED) == 0) { compression = COMPRESSION_NONE; } boolean unsupportedDirectColor = false; if ((flags & FLAG_DIRECT_COLOR) != 0) { // read direct color information (8 bytes) redBits = in.readUnsignedByte(); greenBits = in.readUnsignedByte(); blueBits = in.readUnsignedByte(); unsupportedDirectColor = redBits != 5 || greenBits != 6 || blueBits != 5; in.skipBytes(2); transColor = new byte[3]; in.readFully(transColor); } if (width < 1 || height < 1 || unsupportedDirectColor || (bitsPerPixel != 1 && bitsPerPixel != 2 && bitsPerPixel != 4 && bitsPerPixel != 8 && bitsPerPixel != 16) || (compression != COMPRESSION_NONE && compression != COMPRESSION_RLE && compression != COMPRESSION_SCANLINE)) { throw new WrongFileFormatException("Not a file in Palm image file format."); } /*System.out.println("width=" + width + ", height=" + height + ", bytes per row=" + bytesPerRow + ", flags=" + flags + ", bpp=" + bitsPerPixel + ", version=" + version + ", palette=" + (((flags & FLAG_COLOR_TABLE) != 0) ? "y" : "n") + ", transparent=" + transparencyIndex + ", compression=" + compression);*/ } private void loadImage(DataInput in) throws InvalidFileStructureException, IOException, UnsupportedTypeException, WrongFileFormatException, WrongParameterException { setBoundsIfNecessary(width, height); checkBounds(width, height); PixelImage image = getImage(); /* if there is no image to be reused (image == null), create one; otherwise check if the provided image is of the right type and throw an exception if not */ if (palette != null) { // paletted image if (image == null) { image = new MemoryPaletted8Image(getBoundsWidth(), getBoundsHeight(), palette); } else { if (!(image instanceof Paletted8Image)) { throw new WrongParameterException("Image to be used for loading must be paletted for this file."); } ((Paletted8Image)image).setPalette(palette); } } else { switch(bitsPerPixel) { case(1): // bilevel image (black and white) { if (image == null) { image = new MemoryBilevelImage(getBoundsWidth(), getBoundsHeight()); } else { if (!(image instanceof BilevelImage)) { throw new WrongParameterException("Image to be used for " + "loading must implement BilevelImage for this file."); } } break; } case(16): // RGB direct color { if (image == null) { image = new MemoryRGB24Image(getBoundsWidth(), getBoundsHeight()); } else { if (!(image instanceof RGB24Image)) { throw new WrongParameterException("Image to be used for " + "loading must implement RGB24Image."); } } rgb = new byte[width * 3]; break; } default: // grayscale, 2, 4 or 8 bits per pixel { if (image == null) { image = new MemoryGray8Image(getBoundsWidth(), getBoundsHeight()); } else { if (!(image instanceof Gray8Image)) { throw new WrongParameterException("Image to be used for " + "loading must implement Gray8Image for this file."); } } } } } setImage(image); // check if image has the correct pixel resolution if (image.getWidth() != getBoundsWidth() || image.getHeight() != getBoundsHeight()) { throw new WrongParameterException("Image to be reused has wrong resolution (must have " + getBoundsWidth() + " x " + getBoundsHeight() + " pixels)."); } loadImageData(in); } private void loadImageData(DataInput in) throws InvalidFileStructureException, IOException { PixelImage image = getImage(); // if compression is used, read a short with the compressed data size if (compression != COMPRESSION_NONE) { //int compressedDataSize = in.readShort() & 0xffff; in.readShort(); } byte[] row = new byte[bytesPerRow]; final int NUM_ROWS = getBoundsY2() + 1; for (int y = 0; y < NUM_ROWS; y++) { switch(compression) { case(COMPRESSION_NONE): { in.readFully(row, 0, bytesPerRow); break; } case(COMPRESSION_RLE): { int index = 0; do { int num = in.readUnsignedByte(); if (num < 1 || index + num > bytesPerRow) { String message = "At index=" + index + ", y=" + y + " there is a run length of " + num; System.err.println("ERROR decoding RLE: " + message); throw new InvalidFileStructureException(message); } byte value = in.readByte(); while (num-- > 0) { row[index++] = value; } } while (index < bytesPerRow); break; } case(COMPRESSION_SCANLINE): { int index = 0; int pixelMask = 0; int mask = 0; do { if (mask == 0) { pixelMask = in.readUnsignedByte(); mask = 0x80; } if ((pixelMask & mask) == 0) { index++; } else { row[index++] = in.readByte(); } mask >>= 1; } while (index < bytesPerRow); break; } case(COMPRESSION_PACKBITS): { // compression algorithm unknown, thus not implemented // this statement cannot be reached, the codec makes // sure that an exception gets thrown when the packbits // algorithm is actually encountered in a file; // if you have a description of the algorithm, please // contact the JIU maintainers break; } } store(image, y, row); setProgress(y, NUM_ROWS); } } private void loadPalette(DataInput in) throws InvalidFileStructureException, IOException, UnsupportedTypeException, WrongFileFormatException { if ((flags & FLAG_COLOR_TABLE) == 0) { switch(bitsPerPixel) { case(2): { palette = createSystem2BitGrayscalePalette(); break; } case(4): { palette = createSystem4BitGrayscalePalette(); // or color? break; } case(8): { palette = createSystem8BitPalette(); break; } } return; } int numEntries = in.readShort() & 0xffff; if (numEntries < 1 || numEntries > 256) { throw new WrongFileFormatException("Not a Palm image file, invalid number of palette entries: " + numEntries); } palette = new Palette(numEntries, 255); for (int i = 0; i < numEntries; i++) { //int reserved = in.readUnsignedByte(); in.readUnsignedByte(); int red = in.readUnsignedByte(); int green = in.readUnsignedByte(); int blue = in.readUnsignedByte(); palette.putSample(RGBIndex.INDEX_RED, i, red); palette.putSample(RGBIndex.INDEX_GREEN, i, green); palette.putSample(RGBIndex.INDEX_BLUE, i, blue); } } public void process() throws InvalidFileStructureException, MissingParameterException, OperationFailedException, WrongParameterException { try { initModeFromIOObjects(); if (getMode() == CodecMode.LOAD) { load(); } else if (getMode() == CodecMode.SAVE) { save(); } else { throw new WrongParameterException("Could find neither objects for loading nor for saving."); } } catch (IOException ioe) { throw new OperationFailedException("I/O error in Palm codec: " + ioe.toString()); } } /** * Removes the transparency index if one has been set. * @see #getTransparencyIndex * @see #hasTransparencyIndex * @see #setTransparencyIndex */ public void removeTransparencyIndex() { transparencyIndex = -1; } private void save() throws IOException, OperationFailedException, UnsupportedTypeException { // get image, set bounds if necessary and check existing bounds PixelImage image = getImage(); if (image == null) { throw new MissingParameterException("Need image to save."); } setBoundsIfNecessary(image.getWidth(), image.getHeight()); checkBounds(image.getWidth(), image.getHeight()); // get output object DataOutput out = getOutputAsDataOutput(); if (out == null) { throw new MissingParameterException("Could not get DataOutput object when saving in Palm file format."); } // initialize fields to be written to the header width = getBoundsWidth(); height = getBoundsHeight(); flags = 0; if (hasTransparencyIndex()) { flags |= FLAG_TRANSPARENCY; } if (compression != COMPRESSION_NONE) { flags |= FLAG_COMPRESSED; } version = 0; if (bitsPerPixel > 1) { version = 1; } if (hasTransparencyIndex() || compression != COMPRESSION_NONE) { version = 2; } //nextImageOffset = 0; compressedDataOffset = 0; // check image types if (image instanceof BilevelImage) { save(out, (BilevelImage)image); } else if (image instanceof Gray8Image) { save(out, (Gray8Image)image); } else if (image instanceof Paletted8Image) { save(out, (Paletted8Image)image); } else if (image instanceof RGB24Image) { save(out, (RGB24Image)image); } else { throw new UnsupportedTypeException("Unsupported image type: " + image.getClass().getName()); } } private void save(DataOutput out, BilevelImage image) throws IOException { bytesPerRow = (width + 7) / 8; if ((bytesPerRow % 2) == 1) { bytesPerRow++; } bitsPerPixel = 1; setCorrectVersion(); saveHeader(out); byte[] row = new byte[bytesPerRow]; byte[] prev = null; if (compression == COMPRESSION_SCANLINE) { prev = new byte[row.length]; } final int X1 = getBoundsX1(); final int Y1 = getBoundsY1(); for (int y = 0; y < height; y++) { image.getPackedBytes(X1, y + Y1, width, row, 0, 0); invertBilevelData(row); saveRow(out, y == 0, row, prev); if (compression == COMPRESSION_SCANLINE) { System.arraycopy(row, 0, prev, 0, row.length); } setProgress(y, height); } saveFinalCompressedSize(out); } private void save(DataOutput out, Gray8Image image) throws IOException { bytesPerRow = width; if ((bytesPerRow % 2) == 1) { bytesPerRow++; } bitsPerPixel = 8; flags |= FLAG_COLOR_TABLE; setCorrectVersion(); saveHeader(out); out.writeShort(256); // palette length for (int i = 0; i < 256; i++) { out.writeByte(0); // reserved out.writeByte(i); // red out.writeByte(i); // green out.writeByte(i); // blue } compressedDataOffset += 2 + 4 * 256; saveInitialCompressedSize(out); byte[] row = new byte[width]; byte[] prev = null; if (compression == COMPRESSION_SCANLINE) { prev = new byte[width]; } final int X1 = getBoundsX1(); final int Y1 = getBoundsY1(); for (int y = 0; y < height; y++) { image.getByteSamples(0, X1, y + Y1, width, 1, row, 0); saveRow(out, y == 0, row, prev); if (compression == COMPRESSION_SCANLINE) { System.arraycopy(row, 0, prev, 0, row.length); } setProgress(y, height); } saveFinalCompressedSize(out); } private void save(DataOutput out, Paletted8Image image) throws IOException { Palette palette = image.getPalette(); boolean system256Palette = isPalmSystemPalette256(palette); boolean system16GrayPalette = isPalmSystemPaletteGray16(palette); boolean system16ColorPalette = isPalmSystemPaletteColor16(palette); boolean system4GrayPalette = isPalmSystemPaletteGray4(palette); boolean customPalette = !(system256Palette || system16GrayPalette || system16ColorPalette || system4GrayPalette); if (customPalette) { flags |= FLAG_COLOR_TABLE; } // determine bits per pixel, bytesPerRow if (palette.getNumEntries() <= 4) { bitsPerPixel = 2; bytesPerRow = (width + 3) / 4; } else if (palette.getNumEntries() <= 16) { bitsPerPixel = 4; bytesPerRow = (width + 1) / 2; } else { bitsPerPixel = 8; bytesPerRow = width; } //System.out.println("initial bytesPerRow=" + bytesPerRow); // make sure number of bytes per row is even if ((bytesPerRow % 2) == 1) { bytesPerRow++; } setCorrectVersion(); saveHeader(out); // write the custom palette if necessary if (customPalette) { savePalette(out, palette); } // if compression type != uncompressed write two bytes with compressed size to output saveInitialCompressedSize(out); // initialize row buffers byte[] row = new byte[width]; byte[] prev = null; if (compression == COMPRESSION_SCANLINE) { prev = new byte[row.length]; } byte[] temp = null; if (bitsPerPixel < 8) { temp = new byte[width]; } // get position of upper left corner of image part to be written final int X1 = getBoundsX1(); final int Y1 = getBoundsY1(); // write all rows to file, top to bottom for (int y = 0; y < height; y++) { switch(bitsPerPixel) { case(2): { image.getByteSamples(0, X1, y + Y1, width, 1, temp, 0); ArrayConverter.encodePacked2Bit(temp, 0, row, 0, width); break; } case(4): { image.getByteSamples(0, X1, y + Y1, width, 1, temp, 0); ArrayConverter.encodePacked4Bit(temp, 0, row, 0, width); break; } case(8): { image.getByteSamples(0, X1, y + Y1, width, 1, row, 0); break; } } saveRow(out, y == 0, row, prev); if (compression == COMPRESSION_SCANLINE) { System.arraycopy(row, 0, prev, 0, row.length); } setProgress(y, height); } saveFinalCompressedSize(out); } private void save(DataOutput out, RGB24Image image) throws IOException { bytesPerRow = width * 2; bitsPerPixel = 16; flags |= FLAG_DIRECT_COLOR; setCorrectVersion(); saveHeader(out); // write 8 bytes for direct color information to file out.write(5); // red bits out.write(6); // green bits out.write(5); // blue bits int i = 5; while (i-- > 0) { out.write(0); } compressedDataOffset += 8; // allocate row buffer(s) byte[] row = new byte[width * 2]; byte[] prev = null; if (compression == COMPRESSION_SCANLINE) { prev = new byte[row.length]; } byte[] red = new byte[width]; byte[] green = new byte[width]; byte[] blue = new byte[width]; final int X1 = getBoundsX1(); final int Y1 = getBoundsY1(); for (int y = 0; y < height; y++) { // get samples for each channel of the row to be written out image.getByteSamples(RGBIndex.INDEX_RED, X1, y + Y1, width, 1, red, 0); image.getByteSamples(RGBIndex.INDEX_GREEN, X1, y + Y1, width, 1, green, 0); image.getByteSamples(RGBIndex.INDEX_BLUE, X1, y + Y1, width, 1, blue, 0); // encode row as 16 bit samples, big endian, 5-6-5 ArrayConverter.encodeRGB24ToPackedRGB565BigEndian( red, 0, green, 0, blue, 0, row, 0, width); saveRow(out, y == 0, row, prev); if (compression == COMPRESSION_SCANLINE) { System.arraycopy(row, 0, prev, 0, row.length); } setProgress(y, height); } saveFinalCompressedSize(out); } private void saveFinalCompressedSize(DataOutput out) throws IOException { if ((flags & FLAG_COMPRESSED) == 0) { return; } if (!(out instanceof RandomAccessFile || out instanceof SeekableByteArrayOutputStream)) { return; } long pos = -1; if (out instanceof RandomAccessFile) { RandomAccessFile raf = (RandomAccessFile)out; pos = raf.length(); } else if (out instanceof SeekableByteArrayOutputStream) { SeekableByteArrayOutputStream sbaos = (SeekableByteArrayOutputStream)out; pos = sbaos.getPosition(); } long compressedSize = pos - compressedDataOffset; compressedSize = Math.min(0xffff, compressedSize); /* System.out.println("compressed data offset=" + compressedDataOffset); System.out.println("position after compression=" + pos); System.out.println("compressed size=" + compressedSize + " / " + Integer.toHexString((int)compressedSize)); */ if (out instanceof RandomAccessFile) { RandomAccessFile raf = (RandomAccessFile)out; raf.seek(compressedDataOffset); raf.writeShort((int)compressedSize); } else if (out instanceof SeekableByteArrayOutputStream) { SeekableByteArrayOutputStream sbaos = (SeekableByteArrayOutputStream)out; sbaos.seek((int)compressedDataOffset); sbaos.write((int)(compressedSize >> 8) & 0xff); sbaos.write((int)compressedSize & 0xff); } } private void saveHeader(DataOutput out) throws IOException { out.writeShort(width); out.writeShort(height); out.writeShort(bytesPerRow); out.writeShort(flags); out.writeByte(bitsPerPixel); out.writeByte(version); out.writeShort(0); // next image offset out.writeByte(transparencyIndex); out.writeByte(compression); out.writeShort(0); // reserved compressedDataOffset = 16; } private void saveInitialCompressedSize(DataOutput out) throws IOException { if ((flags & FLAG_COMPRESSED) == 0) { return; } out.writeShort(bytesPerRow * height); // just a guess } private void savePalette(DataOutput out, Palette palette) throws IOException { out.writeShort(palette.getNumEntries()); for (int i = 0; i < palette.getNumEntries(); i++) { out.writeByte(0); // reserved out.writeByte(palette.getSample(RGBIndex.INDEX_RED, i)); out.writeByte(palette.getSample(RGBIndex.INDEX_GREEN, i)); out.writeByte(palette.getSample(RGBIndex.INDEX_BLUE, i)); } compressedDataOffset += 2 + 4 * palette.getNumEntries(); } private void saveRow(DataOutput out, boolean firstRow, byte[] row, byte[] prev) throws IOException { switch(compression) { case(COMPRESSION_NONE): { out.write(row, 0, bytesPerRow); break; } case(COMPRESSION_RLE): { saveRowRLE(out, row); break; } case(COMPRESSION_SCANLINE): { saveRowScanLine(out, firstRow, row, prev); break; } } } /* int srcOffset = 0; // points into uncompressed data array "row" do { // determine length of next run, between 1 and 255 byte value = row[srcOffset]; int lookAheadOffset = srcOffset + 1; int bytesLeft = bytesPerRow - lookAheadOffset; if (bytesLeft > 255) { bytesLeft = 255; } while (bytesLeft != 0 && value == row[lookAheadOffset]) { lookAheadOffset++; bytesLeft--; } int runLength = lookAheadOffset - srcOffset; if (runLength < 1) { System.err.println("FATAL: RUN LENGTH <0"); System.exit(1); } if (srcOffset + runLength > bytesPerRow) { System.err.println("FATAL: srcOffset=" + srcOffset+ " runLength=" + runLength + " bytesPerRow=" + bytesPerRow); System.exit(1); } if (srcOffset == 13 && runLength == 2) { System.err.println("FATAL: 13 2 "); System.exit(1); } // write pair (length-of-run, value) to output out.writeByte(runLength); out.writeByte(value & 0xff); // update srcOffset to point to the next byte in row to be encoded srcOffset += runLength; } while (srcOffset < bytesPerRow);*/ private void saveRowRLE(DataOutput out, byte[] row) throws IOException { int srcOffset = 0; // points into uncompressed data array "row" do { // determine length of next run, between 1 and 255 int runLength = 1; int bytesLeft = bytesPerRow - srcOffset; byte value = row[srcOffset]; while (bytesLeft != 0 && srcOffset + runLength < row.length && value == row[srcOffset + runLength]) { bytesLeft--; runLength++; if (runLength == 255) { bytesLeft = 0; } } srcOffset += runLength; out.writeByte(runLength); out.writeByte(value & 0xff); } while (srcOffset < bytesPerRow); } private void saveRowScanLine(DataOutput out, boolean firstRow, byte[] row, byte[] prev) throws IOException { int bytesLeft = bytesPerRow; int srcOffset = 0; byte[] bytes = new byte[8]; do { int pixelMask = 0; int bitMask = 128; int numBytesToCheck = Math.min(8, bytesLeft); int numOutputBytes = 0; bytesLeft -= numBytesToCheck; while (numBytesToCheck-- != 0) { if (row[srcOffset] != prev[srcOffset]) { pixelMask |= bitMask; bytes[numOutputBytes++] = row[srcOffset]; } srcOffset++; bitMask >>= 1; } out.writeByte(pixelMask); out.write(bytes, 0, numOutputBytes); } while (bytesLeft != 0); } /** * Sets the compression algorithm to be used for saving an image. * @see #getCompression * @param newCompressionType int value that is one of the COMPRESSION_xyz constants of this class * @throws IllegalArgumentException if the compression type is unsupported */ public void setCompression(int newCompressionType) { if (newCompressionType != COMPRESSION_NONE && newCompressionType != COMPRESSION_RLE && newCompressionType != COMPRESSION_SCANLINE) { throw new IllegalArgumentException("Unsupported Palm compression type for writing."); } compression = newCompressionType; } private void setCorrectVersion() { version = 0; if (bitsPerPixel > 1) { version = 1; } if (hasTransparencyIndex() || getCompression() == COMPRESSION_SCANLINE || getCompression() == COMPRESSION_RLE) { version = 2; } } /** * Reuses super.setFile when used for CodecMode.LOAD, but * creates a RandomAccessFile instead of a FileOutputStream * in write mode so that the compressed size can be written * correcly (requires a seek operation). * @param fileName name of the file to be opened * @param codecMode defines whether this codec object is to be used for loading or saving */ public void setFile(String fileName, CodecMode codecMode) throws IOException, UnsupportedCodecModeException { if (codecMode == CodecMode.LOAD) { super.setFile(fileName, codecMode); } else { setRandomAccessFile(new RandomAccessFile(fileName, "rw"), CodecMode.SAVE); } } /** * Sets a new transparency index when saving an image. * If this method is called, the argument value is used as an index * into the palette for a color that is supposed to be transparent. * When the resulting Palm image file is drawn onto some background, * all pixels in the color pointed to by the transparency index are not * supposed to be overdrawn so that the background is visisble at * those places. * @param newIndex the new transparency index, must be smaller than the number of entries in the palette * @see #getTransparencyIndex * @see #hasTransparencyIndex * @see #removeTransparencyIndex */ public void setTransparencyIndex(int newIndex) { if (newIndex < 0) { throw new IllegalArgumentException("Transparency index must be 0 or larger."); } transparencyIndex = newIndex; } private void store(PixelImage image, int y, byte[] row) { if (!isRowRequired(y)) { return; } y -= getBoundsY1(); switch(bitsPerPixel) { case(1): { BilevelImage bimage = (BilevelImage)image; invertBilevelData(row); bimage.putPackedBytes(0, y, getBoundsWidth(), row, getBoundsX1() / 8, getBoundsX1() % 8); break; } case(2): { byte[] dest = new byte[bytesPerRow * 4]; ArrayConverter.decodePacked2Bit(row, 0, dest, 0, bytesPerRow); ByteChannelImage bcimg = (ByteChannelImage)image; bcimg.putByteSamples(0, 0, y, getBoundsWidth(), 1, dest, getBoundsX1()); break; } case(4): { byte[] dest = new byte[bytesPerRow * 2]; ArrayConverter.decodePacked4Bit(row, 0, dest, 0, bytesPerRow); ByteChannelImage bcimg = (ByteChannelImage)image; bcimg.putByteSamples(0, 0, y, getBoundsWidth(), 1, dest, getBoundsX1()); break; } case(8): { ByteChannelImage bcimg = (ByteChannelImage)image; bcimg.putByteSamples(0, 0, y, getBoundsWidth(), 1, row, getBoundsX1()); break; } case(16): { ArrayConverter.decodePackedRGB565BigEndianToRGB24( row, getBoundsX1() * 2, rgb, 0, rgb, width, rgb, width * 2, getBoundsWidth()); RGB24Image img = (RGB24Image)image; img.putByteSamples(RGBIndex.INDEX_RED, 0, y, getBoundsWidth(), 1, rgb, 0); img.putByteSamples(RGBIndex.INDEX_GREEN, 0, y, getBoundsWidth(), 1, rgb, width); img.putByteSamples(RGBIndex.INDEX_BLUE, 0, y, getBoundsWidth(), 1, rgb, width * 2); break; } } } public String suggestFileExtension(PixelImage image) { return ".palm"; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/InvalidFileStructureException.java0000664000000000000000000000243207741250131030120 0ustar /* * InvalidFileStructureException * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import net.sourceforge.jiu.ops.OperationFailedException; /** * This exception is thrown during image loading, when the decoding * process is made impossible by errors in the image file. * If a codec has recognized the file format but finds irregularities in the * data and cannot continue loading, it is supposed to throw an instance of this * exception class. * An unexpected end of the input stream also falls into this category. * This typically means that the file is corrupt, but of course it could * also be because of an error in the codec implementation. *

* If the format is not recognized at all, a {@link WrongFileFormatException} * should be thrown. *

* If the format is recognized but cannot be loaded because the codec * does not fully support the file format, a {@link UnsupportedTypeException} * should be thrown. * @author Marco Schmidt * @see UnsupportedTypeException * @see WrongFileFormatException */ public class InvalidFileStructureException extends OperationFailedException { public InvalidFileStructureException(String message) { super(message); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/codecs/PSDCodec.java0000664000000000000000000002462610572432420023526 0ustar /* * PSDCodec * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.codecs; import java.io.DataInput; import java.io.IOException; import net.sourceforge.jiu.codecs.ImageCodec; import net.sourceforge.jiu.codecs.InvalidFileStructureException; import net.sourceforge.jiu.codecs.UnsupportedTypeException; import net.sourceforge.jiu.codecs.WrongFileFormatException; import net.sourceforge.jiu.data.MemoryGray8Image; import net.sourceforge.jiu.data.MemoryPaletted8Image; import net.sourceforge.jiu.data.MemoryRGB24Image; import net.sourceforge.jiu.data.Gray8Image; import net.sourceforge.jiu.data.Palette; import net.sourceforge.jiu.data.Paletted8Image; import net.sourceforge.jiu.data.RGB24Image; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; /** * A codec to read images from Photoshop PSD files. * PSD was created by Adobe for their * Photoshop * image editing software. * Note that only a small subset of valid PSD files is supported by this codec. * Typical file extension is .psd. * @author Marco Schmidt */ public class PSDCodec extends ImageCodec { private final static int MAGIC_8BPS = 0x38425053; private final static int COLOR_MODE_GRAYSCALE = 1; private final static int COLOR_MODE_INDEXED = 2; private final static int COLOR_MODE_RGB_TRUECOLOR = 3; private final static short COMPRESSION_NONE = 0; private final static short COMPRESSION_PACKBITS = 1; private int magic; private int channels; private int height; private int width; private int depth; private int colorMode; private short compression; private DataInput in; private Gray8Image gray8Image; private Palette palette; private Paletted8Image paletted8Image; private RGB24Image rgb24Image; private void allocate() { gray8Image = null; paletted8Image = null; rgb24Image = null; if (depth == 8 && colorMode == COLOR_MODE_RGB_TRUECOLOR) { rgb24Image = new MemoryRGB24Image(getBoundsWidth(), getBoundsHeight()); setImage(rgb24Image); } else if (channels == 1 && depth == 8 && colorMode == 2) { paletted8Image = new MemoryPaletted8Image(width, height, palette); setImage(paletted8Image); } else if (channels == 1 && depth == 8 && colorMode == COLOR_MODE_GRAYSCALE) { gray8Image = new MemoryGray8Image(width, height); setImage(gray8Image); } else { throw new IllegalArgumentException("Unknown image type in PSD file."); } } private static String getColorTypeName(int colorMode) { switch(colorMode) { case(0): return "Black & white"; case(1): return "Grayscale"; case(2): return "Indexed"; case(3): return "RGB truecolor"; case(4): return "CMYK truecolor"; case(7): return "Multichannel"; case(8): return "Duotone"; case(9): return "Lab"; default: return "Unknown (" + colorMode + ")"; } } public String getFormatName() { return "Photoshop (PSD)"; } public String[] getMimeTypes() { return new String[] {"image/psd", "image/x-psd"}; } public boolean isLoadingSupported() { return true; } public boolean isSavingSupported() { return false; } /** * Attempts to load an Image from argument stream in (which * could, as an example, be a RandomAccessFile instance, it * implements the DataInput interface). * Checks a magic byte sequence and then reads all chunks as they appear * in the IFF file. * Will return the resulting image or null if no image body chunk was * encountered before end-of-stream. * Will throw an exception if the file is corrupt, information is missing * or there were reading errors. */ private void load() throws InvalidFileStructureException, IOException, UnsupportedTypeException, WrongFileFormatException { loadHeader(); //System.out.println(width + " x " + height + ", color=" + colorMode + ", channels=" + channels + ", depth=" + depth); // check values if (width < 1 || height < 1) { throw new InvalidFileStructureException("Cannot load image. " + "Invalid pixel resolution in PSD file header (" + width + " x " + height + ")."); } if (colorMode != COLOR_MODE_RGB_TRUECOLOR && colorMode != COLOR_MODE_GRAYSCALE && colorMode != COLOR_MODE_INDEXED) { throw new UnsupportedTypeException("Cannot load image. Only RGB" + " truecolor and indexed color are supported for PSD files. " + "Found: " +getColorTypeName(colorMode)); } if (depth != 8) { throw new UnsupportedTypeException("Cannot load image. Only a depth of 8 bits " + "per channel is supported (found " + depth + " bits)."); } // COLOR MODE DATA int colorModeSize = in.readInt(); //System.out.println("colorModeSize=" + colorModeSize); byte[] colorMap = null; if (colorMode == COLOR_MODE_INDEXED) { if (colorModeSize != 768) { throw new InvalidFileStructureException("Cannot load image." + " Color map length was expected to be 768 (found " + colorModeSize + ")."); } colorMap = new byte[colorModeSize]; in.readFully(colorMap); palette = new Palette(256, 255); for (int index = 0; index < 256; index++) { palette.putSample(Palette.INDEX_RED, index, colorMap[index] & 0xff); palette.putSample(Palette.INDEX_GREEN, index, colorMap[256 + index] & 0xff); palette.putSample(Palette.INDEX_BLUE, index, colorMap[512 + index] & 0xff); } } else { in.skipBytes(colorModeSize); } // IMAGE RESOURCES int resourceLength = in.readInt(); in.skipBytes(resourceLength); //System.out.println("resourceLength=" + resourceLength); // LAYER AND MASK INFORMATION int miscLength = in.readInt(); in.skipBytes(miscLength); //System.out.println("miscLength=" + miscLength); // IMAGE DATA compression = in.readShort(); if (compression != COMPRESSION_NONE && compression != COMPRESSION_PACKBITS) { throw new UnsupportedTypeException("Cannot load image. Unsupported PSD " + "compression type (" + compression + ")"); } //System.out.println("compression=" + compression); loadImageData(); } /** * Reads the PSD header to private members of this class instance. * @throws IOException if there were reading errors */ private void loadHeader() throws IOException, WrongFileFormatException { magic = in.readInt(); if (magic != MAGIC_8BPS) { throw new WrongFileFormatException("Not a valid PSD file " + "(wrong magic byte sequence)."); } in.readShort(); // skip version short value in.skipBytes(6); channels = in.readShort(); height = in.readInt(); width = in.readInt(); depth = in.readShort(); colorMode = in.readShort(); } private void loadPackbitsCompressedData(byte[] data, int offset, int num) throws InvalidFileStructureException, IOException { int x = offset; int max = offset + num; while (x < max) { byte n = in.readByte(); boolean compressed = false; int count = -1; try { if (n >= 0) { // copy next n + 1 bytes literally in.readFully(data, x, n + 1); x += (n + 1); } else { // if n == -128, nothing happens (stupid design decision) if (n != -128) { compressed = true; // otherwise, compute counter count = -((int)n) + 1; // read another byte byte value = in.readByte(); // write this byte counter times to output while (count-- > 0) { data[x++] = value; } } } } catch (ArrayIndexOutOfBoundsException ioobe) { /* if the encoder did anything wrong, the above code could potentially write beyond array boundaries (e.g. if runs of data exceed line boundaries); this would result in an IndexOutOfBoundsException thrown by the virtual machine; to give a more understandable error message to the user, this exception is caught here and a corresponding IOException is thrown */ throw new InvalidFileStructureException("Error: RLE-compressed image " + "file seems to be corrupt (x=" + x + ", count=" + (compressed ? (-((int)n) + 1) : n) + ", compressed=" + (compressed ? "y" : "n") + ", array length=" + data.length + ")."); } } } private void loadImageData() throws InvalidFileStructureException, IOException { setBoundsIfNecessary(width, height); allocate(); if (compression == COMPRESSION_PACKBITS) { // skip counters in.skipBytes(2 * channels * height); } byte[] data = new byte[width]; int totalScanLines = channels * height; int currentScanLine = 0; for (int c = 0; c < channels; c++) { for (int y = 0, destY = - getBoundsY1(); y < height; y++, destY++) { if (compression == COMPRESSION_PACKBITS) { loadPackbitsCompressedData(data, 0, width); } else { if (compression == COMPRESSION_PACKBITS) { in.readFully(data, 0, width); } } setProgress(currentScanLine++, totalScanLines); if (!isRowRequired(y)) { continue; } if (rgb24Image != null) { int channelIndex = RGB24Image.INDEX_RED; if (c == 1) { channelIndex = RGB24Image.INDEX_GREEN; } if (c == 2) { channelIndex = RGB24Image.INDEX_BLUE; } rgb24Image.putByteSamples(channelIndex, 0, destY, getBoundsWidth(), 1, data, getBoundsX1()); } if (gray8Image != null) { gray8Image.putByteSamples(0, 0, destY, getBoundsWidth(), 1, data, getBoundsX1()); } if (paletted8Image != null) { paletted8Image.putByteSamples(0, 0, destY, getBoundsWidth(), 1, data, getBoundsX1()); } } } } public void process() throws OperationFailedException { initModeFromIOObjects(); try { if (getMode() == CodecMode.LOAD) { in = getInputAsDataInput(); if (in == null) { throw new MissingParameterException("Input stream / file missing."); } load(); } else { throw new OperationFailedException("Only loading is supported in PSD codec."); } } catch (IOException ioe) { throw new OperationFailedException("I/O error: " + ioe.toString()); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/0000775000000000000000000000000010546532075020636 5ustar java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/OperationFailedException.java0000664000000000000000000000124407741250134026422 0ustar /* * OperationFailedException * * Copyright (c) 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.ops; /** * Exception class to indicate that an operation failed during * the execution of the method {@link Operation#process}. * Note that a failure due to missing or wrong parameters must * lead to the dedicated exception classes * {@link WrongParameterException} or {@link MissingParameterException} * being thrown. * * @since 0.7.0 * @author Marco Schmidt */ public class OperationFailedException extends Exception { public OperationFailedException(String message) { super(message); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/ImageToImageOperation.java0000664000000000000000000001644007741250134025653 0ustar /* * ImageToImageOperation * * Copyright (c) 2001, 2002 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.ops; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.Operation; import net.sourceforge.jiu.ops.WrongParameterException; /** * An operation that acesses an input image and produces data for an output image. * This abstract class only provides methods to get and set those images. *

* Normally, an operation creates the output image itself. * However, an output image can be specified by the user with * {@link #setOutputImage}. * This could be done when existing image objects are to be reused. *

* An operation extending ImageToImageOperation must check if * (1) a user-defined output image is available and * (2) whether that image matches the required criteria. * The criteria depend on the operation - example: for an operation that * rotates an image by 180 degrees, an output image must have the same resolution * as the input image and be of the same type. *

* If an output image is not available (case #1), the operation must create * the matching output image itself. * It should know best what is required. * Very generic methods (like rotation of images by 90 degrees) must know * relatively little about the image. * They can make use of PixelImage.createCompatibleImage(int, int) and provide * width and height. * That way, the operation works for all kinds of images, like BilevelImage, * Paletted8Image, Gray8Image, RGB24Image etc. *

* If a user-provided image does not match the required criteria, an appropriate * exception (most of the time {@link WrongParameterException} will do) with a * descriptive error message must be thrown. * In the example of the 90-degree rotation, the width of the output image must * be equal to the height of the input image and vice versa. * The types of input and output must be equal. *

* However, there are limits to the checks on user-provided output images. * As an example, a generic test could not check if a paletted output image * has the same palette as the input counterpart because it treats all images * based on IntegerImage the same. *

* When performing an image-to-image-operation, the input image can possibly be * used as the output image. * This can be done *

*

* Mirroring the image horizontally is an example of an operation that can be * implemented that way - the operation starts at the top left and at the bottom * right pixel, swaps them and proceeds one pixel to the right of the top left * pixel (and one to the left of the bottom right pixel). * * @author Marco Schmidt * @since 0.6.0 */ public abstract class ImageToImageOperation extends Operation { private PixelImage inputImage; private PixelImage outputImage; private boolean canInAndOutBeEqual; /** * Creates an object of this class and sets input image * and output image to the argument values. */ public ImageToImageOperation(PixelImage in, PixelImage out) { super(); setInputImage(in); setOutputImage(out); canInAndOutBeEqual = false; } /** * Creates an object of this class and sets the input image * to the argument value, output image to null. */ public ImageToImageOperation(PixelImage in) { this(in, null); } /** * Creates an object of this class and sets both input image * and output image to null. */ public ImageToImageOperation() { this(null, null); } /** * Returns if input and output image are allowed to be the same object. * @see #setCanInputAndOutputBeEqual */ public boolean canInputAndOutputBeEqual() { return canInAndOutBeEqual; } /** * If both an input and an output image have been specified (both non-null), * this method compares their width and height properties and throws * an exception if the two images do not have the same resolution. * @throws WrongParameterException if input and output images exist and their * resolutions differ */ public void ensureImagesHaveSameResolution() throws WrongParameterException { PixelImage in = getInputImage(); PixelImage out = getOutputImage(); if (in != null && out != null) { if (in.getWidth() != out.getWidth()) { throw new WrongParameterException("Input and output image must have the same width."); } if (in.getHeight() != out.getHeight()) { throw new WrongParameterException("Input and output image must have the same height."); } } } /** * If {@link #getInputImage} returns null this * method throws a {@link net.sourceforge.jiu.ops.MissingParameterException} * complaining that an input image is missing. * @throws MissingParameterException if no input image is available */ public void ensureInputImageIsAvailable() throws MissingParameterException { if (getInputImage() == null) { throw new MissingParameterException("Input image missing."); } } /** * If an output image has been specified this method will compare * its resolution with the argument resolution and throw an exception if the * resolutions differ. * If no output image has been specified nothing happens. * @param width the horizontal pixel resolution that the output image must have * @param height the vertical pixel resolution that the output image must have * @throws WrongParameterException if the resolutions differ */ public void ensureOutputImageResolution(int width, int height) throws WrongParameterException { PixelImage out = getOutputImage(); if (out != null) { if (out.getWidth() != width) { throw new WrongParameterException("Output image must have width " + width + " (got: " + out.getWidth() + ")."); } if (out.getHeight() != height) { throw new WrongParameterException("Output image must have height " + height + " (got: " + out.getHeight() + ")."); } } } /** * Returns the input image stored in this object. * @return input image, possibly null */ public PixelImage getInputImage() { return inputImage; } /** * Returns the output image stored in this object. * @return output image, possibly null */ public PixelImage getOutputImage() { return outputImage; } /** * Specify if input and output image are allowed to be the same object. * @see #canInputAndOutputBeEqual */ public void setCanInputAndOutputBeEqual(boolean newValue) { canInAndOutBeEqual = newValue; } /** * Sets the input image stored in this object to the argument. * Argument can be null. * @param in the new input image of this object */ public void setInputImage(PixelImage in) { inputImage = in; } /** * Sets the output image stored in this object to the argument. * Argument can be null. * @param out the new output image of this object */ public void setOutputImage(PixelImage out) { outputImage = out; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/LookupTableOperation.java0000664000000000000000000001342510324334037025600 0ustar /* * LookupTableOperation * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.ops; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.data.IntegerImage; import net.sourceforge.jiu.ops.ImageToImageOperation; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.WrongParameterException; /** * An operation that replaces samples with values taken from a lookup table. * Operations where each pixel is treated independently from its neighbors * and where a pixel value is always mapped to the same new pixel value * can be implemented this way. * * @author Marco Schmidt * @since 0.6.0 */ public abstract class LookupTableOperation extends ImageToImageOperation { private int[][] intTables; private int numTables; /** * Creates a LookupTableOperation for one lookup table. */ public LookupTableOperation() { this(1); } /** * Creates an object of this class, calling the super constructor with two null * arguments and allocates space for the argument number of lookup tables. * @param numTables number of tables to be used in this operation */ public LookupTableOperation(int numTables) { super(null, null); if (numTables < 1) { throw new IllegalArgumentException("The number of tables must be at least 1; got " + numTables); } intTables = new int[numTables][]; this.numTables = numTables; } /** * Returns the number of tables in this operation. * @return number of tables */ public int getNumTables() { return numTables; } /** * Returns one of the internal int lookup tables. * @param channelIndex the zero-based index of the table to be returned; * from 0 to getNumTables() - 1 * @return the channelIndex'th table */ public int[] getTable(int channelIndex) { return intTables[channelIndex]; } public void prepareImages() throws MissingParameterException, WrongParameterException { ensureInputImageIsAvailable(); PixelImage in = getInputImage(); if (!(in instanceof IntegerImage)) { throw new WrongParameterException("Input image must be of type IntegerImage."); } PixelImage out = getOutputImage(); if (out == null) { out = in.createCompatibleImage(in.getWidth(), in.getHeight()); setOutputImage(out); } else { if (in.getNumChannels() != out.getNumChannels()) { throw new WrongParameterException("Output image must have same number of channels as input image."); } ensureImagesHaveSameResolution(); } } public void process() throws MissingParameterException, WrongParameterException { prepareImages(); process((IntegerImage)getInputImage(), (IntegerImage)getOutputImage()); } private void process(IntegerImage in, IntegerImage out) { boolean useFirstTableOnly = getNumTables() < in.getNumChannels(); final int TOTAL_ITEMS = in.getHeight() * in.getNumChannels(); int processedItems = 0; for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) { int tableIndex; if (useFirstTableOnly) { tableIndex = 0; } else { tableIndex = channelIndex; } process(in, out, channelIndex, tableIndex, processedItems, TOTAL_ITEMS); processedItems += in.getHeight(); } } private void process(IntegerImage in, IntegerImage out, final int CHANNEL_INDEX, int tableIndex, int processedItems, final int TOTAL_ITEMS) { final int[] TABLE = getTable(tableIndex); final int WIDTH = in.getWidth(); final int HEIGHT = in.getHeight(); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { out.putSample(CHANNEL_INDEX, x, y, TABLE[in.getSample(CHANNEL_INDEX, x, y)]); } setProgress(processedItems++, TOTAL_ITEMS); } } /** * Resets the number of tables to be used in this operation to the * argument and drops all actual table data initialized so far. * After a call to this method, {@link #getTable} will return * null as long as no new table data is provided * via {@link #setTable} or {@link #setTables}. * @param numberOfTables the new number of tables for this operation, must be 1 or larger * @throws IllegalArgumentException if the number is zero or smaller */ public void setNumTables(int numberOfTables) { if (numberOfTables < 1) { throw new IllegalArgumentException("Number of tables argument must be larger than zero."); } numTables = numberOfTables; intTables = new int[numTables][]; } /** * Provides a new lookup table for one of the channels. * @param channelIndex the index of the channel for which a table is provided; must be at least 0 and smaller than {@link #getNumTables} * @param tableData the actual table to be used for lookup * @throws IllegalArgumentException if the channel index is not in the valid interval (see above) */ public void setTable(int channelIndex, int[] tableData) { if (channelIndex < 0) { throw new IllegalArgumentException("The channelIndex argument must be at least 0; got " + channelIndex); } if (channelIndex >= getNumTables()) { throw new IllegalArgumentException("The channelIndex argument must be smaller than the number of tables " + getNumTables() + "; got " + channelIndex); } intTables[channelIndex] = tableData; } /** * Sets the tables for all channels to the argument table. * Useful when the same table can be used for all channels. * @param tableData the data that will be used as lookup table for all channels */ public void setTables(int[] tableData) { for (int i = 0; i < getNumTables(); i++) { setTable(i, tableData); } } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/ProgressListener.java0000664000000000000000000000326707741250134025017 0ustar /* * ProgressListener * * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.ops; /** * This interface must be implemented by classes that want to be notified * about progress of an image operation. * * @author Marco Schmidt */ public interface ProgressListener { /** * Set the progress level to a new value, which must be between 0.0f and 1.0f * (including both of these values). * You should not call this method with a value lower than any value you've set * before. * However, this is not checked. * @param progress the degree of progress as a value between 0.0f and 1.0f * @throws IllegalArgumentException if the float argument is not in the mentioned interval */ void setProgress(float progress); /** * Sets a new progress level. * If an operation consists of totalItems steps, which are numbered from 0 to * totalItems - 1, this method can be called after the completion of each step. *

* Example: if there are three steps and the first one is done, the parameters * must be 0 and 3, which will indicated 33% completion. * Parameters 1 and 3 mean 66%, 2 and 3 100%. * If you use 3 and 3, an IllegalArgumentException will be thrown. *

* Computes (float)(zeroBasedIndex + 1) / (float)totalItems and calls * {@link #setProgress(float)} with that value. * * @param zeroBasedIndex the index of the step that was just completed * @param totalItems the number of steps in this operation * @throws IllegalArgumentException if the parameters don't match the above criteria */ void setProgress(int zeroBasedIndex, int totalItems); } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/ImagesToImageOperation.java0000664000000000000000000001055507741250134026037 0ustar /* * ImagesToImageOperation * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.ops; import java.util.Vector; import net.sourceforge.jiu.data.PixelImage; import net.sourceforge.jiu.ops.Operation; import net.sourceforge.jiu.ops.WrongParameterException; /** * An operation that takes several input images and produces one output image. * * @author Marco Schmidt * @since 0.11.0 */ public abstract class ImagesToImageOperation extends Operation { private Vector inputImages = new Vector(); private PixelImage outputImage; /** * Constructs a new ImagesToImageOperation and initializes * input images and output image to null. */ public ImagesToImageOperation() { this(null, null); } /** * Constructs a new ImagesToImageOperation and initializes * input images and output image to the arguments. */ public ImagesToImageOperation(Vector in, PixelImage out) { if (in != null) { for (int i = 0; i < in.size(); i++) { addInputImage((PixelImage)in.elementAt(i)); } } setOutputImage(out); } /** * Adds an image to the end of the internal list of * input images. */ public void addInputImage(PixelImage in) { inputImages.addElement(in); } /** * Checks if all images have the same resolution as given by their * getWidth and getHeight methods. * This method will not complain if input and / or output images are not * available. * @throws WrongParameterException if input and output images exist and their * resolutions differ */ public void ensureImagesHaveSameResolution() throws WrongParameterException { if (inputImages == null || inputImages.size() < 1) { return; } PixelImage in = getInputImage(0); int width = in.getWidth(); int height = in.getHeight(); int index = 1; while (index < inputImages.size()) { in = getInputImage(index); if (in.getWidth() != width) { throw new WrongParameterException("Width of images #0 and #" + index + " are not equal."); } if (in.getHeight() != height) { throw new WrongParameterException("Height of images #0 and #" + index + " are not equal."); } index++; } PixelImage out = getOutputImage(); if (out != null) { if (out.getWidth() != width) { throw new WrongParameterException("Width of input images #0 and output image are not equal."); } if (out.getHeight() != height) { throw new WrongParameterException("Height of input images #0 and output image are not equal."); } } } /** * If an output image has been specified this method will compare * its resolution with the argument resolution and throw an exception if the * resolutions differ. * If no output image has been specified nothing happens. * @param width the horizontal pixel resolution that the output image must have * @param height the vertical pixel resolution that the output image must have * @throws WrongParameterException if the resolutions differ */ public void ensureOutputImageResolution(int width, int height) throws WrongParameterException { PixelImage out = getOutputImage(); if (out != null) { if (out.getWidth() != width) { throw new WrongParameterException("Output image must have width " + width + " (got: " + out.getWidth() + ")."); } if (out.getHeight() != height) { throw new WrongParameterException("Output image must have height " + height + " (got: " + out.getHeight() + ")."); } } } /** * Returns the input image stored in this object. * @return input image, possibly null */ public PixelImage getInputImage(int index) { return (PixelImage)inputImages.elementAt(index); } /** * Return the number of input images currently stored in this operation. * @return number of images */ public int getNumInputImages() { return inputImages.size(); } /** * Returns the output image stored in this object. * @return output image, possibly null */ public PixelImage getOutputImage() { return outputImage; } /** * Sets the output image stored in this object to the argument. * Argument can be null. * @param out the new output image of this object */ public void setOutputImage(PixelImage out) { outputImage = out; } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/MissingParameterException.java0000664000000000000000000000112010324334065026615 0ustar /* * MissingParameterException * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.ops; import net.sourceforge.jiu.ops.OperationFailedException; /** * Exception class to indicate that an operation's parameter is missing * (has not been specified by caller and there was no default value that * could be used). * * @author Marco Schmidt */ public class MissingParameterException extends OperationFailedException { public MissingParameterException(String message) { super(message); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/package.html0000664000000000000000000000060107741250134023110 0ustar

The operation package, with basic functionality for all JIU classes that actually process images. Includes the base class Operation and some extensions, various exception types and classes for progress notification. java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/WrongParameterException.java0000664000000000000000000000110610324334157026306 0ustar /* * WrongParameterException * * Copyright (c) 2001, 2002, 2003, 2004, 2005 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.ops; import net.sourceforge.jiu.ops.OperationFailedException; /** * Exception class to indicate that an operation's parameter is of the wrong * type, does not fall into a valid interval or a similar mistake. * * @author Marco Schmidt * @since 0.6.0 */ public class WrongParameterException extends OperationFailedException { public WrongParameterException(String message) { super(message); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/Operation.java0000664000000000000000000001420110500554243023426 0ustar /* * Operation * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt * All rights reserved. */ package net.sourceforge.jiu.ops; import java.util.Vector; import net.sourceforge.jiu.ops.MissingParameterException; import net.sourceforge.jiu.ops.OperationFailedException; import net.sourceforge.jiu.ops.ProgressListener; import net.sourceforge.jiu.ops.WrongParameterException; /** *

* Base class for all operations. *

*

* It supports progress notification. * All classes that want to be notified by a new progress level of the operation * (defined as value between 0.0f (nothing has been done so far) to 1.0f * (operation finished)) must implement the {@link ProgressListener} interface. *

*

* An abortion state is stored in each Operation object. * It should be queried by a running operation from time to time * (via {@link #getAbort()} - if it returns true, * the operation should terminate and return control to the caller. * The abort state can be modified using {@link #setAbort(boolean)}. *

* @author Marco Schmidt */ public abstract class Operation { /*

* This class class contains a generic system to add parameters to an operation. * Whether an item becomes a parameter is often unclear and must be decided by * the operation implementor. * As an example: one could create an image rotation class with a numerical parameter * for the degrees of rotation. * One could also define a class of its own for each 90, 180 and 270 degrees * (excluding other values). *

*

* The generic parameter system is insufficient in some situations. * Example: A parameter can be defined to be of class Integer or Long, but it cannot * be forced to be in a certain interval. * Even if such a case could be solved by a specially-designed class, checking * of parameters (and maybe their relations among each other) can be done by * overriding the {@link #checkParams()} method. *

*/ private boolean abort; private Vector progressListeners; /** * This constructor creates two internal empty lists for progress listeners and parameters. */ public Operation() { abort = false; progressListeners = new Vector(); } /** * Adds the argument progress listener to the internal list of * progress listeners. * Does not check if the argument already exists in that list, so you have * to check for duplicates yourself. * * @param progressListener the progress listener to be added */ public void addProgressListener(ProgressListener progressListener) { if (progressListener == null) { return; } if (!progressListeners.contains(progressListener)) { progressListeners.addElement(progressListener); } } /** * Adds several progress listeners to this operation object. * @param progressListeners contains zero or more objects implementing ProgressListener; * each will be added by calling {@link #addProgressListener} on it */ public void addProgressListeners(Vector progressListeners) { if (progressListeners != null) { int index = 0; while (index < progressListeners.size()) { ProgressListener listener = (ProgressListener)progressListeners.elementAt(index++); addProgressListener(listener); } } } /** * Returns the current abort status. * If true, a running operation should terminate what it is doing * (return from {@link #process()}). * @return abort status * @see #setAbort */ public boolean getAbort() { return abort; } /** * This method does the actual work of the operation. * It must be called after all parameters have been given to the operation object. * @throws WrongParameterException if at least one of the input parameters was * not initialized appropriately (values out of the valid interval, etc.) * @throws MissingParameterException if any mandatory parameter was not given to the operation * @throws OperationFailedException */ public void process() throws MissingParameterException, OperationFailedException, WrongParameterException { } /** * Removes the argument progress listener from the internal list of * progress listeners. * @param progressListener the progress listener to be removed */ public void removeProgressListener(ProgressListener progressListener) { progressListeners.removeElement(progressListener); } /** * Sets a new abort status. * @param newAbortStatus the new status * @see #getAbort */ public void setAbort(boolean newAbortStatus) { abort = newAbortStatus; } /** * This method will notify all registered progress listeners * about a new progress level. * The argument must be from 0.0f to 1.0f where 0.0f marks the * beginning and 1.0f completion. * The progress value should not be smaller than any value that * was previously set. * @param progress new progress value, from 0.0 to 1.0 */ public void setProgress(float progress) { if (progress < 0.0f || progress > 1.0f) { throw new IllegalArgumentException("Progress values must be from" + " 0.0f to 1.0f; got " + progress); } int index = 0; while (index < progressListeners.size()) { ProgressListener pl = (ProgressListener)progressListeners.elementAt(index++); if (pl != null) { pl.setProgress(progress); } } } /** * This method will notify all registered progress listeners * about a new progress level. * Simply checks the arguments and calls setProgress((float)zeroBasedIndex / (float)totalItems);. * @param zeroBasedIndex the index of the item that was just processed, zero-based * @param totalItems the number of items that will be processed */ public void setProgress(int zeroBasedIndex, int totalItems) { if (zeroBasedIndex < 0 || zeroBasedIndex >= totalItems || totalItems < 1) { throw new IllegalArgumentException("No valid arguments " + " zeroBasedIndex=" + zeroBasedIndex + ", totalItems=" + totalItems); } setProgress((float)zeroBasedIndex / (float)totalItems); } } java-imaging-utilities-0.14.2+3.orig/net/sourceforge/jiu/ops/BatchProcessorOperation.java0000664000000000000000000001505107741250134026301 0ustar /* * BatchProcessorOperation * * Copyright (c) 2003 Marco Schmidt. * All rights reserved. */ package net.sourceforge.jiu.ops; import java.io.File; import java.util.Vector; /** * Small data class for names of directories that are to be * processed. * @author Marco Schmidt */ class DirectoryTree { /** * Input directory name, as found in the file system. */ String input; /** * Corresponding output directory name, may not yet be in the file system. */ String output; } /** * Abstract base class to do batch processing on files and complete directory trees. * For a non-abstract extension of this operation, you must implement {@link #processFile}. * @author Marco Schmidt * @since 0.11.0 */ public abstract class BatchProcessorOperation extends Operation { private boolean collectErrors; private Vector directoryTrees = new Vector(); private Vector errorMessages = new Vector(); private Vector inputFileNames = new Vector(); private String outputDirectory; private boolean overwrite; /** * Adds the argument to the list of directories to be completely * processed. * @param rootDirectoryName name of the root of the directory tree, can be any valid directory name */ public void addDirectoryTree(String rootDirectoryName) { addDirectoryTree(rootDirectoryName, null); } /** * Adds the first argument to the list of directories to be completely * processed, writes all output files to the directory tree specified by * the second argument. * @param rootDirectoryName name of the root of the directory tree, can be any valid directory name * @param outputRootDirectoryName name of the root of the directory tree, can be any valid directory name */ public void addDirectoryTree(String rootDirectoryName, String outputRootDirectoryName) { DirectoryTree tree = new DirectoryTree(); tree.input = rootDirectoryName; tree.output = outputRootDirectoryName; directoryTrees.addElement(tree); } /** * Adds a single name to the list of file names to be processed. * @param fileName name to be added to list */ public void addInputFileName(String fileName) { inputFileNames.addElement(fileName); } /** * Adds a number of file names to the internal list of file names to be processed. * @param fileNameList list of file names, each object in the list must be a String */ public void addInputFileNames(Vector fileNameList) { int index = 0; while (index < fileNameList.size()) { String fileName = (String)fileNameList.elementAt(index++); inputFileNames.addElement(fileName); } } /** * Returns a list of error messages collected during the execution of {@link #process}. * @return list of error messages, each object is a String */ public Vector getErrorMessages() { return errorMessages; } /** * Returns the current overwrite setting. * @return whether existing files are to be overwritten */ public boolean getOverwrite() { return overwrite; } /** * Processes all directory trees and files given to this operation, * calling {@link #processFile} on each file name. */ public void process() { // process directory trees int index = 0; while (index < directoryTrees.size()) { DirectoryTree tree = (DirectoryTree)directoryTrees.elementAt(index++); String output = tree.output; if (output == null) { output = outputDirectory; } processDirectoryTree(tree.input, output); } // process single files index = 0; while (index < inputFileNames.size()) { String fileName = (String)inputFileNames.elementAt(index++); File file = new File(fileName); if (!file.isFile()) { if (collectErrors) { errorMessages.addElement("Cannot process \"" + fileName + "\" (not a file)."); } } String inDir = file.getParent(); String outDir = outputDirectory; if (outDir == null) { outDir = inDir; } processFile(inDir, file.getName(), outDir); } } private void processDirectoryTree(String fromDir, String toDir) { File fromDirFile = new File(fromDir); String[] entries = fromDirFile.list(); for (int i = 0; i < entries.length; i++) { String name = entries[i]; File entry = new File(fromDir, name); if (entry.isFile()) { processFile(fromDir, name, toDir); } else if (entry.isDirectory()) { File inSubDir = new File(fromDir, name); File outSubDir = new File(toDir, name); if (outSubDir.exists()) { if (outSubDir.isFile()) { if (collectErrors) { errorMessages.addElement("Cannot create output directory \"" + outSubDir.getAbsolutePath() + "\" because a file of that name already exists."); } continue; } } else { if (!outSubDir.mkdir()) { if (collectErrors) { errorMessages.addElement("Could not create output directory \"" + outSubDir.getAbsolutePath() + "\"."); } continue; } } processDirectoryTree(inSubDir.getAbsolutePath(), outSubDir.getAbsolutePath()); } } } /** * Method to be called on each file given to this operation. * Non-abstract heirs of this class must implement this method to add functionality. * @param inputDirectory name of directory where the file to be processed resides * @param inputFileName name of file to be processed * @param outputDirectory output directory for that file, need not necessarily be used */ public abstract void processFile(String inputDirectory, String inputFileName, String outputDirectory); /** * Specifies whether error messages are supposed to be collected * during the execution of {@link #process}. * @param collectErrorMessages if true, error messages will be collected, otherwise not * @see #getErrorMessages */ public void setCollectErrorMessages(boolean collectErrorMessages) { collectErrors = collectErrorMessages; } /** * Specifies the output directory for all single files. * Note that you can specify different output directories when dealing * with directory trees. * @param outputDirectoryName name of output directory */ public void setOutputDirectory(String outputDirectoryName) { outputDirectory = outputDirectoryName; } /** * Specify whether existing files are to be overwritten. * @param newValue if true, files are overwritten, otherwise not * @see #getOverwrite */ public void setOverwrite(boolean newValue) { overwrite = newValue; } } java-imaging-utilities-0.14.2+3.orig/packages0000664000000000000000000000121510104724764015633 0ustar net.sourceforge.jiu.apps net.sourceforge.jiu.codecs net.sourceforge.jiu.codecs.jpeg net.sourceforge.jiu.codecs.tiff net.sourceforge.jiu.color net.sourceforge.jiu.color.data net.sourceforge.jiu.color.adjustment net.sourceforge.jiu.color.analysis net.sourceforge.jiu.color.conversion net.sourceforge.jiu.color.dithering net.sourceforge.jiu.color.io net.sourceforge.jiu.color.promotion net.sourceforge.jiu.color.quantization net.sourceforge.jiu.color.reduction net.sourceforge.jiu.data net.sourceforge.jiu.filters net.sourceforge.jiu.geometry net.sourceforge.jiu.gui.awt net.sourceforge.jiu.gui.awt.dialogs net.sourceforge.jiu.ops net.sourceforge.jiu.util java-imaging-utilities-0.14.2+3.orig/resources/0000775000000000000000000000000010546531771016151 5ustar java-imaging-utilities-0.14.2+3.orig/resources/lang/0000775000000000000000000000000010546531776017077 5ustar java-imaging-utilities-0.14.2+3.orig/resources/lang/en.txt0000664000000000000000000001137210404064673020234 0ustar Error loading image The format of the file is unknown. Load image file Screen resolution Could not create histogram. Number of used colors Count colors used... Close Help About... System System information... Color Invert Convert to grayscale File Open... Save as Exit Java Runtime Environment version Java Runtime Environment vendor Java vendor URL Java installation directory Java Virtual Machine specification version Java Virtual Machine specification vendor Java Virtual Machine specification name Java Virtual Machine implementation version Java Virtual Machine implementation vendor Java Virtual Machine implementation name Java Runtime Environment specification version Java Runtime Environment specification vendor Java Runtime Environment specification name Java class format version number Java class path Operating system name Operating system architecture Operating system version Homepage Feedback Median Cut... Error message The number of colors in the image is already small enough. CPU endianness CPU isalist Free memory Used memory Total memory Save as Geometry Flip Mirror Median Cut + contour removal Promote to RGB Reduce to bilevel (threshold)... Reduce to bilevel (Bayer dithering)... Rotate left (90) Rotate right (90) Rotate (180) Rotate (other)... Scale... Scale image New width New height Maintain aspect ratio OK Cancel Number of colors Median Cut color quantization Output color type Average Weighted average Median Paletted RGB truecolor Representative color method Contour removal Method Filters Sharpen Blur Emboss Psychedelic Distillation Lithograph Save as Portable Bitmap (PBM)... Portable Graymap (PGM)... Portable Pixmap (PPM)... Uniform palette... Number of bits, red Number of bits, green Number of bits, blue Ordered dithering None Dithering method Uniform palette color quantization Save image as... Error - too many bits. Total number of bits, colors Edge detection Reduce number of shades of gray Reduce number of shades of gray... Number of bits Number of shades of gray Sun Raster (RAS)... Adjust Contrast... Brightness... Gamma... Adjust contrast Please enter the change of contrast as a percentage value (-100 to 100): Adjust brightness Please enter the change of brightness as a percentage value (-100 to 100): Adjust gamma Please enter the gamma value (0.0 to 10.0): Crop image Left column Top row Right column Bottom row Crop... Convert to minimum color type Histogram Reduce to bilevel using a threshold value Please enter the threshold value: Floyd-Steinberg error diffusion Stucki error diffusion Burkes error diffusion Sierra error diffusion Jarvice, Judice and Ninke error diffusion Stevenson-Arce error diffusion Output quality improvement algorithm None Error diffusion Color image quantization Horizontal Sobel Vertical Sobel Horizontal Prewitt Vertical Prewitt Shear... Shear image Please enter the angle (> -90.0, < 90.0): Hue, saturation, value... Adjust hue, saturation and value Set hue Hue (0..359) Saturation (-100..100) Value (-100..100) Mean... Median... Oil... Apply mean filter Apply median filter Apply oil filter Window width (odd number >= 1) Window height (odd number >= 1) Please enter the window size! Contour removal; number of passes Contour removal; tau distance Palette Save as... Map to arbitrary palette... Save palette Load palette Choose dithering method Websafe palette Palette from file Please choose the palette type! Map to arbitrary palette Equalize Normalize Octree... Octree color image quantization Save co-occurrence matrix Save co-occurrence matrix as... Save co-occurrence frequency matrix Save co-occurrence frequency matrix as... Windows Bitmap (BMP)... Promote Promote to paletted Promote to grayscale (8 bits) Promote to grayscale (16 bits) Promote to RGB (24 bits) Promote to RGB (48 bits) Reduce Save histogram as... Save histogram Edit Undo Redo Texture properties... Contrast Energy Entropy Homogeneity Texture properties Correlation Dissimilarity Minimum... Maximum... Apply minimum filter Apply maximum filter View Zoom in Zoom out Set original size Interpolation type Nearest neighbor (fast) Bilinear (slower) Bicubic (slowest) Palm... Palm OS system palette (256 colors) Palm OS system palette (16 colors) Palm OS system palette (16 shades of gray) Palm OS system palette (4 shades of gray) The image was changed. Do you want to quit without saving? Exit program Yes No Do you really want to close without saving? Close file Bits per pixel bilevel grayscale RGB truecolor unknown Image type paletted Pixels Portable Network Graphics (PNG)... Memory Disk space Graphics Interchange Format (GIF)... image1.jpg java-imaging-utilities-0.14.2+3.orig/resources/lang/fr.txt0000664000000000000000000001340010404065631020226 0ustar Erreur lors du chargement de l'image Le format de ce fichier est inconnu. Chargement de l'image Résolution de l'écran Impossible de créer l'histogramme. Nombre de couleurs utilisées Comptage des couleurs utilisées... Fermer Aide A propos... Système Information système... Couleur Inversion de couleurs Convertir en niveaux de gris Fichier Ouvrir... Enregistrer sous... Quitter Version du JRE (Java Runtime Environment) Fournisseur du JRE URL du fournisseur de Java Répertoire d'installation de Java Version des spécifications de la JVM (machine virtuelle Java) Fournisseur des spécifications de la JVM Nom des spécifications de la JVM Version de l'implémentation de la JVM Fournisseur de l'implémentation de la JVM Nom de l'implémentation de la JVM Version des spécifications du JRE Fournisseur des spécifications du JRE Nom des spécifications du JRE Numéro de version du format des classes Java Class path Java Système d'exploitation Architecture du système d'exploitation Version du système d'exploitation Page d'accueil Retour d'expérience Median Cut... Message d'erreur Le nombre de couleurs dans l'image est déjà au minimum. CPU endianness (boutisme) CPU isalist (jeux d'instructions) Mémoire libre Mémoire utilisée Mémoire totale Enregistrer sous... Géométrie Renverser Miroir Median Cut + suppression du contour Convertir en RGB Reduire en monochrome (Seuil)... Reduire en monochrome (Diffusion Bayer)... Rotation à gauche (90°) Rotation à droite (90°) Rotation (180°) Rotation (personnalisée)... Echelle... Redimensionner l'image Nouvelle largeur Nouvelle hauteur Garder le ratio hauteur/largeur OK Annuler Nombre de couleurs Quantification de la couleur (Algorithme Median Cut) Type de couleur de sortie Moyenne Moyenne pondérée Médiane Palette de couleurs RGB vraies couleurs Méthode pour la couleur représentative Suppression du contour Méthode Filtres Accentuer Flou Relief Distillation psychédélique Lithograph Enregistrer sous... Portable Bitmap (PBM)... Portable Graymap (PGM)... Portable Pixmap (PPM)... Palette uniforme... Nombre de bits, rouge Nombre de bits, vert Nombre de bits, bleu Diffusion ordonnée Aucun Méthode de diffusion Quantification de la couleur de la palette uniforme Enregistrer l'image sous... Erreur - Trop de bits. Nombre total de bits, couleurs Détection des bordures Réduire le nombre de niveaux de gris Réduire le nombre de niveaux de gris Nombre de bits Nombre de niveaux de gris Sun Raster (RAS)... Ajuster Contraste... Luminosité... Gamma... Ajuster le contraste Veuillez saisir le pourcentage de changement de constraste (-100 à 100): Ajuster la luminosité Veuillez saisir le pourcentage de changement de luminosité (-100 à 100): Ajuster le gamma Veuillez saisir la valeur du gamma (0.0 à 10.0): Découper l'image Colonne de gauche Ligne du haut Colonne de droite Ligne du bas Découper... Convertir en monochrome Histogramme Réduire en monochrome en utilisant une valeur seuil Veuillez entrer la valeur seuil: Floyd-Steinberg error diffusion Stucki error diffusion Burkes error diffusion Sierra error diffusion Jarvice, Judice and Ninke error diffusion Stevenson-Arce error diffusion Algorithme d'amélioration de la qualité de sortie Aucun Error diffusion Quantification de la couleur de l'image Sobel (horizontal) Sobel (vertical) Prewitt (horizontal) Prewitt (vertical) Cisailler... Cisailler l'image Veuillez entrer l'angle (> -90.0, < 90.0): Tonalité, saturation, luminosité... Ajuster la tonalité, la saturation et la luminosité Fixer la tonalité Tonalité (0 à 359) Saturation (-100 à 100) Luminosité (-100 à 100) Moyenne... Médiane... Huile... Appliquer le filtre moyen Appliquer le filtre médiane Appliquer le filtre huile Largeur de la fenêtre (Nombre impair >= 1) Hauteur de la fenêtre (Nombre impair >= 1) Veuillez entrer la taille de la fenêtre! Suppression du contour; nombre de passes Suppression du contour; distance tau Palette Enregistrer sous... Map to arbitrary palette... (???) Enregistrer une palette Charger une palette Choisir la méthode de diffusion Palette optimisée pour le web Palette depuis un fichier Veuillez choisir le type de palette! Map to arbitrary palette... (???) Egaliser Normaliser Octree... Quantification de la couleur de l'image (Algorithme Octree) Enregistrer la matrice de co-occurrence Enregistrer la matrice de co-occurrence sous... Enregistrer la matrice de fréquence de co-occurrence Enregistrer la matrice de fréquence de co-occurrence sous... Windows Bitmap (BMP)... Convertir Convertir en palette Convertir en niveaux de gris (8 bits) Convertir en niveaux de gris (16 bits) Convertir en RGB (24 bits) Convertir en RGB (48 bits) Réduire Enregistrer l'histogramme sous... Enregistrer l'histogramme Edition Défaire Refaire Propriétés de la texture... Contraste Energie Entropie Homogénéité Propriétés de la texture Corrélation Dissimilitude Minimum... Maximum... Appliquer le filtre minimum Appliquer le filtre maximum Vue Zoom + Zoom - Revenir à la taille originale Type d'interpolation Voisin le plus proche (rapide) Bilinéaire (lent) Bi-cubique (plus lent) Palm... Palette système Palm OS (256 couleurs) Palette système Palm OS (16 couleurs) Palette système Palm OS (16 niveaux de gris) Palette système Palm OS (4 niveaux de gris) L'image a été modifiée. Voulez-vous quitter l'application sans enregistrer? Quitter l'application Oui Non Voulez-vous réellement quitter l'application sans enregistrer les modifications? Fermer le fichier Bits par pixel Monochrome Niveaux de gris RGB vraies couleurs Inconnu Type d'image Palette de couleurs Pixels Portable Network Graphics (PNG)... Mémoire Espace disque Graphics Interchange Format (GIF)... image1.jpg java-imaging-utilities-0.14.2+3.orig/resources/lang/es.txt0000664000000000000000000001274210404065626020242 0ustar Error cargando imagen El formato del archivo es desconocido. Cargar archivo de imagen Resolución del monitor No se puede crear histograma. Número de colores usado Contar colores usados... Cerrar Ayuda Acerca de... Sistema Información del sistema... Color Invertir Convertir a escala de grises Archivo Abrir... Guardar como Salir Versión del Java Runtime Environment -JRE- Proveedor del Java Runtime Environment URL proveedor Java, Directorio de la instalación Java Especificación de máquina virtual Java (Virtual Machine -JVM-) Especificación del proveedor de Java Virtual Machine Especificación del nombre de Java Virtual Machine Versión de la implementación de Java Virtual Machine Proveedor de la implementación de Java Virtual Machine Nombre de la implementación de Java Virtual Machine Especificación de la Versión de Java Runtime Environment Especificación del vendedor de Java Runtime Environment Especificación del nombre de Java Runtime Environment Número de versión del formato de clases Java Java class path Nombre del Sistema Operativo Arquitectura del Systema Operativo Versión del Sistema Operativo Homepage Contactar Corte mediana... Mensaje de error El número de colores de la imagen ya es mínimo. CPU endianness CPU isalist Memoria libre Memoria usada Memoria total Guardar como Transformaciones Flip (espejo/horizontal) Espejo Median Corte + eliminación de contorno Promover a RGB Reducir a monocromo (umbral)... Reducir a monocromo (Bayer dithering)... Rotar a la izquierda (90) Rotar a la derecha (90) Rotar (180) Rotar (otras magnitudes)... Escala... Escala de imagen Nuevo ancho Nueva altura Mantener relación alto-ancho OK Cancelar Número de colores Cuantización de color a través de Corte mediana Tipo de color de la salida Media Media ponderada Mediana Usando paleta RGB color verdadero Método de color representativo Eliminación de contorno Método Filtros Enfocar Desenfocar Bajo relieve Destilación psicodélica Litografía Guardar como Portable Bitmap (PBM)... Portable Graymap (PGM)... Portable Pixmap (PPM)... Uniform palette... Número de bits, rojo Número de bits, verde Número de bits, azul Dithering ordenado Ningún Método Dithering Cuantización de color por paleta uniforme Guardar imagen como... Error - demasiados bits. Número total de bits, colores Detección de bordes Reducir número de tonos de gris Reducir número de tonos de gris... Número de bits Número de tonos de gris Sun Raster (RAS)... Ajustar Contraste... Brillo... Gamma... Ajustar contraste Por favor ingrese el cambio de contraste como un valor porcentual (-100 to 100): Ajustar brillo Por favor ingrese el cambio de brillo como un valor en porcentaje(-100 to 100): Ajustar gamma Por favor ingrese el valor gamma (0.0 to 10.0): Cortar imagen Columna izquierda Fila al tope Columna derecha Fila izquierda Cortar... Convertir al mínimo tipo de color Histograma Reducir a bilevel usando un valor umbral Por favor ingrese el valor umbral: Difusión de error Floyd-Steinberg Difusión de error Stucki DIfusión de error Burkes Difusión de error Sierra Difusión de error Jarvice, Judice and Ninke Difusion de error Stevenson-Arce Algoritmo de mejoramiento de calidad salida Ningún Difusión del error Cuantización de imagen color Sobel horizontal Sobel vertical Prewitt horizontal Prewitt vertical Sesgar... Sesgar imagen Por favor ingrese el ángulo (> -90.0, < 90.0): tono, saturación, valor... Ajustar tono, saturación y valor colocar tono Tono (0..359) Saturación (-100..100) Valor (-100..100) Media... Mediana... Óleo... Aplicar filtro media Aplicar filtro mediana, Aplicar filtro aceite Ancho de ventana (número impar >= 1) Alto de ventana (número impar >= 1) Por favor ingrese tamaño de ventana! Eliminación de Contorno; número de pasadas Eliminación de contorno ; distancia tau, Paleta Guardar como... Mapa de paleta arbitraria... Salvar paleta Cargar paleta Escoger método de dithering Paleta Websafe (colores web) Paleta desde archivo Por favor, escoja el tipo de paleta Mapa de paleta arbitraria Ecualizar Normalizar Octree... Cuantización de imagen color por Octree Guardar matriz de coocurrencia Guardar matriz de coocurrencia como... Guardar matriz de frecuencia de coocurrencia Guardar matriz de frecuencia de coocurrencia como... Windows Bitmap (BMP)... Promover Promover a definido por paleta Promover a escala de grises (8 bits) Promover a escala de grises (16 bits) Promover a RGB (24 bits) Promover a RGB (48 bits) Reducir Guardar histograma como... Guardar histograma Modificar Deshacer Rehacer Propiedades de textura... Contraste Energía Entropía Homogeneidad Propiedades de textura Correlación Disimilaridad Mínimo... Máximo... Aplicar filtro mínimo Aplicar filtro máximo Ver Ampliar Reducir Tamaño original Tipo de Interpolación Vecino mas cercano (rápido) Bilinear (lento) Bicúbico (mas lento) Palm... Paleta del sistema de Palm OS (256 colores) Paleta del sistema de Palm OS (16 colores) Paleta del sistema de Palm OS (16 tonos de gris) Paleta del sistema de Palm OS (4 tonos de gris) La imagen ha cambiado. ¿Desea salir sin Grabar? Salir del programa Si No ¿Seguro que quiere cerrar sin grabar los cambios? Cerrar fichero bits por píxel monocroma tonos de gris Millones de colores (Rojo Verde Azul) desconocido Tipo con paleta Píxeles Portable Network Graphics (PNG)... memoria espacio en disco Graphics Interchange Format (GIF)... image1.jpg java-imaging-utilities-0.14.2+3.orig/resources/lang/de.txt0000664000000000000000000001237010404065622020214 0ustar Fehler beim Laden eines Bildes Das Dateiformat ist unbekannt. Bilddatei laden Bildschirmauflösung Konnte Histogramm nicht erstellen. Anzahl benutzter Farben Anzahl benutzter Farben ermitteln... Schließen Hilfe Über dieses Programm... System Systeminformation... Farbe Invertieren In Graustufen umwandeln Datei Öffnen... Speichern unter Beenden Version der Java-Laufzeitumgebung Anbieter Java-Laufzeitumgebung URL des Javaanbieters Java-Installationsverzeichnis Version der Spezifikation der JVM Anbieter der Spezifikation der Java-Laufzeitumgebung Name der Spezifikation der Java-Laufzeitumgebung Implementationsversion der JVM Anbieter der JVM-Implementation Name der JVM-Implementation Version der Spezifikation der Java-Laufzeitumgebung Anbieter der Spezifikation der Java-Laufzeitumgebung Name der Spezifikation der Java-Laufzeitumgebung Versionsnummer des Java-Class-Formats Java classpath Name des Betriebssystems Architektur des Betriebssystems Version des Betriebssystems Homepage Feedback Median Cut... Fehlermeldung Anzahl Farben ist bereits ausreichend klein. CPU endianness CPU isalist Freier Speicher Benutzter Speicher Speicher gesamt Speichern unter Geometrie Vertikal spiegeln Horizontal spiegeln Median Cut + Konturenentfernung Erweitern zu RGB Reduzieren zu Schwarz-Weiß (Schwellwert)... Reduzieren zu Schwarz-Weiß (Bayer-Dithering) Rotation (90 links) Rotation (90 rechts) Rotation (180) Rotation (frei)... Skalieren... Bild skalieren Neue Breite Neue Höhe Seitenverhältnis beibehalten OK Abbrechen Anzahl Farben Median Cut Farbquantisierung Ausgabefarbtyp Durchschnitt Gewichteter Durchschnitt Median Farbpalette RGB Echtfarbe Methode für repräsentative Farbe Konturenentfernung Verfahren Filter Schärfen Verwischen Relief Psychedelic Distillation Lithograph Speichern unter Portable Bitmap (PBM)... Portable Graymap (PGM)... Portable Pixmap (PPM)... Einheitlich verteilte Palette... Anzahl Bit, rot Anzahl Bit, grün Anzahl Bit, blau Ordered Dithering Kein Dithering Ditheringverfahren Farbquantisierung (einheitliche Palette) Bild speichern als Fehler - Bitsumme zu groß. Bit gesamt, Anzahl Farben Kanten ermitteln Anzahl Graustufen verringern Anzahl Graustufen verringern... Anzahl Bit Anzahl Graustufen Sun Raster (RAS)... Anpassen Kontrast... Helligkeit... Gamma... Kontrast anpassen Bitte die Kontraständerung als Prozentwert eingeben (-100 bis 100): Helligkeit anpassen Bitte die Helligkeitsänderung als Prozentwert eingeben (-100 bis 100): Gammawert anpassen Bitte den Gammawert eingeben (0.0 bis 10.0): Bild ausschneiden Linke Spalte Oberste Zeile Rechte Spalte Unterste Zeile Ausschneiden... Umwandeln in minimalen Farbtyp Histogramm Zu Schwarz-Weiß reduzieren (Schwellwert) Bitte den Schwellwert eingeben: Floyd-Steinberg-Fehlerdiffusion Stucki-Fehlerdiffusion Burkes-Fehlerdiffusion Sierra-Fehlerdiffusion Jarvice-Judice-und-Ninke-Fehlerdiffusion Stevenson-Arce-Fehlerdiffusion Methode für Qualitätsverbesserung Keine Fehlerdiffusion Farbbildquantisierung Sobel (horizontal) Sobel (vertikal) Prewitt (horizontal) Prewitt (vertikal) Scheren... Bild scheren Bitte den Winkel eingeben (> -90.0, < 90.0): Farbton, Sättigung, Helligkeit... Farbton, Sättigung Helligkeit Farbton setzen Farbton (0..359) Sättigung (-100..100) Helligkeit (-100..100) Mittelwert... Median... Öl... Mittelwertfilter Medianfilter Ölfilter Fensterbreite (ungerade Zahl >= 1) Fensterhöhe (ungerade Zahl >= 1) Bitte die Fenstergröße eingeben! Konturenentfernung; Anzahl Durchgänge Konturenentfernung; Abstand tau Palette Speichern unter... Auf beliebige Palette abbilden... Palette speichern Palette laden Ditheringverfahren auswählen Websafe-Palette Palette aus Datei Bitte den Palettentyp angeben! Auf beliebige Palette abbilden Ausgleichen Normalisieren Octree... Octree-Farbbildquantisierer Co-occurrence-Matrix speichern Co-occurrence-Matrix speichern unter... Co-occurrence-Frequency-Matrix speichern Co-occurrence-Frequency-Matrix speichern unter... Windows Bitmap (BMP)... Farbtiefe erhöhen In Palettenbild umwandeln In Graustufen umwandeln (8 Bit) In Graustufen umwandeln (16 Bit) In RGB umwandeln (24 Bit) In RGB umwandeln (48 Bit) Farbtiefe reduzieren Histogramm speichern unter... Histogramm speichern Bearbeiten Rückgängig Wiederherstellen Textureigenschaften... Kontrast Energie Entropie Homogenität Textureigenschaften Korrelation Verschiedenartigkeit Minimum... Maximum... Minimumfilter anwenden Maximumfilter anwenden Ansicht Vergrößern Verkleinern Originalgröße Interpolationstyp Pixelreplikation (schnell) Bilinear (langsamer) Bikubisch (am langsamsten) Palm... Palm OS Systempalette (256 Farben) Palm OS Systempalette (16 Farben) Palm OS Systempalette (16 Graustufen) Palm OS Systempalette (4 Graustufen) Das Bild wurde geändert. Wollen Sie das Programm beenden, ohne Änderungen zu speichern? Programm beenden Ja Nein Wollen Sie die Datei wirklich schließen ohne die Veränderungen zu speichern? Datei schließen Bits pro Pixel Schwarz-Weiß Graustufen RGB Echtfarbe unbekannt Bildtyp Palette Pixel Portable Network Graphics (PNG)... Arbeitsspeicher Festplatte Graphics Interchange Format (GIF)... image1.jpg java-imaging-utilities-0.14.2+3.orig/resources/images/0000775000000000000000000000000010404072055017402 5ustar java-imaging-utilities-0.14.2+3.orig/resources/images/image1.jpg0000664000000000000000000017425410404072055021264 0ustar ÿØÿàJFIFHHÿþ=Copyright (c) 2005 Marco Schmidt http://schmidt.devlib.org/ÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀ@"ÿÄÿÄH !1AQa"q‘2¡#±ÁðBRÑá3bñ$r‚’¢²CS&4%csÒtƒÂÿÄÿÄ+!1A"Q2aqB‘ð#R±ÿÚ ?ú[ …s—ÕâÛŸJÏž@dÀã ¸]`¢8Î;³“’ã'#xŽ@E—v4޲cœ˜° 1ã¬X'Œ©†75Ó*2_\ÒD$œ½GMåÊÔ0ÁfÞ2 k/¾CÌäHG9h9œ0,߆fÐqy"øÁ[-ŒÎÊV –ÑéƒW€G¦EäÇ€02Y ’œÉG·–UŒ­°‘ÆGîq…¼ÑÖ:É…ÇXuãà\\° {rKŒÍ‚5†;Ã@ÞXr) ‹\©˜öÍ ·ò<œÚhË(K~ù?.Ï\»`QÆyÆBŒí àR¿Ã›Htʉ³U…6( !ôȲœÐA†}²äJ2rN1·+y §¶Ca¾™T…Ú>zb›J¨Åj3Y™¤bhO¦[Équ¼<Ð;årlRGl…zcgR2­àq‘!d»ñÓ+t<›ã˜£+iÅ_LÚLÍ”NJü³+ÒÏ¿)eçŒíœ‡ÂàæÏ8×1Új°ÜXúaÆÈ¥FÃ7•6£Ó+9È8°¢ŠÛ$Òß|†ã‘¬yªF-‹q8V’Êë<2QlU€F±ä‘“^FK-:±ÖF± U†J±V«ð¬+%…`¬+%…` °¬u†VÃU,U§ENK* ’Ýž*=c'“’0£%Î+ÉŒ–¬WÎZF@ŠÊ§,^rÔrÕqŠ·ˆôȆÁÌÐ*pNfx‰6 fúdJf”¨”dŒ‘ÁËy¬Ÿ•Íâ"‡9«TÇ o<剋9¢n8ÖC–4VxÈÚq¢+œ¹\æq×,Wç2ÑQqsYüáÔeg¯¦DŠiW÷ÉÌ ‘’ßYEš/$dó+¾Ie®øÅ‹6Á²…—Œ–üÍ2ØÈç$¹ Ù Ø Z0ÈÇyŽG"°@Ôe€qe€æX ¬Dä² ‚…â-‘9ÙhÉ;¼D2XŒ CŒw€càwÊÙ²§–²¨¶,¼ºúáÁ…¥èyËV[¬œq*ó‡®ḐŒbËe¬o¦Dš„‹Y|$K!#ó•—㌱¨‹ÊöŽÇ:£ ¥Ù²–÷¼¾A]ó; Α2Äg#¾Eµ•ý2·ÙÓbÙažò<žùQ94kÊ•ɨcÀÇEzŒàZò‹Py¼|`qxF[ìXë  XcÃ+ °¬Ã:ÀV:ÈTF±ãÃ!EXñá€,1Ö€*²U…` †B†ްÊAa2U,2ˆ*°¬… Å’ÃÓ#ŽëL>bNHÈFgY<Áëq˜¼ì^~=²dl.2¶jÌâl<À{åP&EÞn?ÓèÓ‹]•ÖF@B=âÊØ°xÄ$Ü®¿L¶f‹PØàqŒÈY8…(¡Øfi‹’ª ÛÛÓæYë–©Ìá6Œ{ˆËD5n¼Îs8brhúd¢š“‘ÎLôÊCPÄÒq™¢Ø¤ÊÊ)Ç¿vFë6‘‘íãq‰Õ†U``äÁ¿l‚ÕõËB¯\Œ¢«Ão¾L¯¦F˜7N@VÆ»d ‹¬Ó²úŒƒ@•4JeÀo¦J];©ã¦5S\æôgd-€çsykVRô2¢2Ðüä÷ Æ® «Éïˉ,ÑÁɵÓ3 1-wã%˼¥ê/,ŠÌ ë}ä¦4Aà=AȈ¤¾¹ac‡™Y¤Ù4X¨Ã“’)c‹È$ÜÐË·ñ˜viQ•×·ÜŒ¹É9Y ÒfJ÷`âÞC–*oýU–ˆ…W9rHS1—6EãW»®sZè›ç4&Ž8ú‘òErŒ¬Úç"C?E7¿"3ÕF?*1ÑFgÞKÀöÿgŸ`ÍÅ’7c[NwZ$¿Ò2Z§A›÷¿F}³›M×9¨)^£ŒÐkÓ"ÔE™sl¸¤PO¦2ƹ%†Éç&!ë|âУ>ìDƒš1Ø ˆDSÓ.Hr6*oô›\Šàe{ù¬ªBŒ†jœ‘'R3qzS¾iI™qFSïC#°Ž™¦ìõÈPzf홤V!f6ze†®K˜É/LXë#k8É`zeÉÊÌŠ®žON3$rë¨y‘„€Í“gƒ•êÖq©Hÿ0‘‚Ä1jk¶xŸ"«=ŠÍsPš6aÀ³w”è¼Lkä‘cFS"Øps;̲†MílPÖÇ®JO¸.ÒH²ç˜ÊßÄÚJ¾F· \ÒótG¥á–ά´}Ç\¸¶áC­pFRãÏӶѲU²>é9ÙsȇP ×Ï+ñ9ãƒÆg/‘½ þ§/Wòn Hï€\ì“è1 <÷9œÌ¤Ëß, vn"½²¢cPyu Ç¿ í– MœSbÍ1ƒ•ùÜå‹-ŒSBÀ'—c-ÜÈ’0¬ž&í°çvk»Å\æ²fi9r« •“#e¢4Ùb‚1^0ã2RXnŸ’Ö@=¡±A퓺Ż&Êg“Ņ õ΃6RÀÝŒé3 #ÓŸòåÉèW4¨5ÈÇ@œ®lbŒížS&ŸŽn(§)xÍü9TˆÑ…Sië–•ãŒÑùs #®iÍÄÈÄ,Çü'雚éòˆ§*’”„œ¾˜¸*×2E[¶e½•†çž2Ê®VÀ“È9j¶DE™j2, Ž2 ÌöSZ°¬{Ç®W±ÎL¨Îm+4=Ã%`å[H9 8É@‘¶TÉèrD”<À 梙Dp=ò¯ŒžiGß×%¶¹ÍåFjÊ¡,âòÂô2¹ÆgyHÊ£dn (ÊŒœØÈ-¶OË'¯ªH–È4·Æ*''äzœ’¨^¹mx!Iàsàåòs;ƒÛ5‘sÇ+¼9Èa€Ã 3D °à è0dž, –‹Ã¬u†ð¬Vë` 1Ö€*Ãmä±ä)²U€k<++ ÇX«!B°Çްã¬1à d°À:à À ;cà 9>Kº=ÈàÙ¥ Yôé|s.§Y$Q´š˜ÓË–^wqšP(ÓÉ:C¸+1µnÏLç3É0‘vù`•A‹çŸóÏ›-ElË£ñ$”†¦Ò¨äp Ýç\Fé&Æ•Ãá@¢(}ºûgÌ:x$jQ ;Ewß±ÿlÛàÞd³4“ªøDƒ§=¸ªÎ\V3¯%5hêCD¡ZÃôºàã]LŒáBS‘`òwËcˆÍ¹"¶TÍ$lPÇ´ÆV'†Ÿ>¹ï“TxÒ)üê*G «ÚÐñ}/°Ëôú‰5Kð£"Ä=þYËu‹W4†Ù÷Qfÿ•}s¤’:Ìv€± ¢½·í˜Y¶´jŽ4R6o“Žg%‚×>—×3 ϘBƒ¹ÍGé÷9 %P’nÎt0[ARÏSŠ"Ììo‚ƾY^¢R‘Ÿñ*¾Y$ÚOh0;6y`dÅ]ƒï’Çå3Ñ28ÄIòòJGS€åŠÇÛY*¼…Ø 9âò5f²äE& _Œ‡˜o/`¾™YP;a4@FÝ—FR¤—)dh¨–òrVFƳ%$@8* KÆL› ¥Ø×!9z5˜ySYÒ³•„†òÁ 9¢v¼˜'¦oÛ3™Ñ3¨q Bç-šB8¼¨9ÝÔØÊ¸‘èíy—ˆë˜b”•ë— QÌáEÈѼ^L2ž¹™Xw8³ÆLKf’ªr  dU²dXÌÕÂ0Œ• ÊÚÙ6‹ ZTd¡AÊ…ž¹T‰]²¨¯$mšüàyÀ=ž3,c§9 0Q‘Æ…‰¢fçvDi¨ÝäŒØšp;ád b õÉGn2£¨÷Åç«pNZ~E¢lAÈSÛ Î2)'kÍ$̶\¶ W)%¯ƒ‘ äúc±e·y[“megw¶i‘iHÊ‹–96¹ë­¹ÑQ‡blXΚ2F±Ö2*À ÃÃc¬5±Ö<aް¬…aY/¦€*ÉVcÈXV<0Ö:¬x`b¬—\+ ë Àë`¢¬U’¬+U†Ì¹}[RA•™oŒ«›QF\™nìVAâò2A« ×µœº¢Nd‘Û;ù‡Í]·ÅØŒÏ$À3ÞTL‘•½r%‰7xa›¤d{›×'¾†,xV:ÁޱÖ- •b¬… 0*Àb¬uU†:²X + V Ée:ÇX` °¬x` aXë%V:à *±áY« Éb¬Vë`Ç ,xð¬Vc¬``¢¬+$Æ@|ÿQ«mËq6ë IéߟŸlÉç—ñÏ*øï|\½[E¦Ô‰çe‘Á¾HÿL«lz©üÕUçpb7qyðù&§Jû>×qÝÓU¿I$+¹šÂÝìõa›Ú)<—0I”‚Þc%€~ã¶ptñË¥]u$·˜€¡?¿|\4ÁÔsÕÅ)]Ë£ÏÉàäk†­ÓK6̬¯¶GQÊ©ê@?lçëupNþv®‚!² 3„’ ×Zïž`ŸÌ•äÓÏ’c+ƒ¸‹áëÐéÎyÕ´Þ"’É!újƒ(¾‡ü§Ž¿¾OQø8;ôèú‰·€ŽÈîÈ8`lg§!Nvô>2Ë<í±Ú7 U\P:Ð÷çöÎ6”G®móÆÂH Àz`GË­åÚvü¾—dR³Èÿ«}²‹ Ýÿ}sÇYF±g¦PS[;ú_1xCÍäH!ê¥jÍŸBA¾rOª]"6¥Ñ ¢‘H îõ&ˆ®¿,æèeÔOˆÆ‘hö€K?N–+§N¿¶uô%­Ô²±A¢‰ô&ìýÿéô8§•lñr¯F½"Ï©/,Ê@^#R:Ìi2†º@ZÉ>˜j&@«(vµ®«í^½ó'ž¢I&‘ëjŸ€QÛó÷þþ~«£ÏÛ¢Ý;’IØÛ–ø¨w㚆­ËÔqês›Índ7( Ðß Êü/QæA.²c°©"¯wN({wúä"M!.6Õý€NÆ?€n5Òë%­¸‡ãÓ9Z]B´µ##L‚˜úIç§×5y­Å­é¢Ô‘ÆkGUd¹b{æ]Bôçß-3:áÄ™Øñ×2ÊûÖ//£Ò–Ñëu>!$Þí¤ÔÆò˜œŠBlŽ,wàûùûƒ$ÞkrŸI +¡ès³º/MQc§XU†À¯€8ì3Ž‹Î`YÆ/ÊÝ|ÿ×5ê9³Ž‡ÙtS©E-騖ê™zÐãôþù$A ÆB¬†ø@5{Gnƒ2™å’LbTU·¥ÒŠ$k8Σ˙Bm&øŸïŒò·TwVî‹t“É*ÆÐŸ-ƒbXØ®ž—Ó¦ig‚%}HiØòñøø½¦þ‡ë×2.¡”M4ŽåSÊ:ž(vêo,Ò¼+ÚÇ,‘€K6Õ«ã·úõç7Q³º|È¢‹Q±T#ÝG¥ÙëÎQ¨’c¨ŠDO0ªî`@éÛ¯¶dØšˆw¶­¤šC¸†#gK¯úúæè§Ž$C*†S‚M_ôóÝ\Öº<’†,ž¿S(T…•Š Ý|*;ÙË´®¿”_%à ÕVÕý+14ÐC¢iݶ¼ÃüDîo@;zôöÇ òj"E|I5[A÷<Ÿ—=³´9.vr—ÂŽ´iÉHÿPå¨WԜեyÙÿˆñùuÀNw{Þq´õ;°Ð"˜û¼Û¨Ÿ_~ùÜÓ Š+rvëÇûç³åüG›‘b·Ù´1²2MüEO©ÀQé`ß* Üó]8þýóÐéžUhÒf·“×&& pfpž•ó91Ó-"6É´¤äK0ËHͰ²;ãà  x xë ëXð¬xÇ…cXÆ:Äc¬u…d- ±áXð…cÇ uXUcÇX¬1áPVðÁEŽ°Ç€,2UвÇXcÃ\X Vë2aް(VðÅuޝ É`U†J°Å”޵—jãa¥%ÈHй+ú®úr{šŸAŸ0×Èúî^QÊ/·] ¤›äX€mÔ:.Ýûæ½6›R`„«HRBŠ»}ë1ª…•V8‘D‘µm{d'¡-ëÇìsT ¨š4ÐǨ$ufÌÝÇ'·¡êscº:Æž‹H‘©´¤[_¨aÚ¯ùâiV(£¸ÄjüFãqSÀ®ßzÅ+Èiü¢€22‹!jïÖÏ÷×ù›–ÄÄ@Pdê5WیˎíJû#ó¢ÖÊÃdžQ ìáiâ:ön—™Bùtð¯3Ž[§µë}žjÓøaÓM©_1ž&‰7'SË·°¿_^3,RGÊÌŠL{¶2¯&®>u÷Ä•²EèаËÙ™Ê#3¢ëæy䟦h…F&çFòÈ!k›ç׿lÌ ÊË`ÊShC`«UtçÐ{e‹¿N#S%[îµdçÞΩøF³7—"3loA'­—Ï36½K U6þ#|-éc×ùfi™µ·,«*í[¨Ö«¯Rzôê+å“iRS±"Vè?ˆ8#žlû×AG;å]ÜlØuŒúV_‡àÅ—p'm=†mÒJU4êÑ(EZÞÿ ‚,Ÿ^¼×8m§’4Œë0sL”6ž¼ACùf¸õn°‘CºöÒ°&ÙÍÃÔ82K‰I‚-\gSJY¬ÙÓÓåÒþ¹Ð‚i§-#ÆTKÈ¢=«<î™ä‡ÊËl,ÍȰ8çƒyÕ‹^¦Ü¥¥p)G#·<}óézoT§§£Ãê=3‚µ³°Ó€¥—&€>Ÿéˆ¿•ž|Èò®˜y’±¢(¶Ó_!ŽY„hÆÇ*G9ïs< ¥“pŒõ±_¾hb nÙF¥˜V˵«]uç»ËßQ§YTl‘Bþ­¦‰÷àPôÌÉ"Å´ŒÚ²²,ÌÊyF½ ‹øVɪ£ÏkÇÄM6²1c_¤ŠÛéC¸=9ýòÍ$¢iäŸb[ `ö¢³ÍfPmÊGaXm ¦øaÓ¥+œÓŽ­ ÷F˜¦[dTsÀ»w–ˆäŽÜä$Ó 3*ÙåÛ|C°ê:W¯®CKRc /s=!º‹®yéß(ÕkÓSâK @í]ŠœîßÞ¸lÃ#Q–ÍLë‰ ÛÛôESÏï?•;t)Itç¿p8ì3—›Qó%i²5uGÐwï}s¡²D„ª*°¢Ìîxûg^á%‰9#”^Gm¤ydò€  íûv‘Øñòʦ¢š4tŒ4l(Ž>ú3NÉþM–Ûñsw`:úÖyZèX²£êU…|BÅ‚?ºùgÖ‡;}ö|Ùp¨ônÁ„›N×cÐ^à=~¼æÀÀ®îÕUÛ¡ïß9±OéY„ïOá‚HUëïÛÜdôòHbü¼w¬`²†ÿßíW,læøÛDˇ–f‹Âƒ«Çöq,¦hT!aúMu®õû÷ʦӬZAEã4Ƹÿs@ñéé¤iâb¶ªÈÞbX‘]hå×99$éötQmZèÒ†Htà6Â/szÙ'Ž?ºÃK¦ŠeŽƒ- v’ˆ¿Jõì9ì2˜L„ÌÌæ5U£^¾ßß¶t•B…RÁ·‹ê~Ù¸Ã>Ñ™O#Dá ´o¯9²7Rvg8¬$€Ï*ɽÿM›69={O׌×á’Sºm^'½ô¯·ôõÏGÚi9 šmLÖÙ vÏIå%X…c¬„¹‹6¨W¯Ó‹d¿,³yíŒo¬[-"Ÿ!û‘Ë®üå»Xõ¬<–=Æ2&% +¾@šÍ_—oó cMê×—$0fQg¶X©ÎiŠÇä\Ëš. «Ê%zœåùïš<¿ùŽL(^ç3ẪJIï–.˜¹§pÉç¦G6UgòTp*‡Â3]ŠÈ–3“7Š?;G¥‹N³FÓFóåDŽ ƒÔž”˜Î2îó5)9¥rßE¾ýuü:_:Yd6\#+±n€ðÝö¬Å/åÆŽéR34Ò1oÖ½6‘ëðçÉrj;>šÊ„cüÌKåí mƒõßçše•çÔ‰|µo⇭¬FÆžkiûæ}€SDžLå€áö‘Ïsš¼p¤Ô˦ ìÿÚ§¯ÎÅfÑ©$š2E .X»Ÿ&0ßP—y£A¬+®Ð„ µ|§ø€ë@×ËÐäá'ÌóÌH‡þ)ø¸ÄL!Óá;¨Ej[r(*³ëgL)èè¯åõ'â~|d¤rÆ<¿ó½jýþY ‹òK$`´+#2{É#ž‡3ju|s^’«‘Áßv7õuÁ98ä £:i7€“«E…`Ëüï­þŽjÝñmYó<Í6ç*êøBqÏ= ç(‰Uà–O1DˆGøØQýòï-ЮÈá’þ=Äz}O9L±Fc—tÆ4f«+jÊ=;žµéš_(ÒÑEíhÀW„‘JôŽE®Û`²ÐÉ^ƒ•,z_¸Äó;ŒØRà=oö¼×¡Gä™×hV~#Ç>÷Gëû‹·£9$íä×FòL#M±m u7ûYþÆt ÓÇ¥Óƒ3"LWoÀ €*먳ê}r¯ ];é%uWVAJVˆçßßí–,S$êÈòsJÛÅózßúûä“Ó7²ÉL1•P’6Ód± `{ó\÷úe«¯h#3*–PÇàü¹9žHôqA$³Ècܸ/Í“År¿®KÉ+,1¢«•AVzŸC~ÝòF-T“4æškÉ¡gC2¾Ñ]¾µu'“öï’„,±yêຠ.çGéGúd­DB{m¿ÅuÇ"¾\ýË+‰4þk<’„G (Ϧ¸=–z8^Î<µD¼:i¡ÓE",€ À¾ÿ¹ùåêÿÅy¡4¥V:&…·§r:õôÌi©X†éжŸáC‹æÅÿ~ùµ,ºÀ#4£nÒ@ dµ5 ãi==rÃ’*[$¢ÚÑè$Šðøõ*‘° Íêy÷¿¹Ì° ]DÊ›¯  sÍ÷××*]MÞ™ž=OåØî@cMån Ëcо6úýûç«ÝäpP—âw<:$ŽiÒ6‡a¿ò©ÿ_ìgJ=‰*…"£j>ăýûçÂSᚉu Mæ3š5¹º;§ožoVR€Ð«|djvŸN:g»ŽIE9Ź0†'ó§š÷³°Ä:æÈYÕ•K(±eGAéý3Ÿ¦!ôá¢v6à8;í_í›4á:­–ÛUg¨êyùæ ÷Ñ™+6ê%ò¢f<кò…”ŠûK;ôë™ùk¶H8ôÈÍY%$žŸ¾XöÊ|Êè0Þ÷™£V‹Àã®>=r€Î{ŒS|œ”\‹Iô9gQë–Æg£[f}¬Ëy¤އ®2*‡ì£ËvêHÉù&ªòß–.k&LÖ(üÀ®ï£¢&iþ) ÝÅ ®*Ç#×)ñåx“Ã:Ä#eS¸/!HHç¡fß Ñ¾¯Âu«±G- ]²0èz_ó—§‹ó0Äò#9ŒyD«îfâ×¥ðñíŸ6qŠG¾2m—ǪyëáQ µ «ÛÓ:i,Žº&@¶¬å:ªÙ½…fA³HËæª£mY=øä×¾['üàX‹1•¶n"©©”s×ý³‡hëåè´úˆ¦‘ÁajD˯„zöäf¨DóAæmI2HÀ ŒX›¾Ý¾Ù¦HßK«š=Fš4}‚5R´²Zšaôï“U][)Òƒ §( „ôä‘ß9ÉÝNš8?;â©afˆXéÈ&‰£Ò¬‘˜g¶ªFæ€3„PYÆá´‹¿ò9×WŠ]k4ɵN”–Uý$Y¦ëw`µ”†yYjpËd«¡gŽ‚‰®kå\Ÿf)tWùvxVTgØGñm Žl€,N:]ó™õ±Ë&™­NÁBƒMhWK±}󡧇W&˜2¾ø‘š Ä€Z‰àwîzA‘“Rºß,!˜ÇF5fT5>Ÿ /ž¼áIºŠ#ImœY5m¥š}F™ÐI8ÜÒ!fØy"@HþXÌbM<š³!WáB—>Š ûQþ˜k4ÍÒÁ§BÑ%3H»˜1Ô âÿíM<:5ÓŠ0“ù¦ Ûn–¡_ø8ÜçjpG%òeZmF™\F‰$êxÄ€w²úe“/ªT>XEn‘¾Ã~•\šûätÒJÚÖ:$ŽÍ2ª½Ú(Q$]tËg¦ÓA±:»>ãºÉ®çíé˜\ÉÓétš=8d« Ö$ýE9$Ž–:ŠXt¤’H Ä­ðíj#ÖwÆÒ¦•àdÔG!eÜT ¢¦Ú½Mñ’i†ÂM Êô}¨ð¥WÓ•¾.:ìÑë cÎDß$…Põ$Ý~GŽLº„xd¶¸ŽBQX€o CŽ™ Ä#ŸJÚ‰Z $v'q"‹{÷SGiã?æÊÔ6zër•eàçJè¨ùoüa1$6ÖÜ,޳õ¿Ï#§C©ÚXºÛo-Ói<ôæøýþ`âbš™ÎŽ-²©`]¶ðŠƒ×¨ë}þû¢ O°-+~¤³¸ß¿~O'®qÊžÍÖ‹´z"(D¯ f“‡KìzÓ¿L¨"‰ÇQ½C¬«¹¶· “ô¾yoƒG<†}Cm™ÖFÙ³“[¶ô×¶ZÚxž8Gq)Ô:ð?@j¾õñ™í’R‚’ðp_L·óí&œBf`ª]”• ¹Üy'æ~ùn¥Öİ.Í4nÄšo‹Ý¯°«õ°9ÅÑÏ›ŸŸ'ƒ ‹4Ó;<±íR¬bÜêy±Áz}³ ú„f ´Î<‚¾×ííš]’g‡p1•<¹,nr¾O' íœÍ_›ÆÁDb•—rÑ"ºûoQžNVz ¼tr6Š)Õv´Î´ð Q»¼³t¬ðùf?P}ä7ž§Øÿ,É$RÇ4m[Ý£@ÕŽhŸ¯?_\Z§—dQv]ß(Ú _§^x¼óÉIªFÕ]´ñ'Ž-ͨŠ(E.Öˆ1c×~`qɬô¾­üÎ’x¶o@AÇË×öÏÇ«TŒ|¢±SY-c¿g£•Õ4’K§É€UøM¡¾Š?Öï7é¹9c$¼y¸à×ìöƒÐƒÅñ’Î?‡™PF¡ÉM€BŽ·ÍuýÖuà (œúÜ|™«£ÁÉ ²pÇÆ›9‹:½°XmÉV°*²XV@F±ãÇXÃc¬€aY:ÃcÉV’Ê,xð¬Â°¬+ \uްŒuxV:ÈR5ޱÖ:¬;qìÇXÇÏ#b…DaÎHž27‚è•a‘¼y;Å~øaX~ø÷bÇX» ÅŽ°ñÕà&ä²¢0ØF^ªr[¦g#X™¶Öš<–=ñ+¸ÆHbÊ‚œ_S’ò«¾Ga±DÆÜ)r°+ûâ…’¬Tqs’¢{`„yYöÉl8öù,QxùÉm 1f¨8Ã’ã"H¬…&²BBO\§v+ɉr4‰>Y?1k’3äIÉ}ÆmóS×™?͘ùÀœ¸!î3óΗNaИ`ÞX°”F†ö•¯ß€}0+˜k4‘L^Fó Q°"‚’VÇ~7Pź Ž PÕÊÍ ª€¦º7=>·ûŒÆVDÕ™`£Õ3Œ¿z®ç§¿Ï>+–.™öq½¢½O‰¾ Ã ÚcÄb(ïæùçÖ³J«Ká,²éÑÌS?7LAëÏ|œ:mý¥æî`“üJryàð.ÍséHaÓÈ’ÉMÁÈI#á4lüC¯¯lç&¢ô(Úßej@•å º‡êv­×<{›¯zÇá *ÃçÀ¥È>^ÇQ¶ˆ÷ö¶\º8Š‘00²6ñ´–QàŒ†ˆ®£ÃR"<Ùa[¶R7z¯7Ï>ƒ1v¬©×EÁš_ —Y§ÕÆSÉ(Ñ;Û|Â@Q|‹<|¾™(tÁ!ƒll]£Voá(<ü}{š=ëlæH‰æjãÒ¬«·N¢·P,^‡:‘öÍϬ×è]Ù|–}©æF>%œVÒ>|›’n)’2Ù³óF™õ Œ[HÚª ŽHȾ™Ía«C)¹Ùþg68ç äw¼Ž«ÍÓ˜äh–MëæTDò7è¥d[TÒB  òÓ°Þ«LÅ«’G^œvãI§¢r;[6ê¼Qcðƒ¤òöȯE%ê€hQ{æt7†ù¥ÃÊe•Ùx4zžF%ðp$âû¦ ÒvU4ö« ½t°i Ô+éÿŒ‚g1H{ ô7ÁÝcÛ=\–ÚTyãI=”h5uùÑÆ±PG)ð¹¾—~þ³v§]&·Ulˆ#B¨¢âzåòÌ—ÓþF Q FÚØ±ð¤ýÍf†xtÞ%V岪ÄxíC§Ï<“Om#Ó ÒgM´â f–4X㎕€ ¢@ùqûVr4rE>¤Âå¨ö‹ÿ˜×NÙºU–I·mÞܘ÷0®–O¯§5Ó2ÂMiUq 8¿,?"hwýsœdÕÙÛVnñe—M¢Ž!¸¹F;É¢ Ž(P5G·ß1L“éõA@Çe±êk§Ó.ñ š @7F~9?Xê:}o›Ì¾%ªXÙdùÎ@¶ ÄJ5ÁuùÛŠmªgH«´jUmK®¡•ò”ÚUÏZ‡+ÁôÍW²óFä,¾àæ¸þc9Z=DPª¥F^Hédò}Å‘×:rO¶1¥ ³¥A*ۮϵzß!˜—tUÕ•hµªF‘š4ß&Év–¦æ€ϧ>ù®M[êt3iT¤ Ä.¦ˆ^ m¢9½@ês™á‘£42˪-¾aZà€SvzsöÍtž•çVþ23næ¶Pó <×;q¦úfdÒ_$Eœi @ª#,âKg¶ u@{ñóã:°†’U¸DNɽ՗e±cÖÏ#§¾YÁ…à†/̹HcVŽÞ˱ÞI*,ž8¯—Ë5!Ôkù!Ò¡b$†qØ1º¡è9äƒé8à“Ù™Mµ£~“M?б’JA #Yó$w'›6:S4ã…ãÖ1”­J9;žnêúwÊü6GÓȰ@Á£o‰ƒ€àtÊÕjtÚy“SjhZ9pKÜ›º$(sßxÅ;þžGr“5kµ ÿ™ø÷!!m…¡To«]ÿ`åÏæÁeƒ®òÂ]¼µ_Â݇&º÷í•.¶md&>u!@£ïCž¾¿CÝCªF•å>QJåì^Hé\tïœ%Ìœ´û:Ǥ94Æ(&’K2Bl†{*AämN¹¿J§U ȦäR€3,àvçû¼çMª¥Í‘#vÚ6!âEuã÷ùÔð}AÔK",,?À[¢Þï‚×Ë:qIgHçÈž6iÕC†ib#Q#£°rº ¯sÇîs.‡TšŸv¹`ŽDm«Aøo‚oŽxõõΤºu’ò´nÑ gE#Óü¤üó—«üÌ~(žòšv,žM(Ž‡Þ¹ã½g_Rª:Ò8ñ;{ìô0D3"ˆ+™khÐ|tξ™HR\ &îFy'‰Ç,zÑÉ5|£’Ä8âºýÇ×½ Ýùxøóvî`×búÓ:z~xê+ÉÏ›‰´ÙÖy8êÞƒ®X Ð¾¹ŸJÍ`E­¸õ?é›*³Ûgª!GŸ8ë%Š!G §Ó, ÅŠ+¬+,¡Ž†KWGÓdƒŒ.KBŽ“Ù†Ó‹-¬u’Û†ÜX¢5KiôÇ´úd±Dk §'GÓKCn=§Ó'x^,Q¸mÉ^,–QV*ÉâúâÈ,+c¬ a’Ã%‚8VK X#Xë%†, °¬uiôÉbˆÖ02{N0‡%š¢ ar{2[G¦L‹D+$Éü>ØìflÒ@8ÉXÈ_¶;È['Ç|>ºÅ¸ä¢ÙfÅÃËCß!¸âÝëŠbÑg”˜y(r½Ã˜1LZ'ä\^U¸y bóÇÈ|A£"V±ùƒ Ë—dÑrDƒßßJ"lwÈ^M—!Y¤e…áxa”‹  tOl³–ª26TÎ!0¼de‚vÏ7B¹âúØåš¥Šƒòqj,ŽåK[m¡Ñ¾Ý³‹§P‘,«²Ô•ß®ž½ŽkžiuÞ”ª¡d Ñ­ç­}=óá8Ûýs-©]¼®šO-7>d•Dnµ<óÁS•–ÕR}42B‰£:†*ÜÒú]ó—ò<@èZgxÌ%]J³!z°Eöã¶N4Ô¨³i«# óÉà^h‚“r2X4p»®½ç×(Õ¤ƒÅ÷Æ$rüÓM°ŒWN5öÍ>"—à m3@Í2ùr8.X†ÜÑàjèåfŽ̈Ý¥„[ö©µÙÅÙä~þÙé\‰¬|œ]Øi«Q$)¥‹ÊtWO,ªz¾ÕÓ×®iÑiË9·•q'Šçißþ¼f=8>¨Öiâ™JBšë†¾ÜŸ­çYLM£UYÚ9Ñ2É@/$X+'ùsû`b"šA þT2’º‘Úż_¾G2Æ%óÁM+¨'â>£ÛØ‘¨¡$ì½W~šTEXÞ‰ üÚZñT Øô?Ë3ùÂ}e]‘Ÿ˜×mw}›ŠâºõÅ£Õ’]JFåÇ´DíilÜO·?#”É?Âʨ¶Ê|ÙU‚©¶ñ\@éÅfц÷£n–ÛU#ÀîŽ#5‚Hõ&Íúôô:5Ò7‡H‚DšGÊPßR?Â@ Jžx¾¦†FChüJC’icGBÒÆ§rÚ‹¯±˜µÇ×?éXЄØGÂ7ÔqÜtÄk•vi𷎳°Ò$€­¤Üû9-Èâ­v2ñ¯_- kÅ?ÞªÒP,›¾hzä<;*.š(x¡y”aZ¨uäúžòØ4é§O%7"-lM×Sɯ¶v‚•Òl’kè#u ªWx"œ•Ý4Æß­ß<éß‹÷™—E müS  DPñºÿÂÛO_Rx¾ùÒ‚8%õlÄd-ðª“úˆ&øë߯=F`Ô¡I£Où†áäE |B¨+Šäx»ÏR„œíœrJD#uĺY§• •¸+dñöôúÖY¦“Tt"O:(£G ñÌHãš³ÔŽ€uôÊSÎ}RéàòÐ ܆-ÀÁ#›cÍõúeš.ž%}V£R¬ðߘ7rÕÁà‚³ÁãŸ|丒¥[5î>Ìúiü½dº™£c_ ÍúXð7u®OÛœêiu’éµ¥¦+e’¸Û@}ª7ÏϽ˜1‘­Ô±Úhù„¨ãá» 9ùæ-<ÓêujÓ©PÑ–øHmÊ žÜ£ëˆ· Òz=»Äõsi¼ˆŽF¶eØoqàœ|"²¯’OÌk\HÛ•¤&Ç<_QמœvÉ@DjÒ)‰D€Ðrm‡{9úý1輩À<‘¢ U>3ÔŽ$Ÿ·¨ÎÒr“M³“Š]#§7†ée™õB8¡·:ß‹$ŸR+¨è>Y³Â[I¬Ò©Að£R´„«u«þÆp4ˆv9–gó™m¯‚Ÿ?ZýÈδB˦2¶$ÒD«ú“Ò¹ô¬éÇ;’LJ¨ôúVÓ!) «½‹ø½s¤æ|+X7å’:S{˜8¤ö®½IýóÓ+(q½¾¹ïŒ“V$tþ>NeŠpð,Á‘•Ÿá)È®™¢r¡Eöa—#. :%¶ñíËñÓ'·Û&CšÀ »ËÃ`ÆCªÉmÉ…¬…ìÃË9v‡¦L™ªE[1ì>™2F.¸²R#´áXìã³XцÜ7V=pM†-£Œ26á·<VW¶ °Ç…` uްJÃa€ x‚Žð±aY`nzä·eXòQ«,Ü0±¼/% &pÈ^;Å $ ÃqÅx_¶J-’úb7…ûc¼ #…cÃ)ãÃa†a€ç à 0Ï®†ņe†` ‰¼0¬üµ“N±ìU°ZÈz?;þSž‚f‹KøOb8^Mm‰…<€míÒ½sÍHÅõYh±hZ³×·\ïk Õë¿éؘØE®v ¥›ç§_|øŠ¼ŸaÞ?ñÿÓˆDþZjÄÈá%†ëú}{"×Ç£Õ²~aôp¿æ"ò£ Wpµ7\Õô¾ÙÅðˆàf¼—uÝh–‡b#4h5²t‚o)¼¢²Û€¾,žœgÉIitvŒ\|öv?M·ÃLÒÈÒ•CBQ‰<Õrµø¡x4ºXÞI¥}«µcf"ëçØü²r†›ÃôáæD•ã1àòàÐà{wô9ƒ[®QàJnÂ0¥™@ÏÂOÐŒåþÒÊÒƒo_Öê5æêI;xXPÑ,CpåWžÝk¿©În¾H5C¬Ó‘ªVËŒîQ@•?ò›ùïÓ=«G¥×x^ó¢3¤•B%(ûºmÛC‘ÍuéèsÌA xDÿÄÓ—Œ²@²»˜Š²jêùéë—‚*RMi™“iS/Š–E#z‰£T”H¼.àM/Rn½2øt¿÷}@•”I#t%êÁïÐòÌÍ«š=n™ŸME( ¢€à_Ãé¿n™¿Oæ<y$’F Ü!'ŸŠF$ð}nùÙ§©œèàf’šVWÞ­å²vòIQÇCÅ}󫦚 I’ÆåHo1 *‘éÍŽ¾ž¹F€A†? ¬e`\Ð# àqî9ç4iäJ’Ç2K±•·ÙZ殿*Ìs%ŽHוÐÒs -0Ú€\¢Öí¶ ¢8è~sŸ7‡dǨÚäØm >ƒ "ëé˜uz·>2±´@ÛvªƒØÝ–V¿nô3¨®úX&œºHǶˆý<¼_Ôæqï&•5lÁù­F§K¬(ªä¥ ¯~GnÙ²-F£MPø~âFgGŒ»8i€÷úáæYÚ=2IçË*« ¡ÕƒÖù÷ëXÖ c¼°jL ³9OBOS@ŽÖúå’ŠŽŒÆÛ.üΓXú±¨E…ÐnWKe4y¢O¡ ,öã0°W‘Ý!  ÛB‚ß[ ¨ëšÒMÔ¬}’HtÌ¿K‚¤WÈíÚ³"4ºtÔYÒQ+E]Xã­ûæáø¦b]›â$ðÌôâÝ™8)ÑA>ÝGÔ_¦S¥VG.Ë'ü]ÍúÛe‘Ï¿_ ï“կг¤mÚ‰$2¨]Ûž,Õ`ûvË´ÓGçÚΣ‰•ä­’Jžÿ§úâ/nƒ-ð­@wÒ«F·±É~~"E䎂Êút9¢]tÅWõ#Z±'øXðl×ý8ËáÌtÞ Œ„>æAYë ¯‘‘†MTG*ˆtûÜ[HÛœŠå‚Ž_©ÎÙµT̨§ÙÖÓκf¢eÃfZØO5Á>½}ù8¡ñ4Ú–I šš1ˆöv_<=ûw¼Ç’'É4òN¥¾¹T·Ä>+vútç7hàNÛÛMÅ¥òâ3ì9çâ$ÑxõïÛÕÃ%5MìãȱÚ9ÒO榨?ð%>Z„,óuÔuºæ²Ø<©bÔR ±Ý¹ç©îÞǨç/ÒŠ}6¬‡§šØv€] /±?Øç <ù&2¤Qh¶…ÝÎþ W7Uôû›ÅþÉù4±Fš¢N’ওËjÖÉQÅž•ëÐuÈÉ£‘%„°A$Hß ¹KÈÿ/òèrý<1™d“PÅOÓæn8  UõªdæÖùZ- x×T ¦ý±ÆX¡v7v@þXx·ràÚº¤‹_ÃÆ„1WW‚+ / º‰=h•ñíÏÂ%_4G*#ic´^=ó~¦g’d‘^ò¶FëEU>ÕÆrw” åá0©Z dµî==xç÷®™ŽY¥*CZvwü&c©þ’dj(YʪTr5v_\$ƒþø‚-Ò¤"À7ÏNø…õ¾Ù_ó ´¡b£h4Xõ;½zž?é—x‘“JM¡)kP8_AÇ#¯NמŒ“âÙŒ^gCNÒGâð½›fV`ßP:{ýûÖw|[ZѲFѹ@Ãp°®—c¿íž+I3~sÍ‘RWÞ¬·Jûõã®w+–‘í 7XßÜÞÙfúðxìsáRFÐªŠ² ŸSÇ_®OS¨¨ËF™T*‘\rlž¯ëòÕžw ÑÔúcÛœåñ4ü·š‰J;VoŸë›´ú”™~|ÕÕsÜ}2Y0$SÛ"Túeø¬Yš3›ÇDöË+ ã5d¢¢ änòí¾Ø¶ Y(« Ëv‹n[3E7ŠÎ_åáåûc$LYF:Ë„u’+Œ†,Ï·$¬¸(Æzdȸí>˜Âùir$‘‹.$v{bÚ;ä¬â¾pM bɈŒ¦HÖe °ÇXU` °Ç…d(aޱâÀ« xV@*ÇXðÀðÀ<0¬uÀãÃ+ à ¬+ 0†<]0 0À 0Ã( Å c居 ,xcÈ(ü©$³ ZêŠÊðÀÛ «UKíß:ÚíDGÁuˆTFZ`cfâPXtùr>YÍñj®¹éÄÏé!5´pyÑlÞ×BÚ¸«7ëòÈh5»gÔÅùx”È›ÁpZÈïÉ¡ÐåHtÚÈä‘!`”µ/½wë÷öÇ&®|JÚ?…¢(ë`’Hÿ|œqìÔ¤u<x¥˜huçõ0x_Ì+Bþª:ç;P¯<1xcó??åîWífÀÖÛ7uéY¿Áã‹Å|¤ªj"tØ#íûghɸ[ì’Š‹£J#(W…—ÍSµŠòêZÁý~—•øN¯K)™5s‘0hø¶¢y[éÐ_nýë*ÕêPC¦ÔEJWÊþ!;×<+Ðó†x4k$P¤rÜjÊҮAR<ž¹RÕHeLšC&§ÇcÕ³Æ#* ¨l¿OqÆ=låôޱ:~_{0Sd©ªkúŒÐc“þìòXTþ¯ ¥JûÊœ8Iž9W‡V®ø-ÍGCGŒI«Ù¤hàhQõ)¤rÅ%­8[ˆíß=Vžh"ð©d,ƒÍjWPj'ôy6O&¯ØVbÐè‰ñ},'“ [Å0*„òyî3n«Â£•dÓÆÊ^ *¨µD×>Ýý{s9$šE‚jÌ“G§Œéæ‚Fk–7PÀİVŽ+õ~ÿ\p3þ_^%Kc¼8 7ï}‡oŸLëiôñé¼C|E[£V,›¾MßûñMLú¦W…DS‰A‚ž¿ÚÈ9˜M¥BqÝ™µŽgñqWTj’Ï}€óÀ<ŸN™£M§Š-RÞkJ³GHà>æJ[Ð×5ß“YI:é<[_©‚iSùpýyˆû€GnÐÏQäø^ŸO«óã:‚êY¼íÏÇ*?kôgO)·Ù¦(d:]>²yib’R’·°nƒÕO$Yãìü´riФeRååHáj…ßN1ÌòþZÐéYáV‘˜)MË#QïÓŽŽcRtK"TXéÒ š7}x&½²Ë³IÚ£rº¯Íê%Id‚‘«ÛDÐ_z®yËR]šq©‹) ùnàP{&øë‘f…<%gcClÜRÜ’8ÝBý>÷—E¨ü”i"¤’Ç?¬ê8$ƒc¦Ñ×·={ã9R¤1WLÙ&¥’50E‘ 1=È®ÎÞ(×é x»³Òó™rl².ª6’IÙ„Œ(•)»r› phzûfé¶œ2 •ƒ“ÿøø¬Ù¡|WõÎ\O¶#ï„„HÃ÷åyô#¼ç®|­£Î¡³n¥·DʯæÇm;ØäƒÍþÙÅ›þã!@Re1ÒH«èy¾>·`)¨µ B°f,Ö­_¨ëf‡'9Þ/©0è"’’=„2˜ÍÑ DüŽp|–é3uñ=¼ý$šp¬'o‹@_‹ß útôå#£ÆÁPˆ‘7[8íÓþnœ_¾?Õ.ŸR®ÛJ:(cDվŭ”G§'ˆ4¬í"±pËuBýþYˆÉ½ª:ÞpI¤B@X‚I"«©Ýíʺåÿ‰g•µ0JÄî “ñ¬$;téõÌ~2Gª'VHmÛYyànî=‡J&ò¿Ö@þ-$p'ðö®×*wt‰$óÉç½÷ÏR¥Å²?Ì»I¶2ˆð°uuȰ,ÜýÛ¡^lSL»Õ-¨16Þ¼ôé|÷éYç!ÕOä¾Òߨn°#§'ëÓŸ\íøn¬ y¥3æúp@¾ÿaÚ³”Z´Ž”û:,Þ| ›¶ˆŠªì4él>¦®¯ß¶cÕ<3NžP]®@zîÛbý½½²RJÞH‰äø‹¢†‘CyýÔ}s3²ê•$ŽQIøNêäW_‘&¹°G¾wÊ•êÝž¿ÂâdÓDŽWg•Ñ®¹7~ÖkÓœZù_Nú­*ΩF¨øýT䛿ç,Éá“¶–ƒêNæ*mÚÉç5ÿ^™–mTÒêõAÌÒËíGˆÚMüt§­þݽšpTqpy»:+â5 ªjI£;¾!gÐ×·Ãû^v4šË• »$d »Û…¾RMWO¶xTxÓdh»HÎf?OÂGŠ$Ÿ§¹ˆr[£Sã¥g¬Ók¡7)f€=Onºf®ÝsÏxtú¸Vf˜™šØ× #¿|èC¬•¼I¢ ÍÅÓiðI¿ê_žzr¥³Èøíº:@c# ‰c­ö=rÀ3Vb…X¶ÞN±l÷Ébˆù~øyy0¼uÇ´úâÆ$d¶ãçì[…· Æ[#º¹ÆË¢UYÃ}÷Åðžø2dÆ@ìr5èsHËb+ï‹nK‘ß7é”ËDÃ$qe0*À x`aްÀðÀ à u€,1á‚…b¬xVc¬2Xc` x` 0ÊãA¼xðÀV<a ް¬+ xVBвU…c¬Êi¨–8§¸áq*’CD¤‚GPx®Ç:©Ó´>X,£lmlºé» éé–“I(ž}^`”IµwYµíF¹þ¹Ç—Jté0ˆ—&Oƒaª#¿ösá§ékqVÎö›MjuZ)lÁ¸ªì}ȧ휊-D©«Š&HÚFBÕP>^Ÿ#Í6«Äçжµ£Y]öÂ%+ñ)?qóÎo‡Î'ñ_Èë9Ñ@kâøÙ#Ÿ\Ac=™žÕ<V)ÓQñ#¬Š¬º’{ò3¯©¿üYN.‘·[$óÔ‘Ö«<¬‘ˆexË€cf¯[ç×åž»ðêE©×ëõp³Ê£Ã¶š†¡cÝI²Ý¯ûôt"mŸÃæ:­>¡ áþIëÔî4nøç”Í<ñÉ,zwHôË.ëU¤ëÐ×#žæ¿lê˜jùq¼1ÚH/%uëׯ¾EõÏ0M:„.Ö€€m:p ç#®eÒépÉ<üÈÌ/š7ÒêÍÞa¡Š]ëL;)yä:õôÊá–ʧŽJói|FIÖFÿ‡7OÒî'ŸÕ}{åš'x'–IE$Í"nÞzòIõã\–™!M1*#G§’UiQ¶—g Ó›á{Ýs^+>«I'—<2¹-·nÚ"Å- ïÓÓŽ©Œ¶ììhµsÏ"ϧh’M²2…eY¢W×ß4È«¦ð½x…Q´êG )ƒƒ`{é~¼ÃA¤Û6£âócHz!Õ{ªºãŽ:æT.4Æ $s>¦tŠ+=àXŸóX¾÷ÛØòQÚ5'HŒêuº:i„Ž7¤ŸªI·|#Ÿóž¹ÉñHe„j´í¨RÛ\©«½ž{z÷ç:éãO$q¶¯pŽFŒ o^,üGaÒ냓ñ8“B^/>IÙÊú+“ú€Z¡À¾Ÿ°Î¹ìçV´k‘XèjÜŸ˜™† $W¯_~¹N®ÑÇ@S™¤)»sJß °=>u—á’_ Ž9äf5&ò„Vúº¾½,לåÚè™4Â5 ¢%'r‘KðRœw óÛÖ²ü\›D’i+2i´~!®†0!Ž2Ûw|( –±Fëê=³Ðkò}Ãå´[­‰) XÇõñ éê;ç'ÂgóµB`]•wò'×ííw›µz¯Ï#iËF_Ul yLõ_Ö8çåÀÎqß.ú5¼t],X#ÔN¤ùd·”BÑäȃô¼âÆR8ØN#ÞUY]x, @¥ŠëD}AõÍš­~›IáÐéçY‹%n‚ í}=höë]3³PcÒø|dÿS¥I_âႪ¨ÿ§ß=¼”—ÄáoesŠ;Æça<»$rx?#]Hë‹U¥üÂyr.æicŽŸ’¶ÕWØûuã6¼ižK(he§8¨¾ý+¿îs.¡ŸƒlRAæ ‡Ôx6,ñžJÚ;µHÔi[Rß :+1 UÂÕYéïéÓ;$rF_ãš5ýIø–ìp:XaòŒ»]ªy<ø‹°y•r*À~V@ëß#¾HßQ!Ú°ÂÔŽ¬•xŽfÕí—Z#¢ˆw’T†nI¥u®OÏÕMÄîÐPYeçì;ñüŽ_¸*Æ"µ|7³o<žÜמxãÓ¥* ´‹'ªŽ@í¼Æ£}sÅtÏ_oß5o‰HHâA"@¾fÒOĶOoØ{g?Y¨iåˆHëŒ qÐ's^•]òí4«£Í‰É ¶<ãŠõªï£7‰†•µg}.Ð#*¥Ñ Ç‘WÅðyè=øÊ Úñ´¬å™S"nÞäð>T:{[46©§tʱ,jª€‘ðŽ¡ÚÍr;‹M4Òj׈áwÜêøëì;v¾}3o’/Itg¶ËôÁ‚B¸s`ÄGQb‡#Žlîéš&ÐUl£m’~÷ûtù Î24‹¥H²\{C¿ X¬¥kD´L~ªÑ†ó yÈ<©`o¨9Tº¹uQ¦‰S£ªñ@°/-ð=¹È #k´áü‰! î¤þ«¾E›ïÒ‡'"ÐÏ 9`Å;Ñ€_ЬqÏûæ¯Üþ,2ÆÛÕ8ʰ¾MßNžßË<³MI´u[5jti&†(ÒEMƒÌ‘MSËÛ¯qöõÎZit€ÊtóÓ|“Dy‰èVì-_&îÆj‰ä0˜R“ F® °£gÒÖ³ jÊJö<Öݼ†QŽã|u®?ß+"Vj.u`+ˆË z»´ë¼)¢X³:ßQǦsQ­t‚%#_˜´¶Ôi·U“þ¿<³E§—ó“èòyhT)'h,Ü þÿžt¿í9BI¡—A N¿Ã,Ъ£žºÿnt¿®Gª/}5#Máðêå•KÍ­ÚHx;¨U~}o9$°øÜ?™”ÈÐÈâ{¦gPM“ÞÉ¿–tMQA¨xÊV¡Á ÜlQ7Ò«½ÞY‡iuzCM DÉ<0¢…¿Zì,ü`roL×ׂM6íœ4{µ±E¨Ì€ÚHéÆÚ&Ô/¿>ß~·•§ÒéFžŠ¼û¼²V÷hÑõ²9=‡nr ñ(5º8\mf7bBWMݯçþ^ù8<3ÛÎï;–Yƒndõº4õÉ$®™˜_fÍ<ß÷?.;Í£­H-I´€R8?q•KKªÕɰ¢*AÐ.Û«¢úw –wŸVîNÄÊÁ…€VnœWèÙÃI¨–8$ÓI4ªÈŒÅ­†Æo†Íð~ÀŸOLÔ¾¤?)mŽ9â’xÕü³½œr®G`9ªäñÁɩIáo%~[’‡j“ÅéþÙ®r²ÔÁ§H˳ÆÄ³-ÿä£È¿N¼}ù‚Em[âm!aæ$`y‹`ñé_â¾:ñY„ÞÙj™»Æ™´Ú (}¢»}yÌz-ü3Ã#PEÓˆö’MSÞàú¹·Äó‡4Šô²4¤»u%kì,“×Ôg;Ää}…ø;ßK¼²ž:ŽOÒºzghÜ Ñ–êEjÃf±xˆ6°•‘|´òÐ~³Â›úõû}øF?+FÜ©lêø¤‘Gã2ìkŸh(ÜG'23C¾G”1wbáh–ºâ»÷û~½!YCÒî,¿ùäŒÏ&Øã‰ä+ øéˆF¾I=:³¦Õ ã+£Eaþ[EßÇ>—ÓŠY%XÀ¡¹G`=øù õÍF“Pa"ý@Nï_ë_í•êË:J€°Üâ¹Þ]ScŸéßB­[+}Û–豘•T+ÖîÉ«µôËz¿Ži+kï%I<‘Ø÷÷é™à›s2#ç3ÜHTv{M÷÷ëšæÓù“!e½ EÙ<÷ïϦk]ßd§F©õC'–FÐ/Û=kp³’Ò¿—7ÂÛ¦•<¿ˆPV5cŽÔ´¨‚Sl'øœ¦ý{ú‡ùûdÖBÒéãVVŒ² „a];€9αN´Œ7³oå Æ³HÀPcW ê}ì×Û­e>pP¨‹$‘Ï1'Õ×&®¸ǾKU¦1Èå£ÞîTï~çúæUœ¤zXë|®¶Šƒž­÷&ýº×å6©¦kMÑÕß[« ð0€nòƒµŸ^ÞßLrº¤ÑÓ‰¹7¼Ýw6yç™`ßšA& ¬’!áI­õÐô?ß7×$no.-¥\ªHÚœò,üÍŠ9ÇÜk‘³ªIÆŽ–Å6UO1¨÷&èŽÞãÛ“Îtu߉SÃôßšFk1"¢ucp8ºÔý8žD0 iÉp§s†…×LÆñC¨žO%TQNü± X$†=÷ õÃÕJŽœ=ƒÆ›M­G¼ìWyÞ@y¬ŽM©¡À¯¿¸ð]f·W¥iu‘¤M¾–4áäõ9óMHY]g‘aÞ Ä€9³dö¯AÎ{ÿQhÓòaKm ´Åk ç»rEñÞó·¦å¦óg›Ÿ‰µñG¬ómŠƒÈY-ç9º]ts‡Ô,êG÷ÆN-S´’©@ €„ž½oíŸNÓVžã$oÜO|/(ËŠYîw}¿Û(]tM;ÿ¬8Ü{]s÷íƒ4ßFû¶í‘VÜ/%‚;à ' 0À 1áaXV<ƒn…áy‚°¬/8.…Æ  0ÀÑ qî 1ELžüEò8b…±îÂñX¡l|ac X¼Xa‚a€cÄ0PÃXV:Å€aޝ ±áX¬u Ée ¬1ãÛí’ËB²{}°®2Yq?3þ"Òͨ”þY¶I$¥» 7Vk¥œä¾™ÂÒYÉ_¬Ã68¼õºï›Ä],"'tžœÈûT)E"Ç~o<î½Qt­nä$t–ž”MWkéíŸ=â}E—øòt:Ù#9Ò–‹x&¹?§Šê;ÖbòæE¨xI)-sciäöîs„x¦–Iõ(‚¡Ñ´bÇ=;g:z‡ÃôI°HXbvîÜz÷ÿ¦n¬Ã{ iô°H@,ñ’/’;{çð–¨ONú”ÓLcòã Ñ ` õéœWÓÜ|3â`G™À5Ç?û³†é¿/øCU¯*<ýTê‘–ëWÏ?\Ö ' YÖ‘Ò µL3êQJÅ ·^Þ÷zrs§¥†J™uÆ8'PJ1Wðô®}¾YåL%uÒE;…r€³ºµÈíÛ=V’3¤ðKË1•v•¢…—i¡Éêx9UHì¿£ùæ£'FZL»Ræ ŠhÒÚEnŠï®b…™[ø­åw[=Ãç~™)—óz`X:¸ =>ß¹ëy =.¢I$ÈpX?çŸAg¿¯¶LªÇŒ¿ñ¿0±Öèäè»5>Ç fš??-m¤±nñ|Žüuùg:r¬»eM‘¹ XÐPAõôïó]z6 6¤¾© l€È´À|v¼óóEÕ£¯fF¢>¶F+V$2+kÑzW#]s6«S ©RcRlz_`Aì@úæ7T3퀿œ[õnàP8ç›Ët~gcçÉð;‹Uõ¯^ƒ¾XºÙe½Ñx†šE –BÖÙë\ÿÓÛ6èµP,«*Èó»†$攚{žµÓ·¼?Cùçp$dm@òŠ .ýëÞ²ý<0éâ—Ë,<² ­‹ëÍÿ} Vôbü¯IâÑèÑ#W ¤üJcª² _N·Eã{å2voõ  ž¿\ñj›Í.‰î9§¿L·KX…¼fŽÆR»GúçÐãõ)Då.ÊÙíeñ™†©×HþxŒÒ2¶õ½ÝÉõqô¼Óà²þKENËçÌÆiÏRƒ’K^£Ð}9ò:EUJØ…Ä ¼ŠH-]>œ·ÏLéA®Ñ3¦PO–>+ Ðôñqü»u=»f'ÁJ‘ô(æI"H°}²Ðl^yßñÔ! Œn¹ëg¥“|{d“Ç¿-6šIFYv©uè ¿^ >Ã=ji«>t¸ÚtzÇ‘°ž>y*͠±á€Vë…†òcà à Go`bÀ^,áŠñÞº 2.ÛGL`ؼã¼UXc° x`¡X«<–(@c¬u€²Ð°ÉmÇY,ª$+dèac%—;}±ì÷Én·d¶ZCÚ1ÐÈî8¬à¶‰_¶²8dJñ^e'gç× ×kÌqÅ*yà)’È ?3œ§êµ2ù1» ÓÔ‘¤–z‘ö3·¨]jN²5’)DlÄÙÊŠ*Aúwëœ9bM:‡É¬ömMкð=3ãë+­ŸSj;eŸ†ãÓë#’à)$(Çà'¡¬Ùè~ù[éºM:F~çç®ïˆúúå߇õG«ÔN‘ìÛ£pÍ_Ÿ|Ë¥ñ)t¾ž[8³Ç·’v½þƒ:;ÝÕì°~AåEòË6ÆrJï~Þùè<|é´þ£ÊÓ… ]›RÌEôç<ÿ€ùºÆÐ~a¼Æí›³ìfÏÄ ¦“ĵjŽT!ähÐzGlÝ6¶KZý ÓC‰jÌŠYúG&ê¿u4yÛ7ø¾©ì’±éÔƒ&Ô2¾æÈ>¼Ÿlͯž-ˆ¬Â”KrìBNÐT“´}ß0jµÇ[¦Œ²&Ù‘bÚl *z×ÈýóÄøÛ›:_ĶoˆÍ¯Ôù¬áŽ@VJ–Ê@•w×3k×S¨ñ ² hŸK 6ä<0]µÀ°IB2˜5šïšãW“Q¥>7€Ã©îï/•_MàZ9¢§mí xg.~F£Œ;:qÁ]ÈÈÚH4ºÿËG4N š7o.PR‹¡®}¯ŸÏ2ëtp<£a•uñ6æ…Õ·ZðZÈçõCºfBÆçQ<ð ‰61Tm¶z‚+žMòÈH²êt#^¨‰32Æö¿Âß=Okâýs “iJ‰ø4í¥‹K#jŽœH¯+ËD°bÄ)àóÐv=s³4º™tï"ÅäËŠNÍ»Z¯¥ótOØç=¿îRI'–dÓC—0úEpÇ’Ð~¦½sZé´SéZaÔUN ,wtÛЛn3Ÿ$¶ŽœsQìÏŸE&²=Ìàî2X°@ n{u¢{enôébñ¿šÌëdš-ñÒÌ缓³IQ_†Jši@ãæ ~ÞùÐÓi4±ê<ødqj¡ö|EמGnª¿"}Žu—$S¦gc‹I£Xÿ1±˜…ƒDBî7Á?P:zæ‰u1ê#Réû£²ûiºIïÒöÎŽ£Eå£M3Â.6“Pü-´nRhXýUUÍ_sº}6žKDéúŠÒÝ)Ú g¥1¼á¦ÍµŠ2øf¾9¼cNªÛtÃR­$\ÿ‡ã&®ªÒ¿ÓŒÕâ:OÃZÎÃS"#•UàQ¡WÏKþù<ï hÓÅàt€EPHûÙMƒmùŽ9ïé¡ü[B"f¨Úibb Ùþ:óñXçÓÓ= ¢—ÑÕ-3<º¶0´Ç„¡fì~vÏA¤ð£§ŸK$¬åa½Q ³íÅúûæ>—ABÿ“ÕÇ#Èûv~¢ k½§‹W&ºñoŠBÒxbÊži!`iIÅÏC}ÿ¨Ã“l‰$ia‹Oª)(YV¤ÑÿÖ÷ìr3j…HW)dmÚAP~œÍáÒ Ðë¼ÖpQàyR‰þÊ}z䎺32Ó©í|›÷öÿlÄÓR£qj‡ ²ìb—ºÀ[ :-X5Á¿Û/–'ÐÀÚvTHÑ °#þ'ŸOïÓ3Ⱥ¨5.’9 [Zt^ÝxùôÂ]H•<´˜Q—•W‚O9¯\Ò¦NƒRÐnŠX%I?VÒ‹`p§ƒÔv¿é”¾žTu"%h¥€ f¦ºº ®ǧÑìÐéãY<ë.GNÁç¿ß ur+‰bŠ1 m¼¢7co0/½w¬Âj.¢F¼± ‰tgËÝ+q´â¿{úb[Ô¦7Ý  m †N¿]µ‹OâO©Yô³ÈÒÇ+D¥@nÚÅŽ³×­~Ä,‹‹•3`ßVHÀdèVËBz×^Üfí¨’>›V’eaJÛHøNê»äЋIr,÷¨1•þ"玗T+¾jYfóßTËDT²í¡²¨ò8§1À»ÐË,jòFÖQ¼“îiMDÊâŽ5$P*ôI4~½½ÓЛLºhÕ¨" ˜½™‹[@³Å}÷ÖJÑÆÃàq°Áº{ô¾¹Ï—ó–hæ]€Iaë#m|Éê2ØCnâv6êIf³g½zsß394í‚MS=O…ø‰ä[ËèÛTôäŽÝÁþ¹V¸¬ÒèåÓÁsn¢Š|B€ºèhO¿®eð¨emRÇÕ–tŽÓVn¯‹õÿ/Ð>cÔ¡—ÊÈΤSÐâûƒßÛ=ÜRr†Ï4cÚ:Ú_Ç=^4šˆHÖË …áý*Tõ`ÇŠëûgwðÃË/„ù’K$›¤jf] ú‘íw×>eáÅ`ðó+‡ž.È£y)‘‡$×§B@>ÜgÒYÔ]o–¾'Ã2G©Ó®×媂žÿ,¤xdqè´ºtrðyáì\¿Æ®œ× ë^ùê5Þ8ðº´Bh›°þ ¿ŸÏ:¦š&Óÿ¿£³¢ŸS¨ÔͧÒ* #–RË! Øå~*£Åß|«NÐ赈Þ)ä™Äà åšÝ}lu¯cÓ%àÑÃkVH‹¼oQyJ6*n®ýjëòÌ“Ï,:Jêè<ÚÏÏZŒ0ä¶Ëçž.r‹=%ÍÚ½^˜èV)7(žDR`¦úMQDe‡gñ 3<›•TêH 3BEqÈ%‡Ìü³–Ò@þ'§ˆ®É¼ÔNÝLJ__psw‘&«Sª‘df0ªAâ@ Ì7rzœûçhÜ¥,™Õ}$#£Ó͈J¸O-ãÝj\ ®ÿ®ªÏLñHI¯†"%’WF ËCy ˆËœô²k4ó4‘r„-ž«Vyã“wÍ—œË‚VhÙ™¤Õ¹Tv†ð(qf÷ôö¼éë´“Ï¡mT¾`›Q,O±ZVb …²OÂþo®]~^ŸG•\ƾ"B³PêŠË}¹ÄøÔv™#,µF¸õ‹¼,°‰#ÚíÑ=A« {w¯Lͧ™Îƒóx[ÌQæ¬Hв9~k¨·Ãôpê$XdҺȴX±$¢©&׊éBùïëÇœœ?†Äãbù¾eí&2‚'©ô¯\òqÆí[£³ wü9,ÒLžtº¶Ü[ôß”¶>œ¦qá×M>¹tò4“)¤Œ§Â ð õÁ?|Ü5RIá:×Y„x—ðÀ[²ñ9=~J/ùäôí ž%çÊŠ®#,Him’9?qUC¦v”w׃=ìÙ$躉"™Z6•*u‰Ôò®lvïÎfÕÊìÑ£abµ¿âa¶ê‡¡çÛœÒX¢nG1D$ÌUŠš•³bÀ?PNP¢#ÓN<²ÎäW&óÅ~ù• }²Rj'Åa“Ë4PMð¸y¡Ïüß¿|½fHº P £\Ò– T• Aèyç-¯ÄUÞ¾L ïYºïÉZ>ƒ¾fñWÛ¦ÒÆ Fª’ÈÌ¿'q^=ÀÛ:ÅU'Ù›ÌÕ6ž7—2«ÛïÄ9Pû(éÉ fªG1HZM2EnÒ§[ùßn§¶KÇeWñ?(ÊʰÉ)ÜŠ c½Ï,MšŽÂuʘéäò!Ô\Wø…PÒѸ€?Ó,Œ££¢‘À¡UJ‘tÀȇ¼žtŠw ÿ˜}³#Ä#ŒÊ­+Hc#ƒºÏ$qÒ¬}3G‡0o ›i0º¬ª‡rÃßáï—Kå„óY·ìeÞ­@)$_îs’ù#qVŽcKª…täÏL#;ǘ~#ÔX÷ôï–ù.Š{jFh€ÊXÛ“ÐŽ{ûeqˆ#ŠVx^)Ô®àOÃëÇ<Ž9×:-fžm k*Hˆˆ–Q³KWȳe¿ß¶[¤X«Ñ^‘š-<ªX5²¬„v¡è_ß9þ<ò!ŽÄAP·Ð ^Ÿ®z†ŠXb ‹S± ‹ñ*ú}½³¯ðYeS Ò1€ÞØë•¥²oôŸL”r²É:£?˜ÓÈìÌâeU?MÀsÏ~9v˜ˆÊŽÉæÛ_B ßÏâ‹E Åü¹e·I6!‘€(ä›5ÛŽrÂ?#¨Õ)7q¤JH'‚ë_Aå^ç:Zr£êÌZ©¼Ý$AjB» yg¹ëí•Iª“U<±£"îKt$þ@e²£KªI£bÅæZ/îÄŽ Ùªm#ǨPŒWÍ‹t ÷¡Á?âQýœÊqèÖÇ‘˜>¤íÙø¥.à+ëÏ|ªHÙÚw0Û{’}«­|¾yªXfØ@OŽqµUx]Þ`^òß5ÎbE}ÎA#íáh… =:Õß4©y ßéÂÌ È̉bІ&èßµŽß˦¶V: 7›00“R(¡°µØ4 Cöõ̺] ‚‹Äèª9ÅýH#::LŸöK1HGQl¼8Ý\©éÖø9nOãкFx5¾zÐ?èj;JÙÓéf½ý1Ï'—elaºCdµÐäz¿;Êôpê!*QÓõ‹6T“ïúWÜXs?JU"‹Éó•0§ý$ònøë×[£>ãz$Ï4zø‚2ĤŠ?S|WÐvÜ.¸»ÉDë#ÆdÚŠ$y”ÀXÕâyŠy‰hö’ß¿¦(M'“íf6Wƒ@`zñöï“ܶtP4E(ÔicHj21µÌ*½xúsÓ‹ÍècGT`TÞÓ·¿õ?ÙÌzm2ŦcØŒ”y`-G6jûŸLqjZ„1 !ˆMíñP7dqïW×|²ÞÙb«HÕï­ž¤p$ƒm¹lY>†ïÛ‘ïVëfüdžýbHò=‘µnÀn@äß®d|¶u„ˆ²ÆàS\Ø$swüó—ã©B¹2ŸÊ΄*ëÓ¯CD‹õ\îÑKq:óÅÝsò¯¦v÷hò>;>áàÞ1‹é¼èa’8¬„2 Ð×\éÞ|—Â|{Q¢˜Jœ£ø[¨mêÓ=n—ñpŸá•MnøÖ¹Îðõ1kf'édž[Šó‡áþ85wÀ$Y#Ûþ™ _ަDCñ^;âúçUÉ­Ÿ“£¿x^xÍWãÔÉ-[—iN|ÿn3—ñ® ‰À>iÝkk[÷À̾xt_fGЯ çxg‹Câue‘ ÐÜñÓ¨öë›Vek£È5“VŽM4é–â¼WŠòØ%xï#бb‰Þ+ņ,›Çxa’ÀðÂðÈZ°¼WŠñÈx^ opZ"ÈÉŠ€6;È aŠð¼‰bðÇ‚ÐcÅ€8(ðÅx÷d±@1â¼W‚Ñ+Âò7Šò‰Þ‘¼W€Yx‰ã!ŒœüÝâš§‡ñdÀF®eœÚ•Ü9_÷9æc‹ËÕy+¨%ÿÆ8>dü¸ÏQâzrž5 ¬{Gð\ÈAE×Ó<«èUÚwW‰J1°Òmº>ÿëžÞRx¥~ô1¡ð¯PåüÈ€¤È=>°ðÄ0øiØ¡ Øx¯¥œÃ ÕD5fÃB¥•GµÛ5A¨ó×SµR ´‚ø¡|o¶eÚѧG[ÿ§ãËñ­s¦ÛHd69ÙçõK8”¶ðvhñݺWvÿê”êu¯+s6žuøymÇÔg2GYg„h i#å›z†àËÙG½r´×i Ñ¶‰ôæÔDmr‘tOßï˜ô06·Áà„93§Ô¨ _§8Á䥗C§ñ;ZI?È«Æ#ˆ,Ó îT¿Q\s_l‡„ÇRx¡‘Œcò…Æâø€ö÷ç.Ò¤šO†M;0h´lɻզ#ƒìW§kö9çrY;:¨ºÑO‹GøÇ…ë•aÓÉ3¢þ]šçq зJ4š]rÝ„ú¹<½¬Gè¡gÔsXðLÃY!Ú4»YT]µG¹ø¯¥Žç¹É¬L‘@³Fï!Ër†ãõ%€ïÓÛŵ ˜š©hŒ’.—Àæœ"ƒo,W%ƒU{ï}ÎaÖjR7ƒ@®¾rK³½]9Þòôý9¨Â§] ÑЪ©ÔÌR…-Åõê~O[ÎkaÔêµÚí`S+íUåO ýÓ;¨¥ìäÛlôrø1"3*¾TÎN ì+wÜæo –D›òéôÓêš`ì 3\G‚G5]®³3ã’IõËŽ&2Y”væÀù®~™£ÃKùš©¸YVBgàÿŽ;ƒÏË0âí§Ñ¸½hêjÉ>'¢m<²£•¢L¨ò ³@Xöïœy›I'‡A©CLÅ·ü!bXØíüîÆ×O<‘®¤éÃ@K: ÚÀ°6HºoaïëY9Õ"ÓG ‚ÄMåUðÐ7Ó“÷3ŸIS,¾Ç§ÔG‚®CãÉÓu ¤¼½ï.xZmJGäß—ñî+°‚#•ø® þªúf#¯Â2·§ˆY.Ú®·×ùgDLq;Hž`j€w-ynw®ß>¹ÖM)&e]ç‡Í‚#& »³lò‹#=ÏxP3Ò+è¦P\§ŸqòÉAzñ!#üä#hmáj7° õàŽùÃM\“xœ§NçhvqAú¯Ÿ¹Ç'ìF^A©¢Ù!e‘p5º…qg¯'ž_¯Ö,@›a=Õí@%~ê/¯nshuo)qkËàòZ¹ôàþùµpë#ƒ@‡PG$Ô›BŽ•\PZïœ$©SÑf‹T4zi–ThÒ2×gõ€@«?§Ó­^Uâ_™Õšx¤‹QI´{èúcâ ÉhA2H‘…h·©;ˆ$W¯éåþ¿|Ûù¹ ™£g­¡ X$¸®Â»âÒ’‘)µHãøVšVIãÜ®â4–9JŽ„Q x?©‡~F_¨Ë¹Ù‘JI â"å;‡¸$—Û‹B÷¨T!‰"DÛ/ø†Ñv@äßc’Ö²iôa¤Ú«.¦DctXK¹¿QãNM˜•¤cÒˆ·éB9gWˆ–*@öÐú‘ÌçCÌ›þÑßæ+£4|îBV¹õ?¯B~œý>Ý.²µ³¡%þ!à [kƒëÓÛ:ðJ$Õ´IwEa±˜‚Eû(=ý½3”çLéÛ'žU‹H¦&óAˆ; `ŽÀ¯¹â¸†Ï‡ê%ÏüQ¸“´­9F¯ót¿Û¾uõèæÔÃð(Ý J¨ .¯Ó·ZôÌsxzø~‘4é YD¡å,/zX«ƒÈ±éÏK!53s…"Z°éC AÜÔ@ʱ n"ìôºËNŒ—g¯zéòÈé·/—ñ´¦£’Vañ5øoåõÊu4s…Œ+Рɲy›zß˽Z[$ÚY]dš5]¥åÈù›½3ŸâM"G.ŸQ 6¹Ûê@"¹¯ŸÏ.]kîÚd%î‚ËÕúÑ=FjHΫLSY)‘Q‹m ´š<ýùê>×–-dz2©tyØâ…€V@E‚ª{nõÖýë-ÓÀd˜!dµÿ ?«§~ã-ñ§ °†XÃWÀýˆëG§ûœ»EùAP»v«Ÿ·íïVyÜiÑ-6âC5Ã;{gUe*”ÄK±¾sº´ªÑ±(à*í6¯?ßQšËï€P&cê=s è펎‡RaÛ20dr Ç_úg3_«’9سÿ>lñö%Õ‰tåc¿„VÓý>Ã9ešEalªdØPž¾ü<ëÆê8³Œ¶ìƒêV-K(;‹òÓ‹ç7†vD1h€x»ã×öÎLñyš†ÞHÚÖ[¡Ô}ËœÙááÖ)†<|7|Ÿ§Ð}sR®Î~NÞ“Å&Ñ:®ÙZÁã{WõÎÖñ<Ñ+´¸’6ƒòäÿ¶yX£ rw` &‡ßúçj]E¥nhÉ4z<ý¶œéÃ)’l÷þâG]§2¿ÃÓ᪫ÍñMªJ5žoÂäa ‘‚´ –뎟0sV‰¥Óꣅv•<;Y5ííÏò9ëRÑæqÞŽý⼎ì7fŒ¼/ Î;À¢Wx^Fý0X²Ñ+¼w‘ay,´H“†V,Px Er@bÇ’Åã#x^,Q+ì/%–‰^;Êɬa†,´Nëä7a»%Š'xnÈnÅ»¢Íد!»ìX-¼W•îÃvK›±n¬¨½ 92…»Å–‹÷`[ŒÍ6¥ åsð¨³ÎbÒxîVþZȪþ#Y-ÕTš%Գݦ˜üF꤯ëžsQ¦’µþJ1IAúlu'§­§\õ¯KøwC#2‘&骧_¯|ñ¾+9‹[«Ei,³­«ÇqõÏmtwšY5ý9þ¨H|[G#tbúóÆweÑþ[Wâÿ—¶e©#¾h?µó%£BáwzX7žçR¡ü/Q=^ý2Ç^I}Y›ð:˜|3ÆuDV,œæjdT]KV”M›ó\}óÐ~ Tÿô§»^]ñ]/Ó<èxÇ(¤9mª¼üÍôÌrvŠÿ*ý#·¤O3ðv’‘×S"±¾O|³¤!?hõøÆI@áH4£¯¥}{fÝ-7áÍ\Q+JÐÏç8 w9·C£E—Âç›PͪóJGtAC³@_êÌIÔ¿§Em:9x¬¯¡RÔF)šcˆ|Ž9ã§ËáKAáú‘ç$ÇWÁäséòǨÓétZ8c”Hed-#-eê¬ ÁïíœÿTŸG¬Ò°uC*‚IGÜhzÑÌqÆ2‹ŠT:Û=ýÞæì¶†6{äæ9?¶S¤|zwòãÿ÷†>Üu¿­åÞË ÂÛ9á¨nçØî½ò­>ÅüE9µHÎøì*óoò¯ÑkFý.®ôúvë$à”TîÜ¢?H6ze­ˆE­bݧQºMê­-5ß=y'žOÜg/ä@uO.¢9Äòq ߦµùŽ8÷ïž§ñ¿ˆhu©ÓÂLš©Ú5 :*©ùúîíŸ<ðˆä}°í•RÍ@|Kïìs¯/ÉÓ9ñMÕ´o1¬É+"™<Ô(T++^üÛ6ÈÒ4Qi¶•õ´ˆWh¢k§C|öÎW†«AâÚmï ¤‘i½—Š÷Waïä†&} ¹b¦bË\ð¡wß¿ÀÜzŒæé<QàÁ2ÄyêdeÜMuxɇƒæ©m;P(­Z…ز6õýò¹aòÿ é$D!d—užñ_×í3$>!¤ÁXåÓ야ÅÜÖ=û_lÇ#¦“èÔQÁšm6Ä]XÛ31zÚž{޾y¶1¥Yxm–Æ1ÐÕWì§"eõO'›a…(+ú&èús‹L-“˸¢’+ ƒ\1£ "Õ:3Léh£–H<]c(çÄ)_Ñ'ìYõÊtúf)à΋,OÀõ#nã5h#xôž%(e`“éÓnÒhùlÈ@7x§†*ªÈcnÚE«_ü¬ñé-¶j‘—Åô±V¢$ÛºŒ Ú~¦¿ôç*}+éöŸ)„eƒbä×Ï׃éÓ:-r~!ñ[®ò´g©!Zºü†eŽ šÂR!øŠdO ÙÓ¥æeiöHïÑG¨ü1âÛ$w+4ewñGËaÀôøó$>§Ó2PµÁi­u#¡çï?ÃèÑx7‹¡‘‘XÝ’lQŠÏ_\ˣȑ¡u;hòdƒ×Ó¥ðs—4$F+ÉLEáј4Ìí©Vˆ1³Å£ôûŸl›¬ÂxuÒF$r‚Ŷæþvûñ•jhA)…š(ÙC c¼Ì›ô®ŸË(‡PÂ$ó÷$[‚©R€â¾·ûæel±~ ôºX´òN]O–¢Au=ºsý޲iÔì!Sg‚^¼ñ@ý¾™ÉÑ0üÎ¥4dÂÉ¿m ?ü ýNM[W"…Ü"Œ³B{úqß9º½D’ëÙÓb”[‚wÿZÍ‘BÐÎ^„FvRP°æìß°4Ev$ò0òtÏ!–En¢¼ êztäŸìfsJεg W©…¼OO(O-‚!@î:ýûôÎæŠ/=÷¬‰SÌD1?ƒ|ÑúÙÁMçjkžÔ­ýHqYØÐé$ƒÂfš¦:ˆÊ¤ ÓgmÓb‡§\³üRLä»9Ó—Iæy‰$’,cø9ÛÐúõ#ïWw”Á¦=.Ò7_ž€—hF})“ã-ñºÖÓ§ì2qÄÍqÉ³ãøŠ‹jëFú×óο£VìÄ¢ge XÔód‹®+;ú(åhÝKX­Ÿ«÷ýó“¢j­yFÂþ£××:QË´l ÔGU7Ç÷y§F`„R¾šI>´èÇŠºõ<ævæG ’»X’›êG¿qyÑü»M…˜BŽü{ýs#4€*5ÔŸ§~sq«$­!`íðÚ~ÜÕzvýòÈ©áÜ ?ëЃ_3öï–A¢D¬ûåzG ó±ÅÔBüÈÃoCǧï×:5«9Ýš¢•c‚§ç׎?ëÀž~Œᕊ“ÒÑ‘‰¿nÙÃÓÇæiìåÁ¼Þ/¤Y<ÎTþ\ðž#ø›R|iäÑÊR5<)6=Ï9äâñ)·†3¹’F퇚Ì-X‚Ü󙜱7gÔt‹´ri#mCíœþ¥QÇÏ9^'øðFY4\o_aŸ¡f`®Q%xTr¤ýÎyÖº;Oò8¦øSpÚ¦=lvÏa£žY|™Š·åÐÚîÏ?£†eÒ!Ó™jˆÐ‹^ ý³¥á ,jc’žH„ˆT ÈüØrOEkLÕøV ðÏŠÎ5(H›àçœ<‘˨šQ•åDå·5’ÄP?r3¿øm?ýŒñR+qJ£õÎ>Ý;éÏ–+Ì1Fæ¨y {Yvåþ þ>£WáÞ.‘Æéü0Ågßéþ¹Öð¹"üχYvuÔSæ†Ú»_á¡òëœÿÂ3™_ÆACgJv¢ß5ìr½­‡Œx”£ Ç–à›b¯cœÜn¦ú^Lñj×S©óe U‰ƒ°ÀWÓ¥d4Ñ´:¸å†·k²È ÚÀqÇÓ‘ñ(å_‡ˆãÛð‚Üêàõ±Ðe •µ ¬vj£Ü@è*ó•˜[;¾Þv’$óT0›z»8^V4â²m*ê?ìÛšD`GN9þ¶aƒO2hü‚]ß`±:ñí÷Δ ~'Ѫ¿ÀñÉñC2Êœí•ãLÃ£Äæ„JFÙÌÙÁm,ún9ª= ‹E+H¦8îfJ™‡Ï‹ç÷Ë4‰(ÒëÜI Vgòƒ‰÷.ÑÁçŸ\ík¤”Kæ†M7ñ߉;¸æ«<Ò›LéèóQN™eJVO6gf‰8B¨¼÷“ÎnÔ‰´O(ÞV™m$'²ok®s&†UO€dŒ UX­ZÙ6}9®/¶cÖÍÑN”‚#yž¬Q¸úîý½³Òãm#ÑÓט´¾£Ž9wKŒ0Uæ¾³wÒÎsRWÒ P€ñÉ»/úvË|xHš™tê…„:H£Ü·É¤ãÿqɳF‹âÒLLGψ$¤Z‚莽€èzå’£7d%–gM9-WÝæ.ˆ6Ä9ïýs—¡ÔÈjZe3F†%Ew< ãÛ› Õ¾§IÙSΰ„!A³ÇH¿|‡„G£Âu)}DjGJCÓׯL‰ºy¾Œ £š=ošŒ]<’<|$ ô랄ȋ­XÈÓÈä·Ndaèk‡?|æx{ªÔƦb‚.C~K¨Ýïð±\Õ¨ŸR‹.¢AÒ"ïX³*3ßïòÍ-¥e ñg}?០–]&ËÒ’×ÏÌ6F%–8uA£B&’5S!5È~‡ßk~Ù2ðô`vþRê8ø½ôËgŠ´’G<’< 2‘ð‘´z#žŸÄSõÉ'³KE„Y’ô‡Êd,‚Àt³éÅsõfšPtútt¢®Ú°À×Ï[®>Y®L’¼>OšûHØÇ›àg°ö£ûdtȯè¡A z•?$ßùGnÙÃŽkhÔ¢–Ó.ÑHÐè¼J9TÇ'æ4òm_¥ïö=±¾½tÚÝ,AØïR<"Ÿ—cÇôÊÒC7‡x¬€äiÐ-šè”kï’×,šIAÚ‰gAu÷#ý3«}Y 5 ñMó–´b­ðŠýDÕÏûà²E¤&$ZЇÊ޹g‰ÀuZíSͶ2º‡%+ôòM} ýÆWE˜ï Ü)O_O_õÌN‰v|DÚ]d@ ü®¦[@Gé•­ò /¥óœm{ñ¸FGã‰|@€+Ó¡9³Â¦—M­Ñì”"¾‹Q»l–s_] f)õR¤¾Sª‰ ÙàýŒé(­ÍM¤Šž@MÈ<µý,OÜÐúæmTÞvƒR©!VüÄ DîÚ Id{Š|Ò“T#¥Øe.)Eïëýõ8´Ói`}ÑIæHâʨ«ù·?ߦqsÅÝ[j$uÔ:)Ylí›”ýù$}jÏ=;êž7wò„HmP€ñDzsÓ%y39/º·½ ‹¿n~•“ÒËy©!^> Ȫ==³Œ§jÒ;Gl»DSòë»;‰øaA@;àzÙºíœé?Å4ªBŸ¿Ï;Z…o)j}Õ{تëò¯Là¬ ÚØà]Ë‘wäXôôõÖ®‹¨ØÉØR¥6V¸õôñœ§á`û²Z6†M -T%#¯îÃéyŸQ£1Õ¦±H—j±6EÆ«¿5uÇ9L:×’u/*7 Z¸ÚÈ@ç¡*Ù´¬蘤ÁXHTYyºÏ'¦$é»4šqÑ’ Biu:x‰4èÎûRèü,GSVx÷ÊN§S/‡™uló& 2’A ïÉ«ôÈícâ(^1 e¤²äñôn3Bx—†ê¤Ö4Ê!ãòЃڀ$÷éß5t“HçVöÊ´LòM '}¨f7ÐØãŽ¼uÆÐÊÐÇæMê6¿ äP#äsî»G¦àÜUH<·[¾¾™³M4—ä³:Ø!…PÖýIõí›z0­ˆ4B¢Añ…®EÑéóƊ.By©äzç;LÛµ  5Ö‡ûåé!ŽQFÊóלè飵梲€µjjÅóÆVÒ$zø™w0,ĵŊþŸ¾E%¤]ÁŒÛ¬Ž(CöýÆKRH*KïÞ- P®yþ¿\Eì“è!ˆ…”3ù›aq¶ù 5^Û‡Ï3êWòÚ™ ’™C­p;-W¥ÒGØT5U×­äOùžV–iæ Ö‡snÿ˜‘žŸq5Gª:JbPTì t¸çíy¹%XáÓ»±;BÕíhÏO‘aœ8ÐòÖIïO§¥c‘$u¡e¢}ŽHrS¡Vu5>1ñ˜€Iø˜/¨»úÖ_£üL<ÍE<Äý*^ÇåYç§RjP’¼Yô&ÿ˜Ácw ¨€_ÂÌOLî¦èˉô? üW§“ˇRάÔWóÎŽ«Ç ˆIc¡Îíß–|ÎS¨ƒ[+õk2³mu8 þ=njқòsj™ôD§b cÜŽx¬áê?$šˆ¥‹á#ƒ}sÌnˆKð_SȯV’)¶]èª:î>œf ¾FžmPǨi€©ÙOÃ]h|ûŸÛ:: ÓM4²FÂ3ùdRÃÔ ={æäÍ>¬³À5 ?ø¨g #p¾·ÏLÁ¤†!¦ðà»ÞY5_°IP:×ÌúúçCÁtjß‚çŸpÞÒytG¯|ç&£I6·s¦áI›cßP/§úæ¹ÑŸ?ðt¯åücU©S.ÍFùÌžZmO‡jH¢mìAôt'ùæ¿Âþ[Æœ 0 n½=¿|¯E.žmLJâ8„€’µÍÇ]8õ̦©vŒ~7µ¼gWctpÎýTÐøô®ù«Ãü2wÚÈ¡åy#(•jæ»sýóuüU j¼{TÚ}+…y… =ïqã‹÷Íž¥•ahc OJfFnþ_~k8òʼ8"ßgzXµ>%¦Ók—Lc!œ¸<—Dµt²G‰Ä«ø‹E$BÛ”1p.‡^G úgcáÓi´?Á‘ÌRîóÞØÈŠ:Ñànè ç/Ršx|CE.éä`¥ ÿNFrŒÞJÏD’ðsô‹$š2ꕤc…iÝVkžýŽu¼FI'©Ó,o=ù¢=æAÀ<öß<ó–“iL!ü§.> ¡T|\òz.ºå>†y]š2ެ»dâϵØù×a‘¿–þÈ¿‘áñ_,mº1MË/5G¨ý~¹¦m< ¢Òèa™diµÏol*¨Û¿9æ’Ãrî?Äiœ¾K¿ç–çCâz)ÄÌÊš½”Eeýý¿žzšù&pÕ3?ˆÏ§Æõ±¨T0™/¯ÅN+ö¯Æ´6ˆ#3ÊĉÖ8ÊÕUëd^}þ¹Z9%ñ=r®æ‘›H&؆¾]>øÆ±|;\u@'ÁŒ2Ñê«É4n¬öíœy»‰"Ó[0y¼=M•!ݹ®†Þ§×¨ÉïøAµ(»ZMhÝ\ Ú@"«¸9¢]CêfÔMª›mÆé ŽExë–A¨‰< O¡æ96Ç3ÊO[y;vãïxåñ¶%ú3 úXµ®$¢Æ‘+zòhÑîvß¶Q®7£’5itèxÚáÛß5MO†ê€1¨Zä€ «õ7×3ëZH4l‚äÔ,a;¨ÛõÔZ­lÑâh§Â|9w¢ƒ¥‡ªÙc¸ÿ¯óÊuºË¦/ð™ ®U…€Ä<tn3OˆKð ™¹“É@¼Q¥`zýNs|J±iewóbÓ îyÌíÇêïŠ[4þ‘<³4¥YP8°IfûžGB>™«O ±h–66eÛ#Z"ìPìl‘òÎr@šwWiOš”~'ÚŽÛß®ucÌ4ÎR‚½‘è à‹õç<«OF²]!Cåé¼;P 0Œê^u (ùŒ»Ä¶N¥â†–1±²n–¨”+3èâ}VaSüIºY4Zc&Úmã`è ýÛ=ŠÑͺh·bÚQZ¬­/ÓŸQ—–rŽÑb“g;@“þRI§/¹Ì€vü6l{zgKĵk!uN®v¡S×…ªÓô‹þîÝ^¡áÖéÔÑA˜{ÃÖú^q¼JHíFŒÈÎý·ÈaÅŸ@kíœã7”ZФ-réõG©ÕJ˜• Åt®ã×+–w |M,nÓ5žZÏËÓ‡¨3Œ+pz·ÆE~æY6›ÊðXè I jÜ<”…€yuºÎéÖŽOerE<£K$gpdV+Øñò=Ë3nšÄ,²NÉü#w¿QñÞùÊɪ—òR9 ÙàWSÇa(˜iôb¾3bìÔ4¶>TÓ8NOh°Fÿ¯XÑèØ»9Ü¡è_>÷^ fˆu˜4rF7FvUP‚=Sÿ\Š|#Lñ•U†uQE¿½söÎnµfy@³(. ry0íÔW•+3¢•3b‚<#ÎF]…Œnv[&Öi ês‡ÌðêÌüJ•fÏ'ãÏï4´ˆc åºñõÌÚïËOªËÍm"ó¹6•Ú9éÇøOßë”j&GÔÆ¼Ed6‡¸U<ÿ>œg*ÉÔ9®@“¯{BμpÒ£œågJ=THY<ß,±µQu}o¿÷ë’MB“µØ•a¹hѾri|—‚1|¥‘Èú·FšGA»‚Ô1;‡½þÝs2®Ì¦Ö‘¦6.ªc@ZÈ$€¿|×åͼ¡P+Àž¿ß®cÓ©ŽM‚nC/^œçD†ò‘Ûp±ÏîÎ2³¼eàšÉ ‡l’±‚ô?L“2küÀ…z|¾ùŸÊi¬n°EXØæÓ¦2¤o¸††ó¢HÄ“lÁ;J$Ûɸµ,åp3È;ã¿éâ¾ùd²7Ë`ßOïß.ÓG²™LorícêGúgN»92ä;b6ÅXõ'%aØ ìÝl  Y±c+ž2$E@=Màsûæb¶.‰M3Û `À_R-OóÌÒÊð&Òz|?|аK#–  DsÏ¿—ï—¶šITsUbýó´gŽŒµg>Dò /ÌfÅDeEј£m$qÁ¬êþBj(zóÓ%7…ˆ’JGQë›÷Láöqc™Ø’UGO‹++1œS ùÖM Èzjï›ôÞóYf*{  qï!‡„pµJdÙå¸R:Ø9™tŽWâ“`s¾Ú=Dr”hø"Ôƒ`å«ä)X㪠 ?ë•r$»#^N:ƒE½ t÷ÍHÓª‘«ž¹Ý‡ÁæZév~™ÅÔjcÔÀ讪Pdr: z$º¶v®9éþ¹1ug;5Ï­¨n=;o^T÷/Y[AV€U‘Ë9Ï<3´E .×¾J Px‚¬‡pQÃ-×Ó6¢êѨÛèê aš¨“Öù£í•y‹•F2_ê ÆsžI#Þ&pžþ¸¯M*ÙÔ·NlñxqÍçXë J¦ [ýr¤“W%†‰ž»›,ÒiÝ–Ì€íiØ^QçŽTu68bxœ8¯¨šùlZɦÈDíEÎÞjÊäži#_)aè2­}ë+ŸÍ€yQ¡”¿.ÕJ>£4¡DeÊlÔHÃo1¡ûf=KhB5Yvì?|ÂcÕI½ëø}ÆëÈG³»¥BôΊ y&Køjf_Ö‘¹•ðùZùç/Ä!m?Šoð™üâBÈC/["ˆã5x#:xž«ÍŒƒ2€9ëÏS–ø”HÚýEÇ·ËfTøNÐz÷÷í˜n•#×ôÌzh“LYÖH¦IÛb)4ÂÍØµå^!$“M«Ø¨±¶,qÆûÂíÚ~ü}ï,˜³yqB¡’îF-e¿o‡ä0×j£Î‰ôû¼¸¤D[%„ÿ?¶H^VY%GCÁ˜À­2‡3®yýšÍB5;‘Ù•·)ÿ›ß¿×=7i$o© Ó‚j²m¸ñ@g4r¶¦5àmoÐ9®kׯ@=ó|Žˆ•ÌêÀëŒE§Œï‰Ú@vj¬Ñ㌫O<:}‰ Yµ1G %·ã5Fý‰:Ë`Ñys¶¦DòX"~*õýŽQ:nðïFþ³m> y÷ IΊ gYŒ*I @ŽÀט#,Цƒ »TXûÚMª7’³¨#è´îÇ|®®¦@ýqoCÙ†Až’1Vá™ß’kÅ×R3‘¡Ôjf%“PYã–N/ê§ï})PÏ$€ü UÈôä—¾c’8É2[g:};¾¥í+RVÍϰÓ8ÞAÔV”}ÀýÎwKùú*íg_9’z– ¼ÿ~¹›S"oXaÑ•‘]Hv'rò?Ó¾za&©’ELí$iSh.µÒÿ—l×¥ PÎ3¡Û}Aûæ` k<¯&òÂÒE²}èõÉ(S¨V]ŹvÐÿ¹ÌMkF ÿGOh`ªª7¨ýKÐð+û÷ƬñÆ UH0=}±À@ó„vãŸåycG;£Ÿ/üG’:ûçéÓ:k´H2Æ3[¹ç)O‘& .ëG\Ó¦Ò¹ø¥a_åïŠMf@&ˆ“Ð:õÏB¯&[h»|sšš õ¾¸›DÒ0ëY«I§ƒHDµî›¾¹®5…ylÂÿûdð¶fèÃû2Å£`Œ ø*½ý³@Ó7ê°Â¨ñ–,ìαF¿Ä~dý0]ò1)úý¿lËfÐ+(è qG.ø1T=}h‡À(Ü,5Èë™5²³ VaÓ­sï… 7°Ž¾œxf¤k ¾ }ó1n¦Ñ¬)àUP÷öÌžúdfb¼Øeþ˜k Ç+m{ Ö¬Oo|Ò‹É«!Ù:¸ÙZxnƒâª¡ŸM¼J7"’vO=Ç×ÛÓ<òê6›•ìÀ\ÕŒy*_`ó êKEÙÛiµŽ²F,Wé&«ï˜u‰ùf­ˆC÷^ÞÙŠO.‚5@6¡Ì³ê§ÔÆKNµÓp•ì¨í¯ˆ¤™JüTo(ŸPîWs«Zþƒœß/òÛ&°ÄóWˆÊHܪßSšÁ^…³®ž2ºtòŠQ^VùͰø·ŸÏ,ƒÜÖyiŠš3œpx›Â…yÛÛ3?O­vJ;æP†ÙÀ殸ù‰ñPë"±Ü ytH>•\Œä d“-¼íñŸ‰Ž{¾Ói¤ó${>½ñ…-—£Ôé]âÖ¹‰¾- ’H4o¥fâbmV­öÈÄ9² I$“÷Íz¹¤o.ËÀ<ÿ|ç'T0²¬Š¦YZþÏ9šÕê ṵħÍ` M´®=X‹V5øÙ>a©ª¾?¾1ˆÝPHërH9é×é–Ëm;$Jwm¦±Ï­Wsˆ¤šdjÑØðÝGåÿiƒTI]Ï휕e]L3@u Ó¯úLëM£}7á„›Îi„Ò ,ö¬å Dòœá‰*¯`µúúf9žÓ5M],i¸¥?)ú½z É⑚—N¨Ë4ÍI$5U}3£ ù© ºmÚDt@«=@ë×¹ÎwŒ$¦=æVx]꼞€óõÎ<7lÜú(ð©//Q0VISÉ@±Ð5ß§^œûç§š]1Ñn{™#ÝÃ)¦øx¿^§¿ïž3ÃÄ‹o,"Š$äòlõã?®zÐúV‚ur"¤>Qv›¯êúLåÍü–t‹N:2Bñéã‹W ¬‘VÛ€ç‚+Ž£-}^†]4Z}R †U? !$ˆç­¾•ês(ˆ>–(vmTClÆ÷7?Ûúf £´}C8ß †Û_WÔœÚâMÙœ«Cñ-TšŸ Aç—q]¼Ø¾ÝŒØ³¾– ó$Z…Cü ìþûå]QòTÈÙ_ðßûe’êÈ¡2Õò=o¶nkXÑŒ÷ezy ~"ßýXÙí¾ÿ‘Í:/õ‡Œ«ø„ÁÿùkÒþ¿Ë9 4óɦm¢9šØW_Ó7ÎÍĈÐ"Ô3²–ŒU•ykçLI<Š^l×3éK„hÉ®[Üu'§®h—u^—×ŒŽ’2f<€=O9/BŒ¿•i`˜ì`R&<0 ž•ü³^§LHá×céÕdZ5ððoùÞm‹àVÔÙ\Öu†R.”‚=G¦ayFÿ.ácÚ´´BÕ|å 3Åç†pYÑXŠêîÿù†]4†VU-Ø/Up?l¢a±š¸Ø(¯ ÿLB) ¬¯7‚iâªU•Ízë’”3,›Ô™’X5’{ŸAÏòʴ͵b&ÀWw»ç55¶ç)ú×óÎ’V̦gÒ,°é°wZfäð IéšB¸c$áö¢ âŽð~¼ôÏCàË¡Ô~ñ=<±BÓE“â_‰M€9ë]xÎ$ò~[H…ë‡$—DŽ8Ó.W¢?ÿÁtªÉµdyŠŠ ò39Ѫ¾¦mC:BDŽ¢ÎÁÉã¿QóË"$FB5Ø_ûV)ˆi¢E&æÑ>¼úæ_åbú1ë¤fÕK $¦R‡p?ï„W_No§¦(!DÕHÒG DhW"¸‹ô¾FiXD“#•@[ª}óWåV’Å‚Ãr‘Ç©ÂjˆŒK'‡i‡4“9ÿŸç×*ÔC"Êìw9y`(°Î”1—O#kHË ùþ™`q´¤‹[~jù͹2œ­…`•,–/u»äkŒÆÚG'(yêlóÛö?lôcK2«6ã·kØ#žz|²¸¼9K1J ks9 ÜT°¯¶úñÜó—êäۢܲ2³Éãëßþ¹¦}È7ߟžPÚI$!8SǦFfJÈøŸ4ð/'ü%½¶Ôzêr‡Ô»È«´n,À²ßOéÔæÍ,:í.»O.‚0ºˆäVp5×§òÊeÒ̲˜Z=Œ¤‚­Á¾ÿ\Ö1¥ ´èÄUN µ›Úƒ`wþ™»ó&YQßc;#¡,ÃÂŽÖ&ÇÃ×Ó5iü%6o4oÚ8»íûwæͤ`‚D„šï×ë„ò³€Ê+qª=óKiX¨S_ #.*®÷¥Å s7WÅþ£Èô¬¸.Ó»mÚ=üš$Ð4(hn£ÁrcC$ÁUÎÝ žG0©;0s#dŽÂ5bô«=sLË&‡WªÓ»d™–F÷•5|ü³Wý—ïY à©öíéšµ~ S4m¹ÂŠ»å½ï-ÇŽ$rHÒ*) ¶íº_ þ§*ÔC"éØ@?‰¸AÐÙþYÛ‹GîÛC¥ »ò°–`Šï2¥Ñm}žZ=F¤óh¸º7?±7ìNtµ{[sF•²= /NzŸ¥××;CE¤Ú>V‹_C‡ä¡¯ød–fM6š*£¦U4²Pº¸Á€W#s›¾´ þã2ù“À •”q#ŸÔhÝûg¬Ãt‚/ H2·{æÿåM Ò…ª ÷ÑÓ­ísQ³í´D¯UßUúP¿AæD€$kŽ}xý³¬tð;£+)*nëöÆÑè”4z‹#ü¢ÁþúâQ”ºE´S·M*7>áÀ° óûõzB|…‘-ÕË"dØŸç­?x‹bhü Š…vÊÞ`cb‰ÝéÏß-_ÅÓéå•ô±Á¦ó‚± *ú}lè½7“3øŒÄÓ¡g¿ñ<„…ÿÓY%בjd*]ã*7Žú¯ŸˆgÍÄ»mŒ‘S8˜™"-ªyYÏ÷ôÈÇÿhNvAe¡Sý-OŠ4lÆaõBŸÛ8ZßÅ3Ê<¸å’OF”ÝfãÍøÄÎTta¦s?Ê`üNÒ¬{Ú].¬ûD£úÍøÄÝþ¦Ø'™‰IôŽ=>™Æñ-V³U©Ý«•¦‘.2Äð(‘CÛ:º|iªÄºÙ:¹*‡°Ìâ}FðlìÊ‹í_>˜#;^öëÐg5ôZ/yU!å ?Â8¬¨NYWoˆõ¡yc@D{¬O÷ý2ãEª †Øµ±ù±uÕCËZ^äs™GÀ ñG5õ;dq!¦)¶#šå—á>™jêãM?–PݺÅg=É ´›¡Ác“ïÛ3…„t[^F˜¨m§ ¹ŽyD¯¸qëÏ|‹Š#}š² T’kß,xÒ.%–6|,}²°7·ÄÄÖ4,üÛè0hlòÕ뛤‹Bi á­{Œƒ4‡õÝå–Š+¯×"òB±JÅ"*–.¿|N»ºŒ9¿|–÷añtÍ‘ZFªFðH䌰ÌBíSô12É}rP¡T¤–rNI·°“GšÆw*ûd–#[Ö(ËF¤ÓÀt”xz¾¹‚HÒÆÒJåÜôûYÊJד˜Qiö*„$BßNøw` l†ÝƇ1#"•Rs[ðZ=¹Vv–ueMĆ/¾e‡F“sR©ª¾YÐÚÒG˜Á@|Z2 XÅ [䟮x©ä‘Ä=AqúA4~y¡ÿîþ4âÚJ ;Ñ7ÏóÉÄžTÖÃá¸éYÑTŽ-(…Q‰k¿¡È¦¬vŒñAoe`*ïü^€zõéÆbÓé¤ó¢•ö²’X´’hŸ_ké›5…¤Ô,`ÖmÁ rIõùe Úƒ$š”T|?Ãíëèg‘Û «5Í^hx ‹ôÒ‹nÖIÈN%’*ÛèÙMÃ¥‹ë”#kÝœI)4)³|i¼ÔºØ'wÞ¸÷ôÎi¸ÄÕÙÉðØ‘˜¯HÎß ’y9ÛY¡K#7êóöZè?l«U +,D‰»vå@8ç*Ó SHÛÑh0®„ž{ß×3Лɛɥ¡AªŒ¬t ÛzÖà~LzøF¯M-)ý`«tAèk4Á¦„Ù•Xž Ð4>›‡íŠEh­`A-aoçòΫFÌPÀ»‚²;2?_¯9°GæD²y $€'çÞ³j…Š%Ž[…CéÔŸï×.]R ¿Àx£Y2m슎é™gó9»ª/ôÍ 2*ÕÒÐb|è–Jj«^A®žÙ^¥›aæÕ½¿íšÉ HÈb1œí"Å~ã*$Å2…F§dgU 9e‘M€Èx9 Wx#{ш±Œµöbd•ŽÁ½}qþ]¾"Xy³W›QЍµÜû°ÇçF?TD–$Pì}s9"9# 1!£6zYþÎ §;€;I€Ç îsS¦B®$rA#ùc‚4†#a9®OQ^—ÓÞ°æ¨)£:YžJ;î° ¶n:Ptª"J#—ÝxÆó;íR7ÉÓPè›FÑóœžâ'¹$^ «›Tú½3ÈÑG{ã°iI,yëȽòzí$/ ‡–O§°ºúç¥ð''GâVCÊÊz>þ™çftœÛjìFvääJ1f=ÄŒæ…J;}:÷£–4jf^T<†=W¯l8€ W¡5†ÅWµ"ûsyÁò"¾Rø#…†Ñ¶É kŸôÇJZ‚ 4ýò n¥®°ÀZ«–g7àϼ[Äq Ü“|zàÅiRpHd•‚§ÄI­£œ°è§Œ\¿Â¾¦O„©¬ÒÍô‡ºüÄ븖 Oå‘»` §†»ã)§N&ÖiÔ„8oþ7•ôhêYÇo- ÿå·5írËÀ|’/Þ·VnÖ,úbix­£­õ¬Î|GDœ,3ËîX'ÿÝ‘oÿ·¡ÿö1oåYµé¹Y2f½,…50¿zû—x¤dx¶²45Z‡F#9GÅgÿíÅ$ÝÿÊò/âšé±Ô°bl”I?@3ªôÒÆ›%³jC!s´=ÿËÉËÒ’Ø«¥wuÛíß8¯«ÔJ?‹©™ÇüÎNS`s•zO¶-ý±nm=Wÿ‘oùÞTH‹ÿï* V?Ó8åǦiÓ6“©iÁ4©<}³KÒ@YÐÝ‚ ”ÿáAýHÊλNÊß6 þ¹Wç4ÿÂðæÿÈœ·ÿ¹YñY:G–!è°+õ`Oï¦ã^\Úôí§ùÞÿ)æÆRÞ9àún4^ÒMT¬÷óQC<éäO\ Guÿø˜tÏ‘OøtЬ¸ûç3SâZQ-©ÕM9ÿù’þy˜›8Éf‘?< ³WÓŒÝl¨ö¼ãê#’EsÁ·롞…‰•H½ÝNy=NU£2³Sp§€®NMQ™„ŒM±³C¹ÄÜÜúäY&.v°Úxç·Ó>{”šÆÎBv²:ãŒçjæ¨mÌ?¤YÎŒÂ8†æ•@<™¥Dž0éñÈ£Î#*{ ò·ši 39þ,£iÙ힇Q¡ÞލŒ¬2þWôêÐè j¯1šÀ5FÇ#¾{øù´ˆ•ž}­J€Zë¡õÊÊíøº“’g(ÖêO·¦ë–SÌC‘ÑEgd±@ƒÏl¨-v°1‚ V½²[¨|×è¿£_„LšÒO+ˆãW¢OÆKÅtòÇ©}G–ß–žY 2…ÀnÞ½Fmü5¥ÓëuÄj8EÜéwžƒñ¬[¿xK"áê'^= ƦuJà.Ý ¨ôç×þù3Õø¬ž¸0äVs´[[n¹îp˜þÆvžîr-1RȆ׾L­’É·¡ã )‰ò2!f $èÄ–éò¼ltÒi(˜7–§ôúßÛ3Ìð+TfÀ<:ä<¦—’T'®Z4ºuÓw|C¶E§v,ÍlEŒœhKðh`dUè+HhàçDm6&`¤åeÙ¸k-x>¹1÷¾2¶»elΩuWd÷Í ÈÝycÃåŠaðžœc`ò&Õ"Å 9Œþ‰‘Ré‹–$„¯Übm Ðk?<·O¢yCXŠôÊ¥€G!+~×™R·DOañ5ÒõèNGq©~Y2F%YÇß OÁdçK~M&Z²Z=/û¬Œ“Û–&ïžN'S°SÓ*ñ¯a„“24„>Y[‡^½òÔeDj{_Q _â³ëË ˆÝÅdè!6¼ú‘¥­¤ÙŽÛÃï‹}à”(ÅañU7÷ÍM¢"Zec\+o÷ôÇ,dU¤-`pz®!– Mžh÷Ï™›=y~ŠÏ‡Æ ň>§Ÿ®FçMIØÉöéëy&$šµZg>Š 9ÏoÄÑò´úXÇ^! ~íg)—Æ|JUÚÚÉöŸð‡!~Ý3¢ôkËþÏWáâ ‹P¦©²Y˜+9ÙÏ>lÇi³f†[†½% "Öó¦¾LŽŽºø®™Ã1U³ú…g;\É©Œ2 î@˜Æ"0<NKNe™„H ú(ôÎq‚‹É¶Q<ð¤ŸLµ’H‡ ‹³©†_ʬ±°SÈ5ÖóŸ<Άúq.oERÙJÙ õ>ç%3%hŽÙD’¼ŸZêk($ž¤“)¶km—A™¿Ã¼ļUöè´’Ì:U¥2x ݪ°Ï`¿áðôY|Å´ú%"ü¤;ä#åþ—‘oü5àö<3Â;(àO­k=¿ì0K8>à¾%↴Z)f *ü#æz ³Äë¼$ßD?ÿÏFý Þ]â?|kÄ–ufz´ãËP=8äýNpZBÆÉä塲};äKÑÈŽøÀ%fð!×5‚“³‡#" šïŽùÊ _ËvDž:älõÁ 7záß+&±‹ê0 Ž:ãr`÷À\,dKzuÄl õ8ò"Ͼ>çf±ƒ‘¼')AøFe‡ñ6›¡'þ®?®}”‡>§• $F!•vϺh5‰¯ÑA«Ž¶ÌÀq÷þY‡Ù¤^âЊäfÐÇâž>Ž^‹@×CØý ÞNÚ7~Ùl0ˆ|KÄ<>}¶],èD‘žk¡ˆöÌ`…B+âÏ®þ$ðñ­&èHMZ½GùO·òÏ“êtÒé§xfB’!*ÊÝAÍ]™(¾:áxŠ=pFD+›¶½²”—Ëöë‘!ÿÍû`æœý°ÏL =°*oõF:­äxËvàä Ìß|<»5ñ÷8-“ãý²§K_ÓÓÓ–½®T„^J)TRO¤,a“êÙ8¼OT¯ÿxZAÈy=‹þUÀÆ…HûgðB]£8¦u`ñ8eø”î`8éÓ$';Ø=-~£éœ&Óü[í?òñ•Ïùƽ­Ï®y¥èþˆâz-ó4 «.Õ®]ºY*Øn®Fy4Öëôì€Ä6òçWOâÉ*˜ÙŠ·¡öÎ3ôòƒ8 Yˆ›š¯š´RÆÚmLOúeE7ùƒ)¯Øç5¼N™ ”¼ßñ]©|š¬Šs„\WFZ/m,QÈ@zcUBÁÌŸY8 úHøÎ¬!Ó¬ˆÊþǘ—‹“þÅöã8¦ÓÐÄá鎻F QÖÛ½ª¾øöK4òHaS¸|E½ÿ×;x2”j<}r¶tŒ>Ô«uë—;vÌÑæeðÝ@”ð§BFs50,NImĪÏnÝT¤q|VW.‹DåƒÆ¼‹ºëPÓØ<¼IZ;•Û[v×?<ÆvFwlsÒMáAˆòËú€=3™/‡µ³GYè|ëX±eéµ-§c @Í7_¦s¤‹RÒ#l=ïŒìV·É X èW¦Pú@]UÉ‘¬Y»ã:C’®Ê¥G/ÈU£lÞã¾X4ãyäØŒèÇUu•~$ 2™åDcÕËe÷[t…Ù +¤FàucÎi“S¦09‹H ©^8ôÎpŠFrà‘Y_‘¸’Ö`pâ›»,´ÎNËúœZ]IѶäj?æ™Ú!Ûïd*—Û:¨ª¢èéÉ㊫¶sÞo4Ûr}pTF@OçÆ&Uè¼×|B‹Ò›¹€QÚ²,@æºöÆÕ·p+²x7›Ò< ‹W×'¹A®ÙEÝŒÒiš@¶EŽÙ3Âõ¼ˆp¢ëŒ7×”§Ô Iåÿ ÓÇÀ…¿–o âAåÓ»NëýÎqµ‰ü_P}|Àz#le¡œ¹52HåÙ˜õ$Ù9å^š>M⼞ªmŠ8ÊÏã:XÏo(4¤é”_‚Á_ÇÖjEXEŒçü³Ì$rxÈÙ¼ê¸`ºE¤G'hÝü"ÿõäi?ní”7âQP´Ëé*•õÿ|áî5çEŠnÔx®³Wÿï©¥öw-üó7šÄõÊÙj±eI &Xœ7Ç9KòÇÎc\>˜Â×Ë×(#´ãÛ’éßÞ@ ä±,0 Uà=27…›Ê ¸ò6oŒÝáú­šMúÍ j뢶/Ö…þøZ]£]0‡M³Hz,jXý†zïÿéçˆKâSàE¶ò€þCêq'ÿPåÒhÌá:= ìVÈ×µŸžyñÝ‹>ýn®Iy° ¥ 8%ŵ‚ÿq OâÚ¥êÏÊ_ÿ°9Ëñ/Çþ-¬C™“E§‚@(×þ.¿jÏÒzâË-$ 2êd•ÙÝÙݳ1²OÏ*Ü[¯˜_9 Jðã#~¸¼å…üò]²áwÆHÉ^V8¼ vÀ&¸nùd/å7X–+¦.(r°yë[ß•×8ïœ<õïlwÔd黯µÛ¶"yã×Y[x‚µ“ë€X ÷U÷ÈŽ/kÊ ß¶HqÈ9Y5òÉ¡X!eœ÷ÿý?ñÊÝá3=n;à'׺ý¹ûúçÏÆiÒj$Òjxœ¬‘°e`z‘¢£ï7ýúb$/ƒ×9^ãQø×†G©Z†dájëòþûgQº3%%úy¾yïÄŸ† ñ¸Ì±ÔZÅ öocþ¹Ý Dx9>ð™³áÚÝ4ÚCéµ4r¡¦S™ œû'þÒxæ›l€$ê?‡(Ÿ¨Ï”x§…êü#XÚ}R#•aч¨ÍYŸé‡uaûbïÆ-Ä­e›lpGD‡êú`¬ù$r óV2²kç€=0ûbéÆHü²5Cç¡»Ž˜÷P8ªÆF¹öÀ& Âúe|ƒŽøãRnªye—JÁ}²ýÞ¸të‡9ü66kÜÿ+Ë ¤ žÛå›:Œ6ñ˜”"Õ0Òñ_Ë:ˆÑ¶ .S'O!±Ât=ï,(ùCé‘Ä üó‹ôÐðLNŽ›S¡.[âÊßé9rêO˜ÊÊôöÎhÄRù¨çÒ‰±–.¦Hå½¢CTO¶qŸ¥khÎy¤ ¥*Ãñd^IT¹ ß#ès”Þ,‘G¨ Þj_ɺ9?W Vy¥Å$¶Œ4΄OåªÈ¬iMŽ;ä›ËœëµB£¡Ê´ó)…‹“°u[à{ŒŸœª€¢}³“‹B´gŸI ܤ¤z¤xnB°zr8㡼Ô(LZ57ëèpb%“L ›ã ´»%#•$Píþ*†mÖ¤Ž0¦Ôï;@$]Lé6– —k(6l{œZ‰iÚSš=Ǧ©Z#G,xH‚Óß9z x‰*7*šÏ^YM ´ôÈÏ¢EO¿W®j<­l´|ÿV#Rv+_¾eYö«&Û×=æ£Ácš"ßPîqOáécœ±*zçž®?Q¶E£Ì”i/Û&±@o®zeð"÷²?ý=ò à ̧x*xÚxa}ø‹<ÙV4Œ„&ø9×ÔøpÓv$„ êDƒhë•óF¬¶r¼¦ Þ=ò>@CŽ3¯©ÑùQ.ÒIïÇLÅ6Ö mø€íš.]2¢b¾¹IøMש4ñÉRÛXr8È-¯7ôÍä‘m—®+çÓ;Ôì0Ä{Œ˜¦UdŒ—z6L¡xÁ=ùéhÖ7×lt=qßL _ ¼à÷ÃvD’Nó€LtëX_4@ñŒ€Kƒ®CqÉðÕÀ, ðñ÷÷Ï"O˜ý s€@2óûd˜Pø²«àúŒ@cV8Û+Ci’-Û•šÆ:q•Žù+â²½¦ÕGªÓ$ðHä`zûgÁEƒYë~%>¨=K¤”×?ý¶õÞ¹–Š©u»Ü{dUöoŸÏå‰ÂAo¯8¾ÿ_C…¿#òÌ^'ázOÒ>®0Êy ÝO¨=²àä5uɆ Èé€ÕŸ#üAø_Wàs£6Ÿ†U>~‡8$ױϼÉME*+ÆÂа°F|ûñàw‹v«Â•ž>­R?ðúüºæ“3Ñá¬Ð»¿ß$à†¦àŽ™Ü gÐÈŸNØwõÁPú1’F¹é€‡|Wl/"ƈÇxÅbxïŒõöÃ!ED¸‹çöÉ{bÛÇÚð²997ì0¾*ð¶.½q_L€f¯¦"ƒÒ±]c/~ß,§„0ÌÇLãô5su›A¿ÕXÚ»djÊ`vÖ"lFù³×å”®¯W 'éÓ*ç*xÖ‡ÅÚ3H³Oãf°$­ïšcñxœ›–=Žq¦Ò#øfþy´Œ¬v_‘Î2ô±}ØFËV$5íÜeÐê‰'’è8¾ùãU«…H÷ÍZOò•Œ›ÃôjäóÏÒIteÄõ© -Áâï“–ùßÁ×<˜ñ´)å–t#.Ñø™þá±ïÎyß ×h4zy<Ø¥!”Ž,‚:å>gæ$¡`ŽÃ¾gÑë®0l‡œDÐâ&ð¡Y@qx]áÓ˜ï…ߦLBks0U÷È–PH[ùœì5g§©Èî ǮBÇ˱˜“ɼ­žŽ" ÙÈ‹LìÓÆ!¹‡|´(¼b®»`[ßcg%^•®PCžØèxÏŒ:ž±Æ:É\×\CN1Y³€êlc|.ëßÇl‰ë‚ãŒ;ÐàbË‘À%a‡>¸‰£X«åXëcŸ|‚/œ].Ö*Î#о"AuõÀóôÀôÀ 5’ ôÈs\Œ¯öÀ$EÆqWÃÉÇÛŽØ&Åœ©‡_~rÊø~Yë×4lwË=ɼ¥{ýë&:VR“³XØuÅÓAßäsÁÃwNp"Íœ;^ OuŽrHo¦VMœaÈŒÞþüR!Ùáºé?†MC#„úoåŸA$“UÉ?|ø*O=ˆÏ þüSçðßq¸ †V5òŸè~™–Š{vˆûäl£P7wY yÛß×G§Ô„“pã¯qéu §iC`ð;ûdÆj±xñá='Œ©ž" Öœÿþ¹ó?ðÍ_†j :¨J8è{7È÷ϵÞg×h4ž#¦m>®’3ëÔ{ƒÛ*fZú>éü²'þ™êÿ~ Õxqiô{§Òõ5ú“æ;üÆy_Ò93TDG’/@¬:œ+©ã‡`óßÝÛ"oâCvHÇ|dAÀö8&Î;9ã#|vûb$0 ¾0>Ø¡Mˆü#ž¸Ö€ÈÑÝqkÂúd(¯üØÆHÖ"8ã‘€*£xWzûaÛ°ÅÓuqÛON/"MŽX»À£‘¡ùã$„áÇ|€©ôèy¯®f“B¤ð(žù´’½ì`¼Ž£(9m Ÿl©´²Gf0Ýz^v:‚FVÊ;Þe¤ûeÒø¦§H…N®‡ªœÕ¥ñxU‰´oØ7 äÿƒí”˜Uº/Olç.H”SøêÉ|€Ã.Òx—™%…ކó.‹}šç(ü¤ÈÖãÓ9?KÑ–búèâQu¼ö®¸Æ¥$Mñõž.6’&÷ÐíݳG5©v¿Å`ílá?J×DÅž 3iüÐÁˆê—Îf$BÞß|æÃâ‘ie¤Ô¸­õúyŸx`§ÔæøkT\g$`F)¤y>'ûŒ®-l- àŸB@¿¯LÐ'Ó),Íê–Žspfiœ¾|Dâ¼/×÷ϲv7‹Ãç€øºŒc® 9ÄoÖðçRMIôÁæ1uøÃÚ²%¾Ù 2lâ-Å †ñØadžp wnP8¡‹€1(ÿ¦2hW|ôþ¸Ã9È^H×Ó)Åsö‰ÿlCÛÝ0Qõ¬ ®1ô|wXG ÅfÍâ>¼`£Ý~ÃÆDc<û`€I³GôïíÐ䮈À ßÔdºÊ»œ›jð ì´ÝØà94N%¡×¶&øEúàé_å’¾9®™ {zà “c—Ϧ€zd&¹ïŒúwôÀ$ZÀ±õ¬‚žzÞÐÀ,»ç#·=²öôí’øVìàÞ|V2/§\ˆxÀXùr2@ÖGuÇ»›û`_·_'¶FÇPoyÊBWŒ¹:}qÝ–(¤¯Û²*ùÈsCŸ¦0ÜžÞøªEyÉ ‚ ß(æ²[ÈFôoŸ‹ñ§‡ëå¹:G#ˆzëèsÙî,þüçÂB´EçÐ þ,üÎ݈¸ð#”ðÐõÈÑOpXzÅe%BÊ:õ×'ºÂŽÿ×<›¢Fd‚Y,sÈ=H}ÆU@òSÞúK#-ñÈŒEqÏ9åü{ðn—ÄËO¤Û§Õu4>ùŽß1ž•X2‚¦Áý±ß' ‘£â^!ế Õ4¸Z7‹èG¨=ÆdæègÛ¼CÃ4~)‡Y È;ÕO±íŸ;ñïÁš}"´Ð-“\²q›LÏôò½Iør ã±ÆRÙzñž1ë‹ç„[{údlæúüð䯦( ‹Y>Ø]ƒë‡!r Q×·\ˆ&½p=8À´»‹Âý8¼‰ëcœc“×% Qï‘n˜rc¢Nˆš `W‹ ¸ý‡9YÁ逾ã%\ñ†Þzý0“‹% Û³‚¦Dõ錞Ûç§l5ÛA6FG=±× ÓB²@ëÈÛ»À|GŒ²‹Ò¸ÊØGÛ5Pî2¶T'•ë€b“b› ϶f3Sp­Cør&‰ªÃŠ}ƒ’ú‚Ç¡¬²-qPTî)èz¶tWN€ž ¨öÌ`‰³£õÇŠ±Ñ3f…þ/L9¿l’©cTlçªðÀž'â{eœ~RÎéAÜG²õûÖ g—H™Ø ÏMàÿ|WÅHȺhI&àŸê–}Á¿øWƒhâó§ÿòÊ-¾ƒ þùÍ^+ãÞà‘îÕê1±‰Ûä?©ãß%³Ç/ÿLâx‚šôLçxÏáÝáø›Íñh¼úøaH÷H~>¼d|wÿ¨Zýqht´˜â7×·Óïž6YYØ´ŒKd“d敳8×f.Ä’I=òJ‰@³ ôÊ7’)xƒW{9ª¢íšØ `•<âê1õÊ '›ÅÀr6û$XÙç=k¦FìÐÇDž·£®”8ôÉq´PÈýqYÓ%Óç€ä^ Ý…aõçÓk’ÜG|€<ž+ Öo°Ÿõ¯íˆh÷ÀžyŒ–+Äx=píêp› YìWœ…Ѽ‘à\ðpõ=±]-WÏÔzð98»±¬± qwöÀKàƯ¦á[Äžž˜Ï</bÁùwÄÌHøÂ닾ב›çäp kžØtï‹ ëÅá|WC€=Ür9=±ÿŠ®¹Ä¯41]ß8莧›ê1ñuÎ@uôÉ*Ǻa–ëcåîqQ=úœÀÆÍwÆÝ:Ñí‘ …õÀ <òpöçW†>‡Øá}Ïl…Û…×60b^¹HH.KNráºúø)"y?±ÀR;ä¾½òG¥\é…dG7c`pAŸc×¶MX«tGB2mò.°Sè?…?+"xˆÉÏéŠfþL®{àØ~Aà_ž|!d(C)63Û~ü^c ¡ñQRfÿ±öþY–ô†ˆcÍP' üŸ‹á=ˆê13ÓY6„‹ûþy"lsñ>Ç (Üè÷[yê:¼H.‰£®)Í©È5ñ¬€i²zã¿\βÕ6=rà¯ó~9ø3Gâa§ÒÓê:ð>ùëŸ<ñO Õø^¤Åª£ÿ)êÜûNî89ŸW¥Óëôͦ%–6ê¬2Ùš®‡bøùäI³žÏÇO¥Ý?†Üðu1×ùçt;ŠU½l…`Ù=FD7=òl¸–Õ«×jÀ¡­ÕKŠø"¯$òâþÐvÈHÈÀ×w!uõÆšûeìtŸLG‚+$N4‹ÌË£©ï‚»ø·{r1zžqn³Ds”÷¼.ÀöÈš¯ìã°Aãœ`û^DñÍcהŠy^Ó^Ø‹|\僷¦B¼‰Q–ÕôÈÚk¡À!Ûƒxß–2*ò5Û UÆ =2µþÙîð Üä ç§ß%Û“xds€G­õ®I‡1]ðR óX™A>™3Ç_¾#ÈÀ³ÿÙjava-imaging-utilities-0.14.2+3.orig/README0000664000000000000000000000503710525777742015033 0ustar JIU - Java Imaging Utilities - README file JIU is a Java library to load, process, analyze and save pixel images. The project's Web site is at . If you want to give feedback (questions, suggestions, bug reports), please write a message on the Open Discussion Forum at . Please check the subjects, the question you want to ask may already have been answered. You can be notified about new releases by subscribing to JIU on its Freshmeat page (under "Subscribe to new releases"). If you don't have a Freshmeat user account yet you can get one for free. The library is distributed under the GNU General Public License version 2. See the LICENSE file that is part of the distribution or visit . Documentation right now consists of the following items: * ChangeLog - changes in reverse chronological order * TODO - what's to be changed / added / removed next * API docs - the classes, their methods and fields, as HTML / PDF / PS / DVI * Manual - a general introduction (only in an early stage) In order to get an impression of what JIU can do, check out the jiuawt demo. One option is to visit . If you have Java installed and activated within your Web browser, it should be the easier and quicker way. However, you are restricted to a single image file. In order to test jiuawt with your own image files, start the jiuawt demo program that is part of the library. You must have a Java Runtime Environment version 1.1 or higher installed (typing "java -version" on the command line should tell you whether you have one and which version it is). Once you have downloaded and decompressed the JIU distribution archive (the .tar.bz2 file), go to the directory where you have decompressed it. That directory will contain jiu.jar. Start it * by typing "run-jiuawt.bat" (under Windows only), * by typing "java -jar jiu.jar", * by double-clicking on jiu.jar in a file manager or * by typing "java -cp jiu.jar net.sourceforge.jiu.apps.jiuawt". jiuawt requires Java 1.1+, so it should run with most virtual machines in use. Note that by default Java VMs only get to use a certain amount of memory. With images one easily reaches that limit. Provide the VM with more memory by starting it with -mxm as parameter between java and -cp (or -jar). So java -mx256m -jar jiu.jar would start the program and give 256 MB to it. You may want to adjust that value according to your needs. java-imaging-utilities-0.14.2+3.orig/doc/0000775000000000000000000000000010612172256014675 5ustar java-imaging-utilities-0.14.2+3.orig/doc/manual/0000775000000000000000000000000010546531742016157 5ustar java-imaging-utilities-0.14.2+3.orig/doc/manual/java-imaging-utilities-manual.tex0000664000000000000000000015524010324347565024530 0ustar \documentclass[a4paper,final,twoside]{book} \usepackage[latin1] {inputenc} \usepackage{hyperref} \usepackage{graphics} \usepackage{a4wide} \title{Manual\\JIU -- The Java Imaging Utilities} \author{Marco Schmidt\\ \\{\tt http://schmidt.devlib.org/jiu/}} \date{\today} \newif\ifpdf \pdffalse \ifx\pdfoutput\undefined \else \ifx\pdfoutput\relax \else \ifcase\pdfoutput \else \pdftrue \fi \fi \fi \ifpdf \pdfcompresslevel 9 \pdfstringdef\infotitle{JIU -- The Java Imaging Utilities -- Manual} \pdfstringdef\infosubject{A manual for developers using the JIU library} \pdfstringdef\infokeywords{manual, documentation, JIU, Java Imaging Utilities, imaging library, image processing, Java, software development} \pdfinfo { /Title (\infotitle) /Author (Marco Schmidt) /Subject (\infosubject) /Keywords (\infokeywords) } \fi %\raggedbottom \newcommand{\class}[1]{{\tt #1}} \newcommand{\code}[1]{{\tt #1}} \newcommand{\interface}[1]{{\tt #1}} \newcommand{\jiu}[0]{{\sl JIU}} \newcommand{\package}[1]{{\tt #1}} \begin{document} %\begin {onecolumn} \maketitle \thispagestyle{empty} \pagestyle{headings} \newpage \tableofcontents \newpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter {Introduction} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{About JIU} I started the \jiu{} (\emph{Java Imaging Utilities}) project \cite{jiuhome} in late 2000 in order to learn about algorithms for image processing, editing, analysis and compression and implement codecs for some file formats.\\ All code is written in the \emph{Java programming language}. \jiu\ is supposed to run with Java runtime environments version 1.1 and higher. This has the advantage of working under most operating systems in use today, 1.1-compatible virtual machines have been written for about any platform. A minor disadvantage is the lack of interesting classes from any higher Java versions, such as the \class{Iterator} class introduced in 1.2. Some (seemingly) basic functionality like sorting had to be rewritten because it was not yet available in Java 1.1.\\ JIU is distributed under the \emph{GNU General Public License} version 2.\\ I still consider JIU as beta software. Not only is it not heavily tested, but I know that I will make changes to it, including its package structure.\\ {\bf So please, do not rely on JIU for any mission-critical parts of your project!} \section{About this manual} This manual is a work in progress, just like \jiu\ itself. Both are very dependent on my spare time. I started writing the manual on October 17th, 2001.\\ It is written for \LaTeX, so that DVI, PostScript and PDF documents can be generated from it. The text file that is the basis for this manual ({\tt manual.tex}) is also included in the source code distribution of \jiu. \section{Why Java?} It seems that image processing algorithms demand more resources---CPU cycles and memory---than many other fields of computing. So why use Java, which seems to be so high-level and wasteful with these resources? It's not like I \emph{had to} use that particular language, I could have implemented the library in C, C++, Haskell, Ada, Delphi, whatever. When I started this section, I had separated parts for advantages and disadvantages. I gave that up and will just list points of interest in regard to picking Java. For some of these points, it is simply not clear whether they are a true advantage or disadvantage. \begin{itemize} \item \emph{Cross-platform}. Java and its bytecode concept lead to true cross-platform development--no more ifdefs to differentiate between platforms with different-sized integer types, etc. \item \emph{Availability} Especially in its 1.1 version, which is used by \jiu, Java is available on most platforms. C and C++ may still have an advantage there, but Java also covers almost all systems from PDAs to high-end servers. \item \emph{Runtime library}. Java's runtime library is very rich. From lists and hashes to Unicode support and other features for i18n, the developer does not have to reinvent the wheel. \item \emph{Built-in cross-platform GUI} Actually, this is more of a combination of points already mentioned. But writing a GUI application that will not look, but at least mostly work the same on very different platforms, is great when dealing with images. \item \emph{Object-orientation}. It is true that OOP is not a panacea, but it helps enforcing good design. Encapsulation, polymorphism and inheritance and the well-known patterns often lead to more elegant solutions. Unfortunately, Java---at least in its current version(s)---lacks a few features of a true OOP language. As an example, there are primitive types that are not derived from \class{Object}. \end{itemize} TODO \section{Why another Java library for imaging?} Okay, so there are reasons to use Java. But why start a completely new thing? There is Sun's own Java extension for imaging, \emph{Java Advanced Imaging} (JAI), as well as a couple of other projects, with full source code availability and a user base. Why not implement for one of those libraries instead of starting from scratch? TODO \section{Credits} \begin{itemize} \item Thanks to SourceForge \cite{sourceforge} for hosting the \jiu{} project! \end{itemize} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter {\jiu{} basics for developers} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% This chapter is for people who don't know \jiu{} and want to learn the basics. \section{Image data types} \subsection{Interfaces} The package \package{net.sourceforge.jiu.data} contains interfaces for the most basic types of images that get typically used in image processing. In the same package you will find implementations of those interfaces that store image data completely in memory. This will work nicely with images that fit well into the system's memory. \begin{itemize} \item \interface{BilevelImage} for images with the two colors black and white (e.g. faxes). \item \interface{Gray8Image} for images with shades of gray (e.g. photographs). \item \interface{Paletted8Image} for color images that have 256 or less different colors. \item \interface{RGB24Image} for truecolor image in RGB color space. \end{itemize} More on the topic of the various classes and interfaces dealing with image data types can be found in chapter \ref{imagedatatypes}. \subsection{Classes} As was said before, for all these interfaces data types exist that implement them by storing the complete image in memory. The names of those classes start with \code{Memory} and then simply copy the name of the interface that they are implementing. Examples: \class{MemoryBilevelImage}, \class{MemoryRGB24Image}. In order to create a new image object, simply call the constructor with width and height as arguments. If you want an RGB truecolor image, this would be it: \begin{verbatim} import net.sourceforge.jiu.data.*; ... MemoryRGB24Image image = new MemoryRGB24Image(1200, 800); \end{verbatim} See the API docs of the interfaces for code examples to get and set pixels. \subsection{AWT image data types} What about Java's existing image classes, \class{java.awt.Image} and its children? You can convert between them and \jiu{}'s own image data classes, but you cannot use them directly. Conversion is done with \jiu{}'s \class{ImageCreator} class. Example: \begin{verbatim} import java.awt.*; import net.sourceforge.jiu.gui.awt.*; ... PixelImage jiuImage = ...; // initialize java.awt.Image awtImage = ImageCreator.convertToAwtImage(jiuImage); // and back RGB24Image anotherJiuImage = ImageCreator.convertImageToRGB24Image(awtImage); \end{verbatim} As the conversion method from AWT to \jiu{} uses AWT's \class{PixelGrabber} (which works on 24 bit pixels in RGB color space) you will always get back an \class{RGB24Image}. \section{Loading images from files} Most of the time, images will be loaded from a file, processed and then saved back to a file. \jiu{} comes with a number of codecs that can do exactly that. In addition, \jiu{} can use \class{java.awt.Toolkit} to reuse the image loading functionality built into the Java Runtime Environment. That makes it possible to load images from JPEG, GIF and PNG files. \begin{verbatim} import net.sourceforge.jiu.gui.awt.*; ... PixelImage image = ToolkitLoader.loadViaToolkitOrCodecs("filename.jpg"); \end{verbatim} This basically just tries all \jiu{} codecs plus \class{java.awt.Toolkit}. If you use codecs directly, you can retrieve additional information, make the codec load another image than the first one, make the codec load only part of an image, etc. But the easiest method of loading an image is the one-liner you see above. If you must avoid AWT for some reason (the bug that keeps an AWT thread from terminating, missing X server, etc.), use the class \class{ImageLoader} from the package \package{net.sourceforge.jiu.codecs}. It will try all codecs that are part of \jiu{}: \begin{verbatim} import net.sourceforge.jiu.codecs.ImageLoader; ... PixelImage image = ImageLoader.load("filename.bmp"); \end{verbatim} \section{Operations} Now that you've created or loaded the image you will probably want to do something with it. In \jiu{}, you'll need an \emph{operation} class to do that (all classes that extend \class{Operation} are operations). Many operations are not derived directly from \class{Operation} but from \class{ImageToImageOperation}, which requires that the operation takes an input image and produces an output image. \jiu{}'s operations are put into different packages, depending on the type of operation. \subsection{Creation and usage} Running operations always follows the same pattern: \begin{itemize} \item Create an object of the operation's class. \item Give all necessary parameters to that object (via methods whose names start with \code{set}). Read the API documentation of the operation class to learn about what parameters exist for a given operation, if they are mandatory or optional and what the default values are. Most of the time you will also find a code snippet that demonstrates the usage. \item Call the \code{process()} method of that object, catching all exceptions that may be thrown (\class{OperationFailedException} or children of that exception class). \item Retrieve any output that the operation might have produced (via methods whose names start with \code{get}). Again, refer to the API documentation of the specific operation that you are trying to use. \end{itemize} As an example, let's say that you have just loaded \code{image} from a file, as seen in the previous section. Now you want to make the image brighter and decide for a 30\% brightness increase. There is a class \class{Brightness} in the package \package{net.sourceforge.jiu.color.adjustment}. \begin{verbatim} import net.sourceforge.jiu.color.adjustment.*; ... Brightness brightness = new Brightness(); brightness.setInputImage(image); brightness.setBrightness(30); brightness.process(); PixelImage adjustedImage = brightness.getOutputImage(); \end{verbatim} Just in case you wonder - \interface{PixelImage} is the most basic interface for image data. Everything in \jiu{} that stores an image must implement it. Because \class{ImageToImageOperation} does not make any assumptions about the types of image data classes that its extensions will deal with, both \code{getInputImage} and \code{setInputImage} deal with this interface. \subsection{Exceptions} Not all errors made when using operations can be determined at compile time. As an example, if you give an image to an operation which is not supported by that operation, this will be determined only after \code{process} has been called. Also, some operations may fail under specific circumstances only -- not enough memory, a particular file does not exist, etc. For these errors the \code{process()} method of \class{Operation} can throw exceptions of type \class{OperationFailedException}. Catch these exceptions to find out about what went wrong, they contain textual descriptions in English: \begin{verbatim} Operation operation = ...; // initialize try { operation.process(); } catch (OperationFailedException ofe) { System.err.println("Operation failed: " + ofe.toString()); } \end{verbatim} \section{Saving images to files} There is no class like \class{ImageLoader} to do saving with a single line of code. But saving works pretty much the same with all of \jiu{}'s codecs. Here is an example that uses \class{PNMCodec} (which supports PBM, PGM and PPM). It will save \code{image} to a file \code{output.pnm}: \begin{verbatim} import net.sourceforge.jiu.codecs.*; ... PNMCodec codec = new PNMCodec(); codec.setFile("output.pnm", CodecMode.SAVE); codec.setImage(image); // image to be saved codec.process(); codec.close(); \end{verbatim} Except for the first line where the codec object is created, the rest of the code can be used with \class{BMPCodec}, \class{PalmCodec} or any of the other codecs that support saving. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter {Terminology} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% This chapter defines some terminology to be used throughout this manual, other parts of the library's documentation and its source code. It clarifies language for those with background knowledge on the field of image processing and introduces concepts to beginners. \section{Images} Images are at the core of this library. They are data structures storing color dots arranged in a two-dimensional grid of a fixed size. An \emph{image} is defined as a sequence of \emph{rows}. The number of rows in an image is called its \emph{height}. Rows are numbered from top to bottom from $0$ to $height - 1$. Each row of an image is a sequence of \emph{pixels}. The number of pixels in a row is called its \emph{width}. All rows of an image must have the same width. Pixels within a row are numbered from left to right from $0$ to $width-1$. Given these definitions, every pixel can be uniquely addressed using a pair of integer numbers: its zero-based horizontal and vertical position. When stating a pixel position, this library names the horizontal position followed by the vertical position: $(x, y)$. Note: this is somewhat different from mathematical notation with regard to matrices, which is usually defined the other way around, vertical before horizontal. A pixel is a single color point. It is made up of one or more \emph{samples}. All pixels of an image have the same number of samples. The samples of a single pixel define its color. Behind every image there is a color model which defines how to get from a pixel's samples to that pixel's color. There has been no mentioning yet of what a sample really is. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter {Image data types} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \label{imagedatatypes} There are quite a few interfaces and classes for image types in \jiu{}. In fact, enough to be a bit confusing for someone trying to get a first impression of the library. This chapter is about both about the image classes from the Java runtime library and those introduced by \jiu{}. \section{Accessing image data with the AWT classes} As I wrote in the first chapter, one of my reasons for writing \jiu{} was that I cannot agree with some of the design decisions made in the runtime library with regard to imaging. The lack of different image class types in the AWT (Abstract Windowing Toolkit, the package \package{java.awt} and everything in it) is one of them. A single abstract \class{Image} class with almost no methods is nice if all you do with images is load them from somewhere and display them in your GUI application. Which is exactly what imaging was like in Java 1.0. Remember applets? However, once you want to manipulate images, there really should be some methods to access data. With \class{ImageProducer}, \class{ImageConsumer} and the various filter classes in \package{java.awt.image} there was a way to manipulate data, but not a straight-forward one. Besides, the setPixels method takes a byte array and a color model as parameters. You cannot just put a green pixel at position (x, y). Or a grey value with 16 bits of precision. Only with Java 1.2 an image class was introduced (\class{BufferedImage}) that comes with getter and setter methods for pixels. Unfortunately, these access methods are restricted to RGB pixels with 24 bits which must be encoded as \class{int} values for some reason. Each RGB pixel must be put together using shift and or operations, requiring to specify a transparency value as well. Not straight-forward. There isn't even a helper encoder and decoder class for those ARGB values. You can also access the data buffers of a \class{BufferedImage} object, but again, you better know what types were used and that data is stored top to bottom, and in each row from left to right. Also, it's not easy to find out how to manipulate a palette (color map) for an image with 256 distinct colors. To summarize--a single (or two) classes aren't enough to represent the wide variety of image types in use. Faxes, medical imaging, satellite data, photos, image data suitable for printing all need different data types. Being forced to have knowledge on how things are done internally is bad design. \section{Image data interfaces in \jiu{}} This gives an overview of the most important image data interfaces and their implementations. You might also want to look into the source code of \package{net.sourceforge.jiu.data}--it's relatively little and, hopefully, readable. \subsection{PixelImage} \jiu{}'s base pixel image data interface is \interface{PixelImage}. It only knows about its resolution (width and height) and the number of channels that it consists of. The smallest common denominator. A \emph{sample} is a single value in one channel at a certain position. A \emph{pixel} is the combination of all samples from all channels at the same position. If an image has only one channel, the terms sample and pixel can be used interchangeably. \subsection{IntegerImage} Derived from \interface{PixelImage} is \interface{IntegerImage}. It only adds the restriction that each sample must be an integer value between $0$ and $2^{31}-1$ (also known as \code{Integer.MAX\_VALUE}) so that it can be stored in Java's \code{int} type (which is a signed 32 bit integer type). Note that this still does not make any assumptions on how those integer values are interpreted as colors. A value of 0 at a certain position in a one-channel image has no meaning yet. \subsection{GrayImage, RGBImage, PalettedImage} The meaning of what the numbers represent comes with this set of interfaces. Note that they are not derived from any other interface. \begin{itemize} \item \interface{GrayImage} is for images with one channel where values go from black over various shades of gray (the number depends on the available precision) to white. \item \interface{RGBImage} is for truecolor images using the RGB color space. It requires three channels for the color components red, green and blue. \item \interface{PalettedImage} is for images that store index values into a list of colors, the palette. This image type always has only one channel. \end{itemize} \subsection{GrayIntegerImage, RGBIntegerImage, PalettedIntegerImage} This layer of interfaces combines \interface{IntegerImage} and the three interfaces from the previous section which define the meaning of an image type's content. \begin{itemize} \item \interface{GrayIntegerImage} is for grayscale images that use integer values up to \code{int} as samples. \item \interface{PalettedIntegerImage} is for paletted images that use \code{int} samples. \item \interface{RGBIntegerImage} - same here, each of the three components stores \code{int} samples. \end{itemize} Although these interfaces describe the meaning, it is still unclear what interval a sample must be from. Values must fit into 32 bits because of the super interface \interface{IntegerImage}, so there is an upper limit. But samples can be smaller, and for efficiency reasons there are types that use less space than an int for each sample. \subsection{BilevelImage, Gray8Image, Gray16Image, RGB24Image, Paletted8Image} These four interfaces are derived from the aforementioned three interfaces and define the number of bits for each sample. \begin{itemize} \item \interface{BilevelImage} extends \interface{GrayIntegerImage} and its pixels have only two possible values -- black and white. \item \interface{Gray8Image} also extends \interface{GrayIntegerImage} and uses eight bits per sample, allowing for 256 shades of gray. \item \interface{Gray16Image} also extends \interface{GrayIntegerImage} and uses sixteen bits per sample, allowing for 65536 shades of gray. \item \interface{Paletted8Image} is a \interface{PalettedIntegerImage} that uses eight bits for the index values. Thus, it can be used for palettes with up to 256 entries. \item \interface{RGB24Image} uses eight bits for each of its three channels, for a total of 24 bits. \end{itemize} \section{Implementations of the image interfaces} Keep in mind that the previous section introduced a lot of types, but they were all interfaces. You will need an implementation of them to actually work on real images. Right now, there is only an in-memory implementation of them. It is planned to provide disk-based implementations as well. In combination with a caching system this will enable the processing of very large images. \subsection{In-memory: MemoryBilevelImage, MemoryGray8Image, MemoryRGB24Image, MemoryPaletted8Image} These are in-memory implementations of the four interfaces described in the previous section. A byte array that is large enough will be allocated for each channel. This will allow fast and random access to samples. However, resolution is limited by the system's main memory (or more precisely, the amount of memory given to a virtual machine). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter {Demo program \code{jiuawt}} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \jiu{} comes with a GUI (graphical user interface) program that lets you play with its various features. The program is based on AWT (the Abstract Windowing Toolkit), not Swing, so that more people can run it (Swing has only been part of Java since version 1.2). All that is required is an installed Java 1.1 Runtime Environment and a system that can be put into some sort of graphics mode. If you have downloaded the non-core version of \jiu{}, you should have a JAR archive called \code{jiu.jar} in the ZIP archive that you downloaded. Let's say that you have extracted that JAR archive from the ZIP archive. There are several ways to start the jiuawt application. \begin{itemize} \item With some Java Runtime Environments (JREs), it is enough to double-click on the JAR archive in some file manager, e.g. the file explorer under Windows. \item If double-clicking doesn't work for you, open a shell (sometimes it's called console, or command prompt, DOS prompt, etc.). Some window where you can enter commands. Change to the directory where the JAR archive is stored. Start the program by typing \code{java -jar jiu.jar}. Under Windows, \code{start jiu.jar} might work as well. \item If your JRE is a bit older, the \code{-jar} switch may be unknown. In that case, change to the directory in the shell and type \code{java -cp jiu.jar net.sourceforge.jiu.apps.jiuawt}. \end{itemize} The jiuawt program requires quite a bit of memory, depending on the size of the images that are processed in it. Java virtual machines often do not give all of the available memory to a running program. In order to give an application more than the default amount, use the \code{-mx} switch of the \code{java} program. Example: \code{java -mx128m -jar jiu.jar}. This will give 128 MB to the virtual machine. Make sure that the \code{-mx} switch is the first argument to the VM. If you are planning to use jiuawt on a regular basis, you might want to create a link to the program. Under Windows, try calling it with \code{javaw.exe} instead of \code{java.exe}. That way, you will not have a DOS box popping up. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter {An overview of built-in classes} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% This chapter is a reference of all classes that are part of JIU. These classes can be classified more or less to belong into the categories operations, data classes and helper classes. \emph{UNFINISHED} \section{Image data} \package{net.sourceforge.jiu.data} contains interfaces and classes for the storage of image data, maybe the most essential package of \jiu{} -- after all, operations work on image data. \section{Operations} \package{net.sourceforge.jiu.ops} has the base operation classes, plus exceptions for most typical failures that can occur in operations. An interface for progress notification is also provided here. \section{Codecs} Codecs are classes to read images from and write them to files (or in some cases, more generally, streams). The package \package{net.sourceforge.jiu.codecs} provides the base codec class \class{ImageCodec} and codecs for several image file formats. \begin{description} \item[ImageCodec] Abstract base class for image I/O operations. Supports progress notification and bounds definitions to load or save only part of an image. \end{description} \section{Color} \package{net.sourceforge.jiu.color} offers operations that modify or analyze the color of an image. \subsection{Analyzing color} \begin{description} \item[AutoDetectColorType] (\ref{autodetectcolortype}) Checks if an image can be converted to an image type that uses less memory without losing information. Can perform that conversion if wanted. \item[TextureAnalysis] Takes the co-occurrence matrix of an image and computes several properties based on it. \end{description} \subsection{Decreasing color depth} Several operations deal with the process of converting images in a way so that they will be of a different color type. The following operation deal with conversions that will lead to a loss of information, which usually also means that less memory will be required for the new version of the image. \begin{description} \item[ErrorDiffusionDithering] (\ref{brightness}) Adjust the brightness of an image, from -100 percent (resulting image is black) to 100 percent (resulting image is white). \item[OrderedDither] (\ref{brightness}) Adjust the brightness of an image, from -100 percent (resulting image is black) to 100 percent (resulting image is white). \item[RgbToGrayConversion] (\ref{brightness}) Adjust the brightness of an image, from -100 percent (resulting image is black) to 100 percent (resulting image is white). \end{description} \section{Other color modifications} \begin{description} \item[Brightness] (\ref{brightness}) Adjust the brightness of an image, from -100 percent (resulting image is black) to 100 percent (resulting image is white). \item[Invert] Replace each pixel with its negative counterpart - light becomes dark, and vice versa. For color images, each channel is processed independent from the others. For paletted images, only the palette is inverted. \end{description} \section{Filters} The \package{net.sourceforge.jiu.filters} package has support for convolution kernel filters and a few non-linear filters. \section{Transformations} The \package{net.sourceforge.jiu.transform} package provides common transformation operations, including scaling, rotating, shearing, cropping, flipping and mirroring. \section{Color data} A set of interfaces and classes for histograms, co-occurrence matrices and co-occurrence frequency matrices. Operations to create and initialize these data classes can be found in the color package. \section{Color quantization} \package{net.sourceforge.jiu.color.quantization} provides interfaces and classes for dealing with color quantization, the lossy process of reducing the number of unique colors in a color image. There are enough classes related to this field of color operations to justify a package of its own. \section{Applications} \jiu{} comes with a couple of demo applications. The package \package{net.sourceforge.jiu.apps} contains these applications as well as classes with functionality used by all demo applications. \section{GUI - AWT} The \package{net.sourceforge.jiu.gui.awt} hierarchy contains all classes that rely on the Abstract Windowing Toolkit (AWT), the packages from the \package{java.awt} hierarchy of the Java core libraries. If you don't use this part of JIU, your application will not be dependent on a target system having an X Window server installed, or any other GUI capabilities. This package provides classes for interoperability of \jiu{} and AWT classes like \class{java.awt.Image}. \section{GUI - AWT dialogs} \package{net.sourceforge.jiu.gui.awt.dialogs} contains a set of dialog classes that are used by the AWT demo application \class{jiuawt}. \section{Utility class} \package{net.sourceforge.jiu.util} holds everything that didn't fit elsewhere. Right now, this includes things as different as sorting, getting system information and operations on arrays. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter {Writing operations} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \label{writingoperations} \section{Basics} The base class for all classes performing analysis, modification and serialization of images or image-related data is \class{net.sourceforge.jiu.ops.Operation}. Any new operation will have to be directly or indirectly derived from that ancestor class.\\ If you are going to contribute your code to \jiu\ itself, contact the maintainers, describe the operation and ask if it is of interest for \jiu. Maybe somebody is already writing this sort of operation, or maybe it does not fit into \jiu. If you contribute to \jiu, read the coding conventions (chapter \ref{codingconventions} on page \pageref{codingconventions}ff) first. Use some package from the \package{net.sourceforge.jiu} hierarchy (also ask the maintainers for a suitable package; maybe a new one has to be created).\\ Instead of directly extending \class{Operation}, study some of its child classes, maybe it is more suitable to extend one of them. \begin{itemize} \item \class{ImageCodec} -- An operation to load or save images from or to streams or files. Chapter \ref{writingimagecodecs} is dedicated completely to image codecs. \item \class{ImageToImageOperation} -- Any operation that takes one or more input images and produces one or more output images. See \ref{usingimagetoimageoperation} for more information. \item \class{LookupTableOperation} -- An extension of \class{ImageToImageOperation} that takes an input image of type \class{IntegerImage} and some tables and produces an output image of the same type by looking up each sample of each channel of the input image in the appropriate table and writing the value found that way to the output image at the same position in the same channel.\\ This is the right choice for operations that process each sample independent from all other samples of the same pixel and all other pixels of the image. As a side effect, it is---at least in theory---easy to parallelize these kinds of operations, in order to take advantage of a multi-processor system. This kind of optimization is not implemented in JIU (yet).\\ Note that looking up a value in an array is relatively expensive. If the operation in question is just a simple addition, you might want to compute the result instead of looking it up. It depends on the Java Virtual Machine, the hardware and the exact nature of the operation which approach is faster. You might want to do some tests. \end{itemize} \section{Using \class{ImageToImageOperation}} \label{usingimagetoimageoperation} As mentioned before, \class{ImageToImageOperation} takes one or more input images and produces one or more output images. \section{Exceptions} The \code{Operation.process()} method has one exception in its \code{throws} clause: \class{OperationFailedException}. That exception class is the base for all exceptions to be thrown during the execution of \code{process}. \section{Progress notification} In some cases, operations might get used in end user applications. Human beings tend to be impatient or fear that the computer has locked up if nothing happens on the screen for a longer time. That is why the \class{Operation} class supports the concept of \emph{progress notification}.\\ All objects that want to be notified about the progress status (in terms of percentage of completion) of an operation must implement the \class{ProgressListener} interface. The objects must then be registered with the \class{Operation} by giving them as arguments to \code{Operation.addProgressListener}.\\ An operation supporting the progress notification concept must call one of the the \code{setProgress} methods in regular intervals. The \code{setProgress} methods of \class{Operation} are very simple---they go over all registered \class{ProgressListener} objects and call their respective \code{setProgress} methods with the same progress argument(s). This could lead to a progress bar being updated in a GUI environment, or a dot printed on the console.\\ Also see the API docs of \begin{itemize} \item \class{Operation} and \item \class{ProgressListener} \end{itemize} and check out some operation classes that use \code{setProgress}. Most of the time, it will be called after each row that has been processed, with the current row number of the total number of rows as parameters. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter {Writing image codecs} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \label{writingimagecodecs} \section{Introduction} The package {\tt net.sourceforge.jiu.codecs} is responsible for loading images from and saving them to files or arbitrary streams. \class{ImageCodec} is the ancestor class for all operations loading or saving images. It extends JIU's base class for operations, {\tt net.sourceforge.jiu.ops.Operation}. This section of the manual describes how to write a new codec that fits into JIU. Looking at the source code of an existing codec should help, too, although this section will contain code examples. It is recommended to read chapter \ref{writingoperations} on writing operations first. If the codec is supposed to be included into JIU itself (which is not necessary, everybody is free to use JIU as long as the licensing rules are obeyed when distributing JIU itself as part of a product), the JIU maintainer(s) should be contacted first and asked whether a new codec for a particular file format is already in the making and if that file format is of interest for JIU. If the codec will become part of JIU, its coding conventions (see chapter \ref{codingconventions}) must be used to maintain overall readability of the source code. \section{Basics} If the codec will be part of JIU, it must be put into the package {\tt net.sourceforge.jiu.codecs}. When writing a new codec, you will have to override the {\tt ImageCodec} class. Let's say you want to implement the (fictional) ACME image file format. The class name should be assembled from the file format's name (its short form, for brevity reasons) and {\tt ImageCodec} at the end of the name, so in this case: \begin{verbatim} public class ACMEImageCodec extends ImageCodec { ... } \end{verbatim} \subsection{Format name} Override the method {\tt getFormatName} and make it return a short String containing the name of the file format with the most popular file extension in parentheses: \begin{verbatim} public void getFormatName() { return "ACME Inc. (ACM)"; } \end{verbatim} Do not include \emph{file format} or \emph{image file format} in that description so that the format name can be used in programs with other natural languages than English. \subsection{File extensions} Override the method {\tt getFileExtensions} and make it return all file extensions that are typical for the file format in lowercase. In case of our fictional ACME file format, this could be: \begin{verbatim} public void getFileExtensions() { return new String[] {".acm", ".acme"}; } \end{verbatim} Override the method {\tt suggestFileExtension(PixelImage image)} to return a file extension that is most appropriate for the argument image object. In most cases, a file format only has one typical file extension anyway. However, some formats (like Portable Anymap) have a different file extension for every image type (grayscale will use {\tt .pgm}, color {\tt .ppm} etc.). For the sake of simplicity, let's say that ACME uses {\tt .acm} most of the time, so: \begin{verbatim} public String suggestFileExtension(PixelImage image) { return ".acm"; } \end{verbatim} This does not have to take into account that the argument image may not be supported at all. That will be checked elsewhere. \subsection{Supported actions} Override the methods \code{isLoadingSupported} and \code{isSavingSupported} to indicate whether loading and saving are supported. \section{Usage example} For a moment, let's get away from writing a codec and take a look at how it will be used. Minimum code example for loading an image: \begin{verbatim} ACMEImageCodec codec = new ACMEImageCodec(); codec.setFile("image.acm", CodecMode.LOAD); codec.process(); PixelImage image = codec.getImage(); codec.close(); \end{verbatim} Minimum code example for saving an image: \begin{verbatim} PixelImage image = ...; // this image is to be saved, initialize it somehow ACMEImageCodec codec = new ACMEImageCodec(); codec.setImage(image); codec.setFile("image.acm", CodecMode.SAVE); codec.process(); codec.close(); \end{verbatim} To sum it up, the following steps are relevant for anybody using the codec: \begin{enumerate} \item Create an object of the codec class, using the constructor with an empty argument list. \item Call the \code{setFile} method with the file name and the appropriate \class{CodecMode} object (\code{CodecMode.LOAD} or \class{CodecMode.SAVE}). \item Give input parameters to it if they are necessary. Most of the time all you have to do is provide an image if you want to save to a file. Other parameters are possible, but they either depend on the file format or are not essential for the codec to work (e.g. defining bounds or dealing with progress notification). \item Call the \code{process()} method which will do the actual work. \item Call the \code{close()} method, which will close any input or output streams or files that have been specified. Maybe process itself calls \code{close()}, but calling it a second time shouldn't do any harm. Not closing streams can become a problem when very many streams are used in a program, e.g. in a batch converter. \item This step is optional: get results, output parameters. The most obvious example for this is a \class{PixelImage} object when loading. The codec must provide get methods for all possible results, e.g. \code{getImage} for an image that was loaded in \code{process}. \end{enumerate} This will be reflected in the codec itself. \section{The {\tt process} method} It is the core of any \class{Operation} implementation and does the actual work. \class{ImageCodec} extends \class{Operation}, so this remains true. As the inner workings of an image codec can become quite complex, having additional methods is a good idea--one huge process method is probably unreadable, unless it is a very simple file format. All methods (except for process and set and get methods to be used to provide and query information) of the new codec \emph{must be declared private}. They are implementation details and of no relevance to the user of the codec. \section{Checking parameters} The first thing to do in any \code{process} method is checking the parameters. \begin{itemize} \item Are the mandatory parameters available? If not, throw a {\tt MissingParameterException}. \item Are all parameters that have been specified valid? If not, throw a WrongParameterException (if the parameters type is wrong etc.) or a an UnsupportedTypeException (if a compression type to be used for saving is not supported by your codec etc.). \end{itemize} For all optional parameters that are missing, initialize them to their default values. Note that some errors can only be detected later in the process. Example: when loading an image, you will have to do some decoding before you find out that, as an example, the file uses a compression method that you do not support. \section{Load or save} Now, find out whether you will have to load or save an image. Call \class{initModeFromIOObjects()}, it will find out whether to load or save from the kinds of I/O objects that have been given to the codec. If no I/O objects have been specified, that method will throw an appropriate exception. Then get the {\tt CodecMode} using {\tt getMode()}. If the resulting mode---either {\tt CodecMode.LOAD} or {\tt CodecMode.SAVE}---is not supported by your implementation, throw an {\tt UnsupportedTypeException} with a descriptive error message. \section{I/O} {\tt ImageCodec} provides set methods to specify input and output objects so that the codec can read or write data. The codec knows the following classes, in ascending order of their abilities: \begin{itemize} \item \class{InputStream} and \class{OutputStream} which only let you read and write byte(s) in a linear way without random access. \item Everything implementing \class{DataInput} and \class{DataOutput}, which let you do the same as \class{InputStream} and \class{OutputStream} but can also read and write types like \code{int} or \code{short} in network byte order (big endian). \item \class{RandomAccessFile} which implements both \class{DataInput} and \class{DataOutput}, thus offering everything these two do plus random access--you will be able to seek to any offset in the file and continue to read or write there. \end{itemize} If you can choose which of these classes you will use, pick the most primitive ones that will work for you. This will make it possible to use your codec in more environments. If your codec is alright with having an \class{InputStream}, it will not only work on files (\class{FileInputStream}) but also on network and other streams. However, some file formats like TIFF need \class{RandomAccessFile}. For some file formats it may be necessary to wrap \class{InputStream} and \class{OutputStream} into some other I/O classes. As an example, to read or write text (as used in some subformat of Portable Anymap), \class{BufferedReader} and \class{BufferedWriter} are very convenient. To improve speed, put your \class{InputStream} and \class{OutputStream} objects into a \class{BufferedInputStream} or \class{BufferedOutputStream} object. If your codec works with the interfaces \class{DataInput} and \class{DataOutput}, you will be able to cover the most cases. Try to do this whenever possible. Call the method \code{getInputAsDataInput} and you will be given either a \class{DataInput} object (if you directly specified one), or a \code{DataInputStream} (created from an \class{InputStream} if you specified one), or a \code{RandomAccessFile} object. All of them implement \class{DataInput}. That works with \class{DataOutput} as well, if you are saving an image. Anyway, make sure that you have I/O objects and that they are of the correct type. If not, throw a \class{MissingParameterException}. \section{Reading and writing primitive values} Normally, image file formats demand that you read a lot of \code{byte}, \code{short} and \code{int} values in a certain order that you will have to interpret as, e.g., image width, color depth or compression type. Instead of calling \code{read()} or \code{readInt()} each time you have to read a primitive value, load the complete header to a byte array. Then get the interesting primitives from that array using the class \class{net.sourceforge.jiu.util.ArrayConverter}. The following example will read 32 bytes and get bytes 12, 13, 14 and 15 as an \code{int} value in little endian byte order: \begin{verbatim} byte[] header = new byte[32]; in.readFully(header); int width = ArrayConverter.getIntLE(12); \end{verbatim} This approach will restrict the number of places for I/O errors to one, the call to the method that reads all the bytes (in the example \code{readFully}). Also, you don't have to skip over values that you don't care about -- you just read everything and interpret only those places of the array that are necessary. The same approach can also be used when writing an array. Create an array (it will by default be filled with zeroes), call the appropriate put methods of \class{ArrayConverter} and write the complete array to output. \section{Bounds} After you have read image width and height from the image, deal with the bounds. Check if bounds have been defined by querying {\tt hasBounds()}. If there are no bounds, set them to the complete image: \begin{verbatim} setBounds(0, 0, width - 1, height - 1); \end{verbatim} If there are bounds and you don't want to support them for whatever reason, throw an \class{OperationFailedException}. If the bounds do not match image width and height, throw an \class{WrongParameterException} (the bounds parameters were false). \section{Loading} After you have parsed the input stream for information, you can do some more checks. If the image file format that you support in your new codec can have multiple images in one stream, call \code{getImageIndex} to find out which one to load. If the index is invalid (if there are not enough images in the stream), throw an \class{InvalidImageIndexException}. Next, check if the image type, compression method etc. are supported by your codec and throw an \class{UnsupportedTypeException} otherwise. Also check if the bounds---if present---fit the actual image resolution. Create an image object of the right type and resolution. If you are regarding the bounds, use \code{getBoundsWidth} and \code{getBoundsHeight} instead of width and height as found in the stream. Load the image and try to use progress notification via the \code{setProgress} methods. Do not use more memory than necessary. As an example, do not load an uncompressed image into a large byte array, that will require twice the memory of the uncompressed image. Instead, create a row buffer, read row by row and put those rows into the image. \section{Performance issues} Even nicer than correct codecs (which read and write image files according to the specifications) are correct \emph{and} fast codecs. Whatever fast means... Correctness should be prefered to speed, but higher speed can sometimes be reached by very simple means. \begin{itemize} \item The caller should use \class{BufferedInputStream} and \class{BufferedOutputStream} when giving streams to the codec. The codecs should not create buffered versions, that is the job of the caller. \item Instead of single bytes, process several bytes at a time. As an example, read a complete row and put a complete row into the image using \code{putSamples} instead of \code{putSample}. \end{itemize} \section{Documentation} As for any other operation, create javadoc-compatible documentation for your image codec. Specify \begin{itemize} \item whether both loading and saving are supported, \item which I/O objects the codec can work with, \item which image types the codec can work with, \item which flavors of the file format are supported (e.g. compression types), \item whether the bounds concept of ImageCodec is supported, \item whether the progress notification concept of ImageCodec is supported, \item a description of all parameters new to this codec - what can be done with them, are they optional etc. and \item some background information on the file format if available---who created it and why, where is it used, etc. \end{itemize} For external HTML links in your documentation, use the \code{target} attribute with the value \code{\_top}: \begin{verbatim} Some site \end{verbatim} This way, a website will be displayed in the complete browser, not only the frame on the right side that previously held the class documentation. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter {Coding conventions} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \label{codingconventions} This chapter is of interest for everybody who wants to contribute code to \jiu. It is described here how code is to be formatted. Having the code follow certain rules consistently throughout all of \jiu's packages is important for readability and maintainability. \section{Import statements} All classes that are used in a class must be explicitly imported, except for classes from the \code{java.lang} package. Each imported class gets its own line, no empty lines between import statements. Do not use the asterisk to import a complete package like in \code{java.io.*}. Sort imports in ascending order, first by package name, then by the class (or interface) name. \section{Indentation} Use tab characters to express indentation. The tabs should be interpreted as four space characters. \section{Avoid large indentation levels} In order to make code more readable, avoid large levels of indentation. If quite a few lines are at level 4, 5, 6 or higher, you probably want to put that code in another method and call that method instead. Nested loops often lead to high indentation levels. If statements with else cases at the end of methods should be rewritten if one of the cases includes little and the other one much code. if (a < 0) { a = 0; } else { // a lot of code } // end of method should become if (a < 0) { a = 0; return; } // a lot of code // end of method \section{Identifier names} \label{identifiernames} Use meaningful names for identifiers. An exception to this can be made for local variables, especially when they are names of loop variables. Use only English as natural language. Do not use any characters but \code{a} to \code{z}, \code{A} to \code{Z} and digits \code{0} to \code{9} (so, no underscore character \code{\_}). Avoid the digits whenever possible. Variable and method names must start with a lowercase letter (exception: final variable names, see \ref{finalvarnames}). Class and interface names must start with an uppercase letter. Method names should start with a verb (get, set, is, etc.). Use capitalization to make reading names easier, e.g. {\tt maxValue} instead of {\tt maxvalue}. Avoid suffixes like \code{Interface} or \code{Impl} to express that a class is an interface or an implementation of some interface. \section{Final variable names} \label{finalvarnames} The letters in names of variables that are declared final must all be uppercase. This makes it of course impossible to use capitalization as suggested in \ref{identifiernames}. That is why the use of the underscore is allowed and encouraged in the names of final variables to separate words: {\tt MAX\_VALUE}. \section{Methods sorted by name} All methods of a class must be sorted by their names in ascending order, no matter what the access modifier of a method is. \section{Thrown exceptions in method signatures} Do not explicitly specify exceptions that extend \code{RuntimeException}, e.g. \code{IllegalArgumentException}. However, do document them. \section{Declaration of fields in classes} All fields, no matter whether they are class or instance variables, public or private, must be declared at the beginning of a class, in one block. \section{Declaration of fields in interfaces} All fields in interfaces must be declared without any access modifiers like \code{public} or \code{static}. \section{No debug or error messages to System.out or System.err} Writing warning or error messages to \code{System.out} or \code{System.err} is forbidden. Whenever errors occur, the exception system must be used, exceptions can carry textual messages in them. \section{Opening braces} Always give opening braces a line of their own: \begin{verbatim} while (i > 12) { System.out.println("i is now " + i); i--; } \end{verbatim} Do not write: \begin{verbatim} while (i > 12) { System.out.println("i is now " + i); i--; } \end{verbatim} \section{One line statement blocks} In \code{if} statements and \code{while} and \code{for} loops, always include single line statements in braces: \begin{verbatim} if (i > 0) { i++; } \end{verbatim} Do not write: \begin{verbatim} if (i > 0) i++; \end{verbatim} Also do not write: \begin{verbatim} if (i > 0) i++; \end{verbatim} \section{Conditional operator} Avoid the ternary conditional operator \code{?:}, use an if statement instead: \begin{verbatim} if (i >= 0 && i < array.length) { return array[i]; } else { return "Not a valid index: " + i; } \end{verbatim} Do not write: \begin{verbatim} return (i >= 0 && i < ARRAY.length) ? ARRAY[i] : "?"; \end{verbatim} An exception to this can be made when the argument to a constructor must be picked from two alternatives within a constructor (\code{this} or \code{super}). Example: \begin{verbatim} public class MyFrame extends Frame { public MyFrame(String title) { super(title == null ? "Untitled" : title, true); ... } } \end{verbatim} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{thebibliography}{HMNA98b} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \bibitem [jiu00] {jiuhome} Java Imaging Utilities homepage, \href{http://schmidt.devlib.org/jiu/}{http://schmidt.devlib.org/jiu/}. \bibitem [sou00] {sourceforge} SourceForge homepage, \href{http://www.sourceforge.net/}{http://www.sourceforge.net/}. \end{thebibliography} \end{document} java-imaging-utilities-0.14.2+3.orig/doc/ChangeLog0000664000000000000000000011066510612172256016460 0ustar 2007-04-20 + JiuHelloWorld, JiuBlue, JiuCountColors: Added three sample programs to the apps package, they are documented on the new tour page http://schmidt.devlib.org/jiu/introduction.html - CountColors: Removed the standalone color counting program, the new JiuCountColors sample does basically the same thing, but with the introductory tour's images 2007-04-19 o create-release.bat: Added the resources directory in the call to tar; resources were included only in the JAR file. Thanks to Stuart Prescott of http://latexdraw.sf.net/ for pointing this out! o Resample: Added modifications by an anonymous contributor which significantly speed up image scaling with this class. o TextureAnalysis: Added static convenience methods compute for matrices and images. o MemoryCoOccurrenceMatrix: Made private attributes final to emphasize that they are only modified in the constructor. Added warning about 16 bit image channels. o docs-html-options, create-release.bat: Changed JIU version to 0.14.2. o RGBToGrayConversion: Added static convenience method convert for users who don't want to change color weights or reuse output image objects. 2007-03-04 o UnsharpMaskKernel: Fixed bug that prevented constructor running smoothly (parameter array in call to super constructor too small). Reported by knuta: . o PSDCodec: Fixed compiler warnings by removing unused variables. o AwtOperationProcessor: Fixed compiler warnings by replacing calls to Dialog.show with calls to Dialog.setVisible(true). o ArrayRotation: Commented out unused private methods. o ArrayScaling: Fixed compiler warning by removing unused local variable. o Dialogs: Replaced call to Dialog.show with call to Dialog.setVisible(true). o JiuAwtFrame: Replaced show with setVisible(true), declared serialVersionUID. o OrderedDither: Fixed compiler warning about unused variable. o OctreeColorQuantizer: Removed unused variable. 2007-01-03 o Contrast: added static convenience method, updated documentation o Histogram1DCreator: added an additional count method for an arbitrary channel o MeanDifference: added a static method "compute" o MatrixCreator: added support for Gray16Image o OnDemandHistogram3D: rewrote it from scratch to use hash tables instead of arrays 2007-01-02 + CountColors: command line application to demonstrate how to count colors with JIU o Histogram1DCreator, Histogram3DCreator: added static count method o HueSaturationValue: made this class work with RGB48Image (by supporting all flavors of RGBIntegerImage) 2006-12-16 o Released current version as 0.14.1 after a request in the Open Discussion forum. o Brightness: added static helper method adjust o create-api-docs.html: got it working again 2006-04-18 [processed some older Sourceforge bug reports, patches and feature requests] o JPEGData: fixed bug which added "scans" to vector "scans" instead of parameter "scan" (reported as bug #1424545 by arno_b) o GIFCodec: added codec.close() to end of usage example in the comment (reported as patches item #1449702 by talbes) o ImageLoader: fixed bug which led to too many open files because codec.close() wasn't called (reported as bug #1402205 by Cameron Dorrat/cdorrat); removed unnecessary imports o PopularityQuantizer: when the source image has less unique colors than the proposed palette size, the palette size is just set to the number of unique colors instead of throwing an exception (reported as feature request #1449716 by talbes) o PopularityQuantizer: made variable paletteIndex change its value in method determinePalette (reported as bug #1201053 by jadesailor) o JiuInfo: updated version to 0.15.0 o jiuawtapplet: added a serialVersionUID variable to make a warning go away 2006-04-17 o Renamed the Windows batch files to have more self-explanatory names. o Changed create-jiu.jar.bat (formerly j.bat) to create a JAR which causes no problems with (my) JDK 1.5 installation o README: changed website URL to http://schmidt.devlib.org/jiu/ o Released as 0.14.0 (note that some of the documents still say 0.13.0, this is just a maintenance release to deliver a correct version of jiu.jar) 2006-01-18 o Added various classes related to jiuawt in order to be able to load the sample file from within an applet o Modified website to offer jiuawt as a frame started from within an applet: http://schmidt.devlib.org/jiu/applet.html + Added sample image file resources/images/image1.jpg 2005-11-02 o JiuInfo: changed version from 0.13.0 to 0.14.0 + JPEGHeaderReader: moved the various readXYZMarker methods from JPEGCodec to this new class o Various JPEG codec changes; the whole JPEG codec still is far from working 2005-10-30 o Changed release options: * only one archive with source and compiled files * compiled LaTeX (API, manual) only as PDF, no more DVI / PS * no more source code HTML files * downloads not available anymore from SourceForge itself o Released as 0.13.0 2004-09-14 o Strings: added constant for French, fixed bug in determineSuitableIsoCode (ISO language code returned by Locale was always assumed to be supported by JIU) + fr.txt: added string translation into French, created by Vincent Jestin 2004-08-07 o renamed JPEGScanHeader to JPEGScan for consistency reasons (it's also JPEGFrame, not JPEGFrameHeader) + JPEGScanComponentSpecification data class o JPEGCodec: added readStartOfScan o docs-html-options: changed version from 0.12.0 to 0.13.0 2004-08-06 o ReduceRGB: class did nothing so far. Can now deal with 24 and 48 bpp RGB input images. Produces the minimum image type necessary for chosen destination bit depth. o Resample: changed single line as suggested by Mike Dillon in order to remove banding artifacts o JPEGCodec: added reading of SOF_n markers, Huffman tables o packages: added net.sourceforge.jiu.codec.jpeg + JPEGHuffmanTable: data class for a single Huffman table + JPEGScanHeader: data class for a scan header 2004-08-05 + Added file package.html to JPEG package. Also class JPEGConstants. o JPEGCodec: added reading of quantization tables 2004-08-04 + Added package for the JPEG file format: net.sourceforge.jiu.codec.jpeg + Added classes JPEGCodec, JPEGData, JPEGQuantizationTable. 2004-08-01 o Resample: instead of Math.round uses simple typecast from float to int; small speed-up o JiuInfo: changed version from 0.12.0 to 0.13.0 2004-07-07 o z.bat, j.bat, d-*.bat: updated to reflect directory structure o Released as 0.12.0. 2004-07-01 o ErrorDiffusionDithering: Added documentation, renamed setType to setTemplateType. o some classes using ErrorDiffusionDithering: changed calls to setType to setTemplateType 2004-06-24 + GIFCodec: added class to standard distribution so that it can be released on 2004-07-07; also see the entry of 2002-06-13 o jiuawt & others: added support to jiuawt to save images as GIF files 2003-09-06 o PNGCodec: added documentation and some smaller methods to write metadata and have more configuration options when encoding o javatips.html: checked links, added link to style sheet 2003-09-05 o ArrayConverter: added methods setIntLE / setIntBE to write int values to a byte array o Strings: long String arrays with jiuawt text messages in all languages removed + StringLoader: a class to read a Strings resource from a text file + en.txt, de.txt, es.txt: text files that contain the String arrays formerly stored as literals in Strings o StringIndexConstants: added various constants o ImageDescriptionCreator: now works with a Strings object, no longer brings its own String objects o EditorState: modified to use StringLoader o jiuawt: modified to work with new EditorState behaviour o JiuAwtFrame: now calls ImageDescriptionCreator with a Strings object o MenuIndexConstants: added FILE_SAVEAS_PNG constant o MenuWrapper: added mapping from FILE_SAVEAS_PNG to corresponding StringIndexConstants constant o JiuInfo: changed JIU_FEEDBACK_EMAIL to JIU_FEEDBACK_ADDRESS and set the constant to http://jiu.sourceforge.net/feedback.html o j.bat: changed version from 0.11.0 to 0.12.0 o package.html: added package.html files for all packages that didn't have one already; now the first impression of the HTML API docs is somewhat better (or at least more complete) o ArrayConverter: added methods setShortLE / setShortBE to write short values to a byte array o PNGCodec: can now save the following image types: bilevel, grayscale 8 and 16 bits, paletted and RGB 24 and 48 bits o various: modified them so that the File | Save as | Portable Network Graphics (PNG)... menu item can be used 2003-09-04 o PNGCodec: continued adding support for interlaced PNGs; now reads all basi*, basn* and s* files from the PNG test suite http://www.schaik.com/pngsuite/pngsuite.html without errors; transparency information is not yet regarded; started implementation of saving images o z.bat: updated JIU version from 0.11.0 to 0.12.0 2003-09-03 o screenshots.html: added OS/2 screenshot by David Mediavilla o ImageDescriptionCreator: made English the default if the requested language is unavailable o Updated CVS repository o PNGCodec: started adding support for reading interlaced PNGs 2003-09-02 o PNGCodec: added support for reading 1, 2 and 4 bit non-interlaced paletted PNGs, 8 and 16 bit grayscale alpha and 48 bit RGB PNGs with an alpha channel o ArrayConverter: added method decodePacked1Bit in order to decode 1 bit paletted values from decompressed PNG data o PNGCodec: added skip method that calls the input stream's skip method, regards the returned number of bytes actually skipped and loops until all bytes have been skipped 2003-09-01 + PNGCodec: added a codec for the PNG format, can read almost all non-interlaced PNGs, throws away all alpha information; I took the class from the 'never finished that' directory and made it work, thanks to Eclipse's debugger; main bug was in the filtering, I always started at the second pixel, but filtering has to start with the first; for all invalid horizontal index values (-1, -2, ...) 0 is to be used o ArrayConverter: added method convertPacked4BitIntensityTo8Bit in order to decode 4 bit grayscale values from decompressed PNG data and store those values in Gray8Image objects o ImageLoader: added PNGCodec to the list of codecs to be used in ImageLoader 2003-08-31 o ArrayConverter: Added a method getShortBE to retrieve a short value in big endian byte order from a byte array o TIFFCodec, TIFFDecoder, TIFFImageFileDirectory: TIFF codec can now read 48 bpp interleaved RGB file type o docs-html-options: changed JIU version from 0.11.0 to 0.12.0 2003-08-30 o JiuInfo: changed version from 0.11.0 to 0.12.0. + RGB48Image: new RGB integer image type with 16 bits per sample, thus 48 bits per pixel + MemoryRGB48Image: implementation of RGB48Image in memory, extends MemoryShortChannelImage + PromotionRGB48: new promotion operation that converts Bilevel/Gray8/Gray16/Paletted8/RGB24Image to the new RGB48Image type o AwtOperationProcessor: added implementation of method colorPromotePromoteToRgb48, a call to PromotionRGB48 o AwtMenuWrapper: added menu item for Promote to 48 bits RGB o ImageCreator: added method Image convertToAwtImage(RGB48Image image, int alpha) to convert from the new RGB48Image to an AWT image type o RGBA: added method convertFromRGB48 to convert short arrays to an RGBA int array o AutoDetectColorType: integrated the new RGB48Image type; now bilevel/gray8/gray16/rgb24/paletted8 images being stored as RGB48Image are recognized and optionally converted to the lower image type o AwtOperationProcessor: added warning when a modified image is to be closed after a user call to File | Close o Strings: added two strings for the above change o StringIndexConstants: added two constants for the change to rows above o PNMCodec: adjusted to read and write 48 bpp color images as ASCII o EqualizeHistogram: fixed bug that lead to an overflow in a computation; now long instead of int is used and the operation works on 48 bpp images o OperationProcessor: enabled a couple of operations for RGB48Image in the isAvailable method o OilFilter: added warning that this operation takes very long with images with 16 bits per sample o AwtOperationProcessor: now works with RGB48Image types, but quickly runs out of memory; a data structure that is less memory-intensive than OnDemandHistogram3D would be required 2003-08-29 o Released JIU 0.11.0. 2003-08-21 o various: created new pages for the project website http://jiu.sourceforge.net, added directory website to workspace, imported webpages 2003-08-20 o various: adjusted about 30 javadoc comments so that javadoc now no longer displays warnings 2003-08-19 o MedianCutContourRemoval: finished implementation o AwtOperationProcessor: changed method colorReduceMedianCut so that the contour removal option can now be used o Palette: added a getSamples method that returns all samples of one channel as an array + MeanDifference: added this class to net.sourceforge.jiu.color.analysis; it can compute the mean difference between two images + PopularityQuantizer: added this quantizer which determines the N color occurring most frequently in an input image and maps the input image to those colors using ArbitraryColorQuantizer o RGBColorList: sort method has been renamed to sortByAxis; new method sortByCounter 2003-08-18 + Statistics: new class in net.sourceforge.jiu.util, for the computation of mean, standard deviation and variance o MedianCutContourRemoval: wrote most of the implementation o MedianCutQuantizer, MedianCutNode: smaller modifications 2003-08-16 o First CVS commit of JIU. 2003-08-15 - ImageArithmetic: removed that class from the package, for the time being; it was nowhere near ready o PalettedImage: is no longer derived from IntegerImage, has no super interface anymore + PalettedIntegerImage: new class that combines PalettedImage and IntegerImage o Paletted8Image: is now derived from PalettedIntegerImage instead of PalettedImage o ReduceShadesOfGray: can now reduce Gray16Image objects o jiuawt/JiuAwtFrame/EditorState: jiuawt now opens an image file given to it as a command line parameter o ConvolutionKernelFilter: can now deal with 16 bits per sample (there was a & 0xff operation which had to be replaced with & MAX) 2003-08-14 o AutoDetectColorType: added support for detecting 8 bit grayscale in 16 bit grayscale images and converting them to 8 bit if desired o OperationProcessor: made sure that reduce to bilevel threshold is available with 16 bit grayscale images (the operation itself already supported it) o OperationProcessor: from now on, creating co-occurrence and c.o. frequency matrices is possible only with 1 or 8 bit images (everything aboves leads to matrices that do not fit into memory; 16 bit => 65536 * 65536 int entries, 2^34 bytes; a smarter [on demand] data structure may be able to deal with that) o MemoryCoOccurrenceMatrix: now throws an IllegalArgumentException if a dimension is specified that leads to an array with more than 2^31 entries; also added documentation 2003-08-11 o various: removed a couple of small test applications in net.sourceforge.jiu.apps; they weren't useful for the general public 2003-08-06 o various: Eclipse showed about 160 warnings about unused imports; removed those imports, added a bit documentation and corrected some code that did not follow coding conventions 2003-06-06 + ImagesToImageOperation: new operation class that takes several input images and produces one output image 2003-03-24 o AwtOperationProcessor, ImageCanvas, EditorState, JiuAwtFrame: changed logic so that zoom factor is kept while modifying the same image + YesNoDialog: a dialog that asks a question and offers yes, no and cancel as buttons o AwtOperationProcessor, StringIndexConstants, Strings: when the image was modified and not yet saved and the user wants to exit, a reminder dialog pops up and asks whether the changes are really to be discarded o ImageCodec: fixed bug (similar to the one fixed on 2003-03-20) that uses setDataInput instead of setInputStream o EditorState, AwtOperationProcessor: bug fixed, after File / Close now there is no way of doing Edit / Undo anymore o PNMCodec: now loads PGM 16 bpp files, also regards bounds now when loading PGM (that wasn't happening before) 2003-03-23 + MemoryShortChannelImage: implementation of ShortChannelImage in memory, similar to MemoryByteChannelImage + Gray16Image: interface that merges ShortChannelImage and GrayIntegerImage, similar to Gray8Image + MemoryGray16Image: grayscale 16 bit image type, similar to MemoryGray8Image + PromotionGray16Image: creates grayscale 16 bpp images from BilevelImage and Gray8Image objects o PNMCodec: now saves Gray16Image objects o AwtOperationProcessor: added PromotionGray16 operation o AwtMenuWrapper: added PromotionGray16 menu item o RGBA: added convertFromGray16 method o ImageCreator: added method convertToAwtImage(Gray16Image image, int alpha) o OperationProcessor: added knowledge of gray16 to isAvailable o jiuawt: now supports a couple of command line arguments 2003-03-21 + BatchProcessorOperation: added this operation that processes file lists + ImageLoadTester: added this extension of BatchProcessorOperation that loads each file and prints a note to stdout 2003-03-20 o ImageCodec: setFile now uses setOutputStream instead of setDataOutput; the latter has no close method which lead to 0-sized output files in some cases. Problem reported by Dan Danilin. 2003-02-28 o ByteChannelImage, MemoryByteChannelImage: removed getSamples and putSamples method that do not have the channel as first argument 2003-02-27 + ShortChannelImage: added this interface, similar to ByteChannelImage (just with 16 bit values) 2002-11-15 + BufferedRGB24Image: added this bridge class between AWT and JIU; a BufferedImage can now be used as a RGB24Image 2002-08-05 + UnsharpMaskKernel: added this kernel, suggested by Niels Donvil 2002-07-30 o Resample: put the filters into classes of their own 2002-07-28 o Strings.java: added Spanish strings, thanks to Luis Salas 2002-07-26 o ErrorDiffusionDithering: removed constructor arguments, added setTemplateData o OctreeColorQuantizer: added setPaletteSize(int) method 2002-07-25 o PixelImage: added createCopy method 2002-07-24 o java-imaging-utilities-manual.tex: added introduction chapter for developers to get into JIU faster 2002-07-23 + SeekableByteOutputStream: extension of OutputStream that allows to seek in the output 2002-07-22 o Resample: changed default type from Box (nearest neighbor) to Triangle (bilinear) 2002-07-21 o PalmCodec: now writes scanline-compressed files 2002-07-20 o PalmCodec: now writes RLE-compressed files 2002-07-16 o PalmCodec: now writes 0xff as value in the compression field when saving images as "uncompressed" o LogLuvConversion: added support for 24 bit LogLuv colors o TiffDecoderLogLuv: added support for COMPRESSION_SGI_LOG_24_PACKED (34677) 2002-07-15 o PalmCodec: now writes 1, 8 and 16 bpp images (uncompressed only) o jiuawt: added File | Save as... | Palm o MapToArbitraryPaletteDialog: now offers the Palm system palettes 2002-07-13 o PalmCodec: started implementation of saving images 2002-07-12 o PalmCodec: resumed work on this codec, put it back into ImageLoader o PalmCodec: added a method to create the default Palm palette o ArrayConverter: added method decodePackedRGB565BigEndianToRGB24 required by 16 bpp Palm image files o PalmCodec: now reads 16 bpp truecolor Palm image files 2002-07-11 + jiuconvert: started a command line converter; this will be helpful for codec development because jiuawt involves too much clicking until one can test the actual changes to a codec 2002-07-05 o TIFFDecoder + heirs: added method getCompressionTypes that returns the TIFF compression values supported by that particular decoder o TIFFCodec: now has an internal hash table of (compression key, decoder class) pairs that will be used to create a decoder; a new registerDecoder method lets developers register external decoders + TIFFDecoderLogLuv: a decoder for SGI LogLuv RLE compression (so far, only the 32 bit flavor is decoded) + LogLuvConversion: a conversion class from LogLuv to RGB o TIFFDecoderLogLuv, LogLuvConversion: now also support decoding and conversion of LogL to grayscale + created new package net.sourceforge.jiu.color.conversion and moved the three color conversion classes there o renamed YCbCrConversion to PCDYCbCrConversion so that it will not be mistaken for the kind of conversion that takes place with JPEG files 2002-07-04 o ImageCodec: now has new methods setFile, isLoadingSupported, isSavingSupported for (hopefully) easier use of the codecs 2002-07-03 + CMYKConversion: performs a simplistic conversion (only direction CMYK to RGB right now) o TIFFDecoder: now can read interleaved CMYK files o TIFFCodec: when reading 2 to 4 BYTE values in a tag, the offset is used directly, no longer as an offset into the file o JiuAwtFrame: when at maximum zoom level, the View | Zoom in menu item is now disabled o JiuAwtFrame: when at minimum zoom level, the View | Zoom out menu item is now disabled o JiuAwtFrame: when at original size zoom level (1:1), the View | Set original size menu item is now disabled 2002-07-02 o EditorState: added API documentation o CoOccurrenceFrequencyMatrix: modified it so that javadoc outputs no more warnings (for the time being), COFM was the last class it was complaining about o TIFFImageFileDirectory, TIFFDecoder: now grayscale and paletted images with 4 bits per pixel can be read 2002-06-30 o HistogramCreator: made two classes from it, Histogram1DCreator and Histogram3DCreator, both extend Operation with progress notification; also changed all classes depending on it 2002-06-27 o JiuInfo: changed version from 0.9.0 to 0.10.0 o MeanFilter: changed it to be an AreaFilterOperation o MedianFilter: changed it to be an AreaFilterOperation o OilFilter: changed it to be an AreaFilterOperation 2002-06-25 o released JIU 0.9.0 o TIFFCodec: fixed bug in TIFFCodec.load(TIFFImageFileDirectory), for some reason the tileIndex was not increased in the while loop 2002-06-24 o JiuAwtFrame now implements ComponentListener and reacts on resize events by doing a new layout of the scrollpane 2002-06-23 o TIFFDecoder: added progress notification for TIFF reading by adding a call to setProgress to TIFFDecoder's private storeRow method o some smaller changes to make the library pass JavaPureCheck with the 1.1 level (removed Graphics2D rendering hints from ImageCanvas and jiuawt menu) 2002-06-22 o MedianCutQuantizer: changed this to be an ImageToImageOperation o ConvolutionKernelFilter: changed this to be an ImageToImageOperation o added View menu to jiuawt and updated various classes to make it do something 2002-06-20 - ImageFrame: removed this class which did not get used anymore (JiuAwtFrame uses ImageCanvas directly) 2002-06-19 o PNMCodec: fixed bug that created truncated PNM streams; added close() at the end of save() so that everything is flushed 2002-06-14 + ClusteredDotDither: added new class for clustered dot dithering 2002-06-13 + GIFCodec: created a class to write GIF files; unfortunatly, I cannot put this into the standard distribution due to LZW patenting problems o changed package structure, added various subpackages to net.sourceforge.jiu.color 2002-06-11 + TIFFDecoderPackbits to decompress Packbits-compressed TIFFs; adjusted TIFFCodec to use this decoder + TIFFDecoderModifiedHuffman to decompress Modified-Huffman-compressed TIFFs; adjusted TIFFCodec to use this decoder 2002-06-09 o TIFFDecoder: added methods to do the storing of uncompressed data; the real decoders (heirs of TIFFDecoder) can now concentrate on decompression + TIFFDecoderUncompressed: first real decoder, simply reads data and stores it 2002-06-06 o adjusted IFFCodec 2002-06-05 o Shear: converted it to an ImageToImageOperation + PixelImage: added getImageType method o Contrast: converted it to a LookupTableOperation + AreaFilterOperation: mean, median and oil will be modified to extend this + MaximumFilter: extends AreaFilterOperation, added this to jiuawt as well + MinimumFilter: dto. 2002-06-03 o adjusted GammaCorrection to be a LookupTableOperation 2002-06-02 o renamed package transform to geometry 2002-06-01 o ImageCodec: added method checkImageResolution o BMPCodec: now reads and writes pixels per meter as physical resolution o BilevelImage, MemoryBilevelImage: cleaned up interface and memory implementation of it o TIFFCodec: removed global variable in; replaced with local variables initialized with getRandomAccessFile() o converted MeanFilter, MedianFilter and OilFilter to be ImageToImageOperations o converted Crop to ImageToImageOperation 2002-05-31 o TIFFCodec: removed methods that are already in ImageCodec (setImageIndex, setInput); added process method, made load private 2002-05-28 o moved getNumComments, getComment and appendComment from PNMCodec to ImageCodec (other file formats can store comments as well) o ImageCodec: added method removeAllComments 2002-05-16 o all classes in the net.sourceforge.codecs.tiff now have Tiff instead of TIFF in their names (naming inconsistency) 2002-05-14 o moved all TIFF-related classes to a new package net.sourceforge.jiu.codecs.tiff 2002-05-13 o RASCodec: removed load and save method o ArbitraryPaletteQuantizer: changed it from a Operation to an ImageToImageOperation; now WrongParameterExceptions are thrown when image types are incorrect 2002-05-12 o BMPCodec: now supports loading 1, 4, 8 and 24 bpp BMP files (all uncompressed, also 4 and 8 bpp RLE compressed) 2002-05-09 + ImageDescriptionCreator: creates a text with a textual description with the properties of a JIU image object o changed JiuAwtFrame to use ImageDescriptionCreator instead of the image's toString() method 2002-04-25 o TransparencyInformation: removed unnecessary method 2002-04-24 o ByteChannelImage: added check for null so that there are no NullPointerExceptions in getAllocatedData 2002-04-23 o Histogram3D: removed MAX_BITS constant o OnDemandHistogram3D: changed meaning of first three int arguments of (int, int, int, int, int, int) from "maximum number of bits" to "number of entries" o HistogramCreator: changed argument to OnDemandHistogram3D constructor from 8 (number of bits) to 256 (number of entries) o checked out Tuukka Pasanen's codecs that read: ICO, OTA, WBMP, XBM and XPM and adjusted ImageLoader to support those new codecs; they don't work yet, but now I'll only have to remove comment characters to activate them 2002-04-23 o BilevelImage is now an interface + added MemoryBilevelImage (which is just a renamed copy of the class BilevelImage which has now become an interface) o cleaned up a lot of code; mostly calls BilevelImage img = new BilevelImage(x, y) which have now changed to BilevelImage img = new MemoryBilevelImage(x, y) 2001-12-23 + thumb: added example program thumb that creates a JPEG thumbnail from an input image using the com.sun.image.codec.jpeg package 2001-12-22 o Operation: added methods getAbort, setAbort 2001-12-09 o ImageCodec: added abstract method String[] getMimeTypes() o all classes extending image codec: added method String[] getMimeTypes() that returns corresponding MIME type strings for that image type 2001-12-07 o TextureAnalysis: added properties correlation, dissimilarity 2001-12-05 o JiuInfo: changed version from 0.8.0 to 0.9.0 2001-12-05 o released 0.8.0 2001-11-15 o HueSaturationValueDialog: now shows a Panel with the color of the hue to be set 2001-11-14 o EditorState: now redo and undo also take care of handling the modified flag 2001-11-13 o Invert: changed the operation to be an ImageToImageOperation + Moved all AWT related information from SystemInfo to new class AwtInfo in the gui.awt package o Moved the SystemInfo class from apps to util o Modified HistogramSerialization and added menu item Save histogram as... to jiuawt o EditorState: added functionality for undo / redo operations; updated the AWT demo program classes 2001-11-10 + During the last days, made major changes to packages apps and gui.awt; now, processing and GUI logic are in separate classes o RGBToGray: replaced that class with new class RgbToGrayConversion, which has user-defineable color weights + PromoteToGray8: new class, works for BilevelImage objects only + PromoteToPaletted8: new class, works for BilevelImage and Gray8Image objects 2001-11-03 o UniformPaletteQuantizerDialog, jiuawt: adjusted to reflect changes to OrderedDither o AutoDetectColorType: further updated class 2001-11-02 o OrderedDither: now supports dithering from RGB to RGB (with 9 or more bits per pixel) 2001-10-30 o GrayImage: added method getSampleAsFloat o BilevelImage, Gray8Image: implemented method getSampleAsFloat as demanded by changed GrayImage interface 2001-10-27 + HTMLTableCodec: started codec to write pixel image to an HTML file with table cells representing single pixels 2001-10-21 o JiuInfo: changed version from 0.7.0 to 0.8.0 o PCDCodec, ImageLoader: adjusted codec and loader to use the process method o PNGCodec, ImageLoader: adjusted codec and loader to use the process method o ScaleReplication, jiuawt: adjusted ScaleReplication to be an ImageToImageOperation, modified call in jiuawt as well 2001-10-20 o created 0.7.0 release on sourceforge, added release info on freshmeat 2001-10-17 + started manual as docs\manual.tex 2001-10-15 + BMPCodec: add new codec, only writing of uncompressed BMPs is supported (bilevel, gray8, paletted, rgb) o jiuawt, Strings, StringIndexConstants: o ImageLoader: removed buggy MBMCodec from set of codecs to be tried 2001-10-10 + OperationFailedException: new operation class o Operation: added process method o ImageCodec: added class to get and set DataIn/Output and In/OutputStream objects o IFFCodec: adjusted methods to use process instead of load 2001-07-23 o MBMCodec: added support for 8-bits-per-pixel images 2001-07-16 + TextureAnalysis: added class to determine contrast, energy, entropy and homogeneity of a co-occurrence matrix o IntegerDialog: commented out console debug messages o ErrorDiffusionDithering: changed error in docs (7 / 16 instead of 1 / 16) 2001-07-12 o JiuInfo: changed version from 0.6.0 to 0.7.0 o Operation, Parameter: changed name of used exception class MissingValueException (does not exist anymore) to MissingParameterException; thanks to Rasmus Kaj for pointing that out 2001-07-10 + finished new Octree color image quantizer (classes OctreeColorQuantizer, OctreeNode and OcrtreeDialog) + added quantizer (with or without dithering) to jiuawt, under Color | Color image quantization + added MatrixSerialization to write text files with the content of co-occurrence matrices and co-occurrence frequency matrices + added matrix I/O features to jiuawt, under Color | Histogram o released 0.6.0 on SourceForge, updated homepage and created a Freshmeat entry 2001-06-27 + added LookupTableOperation + added NormalizeHistogram + added EqualizeHistogram o jiuawt: fixed bug that displayed wrong file name after saving image as RAS 2001-06-26 o YCbCrConversion: changed floatToInt to use a simple (int) cast instead of Math.round + added net.sourceforge.jiu.ops.ImageToImageOperation; it is supposed to be the base class for all operations that convert an image into another image o added batch file for Nicolas Zin's RTF doclet (http://www.efrei.fr/~zin/RTFDoclet.zip); however, MS Word Viewer does not like the generated RTF file (page 38 ErrorDiffusionDithering seems to be a problem) 2001-06-25 o added 0.5.0 release note to http://freshmeat.net (which is back from the dead after 24 hrs) o JiuInfo: changed version from 0.5.0 to 0.6.0 o moved ArrayScaling and ArrayRotation to net.sourceforge.jiu.util 2001-06-24 + ArbitraryPaletteQuantizer: maps RGB images to any given palette; relatively slow o jiuawt: added Color | Color image quantization | Map to arbitrary palette, to map to websafe palette or palette from file; optional error diffusion dithering o released 0.5.0, updated homepage 2001-06-23 + added WebsafePaletteCreator + PaletteSerialization: load palettes from RGB images, save palettes as ASCII PNM images o jiuawt: added Color | Palette menu o jiuawt: added saving of palettes Color | Palette | Save as... 2001-06-21 + added MeanFilter + added WindowSizeDialog (to enter a pair of odd positive numbers) o modified jiuawt to support mean, median and oil filters o modified predefined ConvolutionKernelFilter emboss; now, a value is added so that the embossed version is brighter o updated MedianCutDialog so that all contour removal parameters can be entered 2001-06-20 + added class HueSaturationValue to adjust saturation and value as relative values (and optionally set hue as an absolute value) + added dialog class HueSaturationValueDialog for HueSaturationValue o adjusted jiuawt, Strings, StringIndexConstants to support hue / saturation / value + created package net.sourceforge.jiu.filters; moved the convolution kernel classes (filter and data) there + added OilFilter + added MedianFilter 2001-06-19 o jiuawt: fixed error with Save as PBM/PGM/PNM; now the correct name of the newly-created file is shown in the title bar o PNMCodec: fixed bug with saving binary PBM files; the colors black and white were inverted and are now correct o jiuawt: fixed bug; applying a convolution kernel filter now leads to a modified flag (asterisk in title bar) 2001-06-18 + added class ConvolutionKernelData to store all information for a convolution kernel filtering operation like blur, sharpen etc. o adjusted ConvolutionKernelFilter to use the new kernel data class o moved sample kernel data from jiuawt to ConvolutionKernelFilter o ConvolutionKernelFilter: added convolution kernel data for horizontal and vertical Sobel and Prewitt filters; updated jiuawt with it + added class Shear to transform package + created ShearDialog in the gui.awt.dialogs package o modified jiuawt to support shearing 2001-06-17 o ErrorDiffusion: implemented error diffusion; included six error diffusion templates + net.sourceforge.jiu.color.quantization: added interface RGBQuantizer and adjusted MedianCut and UniformPaletteQuantizer to implement it o adjusted jiuawt, UniformPaletteQuantizerDialog, MedianCutDialog and ReduceGrayscaleDialog to support error diffusion o JiuInfo: changed version from 0.4.0 to 0.5.0 2001-06-16 o released 0.4.0, updated homepage, updated freshmeat.net entry 2001-06-15 o created packages net.sourceforge.jiu.color.quantization and net.sourceforge.jiu.transform and moved some classes there 2001-06-05 + added Brightness, Contrast and GammaCorrection (all in color); added corresponding menu items to jiuawt + added crop operation and dialog 2001-06-01 + MBMCodec: started codec for the MBM image file format (for the EPOC operating system, which is used in devices like those from Psion) 2001-05-31 + PalmCodec: started codec for the Palm OS (for handheld devices like the Palm Pilot, HandSpring Visor etc.) native image file format o RASCodec: palettes are now read correctly (R-G-B) + RASCodec: now supports writing paletted images 2001-05-29 o JiuInfo: changed version from 0.3.0 to 0.4.0 o MedianCut: fixed bug that prevent color quantization of images where the number of colors in the palette was only a little smaller than the number of used colors in the truecolor source image; added method canBeSplit to MedianCutNode; this returns true iff a node is a leaf AND if its set of colors has more than one element (otherwise, splitting isn't possible, can only split sets with two or more colors) o created JIU entry on freshmeat.net; now waiting for acceptance by editor 2001-05-28 o changed the kernel coefficients of the edge detection filter to do a better job; new coefficients taken from the comp.graphics.algorithms FAQ o updated jiu.sourceforge.net homepage 2001-05-27 o released JIU 0.3.0 2001-04-04 + PNMCodec: got a hint from David Reid that PGM loading doesn't work properly; added loadGrayImage method to do that 2001-02-12 o after yesterday's release of 0.2.0, switched JIU version to 0.3.0 + added support for several languages for the demo apps (better now than later), classes net.sourceforge.jiu.apps.Strings and net.sourceforge.jiu.apps.StringIndexConstants o moved system info functionality from net.sourceforge.jiu.apps.jiuawt to net.sourceforge.jiu.apps.SystemInfo o renamed command line application jiu to jiucli (CLI = command line interface), in package net.sourceforge.jiu.apps + added interface JiuInfo with constants on the JIU version, homepage etc.java-imaging-utilities-0.14.2+3.orig/doc/TODO0000664000000000000000000001307510611673734015401 0ustar JIU - Java Imaging Utilities - To do list ========================================= BUGS These are things that just don't work correctly. Should have highest priority. - Some variants of color reduction on 48 bit RGB images do not seem to work in jiuawt. Must be explored further (memory consumption too high with histogram?). - Resample: downscaling with the Box filter sometimes results in visual artifacts ("bars") - TIFFDecoderLogLuv: some files are decoded incorrectly (e.g. 14o1.tif), the colors are wrong in parts of the image - jiuawt: various combinations of Gray16Image / RGB48Image color reduction and dithering algorithms don't work yet - BorderSampleGenerator: does not do anything yet - MedianCutContourRemoval: palette entries are swapped, but image quality does not improve CHANGES / ADJUSTMENTS / REFACTORING These are things that need some changes although they "kind of work". - RGBQuantizer: there is no clean definition of what createPalette does; something like init or findPalette must be defined - TIFFCodec: move reading of TIFFImageFileDirectory objects to a class of its own (it's complex enough and code must be reused for EXIF JPEGs) - ErrorDiffusionDithering: create a class of its own for dithering templates (instead of int[][]) - ConvolutionKernelFilter: now takes 4 * width * height bytes as temporary buffer, should use less memory; make it an AreaFilterOperation (?) - jiuawt: AutoDetectColorType should give some feedback (image cannot be reduced / etc.) - move TIFF fax decoding to FaxDecoder - TextureAnalysis: separate results of the computation process (data) from the operation (TextureAnalysis); move data into a class of its own - CoOccurrenceMatrix: add implementation that creates new "counters"--(index, value) pairs--on demand instead of allocating a huge array. This will help supporting 16-bit integer channels. DOCUMENTATION Places with too little or misleading or wrong documentation. - ConvolutionKernelData - jiuawtapplet ENHANCEMENTS Smaller things that are useful and should be added. - ConvolutionKernelFilter: different edge treatment types: copy border pixels, copy wrap around pixels, copy pixels to output, zero - IntegerImage: add a method that copies sections to int[] adding border pixels where necessary - matrix creation should become operation - better progress notification for AutoDetectColorType and the color quantization operations - add new color spaces - jiuawt: add saving of single channels as grayscale images - jiu.jar should include one or two images in jiu.jar so that jiuawtapplet has something to work on - PSDCodec: add additional class to read image resources (can be reused for TIFF, PSD resources have a tag of their own) - add class ClusteredDotDitherDialog and use it in jiuawt - jiuawt: add preferences dialog - jiuawt: move progress display from status bar to dialog with an abort button - jiuawt: add feature to pick language, e.g. Edit | Language | English, German, Spanish - jiuawt: save settings (EditorState) - automatically add menu shortcuts, e.g. 'f' for file - Brightness, Contrast: make it possible to specify different brightness values for each channel - AreaFilterOperation: optimize it by separating inner and border regions - OrderedDither: adjust for images with 16 bits per sample, allow truecolor output with more than 8 bpp - PNMCodec: support max value and PAM - AwtOperationProcessor: counting colors in RGB48Image quickly leads to OutOfMemory errors with most images; a new Histogram3D implementation which is very conservative with memory allocation could fix this - TIFFDecoder: support Fill order=2, which requires reordering of bits within a byte https://sourceforge.net/forum/message.php?msg_id=2285841 NEW FEATURES Completely new features, classes or even packages that could be added. - add support for transparency information to all image data classes and the image codecs - make ImageToImageOperations work on arbitrary-sized tiles (some operations demand strict sequential processing of lines and therefore do not allow this); good for: multiple threads working on operation, disk-based images (less I/O) - add some interface for image file metadata - add support for disk-based image data types (net.sourceforge.jiu.data.disk) - add transparency information interface - add transparency classes - add image data class implementing IntegerImage that allows an arbitrary number of channels and channel types (byte, short, ...) - add bitblit package that processes transparency information and also does compositing - add morphology package, in jiuawt Filters | Morphological | Erode, Dilate, Close, Open - add Riemersma dithering - add neural net color quantization - Color | Palette | Sort... - Color | Palette | Set transparency index... - Transformation | Rotate (arbitrary angle)... - add benchmark classes (package?) - add junit test cases - jiuawt: add File | Info - jiuawt: add File | Revert... - jiuawt: add File | Recent > 1. lena.png / 2. mandrill.ppm NEW FEATURES THAT REQUIRE JAVA 1.2+ Compatibility with 1.1 was a requirement - so far. I'm thinking about adding features that will require higher Java versions, stored in parentheses. - ImageCanvas: use bilinear / bicubic interpolation via RenderingHints for displaying images (1.2). On the other hand, an artificially improved image quality may not be the right thing for editing. - jiuawt: print image (1.2, better 1.3) - JiuAwtFrame: maximize window on start-up (1.4) - jiuawt: copy image from / to system clipboard (1.4) - jiuawt: create screen capture (1.3) - jiuawt: fullscreen mode (1.4) java-imaging-utilities-0.14.2+3.orig/create-release.bat0000775000000000000000000000056610611675126017516 0ustar set JIUVER=0.14.2 call create-api-docs.bat call create-jiu.jar.bat echo JIU %JIUVER% class files - website: http://schmidt.devlib.org/jiu/ | zip -z jiu.jar tar -c --group 0 --owner 0 -o -f java-imaging-utilities-%JIUVER%.tar LICENSE README *.bat packages *options jiu.jar exclude.txt META-INF resources net doc -X exclude.txt bzip2 -f -9 java-imaging-utilities-%JIUVER%.tar java-imaging-utilities-0.14.2+3.orig/run-jiuawt.bat0000775000000000000000000000003310420727103016716 0ustar java -mx300m -jar jiu.jar java-imaging-utilities-0.14.2+3.orig/exclude.txt0000664000000000000000000000004507741351406016326 0ustar *.bak *.class *.java~ *.dvi *.ps CVS java-imaging-utilities-0.14.2+3.orig/create-api-docs.bat0000775000000000000000000000060110541051300017543 0ustar rem f:\jdk\bin\javadoc -J-cp -Jf:\jdk\lib\tools.jar -J-Dfile.separator=/ @packages @docs-html-options rem java -Dfile.separator=/ -cp f:\jdk\lib\tools.jar com.sun.tools.javadoc.Main @packages @docs-html-optionsjava -cp f:\jdk\lib\tools.jar com.sun.tools.javadoc.Main @packages @docs-html-options java -cp f:\jdk\lib\tools.jar com.sun.tools.javadoc.Main @packages @docs-html-options java-imaging-utilities-0.14.2+3.orig/docs-html-options0000664000000000000000000000055510611673450017444 0ustar -sourcepath . -header 'JIU 0.14.2' -bottom 'Copyright © 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Marco Schmidt' -linkoffline "http://java.sun.com/j2se/1.4/docs/api/" file:///f:\jdk\docs\api -author -use -quiet -windowtitle "JIU API documentation" -doctitle "JIU API specification" -d doc\api java-imaging-utilities-0.14.2+3.orig/LICENSE0000664000000000000000000004313107741351406015144 0ustar GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. java-imaging-utilities-0.14.2+3.orig/create-jiu.jar.bat0000775000000000000000000000141010420727332017420 0ustar set JIU=net\sourceforge\jiu\ del jiu.jar f:\jdk\bin\jar cmf META-INF\MANIFEST.MF jiu.jar %JIU%apps\*.class %JIU%codecs\jpeg\*.class %JIU%codecs\tiff\*.class %JIU%codecs\*.class f:\jdk\bin\jar uf jiu.jar %JIU%color\adjustment\*.class %JIU%color\analysis\*.class %JIU%color\conversion\*.class %JIU%color\data\*.class f:\jdk\bin\jar uf jiu.jar %JIU%color\dithering\*.class %JIU%color\io\*.class %JIU%color\promotion\*.class %JIU%color\quantization\*.class f:\jdk\bin\jar uf jiu.jar %JIU%color\reduction\*.class %JIU%color\*.class %JIU%data\*.class %JIU%filters\*.class %JIU%geometry\*.class f:\jdk\bin\jar uf jiu.jar %JIU%gui\awt\dialogs\*.class %JIU%gui\awt\*.class %JIU%ops\*.class %JIU%util\*.class f:\jdk\bin\jar uf jiu.jar resources\images\* resources\lang\*.txt java-imaging-utilities-0.14.2+3.orig/META-INF/0000775000000000000000000000000010546531755015301 5ustar java-imaging-utilities-0.14.2+3.orig/META-INF/MANIFEST.MF0000664000000000000000000000015710073023360016715 0ustar Manifest-Version: 1.0 Created-By: 1.3.0 (Sun Microsystems Inc.) main-class: net.sourceforge.jiu.apps.jiuawt