2014-07-22 03:26:28 +08:00
|
|
|
//! \file Image.cs
|
|
|
|
//! \date Tue Jul 01 11:29:52 2014
|
|
|
|
//! \brief image class.
|
|
|
|
//
|
2014-07-28 04:50:18 +08:00
|
|
|
// Copyright (C) 2014 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.
|
|
|
|
//
|
2014-07-22 03:26:28 +08:00
|
|
|
|
2015-07-25 14:13:56 +08:00
|
|
|
using System;
|
2014-07-22 03:26:28 +08:00
|
|
|
using System.IO;
|
2015-07-25 14:13:56 +08:00
|
|
|
using System.Linq;
|
2015-04-20 18:04:46 +08:00
|
|
|
using System.Windows.Media;
|
2014-07-22 03:26:28 +08:00
|
|
|
using System.Windows.Media.Imaging;
|
|
|
|
|
|
|
|
namespace GameRes
|
|
|
|
{
|
|
|
|
public class ImageMetaData
|
|
|
|
{
|
2018-12-19 07:25:39 +08:00
|
|
|
/// <summary>Image width in pixels.</summary>
|
2014-07-22 03:26:28 +08:00
|
|
|
public uint Width { get; set; }
|
2018-12-19 07:25:39 +08:00
|
|
|
|
|
|
|
/// <summary>Image height in pixels.</summary>
|
2014-07-22 03:26:28 +08:00
|
|
|
public uint Height { get; set; }
|
2018-12-19 07:25:39 +08:00
|
|
|
|
|
|
|
/// <summary>Horizontal coordinate of the image top left corner.</summary>
|
2014-07-22 03:26:28 +08:00
|
|
|
public int OffsetX { get; set; }
|
2018-12-19 07:25:39 +08:00
|
|
|
|
|
|
|
/// <summary>Vertical coordinate of the image top left corner.</summary>
|
2014-07-22 03:26:28 +08:00
|
|
|
public int OffsetY { get; set; }
|
2018-12-19 07:25:39 +08:00
|
|
|
|
|
|
|
/// <summary>Image bitdepth.</summary>
|
2014-07-22 03:26:28 +08:00
|
|
|
public int BPP { get; set; }
|
2018-12-19 07:25:39 +08:00
|
|
|
|
|
|
|
/// <summary>Image source file name, if any.</summary>
|
2015-08-15 00:01:45 +08:00
|
|
|
public string FileName { get; set; }
|
2018-12-19 07:25:39 +08:00
|
|
|
|
|
|
|
public int iWidth { get { return (int)Width; } }
|
|
|
|
public int iHeight { get { return (int)Height; } }
|
2014-07-22 03:26:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public class ImageEntry : Entry
|
|
|
|
{
|
|
|
|
public override string Type { get { return "image"; } }
|
|
|
|
}
|
|
|
|
|
2017-01-14 20:27:11 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Enumeration representing possible palette serialization formats.
|
|
|
|
/// </summary>
|
|
|
|
public enum PaletteFormat
|
|
|
|
{
|
|
|
|
Rgb = 1,
|
|
|
|
Bgr = 2,
|
|
|
|
RgbX = 5,
|
|
|
|
BgrX = 6,
|
2017-03-09 14:48:53 +08:00
|
|
|
RgbA = 9,
|
|
|
|
BgrA = 10,
|
2017-01-14 20:27:11 +08:00
|
|
|
}
|
|
|
|
|
2014-07-22 03:26:28 +08:00
|
|
|
public class ImageData
|
|
|
|
{
|
|
|
|
private BitmapSource m_bitmap;
|
|
|
|
|
|
|
|
public BitmapSource Bitmap { get { return m_bitmap; } }
|
|
|
|
public uint Width { get { return (uint)m_bitmap.PixelWidth; } }
|
|
|
|
public uint Height { get { return (uint)m_bitmap.PixelHeight; } }
|
|
|
|
public int OffsetX { get; set; }
|
|
|
|
public int OffsetY { get; set; }
|
|
|
|
public int BPP { get { return m_bitmap.Format.BitsPerPixel; } }
|
|
|
|
|
2015-05-04 02:09:16 +08:00
|
|
|
public static double DefaultDpiX { get; set; }
|
|
|
|
public static double DefaultDpiY { get; set; }
|
|
|
|
|
|
|
|
static ImageData ()
|
|
|
|
{
|
|
|
|
SetDefaultDpi (96, 96);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void SetDefaultDpi (double x, double y)
|
|
|
|
{
|
|
|
|
DefaultDpiX = x;
|
|
|
|
DefaultDpiY = y;
|
|
|
|
}
|
|
|
|
|
2014-07-22 03:26:28 +08:00
|
|
|
public ImageData (BitmapSource data, ImageMetaData meta)
|
|
|
|
{
|
|
|
|
m_bitmap = data;
|
|
|
|
OffsetX = meta.OffsetX;
|
|
|
|
OffsetY = meta.OffsetY;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ImageData (BitmapSource data, int x = 0, int y = 0)
|
|
|
|
{
|
|
|
|
m_bitmap = data;
|
|
|
|
OffsetX = x;
|
|
|
|
OffsetY = y;
|
|
|
|
}
|
2015-04-20 18:04:46 +08:00
|
|
|
|
|
|
|
public static ImageData Create (ImageMetaData info, PixelFormat format, BitmapPalette palette,
|
2017-11-26 20:58:43 +08:00
|
|
|
Array pixel_data, int stride)
|
2015-04-20 18:04:46 +08:00
|
|
|
{
|
2015-05-04 02:09:16 +08:00
|
|
|
var bitmap = BitmapSource.Create ((int)info.Width, (int)info.Height, DefaultDpiX, DefaultDpiY,
|
2015-04-20 18:04:46 +08:00
|
|
|
format, palette, pixel_data, stride);
|
|
|
|
bitmap.Freeze();
|
|
|
|
return new ImageData (bitmap, info);
|
|
|
|
}
|
2015-07-11 14:20:50 +08:00
|
|
|
|
|
|
|
public static ImageData Create (ImageMetaData info, PixelFormat format, BitmapPalette palette,
|
2017-11-26 20:58:43 +08:00
|
|
|
Array pixel_data)
|
2015-07-11 14:20:50 +08:00
|
|
|
{
|
|
|
|
return Create (info, format, palette, pixel_data, (int)info.Width*((format.BitsPerPixel+7)/8));
|
|
|
|
}
|
2015-10-12 16:14:57 +08:00
|
|
|
|
|
|
|
public static ImageData CreateFlipped (ImageMetaData info, PixelFormat format, BitmapPalette palette,
|
2017-11-26 20:58:43 +08:00
|
|
|
Array pixel_data, int stride)
|
2015-10-12 16:14:57 +08:00
|
|
|
{
|
|
|
|
var bitmap = BitmapSource.Create ((int)info.Width, (int)info.Height, DefaultDpiX, DefaultDpiY,
|
|
|
|
format, palette, pixel_data, stride);
|
|
|
|
var flipped = new TransformedBitmap (bitmap, new ScaleTransform { ScaleY = -1 });
|
|
|
|
flipped.Freeze();
|
|
|
|
return new ImageData (flipped, info);
|
|
|
|
}
|
2014-07-22 03:26:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public abstract class ImageFormat : IResource
|
|
|
|
{
|
|
|
|
public override string Type { get { return "image"; } }
|
|
|
|
|
2016-10-15 13:34:46 +08:00
|
|
|
public abstract ImageMetaData ReadMetaData (IBinaryStream file);
|
2014-07-25 12:56:32 +08:00
|
|
|
|
2016-10-15 13:34:46 +08:00
|
|
|
public abstract ImageData Read (IBinaryStream file, ImageMetaData info);
|
2014-07-25 12:56:32 +08:00
|
|
|
public abstract void Write (Stream file, ImageData bitmap);
|
|
|
|
|
2016-10-15 13:34:46 +08:00
|
|
|
public static ImageData Read (IBinaryStream file)
|
2014-07-22 03:26:28 +08:00
|
|
|
{
|
2016-10-15 13:34:46 +08:00
|
|
|
var format = FindFormat (file);
|
|
|
|
if (null == format)
|
|
|
|
return null;
|
|
|
|
file.Position = 0;
|
|
|
|
return format.Item1.Read (file, format.Item2);
|
2014-07-22 03:26:28 +08:00
|
|
|
}
|
|
|
|
|
2016-10-15 13:34:46 +08:00
|
|
|
public static System.Tuple<ImageFormat, ImageMetaData> FindFormat (IBinaryStream file)
|
2014-07-25 12:56:32 +08:00
|
|
|
{
|
2016-12-17 22:51:33 +08:00
|
|
|
foreach (var impl in FormatCatalog.Instance.FindFormats<ImageFormat> (file.Name, file.Signature))
|
2014-07-25 12:56:32 +08:00
|
|
|
{
|
2016-12-17 22:51:33 +08:00
|
|
|
try
|
2014-07-25 12:56:32 +08:00
|
|
|
{
|
2016-12-17 22:51:33 +08:00
|
|
|
file.Position = 0;
|
|
|
|
ImageMetaData metadata = impl.ReadMetaData (file);
|
|
|
|
if (null != metadata)
|
2014-07-25 12:56:32 +08:00
|
|
|
{
|
2016-12-17 22:51:33 +08:00
|
|
|
metadata.FileName = file.Name;
|
|
|
|
return Tuple.Create (impl, metadata);
|
2014-07-25 12:56:32 +08:00
|
|
|
}
|
|
|
|
}
|
2016-12-17 22:51:33 +08:00
|
|
|
catch (OperationCanceledException)
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
catch { }
|
2014-07-25 12:56:32 +08:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2014-07-22 03:26:28 +08:00
|
|
|
|
|
|
|
public bool IsBuiltin
|
|
|
|
{
|
|
|
|
get { return this.GetType().Assembly == typeof(ImageFormat).Assembly; }
|
|
|
|
}
|
2015-08-14 10:05:15 +08:00
|
|
|
|
|
|
|
public static ImageFormat FindByTag (string tag)
|
|
|
|
{
|
|
|
|
return FormatCatalog.Instance.ImageFormats.FirstOrDefault (x => x.Tag == tag);
|
|
|
|
}
|
|
|
|
|
2023-10-11 22:53:12 +08:00
|
|
|
static readonly ResourceInstance<ImageFormat> s_JpegFormat = new ResourceInstance<ImageFormat> ("JPEG");
|
|
|
|
static readonly ResourceInstance<ImageFormat> s_PngFormat = new ResourceInstance<ImageFormat> ("PNG");
|
|
|
|
static readonly ResourceInstance<ImageFormat> s_BmpFormat = new ResourceInstance<ImageFormat> ("BMP");
|
|
|
|
static readonly ResourceInstance<ImageFormat> s_TgaFormat = new ResourceInstance<ImageFormat> ("TGA");
|
|
|
|
|
|
|
|
public static ImageFormat Jpeg => s_JpegFormat.Value;
|
|
|
|
public static ImageFormat Png => s_PngFormat.Value;
|
|
|
|
public static ImageFormat Bmp => s_BmpFormat.Value;
|
|
|
|
public static ImageFormat Tga => s_TgaFormat.Value;
|
2017-01-14 20:27:11 +08:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Desereialize color map from <paramref name="input"/> stream, consisting of specified number of
|
|
|
|
/// <paramref name="colors"/> stored in specified <paramref name="format"/>.
|
|
|
|
/// Default number of colors is 256 and format is 4-byte BGRX (where X is an unsignificant byte).
|
|
|
|
/// </summary>
|
|
|
|
public static Color[] ReadColorMap (Stream input, int colors = 0x100, PaletteFormat format = PaletteFormat.BgrX)
|
|
|
|
{
|
|
|
|
int bpp = PaletteFormat.Rgb == format || PaletteFormat.Bgr == format ? 3 : 4;
|
|
|
|
var palette_data = new byte[bpp * colors];
|
|
|
|
if (palette_data.Length != input.Read (palette_data, 0, palette_data.Length))
|
|
|
|
throw new EndOfStreamException();
|
|
|
|
int src = 0;
|
|
|
|
var color_map = new Color[colors];
|
|
|
|
Func<int, Color> get_color;
|
|
|
|
if (PaletteFormat.Bgr == format || PaletteFormat.BgrX == format)
|
|
|
|
get_color = x => Color.FromRgb (palette_data[x+2], palette_data[x+1], palette_data[x]);
|
2017-03-09 14:48:53 +08:00
|
|
|
else if (PaletteFormat.BgrA == format)
|
|
|
|
get_color = x => Color.FromArgb (palette_data[x+3], palette_data[x+2], palette_data[x+1], palette_data[x]);
|
|
|
|
else if (PaletteFormat.RgbA == format)
|
|
|
|
get_color = x => Color.FromArgb (palette_data[x+3], palette_data[x], palette_data[x+1], palette_data[x+2]);
|
2017-01-14 20:27:11 +08:00
|
|
|
else
|
|
|
|
get_color = x => Color.FromRgb (palette_data[x], palette_data[x+1], palette_data[x+2]);
|
|
|
|
|
|
|
|
for (int i = 0; i < colors; ++i)
|
|
|
|
{
|
|
|
|
color_map[i] = get_color (src);
|
|
|
|
src += bpp;
|
|
|
|
}
|
|
|
|
return color_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static BitmapPalette ReadPalette (Stream input, int colors = 0x100, PaletteFormat format = PaletteFormat.BgrX)
|
|
|
|
{
|
|
|
|
return new BitmapPalette (ReadColorMap (input, colors, format));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static BitmapPalette ReadPalette (ArcView file, long offset, int colors = 0x100, PaletteFormat format = PaletteFormat.BgrX)
|
|
|
|
{
|
|
|
|
using (var input = file.CreateStream (offset, (uint)(4 * colors))) // largest possible size for palette
|
|
|
|
return ReadPalette (input, colors, format);
|
|
|
|
}
|
2014-07-22 03:26:28 +08:00
|
|
|
}
|
|
|
|
}
|