private bool IsDuplicate(Bitmap32Bits bm, Dictionary <int, int> duplicateHashToIndex, int sourceIndex, out int duplicateIndex) { int x = ItoX(sourceIndex); int y = ItoY(sourceIndex); int width = Math.Min(mBlockSize, bm.Width - x); int height = Math.Min(mBlockSize, bm.Height - y); int hash = bm.HashInt(x, y, width, height); if (duplicateHashToIndex.ContainsKey(hash)) { // Possibly a match, but needs verification just in case there is a hash collison duplicateIndex = duplicateHashToIndex[hash]; if (bm.IsMatch(x, y, width, height, bm, ItoX(duplicateIndex), ItoY(duplicateIndex))) { return(true); } // Hash collision. This is very rare. mHashCollisions++; duplicateIndex = 0; return(false); } // First time this block has been seen duplicateHashToIndex[hash] = sourceIndex; duplicateIndex = 0; return(false); }
/// <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; }