blob: ae26d9f270f284695f53e799ddee9399e8ae5a14 [file] [log] [blame]
// Copyright 2022 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.
// ----------------
/*
print-average-pixel prints the average color of an image's pixels (as well as
the image file format, width and height). It's a toy program to demonstrate how
to use the wuffs_aux C++ API to decode an image and iterate over its pixels.
*/
#include <inttypes.h>
#include <stdio.h>
// 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__STATIC_FUNCTIONS macro is optional, but when
// combined with WUFFS_IMPLEMENTATION, it demonstrates making all of Wuffs'
// functions have static storage.
//
// This can help the compiler ignore or discard unused code, which can produce
// faster compiles and smaller binaries. Other motivations are discussed in the
// "ALLOW STATIC IMPLEMENTATION" section of
// https://raw.githubusercontent.com/nothings/stb/master/docs/stb_howto.txt
#define WUFFS_CONFIG__STATIC_FUNCTIONS
// 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__ADLER32
#define WUFFS_CONFIG__MODULE__AUX__BASE
#define WUFFS_CONFIG__MODULE__AUX__IMAGE
#define WUFFS_CONFIG__MODULE__BASE
#define WUFFS_CONFIG__MODULE__BMP
#define WUFFS_CONFIG__MODULE__CRC32
#define WUFFS_CONFIG__MODULE__DEFLATE
#define WUFFS_CONFIG__MODULE__GIF
#define WUFFS_CONFIG__MODULE__JPEG
#define WUFFS_CONFIG__MODULE__NETPBM
#define WUFFS_CONFIG__MODULE__NIE
#define WUFFS_CONFIG__MODULE__PNG
#define WUFFS_CONFIG__MODULE__TGA
#define WUFFS_CONFIG__MODULE__WBMP
#define WUFFS_CONFIG__MODULE__ZLIB
// 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"
class MyCallbacks : public wuffs_aux::DecodeImageCallbacks {
public:
MyCallbacks() : m_fourcc(0) {}
uint32_t m_fourcc;
private:
wuffs_base__image_decoder::unique_ptr //
SelectDecoder(uint32_t fourcc,
wuffs_base__slice_u8 prefix_data,
bool prefix_closed) override {
// Save the fourcc value (you can think of it as like a 'MIME type' but in
// uint32_t form) before calling the superclass' implementation.
//
// The "if (m_fourcc == 0)" is because SelectDecoder can be called multiple
// times. Files that are nominally BMP images can contain complete JPEG or
// PNG images. This program prints the outer file format, the first one
// encountered, not the inner one.
if (m_fourcc == 0) {
m_fourcc = fourcc;
}
return wuffs_aux::DecodeImageCallbacks::SelectDecoder(fourcc, prefix_data,
prefix_closed);
}
wuffs_base__pixel_format //
SelectPixfmt(const wuffs_base__image_config& image_config) override {
// This is the same as the superclass' implementation, but makes it
// explicit that this program uses a single-plane pixel buffer (as opposed
// to e.g. 3-plane YCbCr) with 4 bytes per pixel (in B, G, R, A order) and
// premultiplied alpha.
return wuffs_base__make_pixel_format(WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL);
}
AllocPixbufResult //
AllocPixbuf(const wuffs_base__image_config& image_config,
bool allow_uninitialized_memory) override {
// This just calls the superclass' implementation, but if you wanted more
// control about how the pixel buffer's memory is allocated and freed,
// change the code here. For example, if you (the wuffs_aux::DecodeImage
// caller) want to use an already-allocated buffer, instead of Wuffs (the
// callee) allocating a new buffer.
//
// The example/sdl-imageviewer/sdl-imageviewer.cc file demonstrates
// overriding the AllocPixbuf method with the "bring your own buffer"
// approach. In the sdl-imageviewer case, the pixel buffer is allocated by
// SDL and lent to Wuffs, instead of allocated by Wuffs.
return wuffs_aux::DecodeImageCallbacks::AllocPixbuf(
image_config, allow_uninitialized_memory);
}
};
void //
handle(const char* filename, FILE* f) {
MyCallbacks callbacks;
wuffs_aux::sync_io::FileInput input(f);
wuffs_aux::DecodeImageResult res = wuffs_aux::DecodeImage(callbacks, input);
if (!res.error_message.empty()) {
printf("%-30s %s\n", filename, res.error_message.c_str());
return;
} else if (res.pixbuf.pixcfg.pixel_format().repr !=
WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL) {
printf("%-30s internal error: inconsistent pixel format\n", filename);
return;
}
wuffs_base__table_u8 table = res.pixbuf.plane(0);
uint32_t w = res.pixbuf.pixcfg.width();
uint32_t h = res.pixbuf.pixcfg.height();
uint64_t count = 0;
uint64_t color_b = 0;
uint64_t color_g = 0;
uint64_t color_r = 0;
uint64_t color_a = 0;
for (uint32_t y = 0; y < h; y++) {
const uint8_t* ptr = table.ptr + (y * table.stride);
for (uint32_t x = 0; x < w; x++) {
count++;
color_b += *ptr++;
color_g += *ptr++;
color_r += *ptr++;
color_a += *ptr++;
}
}
if (count > 0) {
color_b = (color_b + (count / 2)) / count;
color_g = (color_g + (count / 2)) / count;
color_r = (color_r + (count / 2)) / count;
color_a = (color_a + (count / 2)) / count;
}
printf("%-30s %c%c%c%c %5" PRIu32 " x %5" PRIu32
" AverageARGB: %02X%02X%02X%02X\n", //
filename, //
(0xFF & (callbacks.m_fourcc >> 24)), //
(0xFF & (callbacks.m_fourcc >> 16)), //
(0xFF & (callbacks.m_fourcc >> 8)), //
(0xFF & (callbacks.m_fourcc >> 0)), //
w, h, //
(int)(color_a), //
(int)(color_r), //
(int)(color_g), //
(int)(color_b));
// While it's valid to deference table.ptr in this function, the end of scope
// here means that the res.pixbuf_mem_owner destructor will free the backing
// memory (unless you modified the AllocPixbuf method above).
//
// If you wanted to return the pixel buffer to the caller, either return the
// wuffs_aux::MemOwner (a type alias for "std::unique_ptr<void,
// decltype(&free)>") too, or call res.pixbuf_mem_owner.release() before
// returning and manually free the backing memory at an appropriate time.
}
int //
main(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
FILE* f = fopen(argv[i], "r");
if (!f) {
printf("%-30s could not open file: %s\n", argv[i], strerror(errno));
continue;
}
handle(argv[i], f);
fclose(f);
}
return 0;
}