/* * Copyright (c) 2006-2014, openmetaverse.org * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.org nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; namespace OpenMetaverse.Imaging { #if !NO_UNSAFE /// /// Capability to load TGAs to Bitmap /// public class LoadTGAClass { struct tgaColorMap { public ushort FirstEntryIndex; public ushort Length; public byte EntrySize; public void Read(System.IO.BinaryReader br) { FirstEntryIndex = br.ReadUInt16(); Length = br.ReadUInt16(); EntrySize = br.ReadByte(); } } struct tgaImageSpec { public ushort XOrigin; public ushort YOrigin; public ushort Width; public ushort Height; public byte PixelDepth; public byte Descriptor; public void Read(System.IO.BinaryReader br) { XOrigin = br.ReadUInt16(); YOrigin = br.ReadUInt16(); Width = br.ReadUInt16(); Height = br.ReadUInt16(); PixelDepth = br.ReadByte(); Descriptor = br.ReadByte(); } public byte AlphaBits { get { return (byte)(Descriptor & 0xF); } set { Descriptor = (byte)((Descriptor & ~0xF) | (value & 0xF)); } } public bool BottomUp { get { return (Descriptor & 0x20) == 0x20; } set { Descriptor = (byte)((Descriptor & ~0x20) | (value ? 0x20 : 0)); } } } struct tgaHeader { public byte IdLength; public byte ColorMapType; public byte ImageType; public tgaColorMap ColorMap; public tgaImageSpec ImageSpec; public void Read(System.IO.BinaryReader br) { this.IdLength = br.ReadByte(); this.ColorMapType = br.ReadByte(); this.ImageType = br.ReadByte(); this.ColorMap = new tgaColorMap(); this.ImageSpec = new tgaImageSpec(); this.ColorMap.Read(br); this.ImageSpec.Read(br); } public bool RleEncoded { get { return ImageType >= 9; } } } struct tgaCD { public uint RMask, GMask, BMask, AMask; public byte RShift, GShift, BShift, AShift; public uint FinalOr; public bool NeedNoConvert; } static uint UnpackColor( uint sourceColor, ref tgaCD cd) { if (cd.RMask == 0xFF && cd.GMask == 0xFF && cd.BMask == 0xFF) { // Special case to deal with 8-bit TGA files that we treat as alpha masks return sourceColor << 24; } else { uint rpermute = (sourceColor << cd.RShift) | (sourceColor >> (32 - cd.RShift)); uint gpermute = (sourceColor << cd.GShift) | (sourceColor >> (32 - cd.GShift)); uint bpermute = (sourceColor << cd.BShift) | (sourceColor >> (32 - cd.BShift)); uint apermute = (sourceColor << cd.AShift) | (sourceColor >> (32 - cd.AShift)); uint result = (rpermute & cd.RMask) | (gpermute & cd.GMask) | (bpermute & cd.BMask) | (apermute & cd.AMask) | cd.FinalOr; return result; } } static unsafe void decodeLine( System.Drawing.Imaging.BitmapData b, int line, int byp, byte[] data, ref tgaCD cd) { if (cd.NeedNoConvert) { // fast copy uint* linep = (uint*)((byte*)b.Scan0.ToPointer() + line * b.Stride); fixed (byte* ptr = data) { uint* sptr = (uint*)ptr; for (int i = 0; i < b.Width; ++i) { linep[i] = sptr[i]; } } } else { byte* linep = (byte*)b.Scan0.ToPointer() + line * b.Stride; uint* up = (uint*)linep; int rdi = 0; fixed (byte* ptr = data) { for (int i = 0; i < b.Width; ++i) { uint x = 0; for (int j = 0; j < byp; ++j) { x |= ((uint)ptr[rdi]) << (j << 3); ++rdi; } up[i] = UnpackColor(x, ref cd); } } } } static void decodeRle( System.Drawing.Imaging.BitmapData b, int byp, tgaCD cd, System.IO.BinaryReader br, bool bottomUp) { try { int w = b.Width; // make buffer larger, so in case of emergency I can decode // over line ends. byte[] linebuffer = new byte[(w + 128) * byp]; int maxindex = w * byp; int index = 0; for (int j = 0; j < b.Height; ++j) { while (index < maxindex) { byte blocktype = br.ReadByte(); int bytestoread; int bytestocopy; if (blocktype >= 0x80) { bytestoread = byp; bytestocopy = byp * (blocktype - 0x80); } else { bytestoread = byp * (blocktype + 1); bytestocopy = 0; } //if (index + bytestoread > maxindex) // throw new System.ArgumentException ("Corrupt TGA"); br.Read(linebuffer, index, bytestoread); index += bytestoread; for (int i = 0; i != bytestocopy; ++i) { linebuffer[index + i] = linebuffer[index + i - bytestoread]; } index += bytestocopy; } if (!bottomUp) decodeLine(b, b.Height - j - 1, byp, linebuffer, ref cd); else decodeLine(b, j, byp, linebuffer, ref cd); if (index > maxindex) { Array.Copy(linebuffer, maxindex, linebuffer, 0, index - maxindex); index -= maxindex; } else index = 0; } } catch (System.IO.EndOfStreamException) { } } static void decodePlain( System.Drawing.Imaging.BitmapData b, int byp, tgaCD cd, System.IO.BinaryReader br, bool bottomUp) { int w = b.Width; byte[] linebuffer = new byte[w * byp]; for (int j = 0; j < b.Height; ++j) { br.Read(linebuffer, 0, w * byp); if (!bottomUp) decodeLine(b, b.Height - j - 1, byp, linebuffer, ref cd); else decodeLine(b, j, byp, linebuffer, ref cd); } } static void decodeStandard8( System.Drawing.Imaging.BitmapData b, tgaHeader hdr, System.IO.BinaryReader br) { tgaCD cd = new tgaCD(); cd.RMask = 0x000000ff; cd.GMask = 0x000000ff; cd.BMask = 0x000000ff; cd.AMask = 0x000000ff; cd.RShift = 0; cd.GShift = 0; cd.BShift = 0; cd.AShift = 0; cd.FinalOr = 0x00000000; if (hdr.RleEncoded) decodeRle(b, 1, cd, br, hdr.ImageSpec.BottomUp); else decodePlain(b, 1, cd, br, hdr.ImageSpec.BottomUp); } static void decodeSpecial16( System.Drawing.Imaging.BitmapData b, tgaHeader hdr, System.IO.BinaryReader br) { // i must convert the input stream to a sequence of uint values // which I then unpack. tgaCD cd = new tgaCD(); cd.RMask = 0x00f00000; cd.GMask = 0x0000f000; cd.BMask = 0x000000f0; cd.AMask = 0xf0000000; cd.RShift = 12; cd.GShift = 8; cd.BShift = 4; cd.AShift = 16; cd.FinalOr = 0; if (hdr.RleEncoded) decodeRle(b, 2, cd, br, hdr.ImageSpec.BottomUp); else decodePlain(b, 2, cd, br, hdr.ImageSpec.BottomUp); } static void decodeStandard16( System.Drawing.Imaging.BitmapData b, tgaHeader hdr, System.IO.BinaryReader br) { // i must convert the input stream to a sequence of uint values // which I then unpack. tgaCD cd = new tgaCD(); cd.RMask = 0x00f80000; // from 0xF800 cd.GMask = 0x0000fc00; // from 0x07E0 cd.BMask = 0x000000f8; // from 0x001F cd.AMask = 0x00000000; cd.RShift = 8; cd.GShift = 5; cd.BShift = 3; cd.AShift = 0; cd.FinalOr = 0xff000000; if (hdr.RleEncoded) decodeRle(b, 2, cd, br, hdr.ImageSpec.BottomUp); else decodePlain(b, 2, cd, br, hdr.ImageSpec.BottomUp); } static void decodeSpecial24(System.Drawing.Imaging.BitmapData b, tgaHeader hdr, System.IO.BinaryReader br) { // i must convert the input stream to a sequence of uint values // which I then unpack. tgaCD cd = new tgaCD(); cd.RMask = 0x00f80000; cd.GMask = 0x0000fc00; cd.BMask = 0x000000f8; cd.AMask = 0xff000000; cd.RShift = 8; cd.GShift = 5; cd.BShift = 3; cd.AShift = 8; cd.FinalOr = 0; if (hdr.RleEncoded) decodeRle(b, 3, cd, br, hdr.ImageSpec.BottomUp); else decodePlain(b, 3, cd, br, hdr.ImageSpec.BottomUp); } static void decodeStandard24(System.Drawing.Imaging.BitmapData b, tgaHeader hdr, System.IO.BinaryReader br) { // i must convert the input stream to a sequence of uint values // which I then unpack. tgaCD cd = new tgaCD(); cd.RMask = 0x00ff0000; cd.GMask = 0x0000ff00; cd.BMask = 0x000000ff; cd.AMask = 0x00000000; cd.RShift = 0; cd.GShift = 0; cd.BShift = 0; cd.AShift = 0; cd.FinalOr = 0xff000000; if (hdr.RleEncoded) decodeRle(b, 3, cd, br, hdr.ImageSpec.BottomUp); else decodePlain(b, 3, cd, br, hdr.ImageSpec.BottomUp); } static void decodeStandard32(System.Drawing.Imaging.BitmapData b, tgaHeader hdr, System.IO.BinaryReader br) { // i must convert the input stream to a sequence of uint values // which I then unpack. tgaCD cd = new tgaCD(); cd.RMask = 0x00ff0000; cd.GMask = 0x0000ff00; cd.BMask = 0x000000ff; cd.AMask = 0xff000000; cd.RShift = 0; cd.GShift = 0; cd.BShift = 0; cd.AShift = 0; cd.FinalOr = 0x00000000; cd.NeedNoConvert = true; if (hdr.RleEncoded) decodeRle(b, 4, cd, br, hdr.ImageSpec.BottomUp); else decodePlain(b, 4, cd, br, hdr.ImageSpec.BottomUp); } public static System.Drawing.Size GetTGASize(string filename) { System.IO.FileStream f = System.IO.File.OpenRead(filename); System.IO.BinaryReader br = new System.IO.BinaryReader(f); tgaHeader header = new tgaHeader(); header.Read(br); br.Close(); return new System.Drawing.Size(header.ImageSpec.Width, header.ImageSpec.Height); } public static System.Drawing.Bitmap LoadTGA(System.IO.Stream source) { byte[] buffer = new byte[source.Length]; source.Read(buffer, 0, buffer.Length); System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer); using (System.IO.BinaryReader br = new System.IO.BinaryReader(ms)) { tgaHeader header = new tgaHeader(); header.Read(br); if (header.ImageSpec.PixelDepth != 8 && header.ImageSpec.PixelDepth != 16 && header.ImageSpec.PixelDepth != 24 && header.ImageSpec.PixelDepth != 32) throw new ArgumentException("Not a supported tga file."); if (header.ImageSpec.AlphaBits > 8) throw new ArgumentException("Not a supported tga file."); if (header.ImageSpec.Width > 4096 || header.ImageSpec.Height > 4096) throw new ArgumentException("Image too large."); System.Drawing.Bitmap b; System.Drawing.Imaging.BitmapData bd; // Create a bitmap for the image. // Only include an alpha layer when the image requires one. if (header.ImageSpec.AlphaBits > 0 || header.ImageSpec.PixelDepth == 8 || // Assume 8 bit images are alpha only header.ImageSpec.PixelDepth == 32) // Assume 32 bit images are ARGB { // Image needs an alpha layer b = new System.Drawing.Bitmap( header.ImageSpec.Width, header.ImageSpec.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); bd = b.LockBits(new System.Drawing.Rectangle(0, 0, b.Width, b.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); } else { // Image does not need an alpha layer, so do not include one. b = new System.Drawing.Bitmap( header.ImageSpec.Width, header.ImageSpec.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb); bd = b.LockBits(new System.Drawing.Rectangle(0, 0, b.Width, b.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb); } switch (header.ImageSpec.PixelDepth) { case 8: decodeStandard8(bd, header, br); break; case 16: if (header.ImageSpec.AlphaBits > 0) decodeSpecial16(bd, header, br); else decodeStandard16(bd, header, br); break; case 24: if (header.ImageSpec.AlphaBits > 0) decodeSpecial24(bd, header, br); else decodeStandard24(bd, header, br); break; case 32: decodeStandard32(bd, header, br); break; default: b.UnlockBits(bd); b.Dispose(); return null; } b.UnlockBits(bd); return b; } } public static unsafe ManagedImage LoadTGAImage(System.IO.Stream source) { return LoadTGAImage(source, false); } public static unsafe ManagedImage LoadTGAImage(System.IO.Stream source, bool mask) { byte[] buffer = new byte[source.Length]; source.Read(buffer, 0, buffer.Length); System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer); using (System.IO.BinaryReader br = new System.IO.BinaryReader(ms)) { tgaHeader header = new tgaHeader(); header.Read(br); if (header.ImageSpec.PixelDepth != 8 && header.ImageSpec.PixelDepth != 16 && header.ImageSpec.PixelDepth != 24 && header.ImageSpec.PixelDepth != 32) throw new ArgumentException("Not a supported tga file."); if (header.ImageSpec.AlphaBits > 8) throw new ArgumentException("Not a supported tga file."); if (header.ImageSpec.Width > 4096 || header.ImageSpec.Height > 4096) throw new ArgumentException("Image too large."); byte[] decoded = new byte[header.ImageSpec.Width * header.ImageSpec.Height * 4]; System.Drawing.Imaging.BitmapData bd = new System.Drawing.Imaging.BitmapData(); fixed (byte* pdecoded = &decoded[0]) { bd.Width = header.ImageSpec.Width; bd.Height = header.ImageSpec.Height; bd.PixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppPArgb; bd.Stride = header.ImageSpec.Width * 4; bd.Scan0 = (IntPtr)pdecoded; switch (header.ImageSpec.PixelDepth) { case 8: decodeStandard8(bd, header, br); break; case 16: if (header.ImageSpec.AlphaBits > 0) decodeSpecial16(bd, header, br); else decodeStandard16(bd, header, br); break; case 24: if (header.ImageSpec.AlphaBits > 0) decodeSpecial24(bd, header, br); else decodeStandard24(bd, header, br); break; case 32: decodeStandard32(bd, header, br); break; default: return null; } } int n = header.ImageSpec.Width * header.ImageSpec.Height; ManagedImage image; if (mask && header.ImageSpec.AlphaBits == 0 && header.ImageSpec.PixelDepth == 8) { image = new ManagedImage(header.ImageSpec.Width, header.ImageSpec.Height, ManagedImage.ImageChannels.Alpha); int p = 3; for (int i = 0; i < n; i++) { image.Alpha[i] = decoded[p]; p += 4; } } else { image = new ManagedImage(header.ImageSpec.Width, header.ImageSpec.Height, ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha); int p = 0; for (int i = 0; i < n; i++) { image.Blue[i] = decoded[p++]; image.Green[i] = decoded[p++]; image.Red[i] = decoded[p++]; image.Alpha[i] = decoded[p++]; } } br.Close(); return image; } } public static System.Drawing.Bitmap LoadTGA(string filename) { try { using (System.IO.FileStream f = System.IO.File.OpenRead(filename)) { return LoadTGA(f); } } catch (System.IO.DirectoryNotFoundException) { return null; // file not found } catch (System.IO.FileNotFoundException) { return null; // file not found } } } #endif }