/// <summary>
 /// Release resources for garbage collection
 /// </summary>
 public void CloseEncoder()
 {
     _YEncoder  = null;
     _CbEncoder = null;
     _CrEncoder = null;
     Close();
 }
        /// <summary>
        /// Encodes one data chunk into output stream. Settings parameter controls
        /// how much data is generated.The chunk data is written to Stream
        /// with no IFF header.  Successive calls to EncodeChunk encode
        /// successive chunks.You must call CloseCodec after encoding the last
        /// chunk of a file.
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public int EncodeChunk(Stream stream, InterWaveEncoderSettings settings)
        {
            // Check
            if (settings.Slices == 0 && settings.Bytes == 0 && settings.Decibels == 0)
            {
                throw new DjvuArgumentException("Encoder needs stop condition", nameof(settings));
            }

            if (_YMap == null)
            {
                throw new DjvuInvalidOperationException($"Cannot encode! Target encoder map is null {nameof(_YMap)}");
            }

            // Open
            if (_YEncoder == null)
            {
                _CSlices  = _CSerial = _CBytes = 0;
                _YEncoder = new InterWaveEncoder(_YMap);
                if (_CrMap != null && _CbMap != null)
                {
                    _CbEncoder = new InterWaveEncoder(_CbMap);
                    _CrEncoder = new InterWaveEncoder(_CrMap);
                }
            }

            // Prepare zcodec slices
            int flag    = 1;
            int nslices = 0;

            using (MemoryStream coderStream = new MemoryStream())
            {
                float   estdb = -1.0f;
                ZPCodec zp    = new ZPCodec(coderStream, true, true);

                while (flag != 0)
                {
                    if (settings.Decibels > 0 && estdb >= settings.Decibels)
                    {
                        break;
                    }

                    if (settings.Bytes > 0 && coderStream.Position + _CBytes >= settings.Bytes)
                    {
                        break;
                    }

                    if (settings.Slices > 0 && nslices + _CSlices >= settings.Slices)
                    {
                        break;
                    }

                    flag = _YEncoder.CodeSlice(zp);

                    if (flag != 0 && settings.Decibels > 0)
                    {
                        if (_YEncoder._CurrentBand == 0 || estdb >= settings.Decibels - DecibelPrune)
                        {
                            estdb = _YEncoder.EstimateDecibel(_dBFrac);
                        }
                    }

                    if (_CrEncoder != null && _CbEncoder != null && _CSlices + nslices >= _CrCbDelay)
                    {
                        flag |= _CbEncoder.CodeSlice(zp);
                        flag |= _CrEncoder.CodeSlice(zp);
                    }

                    nslices++;
                }

                zp.Flush();

                // Write primary header
                stream.WriteByte((byte)_CSerial);
                stream.WriteByte((byte)(nslices));

                // Write extended header
                if (_CSerial == 0)
                {
                    byte major = InterWaveCodec.MajorVersion;
                    if (!(_CrMap != null && _CbMap != null))
                    {
                        major |= 0x80;
                    }

                    stream.WriteByte(major);
                    stream.WriteByte(InterWaveCodec.MinorVersion);

                    byte xhi       = (byte)((_YMap.Width >> 8) & 0xff);
                    byte xlo       = (byte)(_YMap.Width & 0xff);
                    byte yhi       = (byte)((_YMap.Height >> 8) & 0xff);
                    byte ylo       = (byte)(_YMap.Height & 0xff);
                    byte crCbDelay = (byte)(_CrCbHalf ? 0x00 : 0x80);
                    crCbDelay |= (byte)(_CrCbDelay >= 0 ? _CrCbDelay : 0x00);

                    stream.WriteByte(xhi);
                    stream.WriteByte(xlo);
                    stream.WriteByte(yhi);
                    stream.WriteByte(ylo);
                    stream.WriteByte(crCbDelay);
                }

                byte[] buffer = coderStream.GetBuffer();
                stream.Write(buffer, 0, (int)coderStream.Position);
                _CBytes += (int)coderStream.Position;
            }

            _CSlices += nslices;
            _CSerial += 1;
            return(flag);
        }