Pyrogenesis HEAD
Pyrogenesis, a RTS Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
tex.h
Go to the documentation of this file.
1/* Copyright (C) 2022 Wildfire Games.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining
4 * a copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23/*
24 * read/write 2d texture files; allows conversion between pixel formats
25 * and automatic orientation correction.
26 */
27
28/**
29
30Introduction
31------------
32
33This module allows reading/writing 2d images in various file formats and
34encapsulates them in Tex objects.
35It supports converting between pixel formats; this is to an extent done
36automatically when reading/writing. Provision is also made for flipping
37all images to a default orientation.
38
39
40Format Conversion
41-----------------
42
43Image file formats have major differences in their native pixel format:
44some store in BGR order, or have rows arranged bottom-up.
45We must balance runtime cost/complexity and convenience for the
46application (not dumping the entire problem on its lap).
47That means rejecting really obscure formats (e.g. right-to-left pixels),
48but converting everything else to uncompressed RGB "plain" format
49except where noted in enum TexFlags (1).
50
51Note: conversion is implemented as a pipeline: e.g. "DDS decompress +
52vertical flip" would be done by decompressing to RGB (DDS codec) and then
53flipping (generic transform). This is in contrast to all<->all
54conversion paths: that would be much more complex, if more efficient.
55
56Since any kind of preprocessing at runtime is undesirable (the absolute
57priority is minimizing load time), prefer file formats that are
58close to the final pixel format.
59
601) one of the exceptions is S3TC compressed textures. glCompressedTexImage2D
61 requires these be passed in their original format; decompressing would be
62 counterproductive. In this and similar cases, TexFlags indicates such
63 deviations from the plain format.
64
65
66Default Orientation
67-------------------
68
69After loading, all images (except DDS, because its orientation is
70indeterminate) are automatically converted to the global row
71orientation: top-down or bottom-up, as specified by
72tex_set_global_orientation. If that isn't called, the default is top-down
73to match Photoshop's DDS output (since this is meant to be the
74no-preprocessing-required optimized format).
75Reasons to change it might be to speed up loading bottom-up
76BMP or TGA images, or to match OpenGL's convention for convenience;
77however, be aware of the abovementioned issues with DDS.
78
79Rationale: it is not expected that this will happen at the renderer layer
80(a 'flip all texcoords' flag is too much trouble), so the
81application would have to do the same anyway. By taking care of it here,
82we unburden the app and save time, since some codecs (e.g. PNG) can
83flip for free when loading.
84
85
86Codecs / IO Implementation
87--------------------------
88
89To ease adding support for new formats, they are organized as codecs.
90The interface aims to minimize code duplication, so it's organized
91following the principle of "Template Method" - this module both
92calls into codecs, and provides helper functions that they use.
93
94IO is done via VFS, but the codecs are decoupled from this and
95work with memory buffers. Access to them is endian-safe.
96
97When "writing", the image is put into an expandable memory region.
98This supports external libraries like libpng that do not know the
99output size beforehand, but avoids the need for a buffer between
100library and IO layer. Read and write are zero-copy.
101
102**/
103
104#ifndef INCLUDED_TEX
105#define INCLUDED_TEX
106
107#include "lib/os_path.h"
110
111#include <vector>
112
113namespace ERR
114{
115 const Status TEX_UNKNOWN_FORMAT = -120100;
117 const Status TEX_FMT_INVALID = -120102;
120 const Status TEX_INVALID_LAYOUT = -120105;
121 const Status TEX_COMPRESSED = -120106;
122 const Status TEX_INVALID_SIZE = -120107;
123}
124
125namespace WARN
126{
127 const Status TEX_INVALID_DATA = +120108;
128}
129
130namespace INFO
131{
133}
134
135
136/**
137 * flags describing the pixel format. these are to be interpreted as
138 * deviations from "plain" format, i.e. uncompressed RGB.
139 **/
141{
142 /**
143 * flags & TEX_DXT is a field indicating compression.
144 * if 0, the texture is uncompressed;
145 * otherwise, it holds the S3TC type: 1,3,5 or DXT1A.
146 * not converted by default - glCompressedTexImage2D receives
147 * the compressed data.
148 **/
149 TEX_DXT = 0x7, // mask
150
151 /**
152 * we need a special value for DXT1a to avoid having to consider
153 * flags & TEX_ALPHA to determine S3TC type.
154 * the value is arbitrary; do not rely on it!
155 **/
156 DXT1A = 7,
157
158 /**
159 * indicates B and R pixel components are exchanged. depending on
160 * flags & TEX_ALPHA or bpp, this means either BGR or BGRA.
161 * not converted by default - it's an acceptable format for OpenGL.
162 **/
163 TEX_BGR = 0x08,
164
165 /**
166 * indicates the image contains an alpha channel. this is set for
167 * your convenience - there are many formats containing alpha and
168 * divining this information from them is hard.
169 * (conversion is not applicable here)
170 **/
171 TEX_ALPHA = 0x10,
172
173 /**
174 * indicates the image is 8bpp greyscale. this is required to
175 * differentiate between alpha-only and intensity formats.
176 * not converted by default - it's an acceptable format for OpenGL.
177 **/
178 TEX_GREY = 0x20,
179
180 /**
181 * flags & TEX_ORIENTATION is a field indicating orientation,
182 * i.e. in what order the pixel rows are stored.
183 *
184 * tex_load always sets this to the global orientation
185 * (and flips the image accordingly to match).
186 * texture codecs may in intermediate steps during loading set this
187 * to 0 if they don't know which way around they are (e.g. DDS),
188 * or to whatever their file contains.
189 **/
193
194 /**
195 * indicates the image data includes mipmaps. they are stored from lowest
196 * to highest (1x1), one after the other.
197 * (conversion is not applicable here)
198 **/
199 TEX_MIPMAPS = 0x100,
200
201 TEX_UNDEFINED_FLAGS = ~0x1FF
203
204/**
205 * Stores all data describing an image.
206 * TODO: rename to TextureData.
207 **/
208class Tex
209{
210public:
211 struct MIPLevel
212 {
213 // A pointer to the mip level image data (pixels).
218 };
219
220 /**
221 * file buffer or image data. note: during the course of transforms
222 * (which may occur when being loaded), this may be replaced with
223 * a new buffer (e.g. if decompressing file contents).
224 **/
225 std::shared_ptr<u8> m_Data;
226
228
229 /**
230 * offset to image data in file. this is required since
231 * tex_get_data needs to return the pixels, but data
232 * returns the actual file buffer. zero-copy load and
233 * write-back to file is also made possible.
234 **/
235 size_t m_Ofs;
236
237 size_t m_Width;
238 size_t m_Height;
239 size_t m_Bpp;
240
241 /// see TexFlags and "Format Conversion" in docs.
242 size_t m_Flags;
243
245 {
246 free();
247 }
248
249 /**
250 * Is the texture object valid and self-consistent?
251 *
252 * @return Status
253 **/
254 Status validate() const;
255
256 /**
257 * free all resources associated with the image and make further
258 * use of it impossible.
259 *
260 * @return Status
261 **/
262 void free();
263
264 /**
265 * decode an in-memory texture file into texture object.
266 *
267 * FYI, currently BMP, TGA, JPG, JP2, PNG, DDS are supported - but don't
268 * rely on this (not all codecs may be included).
269 *
270 * @param data Input data.
271 * @param data_size Its size [bytes].
272 * @return Status.
273 **/
274 Status decode(const std::shared_ptr<u8>& data, size_t data_size);
275
276 /**
277 * encode a texture into a memory buffer in the desired file format.
278 *
279 * @param extension (including '.').
280 * @param da Output memory array. Allocated here; caller must free it
281 * when no longer needed. Invalid unless function succeeds.
282 * @return Status
283 **/
284 Status encode(const OsPath& extension, DynArray* da);
285
286 /**
287 * store the given image data into a Tex object; this will be as if
288 * it had been loaded via tex_load.
289 *
290 * rationale: support for in-memory images is necessary for
291 * emulation of glCompressedTexImage2D and useful overall.
292 * however, we don't want to provide an alternate interface for each API;
293 * these would have to be changed whenever fields are added to Tex.
294 * instead, provide one entry point for specifying images.
295 * note: since we do not know how <img> was allocated, the caller must free
296 * it themselves (after calling tex_free, which is required regardless of
297 * alloc type).
298 *
299 * we need only add bookkeeping information and "wrap" it in
300 * our Tex struct, hence the name.
301 *
302 * @param w,h Pixel dimensions.
303 * @param bpp Bits per pixel.
304 * @param flags TexFlags.
305 * @param data Img texture data. note: size is calculated from other params.
306 * @param ofs
307 * @return Status
308 **/
309 Status wrap(size_t w, size_t h, size_t bpp, size_t flags, const std::shared_ptr<u8>& data, size_t ofs);
310
311 //
312 // modify image
313 //
314
315 /**
316 * Change the pixel format.
317 *
318 * @param transforms TexFlags that are to be flipped.
319 * @return Status
320 **/
321 Status transform(size_t transforms);
322
323 /**
324 * Change the pixel format (2nd version)
325 * (note: this is equivalent to Tex::transform(t, t->flags^new_flags).
326 *
327 * @param new_flags desired new value of TexFlags.
328 * @return Status
329 **/
330 Status transform_to(size_t new_flags);
331
332 //
333 // return image information
334 //
335
336 /**
337 * return a pointer to the image data (pixels), taking into account any
338 * header(s) that may come before it.
339 *
340 * @return pointer to the data.
341 **/
342 u8* get_data();
343
344 const std::vector<MIPLevel>& GetMIPLevels() const { return m_MIPLevels; }
345
346 /**
347 * return the ARGB value of the 1x1 mipmap level of the texture.
348 *
349 * @return ARGB value (or 0 if texture does not have mipmaps)
350 **/
351 u32 get_average_color() const;
352
353 /**
354 * return total byte size of the image pixels. (including mipmaps!)
355 * rationale: this is preferable to calculating manually because it's
356 * less error-prone (e.g. confusing bits_per_pixel with bytes).
357 *
358 * @return size [bytes]
359 **/
360 size_t img_size() const;
361
362private:
363 void UpdateMIPLevels();
364
365 std::vector<MIPLevel> m_MIPLevels;
366};
367
368
369/**
370 * Set the orientation to which all loaded images will
371 * automatically be converted (excepting file formats that don't specify
372 * their orientation, i.e. DDS). See "Default Orientation" in docs.
373 * @param orientation Either TEX_BOTTOM_UP or TEX_TOP_DOWN.
374 **/
375extern void tex_set_global_orientation(int orientation);
376
377
378/**
379 * special value for levels_to_skip: the callback will only be called
380 * for the base mipmap level (i.e. 100%)
381 **/
382const int TEX_BASE_LEVEL_ONLY = -1;
383
384/**
385 * callback function for each mipmap level.
386 *
387 * @param level number; 0 for base level (i.e. 100%), or the first one
388 * in case some were skipped.
389 * @param level_w, level_h pixel dimensions (powers of 2, never 0)
390 * @param level_data the level's texels
391 * @param level_data_size [bytes]
392 * @param cbData passed through from tex_util_foreach_mipmap.
393 **/
394typedef void (*MipmapCB)(size_t level, size_t level_w, size_t level_h, const u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData);
395
396/**
397 * for a series of mipmaps stored from base to highest, call back for
398 * each level.
399 *
400 * @param w,h Pixel dimensions.
401 * @param bpp Bits per pixel.
402 * @param data Series of mipmaps.
403 * @param levels_to_skip Number of levels (counting from base) to skip, or
404 * TEX_BASE_LEVEL_ONLY to only call back for the base image.
405 * Rationale: this avoids needing to special case for images with or
406 * without mipmaps.
407 * @param data_padding Minimum pixel dimensions of mipmap levels.
408 * This is used in S3TC images, where each level is actually stored in
409 * 4x4 blocks. usually 1 to indicate levels are consecutive.
410 * @param cb MipmapCB to call.
411 * @param cbData Extra data to pass to cb.
412 **/
413extern void tex_util_foreach_mipmap(size_t w, size_t h, size_t bpp, const u8* data, int levels_to_skip, size_t data_padding, MipmapCB cb, void* RESTRICT cbData);
414
415
416//
417// image writing
418//
419
420/**
421 * Is the file's extension that of a texture format supported by tex_load?
422 *
423 * Rationale: tex_load complains if the given file is of an
424 * unsupported type. this API allows users to preempt that warning
425 * (by checking the filename themselves), and also provides for e.g.
426 * enumerating only images in a file picker.
427 * an alternative might be a flag to suppress warning about invalid files,
428 * but this is open to misuse.
429 *
430 * @param pathname Only the extension (starting with '.') is used. case-insensitive.
431 * @return bool
432 **/
433extern bool tex_is_known_extension(const VfsPath& pathname);
434
435/**
436 * return the minimum header size (i.e. offset to pixel data) of the
437 * file format corresponding to the filename.
438 *
439 * rationale: this can be used to optimize calls to tex_write: when
440 * allocating the buffer that will hold the image, allocate this much
441 * extra and pass the pointer as base+hdr_size. this allows writing the
442 * header directly into the output buffer and makes for zero-copy IO.
443 *
444 * @param filename Filename; only the extension (that after '.') is used.
445 * case-insensitive.
446 * @return size [bytes] or 0 on error (i.e. no codec found).
447 **/
448extern size_t tex_hdr_size(const VfsPath& filename);
449
450#endif // INCLUDED_TEX
Definition: path.h:80
Stores all data describing an image.
Definition: tex.h:209
Status wrap(size_t w, size_t h, size_t bpp, size_t flags, const std::shared_ptr< u8 > &data, size_t ofs)
store the given image data into a Tex object; this will be as if it had been loaded via tex_load.
Definition: tex.cpp:588
const std::vector< MIPLevel > & GetMIPLevels() const
Definition: tex.h:344
size_t m_DataSize
Definition: tex.h:227
size_t m_Flags
see TexFlags and "Format Conversion" in docs.
Definition: tex.h:242
size_t m_Height
Definition: tex.h:238
Status validate() const
Is the texture object valid and self-consistent?
Definition: tex.cpp:57
std::shared_ptr< u8 > m_Data
file buffer or image data.
Definition: tex.h:225
u8 * get_data()
return a pointer to the image data (pixels), taking into account any header(s) that may come before i...
Definition: tex.cpp:625
size_t img_size() const
return total byte size of the image pixels.
Definition: tex.cpp:681
u32 get_average_color() const
return the ARGB value of the 1x1 mipmap level of the texture.
Definition: tex.cpp:637
Status encode(const OsPath &extension, DynArray *da)
encode a texture into a memory buffer in the desired file format.
Definition: tex.cpp:750
Status transform_to(size_t new_flags)
Change the pixel format (2nd version) (note: this is equivalent to Tex::transform(t,...
Definition: tex.cpp:489
size_t m_Ofs
offset to image data in file.
Definition: tex.h:235
std::vector< MIPLevel > m_MIPLevels
Definition: tex.h:365
~Tex()
Definition: tex.h:244
Status decode(const std::shared_ptr< u8 > &data, size_t data_size)
decode an in-memory texture file into texture object.
Definition: tex.cpp:715
void UpdateMIPLevels()
Definition: tex.cpp:777
Status transform(size_t transforms)
Change the pixel format.
Definition: tex.cpp:460
size_t m_Width
Definition: tex.h:237
size_t m_Bpp
Definition: tex.h:239
void free()
free all resources associated with the image and make further use of it impossible.
Definition: tex.cpp:608
#define RESTRICT
Definition: code_annotation.h:334
Definition: debug.h:395
const Status TEX_NOT_8BIT_PRECISION
Definition: tex.h:119
const Status TEX_INVALID_LAYOUT
Definition: tex.h:120
const Status TEX_INVALID_SIZE
Definition: tex.h:122
const Status TEX_UNKNOWN_FORMAT
Definition: tex.h:115
const Status TEX_INVALID_COLOR_TYPE
Definition: tex.h:118
const Status TEX_COMPRESSED
Definition: tex.h:121
const Status TEX_INCOMPLETE_HEADER
Definition: tex.h:116
const Status TEX_FMT_INVALID
Definition: tex.h:117
Definition: debug.h:411
const Status TEX_CODEC_CANNOT_HANDLE
Definition: tex.h:132
Definition: tex.h:126
const Status TEX_INVALID_DATA
Definition: tex.h:127
i64 Status
Error handling system.
Definition: status.h:173
provides a memory range that can be expanded but doesn't waste physical memory or relocate itself.
Definition: dynarray.h:40
Definition: tex.h:212
u32 height
Definition: tex.h:217
u8 * data
Definition: tex.h:214
u32 dataSize
Definition: tex.h:215
u32 width
Definition: tex.h:216
void(* MipmapCB)(size_t level, size_t level_w, size_t level_h, const u8 *RESTRICT level_data, size_t level_data_size, void *RESTRICT cbData)
callback function for each mipmap level.
Definition: tex.h:394
bool tex_is_known_extension(const VfsPath &pathname)
Is the file's extension that of a texture format supported by tex_load?
Definition: tex.cpp:565
void tex_set_global_orientation(int orientation)
Set the orientation to which all loaded images will automatically be converted (excepting file format...
Definition: tex.cpp:508
TexFlags
flags describing the pixel format.
Definition: tex.h:141
@ TEX_BGR
indicates B and R pixel components are exchanged.
Definition: tex.h:163
@ TEX_ORIENTATION
Definition: tex.h:192
@ TEX_GREY
indicates the image is 8bpp greyscale.
Definition: tex.h:178
@ TEX_MIPMAPS
mask
Definition: tex.h:199
@ TEX_BOTTOM_UP
flags & TEX_ORIENTATION is a field indicating orientation, i.e.
Definition: tex.h:190
@ TEX_UNDEFINED_FLAGS
Definition: tex.h:201
@ TEX_ALPHA
indicates the image contains an alpha channel.
Definition: tex.h:171
@ DXT1A
we need a special value for DXT1a to avoid having to consider flags & TEX_ALPHA to determine S3TC typ...
Definition: tex.h:156
@ TEX_TOP_DOWN
Definition: tex.h:191
@ TEX_DXT
flags & TEX_DXT is a field indicating compression.
Definition: tex.h:149
const int TEX_BASE_LEVEL_ONLY
special value for levels_to_skip: the callback will only be called for the base mipmap level (i....
Definition: tex.h:382
size_t tex_hdr_size(const VfsPath &filename)
return the minimum header size (i.e.
Definition: tex.cpp:701
void tex_util_foreach_mipmap(size_t w, size_t h, size_t bpp, const u8 *data, int levels_to_skip, size_t data_padding, MipmapCB cb, void *RESTRICT cbData)
for a series of mipmaps stored from base to highest, call back for each level.
Definition: tex.cpp:128
uint8_t u8
Definition: types.h:37
uint32_t u32
Definition: types.h:39