/// <summary> Performs the cleanup pass on the specified data and bit-plane. It codes /// all insignificant samples which have its "visited" state bit off, using /// the ZC, SC, and RLC primitives. It toggles the "visited" state bit to 0 /// (off) for all samples in the code-block. /// /// </summary> /// <param name="srcblk">The code-block data to code /// /// </param> /// <param name="mq">The MQ-coder to use /// /// </param> /// <param name="doterm">If true it performs an MQ-coder termination after the end /// of the pass /// /// </param> /// <param name="bp">The bit-plane to code /// /// </param> /// <param name="state">The state information for the code-block /// /// </param> /// <param name="fs">The distortion estimation lookup table for SC /// /// </param> /// <param name="zc_lut">The ZC lookup table to use in ZC. /// /// </param> /// <param name="symbuf">The buffer to hold symbols to send to the MQ coder /// /// </param> /// <param name="ctxtbuf">A buffer to hold the contexts to use in sending the /// buffered symbols to the MQ coder. /// /// </param> /// <param name="ratebuf">The buffer where to store the rate (i.e. coded lenth) at /// the end of this coding pass. /// /// </param> /// <param name="pidx">The coding pass index. Is the index in the 'ratebuf' array /// where to store the coded length after this coding pass. /// /// </param> /// <param name="ltpidx">The index of the last pass that was terminated, or /// negative if none. /// /// </param> /// <param name="options">The bitmask of entropy coding options to apply to the /// code-block /// /// </param> /// <returns> The decrease in distortion for this pass, in the fixed-point /// normalized representation of the 'FS_LOSSY' and 'FS_LOSSLESS' tables. /// /// </returns> static private int cleanuppass(CBlkWTData srcblk, MQCoder mq, bool doterm, int bp, int[] state, int[] fs, int[] zc_lut, int[] symbuf, int[] ctxtbuf, int[] ratebuf, int pidx, int ltpidx, int options) { // NOTE: The speedup mode of the MQ coder has been briefly tried to // speed up the coding of insignificants RLCs, without any success // (i.e. no speedup whatsoever). The use of the speedup mode should be // revisisted more in depth and the implementationn of it in MQCoder // should be reviewed for optimization opportunities. int j, sj; // The state index for line and stripe int k, sk; // The data index for line and stripe int nsym = 0; // Symbol counter for symbol and context buffers int dscanw; // The data scan-width int sscanw; // The state scan-width int jstep; // Stripe to stripe step for 'sj' int kstep; // Stripe to stripe step for 'sk' int stopsk; // The loop limit on the variable sk int csj; // Local copy (i.e. cached) of 'state[j]' int mask; // The mask for the current bit-plane int sym; // The symbol to code int rlclen; // Length of RLC int ctxt; // The context to use int[] data; // The data buffer int dist; // The distortion reduction for this pass int shift; // Shift amount for distortion int upshift; // Shift left amount for distortion int downshift; // Shift right amount for distortion int normval; // The normalized sample magnitude value int s; // The stripe index bool causal; // Flag to indicate if stripe-causal context // formation is to be used int nstripes; // The number of stripes in the code-block int sheight; // Height of the current stripe int off_ul, off_ur, off_dr, off_dl; // offsets // Initialize local variables dscanw = srcblk.scanw; sscanw = srcblk.w + 2; jstep = sscanw * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT / 2 - srcblk.w; kstep = dscanw * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT - srcblk.w; mask = 1 << bp; data = (int[]) srcblk.Data; nstripes = (srcblk.h + CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT - 1) / CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT; dist = 0; // We use the MSE_LKP_BITS-1 bits below the bit just coded for // distortion estimation. shift = bp - (MSE_LKP_BITS - 1); upshift = (shift >= 0)?0:- shift; downshift = (shift <= 0)?0:shift; causal = (options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_VERT_STR_CAUSAL) != 0; // Pre-calculate offsets in 'state' for diagonal neighbors off_ul = - sscanw - 1; // up-left off_ur = - sscanw + 1; // up-right off_dr = sscanw + 1; // down-right off_dl = sscanw - 1; // down-left // Code stripe by stripe sk = srcblk.offset; sj = sscanw + 1; for (s = nstripes - 1; s >= 0; s--, sk += kstep, sj += jstep) { sheight = (s != 0)?CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT:srcblk.h - (nstripes - 1) * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT; stopsk = sk + srcblk.w; // Scan by set of 1 stripe column at a time for (nsym = 0; sk < stopsk; sk++, sj++) { // Start column j = sj; csj = state[j]; { // Check for RLC: if all samples are not significant, not // visited and do not have a non-zero context, and column // is full height, we do RLC. if (csj == 0 && state[j + sscanw] == 0 && sheight == CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT) { k = sk; if ((data[k] & mask) != 0) { rlclen = 0; } else if ((data[k += dscanw] & mask) != 0) { rlclen = 1; } else if ((data[k += dscanw] & mask) != 0) { rlclen = 2; j += sscanw; csj = state[j]; } else if ((data[k += dscanw] & mask) != 0) { rlclen = 3; j += sscanw; csj = state[j]; } else { // Code insignificant RLC symbuf[nsym] = 0; ctxtbuf[nsym++] = RLC_CTXT; // Goto next column continue; } // Code significant RLC symbuf[nsym] = 1; ctxtbuf[nsym++] = RLC_CTXT; // Send MSB bit index symbuf[nsym] = rlclen >> 1; ctxtbuf[nsym++] = UNIF_CTXT; // Send LSB bit index symbuf[nsym] = rlclen & 0x01; ctxtbuf[nsym++] = UNIF_CTXT; // Code sign of sample that became significant // Update distortion normval = (data[k] >> downshift) << upshift; dist += fs[normval & ((1 << (MSE_LKP_BITS - 1)) - 1)]; // Apply sign coding sym = SupportClass.URShift(data[k], 31); if ((rlclen & 0x01) == 0) { // Sample that became significant is first row of // its column half ctxt = SC_LUT[(SupportClass.URShift(csj, SC_SHIFT_R1)) & SC_MASK]; symbuf[nsym] = sym ^ (SupportClass.URShift(ctxt, SC_SPRED_SHIFT)); ctxtbuf[nsym++] = ctxt & SC_LUT_MASK; // Update state information (significant bit, // visited bit, neighbor significant bit of // neighbors, non zero context of neighbors, sign // of neighbors) if (rlclen != 0 || !causal) { // If in causal mode do not change contexts of // previous stripe. state[j + off_ul] |= STATE_NZ_CTXT_R2 | STATE_D_DR_R2; state[j + off_ur] |= STATE_NZ_CTXT_R2 | STATE_D_DL_R2; } // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2 | STATE_V_U_SIGN_R2; if (rlclen != 0 || !causal) { // If in causal mode do not change // contexts of previous stripe. state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2 | STATE_V_D_SIGN_R2; } state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_H_L_SIGN_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_H_R_SIGN_R1 | STATE_D_UR_R2; } else { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2; if (rlclen != 0 || !causal) { // If in causal mode do not change // contexts of previous stripe. state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2; } state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_D_UR_R2; } // Changes to csj are saved later if ((rlclen >> 1) != 0) { // Sample that became significant is in bottom // half of column => jump to bottom half //UPGRADE_NOTE: Labeled break statement was changed to a goto statement. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1012'" goto top_half_brk; } // Otherwise sample that became significant is in // top half of column => continue on top half } else { // Sample that became significant is second row of // its column half ctxt = SC_LUT[(SupportClass.URShift(csj, SC_SHIFT_R2)) & SC_MASK]; symbuf[nsym] = sym ^ (SupportClass.URShift(ctxt, SC_SPRED_SHIFT)); ctxtbuf[nsym++] = ctxt & SC_LUT_MASK; // Update state information (significant bit, // neighbor significant bit of neighbors, non zero // context of neighbors, sign of neighbors) state[j + off_dl] |= STATE_NZ_CTXT_R1 | STATE_D_UR_R1; state[j + off_dr] |= STATE_NZ_CTXT_R1 | STATE_D_UL_R1; // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1 | STATE_V_D_SIGN_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1 | STATE_V_U_SIGN_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2 | STATE_H_L_SIGN_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2 | STATE_H_R_SIGN_R2; } else { csj |= STATE_SIG_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2; } // Save changes to csj state[j] = csj; if ((rlclen >> 1) != 0) { // Sample that became significant is in bottom // half of column => we're done with this // column continue; } // Otherwise sample that became significant is in // top half of column => we're done with top // column j += sscanw; csj = state[j]; //UPGRADE_NOTE: Labeled break statement was changed to a goto statement. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1012'" goto top_half_brk; } } // Do half top of column // If any of the two samples is not significant and has // not been visited in the current bit-plane we can not // skip them if ((((csj >> 1) | csj) & VSTD_MASK_R1R2) != VSTD_MASK_R1R2) { k = sk; // Scan first row if ((csj & (STATE_SIG_R1 | STATE_VISITED_R1)) == 0) { // Apply zero coding ctxtbuf[nsym] = zc_lut[csj & ZC_MASK]; if ((symbuf[nsym++] = SupportClass.URShift((data[k] & mask), bp)) != 0) { // Became significant // Apply sign coding sym = SupportClass.URShift(data[k], 31); ctxt = SC_LUT[(SupportClass.URShift(csj, SC_SHIFT_R1)) & SC_MASK]; symbuf[nsym] = sym ^ (SupportClass.URShift(ctxt, SC_SPRED_SHIFT)); ctxtbuf[nsym++] = ctxt & SC_LUT_MASK; // Update state information (significant bit, // visited bit, neighbor significant bit of // neighbors, non zero context of neighbors, // sign of neighbors) if (!causal) { // If in causal mode do not change // contexts of previous stripe. state[j + off_ul] |= STATE_NZ_CTXT_R2 | STATE_D_DR_R2; state[j + off_ur] |= STATE_NZ_CTXT_R2 | STATE_D_DL_R2; } // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2 | STATE_V_U_SIGN_R2; if (!causal) { // If in causal mode do not change // contexts of previous stripe. state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2 | STATE_V_D_SIGN_R2; } state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_H_L_SIGN_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_H_R_SIGN_R1 | STATE_D_UR_R2; } else { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2; if (!causal) { // If in causal mode do not change // contexts of previous stripe. state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2; } state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_D_UR_R2; } // Update distortion normval = (data[k] >> downshift) << upshift; dist += fs[normval & ((1 << (MSE_LKP_BITS - 1)) - 1)]; } } if (sheight < 2) { csj &= ~ (STATE_VISITED_R1 | STATE_VISITED_R2); state[j] = csj; continue; } // Scan second row if ((csj & (STATE_SIG_R2 | STATE_VISITED_R2)) == 0) { k += dscanw; // Apply zero coding ctxtbuf[nsym] = zc_lut[(SupportClass.URShift(csj, STATE_SEP)) & ZC_MASK]; if ((symbuf[nsym++] = SupportClass.URShift((data[k] & mask), bp)) != 0) { // Became significant // Apply sign coding sym = SupportClass.URShift(data[k], 31); ctxt = SC_LUT[(SupportClass.URShift(csj, SC_SHIFT_R2)) & SC_MASK]; symbuf[nsym] = sym ^ (SupportClass.URShift(ctxt, SC_SPRED_SHIFT)); ctxtbuf[nsym++] = ctxt & SC_LUT_MASK; // Update state information (significant bit, // visited bit, neighbor significant bit of // neighbors, non zero context of neighbors, // sign of neighbors) state[j + off_dl] |= STATE_NZ_CTXT_R1 | STATE_D_UR_R1; state[j + off_dr] |= STATE_NZ_CTXT_R1 | STATE_D_UL_R1; // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R2 | STATE_VISITED_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1 | STATE_V_D_SIGN_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1 | STATE_V_U_SIGN_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2 | STATE_H_L_SIGN_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2 | STATE_H_R_SIGN_R2; } else { csj |= STATE_SIG_R2 | STATE_VISITED_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2; } // Update distortion normval = (data[k] >> downshift) << upshift; dist += fs[normval & ((1 << (MSE_LKP_BITS - 1)) - 1)]; } } } csj &= ~ (STATE_VISITED_R1 | STATE_VISITED_R2); state[j] = csj; // Do half bottom of column if (sheight < 3) continue; j += sscanw; csj = state[j]; } //UPGRADE_NOTE: Label 'top_half_brk' was added. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1011'" top_half_brk: ; // end of 'top_half' block // If any of the two samples is not significant and has // not been visited in the current bit-plane we can not // skip them if ((((csj >> 1) | csj) & VSTD_MASK_R1R2) != VSTD_MASK_R1R2) { k = sk + (dscanw << 1); // Scan first row if ((csj & (STATE_SIG_R1 | STATE_VISITED_R1)) == 0) { // Apply zero coding ctxtbuf[nsym] = zc_lut[csj & ZC_MASK]; if ((symbuf[nsym++] = SupportClass.URShift((data[k] & mask), bp)) != 0) { // Became significant // Apply sign coding sym = SupportClass.URShift(data[k], 31); ctxt = SC_LUT[(SupportClass.URShift(csj, SC_SHIFT_R1)) & SC_MASK]; symbuf[nsym] = sym ^ (SupportClass.URShift(ctxt, SC_SPRED_SHIFT)); ctxtbuf[nsym++] = ctxt & SC_LUT_MASK; // Update state information (significant bit, // visited bit, neighbor significant bit of // neighbors, non zero context of neighbors, // sign of neighbors) state[j + off_ul] |= STATE_NZ_CTXT_R2 | STATE_D_DR_R2; state[j + off_ur] |= STATE_NZ_CTXT_R2 | STATE_D_DL_R2; // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2 | STATE_V_U_SIGN_R2; state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2 | STATE_V_D_SIGN_R2; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_H_L_SIGN_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_H_R_SIGN_R1 | STATE_D_UR_R2; } else { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2; state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_D_UR_R2; } // Update distortion normval = (data[k] >> downshift) << upshift; dist += fs[normval & ((1 << (MSE_LKP_BITS - 1)) - 1)]; } } if (sheight < 4) { csj &= ~ (STATE_VISITED_R1 | STATE_VISITED_R2); state[j] = csj; continue; } // Scan second row if ((csj & (STATE_SIG_R2 | STATE_VISITED_R2)) == 0) { k += dscanw; // Apply zero coding ctxtbuf[nsym] = zc_lut[(SupportClass.URShift(csj, STATE_SEP)) & ZC_MASK]; if ((symbuf[nsym++] = SupportClass.URShift((data[k] & mask), bp)) != 0) { // Became significant // Apply sign coding sym = SupportClass.URShift(data[k], 31); ctxt = SC_LUT[(SupportClass.URShift(csj, SC_SHIFT_R2)) & SC_MASK]; symbuf[nsym] = sym ^ (SupportClass.URShift(ctxt, SC_SPRED_SHIFT)); ctxtbuf[nsym++] = ctxt & SC_LUT_MASK; // Update state information (significant bit, // visited bit, neighbor significant bit of // neighbors, non zero context of neighbors, // sign of neighbors) state[j + off_dl] |= STATE_NZ_CTXT_R1 | STATE_D_UR_R1; state[j + off_dr] |= STATE_NZ_CTXT_R1 | STATE_D_UL_R1; // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R2 | STATE_VISITED_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1 | STATE_V_D_SIGN_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1 | STATE_V_U_SIGN_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2 | STATE_H_L_SIGN_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2 | STATE_H_R_SIGN_R2; } else { csj |= STATE_SIG_R2 | STATE_VISITED_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2; } // Update distortion normval = (data[k] >> downshift) << upshift; dist += fs[normval & ((1 << (MSE_LKP_BITS - 1)) - 1)]; } } } csj &= ~ (STATE_VISITED_R1 | STATE_VISITED_R2); state[j] = csj; } // Code all buffered symbols, if any if (nsym > 0) mq.codeSymbols(symbuf, ctxtbuf, nsym); } // Insert a segment marker if we need to if ((options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_SEG_SYMBOLS) != 0) { mq.codeSymbols(SEG_SYMBOLS, SEG_SYMB_CTXTS, SEG_SYMBOLS.Length); } // Reset the MQ context states if we need to if ((options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_RESET_MQ) != 0) { mq.resetCtxts(); } // Terminate the MQ bit stream if we need to if (doterm) { ratebuf[pidx] = mq.terminate(); // Termination has special length } else { // Use normal length calculation ratebuf[pidx] = mq.NumCodedBytes; } // Add length of previous segments, if any if (ltpidx >= 0) { ratebuf[pidx] += ratebuf[ltpidx]; } // Finish length calculation if needed if (doterm) { mq.finishLengthCalculation(ratebuf, pidx); } // Return the reduction in distortion return dist; }
/// <summary> Compresses the code-block in 'srcblk' and puts the results in 'ccb', /// using the specified options and temporary storage. /// /// </summary> /// <param name="c">The component for which to return the next code-block. /// /// </param> /// <param name="ccb">The object where the compressed data will be stored. If the /// 'data' array of 'cbb' is not null it may be reused to return the /// compressed data. /// /// </param> /// <param name="srcblk">The code-block data to code /// /// </param> /// <param name="mq">The MQ-coder to use /// /// </param> /// <param name="bout">The bit level output to use. Used only if 'OPT_BYPASS' is /// turned on in the 'options' argument. /// /// </param> /// <param name="out">The byte buffer trough which the compressed data is stored. /// /// </param> /// <param name="state">The state information for the code-block /// /// </param> /// <param name="distbuf">The buffer where to store the distortion at /// the end of each coding pass. /// /// </param> /// <param name="ratebuf">The buffer where to store the rate (i.e. coded lenth) at /// the end of each coding pass. /// /// </param> /// <param name="istermbuf">The buffer where to store the terminated flag for each /// coding pass. /// /// </param> /// <param name="symbuf">The buffer to hold symbols to send to the MQ coder /// /// </param> /// <param name="ctxtbuf">A buffer to hold the contexts to use in sending the /// buffered symbols to the MQ coder. /// /// </param> /// <param name="options">The options to use when coding this code-block /// /// </param> /// <param name="rev">The reversible flag. Should be true if the source of this /// code-block's data is reversible. /// /// </param> /// <param name="lcType">The type of length calculation to use with the MQ coder. /// /// </param> /// <param name="tType">The type of termination to use with the MQ coder. /// /// </param> /// <seealso cref="getNextCodeBlock"> /// /// </seealso> static private void compressCodeBlock(int c, CBlkRateDistStats ccb, CBlkWTData srcblk, MQCoder mq, BitToByteOutput bout, ByteOutputBuffer out_Renamed, int[] state, double[] distbuf, int[] ratebuf, bool[] istermbuf, int[] symbuf, int[] ctxtbuf, int options, bool rev, int lcType, int tType) { // NOTE: This method should not access any non-final instance or // static variables, either directly or indirectly through other // methods in order to be sure that the method is thread safe. int[] zc_lut; // The ZC lookup table to use int skipbp; // The number of non-significant bit-planes to skip int curbp; // The current magnitude bit-plane (starts at 30) int[] fm; // The distortion estimation lookup table for MR int[] fs; // The distortion estimation lookup table for SC int lmb; // The least significant magnitude bit int npass; // The number of coding passes, for R-D statistics double msew; // The distortion (MSE weight) for the current bit-plane double totdist; // The total cumulative distortion decrease int ltpidx; // The index of the last pass which is terminated // Check error-resilient termination if ((options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_PRED_TERM) != 0 && tType != MQCoder.TERM_PRED_ER) { throw new System.ArgumentException("Embedded error-resilient info " + "in MQ termination option " + "specified but incorrect MQ " + "termination " + "policy specified"); } // Set MQ flags mq.LenCalcType = lcType; mq.TermType = tType; lmb = 30 - srcblk.magbits + 1; // If there are more bit-planes to code than the implementation // bit-depth set lmb to 0 lmb = (lmb < 0)?0:lmb; // Reset state ArrayUtil.intArraySet(state, 0); // Find the most significant bit-plane skipbp = calcSkipMSBP(srcblk, lmb); // Initialize output code-block ccb.m = srcblk.m; ccb.n = srcblk.n; ccb.sb = srcblk.sb; ccb.nROIcoeff = srcblk.nROIcoeff; ccb.skipMSBP = skipbp; if (ccb.nROIcoeff != 0) { ccb.nROIcp = 3 * (srcblk.nROIbp - skipbp - 1) + 1; } else { ccb.nROIcp = 0; } // Choose correct ZC lookup table for global orientation switch (srcblk.sb.orientation) { case Subband.WT_ORIENT_HL: zc_lut = ZC_LUT_HL; break; case Subband.WT_ORIENT_LL: case Subband.WT_ORIENT_LH: zc_lut = ZC_LUT_LH; break; case Subband.WT_ORIENT_HH: zc_lut = ZC_LUT_HH; break; default: throw new System.InvalidOperationException("JJ2000 internal error"); } // Loop on significant magnitude bit-planes doing the 3 passes curbp = 30 - skipbp; fs = FS_LOSSY; fm = FM_LOSSY; msew = System.Math.Pow(2, ((curbp - lmb) << 1) - MSE_LKP_FRAC_BITS) * srcblk.sb.stepWMSE * srcblk.wmseScaling; totdist = 0f; npass = 0; ltpidx = - 1; // First significant bit-plane has only the pass pass if (curbp >= lmb) { // Do we need the "lossless" 'fs' table ? if (rev && curbp == lmb) { fs = FM_LOSSLESS; } // We terminate if regular termination, last bit-plane, or next // bit-plane is "raw". istermbuf[npass] = (options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_TERM_PASS) != 0 || curbp == lmb || ((options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_BYPASS) != 0 && (31 - CSJ2K.j2k.entropy.StdEntropyCoderOptions.NUM_NON_BYPASS_MS_BP - skipbp) >= curbp); totdist += cleanuppass(srcblk, mq, istermbuf[npass], curbp, state, fs, zc_lut, symbuf, ctxtbuf, ratebuf, npass, ltpidx, options) * msew; distbuf[npass] = totdist; if (istermbuf[npass]) ltpidx = npass; npass++; msew *= 0.25; curbp--; } // Other bit-planes have all passes while (curbp >= lmb) { // Do we need the "lossless" 'fs' and 'fm' tables ? if (rev && curbp == lmb) { fs = FS_LOSSLESS; fm = FM_LOSSLESS; } // Do the significance propagation pass // We terminate if regular termination only istermbuf[npass] = (options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_TERM_PASS) != 0; if ((options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_BYPASS) == 0 || (31 - CSJ2K.j2k.entropy.StdEntropyCoderOptions.NUM_NON_BYPASS_MS_BP - skipbp <= curbp)) { // No bypass coding totdist += sigProgPass(srcblk, mq, istermbuf[npass], curbp, state, fs, zc_lut, symbuf, ctxtbuf, ratebuf, npass, ltpidx, options) * msew; } else { // Bypass ("raw") coding bout.PredTerm = (options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_PRED_TERM) != 0; totdist += rawSigProgPass(srcblk, bout, istermbuf[npass], curbp, state, fs, ratebuf, npass, ltpidx, options) * msew; } distbuf[npass] = totdist; if (istermbuf[npass]) ltpidx = npass; npass++; // Do the magnitude refinement pass // We terminate if regular termination or bypass ("raw") coding istermbuf[npass] = (options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_TERM_PASS) != 0 || ((options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_BYPASS) != 0 && (31 - CSJ2K.j2k.entropy.StdEntropyCoderOptions.NUM_NON_BYPASS_MS_BP - skipbp > curbp)); if ((options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_BYPASS) == 0 || (31 - CSJ2K.j2k.entropy.StdEntropyCoderOptions.NUM_NON_BYPASS_MS_BP - skipbp <= curbp)) { // No bypass coding totdist += magRefPass(srcblk, mq, istermbuf[npass], curbp, state, fm, symbuf, ctxtbuf, ratebuf, npass, ltpidx, options) * msew; } else { // Bypass ("raw") coding bout.PredTerm = (options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_PRED_TERM) != 0; totdist += rawMagRefPass(srcblk, bout, istermbuf[npass], curbp, state, fm, ratebuf, npass, ltpidx, options) * msew; } distbuf[npass] = totdist; if (istermbuf[npass]) ltpidx = npass; npass++; // Do the clenup pass // We terminate if regular termination, last bit-plane, or next // bit-plane is "raw". istermbuf[npass] = (options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_TERM_PASS) != 0 || curbp == lmb || ((options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_BYPASS) != 0 && (31 - CSJ2K.j2k.entropy.StdEntropyCoderOptions.NUM_NON_BYPASS_MS_BP - skipbp) >= curbp); totdist += cleanuppass(srcblk, mq, istermbuf[npass], curbp, state, fs, zc_lut, symbuf, ctxtbuf, ratebuf, npass, ltpidx, options) * msew; distbuf[npass] = totdist; if (istermbuf[npass]) ltpidx = npass; npass++; // Goto next bit-plane msew *= 0.25; curbp--; } // Copy compressed data and rate-distortion statistics to output ccb.data = new byte[out_Renamed.size()]; out_Renamed.toByteArray(0, out_Renamed.size(), ccb.data, 0); checkEndOfPassFF(ccb.data, ratebuf, istermbuf, npass); ccb.selectConvexHull(ratebuf, distbuf, (options & (CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_BYPASS | CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_TERM_PASS)) != 0?istermbuf:null, npass, rev); // Reset MQ coder and bit output for next code-block mq.reset(); if (bout != null) bout.reset(); }
/// <summary> Performs the magnitude refinement pass on the specified data and /// bit-plane. It codes the samples which are significant and which do not /// have the "visited" state bit turned on, using the MR primitive. The /// "visited" state bit is not mofified for any samples. /// /// </summary> /// <param name="srcblk">The code-block data to code /// /// </param> /// <param name="mq">The MQ-coder to use /// /// </param> /// <param name="doterm">If true it performs an MQ-coder termination after the end /// of the pass /// /// </param> /// <param name="bp">The bit-plane to code /// /// </param> /// <param name="state">The state information for the code-block /// /// </param> /// <param name="fm">The distortion estimation lookup table for MR /// /// </param> /// <param name="symbuf">The buffer to hold symbols to send to the MQ coder /// /// </param> /// <param name="ctxtbuf">A buffer to hold the contexts to use in sending the /// buffered symbols to the MQ coder. /// /// </param> /// <param name="ratebuf">The buffer where to store the rate (i.e. coded lenth) at /// the end of this coding pass. /// /// </param> /// <param name="pidx">The coding pass index. Is the index in the 'ratebuf' array /// where to store the coded length after this coding pass. /// /// </param> /// <param name="ltpidx">The index of the last pass that was terminated, or /// negative if none. /// /// </param> /// <param name="options">The bitmask of entropy coding options to apply to the /// code-block /// /// </param> /// <returns> The decrease in distortion for this pass, in the fixed-point /// normalized representation of the 'FS_LOSSY' and 'FS_LOSSLESS' tables. /// /// </returns> static private int magRefPass(CBlkWTData srcblk, MQCoder mq, bool doterm, int bp, int[] state, int[] fm, int[] symbuf, int[] ctxtbuf, int[] ratebuf, int pidx, int ltpidx, int options) { int j, sj; // The state index for line and stripe int k, sk; // The data index for line and stripe int nsym = 0; // Symbol counter for symbol and context buffers int dscanw; // The data scan-width int sscanw; // The state scan-width int jstep; // Stripe to stripe step for 'sj' int kstep; // Stripe to stripe step for 'sk' int stopsk; // The loop limit on the variable sk int csj; // Local copy (i.e. cached) of 'state[j]' int mask; // The mask for the current bit-plane int[] data; // The data buffer int dist; // The distortion reduction for this pass int shift; // Shift amount for distortion int upshift; // Shift left amount for distortion int downshift; // Shift right amount for distortion int normval; // The normalized sample magnitude value int s; // The stripe index int nstripes; // The number of stripes in the code-block int sheight; // Height of the current stripe // Initialize local variables dscanw = srcblk.scanw; sscanw = srcblk.w + 2; jstep = sscanw * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT / 2 - srcblk.w; kstep = dscanw * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT - srcblk.w; mask = 1 << bp; data = (int[]) srcblk.Data; nstripes = (srcblk.h + CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT - 1) / CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT; dist = 0; // We use the bit just coded plus MSE_LKP_BITS-1 bits below the bit // just coded for distortion estimation. shift = bp - (MSE_LKP_BITS - 1); upshift = (shift >= 0)?0:- shift; downshift = (shift <= 0)?0:shift; // Code stripe by stripe sk = srcblk.offset; sj = sscanw + 1; for (s = nstripes - 1; s >= 0; s--, sk += kstep, sj += jstep) { sheight = (s != 0)?CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT:srcblk.h - (nstripes - 1) * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT; stopsk = sk + srcblk.w; // Scan by set of 1 stripe column at a time for (nsym = 0; sk < stopsk; sk++, sj++) { // Do half top of column j = sj; csj = state[j]; // If any of the two samples is significant and not yet // visited in the current bit-plane we can not skip them if ((((SupportClass.URShift(csj, 1)) & (~ csj)) & VSTD_MASK_R1R2) != 0) { k = sk; // Scan first row if ((csj & (STATE_SIG_R1 | STATE_VISITED_R1)) == STATE_SIG_R1) { // Apply MR primitive symbuf[nsym] = SupportClass.URShift((data[k] & mask), bp); ctxtbuf[nsym++] = MR_LUT[csj & MR_MASK]; // Update the STATE_PREV_MR bit csj |= STATE_PREV_MR_R1; // Update distortion normval = (data[k] >> downshift) << upshift; dist += fm[normval & ((1 << MSE_LKP_BITS) - 1)]; } if (sheight < 2) { state[j] = csj; continue; } // Scan second row if ((csj & (STATE_SIG_R2 | STATE_VISITED_R2)) == STATE_SIG_R2) { k += dscanw; // Apply MR primitive symbuf[nsym] = SupportClass.URShift((data[k] & mask), bp); ctxtbuf[nsym++] = MR_LUT[(SupportClass.URShift(csj, STATE_SEP)) & MR_MASK]; // Update the STATE_PREV_MR bit csj |= STATE_PREV_MR_R2; // Update distortion normval = (data[k] >> downshift) << upshift; dist += fm[normval & ((1 << MSE_LKP_BITS) - 1)]; } state[j] = csj; } // Do half bottom of column if (sheight < 3) continue; j += sscanw; csj = state[j]; // If any of the two samples is significant and not yet // visited in the current bit-plane we can not skip them if ((((SupportClass.URShift(csj, 1)) & (~ csj)) & VSTD_MASK_R1R2) != 0) { k = sk + (dscanw << 1); // Scan first row if ((csj & (STATE_SIG_R1 | STATE_VISITED_R1)) == STATE_SIG_R1) { // Apply MR primitive symbuf[nsym] = SupportClass.URShift((data[k] & mask), bp); ctxtbuf[nsym++] = MR_LUT[csj & MR_MASK]; // Update the STATE_PREV_MR bit csj |= STATE_PREV_MR_R1; // Update distortion normval = (data[k] >> downshift) << upshift; dist += fm[normval & ((1 << MSE_LKP_BITS) - 1)]; } if (sheight < 4) { state[j] = csj; continue; } // Scan second row if ((state[j] & (STATE_SIG_R2 | STATE_VISITED_R2)) == STATE_SIG_R2) { k += dscanw; // Apply MR primitive symbuf[nsym] = SupportClass.URShift((data[k] & mask), bp); ctxtbuf[nsym++] = MR_LUT[(SupportClass.URShift(csj, STATE_SEP)) & MR_MASK]; // Update the STATE_PREV_MR bit csj |= STATE_PREV_MR_R2; // Update distortion normval = (data[k] >> downshift) << upshift; dist += fm[normval & ((1 << MSE_LKP_BITS) - 1)]; } state[j] = csj; } } // Code all buffered symbols, if any if (nsym > 0) mq.codeSymbols(symbuf, ctxtbuf, nsym); } // Reset the MQ context states if we need to if ((options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_RESET_MQ) != 0) { mq.resetCtxts(); } // Terminate the MQ bit stream if we need to if (doterm) { ratebuf[pidx] = mq.terminate(); // Termination has special length } else { // Use normal length calculation ratebuf[pidx] = mq.NumCodedBytes; } // Add length of previous segments, if any if (ltpidx >= 0) { ratebuf[pidx] += ratebuf[ltpidx]; } // Finish length calculation if needed if (doterm) { mq.finishLengthCalculation(ratebuf, pidx); } // Return the reduction in distortion return dist; }
/// <summary> Performs the magnitude refinement pass on the specified data and /// bit-plane, without using the arithmetic coder. It codes the samples /// which are significant and which do not have the "visited" state bit /// turned on, using the MR primitive. The "visited" state bit is not /// mofified for any samples. /// /// <p>In this method, the arithmetic coder is bypassed, and raw bits are /// directly written in the bit stream (useful when distribution are close /// to uniform, for intance, at high bit-rates and at lossless /// compression). The 'STATE_PREV_MR_R1' and 'STATE_PREV_MR_R2' bits are /// not set because they are used only when the arithmetic coder is not /// bypassed.</p> /// /// </summary> /// <param name="srcblk">The code-block data to code /// /// </param> /// <param name="bout">The bit based output /// /// </param> /// <param name="doterm">If true the bit based output is byte aligned after the /// end of the pass. /// /// </param> /// <param name="bp">The bit-plane to code /// /// </param> /// <param name="state">The state information for the code-block /// /// </param> /// <param name="fm">The distortion estimation lookup table for MR /// /// </param> /// <param name="ratebuf">The buffer where to store the rate (i.e. coded lenth) at /// the end of this coding pass. /// /// </param> /// <param name="pidx">The coding pass index. Is the index in the 'ratebuf' array /// where to store the coded length after this coding pass. /// /// </param> /// <param name="ltpidx">The index of the last pass that was terminated, or /// negative if none. /// /// </param> /// <param name="options">The bitmask of entropy coding options to apply to the /// code-block /// /// </param> /// <returns> The decrease in distortion for this pass, in the fixed-point /// normalized representation of the 'FS_LOSSY' and 'FS_LOSSLESS' tables. /// /// </returns> static private int rawMagRefPass(CBlkWTData srcblk, BitToByteOutput bout, bool doterm, int bp, int[] state, int[] fm, int[] ratebuf, int pidx, int ltpidx, int options) { int j, sj; // The state index for line and stripe int k, sk; // The data index for line and stripe int dscanw; // The data scan-width int sscanw; // The state scan-width int jstep; // Stripe to stripe step for 'sj' int kstep; // Stripe to stripe step for 'sk' int stopsk; // The loop limit on the variable sk int csj; // Local copy (i.e. cached) of 'state[j]' int mask; // The mask for the current bit-plane int[] data; // The data buffer int dist; // The distortion reduction for this pass int shift; // Shift amount for distortion int upshift; // Shift left amount for distortion int downshift; // Shift right amount for distortion int normval; // The normalized sample magnitude value int s; // The stripe index int nstripes; // The number of stripes in the code-block int sheight; // Height of the current stripe int nsym = 0; // Initialize local variables dscanw = srcblk.scanw; sscanw = srcblk.w + 2; jstep = sscanw * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT / 2 - srcblk.w; kstep = dscanw * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT - srcblk.w; mask = 1 << bp; data = (int[]) srcblk.Data; nstripes = (srcblk.h + CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT - 1) / CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT; dist = 0; // We use the bit just coded plus MSE_LKP_BITS-1 bits below the bit // just coded for distortion estimation. shift = bp - (MSE_LKP_BITS - 1); upshift = (shift >= 0)?0:- shift; downshift = (shift <= 0)?0:shift; // Code stripe by stripe sk = srcblk.offset; sj = sscanw + 1; for (s = nstripes - 1; s >= 0; s--, sk += kstep, sj += jstep) { sheight = (s != 0)?CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT:srcblk.h - (nstripes - 1) * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT; stopsk = sk + srcblk.w; // Scan by set of 1 stripe column at a time for (; sk < stopsk; sk++, sj++) { // Do half top of column j = sj; csj = state[j]; // If any of the two samples is significant and not yet // visited in the current bit-plane we can not skip them if ((((SupportClass.URShift(csj, 1)) & (~ csj)) & VSTD_MASK_R1R2) != 0) { k = sk; // Scan first row if ((csj & (STATE_SIG_R1 | STATE_VISITED_R1)) == STATE_SIG_R1) { // Code bit "raw" bout.writeBit(SupportClass.URShift((data[k] & mask), bp)); nsym++; // No need to set STATE_PREV_MR_R1 since all magnitude // refinement passes to follow are "raw" // Update distortion normval = (data[k] >> downshift) << upshift; dist += fm[normval & ((1 << MSE_LKP_BITS) - 1)]; } if (sheight < 2) continue; // Scan second row if ((csj & (STATE_SIG_R2 | STATE_VISITED_R2)) == STATE_SIG_R2) { k += dscanw; // Code bit "raw" bout.writeBit(SupportClass.URShift((data[k] & mask), bp)); nsym++; // No need to set STATE_PREV_MR_R2 since all magnitude // refinement passes to follow are "raw" // Update distortion normval = (data[k] >> downshift) << upshift; dist += fm[normval & ((1 << MSE_LKP_BITS) - 1)]; } } // Do half bottom of column if (sheight < 3) continue; j += sscanw; csj = state[j]; // If any of the two samples is significant and not yet // visited in the current bit-plane we can not skip them if ((((SupportClass.URShift(csj, 1)) & (~ csj)) & VSTD_MASK_R1R2) != 0) { k = sk + (dscanw << 1); // Scan first row if ((csj & (STATE_SIG_R1 | STATE_VISITED_R1)) == STATE_SIG_R1) { // Code bit "raw" bout.writeBit(SupportClass.URShift((data[k] & mask), bp)); nsym++; // No need to set STATE_PREV_MR_R1 since all magnitude // refinement passes to follow are "raw" // Update distortion normval = (data[k] >> downshift) << upshift; dist += fm[normval & ((1 << MSE_LKP_BITS) - 1)]; } if (sheight < 4) continue; // Scan second row if ((state[j] & (STATE_SIG_R2 | STATE_VISITED_R2)) == STATE_SIG_R2) { k += dscanw; // Code bit "raw" bout.writeBit(SupportClass.URShift((data[k] & mask), bp)); nsym++; // No need to set STATE_PREV_MR_R2 since all magnitude // refinement passes to follow are "raw" // Update distortion normval = (data[k] >> downshift) << upshift; dist += fm[normval & ((1 << MSE_LKP_BITS) - 1)]; } } } } // Get length and terminate if needed if (doterm) { ratebuf[pidx] = bout.terminate(); } else { ratebuf[pidx] = bout.length(); } // Add length of previous segments, if any if (ltpidx >= 0) { ratebuf[pidx] += ratebuf[ltpidx]; } // Return the reduction in distortion return dist; }
/// <summary> Calculates the number of magnitude bit-planes that are to be skipped, /// because they are non-significant. The algorithm looks for the largest /// magnitude and calculates the most significant bit-plane of it. /// /// </summary> /// <param name="cblk">The code-block of data to scan /// /// </param> /// <param name="lmb">The least significant magnitude bit in the data /// /// </param> /// <returns> The number of magnitude bit-planes to skip (i.e. all zero most /// significant bit-planes). /// /// </returns> static private int calcSkipMSBP(CBlkWTData cblk, int lmb) { int k, kmax, mask; int[] data; int maxmag; int mag; int w, h; int msbp; int l; data = (int[]) cblk.Data; w = cblk.w; h = cblk.h; // First look for the maximum magnitude in the code-block maxmag = 0; // Consider only magnitude bits that are in non-fractional bit-planes. mask = 0x7FFFFFFF & (~ ((1 << lmb) - 1)); for (l = h - 1, k = cblk.offset; l >= 0; l--) { for (kmax = k + w; k < kmax; k++) { mag = data[k] & mask; if (mag > maxmag) maxmag = mag; } k += cblk.scanw - w; } // Now calculate the number of all zero most significant // bit-planes for the maximum magnitude. msbp = 30; do { if (((1 << msbp) & maxmag) != 0) break; msbp--; } while (msbp >= lmb); // Return the number of non-significant bit-planes to skip return 30 - msbp; }
/// <summary> Performs the significance propagation pass on the specified data and /// bit-plane, without using the arithmetic coder. It codes all /// insignificant samples which have, at least, one of its immediate eight /// neighbors already significant, using the ZC and SC primitives as /// needed. It toggles the "visited" state bit to 1 for all those samples. /// /// <p>In this method, the arithmetic coder is bypassed, and raw bits are /// directly written in the bit stream (useful when distribution are close /// to uniform, for intance, at high bit-rates and at lossless /// compression).</p> /// /// </summary> /// <param name="srcblk">The code-block data to code /// /// </param> /// <param name="bout">The bit based output /// /// </param> /// <param name="doterm">If true the bit based output is byte aligned after the /// end of the pass. /// /// </param> /// <param name="bp">The bit-plane to code /// /// </param> /// <param name="state">The state information for the code-block /// /// </param> /// <param name="fs">The distortion estimation lookup table for SC /// /// </param> /// <param name="ratebuf">The buffer where to store the rate (i.e. coded lenth) at /// the end of this coding pass. /// /// </param> /// <param name="pidx">The coding pass index. Is the index in the 'ratebuf' array /// where to store the coded length after this coding pass. /// /// </param> /// <param name="ltpidx">The index of the last pass that was terminated, or /// negative if none. /// /// </param> /// <param name="options">The bitmask of entropy coding options to apply to the /// code-block /// /// </param> /// <returns> The decrease in distortion for this pass, in the fixed-point /// normalized representation of the 'FS_LOSSY' and 'FS_LOSSLESS' tables. /// /// </returns> static private int rawSigProgPass(CBlkWTData srcblk, BitToByteOutput bout, bool doterm, int bp, int[] state, int[] fs, int[] ratebuf, int pidx, int ltpidx, int options) { int j, sj; // The state index for line and stripe int k, sk; // The data index for line and stripe int dscanw; // The data scan-width int sscanw; // The state scan-width int jstep; // Stripe to stripe step for 'sj' int kstep; // Stripe to stripe step for 'sk' int stopsk; // The loop limit on the variable sk int csj; // Local copy (i.e. cached) of 'state[j]' int mask; // The mask for the current bit-plane int nsym = 0; // Number of symbol int sym; // The symbol to code int[] data; // The data buffer int dist; // The distortion reduction for this pass int shift; // Shift amount for distortion int upshift; // Shift left amount for distortion int downshift; // Shift right amount for distortion int normval; // The normalized sample magnitude value int s; // The stripe index bool causal; // Flag to indicate if stripe-causal context // formation is to be used int nstripes; // The number of stripes in the code-block int sheight; // Height of the current stripe int off_ul, off_ur, off_dr, off_dl; // offsets // Initialize local variables dscanw = srcblk.scanw; sscanw = srcblk.w + 2; jstep = sscanw * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT / 2 - srcblk.w; kstep = dscanw * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT - srcblk.w; mask = 1 << bp; data = (int[]) srcblk.Data; nstripes = (srcblk.h + CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT - 1) / CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT; dist = 0; // We use the MSE_LKP_BITS-1 bits below the bit just coded for // distortion estimation. shift = bp - (MSE_LKP_BITS - 1); upshift = (shift >= 0)?0:- shift; downshift = (shift <= 0)?0:shift; causal = (options & CSJ2K.j2k.entropy.StdEntropyCoderOptions.OPT_VERT_STR_CAUSAL) != 0; // Pre-calculate offsets in 'state' for neighbors off_ul = - sscanw - 1; // up-left off_ur = - sscanw + 1; // up-right off_dr = sscanw + 1; // down-right off_dl = sscanw - 1; // down-left // Code stripe by stripe sk = srcblk.offset; sj = sscanw + 1; for (s = nstripes - 1; s >= 0; s--, sk += kstep, sj += jstep) { sheight = (s != 0)?CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT:srcblk.h - (nstripes - 1) * CSJ2K.j2k.entropy.StdEntropyCoderOptions.STRIPE_HEIGHT; stopsk = sk + srcblk.w; // Scan by set of 1 stripe column at a time for (; sk < stopsk; sk++, sj++) { // Do half top of column j = sj; csj = state[j]; // If any of the two samples is not significant and has a // non-zero context (i.e. some neighbor is significant) we can // not skip them if ((((~ csj) & (csj << 2)) & SIG_MASK_R1R2) != 0) { k = sk; // Scan first row if ((csj & (STATE_SIG_R1 | STATE_NZ_CTXT_R1)) == STATE_NZ_CTXT_R1) { // Apply zero coding sym = SupportClass.URShift((data[k] & mask), bp); bout.writeBit(sym); nsym++; if (sym != 0) { // Became significant // Apply sign coding sym = SupportClass.URShift(data[k], 31); bout.writeBit(sym); nsym++; // Update state information (significant bit, // visited bit, neighbor significant bit of // neighbors, non zero context of neighbors, sign // of neighbors) if (!causal) { // If in causal mode do not change contexts of // previous stripe. state[j + off_ul] |= STATE_NZ_CTXT_R2 | STATE_D_DR_R2; state[j + off_ur] |= STATE_NZ_CTXT_R2 | STATE_D_DL_R2; } // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2 | STATE_V_U_SIGN_R2; if (!causal) { // If in causal mode do not change // contexts of previous stripe. state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2 | STATE_V_D_SIGN_R2; } state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_H_L_SIGN_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_H_R_SIGN_R1 | STATE_D_UR_R2; } else { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2; if (!causal) { // If in causal mode do not change // contexts of previous stripe. state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2; } state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_D_UR_R2; } // Update distortion normval = (data[k] >> downshift) << upshift; dist += fs[normval & ((1 << (MSE_LKP_BITS - 1)) - 1)]; } else { csj |= STATE_VISITED_R1; } } if (sheight < 2) { state[j] = csj; continue; } // Scan second row if ((csj & (STATE_SIG_R2 | STATE_NZ_CTXT_R2)) == STATE_NZ_CTXT_R2) { k += dscanw; // Apply zero coding sym = SupportClass.URShift((data[k] & mask), bp); bout.writeBit(sym); nsym++; if (sym != 0) { // Became significant // Apply sign coding sym = SupportClass.URShift(data[k], 31); bout.writeBit(sym); nsym++; // Update state information (significant bit, // visited bit, neighbor significant bit of // neighbors, non zero context of neighbors, sign // of neighbors) state[j + off_dl] |= STATE_NZ_CTXT_R1 | STATE_D_UR_R1; state[j + off_dr] |= STATE_NZ_CTXT_R1 | STATE_D_UL_R1; // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R2 | STATE_VISITED_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1 | STATE_V_D_SIGN_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1 | STATE_V_U_SIGN_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2 | STATE_H_L_SIGN_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2 | STATE_H_R_SIGN_R2; } else { csj |= STATE_SIG_R2 | STATE_VISITED_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2; } // Update distortion normval = (data[k] >> downshift) << upshift; dist += fs[normval & ((1 << (MSE_LKP_BITS - 1)) - 1)]; } else { csj |= STATE_VISITED_R2; } } state[j] = csj; } // Do half bottom of column if (sheight < 3) continue; j += sscanw; csj = state[j]; // If any of the two samples is not significant and has a // non-zero context (i.e. some neighbor is significant) we can // not skip them if ((((~ csj) & (csj << 2)) & SIG_MASK_R1R2) != 0) { k = sk + (dscanw << 1); // Scan first row if ((csj & (STATE_SIG_R1 | STATE_NZ_CTXT_R1)) == STATE_NZ_CTXT_R1) { sym = SupportClass.URShift((data[k] & mask), bp); bout.writeBit(sym); nsym++; if (sym != 0) { // Became significant // Apply sign coding sym = SupportClass.URShift(data[k], 31); bout.writeBit(sym); nsym++; // Update state information (significant bit, // visited bit, neighbor significant bit of // neighbors, non zero context of neighbors, sign // of neighbors) state[j + off_ul] |= STATE_NZ_CTXT_R2 | STATE_D_DR_R2; state[j + off_ur] |= STATE_NZ_CTXT_R2 | STATE_D_DL_R2; // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2 | STATE_V_U_SIGN_R2; state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2 | STATE_V_D_SIGN_R2; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_H_L_SIGN_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_H_R_SIGN_R1 | STATE_D_UR_R2; } else { csj |= STATE_SIG_R1 | STATE_VISITED_R1 | STATE_NZ_CTXT_R2 | STATE_V_U_R2; state[j - sscanw] |= STATE_NZ_CTXT_R2 | STATE_V_D_R2; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_L_R1 | STATE_D_UL_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_H_R_R1 | STATE_D_UR_R2; } // Update distortion normval = (data[k] >> downshift) << upshift; dist += fs[normval & ((1 << (MSE_LKP_BITS - 1)) - 1)]; } else { csj |= STATE_VISITED_R1; } } if (sheight < 4) { state[j] = csj; continue; } if ((csj & (STATE_SIG_R2 | STATE_NZ_CTXT_R2)) == STATE_NZ_CTXT_R2) { k += dscanw; // Apply zero coding sym = SupportClass.URShift((data[k] & mask), bp); bout.writeBit(sym); nsym++; if (sym != 0) { // Became significant // Apply sign coding sym = SupportClass.URShift(data[k], 31); bout.writeBit(sym); nsym++; // Update state information (significant bit, // visited bit, neighbor significant bit of // neighbors, non zero context of neighbors, sign // of neighbors) state[j + off_dl] |= STATE_NZ_CTXT_R1 | STATE_D_UR_R1; state[j + off_dr] |= STATE_NZ_CTXT_R1 | STATE_D_UL_R1; // Update sign state information of neighbors if (sym != 0) { csj |= STATE_SIG_R2 | STATE_VISITED_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1 | STATE_V_D_SIGN_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1 | STATE_V_U_SIGN_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2 | STATE_H_L_SIGN_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2 | STATE_H_R_SIGN_R2; } else { csj |= STATE_SIG_R2 | STATE_VISITED_R2 | STATE_NZ_CTXT_R1 | STATE_V_D_R1; state[j + sscanw] |= STATE_NZ_CTXT_R1 | STATE_V_U_R1; state[j + 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DL_R1 | STATE_H_L_R2; state[j - 1] |= STATE_NZ_CTXT_R1 | STATE_NZ_CTXT_R2 | STATE_D_DR_R1 | STATE_H_R_R2; } // Update distortion normval = (data[k] >> downshift) << upshift; dist += fs[normval & ((1 << (MSE_LKP_BITS - 1)) - 1)]; } else { csj |= STATE_VISITED_R2; } } state[j] = csj; } } } // Get length and terminate if needed if (doterm) { ratebuf[pidx] = bout.terminate(); } else { ratebuf[pidx] = bout.length(); } // Add length of previous segments, if any if (ltpidx >= 0) { ratebuf[pidx] += ratebuf[ltpidx]; } // Return the reduction in distortion return dist; }
/// <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; }
/// <summary> Returns the next code-block in the current tile for the specified /// component, as a copy (see below). 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 always a copy of the internal /// data of this object, and it can be modified "in place" without /// any problems after being returned. The 'offset' of the returned data is /// 0, and the 'scanw' is the same as the code-block width. 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. 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 'c', or /// null if all code-blocks for the current tile have been returned. /// /// </returns> /// <seealso cref="CBlkWTData"> /// /// </seealso> public override CBlkWTData getNextCodeBlock(int c, CBlkWTData cblk) { // We can not directly use getNextInternCodeBlock() since that returns // a reference to the internal buffer, we have to copy that data int j, k; int w; System.Object dst_data; // a int[] or float[] object int[] dst_data_int; float[] dst_data_float; System.Object src_data; // a int[] or float[] object intData = (filters.getWTDataType(tIdx, c) == DataBlk.TYPE_INT); dst_data = null; // Cache the data array, if any if (cblk != null) { dst_data = cblk.Data; } // Get the next code-block cblk = getNextInternCodeBlock(c, cblk); if (cblk == null) { return null; // No more code-blocks in current tile for component // c } // Ensure size of output buffer if (intData) { // int data dst_data_int = (int[]) dst_data; if (dst_data_int == null || dst_data_int.Length < cblk.w * cblk.h) { dst_data = new int[cblk.w * cblk.h]; } } else { // float data dst_data_float = (float[]) dst_data; if (dst_data_float == null || dst_data_float.Length < cblk.w * cblk.h) { dst_data = new float[cblk.w * cblk.h]; } } // Copy data line by line src_data = cblk.Data; w = cblk.w; for (j = w * (cblk.h - 1), k = cblk.offset + (cblk.h - 1) * cblk.scanw; j >= 0; j -= w, k -= cblk.scanw) { // CONVERSION PROBLEM? Array.Copy((System.Array)src_data, k, (System.Array)dst_data, j, w); } cblk.Data = dst_data; cblk.offset = 0; cblk.scanw = w; return cblk; }
/// <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> /// /// <p>The data returned by this method is a copy of the orignal /// data. Therfore it can be modified "in place" without any problems after /// being returned. The 'offset' of the returned data is 0, and the 'scanw' /// is the same as the code-block width. See the 'CBlkWTData' class.</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 getNextCodeBlock(int c, CBlkWTData cblk) { return getNextInternCodeBlock(c, cblk); }
/// <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> 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 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 '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. 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 override CBlkWTData getNextInternCodeBlock(int c, CBlkWTData cblk) { // NOTE: this method is declared final since getNextCodeBlock() relies // on this particular implementation int k, j; int tmp, shiftBits, jmin; int w, h; int[] outarr; float[] infarr = null; CBlkWTDataFloat infblk; float invstep; // The inverse of the quantization step size bool intq; // flag for quantizig ints SubbandAn sb; float stepUDR; // The quantization step size (for a dynamic // range of 1, or unit) int g = ((System.Int32) gbs.getTileCompVal(tIdx, c)); // Are we quantizing ints or floats? intq = (src.getDataType(tIdx, c) == DataBlk.TYPE_INT); // Check that we have an output object if (cblk == null) { cblk = new CBlkWTDataInt(); } // Cache input float code-block infblk = this.infblk; // Get data to quantize. When quantizing int data 'cblk' is used to // get the data to quantize and to return the quantized data as well, // that's why 'getNextCodeBlock()' is used. This can not be done when // quantizing float data because of the different data types, that's // why 'getNextInternCodeBlock()' is used in that case. if (intq) { // Source data is int cblk = src.getNextCodeBlock(c, cblk); if (cblk == null) { return null; // No more code-blocks in current tile for comp. } // Input and output arrays are the same (for "in place" quant.) outarr = (int[]) cblk.Data; } else { // Source data is float // Can not use 'cblk' to get float data, use 'infblk' infblk = (CBlkWTDataFloat) src.getNextInternCodeBlock(c, infblk); if (infblk == null) { // Release buffer from infblk: this enables to garbage collect // the big buffer when we are done with last code-block of // component. this.infblk.Data = null; return null; // No more code-blocks in current tile for comp. } this.infblk = infblk; // Save local cache infarr = (float[]) infblk.Data; // Get output data array and check that there is memory to put the // quantized coeffs in outarr = (int[]) cblk.Data; if (outarr == null || outarr.Length < infblk.w * infblk.h) { outarr = new int[infblk.w * infblk.h]; cblk.Data = outarr; } cblk.m = infblk.m; cblk.n = infblk.n; cblk.sb = infblk.sb; cblk.ulx = infblk.ulx; cblk.uly = infblk.uly; cblk.w = infblk.w; cblk.h = infblk.h; cblk.wmseScaling = infblk.wmseScaling; cblk.offset = 0; cblk.scanw = cblk.w; } // Cache width, height and subband of code-block w = cblk.w; h = cblk.h; sb = cblk.sb; if (isReversible(tIdx, c)) { // Reversible only for int data cblk.magbits = g - 1 + src.getNomRangeBits(c) + sb.anGainExp; shiftBits = 31 - cblk.magbits; // Update the convertFactor field cblk.convertFactor = (1 << shiftBits); // Since we used getNextCodeBlock() to get the int data then // 'offset' is 0 and 'scanw' is the width of the code-block The // input and output arrays are the same (i.e. "in place") for (j = w * h - 1; j >= 0; j--) { tmp = (outarr[j] << shiftBits); outarr[j] = ((tmp < 0)?(1 << 31) | (- tmp):tmp); } } else { // Non-reversible, use step size //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Float.floatValue' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" float baseStep = (float) ((System.Single) qsss.getTileCompVal(tIdx, c)); // Calculate magnitude bits and quantization step size if (isDerived(tIdx, 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'" cblk.magbits = g - 1 + sb.level - (int) System.Math.Floor(System.Math.Log(baseStep) / log2); stepUDR = baseStep / (1 << sb.level); } 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'" cblk.magbits = g - 1 - (int) System.Math.Floor(System.Math.Log(baseStep / (sb.l2Norm * (1 << sb.anGainExp))) / log2); stepUDR = baseStep / (sb.l2Norm * (1 << sb.anGainExp)); } shiftBits = 31 - cblk.magbits; // Calculate step that decoder will get and use that one. stepUDR = convertFromExpMantissa(convertToExpMantissa(stepUDR)); invstep = 1.0f / ((1L << (src.getNomRangeBits(c) + sb.anGainExp)) * stepUDR); // Normalize to magnitude bits (output fractional point) invstep *= (1 << (shiftBits - src.getFixedPoint(c))); // Update convertFactor and stepSize fields cblk.convertFactor = invstep; cblk.stepSize = ((1L << (src.getNomRangeBits(c) + sb.anGainExp)) * stepUDR); if (intq) { // Quantizing int data // Since we used getNextCodeBlock() to get the int data then // 'offset' is 0 and 'scanw' is the width of the code-block // The input and output arrays are the same (i.e. "in place") for (j = w * h - 1; j >= 0; j--) { //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'" tmp = (int) (outarr[j] * invstep); outarr[j] = ((tmp < 0)?(1 << 31) | (- tmp):tmp); } } else { // Quantizing float data for (j = w * h - 1, k = infblk.offset + (h - 1) * infblk.scanw + w - 1, jmin = w * (h - 1); j >= 0; jmin -= w) { for (; j >= jmin; k--, j--) { //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'" tmp = (int) (infarr[k] * invstep); outarr[j] = ((tmp < 0)?(1 << 31) | (- tmp):tmp); } // Jump to beggining of previous line in input k -= (infblk.scanw - w); } } } // Return the quantized code-block return cblk; }
/// <summary> Returns the next code-block in the current tile for the specified /// component, as a copy (see below). 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 always a copy of the /// data. Therfore it can be modified "in place" without any problems after /// being returned. The 'offset' of the returned data is 0, and the 'scanw' /// is the same as the code-block width. 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. 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 override CBlkWTData getNextCodeBlock(int c, CBlkWTData cblk) { return getNextInternCodeBlock(c, cblk); }
/// <summary> Returns the next code-block in the current tile for the specified /// component, as a copy (see below). 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 always a copy of the internal /// data of this object, and it can be modified "in place" without /// any problems after being returned. The 'offset' of the returned data is /// 0, and the 'scanw' is the same as the code-block width. 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. 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 'c', or /// null if all code-blocks for the current tile have been returned. /// /// </returns> /// <seealso cref="CBlkWTData"> /// /// </seealso> public override CBlkWTData getNextCodeBlock(int c, CBlkWTData cblk) { // We can not directly use getNextInternCodeBlock() since that returns // a reference to the internal buffer, we have to copy that data int j, k; int w; System.Object dst_data; // a int[] or float[] object int[] dst_data_int; float[] dst_data_float; System.Object src_data; // a int[] or float[] object intData = (filters.getWTDataType(tIdx, c) == DataBlk.TYPE_INT); dst_data = null; // Cache the data array, if any if (cblk != null) { dst_data = cblk.Data; } // Get the next code-block cblk = getNextInternCodeBlock(c, cblk); if (cblk == null) { return(null); // No more code-blocks in current tile for component // c } // Ensure size of output buffer if (intData) { // int data dst_data_int = (int[])dst_data; if (dst_data_int == null || dst_data_int.Length < cblk.w * cblk.h) { dst_data = new int[cblk.w * cblk.h]; } } else { // float data dst_data_float = (float[])dst_data; if (dst_data_float == null || dst_data_float.Length < cblk.w * cblk.h) { dst_data = new float[cblk.w * cblk.h]; } } // Copy data line by line src_data = cblk.Data; w = cblk.w; for (j = w * (cblk.h - 1), k = cblk.offset + (cblk.h - 1) * cblk.scanw; j >= 0; j -= w, k -= cblk.scanw) { // CONVERSION PROBLEM? Array.Copy((System.Array)src_data, k, (System.Array)dst_data, j, w); } cblk.Data = dst_data; cblk.offset = 0; cblk.scanw = w; return(cblk); }
/// <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 abstract CSJ2K.j2k.wavelet.analysis.CBlkWTData getNextCodeBlock(int param1, CSJ2K.j2k.wavelet.analysis.CBlkWTData param2);