/** * Read and decompress a new scanline from the specified {@link Inflater}. If more data is needed, * then {@code null} is returned. */ public ScanlineWrapper readNext(Inflater inflater) throws IOException { try { // Decompress the filter-type first, that is // contained as first byte in a scanline if (ftype == FILTER_INVALID) { inflater.inflate(buffer, 0, 1); ftype = buffer[0]; } // Decompress one scanline, skipping the already read bytes final int numBytesRequired = length - offset; final int numBytesInflated = inflater.inflate(buffer, offset, numBytesRequired); if (numBytesInflated != numBytesRequired) { // A full scanline could not be read or decompressed, save // the position inside the buffer and wait for more input offset = numBytesInflated; return null; } else offset = 0; } catch (DataFormatException dfe) { // Rethrow as IOException throw new IOException(dfe); } // Ensure a valid filter-type! if (ftype < FILTER_NONE || ftype > FILTER_PAETH) throw new IOException("Invalid filter type found: " + ftype); // Reconstruct the decompressed scanline using its filter final int[] curline = this.getCurScanline(); final int[] preline = this.getPreScanline(); final IScanlineFilter filter = LUT_FILTERS[ftype]; filter.reconstruct(curline, preline, buffer, length, delta); // Prepare for next reconstruction ftype = FILTER_INVALID; // Swap the scanlines! curid = 1 - curid; ++numScanlinesRead; // Return the reconstructed scanline wrapper.setScanline(curline, 0, width); return wrapper; }
/** * Prepare this {@link ScanlineReader} for reading new scanlines of specified width and with * specified number of samples-per-pixel and bits-per-sample. */ public void prepare(int width, int spp, int bps) { final int n = bps >> 3; // Update the current ScanlineWrapper: // bps = 1,2,4 --> bps >> 3 = 0 // bps = 8 --> bps >> 3 = 1 // bps = 16 --> bps >> 3 = 2 wrapper = wrappers[n]; wrapper.prepare(bps); // For reconstruction of a byte of the pixel X the corresponding // byte in the pixel immediately before X should be used. But when // the bitdepth is less than 8, then simply use the previous byte. this.delta = (bps < 8) ? 1 : (spp * n); this.width = width; this.numScanlinesRead = 0; this.ftype = FILTER_INVALID; this.offset = 0; // Calculate the sanline's length in bytes final int numbits = spp * bps * width; this.length = (numbits >> 3); if ((numbits & 0x07) != 0) { // numbits is not a multiple of 8, // one additional byte is needed! ++length; } // Update the buffers for scanlines if (buffer.length < length) { buffer = new byte[length]; scanlines[0] = new int[length]; scanlines[1] = new int[length]; } }