package ar.com.hjg.pngj; public class Deinterlacer { final ImageInfo imi; private int pass; // 1-7 private int rows, cols; int dY, dX, oY, oX; // current step and offset (in pixels) int oXsamples, dXsamples; // step in samples // current row in the virtual subsampled image; this increments (by 1) from 0 to rows/dy 7 times private int currRowSubimg; // in the real image, this will cycle from 0 to im.rows in different steps, 7 times private int currRowReal; private int currRowSeq; // not counting empty rows int totalRows; private boolean ended = false; public Deinterlacer(final ImageInfo iminfo) { imi = iminfo; pass = 0; currRowSubimg = -1; currRowReal = -1; currRowSeq = 0; ended = false; totalRows = 0; // lazy compute setPass(1); setRow(0); } /** this refers to the row currRowSubimg */ private void setRow(final int n) { // This should be called only intercally, in sequential order currRowSubimg = n; currRowReal = n * dY + oY; if (currRowReal < 0 || currRowReal >= imi.rows) throw new PngjExceptionInternal("bad row - this should not happen"); } /** Skips passes with no rows. Return false is no more rows */ boolean nextRow() { currRowSeq++; if (rows == 0 || currRowSubimg >= rows - 1) { // next pass if (pass == 7) { ended = true; return false; } setPass(pass + 1); if (rows == 0) { currRowSeq--; return nextRow(); } setRow(0); } else setRow(currRowSubimg + 1); return true; } boolean isEnded() { return ended; } void setPass(final int p) { if (pass == p) return; pass = p; final byte[] pp = Deinterlacer.paramsForPass(p);// dx,dy,ox,oy dX = pp[0]; dY = pp[1]; oX = pp[2]; oY = pp[3]; rows = imi.rows > oY ? (imi.rows + dY - 1 - oY) / dY : 0; cols = imi.cols > oX ? (imi.cols + dX - 1 - oX) / dX : 0; if (cols == 0) rows = 0; // well, really... dXsamples = dX * imi.channels; oXsamples = oX * imi.channels; } static byte[] paramsForPass(final int p) {// dx,dy,ox,oy switch (p) { case 1: return new byte[] { 8, 8, 0, 0 }; case 2: return new byte[] { 8, 8, 4, 0 }; case 3: return new byte[] { 4, 8, 0, 4 }; case 4: return new byte[] { 4, 4, 2, 0 }; case 5: return new byte[] { 2, 4, 0, 2 }; case 6: return new byte[] { 2, 2, 1, 0 }; case 7: return new byte[] { 1, 2, 0, 1 }; default: throw new PngjExceptionInternal("bad interlace pass" + p); } } /** * current row number inside the "sub image" */ int getCurrRowSubimg() { return currRowSubimg; } /** * current row number inside the "real image" */ int getCurrRowReal() { return currRowReal; } /** * current pass number (1-7) */ int getPass() { return pass; } /** * How many rows has the current pass? **/ int getRows() { return rows; } /** * How many columns (pixels) are there in the current row */ int getCols() { return cols; } public int getPixelsToRead() { return getCols(); } public int getBytesToRead() { // not including filter byte return (imi.bitspPixel * getPixelsToRead() + 7) / 8; } public int getdY() { return dY; } /* * in pixels */ public int getdX() { return dX; } public int getoY() { return oY; } /* * in pixels */ public int getoX() { return oX; } public int getTotalRows() { if (totalRows == 0) for (int p = 1; p <= 7; p++) { final byte[] pp = Deinterlacer.paramsForPass(p); // dx dy ox oy final int rows = imi.rows > pp[3] ? (imi.rows + pp[1] - 1 - pp[3]) / pp[1] : 0; final int cols = imi.cols > pp[2] ? (imi.cols + pp[0] - 1 - pp[2]) / pp[0] : 0; if (rows > 0 && cols > 0) totalRows += rows; } return totalRows; } /** * total unfiltered bytes in the image, including the filter byte */ public long getTotalRawBytes() { // including the filter byte long bytes = 0; for (int p = 1; p <= 7; p++) { final byte[] pp = Deinterlacer.paramsForPass(p); // dx dy ox oy final int rows = imi.rows > pp[3] ? (imi.rows + pp[1] - 1 - pp[3]) / pp[1] : 0; final int cols = imi.cols > pp[2] ? (imi.cols + pp[0] - 1 - pp[2]) / pp[0] : 0; final int bytesr = (imi.bitspPixel * cols + 7) / 8; // without filter byte if (rows > 0 && cols > 0) bytes += rows * (1 + (long) bytesr); } return bytes; } public int getCurrRowSeq() { return currRowSeq; } }