public void ReadPixelData(MifHeader header, byte[] buffer, bool isFirstFrame) { if (header.Flags.HasFlag(MifFlags.Compressed)) { // Read mode. MifCompressionMode mode = (MifCompressionMode)ReadByte(); switch (mode) { case MifCompressionMode.None: // 0: not compressed ReadRawPixelData(buffer); break; case MifCompressionMode.Delta: // 1: delta encoded if (isFirstFrame) throw new InvalidDataException("The first frame must not be delta encoded."); ReadDeltaEncodedPixelData(buffer); break; default: throw new NotSupportedException(string.Format( "Compression mode {0} is not supported.", mode)); } } else { ReadRawPixelData(buffer); } }
/// <summary> /// Read a frame. /// </summary> public MifFrame ReadFrame(MifHeader header, MifFrame prevFrame) { MifFrame frame = new MifFrame(); // Read Delay field if present. if (header.Flags.HasFlag(MifFlags.HasDelay)) { frame.delay = ReadInt32(); } // Read primary channels. frame.colorData = new byte[2 * header.ImageWidth * header.ImageHeight]; if (prevFrame != null && prevFrame.colorData != null) { prevFrame.colorData.CopyTo(frame.colorData, 0); ReadPixelData(header, frame.colorData, false); } else { ReadPixelData(header, frame.colorData, true); } // Read alpha channel if present. if (header.Flags.HasFlag(MifFlags.HasAlpha)) { frame.alphaData = new byte[header.ImageWidth * header.ImageHeight]; if (prevFrame != null && prevFrame.alphaData != null) { prevFrame.alphaData.CopyTo(frame.alphaData, 0); ReadPixelData(header, frame.alphaData, false); } else { ReadPixelData(header, frame.alphaData, true); } } return frame; }
/// <summary> /// Reads MIF file header. This method does not validate the fields /// of the header. /// </summary> public MifHeader ReadHeader() { MifHeader header = new MifHeader(); header.Version = ReadInt32(); header.ImageWidth = ReadInt32(); header.ImageHeight = ReadInt32(); header.Flags = (MifFlags)ReadInt32(); header.FrameCount = ReadInt32(); return header; }
/// <summary> /// Creates a MIF image from the specified stream. /// </summary> /// <param name="stream">The stream to read the image from. This /// stream is automatically disposed by the constructor, whether the /// constructor succeeds or throws an exception. If this is not what /// you want, create an extra layer on top of the stream to ignore /// Dispose(). /// </param> /// <exception cref="InvalidDataException">The stream does not /// contain a valid MIF image format.</exception> public MifImage(Stream stream) { // Create a MIF reader on the stream. using (stream) using (MifReader reader = new MifReader(stream)) { // Reads and validates the MIF file header. this.header = reader.ReadHeader(); if (!header.Flags.HasFlag(MifFlags.HasImage)) { throw new InvalidDataException("The stream does not contain an image."); } if (header.ImageWidth <= 0) { throw new InvalidDataException("ImageWidth field must be positive."); } if (header.ImageHeight <= 0) { throw new InvalidDataException("ImageHeight field must be positive."); } if (header.FrameCount <= 0) { throw new InvalidDataException("FrameCount field must be positive."); } // TODO: avoid DoS attach if FrameCount, Width, or Height // are very large. // Read all frames at once so that we can close the stream // and navigate through the frames easily later. frameDiff = new MifFrameDiff[header.FrameCount]; delays = new int[header.FrameCount]; MifFrame firstFrame = null, prevFrame = null; bool alphaPresent = false; for (int i = 0; i < header.FrameCount; i++) { // Read the next frame in uncompressed format. MifFrame thisFrame = reader.ReadFrame(header, prevFrame); delays[i] = thisFrame.delay; if (i == 0) firstFrame = thisFrame; #if TEST_ZIP_COMPRESSION using (MemoryStream input=new MemoryStream(thisFrame.colorData)) using (MemoryStream output=new MemoryStream()) using (DeflateStream zip = new DeflateStream(output, CompressionMode.Compress)) { input.CopyTo(zip); } using (MemoryStream input = new MemoryStream(thisFrame.alphaData)) using (MemoryStream output = new MemoryStream()) using (DeflateStream zip = new DeflateStream(output, CompressionMode.Compress)) { input.CopyTo(zip); } #endif // Check whether this frame contains non-opaque alpha. if (!alphaPresent && thisFrame.alphaData != null) { alphaPresent = thisFrame.alphaData.Any(a => (a < 0x20)); } // Store the difference of thisFrame from prevFrame using // delta encoding. if (i > 0) { frameDiff[i] = new MifFrameDiff(prevFrame, thisFrame); } prevFrame = thisFrame; } // Delta-encode the first frame from the last frame. if (header.FrameCount > 1) { frameDiff[0] = new MifFrameDiff(prevFrame, firstFrame); } // If no frame contains a non-opaque alpha, then we don't need // to store the alpha channel at all. In this case, the bitmap // is 16 bpp. Otherwise, we create a 32-bpp bitmap. #if true if (!alphaPresent) { int stride = (header.ImageWidth * 2 + 3) / 4 * 4; bmpBuffer = new int[stride / 4 * header.ImageHeight]; bmpBufferHandle = GCHandle.Alloc(bmpBuffer, GCHandleType.Pinned); bitmap = new Bitmap( header.ImageWidth, header.ImageHeight, stride, PixelFormat.Format16bppRgb565, bmpBufferHandle.AddrOfPinnedObject()); colorBuffer = new ArrayPixelBuffer<int>( bmpBuffer, PixelFormat.Format16bppRgb565, stride, header.ImageWidth * 2); alphaBuffer = null; } else // 32-bpp with alpha channel #endif { bmpBuffer = new int[header.ImageWidth * header.ImageHeight]; bmpBufferHandle = GCHandle.Alloc(bmpBuffer, GCHandleType.Pinned); bitmap = new Bitmap( header.ImageWidth, header.ImageHeight, header.ImageWidth * 4, PixelFormat.Format32bppArgb, bmpBufferHandle.AddrOfPinnedObject()); colorBuffer = new MifColorBuffer32(bmpBuffer); alphaBuffer = new MifAlphaBuffer32(bmpBuffer); } // Render the first frame in the bitmap buffer. this.activeIndex = 0; firstFrame.UpdateBitmap(colorBuffer, alphaBuffer); // If there's only one frame, we don't need frames[] array. if (frameDiff.Length == 1) { frameDiff = null; } // If there's no alpha, we don't need to store alpha diff, // although this won't really save much memory as we're // run-length encoded. if (!alphaPresent && frameDiff != null) { foreach (MifFrameDiff f in frameDiff) f.RemoveAlpha(); } } }