1// tTexture.h 
2// 
3// A tTexture is a 'hardware-ready' format. tTextures contain functionality for creating mipmap layers in a variety of 
4// block-compressed and uncompressed formats. A tTexture stores each mipmap layer in a tLayer. A tTexture can be 
5// created from either a tPicture or a dds file. The purpose of a dds file is so that content-creators have control 
6// over the authoring of each mipmap level and the exact pixel format used. Basically if you've created a dds file, 
7// you're saying you want the final hardware to use the image data unchanged and as authored -- same mip levels, same 
8// pixel format, same dimensions. For this reason, dds files should not be loaded into tPictures where image 
9// manipulation occurs and possibly lossy block-compressed dds images would be decompressed. A dds file may contain more 
10// than one image if it is a cubemap, but a tTexture only ever represents a single image. The tTexture dds constructor 
11// allows you to decide which one gets loaded. tTextures can save and load to a tChunk-based format, and are therefore 
12// useful at both pipeline and for runtime loading. To save to a tChunk file format a tTexture will call the Save 
13// method of all the tLayers. 
14// 
15// Copyright (c) 2006, 2016, 2017, 2019, 2020, 2023 Tristan Grimmer. 
16// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
17// granted, provided that the above copyright notice and this permission notice appear in all copies. 
18// 
19// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
20// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
21// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
22// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
23// PERFORMANCE OF THIS SOFTWARE. 
24 
25#pragma once 
26#include <Foundation/tList.h> 
27#include <Foundation/tString.h> 
28#include <System/tChunk.h> 
29#include "Image/tImageDDS.h" 
30#include "Image/tPicture.h" 
31#include "Image/tResample.h" 
32namespace tImage 
33
34 
35 
36// @todo This class needs some work. It's too strongly linked to tImageDDS. It should probably just take in layers and 
37// also be able to convert frames to layers and support various types of conversion/compression. 
38class tTexture : public tLink<tTexture
39
40public
41 // Creates an empty and initially invalid tTexture. You must manually call Set or Load. 
42 tTexture() { } 
43 
44 // This constructor is for cases where you just have a list of layers that you want to give the tTexture. After 
45 // construction the layer list will be empty. This is used by the tCubemap class so it can grab all 6 sides of a 
46 // cubemap dds and give them to 6 different tTextures. 
47 tTexture(tList<tLayer>& layers) { Set(layers); } 
48 
49 // Constructs from a dds file or dds object. Since dds files can have up to 6 images in them, these are explicitly 
50 // different constructors than the ones that load other image types. Additionally, the dds constructors don't mess 
51 // with compression or mip layers. What you put in the dds you get. correctRowOrder should normally be left set to 
52 // true unless you are loading a cubemap surface. Essentially dds files are upside down in terms of row order, but 
53 // only the cubemap loader know about it (and they use left handed side ordering). 
54 tTexture(const tString& ddsFile, tFaceIndex face = tFaceIndex_Default, bool correctRowOrder = true) { Load(ddsFile, face, correctRowOrder); } 
55 
56 // Similar to above but accepts an in-memory object. The ddsObject will be invalid after as the layers are stolen 
57 // from it. If necessary, a constructor can easily be added that does a copy and keeps it valid, but it will be 
58 // less efficient. 
59 tTexture(tImageDDS& ddsObject, tFaceIndex face = tFaceIndex_Default) { Set(ddsObject, face); } 
60 
61 // The other constructors require you to know a quality setting for resampling (mipmap generation) and compression. 
62 // For simplicity there is only Fast and Production quality settings, and it affects resampling _and_ compression. 
63 enum class tQuality 
64
65 Fast, // Bilinear resample filter. Fast BCn compress mode. 
66 Development, // Bicubic resample filter. High quality BCn compression. 
67 Production // Lanczos sinc-based resample filter. High quality BCn compression. 
68 }; 
69 
70 // Same as above except that an in-memory tPicture is used instead of a filename. The supplied tPicture will be 
71 // invalid after this constructor. This is because resampling may occur on the tPicture. 
72 tTexture 
73
74 tPicture& imageObject, bool generateMipMaps, tPixelFormat pixelFormat = tPixelFormat::Auto
75 tQuality quality = tQuality::Production, int forceWidth = 0, int forceHeight = 0 
76 ) { Set(imageObject, generateMipMaps, pixelFormat, quality, forceWidth, forceHeight); } 
77 
78 virtual ~tTexture() { Clear(); } 
79 
80 // If any constructor fails, you will be left with an invalid object. 
81 bool IsValid() const { return (Layers.GetNumItems() > 0) ? true : false; } 
82 
83 // See the corresponding constructors for a description of the behaviour of these functions. These functions all 
84 // return true on success. On failure, the tTexture is left invalid and in the case of the layer list Set, the list 
85 // is emptied. 
86 bool Set(tList<tLayer>&); 
87 bool Load(const tString& ddsFile, tFaceIndex = tFaceIndex_Default, bool correctRowOrder = true); 
88 bool Set(tImageDDS& ddsObject, tFaceIndex = tFaceIndex_Default); 
89 bool Set 
90
91 tPicture& imageObject, bool generateMipMaps, tPixelFormat = tPixelFormat::Auto
92 tQuality = tQuality::Production, int forceWidth = 0, int forceHeight = 0 
93 ); 
94 
95 void Clear() { Layers.Clear(); Opaque = true; } 
96 
97 int GetWidth() const /* Returns width of the main layer. */ { return IsValid() ? Layers.First()->Width : 0; } 
98 int GetHeight() const /* Returns width of the main layer. */ { return IsValid() ? Layers.First()->Height : 0; } 
99 tPixelFormat GetPixelFormat() const { return IsValid() ? Layers.First()->PixelFormat : tPixelFormat::Invalid; } 
100 bool IsMipmapped() const { return (Layers.GetNumItems() > 1) ? true : false; } 
101 void RemoveMipmaps(); 
102 bool IsOpaque() const { return Opaque; } 
103 int GetNumLayers() const { return Layers.GetNumItems(); } 
104 int GetNumMipmaps() const { return Layers.GetNumItems(); } 
105 tLayer* GetFirstLayer() const { return Layers.First(); } 
106 tLayer* GetMainLayer() const { return Layers.First(); } 
107 void StealLayers(tList<tLayer>&); // Leaves the object invalid. 
108 const tList<tLayer>& GetLayers() { return Layers; } 
109 int GetTotalPixelDataSize() const
110 
111 // Save and Load to tChunk format. 
112 void Save(tChunkWriter&) const
113 void Load(const tChunk&); 
114 
115 // Returns 1 + log2( max(width, height) ). The returned number is how many mipmaps it would take to make the 
116 // smallest a 1x1 square. Some pipelines may care about this and require all of them if mipmapping at all. 
117 int ComputeMaxNumberOfMipmaps() const
118 
119 // Textures are considered equal if the pixel format, opacity, and layers are the same. Invalid textures are always 
120 // considered not equal to other textures, even other invalid textures. 
121 bool operator==(const tTexture&) const
122 bool operator!=(const tTexture& src) const { return !(*this == src); } 
123 
124private
125 tPixelFormat DeterminePixelFormat(const tPicture&); 
126 tResampleFilter DetermineFilter(tQuality); 
127 int DetermineBlockEncodeQualityLevel(tQuality); 
128 
129 void ProcessImageTo_R8G8B8_Or_R8G8B8A8(tPicture&, tPixelFormat, bool generateMipmaps, tQuality); 
130 void ProcessImageTo_G3B5R5G3(tPicture&, bool generateMipmaps, tQuality); 
131 void ProcessImageTo_BCTC(tPicture&, tPixelFormat, bool generateMipmaps, tQuality); 
132 
133 bool Opaque = true; // Only true if the texture is completely opaque. 
134 
135 // The tTexture is only valid if there is at least one layer. The texture is considered to have mipmaps if the 
136 // number of layers is > 1. 
137 tList<tLayer> Layers
138 
139 static bool BC7EncInitialized
140}; 
141 
142 
143// Implementation below this line. 
144 
145 
146inline int tTexture::GetTotalPixelDataSize() const 
147
148 int total = 0
149 for (tLayer* layer = Layers.First(); layer; layer = layer->Next()) 
150 total += layer->GetDataSize(); 
151 
152 return total
153
154 
155 
156inline tPixelFormat tTexture::DeterminePixelFormat(const tPicture& image
157
158 if (Opaque
159 return tPixelFormat::BC1DXT1
160 else 
161 return tPixelFormat::BC3DXT4DXT5
162
163 
164 
165inline tResampleFilter tTexture::DetermineFilter(tQuality quality
166
167 switch (quality
168
169 case tQuality::Fast: return tResampleFilter::Bilinear
170 case tQuality::Development: return tResampleFilter::Bicubic
171 case tQuality::Production: return tResampleFilter::Lanczos
172
173 return tResampleFilter::Bicubic
174
175 
176 
177inline int tTexture::DetermineBlockEncodeQualityLevel(tQuality quality
178
179 switch (quality
180
181 case tQuality::Fast: return 4
182 case tQuality::Development: return 10
183 case tQuality::Production: return 10
184
185 return 4
186
187 
188 
189inline void tTexture::RemoveMipmaps() 
190
191 if (!IsMipmapped()) 
192 return
193 
194 tLayer* main = Layers.Remove(); 
195 Layers.Empty(); 
196 Layers.Append(item: main); 
197
198 
199 
200inline void tTexture::StealLayers(tList<tLayer>& layers
201
202 while (!Layers.IsEmpty()) 
203 layers.Append(item: Layers.Remove()); 
204 
205 Clear(); 
206
207 
208 
209
210