/// <summary> Returns a block of image data containing the specifed rectangular area, /// in the specified component, as a reference to the internal buffer (see /// below). The rectangular area is specified by the coordinates and /// dimensions of the 'blk' object. /// /// <p>The area to return is specified by the 'ulx', 'uly', 'w' and 'h' /// members of the 'blk' argument. These members are not modified by this /// method.</p> /// /// <p>The data returned by this method can be the data in the internal /// buffer of this object, if any, and thus can not be modified by the /// caller. The 'offset' and 'scanw' of the returned data can be /// arbitrary. See the 'DataBlk' class.</p> /// /// <p>The returned data has its 'progressive' attribute unset /// (i.e. false).</p> /// /// </summary> /// <param name="blk">Its coordinates and dimensions specify the area to return. /// /// </param> /// <param name="c">The index of the component from which to get the data. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> /// <seealso cref="getInternCompData"> /// /// </seealso> public override DataBlk getInternCompData(DataBlk blk, int c) { int tIdx = TileIdx; if (src.getSynSubbandTree(tIdx, c).HorWFilter == null) { dtype = DataBlk.TYPE_INT; } else { dtype = src.getSynSubbandTree(tIdx, c).HorWFilter.DataType; } //If the source image has not been decomposed if (reconstructedComps[c] == null) { //Allocate component data buffer switch (dtype) { case DataBlk.TYPE_FLOAT: reconstructedComps[c] = new DataBlkFloat(0, 0, getTileCompWidth(tIdx, c), getTileCompHeight(tIdx, c)); break; case DataBlk.TYPE_INT: reconstructedComps[c] = new DataBlkInt(0, 0, getTileCompWidth(tIdx, c), getTileCompHeight(tIdx, c)); break; } //Reconstruct source image waveletTreeReconstruction(reconstructedComps[c], src.getSynSubbandTree(tIdx, c), c); if (pw != null && c == src.NumComps - 1) { pw.terminateProgressWatch(); } } if (blk.DataType != dtype) { if (dtype == DataBlk.TYPE_INT) { blk = new DataBlkInt(blk.ulx, blk.uly, blk.w, blk.h); } else { blk = new DataBlkFloat(blk.ulx, blk.uly, blk.w, blk.h); } } // Set the reference to the internal buffer blk.Data = reconstructedComps[c].Data; blk.offset = reconstructedComps[c].w * blk.uly + blk.ulx; blk.scanw = reconstructedComps[c].w; blk.progressive = false; return(blk); }
/// <summary> Constructor of the ROI scaler, takes a Quantizer as source of data to /// scale. /// /// </summary> /// <param name="src">The quantizer that is the source of data. /// /// </param> /// <param name="mg">The mask generator that will be used for all components /// /// </param> /// <param name="roi">Flag indicating whether there are rois specified. /// /// </param> /// <param name="sLev">The resolution levels that belong entirely to ROI /// /// </param> /// <param name="uba">Flag indicating whether block aligning is used. /// /// </param> /// <param name="encSpec">The encoder specifications for addition of roi specs /// /// </param> public ROIScaler(Quantizer src, ROIMaskGenerator mg, bool roi, int sLev, bool uba, EncoderSpecs encSpec) : base(src) { this.src = src; this.roi = roi; this.useStartLevel = sLev; if (roi) { // If there is no ROI, no need to do this this.mg = mg; roiMask = new DataBlkInt(); calcMaxMagBits(encSpec); blockAligned = uba; } }
/// <summary>General utility used by ctors </summary> private void initialize() { tempInt = new DataBlkInt[ncomps]; tempFloat = new DataBlkFloat[ncomps]; /* For each component, get the maximum data value, a reference * to the pixel data and set up working and temporary DataBlks * for both integer and float output. */ for (int i = 0; i < ncomps; ++i) { tempInt[i] = new DataBlkInt(); tempFloat[i] = new DataBlkFloat(); } }
/// <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 copy of the internal data, therefore the returned data /// can be modified "in place". /// /// <P> After being read the coefficients are level shifted by subtracting /// 2^(nominal bit range - 1) /// /// <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' of /// the returned data is 0, and the 'scanw' is the same as the block's /// width. See the 'DataBlk' class. /// /// <P>If the data array in 'blk' is 'null', then a new one is created. If /// the data array is not 'null' then it is reused, and it must be large /// enough to contain the block's data. Otherwise an 'ArrayStoreException' /// or an 'IndexOutOfBoundsException' is thrown by the Java system. /// /// <P>The returned data has its 'progressive' attribute unset /// (i.e. false). /// /// <P>When an I/O exception is encountered the JJ2KExceptionHandler is /// used. The exception is passed to its handleException method. The action /// that is taken depends on the action that has been registered in /// JJ2KExceptionHandler. See JJ2KExceptionHandler for details. /// /// </summary> /// <param name="blk">Its coordinates and dimensions specify the area to /// return. If it contains a non-null data array, then it must have the /// correct dimensions. If it contains a null data array a new one is /// created. The 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. Only /// 0,1 and 2 are valid. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> /// <seealso cref="getInternCompData"> /// /// </seealso> /// <seealso cref="JJ2KExceptionHandler"> /// /// </seealso> public override DataBlk getCompData(DataBlk blk, int c) { // NOTE: can not directly call getInterCompData since that returns // internally buffered data. int ulx, uly, w, h; // Check type of block provided as an argument if (blk.DataType != DataBlk.TYPE_INT) { DataBlkInt tmp = new DataBlkInt(blk.ulx, blk.uly, blk.w, blk.h); blk = tmp; } int[] bakarr = (int[])blk.Data; // Save requested block size ulx = blk.ulx; uly = blk.uly; w = blk.w; h = blk.h; // Force internal data buffer to be different from external blk.Data = null; getInternCompData(blk, c); // Copy the data if (bakarr == null) { bakarr = new int[w * h]; } if (blk.offset == 0 && blk.scanw == w) { // Requested and returned block buffer are the same size // CONVERSION PROBLEM? Array.Copy((System.Array)blk.Data, 0, (System.Array)bakarr, 0, w * h); } else { // Requested and returned block are different for (int i = h - 1; i >= 0; i--) { // copy line by line // CONVERSION PROBLEM? Array.Copy((System.Array)blk.Data, blk.offset + i * blk.scanw, (System.Array)bakarr, i * w, w); } } blk.Data = bakarr; blk.offset = 0; blk.scanw = blk.w; return(blk); }
/// <summary> Closes the underlying file or netwrok connection to where the data is /// written. Any call to other methods of the class become illegal after a /// call to this one. /// /// </summary> /// <exception cref="IOException">If an I/O error occurs. /// /// </exception> public override void close() { int i; // Finish writing the file, writing 0s at the end if the data at end // has not been written. if (out_Renamed.Length != w * h * packBytes + offset) { // Goto end of file out_Renamed.Seek(out_Renamed.Length, System.IO.SeekOrigin.Begin); // Fill with 0s for (i = offset + w * h * packBytes - (int)out_Renamed.Length; i > 0; i--) { out_Renamed.WriteByte((System.Byte) 0); } } out_Renamed.Close(); src = null; out_Renamed = null; db = null; }
/// <summary> This functions gets a DataBlk the size of the current code-block an /// fills this block with the ROI mask. /// /// <P> In order to get the mask for a particular Subband, the subband tree /// is traversed and at each decomposition, the ROI masks are computed. /// /// <P> The widths of the synthesis filters corresponding to the wavelet /// filters used in the wavelet transform are used to expand the ROI masks /// in the decompositions. /// /// </summary> /// <param name="db">The data block that is to be filled with the mask /// /// </param> /// <param name="sb">The root of the subband tree to which db belongs /// /// </param> /// <param name="magbits">The max number of magnitude bits in any code-block /// /// </param> /// <param name="c">The number of the component /// /// </param> /// <returns> Whether or not a mask was needed for this tile /// /// </returns> public override bool getROIMask(DataBlkInt db, Subband sb, int magbits, int c) { int x = db.ulx; int y = db.uly; int w = db.w; int h = db.h; int tilew = sb.w; int tileh = sb.h; int[] maskData = (int[])db.Data; int i, j, k, bi, wrap; // If the ROI mask has not been calculated for this tile and // component, do so now. if (!tileMaskMade[c]) { makeMask(sb, magbits, c); tileMaskMade[c] = true; } if (!roiInTile) { return(false); } int[] mask = roiMask[c]; // local copy // Copy relevant part of the ROI mask to the datablock i = (y + h - 1) * tilew + x + w - 1; bi = w * h - 1; wrap = tilew - w; for (j = h; j > 0; j--) { for (k = w; k > 0; k--, i--, bi--) { maskData[bi] = mask[i]; } i -= wrap; } return(true); }
/// <summary>General utility used by ctors </summary> private void initialize() { this.pl = csMap.pl; this.ncomps = src.NumComps; shiftValueArray = new int[ncomps]; maxValueArray = new int[ncomps]; fixedPtBitsArray = new int[ncomps]; srcBlk = new DataBlk[ncomps]; inInt = new DataBlkInt[ncomps]; inFloat = new DataBlkFloat[ncomps]; workInt = new DataBlkInt[ncomps]; workFloat = new DataBlkFloat[ncomps]; dataInt = new int[ncomps][]; dataFloat = new float[ncomps][]; workDataInt = new int[ncomps][]; workDataFloat = new float[ncomps][]; dataInt = new int[ncomps][]; dataFloat = new float[ncomps][]; /* For each component, get a reference to the pixel data and * set up working DataBlks for both integer and float output. */ for (int i = 0; i < ncomps; ++i) { shiftValueArray[i] = 1 << (src.getNomRangeBits(i) - 1); maxValueArray[i] = (1 << src.getNomRangeBits(i)) - 1; fixedPtBitsArray[i] = src.getFixedPoint(i); inInt[i] = new DataBlkInt(); inFloat[i] = new DataBlkFloat(); workInt[i] = new DataBlkInt(); workInt[i].progressive = inInt[i].progressive; workFloat[i] = new DataBlkFloat(); workFloat[i].progressive = inFloat[i].progressive; } }
/// <summary> This functions gets a DataBlk the size of the current code-block and /// fills this block with the ROI mask. /// /// <P> In order to get the mask for a particular Subband, the subband tree /// is traversed and at each decomposition, the ROI masks are computed. The /// roi bondaries for each subband are stored in the SubbandRectROIMask /// tree. /// /// </summary> /// <param name="db">The data block that is to be filled with the mask /// /// </param> /// <param name="sb">The root of the subband tree to which db belongs /// /// </param> /// <param name="magbits">The max number of magnitude bits in any code-block /// /// </param> /// <param name="c">The component for which to get the mask /// /// </param> /// <returns> Whether or not a mask was needed for this tile /// /// </returns> public override bool getROIMask(DataBlkInt db, Subband sb, int magbits, int c) { int x = db.ulx; int y = db.uly; int w = db.w; int h = db.h; int[] mask = db.DataInt; int i, j, k, r, maxk, maxj; // mink, minj removed int ulx = 0, uly = 0, lrx = 0, lry = 0; int wrap; int maxROI; int[] culxs; int[] culys; int[] clrxs; int[] clrys; SubbandRectROIMask srm; // If the ROI bounds have not been calculated for this tile and // component, do so now. if (!tileMaskMade[c]) { makeMask(sb, magbits, c); tileMaskMade[c] = true; } if (!roiInTile) { return(false); } // Find relevant subband mask and get ROI bounds srm = (SubbandRectROIMask)sMasks[c].getSubbandRectROIMask(x, y); culxs = srm.ulxs; culys = srm.ulys; clrxs = srm.lrxs; clrys = srm.lrys; maxROI = culxs.Length - 1; // Make sure that only parts of ROIs within the code-block are used // and make the bounds local to this block the LR bounds are counted // as the distance from the lower right corner of the block x -= srm.ulx; y -= srm.uly; for (r = maxROI; r >= 0; r--) { ulx = culxs[r] - x; if (ulx < 0) { ulx = 0; } else if (ulx >= w) { ulx = w; } uly = culys[r] - y; if (uly < 0) { uly = 0; } else if (uly >= h) { uly = h; } lrx = clrxs[r] - x; if (lrx < 0) { lrx = -1; } else if (lrx >= w) { lrx = w - 1; } lry = clrys[r] - y; if (lry < 0) { lry = -1; } else if (lry >= h) { lry = h - 1; } // Add the masks of the ROI i = w * lry + lrx; maxj = (lrx - ulx); wrap = w - maxj - 1; maxk = lry - uly; for (k = maxk; k >= 0; k--) { for (j = maxj; j >= 0; j--, i--) { mask[i] = magbits; } i -= wrap; } } return(true); }
/// <summary> This functions gets a DataBlk with the size of the current code-block /// and fills it with the ROI mask. The lowest scaling value in the mask /// for this code-block is returned by the function to be used for /// modifying the rate distortion estimations. /// /// </summary> /// <param name="db">The data block that is to be filled with the mask /// /// </param> /// <param name="sb">The root of the current subband tree /// /// </param> /// <param name="magbits">The number of magnitude bits in this code-block /// /// </param> /// <param name="c">Component number /// /// </param> /// <returns> Whether or not a mask was needed for this tile /// </returns> public abstract bool getROIMask(DataBlkInt db, Subband sb, int magbits, int c);
/// <summary> Writes the data of the specified area to the file, coordinates are /// relative to the current tile of the source. Before writing, the /// coefficients are limited to the nominal range. /// /// <p>This method may not be called concurrently from different /// threads.</p> /// /// <p>If the data returned from the BlkImgDataSrc source is progressive, /// then it is requested over and over until it is not progressive /// anymore.</p> /// /// </summary> /// <param name="ulx">The horizontal coordinate of the upper-left corner of the /// area to write, relative to the current tile. /// /// </param> /// <param name="uly">The vertical coordinate of the upper-left corner of the area /// to write, relative to the current tile. /// /// </param> /// <param name="width">The width of the area to write. /// /// </param> /// <param name="height">The height of the area to write. /// /// </param> /// <exception cref="IOException">If an I/O error occurs. /// /// </exception> public override void write(int ulx, int uly, int w, int h) { int k, j, i, c; // In local variables for faster access int fracbits; // variables used during coeff saturation int shift, tmp, maxVal; int tOffx, tOffy; // Active tile offset in the X and Y direction // Active tiles in all components have same offset since they are at // same resolution (PPM does not support anything 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'" tOffx = src.getCompULX(cps[0]) - (int)System.Math.Ceiling(src.ImgULX / (double)src.getCompSubsX(cps[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'" tOffy = src.getCompULY(cps[0]) - (int)System.Math.Ceiling(src.ImgULY / (double)src.getCompSubsY(cps[0])); // Check the array size if (db.data_array != null && db.data_array.Length < w) { // A new one will be allocated by getInternCompData() db.data_array = null; } // Check the line buffer if (buf == null || buf.Length < 3 * w) { buf = new byte[3 * w]; } // Write the data to the file // Write line by line for (i = 0; i < h; i++) { // Write into buffer first loop over the three components and // write for each for (c = 0; c < 3; c++) { maxVal = (1 << src.getNomRangeBits(cps[c])) - 1; shift = levShift[c]; // Initialize db db.ulx = ulx; db.uly = uly + i; db.w = w; db.h = 1; // Request the data and make sure it is not progressive do { db = (DataBlkInt)src.getInternCompData(db, cps[c]); }while (db.progressive); // Get the fracbits value fracbits = fb[c]; // Write all bytes in the line if (fracbits == 0) { for (k = db.offset + w - 1, j = 3 * w - 1 + c - 2; j >= 0; k--) { tmp = db.data_array[k] + shift; buf[j] = (byte)((tmp < 0)?0:((tmp > maxVal)?maxVal:tmp)); j -= 3; } } else { for (k = db.offset + w - 1, j = 3 * w - 1 + c - 2; j >= 0; k--) { tmp = (SupportClass.URShift(db.data_array[k], fracbits)) + shift; buf[j] = (byte)((tmp < 0)?0:((tmp > maxVal)?maxVal:tmp)); j -= 3; } } } // Write buffer into file out_Renamed.Seek(offset + 3 * (this.w * (uly + tOffy + i) + ulx + tOffx), System.IO.SeekOrigin.Begin); out_Renamed.Write(buf, 0, 3 * w); } }
/// <summary> This function generates the ROI mask for one tile-component. /// /// <P> Once the mask is generated in the pixel domain. it is decomposed /// following the same decomposition scheme as the wavelet transform. /// /// </summary> /// <param name="sb">The root of the subband tree used in the decomposition /// /// </param> /// <param name="magbits">The max number of magnitude bits in any code-block /// /// </param> /// <param name="c">component number /// </param> public override void makeMask(Subband sb, int magbits, int c) { int[] mask; // local copy ROI[] rois = this.roi_array; // local copy int i, j, k, r, maxj; // mink, minj removed int lrx, lry; int x, y, w, h; int cx, cy, rad; int wrap; int curScalVal; int tileulx = sb.ulcx; int tileuly = sb.ulcy; int tilew = sb.w; int tileh = sb.h; int lineLen = (tilew > tileh)?tilew:tileh; // Make sure there is a sufficiently large mask buffer if (roiMask[c] == null || (roiMask[c].Length < (tilew * tileh))) { roiMask[c] = new int[tilew * tileh]; mask = roiMask[c]; } else { mask = roiMask[c]; for (i = tilew * tileh - 1; i >= 0; i--) { mask[i] = 0; } } // Make sure there are sufficiently large line buffers if (maskLineLow == null || (maskLineLow.Length < (lineLen + 1) / 2)) { maskLineLow = new int[(lineLen + 1) / 2]; } if (maskLineHigh == null || (maskLineHigh.Length < (lineLen + 1) / 2)) { maskLineHigh = new int[(lineLen + 1) / 2]; } roiInTile = false; // Generate ROIs in pixel domain: for (r = rois.Length - 1; r >= 0; r--) { if (rois[r].comp == c) { curScalVal = magbits; if (rois[r].arbShape) { ImgReaderPGM maskPGM = rois[r].maskPGM; // Local copy if ((src.ImgWidth != maskPGM.ImgWidth) || (src.ImgHeight != maskPGM.ImgHeight)) { throw new System.ArgumentException("Input image and" + " ROI mask must " + "have the same " + "size"); } x = src.ImgULX; y = src.ImgULY; lrx = x + src.ImgWidth - 1; lry = y + src.ImgHeight - 1; if ((x > tileulx + tilew) || (y > tileuly + tileh) || (lrx < tileulx) || (lry < tileuly)) { // Roi not in tile continue; } // Check bounds x -= tileulx; lrx -= tileulx; y -= tileuly; lry -= tileuly; int offx = 0; int offy = 0; if (x < 0) { offx = -x; x = 0; } if (y < 0) { offy = -y; y = 0; } w = (lrx > (tilew - 1))?tilew - x:lrx + 1 - x; h = (lry > (tileh - 1))?tileh - y:lry + 1 - y; // Get shape line by line to reduce memory DataBlkInt srcblk = new DataBlkInt(); int mDcOff = -ImgReaderPGM.DC_OFFSET; int nROIcoeff = 0; int[] src_data; srcblk.ulx = offx; srcblk.w = w; srcblk.h = 1; i = (y + h - 1) * tilew + x + w - 1; maxj = w; wrap = tilew - maxj; for (k = h; k > 0; k--) { srcblk.uly = offy + k - 1; srcblk = (DataBlkInt)maskPGM.getInternCompData(srcblk, 0); src_data = srcblk.DataInt; for (j = maxj; j > 0; j--, i--) { if (src_data[j - 1] != mDcOff) { mask[i] = curScalVal; nROIcoeff++; } } i -= wrap; } if (nROIcoeff != 0) { roiInTile = true; } } else if (rois[r].rect) { // Rectangular ROI x = rois[r].ulx; y = rois[r].uly; lrx = rois[r].w + x - 1; lry = rois[r].h + y - 1; if ((x > tileulx + tilew) || (y > tileuly + tileh) || (lrx < tileulx) || (lry < tileuly)) { // Roi not in tile continue; } roiInTile = true; // Check bounds x -= tileulx; lrx -= tileulx; y -= tileuly; lry -= tileuly; x = (x < 0)?0:x; y = (y < 0)?0:y; w = (lrx > (tilew - 1))?tilew - x:lrx + 1 - x; h = (lry > (tileh - 1))?tileh - y:lry + 1 - y; i = (y + h - 1) * tilew + x + w - 1; maxj = w; wrap = tilew - maxj; for (k = h; k > 0; k--) { for (j = maxj; j > 0; j--, i--) { mask[i] = curScalVal; } i -= wrap; } } else { // Non-rectangular ROI. So far only circular case cx = rois[r].x - tileulx; cy = rois[r].y - tileuly; rad = rois[r].r; i = tileh * tilew - 1; for (k = tileh - 1; k >= 0; k--) { for (j = tilew - 1; j >= 0; j--, i--) { if (((j - cx) * (j - cx) + (k - cy) * (k - cy) < rad * rad)) { mask[i] = curScalVal; roiInTile = true; } } } } } } // If wavelet transform is used if (sb.isNode) { // Decompose the mask according to the subband tree // Calculate size of padded line buffer WaveletFilter vFilter = sb.VerWFilter; WaveletFilter hFilter = sb.HorWFilter; int lvsup = vFilter.SynLowNegSupport + vFilter.SynLowPosSupport; int hvsup = vFilter.SynHighNegSupport + vFilter.SynHighPosSupport; int lhsup = hFilter.SynLowNegSupport + hFilter.SynLowPosSupport; int hhsup = hFilter.SynHighNegSupport + hFilter.SynHighPosSupport; lvsup = (lvsup > hvsup)?lvsup:hvsup; lhsup = (lhsup > hhsup)?lhsup:hhsup; lvsup = (lvsup > lhsup)?lvsup:lhsup; paddedMaskLine = new int[lineLen + lvsup]; if (roiInTile) { decomp(sb, tilew, tileh, c); } } }
/// <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); }
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 the specified code-block in the current tile for the specified /// component (as a reference or copy). /// /// <p>The returned code-block may be progressive, which is indicated by /// the 'progressive' variable of the returned 'DataBlk' /// object. If a code-block is progressive it means that in a later request /// to this method for the same code-block it is possible to retrieve data /// which is a better approximation, since meanwhile more data to decode /// for the code-block could have been received. If the code-block is not /// progressive then later calls to this method for the same code-block /// will return the exact same data values.</p> /// /// <p>The data returned by this method can be the data in the internal /// buffer of this object, if any, and thus can not be modified by the /// caller. The 'offset' and 'scanw' of the returned data can be /// arbitrary. See the 'DataBlk' class.</p> /// /// </summary> /// <param name="c">The component for which to return the next code-block. /// /// </param> /// <param name="m">The vertical index of the code-block to return, in the /// specified subband. /// /// </param> /// <param name="n">The horizontal index of the code-block to return, in the /// specified subband. /// /// </param> /// <param name="sb">The subband in which the code-block to return is. /// /// </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. If the /// "data" array of the object is non-null it will be reused, if possible, /// to return the data. /// /// </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="DataBlk"> /// /// </seealso> public override DataBlk getInternCodeBlock(int c, int m, int n, SubbandSyn sb, DataBlk cblk) { // This method is declared final since getNextCodeBlock() relies on // the actual implementation of this method. int j, jmin, k; int temp; float step; int shiftBits; int magBits; int[] outiarr, inarr; float[] outfarr; int w, h; bool reversible = qts.isReversible(tIdx, c); bool derived = qts.isDerived(tIdx, c); StdDequantizerParams params_Renamed = (StdDequantizerParams)qsss.getTileCompVal(tIdx, c); int G = ((System.Int32)gbs.getTileCompVal(tIdx, c)); outdtype = cblk.DataType; if (reversible && outdtype != DataBlk.TYPE_INT) { throw new System.ArgumentException("Reversible quantizations " + "must use int data"); } // To get compiler happy outiarr = null; outfarr = null; inarr = null; // Get source data and initialize output DataBlk object. switch (outdtype) { case DataBlk.TYPE_INT: // With int data we can use the same DataBlk object to get the // data from the source and return the dequantized data, and we // can also work "in place" (i.e. same buffer). cblk = src.getCodeBlock(c, m, n, sb, cblk); // Input and output arrays are the same outiarr = (int[])cblk.Data; break; case DataBlk.TYPE_FLOAT: // With float data we must use a different DataBlk objects to get // the data from the source and to return the dequantized data. inblk = (DataBlkInt)src.getInternCodeBlock(c, m, n, sb, inblk); inarr = inblk.DataInt; if (cblk == null) { cblk = new DataBlkFloat(); } // Copy the attributes of the CodeBlock object cblk.ulx = inblk.ulx; cblk.uly = inblk.uly; cblk.w = inblk.w; cblk.h = inblk.h; cblk.offset = 0; cblk.scanw = cblk.w; cblk.progressive = inblk.progressive; // Get output data array and check its size outfarr = (float[])cblk.Data; if (outfarr == null || outfarr.Length < cblk.w * cblk.h) { outfarr = new float[cblk.w * cblk.h]; cblk.Data = outfarr; } break; } magBits = sb.magbits; // Calculate quantization step and number of magnitude bits // depending on reversibility and derivedness and perform // inverse quantization if (reversible) { shiftBits = 31 - magBits; // For int data Inverse quantization happens "in-place". The input // array has an offset of 0 and scan width equal to the code-block // width. for (j = outiarr.Length - 1; j >= 0; j--) { temp = outiarr[j]; // input array is same as output one outiarr[j] = (temp >= 0) ? (temp >> shiftBits) : -((temp & 0x7FFFFFFF) >> shiftBits); } } else { // Not reversible if (derived) { // Max resolution level int mrl = src.getSynSubbandTree(TileIdx, c).resLvl; step = params_Renamed.nStep[0][0] * (1L << (rb[c] + sb.anGainExp + mrl - sb.level)); } else { step = params_Renamed.nStep[sb.resLvl][sb.sbandIdx] * (1L << (rb[c] + sb.anGainExp)); } shiftBits = 31 - magBits; // Adjust step to the number of shiftBits step /= (1 << shiftBits); switch (outdtype) { case DataBlk.TYPE_INT: // For int data Inverse quantization happens "in-place". The // input array has an offset of 0 and scan width equal to the // code-block width. for (j = outiarr.Length - 1; j >= 0; j--) { temp = outiarr[j]; // input array is same as output one //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'" outiarr[j] = (int)(((float)((temp >= 0) ? temp : -(temp & 0x7FFFFFFF))) * step); } break; case DataBlk.TYPE_FLOAT: // For float data the inverse quantization can not happen // "in-place". w = cblk.w; h = cblk.h; for (j = w * h - 1, k = inblk.offset + (h - 1) * inblk.scanw + w - 1, jmin = w * (h - 1); j >= 0; jmin -= w) { for (; j >= jmin; k--, j--) { temp = inarr[k]; //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'" outfarr[j] = ((float)((temp >= 0) ? temp : -(temp & 0x7FFFFFFF))) * step; } // Jump to beggining of previous line in input k -= (inblk.scanw - w); } break; } } // Return the output code-block return(cblk); }
/// <summary> Apply forward component transformation to obtain requested component /// from specified block of data. Whatever the type of requested DataBlk, /// it always returns a DataBlkInt. /// /// </summary> /// <param name="blk">Determine the rectangular area to return /// /// </param> /// <param name="c">The index of the requested component /// /// </param> /// <returns> Data of requested component /// /// </returns> private DataBlk forwRCT(DataBlk blk, int c) { int k, k0, k1, k2, mink, i; int w = blk.w; //width of output block int h = blk.h; //height of ouput block int[] outdata; //array of output data //If asking for Yr, Ur or Vr do transform if (c >= 0 && c <= 2) { // Check that request data type is int if (blk.DataType != DataBlk.TYPE_INT) { if (outBlk == null || outBlk.DataType != DataBlk.TYPE_INT) { outBlk = new DataBlkInt(); } outBlk.w = w; outBlk.h = h; outBlk.ulx = blk.ulx; outBlk.uly = blk.uly; blk = outBlk; } //Reference to output block data array outdata = (int[])blk.Data; //Create data array of blk if necessary if (outdata == null || outdata.Length < h * w) { outdata = new int[h * w]; blk.Data = outdata; } // Block buffers for input RGB data int[] data0, data1, bdata; // input data arrays if (block0 == null) { block0 = new DataBlkInt(); } if (block1 == null) { block1 = new DataBlkInt(); } if (block2 == null) { block2 = new DataBlkInt(); } block0.w = block1.w = block2.w = blk.w; block0.h = block1.h = block2.h = blk.h; block0.ulx = block1.ulx = block2.ulx = blk.ulx; block0.uly = block1.uly = block2.uly = blk.uly; //Fill in buffer blocks (to be read only) // Returned blocks may have different size and position block0 = (DataBlkInt)src.getInternCompData(block0, 0); data0 = (int[])block0.Data; block1 = (DataBlkInt)src.getInternCompData(block1, 1); data1 = (int[])block1.Data; block2 = (DataBlkInt)src.getInternCompData(block2, 2); bdata = (int[])block2.Data; // Set the progressiveness of the output data blk.progressive = block0.progressive || block1.progressive || block2.progressive; blk.offset = 0; blk.scanw = w; //Perform conversion // Initialize general indexes k = w * h - 1; k0 = block0.offset + (h - 1) * block0.scanw + w - 1; k1 = block1.offset + (h - 1) * block1.scanw + w - 1; k2 = block2.offset + (h - 1) * block2.scanw + w - 1; switch (c) { case 0: //RGB to Yr conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--, k2--) { // Use int arithmetic with 12 fractional bits // and rounding outdata[k] = (data0[k] + 2 * data1[k] + bdata[k]) >> 2; // Same as / 4 } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; case 1: //RGB to Ur conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k1--, k2--) { // Use int arithmetic with 12 fractional bits // and rounding outdata[k] = bdata[k2] - data1[k1]; } // Jump to beggining of previous line in input k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; case 2: //RGB to Vr conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--) { // Use int arithmetic with 12 fractional bits // and rounding outdata[k] = data0[k0] - data1[k1]; } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); } break; } } else if (c >= 3) { // Requesting a component which is not Y, Ur or Vr => // just pass the data return(src.getInternCompData(blk, c)); } else { // Requesting a non valid component index throw new System.ArgumentException(); } return(blk); }
/// <summary> Writes the data of the specified area to the file, coordinates are /// relative to the current tile of the source. Before writing, the /// coefficients are limited to the nominal range. /// /// <p>This method may not be called concurrently from different /// threads.</p> /// /// <p>If the data returned from the BlkImgDataSrc source is progressive, /// then it is requested over and over until it is not progressive /// anymore.</p> /// /// </summary> /// <param name="ulx">The horizontal coordinate of the upper-left corner of the /// area to write, relative to the current tile. /// /// </param> /// <param name="uly">The vertical coordinate of the upper-left corner of the area /// to write, relative to the current tile. /// /// </param> /// <param name="width">The width of the area to write. /// /// </param> /// <param name="height">The height of the area to write. /// /// </param> /// <exception cref="IOException">If an I/O error occurs. /// /// </exception> public override void write(int ulx, int uly, int w, int h) { int k, i, j; int fracbits = fb; // In local variable for faster access int tOffx, tOffy; // Active tile offset in the X and Y direction // Initialize db db.ulx = ulx; db.uly = uly; db.w = w; db.h = h; // Get the current active tile offset //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'" tOffx = src.getCompULX(c) - (int)System.Math.Ceiling(src.ImgULX / (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'" tOffy = src.getCompULY(c) - (int)System.Math.Ceiling(src.ImgULY / (double)src.getCompSubsY(c)); // Check the array size if (db.data_array != null && db.data_array.Length < w * h) { // A new one will be allocated by getInternCompData() db.data_array = null; } // Request the data and make sure it is not // progressive do { db = (DataBlkInt)src.getInternCompData(db, c); }while (db.progressive); // variables used during coeff saturation int tmp, maxVal = (1 << src.getNomRangeBits(c)) - 1; // If nominal bitdepth greater than 8, calculate down shift int downShift = src.getNomRangeBits(c) - 8; if (downShift < 0) { downShift = 0; } // Check line buffer if (buf == null || buf.Length < w) { buf = new byte[w]; // Expand buffer } // Write line by line for (i = 0; i < h; i++) { // Skip to beggining of line in file out_Renamed.Seek(this.offset + this.w * (uly + tOffy + i) + ulx + tOffx, System.IO.SeekOrigin.Begin); // Write all bytes in the line if (fracbits == 0) { for (k = db.offset + i * db.scanw + w - 1, j = w - 1; j >= 0; j--, k--) { tmp = db.data_array[k] + levShift; buf[j] = (byte)(((tmp < 0)?0:((tmp > maxVal)?maxVal:tmp)) >> downShift); } } else { for (k = db.offset + i * db.scanw + w - 1, j = w - 1; j >= 0; j--, k--) { tmp = (db.data_array[k] >> fracbits) + levShift; buf[j] = (byte)(((tmp < 0)?0:((tmp > maxVal)?maxVal:tmp)) >> downShift); } } out_Renamed.Write(buf, 0, w); } }
/// <summary> Return a DataBlk containing the requested component /// upsampled by the scale factor applied to the particular /// scaling direction /// /// 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 copy of the internal data, therefore the returned data /// can be modified "in place". /// /// <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' of /// the returned data is 0, and the 'scanw' is the same as the block's /// width. See the 'DataBlk' class. /// /// <P>If the data array in 'blk' is 'null', then a new one is created. If /// the data array is not 'null' then it is reused, and it must be large /// enough to contain the block's data. Otherwise an 'ArrayStoreException' /// or an 'IndexOutOfBoundsException' is thrown by the Java system. /// /// <P>The returned data has its 'progressive' attribute set to that of the /// input data. /// /// </summary> /// <param name="blk">Its coordinates and dimensions specify the area to /// return. If it contains a non-null data array, then it must have the /// correct dimensions. If it contains a null data array a new one is /// created. The 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. Only 0 /// and 3 are valid. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> /// <seealso cref="getCompData"> /// </seealso> public override DataBlk getInternCompData(DataBlk outblk, int c) { // If the scaling factor of this channel is 1 in both // directions, simply return the source DataBlk. if (src.getCompSubsX(c) == 1 && src.getCompSubsY(c) == 1) { return(src.getInternCompData(outblk, c)); } int wfactor = src.getCompSubsX(c); int hfactor = src.getCompSubsY(c); if ((wfactor != 2 && wfactor != 1) || (hfactor != 2 && hfactor != 1)) { throw new System.ArgumentException("Upsampling by other than 2:1" + " not supported"); } int leftedgeOut = -1; // offset to the start of the output scanline int rightedgeOut = -1; // offset to the end of the output // scanline + 1 int leftedgeIn = -1; // offset to the start of the input scanline int rightedgeIn = -1; // offset to the end of the input scanline + 1 int y0In, y1In, y0Out, y1Out; int x0In, x1In, x0Out, x1Out; y0Out = outblk.uly; y1Out = y0Out + outblk.h - 1; x0Out = outblk.ulx; x1Out = x0Out + outblk.w - 1; y0In = y0Out / hfactor; y1In = y1Out / hfactor; x0In = x0Out / wfactor; x1In = x1Out / wfactor; // Calculate the requested height and width, requesting an extra // row and or for upsampled channels. int reqW = x1In - x0In + 1; int reqH = y1In - y0In + 1; // Initialize general input and output indexes int kOut = -1; int kIn = -1; int yIn; switch (outblk.DataType) { case DataBlk.TYPE_INT: DataBlkInt inblkInt = new DataBlkInt(x0In, y0In, reqW, reqH); inblkInt = (DataBlkInt)src.getInternCompData(inblkInt, c); dataInt[c] = inblkInt.DataInt; // Reference the working array int[] outdataInt = (int[])outblk.Data; // Create data array if necessary if (outdataInt == null || outdataInt.Length != outblk.w * outblk.h) { outdataInt = new int[outblk.h * outblk.w]; outblk.Data = outdataInt; } // The nitty-gritty. for (int yOut = y0Out; yOut <= y1Out; ++yOut) { yIn = yOut / hfactor; leftedgeIn = inblkInt.offset + (yIn - y0In) * inblkInt.scanw; rightedgeIn = leftedgeIn + inblkInt.w; leftedgeOut = outblk.offset + (yOut - y0Out) * outblk.scanw; rightedgeOut = leftedgeOut + outblk.w; kIn = leftedgeIn; kOut = leftedgeOut; if ((x0Out & 0x1) == 1) { // first is odd do the pixel once. outdataInt[kOut++] = dataInt[c][kIn++]; } if ((x1Out & 0x1) == 0) { // last is even adjust loop bounds rightedgeOut--; } while (kOut < rightedgeOut) { outdataInt[kOut++] = dataInt[c][kIn]; outdataInt[kOut++] = dataInt[c][kIn++]; } if ((x1Out & 0x1) == 0) { // last is even do the pixel once. outdataInt[kOut++] = dataInt[c][kIn]; } } outblk.progressive = inblkInt.progressive; break; case DataBlk.TYPE_FLOAT: DataBlkFloat inblkFloat = new DataBlkFloat(x0In, y0In, reqW, reqH); inblkFloat = (DataBlkFloat)src.getInternCompData(inblkFloat, c); dataFloat[c] = inblkFloat.DataFloat; // Reference the working array float[] outdataFloat = (float[])outblk.Data; // Create data array if necessary if (outdataFloat == null || outdataFloat.Length != outblk.w * outblk.h) { outdataFloat = new float[outblk.h * outblk.w]; outblk.Data = outdataFloat; } // The nitty-gritty. for (int yOut = y0Out; yOut <= y1Out; ++yOut) { yIn = yOut / hfactor; leftedgeIn = inblkFloat.offset + (yIn - y0In) * inblkFloat.scanw; rightedgeIn = leftedgeIn + inblkFloat.w; leftedgeOut = outblk.offset + (yOut - y0Out) * outblk.scanw; rightedgeOut = leftedgeOut + outblk.w; kIn = leftedgeIn; kOut = leftedgeOut; if ((x0Out & 0x1) == 1) { // first is odd do the pixel once. outdataFloat[kOut++] = dataFloat[c][kIn++]; } if ((x1Out & 0x1) == 0) { // last is even adjust loop bounds rightedgeOut--; } while (kOut < rightedgeOut) { outdataFloat[kOut++] = dataFloat[c][kIn]; outdataFloat[kOut++] = dataFloat[c][kIn++]; } if ((x1Out & 0x1) == 0) { // last is even do the pixel once. outdataFloat[kOut++] = dataFloat[c][kIn]; } } outblk.progressive = inblkFloat.progressive; break; case DataBlk.TYPE_SHORT: case DataBlk.TYPE_BYTE: default: // Unsupported output type. throw new System.ArgumentException("invalid source datablock " + "type"); } return(outblk); }
/// <summary> Returns, in the blk argument, the 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> After being read the coefficients are level shifted by subtracting /// 2^(nominal bit range - 1) /// /// <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>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 always has its 'progressive' attribute unset /// (i.e. false). /// /// <P>When an I/O exception is encountered the JJ2KExceptionHandler is /// used. The exception is passed to its handleException method. The action /// that is taken depends on the action that has been registered in /// JJ2KExceptionHandler. See JJ2KExceptionHandler for details. /// /// <P>This method implements buffering for the 3 components: When the /// first one is asked, all the 3 components are read and stored until they /// are needed. /// /// </summary> /// <param name="blk">Its coordinates and dimensions specify the area to /// return. 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. Only 0, /// 1 and 3 are valid. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> /// <seealso cref="getCompData"> /// /// </seealso> /// <seealso cref="JJ2KExceptionHandler"> /// /// </seealso> public override DataBlk getInternCompData(DataBlk blk, int c) { // Check component index if (c < 0 || c > 2) { throw new System.ArgumentException(); } // Check type of block provided as an argument if (blk.DataType != DataBlk.TYPE_INT) { if (intBlk == null) { intBlk = new DataBlkInt(blk.ulx, blk.uly, blk.w, blk.h); } else { intBlk.ulx = blk.ulx; intBlk.uly = blk.uly; intBlk.w = blk.w; intBlk.h = blk.h; } blk = intBlk; } // If asking a component for the first time for this block, read all of the components if ((barr == null) || (dbi.ulx > blk.ulx) || (dbi.uly > blk.uly) || (dbi.ulx + dbi.w < blk.ulx + blk.w) || (dbi.uly + dbi.h < blk.uly + blk.h)) { int i; int[] red, green, blue, alpha; int componentCount = (image.PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3; barr = new int[componentCount][]; // Reset data arrays if needed if (barr[c] == null || barr[c].Length < blk.w * blk.h) { barr[c] = new int[blk.w * blk.h]; } blk.Data = barr[c]; i = (c + 1) % 3; if (barr[i] == null || barr[i].Length < blk.w * blk.h) { barr[i] = new int[blk.w * blk.h]; } i = (c + 2) % 3; if (barr[i] == null || barr[i].Length < blk.w * blk.h) { barr[i] = new int[blk.w * blk.h]; } if (componentCount == 4) { if (barr[3] == null || barr[3].Length < blk.w * blk.h) { barr[3] = new int[blk.w * blk.h]; } } // set attributes of the DataBlk used for buffering dbi.ulx = blk.ulx; dbi.uly = blk.uly; dbi.w = blk.w; dbi.h = blk.h; red = barr[0]; green = barr[1]; blue = barr[2]; alpha = (componentCount == 4) ? barr[3] : null; Bitmap bitmap = (Bitmap)image; BitmapData data = bitmap.LockBits(new Rectangle(blk.ulx, blk.uly, blk.w, blk.h), ImageLockMode.ReadOnly, (componentCount == 3) ? PixelFormat.Format24bppRgb : PixelFormat.Format32bppArgb); unsafe { byte *ptr = (byte *)data.Scan0.ToPointer(); int k = 0; for (int j = 0; j < blk.w * blk.h; j++) { blue[k] = ((byte)*(ptr + 0) & 0xFF) - 128; green[k] = ((byte)*(ptr + 1) & 0xFF) - 128; red[k] = ((byte)*(ptr + 2) & 0xFF) - 128; if (componentCount == 4) { alpha[k] = ((byte)*(ptr + 3) & 0xFF) - 128; } ++k; ptr += 3; } } bitmap.UnlockBits(data); barr[0] = red; barr[1] = green; barr[2] = blue; if (componentCount == 4) { barr[3] = alpha; } // Set buffer attributes blk.Data = barr[c]; blk.offset = 0; blk.scanw = blk.w; } else { //Asking for the 2nd or 3rd (or 4th) block component blk.Data = barr[c]; blk.offset = (blk.ulx - dbi.ulx) * dbi.w + blk.ulx - dbi.ulx; blk.scanw = dbi.scanw; } // Turn off the progressive attribute blk.progressive = false; return(blk); }
/// <summary> Returns, in the blk argument, the 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> After being read the coefficients are level shifted by subtracting /// 2^(nominal bit range - 1) /// /// <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>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 always has its 'progressive' attribute unset /// (i.e. false). /// /// <P>When an I/O exception is encountered the JJ2KExceptionHandler is /// used. The exception is passed to its handleException method. The action /// that is taken depends on the action that has been registered in /// JJ2KExceptionHandler. See JJ2KExceptionHandler for details. /// /// <P>This method implements buffering for the 3 components: When the /// first one is asked, all the 3 components are read and stored until they /// are needed. /// /// </summary> /// <param name="blk">Its coordinates and dimensions specify the area to /// return. 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. Only 0, /// 1 and 3 are valid. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> /// <seealso cref="getCompData"> /// /// </seealso> /// <seealso cref="JJ2KExceptionHandler"> /// /// </seealso> public override DataBlk getInternCompData(DataBlk blk, int c) { // Check component index if (c < 0 || c > 2) { throw new System.ArgumentException(); } // Check type of block provided as an argument if (blk.DataType != DataBlk.TYPE_INT) { if (intBlk == null) { intBlk = new DataBlkInt(blk.ulx, blk.uly, blk.w, blk.h); } else { intBlk.ulx = blk.ulx; intBlk.uly = blk.uly; intBlk.w = blk.w; intBlk.h = blk.h; } blk = intBlk; } // If asking a component for the first time for this block, read the 3 // components if ((barr[c] == null) || (dbi.ulx > blk.ulx) || (dbi.uly > blk.uly) || (dbi.ulx + dbi.w < blk.ulx + blk.w) || (dbi.uly + dbi.h < blk.uly + blk.h)) { int k, j, i, mi; int[] red, green, blue; // Reset data arrays if needed if (barr[c] == null || barr[c].Length < blk.w * blk.h) { barr[c] = new int[blk.w * blk.h]; } blk.Data = barr[c]; i = (c + 1) % 3; if (barr[i] == null || barr[i].Length < blk.w * blk.h) { barr[i] = new int[blk.w * blk.h]; } i = (c + 2) % 3; if (barr[i] == null || barr[i].Length < blk.w * blk.h) { barr[i] = new int[blk.w * blk.h]; } // set attributes of the DataBlk used for buffering dbi.ulx = blk.ulx; dbi.uly = blk.uly; dbi.w = blk.w; dbi.h = blk.h; // Check line buffer if (buf == null || buf.Length < 3 * blk.w) { buf = new byte[3 * blk.w]; } red = barr[0]; green = barr[1]; blue = barr[2]; try { // Read line by line mi = blk.uly + blk.h; for (i = blk.uly; i < mi; i++) { // Reposition in input offset takes care of // header offset in_Renamed.Seek(offset + i * 3 * w + 3 * blk.ulx, System.IO.SeekOrigin.Begin); in_Renamed.Read(buf, 0, 3 * blk.w); for (k = (i - blk.uly) * blk.w + blk.w - 1, j = 3 * blk.w - 1; j >= 0; k--) { // Read every third sample blue[k] = (((byte)buf[j--]) & 0xFF) - DC_OFFSET; green[k] = (((byte)buf[j--]) & 0xFF) - DC_OFFSET; red[k] = (((byte)buf[j--]) & 0xFF) - DC_OFFSET; } } } catch (System.IO.IOException e) { JJ2KExceptionHandler.handleException(e); } barr[0] = red; barr[1] = green; barr[2] = blue; // Set buffer attributes blk.Data = barr[c]; blk.offset = 0; blk.scanw = blk.w; } else { //Asking for the 2nd or 3rd block component blk.Data = barr[c]; blk.offset = (blk.ulx - dbi.ulx) * dbi.w + blk.ulx - dbi.ulx; blk.scanw = dbi.scanw; } // Turn off the progressive attribute blk.progressive = false; return(blk); }
/// <summary> Performs the inverse wavelet transform on the whole component. It /// iteratively reconstructs the subbands from leaves up to the root /// node. This method is recursive, the first call to it the 'sb' must be /// the root of the subband tree. The method will then process the entire /// subband tree by calling itslef recursively. /// /// </summary> /// <param name="img">The buffer for the image/wavelet data. /// /// </param> /// <param name="sb">The subband to reconstruct. /// /// </param> /// <param name="c">The index of the component to reconstruct /// /// </param> private void waveletTreeReconstruction(DataBlk img, SubbandSyn sb, int c) { DataBlk subbData; // If the current subband is a leaf then get the data from the source if (!sb.isNode) { int i, m, n; System.Object src_data, dst_data; Coord ncblks; if (sb.w == 0 || sb.h == 0) { return; // If empty subband do nothing } // Get all code-blocks in subband if (dtype == DataBlk.TYPE_INT) { subbData = new DataBlkInt(); } else { subbData = new DataBlkFloat(); } ncblks = sb.numCb; dst_data = img.Data; for (m = 0; m < ncblks.y; m++) { for (n = 0; n < ncblks.x; n++) { subbData = src.getInternCodeBlock(c, m, n, sb, subbData); src_data = subbData.Data; if (pw != null) { nDecCblk++; pw.updateProgressWatch(nDecCblk, null); } // Copy the data line by line for (i = subbData.h - 1; i >= 0; i--) { // CONVERSION PROBLEM Array.Copy((System.Array)src_data, subbData.offset + i * subbData.scanw, (System.Array)dst_data, (subbData.uly + i) * img.w + subbData.ulx, subbData.w); } } } } else if (sb.isNode) { // Reconstruct the lower resolution levels if the current subbands // is a node //Perform the reconstruction of the LL subband waveletTreeReconstruction(img, (SubbandSyn)sb.LL, c); if (sb.resLvl <= reslvl - maxImgRes + ndl[c]) { //Reconstruct the other subbands waveletTreeReconstruction(img, (SubbandSyn)sb.HL, c); waveletTreeReconstruction(img, (SubbandSyn)sb.LH, c); waveletTreeReconstruction(img, (SubbandSyn)sb.HH, c); //Perform the 2D wavelet decomposition of the current subband wavelet2DReconstruction(img, (SubbandSyn)sb, c); } } }
/// <summary> Apply forward irreversible component transformation to obtain requested /// component from specified block of data. Whatever the type of requested /// DataBlk, it always returns a DataBlkFloat. /// /// </summary> /// <param name="blk">Determine the rectangular area to return /// /// </param> /// <param name="c">The index of the requested component /// /// </param> /// <returns> Data of requested component /// /// </returns> private DataBlk forwICT(DataBlk blk, int c) { int k, k0, k1, k2, mink, i; int w = blk.w; //width of output block int h = blk.h; //height of ouput block float[] outdata; //array of output data if (blk.DataType != DataBlk.TYPE_FLOAT) { if (outBlk == null || outBlk.DataType != DataBlk.TYPE_FLOAT) { outBlk = new DataBlkFloat(); } outBlk.w = w; outBlk.h = h; outBlk.ulx = blk.ulx; outBlk.uly = blk.uly; blk = outBlk; } //Reference to output block data array outdata = (float[])blk.Data; //Create data array of blk if necessary if (outdata == null || outdata.Length < w * h) { outdata = new float[h * w]; blk.Data = outdata; } //If asking for Y, Cb or Cr do transform if (c >= 0 && c <= 2) { int[] data0, data1, data2; // input data arrays if (block0 == null) { block0 = new DataBlkInt(); } if (block1 == null) { block1 = new DataBlkInt(); } if (block2 == null) { block2 = new DataBlkInt(); } block0.w = block1.w = block2.w = blk.w; block0.h = block1.h = block2.h = blk.h; block0.ulx = block1.ulx = block2.ulx = blk.ulx; block0.uly = block1.uly = block2.uly = blk.uly; // Returned blocks may have different size and position block0 = (DataBlkInt)src.getInternCompData(block0, 0); data0 = (int[])block0.Data; block1 = (DataBlkInt)src.getInternCompData(block1, 1); data1 = (int[])block1.Data; block2 = (DataBlkInt)src.getInternCompData(block2, 2); data2 = (int[])block2.Data; // Set the progressiveness of the output data blk.progressive = block0.progressive || block1.progressive || block2.progressive; blk.offset = 0; blk.scanw = w; //Perform conversion // Initialize general indexes k = w * h - 1; k0 = block0.offset + (h - 1) * block0.scanw + w - 1; k1 = block1.offset + (h - 1) * block1.scanw + w - 1; k2 = block2.offset + (h - 1) * block2.scanw + w - 1; switch (c) { case 0: //RGB to Y conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--, k2--) { outdata[k] = 0.299f * data0[k0] + 0.587f * data1[k1] + 0.114f * data2[k2]; } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; case 1: //RGB to Cb conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--, k2--) { outdata[k] = (-0.16875f) * data0[k0] - 0.33126f * data1[k1] + 0.5f * data2[k2]; } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; case 2: //RGB to Cr conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--, k2--) { outdata[k] = 0.5f * data0[k0] - 0.41869f * data1[k1] - 0.08131f * data2[k2]; } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; } } else if (c >= 3) { // Requesting a component which is not Y, Cb or Cr => // just pass the data // Variables DataBlkInt indb = new DataBlkInt(blk.ulx, blk.uly, w, h); int[] indata; // input data array // Get the input data // (returned block may be larger than requested one) src.getInternCompData(indb, c); indata = (int[])indb.Data; // Copy the data converting from int to float k = w * h - 1; k0 = indb.offset + (h - 1) * indb.scanw + w - 1; for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--) { //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'" outdata[k] = (float)indata[k0]; } // Jump to beggining of next line in input k0 += indb.w - w; } // Set the progressivity blk.progressive = indb.progressive; blk.offset = 0; blk.scanw = w; return(blk); } else { // Requesting a non valid component index throw new System.ArgumentException(); } return(blk); }
/// <summary> This function gets a datablk from the entropy coder. The sample sin the /// block, which consists of the quantized coefficients from the quantizer, /// are scaled by the values given for any ROIs specified. /// /// <p>The function calls on a ROIMaskGenerator to get the mask for scaling /// the coefficients in the current block.</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. If the /// "data" array of the object is non-null it will be reused, if possible, /// to return the data. /// /// </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 virtual CBlkWTData getNextInternCodeBlock(int c, CBlkWTData cblk) { int mi, i, j, k, wrap; int ulx, uly, w, h; DataBlkInt mask = roiMask; // local copy of mask int[] maskData; // local copy of mask data int[] data; // local copy of quantized data int tmp; int bitMask = 0x7FFFFFFF; SubbandAn root, sb; int maxBits = 0; // local copy bool roiInTile; bool sbInMask; int nROIcoeff = 0; // Get codeblock's data from quantizer cblk = src.getNextCodeBlock(c, cblk); // If there is no ROI in the image, or if we already got all // code-blocks if (!roi || cblk == null) { return(cblk); } data = (int[])cblk.Data; sb = cblk.sb; ulx = cblk.ulx; uly = cblk.uly; w = cblk.w; h = cblk.h; sbInMask = (sb.resLvl <= useStartLevel); // Check that there is an array for the mask and set it to zero maskData = mask.DataInt; // local copy of mask data if (maskData == null || w * h > maskData.Length) { maskData = new int[w * h]; mask.DataInt = maskData; } else { for (i = w * h - 1; i >= 0; i--) { maskData[i] = 0; } } mask.ulx = ulx; mask.uly = uly; mask.w = w; mask.h = h; // Get ROI mask from generator root = src.getAnSubbandTree(tIdx, c); maxBits = maxMagBits[tIdx][c]; roiInTile = mg.getROIMask(mask, root, maxBits, c); // If there is no ROI in this tile, return the code-block untouched if (!roiInTile && (!sbInMask)) { cblk.nROIbp = 0; return(cblk); } // Update field containing the number of ROI magnitude bit-planes cblk.nROIbp = cblk.magbits; // If the entire subband belongs to the ROI mask, The code-block is // set to belong entirely to the ROI with the highest scaling value if (sbInMask) { // Scale the wmse so that instead of scaling the coefficients, the // wmse is scaled. //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'" cblk.wmseScaling *= (float)(1 << (maxBits << 1)); cblk.nROIcoeff = w * h; return(cblk); } // In 'block aligned' mode, the code-block is set to belong entirely // to the ROI with the highest scaling value if one coefficient, at // least, belongs to the ROI if (blockAligned) { wrap = cblk.scanw - w; mi = h * w - 1; i = cblk.offset + cblk.scanw * (h - 1) + w - 1; int nroicoeff = 0; for (j = h; j > 0; j--) { for (k = w - 1; k >= 0; k--, i--, mi--) { if (maskData[mi] != 0) { nroicoeff++; } } i -= wrap; } if (nroicoeff != 0) { // Include the subband //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'" cblk.wmseScaling *= (float)(1 << (maxBits << 1)); cblk.nROIcoeff = w * h; } return(cblk); } // Scale background coefficients bitMask = (((1 << cblk.magbits) - 1) << (31 - cblk.magbits)); wrap = cblk.scanw - w; mi = h * w - 1; i = cblk.offset + cblk.scanw * (h - 1) + w - 1; for (j = h; j > 0; j--) { for (k = w; k > 0; k--, i--, mi--) { tmp = data[i]; if (maskData[mi] != 0) { // ROI coeff. We need to erase fractional bits to ensure // that they do not conflict with BG coeffs. This is only // strictly necessary for ROI coeffs. which non-fractional // magnitude is zero, but much better BG quality can be // achieved if done if reset to zero since coding zeros is // much more efficient (the entropy coder knows nothing // about ROI and cannot avoid coding the ROI fractional // bits, otherwise this would not be necessary). data[i] = (unchecked ((int)0x80000000) & tmp) | (tmp & bitMask); nROIcoeff++; } else { // BG coeff. it is not necessary to erase fractional bits data[i] = (unchecked ((int)0x80000000) & tmp) | ((tmp & 0x7FFFFFFF) >> maxBits); } } i -= wrap; } // Modify the number of significant bit-planes in the code-block cblk.magbits += maxBits; // Store the number of ROI coefficients present in the code-block cblk.nROIcoeff = nROIcoeff; return(cblk); }
/// <summary> Writes the data of the specified area to the file, coordinates are /// relative to the current tile of the source. Before writing, the /// coefficients are limited to the nominal range and packed into 1,2 or 4 /// bytes (according to the bit-depth). /// /// <p>If the data is unisigned, level shifting is applied adding 2^(bit /// depth - 1)</p> /// /// <p>This method may not be called concurrently from different /// threads.</p> /// /// <p>If the data returned from the BlkImgDataSrc source is progressive, /// then it is requested over and over until it is not progressive /// anymore.</p> /// /// </summary> /// <param name="ulx">The horizontal coordinate of the upper-left corner of the /// area to write, relative to the current tile. /// /// </param> /// <param name="uly">The vertical coordinate of the upper-left corner of the area /// to write, relative to the current tile. /// /// </param> /// <param name="width">The width of the area to write. /// /// </param> /// <param name="height">The height of the area to write. /// /// </param> /// <exception cref="IOException">If an I/O error occurs. /// /// </exception> public override void write(int ulx, int uly, int w, int h) { int k, i, j; int fracbits = fb; // In local variable for faster access int tOffx, tOffy; // Active tile offset in the X and Y direction // Initialize db db.ulx = ulx; db.uly = uly; db.w = w; db.h = h; // Get the current active tile offset //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'" tOffx = src.getCompULX(c) - (int)System.Math.Ceiling(src.ImgULX / (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'" tOffy = src.getCompULY(c) - (int)System.Math.Ceiling(src.ImgULY / (double)src.getCompSubsY(c)); // Check the array size if (db.data_array != null && db.data_array.Length < w * h) { // A new one will be allocated by getInternCompData() db.data_array = null; } // Request the data and make sure it is not // progressive do { db = (DataBlkInt)src.getInternCompData(db, c); }while (db.progressive); int tmp; // Check line buffer if (buf == null || buf.Length < packBytes * w) { buf = new byte[packBytes * w]; // Expand buffer } switch (packBytes) { case 1: // Samples packed into 1 byte // Write line by line for (i = 0; i < h; i++) { // Skip to beggining of line in file out_Renamed.Seek(offset + this.w * (uly + tOffy + i) + ulx + tOffx, System.IO.SeekOrigin.Begin); // Write all bytes in the line if (fracbits == 0) { for (k = db.offset + i * db.scanw + w - 1, j = w - 1; j >= 0; k--) { tmp = db.data_array[k] + levShift; buf[j--] = (byte)((tmp < minVal)?minVal:((tmp > maxVal)?maxVal:tmp)); } } else { for (k = db.offset + i * db.scanw + w - 1, j = w - 1; j >= 0; k--) { tmp = (SupportClass.URShift(db.data_array[k], fracbits)) + levShift; buf[j--] = (byte)((tmp < minVal)?minVal:((tmp > maxVal)?maxVal:tmp)); } } out_Renamed.Write(buf, 0, w); } break; case 2: // Samples packed in to 2 bytes (short) // Write line by line for (i = 0; i < h; i++) { // Skip to beggining of line in file out_Renamed.Seek(offset + 2 * (this.w * (uly + tOffy + i) + ulx + tOffx), System.IO.SeekOrigin.Begin); // Write all bytes in the line if (fracbits == 0) { for (k = db.offset + i * db.scanw + w - 1, j = (w << 1) - 1; j >= 0; k--) { tmp = db.data_array[k] + levShift; tmp = (tmp < minVal)?minVal:((tmp > maxVal)?maxVal:tmp); buf[j--] = (byte)tmp; // no need for 0xFF mask since // truncation will do it already buf[j--] = (byte)(SupportClass.URShift(tmp, 8)); } } else { for (k = db.offset + i * db.scanw + w - 1, j = (w << 1) - 1; j >= 0; k--) { tmp = (SupportClass.URShift(db.data_array[k], fracbits)) + levShift; tmp = (tmp < minVal)?minVal:((tmp > maxVal)?maxVal:tmp); buf[j--] = (byte)tmp; // no need for 0xFF mask since // truncation will do it already buf[j--] = (byte)(SupportClass.URShift(tmp, 8)); } } out_Renamed.Write(buf, 0, w << 1); } break; case 4: // Write line by line for (i = 0; i < h; i++) { // Skip to beggining of line in file out_Renamed.Seek(offset + 4 * (this.w * (uly + tOffy + i) + ulx + tOffx), System.IO.SeekOrigin.Begin); // Write all bytes in the line if (fracbits == 0) { for (k = db.offset + i * db.scanw + w - 1, j = (w << 2) - 1; j >= 0; k--) { tmp = db.data_array[k] + levShift; tmp = (tmp < minVal)?minVal:((tmp > maxVal)?maxVal:tmp); buf[j--] = (byte)tmp; // No need to use 0xFF buf[j--] = (byte)(SupportClass.URShift(tmp, 8)); // masks since truncation buf[j--] = (byte)(SupportClass.URShift(tmp, 16)); // will have already the buf[j--] = (byte)(SupportClass.URShift(tmp, 24)); // same effect } } else { for (k = db.offset + i * db.scanw + w - 1, j = (w << 2) - 1; j >= 0; k--) { tmp = (SupportClass.URShift(db.data_array[k], fracbits)) + levShift; tmp = (tmp < minVal)?minVal:((tmp > maxVal)?maxVal:tmp); buf[j--] = (byte)tmp; // No need to use 0xFF buf[j--] = (byte)(SupportClass.URShift(tmp, 8)); // masks since truncation buf[j--] = (byte)(SupportClass.URShift(tmp, 16)); // will have already the buf[j--] = (byte)(SupportClass.URShift(tmp, 24)); // same effect } } out_Renamed.Write(buf, 0, w << 2); } break; default: throw new System.IO.IOException("PGX supports only bit-depth between " + "1 and 31"); } }
/// <summary> Returns, in the blk argument, the 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> After being read the coefficients are level shifted by subtracting /// 2^(nominal bit range - 1) /// /// <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>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 always has its 'progressive' attribute unset /// (i.e. false). /// /// <P>When an I/O exception is encountered the JJ2KExceptionHandler is /// used. The exception is passed to its handleException method. The action /// that is taken depends on the action that has been registered in /// JJ2KExceptionHandler. See JJ2KExceptionHandler for details. /// /// </summary> /// <param name="blk">Its coordinates and dimensions specify the area to /// return. 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. Only 0 /// is valid. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> /// <seealso cref="getCompData"> /// /// </seealso> /// <seealso cref="JJ2KExceptionHandler"> /// /// </seealso> public override DataBlk getInternCompData(DataBlk blk, int c) { int k, j, i, mi; int[] barr; // Check component index if (c != 0) { throw new System.ArgumentException(); } // Check type of block provided as an argument if (blk.DataType != DataBlk.TYPE_INT) { if (intBlk == null) { intBlk = new DataBlkInt(blk.ulx, blk.uly, blk.w, blk.h); } else { intBlk.ulx = blk.ulx; intBlk.uly = blk.uly; intBlk.w = blk.w; intBlk.h = blk.h; } blk = intBlk; } // Get data array barr = (int[])blk.Data; if (barr == null || barr.Length < blk.w * blk.h) { barr = new int[blk.w * blk.h]; blk.Data = barr; } // Check line buffer if (buf == null || buf.Length < blk.w) { buf = new byte[blk.w]; } try { // Read line by line mi = blk.uly + blk.h; for (i = blk.uly; i < mi; i++) { // Reposition in input in_Renamed.Seek(offset + i * w + blk.ulx, System.IO.SeekOrigin.Begin); in_Renamed.Read(buf, 0, blk.w); for (k = (i - blk.uly) * blk.w + blk.w - 1, j = blk.w - 1; j >= 0; j--, k--) { barr[k] = (((int)buf[j]) & 0xFF) - DC_OFFSET; } } } catch (System.IO.IOException e) { JJ2KExceptionHandler.handleException(e); } // Turn off the progressive attribute blk.progressive = false; // Set buffer attributes blk.offset = 0; blk.scanw = blk.w; return(blk); }
/// <summary> Returns, in the blk argument, the 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>After being read the coefficients are level shifted by subtracting /// 2^(nominal bit range - 1)<p> /// /// <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>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 always has its 'progressive' attribute unset /// (i.e. false).</p> /// /// <p>When an I/O exception is encountered the JJ2KExceptionHandler is /// used. The exception is passed to its handleException method. The action /// that is taken depends on the action that has been registered in /// JJ2KExceptionHandler. See JJ2KExceptionHandler for details.</p> /// /// </summary> /// <param name="blk">Its coordinates and dimensions specify the area to /// return. 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. Only 0 /// is valid. /// /// </param> /// <returns> The requested DataBlk /// /// </returns> /// <seealso cref="getCompData"> /// </seealso> /// <seealso cref="JJ2KExceptionHandler"> /// /// </seealso> public override DataBlk getInternCompData(DataBlk blk, int c) { int k, j, i, mi; // counters int levShift = 1 << (bitDepth - 1); // Check component index if (c != 0) { throw new System.ArgumentException(); } // Check type of block provided as an argument if (blk.DataType != DataBlk.TYPE_INT) { if (intBlk == null) { intBlk = new DataBlkInt(blk.ulx, blk.uly, blk.w, blk.h); } else { intBlk.ulx = blk.ulx; intBlk.uly = blk.uly; intBlk.w = blk.w; intBlk.h = blk.h; } blk = intBlk; } // Get data array int[] barr = (int[])blk.Data; if (barr == null || barr.Length < blk.w * blk.h * packBytes) { barr = new int[blk.w * blk.h]; blk.Data = barr; } int paddingLength = (32 - bitDepth); if (buf == null || buf.Length < packBytes * blk.w) { buf = new byte[packBytes * blk.w]; } try { switch (packBytes) { // Switch between one of the 3 byte packet type case 1: // Samples packed into 1 byte // Read line by line mi = blk.uly + blk.h; if (isSigned) { for (i = blk.uly; i < mi; i++) { // Reposition in input in_Renamed.Seek(offset + i * w + blk.ulx, System.IO.SeekOrigin.Begin); in_Renamed.Read(buf, 0, blk.w); for (k = (i - blk.uly) * blk.w + blk.w - 1, j = blk.w - 1; j >= 0; k--) { barr[k] = (((buf[j--] & 0xFF) << paddingLength) >> paddingLength); } } } else { // Not signed for (i = blk.uly; i < mi; i++) { // Reposition in input in_Renamed.Seek(offset + i * w + blk.ulx, System.IO.SeekOrigin.Begin); in_Renamed.Read(buf, 0, blk.w); for (k = (i - blk.uly) * blk.w + blk.w - 1, j = blk.w - 1; j >= 0; k--) { barr[k] = (SupportClass.URShift(((buf[j--] & 0xFF) << paddingLength), paddingLength)) - levShift; } } } break; case 2: // Samples packed into 2 bytes // Read line by line mi = blk.uly + blk.h; if (isSigned) { for (i = blk.uly; i < mi; i++) { // Reposition in input in_Renamed.Seek(offset + 2 * (i * w + blk.ulx), System.IO.SeekOrigin.Begin); in_Renamed.Read(buf, 0, blk.w << 1); switch (byteOrder) { case CSJ2K.j2k.io.EndianType_Fields.LITTLE_ENDIAN: for (k = (i - blk.uly) * blk.w + blk.w - 1, j = (blk.w << 1) - 1; j >= 0; k--) { barr[k] = ((((buf[j--] & 0xFF) << 8) | (buf[j--] & 0xFF)) << paddingLength) >> paddingLength; } break; case CSJ2K.j2k.io.EndianType_Fields.BIG_ENDIAN: for (k = (i - blk.uly) * blk.w + blk.w - 1, j = (blk.w << 1) - 1; j >= 0; k--) { barr[k] = (((buf[j--] & 0xFF) | ((buf[j--] & 0xFF) << 8)) << paddingLength) >> paddingLength; } break; default: throw new System.ApplicationException("Internal JJ2000 bug"); } } } else { // If not signed for (i = blk.uly; i < mi; i++) { // Reposition in input in_Renamed.Seek(offset + 2 * (i * w + blk.ulx), System.IO.SeekOrigin.Begin); in_Renamed.Read(buf, 0, blk.w << 1); switch (byteOrder) { case CSJ2K.j2k.io.EndianType_Fields.LITTLE_ENDIAN: for (k = (i - blk.uly) * blk.w + blk.w - 1, j = (blk.w << 1) - 1; j >= 0; k--) { barr[k] = (SupportClass.URShift(((((buf[j--] & 0xFF) << 8) | (buf[j--] & 0xFF)) << paddingLength), paddingLength)) - levShift; } break; case CSJ2K.j2k.io.EndianType_Fields.BIG_ENDIAN: for (k = (i - blk.uly) * blk.w + blk.w - 1, j = (blk.w << 1) - 1; j >= 0; k--) { barr[k] = (SupportClass.URShift((((buf[j--] & 0xFF) | ((buf[j--] & 0xFF) << 8)) << paddingLength), paddingLength)) - levShift; } break; default: throw new System.ApplicationException("Internal JJ2000 bug"); } } } break; case 4: // Samples packed into 4 bytes // Read line by line mi = blk.uly + blk.h; if (isSigned) { for (i = blk.uly; i < mi; i++) { // Reposition in input in_Renamed.Seek(offset + 4 * (i * w + blk.ulx), System.IO.SeekOrigin.Begin); in_Renamed.Read(buf, 0, blk.w << 2); switch (byteOrder) { case CSJ2K.j2k.io.EndianType_Fields.LITTLE_ENDIAN: for (k = (i - blk.uly) * blk.w + blk.w - 1, j = (blk.w << 2) - 1; j >= 0; k--) { barr[k] = ((((buf[j--] & 0xFF) << 24) | ((buf[j--] & 0xFF) << 16) | ((buf[j--] & 0xFF) << 8) | (buf[j--] & 0xFF)) << paddingLength) >> paddingLength; } break; case CSJ2K.j2k.io.EndianType_Fields.BIG_ENDIAN: for (k = (i - blk.uly) * blk.w + blk.w - 1, j = (blk.w << 2) - 1; j >= 0; k--) { barr[k] = (((buf[j--] & 0xFF) | ((buf[j--] & 0xFF) << 8) | ((buf[j--] & 0xFF) << 16) | ((buf[j--] & 0xFF) << 24)) << paddingLength) >> paddingLength; } break; default: throw new System.ApplicationException("Internal JJ2000 bug"); } } } else { for (i = blk.uly; i < mi; i++) { // Reposition in input in_Renamed.Seek(offset + 4 * (i * w + blk.ulx), System.IO.SeekOrigin.Begin); in_Renamed.Read(buf, 0, blk.w << 2); switch (byteOrder) { case CSJ2K.j2k.io.EndianType_Fields.LITTLE_ENDIAN: for (k = (i - blk.uly) * blk.w + blk.w - 1, j = (blk.w << 2) - 1; j >= 0; k--) { barr[k] = (SupportClass.URShift(((((buf[j--] & 0xFF) << 24) | ((buf[j--] & 0xFF) << 16) | ((buf[j--] & 0xFF) << 8) | (buf[j--] & 0xFF)) << paddingLength), paddingLength)) - levShift; } break; case CSJ2K.j2k.io.EndianType_Fields.BIG_ENDIAN: for (k = (i - blk.uly) * blk.w + blk.w - 1, j = (blk.w << 2) - 1; j >= 0; k--) { barr[k] = (SupportClass.URShift((((buf[j--] & 0xFF) | ((buf[j--] & 0xFF) << 8) | ((buf[j--] & 0xFF) << 16) | ((buf[j--] & 0xFF) << 24)) << paddingLength), paddingLength)) - levShift; } break; default: throw new System.ApplicationException("Internal JJ2000 bug"); } } } break; default: throw new System.IO.IOException("PGX supports only bit-depth between" + " 1 and 31"); } } catch (System.IO.IOException e) { JJ2KExceptionHandler.handleException(e); } // Turn off the progressive attribute blk.progressive = false; // Set buffer attributes blk.offset = 0; blk.scanw = blk.w; return(blk); }
public override DataBlk getCompData(DataBlk blk, int c) { var newBlk = new DataBlkInt(blk.ulx, blk.uly, blk.w, blk.h); return(this.getInternCompData(newBlk, c)); }