2014-07-21 23:26:28 +04:00
|
|
|
//! \file Image.cs
|
|
|
|
//! \date Tue Jul 01 11:29:52 2014
|
|
|
|
//! \brief image class.
|
|
|
|
//
|
2014-07-28 00:50:18 +04: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-21 23:26:28 +04:00
|
|
|
|
2015-07-25 10:13:56 +04:00
|
|
|
using System;
|
2014-07-21 23:26:28 +04:00
|
|
|
using System.IO;
|
2015-07-25 10:13:56 +04:00
|
|
|
using System.Linq;
|
2015-04-20 14:04:46 +04:00
|
|
|
using System.Windows.Media;
|
2014-07-21 23:26:28 +04:00
|
|
|
using System.Windows.Media.Imaging;
|
|
|
|
|
|
|
|
namespace GameRes
|
|
|
|
{
|
|
|
|
public class ImageMetaData
|
|
|
|
{
|
|
|
|
public uint Width { get; set; }
|
|
|
|
public uint Height { get; set; }
|
|
|
|
public int OffsetX { get; set; }
|
|
|
|
public int OffsetY { get; set; }
|
|
|
|
public int BPP { get; set; }
|
2015-08-14 20:01:45 +04:00
|
|
|
public string FileName { get; set; }
|
2014-07-21 23:26:28 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
public class ImageEntry : Entry
|
|
|
|
{
|
|
|
|
public override string Type { get { return "image"; } }
|
|
|
|
}
|
|
|
|
|
2017-01-14 16:27:11 +04:00
|
|
|
/// <summary>
|
|
|
|
/// Enumeration representing possible palette serialization formats.
|
|
|
|
/// </summary>
|
|
|
|
public enum PaletteFormat
|
|
|
|
{
|
|
|
|
Rgb = 1,
|
|
|
|
Bgr = 2,
|
|
|
|
RgbX = 5,
|
|
|
|
BgrX = 6,
|
|
|
|
}
|
|
|
|
|
2014-07-21 23:26:28 +04: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-03 22:09:16 +04: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-21 23:26:28 +04: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 14:04:46 +04:00
|
|
|
|
|
|
|
public static ImageData Create (ImageMetaData info, PixelFormat format, BitmapPalette palette,
|
2015-07-11 10:20:50 +04:00
|
|
|
byte[] pixel_data, int stride)
|
2015-04-20 14:04:46 +04:00
|
|
|
{
|
2015-05-03 22:09:16 +04:00
|
|
|
var bitmap = BitmapSource.Create ((int)info.Width, (int)info.Height, DefaultDpiX, DefaultDpiY,
|
2015-04-20 14:04:46 +04:00
|
|
|
format, palette, pixel_data, stride);
|
|
|
|
bitmap.Freeze();
|
|
|
|
return new ImageData (bitmap, info);
|
|
|
|
}
|
2015-07-11 10:20:50 +04:00
|
|
|
|
|
|
|
public static ImageData Create (ImageMetaData info, PixelFormat format, BitmapPalette palette,
|
|
|
|
byte[] pixel_data)
|
|
|
|
{
|
|
|
|
return Create (info, format, palette, pixel_data, (int)info.Width*((format.BitsPerPixel+7)/8));
|
|
|
|
}
|
2015-10-12 12:14:57 +04:00
|
|
|
|
|
|
|
public static ImageData CreateFlipped (ImageMetaData info, PixelFormat format, BitmapPalette palette,
|
|
|
|
byte[] pixel_data, int stride)
|
|
|
|
{
|
|
|
|
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-21 23:26:28 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
public abstract class ImageFormat : IResource
|
|
|
|
{
|
|
|
|
public override string Type { get { return "image"; } }
|
|
|
|
|
2016-10-15 09:34:46 +04:00
|
|
|
public abstract ImageMetaData ReadMetaData (IBinaryStream file);
|
2014-07-25 08:56:32 +04:00
|
|
|
|
2016-10-15 09:34:46 +04:00
|
|
|
public abstract ImageData Read (IBinaryStream file, ImageMetaData info);
|
2014-07-25 08:56:32 +04:00
|
|
|
public abstract void Write (Stream file, ImageData bitmap);
|
|
|
|
|
2016-10-15 09:34:46 +04:00
|
|
|
public static ImageData Read (IBinaryStream file)
|
2014-07-21 23:26:28 +04:00
|
|
|
{
|
2016-10-15 09:34:46 +04:00
|
|
|
var format = FindFormat (file);
|
|
|
|
if (null == format)
|
|
|
|
return null;
|
|
|
|
file.Position = 0;
|
|
|
|
return format.Item1.Read (file, format.Item2);
|
2014-07-21 23:26:28 +04:00
|
|
|
}
|
|
|
|
|
2016-10-15 09:34:46 +04:00
|
|
|
public static System.Tuple<ImageFormat, ImageMetaData> FindFormat (IBinaryStream file)
|
2014-07-25 08:56:32 +04:00
|
|
|
{
|
2016-12-17 18:51:33 +04:00
|
|
|
foreach (var impl in FormatCatalog.Instance.FindFormats<ImageFormat> (file.Name, file.Signature))
|
2014-07-25 08:56:32 +04:00
|
|
|
{
|
2016-12-17 18:51:33 +04:00
|
|
|
try
|
2014-07-25 08:56:32 +04:00
|
|
|
{
|
2016-12-17 18:51:33 +04:00
|
|
|
file.Position = 0;
|
|
|
|
ImageMetaData metadata = impl.ReadMetaData (file);
|
|
|
|
if (null != metadata)
|
2014-07-25 08:56:32 +04:00
|
|
|
{
|
2016-12-17 18:51:33 +04:00
|
|
|
metadata.FileName = file.Name;
|
|
|
|
return Tuple.Create (impl, metadata);
|
2014-07-25 08:56:32 +04:00
|
|
|
}
|
|
|
|
}
|
2016-12-17 18:51:33 +04:00
|
|
|
catch (OperationCanceledException)
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
catch { }
|
2014-07-25 08:56:32 +04:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2014-07-21 23:26:28 +04:00
|
|
|
|
|
|
|
public bool IsBuiltin
|
|
|
|
{
|
|
|
|
get { return this.GetType().Assembly == typeof(ImageFormat).Assembly; }
|
|
|
|
}
|
2015-08-14 06:05:15 +04:00
|
|
|
|
|
|
|
public static ImageFormat FindByTag (string tag)
|
|
|
|
{
|
|
|
|
return FormatCatalog.Instance.ImageFormats.FirstOrDefault (x => x.Tag == tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
static readonly Lazy<ImageFormat> s_JpegFormat = new Lazy<ImageFormat> (() => FindByTag ("JPEG"));
|
|
|
|
static readonly Lazy<ImageFormat> s_PngFormat = new Lazy<ImageFormat> (() => FindByTag ("PNG"));
|
|
|
|
static readonly Lazy<ImageFormat> s_BmpFormat = new Lazy<ImageFormat> (() => FindByTag ("BMP"));
|
2015-11-09 00:09:36 +04:00
|
|
|
static readonly Lazy<ImageFormat> s_TgaFormat = new Lazy<ImageFormat> (() => FindByTag ("TGA"));
|
2015-08-14 06:05:15 +04:00
|
|
|
|
|
|
|
public static ImageFormat Jpeg { get { return s_JpegFormat.Value; } }
|
|
|
|
public static ImageFormat Png { get { return s_PngFormat.Value; } }
|
|
|
|
public static ImageFormat Bmp { get { return s_BmpFormat.Value; } }
|
2015-11-09 00:09:36 +04:00
|
|
|
public static ImageFormat Tga { get { return s_TgaFormat.Value; } }
|
2017-01-14 16:27:11 +04: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]);
|
|
|
|
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-21 23:26:28 +04:00
|
|
|
}
|
|
|
|
}
|