Add Tga file support (#25)

* Add minimal tga image loader

* Update TGA class ,add support for importing tga texture files and generate mipmap from tga images

* Add copyright and resource links

* Partial Add SaveImage

* Move TGAReader/Writer outside of TGA.cs

* Add check to return early when `DataTypeCode` is set to NO_DATA

* Add support for loading ExtensionData

* Change PNG signature check more clear

* Remove unnecessary using statements

* Move Debug stuff into Debug methods and call LoadImage before LoadFooter

* Update TGAReader

* Update TGA Reader/Writer

* Remove TGATimeSpan and use TimeSpan and DateTime instead

* Update TGAHeader member varible name and type

* Update TGA- Reader/Writer Constructor to not accept boolean flag 'useLittleEndian'

* TGAReader - Optimized 'TGA_HandleRGB'

* TGAReader - Update and rename 'TGA_HandleRLE_RGB'

* TGAWriter - Remove RLE RGB from switch statement

* TGA - Add TGA.FromFile, Move TGADataTypeCode to its own file

* Move Header, Footer and ExtentionData to there own cs file

* Move Tga files into IO folder

* Changed visibility of tga related classes to internal

* TGA - Only support reading/writting of raw RGB 32 bit images

* Update IO/TGA classes
This commit is contained in:
Miku-666
2024-03-24 14:48:49 +01:00
committed by GitHub
parent 3cb6097d14
commit f645914ee6
13 changed files with 721 additions and 13 deletions

View File

@@ -0,0 +1,60 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
namespace PckStudio.IO.TGA
{
internal enum TGADataTypeCode : byte
{
/// <summary>
/// No image data included.
/// </summary>
NO_DATA = 0,
/// <summary>
/// Uncompressed, color-mapped images.
/// </summary>
COLORMAPPED = 1,
/// <summary>
/// Uncompressed, RGB images.
/// </summary>
RGB = 2,
/// <summary>
/// Uncompressed, black and white images.
/// </summary>
BLACK_WHITE = 3,
/// <summary>
/// Runlength encoded color-mapped images.
/// </summary>
RLE_COLORMAPPED = 9,
/// <summary>
/// Runlength encoded RGB images.
/// </summary>
RLE_RGB = 10,
/// <summary>
/// Compressed, black and white images.
/// </summary>
COMPRESSED_BLACK_WHITE = 11,
/// <summary>
/// Compressed color-mapped data, using Huffman, Delta, and runlength encoding.
/// </summary>
COMPRESSED_RLE_COLORMAPPED = 32,
/// <summary>
/// Compressed color-mapped data, using Huffman, Delta, and runlength encoding. 4-pass quadtree-type process.
/// </summary>
COMPRESSED_RLE_COLORMAPPED_4 = 33,
}
}

View File

@@ -0,0 +1,33 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System.IO;
using System.Drawing;
namespace PckStudio.IO.TGA
{
internal static class TGADeserializer
{
private static TGAReader reader = new TGAReader();
public static Image DeserializeFromStream(Stream stream)
{
TGAFileData tgaImg = reader.FromStream(stream);
return tgaImg.Bitmap;
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Runtime.Serialization;
namespace PckStudio.IO.TGA
{
[Serializable]
internal class TGAException : Exception
{
public TGAException()
{
}
public TGAException(string message) : base(message)
{
}
public TGAException(string message, Exception innerException) : base(message, innerException)
{
}
protected TGAException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

View File

@@ -0,0 +1,63 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.Windows.Forms;
namespace PckStudio.IO.TGA
{
internal struct TGAExtentionData
{
public const short ExtensionSize = 0x1EF;
public string AuthorName;
public string AuthorComment;
public DateTime TimeStamp;
public string JobID;
public TimeSpan JobTime;
public string SoftwareID;
public byte[] SoftwareVersion;
public int KeyColor;
public int PixelAspectRatio;
public int GammaValue;
public int ColorCorrectionOffset;
public int PostageStampOffset;
public int ScanLineOffset;
public byte AttributesType;
public static TGAExtentionData Create()
{
var extensionData = new TGAExtentionData();
extensionData.AuthorName = "";
extensionData.AuthorComment = "";
extensionData.AuthorComment = "";
extensionData.TimeStamp = DateTime.Now;
extensionData.JobID = "";
extensionData.JobTime = new TimeSpan(extensionData.TimeStamp.Hour, extensionData.TimeStamp.Minute, extensionData.TimeStamp.Second);
extensionData.SoftwareID = Application.ProductName;
Version.TryParse(Application.ProductVersion, out Version currentVersion);
extensionData.SoftwareVersion = [(byte)currentVersion.Major, (byte)currentVersion.Minor, (byte)currentVersion.Build];
extensionData.KeyColor = 0;
extensionData.PixelAspectRatio = 0;
extensionData.GammaValue = 0;
extensionData.ColorCorrectionOffset = 0;
extensionData.PostageStampOffset = 0;
extensionData.ScanLineOffset = 0;
extensionData.AttributesType = 3;
return extensionData;
}
}
}

View File

@@ -0,0 +1,41 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System.IO;
using System.Drawing;
using System;
namespace PckStudio.IO.TGA
{
internal class TGAFileData
{
public TGAFileData(TGAHeader header, Image bitmap, TGAFooter footer, TGAExtentionData extentionData)
{
if (bitmap.Width != header.Width || bitmap.Height != header.Height)
throw new InvalidDataException("Header resolution doesn't match Image resolution");
Header = header;
Bitmap = bitmap;
Footer = footer;
ExtentionData = extentionData;
}
public readonly TGAHeader Header;
public readonly Image Bitmap;
public readonly TGAFooter Footer;
public readonly TGAExtentionData ExtentionData;
}
}

View File

@@ -0,0 +1,27 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
namespace PckStudio.IO.TGA
{
internal struct TGAFooter
{
internal const string Signature = "TRUEVISION-XFILE";
public int ExtensionDataOffset;
public int DeveloperAreaDataOffset;
}
}

View File

@@ -0,0 +1,37 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
namespace PckStudio.IO.TGA
{
/// <summary>
/// Resources:
/// <http://www.paulbourke.net/dataformats/tga/>
/// <https://en.wikipedia.org/wiki/Truevision_TGA>
/// </summary>
internal struct TGAHeader
{
public byte[] Id;
public TGADataTypeCode DataTypeCode;
public (byte Type, short Origin/*Offset*/, short Length, byte Depth) Colormap;
public (short X, short Y) Origin;
public short Width;
public short Height;
public byte BitsPerPixel;
public byte ImageDescriptor;
}
}

View File

@@ -0,0 +1,241 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using OMI.Workers;
using OMI;
namespace PckStudio.IO.TGA
{
internal class TGAReader : IDataFormatReader<TGAFileData>, IDataFormatReader
{
object IDataFormatReader.FromStream(Stream stream) => FromStream(stream);
object IDataFormatReader.FromFile(string filename) => FromFile(filename);
public TGAFileData FromFile(string filename)
{
if (File.Exists(filename))
{
using( var fs = File.OpenRead(filename) )
{
return FromStream(fs);
}
}
throw new FileNotFoundException(filename);
}
public TGAFileData FromStream(Stream stream)
{
using var reader = new EndiannessAwareBinaryReader(stream, Encoding.ASCII, leaveOpen: true, Endianness.LittleEndian);
TGAHeader header = LoadHeader(reader);
Image image = LoadImage(reader, header);
TGAFooter footer = LoadFooter(reader);
TGAExtentionData extentionData = LoadExtentionData(reader, footer);
return new TGAFileData(header, image, footer, extentionData);
}
private static void TGA_HandleRGB(EndiannessAwareBinaryReader reader, TGAHeader header, BitmapData bitmapData)
{
int bytesPerPixel = header.BitsPerPixel / 8;
byte[] data = reader.ReadBytes(header.Height * header.Width * bytesPerPixel);
Marshal.Copy(data, 0, bitmapData.Scan0, data.Length);
}
private static void TGA_HandleNoData(EndiannessAwareBinaryReader _, TGAHeader header, BitmapData bitmapData)
{
Random r = new Random();
byte[] bytes = new byte[bitmapData.Width * bitmapData.Height * 4];
r.NextBytes(bytes);
Marshal.Copy(bytes, 0, bitmapData.Scan0, bytes.Length);
}
private static TGAHeader LoadHeader(EndiannessAwareBinaryReader reader)
{
var header = new TGAHeader();
byte[] bytes = reader.ReadBytes(3);
(var headerIdLength, header.Colormap.Type, header.DataTypeCode) = (bytes[0], bytes[1], (TGADataTypeCode)bytes[2]);
header.Colormap.Origin = reader.ReadInt16();
header.Colormap.Length = reader.ReadInt16();
header.Colormap.Depth = reader.ReadByte();
header.Origin.X = reader.ReadInt16();
header.Origin.Y = reader.ReadInt16();
header.Width = reader.ReadInt16();
header.Height = reader.ReadInt16();
header.BitsPerPixel = reader.ReadByte();
header.ImageDescriptor = reader.ReadByte();
header.Id = reader.ReadBytes(headerIdLength);
DebugLogHeader(header);
return header;
}
private static PixelFormat GetPixelFormat(int bytesPerPixel)
{
return bytesPerPixel switch
{
2 => PixelFormat.Format16bppArgb1555,
3 => PixelFormat.Format24bppRgb,
4 => PixelFormat.Format32bppArgb,
_ => throw new NotSupportedException(nameof(bytesPerPixel))
};
}
private static Image LoadImage(EndiannessAwareBinaryReader reader, TGAHeader header)
{
if (header.DataTypeCode != TGADataTypeCode.RGB)
throw new NotSupportedException(nameof(header.DataTypeCode));
Bitmap bitmap = new Bitmap(header.Width, header.Height);
BitmapData bitmapData = bitmap.LockBits(
new Rectangle(0, 0, header.Width, header.Height),
ImageLockMode.WriteOnly,
GetPixelFormat(header.BitsPerPixel >> 3));
if (header.DataTypeCode == TGADataTypeCode.NO_DATA)
{
TGA_HandleNoData(reader, header, bitmapData);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
TGA_HandleRGB(reader, header, bitmapData);
bitmap.UnlockBits(bitmapData);
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
return bitmap;
}
private static TGAFooter LoadFooter(EndiannessAwareBinaryReader reader)
{
long origin = reader.BaseStream.Position;
reader.BaseStream.Seek(-26, SeekOrigin.End);
TGAFooter footer = new TGAFooter();
footer.ExtensionDataOffset = reader.ReadInt32(); // optional
footer.DeveloperAreaDataOffset = reader.ReadInt32(); // optional
string signature = reader.ReadString(16);
Debug.Assert(signature.Equals(TGAFooter.Signature) || reader.ReadInt16() != 0x002E,
"Footer signature invalid");
reader.BaseStream.Seek(origin, SeekOrigin.Begin);
DebugLogFooter(footer);
return footer;
}
private static TGAExtentionData LoadExtentionData(EndiannessAwareBinaryReader reader, TGAFooter footer)
{
if (footer.ExtensionDataOffset > 0)
{
reader.BaseStream.Seek(footer.ExtensionDataOffset, SeekOrigin.Begin);
if (reader.ReadInt16() == TGAExtentionData.ExtensionSize)
{
TGAExtentionData extentionData = new TGAExtentionData();
extentionData.AuthorName = reader.ReadString(41);
extentionData.AuthorComment = reader.ReadString(324);
short month = reader.ReadInt16();
short day = reader.ReadInt16();
short year = reader.ReadInt16();
short hour = reader.ReadInt16();
short minute = reader.ReadInt16();
short second = reader.ReadInt16();
extentionData.TimeStamp = new DateTime(year, month, day, hour, minute, second);
extentionData.JobID = reader.ReadString(41);
extentionData.JobTime = new TimeSpan(
hours: reader.ReadInt16(),
minutes: reader.ReadInt16(),
seconds: reader.ReadInt16()
);
extentionData.SoftwareID = reader.ReadString(41);
extentionData.SoftwareVersion = reader.ReadBytes(3);
extentionData.KeyColor = reader.ReadInt32();
extentionData.PixelAspectRatio = reader.ReadInt32();
extentionData.GammaValue = reader.ReadInt32();
extentionData.ColorCorrectionOffset = reader.ReadInt32();
extentionData.PostageStampOffset = reader.ReadInt32();
extentionData.ScanLineOffset = reader.ReadInt32();
extentionData.AttributesType = reader.ReadByte();
DebugLogExtentionData(extentionData);
return extentionData;
}
}
return default;
}
[Conditional("DEBUG")]
[DebuggerHidden]
[DebuggerStepThrough]
private static void DebugLogExtentionData(TGAExtentionData extentionData)
{
Debug.WriteLine("-------Extention Data-------", category: nameof(TGAReader));
Debug.WriteLine(string.Format("Author Name: {0}", args: extentionData.AuthorName), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Author Comment: {0}", args: extentionData.AuthorComment), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Time Stamp: {0}", args: extentionData.TimeStamp), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Job ID: {0}", args: extentionData.JobID), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Job Time: {0}", args: extentionData.JobTime), category: nameof(TGAReader));
Debug.WriteLine(string.Format("SoftwareID: {0}", args: extentionData.SoftwareID), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Software Version: {0}.{1}.{2}", extentionData.SoftwareVersion[0], extentionData.SoftwareVersion[1], extentionData.SoftwareVersion[2]), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Key Color: {0}", args: extentionData.KeyColor), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Pixel Aspect Ratio: {0}", args: extentionData.PixelAspectRatio), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Gamma Value: {0}", args: extentionData.GammaValue), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Color Correction Offset: {0}", args: extentionData.ColorCorrectionOffset), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Postage Stamp Offset: {0}", args: extentionData.PostageStampOffset), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Scan Line Offset: {0}", args: extentionData.ScanLineOffset), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Attributes Type: {0}", args: extentionData.AttributesType), category: nameof(TGAReader));
Debug.WriteLine("----------------------------", category: nameof(TGAReader));
}
[Conditional("DEBUG")]
[DebuggerHidden]
[DebuggerStepThrough]
private static void DebugLogHeader(TGAHeader header)
{
Debug.WriteLine("------Header Data------", category: nameof(TGAReader));
Debug.WriteLine(string.Format("ID length: {0}", args: header.Id.Length), category: nameof(TGAReader));
Debug.WriteLineIf(header.Id.Length > 0, $"ID: {header.Id}", category: nameof(TGAReader));
Debug.WriteLine(string.Format("Colourmap type: {0}", args: header.Colormap.Type), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Image type: {0}", args: header.DataTypeCode), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Colour map offset: {0}", args: header.Colormap.Origin), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Colour map length: {0}", args: header.Colormap.Length), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Colour map depth: {0}", args: header.Colormap.Depth), category: nameof(TGAReader));
Debug.WriteLine(string.Format("X origin: {0}", args: header.Origin.X), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Y origin: {0}", args: header.Origin.Y), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Width: {0}", args: header.Width), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Height: {0}", args: header.Height), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Bits per pixel: {0}", args: header.BitsPerPixel), category: nameof(TGAReader));
Debug.WriteLine(string.Format("Descriptor: {0}", args: header.ImageDescriptor), category: nameof(TGAReader));
Debug.WriteLine("-----------------------", category: nameof(TGAReader));
}
[Conditional("DEBUG")]
[DebuggerHidden]
[DebuggerStepThrough]
private static void DebugLogFooter(TGAFooter footer)
{
Debug.WriteLine("-------Footer Data-------", category: nameof(TGAReader));
Debug.WriteLine($"Extension Data Offset: {footer.ExtensionDataOffset:x}", category: nameof(TGAReader));
Debug.WriteLine($"Developer Area Data Offset: {footer.DeveloperAreaDataOffset:x}", category: nameof(TGAReader));
Debug.WriteLine("-----------------------", category: nameof(TGAReader));
}
}
}

View File

@@ -0,0 +1,32 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System.IO;
using System.Drawing;
namespace PckStudio.IO.TGA
{
internal static class TGASerializer
{
private static TGAWriter writer = new TGAWriter();
public static void SerializeToStream(ref Stream stream, Image image)
{
writer.WriteToStream(stream, image);
}
}
}

View File

@@ -0,0 +1,143 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.IO;
using System.Drawing;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Text;
using OMI;
using System.Windows.Forms;
using DiscordRPC;
namespace PckStudio.IO.TGA
{
internal class TGAWriter
{
private Bitmap _bitmap;
private int extensionDataOffset = 0;
public TGAWriter()
{
}
private void WriteHeader(EndiannessAwareBinaryWriter writer)
{
writer.Write(new byte[]
{
0, // header.Id.Length
0, // colormap type
(byte)TGADataTypeCode.RGB
});
writer.Write(0); // Colormap.Origin
writer.Write(0); // Colormap.Length
writer.Write(0); // Colormap.Depth
writer.Write(0); // Origin.X
writer.Write(0); // Origin.Y
writer.Write(_bitmap.Width);
writer.Write(_bitmap.Height);
writer.Write(32); // BitsPerPixel
writer.Write(8); // ImageDescriptor
}
private void WriteImage(EndiannessAwareBinaryWriter writer)
{
_bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
BitmapData bitmapData = _bitmap.LockBits(
new Rectangle(0, 0, _bitmap.Width, _bitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
byte[] buffer = new byte[_bitmap.Width * _bitmap.Height * 4];
Marshal.Copy(bitmapData.Scan0, buffer, 0, _bitmap.Width * _bitmap.Height * 4);
writer.Write(buffer);
}
private void WriteFooter(EndiannessAwareBinaryWriter writer)
{
writer.Write(extensionDataOffset); // extensionDataOffset
writer.Write(0); // developerAreaDataOffset
writer.WriteString(TGAFooter.Signature);
writer.Write((byte)0x2E);
writer.Write((byte)0x00);
}
private void WriteExtensionData(EndiannessAwareBinaryWriter writer)
{
extensionDataOffset = Convert.ToInt32(writer.BaseStream.Position);
TGAExtentionData extentionData = TGAExtentionData.Create();
writer.Write(TGAExtentionData.ExtensionSize);
// Author Name
writer.WriteString(extentionData.AuthorName, 41);
// Author Comment
writer.WriteString(extentionData.AuthorComment, 324);
// Timestamp
writer.Write((short)extentionData.TimeStamp.Month);
writer.Write((short)extentionData.TimeStamp.Day);
writer.Write((short)extentionData.TimeStamp.Year);
writer.Write((short)extentionData.TimeStamp.Hour);
writer.Write((short)extentionData.TimeStamp.Minute);
writer.Write((short)extentionData.TimeStamp.Second);
// Job id
writer.WriteString(extentionData.JobID, 41);
// Job time
writer.Write((short)extentionData.JobTime.Hours);
writer.Write((short)extentionData.JobTime.Minutes);
writer.Write((short)extentionData.JobTime.Seconds);
// Software Id
writer.WriteString(extentionData.SoftwareID, 41);
// Software version
writer.Write(extentionData.SoftwareVersion, 0, 3);
// Key color
writer.Write(extentionData.KeyColor);
// Pixel aspect ratio
writer.Write(extentionData.PixelAspectRatio);
// Gamma value
writer.Write(extentionData.GammaValue);
// Color correction offset
writer.Write(extentionData.ColorCorrectionOffset);
// Postage stamp offset
writer.Write(extentionData.PostageStampOffset);
// Scan line offset
writer.Write(extentionData.ScanLineOffset);
// Attributes type
writer.Write(extentionData.AttributesType);
}
public void WriteToStream(Stream stream, Image image)
{
_bitmap = new Bitmap(image);
using (var writer = new EndiannessAwareBinaryWriter(stream, Encoding.ASCII, leaveOpen: true, Endianness.LittleEndian))
{
WriteHeader(writer);
WriteImage(writer);
WriteExtensionData(writer);
WriteFooter(writer);
}
}
public void WriteToFile(string filename, Image image)
{
using (var fs = File.OpenWrite(filename))
{
WriteToStream(fs, image);
}
}
}
}

View File

@@ -9,6 +9,7 @@ using System.Text;
using System.Threading.Tasks;
using OMI.Formats.Pck;
using OMI.Workers;
using PckStudio.IO.TGA;
namespace PckStudio.Extensions
{
@@ -30,7 +31,10 @@ namespace PckStudio.Extensions
{
try
{
return Image.FromStream(stream);
if (Path.GetExtension(file.Filename) == ".tga")
return TGADeserializer.DeserializeFromStream(stream);
else
return Image.FromStream(stream);
}
catch(Exception ex)
{

View File

@@ -571,11 +571,7 @@ namespace PckStudio
case PckFileType.CapeFile:
case PckFileType.TextureFile:
{
// TODO: Add tga support
if (Path.GetExtension(file.Filename) == ".tga") break;
using MemoryStream stream = new MemoryStream(file.Data);
var img = Image.FromStream(stream);
Image img = file.GetTexture();
if (img.RawFormat != ImageFormat.Jpeg || img.RawFormat != ImageFormat.Png)
{
@@ -595,7 +591,6 @@ namespace PckStudio
Debug.WriteLine(string.Format("An error occured of type: {0} with message: {1}", ex.GetType(), ex.Message), "Exception");
}
if ((file.Filename.StartsWith("res/textures/blocks/") || file.Filename.StartsWith("res/textures/items/")) &&
file.Filetype == PckFileType.TextureFile
&& !file.IsMipmappedFile())
@@ -1953,7 +1948,7 @@ namespace PckStudio
private void addTextureToolStripMenuItem_Click(object sender, EventArgs e)
{
using OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Filter = "Texture File(*.png;*.tga)|*.png;*.tga";
fileDialog.Filter = "Texture File(*.png,*.tga)|*.png;*.tga";
if (fileDialog.ShowDialog() == DialogResult.OK)
{
using TextPrompt renamePrompt = new TextPrompt(Path.GetFileName(fileDialog.FileName));
@@ -1997,9 +1992,6 @@ namespace PckStudio
string textureExtension = Path.GetExtension(file.Filename);
// TGA is not yet supported
if (textureExtension == ".tga") return;
using NumericPrompt numericPrompt = new NumericPrompt(0);
numericPrompt.Minimum = 1;
numericPrompt.Maximum = 4; // 5 is the presumed max MipMap level
@@ -2018,7 +2010,7 @@ namespace PckStudio
PckFileData MipMappedFile = new PckFileData(mippedPath, PckFileType.TextureFile);
Image originalTexture = Image.FromStream(new MemoryStream(file.Data));
Image originalTexture = file.GetTexture();
int NewWidth = Math.Max(originalTexture.Width / (int)Math.Pow(2, i - 1), 1);
int NewHeight = Math.Max(originalTexture.Height / (int)Math.Pow(2, i - 1), 1);

View File

@@ -133,11 +133,14 @@
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<ItemGroup>
<Compile Include="Classes\IO\TGA\TGADeserializer.cs" />
<Compile Include="Classes\IO\TGA\TGASerializer.cs" />
<Compile Include="Extensions\LocFileExtensions.cs" />
<Compile Include="Extensions\PckFileDataExtensions.cs" />
<Compile Include="Extensions\TreeNodeExtensions.cs" />
<Compile Include="Helper\AnimationHelper.cs" />
<Compile Include="Internal\AnimationCategory.cs" />
<Compile Include="Internal\CommitInfo.cs" />
<Compile Include="Internal\SkinAnimFlag.cs" />
<Compile Include="Internal\SkinAnimMask.cs" />
<Compile Include="Properties\Resources.Designer.cs">
@@ -245,7 +248,14 @@
<Compile Include="Features\WiiUPanel.Designer.cs">
<DependentUpon>WiiUPanel.cs</DependentUpon>
</Compile>
<Compile Include="Internal\CommitInfo.cs" />
<Compile Include="Classes\IO\TGA\TGAFileData.cs" />
<Compile Include="Classes\IO\TGA\TGADataTypeCode.cs" />
<Compile Include="Classes\IO\TGA\TGAException.cs" />
<Compile Include="Classes\IO\TGA\TGAExtentionData.cs" />
<Compile Include="Classes\IO\TGA\TGAFooter.cs" />
<Compile Include="Classes\IO\TGA\TGAHeader.cs" />
<Compile Include="Classes\IO\TGA\TGAReader.cs" />
<Compile Include="Classes\IO\TGA\TGAWriter.cs" />
<Compile Include="Forms\Additional-Popups\EntityForms\AddEntry.cs">
<SubType>Form</SubType>
</Compile>