public static ImageSource Decode(TgaHeader tgaHeader, byte[] imageBuffer, int size) { const double DPI_X = 96d; const double DPI_Y = 96d; var stride = GetStride(tgaHeader); var pixelFormat = GetPixelFormat(tgaHeader); var bitmap = BitmapSource.Create( tgaHeader.Width, tgaHeader.Height, DPI_X, DPI_Y, pixelFormat, null, imageBuffer, stride); var transformedBmp = new TransformedBitmap(); transformedBmp.BeginInit(); transformedBmp.Source = bitmap; transformedBmp.Transform = CreateShrinkTransform(tgaHeader, size); transformedBmp.EndInit(); return(transformedBmp); }
public static int GetImageDataOffset(TgaHeader tgaHeader) { int offset = tgaHeader.IdLength; offset += tgaHeader.ColorMapType == 0 ? 0 : GetColorMapLength(tgaHeader); return(offset); }
public static bool WriteTga(this RayImage img, Stream output) { try { var sizeHeader = Marshal.SizeOf <TgaHeader>(); //const int sizeHeader = 18; var sizePixels = img.Width * img.Height * 3; var buffer = new byte[sizeHeader + sizePixels]; var span = new Span <byte>(buffer); // write header. // IMPORTENT TO USE REF HERE. ref TgaHeader header = ref MemoryMarshal.Cast <byte, TgaHeader>(span)[0]; //var header = MemoryMarshal.Cast<byte, TgaHeader>(span)[0]; //var header = MemoryMarshal.AsRef<TgaHeader>(span.Slice(0, sizeHeader)); header.DataTypeCode = 2; header.Width = (short)img.Width; header.Height = (short)img.Height; header.BitsPerPixel = 24; // convert rgb3f to bgr8 var slicePixels = span.Slice(sizeHeader, sizePixels); FormatUtils.ConvertBGR8(img, ref slicePixels); output.Write(span); output.Flush(); return(true); }
// For now, we only export in uncompressed ARGB32 format. If someone requests this functionality, // we can always add more through an export dialog. public void Export(Document document, string fileName) { ImageSurface surf = document.GetFlattenedImage(); // Assumes the surface is in ARGB32 format BinaryWriter writer = new BinaryWriter(new FileStream(fileName, FileMode.Create, FileAccess.Write)); try { TgaHeader header = new TgaHeader(); header.idLength = 0; header.cmapType = 0; header.imageType = 2; // uncompressed RGB header.cmapIndex = 0; header.cmapLength = 0; header.cmapEntrySize = 0; header.xOrigin = 0; header.yOrigin = 0; header.imageWidth = (ushort)surf.Width; header.imageHeight = (ushort)surf.Height; header.pixelDepth = 32; header.imageDesc = 8; // 32-bit, lower-left origin, which is weird but hey... header.WriteTo(writer); byte[] data = surf.Data; // It just so happens that the Cairo ARGB32 internal representation matches // the TGA format, except vertically-flipped. In little-endian, of course. for (int y = surf.Height - 1; y >= 0; y--) { writer.Write(data, surf.Stride * y, surf.Stride); } } finally { (surf as IDisposable).Dispose(); writer.Close(); } }
public static TgaHeader TakeTga(Stream stream) { using (var br = new BinaryReader(stream)) { var header = new TgaHeader(); /* char */ header.idlength = br.ReadByte(); /* char */ header.colourmaptype = br.ReadByte(); /* char */ header.datatypecode = br.ReadByte(); /* short */ header.colourmaporigin = br.ReadInt16(); /* short */ header.colourmaplength = br.ReadInt16(); /* char */ header.colourmapdepth = br.ReadByte(); /* short */ header.x_origin = br.ReadInt16(); /* short */ header.y_origin = br.ReadInt16(); /* short */ header.width = br.ReadInt16(); /* short */ header.height = br.ReadInt16(); /* char */ header.bitsperpixel = br.ReadByte(); /* char */ header.imagedescriptor = br.ReadByte(); if (header.datatypecode != 2) { throw new Exception(string.Format("Only uncompressed RGB and RGBA images are supported. Got {0} data type code", header.datatypecode)); } if (header.bitsperpixel != 24 && header.bitsperpixel != 32) { throw new Exception(string.Format("Only 24- and 32-bit images are supported. Got {0} bits per pixel", header.bitsperpixel)); } return(header); } }
// For now, we only export in uncompressed ARGB32 format. If someone requests this functionality, // we can always add more through an export dialog. public void Export(Document document, string fileName) { ImageSurface surf = document.GetFlattenedImage (); // Assumes the surface is in ARGB32 format BinaryWriter writer = new BinaryWriter (new FileStream (fileName, FileMode.Create, FileAccess.Write)); try { TgaHeader header = new TgaHeader(); header.idLength = (byte) (ImageIdField.Length + 1); header.cmapType = 0; header.imageType = 2; // uncompressed RGB header.cmapIndex = 0; header.cmapLength = 0; header.cmapEntrySize = 0; header.xOrigin = 0; header.yOrigin = 0; header.imageWidth = (ushort) surf.Width; header.imageHeight = (ushort) surf.Height; header.pixelDepth = 32; header.imageDesc = 8; // 32-bit, lower-left origin, which is weird but hey... header.WriteTo (writer); writer.Write(ImageIdField); byte[] data = surf.Data; // It just so happens that the Cairo ARGB32 internal representation matches // the TGA format, except vertically-flipped. In little-endian, of course. for (int y = surf.Height - 1; y >= 0; y--) writer.Write (data, surf.Stride * y, surf.Stride); } finally { (surf as IDisposable).Dispose (); writer.Close (); } }
public static int GetStride(TgaHeader tgaHeader) { var bytesPerPixel = GetBytesPerPixel(tgaHeader); var stride = bytesPerPixel * tgaHeader.Width; return(stride); }
private static double GetScale(TgaHeader tgaHeader, int size, Func <TgaHeader, bool> isFlipped) { var divisor = Math.Max(tgaHeader.Width, tgaHeader.Height); var scaleY = (isFlipped(tgaHeader) ? -1 : 1) * ((double)size / divisor); return(scaleY); }
private static void SaveTgaRowRaw(Stream output, Surface input, ref TgaHeader header, int y) { for (int x = 0; x < input.Width; ++x) { ColorBgra color = input[x, y]; WriteColor(output, color, header.pixelDepth); } }
private string ReadIdString(BinaryReader reader, TgaHeader header) { byte[] idString = reader.ReadBytes(header.idLength); ASCIIEncoding encoding = new ASCIIEncoding(); return(encoding.GetString(idString, 0, idString.Length)); }
private static ScaleTransform CreateShrinkTransform(TgaHeader tgaHeader, int size) { var scaleX = GetScaleX(tgaHeader, size); var scaleY = GetScaleY(tgaHeader, size); var transformedBmpTransform = new ScaleTransform(scaleX, scaleY); return(transformedBmpTransform); }
private byte ExpandCompressedLine(MemoryBlock dst, int dstIndex, ref TgaHeader header, Stream input, int width, int y, byte rleLeftOver, ColorBgra[] palette) { byte num; long position = 0L; for (int i = 0; i < width; i += num) { if (rleLeftOver != 0xff) { num = rleLeftOver; rleLeftOver = 0xff; } else { int num4 = input.ReadByte(); if (num4 == -1) { throw new EndOfStreamException(); } num = (byte)num4; } if ((num & 0x80) != 0) { num = (byte)(num - 0x7f); if ((i + num) > width) { rleLeftOver = (byte)(0x80 + ((num - (width - i)) - 1)); position = input.Position; num = (byte)(width - i); } ColorBgra bgra = this.ReadColor(input, header.pixelDepth, palette); for (int j = 0; j < num; j++) { int num6 = dstIndex + (j * 4); dst[(long)num6] = bgra[0]; dst[(long)(1 + num6)] = bgra[1]; dst[(long)(2 + num6)] = bgra[2]; dst[(long)(3 + num6)] = bgra[3]; } if (rleLeftOver != 0xff) { input.Position = position; } } else { num = (byte)(num + 1); if ((i + num) > width) { rleLeftOver = (byte)((num - (width - i)) - 1); num = (byte)(width - i); } this.ExpandUncompressedLine(dst, dstIndex, ref header, input, num, y, i, palette); } dstIndex += num * 4; } return(rleLeftOver); }
public static ImageSource Decode(byte[] data, TgaHeader tgaHeader, int size) { var imageDataOffset = RgbBufferDecoder.GetImageDataOffset(tgaHeader); var numberOfPixels = RgbBufferDecoder.GetStride(tgaHeader) * tgaHeader.Height; var imageBuffer = data.Skip(imageDataOffset).Take(numberOfPixels).ToArray(); return(RgbBufferDecoder.Decode(tgaHeader, imageBuffer, size)); }
private static void SaveTgaRowRle(Stream output, Surface input, ref TgaHeader header, int y) { TgaPacketStateMachine machine = new TgaPacketStateMachine(output, header.pixelDepth); for (int i = 0; i < input.Width; i++) { machine.Push(input[i, y]); } machine.Flush(); }
private void SaveTga(Surface input, Stream output, SavableBitDepths bitDepth, bool rleCompress, ProgressEventHandler progressCallback) { TgaHeader header = new TgaHeader(); header.idLength = 0; header.cmapType = 0; header.imageType = rleCompress ? TgaType.RleRgb : TgaType.Rgb; header.cmapIndex = 0; header.cmapLength = 0; header.cmapEntrySize = 0; // if bpp=8, set this to 24 header.xOrigin = 0; header.yOrigin = 0; header.imageWidth = (ushort)input.Width; header.imageHeight = (ushort)input.Height; header.imageDesc = 0; switch (bitDepth) { case SavableBitDepths.Rgba32: header.pixelDepth = 32; header.imageDesc |= 8; break; case SavableBitDepths.Rgb24: header.pixelDepth = 24; break; default: throw new InvalidEnumArgumentException("bitDepth", (int)bitDepth, typeof(SavableBitDepths)); } header.Write(output); // write palette if doing 8-bit // ... todo? for (int y = input.Height - 1; y >= 0; --y) { // non-rle output if (rleCompress) { SaveTgaRowRle(output, input, ref header, y); } else { SaveTgaRowRaw(output, input, ref header, y); } if (progressCallback != null) { progressCallback(this, new ProgressEventArgs(100.0 * ((double)(input.Height - y) / (double)input.Height))); } } }
private void ExpandUncompressedLine(MemoryBlock dst, int dstIndex, ref TgaHeader header, Stream input, int width, int y, int xoffset, ColorBgra[] palette) { for (int i = 0; i < width; i++) { ColorBgra bgra = this.ReadColor(input, header.pixelDepth, palette); dst[(long)dstIndex] = bgra[0]; dst[(long)(1 + dstIndex)] = bgra[1]; dst[(long)(2 + dstIndex)] = bgra[2]; dst[(long)(3 + dstIndex)] = bgra[3]; dstIndex += 4; } }
private static PixelFormat GetPixelFormat(TgaHeader tgaHeader) { switch (tgaHeader.BitsPerPixel) { case 24: return(PixelFormats.Bgr24); case 32: return(PixelFormats.Bgra32); } throw new UnsupportedPixelFormatException(); }
private void ExpandUncompressedLine(MemoryBlock dst, int dstIndex, ref TgaHeader header, Stream input, int width, int y, int xoffset, ColorBgra[] palette) { for (int i = 0; i < width; ++i) { ColorBgra color = ReadColor(input, header.pixelDepth, palette); dst[dstIndex] = color[0]; dst[1 + dstIndex] = color[1]; dst[2 + dstIndex] = color[2]; dst[3 + dstIndex] = color[3]; dstIndex += 4; } }
private byte[] ReadColorMap(BinaryReader reader, TgaHeader header) { if ((ImageDataType)header.dataType != ImageDataType.UncompressedMappedColor) { return(null); } int bytesPerEntry = header.colorMapDepth / 8; byte[] colorMap = reader.ReadBytes(bytesPerEntry * header.colorMapLength); return(colorMap); }
private static TgaHeader LoadTgaHeader(BinaryReader r) { TgaHeader th = new TgaHeader(); r.BaseStream.Seek(0, SeekOrigin.Current); // fread(&th, sizeof(gvImgTGAHeader), 1, fp); // due to byte alignment read the components one at a time th.identSize = r.ReadByte(); th.colorMapType = r.ReadByte(); th.imageType = r.ReadByte(); th.colorMapStart = r.ReadUInt16(); th.colorMapLength = r.ReadUInt16(); th.colorMapBits = r.ReadByte(); th.xStart = r.ReadUInt16(); th.ySstart = r.ReadUInt16(); th.width = r.ReadUInt16(); th.height = r.ReadUInt16(); th.bits = r.ReadByte(); th.descriptor = r.ReadByte(); Debug.LogFormat("TGA descriptor = {0}", th.descriptor); // check if the image contains no data if (th.imageType == 0) { new Exception("TGA image contains no data."); // GV_IMG_ERROR_NO_DATA; } // check for other types (compressed images) if (th.imageType > 10) { new Exception("compressed TGA not supported."); //GV_IMG_ERROR_COMPRESSED_FILE; } // check if the image is color indexed if (th.imageType == 1 || th.imageType == 9) { new Exception("color indexed TGA not supported."); //GV_IMG_ERROR_INDEXED_COLOR; } // for now we only consider 24bit rgb or rle images if (th.bits != 24 && th.bits != 32) { throw new Exception("only 24/32 bits TGA supported."); //GV_IMG_ERROR_BITS; } // width and height must be valid ones if (th.width <= 0 || th.height <= 0) { throw new Exception("TGA texture has invalid size."); // GV_IMG_ERROR_SIZE; } // skips id header r.BaseStream.Seek(th.identSize, SeekOrigin.Current); return(th); }
private void SaveTga(Surface input, Stream output, InternalFileType.SavableBitDepths bitDepth, bool rleCompress, ProgressEventHandler progressCallback) { TgaHeader header = new TgaHeader { idLength = 0, cmapType = 0, imageType = rleCompress ? TgaType.RleRgb : TgaType.Rgb, cmapIndex = 0, cmapLength = 0, cmapEntrySize = 0, xOrigin = 0, yOrigin = 0, imageWidth = (ushort)input.Width, imageHeight = (ushort)input.Height, imageDesc = 0 }; if (bitDepth != InternalFileType.SavableBitDepths.Rgba32) { if (bitDepth != InternalFileType.SavableBitDepths.Rgb24) { throw ExceptionUtil.InvalidEnumArgumentException <InternalFileType.SavableBitDepths>(bitDepth, "bitDepth"); } } else { header.pixelDepth = 0x20; header.imageDesc = (byte)(header.imageDesc | 8); goto Label_00AC; } header.pixelDepth = 0x18; Label_00AC: header.Write(output); for (int i = input.Height - 1; i >= 0; i--) { if (rleCompress) { SaveTgaRowRle(output, input, ref header, i); } else { SaveTgaRowRaw(output, input, ref header, i); } if (progressCallback != null) { progressCallback(this, new ProgressEventArgs(100.0 * (((double)(input.Height - i)) / ((double)input.Height)))); } } }
private int CalculateOpenGlPixelFormat(TgaHeader header) { int bpp = GetPixelDepth(header); switch (bpp) { case 24: return(Gl.GL_BGR); case 32: return(Gl.GL_BGRA); default: throw new InvalidOperationException("Unknown pixel depth."); } }
private byte[] ReadPixels(BinaryReader reader, TgaHeader header, byte[] colorMap) { if (header.bitsPerPixel == 16) { throw new NotImplementedException("16bpp RGB unpacking is not supported yet."); } int bytesPerPixel = header.bitsPerPixel / 8; int pixelCount = header.imageHeight * header.imageWidth; int expectedPayloadSize = pixelCount * bytesPerPixel; byte[] pixelPayload; if ((ImageDataType)header.dataType == ImageDataType.RleCompressedUnmappedColor) { int payloadSize = (int)(reader.BaseStream.Length - reader.BaseStream.Position); pixelPayload = reader.ReadBytes(payloadSize); pixelPayload = DecompressRle(pixelPayload, header); } else { // ignore footer pixelPayload = reader.ReadBytes(expectedPayloadSize); } if (pixelPayload.Length != expectedPayloadSize) { throw new InvalidDataException("Targa payload size (" + pixelPayload.Length + ") does not match expected size (" + expectedPayloadSize + ")"); } if ((ColorMapType)header.colorMapType == ColorMapType.ColorMapPresent) { pixelPayload = ExpandPayloadToUnmappedPixels(header, pixelPayload, colorMap); } if (YOriginIsAtBottomOfImage(header.imageDescriptor)) { InvertImageAroundY(pixelPayload, header); } return(pixelPayload); }
private byte[] ExpandPayloadToUnmappedPixels(TgaHeader header, byte[] pixelPayload, byte[] colorMap) { int mappedPixelSize = header.bitsPerPixel / 8; if (mappedPixelSize != 1) { throw new NotSupportedException("Mapped pixel elements greater than 8bpp are not supported yet."); } int unmappedPixelSize = header.colorMapDepth / 8; if (unmappedPixelSize != 3 && unmappedPixelSize != 4) { throw new NotSupportedException("Unmapped pixel elements other than 24bpp and 32bpp are not supported yet."); } int pixelCount = header.imageHeight * header.imageWidth; byte[] unmappedPixels = new byte[unmappedPixelSize * pixelCount]; MemoryStream unmappedPixelStream = new MemoryStream(unmappedPixels); for (int i = 0; i < pixelCount; ++i) { int index = pixelPayload[i]; int mapEntry = index * unmappedPixelSize; if (unmappedPixelSize == 3) { unmappedPixelStream.WriteByte(colorMap[mapEntry]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 1]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 2]); } else { unmappedPixelStream.WriteByte(colorMap[mapEntry]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 1]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 2]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 3]); } } return(unmappedPixels); }
private TgaHeader ReadHeader(BinaryReader reader) { TgaHeader header = new TgaHeader(); header.idLength = reader.ReadByte(); header.colorMapType = reader.ReadByte(); header.dataType = reader.ReadByte(); header.colorMapStart = reader.ReadInt16(); header.colorMapLength = reader.ReadInt16(); header.colorMapDepth = reader.ReadByte(); header.xOrigin = reader.ReadInt16(); header.yOrigin = reader.ReadInt16(); header.imageWidth = reader.ReadInt16(); header.imageHeight = reader.ReadInt16(); header.bitsPerPixel = reader.ReadByte(); header.imageDescriptor = reader.ReadByte(); return(header); }
private void SaveTga(Surface input, Stream output, SaveConfigToken token, ProgressEventHandler progressCallback) { TgaSaveConfigToken tgaToken = (TgaSaveConfigToken)token; TgaHeader header = new TgaHeader(); header.idLength = 0; header.cmapType = 0; header.imageType = tgaToken.RleCompress ? TgaType.RleRgb : TgaType.Rgb; header.cmapIndex = 0; header.cmapLength = 0; header.cmapEntrySize = 0; // if bpp=8, set this to 24 header.xOrigin = 0; header.yOrigin = 0; header.imageWidth = (ushort)input.Width; header.imageHeight = (ushort)input.Height; header.pixelDepth = (byte)tgaToken.BitDepth; header.imageDesc = 0; header.Write(output); // write palette if doing 8-bit // ... todo? for (int y = input.Height - 1; y >= 0; --y) { // non-rle output if (tgaToken.RleCompress) { SaveTgaRowRle(output, input, ref header, y); } else { SaveTgaRowRaw(output, input, ref header, y); } if (progressCallback != null) { progressCallback(this, new ProgressEventArgs(100.0 * ((double)(input.Height - y) / (double)input.Height))); } } }
private void InvertImageAroundY(byte[] pixels, TgaHeader header) { int bytesPerPixel = GetPixelDepth(header) / 8; int bytesPerRow = header.imageWidth * bytesPerPixel; byte[] row = new byte[bytesPerRow]; int topOffset = 0; int bottomOffset = bytesPerRow * (header.imageHeight - 1); int totalRowsToReflect = header.imageHeight / 2; for (int i = 0; i < totalRowsToReflect; ++i) { Buffer.BlockCopy(pixels, topOffset, row, 0, bytesPerRow); Buffer.BlockCopy(pixels, bottomOffset, pixels, topOffset, bytesPerRow); Buffer.BlockCopy(row, 0, pixels, bottomOffset, bytesPerRow); topOffset += bytesPerRow; bottomOffset -= bytesPerRow; } }
private static byte[] GetImageBuffer(byte[] data, TgaHeader tgaHeader) { var imageDataOffset = RgbBufferDecoder.GetImageDataOffset(tgaHeader); var bytesPerPixel = RgbBufferDecoder.GetBytesPerPixel(tgaHeader); var pixelsToDecode = tgaHeader.Width * tgaHeader.Height; var outBuffer = new byte[pixelsToDecode * bytesPerPixel]; for (int i = 0, inOffset = imageDataOffset, outOffset = 0; i < pixelsToDecode;) { var header = data[inOffset++]; var pixelCount = GetPixelCount(header); if (IsRle(header)) { for (int count = 0; count < pixelCount; count++) { Array.Copy(data, inOffset, outBuffer, outOffset, bytesPerPixel); outOffset += bytesPerPixel; } inOffset += bytesPerPixel; } else { var pixelsBytesLength = pixelCount * bytesPerPixel; Array.Copy(data, inOffset, outBuffer, outOffset, pixelsBytesLength); inOffset += pixelsBytesLength; outOffset += pixelsBytesLength; } i += pixelCount; } return(outBuffer); }
private byte[] DecompressRle(byte[] rleCompressedPixels, TgaHeader header) { int bytesPerPixel = GetPixelDepth(header) / 8; int decompressedByteCount = header.imageWidth * header.imageHeight * bytesPerPixel; byte[] decompressedPixels = new byte[decompressedByteCount]; using (BinaryReader reader = new BinaryReader(new MemoryStream(rleCompressedPixels))) using (BinaryWriter writer = new BinaryWriter(new MemoryStream(decompressedPixels))) { while (writer.BaseStream.Position != writer.BaseStream.Length) { byte packetHeader = reader.ReadByte(); int pixelCountThisPayload = (packetHeader & 0x7F) + 1; if (PacketPreceedsRlePayload(packetHeader)) { byte[] runColor = reader.ReadBytes(bytesPerPixel); while (pixelCountThisPayload > 0) { writer.Write(runColor); --pixelCountThisPayload; } } else { // copy straight through int payloadSizeInBytes = pixelCountThisPayload * bytesPerPixel; byte[] payload = reader.ReadBytes(payloadSizeInBytes); writer.Write(payload); } } } return(decompressedPixels); }
protected override Document OnLoad(System.IO.Stream input) { TgaHeader header = new TgaHeader(input); bool compressed; switch (header.imageType) { case TgaType.Map: case TgaType.Rgb: case TgaType.Mono: compressed = false; break; case TgaType.RleMap: case TgaType.RleRgb: case TgaType.RleMono: compressed = true; break; default: throw new FormatException("unknown TGA image type"); } if (header.imageWidth == 0 || header.imageHeight == 0 || header.pixelDepth == 0 || header.cmapLength > 256) { throw new FormatException("bad TGA header"); } if (header.pixelDepth != 8 && header.pixelDepth != 15 && header.pixelDepth != 16 && header.pixelDepth != 24 && header.pixelDepth != 32) { throw new FormatException("bad TGA header: pixelDepth not one of {8, 15, 16, 24, 32}"); } if (header.idLength > 0) { input.Position += header.idLength; // skip descriptor } BitmapLayer layer = Layer.CreateBackgroundLayer(header.imageWidth, header.imageHeight); try { Surface surface = layer.Surface; surface.Clear((ColorBgra)0xffffffff); ColorBgra[] palette = null; if (header.cmapType != 0) { palette = LoadPalette(input, header.cmapLength); } if (header.imageType == TgaType.Mono || header.imageType == TgaType.RleMono) { palette = CreateGrayPalette(); } // Bits 0 - 3 of the image descriptor byte describe number of bits used for alpha channel // For loading, we won't worry about this. Not all TGA implementations are correct (such // as older Paint.NET TGA implementations!) and we don't want to lose all their alpha bits. //int alphaBits = header.imageDesc & 0xf; // Bits 4 & 5 of the image descriptor byte control the ordering of the pixels bool xReversed = ((header.imageDesc & 16) == 16); bool yReversed = ((header.imageDesc & 32) == 32); byte rleLeftOver = 255; // for images with illegal packet boundary for (int y = 0; y < header.imageHeight; ++y) { MemoryBlock dstRow; if (yReversed) { dstRow = surface.GetRow(y); } else { dstRow = surface.GetRow(header.imageHeight - y - 1); } if (compressed) { rleLeftOver = ExpandCompressedLine(dstRow, 0, ref header, input, header.imageWidth, y, rleLeftOver, palette); } else { ExpandUncompressedLine(dstRow, 0, ref header, input, header.imageWidth, y, 0, palette); } } if (xReversed) { MirrorX(surface); } Document document = new Document(surface.Width, surface.Height); document.Layers.Add(layer); return document; } catch { if (layer != null) { layer.Dispose(); layer = null; } throw; } }
private TgaHeader ReadHeader(BinaryReader reader) { TgaHeader header = new TgaHeader(); header.idLength = reader.ReadByte(); header.colorMapType = reader.ReadByte(); header.dataType = reader.ReadByte(); header.colorMapStart = reader.ReadInt16(); header.colorMapLength = reader.ReadInt16(); header.colorMapDepth = reader.ReadByte(); header.xOrigin = reader.ReadInt16(); header.yOrigin = reader.ReadInt16(); header.imageWidth = reader.ReadInt16(); header.imageHeight = reader.ReadInt16(); header.bitsPerPixel = reader.ReadByte(); header.imageDescriptor = reader.ReadByte(); return header; }
private byte ExpandCompressedLine(MemoryBlock dst, int dstIndex, ref TgaHeader header, Stream input, int width, int y, byte rleLeftOver, ColorBgra[] palette) { byte rle; long filePos = 0; int x = 0; while (x < width) { if (rleLeftOver != 255) { rle = rleLeftOver; rleLeftOver = 255; } else { int byte1 = input.ReadByte(); if (byte1 == -1) { throw new EndOfStreamException(); } else { rle = (byte)byte1; } } if ((rle & 128) != 0) { // RLE Encoded packet rle -= 127; // calculate real repeat count if ((x + rle) > width) { rleLeftOver = (byte)(128 + (rle - (width - x) - 1)); filePos = input.Position; rle = (byte)(width - x); } ColorBgra color = ReadColor(input, header.pixelDepth, palette); for (int ix = 0; ix < rle; ++ix) { int index = dstIndex + (ix * ColorBgra.SizeOf); dst[index] = color[0]; dst[1 + index] = color[1]; dst[2 + index] = color[2]; dst[3 + index] = color[3]; } if (rleLeftOver != 255) { input.Position = filePos; } } else { // Raw packet rle += 1; // calculate real repeat count if ((x + rle) > width) { rleLeftOver = (byte)(rle - (width - x) - 1); rle = (byte)(width - x); } ExpandUncompressedLine(dst, dstIndex, ref header, input, rle, y, x, palette); } dstIndex += rle * ColorBgra.SizeOf; x += rle; } return rleLeftOver; }
private int CalculateOpenGlPixelFormat(TgaHeader header) { int bpp = GetPixelDepth(header); switch (bpp) { case 24: return Gl.GL_BGR; case 32: return Gl.GL_BGRA; default: throw new InvalidOperationException("Unknown pixel depth."); } }
private int GetPixelDepth(TgaHeader header) { ColorMapType colorMapType = (ColorMapType)header.colorMapType; return (colorMapType == ColorMapType.ColorMapPresent) ? header.colorMapDepth : header.bitsPerPixel; }
private byte[] ExpandPayloadToUnmappedPixels(TgaHeader header, byte[] pixelPayload, byte[] colorMap) { int mappedPixelSize = header.bitsPerPixel/8; if (mappedPixelSize != 1) throw new NotSupportedException("Mapped pixel elements greater than 8bpp are not supported yet."); int unmappedPixelSize = header.colorMapDepth/8; if (unmappedPixelSize != 3 && unmappedPixelSize != 4) throw new NotSupportedException("Unmapped pixel elements other than 24bpp and 32bpp are not supported yet."); int pixelCount = header.imageHeight*header.imageWidth; byte[] unmappedPixels = new byte[unmappedPixelSize*pixelCount]; MemoryStream unmappedPixelStream = new MemoryStream(unmappedPixels); for (int i = 0; i < pixelCount; ++i) { int index = pixelPayload[i]; int mapEntry = index*unmappedPixelSize; if (unmappedPixelSize == 3) { unmappedPixelStream.WriteByte(colorMap[mapEntry]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 1]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 2]); } else { unmappedPixelStream.WriteByte(colorMap[mapEntry]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 1]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 2]); unmappedPixelStream.WriteByte(colorMap[mapEntry + 3]); } } return unmappedPixels; }
private byte[] DecompressRle(byte[] rleCompressedPixels, TgaHeader header) { int bytesPerPixel = GetPixelDepth(header) / 8; int decompressedByteCount = header.imageWidth * header.imageHeight * bytesPerPixel; byte[] decompressedPixels = new byte[decompressedByteCount]; using (BinaryReader reader = new BinaryReader(new MemoryStream(rleCompressedPixels))) using (BinaryWriter writer = new BinaryWriter(new MemoryStream(decompressedPixels))) { while (writer.BaseStream.Position != writer.BaseStream.Length) { byte packetHeader = reader.ReadByte(); int pixelCountThisPayload = (packetHeader & 0x7F) + 1; if (PacketPreceedsRlePayload(packetHeader)) { byte[] runColor = reader.ReadBytes(bytesPerPixel); while (pixelCountThisPayload > 0) { writer.Write(runColor); --pixelCountThisPayload; } } else { // copy straight through int payloadSizeInBytes = pixelCountThisPayload * bytesPerPixel; byte[] payload = reader.ReadBytes(payloadSizeInBytes); writer.Write(payload); } } } return decompressedPixels; }
private byte[] ReadPixels(BinaryReader reader, TgaHeader header, byte[] colorMap) { if (header.bitsPerPixel == 16) throw new NotImplementedException("16bpp RGB unpacking is not supported yet."); int bytesPerPixel = header.bitsPerPixel / 8; int pixelCount = header.imageHeight * header.imageWidth; int expectedPayloadSize = pixelCount * bytesPerPixel; byte[] pixelPayload; if ((ImageDataType)header.dataType == ImageDataType.RleCompressedUnmappedColor) { int payloadSize = (int)(reader.BaseStream.Length - reader.BaseStream.Position); pixelPayload = reader.ReadBytes(payloadSize); pixelPayload = DecompressRle(pixelPayload, header); } else { // ignore footer pixelPayload = reader.ReadBytes(expectedPayloadSize); } if (pixelPayload.Length != expectedPayloadSize) { throw new InvalidDataException("Targa payload size (" + pixelPayload.Length + ") does not match expected size (" + expectedPayloadSize + ")"); } if ((ColorMapType)header.colorMapType == ColorMapType.ColorMapPresent) pixelPayload = ExpandPayloadToUnmappedPixels(header, pixelPayload, colorMap); if (YOriginIsAtBottomOfImage(header.imageDescriptor)) InvertImageAroundY(pixelPayload, header); return pixelPayload; }
private byte[] ReadColorMap(BinaryReader reader, TgaHeader header) { if ((ImageDataType)header.dataType != ImageDataType.UncompressedMappedColor) return null; int bytesPerEntry = header.colorMapDepth/8; byte[] colorMap = reader.ReadBytes(bytesPerEntry*header.colorMapLength); return colorMap; }
private string ReadIdString(BinaryReader reader, TgaHeader header) { byte[] idString = reader.ReadBytes(header.idLength); ASCIIEncoding encoding = new ASCIIEncoding(); return encoding.GetString(idString, 0, idString.Length); }
private byte ExpandCompressedLine(MemoryBlock dst, int dstIndex, ref TgaHeader header, Stream input, int width, int y, byte rleLeftOver, ColorBgra[] palette) { byte rle; long filePos = 0; int x = 0; while (x < width) { if (rleLeftOver != 255) { rle = rleLeftOver; rleLeftOver = 255; } else { int byte1 = input.ReadByte(); if (byte1 == -1) { throw new EndOfStreamException(); } else { rle = (byte)byte1; } } if ((rle & 128) != 0) { // RLE Encoded packet rle -= 127; // calculate real repeat count if ((x + rle) > width) { rleLeftOver = (byte)(128 + (rle - (width - x) - 1)); filePos = input.Position; rle = (byte)(width - x); } ColorBgra color = ReadColor(input, header.pixelDepth, palette); for (int ix = 0; ix < rle; ++ix) { int index = dstIndex + (ix * ColorBgra.SizeOf); dst[index] = color[0]; dst[1 + index] = color[1]; dst[2 + index] = color[2]; dst[3 + index] = color[3]; } if (rleLeftOver != 255) { input.Position = filePos; } } else { // Raw packet rle += 1; // calculate real repeat count if ((x + rle) > width) { rleLeftOver = (byte)(rle - (width - x) - 1); rle = (byte)(width - x); } ExpandUncompressedLine(dst, dstIndex, ref header, input, rle, y, x, palette); } dstIndex += rle * ColorBgra.SizeOf; x += rle; } return(rleLeftOver); }
private static void SaveTgaRowRle(Stream output, Surface input, ref TgaHeader header, int y) { TgaPacketStateMachine machine = new TgaPacketStateMachine(output, header.pixelDepth); for (int x = 0; x < input.Width; ++x) { machine.Push(input[x, y]); } machine.Flush(); }
private int GetPixelDepth(TgaHeader header) { ColorMapType colorMapType = (ColorMapType)header.colorMapType; return((colorMapType == ColorMapType.ColorMapPresent) ? header.colorMapDepth : header.bitsPerPixel); }
public static Image LoadTga( string path ) { var fs = File.OpenRead( path ); var br = new BinaryReader( fs ); var header = new TgaHeader(); /* char */ header.idlength = br.ReadByte(); /* char */ header.colourmaptype = br.ReadByte(); /* char */ header.datatypecode = br.ReadByte(); /* short */ header.colourmaporigin = br.ReadInt16(); /* short */ header.colourmaplength = br.ReadInt16(); /* char */ header.colourmapdepth = br.ReadByte(); /* short */ header.x_origin = br.ReadInt16(); /* short */ header.y_origin = br.ReadInt16(); /* short */ header.width = br.ReadInt16(); /* short */ header.height = br.ReadInt16(); /* char */ header.bitsperpixel = br.ReadByte(); /* char */ header.imagedescriptor = br.ReadByte(); if ( header.datatypecode != 2 ) { throw new Exception(string.Format("Only uncompressed RGB and RGBA images are supported. Got {0} data type code", header.datatypecode)); } if ( header.bitsperpixel != 24 && header.bitsperpixel != 32 ) { throw new Exception(string.Format("Only 24- and 32-bit images are supported. Got {0} bits per pixel", header.bitsperpixel)); } int w = header.width; int h = header.height; int bytePerPixel = header.bitsperpixel / 8; Image image = new Image( w, h ); byte[] data = new byte[ w * h * bytePerPixel ]; br.Read( data, 0, w * h * bytePerPixel ); br.Dispose(); fs.Dispose(); bool flip = !MathUtil.IsBitSet(header.imagedescriptor, 5); unsafe { if ( bytePerPixel==3 ) { for ( int x=0; x<w; ++x ) { for ( int y=0; y<h; ++y ) { int p = flip ? ((h-y-1) * w + x) : (y * w + x); image.RawImageData[ y * w + x ].Red = data[p*3+2] / 255.0f; image.RawImageData[ y * w + x ].Green = data[p*3+1] / 255.0f; image.RawImageData[ y * w + x ].Blue = data[p*3+0] / 255.0f; image.RawImageData[ y * w + x ].Alpha = 255 / 255.0f; } } } else { for ( int x=0; x<w; ++x ) { for ( int y=0; y<h; ++y ) { int p = flip ? ((h-y-1) * w + x) : (y * w + x); image.RawImageData[ y * w + x ].Red = data[p*4+2] / 255.0f; image.RawImageData[ y * w + x ].Green = data[p*4+1] / 255.0f; image.RawImageData[ y * w + x ].Blue = data[p*4+0] / 255.0f; image.RawImageData[ y * w + x ].Alpha = data[p*4+3] / 255.0f; } } } } image.Name = path; return image; }
public static TgaHeader FromStream(Stream stream) { var h = new TgaHeader(); h.IDLength = (byte)stream.ReadByte(); h.ColorMapType = (byte)stream.ReadByte(); h.ImageType = (byte)stream.ReadByte(); h.CMapStart = ReadShort(stream); h.CMapLength = ReadShort(stream); h.CMapDepth = (byte)stream.ReadByte(); h.XOffset = ReadShort(stream); h.YOffset = ReadShort(stream); h.Width = ReadShort(stream); h.Height = ReadShort(stream); h.PixelDepth = (byte)stream.ReadByte(); h.ImageDescriptor = (byte)stream.ReadByte(); return h; }