From 5df1bc735b680cb4ed6074d83e6eb06708b8bec7 Mon Sep 17 00:00:00 2001 From: morkt Date: Thu, 18 Oct 2018 23:07:26 +0400 Subject: [PATCH] (KGD): use PngBitmapDecoder to decode image. --- Legacy/KeroQ/ImageKGD.cs | 174 +++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 88 deletions(-) diff --git a/Legacy/KeroQ/ImageKGD.cs b/Legacy/KeroQ/ImageKGD.cs index 2ac93d8c..79a2622b 100644 --- a/Legacy/KeroQ/ImageKGD.cs +++ b/Legacy/KeroQ/ImageKGD.cs @@ -26,13 +26,20 @@ using System; using System.ComponentModel.Composition; using System.IO; -using System.Windows.Media; -using GameRes.Compression; +using System.Windows.Media.Imaging; +using GameRes.Utility; // [011130][KeroQ] Nijuubako +// [030131][KeroQ] Moekan namespace GameRes.Formats.KeroQ { + internal class KgdMetaData : ImageMetaData + { + public byte BitsPerPlane; + public byte ColorType; + } + [Export(typeof(ImageFormat))] public class KgdFormat : ImageFormat { @@ -45,90 +52,50 @@ namespace GameRes.Formats.KeroQ var header = file.ReadHeader (0x19); if (header.ToInt32 (4) != 0x10 || header[8] != 1) return null; - return new ImageMetaData { + byte bpp = header[0x11]; + byte color_type = header[0x12]; + switch (color_type) + { + case 2: bpp *= 3; break; + case 3: bpp = 24; break; + case 4: bpp *= 2; break; + case 6: bpp *= 4; break; + case 0: break; + default: return null; + } + return new KgdMetaData { Width = header.ToUInt32 (9), Height = header.ToUInt32 (0xD), - BPP = 2 == header[0x12] ? 24 : 32, + BPP = bpp, + BitsPerPlane = header[0x11], + ColorType = color_type, }; } public override ImageData Read (IBinaryStream file, ImageMetaData info) { - file.Position = 0x19; - using (var packed = new KgdStream (file)) - using (var input = new ZLibStream (packed, CompressionMode.Decompress)) + using (var png = new PngRestoreStream (file, (KgdMetaData)info)) { - int stride = ((int)info.Width * info.BPP + 7) / 8; - int pixel_size = (info.BPP + 7) / 8; - var buffer = new byte[stride+1]; - var prev_line = new byte[stride]; - var pixels = new byte[stride * (int)info.Height]; - int dst = 0; - for (uint i = 0; i < info.Height; ++i) + var decoder = new PngBitmapDecoder (png, + BitmapCreateOptions.None, BitmapCacheOption.OnLoad); + var frame = decoder.Frames[0]; + int pixel_size = (frame.Format.BitsPerPixel + 7) / 8; + if (pixel_size < 3) { - if (input.Read (buffer, 0, buffer.Length) == 0) - break; - switch (buffer[0]) - { - case 1: // PNG_FILTER_VALUE_SUB - for (int j = pixel_size; j < stride; ++j) - { - buffer[1+j] += buffer[1+j-pixel_size]; - } - break; - - case 2: // PNG_FILTER_VALUE_UP - for (int j = 0; j < stride; ++j) - { - buffer[1+j] += prev_line[j]; - } - break; - - case 3: // PNG_FILTER_VALUE_AVG - for (int j = 0; j < pixel_size; ++j) - { - buffer[1+j] += (byte)(prev_line[j] >> 1); - } - for (int j = pixel_size; j < stride; ++j) - { - int v = (prev_line[j] + buffer[1+j-pixel_size]) >> 1; - buffer[1+j] += (byte)v; - } - break; - - case 4: // PNG_FILTER_VALUE_PAETH - for (int j = 0; j < pixel_size; ++j) - { - buffer[1+j] += prev_line[j]; - } - int src = 1; - for (int j = pixel_size; j < stride; ++j) - { - byte y = prev_line[j]; - byte x = buffer[src++]; - byte z = prev_line[j-pixel_size]; - int yz = y - z; - int xz = x - z; - int ayz = Math.Abs (yz); - int axz = Math.Abs (xz); - int axy = Math.Abs (xz + yz); - if (!(ayz > axz || ayz > axy)) - z = x; - else if (axz <= axy) - z = y; - buffer[1+j] += (byte)z; - } - break; - - case 0: - break; - } - Buffer.BlockCopy (buffer, 1, prev_line, 0, stride); - Buffer.BlockCopy (buffer, 1, pixels, dst, stride); - dst += stride; + frame.Freeze(); + return new ImageData (frame, info); } - PixelFormat format = 24 == info.BPP ? PixelFormats.Bgr24 : PixelFormats.Bgra32; - return ImageData.Create (info, PixelFormats.Bgr24, null, pixels, stride); + int stride = frame.PixelWidth * pixel_size; + var pixels = new byte[stride * frame.PixelHeight]; + frame.CopyPixels (pixels, stride, 0); + for (int dst = 0; dst < pixels.Length; dst += stride) + for (int i = 0; i < stride; i += pixel_size) + { + byte r = pixels[dst+i]; + pixels[dst+i] = pixels[dst+i+2]; + pixels[dst+i+2] = r; + } + return ImageData.Create (info, frame.Format, frame.Palette, pixels, stride); } } @@ -138,17 +105,19 @@ namespace GameRes.Formats.KeroQ } } - internal class KgdStream : InputProxyStream + internal class PngRestoreStream : InputProxyStream { IBinaryStream m_input; bool m_eof = false; - byte[] m_buffer = new byte[0x2000]; + byte[] m_buffer = new byte[0x200C]; int m_buffer_pos = 0; int m_buffer_size = 0; - public KgdStream (IBinaryStream input) : base (input.AsStream, true) + public PngRestoreStream (IBinaryStream input, KgdMetaData info) : base (input.AsStream, true) { m_input = input; + PrepareHeader (info); + m_input.Position = 0x19; } public override bool CanSeek { get { return false; } } @@ -156,12 +125,15 @@ namespace GameRes.Formats.KeroQ public override int Read (byte[] buffer, int offset, int count) { int read = 0; - while (!m_eof && count > 0) + while (count > 0) { if (m_buffer_pos >= m_buffer_size) { + if (m_eof) + break; FillBuffer(); - continue; + if (0 == m_buffer_size) + break; } int avail = Math.Min (count, m_buffer_size - m_buffer_pos); Buffer.BlockCopy (m_buffer, m_buffer_pos, buffer, offset, avail); @@ -175,22 +147,29 @@ namespace GameRes.Formats.KeroQ public override int ReadByte () { - if (m_eof) - return -1; if (m_buffer_pos >= m_buffer_size) { - FillBuffer(); if (m_eof) return -1; + FillBuffer(); + if (0 == m_buffer_size) + return -1; } return m_buffer[m_buffer_pos++]; } void FillBuffer () { + m_buffer_pos = m_buffer_size = 0; + if (m_eof) + return; if (m_input.PeekByte() == -1) { m_eof = true; + BigEndian.Pack (0, m_buffer, 0); + BigEndian.Pack (0x49454E44, m_buffer, 4); // 'IEND' + BigEndian.Pack (0xAE426082, m_buffer, 8); + m_buffer_size = 12; return; } int chunk_size = m_input.ReadInt32(); @@ -200,10 +179,29 @@ namespace GameRes.Formats.KeroQ m_eof = true; return; } - if (chunk_size > m_buffer.Length) - m_buffer = new byte[chunk_size]; - m_buffer_size = m_input.Read (m_buffer, 0, chunk_size); - m_buffer_pos = 0; + if (chunk_size + 12 > m_buffer.Length) + m_buffer = new byte[chunk_size+12]; + chunk_size = m_input.Read (m_buffer, 8, chunk_size); + BigEndian.Pack (chunk_size, m_buffer, 0); + BigEndian.Pack (0x49444154, m_buffer, 4); // 'IDAT' + uint checksum = Adler32.Compute (m_buffer, 8, chunk_size); + BigEndian.Pack (checksum, m_buffer, 8+chunk_size); + m_buffer_size = 12+chunk_size; + return; + } + + void PrepareHeader (KgdMetaData info) + { + Buffer.BlockCopy (PngFormat.HeaderBytes, 0, m_buffer, 0, 8); + BigEndian.Pack (0x0D, m_buffer, 8); + BigEndian.Pack (0x49484452, m_buffer, 0x0C); // 'IHDR' + BigEndian.Pack (info.Width, m_buffer, 0x10); + BigEndian.Pack (info.Height, m_buffer, 0x14); + m_buffer[0x18] = info.BitsPerPlane; + m_buffer[0x19] = info.ColorType; + uint checksum = Adler32.Compute (m_buffer, 0x10, 0x0D); + BigEndian.Pack (checksum, m_buffer, 0x1D); + m_buffer_size = 0x21; } } }