/// <summary> /// Build a bitmap with any of the given score bits set /// </summary> Frame BuildBitmap(Bitmap32Bits frame, List <Block> blocks, Dictionary <int, int> duplicates, Score score, ImageFormat format) { // Count blocks to write to target int numTargetBlocks = 0; foreach (var block in blocks) { if ((block.Score & score & (Score.Jpg | Score.Png)) != Score.None) { numTargetBlocks++; } } // Generate the bitmap int size = (int)Math.Sqrt(numTargetBlocks) + 1; var bmTarget = new Bitmap(size * mBlockSize, size * mBlockSize, PixelFormat.Format32bppArgb); var bmdTarget = new Bitmap32Bits(bmTarget, ImageLockMode.WriteOnly); var blockWriter = new BlockWriter(frame, bmdTarget, mBlockSize, duplicates); foreach (var block in blocks) { if ((block.Score & score) != Score.None) { blockWriter.Write(block); } } bmdTarget.Dispose(); // Compress frame to stream var targetStream = new MemoryStream(); bmTarget.Save(targetStream, format); bmTarget.Dispose(); return(new Frame(blockWriter.GetDrawString(), targetStream, format)); }
/// <summary> /// Analyze a bitmap, and keep a copy as the new background. If the bitmap is not /// the same size as the previous one, a new comparison is made. You must /// dispose the bitmap when done with it. Returns 1 or 2 frames (PNG/JPG) /// depending on settings /// </summary> public Frame [] Compress(Bitmap frame) { // Generate full frame JPG or PNG (debugging output) if (mOutput == OutputType.FullFrameJpg || mOutput == OutputType.FullFramePng) { var f = GenerateFullFrame((Bitmap)frame, mOutput == OutputType.FullFrameJpg ? ImageFormat.Jpeg : ImageFormat.Png); return(new Frame[] { f }); } // Force full frame analysis if requested bool disableDelta = mForceDisableDelta || mFullFrameAnalysis || Output == OutputType.HideJpg || Output == OutputType.HidePng; mForceDisableDelta = false; // Optionally create background if it doesn't exist, or the size changed if (mBackground == null || mBackground.Width != frame.Width || mBackground.Height != frame.Height) { // New background, do not compare with previous disableDelta = true; if (mBackground != null) { mBackground.Dispose(); } mBackground = new Bitmap32Bits(frame.Width, frame.Height); } // Number of blocks visible (even if partially visible) mBlocksX = (frame.Width + mBlockSize - 1) / mBlockSize; // Score bitmap mFrameIndex++; var frameBits = new Bitmap32Bits(frame, ImageLockMode.ReadOnly); List <Block> blocks; Dictionary <int, int> duplicates; int jpgCount; int pngCount; ScoreFrame(frameBits, disableDelta, out blocks, out duplicates, out jpgCount, out pngCount); // Build the bitmaps (JPG and PNG) Frame[] frames; if (jpgCount == 0 || pngCount == 0) { // Build one frame (both PNG and JPG) frames = new Frame[] { BuildBitmap(frameBits, blocks, duplicates, Score.Solid | Score.Duplicate | Score.Jpg | Score.Png, pngCount == 0 ? ImageFormat.Jpeg : ImageFormat.Png) }; } else { // Build two frames (PNG first, and JPG second) frames = new Frame[] { BuildBitmap(frameBits, blocks, duplicates, Score.Solid | Score.Duplicate | Score.Png, ImageFormat.Png), BuildBitmap(frameBits, blocks, duplicates, Score.Duplicate | Score.Jpg, ImageFormat.Jpeg) }; } frameBits.Dispose(); // Optionally create compression maps and other debug output if (mOutput == OutputType.CompressionMap) { CopyDuplicateAttributesForDebug(blocks, duplicates); var map = GenerateCompressionMap(frame.Width, frame.Height, blocks); var f = GenerateFullFrame(map, ImageFormat.Png); map.Dispose(); return(new Frame[] { f }); } else if (mOutput == OutputType.HideJpg || mOutput == OutputType.HidePng) { CopyDuplicateAttributesForDebug(blocks, duplicates); var map = GenerateHideMap(frame, blocks, mOutput == OutputType.HideJpg ? Score.Jpg : Score.Png); var f = GenerateFullFrame(map, ImageFormat.Jpeg); map.Dispose(); return(new Frame[] { f }); } return(frames); }