Pyrogenesis  trunk
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 
30 Introduction
31 ------------
32 
33 This module allows reading/writing 2d images in various file formats and
34 encapsulates them in Tex objects.
35 It supports converting between pixel formats; this is to an extent done
36 automatically when reading/writing. Provision is also made for flipping
37 all images to a default orientation.
38 
39 
40 Format Conversion
41 -----------------
42 
43 Image file formats have major differences in their native pixel format:
44 some store in BGR order, or have rows arranged bottom-up.
45 We must balance runtime cost/complexity and convenience for the
46 application (not dumping the entire problem on its lap).
47 That means rejecting really obscure formats (e.g. right-to-left pixels),
48 but converting everything else to uncompressed RGB "plain" format
49 except where noted in enum TexFlags (1).
50 
51 Note: conversion is implemented as a pipeline: e.g. "DDS decompress +
52 vertical flip" would be done by decompressing to RGB (DDS codec) and then
53 flipping (generic transform). This is in contrast to all<->all
54 conversion paths: that would be much more complex, if more efficient.
55 
56 Since any kind of preprocessing at runtime is undesirable (the absolute
57 priority is minimizing load time), prefer file formats that are
58 close to the final pixel format.
59 
60 1) 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 
66 Default Orientation
67 -------------------
68 
69 After loading, all images (except DDS, because its orientation is
70 indeterminate) are automatically converted to the global row
71 orientation: top-down or bottom-up, as specified by
72 tex_set_global_orientation. If that isn't called, the default is top-down
73 to match Photoshop's DDS output (since this is meant to be the
74 no-preprocessing-required optimized format).
75 Reasons to change it might be to speed up loading bottom-up
76 BMP or TGA images, or to match OpenGL's convention for convenience;
77 however, be aware of the abovementioned issues with DDS.
78 
79 Rationale: it is not expected that this will happen at the renderer layer
80 (a 'flip all texcoords' flag is too much trouble), so the
81 application would have to do the same anyway. By taking care of it here,
82 we unburden the app and save time, since some codecs (e.g. PNG) can
83 flip for free when loading.
84 
85 
86 Codecs / IO Implementation
87 --------------------------
88 
89 To ease adding support for new formats, they are organized as codecs.
90 The interface aims to minimize code duplication, so it's organized
91 following the principle of "Template Method" - this module both
92 calls into codecs, and provides helper functions that they use.
93 
94 IO is done via VFS, but the codecs are decoupled from this and
95 work with memory buffers. Access to them is endian-safe.
96 
97 When "writing", the image is put into an expandable memory region.
98 This supports external libraries like libpng that do not know the
99 output size beforehand, but avoids the need for a buffer between
100 library 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"
108 #include "lib/file/vfs/vfs_path.h"
109 #include "lib/allocators/dynarray.h"
110 
111 #include <vector>
112 
113 namespace ERR
114 {
115  const Status TEX_UNKNOWN_FORMAT = -120100;
116  const Status TEX_INCOMPLETE_HEADER = -120101;
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 
125 namespace WARN
126 {
127  const Status TEX_INVALID_DATA = +120108;
128 }
129 
130 namespace 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  **/
191  TEX_TOP_DOWN = 0x80,
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 
202 };
203 
204 /**
205  * Stores all data describing an image.
206  * TODO: rename to TextureData.
207  **/
208 class Tex
209 {
210 public:
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 
227  size_t m_DataSize;
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 
362 private:
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  **/
375 extern 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  **/
382 const 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  **/
394 typedef 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  **/
413 extern 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  **/
433 extern 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  **/
448 extern size_t tex_hdr_size(const VfsPath& filename);
449 
450 #endif // INCLUDED_TEX
const Status TEX_INVALID_COLOR_TYPE
Definition: tex.h:118
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
const std::vector< MIPLevel > & GetMIPLevels() const
Definition: tex.h:344
const Status TEX_CODEC_CANNOT_HANDLE
Definition: tex.h:132
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
Stores all data describing an image.
Definition: tex.h:208
const Status TEX_COMPRESSED
Definition: tex.h:121
u32 dataSize
Definition: tex.h:215
size_t m_Flags
see TexFlags and "Format Conversion" in docs.
Definition: tex.h:242
~Tex()
Definition: tex.h:244
flags & TEX_DXT is a field indicating compression.
Definition: tex.h:149
mask
Definition: tex.h:199
indicates the image contains an alpha channel.
Definition: tex.h:171
Definition: tex.h:125
provides a memory range that can be expanded but doesn&#39;t waste physical memory or relocate itself...
Definition: dynarray.h:39
const Status TEX_INCOMPLETE_HEADER
Definition: tex.h:116
indicates the image is 8bpp greyscale.
Definition: tex.h:178
indicates B and R pixel components are exchanged.
Definition: tex.h:163
uint8_t u8
Definition: types.h:37
flags & TEX_ORIENTATION is a field indicating orientation, i.e.
Definition: tex.h:190
Definition: debug.h:423
Definition: tex.h:191
size_t m_Ofs
offset to image data in file.
Definition: tex.h:235
const Status TEX_FMT_INVALID
Definition: tex.h:117
uint32_t u32
Definition: types.h:39
Definition: path.h:79
size_t m_Width
Definition: tex.h:237
const Status TEX_NOT_8BIT_PRECISION
Definition: tex.h:119
Definition: tex.h:192
size_t m_Bpp
Definition: tex.h:239
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
size_t tex_hdr_size(const VfsPath &filename)
return the minimum header size (i.e.
Definition: tex.cpp:701
u32 height
Definition: tex.h:217
const Status TEX_UNKNOWN_FORMAT
Definition: tex.h:115
i64 Status
Error handling system.
Definition: status.h:169
Definition: tex.h:211
Introduction
Definition: debug.h:407
we need a special value for DXT1a to avoid having to consider flags & TEX_ALPHA to determine S3TC typ...
Definition: tex.h:156
const Status TEX_INVALID_DATA
Definition: tex.h:127
std::vector< MIPLevel > m_MIPLevels
Definition: tex.h:365
const char * extension
Definition: mongoose.cpp:1741
const Status TEX_INVALID_LAYOUT
Definition: tex.h:120
#define RESTRICT
Definition: code_annotation.h:332
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&#39;s extension that of a texture format supported by tex_load?
Definition: tex.cpp:565
size_t m_Height
Definition: tex.h:238
u32 width
Definition: tex.h:216
const Status TEX_INVALID_SIZE
Definition: tex.h:122
std::shared_ptr< u8 > m_Data
file buffer or image data.
Definition: tex.h:225
u8 * data
Definition: tex.h:214
Definition: tex.h:201
size_t m_DataSize
Definition: tex.h:227
TexFlags
flags describing the pixel format.
Definition: tex.h:140