package jogamp.opengl.util.pngj; import java.util.Arrays; /** * Lightweight wrapper for an image scanline, used for read and write. *
* This object can be (usually it is) reused while iterating over the image lines. *
* See scanline
field, to understand the format.
*/
public class ImageLine {
public final ImageInfo imgInfo;
/**
* tracks the current row number (from 0 to rows-1)
*/
private int rown = 0;
/**
* The 'scanline' is an array of integers, corresponds to an image line (row).
*
* 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) *
* For bitdepth 1/2/4 , each element is a PACKED byte! To get an unpacked copy, see tf_pack()
and its
* inverse tf_unpack()
*
* To convert a indexed line to RGB balues, see ImageLineHelper.tf_palIdx2RGB()
(can't do the reverse)
*/
public final int[] scanline; // see explanation above!!
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
public ImageLine(ImageInfo imgInfo) {
this.imgInfo = imgInfo;
channels = imgInfo.channels;
scanline = new int[imgInfo.samplesPerRowP];
this.bitDepth = imgInfo.bitDepth;
}
/** This row number inside the image (0 is top) */
public int getRown() {
return rown;
}
/** Increments row number */
public void incRown() {
this.rown++;
}
/** Sets row number */
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);
}
/**
* Returns a copy from scanline, in byte array.
*
* 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;
}
/**
* Unpacks scanline (for bitdepth 1-2-4) into buffer.
*
* You can (OPTIONALLY) pass an preallocated array to use. *
* 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]; if (bitDepth >= 8) System.arraycopy(scanline, 0, buf, 0, scanline.length); else { int mask, offset, v; int mask0 = getMaskForPackedFormats(); int offset0 = 8 - bitDepth; 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++; } } } return buf; } /** * Packs scanline (for bitdepth 1-2-4) from buffer. *
* 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; 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++; } } } } 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("?"); } public FilterType getFilterUsed() { return filterUsed; } /** * Basic info */ public String toString() { return "row=" + rown + " cols=" + imgInfo.cols + " bpc=" + imgInfo.bitDepth + " size=" + scanline.length; } }