mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-23 19:34:15 +08:00
(PICT): implemented macintosh image format.
This commit is contained in:
parent
b7c4c91fa4
commit
323dcd7860
436
ArcFormats/Macintosh/ImagePICT.cs
Normal file
436
ArcFormats/Macintosh/ImagePICT.cs
Normal file
@ -0,0 +1,436 @@
|
||||
//! \file ImagePICT.cs
|
||||
//! \date 2023 Aug 24
|
||||
//! \brief Macintosh picture format.
|
||||
//
|
||||
// Copyright (C) 2023 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Apple
|
||||
{
|
||||
internal class PictMetaData : ImageMetaData
|
||||
{
|
||||
public uint DataOffset;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class PictFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get => "PICT/MAC"; }
|
||||
public override string Description { get => "Apple Macintosh image format"; }
|
||||
public override uint Signature { get => 0; }
|
||||
|
||||
public PictFormat ()
|
||||
{
|
||||
Signatures = new[] { 0u, 0x54434950u };
|
||||
Extensions = new[] { "pct", "pict", "pic" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
int header_pos = 0x200;
|
||||
if (file.Signature == 0x54434950) // 'PICT'
|
||||
header_pos = 4;
|
||||
if (file.Length < header_pos + 0x10)
|
||||
return null;
|
||||
file.Position = header_pos + 2;
|
||||
short top = file.ReadI16BE();
|
||||
short left = file.ReadI16BE();
|
||||
short bottom = file.ReadI16BE();
|
||||
short right = file.ReadI16BE();
|
||||
if (file.ReadU16BE() != 0x11)
|
||||
return null;
|
||||
int version = file.ReadU16BE();
|
||||
if (version != 0x2FF)
|
||||
return null;
|
||||
return new PictMetaData {
|
||||
Width = (uint)(right - left),
|
||||
Height = (uint)(bottom - top),
|
||||
OffsetX = left,
|
||||
OffsetY = top,
|
||||
BPP = 32,
|
||||
DataOffset = (uint)file.Position,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var decoder = new PictReader (file, (PictMetaData)info);
|
||||
var pixels = decoder.Unpack();
|
||||
return ImageData.Create (info, decoder.Format, decoder.Palette, pixels);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("PictFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal static class BinaryStreamExtension
|
||||
{
|
||||
static public short ReadI16BE (this IBinaryStream file)
|
||||
{
|
||||
return Binary.BigEndian (file.ReadInt16());
|
||||
}
|
||||
|
||||
static public ushort ReadU16BE (this IBinaryStream file)
|
||||
{
|
||||
return Binary.BigEndian (file.ReadUInt16());
|
||||
}
|
||||
|
||||
static public int ReadI32BE (this IBinaryStream file)
|
||||
{
|
||||
return Binary.BigEndian (file.ReadInt32());
|
||||
}
|
||||
|
||||
static public uint ReadU32BE (this IBinaryStream file)
|
||||
{
|
||||
return Binary.BigEndian (file.ReadUInt32());
|
||||
}
|
||||
}
|
||||
|
||||
internal class Pixmap
|
||||
{
|
||||
public short Version;
|
||||
public short PackType;
|
||||
public int PackSize;
|
||||
public int HorizRes;
|
||||
public int VertRes;
|
||||
public short PixelType;
|
||||
public short BPP;
|
||||
public short CompCount;
|
||||
public short CompSize;
|
||||
public int PlaneBytes;
|
||||
public int Table;
|
||||
|
||||
public void Deserialize (IBinaryStream input)
|
||||
{
|
||||
Version = input.ReadI16BE();
|
||||
PackType = input.ReadI16BE();
|
||||
PackSize = input.ReadI32BE();
|
||||
HorizRes = input.ReadI32BE() >> 16; // read 2 bytes and skip next 2
|
||||
VertRes = input.ReadI32BE() >> 16;
|
||||
PixelType = input.ReadI16BE();
|
||||
BPP = input.ReadI16BE();
|
||||
CompCount = input.ReadI16BE();
|
||||
CompSize = input.ReadI16BE();
|
||||
PlaneBytes = input.ReadI32BE();
|
||||
Table = input.ReadI32BE();
|
||||
input.Seek (4, SeekOrigin.Current);
|
||||
if (BPP <= 0 || BPP > 32 || CompCount <= 0 || CompCount > 4 || CompSize <= 0)
|
||||
throw new InvalidFormatException();
|
||||
}
|
||||
}
|
||||
|
||||
internal class PictReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
PictMetaData m_info;
|
||||
|
||||
public PixelFormat Format { get; private set; }
|
||||
public BitmapPalette Palette { get; private set; }
|
||||
|
||||
public PictReader (IBinaryStream input, PictMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
bool HasAlpha = false;
|
||||
byte[] m_buffer;
|
||||
|
||||
public byte[] Unpack ()
|
||||
{
|
||||
Color[] colormap = null;
|
||||
Pixmap pixmap = null;
|
||||
m_input.Position = m_info.DataOffset;
|
||||
|
||||
while (m_input.PeekByte() != -1)
|
||||
{
|
||||
if ((m_input.Position & 1) != 0)
|
||||
Skip (1);
|
||||
int code = m_input.ReadU16BE();
|
||||
if (0x00FF == code || 0xFFFF == code) // EOF
|
||||
break;
|
||||
switch (code)
|
||||
{
|
||||
case 0x0000: // NOP
|
||||
continue;
|
||||
|
||||
case 0x0001: // Clip
|
||||
{
|
||||
int length = m_input.ReadU16BE();
|
||||
if (length < 2)
|
||||
throw new InvalidFormatException();
|
||||
Skip (length-2);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0090:
|
||||
case 0x0091:
|
||||
case 0x0098:
|
||||
case 0x0099:
|
||||
case 0x009A:
|
||||
case 0x009B: // BitsRect
|
||||
{
|
||||
int stride = 0;
|
||||
if (code != 0x9A && code != 0x9B)
|
||||
stride = m_input.ReadU16BE();
|
||||
else
|
||||
Skip (6);
|
||||
// FIXME we just read the first bitmap and override an existing frame
|
||||
// TODO place bitmap into frame according to its RECT
|
||||
m_info.OffsetY = m_input.ReadI16BE();
|
||||
m_info.OffsetX = m_input.ReadI16BE();
|
||||
m_info.Height = (uint)(m_input.ReadI16BE() - m_info.OffsetY);
|
||||
m_info.Width = (uint)(m_input.ReadI16BE() - m_info.OffsetX);
|
||||
if (0x9A == code || 0x9B == code || (stride & 0x8000) != 0)
|
||||
{
|
||||
pixmap = new Pixmap();
|
||||
pixmap.Deserialize (m_input);
|
||||
HasAlpha = pixmap.CompCount == 4;
|
||||
}
|
||||
if (code != 0x9A && code != 0x9B)
|
||||
{
|
||||
int colors = 2;
|
||||
int flags = 0;
|
||||
if ((stride & 0x8000) != 0)
|
||||
{
|
||||
Skip (4);
|
||||
flags = m_input.ReadU16BE();
|
||||
colors = m_input.ReadU16BE() + 1;
|
||||
}
|
||||
if (null == colormap)
|
||||
colormap = new Color[colors];
|
||||
if ((stride & 0x8000) != 0)
|
||||
{
|
||||
for (int i = 0; i < colors; i++)
|
||||
{
|
||||
int c = m_input.ReadU16BE() % colors;
|
||||
if ((flags & 0x8000) != 0)
|
||||
c = i;
|
||||
int r = m_input.ReadU16BE() / 0x101;
|
||||
int g = m_input.ReadU16BE() / 0x101;
|
||||
int b = m_input.ReadU16BE() / 0x101;
|
||||
colormap[c] = Color.FromRgb ((byte)r, (byte)g, (byte)b);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var White = Color.FromRgb (0xFF, 0xFF, 0xFF);
|
||||
for (int i = 0; i < colors; i++)
|
||||
{
|
||||
colormap[i] = Color.Subtract (White, colormap[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Skip (8+8+2);
|
||||
// -> Skip (8); // source RECT
|
||||
// Skip (8); // destination RECT
|
||||
// Skip (2); // transfer mode
|
||||
if (code == 0x91 || code == 0x99 || code == 0x9b)
|
||||
{
|
||||
int length = m_input.ReadU16BE();
|
||||
if (length > 2)
|
||||
Skip (length - 2);
|
||||
}
|
||||
if (code != 0x9A && code != 0x9B && (stride & 0x8000) == 0)
|
||||
DecodeRleBitmap (stride, 1);
|
||||
else
|
||||
DecodeRleBitmap (stride, pixmap.BPP);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x00A1: // LongComment
|
||||
{
|
||||
m_input.ReadU16BE(); // comment type
|
||||
int length = m_input.ReadU16BE();
|
||||
Skip (length);
|
||||
break;
|
||||
}
|
||||
case 0x0C00: // Header
|
||||
Skip (0x18);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException (string.Format ("Unknown code 0x{0:X4} in PICT stream.", code));
|
||||
}
|
||||
}
|
||||
if (colormap != null)
|
||||
Palette = new BitmapPalette (colormap);
|
||||
if (null == m_buffer)
|
||||
throw new InvalidFormatException();
|
||||
SetFormat (pixmap);
|
||||
return RepackPixels (pixmap);
|
||||
}
|
||||
|
||||
byte[] RepackPixels (Pixmap pixmap)
|
||||
{
|
||||
int bpp = m_info.BPP;
|
||||
if (bpp <= 16)
|
||||
return m_buffer;
|
||||
int bytes_per_pixel = bpp / 8;
|
||||
int stride = m_info.iWidth * bytes_per_pixel;
|
||||
var pixels = new byte[stride * m_info.iHeight];
|
||||
int src = 0;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int dst = y * stride;
|
||||
for (int x = 0; x < m_info.iWidth; ++x)
|
||||
{
|
||||
if (HasAlpha)
|
||||
{
|
||||
pixels[dst+3] = m_buffer[src];
|
||||
pixels[dst+2] = m_buffer[src+m_info.iWidth];
|
||||
pixels[dst+1] = m_buffer[src+m_info.iWidth*2];
|
||||
pixels[dst] = m_buffer[src+m_info.iWidth*3];
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels[dst+2] = m_buffer[src];
|
||||
pixels[dst+1] = m_buffer[src+m_info.iWidth];
|
||||
pixels[dst] = m_buffer[src+m_info.iWidth*2];
|
||||
}
|
||||
++src;
|
||||
dst += bytes_per_pixel;
|
||||
}
|
||||
src += (pixmap.CompCount - 1) * m_info.iWidth;
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
void SetFormat (Pixmap pixmap)
|
||||
{
|
||||
int bpp = null == pixmap ? 8 : pixmap.BPP;
|
||||
if (32 == bpp)
|
||||
{
|
||||
if (4 == pixmap.CompCount)
|
||||
Format = PixelFormats.Bgra32;
|
||||
else
|
||||
Format = PixelFormats.Bgr32;
|
||||
}
|
||||
else if (24 == bpp)
|
||||
Format = PixelFormats.Bgr24;
|
||||
else if (16 == bpp)
|
||||
Format = PixelFormats.Bgr555;
|
||||
else if (8 == bpp)
|
||||
{
|
||||
if (Palette != null)
|
||||
Format = PixelFormats.Indexed8;
|
||||
else
|
||||
Format = PixelFormats.Gray8;
|
||||
}
|
||||
else
|
||||
throw new NotSupportedException (string.Format ("Not supported PICT bitdepth -- {0}bpp", bpp));
|
||||
m_info.BPP = bpp;
|
||||
}
|
||||
|
||||
void Skip (int amount)
|
||||
{
|
||||
m_input.Seek (amount, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
byte[] m_unpack_buffer = new byte[0x800];
|
||||
byte[] m_scanline;
|
||||
|
||||
void DecodeRleBitmap (int stride, int bpp)
|
||||
{
|
||||
if (bpp < 8)
|
||||
throw new NotSupportedException();
|
||||
if (bpp <= 8)
|
||||
stride &= 0x7fff;
|
||||
int width = m_info.iWidth;
|
||||
int bytes_per_pixel = 1;
|
||||
if (16 == bpp)
|
||||
{
|
||||
bytes_per_pixel = 2;
|
||||
width *= 2;
|
||||
}
|
||||
else if (32 == bpp)
|
||||
width *= HasAlpha ? 4 : 3;
|
||||
if (stride == 0)
|
||||
stride = width;
|
||||
int stride_32bpp = m_info.iWidth * 4;
|
||||
|
||||
int total_bytes = stride_32bpp * m_info.iHeight;
|
||||
if (null == m_buffer || m_buffer.Length < total_bytes)
|
||||
m_buffer = new byte[total_bytes];
|
||||
int scanline_length = stride_32bpp * 2;
|
||||
if (null == m_scanline || m_scanline.Length < scanline_length)
|
||||
{
|
||||
m_scanline = new byte[scanline_length];
|
||||
}
|
||||
if (stride < 8)
|
||||
{
|
||||
int dst = 0;
|
||||
int row_size = width * (bpp / 8);
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
m_input.Read (m_buffer, dst, stride);
|
||||
dst += row_size;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int dst = y * width;
|
||||
if (stride > 200)
|
||||
scanline_length = m_input.ReadU16BE();
|
||||
else
|
||||
scanline_length = m_input.ReadUInt8();
|
||||
if (scanline_length >= m_scanline.Length || scanline_length == 0)
|
||||
throw new InvalidFormatException();
|
||||
m_input.Read (m_scanline, 0, scanline_length);
|
||||
for (int j = 0; j < scanline_length; )
|
||||
{
|
||||
if ((m_scanline[j] & 0x80) == 0)
|
||||
{
|
||||
int pixel_count = m_scanline[j] + 1;
|
||||
int count = pixel_count * bytes_per_pixel;
|
||||
int src = j + 1;
|
||||
if ((dst + count) <= total_bytes)
|
||||
Buffer.BlockCopy (m_scanline, src, m_buffer, dst, count);
|
||||
dst += count;
|
||||
j += count + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = (m_scanline[j] ^ 0xFF) + 2;
|
||||
int src = j + 1;
|
||||
while (count --> 0)
|
||||
{
|
||||
if ((dst + bytes_per_pixel) <= total_bytes)
|
||||
Buffer.BlockCopy (m_scanline, src, m_buffer, dst, bytes_per_pixel);
|
||||
dst += bytes_per_pixel;
|
||||
}
|
||||
j += bytes_per_pixel + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user