package ar.com.hjg.pngj; /** * 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. * * Format: byte (one bytes per sample) (for 16bpp the extra byte is placed in an * extra array) */ public class ImageLineByte implements IImageLine, IImageLineArray { public final ImageInfo imgInfo; final byte[] scanline; final byte[] scanline2; // only used for 16 bpp (less significant byte) Normally you'd prefer // ImageLineInt in this case protected FilterType filterType; // informational ; only filled by the reader. not significant for // interlaced final int size; // = imgInfo.samplePerRowPacked, if packed:imgInfo.samplePerRow elswhere public ImageLineByte(final ImageInfo imgInfo) { this(imgInfo, null); } public ImageLineByte(final ImageInfo imgInfo, final byte[] sci) { this.imgInfo = imgInfo; filterType = FilterType.FILTER_UNKNOWN; size = imgInfo.samplesPerRow; scanline = sci != null && sci.length >= size ? sci : new byte[size]; scanline2 = imgInfo.bitDepth == 16 ? new byte[size] : null; } /** * Returns a factory for this object */ public static IImageLineFactory getFactory() { return iminfo -> new ImageLineByte(iminfo); } public FilterType getFilterUsed() { return filterType; } /** * One byte per sample. This can be used also for 16bpp images, but in this * case this loses the less significant * 8-bits ; see also getScanlineByte2 and getElem. */ public byte[] getScanlineByte() { return scanline; } /** * only for 16bpp (less significant byte) * * @return null for less than 16bpp */ public byte[] getScanlineByte2() { return scanline2; } /** * Basic info */ @Override public String toString() { return " cols=" + imgInfo.cols + " bpc=" + imgInfo.bitDepth + " size=" + scanline.length; } @Override public void readFromPngRaw(final byte[] raw, final int len, final int offset, final int step) { filterType = FilterType.getByVal(raw[0]); // only for non interlaced line the filter is significative final int len1 = len - 1; final int step1 = (step - 1) * imgInfo.channels; if (imgInfo.bitDepth == 8) { if (step == 1) System.arraycopy(raw, 1, scanline, 0, len1); else for (int s = 1, c = 0, i = offset * imgInfo.channels; s <= len1; s++, i++) { scanline[i] = raw[s]; c++; if (c == imgInfo.channels) { c = 0; i += step1; } } } else if (imgInfo.bitDepth == 16) { if (step == 1) for (int i = 0, s = 1; i < imgInfo.samplesPerRow; i++) { scanline[i] = raw[s++]; // get the first byte scanline2[i] = raw[s++]; // get the first byte } else for (int s = 1, c = 0, i = offset != 0 ? offset * imgInfo.channels : 0; s <= len1; i++) { scanline[i] = raw[s++]; scanline2[i] = raw[s++]; c++; if (c == imgInfo.channels) { c = 0; i += step1; } } } else { // packed formats int mask0, mask, shi, bd; bd = imgInfo.bitDepth; mask0 = ImageLineHelper.getMaskForPackedFormats(bd); for (int i = offset * imgInfo.channels, r = 1, c = 0; r < len; r++) { mask = mask0; shi = 8 - bd; do { scanline[i] = (byte) ((raw[r] & mask) >> shi); mask >>= bd; shi -= bd; i++; c++; if (c == imgInfo.channels) { c = 0; i += step1; } } while (mask != 0 && i < size); } } } @Override public void writeToPngRaw(final byte[] raw) { raw[0] = (byte) filterType.val; if (imgInfo.bitDepth == 8) System.arraycopy(scanline, 0, raw, 1, size); else if (imgInfo.bitDepth == 16) for (int i = 0, s = 1; i < size; i++) { raw[s++] = scanline[i]; raw[s++] = scanline2[i]; } else { // packed formats int shi, bd, v; bd = imgInfo.bitDepth; shi = 8 - bd; v = 0; for (int i = 0, r = 1; i < size; i++) { v |= scanline[i] << shi; shi -= bd; if (shi < 0 || i == size - 1) { raw[r++] = (byte) v; shi = 8 - bd; v = 0; } } } } @Override public void endReadFromPngRaw() {} @Override public int getSize() { return size; } @Override public int getElem(final int i) { return scanline2 == null ? scanline[i] & 0xFF : (scanline[i] & 0xFF) << 8 | scanline2[i] & 0xFF; } public byte[] getScanline() { return scanline; } @Override public ImageInfo getImageInfo() { return imgInfo; } @Override public FilterType getFilterType() { return filterType; } /** * This should rarely be used by client code. Only relevant if * FilterPreserve==true */ public void setFilterType(final FilterType ft) { filterType = ft; } }