private float optimizeBitstreamLayer(int layerIdx, float fmaxt, int maxBytes, int prevBytes)
{
int nt; // The total number of tiles
int nc; // The total number of components
int numLvls; // The total number of resolution levels
int actualBytes; // Actual number of bytes for a layer
float fmint; // Minimum of the current threshold interval
float ft; // Current threshold
SubbandAn sb; // Current subband
BitOutputBuffer hBuff; // The packet head buffer
byte[] bBuff; // The packet body buffer
int sidx; // The index in the summary table
bool sopUsed; // Should SOP markers be used ?
bool ephUsed; // Should EPH markers be used ?
//int precinctIdx; // Precinct index for current packet
int nPrec; // Number of precincts in the current resolution level
// Save the packet encoder state
pktEnc.save();
nt = src.getNumTiles();
nc = src.NumComps;
hBuff = null;
bBuff = null;
// Estimate the minimum slope to start with from the summary
// information in 'RDSlopesRates'. This is a real minimum since it
// does not include the packet head overhead, which is always
// non-zero.
// Look for the summary entry that gives 'maxBytes' or more data
for (sidx = RD_SUMMARY_SIZE - 1; sidx > 0; sidx--)
{
if (RDSlopesRates[sidx] >= maxBytes)
{
break;
}
}
// Get the corresponding minimum slope
fmint = getSlopeFromSIndex(sidx);
// Ensure that it is smaller the maximum slope
if (fmint >= fmaxt)
{
sidx--;
fmint = getSlopeFromSIndex(sidx);
}
// If we are using the last entry of the summary, then that
// corresponds to all the data, Thus, set the minimum slope to 0.
if (sidx <= 0)
fmint = 0;
// We look for the best threshold 'ft', which is the lowest threshold
// that generates no more than 'maxBytes' code bytes.
// The search is done iteratively using a binary split algorithm. We
// start with 'fmaxt' as the maximum possible threshold, and 'fmint'
// as the minimum threshold. The threshold 'ft' is calculated as the
// middle point of 'fmaxt'-'fmint' interval. The 'fmaxt' or 'fmint'
// bounds are moved according to the number of bytes obtained from a
// simulation, where 'ft' is used as the threshold.
// We stop whenever the interval is sufficiently small, and thus
// enough precision is achieved.
// Initialize threshold as the middle point of the interval.
ft = (fmaxt + fmint) / 2f;
// If 'ft' reaches 'fmint' it means that 'fmaxt' and 'fmint' are so
// close that the average is 'fmint', due to rounding. Force it to
// 'fmaxt' instead, since 'fmint' is normally an exclusive lower
// bound.
if (ft <= fmint)
ft = fmaxt;
do
{
// Get the number of bytes used by this layer, if 'ft' is the
// threshold, by simulation.
actualBytes = prevBytes;
src.setTile(0, 0);
for (int t = 0; t < nt; t++)
{
for (int c = 0; c < nc; c++)
{
// set boolean sopUsed here (SOP markers)
sopUsed = ((System.String) encSpec.sops.getTileDef(t)).ToUpper().Equals("on".ToUpper());
// set boolean ephUsed here (EPH markers)
ephUsed = ((System.String) encSpec.ephs.getTileDef(t)).ToUpper().Equals("on".ToUpper());
// Get LL subband
sb = (SubbandAn) src.getAnSubbandTree(t, c);
numLvls = sb.resLvl + 1;
sb = (SubbandAn) sb.getSubbandByIdx(0, 0);
//loop on resolution levels
for (int r = 0; r < numLvls; r++)
{
nPrec = numPrec[t][c][r].x * numPrec[t][c][r].y;
for (int p = 0; p < nPrec; p++)
{
findTruncIndices(layerIdx, c, r, t, sb, ft, p);
hBuff = pktEnc.encodePacket(layerIdx + 1, c, r, t, cblks[t][c][r], truncIdxs[t][layerIdx][c][r], hBuff, bBuff, p);
if (pktEnc.PacketWritable)
{
bBuff = pktEnc.LastBodyBuf;
actualBytes += bsWriter.writePacketHead(hBuff.Buffer, hBuff.Length, true, sopUsed, ephUsed);
actualBytes += bsWriter.writePacketBody(bBuff, pktEnc.LastBodyLen, true, pktEnc.ROIinPkt, pktEnc.ROILen);
}
} // end loop on precincts
sb = sb.parentband;
} // End loop on resolution levels
} // End loop on components
} // End loop on tiles
// Move the interval bounds according to simulation result
if (actualBytes > maxBytes)
{
// 'ft' is too low and generates too many bytes, make it the
// new minimum.
fmint = ft;
}
else
{
// 'ft' is too high and does not generate as many bytes as we
// are allowed too, make it the new maximum.
fmaxt = ft;
}
// Update 'ft' for the new iteration as the middle point of the
// new interval.
ft = (fmaxt + fmint) / 2f;
// If 'ft' reaches 'fmint' it means that 'fmaxt' and 'fmint' are
// so close that the average is 'fmint', due to rounding. Force it
// to 'fmaxt' instead, since 'fmint' is normally an exclusive
// lower bound.
if (ft <= fmint)
ft = fmaxt;
// Restore previous packet encoder state
pktEnc.restore();
// We continue to iterate, until the threshold reaches the upper
// limit of the interval, within a FLOAT_REL_PRECISION relative
// tolerance, or a FLOAT_ABS_PRECISION absolute tolerance. This is
// the sign that the interval is sufficiently small.
}
while (ft < fmaxt * (1f - FLOAT_REL_PRECISION) && ft < (fmaxt - FLOAT_ABS_PRECISION));
// If we have a threshold which is close to 0, set it to 0 so that
// everything is taken into the layer. This is to avoid not sending
// some least significant bit-planes in the lossless case. We use the
// FLOAT_ABS_PRECISION value as a measure of "close" to 0.
if (ft <= FLOAT_ABS_PRECISION)
{
ft = 0f;
}
else
{
// Otherwise make the threshold 'fmaxt', just to be sure that we
// will not send more bytes than allowed.
ft = fmaxt;
}
return ft;
}