(CmFormat): implemented 8bpp images with alpha-channel.

This commit is contained in:
morkt 2016-02-21 22:09:29 +04:00
parent a73471c0b5
commit f11dae97ab

View File

@ -2,7 +2,7 @@
//! \date Sun May 03 10:26:35 2015 //! \date Sun May 03 10:26:35 2015
//! \brief MAI image formats implementation. //! \brief MAI image formats implementation.
// //
// Copyright (C) 2015 by morkt // Copyright (C) 2015-2016 by morkt
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to // of this software and associated documentation files (the "Software"), to
@ -33,7 +33,7 @@ using GameRes.Utility;
namespace GameRes.Formats.MAI namespace GameRes.Formats.MAI
{ {
internal class CmpMetaData : ImageMetaData internal class CmMetaData : ImageMetaData
{ {
public int Colors; public int Colors;
public bool IsCompressed; public bool IsCompressed;
@ -42,20 +42,20 @@ namespace GameRes.Formats.MAI
} }
[Export(typeof(ImageFormat))] [Export(typeof(ImageFormat))]
public class CmpFormat : ImageFormat public class CmFormat : ImageFormat
{ {
public override string Tag { get { return "CMP/MAI"; } } public override string Tag { get { return "CM/MAI"; } }
public override string Description { get { return "MAI image format"; } } public override string Description { get { return "MAI image format"; } }
public override uint Signature { get { return 0; } } public override uint Signature { get { return 0; } }
public CmpFormat () public CmFormat ()
{ {
Extensions = new string[] { "cmp" }; Extensions = new string[] { "cm" };
} }
public override void Write (Stream file, ImageData image) public override void Write (Stream file, ImageData image)
{ {
throw new NotImplementedException ("CmpFormat.Write not implemented"); throw new NotImplementedException ("CmFormat.Write not implemented");
} }
public override ImageMetaData ReadMetaData (Stream stream) public override ImageMetaData ReadMetaData (Stream stream)
@ -70,7 +70,7 @@ namespace GameRes.Formats.MAI
uint size = LittleEndian.ToUInt32 (header, 0); uint size = LittleEndian.ToUInt32 (header, 0);
if (size != stream.Length) if (size != stream.Length)
return null; return null;
var info = new CmpMetaData(); var info = new CmMetaData();
info.Width = LittleEndian.ToUInt16 (header, 4); info.Width = LittleEndian.ToUInt16 (header, 4);
info.Height = LittleEndian.ToUInt16 (header, 6); info.Height = LittleEndian.ToUInt16 (header, 6);
info.Colors = LittleEndian.ToUInt16 (header, 8); info.Colors = LittleEndian.ToUInt16 (header, 8);
@ -85,11 +85,7 @@ namespace GameRes.Formats.MAI
public override ImageData Read (Stream stream, ImageMetaData info) public override ImageData Read (Stream stream, ImageMetaData info)
{ {
var meta = info as CmpMetaData; var reader = new Reader (stream, (CmMetaData)info);
if (null == meta)
throw new ArgumentException ("CmpFormat.Read should be supplied with CmpMetaData", "info");
var reader = new Reader (stream, meta);
reader.Unpack(); reader.Unpack();
return ImageData.CreateFlipped (info, reader.Format, reader.Palette, reader.Data, reader.Stride); return ImageData.CreateFlipped (info, reader.Format, reader.Palette, reader.Data, reader.Stride);
} }
@ -109,7 +105,7 @@ namespace GameRes.Formats.MAI
public byte[] Data { get { return m_pixels; } } public byte[] Data { get { return m_pixels; } }
public int Stride { get { return m_width * m_pixel_size; } } public int Stride { get { return m_width * m_pixel_size; } }
public Reader (Stream stream, CmpMetaData info) public Reader (Stream stream, CmMetaData info)
{ {
m_input = stream; m_input = stream;
m_width = (int)info.Width; m_width = (int)info.Width;
@ -124,9 +120,12 @@ namespace GameRes.Formats.MAI
case 4: Format = PixelFormats.Bgr32; break; case 4: Format = PixelFormats.Bgr32; break;
default: throw new InvalidFormatException ("Invalid color depth"); default: throw new InvalidFormatException ("Invalid color depth");
} }
m_input.Position = info.DataOffset;
if (info.Colors > 0) if (info.Colors > 0)
{
m_input.Position = 0x20;
Palette = RleDecoder.ReadPalette (m_input, info.Colors, 3); Palette = RleDecoder.ReadPalette (m_input, info.Colors, 3);
}
m_input.Position = info.DataOffset;
int size = info.IsCompressed ? m_width*m_height*m_pixel_size : (int)info.DataLength; int size = info.IsCompressed ? m_width*m_height*m_pixel_size : (int)info.DataLength;
m_pixels = new byte[size]; m_pixels = new byte[size];
} }
@ -141,7 +140,7 @@ namespace GameRes.Formats.MAI
} }
} }
internal class AmiMetaData : CmpMetaData internal class AmiMetaData : CmMetaData
{ {
public uint MaskWidth; public uint MaskWidth;
public uint MaskHeight; public uint MaskHeight;
@ -200,11 +199,7 @@ namespace GameRes.Formats.MAI
public override ImageData Read (Stream stream, ImageMetaData info) public override ImageData Read (Stream stream, ImageMetaData info)
{ {
var meta = info as AmiMetaData; var reader = new Reader (stream, (AmiMetaData)info);
if (null == meta)
throw new ArgumentException ("AmiFormat.Read should be supplied with AmiMetaData", "info");
var reader = new Reader (stream, meta);
reader.Unpack(); reader.Unpack();
return ImageData.Create (info, reader.Format, reader.Palette, reader.Data); return ImageData.Create (info, reader.Format, reader.Palette, reader.Data);
} }
@ -231,7 +226,7 @@ namespace GameRes.Formats.MAI
m_width = (int)info.Width; m_width = (int)info.Width;
m_height = (int)info.Height; m_height = (int)info.Height;
m_pixel_size = info.BPP/8; m_pixel_size = info.BPP/8;
if (m_pixel_size != 3 && m_pixel_size != 4) if (m_pixel_size != 3 && m_pixel_size != 4 && m_pixel_size != 1)
throw new InvalidFormatException ("Invalid color depth"); throw new InvalidFormatException ("Invalid color depth");
Format = PixelFormats.Bgra32; Format = PixelFormats.Bgra32;
int size = info.IsCompressed ? m_width*m_height*m_pixel_size : (int)info.DataLength; int size = info.IsCompressed ? m_width*m_height*m_pixel_size : (int)info.DataLength;
@ -256,17 +251,32 @@ namespace GameRes.Formats.MAI
else else
m_input.Read (m_alpha, 0, m_alpha.Length); m_input.Read (m_alpha, 0, m_alpha.Length);
int stride = m_width * m_pixel_size; Action<int, int, byte> copy_pixel;
if (m_pixel_size > 1)
copy_pixel = (src, dst, alpha) => {
m_pixels[dst] = m_output[src];
m_pixels[dst+1] = m_output[src+1];
m_pixels[dst+2] = m_output[src+2];
m_pixels[dst+3] = alpha;
};
else
copy_pixel = (src, dst, alpha) => {
var color = Palette.Colors[m_output[src]];
m_pixels[dst] = color.B;
m_pixels[dst+1] = color.G;
m_pixels[dst+2] = color.R;
m_pixels[dst+3] = alpha;
};
int src_stride = m_width * m_pixel_size;
for (int y = 0; y < m_height; ++y) for (int y = 0; y < m_height; ++y)
{ {
int dst_line = y*m_width; int dst_line = y*m_width*4;
int src_line = (m_height-1-y)*m_width; int src_line = (m_height-1-y)*src_stride;;
for (int x = 0; x < m_width; ++x) for (int x = 0; x < m_width; ++x)
{ {
m_pixels[(dst_line+x)*4] = m_output[(src_line+x)*m_pixel_size]; copy_pixel (src_line, dst_line, m_alpha[y*m_width+x]);
m_pixels[(dst_line+x)*4+1] = m_output[(src_line+x)*m_pixel_size+1]; src_line += m_pixel_size;
m_pixels[(dst_line+x)*4+2] = m_output[(src_line+x)*m_pixel_size+2]; dst_line += 4;
m_pixels[(dst_line+x)*4+3] = m_alpha[dst_line+x];
} }
} }
} }
@ -287,7 +297,7 @@ namespace GameRes.Formats.MAI
public override void Write (Stream file, ImageData image) public override void Write (Stream file, ImageData image)
{ {
throw new NotImplementedException ("AmiFormat.Write not implemented"); throw new NotImplementedException ("MaskFormat.Write not implemented");
} }
public override ImageMetaData ReadMetaData (Stream stream) public override ImageMetaData ReadMetaData (Stream stream)
@ -299,23 +309,33 @@ namespace GameRes.Formats.MAI
return null; return null;
uint width = input.ReadUInt32(); uint width = input.ReadUInt32();
uint height = input.ReadUInt32(); uint height = input.ReadUInt32();
if ((width*height + 0x410) != size) int compressed = input.ReadInt32();
if (compressed > 1 || 0 == compressed && (width*height + 0x410) != size)
return null; return null;
return new ImageMetaData { return new CmMetaData {
Width = width, Width = width,
Height = height, Height = height,
BPP = 8 BPP = 8,
IsCompressed = 1 == compressed,
DataOffset = 0x10,
DataLength = size,
}; };
} }
} }
public override ImageData Read (Stream stream, ImageMetaData info) public override ImageData Read (Stream stream, ImageMetaData info)
{ {
stream.Position = 0x10; var meta = (CmMetaData)info;
stream.Position = meta.DataOffset;
var palette = RleDecoder.ReadPalette (stream, 0x100, 4); var palette = RleDecoder.ReadPalette (stream, 0x100, 4);
byte[] pixels = new byte[info.Width*info.Height]; var pixels = new byte[info.Width*info.Height];
if (pixels.Length != stream.Read (pixels, 0, pixels.Length)) if (meta.IsCompressed)
{
int packed_size = (int)(stream.Length - meta.DataOffset);
RleDecoder.Unpack (stream, packed_size, pixels, 1);
}
else if (pixels.Length != stream.Read (pixels, 0, pixels.Length))
throw new InvalidFormatException(); throw new InvalidFormatException();
return ImageData.Create (info, PixelFormats.Indexed8, palette, pixels); return ImageData.Create (info, PixelFormats.Indexed8, palette, pixels);