public static PortableImage FromStream(Stream stream, ParameterList parameters = null)
{
RandomAccessIO in_stream = new ISRandomAccessIO(stream);
// Initialize default parameters
ParameterList defpl = GetDefaultDecoderParameterList(decoder_pinfo);
// Create parameter list using defaults
ParameterList pl = parameters ?? new ParameterList(defpl);
// **** File Format ****
// If the codestream is wrapped in the jp2 fileformat, Read the
// file format wrapper
FileFormatReader ff = new FileFormatReader(in_stream);
ff.readFileFormat();
if (ff.JP2FFUsed)
{
in_stream.seek(ff.FirstCodeStreamPos);
}
// +----------------------------+
// | Instantiate decoding chain |
// +----------------------------+
// **** Header decoder ****
// Instantiate header decoder and read main header
HeaderInfo hi = new HeaderInfo();
HeaderDecoder hd;
try
{
hd = new HeaderDecoder(in_stream, pl, hi);
}
catch (EndOfStreamException e)
{
throw new InvalidOperationException("Codestream too short or bad header, unable to decode.", e);
}
int nCompCod = hd.NumComps;
int nTiles = hi.sizValue.NumTiles;
DecoderSpecs decSpec = hd.DecoderSpecs;
// Get demixed bitdepths
int[] depth = new int[nCompCod];
for (int i = 0; i < nCompCod; i++)
{
depth[i] = hd.getOriginalBitDepth(i);
}
// **** Bit stream reader ****
BitstreamReaderAgent breader;
try
{
breader = BitstreamReaderAgent.createInstance(in_stream, hd, pl, decSpec, false, hi);
}
catch (IOException e)
{
throw new InvalidOperationException("Error while reading bit stream header or parsing packets.", e);
}
catch (ArgumentException e)
{
throw new InvalidOperationException("Cannot instantiate bit stream reader.", e);
}
// **** Entropy decoder ****
EntropyDecoder entdec;
try
{
entdec = hd.createEntropyDecoder(breader, pl);
}
catch (ArgumentException e)
{
throw new InvalidOperationException("Cannot instantiate entropy decoder.", e);
}
// **** ROI de-scaler ****
ROIDeScaler roids;
try
{
roids = hd.createROIDeScaler(entdec, pl, decSpec);
}
catch (ArgumentException e)
{
throw new InvalidOperationException("Cannot instantiate roi de-scaler.", e);
}
// **** Dequantizer ****
Dequantizer deq;
try
{
deq = hd.createDequantizer(roids, depth, decSpec);
}
catch (ArgumentException e)
{
throw new InvalidOperationException("Cannot instantiate dequantizer.", e);
}
// **** Inverse wavelet transform ***
InverseWT invWT;
try
{
// full page inverse wavelet transform
invWT = InverseWT.createInstance(deq, decSpec);
}
catch (ArgumentException e)
{
throw new InvalidOperationException("Cannot instantiate inverse wavelet transform.", e);
}
int res = breader.ImgRes;
invWT.ImgResLevel = res;
// **** Data converter **** (after inverse transform module)
ImgDataConverter converter = new ImgDataConverter(invWT, 0);
// **** Inverse component transformation ****
InvCompTransf ictransf = new InvCompTransf(converter, decSpec, depth, pl);
// **** Color space mapping ****
BlkImgDataSrc color;
if (ff.JP2FFUsed && pl.getParameter("nocolorspace").Equals("off"))
{
try
{
ColorSpace csMap = new ColorSpace(in_stream, hd, pl);
BlkImgDataSrc channels = hd.createChannelDefinitionMapper(ictransf, csMap);
BlkImgDataSrc resampled = hd.createResampler(channels, csMap);
BlkImgDataSrc palettized = hd.createPalettizedColorSpaceMapper(resampled, csMap);
color = hd.createColorSpaceMapper(palettized, csMap);
}
catch (ArgumentException e)
{
throw new InvalidOperationException("Could not instantiate ICC profiler.", e);
}
catch (ColorSpaceException e)
{
throw new InvalidOperationException("Error processing ColorSpace information.", e);
}
}
else
{
// Skip colorspace mapping
color = ictransf;
}
// This is the last image in the decoding chain and should be
// assigned by the last transformation:
BlkImgDataSrc decodedImage = color;
if (color == null)
{
decodedImage = ictransf;
}
int numComps = decodedImage.NumComps;
int bytesPerPixel = (numComps == 4 ? 4 : 3);
// **** Copy to Bitmap ****
var dst = new PortableImage(decodedImage.ImgWidth, decodedImage.ImgHeight, numComps);
Coord numTiles = decodedImage.getNumTiles(null);
int tIdx = 0;
for (int y = 0; y < numTiles.y; y++)
{
// Loop on horizontal tiles
for (int x = 0; x < numTiles.x; x++, tIdx++)
{
decodedImage.setTile(x, y);
int height = decodedImage.getTileCompHeight(tIdx, 0);
int width = decodedImage.getTileCompWidth(tIdx, 0);
int tOffx = decodedImage.getCompULX(0)
- (int)Math.Ceiling(decodedImage.ImgULX / (double)decodedImage.getCompSubsX(0));
int tOffy = decodedImage.getCompULY(0)
- (int)Math.Ceiling(decodedImage.ImgULY / (double)decodedImage.getCompSubsY(0));
DataBlkInt[] db = new DataBlkInt[numComps];
int[] ls = new int[numComps];
int[] mv = new int[numComps];
int[] fb = new int[numComps];
for (int i = 0; i < numComps; i++)
{
db[i] = new DataBlkInt();
ls[i] = 1 << (decodedImage.getNomRangeBits(0) - 1);
mv[i] = (1 << decodedImage.getNomRangeBits(0)) - 1;
fb[i] = decodedImage.getFixedPoint(0);
}
for (int l = 0; l < height; l++)
{
for (int i = numComps - 1; i >= 0; i--)
{
db[i].ulx = 0;
db[i].uly = l;
db[i].w = width;
db[i].h = 1;
decodedImage.getInternCompData(db[i], i);
}
int[] k = new int[numComps];
for (int i = numComps - 1; i >= 0; i--) k[i] = db[i].offset + width - 1;
byte[] rowvalues = new byte[width * bytesPerPixel];
for (int i = width - 1; i >= 0; i--)
{
int[] tmp = new int[numComps];
for (int j = numComps - 1; j >= 0; j--)
{
tmp[j] = (db[j].data_array[k[j]--] >> fb[j]) + ls[j];
tmp[j] = (tmp[j] < 0) ? 0 : ((tmp[j] > mv[j]) ? mv[j] : tmp[j]);
if (decodedImage.getNomRangeBits(j) != 8)
tmp[j] =
(int)
Math.Round(
((double)tmp[j] / Math.Pow(2D, (double)decodedImage.getNomRangeBits(j)))
* 255D);
}
int offset = i * bytesPerPixel;
switch (numComps)
{
case 1:
rowvalues[offset + 0] = (byte)tmp[0];
rowvalues[offset + 1] = (byte)tmp[0];
rowvalues[offset + 2] = (byte)tmp[0];
break;
case 3:
rowvalues[offset + 0] = (byte)tmp[2];
rowvalues[offset + 1] = (byte)tmp[1];
rowvalues[offset + 2] = (byte)tmp[0];
break;
case 4:
rowvalues[offset + 0] = (byte)tmp[3];
rowvalues[offset + 1] = (byte)tmp[2];
rowvalues[offset + 2] = (byte)tmp[1];
rowvalues[offset + 3] = (byte)tmp[0];
break;
}
}
dst.FillRow(tOffx, tOffy + l, width, rowvalues);
}
}
}
return dst;
}