Overte C++ Documentation
KTX.h
1 //
2 // KTX.h
3 // ktx/src/ktx
4 //
5 // Created by Zach Pomerantz on 2/08/2017.
6 // Copyright 2017 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 #pragma once
12 #ifndef hifi_ktx_KTX_h
13 #define hifi_ktx_KTX_h
14 
15 #include <array>
16 #include <list>
17 #include <vector>
18 #include <cstdint>
19 #include <cstring>
20 #include <string>
21 #include <memory>
22 
23 #include <shared/Storage.h>
24 
25 #include "../khronos/KHR.h"
26 
27 /*
28 
29 KTX Specification: https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
30 
31 
32 **** A KTX header is 64 bytes layed out as follows
33 
34 Byte[12] identifier
35 UInt32 endianness
36 UInt32 glType
37 UInt32 glTypeSize
38 UInt32 glFormat
39 Uint32 glInternalFormat
40 Uint32 glBaseInternalFormat
41 UInt32 pixelWidth
42 UInt32 pixelHeight
43 UInt32 pixelDepth
44 UInt32 numberOfArrayElements
45 UInt32 numberOfFaces
46 UInt32 numberOfMipmapLevels
47 UInt32 bytesOfKeyValueData
48 
49 **** Each KTX key value pair block is 4 byte aligned
50 
51 for each keyValuePair that fits in bytesOfKeyValueData
52  UInt32 keyAndValueByteSize
53  Byte keyAndValue[keyAndValueByteSize]
54  Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)]
55 end
56 
57 **** Each mip and cube face is 4 byte aligned
58 
59 for each mipmap_level in numberOfMipmapLevels*
60  UInt32 imageSize;
61  for each array_element in numberOfArrayElements*
62  for each face in numberOfFaces
63  for each z_slice in pixelDepth*
64  for each row or row_of_blocks in pixelHeight*
65  for each pixel or block_of_pixels in pixelWidth
66  Byte data[format-specific-number-of-bytes]**
67  end
68  end
69  end
70  Byte cubePadding[0-3]
71  end
72  end
73  Byte mipPadding[3 - ((imageSize + 3) % 4)]
74 end
75 
76 * Replace with 1 if this field is 0.
77 
78 ** Uncompressed texture data matches a GL_UNPACK_ALIGNMENT of 4.
79 */
80 
81 namespace ktx {
82  // Alignment constants
83  static const uint32_t ALIGNMENT { sizeof(uint32_t) };
84  static const uint32_t ALIGNMENT_REMAINDER { ALIGNMENT - 1 };
85  static const uint32_t NUM_CUBEMAPFACES = khronos::gl::texture::cubemap::NUM_CUBEMAPFACES;
86 
87  // FIXME move out of this header, not specific to ktx
88  const std::string HIFI_MIN_POPULATED_MIP_KEY { "hifi.minMip" };
89 
90 
91  using Byte = uint8_t;
92 
93  using GLType = khronos::gl::Type;
94  using GLFormat = khronos::gl::texture::Format;
95  using GLInternalFormat = khronos::gl::texture::InternalFormat;
96  using GLBaseInternalFormat = khronos::gl::texture::BaseInternalFormat;
97 
98  using Storage = storage::Storage;
99  using StoragePointer = std::shared_ptr<const Storage>;
100 
101  struct ImageDescriptor;
102  using ImageDescriptors = std::vector<ImageDescriptor>;
103 
104  bool checkIdentifier(const Byte* identifier);
105 
106  // Returns the number of bytes required be added to the passed value to make it 4 byte aligned
107  template <typename T>
108  inline uint8_t evalPadding(T value) {
109  return ALIGNMENT_REMAINDER - ((value + ALIGNMENT_REMAINDER) % ALIGNMENT);
110  }
111 
112  // Returns the passed value rounded up to the next 4 byte aligned value, if it's not already 4 byte aligned
113  template <typename T>
114  inline T evalPaddedSize(T value) {
115  return (value + ALIGNMENT_REMAINDER) & ~(T)ALIGNMENT_REMAINDER;
116  }
117 
118  template <typename T>
119  inline T evalAlignedCount(T value) {
120  return (value + ALIGNMENT_REMAINDER) / ALIGNMENT;
121  }
122 
123  template <typename T>
124  inline bool checkAlignment(T value) {
125  return ((value & ALIGNMENT_REMAINDER) == 0);
126  }
127 
128 
129  // Header
130  struct Header {
131  static const uint32_t COMPRESSED_FORMAT { 0 };
132  static const uint32_t COMPRESSED_TYPE { 0 };
133  static const uint32_t COMPRESSED_TYPE_SIZE { 1 };
134  static const size_t IDENTIFIER_LENGTH { 12 };
135  using Identifier = std::array<uint8_t, IDENTIFIER_LENGTH>;
136  static const Identifier IDENTIFIER;
137 
138  static const uint32_t ENDIAN_TEST = 0x04030201;
139  static const uint32_t REVERSE_ENDIAN_TEST = 0x01020304;
140 
141  Header();
142 
143  Byte identifier[IDENTIFIER_LENGTH];
144  uint32_t endianness { ENDIAN_TEST };
145 
146  uint32_t glType { static_cast<uint32_t>(GLType::UNSIGNED_BYTE) };
147  uint32_t glTypeSize { 0 };
148  uint32_t glFormat { static_cast<uint32_t>(GLFormat::RGBA) };
149  uint32_t glInternalFormat { static_cast<uint32_t>(GLInternalFormat::RGBA8) };
150  uint32_t glBaseInternalFormat { static_cast<uint32_t>(GLBaseInternalFormat::RGBA) };
151 
152  uint32_t pixelWidth { 1 };
153  uint32_t pixelHeight { 1 };
154  uint32_t pixelDepth { 0 };
155  uint32_t numberOfArrayElements { 0 };
156  uint32_t numberOfFaces { 1 };
157  uint32_t numberOfMipmapLevels { 1 };
158 
159  uint32_t bytesOfKeyValueData { 0 };
160 
161  uint32_t getPixelWidth() const { return (pixelWidth ? pixelWidth : 1); }
162  uint32_t getPixelHeight() const { return (pixelHeight ? pixelHeight : 1); }
163  uint32_t getPixelDepth() const { return (pixelDepth ? pixelDepth : 1); }
164  uint32_t getNumberOfSlices() const { return (numberOfArrayElements ? numberOfArrayElements : 1); }
165  uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); }
166  bool isArray() const { return numberOfArrayElements > 0; }
167  bool isCompressed() const { return glFormat == COMPRESSED_FORMAT; }
168 
169  uint32_t evalMaxDimension() const;
170  uint32_t evalPixelOrBlockWidth(uint32_t level) const;
171  uint32_t evalPixelOrBlockHeight(uint32_t level) const;
172  uint32_t evalPixelOrBlockDepth(uint32_t level) const;
173 
174  size_t evalPixelOrBlockBitSize() const;
175  size_t evalRowSize(uint32_t level) const;
176  size_t evalFaceSize(uint32_t level) const;
177  size_t evalImageSize(uint32_t level) const;
178 
179  // FIXME base internal format should automatically be determined by internal format
180  // FIXME type size should automatically be determined by type
181  void setUncompressed(GLType type, uint32_t typeSize, GLFormat format, GLInternalFormat internalFormat, GLBaseInternalFormat baseInternalFormat) {
182  glType = (uint32_t) type;
183  glTypeSize = typeSize;
184  glFormat = (uint32_t) format;
185  glInternalFormat = (uint32_t) internalFormat;
186  glBaseInternalFormat = (uint32_t) baseInternalFormat;
187  }
188 
189  // FIXME base internal format should automatically be determined by internal format
190  void setCompressed(GLInternalFormat internalFormat, GLBaseInternalFormat baseInternalFormat) {
191  glType = COMPRESSED_TYPE;
192  glFormat = COMPRESSED_FORMAT;
193  glTypeSize = COMPRESSED_TYPE_SIZE;
194  glInternalFormat = (uint32_t) internalFormat;
195  glBaseInternalFormat = (uint32_t) baseInternalFormat;
196  }
197 
198  GLType getGLType() const { return (GLType)glType; }
199  uint32_t getTypeSize() const { return glTypeSize; }
200  GLFormat getGLFormat() const { return (GLFormat)glFormat; }
201  GLInternalFormat getGLInternaFormat() const { return (GLInternalFormat)glInternalFormat; }
202  GLBaseInternalFormat getGLBaseInternalFormat() const { return (GLBaseInternalFormat)glBaseInternalFormat; }
203 
204  void set1D(uint32_t width) { setDimensions(width); }
205  void set1DArray(uint32_t width, uint32_t numSlices) { setDimensions(width, 0, 0, (numSlices > 0 ? numSlices : 1)); }
206  void set2D(uint32_t width, uint32_t height) { setDimensions(width, height); }
207  void set2DArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1)); }
208  void set3D(uint32_t width, uint32_t height, uint32_t depth) { setDimensions(width, height, depth); }
209  void set3DArray(uint32_t width, uint32_t height, uint32_t depth, uint32_t numSlices) { setDimensions(width, height, depth, (numSlices > 0 ? numSlices : 1)); }
210  void setCube(uint32_t width, uint32_t height) { setDimensions(width, height, 0, 0, NUM_CUBEMAPFACES); }
211  void setCubeArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1), NUM_CUBEMAPFACES); }
212 
213  bool isValid() const;
214 
215  // Generate a set of image descriptors based on the assumption that the full mip pyramid is populated
216  ImageDescriptors generateImageDescriptors() const;
217 
218  private:
219  uint32_t evalPixelOrBlockDimension(uint32_t pixelDimension) const;
220  uint32_t evalMipPixelOrBlockDimension(uint32_t level, uint32_t pixelDimension) const;
221 
222  static inline uint32_t evalMipDimension(uint32_t mipLevel, uint32_t pixelDimension) {
223  return std::max(pixelDimension >> mipLevel, 1U);
224  }
225 
226  void setDimensions(uint32_t width, uint32_t height = 0, uint32_t depth = 0, uint32_t numSlices = 0, uint32_t numFaces = 1) {
227  pixelWidth = (width > 0 ? width : 1);
228  pixelHeight = height;
229  pixelDepth = depth;
230  numberOfArrayElements = numSlices;
231  numberOfFaces = numFaces;
232  }
233  };
234 
235  // Size as specified by the KTX specification
236  static const size_t KTX_HEADER_SIZE { 64 };
237  static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static and should not change from the spec");
238  static const size_t KV_SIZE_WIDTH { ALIGNMENT }; // Number of bytes for keyAndValueByteSize
239  static const size_t IMAGE_SIZE_WIDTH { ALIGNMENT }; // Number of bytes for imageSize
240 
241  // Key Values
242  struct KeyValue {
243  uint32_t _byteSize { 0 };
244  std::string _key;
245  std::vector<Byte> _value;
246 
247 
248  KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value);
249 
250  KeyValue(const std::string& key, const std::string& value);
251 
252  uint32_t serializedByteSize() const;
253 
254  static KeyValue parseSerializedKeyAndValue(uint32_t srcSize, const Byte* srcBytes);
255  static uint32_t writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval);
256 
257  using KeyValues = std::list<KeyValue>;
258  static uint32_t serializedKeyValuesByteSize(const KeyValues& keyValues);
259 
260  };
261  using KeyValues = KeyValue::KeyValues;
262 
263  struct ImageHeader {
264  using FaceOffsets = std::vector<size_t>;
265  using FaceBytes = std::vector<const Byte*>;
266 
267  const uint32_t _numFaces;
268  // This is the byte offset from the _start_ of the image region. For example, level 0
269  // will have a byte offset of 0.
270  const size_t _imageOffset;
271  const uint32_t _imageSize;
272  const uint32_t _faceSize;
273  const uint32_t _padding;
274 
275  ImageHeader(bool cube, size_t imageOffset, uint32_t imageSize, uint32_t padding) :
276  _numFaces(cube ? NUM_CUBEMAPFACES : 1),
277  _imageOffset(imageOffset),
278  _imageSize(imageSize * _numFaces),
279  _faceSize(imageSize),
280  _padding(padding) {
281  }
282  };
283 
284  struct Image;
285 
286  // Image without the image data itself
287  struct ImageDescriptor : public ImageHeader {
288  const FaceOffsets _faceOffsets;
289  ImageDescriptor(const ImageHeader& header, const FaceOffsets& offsets) : ImageHeader(header), _faceOffsets(offsets) {}
290  Image toImage(const ktx::StoragePointer& storage) const;
291  };
292 
293  // Image with the image data itself
294  struct Image : public ImageHeader {
295  FaceBytes _faceBytes;
296  Image(const ImageHeader& header, const FaceBytes& faces) : ImageHeader(header), _faceBytes(faces) {}
297  Image(size_t imageOffset, uint32_t imageSize, uint32_t padding, const Byte* bytes) :
298  ImageHeader(false, imageOffset, imageSize, padding),
299  _faceBytes(1, bytes) {}
300  Image(size_t imageOffset, uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) :
301  ImageHeader(true, imageOffset, pageSize, padding)
302  {
303  if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) {
304  _faceBytes = cubeFaceBytes;
305  }
306  }
307 
308  ImageDescriptor toImageDescriptor(const Byte* baseAddress) const;
309  };
310 
311  using Images = std::vector<Image>;
312 
313  class KTX;
314 
315  // A KTX descriptor is a lightweight container for all the information about a serialized KTX file, but without the
316  // actual image / face data available.
317  struct KTXDescriptor {
318  KTXDescriptor(const Header& header, const KeyValues& keyValues, const ImageDescriptors& imageDescriptors) : header(header), keyValues(keyValues), images(imageDescriptors) {}
319  const Header header;
320  const KeyValues keyValues;
321  const ImageDescriptors images;
322  size_t getMipFaceTexelsSize(uint16_t mip = 0, uint8_t face = 0) const;
323  size_t getMipFaceTexelsOffset(uint16_t mip = 0, uint8_t face = 0) const;
324  size_t getValueOffsetForKey(const std::string& key) const;
325  };
326 
327  class KTX {
328  void resetStorage(const StoragePointer& src);
329 
330  KTX() {}
331  KTX(const StoragePointer& storage, const Header& header, const KeyValues& keyValues, const Images& images);
332  public:
333  static bool validate(const StoragePointer& src);
334 
335  // Define a KTX object manually to write it somewhere (in a file on disk?)
336  // This path allocate the Storage where to store header, keyvalues and copy mips
337  // Then COPY all the data
338  static std::unique_ptr<KTX> create(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues());
339  static std::unique_ptr<KTX> createBare(const Header& header, const KeyValues& keyValues = KeyValues());
340 
341  // Instead of creating a full Copy of the src data in a KTX object, the write serialization can be performed with the
342  // following two functions
343  // size_t sizeNeeded = KTX::evalStorageSize(header, images);
344  //
345  // //allocate a buffer of size "sizeNeeded" or map a file with enough capacity
346  // Byte* destBytes = new Byte[sizeNeeded];
347  //
348  // // THen perform the writing of the src data to the destinnation buffer
349  // write(destBytes, sizeNeeded, header, images);
350  //
351  // This is exactly what is done in the create function
352  static size_t evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues());
353  static size_t evalStorageSize(const Header& header, const ImageDescriptors& images, const KeyValues& keyValues = KeyValues());
354  static size_t write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& images, const KeyValues& keyValues = KeyValues());
355  static size_t writeWithoutImages(Byte* destBytes, size_t destByteSize, const Header& header, const ImageDescriptors& descriptors, const KeyValues& keyValues = KeyValues());
356  static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues);
357  static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images);
358 
359  void writeMipData(uint16_t level, const Byte* sourceBytes, size_t source_size);
360 
361  // Parse a block of memory and create a KTX object from it
362  static std::unique_ptr<KTX> create(const StoragePointer& src);
363 
364  static bool checkHeaderFromStorage(size_t srcSize, const Byte* srcBytes);
365  static KeyValues parseKeyValues(size_t srcSize, const Byte* srcBytes);
366  static Images parseImages(const Header& header, size_t srcSize, const Byte* srcBytes);
367 
368  // Access raw pointers to the main sections of the KTX
369  const Header& getHeader() const;
370 
371  const Byte* getKeyValueData() const;
372  const Byte* getTexelsData() const;
373  storage::StoragePointer getMipFaceTexelsData(uint16_t mip = 0, uint8_t face = 0) const;
374  const StoragePointer& getStorage() const { return _storage; }
375 
376  KTXDescriptor toDescriptor() const;
377  size_t getKeyValueDataSize() const;
378  size_t getTexelsDataSize() const;
379  bool isValid() const;
380 
381  Header _header;
382  StoragePointer _storage;
383  KeyValues _keyValues;
384  Images _images;
385 
386  friend struct KTXDescriptor;
387  };
388 
389 }
390 
391 Q_DECLARE_METATYPE(ktx::KTXDescriptor*);
392 
393 #endif // hifi_ktx_KTX_h