/// <summary> Apply the inverse component transformation associated with the current /// tile. If no component transformation has been requested by the user, /// data are not modified. Else, appropriate method is called (invRCT or /// invICT). /// /// </summary> /// <seealso cref="invRCT"> /// /// </seealso> /// <seealso cref="invICT"> /// /// </seealso> /// <param name="blk">Determines the rectangular area to return. /// /// </param> /// <param name="c">Index of the output component. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> public virtual DataBlk getInternCompData(DataBlk blk, int c) { // if specified in the command line that no component transform should // be made, return original data if (noCompTransf) { return(src.getInternCompData(blk, c)); } switch (transfType) { case NONE: return(src.getInternCompData(blk, c)); case INV_RCT: return(invRCT(blk, c)); case INV_ICT: return(invICT(blk, c)); default: throw new System.ArgumentException("Non JPEG 2000 part I" + " component transformation"); } }
/// <summary> Apply the component transformation associated with the current tile. If /// no component transformation has been requested by the user, data are /// not modified. Else, appropriate method is called (forwRCT or forwICT). /// /// </summary> /// <seealso cref="forwRCT"> /// /// </seealso> /// <seealso cref="forwICT"> /// /// </seealso> /// <param name="blk">Determines the rectangular area to return. /// /// </param> /// <param name="c">Index of the output component. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> public virtual DataBlk getInternCompData(DataBlk blk, int c) { switch (transfType) { case NONE: return(src.getInternCompData(blk, c)); case FORW_RCT: return(forwRCT(blk, c)); case FORW_ICT: return(forwICT(blk, c)); default: throw new System.ArgumentException("Non JPEG 2000 part 1 " + "component" + " transformation for tile: " + tIdx); } }
/// <summary> Returns, in the blk argument, a block of image data containing the /// specifed rectangular area, in the specified component. The data is /// returned, as a reference to the internal data, if any, instead of as a /// copy, therefore the returned data should not be modified. /// /// <p>The rectangular area to return is specified by the 'ulx', 'uly', 'w' /// and 'h' members of the 'blk' argument, relative to the current /// tile. These members are not modified by this method. The 'offset' and /// 'scanw' of the returned data can be arbitrary. See the 'DataBlk' /// class.</p> /// /// <p>This method, in general, is more efficient than the 'getCompData()' /// method since it may not copy the data. However if the array of returned /// data is to be modified by the caller then the other method is probably /// preferable.</p> /// /// <p>If the data array in <tt>blk</tt> is <tt>null</tt>, then a new one /// is created if necessary. The implementation of this interface may /// choose to return the same array or a new one, depending on what is more /// efficient. Therefore, the data array in <tt>blk</tt> prior to the /// method call should not be considered to contain the returned data, a /// new array may have been created. Instead, get the array from /// <tt>blk</tt> after the method has returned.</p> /// /// <p>The returned data may have its 'progressive' attribute set. In this /// case the returned data is only an approximation of the "final" /// data.</p> /// /// </summary> /// <param name="blk">Its coordinates and dimensions specify the area to return, /// relative to the current tile. Some fields in this object are modified /// to return the data. /// /// </param> /// <param name="c">The index of the component from which to get the data. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> /// <seealso cref="getCompData"> /// /// </seealso> public DataBlk getInternCompData(DataBlk blk, int c) { // Check that block is inside tile if (blk.ulx < 0 || blk.uly < 0 || blk.w > compW[c] || blk.h > compH[c]) { throw new System.ArgumentException("Block is outside the tile"); } // Translate to the sources coordinates //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" int incx = (int)System.Math.Ceiling(x0siz / (double)src.getCompSubsX(c)); //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" int incy = (int)System.Math.Ceiling(y0siz / (double)src.getCompSubsY(c)); blk.ulx -= incx; blk.uly -= incy; blk = src.getInternCompData(blk, c); // Translate back to the tiled coordinates blk.ulx += incx; blk.uly += incy; return(blk); }
public static Image FromStream(Stream stream) { RandomAccessIO in_stream = new ISRandomAccessIO(stream); // Initialize default parameters ParameterList defpl = GetDefaultParameterList(decoder_pinfo); // Create parameter list using defaults ParameterList pl = 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 ApplicationException("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 ApplicationException("Error while reading bit stream header or parsing packets.", e); } catch (ArgumentException e) { throw new ApplicationException("Cannot instantiate bit stream reader.", e); } // **** Entropy decoder **** EntropyDecoder entdec; try { entdec = hd.createEntropyDecoder(breader, pl); } catch (ArgumentException e) { throw new ApplicationException("Cannot instantiate entropy decoder.", e); } // **** ROI de-scaler **** ROIDeScaler roids; try { roids = hd.createROIDeScaler(entdec, pl, decSpec); } catch (ArgumentException e) { throw new ApplicationException("Cannot instantiate roi de-scaler.", e); } // **** Dequantizer **** Dequantizer deq; try { deq = hd.createDequantizer(roids, depth, decSpec); } catch (ArgumentException e) { throw new ApplicationException("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 ApplicationException("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 ApplicationException("Could not instantiate ICC profiler.", e); } catch (ColorSpaceException e) { throw new ApplicationException("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; // Assuming 8-bit components // **** Copy to Bitmap **** PixelFormat pixelFormat; switch (numComps) { case 1: pixelFormat = PixelFormat.Format24bppRgb; break; case 3: pixelFormat = PixelFormat.Format24bppRgb; break; case 4: case 5: pixelFormat = PixelFormat.Format32bppArgb; break; default: throw new ApplicationException("Unsupported PixelFormat. " + numComps + " components."); } Bitmap dst = new Bitmap(decodedImage.ImgWidth, decodedImage.ImgHeight, pixelFormat); 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; } int outputBytesPerPixel = Math.Max(3, Math.Min(4, bytesPerPixel)); byte[] rowvalues = new byte[width * outputBytesPerPixel]; 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 * outputBytesPerPixel; 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: case 5: rowvalues[offset + 0] = (byte)tmp[2]; rowvalues[offset + 1] = (byte)tmp[1]; rowvalues[offset + 2] = (byte)tmp[0]; rowvalues[offset + 3] = (byte)tmp[3]; break; } } BitmapData dstdata = dst.LockBits( new System.Drawing.Rectangle(tOffx, tOffy + l, width, 1), ImageLockMode.WriteOnly, pixelFormat); IntPtr ptr = dstdata.Scan0; System.Runtime.InteropServices.Marshal.Copy(rowvalues, 0, ptr, rowvalues.Length); dst.UnlockBits(dstdata); } } } return(dst); }
/// <summary> Returns, in the blk argument, a block of image data containing the /// specifed rectangular area, in the specified component. The data is /// returned, as a reference to the internal data, if any, instead of as a /// copy, therefore the returned data should not be modified. /// /// <P>The rectangular area to return is specified by the 'ulx', 'uly', 'w' /// and 'h' members of the 'blk' argument, relative to the current /// tile. These members are not modified by this method. The 'offset' and /// 'scanw' of the returned data can be arbitrary. See the 'DataBlk' class. /// /// <P>This method, in general, is more efficient than the 'getCompData()' /// method since it may not copy the data. However if the array of returned /// data is to be modified by the caller then the other method is probably /// preferable. /// /// <P>If possible, the data in the returned 'DataBlk' should be the /// internal data itself, instead of a copy, in order to increase the data /// transfer efficiency. However, this depends on the particular /// implementation (it may be more convenient to just return a copy of the /// data). This is the reason why the returned data should not be modified. /// /// <P>If the data array in <tt>blk</tt> is <tt>null</tt>, then a new one /// is created if necessary. The implementation of this interface may /// choose to return the same array or a new one, depending on what is more /// efficient. Therefore, the data array in <tt>blk</tt> prior to the /// method call should not be considered to contain the returned data, a /// new array may have been created. Instead, get the array from /// <tt>blk</tt> after the method has returned. /// /// <P>The returned data may have its 'progressive' attribute set. In this /// case the returned data is only an approximation of the "final" data. /// /// </summary> /// <param name="blk">Its coordinates and dimensions specify the area to return, /// relative to the current tile. Some fields in this object are modified /// to return the data. /// /// </param> /// <param name="c">The index of the component from which to get the data. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> /// <seealso cref="getCompData"> /// /// </seealso> public virtual DataBlk getInternCompData(DataBlk out_Renamed, int c) { return(src.getInternCompData(out_Renamed, c)); }
/// <summary> Implements the 'getInternCompData()' and the 'getCompData()' /// methods. The 'intern' flag signals which of the two methods should run /// as. /// /// </summary> /// <param name="blk">The data block to get. /// /// </param> /// <param name="c">The index of the component from which to get the data. /// /// </param> /// <param name="intern">If true behave as 'getInternCompData(). Otherwise behave /// as 'getCompData()' /// /// </param> /// <returns> The requested data block /// /// </returns> /// <seealso cref="getInternCompData"> /// /// </seealso> /// <seealso cref="getCompData"> /// /// </seealso> private DataBlk getData(DataBlk blk, int c, bool intern) { DataBlk reqBlk; // Reference to block used in request to source // Keep request data type int otype = blk.DataType; if (otype == srcBlk.DataType) { // Probably requested type is same as source type reqBlk = blk; } else { // Probably requested type is not the same as source type reqBlk = srcBlk; // We need to copy requested coordinates and size reqBlk.ulx = blk.ulx; reqBlk.uly = blk.uly; reqBlk.w = blk.w; reqBlk.h = blk.h; } // Get source data block if (intern) { // We can use the intern variant srcBlk = src.getInternCompData(reqBlk, c); } else { // Do not use the intern variant. Note that this is not optimal // since if we are going to convert below then we could have used // the intern variant. But there is currently no way to know if we // will need to do conversion or not before getting the data. srcBlk = src.getCompData(reqBlk, c); } // Check if casting is needed if (srcBlk.DataType == otype) { return(srcBlk); } int i; int k, kSrc, kmin; float mult; int w = srcBlk.w; int h = srcBlk.h; switch (otype) { case DataBlk.TYPE_FLOAT: // Cast INT -> FLOAT float[] farr; int[] srcIArr; // Get data array from resulting blk farr = (float[])blk.Data; if (farr == null || farr.Length < w * h) { farr = new float[w * h]; blk.Data = farr; } blk.scanw = srcBlk.w; blk.offset = 0; blk.progressive = srcBlk.progressive; srcIArr = (int[])srcBlk.Data; // Cast data from source to blk fp = src.getFixedPoint(c); if (fp != 0) { mult = 1.0f / (1 << fp); for (i = h - 1, k = w * h - 1, kSrc = srcBlk.offset + (h - 1) * srcBlk.scanw + w - 1; i >= 0; i--) { for (kmin = k - w; k > kmin; k--, kSrc--) { farr[k] = ((srcIArr[kSrc] * mult)); } // Jump to geggining of next line in source kSrc -= (srcBlk.scanw - w); } } else { for (i = h - 1, k = w * h - 1, kSrc = srcBlk.offset + (h - 1) * srcBlk.scanw + w - 1; i >= 0; i--) { for (kmin = k - w; k > kmin; k--, kSrc--) { //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" farr[k] = ((float)(srcIArr[kSrc])); } // Jump to geggining of next line in source kSrc -= (srcBlk.scanw - w); } } break; // End of cast INT-> FLOAT case DataBlk.TYPE_INT: // cast FLOAT -> INT int[] iarr; float[] srcFArr; // Get data array from resulting blk iarr = (int[])blk.Data; if (iarr == null || iarr.Length < w * h) { iarr = new int[w * h]; blk.Data = iarr; } blk.scanw = srcBlk.w; blk.offset = 0; blk.progressive = srcBlk.progressive; srcFArr = (float[])srcBlk.Data; // Cast data from source to blk if (fp != 0) { //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" mult = (float)(1 << fp); for (i = h - 1, k = w * h - 1, kSrc = srcBlk.offset + (h - 1) * srcBlk.scanw + w - 1; i >= 0; i--) { for (kmin = k - w; k > kmin; k--, kSrc--) { if (srcFArr[kSrc] > 0.0f) { //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" iarr[k] = (int)(srcFArr[kSrc] * mult + 0.5f); } else { //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" iarr[k] = (int)(srcFArr[kSrc] * mult - 0.5f); } } // Jump to geggining of next line in source kSrc -= (srcBlk.scanw - w); } } else { for (i = h - 1, k = w * h - 1, kSrc = srcBlk.offset + (h - 1) * srcBlk.scanw + w - 1; i >= 0; i--) { for (kmin = k - w; k > kmin; k--, kSrc--) { if (srcFArr[kSrc] > 0.0f) { //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" iarr[k] = (int)(srcFArr[kSrc] + 0.5f); } else { //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" iarr[k] = (int)(srcFArr[kSrc] - 0.5f); } } // Jump to geggining of next line in source kSrc -= (srcBlk.scanw - w); } } break; // End cast FLOAT -> INT default: throw new System.ArgumentException("Only integer and float data " + "are " + "supported by JJ2000"); } return(blk); }
/// <summary> Returns the next code-block in the current tile for the specified /// component. The order in which code-blocks are returned is not /// specified. However each code-block is returned only once and all /// code-blocks will be returned if the method is called 'N' times, where /// 'N' is the number of code-blocks in the tile. After all the code-blocks /// have been returned for the current tile calls to this method will /// return 'null'. /// /// <p>When changing the current tile (through 'setTile()' or 'nextTile()') /// this method will always return the first code-block, as if this method /// was never called before for the new current tile.</p> /// /// <p>The data returned by this method is the data in the internal buffer /// of this object, and thus can not be modified by the caller. The /// 'offset' and 'scanw' of the returned data have, in general, some /// non-zero value. The 'magbits' of the returned data is not set by this /// method and should be ignored. See the 'CBlkWTData' class.</p> /// /// <p>The 'ulx' and 'uly' members of the returned 'CBlkWTData' object /// contain the coordinates of the top-left corner of the block, with /// respect to the tile, not the subband.</p> /// /// </summary> /// <param name="c">The component for which to return the next code-block. /// /// </param> /// <param name="cblk">If non-null this object will be used to return the new /// code-block. If null a new one will be allocated and returned. /// /// </param> /// <returns> The next code-block in the current tile for component 'n', or /// null if all code-blocks for the current tile have been returned. /// /// </returns> /// <seealso cref="CBlkWTData"> /// /// </seealso> public override CBlkWTData getNextInternCodeBlock(int c, CBlkWTData cblk) { int cbm, cbn, cn, cm; int acb0x, acb0y; SubbandAn sb; intData = (filters.getWTDataType(tIdx, c) == DataBlk.TYPE_INT); //If the source image has not been decomposed if (decomposedComps[c] == null) { int k, w, h; DataBlk bufblk; System.Object dst_data; w = getTileCompWidth(tIdx, c); h = getTileCompHeight(tIdx, c); //Get the source image data if (intData) { decomposedComps[c] = new DataBlkInt(0, 0, w, h); bufblk = new DataBlkInt(); } else { decomposedComps[c] = new DataBlkFloat(0, 0, w, h); bufblk = new DataBlkFloat(); } // Get data from source line by line (this diminishes the memory // requirements on the data source) dst_data = decomposedComps[c].Data; int lstart = getCompULX(c); bufblk.ulx = lstart; bufblk.w = w; bufblk.h = 1; int kk = getCompULY(c); for (k = 0; k < h; k++, kk++) { bufblk.uly = kk; bufblk.ulx = lstart; bufblk = src.getInternCompData(bufblk, c); // CONVERSION PROBLEM? Array.Copy((System.Array)bufblk.Data, bufblk.offset, (System.Array)dst_data, k * w, w); } //Decompose source image waveletTreeDecomposition(decomposedComps[c], getAnSubbandTree(tIdx, c), c); // Make the first subband the current one currentSubband[c] = getNextSubband(c); lastn[c] = -1; lastm[c] = 0; } // Get the next code-block to "send" do { // Calculate number of code-blocks in current subband ncblks = currentSubband[c].numCb; // Goto next code-block lastn[c]++; if (lastn[c] == ncblks.x) { // Got to end of this row of // code-blocks lastn[c] = 0; lastm[c]++; } if (lastm[c] < ncblks.y) { // Not past the last code-block in the subband, we can return // this code-block break; } // If we get here we already sent all code-blocks in this subband, // goto next subband currentSubband[c] = getNextSubband(c); lastn[c] = -1; lastm[c] = 0; if (currentSubband[c] == null) { // We don't need the transformed data any more (a priori) decomposedComps[c] = null; // All code-blocks from all subbands in the current // tile have been returned so we return a null // reference return(null); } // Loop to find the next code-block }while (true); // Project code-block partition origin to subband. Since the origin is // always 0 or 1, it projects to the low-pass side (throught the ceil // operator) as itself (i.e. no change) and to the high-pass side // (through the floor operator) as 0, always. acb0x = cb0x; acb0y = cb0y; switch (currentSubband[c].sbandIdx) { case Subband.WT_ORIENT_LL: // No need to project since all low-pass => nothing to do break; case Subband.WT_ORIENT_HL: acb0x = 0; break; case Subband.WT_ORIENT_LH: acb0y = 0; break; case Subband.WT_ORIENT_HH: acb0x = 0; acb0y = 0; break; default: throw new System.ApplicationException("Internal JJ2000 error"); } // Initialize output code-block if (cblk == null) { if (intData) { cblk = new CBlkWTDataInt(); } else { cblk = new CBlkWTDataFloat(); } } cbn = lastn[c]; cbm = lastm[c]; sb = currentSubband[c]; cblk.n = cbn; cblk.m = cbm; cblk.sb = sb; // Calculate the indexes of first code-block in subband with respect // to the partitioning origin, to then calculate the position and size // NOTE: when calculating "floor()" by integer division the dividend // and divisor must be positive, we ensure that by adding the divisor // to the dividend and then substracting 1 to the result of the // division cn = (sb.ulcx - acb0x + sb.nomCBlkW) / sb.nomCBlkW - 1; cm = (sb.ulcy - acb0y + sb.nomCBlkH) / sb.nomCBlkH - 1; if (cbn == 0) { // Left-most code-block, starts where subband starts cblk.ulx = sb.ulx; } else { // Calculate starting canvas coordinate and convert to subb. coords cblk.ulx = (cn + cbn) * sb.nomCBlkW - (sb.ulcx - acb0x) + sb.ulx; } if (cbm == 0) { // Bottom-most code-block, starts where subband starts cblk.uly = sb.uly; } else { cblk.uly = (cm + cbm) * sb.nomCBlkH - (sb.ulcy - acb0y) + sb.uly; } if (cbn < ncblks.x - 1) { // Calculate where next code-block starts => width cblk.w = (cn + cbn + 1) * sb.nomCBlkW - (sb.ulcx - acb0x) + sb.ulx - cblk.ulx; } else { // Right-most code-block, ends where subband ends cblk.w = sb.ulx + sb.w - cblk.ulx; } if (cbm < ncblks.y - 1) { // Calculate where next code-block starts => height cblk.h = (cm + cbm + 1) * sb.nomCBlkH - (sb.ulcy - acb0y) + sb.uly - cblk.uly; } else { // Bottom-most code-block, ends where subband ends cblk.h = sb.uly + sb.h - cblk.uly; } cblk.wmseScaling = 1f; // Since we are in getNextInternCodeBlock() we can return a // reference to the internal buffer, no need to copy. Just initialize // the 'offset' and 'scanw' cblk.offset = cblk.uly * decomposedComps[c].w + cblk.ulx; cblk.scanw = decomposedComps[c].w; // For the data just put a reference to our buffer cblk.Data = decomposedComps[c].Data; // Return code-block return(cblk); }