| // 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; |
| } |