mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-23 13:45: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];
|
||||
Format = PixelFormats.Bgra32;
|
||||
}
|
||||
else if (m_info.HasAlpha)
|
||||
{
|
||||
Format = PixelFormats.Bgra32;
|
||||
}
|
||||
else
|
||||
{
|
||||
Format = PixelFormats.Bgr32;
|
||||
@ -90,13 +94,22 @@ namespace GameRes.Formats.Google
|
||||
|
||||
public void Decode ()
|
||||
{
|
||||
if (m_info.IsLossless)
|
||||
throw new NotImplementedException ("Lossless WebP not implemented");
|
||||
m_input.BaseStream.Position = m_info.DataOffset;
|
||||
GetHeaders();
|
||||
EnterCritical();
|
||||
InitFrame();
|
||||
ParseFrame();
|
||||
if (m_info.IsLossless)
|
||||
{
|
||||
m_io.opaque = m_output;
|
||||
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 ()
|
||||
|
@ -35,6 +35,7 @@ namespace GameRes.Formats.Google
|
||||
{
|
||||
public WebPFeature Flags;
|
||||
public bool IsLossless;
|
||||
public bool HasAlpha;
|
||||
public long DataOffset;
|
||||
public int DataSize;
|
||||
public long AlphaOffset;
|
||||
@ -102,12 +103,24 @@ namespace GameRes.Formats.Google
|
||||
{
|
||||
if (chunk_size < 10 || 10 != stream.Read (header, 0, 10))
|
||||
return null;
|
||||
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;
|
||||
if (info.IsLossless)
|
||||
{
|
||||
if (header[0] != 0x2F || (header[4] >> 5) != 0)
|
||||
return null;
|
||||
uint wh = LittleEndian.ToUInt32 (header, 1);
|
||||
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;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Google
|
||||
@ -405,7 +406,7 @@ namespace GameRes.Formats.Google
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -598,6 +599,13 @@ namespace GameRes.Formats.Google
|
||||
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 ()
|
||||
{
|
||||
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 ()
|
||||
{
|
||||
// Initialization.
|
||||
@ -634,14 +666,67 @@ namespace GameRes.Formats.Google
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
other.val_ = val_;
|
||||
|
Loading…
Reference in New Issue
Block a user