BitMiracle.LibTiff.Classic.Internal.LZWCodec.LZWEncode C# (CSharp) Method

LZWEncode() private method

Encode a chunk of pixels.
Uses an open addressing double hashing (no chaining) on the prefix code/next character combination. We do a variant of Knuth's algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime secondary probe. Here, the modular division first probe is gives way to a faster exclusive-or manipulation. Also do block compression with an adaptive reset, whereby the code table is cleared when the compression ratio decreases, but after the table fills. The variable-length output codes are re-sized at this point, and a CODE_CLEAR is generated for the decoder.
private LZWEncode ( byte buffer, int offset, int count, short plane ) : bool
buffer byte
offset int
count int
plane short
return bool
        private bool LZWEncode(byte[] buffer, int offset, int count, short plane)
        {
            Debug.Assert(m_enc_hashtab != null);
            if (m_enc_oldcode == -1 && count > 0)
            {
                // NB: This is safe because it can only happen at the start of a strip where we
                //     know there is space in the data buffer.
                PutNextCode(CODE_CLEAR);
                m_enc_oldcode = buffer[offset];
                offset++;
                count--;
                m_enc_incount++;
            }

            while (count > 0)
            {
                int c = buffer[offset];
                offset++;
                count--;
                m_enc_incount++;
                int fcode = (c << BITS_MAX) + m_enc_oldcode;
                int h = (c << HSHIFT) ^ m_enc_oldcode; // xor hashing

                // Check hash index for an overflow.
                if (h >= HSIZE)
                    h -= HSIZE;

                if (m_enc_hashtab[h].hash == fcode)
                {
                    m_enc_oldcode = m_enc_hashtab[h].code;
                    continue;
                }

                bool hit = false;

                if (m_enc_hashtab[h].hash >= 0)
                {
                    // Primary hash failed, check secondary hash.
                    int disp = HSIZE - h;
                    if (h == 0)
                        disp = 1;
                    do
                    {
                        h -= disp;
                        if (h < 0)
                            h += HSIZE;

                        if (m_enc_hashtab[h].hash == fcode)
                        {
                            m_enc_oldcode = m_enc_hashtab[h].code;
                            hit = true;
                            break;
                        }
                    }
                    while (m_enc_hashtab[h].hash >= 0);
                }

                if (!hit)
                {
                    // New entry, emit code and add to table.
                    // Verify there is space in the buffer for the code and any potential Clear
                    // code that might be emitted below. The value of limit is setup so that there
                    // are at least 4 bytes free - room for 2 codes.
                    if (m_tif.m_rawcp > m_enc_rawlimit)
                    {
                        m_tif.m_rawcc = m_tif.m_rawcp;
                        m_tif.flushData1();
                        m_tif.m_rawcp = 0;
                    }

                    PutNextCode(m_enc_oldcode);
                    m_enc_oldcode = c;
                    m_enc_hashtab[h].code = m_free_ent;
                    m_free_ent++;
                    m_enc_hashtab[h].hash = fcode;
                    if (m_free_ent == CODE_MAX - 1)
                    {
                        // table is full, emit clear code and reset
                        cl_hash();
                        m_enc_ratio = 0;
                        m_enc_incount = 0;
                        m_enc_outcount = 0;
                        m_free_ent = CODE_FIRST;
                        PutNextCode(CODE_CLEAR);
                        m_nbits = BITS_MIN;
                        m_maxcode = CODE_MIN;
                    }
                    else
                    {
                        // If the next entry is going to be too big for the code size, then
                        // increase it, if possible.
                        if (m_free_ent > m_maxcode)
                        {
                            m_nbits++;
                            Debug.Assert(m_nbits <= BITS_MAX);
                            m_maxcode = (short)MAXCODE(m_nbits);
                        }
                        else if (m_enc_incount >= m_enc_checkpoint)
                        {
                            // Check compression ratio and, if things seem to be slipping, clear
                            // the hash table and reset state. The compression ratio is
                            // a 24 + 8-bit fractional number.
                            m_enc_checkpoint = m_enc_incount + CHECK_GAP;

                            int rat;
                            if (m_enc_incount > 0x007fffff)
                            {
                                // NB: shift will overflow
                                rat = m_enc_outcount >> 8;
                                rat = (rat == 0 ? 0x7fffffff : m_enc_incount / rat);
                            }
                            else
                                rat = (m_enc_incount << 8) / m_enc_outcount;

                            if (rat <= m_enc_ratio)
                            {
                                cl_hash();
                                m_enc_ratio = 0;
                                m_enc_incount = 0;
                                m_enc_outcount = 0;
                                m_free_ent = CODE_FIRST;
                                PutNextCode(CODE_CLEAR);
                                m_nbits = BITS_MIN;
                                m_maxcode = CODE_MIN;
                            }
                            else
                                m_enc_ratio = rat;
                        }
                    }
                }
            }

            return true;
        }