Nigel Tao | 0f98004 | 2022-05-25 16:59:13 +1000 | [diff] [blame^] | 1 | // Copyright 2022 The Wuffs Authors. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | // ---------------- |
| 16 | |
| 17 | /* |
| 18 | print-average-pixel prints the average color of an image's pixels (as well as |
| 19 | the image file format, width and height). It's a toy program to demonstrate how |
| 20 | to use the wuffs_aux C++ API to decode an image and iterate over its pixels. |
| 21 | */ |
| 22 | |
| 23 | #include <inttypes.h> |
| 24 | #include <stdio.h> |
| 25 | |
| 26 | // Wuffs ships as a "single file C library" or "header file library" as per |
| 27 | // https://github.com/nothings/stb/blob/master/docs/stb_howto.txt |
| 28 | // |
| 29 | // To use that single file as a "foo.c"-like implementation, instead of a |
| 30 | // "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or |
| 31 | // compiling it. |
| 32 | #define WUFFS_IMPLEMENTATION |
| 33 | |
| 34 | // Defining the WUFFS_CONFIG__STATIC_FUNCTIONS macro is optional, but when |
| 35 | // combined with WUFFS_IMPLEMENTATION, it demonstrates making all of Wuffs' |
| 36 | // functions have static storage. |
| 37 | // |
| 38 | // This can help the compiler ignore or discard unused code, which can produce |
| 39 | // faster compiles and smaller binaries. Other motivations are discussed in the |
| 40 | // "ALLOW STATIC IMPLEMENTATION" section of |
| 41 | // https://raw.githubusercontent.com/nothings/stb/master/docs/stb_howto.txt |
| 42 | #define WUFFS_CONFIG__STATIC_FUNCTIONS |
| 43 | |
| 44 | // Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of |
| 45 | // release/c/etc.c choose which parts of Wuffs to build. That file contains the |
| 46 | // entire Wuffs standard library, implementing a variety of codecs and file |
| 47 | // formats. Without this macro definition, an optimizing compiler or linker may |
| 48 | // very well discard Wuffs code for unused codecs, but listing the Wuffs |
| 49 | // modules we use makes that process explicit. Preprocessing means that such |
| 50 | // code simply isn't compiled. |
| 51 | #define WUFFS_CONFIG__MODULES |
| 52 | #define WUFFS_CONFIG__MODULE__ADLER32 |
| 53 | #define WUFFS_CONFIG__MODULE__AUX__BASE |
| 54 | #define WUFFS_CONFIG__MODULE__AUX__IMAGE |
| 55 | #define WUFFS_CONFIG__MODULE__BASE |
| 56 | #define WUFFS_CONFIG__MODULE__BMP |
| 57 | #define WUFFS_CONFIG__MODULE__CRC32 |
| 58 | #define WUFFS_CONFIG__MODULE__DEFLATE |
| 59 | #define WUFFS_CONFIG__MODULE__GIF |
| 60 | #define WUFFS_CONFIG__MODULE__LZW |
| 61 | #define WUFFS_CONFIG__MODULE__NIE |
| 62 | #define WUFFS_CONFIG__MODULE__PNG |
| 63 | #define WUFFS_CONFIG__MODULE__TGA |
| 64 | #define WUFFS_CONFIG__MODULE__WBMP |
| 65 | #define WUFFS_CONFIG__MODULE__ZLIB |
| 66 | |
| 67 | // If building this program in an environment that doesn't easily accommodate |
| 68 | // relative includes, you can use the script/inline-c-relative-includes.go |
| 69 | // program to generate a stand-alone C file. |
| 70 | #include "../release/c/wuffs-unsupported-snapshot.c" |
| 71 | |
| 72 | class MyCallbacks : public wuffs_aux::DecodeImageCallbacks { |
| 73 | public: |
| 74 | MyCallbacks() : m_fourcc(0) {} |
| 75 | |
| 76 | uint32_t m_fourcc; |
| 77 | |
| 78 | private: |
| 79 | wuffs_base__image_decoder::unique_ptr // |
| 80 | SelectDecoder(uint32_t fourcc, |
| 81 | wuffs_base__slice_u8 prefix_data, |
| 82 | bool prefix_closed) override { |
| 83 | // Save the fourcc value (you can think of it as like a 'MIME type' but in |
| 84 | // uint32_t form) before calling the superclass' implementation. |
| 85 | // |
| 86 | // The "if (m_fourcc == 0)" is because SelectDecoder can be called multiple |
| 87 | // times. Files that are nominally BMP images can contain complete JPEG or |
| 88 | // PNG images. This program prints the outer file format, the first one |
| 89 | // encountered, not the inner one. |
| 90 | if (m_fourcc == 0) { |
| 91 | m_fourcc = fourcc; |
| 92 | } |
| 93 | return wuffs_aux::DecodeImageCallbacks::SelectDecoder(fourcc, prefix_data, |
| 94 | prefix_closed); |
| 95 | } |
| 96 | |
| 97 | wuffs_base__pixel_format // |
| 98 | SelectPixfmt(const wuffs_base__image_config& image_config) override { |
| 99 | // This is the same as the superclass' implementation, but makes it |
| 100 | // explicit that this program uses a single-plane pixel buffer (as opposed |
| 101 | // to e.g. 3-plane YCbCr) with 4 bytes per pixel (in B, G, R, A order) and |
| 102 | // premultiplied alpha. |
| 103 | return wuffs_base__make_pixel_format(WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL); |
| 104 | } |
| 105 | |
| 106 | AllocPixbufResult // |
| 107 | AllocPixbuf(const wuffs_base__image_config& image_config, |
| 108 | bool allow_uninitialized_memory) override { |
| 109 | // This just calls the superclass' implementation, but if you wanted more |
| 110 | // control about how the pixel buffer's memory is allocated and freed, |
| 111 | // change the code here. For example, if you (the wuffs_aux::DecodeImage |
| 112 | // caller) want to use an already-allocated buffer, instead of Wuffs (the |
| 113 | // callee) allocating a new buffer. |
| 114 | return wuffs_aux::DecodeImageCallbacks::AllocPixbuf( |
| 115 | image_config, allow_uninitialized_memory); |
| 116 | } |
| 117 | }; |
| 118 | |
| 119 | void // |
| 120 | handle(const char* filename, FILE* f) { |
| 121 | MyCallbacks callbacks; |
| 122 | wuffs_aux::sync_io::FileInput input(f); |
| 123 | wuffs_aux::DecodeImageResult res = wuffs_aux::DecodeImage(callbacks, input); |
| 124 | if (!res.error_message.empty()) { |
| 125 | printf("%-32s %s\n", filename, res.error_message.c_str()); |
| 126 | return; |
| 127 | } else if (res.pixbuf.pixcfg.pixel_format().repr != |
| 128 | WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL) { |
| 129 | printf("%-32s internal error: inconsistent pixel format\n", filename); |
| 130 | return; |
| 131 | } |
| 132 | |
| 133 | wuffs_base__table_u8 table = res.pixbuf.plane(0); |
| 134 | uint32_t w = res.pixbuf.pixcfg.width(); |
| 135 | uint32_t h = res.pixbuf.pixcfg.height(); |
| 136 | |
| 137 | uint64_t count = 0; |
| 138 | uint64_t color_b = 0; |
| 139 | uint64_t color_g = 0; |
| 140 | uint64_t color_r = 0; |
| 141 | uint64_t color_a = 0; |
| 142 | for (uint32_t y = 0; y < h; y++) { |
| 143 | const uint8_t* ptr = table.ptr + y * table.stride; |
| 144 | for (uint32_t x = 0; x < w; x++) { |
| 145 | count++; |
| 146 | color_b += *ptr++; |
| 147 | color_g += *ptr++; |
| 148 | color_r += *ptr++; |
| 149 | color_a += *ptr++; |
| 150 | } |
| 151 | } |
| 152 | if (count > 0) { |
| 153 | color_b = (color_b + (count / 2)) / count; |
| 154 | color_g = (color_g + (count / 2)) / count; |
| 155 | color_r = (color_r + (count / 2)) / count; |
| 156 | color_a = (color_a + (count / 2)) / count; |
| 157 | } |
| 158 | |
| 159 | printf("%-32s %c%c%c%c %5" PRIu32 " x %5" PRIu32 |
| 160 | " AverageARGB: %02X%02X%02X%02X\n", // |
| 161 | filename, // |
| 162 | (0xFF & (callbacks.m_fourcc >> 24)), // |
| 163 | (0xFF & (callbacks.m_fourcc >> 16)), // |
| 164 | (0xFF & (callbacks.m_fourcc >> 8)), // |
| 165 | (0xFF & (callbacks.m_fourcc >> 0)), // |
| 166 | w, h, // |
| 167 | (int)(color_a), // |
| 168 | (int)(color_r), // |
| 169 | (int)(color_g), // |
| 170 | (int)(color_b)); |
| 171 | |
| 172 | // While it's valid to deference table.ptr in this function, the end of scope |
| 173 | // here means that the res.pixbuf_mem_owner destructor will free the backing |
| 174 | // memory (unless you modified the AllocPixbuf method above). |
| 175 | // |
| 176 | // If you wanted to return the pixel buffer to the caller, either return the |
| 177 | // wuffs_aux::MemOwner (a type alias for "std::unique_ptr<void, |
| 178 | // decltype(&free)>") too, or call res.pixbuf_mem_owner.release() before |
| 179 | // returning and manually free the backing memory at an appropriate time. |
| 180 | } |
| 181 | |
| 182 | int // |
| 183 | main(int argc, char** argv) { |
| 184 | for (int i = 1; i < argc; i++) { |
| 185 | FILE* f = fopen(argv[i], "r"); |
| 186 | if (!f) { |
| 187 | printf("%-32s could not open file: %s\n", argv[i], strerror(errno)); |
| 188 | continue; |
| 189 | } |
| 190 | handle(argv[i], f); |
| 191 | fclose(f); |
| 192 | } |
| 193 | return 0; |
| 194 | } |