std/handsum: add new package

This commit is contained in:
Nigel Tao
2025-02-24 11:18:24 +11:00
parent 9a9ab6ff86
commit 43a6814dfe
17 changed files with 1833 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ The LICENSE has changed from a single license (Apache 2) to a dual license
- Added `get_quirk(key: u32) u64`.
- Added `std/crc64`.
- Added `std/etc2`.
- Added `std/handsum`.
- Added `std/jpeg`.
- Added `std/lzip`.
- Added `std/lzma`.

View File

@@ -55,6 +55,7 @@ to enable.
- `ETC2: BASE`
- `GIF: BASE`
- `GZIP: BASE, CRC32, DEFLATE`
- `HANDSUM: BASE`
- `JPEG: BASE`
- `JSON: BASE`
- `LZIP: BASE, CRC32, LZMA`

View File

@@ -126,6 +126,7 @@ In Wuffs syntax, the `base.image_decoder` methods are:
- [std/bmp](/std/bmp)
- [std/etc2](/std/etc2)
- [std/gif](/std/gif)
- [std/handsum](/std/handsum)
- [std/jpeg](/std/jpeg)
- [std/netpbm](/std/netpbm)
- [std/nie](/std/nie)

View File

@@ -68,6 +68,7 @@ that Wuffs decodes to just JPEG, for smaller binaries and faster compiles.
#define WUFFS_CONFIG__MODULE__DEFLATE
#define WUFFS_CONFIG__MODULE__ETC2
#define WUFFS_CONFIG__MODULE__GIF
#define WUFFS_CONFIG__MODULE__HANDSUM
#define WUFFS_CONFIG__MODULE__NETPBM
#define WUFFS_CONFIG__MODULE__NIE
#define WUFFS_CONFIG__MODULE__PNG
@@ -184,6 +185,7 @@ union {
wuffs_bmp__decoder bmp;
wuffs_etc2__decoder etc2;
wuffs_gif__decoder gif;
wuffs_handsum__decoder handsum;
wuffs_netpbm__decoder netpbm;
wuffs_nie__decoder nie;
wuffs_png__decoder png;
@@ -416,6 +418,16 @@ initialize_image_decoder() {
&g_potential_decoders.gif);
return NULL;
case WUFFS_BASE__FOURCC__HNSM:
status = wuffs_handsum__decoder__initialize(
&g_potential_decoders.handsum, sizeof g_potential_decoders.handsum,
WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
TRY(wuffs_base__status__message(&status));
g_image_decoder =
wuffs_handsum__decoder__upcast_as__wuffs_base__image_decoder(
&g_potential_decoders.handsum);
return NULL;
case WUFFS_BASE__FOURCC__NIE:
status = wuffs_nie__decoder__initialize(
&g_potential_decoders.nie, sizeof g_potential_decoders.nie,

View File

@@ -76,6 +76,7 @@ The Escape key quits.
#define WUFFS_CONFIG__MODULE__DEFLATE
#define WUFFS_CONFIG__MODULE__ETC2
#define WUFFS_CONFIG__MODULE__GIF
#define WUFFS_CONFIG__MODULE__HANDSUM
#define WUFFS_CONFIG__MODULE__JPEG
#define WUFFS_CONFIG__MODULE__NETPBM
#define WUFFS_CONFIG__MODULE__NIE

View File

@@ -74,6 +74,7 @@ isn't otherwise doing anything.
#define WUFFS_CONFIG__MODULE__DEFLATE
#define WUFFS_CONFIG__MODULE__ETC2
#define WUFFS_CONFIG__MODULE__GIF
#define WUFFS_CONFIG__MODULE__HANDSUM
#define WUFFS_CONFIG__MODULE__JPEG
#define WUFFS_CONFIG__MODULE__NETPBM
#define WUFFS_CONFIG__MODULE__NIE

View File

@@ -127,6 +127,7 @@ for a C compiler $CC, such as clang or gcc.
#define WUFFS_CONFIG__MODULE__DEFLATE
#define WUFFS_CONFIG__MODULE__ETC2
#define WUFFS_CONFIG__MODULE__GIF
#define WUFFS_CONFIG__MODULE__HANDSUM
#define WUFFS_CONFIG__MODULE__NETPBM
#define WUFFS_CONFIG__MODULE__NIE
#define WUFFS_CONFIG__MODULE__PNG

View File

@@ -72,6 +72,11 @@ DecodeImageCallbacks::SelectDecoder(uint32_t fourcc,
return wuffs_gif__decoder::alloc_as__wuffs_base__image_decoder();
#endif
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__HANDSUM)
case WUFFS_BASE__FOURCC__HNSM:
return wuffs_handsum__decoder::alloc_as__wuffs_base__image_decoder();
#endif
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__JPEG)
case WUFFS_BASE__FOURCC__JPEG:
return wuffs_jpeg__decoder::alloc_as__wuffs_base__image_decoder();

View File

@@ -87,6 +87,7 @@ class DecodeImageCallbacks {
// - WUFFS_BASE__FOURCC__BMP
// - WUFFS_BASE__FOURCC__ETC2
// - WUFFS_BASE__FOURCC__GIF
// - WUFFS_BASE__FOURCC__HNSM
// - WUFFS_BASE__FOURCC__JPEG
// - WUFFS_BASE__FOURCC__NIE
// - WUFFS_BASE__FOURCC__NPBM

View File

@@ -91,6 +91,11 @@ wuffs_drop_in__stb__make_decoder( //
return wuffs_gif__decoder__alloc_as__wuffs_base__image_decoder();
#endif
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__HANDSUM)
case WUFFS_BASE__FOURCC__HNSM:
return wuffs_handsum__decoder__alloc_as__wuffs_base__image_decoder();
#endif
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__JPEG)
case WUFFS_BASE__FOURCC__JPEG:
return wuffs_jpeg__decoder__alloc_as__wuffs_base__image_decoder();

File diff suppressed because it is too large Load Diff

View File

@@ -62,6 +62,7 @@
#define WUFFS_CONFIG__MODULE__ETC2
#define WUFFS_CONFIG__MODULE__GIF
#define WUFFS_CONFIG__MODULE__GZIP
#define WUFFS_CONFIG__MODULE__HANDSUM
#define WUFFS_CONFIG__MODULE__JPEG
#define WUFFS_CONFIG__MODULE__NETPBM
#define WUFFS_CONFIG__MODULE__NIE
@@ -119,6 +120,9 @@ handle_image_decoder(wuffs_base__io_buffer src,
case WUFFS_BASE__FOURCC__GIF:
dec = wuffs_gif__decoder::alloc_as__wuffs_base__image_decoder();
break;
case WUFFS_BASE__FOURCC__HNSM:
dec = wuffs_handsum__decoder::alloc_as__wuffs_base__image_decoder();
break;
case WUFFS_BASE__FOURCC__JPEG:
dec = wuffs_jpeg__decoder::alloc_as__wuffs_base__image_decoder();
break;

View File

@@ -54,6 +54,7 @@ to use the wuffs_aux C++ API to decode an image and iterate over its pixels.
#define WUFFS_CONFIG__MODULE__DEFLATE
#define WUFFS_CONFIG__MODULE__ETC2
#define WUFFS_CONFIG__MODULE__GIF
#define WUFFS_CONFIG__MODULE__HANDSUM
#define WUFFS_CONFIG__MODULE__JPEG
#define WUFFS_CONFIG__MODULE__NETPBM
#define WUFFS_CONFIG__MODULE__NIE

View File

@@ -50,6 +50,7 @@ print-image-metadata prints images' metadata.
#define WUFFS_CONFIG__MODULE__DEFLATE
#define WUFFS_CONFIG__MODULE__ETC2
#define WUFFS_CONFIG__MODULE__GIF
#define WUFFS_CONFIG__MODULE__HANDSUM
#define WUFFS_CONFIG__MODULE__JPEG
#define WUFFS_CONFIG__MODULE__NETPBM
#define WUFFS_CONFIG__MODULE__NIE
@@ -381,6 +382,9 @@ redirect:
case WUFFS_BASE__FOURCC__GIF:
dec = wuffs_gif__decoder::alloc_as__wuffs_base__image_decoder();
break;
case WUFFS_BASE__FOURCC__HNSM:
dec = wuffs_handsum__decoder::alloc_as__wuffs_base__image_decoder();
break;
case WUFFS_BASE__FOURCC__JPEG:
dec = wuffs_jpeg__decoder::alloc_as__wuffs_base__image_decoder();
break;

View File

@@ -0,0 +1,304 @@
// Copyright 2025 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
// --------
// Handsum is a very lossy format for very small thumbnails. Very small in
// terms of image dimensions, up to 32×32 pixels, but also in terms of file
// size. Every Handsum image file is exactly 48 bytes (384 bits) long. This can
// imply using as little as 0.046875 bytes (0.375 bits) per pixel.
//
// Each Handsum image is essentially a scaled 16×16 pixel YCbCr 4:2:0 JPEG MCU
// (Minimum Coded Unit; 4 Luma and 2 Chroma blocks), keeping only the 15 lowest
// frequency DCT (Discrete Cosine Transform) coefficients. Each of the (6 × 15)
// = 90 coefficients are encoded as one nibble (4 bits) with fixed quantization
// factors, totalling 45 bytes. The initial 3 bytes holds a 16-bit magic
// signature, 2-bit version number and 6-bit aspect ratio.
//
// As of February 2025, the latest version is Version 0. All Version 0 files
// use the sRGB color profile.
pub status "#bad header"
pub status "#truncated input"
pub status "#unsupported Handsum file"
pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
pub struct decoder? implements base.image_decoder(
width : base.u32[..= 32],
height : base.u32[..= 32],
// The call sequence state machine is discussed in
// (/doc/std/image-decoders-call-sequence.md).
call_sequence : base.u8,
swizzler : base.pixel_swizzler,
util : base.utility,
) + (
buffers : array[2] array[32] array[128] base.u8,
)
pub func decoder.get_quirk(key: base.u32) base.u64 {
return 0
}
pub func decoder.set_quirk!(key: base.u32, value: base.u64) base.status {
return base."#unsupported option"
}
pub func decoder.decode_image_config?(dst: nptr base.image_config, src: base.io_reader) {
var status : base.status
while true {
status =? this.do_decode_image_config?(dst: args.dst, src: args.src)
if (status == base."$short read") and args.src.is_closed() {
return "#truncated input"
}
yield? status
}
}
pri func decoder.do_decode_image_config?(dst: nptr base.image_config, src: base.io_reader) {
var c32 : base.u32
if this.call_sequence <> 0x00 {
return base."#bad call sequence"
}
c32 = args.src.read_u16le_as_u32?()
if c32 <> '\xFE\xD7'le {
return "#bad header"
}
c32 = args.src.read_u8_as_u32?()
if (c32 & 0xC0) <> 0x00 {
return "#unsupported Handsum file"
}
if (c32 & 0x20) == 0x00 { // Landscape.
this.width = 32
this.height = (c32 & 0x1F) + 1
} else { // Portrait.
this.width = (c32 & 0x1F) + 1
this.height = 32
}
if args.dst <> nullptr {
args.dst.set!(
pixfmt: base.PIXEL_FORMAT__BGRX,
pixsub: 0,
width: this.width,
height: this.height,
first_frame_io_position: 3,
first_frame_is_opaque: true)
}
this.call_sequence = 0x20
}
pub func decoder.decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) {
var status : base.status
while true {
status =? this.do_decode_frame_config?(dst: args.dst, src: args.src)
if (status == base."$short read") and args.src.is_closed() {
return "#truncated input"
}
yield? status
}
}
pri func decoder.do_decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) {
if this.call_sequence == 0x20 {
// No-op.
} else if this.call_sequence < 0x20 {
this.do_decode_image_config?(dst: nullptr, src: args.src)
} else if this.call_sequence == 0x28 {
if 3 <> args.src.position() {
return base."#bad restart"
}
} else if this.call_sequence == 0x40 {
this.call_sequence = 0x60
return base."@end of data"
} else {
return base."@end of data"
}
if args.dst <> nullptr {
args.dst.set!(bounds: this.util.make_rect_ie_u32(
min_incl_x: 0,
min_incl_y: 0,
max_excl_x: this.width,
max_excl_y: this.height),
duration: 0,
index: 0,
io_position: 3,
disposal: 0,
opaque_within_bounds: true,
overwrite_instead_of_blend: false,
background_color: 0xFF00_0000)
}
this.call_sequence = 0x40
}
pub func decoder.decode_frame?(dst: ptr base.pixel_buffer, src: base.io_reader, blend: base.pixel_blend, workbuf: slice base.u8, opts: nptr base.decode_frame_options) {
var status : base.status
while true {
status =? this.do_decode_frame?(dst: args.dst, src: args.src, blend: args.blend, workbuf: args.workbuf, opts: args.opts)
if (status == base."$short read") and args.src.is_closed() {
return "#truncated input"
}
yield? status
}
}
pri func decoder.do_decode_frame?(dst: ptr base.pixel_buffer, src: base.io_reader, blend: base.pixel_blend, workbuf: slice base.u8, opts: nptr base.decode_frame_options) {
var status : base.status
if this.call_sequence == 0x40 {
// No-op.
} else if this.call_sequence < 0x40 {
this.do_decode_frame_config?(dst: nullptr, src: args.src)
} else {
return base."@end of data"
}
status = this.swizzler.prepare!(
dst_pixfmt: args.dst.pixel_format(),
dst_palette: args.dst.palette(),
src_pixfmt: this.util.make_pixel_format(repr: base.PIXEL_FORMAT__BGRX),
src_palette: this.util.empty_slice_u8(),
blend: args.blend)
if not status.is_ok() {
return status
}
this.from_src_to_pixels?(src: args.src)
status = this.from_pixels_to_dst!(dst: args.dst, which: 0)
if not status.is_ok() {
return status
}
this.call_sequence = 0x60
}
pri func decoder.from_src_to_pixels?(src: base.io_reader) {
var y : base.u32
var x : base.u32
// TODO: actually implement the format. For now, fill in a placeholder
// blue-green gradient.
y = 0
while y < 32 {
x = 0
while x < 32,
inv y < 32,
{
this.buffers[0][y][(4 * x) + 0] = (x * 8) as base.u8
this.buffers[0][y][(4 * x) + 1] = (y * 8) as base.u8
this.buffers[0][y][(4 * x) + 2] = 0x00
this.buffers[0][y][(4 * x) + 3] = 0xFF
x += 1
}
y += 1
}
}
pri func decoder.from_pixels_to_dst!(dst: ptr base.pixel_buffer, which: base.u32[..= 1]) base.status {
var dst_pixfmt : base.pixel_format
var dst_bits_per_pixel : base.u32[..= 256]
var dst_bytes_per_pixel : base.u32[..= 32]
var dst_bytes_per_row : base.u64
var tab : table base.u8
var y : base.u32
var dst : slice base.u8
var src : slice base.u8
// TODO: the dst_pixfmt variable shouldn't be necessary. We should be able
// to chain the two calls: "args.dst.pixel_format().bits_per_pixel()".
dst_pixfmt = args.dst.pixel_format()
dst_bits_per_pixel = dst_pixfmt.bits_per_pixel()
if (dst_bits_per_pixel & 7) <> 0 {
return base."#unsupported option"
}
dst_bytes_per_pixel = dst_bits_per_pixel / 8
dst_bytes_per_row = (this.width * dst_bytes_per_pixel) as base.u64
tab = args.dst.plane(p: 0)
while y < this.height {
assert y < 32 via "a < b: a < c; c <= b"(c: this.height)
src = this.buffers[args.which][y][.. this.width * 4]
dst = tab.row_u32(y: y)
if dst_bytes_per_row < dst.length() {
dst = dst[.. dst_bytes_per_row]
}
this.swizzler.swizzle_interleaved_from_slice!(
dst: dst,
dst_palette: args.dst.palette(),
src: src)
y += 1
}
return ok
}
pub func decoder.frame_dirty_rect() base.rect_ie_u32 {
return this.util.make_rect_ie_u32(
min_incl_x: 0,
min_incl_y: 0,
max_excl_x: this.width,
max_excl_y: this.height)
}
pub func decoder.num_animation_loops() base.u32 {
return 0
}
pub func decoder.num_decoded_frame_configs() base.u64 {
if this.call_sequence > 0x20 {
return 1
}
return 0
}
pub func decoder.num_decoded_frames() base.u64 {
if this.call_sequence > 0x40 {
return 1
}
return 0
}
pub func decoder.restart_frame!(index: base.u64, io_position: base.u64) base.status {
if this.call_sequence < 0x20 {
return base."#bad call sequence"
}
if (args.index <> 0) or (args.io_position <> 3) {
return base."#bad argument"
}
this.call_sequence = 0x28
return ok
}
pub func decoder.set_report_metadata!(fourcc: base.u32, report: base.bool) {
// No-op. Handsum doesn't support metadata.
}
pub func decoder.tell_me_more?(dst: base.io_writer, minfo: nptr base.more_information, src: base.io_reader) {
return base."#no more information"
}
pub func decoder.workbuf_len() base.range_ii_u64 {
return this.util.make_range_ii_u64(min_incl: 0, max_incl: 0)
}

196
test/c/std/handsum.c Normal file
View File

@@ -0,0 +1,196 @@
// Copyright 2025 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
// ----------------
/*
This test program is typically run indirectly, by the "wuffs test" or "wuffs
bench" commands. These commands take an optional "-mimic" flag to check that
Wuffs' output mimics (i.e. exactly matches) other libraries' output, such as
giflib for GIF, libpng for PNG, etc.
To manually run this test:
for CC in clang gcc; do
$CC -std=c99 -Wall -Werror handsum.c && ./a.out
rm -f a.out
done
Each edition should print "PASS", amongst other information, and exit(0).
Add the "wuffs mimic cflags" (everything after the colon below) to the C
compiler flags (after the .c file) to run the mimic tests.
To manually run the benchmarks, replace "-Wall -Werror" with "-O3" and replace
the first "./a.out" with "./a.out -bench". Combine these changes with the
"wuffs mimic cflags" to run the mimic benchmarks.
*/
// ¿ wuffs mimic cflags: -DWUFFS_MIMIC
// Wuffs ships as a "single file C library" or "header file library" as per
// https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
//
// To use that single file as a "foo.c"-like implementation, instead of a
// "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or
// compiling it.
#define WUFFS_IMPLEMENTATION
// Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
// release/c/etc.c choose which parts of Wuffs to build. That file contains the
// entire Wuffs standard library, implementing a variety of codecs and file
// formats. Without this macro definition, an optimizing compiler or linker may
// very well discard Wuffs code for unused codecs, but listing the Wuffs
// modules we use makes that process explicit. Preprocessing means that such
// code simply isn't compiled.
#define WUFFS_CONFIG__MODULES
#define WUFFS_CONFIG__MODULE__BASE
#define WUFFS_CONFIG__MODULE__HANDSUM
// If building this program in an environment that doesn't easily accommodate
// relative includes, you can use the script/inline-c-relative-includes.go
// program to generate a stand-alone C file.
#include "../../../release/c/wuffs-unsupported-snapshot.c"
#include "../testlib/testlib.c"
#ifdef WUFFS_MIMIC
// No mimic library.
#endif
// ---------------- Handsum Tests
const char* //
test_wuffs_handsum_decode_interface() {
CHECK_FOCUS(__func__);
wuffs_handsum__decoder dec;
CHECK_STATUS("initialize",
wuffs_handsum__decoder__initialize(
&dec, sizeof dec, WUFFS_VERSION,
WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED));
return do_test__wuffs_base__image_decoder(
wuffs_handsum__decoder__upcast_as__wuffs_base__image_decoder(&dec),
"test/data/bricks-color.handsum", 0, SIZE_MAX, 32, 24, 0xFF00B8F8);
}
const char* //
test_wuffs_handsum_decode_truncated_input() {
CHECK_FOCUS(__func__);
wuffs_base__io_buffer src =
wuffs_base__ptr_u8__reader(g_src_array_u8, 0, false);
wuffs_handsum__decoder dec;
CHECK_STATUS("initialize",
wuffs_handsum__decoder__initialize(
&dec, sizeof dec, WUFFS_VERSION,
WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED));
wuffs_base__status status =
wuffs_handsum__decoder__decode_image_config(&dec, NULL, &src);
if (status.repr != wuffs_base__suspension__short_read) {
RETURN_FAIL("closed=false: have \"%s\", want \"%s\"", status.repr,
wuffs_base__suspension__short_read);
}
src.meta.closed = true;
status = wuffs_handsum__decoder__decode_image_config(&dec, NULL, &src);
if (status.repr != wuffs_handsum__error__truncated_input) {
RETURN_FAIL("closed=true: have \"%s\", want \"%s\"", status.repr,
wuffs_handsum__error__truncated_input);
}
return NULL;
}
const char* //
test_wuffs_handsum_decode_frame_config() {
CHECK_FOCUS(__func__);
wuffs_handsum__decoder dec;
CHECK_STATUS("initialize",
wuffs_handsum__decoder__initialize(
&dec, sizeof dec, WUFFS_VERSION,
WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED));
wuffs_base__frame_config fc = ((wuffs_base__frame_config){});
wuffs_base__io_buffer src = ((wuffs_base__io_buffer){
.data = g_src_slice_u8,
});
CHECK_STRING(read_file(&src, "test/data/mona-lisa.21x32.handsum"));
CHECK_STATUS("decode_frame_config #0",
wuffs_handsum__decoder__decode_frame_config(&dec, &fc, &src));
uint32_t have = wuffs_base__frame_config__width(&fc);
uint32_t want = 21;
if (have != want) {
RETURN_FAIL("width: have %u, want %u", have, want);
}
wuffs_base__status status =
wuffs_handsum__decoder__decode_frame_config(&dec, &fc, &src);
if (status.repr != wuffs_base__note__end_of_data) {
RETURN_FAIL("decode_frame_config #1: have \"%s\", want \"%s\"", status.repr,
wuffs_base__note__end_of_data);
}
return NULL;
}
// ---------------- Mimic Tests
#ifdef WUFFS_MIMIC
// No mimic tests.
#endif // WUFFS_MIMIC
// ---------------- Handsum Benches
// No Handsum benches.
// ---------------- Mimic Benches
#ifdef WUFFS_MIMIC
// No mimic benches.
#endif // WUFFS_MIMIC
// ---------------- Manifest
proc g_tests[] = {
test_wuffs_handsum_decode_frame_config,
test_wuffs_handsum_decode_interface,
test_wuffs_handsum_decode_truncated_input,
#ifdef WUFFS_MIMIC
// No mimic tests.
#endif // WUFFS_MIMIC
NULL,
};
proc g_benches[] = {
// No Handsum benches.
#ifdef WUFFS_MIMIC
// No mimic benches.
#endif // WUFFS_MIMIC
NULL,
};
int //
main(int argc, char** argv) {
g_proc_package_name = "std/handsum";
return test_main(argc, argv, g_tests, g_benches);
}

View File

@@ -48,6 +48,7 @@ OK. 076cb375 test/data/bricks-color.bmp
OK. bdbbfadb test/data/bricks-color.etc1.pkm
OK. 41e6110e test/data/bricks-color.etc1s.pkm
OK. 5670f263 test/data/bricks-color.etc2.pkm
OK. 16e19d11 test/data/bricks-color.handsum
OK. 72a1f9cc test/data/bricks-color.jpeg
OK. 076cb375 test/data/bricks-color.lossless.webp
OK. 9d451b1c test/data/bricks-color.lossy.webp
@@ -121,6 +122,7 @@ OK. ed4b78fc test/data/hippopotamus.regular.gif
OK. dcbb225a test/data/hippopotamus.regular.png
BAD e4c5926b test/data/hippopotamus.regular.truncated.png
OK. f6bb81e2 test/data/mona-lisa.21x32.etc2.pkm
OK. e93a8448 test/data/mona-lisa.21x32.handsum
OK. 012ce4cf test/data/mona-lisa.21x32.png
OK. 0a22e689 test/data/mona-lisa.21x32.q50.jpeg
OK. 059ecb80 test/data/mona-lisa.21x32.q90.jpeg