/// <summary> /// Function to read uncompressed TGA scanline data. /// </summary> /// <param name="reader">The reader used to read in the data from the source stream.</param> /// <param name="width">Image width.</param> /// <param name="dest">Destination buffer pointner</param> /// <param name="format">Format of the destination buffer.</param> /// <param name="conversionFlags">Flags used for conversion.</param> private unsafe bool ReadCompressed(GorgonBinaryReader reader, int width, byte *dest, BufferFormat format, TGAConversionFlags conversionFlags) { bool setOpaque = true; bool flipHorizontal = (conversionFlags & TGAConversionFlags.InvertX) == TGAConversionFlags.InvertX; bool expand = (conversionFlags & TGAConversionFlags.Expand) == TGAConversionFlags.Expand; for (int x = 0; x < width;) { if (reader.BaseStream.Position >= reader.BaseStream.Length) { throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec)); } byte rleBlock = reader.ReadByte(); int size = (rleBlock & 0x7F) + 1; if ((rleBlock & 0x80) != 0) { if (!DecodeRleEncodedRun(reader, ref dest, ref x, size, width, expand, flipHorizontal, format)) { setOpaque = false; } continue; } if (!DecodeUncompressedRun(reader, ref dest, ref x, size, width, expand, flipHorizontal, format)) { setOpaque = false; } } return(setOpaque); }
/// <summary> /// Function to determine if a physical file system can be read by this provider. /// </summary> /// <param name="physicalPath">Path to the packed file containing the file system.</param> /// <returns><b>true</b> if the provider can read the packed file, <b>false</b> if not.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="physicalPath"/> parameter is <b>null</b>.</exception> /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="physicalPath"/> parameter is an empty string.</exception> /// <remarks> /// <para> /// This will test a physical file system (e.g. a Zip file) to see if the provider can open it or not. If used with a directory on an operating system file system, this method should always return /// <b>false</b>. /// </para> /// <para> /// When used with a <see cref="IGorgonFileSystemProvider"/> that supports a non operating system based physical file system, such as the <see cref="GorgonFileSystemRamDiskProvider"/>, then this /// method should compare the <paramref name="physicalPath"/> with its <see cref="IGorgonFileSystemProvider.Prefix"/> to ensure that the <see cref="IGorgonFileSystem"/> requesting the provider is using the correct provider. /// </para> /// <para> /// Implementors of a <see cref="GorgonFileSystemProvider"/> should override this method to determine if a packed file can be read by reading the header of the file specified in <paramref name="physicalPath"/>. /// </para> /// </remarks> protected override bool OnCanReadFile(string physicalPath) { string header; using (var reader = new GorgonBinaryReader(File.Open(physicalPath, FileMode.Open, FileAccess.Read, FileShare.Read))) { // If the length of the stream is less or equal to the header size, it's unlikely that we can read this file. if (reader.BaseStream.Length <= GorPackHeader.Length) { return(false); } reader.ReadByte(); header = new string(reader.ReadChars(GorPackHeader.Length)); } return(string.Equals(header, GorPackHeader)); }
/// <summary> /// Function to determine if a file can be read by this provider. /// </summary> /// <param name="physicalPath">Path to the file containing the file system.</param> /// <returns> /// TRUE if the provider can read the packed file, FALSE if not. /// </returns> /// <remarks>This method is applicable to packed files only. /// <para>Implementors must use this method to determine if a packed file can be read by reading the header of the file.</para> /// </remarks> /// <exception cref="System.ArgumentNullException">Thrown when the <paramref name="physicalPath"/> parameter is NULL (Nothing in VB.Net).</exception> /// <exception cref="System.ArgumentException">Thrown when the <paramref name="physicalPath"/> parameter is an empty string.</exception> public override bool CanReadFile(string physicalPath) { string header; if (physicalPath == null) { throw new ArgumentNullException("physicalPath"); } if (string.IsNullOrWhiteSpace(physicalPath)) { throw new ArgumentException(Resources.GORFS_PARAMETER_MUST_NOT_BE_EMPTY, "physicalPath"); } using (var reader = new GorgonBinaryReader(File.Open(physicalPath, FileMode.Open, FileAccess.Read, FileShare.Read), false)) { reader.ReadByte(); header = new string(reader.ReadChars(GorgonGorPackPlugIn.GorPackHeader.Length)); } return(string.Equals(header, GorgonGorPackPlugIn.GorPackHeader)); }
/// <summary> /// Function to decode an uncompressed run of pixel data. /// </summary> /// <param name="reader">The reader used to read the data from the stream.</param> /// <param name="dest">The destination buffer pointer.</param> /// <param name="x">The current horizontal position in the scanline.</param> /// <param name="runLength">The size of the run, in pixels.</param> /// <param name="width">The total width of the run.</param> /// <param name="expand"><b>true</b> to expand a 24bpp scanline to 32bpp, or <b>false</b> if no expansion is needed.</param> /// <param name="flipHorizontal"><b>true</b> to decode the pixels from right to left, or <b>false</b> to decode from left to right.</param> /// <param name="format">The pixel format.</param> /// <returns><b>true</b> if the run contains entirely transparent pixels, or <b>false</b> if not.</returns> private unsafe bool DecodeUncompressedRun(GorgonBinaryReader reader, ref byte *dest, ref int x, int runLength, int width, bool expand, bool flipHorizontal, BufferFormat format) { bool result = true; switch (format) { case BufferFormat.R8_UNorm: for (; runLength > 0; --runLength, ++x) { if (x >= width) { throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec)); } if (!flipHorizontal) { *(dest++) = reader.ReadByte(); } else { *(dest--) = reader.ReadByte(); } } break; case BufferFormat.B5G5R5A1_UNorm: { ushort *destPtr = (ushort *)dest; for (; runLength > 0; runLength--, ++x) { if (x >= width) { throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec)); } ushort pixel = reader.ReadUInt16(); if ((pixel & 0x8000) != 0) { result = false; } if (!flipHorizontal) { *(destPtr++) = pixel; } else { *(destPtr--) = pixel; } } // Send the updated destination address back to the calling function. // This is kind of ugly, but too lazy to make it nice. dest = (byte *)destPtr; } return(result); case BufferFormat.R8G8B8A8_UNorm: { uint *destPtr = (uint *)dest; for (; runLength > 0; --runLength, ++x) { if (x >= width) { throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec)); } uint pixel; if (expand) { pixel = (uint)((reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte() | 0xFF000000); result = false; } else { pixel = reader.ReadUInt32(); if ((pixel & 0xFF000000) != 0) { result = false; } } if (!flipHorizontal) { *(destPtr++) = pixel; } else { *(destPtr--) = pixel; } } // Send the updated destination address back to the calling function. // This is kind of ugly, but too lazy to make it nice. dest = (byte *)destPtr; } return(result); } return(false); }
/// <summary> /// Function to decode an uncompressed run of pixel data. /// </summary> /// <param name="reader">The reader used to read the data in the stream.</param> /// <param name="dest">The destination buffer pointer.</param> /// <param name="x">The current horizontal position in the scanline.</param> /// <param name="runLength">The size of the run, in pixels.</param> /// <param name="width">The total width of the run.</param> /// <param name="expand"><b>true</b> to expand a 24bpp scanline to 32bpp, or <b>false</b> if no expansion is needed.</param> /// <param name="flipHorizontal"><b>true</b> to decode the pixels from right to left, or <b>false</b> to decode from left to right.</param> /// <param name="format">The pixel format.</param> /// <returns><b>true</b> if the run contains entirely transparent pixels, or <b>false</b> if not.</returns> private unsafe bool DecodeRleEncodedRun(GorgonBinaryReader reader, ref byte *dest, ref int x, int runLength, int width, bool expand, bool flipHorizontal, BufferFormat format) { bool result = true; switch (format) { case BufferFormat.R8_UNorm: for (; runLength > 0; --runLength, ++x) { if (x >= width) { throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec)); } if (!flipHorizontal) { *(dest++) = reader.ReadByte(); } else { *(dest--) = reader.ReadByte(); } } break; case BufferFormat.B5G5R5A1_UNorm: { ushort *destPtr = (ushort *)dest; ushort pixel = reader.ReadUInt16(); if ((pixel & 0x8000) != 0) { result = false; } for (; runLength > 0; runLength--, ++x) { if (x >= width) { throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec)); } if (!flipHorizontal) { *(destPtr++) = pixel; } else { *(destPtr--) = pixel; } } dest = (byte *)destPtr; } return(result); case BufferFormat.R8G8B8A8_UNorm: { uint pixel; // Do expansion. if (expand) { pixel = (uint)((reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte() | 0xFF000000); result = false; } else { pixel = reader.ReadUInt32(); if ((pixel & 0xFF000000) != 0) { result = false; } } uint *destPtr = (uint *)dest; for (; runLength > 0; --runLength, ++x) { if (x >= width) { throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec)); } if (!flipHorizontal) { *(destPtr++) = pixel; } else { *(destPtr--) = pixel; } } dest = (byte *)destPtr; } return(result); } return(false); }
/// <summary> /// Function to read in the TGA header from a stream. /// </summary> /// <param name="reader">The reader used to read the stream containing the data.</param> /// <param name="conversionFlags">Flags for conversion.</param> /// <returns>New image settings.</returns> private static IGorgonImageInfo ReadHeader(GorgonBinaryReader reader, out TGAConversionFlags conversionFlags) { conversionFlags = TGAConversionFlags.None; // Get the header for the file. TgaHeader header = reader.ReadValue <TgaHeader>(); if ((header.ColorMapType != 0) || (header.ColorMapLength != 0) || (header.Width <= 0) || (header.Height <= 0) || ((header.Descriptor & TgaDescriptor.Interleaved2Way) == TgaDescriptor.Interleaved2Way) || ((header.Descriptor & TgaDescriptor.Interleaved4Way) == TgaDescriptor.Interleaved4Way)) { throw new NotSupportedException(Resources.GORIMG_ERR_TGA_TYPE_NOT_SUPPORTED); } BufferFormat pixelFormat = BufferFormat.Unknown; switch (header.ImageType) { case TgaImageType.TrueColor: case TgaImageType.TrueColorRLE: switch (header.BPP) { case 16: pixelFormat = BufferFormat.B5G5R5A1_UNorm; break; case 24: case 32: pixelFormat = BufferFormat.R8G8B8A8_UNorm; if (header.BPP == 24) { conversionFlags |= TGAConversionFlags.Expand; } break; } if (header.ImageType == TgaImageType.TrueColorRLE) { conversionFlags |= TGAConversionFlags.RLE; } break; case TgaImageType.BlackAndWhite: case TgaImageType.BlackAndWhiteRLE: if (header.BPP == 8) { pixelFormat = BufferFormat.R8_UNorm; } else { throw new IOException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, header.ImageType)); } if (header.ImageType == TgaImageType.BlackAndWhiteRLE) { conversionFlags |= TGAConversionFlags.RLE; } break; default: throw new IOException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, header.ImageType)); } var settings = new GorgonImageInfo(ImageType.Image2D, pixelFormat) { MipCount = 1, ArrayCount = 1, Width = header.Width, Height = header.Height }; if ((header.Descriptor & TgaDescriptor.InvertX) == TgaDescriptor.InvertX) { conversionFlags |= TGAConversionFlags.InvertX; } if ((header.Descriptor & TgaDescriptor.InvertY) == TgaDescriptor.InvertY) { conversionFlags |= TGAConversionFlags.InvertY; } if (header.IDLength <= 0) { return(settings); } // Skip these bytes. for (int i = 0; i < header.IDLength; i++) { reader.ReadByte(); } return(settings); }