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

com.idrsolutions.image.jpeg2000.Jpeg2000Decoder Maven / Gradle / Ivy

There is a newer version: 20151002
Show newest version
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.idrsolutions.image.jpeg2000;

import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.List;

/**
 * Class reads Jpeg2000 images as BufferedImage
 *
 * 

Example:

*

 * Jpeg2000Decoder decoder = new Jpeg2000Decoder();
 * // Make NO assumptions about type of BufferedImage type returned (may change)
 * BufferedImage decodedImage = decoder.read(jpxByteData);
 * 
* */ public class Jpeg2000Decoder { private static final boolean debug = false; /** * Decodes and returns the Jpeg2000 image as a BufferedImage. *

* Make NO assumptions about type of BufferedImage type returned (may change) * * @param jpxRawData A byte[] array containing the JPEG2000 data * @return BufferedImage The decoded image * @throws Exception */ public BufferedImage read(final byte[] jpxRawData) throws Exception { final Info info = new Info(); final JPXReader reader = new JPXReader(jpxRawData); if (Markers.SOC == ((jpxRawData[0] & 0xff) << 8 | (jpxRawData[1] & 0xff))) { readCodeStream(info, reader, jpxRawData.length); } else { decodeMain(info, reader); decodeContiguousCodeStreamBoxes(info, reader); } generateTileMap(info); decodeTileOffsets(info, reader); if (info.palette != null && info.siz.nComp == 1) { return convertPalette(info); } else if (info.enumerateCS == Info.CS_CMYK) { return convertCMYKTileToRGB(info, true); } else { return convertTileComponentsToBuffered(info); } } private static void decodeMain(Info info, JPXReader reader) { final int signLen = reader.readInt(); final int signType = reader.readInt(); if (signLen != 12 && signType != Boxes.JP) { throw new RuntimeException("Jpeg2000 Error: Not a valid jp2 file "); } reader.readInt(); //read signature content long ftypLen = reader.readInt(); final int ftypType = reader.readInt(); if (ftypType != Boxes.FTYP) { throw new RuntimeException("Jpeg2000 Error: Not a valid filetype declared in file "); } boolean isJP2BRFound = false; long remaining; if (ftypLen == 1) { ftypLen = reader.readLong(); remaining = ftypLen - 16; } else { remaining = ftypLen - 8; } final long size = remaining / 4; for (int i = 0; i < size; i++) { if (reader.readInt() == Boxes.JP2) { isJP2BRFound = true; } } if (!isJP2BRFound) { throw new RuntimeException("Jpeg2000 Error: Not a valid JP2 Branded File"); } boolean hasBoxes = true; while (reader.getRemaining() > 0 && hasBoxes) { final int offset = reader.getPosition(); long tempLen = reader.readInt(); final int tempType = reader.readInt(); if (tempLen == 1) { tempLen = reader.readLong(); } else if (tempLen == 0) { hasBoxes = false; } switch (tempType) { case Boxes.JP2H: //read image header box first long tLen = reader.readInt(); int tType; reader.readInt(); //initial tType if (tLen == 1) { reader.readLong(); //read next } info.imageHeight = reader.readInt(); info.imageWidth = reader.readInt(); info.nComp = reader.readUShort(); info.bitDepth = reader.readUByte(); info.compressionType = reader.readUByte(); info.unknownColorSpace = reader.readUByte(); info.ip = reader.readUByte(); final long ii = offset + tempLen; while (reader.getPosition() < ii) { final int start = reader.getPosition(); tLen = reader.readInt(); tType = reader.readInt(); if (tLen == 1) { reader.readLong();//tLen = } switch (tType) { case Boxes.BPCC: info.bitDepths = new byte[info.nComp]; for (int i = 0; i < info.bitDepths.length; i++) { info.bitDepths[i] = reader.readByte(); } break; case Boxes.COLR: final int m = reader.readUByte(); reader.readByte();//int p = reader.readUByte();//int a = if (m == 1) { info.enumerateCS = reader.readInt(); } else if (m == 2) { reader.readInt(); //restricted ICC; } if (debug) { System.err.println("has ICC box"); } reader.setPosition((int) (start + tLen)); break; case Boxes.PCLR: final Palette pal = new Palette(); pal.nEntries = reader.readUShort(); pal.nColumns = reader.readUByte(); pal.bitDepts = new int[pal.nColumns]; for (int i = 0; i < pal.nColumns; i++) { pal.bitDepts[i] = reader.readUByte(); } pal.cValues = new int[pal.nEntries][pal.nColumns]; for (int i = 0; i < pal.nEntries; i++) { for (int j = 0; j < pal.nColumns; j++) { pal.cValues[i][j] = reader.readUByte(); } } info.palette = pal; if (debug) { System.err.println("has PALLETTE box"); } reader.setPosition((int) (start + tLen)); break; case Boxes.CMAP: int mapLen = (int) (tLen - (reader.getPosition() - start)); mapLen /= 4; final Cmap cmap = new Cmap(); cmap.cmp = new int[mapLen]; cmap.mtyp = new int[mapLen]; cmap.pcol = new int[mapLen]; for (int i = 0; i < mapLen; i++) { cmap.cmp[i] = reader.readUShort(); cmap.mtyp[i] = reader.readUByte(); cmap.pcol[i] = reader.readUByte(); } info.cmap = cmap; if (debug) { System.err.println("has CMAP box"); } reader.setPosition((int) (start + tLen)); break; case Boxes.CDEF: if (debug) { System.err.println("has CDef box"); } int nDef = reader.readShort(); for (int i = 0; i < nDef; i++) { int key = reader.readShort(); int type = reader.readShort(); int val = reader.readShort(); if (type == 0) { info.cDef.put(key, val); } } reader.setPosition((int) (start + tLen)); break; case Boxes.RES: if (debug) { System.err.println("has resolutions box"); } reader.setPosition((int) (start + tLen)); break; default: reader.setPosition((int) (start + tLen)); } } reader.setPosition((int) ii); break; case Boxes.JP2C: info.contiguousCodeStreamBoxes.add(offset); reader.setPosition((int) (offset + tempLen)); break; case Boxes.JP2I: reader.setPosition((int) (offset + tempLen)); break; case Boxes.XML: reader.setPosition((int) (offset + tempLen)); break; case Boxes.UUID: reader.setPosition((int) (offset + tempLen)); break; case Boxes.UINF: reader.setPosition((int) (offset + tempLen)); break; default: if (debug) { System.out.println("undefined header found " + tempType); } reader.setPosition((int) (offset + tempLen)); break; } } } private static void generateTileMap(Info info) { final SIZ siz = info.siz; final int numXTiles = (int) Math.ceil(1.0 * (siz.Xsiz - siz.XTOsiz) / siz.XTsiz); final int numYTiles = (int) Math.ceil(1.0 * (siz.Ysiz - siz.YTOsiz) / siz.YTsiz); int index = 0; for (int q = 0; q < numYTiles; q++) { for (int p = 0; p < numXTiles; p++) { final Tile tile = new Tile(); tile.tx0 = Math.max(siz.XTOsiz + p * siz.XTsiz, siz.XOsiz); tile.ty0 = Math.max(siz.YTOsiz + q * siz.YTsiz, siz.YOsiz); tile.tx1 = Math.min(siz.XTOsiz + (p + 1) * siz.XTsiz, siz.Xsiz); tile.ty1 = Math.min(siz.YTOsiz + (q + 1) * siz.YTsiz, siz.Ysiz); for (int i = 0; i < siz.nComp; i++) { final int XRsiz_ = siz.precisionInfo[i][1]; final int YRsiz_ = siz.precisionInfo[i][2]; final TileComponent tileComp = new TileComponent(); tileComp.x0 = (int) Math.ceil(1.0 * tile.tx0 / XRsiz_); tileComp.x1 = (int) Math.ceil(1.0 * tile.tx1 / XRsiz_); tileComp.y0 = (int) Math.ceil(1.0 * tile.ty0 / YRsiz_); tileComp.y1 = (int) Math.ceil(1.0 * tile.ty1 / YRsiz_); tile.components.add(tileComp); } info.tilesMap.put(index, tile); index++; } } } private static void decodeContiguousCodeStreamBoxes(Info info, JPXReader reader) { for (final int is : info.contiguousCodeStreamBoxes) { reader.setPosition(is); long tempLen = reader.readInt(); reader.readInt();//read it and ignore if (tempLen == 1) { tempLen = reader.readLong(); } else if (tempLen == 0) { tempLen = reader.getLimit() - is; } final long maxRead = is + tempLen; readCodeStream(info, reader, maxRead); } } private static void readCodeStream(Info info, JPXReader reader, long maxRead) { while (reader.getPosition() < maxRead) { final int header = reader.readUShort(); switch (header) { case Markers.SOC: break; case Markers.SIZ: reader.readUShort();//int LSIZ info.siz = readSIZ(reader); if (debug) { System.out.println("Width " + info.imageWidth + " Height " + info.imageHeight); System.out.println("SIZ info : " + info.siz); } info.qcc = new QCD[info.siz.nComp]; break; case Markers.COD: reader.readUShort();//int LCOD info.cod = readCOD(reader); if (debug) { System.out.println("info.cod : \n" + info.cod); } break; case Markers.COC: if (debug) { System.err.println("contains coc parameters"); } final int LCOC = reader.readUShort(); reader.setPosition(reader.getPosition() + LCOC - 2); break; case Markers.QCD: final int LQCD = reader.readUShort(); info.qcd = readQCD(reader, LQCD); if (debug) { System.out.println("info.qcd : \n" + info.qcd); } break; case Markers.QCC: final int LQCC = reader.readUShort(); int cVal = reader.readUByte(); final QCD qcc = new QCD(); final Byte qccQS = reader.readByte(); JPXBitReader qccBR = new JPXBitReader(qccQS); qcc.guardBits = qccBR.readBits(3); qcc.quantBits = qccBR.readBits(5); qcc.hasScalar = false; final int qccBalance = LQCC - 4; int qccNB; switch (qcc.quantBits) { case 0: qcc.hasScalar = true; qccNB = qccBalance; qcc.exponentB = new int[qccNB]; qcc.mantissaB = new int[qccNB]; for (int i = 0; i < qccNB; i++) { qccBR = new JPXBitReader(reader.readByte()); qcc.exponentB[i] = qccBR.readBits(5); qcc.mantissaB[i] = 0; } break; case 1: qcc.hasScalar = false; final byte[] temp = {reader.readByte(), reader.readByte()}; qccBR = new JPXBitReader(temp); final int eB = qccBR.readBits(5); final int muB = qccBR.readBits(11); qcc.exponentB = new int[]{eB}; qcc.mantissaB = new int[]{muB}; break; case 2: qccNB = qccBalance / 2; qcc.hasScalar = true; qcc.exponentB = new int[qccNB]; qcc.mantissaB = new int[qccNB]; for (int i = 0; i < qccNB; i++) { final byte[] tt = {reader.readByte(), reader.readByte()}; qccBR = new JPXBitReader(tt); qcc.exponentB[i] = qccBR.readBits(5); qcc.mantissaB[i] = qccBR.readBits(11); } break; } if (debug) { System.out.println("Contains Info QCC " + qcc); } info.qcc[cVal] = qcc; break; case Markers.RGN: final int LRGN = reader.readUShort(); reader.setPosition(reader.getPosition() + LRGN - 2); break; case Markers.POC: final int LPOC = reader.readUShort(); reader.setPosition(reader.getPosition() + LPOC - 2); break; case Markers.PPM: final int LPPM = reader.readUShort(); reader.setPosition(reader.getPosition() + LPPM - 2); break; case Markers.PLM: final int LPLM = reader.readUShort(); reader.setPosition(reader.getPosition() + LPLM - 2); break; case Markers.TLM: final int LTLM = reader.readUShort(); reader.setPosition(reader.getPosition() + LTLM - 2); break; case Markers.CRG: final int LCRG = reader.readUShort(); reader.setPosition(reader.getPosition() + LCRG - 2); break; case Markers.COM: final int LCOM = reader.readUShort(); reader.setPosition(reader.getPosition() + LCOM - 2); break; case Markers.SOT: final int tileOffset = reader.getPosition() - 2; reader.readInt(); final int LTP = reader.readInt(); reader.readShort(); reader.setPosition(tileOffset + LTP); info.tileOffsets.add(tileOffset); break; case Markers.EOC: return; default: System.err.println("undefined header in jpeg2000file" + header); } } } private static SIZ readSIZ(JPXReader reader) { final SIZ siz = new SIZ(); siz.capabilities = reader.readUShort(); siz.Xsiz = reader.readInt(); siz.Ysiz = reader.readInt(); siz.XOsiz = reader.readInt(); siz.YOsiz = reader.readInt(); siz.XTsiz = reader.readInt(); siz.YTsiz = reader.readInt(); siz.XTOsiz = reader.readInt(); siz.YTOsiz = reader.readInt(); siz.nComp = reader.readUShort(); siz.precisionInfo = new int[siz.nComp][3]; for (int i = 0; i < siz.nComp; i++) { siz.precisionInfo[i][0] = reader.readUByte(); siz.precisionInfo[i][1] = reader.readUByte(); siz.precisionInfo[i][2] = reader.readUByte(); } return siz; } private static COD readCOD(JPXReader reader) { final COD cod = new COD(); final boolean[] bools = toBoolean8(reader.readByte()); cod.hasPrecint = bools[7]; cod.hasSOP = bools[6]; cod.hasEPH = bools[5]; cod.progressionOrder = reader.readUByte(); cod.nLayers = reader.readUShort(); cod.multiCompTransform = reader.readByte(); cod.nDecompLevel = reader.readUByte(); cod.xcb = reader.readUByte() + 2; cod.ycb = reader.readUByte() + 2; cod.codeBlockStyle = reader.readByte(); cod.transformation = reader.readUByte(); if (cod.hasPrecint) { cod.precintSizes = new int[cod.nDecompLevel + 1]; for (int i = 0; i < cod.precintSizes.length; i++) { cod.precintSizes[i] = reader.readByte(); } } return cod; } private static QCD readQCD(JPXReader reader, int qcdLength){ final QCD qcd = new QCD(); final Byte qs = reader.readByte(); JPXBitReader br = new JPXBitReader(qs); qcd.guardBits = br.readBits(3); qcd.quantBits = br.readBits(5); qcd.hasScalar = false; final int balance = qcdLength - 3; int NB; switch (qcd.quantBits) { case 0: qcd.hasScalar = true; NB = balance; qcd.exponentB = new int[NB]; qcd.mantissaB = new int[NB]; for (int i = 0; i < NB; i++) { br = new JPXBitReader(reader.readByte()); qcd.exponentB[i] = br.readBits(5); qcd.mantissaB[i] = 0; } break; case 1: qcd.hasScalar = false; final byte[] temp = {reader.readByte(), reader.readByte()}; br = new JPXBitReader(temp); final int eB = br.readBits(5); final int muB = br.readBits(11); qcd.exponentB = new int[]{eB}; qcd.mantissaB = new int[]{muB}; break; case 2: NB = balance / 2; qcd.hasScalar = true; qcd.exponentB = new int[NB]; qcd.mantissaB = new int[NB]; for (int i = 0; i < NB; i++) { final byte[] tt = {reader.readByte(), reader.readByte()}; br = new JPXBitReader(tt); qcd.exponentB[i] = br.readBits(5); qcd.mantissaB[i] = br.readBits(11); } break; } return qcd; } private static void decodeTileOffsets(Info info, JPXReader reader) { for (final int offset : info.tileOffsets) { reader.setPosition(offset); reader.readUShort();//final int SOT = reader.readUShort();//final int LSOT = final int tileIndex = reader.readUShort(); //index of tile final int lengthTilePart = reader.readInt(); //length of tilestream final int indexTilePart = reader.readUByte();//tile part index final int numberTilePart = reader.readUByte();//number of tile parts final int totalLen = lengthTilePart - 12; final int maxRead = offset + lengthTilePart; final Tile tile = new Tile(); tile.cod = info.cod; tile.qcd = info.qcd; tile.qcc = info.qcc; tile.index = tileIndex; tile.partIndex = indexTilePart; tile.partCount = numberTilePart; int otherRead = 0; while (reader.getPosition() < maxRead) { final int header = reader.readUShort(); switch (header) { case Markers.COD: final int LCOD = reader.readUShort();//int LCOD otherRead = otherRead + 2 + LCOD; tile.cod = readCOD(reader); if (debug) { System.err.println("Contains Tile COD " + tile.cod); } break; case Markers.QCD: final int LQCD = reader.readUShort();//int LQCD otherRead = otherRead + 2 + LQCD; tile.qcd = readQCD(reader, LQCD); if (debug) { System.err.println("Contains Tile QCD " + tile.qcd); } break; case Markers.QCC: final int LQCC = reader.readUShort(); int cVal = reader.readUByte(); final QCD qcc = new QCD(); final Byte qccQS = reader.readByte(); JPXBitReader qccBR = new JPXBitReader(qccQS); qcc.guardBits = qccBR.readBits(3); qcc.quantBits = qccBR.readBits(5); qcc.hasScalar = false; final int qccBalance = LQCC - 4; int qccNB; switch (qcc.quantBits) { case 0: qcc.hasScalar = true; qccNB = qccBalance; qcc.exponentB = new int[qccNB]; qcc.mantissaB = new int[qccNB]; for (int i = 0; i < qccNB; i++) { qccBR = new JPXBitReader(reader.readByte()); qcc.exponentB[i] = qccBR.readBits(5); qcc.mantissaB[i] = 0; } break; case 1: qcc.hasScalar = false; final byte[] temp = {reader.readByte(), reader.readByte()}; qccBR = new JPXBitReader(temp); final int eB = qccBR.readBits(5); final int muB = qccBR.readBits(11); qcc.exponentB = new int[]{eB}; qcc.mantissaB = new int[]{muB}; break; case 2: qccNB = qccBalance / 2; qcc.hasScalar = true; qcc.exponentB = new int[qccNB]; qcc.mantissaB = new int[qccNB]; for (int i = 0; i < qccNB; i++) { final byte[] tt = {reader.readByte(), reader.readByte()}; qccBR = new JPXBitReader(tt); qcc.exponentB[i] = qccBR.readBits(5); qcc.mantissaB[i] = qccBR.readBits(11); } break; } if (debug) { System.out.println("Contains Tile QCC " + qcc); } tile.qcc[cVal] = qcc; break; case Markers.COC: case Markers.RGN: case Markers.POC: case Markers.PPT: case Markers.PLT: case Markers.COM: if (debug) { System.out.println("these marker is not supported yet " + header); } final int temp = reader.readUShort(); otherRead = otherRead + 2 + temp; reader.setPosition(reader.getPosition() + temp - 2); break; case Markers.SOD: otherRead += 2; if (debug) { System.out.println("start reading"); System.out.println("Number of tile parts " + numberTilePart); } byte bb[] = new byte[totalLen - otherRead]; for (int i = 0; i < bb.length; i++) { bb[i] = reader.readByte(); } tile.data = bb; if (tile.partIndex == 0) { initializeDimensions(info, tile, tile.index); } final TileParser parser = new TileParser(tile.data, info.tilesMap.get(tile.index)); parser.parseTile(); break; case Markers.EOC: break; } } } } private static void initializeDimensions(Info info, Tile cur, int tileIndex) { final Tile tile = info.tilesMap.get(tileIndex); tile.cod = tile.cod != null ? tile.cod : cur.cod; tile.qcd = tile.qcd != null ? tile.qcd : cur.qcd; tile.qcc = tile.qcc != null ? tile.qcc : cur.qcc; switch (tile.cod.progressionOrder) { case Markers.LRCP: tile.progress = new LRCP(info, tileIndex); break; case Markers.RLCP: tile.progress = new RLCP(info, tileIndex); break; case Markers.RPCL: System.err.print("This progression order not supported"); break; case Markers.PCRL: System.err.print("This progression order not supported"); break; case Markers.CPRL: System.err.print("This progression order not supported"); break; default: System.err.println("Unknown progression order found"); break; } final int NL = tile.cod.nDecompLevel; final int xcb = tile.cod.xcb; final int ycb = tile.cod.xcb; final int ppx = 15; final int ppy = 15; for (final TileComponent tc : tile.components) { for (int r = 0; r <= NL; r++) { final int xcb_ = r == 0 ? Math.min(xcb, ppx) : Math.min(xcb, ppx - 1); final int ycb_ = r == 0 ? Math.min(ycb, ppy) : Math.min(ycb, ppy - 1); final TileResolution tr = new TileResolution(); final int NL_R2 = 1 << (NL - r); tr.x0 = (int) Math.ceil(1.0 * tc.x0 / NL_R2); tr.x1 = (int) Math.ceil(1.0 * tc.x1 / NL_R2); tr.y0 = (int) Math.ceil(1.0 * tc.y0 / NL_R2); tr.y1 = (int) Math.ceil(1.0 * tc.y1 / NL_R2); updatePrecinctInfo(tr, r, ppx, ppy); if (r == 0) { final int powN = 1 << NL; final TileBand tb = new TileBand(TileBand.LL); tb.x0 = (int) Math.abs(Math.ceil(1.0 * tc.x0 / powN)); tb.y0 = (int) Math.abs(Math.ceil(1.0 * tc.y0 / powN)); tb.x1 = (int) Math.abs(Math.ceil(1.0 * tc.x1 / powN)); tb.y1 = (int) Math.abs(Math.ceil(1.0 * tc.y1 / powN)); tr.tileBands.add(tb); updateCodeBlocks(tr, tb, xcb_, ycb_); } else { final int n = NL + 1 - r; final int powN = 1 << n; final int powM = 1 << (n - 1); TileBand tb = new TileBand(TileBand.HL); tb.x0 = (int) Math.abs(Math.ceil((1.0 * tc.x0 - (powM * 1)) / powN)); tb.y0 = (int) Math.abs(Math.ceil((1.0 * tc.y0 - (powM * 0)) / powN)); tb.x1 = (int) Math.abs(Math.ceil((1.0 * tc.x1 - (powM * 1)) / powN)); tb.y1 = (int) Math.abs(Math.ceil((1.0 * tc.y1 - (powM * 0)) / powN)); tr.tileBands.add(tb); updateCodeBlocks(tr, tb, xcb_, ycb_); tb = new TileBand(TileBand.LH); tb.x0 = (int) Math.abs(Math.ceil((1.0 * tc.x0 - (powM * 0)) / powN)); tb.y0 = (int) Math.abs(Math.ceil((1.0 * tc.y0 - (powM * 1)) / powN)); tb.x1 = (int) Math.abs(Math.ceil((1.0 * tc.x1 - (powM * 0)) / powN)); tb.y1 = (int) Math.abs(Math.ceil((1.0 * tc.y1 - (powM * 1)) / powN)); tr.tileBands.add(tb); updateCodeBlocks(tr, tb, xcb_, ycb_); tb = new TileBand(TileBand.HH); tb.x0 = (int) Math.abs(Math.ceil((1.0 * tc.x0 - (powM * 1)) / powN)); tb.y0 = (int) Math.abs(Math.ceil((1.0 * tc.y0 - (powM * 1)) / powN)); tb.x1 = (int) Math.abs(Math.ceil((1.0 * tc.x1 - (powM * 1)) / powN)); tb.y1 = (int) Math.abs(Math.ceil((1.0 * tc.y1 - (powM * 1)) / powN)); tr.tileBands.add(tb); updateCodeBlocks(tr, tb, xcb_, ycb_); } tc.resolutions.add(tr); } } } private static void updatePrecinctInfo(TileResolution resolution, int r, int ppx, int ppy) { final PrecinctInfo pInfo = new PrecinctInfo(); pInfo.precinctWidth = 1 << ppx; pInfo.precinctHeight = 1 << ppy; pInfo.precinctWidthInSubband = 1 << (ppx + (r == 0 ? 0 : -1)); pInfo.precinctHeightInSubband = 1 << (ppy + (r == 0 ? 0 : -1)); pInfo.numPrecinctsWide = (int) (resolution.x1 > resolution.x0 ? Math.ceil(1.0 * resolution.x1 / pInfo.precinctWidth) - Math.floor(1.0 * resolution.x0 / pInfo.precinctWidth) : 0); pInfo.numPrecinctsHigh = (int) (resolution.y1 > resolution.y0 ? Math.ceil(1.0 * resolution.y1 / pInfo.precinctHeight) - Math.floor(1.0 * resolution.y0 / pInfo.precinctHeight) : 0); pInfo.numPrecincts = pInfo.numPrecinctsWide * pInfo.numPrecinctsHigh; resolution.precinctInfo = pInfo; } private static void updateCodeBlocks(TileResolution tr, TileBand tb, int xcb_, int ycb_) { final int codeblockWidth = 1 << xcb_; final int codeblockHeight = 1 << ycb_; final int cbx0 = tb.x0 >> xcb_; final int cby0 = tb.y0 >> ycb_; final int cbx1 = (tb.x1 + codeblockWidth - 1) >> xcb_; final int cby1 = (tb.y1 + codeblockHeight - 1) >> ycb_; final PrecinctInfo precintInfo = tr.precinctInfo; final List codeblocks = tb.codeBlocks; final List precincts = tb.precincts; for (int j = cby0; j < cby1; j++) { for (int i = cbx0; i < cbx1; i++) { final CodeBlock cblk = new CodeBlock(); cblk.x = i; cblk.y = j; cblk.tbx0 = codeblockWidth * i; cblk.tby0 = codeblockHeight * j; cblk.tbx1 = codeblockWidth * (i + 1); cblk.tby1 = codeblockHeight * (j + 1); cblk.tbx0_ = Math.max(tb.x0, cblk.tbx0); cblk.tby0_ = Math.max(tb.y0, cblk.tby0); cblk.tbx1_ = Math.min(tb.x1, cblk.tbx1); cblk.tby1_ = Math.min(tb.y1, cblk.tby1); final int pi = (int) Math.floor((cblk.tbx0_ - tb.x0) / (precintInfo.precinctWidthInSubband * 1.0)); final int pj = (int) Math.floor((cblk.tby0_ - tb.y0) / (precintInfo.precinctHeightInSubband * 1.0)); final int precintNumber = pi + (pj * precintInfo.numPrecinctsWide); cblk.precinctNumber = precintNumber; cblk.subbandType = tb.type; cblk.Lblock = 3; if (cblk.tbx1_ <= cblk.tbx0_ || cblk.tby1_ <= cblk.tby0_) { continue; } codeblocks.add(cblk); Precinct precinct; if (precincts.size() > precintNumber) { precinct = precincts.get(precintNumber); if (i < precinct.cbx0) { precinct.cbx0 = i; } else if (i > precinct.cbx1) { precinct.cbx1 = i; } if (j < precinct.cby0) { precinct.cbx0 = j; } else if (j > precinct.cby1) { precinct.cby1 = j; } } else { precinct = new Precinct(); precinct.cby0 = j; precinct.cby1 = j; precinct.cbx0 = i; precinct.cbx1 = i; precincts.add(precinct); } cblk.precinct = precinct; } } tb.codeBlockInfo = new CodeBlockInfo(); tb.codeBlockInfo.codeBlockWidth = xcb_; tb.codeBlockInfo.codeBlockHeight = ycb_; tb.codeBlockInfo.numCodeBlockWide = cbx1 - cbx0 + 1; tb.codeBlockInfo.numCodeBlockHigh = cby1 - cby0 + 1; } private static BufferedImage convertPalette(Info info) { final SIZ siz = info.siz; final int componentsCount = siz.nComp; final List resultImages = new ArrayList(); for (int i = 0; i < info.tilesMap.size(); i++) { final Tile tile = info.tilesMap.get(i); SubbandCoefficient[] transformedTiles = new SubbandCoefficient[componentsCount]; for (int c = 0; c < componentsCount; c++) { transformedTiles[c] = transformTile(info, tile, c); tile.components.get(c).resolutions.clear(); } final SubbandCoefficient inputSC = transformedTiles[0]; final SubbandCoefficient result = new SubbandCoefficient(); result.x = inputSC.x; result.y = inputSC.y; result.width = inputSC.width; result.height = inputSC.height; byte[] out = new byte[result.width * result.height * componentsCount]; result.byteItems = out; int shift; float offset, min, max; int pos; for (int c = 0; c < componentsCount; c++) { final float[] items = transformedTiles[c].floatItems; shift = (info.siz.precisionInfo[c][0] + 1) - 8; if (shift == 0) { offset = 128.5f; max = 127.5f; min = -max; pos = c; for (int j = 0; j < items.length; j++) { final float val = items[j]; out[pos] = (byte) (val <= min ? 0 : val >= max ? 255 : (val + offset)); pos += componentsCount; } } else if (shift < 0) { pos = c; offset = 1 << (info.siz.precisionInfo[c][0]); final int minVal = 0; final int maxVal = (1 << (info.siz.precisionInfo[c][0] + 1)) - 1; for (int j = 0; j < items.length; j++) { final float val = items[j] + offset; out[pos] = (byte) Math.max(minVal, Math.min(val, maxVal)); pos += componentsCount; } } } resultImages.add(result); } info.tilesMap.clear(); byte[] mainData; if (resultImages.size() == 1) { mainData = resultImages.get(0).byteItems; } else { mainData = new byte[siz.nComp * siz.Xsiz * siz.Ysiz]; final int c = siz.nComp; final int mainStripLen = siz.Xsiz * c; for (final SubbandCoefficient sc : resultImages) { final byte[] subData = sc.byteItems; final int x = sc.x; final int y = sc.y; final int w = sc.width; final int h = sc.height; final int subStripLen = w * c; final int xc = (x * c); for (int i = 0; i < h; i++) { final int stripOffset = i * subStripLen; final int mainOffset = ((i + y) * mainStripLen) + xc; System.arraycopy(subData, stripOffset, mainData, mainOffset, subStripLen); } sc.byteItems = null; } } BufferedImage image; if (info.enumerateCS == Info.CS_CMYK) { byte[] tempData = new byte[4 * siz.Xsiz * siz.Ysiz]; int pos = 0; for (int i = 0; i < mainData.length; i++) { final int[] cc = info.palette.cValues[mainData[i] & 0xff]; for (int j = 0; j < cc.length; j++) { tempData[pos] = (byte) cc[j]; pos++; } } image = new BufferedImage(siz.Xsiz, siz.Ysiz, BufferedImage.TYPE_INT_RGB); int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); pos = 0; double r, g, b, cc = 0, mm = 0, yy = 0, kk = 0, c, m, y, k; for (int i = 0; i < imageData.length; i++) { c = (tempData[pos++] & 0xff) / 255.0; m = (tempData[pos++] & 0xff) / 255.0; y = (tempData[pos++] & 0xff) / 255.0; k = (tempData[pos++] & 0xff) / 255.0; if (c == cc && m == mm && y == yy && k == kk && i > 0) { imageData[i] = imageData[i - 1]; } else { final double dif = 1 - k; r = 255 * (1 - c) * dif; g = 255 * (1 - m) * dif; b = 255 * (1 - y) * dif; cc = c; mm = m; yy = y; kk = k; imageData[i] = (((int) r) << 16) | (((int) g) << 8) | ((int) b); } } } else { image = generateBufferedImage(info.palette.nColumns, siz.Xsiz, siz.Ysiz); byte[] imageData = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); int pos = 0; for (int i = 0; i < mainData.length; i++) { final int[] cc = info.palette.cValues[mainData[i] & 0xff]; for (int j = 0; j < cc.length; j++) { imageData[pos] = (byte) cc[j]; pos++; } } } return image; } private static BufferedImage convertCMYKTileToRGB(Info info, boolean convertCMYK) { final SIZ siz = info.siz; final int componentsCount = siz.nComp; final List resultImages = new ArrayList(); for (int i = 0; i < info.tilesMap.size(); i++) { final Tile tile = info.tilesMap.get(i); SubbandCoefficient[] transformedTiles = new SubbandCoefficient[componentsCount]; for (int c = 0; c < componentsCount; c++) { transformedTiles[c] = transformTile(info, tile, c); tile.components.get(c).resolutions.clear(); } final SubbandCoefficient inputSC = transformedTiles[0]; final SubbandCoefficient result = new SubbandCoefficient(); result.x = inputSC.x; result.y = inputSC.y; result.width = inputSC.width; result.height = inputSC.height; byte[] out = new byte[result.width * result.height * componentsCount]; result.byteItems = out; int shift; float offset, maxK, min, max; int pos = 0; double c1, c2, c3, c4, r, g, b; if (tile.cod.multiCompTransform == 1) { final float[] comp1Floats = transformedTiles[0].floatItems; final float[] comp2Floats = transformedTiles[1].floatItems; final float[] comp3Floats = transformedTiles[2].floatItems; final float[] comp4Floats = transformedTiles[3].floatItems; shift = (info.siz.precisionInfo[0][0] + 1) - 8; offset = (128 << shift) + 0.5f; max = 255 * (1 << shift); maxK = max * 0.5f; min = -maxK; if (tile.cod.transformation == 0) { pos = 0; for (int j = 0; j < comp1Floats.length; j++) { c1 = comp1Floats[j] + offset; c2 = comp2Floats[j]; c3 = comp3Floats[j]; c4 = comp4Floats[j]; r = c1 + 1.402 * c3; g = c1 - 0.34413 * c2 - 0.71414 * c3; b = c1 + 1.772 * c2; out[pos++] = (byte) (r <= 0 ? 0 : r >= max ? 255 : ((int) r) >> shift); out[pos++] = (byte) (g <= 0 ? 0 : g >= max ? 255 : ((int) g) >> shift); out[pos++] = (byte) (b <= 0 ? 0 : b >= max ? 255 : ((int) b) >> shift); out[pos++] = (byte) (c4 <= min ? 0 : c4 >= maxK ? 255 : ((int) (c4 + offset)) >> shift); } } else { for (int j = 0; j < comp1Floats.length; j++) { c1 = comp1Floats[j] + offset; c2 = comp2Floats[j]; c3 = comp3Floats[j]; c4 = comp4Floats[j]; g = c1 - (((int) (c3 + c2)) >> 2); r = g + c3; b = g + c2; out[pos++] = (byte) (r <= 0 ? 0 : r >= max ? 255 : ((int) r) >> shift); out[pos++] = (byte) (g <= 0 ? 0 : g >= max ? 255 : ((int) g) >> shift); out[pos++] = (byte) (b <= 0 ? 0 : b >= max ? 255 : ((int) b) >> shift); out[pos++] = (byte) (c4 <= min ? 0 : c4 >= maxK ? 255 : ((int) (c4 + offset)) >> shift); } } } else { for (int c = 0; c < componentsCount; c++) { final float[] items = transformedTiles[c].floatItems; shift = (info.siz.precisionInfo[c][0] + 1) - 8; offset = (128 << shift) + 0.5f; max = (int) (127.5f * (1 << shift)); min = -max; pos = c; for (int j = 0; j < items.length; j++) { final float val = items[j]; out[pos] = (byte) (val <= min ? 0 : val >= max ? 255 : ((int) (val + offset)) >> shift); pos += componentsCount; } } } resultImages.add(result); } info.tilesMap.clear(); byte[] mainData; if (resultImages.size() == 1) { mainData = resultImages.get(0).byteItems; } else { mainData = new byte[siz.nComp * siz.Xsiz * siz.Ysiz]; final int c = siz.nComp; final int mainStripLen = siz.Xsiz * c; for (final SubbandCoefficient sc : resultImages) { final byte[] subData = sc.byteItems; final int x = sc.x; final int y = sc.y; final int w = sc.width; final int h = sc.height; final int subStripLen = w * c; final int xc = (x * c); for (int i = 0; i < h; i++) { final int stripOffset = i * subStripLen; final int mainOffset = ((i + y) * mainStripLen) + xc; System.arraycopy(subData, stripOffset, mainData, mainOffset, subStripLen); } sc.byteItems = null; } } BufferedImage image; if (convertCMYK) { image = new BufferedImage(siz.Xsiz, siz.Ysiz, BufferedImage.TYPE_INT_RGB); int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); int pos = 0; int r, g, b; byte c, m, y, k; final EnumeratedSpace cs = new EnumeratedSpace(); for (int i = 0; i < imageData.length; i++) { c = mainData[pos++]; m = mainData[pos++]; y = mainData[pos++]; k = mainData[pos++]; final byte[] rgb = cs.getRGB(c, m, y, k); r = rgb[0] & 0xff; g = rgb[1] & 0xff; b = rgb[2] & 0xff; imageData[i] = (r << 16) | (g << 8) | b; } } else { image = generateBufferedImage(4, siz.Xsiz, siz.Ysiz); final byte[] imageData = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); System.arraycopy(mainData, 0, imageData, 0, mainData.length); } return image; } private static BufferedImage convertTileComponentsToBuffered(Info info) { final SIZ siz = info.siz; final int componentsCount = siz.nComp; final List resultImages = new ArrayList(); for (int i = 0; i < info.tilesMap.size(); i++) { final Tile tile = info.tilesMap.get(i); SubbandCoefficient[] transformedTiles = new SubbandCoefficient[componentsCount]; for (int c = 0; c < componentsCount; c++) { transformedTiles[c] = transformTile(info, tile, c); tile.components.get(c).resolutions.clear(); } final SubbandCoefficient inputSC = transformedTiles[0]; final SubbandCoefficient result = new SubbandCoefficient(); result.x = inputSC.x; result.y = inputSC.y; result.width = inputSC.width; result.height = inputSC.height; byte[] out; if (info.tilesMap.size() == 1) { result.bufferedImage = generateBufferedImage(componentsCount, result.width, result.height); out = ((DataBufferByte) result.bufferedImage.getRaster().getDataBuffer()).getData(); } else { out = new byte[result.width * result.height * componentsCount]; result.byteItems = out; } int shift; double offset; int min, max; int pos = 0; double y0, y1, y2; double r, g, b; if (tile.cod.multiCompTransform != 0) { final float[] y0items = getMappedComponent(transformedTiles, 0, info); final float[] y1items = getMappedComponent(transformedTiles, 1, info); final float[] y2items = getMappedComponent(transformedTiles, 2, info); shift = (info.siz.precisionInfo[0][0] + 1) - 8; offset = (128 << shift) + 0.5f; max = 255 * (1 << shift); final int alpha01 = componentsCount - 3; if (tile.cod.transformation == 0) { for (int j = 0; j < y0items.length; j++, pos += alpha01) { y0 = y0items[j] + offset; y1 = y1items[j]; y2 = y2items[j]; r = (y0 + 1.402 * y2); g = (y0 - 0.34413 * y1 - 0.71414 * y2); b = (y0 + 1.772 * y1); out[pos++] = (byte) (r < 0 ? 0 : r > max ? 255 : ((int) r) >> shift); out[pos++] = (byte) (g < 0 ? 0 : g > max ? 255 : ((int) g) >> shift); out[pos++] = (byte) (b < 0 ? 0 : b > max ? 255 : ((int) b) >> shift); } } else { final int yLen = y0items.length; for (int j = 0; j < yLen; j++, pos += alpha01) { y0 = y0items[j] + offset; y1 = y1items[j]; y2 = y2items[j]; g = (y0 - (((int) (y2 + y1)) >> 2)); r = g + y2; b = g + y1; out[pos++] = (byte) (r < 0 ? 0 : r > max ? 255 : ((int) r) >> shift); out[pos++] = (byte) (g < 0 ? 0 : g > max ? 255 : ((int) g) >> shift); out[pos++] = (byte) (b < 0 ? 0 : b > max ? 255 : ((int) b) >> shift); } } } else { // no multi-component transform if (info.enumerateCS == Info.CS_SYCC && componentsCount == 3) { final float[] y0items = getMappedComponent(transformedTiles, 0, info); final float[] y1items = getMappedComponent(transformedTiles, 1, info); final float[] y2items = getMappedComponent(transformedTiles, 2, info); shift = (info.siz.precisionInfo[0][0] + 1) - 8; offset = (128 << shift) + 0.5f; max = 255 * (1 << shift); final int yLen = y0items.length; for (int j = 0; j < yLen; j++) { y0 = y0items[j] + offset; y1 = y1items[j]; y2 = y2items[j]; r = (y0 + 1.402 * y2); g = (y0 - 0.34413 * y1 - 0.71414 * y2); b = (y0 + 1.772 * y1); out[pos++] = (byte) (r < 0 ? 0 : r > max ? 255 : ((int) r) >> shift); out[pos++] = (byte) (g < 0 ? 0 : g > max ? 255 : ((int) g) >> shift); out[pos++] = (byte) (b < 0 ? 0 : b > max ? 255 : ((int) b) >> shift); } } else { for (int c = 0; c < componentsCount; c++) { final float[] items = transformedTiles[c].floatItems; shift = (info.siz.precisionInfo[c][0] + 1) - 8; offset = (128 << shift) + 0.5f; max = (int) (127.5f * (1 << shift)); min = -max; pos = c; for (int j = 0; j < items.length; j++) { final float val = items[j]; out[pos] = (byte) (val <= min ? 0 : val >= max ? 255 : ((int) (val + offset)) >> shift); pos += componentsCount; } } } } inputSC.floatItems = null; resultImages.add(result); } info.tilesMap.clear(); if (resultImages.size() == 1) { return resultImages.get(0).bufferedImage; } else { final BufferedImage image = generateBufferedImage(siz.nComp, siz.Xsiz, siz.Ysiz); final byte[] mainData = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); final int c = siz.nComp; final int mainStripLen = siz.Xsiz * c; for (final SubbandCoefficient sc : resultImages) { final byte[] subData = sc.byteItems; final int x = sc.x; final int y = sc.y; final int w = sc.width; final int h = sc.height; final int subStripLen = w * c; final int xc = (x * c); for (int i = 0; i < h; i++) { final int stripOffset = i * subStripLen; final int mainOffset = ((i + y) * mainStripLen) + xc; System.arraycopy(subData, stripOffset, mainData, mainOffset, subStripLen); } sc.byteItems = null; } return image; } } private static float[] getMappedComponent(SubbandCoefficient[] arr, int keyVal, Info info) { if (info.cDef.isEmpty()) { return arr[keyVal].floatItems; } else { if (info.cDef.containsKey(keyVal)) { return arr[info.cDef.get(keyVal) - 1].floatItems; } else { return arr[keyVal].floatItems; } } } private static SubbandCoefficient transformTile(Info info, Tile tile, int c) { final SIZ siz = info.siz; final TileComponent comp = tile.components.get(c); final QCD qcd = tile.qcc[c] != null ? tile.qcc[c] : tile.qcd; final COD cod = tile.cod; final int NL = cod.nDecompLevel; final int guardBits = qcd.guardBits; final int precision = siz.precisionInfo[c][0] + 1; final boolean reversible = cod.transformation == 1; final Trns trns = new Trns(reversible); List subcos = new ArrayList(); int b = 0; for (int i = 0; i <= NL; i++) { TileResolution resolution = comp.resolutions.get(i); int width = resolution.getWidth(); int height = resolution.getHeight(); float[] coefficients = new float[width * height]; for (TileBand subband1 : resolution.tileBands) { int mu, epsilon; if (!qcd.hasScalar) { epsilon = qcd.exponentB[0] + (i > 0 ? 1 - i : 0); mu = qcd.mantissaB[0]; } else { epsilon = qcd.exponentB[b]; mu = qcd.mantissaB[b]; b++; } int multi = subband1.getMultiplier(); double toBePowered = precision + multi - epsilon; float delta = (float) (cod.transformation == 1 ? 1 : Math.pow(2, toBePowered) * (1 + mu / (2048 * 1.0f))); int mb = (guardBits + epsilon - 1); sendForPassing(coefficients, width, subband1, delta, mb, reversible); } SubbandCoefficient sc = new SubbandCoefficient(); sc.width = width; sc.height = height; sc.floatItems = coefficients; subcos.add(sc); } SubbandCoefficient result = trns.getInversed(subcos, comp.x0, comp.y0); result.x = comp.x0; result.y = comp.y0; int sw = result.width; int sh = result.height; int dw = result.width * siz.precisionInfo[c][1]; int dh = result.height * siz.precisionInfo[c][1]; if (sw != dw || sh != dh) { result.floatItems = applyBilinearScaling(result.floatItems, sw, sh, dw, dh); } return result; } private static float[] applyBilinearScaling(float[] data, int sw, int sh, int dw, int dh) { if (sh == 1) { float[] temp = new float[2 * sw]; System.arraycopy(data, 0, temp, 0, sw); System.arraycopy(data, 0, temp, sw, sw); sh = 2; data = temp; } float[] temp = new float[dw * dh]; float A, B, C, D; int x, y, index; float xRatio = ((float) (sw - 1)) / dw; float yRatio = ((float) (sh - 1)) / dh; float xDiff, yDiff; int offset = 0; for (int i = 0; i < dh; i++) { for (int j = 0; j < dw; j++) { x = (int) (xRatio * j); y = (int) (yRatio * i); xDiff = (xRatio * j) - x; yDiff = (yRatio * i) - y; index = y * sw + x; A = data[index]; B = data[index + 1]; C = data[index + sw]; D = data[index + sw + 1]; temp[offset++] = (A * (1 - xDiff) * (1 - yDiff) + B * (xDiff) * (1 - yDiff) + C * (yDiff) * (1 - xDiff) + D * (xDiff * yDiff)); } } return temp; } private static void sendForPassing(float[] coefficients, int resWidth, TileBand subband, float delta, int mb, boolean reversible) { int width = subband.x1 - subband.x0; List codeBlocks = subband.codeBlocks; int right = 0; int bottom = 0; switch (subband.type) { case TileBand.HH: right = 1; bottom = resWidth; break; case TileBand.LH: bottom = resWidth; break; case TileBand.HL: right = 1; break; } for (CodeBlock codeBlock : codeBlocks) { int blockWidth = codeBlock.tbx1_ - codeBlock.tbx0_; int blockHeight = codeBlock.tby1_ - codeBlock.tby0_; if (blockWidth == 0 || blockHeight == 0 || codeBlock.dataList.isEmpty()) { continue; } Tier1Decoder tier1 = new Tier1Decoder(blockWidth, blockHeight, subband, codeBlock.zeroBitPlanes, mb); List datas = codeBlock.dataList; int totalLength = 0; int codingpasses = 0; for (BlockData dataItem : datas) { totalLength += (dataItem.end - dataItem.start); codingpasses += (dataItem.nCodingPass & 0xff); } byte[] encodedData = new byte[totalLength]; int position = 0; for (BlockData dataItem : datas) { byte[] chunk = new byte[dataItem.end - dataItem.start]; System.arraycopy(dataItem.data, dataItem.start, chunk, 0, chunk.length); System.arraycopy(chunk, 0, encodedData, position, chunk.length); position += chunk.length; } EntropyDecoder decoder = new EntropyDecoder(encodedData, 0, totalLength); tier1.setDecoder(decoder); int currentCodingpassType = 2; for (int j = 0; j < codingpasses; j++) { switch (currentCodingpassType) { case 0: tier1.runSPP(); break; case 1: tier1.runMRP(); break; case 2: tier1.runCP(); break; } currentCodingpassType = (currentCodingpassType + 1) % 3; } int offset = (codeBlock.tbx0_ - subband.x0) + (codeBlock.tby0_ - subband.y0) * width; byte[] sign = tier1.coefficientsSign; Object magObj = tier1.magnitude; byte[] bitsDecoded = tier1.bitsDecoded; float magnitudeCorrection = reversible ? 0.0f : 0.5f; position = 0; boolean interleave = (subband.type != TileBand.LL); for (int j = 0; j < blockHeight; j++) { int row = (offset / width); int levelOffset = 2 * row * (resWidth - width) + right + bottom; for (int k = 0; k < blockWidth; k++) { float n = 0.0f; switch (tier1.mbType) { case Tier1Decoder.BYTEMB: n = ((byte[]) magObj)[position] & 0xff; break; case Tier1Decoder.SHORTMB: n = ((short[]) magObj)[position] & 0xffff; break; case Tier1Decoder.INTMB: n = ((int[]) magObj)[position]; break; } if (n != 0) { n = (n + magnitudeCorrection) * delta; if (sign[position] != 0) { n = -n; } int nb = bitsDecoded[position] & 0xff; int pos = interleave ? (levelOffset + (offset << 1)) : offset; if (reversible && (nb >= mb)) { coefficients[pos] = n; } else { coefficients[pos] = n * (1 << (mb - nb)); } } offset++; position++; } offset += width - blockWidth; } tier1.neighborSigns = null; tier1.coefficientsSign = null; tier1.magnitude = null; tier1.currentFlag = null; tier1.bitsDecoded = null; tier1.cx = null; } } private static boolean[] toBoolean8(byte b) { boolean[] bool = new boolean[8]; for (int j = 7; j >= 0; j--) { bool[j] = ((b >> j) & 1) == 1; } return bool; } private static BufferedImage generateBufferedImage(int nComp, int width, int height) { ICC_ColorSpace cSpace; ComponentColorModel model; WritableRaster ras; switch (nComp) { case 1: cSpace = new ICC_ColorSpace(ICC_Profile.getInstance(ColorSpace.CS_GRAY)); model = new ComponentColorModel(cSpace, false, false, 1, DataBuffer.TYPE_BYTE); ras = model.createCompatibleWritableRaster(width, height); return new BufferedImage(model, ras, false, null); case 2: cSpace = new ICC_ColorSpace(ICC_Profile.getInstance(ColorSpace.CS_GRAY)); model = new ComponentColorModel(cSpace, true, false, 1, DataBuffer.TYPE_BYTE); ras = model.createCompatibleWritableRaster(width, height); return new BufferedImage(model, ras, false, null); case 3: cSpace = new ICC_ColorSpace(ICC_Profile.getInstance(ColorSpace.CS_sRGB)); model = new ComponentColorModel(cSpace, false, false, 1, DataBuffer.TYPE_BYTE); ras = model.createCompatibleWritableRaster(width, height); return new BufferedImage(model, ras, false, null); case 4: cSpace = new ICC_ColorSpace(ICC_Profile.getInstance(ColorSpace.CS_sRGB)); model = new ComponentColorModel(cSpace, true, false, 1, DataBuffer.TYPE_BYTE); ras = model.createCompatibleWritableRaster(width, height); return new BufferedImage(model, ras, false, null); } return null; } /** * Not recommended for external use. *

* decodes JPEG2000 image data as rgb/gray image bytes
* Example: if rgb component image then returned byte array contains r * followed by g followed by b * * @param jpxRawData A byte[] array containing the JPEG data * @return BufferedImage to read image * @throws Exception Provides for different exceptions thrown under java * lang package */ public byte[] readComponentsAsConvertedBytes(final byte[] jpxRawData) throws Exception { final Info info = new Info(); final JPXReader reader = new JPXReader(jpxRawData); if (Markers.SOC == ((jpxRawData[0] & 0xff) << 8 | (jpxRawData[1] & 0xff))) { readCodeStream(info, reader, jpxRawData.length); } else { decodeMain(info, reader); decodeContiguousCodeStreamBoxes(info, reader); } generateTileMap(info); decodeTileOffsets(info, reader); final SIZ siz = info.siz; final int componentsCount = siz.nComp; final List resultImages = new ArrayList(); for (int i = 0; i < info.tilesMap.size(); i++) { final Tile tile = info.tilesMap.get(i); SubbandCoefficient[] transformedTiles = new SubbandCoefficient[componentsCount]; for (int c = 0; c < componentsCount; c++) { transformedTiles[c] = transformTile(info, tile, c); tile.components.get(c).resolutions.clear(); } final SubbandCoefficient inputSC = transformedTiles[0]; final SubbandCoefficient result = new SubbandCoefficient(); result.x = inputSC.x; result.y = inputSC.y; result.width = inputSC.width; result.height = inputSC.height; byte[] out = new byte[result.width * result.height * componentsCount]; result.byteItems = out; int shift; float offset, maxK, min, max; int pos = 0; double c1, c2, c3, c4, r, g, b; float[] comp1, comp2, comp3, comp4 = null; if (tile.cod.multiCompTransform == 1) { comp1 = transformedTiles[0].floatItems; comp2 = transformedTiles[1].floatItems; comp3 = transformedTiles[2].floatItems; if (tile.components.size() == 4) { comp4 = transformedTiles[3].floatItems; } shift = (info.siz.precisionInfo[0][0] + 1) - 8; offset = (128 << shift) + 0.5f; max = 255 * (1 << shift); maxK = max * 0.5f; min = -maxK; if (tile.cod.transformation == 0) { pos = 0; for (int j = 0; j < comp1.length; j++) { c1 = comp1[j] + offset; c2 = comp2[j]; c3 = comp3[j]; r = c1 + 1.402 * c3; g = c1 - 0.34413 * c2 - 0.71414 * c3; b = c1 + 1.772 * c2; out[pos++] = (byte) (r <= 0 ? 0 : r >= max ? 255 : ((int) r) >> shift); out[pos++] = (byte) (g <= 0 ? 0 : g >= max ? 255 : ((int) g) >> shift); out[pos++] = (byte) (b <= 0 ? 0 : b >= max ? 255 : ((int) b) >> shift); if (comp4 != null) { c4 = comp4[j]; out[pos++] = (byte) (c4 <= min ? 0 : c4 >= maxK ? 255 : ((int) (c4 + offset)) >> shift); } } } else { for (int j = 0; j < comp1.length; j++) { c1 = comp1[j] + offset; c2 = comp2[j]; c3 = comp3[j]; g = c1 - (((int) (c3 + c2)) >> 2); r = g + c3; b = g + c2; out[pos++] = (byte) (r <= 0 ? 0 : r >= max ? 255 : ((int) r) >> shift); out[pos++] = (byte) (g <= 0 ? 0 : g >= max ? 255 : ((int) g) >> shift); out[pos++] = (byte) (b <= 0 ? 0 : b >= max ? 255 : ((int) b) >> shift); if (comp4 != null) { c4 = comp4[j]; out[pos++] = (byte) (c4 <= min ? 0 : c4 >= maxK ? 255 : ((int) (c4 + offset)) >> shift); } } } } else { for (int c = 0; c < componentsCount; c++) { final float[] items = transformedTiles[c].floatItems; shift = (info.siz.precisionInfo[c][0] + 1) - 8; offset = (128 << shift) + 0.5f; max = (int) (127.5f * (1 << shift)); min = -max; pos = c; for (int j = 0; j < items.length; j++) { final float val = items[j]; out[pos] = (byte) (val <= min ? 0 : val >= max ? 255 : ((int) (val + offset)) >> shift); pos += componentsCount; } } } resultImages.add(result); } info.tilesMap.clear(); byte[] mainData; if (resultImages.size() == 1) { mainData = resultImages.get(0).byteItems; } else { mainData = new byte[siz.nComp * siz.Xsiz * siz.Ysiz]; final int c = siz.nComp; final int mainStripLen = siz.Xsiz * c; for (final SubbandCoefficient sc : resultImages) { final byte[] subData = sc.byteItems; final int x = sc.x; final int y = sc.y; final int w = sc.width; final int h = sc.height; final int subStripLen = w * c; final int xc = (x * c); for (int i = 0; i < h; i++) { final int stripOffset = i * subStripLen; final int mainOffset = ((i + y) * mainStripLen) + xc; System.arraycopy(subData, stripOffset, mainData, mainOffset, subStripLen); } sc.byteItems = null; } } boolean isPalette = info.palette != null && info.siz.nComp == 1; if (isPalette) { byte[] imageData = new byte[info.palette.nColumns * siz.Xsiz * siz.Ysiz]; int pos = 0; for (int i = 0; i < mainData.length; i++) { final int[] cc = info.palette.cValues[mainData[i] & 0xff]; for (int j = 0; j < cc.length; j++) { imageData[pos] = (byte) cc[j]; pos++; } } mainData = imageData; } if (info.enumerateCS == Info.CS_CMYK) { final EnumeratedSpace cs = new EnumeratedSpace(); byte c, m, y, k; int a = 0, p = 0; byte[] rgbData = new byte[info.imageHeight * info.imageWidth * 3]; for (int i = 0; i < mainData.length; i += 4) { c = mainData[a++]; m = mainData[a++]; y = mainData[a++]; k = mainData[a++]; final byte[] rgb = cs.getRGB(c, m, y, k); rgbData[p++] = rgb[0]; rgbData[p++] = rgb[1]; rgbData[p++] = rgb[2]; } mainData = rgbData; } return mainData; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy