diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index f74ca076..891d1f2b 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -268,6 +268,7 @@ + diff --git a/ArcFormats/KiriKiri/ImageTLG.cs b/ArcFormats/KiriKiri/ImageTLG.cs index 0712af3e..47d208c0 100644 --- a/ArcFormats/KiriKiri/ImageTLG.cs +++ b/ArcFormats/KiriKiri/ImageTLG.cs @@ -8,6 +8,7 @@ // C# port by morkt // +using GameRes.Formats.QoiCodec; using GameRes.Utility; using K4os.Compression.LZ4; using System; @@ -1220,95 +1221,6 @@ namespace GameRes.Formats.KiriKiri } } - static class QoiCodec - { - public const int Index = 0x00; - public const int Diff = 0x40; - public const int Luma = 0x80; - public const int Run = 0xC0; - public const int Rgb = 0xFE; - public const int Rgba = 0xFF; - public const int Mask2 = 0xC0; - public const int HashTableSize = 64; - } - - class QoiDecodeStream - { - readonly IBinaryStream m_input; - readonly byte[] m_table; - uint m_pixel; - - public QoiDecodeStream (IBinaryStream input) - { - m_input = input; - m_table = new byte [4*QoiCodec.HashTableSize]; - m_pixel = 0xFF000000; - } - - public int Read (out uint output) - { - var r = (byte)m_pixel; - var g = (byte)(m_pixel >> 8); - var b = (byte)(m_pixel >> 16); - var a = (byte)(m_pixel >> 24); - var run = 1; - var b1 = m_input.ReadByte (); - if (-1 == b1) - throw new EndOfStreamException (); - if (QoiCodec.Rgb == b1) - { - var rgb = m_input.ReadInt24 (); - r = (byte)rgb; - g = (byte)(rgb >> 8); - b = (byte)(rgb >> 16); - } - else if (QoiCodec.Rgba == b1) - { - var rgba = m_input.ReadInt32 (); - r = (byte)rgba; - g = (byte)(rgba >> 8); - b = (byte)(rgba >> 16); - a = (byte)(rgba >> 24); - } - else if (QoiCodec.Index == (b1 & QoiCodec.Mask2)) - { - var p1 = (b1 & ~QoiCodec.Mask2) * 4; - r = m_table[p1 ]; - g = m_table[p1+1]; - b = m_table[p1+2]; - a = m_table[p1+3]; - } - else if (QoiCodec.Diff == (b1 & QoiCodec.Mask2)) - { - r += (byte)(((b1 >> 4) & 0x03) - 2); - g += (byte)(((b1 >> 2) & 0x03) - 2); - b += (byte)((b1 & 0x03) - 2); - } - else if (QoiCodec.Luma == (b1 & QoiCodec.Mask2)) - { - var b2 = m_input.ReadByte (); - if (-1 == b2) - throw new EndOfStreamException (); - var vg = (b1 & 0x3F) - 32; - r += (byte)(vg - 8 + ((b2 >> 4) & 0x0F)); - g += (byte)vg; - b += (byte)(vg - 8 + (b2 & 0x0F)); - } - else if (QoiCodec.Run == (b1 & QoiCodec.Mask2)) - { - run = (b1 & 0x3F) + 1; - } - var p2 = (r*3 + g*5 + b*7 + a*11) % QoiCodec.HashTableSize*4; - m_table[p2 ] = r; - m_table[p2+1] = g; - m_table[p2+2] = b; - m_table[p2+3] = a; - m_pixel = (uint)(r | (g << 8) | (b << 16) | (a << 24)); - output = (uint)(b | (g << 8) | (r << 16) | (a << 24)); - return run; - } - } - class QoiBlockDecoder { readonly QoiDecodeStream m_qoi; diff --git a/ArcFormats/QoiCodec.cs b/ArcFormats/QoiCodec.cs new file mode 100644 index 00000000..377448bf --- /dev/null +++ b/ArcFormats/QoiCodec.cs @@ -0,0 +1,118 @@ +//! \file QoiCodec.cs +//! \date Sun Apr 12 2026 +//! \brief Quite OK Image Format +// +// Copyright (C) 2016 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 System.IO; + +namespace GameRes.Formats.QoiCodec +{ + static class QoiConst + { + public const int Index = 0x00; + public const int Diff = 0x40; + public const int Luma = 0x80; + public const int Run = 0xC0; + public const int Rgb = 0xFE; + public const int Rgba = 0xFF; + public const int Mask2 = 0xC0; + public const int HashTableSize = 64; + } + + public class QoiDecodeStream + { + readonly IBinaryStream m_input; + readonly byte[] m_table; + uint m_pixel; + + public QoiDecodeStream (IBinaryStream input) + { + m_input = input; + m_table = new byte [4*QoiConst.HashTableSize]; + m_pixel = 0xFF000000; + } + + public int Read (out uint output) + { + var r = (byte)m_pixel; + var g = (byte)(m_pixel >> 8); + var b = (byte)(m_pixel >> 16); + var a = (byte)(m_pixel >> 24); + var run = 1; + var b1 = m_input.ReadByte (); + if (-1 == b1) + throw new EndOfStreamException (); + if (QoiConst.Rgb == b1) + { + var rgb = m_input.ReadInt24 (); + r = (byte)rgb; + g = (byte)(rgb >> 8); + b = (byte)(rgb >> 16); + } + else if (QoiConst.Rgba == b1) + { + var rgba = m_input.ReadInt32 (); + r = (byte)rgba; + g = (byte)(rgba >> 8); + b = (byte)(rgba >> 16); + a = (byte)(rgba >> 24); + } + else if (QoiConst.Index == (b1 & QoiConst.Mask2)) + { + var p1 = (b1 & ~QoiConst.Mask2) * 4; + r = m_table[p1 ]; + g = m_table[p1+1]; + b = m_table[p1+2]; + a = m_table[p1+3]; + } + else if (QoiConst.Diff == (b1 & QoiConst.Mask2)) + { + r += (byte)(((b1 >> 4) & 0x03) - 2); + g += (byte)(((b1 >> 2) & 0x03) - 2); + b += (byte)((b1 & 0x03) - 2); + } + else if (QoiConst.Luma == (b1 & QoiConst.Mask2)) + { + var b2 = m_input.ReadByte (); + if (-1 == b2) + throw new EndOfStreamException (); + var vg = (b1 & 0x3F) - 32; + r += (byte)(vg - 8 + ((b2 >> 4) & 0x0F)); + g += (byte)vg; + b += (byte)(vg - 8 + (b2 & 0x0F)); + } + else if (QoiConst.Run == (b1 & QoiConst.Mask2)) + { + run = (b1 & 0x3F) + 1; + } + var p2 = (r*3 + g*5 + b*7 + a*11) % QoiConst.HashTableSize*4; + m_table[p2 ] = r; + m_table[p2+1] = g; + m_table[p2+2] = b; + m_table[p2+3] = a; + m_pixel = (uint)(r | (g << 8) | (b << 16) | (a << 24)); + output = (uint)(b | (g << 8) | (r << 16) | (a << 24)); + return run; + } + } +} diff --git a/ArcFormats/YuRis/ImageYDG.cs b/ArcFormats/YuRis/ImageYDG.cs index 8aeae9b8..afa11e81 100644 --- a/ArcFormats/YuRis/ImageYDG.cs +++ b/ArcFormats/YuRis/ImageYDG.cs @@ -30,6 +30,7 @@ using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Media; +using GameRes.Formats.QoiCodec; using GameRes.Utility; namespace GameRes.Formats.YuRis @@ -233,96 +234,5 @@ namespace GameRes.Formats.YuRis return output; } } - - static class QoiCodec - { - public const int Index = 0x00; - public const int Diff = 0x40; - public const int Luma = 0x80; - public const int Run = 0xC0; - public const int Rgb = 0xFE; - public const int Rgba = 0xFF; - public const int Mask2 = 0xC0; - public const int HashTableSize = 64; - } - - class QoiDecodeStream - { - readonly IBinaryStream m_input; - readonly byte[] m_table; - uint m_pixel; - - public QoiDecodeStream(IBinaryStream input) - { - m_input = input; - m_table = new byte[4 * QoiCodec.HashTableSize]; - m_pixel = 0xFF000000; - } - - public int Read(out uint output) - { - var r = (byte)m_pixel; - var g = (byte)(m_pixel >> 8); - var b = (byte)(m_pixel >> 16); - var a = (byte)(m_pixel >> 24); - var run = 1; - var b1 = m_input.ReadByte(); - if (-1 == b1) - throw new EndOfStreamException(); - - if (QoiCodec.Rgb == b1) - { - var rgb = m_input.ReadInt24(); - r = (byte)rgb; - g = (byte)(rgb >> 8); - b = (byte)(rgb >> 16); - } - else if (QoiCodec.Rgba == b1) - { - var rgba = m_input.ReadInt32(); - r = (byte)rgba; - g = (byte)(rgba >> 8); - b = (byte)(rgba >> 16); - a = (byte)(rgba >> 24); - } - else if (QoiCodec.Index == (b1 & QoiCodec.Mask2)) - { - var p1 = (b1 & ~QoiCodec.Mask2) * 4; - r = m_table[p1]; - g = m_table[p1 + 1]; - b = m_table[p1 + 2]; - a = m_table[p1 + 3]; - } - else if (QoiCodec.Diff == (b1 & QoiCodec.Mask2)) - { - r += (byte)(((b1 >> 4) & 0x03) - 2); - g += (byte)(((b1 >> 2) & 0x03) - 2); - b += (byte)((b1 & 0x03) - 2); - } - else if (QoiCodec.Luma == (b1 & QoiCodec.Mask2)) - { - var b2 = m_input.ReadByte(); - if (-1 == b2) - throw new EndOfStreamException(); - var vg = (b1 & 0x3F) - 32; - r += (byte)(vg - 8 + ((b2 >> 4) & 0x0F)); - g += (byte)vg; - b += (byte)(vg - 8 + (b2 & 0x0F)); - } - else if (QoiCodec.Run == (b1 & QoiCodec.Mask2)) - { - run = (b1 & 0x3F) + 1; - } - - var p2 = (r * 3 + g * 5 + b * 7 + a * 11) % QoiCodec.HashTableSize * 4; - m_table[p2 ] = r; - m_table[p2 + 1] = g; - m_table[p2 + 2] = b; - m_table[p2 + 3] = a; - m_pixel = (uint)(r | (g << 8) | (b << 16) | (a << 24)); - output = (uint)(b | (g << 8) | (r << 16) | (a << 24)); - return run; - } - } } }