LCOV - code coverage report
Current view: top level - source/lib/tex - tex.h (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 6 6 100.0 %
Date: 2023-01-19 00:18:29 Functions: 4 4 100.0 %

          Line data    Source code
       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;
     118             :     const Status TEX_INVALID_COLOR_TYPE  = -120103;
     119             :     const Status TEX_NOT_8BIT_PRECISION  = -120104;
     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             : {
     132             :     const Status TEX_CODEC_CANNOT_HANDLE = +120109;
     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             :  **/
     140             : enum TexFlags
     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             :      **/
     190             :     TEX_BOTTOM_UP = 0x40,
     191             :     TEX_TOP_DOWN  = 0x80,
     192             :     TEX_ORIENTATION = TEX_BOTTOM_UP|TEX_TOP_DOWN,    /// mask
     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
     202             : };
     203             : 
     204             : /**
     205             :  * Stores all data describing an image.
     206             :  * TODO: rename to TextureData.
     207             :  **/
     208          24 : class Tex
     209             : {
     210             : public:
     211             :     struct MIPLevel
     212             :     {
     213             :         // A pointer to the mip level image data (pixels).
     214             :         u8* data;
     215             :         u32 dataSize;
     216             :         u32 width;
     217             :         u32 height;
     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             : 
     244          24 :     ~Tex()
     245          24 :     {
     246          24 :         free();
     247          24 :     }
     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           6 :     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

Generated by: LCOV version 1.13