All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.jbig2.image.JBIG2Bitmap Maven / Gradle / Ivy

The newest version!
/**
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.jpedal.org
 * (C) Copyright 1997-2008, IDRsolutions and Contributors.
 * Main Developer: Simon Barnett
 *
 * 	This file is part of JPedal
 *
 * Copyright (c) 2008, IDRsolutions
 * Copyright (c) 2011, Boris von Loesch 
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the IDRsolutions nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY IDRsolutions ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL IDRsolutions BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Other JBIG2 image decoding implementations include
 * jbig2dec (http://jbig2dec.sourceforge.net/)
 * xpdf (http://www.foolabs.com/xpdf/)
 * 
 * The final draft JBIG2 specification can be found at http://www.jpeg.org/public/fcd14492.pdf
 * 
 * All three of the above resources were used in the writing of this software, with methodologies,
 * processes and inspiration taken from all three.
 *
 * ---------------
 * JBIG2Bitmap.java
 * ---------------
 */
package org.jpedal.jbig2.image;

import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.IOException;

import org.jpedal.jbig2.JBIG2Exception;
import org.jpedal.jbig2.decoders.ArithmeticDecoder;
import org.jpedal.jbig2.decoders.DecodeIntResult;
import org.jpedal.jbig2.decoders.HuffmanDecoder;
import org.jpedal.jbig2.decoders.JBIG2StreamDecoder;
import org.jpedal.jbig2.decoders.MMRDecoder;
import org.jpedal.jbig2.util.BinaryOperation;

public final class JBIG2Bitmap {

	private int width, height, line;
	private int bitmapNumber;
	public FastBitSet data;

	// private BitSet data;

	// private static int counter = 0;

	private ArithmeticDecoder arithmeticDecoder;
	private HuffmanDecoder huffmanDecoder;
	private MMRDecoder mmrDecoder;

	public JBIG2Bitmap(int width, int height, ArithmeticDecoder arithmeticDecoder, HuffmanDecoder huffmanDecoder, MMRDecoder mmrDecoder) {
		this.width = width;
		this.height = height;
		this.arithmeticDecoder = arithmeticDecoder;
		this.huffmanDecoder = huffmanDecoder;
		this.mmrDecoder = mmrDecoder;

		this.line = (width + 7) >> 3;

		this.data = new FastBitSet(width * height);
	}

	public void readBitmap(boolean useMMR, int template, boolean typicalPredictionGenericDecodingOn, boolean useSkip, JBIG2Bitmap skipBitmap,
			short[] adaptiveTemplateX, short[] adaptiveTemplateY, int mmrDataLength) throws IOException, JBIG2Exception {

		if (useMMR) {

			// MMRDecoder mmrDecoder = MMRDecoder.getInstance();
			this.mmrDecoder.reset();

			int[] referenceLine = new int[this.width + 2];
			int[] codingLine = new int[this.width + 2];
			codingLine[0] = codingLine[1] = this.width;

			for (int row = 0; row < this.height; row++) {

				int i = 0;
				for (; codingLine[i] < this.width; i++) {
					referenceLine[i] = codingLine[i];
				}
				referenceLine[i] = referenceLine[i + 1] = this.width;

				int referenceI = 0;
				int codingI = 0;
				int a0 = 0;

				do {
					int code1 = this.mmrDecoder.get2DCode(), code2, code3;

					switch (code1) {
						case MMRDecoder.twoDimensionalPass:
							if (referenceLine[referenceI] < this.width) {
								a0 = referenceLine[referenceI + 1];
								referenceI += 2;
							}
							break;
						case MMRDecoder.twoDimensionalHorizontal:
							if ((codingI & 1) != 0) {
								code1 = 0;
								do {
									code1 += code3 = this.mmrDecoder.getBlackCode();
								}
								while (code3 >= 64);

								code2 = 0;
								do {
									code2 += code3 = this.mmrDecoder.getWhiteCode();
								}
								while (code3 >= 64);
							}
							else {
								code1 = 0;
								do {
									code1 += code3 = this.mmrDecoder.getWhiteCode();
								}
								while (code3 >= 64);

								code2 = 0;
								do {
									code2 += code3 = this.mmrDecoder.getBlackCode();
								}
								while (code3 >= 64);

							}
							if (code1 > 0 || code2 > 0) {
								a0 = codingLine[codingI++] = a0 + code1;
								a0 = codingLine[codingI++] = a0 + code2;

								while (referenceLine[referenceI] <= a0 && referenceLine[referenceI] < this.width) {
									referenceI += 2;
								}
							}
							break;
						case MMRDecoder.twoDimensionalVertical0:
							a0 = codingLine[codingI++] = referenceLine[referenceI];
							if (referenceLine[referenceI] < this.width) {
								referenceI++;
							}

							break;
						case MMRDecoder.twoDimensionalVerticalR1:
							a0 = codingLine[codingI++] = referenceLine[referenceI] + 1;
							if (referenceLine[referenceI] < this.width) {
								referenceI++;
								while (referenceLine[referenceI] <= a0 && referenceLine[referenceI] < this.width) {
									referenceI += 2;
								}
							}

							break;
						case MMRDecoder.twoDimensionalVerticalR2:
							a0 = codingLine[codingI++] = referenceLine[referenceI] + 2;
							if (referenceLine[referenceI] < this.width) {
								referenceI++;
								while (referenceLine[referenceI] <= a0 && referenceLine[referenceI] < this.width) {
									referenceI += 2;
								}
							}

							break;
						case MMRDecoder.twoDimensionalVerticalR3:
							a0 = codingLine[codingI++] = referenceLine[referenceI] + 3;
							if (referenceLine[referenceI] < this.width) {
								referenceI++;
								while (referenceLine[referenceI] <= a0 && referenceLine[referenceI] < this.width) {
									referenceI += 2;
								}
							}

							break;
						case MMRDecoder.twoDimensionalVerticalL1:
							a0 = codingLine[codingI++] = referenceLine[referenceI] - 1;
							if (referenceI > 0) {
								referenceI--;
							}
							else {
								referenceI++;
							}

							while (referenceLine[referenceI] <= a0 && referenceLine[referenceI] < this.width) {
								referenceI += 2;
							}

							break;
						case MMRDecoder.twoDimensionalVerticalL2:
							a0 = codingLine[codingI++] = referenceLine[referenceI] - 2;
							if (referenceI > 0) {
								referenceI--;
							}
							else {
								referenceI++;
							}

							while (referenceLine[referenceI] <= a0 && referenceLine[referenceI] < this.width) {
								referenceI += 2;
							}

							break;
						case MMRDecoder.twoDimensionalVerticalL3:
							a0 = codingLine[codingI++] = referenceLine[referenceI] - 3;
							if (referenceI > 0) {
								referenceI--;
							}
							else {
								referenceI++;
							}

							while (referenceLine[referenceI] <= a0 && referenceLine[referenceI] < this.width) {
								referenceI += 2;
							}

							break;
						default:
							if (JBIG2StreamDecoder.debug) System.out.println("Illegal code in JBIG2 MMR bitmap data");

							break;
					}
				}
				while (a0 < this.width);

				codingLine[codingI++] = this.width;

				for (int j = 0; codingLine[j] < this.width; j += 2) {
					for (int col = codingLine[j]; col < codingLine[j + 1]; col++) {
						setPixel(col, row, 1);
					}
				}
			}

			if (mmrDataLength >= 0) {
				this.mmrDecoder.skipTo(mmrDataLength);
			}
			else {
				if (this.mmrDecoder.get24Bits() != 0x001001) {
					if (JBIG2StreamDecoder.debug) System.out.println("Missing EOFB in JBIG2 MMR bitmap data");
				}
			}

		}
		else {

			// ArithmeticDecoder arithmeticDecoder = ArithmeticDecoder.getInstance();

			BitmapPointer cxPtr0 = new BitmapPointer(this), cxPtr1 = new BitmapPointer(this);
			BitmapPointer atPtr0 = new BitmapPointer(this), atPtr1 = new BitmapPointer(this), atPtr2 = new BitmapPointer(this), atPtr3 = new BitmapPointer(
					this);

			long ltpCX = 0;
			if (typicalPredictionGenericDecodingOn) {
				switch (template) {
					case 0:
						ltpCX = 0x3953;
						break;
					case 1:
						ltpCX = 0x079a;
						break;
					case 2:
						ltpCX = 0x0e3;
						break;
					case 3:
						ltpCX = 0x18a;
						break;
				}
			}

			boolean ltp = false;
			long cx, cx0, cx1, cx2;

			for (int row = 0; row < this.height; row++) {
				if (typicalPredictionGenericDecodingOn) {
					int bit = this.arithmeticDecoder.decodeBit(ltpCX, this.arithmeticDecoder.genericRegionStats);
					if (bit != 0) {
						ltp = !ltp;
					}

					if (ltp) {
						duplicateRow(row, row - 1);
						continue;
					}
				}

				int pixel;

				switch (template) {
					case 0:

						cxPtr0.setPointer(0, row - 2);
						cx0 = (cxPtr0.nextPixel() << 1);
						cx0 |= cxPtr0.nextPixel();
						// cx0 = (BinaryOperation.bit32ShiftL(cx0, 1)) | cxPtr0.nextPixel();

						cxPtr1.setPointer(0, row - 1);
						cx1 = (cxPtr1.nextPixel() << 2);
						cx1 |= (cxPtr1.nextPixel() << 1);
						cx1 |= (cxPtr1.nextPixel());

						// cx1 = (BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel();
						// cx1 = (BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel();

						cx2 = 0;

						atPtr0.setPointer(adaptiveTemplateX[0], row + adaptiveTemplateY[0]);
						atPtr1.setPointer(adaptiveTemplateX[1], row + adaptiveTemplateY[1]);
						atPtr2.setPointer(adaptiveTemplateX[2], row + adaptiveTemplateY[2]);
						atPtr3.setPointer(adaptiveTemplateX[3], row + adaptiveTemplateY[3]);

						for (int col = 0; col < this.width; col++) {

							cx = (BinaryOperation.bit32ShiftL(cx0, 13)) | (BinaryOperation.bit32ShiftL(cx1, 8))
									| (BinaryOperation.bit32ShiftL(cx2, 4)) | (atPtr0.nextPixel() << 3) | (atPtr1.nextPixel() << 2)
									| (atPtr2.nextPixel() << 1) | atPtr3.nextPixel();

							if (useSkip && skipBitmap.getPixel(col, row) != 0) {
								pixel = 0;
							}
							else {
								pixel = this.arithmeticDecoder.decodeBit(cx, this.arithmeticDecoder.genericRegionStats);
								if (pixel != 0) {
									this.data.set(row * this.width + col);
								}
							}

							cx0 = ((BinaryOperation.bit32ShiftL(cx0, 1)) | cxPtr0.nextPixel()) & 0x07;
							cx1 = ((BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel()) & 0x1f;
							cx2 = ((BinaryOperation.bit32ShiftL(cx2, 1)) | pixel) & 0x0f;
						}
						break;

					case 1:

						cxPtr0.setPointer(0, row - 2);
						cx0 = (cxPtr0.nextPixel() << 2);
						cx0 |= (cxPtr0.nextPixel() << 1);
						cx0 |= (cxPtr0.nextPixel());
						/*
						 * cx0 = cxPtr0.nextPixel(); cx0 = (BinaryOperation.bit32ShiftL(cx0, 1)) | cxPtr0.nextPixel(); cx0 =
						 * (BinaryOperation.bit32ShiftL(cx0, 1)) | cxPtr0.nextPixel();
						 */

						cxPtr1.setPointer(0, row - 1);
						cx1 = (cxPtr1.nextPixel() << 2);
						cx1 |= (cxPtr1.nextPixel() << 1);
						cx1 |= (cxPtr1.nextPixel());
						/*
						 * cx1 = cxPtr1.nextPixel(); cx1 = (BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel(); cx1 =
						 * (BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel();
						 */

						cx2 = 0;

						atPtr0.setPointer(adaptiveTemplateX[0], row + adaptiveTemplateY[0]);

						for (int col = 0; col < this.width; col++) {

							cx = (BinaryOperation.bit32ShiftL(cx0, 9)) | (BinaryOperation.bit32ShiftL(cx1, 4))
									| (BinaryOperation.bit32ShiftL(cx2, 1)) | atPtr0.nextPixel();

							if (useSkip && skipBitmap.getPixel(col, row) != 0) {
								pixel = 0;
							}
							else {
								pixel = this.arithmeticDecoder.decodeBit(cx, this.arithmeticDecoder.genericRegionStats);
								if (pixel != 0) {
									this.data.set(row * this.width + col);
								}
							}

							cx0 = ((BinaryOperation.bit32ShiftL(cx0, 1)) | cxPtr0.nextPixel()) & 0x0f;
							cx1 = ((BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel()) & 0x1f;
							cx2 = ((BinaryOperation.bit32ShiftL(cx2, 1)) | pixel) & 0x07;
						}
						break;

					case 2:

						cxPtr0.setPointer(0, row - 2);
						cx0 = (cxPtr0.nextPixel() << 1);
						cx0 |= (cxPtr0.nextPixel());
						/*
						 * cx0 = cxPtr0.nextPixel(); cx0 = (BinaryOperation.bit32ShiftL(cx0, 1)) | cxPtr0.nextPixel();
						 */

						cxPtr1.setPointer(0, row - 1);
						cx1 = (cxPtr1.nextPixel() << 1);
						cx1 |= (cxPtr1.nextPixel());
						/*
						 * cx1 = cxPtr1.nextPixel(); cx1 = (BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel();
						 */

						cx2 = 0;

						atPtr0.setPointer(adaptiveTemplateX[0], row + adaptiveTemplateY[0]);

						for (int col = 0; col < this.width; col++) {

							cx = (BinaryOperation.bit32ShiftL(cx0, 7)) | (BinaryOperation.bit32ShiftL(cx1, 3))
									| (BinaryOperation.bit32ShiftL(cx2, 1)) | atPtr0.nextPixel();

							if (useSkip && skipBitmap.getPixel(col, row) != 0) {
								pixel = 0;
							}
							else {
								pixel = this.arithmeticDecoder.decodeBit(cx, this.arithmeticDecoder.genericRegionStats);
								if (pixel != 0) {
									this.data.set(row * this.width + col);
								}
							}

							cx0 = ((BinaryOperation.bit32ShiftL(cx0, 1)) | cxPtr0.nextPixel()) & 0x07;
							cx1 = ((BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel()) & 0x0f;
							cx2 = ((BinaryOperation.bit32ShiftL(cx2, 1)) | pixel) & 0x03;
						}
						break;

					case 3:

						cxPtr1.setPointer(0, row - 1);
						cx1 = (cxPtr1.nextPixel() << 1);
						cx1 |= (cxPtr1.nextPixel());
						/*
						 * cx1 = cxPtr1.nextPixel(); cx1 = (BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel();
						 */
						cx2 = 0;

						atPtr0.setPointer(adaptiveTemplateX[0], row + adaptiveTemplateY[0]);

						for (int col = 0; col < this.width; col++) {

							cx = (BinaryOperation.bit32ShiftL(cx1, 5)) | (BinaryOperation.bit32ShiftL(cx2, 1)) | atPtr0.nextPixel();

							if (useSkip && skipBitmap.getPixel(col, row) != 0) {
								pixel = 0;

							}
							else {
								pixel = this.arithmeticDecoder.decodeBit(cx, this.arithmeticDecoder.genericRegionStats);
								if (pixel != 0) {
									this.data.set(row * this.width + col);
								}
							}

							cx1 = ((BinaryOperation.bit32ShiftL(cx1, 1)) | cxPtr1.nextPixel()) & 0x1f;
							cx2 = ((BinaryOperation.bit32ShiftL(cx2, 1)) | pixel) & 0x0f;
						}
						break;
				}
			}
		}
	}

	public void readGenericRefinementRegion(int template, boolean typicalPredictionGenericRefinementOn, JBIG2Bitmap referredToBitmap,
			int referenceDX, int referenceDY, short[] adaptiveTemplateX, short[] adaptiveTemplateY) throws IOException, JBIG2Exception {

		// ArithmeticDecoder arithmeticDecoder = ArithmeticDecoder.getInstance();

		BitmapPointer cxPtr0, cxPtr1, cxPtr2, cxPtr3, cxPtr4, cxPtr5, cxPtr6, typicalPredictionGenericRefinementCXPtr0, typicalPredictionGenericRefinementCXPtr1, typicalPredictionGenericRefinementCXPtr2;

		long ltpCX;
		if (template != 0) {
			ltpCX = 0x008;

			cxPtr0 = new BitmapPointer(this);
			cxPtr1 = new BitmapPointer(this);
			cxPtr2 = new BitmapPointer(referredToBitmap);
			cxPtr3 = new BitmapPointer(referredToBitmap);
			cxPtr4 = new BitmapPointer(referredToBitmap);
			cxPtr5 = new BitmapPointer(this);
			cxPtr6 = new BitmapPointer(this);
			typicalPredictionGenericRefinementCXPtr0 = new BitmapPointer(referredToBitmap);
			typicalPredictionGenericRefinementCXPtr1 = new BitmapPointer(referredToBitmap);
			typicalPredictionGenericRefinementCXPtr2 = new BitmapPointer(referredToBitmap);
		}
		else {
			ltpCX = 0x0010;

			cxPtr0 = new BitmapPointer(this);
			cxPtr1 = new BitmapPointer(this);
			cxPtr2 = new BitmapPointer(referredToBitmap);
			cxPtr3 = new BitmapPointer(referredToBitmap);
			cxPtr4 = new BitmapPointer(referredToBitmap);
			cxPtr5 = new BitmapPointer(this);
			cxPtr6 = new BitmapPointer(referredToBitmap);
			typicalPredictionGenericRefinementCXPtr0 = new BitmapPointer(referredToBitmap);
			typicalPredictionGenericRefinementCXPtr1 = new BitmapPointer(referredToBitmap);
			typicalPredictionGenericRefinementCXPtr2 = new BitmapPointer(referredToBitmap);
		}

		long cx, cx0, cx2, cx3, cx4;
		long typicalPredictionGenericRefinementCX0, typicalPredictionGenericRefinementCX1, typicalPredictionGenericRefinementCX2;
		boolean ltp = false;

		for (int row = 0; row < this.height; row++) {

			if (template != 0) {

				cxPtr0.setPointer(0, row - 1);
				cx0 = cxPtr0.nextPixel();

				cxPtr1.setPointer(-1, row);

				cxPtr2.setPointer(-referenceDX, row - 1 - referenceDY);

				cxPtr3.setPointer(-1 - referenceDX, row - referenceDY);
				cx3 = cxPtr3.nextPixel();
				cx3 = (BinaryOperation.bit32ShiftL(cx3, 1)) | cxPtr3.nextPixel();

				cxPtr4.setPointer(-referenceDX, row + 1 - referenceDY);
				cx4 = cxPtr4.nextPixel();

				typicalPredictionGenericRefinementCX0 = typicalPredictionGenericRefinementCX1 = typicalPredictionGenericRefinementCX2 = 0;

				if (typicalPredictionGenericRefinementOn) {
					typicalPredictionGenericRefinementCXPtr0.setPointer(-1 - referenceDX, row - 1 - referenceDY);
					typicalPredictionGenericRefinementCX0 = typicalPredictionGenericRefinementCXPtr0.nextPixel();
					typicalPredictionGenericRefinementCX0 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX0, 1))
							| typicalPredictionGenericRefinementCXPtr0.nextPixel();
					typicalPredictionGenericRefinementCX0 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX0, 1))
							| typicalPredictionGenericRefinementCXPtr0.nextPixel();

					typicalPredictionGenericRefinementCXPtr1.setPointer(-1 - referenceDX, row - referenceDY);
					typicalPredictionGenericRefinementCX1 = typicalPredictionGenericRefinementCXPtr1.nextPixel();
					typicalPredictionGenericRefinementCX1 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX1, 1))
							| typicalPredictionGenericRefinementCXPtr1.nextPixel();
					typicalPredictionGenericRefinementCX1 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX1, 1))
							| typicalPredictionGenericRefinementCXPtr1.nextPixel();

					typicalPredictionGenericRefinementCXPtr2.setPointer(-1 - referenceDX, row + 1 - referenceDY);
					typicalPredictionGenericRefinementCX2 = typicalPredictionGenericRefinementCXPtr2.nextPixel();
					typicalPredictionGenericRefinementCX2 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX2, 1))
							| typicalPredictionGenericRefinementCXPtr2.nextPixel();
					typicalPredictionGenericRefinementCX2 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX2, 1))
							| typicalPredictionGenericRefinementCXPtr2.nextPixel();
				}

				for (int col = 0; col < this.width; col++) {

					cx0 = ((BinaryOperation.bit32ShiftL(cx0, 1)) | cxPtr0.nextPixel()) & 7;
					cx3 = ((BinaryOperation.bit32ShiftL(cx3, 1)) | cxPtr3.nextPixel()) & 7;
					cx4 = ((BinaryOperation.bit32ShiftL(cx4, 1)) | cxPtr4.nextPixel()) & 3;

					if (typicalPredictionGenericRefinementOn) {
						typicalPredictionGenericRefinementCX0 = ((BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX0, 1)) | typicalPredictionGenericRefinementCXPtr0
								.nextPixel()) & 7;
						typicalPredictionGenericRefinementCX1 = ((BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX1, 1)) | typicalPredictionGenericRefinementCXPtr1
								.nextPixel()) & 7;
						typicalPredictionGenericRefinementCX2 = ((BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX2, 1)) | typicalPredictionGenericRefinementCXPtr2
								.nextPixel()) & 7;

						int decodeBit = this.arithmeticDecoder.decodeBit(ltpCX, this.arithmeticDecoder.refinementRegionStats);
						if (decodeBit != 0) {
							ltp = !ltp;
						}
						if (typicalPredictionGenericRefinementCX0 == 0 && typicalPredictionGenericRefinementCX1 == 0
								&& typicalPredictionGenericRefinementCX2 == 0) {
							setPixel(col, row, 0);
							continue;
						}
						else
							if (typicalPredictionGenericRefinementCX0 == 7 && typicalPredictionGenericRefinementCX1 == 7
									&& typicalPredictionGenericRefinementCX2 == 7) {
								setPixel(col, row, 1);
								continue;
							}
					}

					cx = (BinaryOperation.bit32ShiftL(cx0, 7)) | (cxPtr1.nextPixel() << 6) | (cxPtr2.nextPixel() << 5)
							| (BinaryOperation.bit32ShiftL(cx3, 2)) | cx4;

					int pixel = this.arithmeticDecoder.decodeBit(cx, this.arithmeticDecoder.refinementRegionStats);
					if (pixel == 1) {
						this.data.set(row * this.width + col);
					}
				}

			}
			else {

				cxPtr0.setPointer(0, row - 1);
				cx0 = cxPtr0.nextPixel();

				cxPtr1.setPointer(-1, row);

				cxPtr2.setPointer(-referenceDX, row - 1 - referenceDY);
				cx2 = cxPtr2.nextPixel();

				cxPtr3.setPointer(-1 - referenceDX, row - referenceDY);
				cx3 = cxPtr3.nextPixel();
				cx3 = (BinaryOperation.bit32ShiftL(cx3, 1)) | cxPtr3.nextPixel();

				cxPtr4.setPointer(-1 - referenceDX, row + 1 - referenceDY);
				cx4 = cxPtr4.nextPixel();
				cx4 = (BinaryOperation.bit32ShiftL(cx4, 1)) | cxPtr4.nextPixel();

				cxPtr5.setPointer(adaptiveTemplateX[0], row + adaptiveTemplateY[0]);

				cxPtr6.setPointer(adaptiveTemplateX[1] - referenceDX, row + adaptiveTemplateY[1] - referenceDY);

				typicalPredictionGenericRefinementCX0 = typicalPredictionGenericRefinementCX1 = typicalPredictionGenericRefinementCX2 = 0;
				if (typicalPredictionGenericRefinementOn) {
					typicalPredictionGenericRefinementCXPtr0.setPointer(-1 - referenceDX, row - 1 - referenceDY);
					typicalPredictionGenericRefinementCX0 = typicalPredictionGenericRefinementCXPtr0.nextPixel();
					typicalPredictionGenericRefinementCX0 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX0, 1))
							| typicalPredictionGenericRefinementCXPtr0.nextPixel();
					typicalPredictionGenericRefinementCX0 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX0, 1))
							| typicalPredictionGenericRefinementCXPtr0.nextPixel();

					typicalPredictionGenericRefinementCXPtr1.setPointer(-1 - referenceDX, row - referenceDY);
					typicalPredictionGenericRefinementCX1 = typicalPredictionGenericRefinementCXPtr1.nextPixel();
					typicalPredictionGenericRefinementCX1 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX1, 1))
							| typicalPredictionGenericRefinementCXPtr1.nextPixel();
					typicalPredictionGenericRefinementCX1 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX1, 1))
							| typicalPredictionGenericRefinementCXPtr1.nextPixel();

					typicalPredictionGenericRefinementCXPtr2.setPointer(-1 - referenceDX, row + 1 - referenceDY);
					typicalPredictionGenericRefinementCX2 = typicalPredictionGenericRefinementCXPtr2.nextPixel();
					typicalPredictionGenericRefinementCX2 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX2, 1))
							| typicalPredictionGenericRefinementCXPtr2.nextPixel();
					typicalPredictionGenericRefinementCX2 = (BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX2, 1))
							| typicalPredictionGenericRefinementCXPtr2.nextPixel();
				}

				for (int col = 0; col < this.width; col++) {

					cx0 = ((BinaryOperation.bit32ShiftL(cx0, 1)) | cxPtr0.nextPixel()) & 3;
					cx2 = ((BinaryOperation.bit32ShiftL(cx2, 1)) | cxPtr2.nextPixel()) & 3;
					cx3 = ((BinaryOperation.bit32ShiftL(cx3, 1)) | cxPtr3.nextPixel()) & 7;
					cx4 = ((BinaryOperation.bit32ShiftL(cx4, 1)) | cxPtr4.nextPixel()) & 7;

					if (typicalPredictionGenericRefinementOn) {
						typicalPredictionGenericRefinementCX0 = ((BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX0, 1)) | typicalPredictionGenericRefinementCXPtr0
								.nextPixel()) & 7;
						typicalPredictionGenericRefinementCX1 = ((BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX1, 1)) | typicalPredictionGenericRefinementCXPtr1
								.nextPixel()) & 7;
						typicalPredictionGenericRefinementCX2 = ((BinaryOperation.bit32ShiftL(typicalPredictionGenericRefinementCX2, 1)) | typicalPredictionGenericRefinementCXPtr2
								.nextPixel()) & 7;

						int decodeBit = this.arithmeticDecoder.decodeBit(ltpCX, this.arithmeticDecoder.refinementRegionStats);
						if (decodeBit == 1) {
							ltp = !ltp;
						}
						if (typicalPredictionGenericRefinementCX0 == 0 && typicalPredictionGenericRefinementCX1 == 0
								&& typicalPredictionGenericRefinementCX2 == 0) {
							setPixel(col, row, 0);
							continue;
						}
						else
							if (typicalPredictionGenericRefinementCX0 == 7 && typicalPredictionGenericRefinementCX1 == 7
									&& typicalPredictionGenericRefinementCX2 == 7) {
								setPixel(col, row, 1);
								continue;
							}
					}

					cx = (BinaryOperation.bit32ShiftL(cx0, 11)) | (cxPtr1.nextPixel() << 10) | (BinaryOperation.bit32ShiftL(cx2, 8))
							| (BinaryOperation.bit32ShiftL(cx3, 5)) | (BinaryOperation.bit32ShiftL(cx4, 2)) | (cxPtr5.nextPixel() << 1)
							| cxPtr6.nextPixel();

					int pixel = this.arithmeticDecoder.decodeBit(cx, this.arithmeticDecoder.refinementRegionStats);
					if (pixel == 1) {
						setPixel(col, row, 1);
					}
				}
			}
		}
	}

	public void readTextRegion(boolean huffman, boolean symbolRefine, int noOfSymbolInstances, int logStrips, int noOfSymbols,
			int[][] symbolCodeTable, int symbolCodeLength, JBIG2Bitmap[] symbols, int defaultPixel, int combinationOperator, boolean transposed,
			int referenceCorner, int sOffset, int[][] huffmanFSTable, int[][] huffmanDSTable, int[][] huffmanDTTable, int[][] huffmanRDWTable,
			int[][] huffmanRDHTable, int[][] huffmanRDXTable, int[][] huffmanRDYTable, int[][] huffmanRSizeTable, int template,
			short[] symbolRegionAdaptiveTemplateX, short[] symbolRegionAdaptiveTemplateY, JBIG2StreamDecoder decoder) throws JBIG2Exception,
			IOException {

		JBIG2Bitmap symbolBitmap;

		int strips = 1 << logStrips;

		clear(defaultPixel);

		// HuffmanDecoder huffDecoder = HuffmanDecoder.getInstance();
		// ArithmeticDecoder arithmeticDecoder = ArithmeticDecoder.getInstance();

		int t;
		if (huffman) {
			t = this.huffmanDecoder.decodeInt(huffmanDTTable).intResult();
		}
		else {
			t = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iadtStats).intResult();
		}
		t *= -strips;

		int currentInstance = 0;
		int firstS = 0;
		int dt, tt, ds, s;
		while (currentInstance < noOfSymbolInstances) {

			if (huffman) {
				dt = this.huffmanDecoder.decodeInt(huffmanDTTable).intResult();
			}
			else {
				dt = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iadtStats).intResult();
			}
			t += dt * strips;

			if (huffman) {
				ds = this.huffmanDecoder.decodeInt(huffmanFSTable).intResult();
			}
			else {
				ds = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iafsStats).intResult();
			}
			firstS += ds;
			s = firstS;

			while (true) {

				if (strips == 1) {
					dt = 0;
				}
				else
					if (huffman) {
						dt = decoder.readBits(logStrips);
					}
					else {
						dt = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iaitStats).intResult();
					}
				tt = t + dt;

				long symbolID;
				if (huffman) {
					if (symbolCodeTable != null) {
						symbolID = this.huffmanDecoder.decodeInt(symbolCodeTable).intResult();
					}
					else {
						symbolID = decoder.readBits(symbolCodeLength);
					}
				}
				else {
					symbolID = this.arithmeticDecoder.decodeIAID(symbolCodeLength, this.arithmeticDecoder.iaidStats);
				}

				if (symbolID >= noOfSymbols) {
					if (JBIG2StreamDecoder.debug) System.out.println("Invalid symbol number in JBIG2 text region");
				}
				else {
					symbolBitmap = null;

					int ri;
					if (symbolRefine) {
						if (huffman) {
							ri = decoder.readBit();
						}
						else {
							ri = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iariStats).intResult();
						}
					}
					else {
						ri = 0;
					}
					if (ri != 0) {

						int refinementDeltaWidth, refinementDeltaHeight, refinementDeltaX, refinementDeltaY;

						if (huffman) {
							refinementDeltaWidth = this.huffmanDecoder.decodeInt(huffmanRDWTable).intResult();
							refinementDeltaHeight = this.huffmanDecoder.decodeInt(huffmanRDHTable).intResult();
							refinementDeltaX = this.huffmanDecoder.decodeInt(huffmanRDXTable).intResult();
							refinementDeltaY = this.huffmanDecoder.decodeInt(huffmanRDYTable).intResult();

							decoder.consumeRemainingBits();
							this.arithmeticDecoder.start();
						}
						else {
							refinementDeltaWidth = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iardwStats).intResult();
							refinementDeltaHeight = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iardhStats).intResult();
							refinementDeltaX = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iardxStats).intResult();
							refinementDeltaY = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iardyStats).intResult();
						}
						refinementDeltaX = ((refinementDeltaWidth >= 0) ? refinementDeltaWidth : refinementDeltaWidth - 1) / 2 + refinementDeltaX;
						refinementDeltaY = ((refinementDeltaHeight >= 0) ? refinementDeltaHeight : refinementDeltaHeight - 1) / 2 + refinementDeltaY;

						symbolBitmap = new JBIG2Bitmap(refinementDeltaWidth + symbols[(int) symbolID].width, refinementDeltaHeight
								+ symbols[(int) symbolID].height, this.arithmeticDecoder, this.huffmanDecoder, this.mmrDecoder);

						symbolBitmap.readGenericRefinementRegion(template, false, symbols[(int) symbolID], refinementDeltaX, refinementDeltaY,
								symbolRegionAdaptiveTemplateX, symbolRegionAdaptiveTemplateY);

					}
					else {
						symbolBitmap = symbols[(int) symbolID];
					}

					int bitmapWidth = symbolBitmap.width - 1;
					int bitmapHeight = symbolBitmap.height - 1;
					if (transposed) {
						switch (referenceCorner) {
							case 0: // bottom left
								combine(symbolBitmap, tt, s, combinationOperator);
								break;
							case 1: // top left
								combine(symbolBitmap, tt, s, combinationOperator);
								break;
							case 2: // bottom right
								combine(symbolBitmap, (tt - bitmapWidth), s, combinationOperator);
								break;
							case 3: // top right
								combine(symbolBitmap, (tt - bitmapWidth), s, combinationOperator);
								break;
						}
						s += bitmapHeight;
					}
					else {
						switch (referenceCorner) {
							case 0: // bottom left
								combine(symbolBitmap, s, (tt - bitmapHeight), combinationOperator);
								break;
							case 1: // top left
								combine(symbolBitmap, s, tt, combinationOperator);
								break;
							case 2: // bottom right
								combine(symbolBitmap, s, (tt - bitmapHeight), combinationOperator);
								break;
							case 3: // top right
								combine(symbolBitmap, s, tt, combinationOperator);
								break;
						}
						s += bitmapWidth;
					}
				}

				currentInstance++;

				DecodeIntResult decodeIntResult;

				if (huffman) {
					decodeIntResult = this.huffmanDecoder.decodeInt(huffmanDSTable);
				}
				else {
					decodeIntResult = this.arithmeticDecoder.decodeInt(this.arithmeticDecoder.iadsStats);
				}

				if (!decodeIntResult.booleanResult()) {
					break;
				}

				ds = decodeIntResult.intResult();

				s += sOffset + ds;
			}
		}
	}

	public void clear(int defPixel) {
		this.data.setAll(defPixel == 1);
		// data.set(0, data.size(), defPixel == 1);
	}

	public void combine(JBIG2Bitmap bitmap, int x, int y, long combOp) {
		int srcWidth = bitmap.width;
		int srcHeight = bitmap.height;

		// int maxRow = y + srcHeight;
		// int maxCol = x + srcWidth;
		//
		// for (int row = y; row < maxRow; row++) {
		// for (int col = x; col < maxCol; srcCol += 8, col += 8) {
		//
		// byte srcPixelByte = bitmap.getPixelByte(srcCol, srcRow);
		// byte dstPixelByte = getPixelByte(col, row);
		// byte endPixelByte;
		//
		// switch ((int) combOp) {
		// case 0: // or
		// endPixelByte = (byte) (dstPixelByte | srcPixelByte);
		// break;
		// case 1: // and
		// endPixelByte = (byte) (dstPixelByte & srcPixelByte);
		// break;
		// case 2: // xor
		// endPixelByte = (byte) (dstPixelByte ^ srcPixelByte);
		// break;
		// case 3: // xnor
		// endPixelByte = (byte) ~(dstPixelByte ^ srcPixelByte);
		// break;
		// case 4: // replace
		// default:
		// endPixelByte = srcPixelByte;
		// break;
		// }
		// int used = maxCol - col;
		// if (used < 8) {
		// // mask bits
		// endPixelByte = (byte) ((endPixelByte & (0xFF >> (8 - used))) | (dstPixelByte & (0xFF << (used))));
		// }
		// setPixelByte(col, row, endPixelByte);
		// }
		//
		// srcCol = 0;
		// srcRow++;
		int minWidth = srcWidth;
		if (x + srcWidth > this.width) {
			// Should not happen but occurs sometimes because there is something wrong with halftone pics
			minWidth = this.width - x;
		}
		if (y + srcHeight > this.height) {
			// Should not happen but occurs sometimes because there is something wrong with halftone pics
			srcHeight = this.height - y;
		}

		int srcIndx = 0;
		int indx = y * this.width + x;
		if (combOp == 0) {
			if (x == 0 && y == 0 && srcHeight == this.height && srcWidth == this.width) {
				for (int i = 0; i < this.data.w.length; i++) {
					this.data.w[i] |= bitmap.data.w[i];
				}
			}
			for (int row = y; row < y + srcHeight; row++) {
				indx = row * this.width + x;
				this.data.or(indx, bitmap.data, srcIndx, minWidth);
				/*
				 * for (int col = 0; col < minWidth; col++) { if (bitmap.data.get(srcIndx + col)) data.set(indx); //data.set(indx,
				 * bitmap.data.get(srcIndx + col) || data.get(indx)); indx++; }
				 */
				srcIndx += srcWidth;
			}
		}
		else
			if (combOp == 1) {
				if (x == 0 && y == 0 && srcHeight == this.height && srcWidth == this.width) {
					for (int i = 0; i < this.data.w.length; i++) {
						this.data.w[i] &= bitmap.data.w[i];
					}
				}
				for (int row = y; row < y + srcHeight; row++) {
					indx = row * this.width + x;
					for (int col = 0; col < minWidth; col++) {
						this.data.set(indx, bitmap.data.get(srcIndx + col) && this.data.get(indx));
						indx++;
					}
					srcIndx += srcWidth;
				}
			}

			else
				if (combOp == 2) {
					if (x == 0 && y == 0 && srcHeight == this.height && srcWidth == this.width) {
						for (int i = 0; i < this.data.w.length; i++) {
							this.data.w[i] ^= bitmap.data.w[i];
						}
					}
					else {
						for (int row = y; row < y + srcHeight; row++) {
							indx = row * this.width + x;
							for (int col = 0; col < minWidth; col++) {
								this.data.set(indx, bitmap.data.get(srcIndx + col) ^ this.data.get(indx));
								indx++;
							}
							srcIndx += srcWidth;
						}
					}
				}

				else
					if (combOp == 3) {
						for (int row = y; row < y + srcHeight; row++) {
							indx = row * this.width + x;
							for (int col = 0; col < minWidth; col++) {
								boolean srcPixel = bitmap.data.get(srcIndx + col);
								boolean pixel = this.data.get(indx);
								this.data.set(indx, pixel == srcPixel);
								indx++;
							}
							srcIndx += srcWidth;
						}
					}

					else
						if (combOp == 4) {
							if (x == 0 && y == 0 && srcHeight == this.height && srcWidth == this.width) {
								for (int i = 0; i < this.data.w.length; i++) {
									this.data.w[i] = bitmap.data.w[i];
								}
							}
							else {
								for (int row = y; row < y + srcHeight; row++) {
									indx = row * this.width + x;
									for (int col = 0; col < minWidth; col++) {
										this.data.set(indx, bitmap.data.get(srcIndx + col));
										srcIndx++;
										indx++;
									}
									srcIndx += srcWidth;
								}
							}
						}
	}

	/**
	 * set a full byte of pixels
	 */
	// private void setPixelByte(int col, int row, byte bits) {
	// data.setByte(row, col, bits);
	// }

	/**
	 * get a byte of pixels
	 */
	// public byte getPixelByte(int col, int row) {
	// return data.getByte(row, col);
	// }

	private void duplicateRow(int yDest, int ySrc) {
		// for (int i = 0; i < width;) {
		// setPixelByte(i, yDest, getPixelByte(i, ySrc));
		// i += 8;
		// }
		for (int i = 0; i < this.width; i++) {
			setPixel(i, yDest, getPixel(i, ySrc));
		}
	}

	public int getWidth() {
		return this.width;
	}

	public int getHeight() {
		return this.height;
	}

	public byte[] getData(boolean switchPixelColor) {
		// byte[] bytes = new byte[height * line];
		//
		// for (int i = 0; i < height; i++) {
		// System.arraycopy(data.bytes[i], 0, bytes, line * i, line);
		// }
		//
		// for (int i = 0; i < bytes.length; i++) {
		// // reverse bits
		//
		// int value = bytes[i];
		// value = (value & 0x0f) << 4 | (value & 0xf0) >> 4;
		// value = (value & 0x33) << 2 | (value & 0xcc) >> 2;
		// value = (value & 0x55) << 1 | (value & 0xaa) >> 1;
		//
		// if (switchPixelColor) {
		// value ^= 0xff;
		// }
		//
		// bytes[i] = (byte) (value & 0xFF);
		// }
		//
		// return bytes;
		byte[] bytes = new byte[this.height * this.line];

		int count = 0, offset = 0;
		long k = 0;
		for (int row = 0; row < this.height; row++) {
			for (int col = 0; col < this.width; col++) {
				if ((count & FastBitSet.mask) == 0) {
					k = this.data.w[count >>> FastBitSet.pot];
				}
				// if ((k & (1L << count)) != 0) {
				int bit = 7 - (offset & 0x7);
				bytes[offset >> 3] |= ((k >>> count) & 1) << bit;
				// }
				count++;
				offset++;
			}

			offset = (this.line * 8 * (row + 1));
		}

		if (switchPixelColor) {
			for (int i = 0; i < bytes.length; i++) {
				bytes[i] ^= 0xff;
			}
		}

		return bytes;
	}

	public JBIG2Bitmap getSlice(int x, int y, int width, int height) {
		// JBIG2Bitmap slice = new JBIG2Bitmap(width, height);
		//
		// int sliceRow = 0, sliceCol = 0;
		// int maxCol = x + width;
		//
		// //ShowGUIMessage.showGUIMessage("x", this.getBufferedImage(), "xx");
		//
		// System.out.println(">>> getSlice x = "+x+" y = "+y+ " width = "+width+ " height = "+height);
		// System.out.println(">>> baseImage width = "+this.width+ " height = "+this.height);
		//
		// System.out.println("counter = "+counter);
		// if(counter == 17){
		// System.out.println();
		// //ShowGUIMessage.showGUIMessage("x", this.getBufferedImage(), "xx");
		// }
		//
		// ShowGUIMessage.showGUIMessage("x", this.getBufferedImage(), "xx");
		//
		// for (int row = y; row < height; row++) {
		// for (int col = x; col < maxCol; col += 8, sliceCol += 8) {
		// slice.setPixelByte(sliceCol, sliceRow, getPixelByte(col, row));
		// //if(counter > 10)
		// //ShowGUIMessage.showGUIMessage("new", slice.getBufferedImage(), "new");
		// }
		// sliceCol = 0;
		// sliceRow++;
		// }
		// counter++;
		//
		// ShowGUIMessage.showGUIMessage("new", slice.getBufferedImage(), "new");
		//
		// return slice;

		JBIG2Bitmap slice = new JBIG2Bitmap(width, height, this.arithmeticDecoder, this.huffmanDecoder, this.mmrDecoder);

		/*
		 * int sliceRow = 0, sliceCol = 0; for (int row = y; row < height; row++) { for (int col = x; col < x + width; col++) {
		 * //System.out.println("row = "+row +" column = "+col); //slice.setPixel(sliceCol, sliceRow, getPixel(col, row));
		 * slice.data.set(sliceRow*slice.width + sliceCol, data.get(row*this.width + col)); sliceCol++; } sliceCol = 0; sliceRow++; } return slice;
		 */
		// int sliceRow = 0, sliceCol = 0;
		int sliceIndx = 0;
		for (int row = y; row < height; row++) {
			int indx = row * this.width + x;
			for (int col = x; col < x + width; col++) {
				if (this.data.get(indx)) slice.data.set(sliceIndx);
				sliceIndx++;
				indx++;
			}
		}

		return slice;
	}

	/**
	 * private static void setPixel(int col, int row, FastBitSet data, int value) { if (value == 1) data.set(row, col); else data.clear(row, col); }/
	 **/

	// private void setPixelByte(int col, int row, FastBitSet data, byte bits) {
	// data.setByte(row, col, bits);
	// }

	// public void setPixel(int col, int row, int value) {
	// setPixel(col, row, data, value);
	// }

	// public int getPixel(int col, int row) {
	// return data.get(row, col) ? 1 : 0;
	// }

	private void setPixel(int col, int row, FastBitSet data, int value) {
		int index = (row * this.width) + col;

		data.set(index, value == 1);
	}

	public void setPixel(int col, int row, int value) {
		setPixel(col, row, this.data, value);
	}

	public int getPixel(int col, int row) {
		return this.data.get((row * this.width) + col) ? 1 : 0;
	}

	public void expand(int newHeight, int defaultPixel) {
		// System.out.println("expand FastBitSet");
		// FastBitSet newData = new FastBitSet(width, newHeight);
		//
		// for (int row = 0; row < height; row++) {
		// for (int col = 0; col < width; col += 8) {
		// setPixelByte(col, row, newData, getPixelByte(col, row));
		// }
		// }
		//
		// this.height = newHeight;
		// this.data = newData;
		FastBitSet newData = new FastBitSet(newHeight * this.width);
		for (int row = 0; row < this.height; row++) {
			for (int col = 0; col < this.width; col++) {
				setPixel(col, row, newData, getPixel(col, row));
			}
		}

		this.height = newHeight;
		this.data = newData;
	}

	public void setBitmapNumber(int segmentNumber) {
		this.bitmapNumber = segmentNumber;
	}

	public int getBitmapNumber() {
		return this.bitmapNumber;
	}

	public BufferedImage getBufferedImage() {
		byte[] bytes = getData(true);

		if (bytes == null) return null;

		// make a a DEEP copy so we can't alter
		int len = bytes.length;
		byte[] copy = new byte[len];
		System.arraycopy(bytes, 0, copy, 0, len);

		/** create an image from the raw data */
		DataBuffer db = new DataBufferByte(copy, copy.length);

		WritableRaster raster = Raster.createPackedRaster(db, this.width, this.height, 1, null);

		BufferedImage image = new BufferedImage(this.width, this.height, BufferedImage.TYPE_BYTE_BINARY);
		image.setData(raster);

		return image;
	}

	/**
	 * Faster BitSet implementation. Does not perfom any bound checks.
	 * 
	 * @author Boris von Loesch
	 * 
	 */
	static final class FastBitSet {
		long[] w;
		static final int pot = (Long.SIZE == 32) ? 5 : (Long.SIZE == 16) ? 4 : (Long.SIZE == 64) ? 6 : 7;
		static final int mask = (int) ((-1L) >>> (Long.SIZE - pot));
		int length;

		public FastBitSet(int length) {
			this.length = length;
			int wcount = length / Long.SIZE;
			if (length % Long.SIZE != 0) wcount++;
			this.w = new long[wcount];
		}

		public final int size() {
			return this.length;
		}

		public void setAll(boolean value) {
			if (value) for (int i = 0; i < this.w.length; i++) {
				this.w[i] = -1L;
			}
			else for (int i = 0; i < this.w.length; i++) {
				this.w[i] = 0;
			}

		}

		public void set(int start, int end, boolean value) {
			if (value) {
				for (int i = start; i < end; i++) {
					set(i);
				}
			}
			else {
				for (int i = start; i < end; i++) {
					clear(i);
				}
			}
		}

		public void or(int startindex, final FastBitSet set, int setStartIndex, final int length) {
			final int shift = startindex - setStartIndex;
			long k = set.w[setStartIndex >> pot];
			// Cyclic shift
			k = (k << shift) | (k >>> (Long.SIZE - shift));
			if ((setStartIndex & mask) + length <= Long.SIZE) {
				setStartIndex += shift;
				for (int i = 0; i < length; i++) {
					this.w[(startindex) >>> pot] |= k & (1L << setStartIndex);
					setStartIndex++;
					startindex++;
				}
			}
			else {
				for (int i = 0; i < length; i++) {
					if ((setStartIndex & mask) == 0) {
						k = set.w[(setStartIndex) >> pot];
						k = (k << shift) | (k >>> (Long.SIZE - shift));
					}
					this.w[(startindex) >>> pot] |= k & (1L << (setStartIndex + shift));
					setStartIndex++;
					startindex++;
				}
			}
		}

		public void set(int index, boolean value) {
			if (value) set(index);
			else clear(index);
		}

		public void set(int index) {
			int windex = index >>> pot;
			this.w[windex] |= (1L << index);
		}

		public void clear(int index) {
			int windex = index >>> pot;
			this.w[windex] &= ~(1L << index);
		}

		public final boolean get(int index) {
			int windex = index >>> pot;
			return ((this.w[windex] & (1L << index)) != 0);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy