/// <summary> /// Consolidate the incoming rows. /// Returns true if the buffer has to be flushed, in which case the properties BufferOutput will be valid /// and can be used by the caller to raise the FrameProducedEvent. /// </summary> public bool Consolidate(byte[] buffer) { if (!enabled) { throw new InvalidOperationException(); } bool flush = false; int bytesPerPixel = ImageFormatHelper.BytesPerPixel(imageDescriptor.Format); int stride = imageDescriptor.Width * bytesPerPixel; // Consolidate the current sub-frame. Buffer.BlockCopy(buffer, 0, bufferCurrent, row * imageDescriptor.Width * bytesPerPixel, imageDescriptor.Width * bytesPerPixel * consolidationHeight); row += consolidationHeight; if (waterfallEnabled && (row % waterfallFlushHeight == 0)) { // Flush the current and old image to output in sliding window mode. // "section" is at 0 after we grabbed the first section of rows. int totalSections = outputHeight / waterfallFlushHeight; // Rows are always added to the bottom, so here we continue this pattern. // The current image is copied at the bottom, and the old image is copied at the top. // We just need to figure out which portion of each image to copy. int bufferCurrentSource = 0; int bufferCurrentLength = (section + 1) * waterfallFlushHeight * stride; int bufferCurrentDestination = (totalSections - (section + 1)) * waterfallFlushHeight * stride; int bufferOldSource = bufferCurrentLength; int bufferOldLength = bufferCurrentDestination; int bufferOldDestination = 0; Buffer.BlockCopy(bufferCurrent, bufferCurrentSource, bufferOutput, bufferCurrentDestination, bufferCurrentLength); Buffer.BlockCopy(bufferOld, bufferOldSource, bufferOutput, bufferOldDestination, bufferOldLength); section = (section + 1) % totalSections; flush = true; } if (row >= outputHeight) { row = 0; section = 0; if (!waterfallEnabled) { flush = true; } // Swap buffers. byte[] pfBufferTemp = bufferOld; bufferOld = bufferCurrent; bufferCurrent = bufferOld; } return(flush); }