blob: 89b0dce114d43434600cf57f4ed76179511aebce [file] [log] [blame]
Nigel Tao0f980042022-05-25 16:59:13 +10001// 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/*
18print-average-pixel prints the average color of an image's pixels (as well as
19the image file format, width and height). It's a toy program to demonstrate how
20to 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
72class 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.
Nigel Taoa56dbb32022-05-26 14:28:30 +1000114 //
115 // The example/sdl-imageviewer/sdl-imageviewer.cc file demonstrates
116 // overriding the AllocPixbuf method with the "bring your own buffer"
117 // approach. In the sdl-imageviewer case, the pixel buffer is allocated by
118 // SDL and lent to Wuffs, instead of allocated by Wuffs.
Nigel Tao0f980042022-05-25 16:59:13 +1000119 return wuffs_aux::DecodeImageCallbacks::AllocPixbuf(
120 image_config, allow_uninitialized_memory);
121 }
122};
123
124void //
125handle(const char* filename, FILE* f) {
126 MyCallbacks callbacks;
127 wuffs_aux::sync_io::FileInput input(f);
128 wuffs_aux::DecodeImageResult res = wuffs_aux::DecodeImage(callbacks, input);
129 if (!res.error_message.empty()) {
Nigel Taoa56dbb32022-05-26 14:28:30 +1000130 printf("%-30s %s\n", filename, res.error_message.c_str());
Nigel Tao0f980042022-05-25 16:59:13 +1000131 return;
132 } else if (res.pixbuf.pixcfg.pixel_format().repr !=
133 WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL) {
Nigel Taoa56dbb32022-05-26 14:28:30 +1000134 printf("%-30s internal error: inconsistent pixel format\n", filename);
Nigel Tao0f980042022-05-25 16:59:13 +1000135 return;
136 }
137
138 wuffs_base__table_u8 table = res.pixbuf.plane(0);
139 uint32_t w = res.pixbuf.pixcfg.width();
140 uint32_t h = res.pixbuf.pixcfg.height();
141
142 uint64_t count = 0;
143 uint64_t color_b = 0;
144 uint64_t color_g = 0;
145 uint64_t color_r = 0;
146 uint64_t color_a = 0;
147 for (uint32_t y = 0; y < h; y++) {
Nigel Taoa56dbb32022-05-26 14:28:30 +1000148 const uint8_t* ptr = table.ptr + (y * table.stride);
Nigel Tao0f980042022-05-25 16:59:13 +1000149 for (uint32_t x = 0; x < w; x++) {
150 count++;
151 color_b += *ptr++;
152 color_g += *ptr++;
153 color_r += *ptr++;
154 color_a += *ptr++;
155 }
156 }
157 if (count > 0) {
158 color_b = (color_b + (count / 2)) / count;
159 color_g = (color_g + (count / 2)) / count;
160 color_r = (color_r + (count / 2)) / count;
161 color_a = (color_a + (count / 2)) / count;
162 }
163
Nigel Taoa56dbb32022-05-26 14:28:30 +1000164 printf("%-30s %c%c%c%c %5" PRIu32 " x %5" PRIu32
Nigel Tao0f980042022-05-25 16:59:13 +1000165 " AverageARGB: %02X%02X%02X%02X\n", //
166 filename, //
167 (0xFF & (callbacks.m_fourcc >> 24)), //
168 (0xFF & (callbacks.m_fourcc >> 16)), //
169 (0xFF & (callbacks.m_fourcc >> 8)), //
170 (0xFF & (callbacks.m_fourcc >> 0)), //
171 w, h, //
172 (int)(color_a), //
173 (int)(color_r), //
174 (int)(color_g), //
175 (int)(color_b));
176
177 // While it's valid to deference table.ptr in this function, the end of scope
178 // here means that the res.pixbuf_mem_owner destructor will free the backing
179 // memory (unless you modified the AllocPixbuf method above).
180 //
181 // If you wanted to return the pixel buffer to the caller, either return the
182 // wuffs_aux::MemOwner (a type alias for "std::unique_ptr<void,
183 // decltype(&free)>") too, or call res.pixbuf_mem_owner.release() before
184 // returning and manually free the backing memory at an appropriate time.
185}
186
187int //
188main(int argc, char** argv) {
189 for (int i = 1; i < argc; i++) {
190 FILE* f = fopen(argv[i], "r");
191 if (!f) {
Nigel Taoa56dbb32022-05-26 14:28:30 +1000192 printf("%-30s could not open file: %s\n", argv[i], strerror(errno));
Nigel Tao0f980042022-05-25 16:59:13 +1000193 continue;
194 }
195 handle(argv[i], f);
196 fclose(f);
197 }
198 return 0;
199}