mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-23 21:55:34 +08:00
(WebP): ported lossless support (incomplete).
This commit is contained in:
parent
e2a0d76b65
commit
bc62faa71a
@ -74,6 +74,10 @@ namespace GameRes.Formats.Google
|
|||||||
m_alpha_plane = new byte[info.Width * info.Height];
|
m_alpha_plane = new byte[info.Width * info.Height];
|
||||||
Format = PixelFormats.Bgra32;
|
Format = PixelFormats.Bgra32;
|
||||||
}
|
}
|
||||||
|
else if (m_info.HasAlpha)
|
||||||
|
{
|
||||||
|
Format = PixelFormats.Bgra32;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Format = PixelFormats.Bgr32;
|
Format = PixelFormats.Bgr32;
|
||||||
@ -90,13 +94,22 @@ namespace GameRes.Formats.Google
|
|||||||
|
|
||||||
public void Decode ()
|
public void Decode ()
|
||||||
{
|
{
|
||||||
if (m_info.IsLossless)
|
|
||||||
throw new NotImplementedException ("Lossless WebP not implemented");
|
|
||||||
m_input.BaseStream.Position = m_info.DataOffset;
|
m_input.BaseStream.Position = m_info.DataOffset;
|
||||||
GetHeaders();
|
if (m_info.IsLossless)
|
||||||
EnterCritical();
|
{
|
||||||
InitFrame();
|
m_io.opaque = m_output;
|
||||||
ParseFrame();
|
var ld = new LosslessDecoder();
|
||||||
|
ld.Init (m_input, m_info.DataSize, m_io);
|
||||||
|
if (!ld.DecodeImage())
|
||||||
|
throw new InvalidFormatException();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GetHeaders();
|
||||||
|
EnterCritical();
|
||||||
|
InitFrame();
|
||||||
|
ParseFrame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadInt24 ()
|
int ReadInt24 ()
|
||||||
|
@ -35,6 +35,7 @@ namespace GameRes.Formats.Google
|
|||||||
{
|
{
|
||||||
public WebPFeature Flags;
|
public WebPFeature Flags;
|
||||||
public bool IsLossless;
|
public bool IsLossless;
|
||||||
|
public bool HasAlpha;
|
||||||
public long DataOffset;
|
public long DataOffset;
|
||||||
public int DataSize;
|
public int DataSize;
|
||||||
public long AlphaOffset;
|
public long AlphaOffset;
|
||||||
@ -102,12 +103,24 @@ namespace GameRes.Formats.Google
|
|||||||
{
|
{
|
||||||
if (chunk_size < 10 || 10 != stream.Read (header, 0, 10))
|
if (chunk_size < 10 || 10 != stream.Read (header, 0, 10))
|
||||||
return null;
|
return null;
|
||||||
if (header[3] != 0x9D || header[4] != 1 || header[5] != 0x2A)
|
if (info.IsLossless)
|
||||||
return null;
|
{
|
||||||
if (0 != (header[0] & 1)) // not a keyframe
|
if (header[0] != 0x2F || (header[4] >> 5) != 0)
|
||||||
return null;
|
return null;
|
||||||
info.Width = LittleEndian.ToUInt16 (header, 6) & 0x3FFFu;
|
uint wh = LittleEndian.ToUInt32 (header, 1);
|
||||||
info.Height = LittleEndian.ToUInt16 (header, 8) & 0x3FFFu;
|
info.Width = (wh & 0x3FFFu) + 1;
|
||||||
|
info.Height = ((wh >> 14) & 0x3FFFu) + 1;
|
||||||
|
info.HasAlpha = 0 != (header[4] & 0x10);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (header[3] != 0x9D || header[4] != 1 || header[5] != 0x2A)
|
||||||
|
return null;
|
||||||
|
if (0 != (header[0] & 1)) // not a keyframe
|
||||||
|
return null;
|
||||||
|
info.Width = LittleEndian.ToUInt16 (header, 6) & 0x3FFFu;
|
||||||
|
info.Height = LittleEndian.ToUInt16 (header, 8) & 0x3FFFu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using GameRes.Utility;
|
using GameRes.Utility;
|
||||||
|
|
||||||
namespace GameRes.Formats.Google
|
namespace GameRes.Formats.Google
|
||||||
@ -405,7 +406,7 @@ namespace GameRes.Formats.Google
|
|||||||
}
|
}
|
||||||
static uint Predictor11(uint left, uint[] data, int top)
|
static uint Predictor11(uint left, uint[] data, int top)
|
||||||
{
|
{
|
||||||
return Select (data[top], data[left], data[top-1]);
|
return Select (data[top], left, data[top-1]);
|
||||||
}
|
}
|
||||||
static uint Predictor12 (uint left, uint[] data, int top)
|
static uint Predictor12 (uint left, uint[] data, int top)
|
||||||
{
|
{
|
||||||
@ -598,6 +599,13 @@ namespace GameRes.Formats.Google
|
|||||||
br_.Init (data, data_i, (uint)data_size);
|
br_.Init (data, data_i, (uint)data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Init (BinaryReader input, int length, VP8Io io)
|
||||||
|
{
|
||||||
|
io_ = io;
|
||||||
|
br_.Init (input, (uint)length);
|
||||||
|
DecodeHeader();
|
||||||
|
}
|
||||||
|
|
||||||
public bool Is8bOptimizable ()
|
public bool Is8bOptimizable ()
|
||||||
{
|
{
|
||||||
if (hdr_.color_cache_size_ > 0)
|
if (hdr_.color_cache_size_ > 0)
|
||||||
@ -616,6 +624,30 @@ namespace GameRes.Formats.Google
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void DecodeHeader ()
|
||||||
|
{
|
||||||
|
status_ = VP8StatusCode.Ok;
|
||||||
|
|
||||||
|
ReadImageInfo();
|
||||||
|
state_ = VP8DecodeState.ReadDim;
|
||||||
|
uint[] data = null;
|
||||||
|
if (!DecodeImageStream (io_.width, io_.height, true, ref data, false))
|
||||||
|
throw new InvalidFormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadImageInfo ()
|
||||||
|
{
|
||||||
|
if (0x2F != br_.ReadBits (8))
|
||||||
|
throw new InvalidFormatException();
|
||||||
|
width_ = (int)br_.ReadBits (14) + 1;
|
||||||
|
height_ = (int)br_.ReadBits (14) + 1;
|
||||||
|
io_.width = width_;
|
||||||
|
io_.height = height_;
|
||||||
|
bool has_alpha = 0 != br_.ReadBits (1);
|
||||||
|
if (br_.ReadBits (3) != 0)
|
||||||
|
throw new InvalidFormatException();
|
||||||
|
}
|
||||||
|
|
||||||
public bool DecodeImage ()
|
public bool DecodeImage ()
|
||||||
{
|
{
|
||||||
// Initialization.
|
// Initialization.
|
||||||
@ -634,14 +666,67 @@ namespace GameRes.Formats.Google
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decode.
|
// Decode.
|
||||||
return DecodeImageData (pixels32_, width_, height_, height_, ProcessRows);
|
return DecodeImageData (pixels32_, width_, height_, height_, (dec, row) => dec.ProcessRows (row));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes (transforms, scales & color-converts) the rows decoded after the
|
// Processes (transforms, scales & color-converts) the rows decoded after the
|
||||||
// last call.
|
// last call.
|
||||||
static void ProcessRows (LosslessDecoder dec, int row)
|
void ProcessRows (int row)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException ("Lossless RGB decoder not implemented");
|
int rows = width_ * last_row_;
|
||||||
|
int num_rows = row - last_row_;
|
||||||
|
|
||||||
|
if (num_rows <= 0) return; // Nothing to be done.
|
||||||
|
ApplyInverseTransforms (num_rows, pixels32_, rows);
|
||||||
|
|
||||||
|
// Emit output.
|
||||||
|
int rows_data = argb_cache_;
|
||||||
|
int in_stride = io_.width * sizeof(uint); // in unit of RGBA
|
||||||
|
int out_stride = in_stride;
|
||||||
|
if (SetCropWindow (last_row_, row))
|
||||||
|
{
|
||||||
|
int rgba = last_out_row_ * out_stride;
|
||||||
|
int num_rows_out = EmitRows (pixels32_, rows_data, in_stride,
|
||||||
|
io_.mb_w, io_.mb_h, io_.opaque, rgba, out_stride);
|
||||||
|
// Update 'last_out_row_'.
|
||||||
|
last_out_row_ += num_rows_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 'last_row_'.
|
||||||
|
last_row_ = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmitRows (uint[] input, int row_in, int in_stride, int mb_w, int mb_h, byte[] output, int row_out, int out_stride)
|
||||||
|
{
|
||||||
|
int lines = mb_h;
|
||||||
|
while (lines --> 0)
|
||||||
|
{
|
||||||
|
Buffer.BlockCopy (input, row_in, output, row_out, mb_w * 4);
|
||||||
|
row_in += in_stride;
|
||||||
|
row_out += out_stride;
|
||||||
|
}
|
||||||
|
return mb_h; // Num rows out == num rows in.
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Cropping.
|
||||||
|
|
||||||
|
// Sets io->mb_y, io->mb_h & io->mb_w according to start row, end row and
|
||||||
|
// crop options. Also updates the input data pointer, so that it points to the
|
||||||
|
// start of the cropped window.
|
||||||
|
// Returns true if the crop window is not empty.
|
||||||
|
bool SetCropWindow (int y_start, int y_end)
|
||||||
|
{
|
||||||
|
if (y_end > io_.height)
|
||||||
|
{
|
||||||
|
y_end = io_.height; // make sure we don't overflow on last row.
|
||||||
|
}
|
||||||
|
if (y_start >= y_end) return false; // Crop window is empty.
|
||||||
|
|
||||||
|
io_.mb_y = y_start;
|
||||||
|
io_.mb_w = io_.width;
|
||||||
|
io_.mb_h = y_end - y_start;
|
||||||
|
return true; // Non-empty crop window.
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@ -1568,6 +1653,14 @@ namespace GameRes.Formats.Google
|
|||||||
buf_ = input;
|
buf_ = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Init (BinaryReader input, uint length)
|
||||||
|
{
|
||||||
|
var buf = input.ReadBytes ((int)length);
|
||||||
|
if (buf.Length != length)
|
||||||
|
throw new EndOfStreamException();
|
||||||
|
Init (buf, 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
public void CopyStateTo (LBitReader other)
|
public void CopyStateTo (LBitReader other)
|
||||||
{
|
{
|
||||||
other.val_ = val_;
|
other.val_ = val_;
|
||||||
|
Loading…
Reference in New Issue
Block a user