aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2012-12-31 16:39:15 +0100
committerSven Gothel <[email protected]>2012-12-31 16:39:15 +0100
commit921b33825340d27deec2883ded21cb7434decc94 (patch)
tree164ce843d4939144f63fd7d7db24124c86a332d4 /src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java
parent96f8f55a8db5f430fc17ce9ea1bf45e4728ab6ac (diff)
Update PNGJ 0.85 -> 1.12 (w/ interlace read support) ; Added PNG Interlace read tests (TestPNGTextureFromFileNEWT)
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java')
-rw-r--r--src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java355
1 files changed, 250 insertions, 105 deletions
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java
index bfbb35b7c..9f8a13230 100644
--- a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java
+++ b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java
@@ -1,6 +1,6 @@
package jogamp.opengl.util.pngj;
-import java.util.Arrays;
+import jogamp.opengl.util.pngj.ImageLineHelper.ImageLineStats;
/**
* Lightweight wrapper for an image scanline, used for read and write.
@@ -20,26 +20,87 @@ public class ImageLine {
/**
* The 'scanline' is an array of integers, corresponds to an image line (row).
* <p>
- * Except for 'packed' formats (gray/indexed with 1-2-4 bitdepth) each int is a "sample" (one for channel), (0-255
- * or 0-65535) in the respective PNG sequence sequence : (R G B R G B...) or (R G B A R G B A...) or (g g g ...) or
- * ( i i i) (palette index)
+ * Except for 'packed' formats (gray/indexed with 1-2-4 bitdepth) each <code>int</code> is a "sample" (one for
+ * channel), (0-255 or 0-65535) in the corresponding PNG sequence: <code>R G B R G B...</code> or
+ * <code>R G B A R G B A...</tt>
+ * or <code>g g g ...</code> or <code>i i i</code> (palette index)
* <p>
- * For bitdepth 1/2/4 , each element is a PACKED byte! To get an unpacked copy, see <code>tf_pack()</code> and its
- * inverse <code>tf_unpack()</code>
+ * For bitdepth=1/2/4 , and if samplesUnpacked=false, each value is a PACKED byte!
* <p>
- * To convert a indexed line to RGB balues, see <code>ImageLineHelper.tf_palIdx2RGB()</code> (can't do the reverse)
+ * To convert a indexed line to RGB balues, see <code>ImageLineHelper.palIdx2RGB()</code> (you can't do the reverse)
*/
- public final int[] scanline; // see explanation above!!
+ public final int[] scanline;
+ /**
+ * Same as {@link #scanline}, but with one byte per sample. Only one of scanline and scanlineb is valid - this
+ * depends on {@link #sampleType}
+ */
+ public final byte[] scanlineb;
protected FilterType filterUsed; // informational ; only filled by the reader
- public final int channels; // copied from imgInfo, more handy
- public final int bitDepth; // copied from imgInfo, more handy
+ final int channels; // copied from imgInfo, more handy
+ final int bitDepth; // copied from imgInfo, more handy
+ final int elementsPerRow; // = imgInfo.samplePerRowPacked, if packed:imgInfo.samplePerRow elswhere
+
+ public enum SampleType {
+ INT, // 4 bytes per sample
+ // SHORT, // 2 bytes per sample
+ BYTE // 1 byte per sample
+ }
+
+ /**
+ * tells if we are using BYTE or INT to store the samples.
+ */
+ public final SampleType sampleType;
+
+ /**
+ * true: each element of the scanline array represents a sample always, even for internally packed PNG formats
+ *
+ * false: if the original image was of packed type (bit depth less than 8) we keep samples packed in a single array
+ * element
+ */
+ public final boolean samplesUnpacked;
+ /**
+ * default mode: INT packed
+ */
public ImageLine(ImageInfo imgInfo) {
+ this(imgInfo, SampleType.INT, false);
+ }
+
+ /**
+ *
+ * @param imgInfo
+ * Inmutable ImageInfo, basic parameter of the image we are reading or writing
+ * @param stype
+ * INT or BYTE : this determines which scanline is the really used one
+ * @param unpackedMode
+ * If true, we use unpacked format, even for packed original images
+ *
+ */
+ public ImageLine(ImageInfo imgInfo, SampleType stype, boolean unpackedMode) {
+ this(imgInfo, stype, unpackedMode, null, null);
+ }
+
+ /**
+ * If a preallocated array is passed, the copy is shallow
+ */
+ ImageLine(ImageInfo imgInfo, SampleType stype, boolean unpackedMode, int[] sci, byte[] scb) {
this.imgInfo = imgInfo;
channels = imgInfo.channels;
- scanline = new int[imgInfo.samplesPerRowP];
- this.bitDepth = imgInfo.bitDepth;
+ bitDepth = imgInfo.bitDepth;
+ filterUsed = FilterType.FILTER_UNKNOWN;
+ this.sampleType = stype;
+ this.samplesUnpacked = unpackedMode || !imgInfo.packed;
+ elementsPerRow = this.samplesUnpacked ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked;
+ if (stype == SampleType.INT) {
+ scanline = sci != null ? sci : new int[elementsPerRow];
+ scanlineb = null;
+ } else if (stype == SampleType.BYTE) {
+ scanlineb = scb != null ? scb : new byte[elementsPerRow];
+ scanline = null;
+ } else
+ throw new PngjExceptionInternal("bad ImageLine initialization");
+ this.rown = -1;
}
/** This row number inside the image (0 is top) */
@@ -47,129 +108,213 @@ public class ImageLine {
return rown;
}
- /** Increments row number */
- public void incRown() {
- this.rown++;
- }
-
- /** Sets row number */
+ /** Sets row number (0 : Rows-1) */
public void setRown(int n) {
this.rown = n;
}
- /** Sets scanline, making copy from passed array */
- public void setScanLine(int[] b) {
- System.arraycopy(b, 0, scanline, 0, scanline.length);
+ /*
+ * Unpacks scanline (for bitdepth 1-2-4)
+ *
+ * Arrays must be prealocated. src : samplesPerRowPacked dst : samplesPerRow
+ *
+ * This usually works in place (with src==dst and length=samplesPerRow)!
+ *
+ * If not, you should only call this only when necesary (bitdepth <8)
+ *
+ * If <code>scale==true<code>, it scales the value (just a bit shift) towards 0-255.
+ */
+ static void unpackInplaceInt(final ImageInfo iminfo, final int[] src, final int[] dst, final boolean scale) {
+ final int bitDepth = iminfo.bitDepth;
+ if (bitDepth >= 8)
+ return; // nothing to do
+ final int mask0 = ImageLineHelper.getMaskForPackedFormatsLs(bitDepth);
+ final int scalefactor = 8 - bitDepth;
+ final int offset0 = 8 * iminfo.samplesPerRowPacked - bitDepth * iminfo.samplesPerRow;
+ int mask, offset, v;
+ if (offset0 != 8) {
+ mask = mask0 << offset0;
+ offset = offset0; // how many bits to shift the mask to the right to recover mask0
+ } else {
+ mask = mask0;
+ offset = 0;
+ }
+ for (int j = iminfo.samplesPerRow - 1, i = iminfo.samplesPerRowPacked - 1; j >= 0; j--) {
+ v = (src[i] & mask) >> offset;
+ if (scale)
+ v <<= scalefactor;
+ dst[j] = v;
+ mask <<= bitDepth;
+ offset += bitDepth;
+ if (offset == 8) {
+ mask = mask0;
+ offset = 0;
+ i--;
+ }
+ }
}
- /**
- * Returns a copy from scanline, in byte array.
+ /*
+ * Unpacks scanline (for bitdepth 1-2-4)
*
- * You can (OPTIONALLY) pass an preallocated array to use.
- **/
- public int[] getScanLineCopy(int[] b) {
- if (b == null || b.length < scanline.length)
- b = new int[scanline.length];
- System.arraycopy(scanline, 0, b, 0, scanline.length);
- return b;
+ * Arrays must be prealocated. src : samplesPerRow dst : samplesPerRowPacked
+ *
+ * This usually works in place (with src==dst and length=samplesPerRow)! If not, you should only call this only when
+ * necesary (bitdepth <8)
+ *
+ * The trailing elements are trash
+ *
+ *
+ * If <code>scale==true<code>, it scales the value (just a bit shift) towards 0-255.
+ */
+ static void packInplaceInt(final ImageInfo iminfo, final int[] src, final int[] dst, final boolean scaled) {
+ final int bitDepth = iminfo.bitDepth;
+ if (bitDepth >= 8)
+ return; // nothing to do
+ final int mask0 = ImageLineHelper.getMaskForPackedFormatsLs(bitDepth);
+ final int scalefactor = 8 - bitDepth;
+ final int offset0 = 8 - bitDepth;
+ int v, v0;
+ int offset = 8 - bitDepth;
+ v0 = src[0]; // first value is special for in place
+ dst[0] = 0;
+ if (scaled)
+ v0 >>= scalefactor;
+ v0 = ((v0 & mask0) << offset);
+ for (int i = 0, j = 0; j < iminfo.samplesPerRow; j++) {
+ v = src[j];
+ if (scaled)
+ v >>= scalefactor;
+ dst[i] |= ((v & mask0) << offset);
+ offset -= bitDepth;
+ if (offset < 0) {
+ offset = offset0;
+ i++;
+ dst[i] = 0;
+ }
+ }
+ dst[0] |= v0;
}
- /**
- * Unpacks scanline (for bitdepth 1-2-4) into buffer.
- * <p>
- * You can (OPTIONALLY) pass an preallocated array to use.
- * <p>
- * If scale==TRUE scales the value (just a bit shift).
- */
- public int[] tf_unpack(int[] buf, boolean scale) {
- int len = scanline.length;
- if (bitDepth == 1)
- len *= 8;
- else if (bitDepth == 2)
- len *= 4;
- else if (bitDepth == 4)
- len *= 2;
- if (buf == null)
- buf = new int[len];
+ static void unpackInplaceByte(final ImageInfo iminfo, final byte[] src, final byte[] dst, final boolean scale) {
+ final int bitDepth = iminfo.bitDepth;
if (bitDepth >= 8)
- System.arraycopy(scanline, 0, buf, 0, scanline.length);
- else {
- int mask, offset, v;
- int mask0 = getMaskForPackedFormats();
- int offset0 = 8 - bitDepth;
+ return; // nothing to do
+ final int mask0 = ImageLineHelper.getMaskForPackedFormatsLs(bitDepth);
+ final int scalefactor = 8 - bitDepth;
+ final int offset0 = 8 * iminfo.samplesPerRowPacked - bitDepth * iminfo.samplesPerRow;
+ int mask, offset, v;
+ if (offset0 != 8) {
+ mask = mask0 << offset0;
+ offset = offset0; // how many bits to shift the mask to the right to recover mask0
+ } else {
mask = mask0;
- offset = offset0;
- for (int i = 0, j = 0; i < len; i++) {
- v = (scanline[j] & mask) >> offset;
- if (scale)
- v <<= offset0;
- buf[i] = v;
- mask = mask >> bitDepth;
- offset -= bitDepth;
- if (mask == 0) { // new byte in source
- mask = mask0;
- offset = offset0;
- j++;
- }
+ offset = 0;
+ }
+ for (int j = iminfo.samplesPerRow - 1, i = iminfo.samplesPerRowPacked - 1; j >= 0; j--) {
+ v = (src[i] & mask) >> offset;
+ if (scale)
+ v <<= scalefactor;
+ dst[j] = (byte) v;
+ mask <<= bitDepth;
+ offset += bitDepth;
+ if (offset == 8) {
+ mask = mask0;
+ offset = 0;
+ i--;
}
}
- return buf;
}
- /**
- * Packs scanline (for bitdepth 1-2-4) from buffer.
- * <p>
- * If scale==TRUE scales the value (just a bit shift).
- */
- public void tf_pack(int[] buf, boolean scale) { // writes scanline
- int len = scanline.length;
- if (bitDepth == 1)
- len *= 8;
- else if (bitDepth == 2)
- len *= 4;
- else if (bitDepth == 4)
- len *= 2;
+ /** size original: samplesPerRow sizeFinal: samplesPerRowPacked (trailing elements are trash!) **/
+ static void packInplaceByte(final ImageInfo iminfo, final byte[] src, final byte[] dst, final boolean scaled) {
+ final int bitDepth = iminfo.bitDepth;
if (bitDepth >= 8)
- System.arraycopy(buf, 0, scanline, 0, scanline.length);
- else {
- int offset0 = 8 - bitDepth;
- int mask0 = getMaskForPackedFormats() >> offset0;
- int offset, v;
- offset = offset0;
- Arrays.fill(scanline, 0);
- for (int i = 0, j = 0; i < len; i++) {
- v = buf[i];
- if (scale)
- v >>= offset0;
- v = (v & mask0) << offset;
- scanline[j] |= v;
- offset -= bitDepth;
- if (offset < 0) { // new byte in scanline
- offset = offset0;
- j++;
- }
+ return; // nothing to do
+ final int mask0 = ImageLineHelper.getMaskForPackedFormatsLs(bitDepth);
+ final int scalefactor = 8 - bitDepth;
+ final int offset0 = 8 - bitDepth;
+ int v, v0;
+ int offset = 8 - bitDepth;
+ v0 = src[0]; // first value is special
+ dst[0] = 0;
+ if (scaled)
+ v0 >>= scalefactor;
+ v0 = ((v0 & mask0) << offset);
+ for (int i = 0, j = 0; j < iminfo.samplesPerRow; j++) {
+ v = src[j];
+ if (scaled)
+ v >>= scalefactor;
+ dst[i] |= ((v & mask0) << offset);
+ offset -= bitDepth;
+ if (offset < 0) {
+ offset = offset0;
+ i++;
+ dst[i] = 0;
}
}
+ dst[0] |= v0;
}
- private int getMaskForPackedFormats() { // Utility function for pacj/unpack
- if (bitDepth == 1)
- return 0x80;
- if (bitDepth == 2)
- return 0xc0;
- if (bitDepth == 4)
- return 0xf0;
- throw new RuntimeException("?");
+ /**
+ * Creates a new ImageLine similar to this, but unpacked
+ *
+ * The caller must be sure that the original was really packed
+ */
+ public ImageLine unpackToNewImageLine() {
+ ImageLine newline = new ImageLine(imgInfo, sampleType, true);
+ if (sampleType == SampleType.INT)
+ unpackInplaceInt(imgInfo, scanline, newline.scanline, false);
+ else
+ unpackInplaceByte(imgInfo, scanlineb, newline.scanlineb, false);
+ return newline;
+ }
+
+ /**
+ * Creates a new ImageLine similar to this, but packed
+ *
+ * The caller must be sure that the original was really unpacked
+ */
+ public ImageLine packToNewImageLine() {
+ ImageLine newline = new ImageLine(imgInfo, sampleType, false);
+ if (sampleType == SampleType.INT)
+ packInplaceInt(imgInfo, scanline, newline.scanline, false);
+ else
+ packInplaceByte(imgInfo, scanlineb, newline.scanlineb, false);
+ return newline;
}
public FilterType getFilterUsed() {
return filterUsed;
}
+ public void setFilterUsed(FilterType ft) {
+ filterUsed = ft;
+ }
+
+ public int[] getScanlineInt() {
+ return scanline;
+ }
+
+ public byte[] getScanlineByte() {
+ return scanlineb;
+ }
+
/**
* Basic info
*/
public String toString() {
return "row=" + rown + " cols=" + imgInfo.cols + " bpc=" + imgInfo.bitDepth + " size=" + scanline.length;
}
+
+ /**
+ * Prints some statistics - just for debugging
+ */
+ public static void showLineInfo(ImageLine line) {
+ System.out.println(line);
+ ImageLineStats stats = new ImageLineHelper.ImageLineStats(line);
+ System.out.println(stats);
+ System.out.println(ImageLineHelper.infoFirstLastPixels(line));
+ }
+
}