예제 #1
0
        public void Write(FrameCompressor.Block block)
        {
            int sourceX = (block.Index % mSourceBlocksX) * mBlockSize;
            int sourceY = (block.Index / mSourceBlocksX) * mBlockSize;

            if (block.Index != mSourceIndex)
            {
                // Skip clear blocks
                if (mSkipsInARow == 0)
                {
                    Flush();
                }
                mSkipsInARow += block.Index - mSourceIndex;
                mSourceIndex  = block.Index;
            }
            if (block.Score.HasFlag(FrameCompressor.Score.Solid))
            {
                // Set solid color if different from previous color
                if (mSource[sourceX, sourceY] != mSolidColor)
                {
                    Flush();
                    mSolidColor = mSource[sourceX, sourceY];
                    mSb.Append('s');
                    mSb.Append(mSolidColor & 0xFFFFFF);
                }
                // Cache solid color
                if (mSolidsInARow == 0)
                {
                    Flush();
                }
                mSolidsInARow++;
                mSourceIndex++;
                return;
            }
            if (block.Score.HasFlag(FrameCompressor.Score.Duplicate))
            {
                // Set duplicate cursor position if different from previous location
                int duplicateCursor = mDuplicatedSourceToTarget[mDuplicates[block.Index]];
                if (duplicateCursor < 0)
                {
                    // This duplicate was not written to the target
                    return;
                }
                if (duplicateCursor != mDuplicateCursor)
                {
                    Flush();
                    mDuplicateCursor = duplicateCursor;
                    mSb.Append('d');
                    mSb.Append(mDuplicateCursor);
                }
                // Cache duplicate block
                if (mDuplicatesInARow == 0)
                {
                    Flush();
                }
                mDuplicatesInARow++;
                mSourceIndex++;
                return;
            }
            // Create duplicate source to target map as we go
            if (mDuplicatedSourceToTarget.ContainsKey(mSourceIndex))
            {
                mDuplicatedSourceToTarget[mSourceIndex] = mTargetIndex;
            }
            // Cache copy to target
            if (mCopiesInARow == 0)
            {
                Flush();
            }
            mCopiesInARow++;
            mSourceIndex++;
            int targetX = (mTargetIndex % mTargetBlocksX) * mBlockSize;
            int targetY = (mTargetIndex / mTargetBlocksX) * mBlockSize;

            mTargetIndex++;


            // Copy source to target (source can be non block size multiple, but not target)
            mSource.Copy(sourceX, sourceY, Math.Min(mBlockSize, mSource.Width - sourceX),
                         Math.Min(mBlockSize, mSource.Height - sourceY), mTarget, targetX, targetY);
        }
예제 #2
0
        /// <summary>
        /// Score a frame, creating a list of blocks
        /// </summary>
        private void ScoreFrame(Bitmap32Bits frame,
                                bool disableDelta,
                                out List <Block> blocks,
                                out Dictionary <int, int> duplicates,
                                out int jpgCount,
                                out int pngCount)
        {
            blocks     = new List <Block>();
            duplicates = new Dictionary <int, int>();
            jpgCount   = 0;
            pngCount   = 0;

            var sourceIndex          = -1;
            var duplicateHashToIndex = new Dictionary <int, int>();

            for (int y = 0; y < frame.Height; y += mBlockSize)
            {
                for (int x = 0; x < frame.Width; x += mBlockSize)
                {
                    sourceIndex++;
                    Debug.Assert(ItoX(sourceIndex) == x);
                    Debug.Assert(ItoY(sourceIndex) == y);

                    // Skip clear blocks
                    int width  = Math.Min(mBlockSize, frame.Width - x);
                    int height = Math.Min(mBlockSize, frame.Height - y);
                    if (!disableDelta && frame.IsMatch(x, y, width, height, mBackground, x, y))
                    {
                        continue; // Nothing changed, skip it
                    }
                    frame.Copy(x, y, width, height, mBackground, x, y);

                    // Check for solid blocks
                    if (frame.IsSolid(x, y, width, height))
                    {
                        blocks.Add(new Block(sourceIndex, Score.Solid));
                        continue;
                    }
                    // Check for duplicates
                    int duplicateIndex;
                    if (IsDuplicate(frame, duplicateHashToIndex, sourceIndex, out duplicateIndex))
                    {
                        blocks.Add(new Block(sourceIndex, Score.Duplicate));
                        duplicates[sourceIndex] = duplicateIndex;
                        continue;
                    }
                    // Compression: SmartPNG, JPG, or PNG
                    bool usePng = mCompression == CompressionType.Png;
                    var  score  = Score.None;
                    if (mCompression == CompressionType.SmartPng && width >= 4 && height >= 4)
                    {
                        // SmartPng - Check to see if the block would compress well with PNG
                        var rleCount       = frame.RleCount(x, y, width, height);
                        var rleTotal       = (height - 1) * (width - 1);
                        var rleCompression = rleCount / (float)rleTotal;
                        score = (Score)((int)(rleCompression * 9.99) << (int)Score.ScoreShift) & Score.ScoreMask;
                        if (rleCompression > mPngCompressionThreshold)
                        {
                            if (!frame.LowFrequency(x, y, width, height))
                            {
                                usePng = true; // Good compression, not low frequency
                            }
                            else
                            {
                                score |= Score.LowFrequency;
                            }
                        }
                    }
                    blocks.Add(new Block(sourceIndex, score | (usePng ? Score.Png : Score.Jpg)));
                    if (usePng)
                    {
                        pngCount++;
                    }
                    else
                    {
                        jpgCount++;
                    }
                }
            }
            mDuplicates = duplicates.Count;
        }