Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
tex.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 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  * support routines for 2d texture access/writing.
25  */
26 
27 #include "precompiled.h"
28 #include "tex.h"
29 
30 #include <math.h>
31 #include <stdlib.h>
32 #include <algorithm>
33 
34 #include "lib/timer.h"
35 #include "lib/bits.h"
37 #include "lib/sysdep/cpu.h"
38 
39 #include "tex_codec.h"
40 
41 
43  { ERR::TEX_FMT_INVALID, L"Invalid/unsupported texture format" },
44  { ERR::TEX_INVALID_COLOR_TYPE, L"Invalid color type" },
45  { ERR::TEX_NOT_8BIT_PRECISION, L"Not 8-bit channel precision" },
46  { ERR::TEX_INVALID_LAYOUT, L"Unsupported texel layout, e.g. right-to-left" },
47  { ERR::TEX_COMPRESSED, L"Unsupported texture compression" },
48  { WARN::TEX_INVALID_DATA, L"Warning: invalid texel data encountered" },
49  { ERR::TEX_INVALID_SIZE, L"Texture size is incorrect" },
50  { INFO::TEX_CODEC_CANNOT_HANDLE, L"Texture codec cannot handle the given format" }
51 };
52 STATUS_ADD_DEFINITIONS(texStatusDefinitions);
53 
54 
55 //-----------------------------------------------------------------------------
56 // validation
57 //-----------------------------------------------------------------------------
58 
59 // be careful not to use other tex_* APIs here because they call us.
61 {
62  if(t->flags & TEX_UNDEFINED_FLAGS)
64 
65  // pixel data (only check validity if the image is still in memory;
66  // ogl_tex frees the data after uploading to GL)
67  if(t->data)
68  {
69  // file size smaller than header+pixels.
70  // possible causes: texture file header is invalid,
71  // or file wasn't loaded completely.
72  if(t->dataSize < t->ofs + t->w*t->h*t->bpp/8)
74  }
75 
76  // bits per pixel
77  // (we don't bother checking all values; a sanity check is enough)
78  if(t->bpp % 4 || t->bpp > 32)
80 
81  // flags
82  // .. DXT value
83  const size_t dxt = t->flags & TEX_DXT;
84  if(dxt != 0 && dxt != 1 && dxt != DXT1A && dxt != 3 && dxt != 5)
86  // .. orientation
87  const size_t orientation = t->flags & TEX_ORIENTATION;
88  if(orientation == (TEX_BOTTOM_UP|TEX_TOP_DOWN))
90 
91  return INFO::OK;
92 }
93 
94 #define CHECK_TEX(t) RETURN_STATUS_IF_ERR(tex_validate(t))
95 
96 
97 // check if the given texture format is acceptable: 8bpp grey,
98 // 24bpp color or 32bpp color+alpha (BGR / upside down are permitted).
99 // basically, this is the "plain" format understood by all codecs and
100 // tex_codec_plain_transform.
101 Status tex_validate_plain_format(size_t bpp, size_t flags)
102 {
103  const bool alpha = (flags & TEX_ALPHA ) != 0;
104  const bool grey = (flags & TEX_GREY ) != 0;
105  const bool dxt = (flags & TEX_DXT ) != 0;
106  const bool mipmaps = (flags & TEX_MIPMAPS) != 0;
107 
108  if(dxt || mipmaps)
110 
111  // grey must be 8bpp without alpha, or it's invalid.
112  if(grey)
113  {
114  if(bpp == 8 && !alpha)
115  return INFO::OK;
117  }
118 
119  if(bpp == 24 && !alpha)
120  return INFO::OK;
121  if(bpp == 32 && alpha)
122  return INFO::OK;
123 
125 }
126 
127 
128 //-----------------------------------------------------------------------------
129 // mipmaps
130 //-----------------------------------------------------------------------------
131 
132 void tex_util_foreach_mipmap(size_t w, size_t h, size_t bpp, const u8* pixels, int levels_to_skip, size_t data_padding, MipmapCB cb, void* RESTRICT cbData)
133 {
134  ENSURE(levels_to_skip >= 0 || levels_to_skip == TEX_BASE_LEVEL_ONLY);
135 
136  size_t level_w = w, level_h = h;
137  const u8* level_data = pixels;
138 
139  // we iterate through the loop (necessary to skip over image data),
140  // but do not actually call back until the requisite number of
141  // levels have been skipped (i.e. level == 0).
142  int level = (levels_to_skip == TEX_BASE_LEVEL_ONLY)? 0 : -levels_to_skip;
143 
144  // until at level 1x1:
145  for(;;)
146  {
147  // used to skip past this mip level in <data>
148  const size_t level_dataSize = (size_t)(round_up(level_w, data_padding) * round_up(level_h, data_padding) * bpp/8);
149 
150  if(level >= 0)
151  cb((size_t)level, level_w, level_h, level_data, level_dataSize, cbData);
152 
153  level_data += level_dataSize;
154 
155  // 1x1 reached - done
156  if(level_w == 1 && level_h == 1)
157  break;
158  level_w /= 2;
159  level_h /= 2;
160  // if the texture is non-square, one of the dimensions will become
161  // 0 before the other. to satisfy OpenGL's expectations, change it
162  // back to 1.
163  if(level_w == 0) level_w = 1;
164  if(level_h == 0) level_h = 1;
165  level++;
166 
167  // special case: no mipmaps, we were only supposed to call for
168  // the base level
169  if(levels_to_skip == TEX_BASE_LEVEL_ONLY)
170  break;
171  }
172 }
173 
174 
176 {
178 
179  size_t prev_level_w;
180  size_t prev_level_h;
183 };
184 
185 // uses 2x2 box filter
186 static void create_level(size_t level, size_t level_w, size_t level_h, const u8* RESTRICT level_data, size_t level_dataSize, void* RESTRICT cbData)
187 {
188  CreateLevelData* cld = (CreateLevelData*)cbData;
189  const size_t src_w = cld->prev_level_w;
190  const size_t src_h = cld->prev_level_h;
191  const u8* src = cld->prev_level_data;
192  u8* dst = (u8*)level_data;
193 
194  // base level - must be copied over from source buffer
195  if(level == 0)
196  {
197  ENSURE(level_dataSize == cld->prev_level_dataSize);
198  memcpy(dst, src, level_dataSize);
199  }
200  else
201  {
202  const size_t num_components = cld->num_components;
203  const size_t dx = num_components, dy = dx*src_w;
204 
205  // special case: image is too small for 2x2 filter
206  if(cld->prev_level_w == 1 || cld->prev_level_h == 1)
207  {
208  // image is either a horizontal or vertical line.
209  // their memory layout is the same (packed pixels), so no special
210  // handling is needed; just pick max dimension.
211  for(size_t y = 0; y < std::max(src_w, src_h); y += 2)
212  {
213  for(size_t i = 0; i < num_components; i++)
214  {
215  *dst++ = (src[0]+src[dx]+1)/2;
216  src += 1;
217  }
218 
219  src += dx; // skip to next pixel (since box is 2x2)
220  }
221  }
222  // normal
223  else
224  {
225  for(size_t y = 0; y < src_h; y += 2)
226  {
227  for(size_t x = 0; x < src_w; x += 2)
228  {
229  for(size_t i = 0; i < num_components; i++)
230  {
231  *dst++ = (src[0]+src[dx]+src[dy]+src[dx+dy]+2)/4;
232  src += 1;
233  }
234 
235  src += dx; // skip to next pixel (since box is 2x2)
236  }
237 
238  src += dy; // skip to next row (since box is 2x2)
239  }
240  }
241 
242  ENSURE(dst == level_data + level_dataSize);
243  ENSURE(src == cld->prev_level_data + cld->prev_level_dataSize);
244  }
245 
246  cld->prev_level_data = level_data;
247  cld->prev_level_dataSize = level_dataSize;
248  cld->prev_level_w = level_w;
249  cld->prev_level_h = level_h;
250 }
251 
252 
253 static Status add_mipmaps(Tex* t, size_t w, size_t h, size_t bpp, void* newData, size_t dataSize)
254 {
255  // this code assumes the image is of POT dimension; we don't
256  // go to the trouble of implementing image scaling because
257  // the only place this is used (ogl_tex_upload) requires POT anyway.
258  if(!is_pow2(w) || !is_pow2(h))
260  t->flags |= TEX_MIPMAPS; // must come before tex_img_size!
261  const size_t mipmap_size = tex_img_size(t);
262  shared_ptr<u8> mipmapData;
263  AllocateAligned(mipmapData, mipmap_size);
264  CreateLevelData cld = { bpp/8, w, h, (const u8*)newData, dataSize };
265  tex_util_foreach_mipmap(w, h, bpp, mipmapData.get(), 0, 1, create_level, &cld);
266  t->data = mipmapData;
267  t->dataSize = mipmap_size;
268  t->ofs = 0;
269 
270  return INFO::OK;
271 }
272 
273 
274 //-----------------------------------------------------------------------------
275 // pixel format conversion (transformation)
276 //-----------------------------------------------------------------------------
277 
278 TIMER_ADD_CLIENT(tc_plain_transform);
279 
280 // handles BGR and row flipping in "plain" format (see below).
281 //
282 // called by codecs after they get their format-specific transforms out of
283 // the way. note that this approach requires several passes over the image,
284 // but is much easier to maintain than providing all<->all conversion paths.
285 //
286 // somewhat optimized (loops are hoisted, cache associativity accounted for)
287 static Status plain_transform(Tex* t, size_t transforms)
288 {
289 TIMER_ACCRUE(tc_plain_transform);
290 
291  // (this is also called directly instead of through ogl_tex, so
292  // we need to validate)
293  CHECK_TEX(t);
294 
295  // extract texture info
296  const size_t w = t->w, h = t->h, bpp = t->bpp;
297  const size_t flags = t->flags;
298  u8* const srcStorage = tex_get_data(t);
299 
300  // sanity checks (not errors, we just can't handle these cases)
301  // .. unknown transform
302  if(transforms & ~(TEX_BGR|TEX_ORIENTATION|TEX_MIPMAPS|TEX_ALPHA))
304  // .. data is not in "plain" format
306  // .. nothing to do
307  if(!transforms)
308  return INFO::OK;
309 
310  const size_t srcSize = tex_img_size(t);
311  size_t dstSize = srcSize;
312 
313  if(transforms & TEX_ALPHA)
314  {
315  // add alpha channel
316  if(bpp == 24)
317  {
318  dstSize = (srcSize / 3) * 4;
319  t->bpp = 32;
320  }
321  // remove alpha channel
322  else if(bpp == 32)
323  {
325  }
326  // can't have alpha with grayscale
327  else
328  {
330  }
331  }
332 
333  // allocate copy of the image data.
334  // rationale: L1 cache is typically A2 => swapping in-place with a
335  // line buffer leads to thrashing. we'll assume the whole texture*2
336  // fits in cache, allocate a copy, and transfer directly from there.
337  //
338  // this is necessary even when not flipping because the initial data
339  // is read-only.
340  shared_ptr<u8> dstStorage;
341  AllocateAligned(dstStorage, dstSize);
342 
343  // setup row source/destination pointers (simplifies outer loop)
344  u8* dst = (u8*)dstStorage.get();
345  const u8* src;
346  const size_t pitch = w * bpp/8; // source bpp (not necessarily dest bpp)
347  // .. avoid y*pitch multiply in row loop; instead, add row_ofs.
348  ssize_t row_ofs = (ssize_t)pitch;
349 
350  // flipping rows (0,1,2 -> 2,1,0)
351  if(transforms & TEX_ORIENTATION)
352  {
353  src = (const u8*)srcStorage+srcSize-pitch; // last row
354  row_ofs = -(ssize_t)pitch;
355  }
356  // adding/removing alpha channel (can't convert in-place)
357  else if(transforms & TEX_ALPHA)
358  {
359  src = (const u8*)srcStorage;
360  }
361  // do other transforms in-place
362  else
363  {
364  src = (const u8*)dstStorage.get();
365  memcpy(dstStorage.get(), srcStorage, srcSize);
366  }
367 
368  // no conversion necessary
369  if(!(transforms & (TEX_BGR | TEX_ALPHA)))
370  {
371  if(src != dst) // avoid overlapping memcpy if not flipping rows
372  {
373  for(size_t y = 0; y < h; y++)
374  {
375  memcpy(dst, src, pitch);
376  dst += pitch;
377  src += row_ofs;
378  }
379  }
380  }
381  // RGB -> BGRA, BGR -> RGBA
382  else if(bpp == 24 && (transforms & TEX_ALPHA) && (transforms & TEX_BGR))
383  {
384  for(size_t y = 0; y < h; y++)
385  {
386  for(size_t x = 0; x < w; x++)
387  {
388  // need temporaries in case src == dst (i.e. not flipping)
389  const u8 b = src[0], g = src[1], r = src[2];
390  dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = 0xFF;
391  dst += 4;
392  src += 3;
393  }
394  src += row_ofs - pitch; // flip? previous row : stay
395  }
396  }
397  // RGB -> RGBA, BGR -> BGRA
398  else if(bpp == 24 && (transforms & TEX_ALPHA) && !(transforms & TEX_BGR))
399  {
400  for(size_t y = 0; y < h; y++)
401  {
402  for(size_t x = 0; x < w; x++)
403  {
404  // need temporaries in case src == dst (i.e. not flipping)
405  const u8 r = src[0], g = src[1], b = src[2];
406  dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = 0xFF;
407  dst += 4;
408  src += 3;
409  }
410  src += row_ofs - pitch; // flip? previous row : stay
411  }
412  }
413  // RGB <-> BGR
414  else if(bpp == 24 && !(transforms & TEX_ALPHA))
415  {
416  for(size_t y = 0; y < h; y++)
417  {
418  for(size_t x = 0; x < w; x++)
419  {
420  // need temporaries in case src == dst (i.e. not flipping)
421  const u8 b = src[0], g = src[1], r = src[2];
422  dst[0] = r; dst[1] = g; dst[2] = b;
423  dst += 3;
424  src += 3;
425  }
426  src += row_ofs - pitch; // flip? previous row : stay
427  }
428  }
429  // RGBA <-> BGRA
430  else if(bpp == 32 && !(transforms & TEX_ALPHA))
431  {
432  for(size_t y = 0; y < h; y++)
433  {
434  for(size_t x = 0; x < w; x++)
435  {
436  // need temporaries in case src == dst (i.e. not flipping)
437  const u8 b = src[0], g = src[1], r = src[2], a = src[3];
438  dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = a;
439  dst += 4;
440  src += 4;
441  }
442  src += row_ofs - pitch; // flip? previous row : stay
443  }
444  }
445  else
446  {
447  debug_warn(L"unsupported transform");
449  }
450 
451  t->data = dstStorage;
452  t->dataSize = dstSize;
453  t->ofs = 0;
454 
455  if(!(t->flags & TEX_MIPMAPS) && transforms & TEX_MIPMAPS)
456  RETURN_STATUS_IF_ERR(add_mipmaps(t, w, h, bpp, dstStorage.get(), dstSize));
457 
458  CHECK_TEX(t);
459  return INFO::OK;
460 }
461 
462 
463 TIMER_ADD_CLIENT(tc_transform);
464 
465 // change <t>'s pixel format by flipping the state of all TEX_* flags
466 // that are set in transforms.
467 Status tex_transform(Tex* t, size_t transforms)
468 {
469  TIMER_ACCRUE(tc_transform);
470  CHECK_TEX(t);
471 
472  const size_t target_flags = t->flags ^ transforms;
473  size_t remaining_transforms;
474  for(;;)
475  {
476  remaining_transforms = target_flags ^ t->flags;
477  // we're finished (all required transforms have been done)
478  if(remaining_transforms == 0)
479  return INFO::OK;
480 
481  Status ret = tex_codec_transform(t, remaining_transforms);
482  if(ret != INFO::OK)
483  break;
484  }
485 
486  // last chance
487  RETURN_STATUS_IF_ERR(plain_transform(t, remaining_transforms));
488  return INFO::OK;
489 }
490 
491 
492 // change <t>'s pixel format to the new format specified by <new_flags>.
493 // (note: this is equivalent to tex_transform(t, t->flags^new_flags).
494 Status tex_transform_to(Tex* t, size_t new_flags)
495 {
496  // tex_transform takes care of validating <t>
497  const size_t transforms = t->flags ^ new_flags;
498  return tex_transform(t, transforms);
499 }
500 
501 
502 //-----------------------------------------------------------------------------
503 // image orientation
504 //-----------------------------------------------------------------------------
505 
506 // see "Default Orientation" in docs.
507 
509 
510 // set the orientation (either TEX_BOTTOM_UP or TEX_TOP_DOWN) to which
511 // all loaded images will automatically be converted
512 // (excepting file formats that don't specify their orientation, i.e. DDS).
514 {
515  ENSURE(o == TEX_TOP_DOWN || o == TEX_BOTTOM_UP);
516  global_orientation = o;
517 }
518 
519 
521 {
522  // (can't use normal CHECK_TEX due to void return)
524 
525  size_t orientation = t->flags & TEX_ORIENTATION;
526  // if codec knows which way around the image is (i.e. not DDS):
527  if(orientation)
528  {
529  // flip image if necessary
530  size_t transforms = orientation ^ global_orientation;
531  WARN_IF_ERR(plain_transform(t, transforms));
532  }
533 
534  // indicate image is at global orientation. this is still done even
535  // if the codec doesn't know: the default orientation should be chosen
536  // to make that work correctly (see "Default Orientation" in docs).
537  t->flags = (t->flags & ~TEX_ORIENTATION) | global_orientation;
538 
539  // (can't use normal CHECK_TEX due to void return)
541 }
542 
543 
544 // indicate if the orientation specified by <src_flags> matches
545 // dst_orientation (if the latter is 0, then the global_orientation).
546 // (we ask for src_flags instead of src_orientation so callers don't
547 // have to mask off TEX_ORIENTATION)
548 bool tex_orientations_match(size_t src_flags, size_t dst_orientation)
549 {
550  const size_t src_orientation = src_flags & TEX_ORIENTATION;
551  if(dst_orientation == 0)
552  dst_orientation = global_orientation;
553  return (src_orientation == dst_orientation);
554 }
555 
556 
557 //-----------------------------------------------------------------------------
558 // misc. API
559 //-----------------------------------------------------------------------------
560 
561 // indicate if <filename>'s extension is that of a texture format
562 // supported by tex_load. case-insensitive.
563 //
564 // rationale: tex_load complains if the given file is of an
565 // unsupported type. this API allows users to preempt that warning
566 // (by checking the filename themselves), and also provides for e.g.
567 // enumerating only images in a file picker.
568 // an alternative might be a flag to suppress warning about invalid files,
569 // but this is open to misuse.
570 bool tex_is_known_extension(const VfsPath& pathname)
571 {
572  const TexCodecVTbl* dummy;
573  // found codec for it => known extension
574  const OsPath extension = pathname.Extension();
575  if(tex_codec_for_filename(extension, &dummy) == INFO::OK)
576  return true;
577 
578  return false;
579 }
580 
581 
582 // store the given image data into a Tex object; this will be as if
583 // it had been loaded via tex_load.
584 //
585 // rationale: support for in-memory images is necessary for
586 // emulation of glCompressedTexImage2D and useful overall.
587 // however, we don't want to provide an alternate interface for each API;
588 // these would have to be changed whenever fields are added to Tex.
589 // instead, provide one entry point for specifying images.
590 //
591 // we need only add bookkeeping information and "wrap" it in
592 // our Tex struct, hence the name.
593 Status tex_wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr<u8>& data, size_t ofs, Tex* t)
594 {
595  t->w = w;
596  t->h = h;
597  t->bpp = bpp;
598  t->flags = flags;
599  t->data = data;
600  t->dataSize = ofs + w*h*bpp/8;
601  t->ofs = ofs;
602 
603  CHECK_TEX(t);
604  return INFO::OK;
605 }
606 
607 
608 // free all resources associated with the image and make further
609 // use of it impossible.
610 void tex_free(Tex* t)
611 {
612  // do not validate <t> - this is called from tex_load if loading
613  // failed, so not all fields may be valid.
614 
615  t->data.reset();
616 
617  // do not zero out the fields! that could lead to trouble since
618  // ogl_tex_upload followed by ogl_tex_free is legit, but would
619  // cause OglTex_validate to fail (since its Tex.w is == 0).
620 }
621 
622 
623 //-----------------------------------------------------------------------------
624 // getters
625 //-----------------------------------------------------------------------------
626 
627 // returns a pointer to the image data (pixels), taking into account any
628 // header(s) that may come before it.
629 u8* tex_get_data(const Tex* t)
630 {
631  // (can't use normal CHECK_TEX due to u8* return value)
633 
634  u8* p = t->data.get();
635  if(!p)
636  return 0;
637  return p + t->ofs;
638 }
639 
640 // returns colour of 1x1 mipmap level
642 {
643  // require mipmaps
644  if(!(t->flags & TEX_MIPMAPS))
645  return 0;
646 
647  // find the total size of image data
648  size_t size = tex_img_size(t);
649 
650  // compute the size of the last (1x1) mipmap level
651  const size_t data_padding = (t->flags & TEX_DXT)? 4 : 1;
652  size_t last_level_size = (size_t)(data_padding * data_padding * t->bpp/8);
653 
654  // construct a new texture based on the current one,
655  // but set its data pointer offset to the last mipmap level's data
656  Tex basetex = *t;
657  basetex.w = 1;
658  basetex.h = 1;
659  basetex.ofs += size - last_level_size;
660 
661  // convert to BGRA
663 
664  // extract components into u32
665  ENSURE(basetex.dataSize >= basetex.ofs+4);
666  u8 b = basetex.data.get()[basetex.ofs];
667  u8 g = basetex.data.get()[basetex.ofs+1];
668  u8 r = basetex.data.get()[basetex.ofs+2];
669  u8 a = basetex.data.get()[basetex.ofs+3];
670  return b + (g << 8) + (r << 16) + (a << 24);
671 }
672 
673 
674 static void add_level_size(size_t UNUSED(level), size_t UNUSED(level_w), size_t UNUSED(level_h), const u8* RESTRICT UNUSED(level_data), size_t level_dataSize, void* RESTRICT cbData)
675 {
676  size_t* ptotal_size = (size_t*)cbData;
677  *ptotal_size += level_dataSize;
678 }
679 
680 // return total byte size of the image pixels. (including mipmaps!)
681 // this is preferable to calculating manually because it's
682 // less error-prone (e.g. confusing bits_per_pixel with bytes).
683 size_t tex_img_size(const Tex* t)
684 {
685  // (can't use normal CHECK_TEX due to size_t return value)
687 
688  const int levels_to_skip = (t->flags & TEX_MIPMAPS)? 0 : TEX_BASE_LEVEL_ONLY;
689  const size_t data_padding = (t->flags & TEX_DXT)? 4 : 1;
690  size_t out_size = 0;
691  tex_util_foreach_mipmap(t->w, t->h, t->bpp, 0, levels_to_skip, data_padding, add_level_size, &out_size);
692  return out_size;
693 }
694 
695 
696 // return the minimum header size (i.e. offset to pixel data) of the
697 // file format indicated by <fn>'s extension (that is all it need contain:
698 // e.g. ".bmp"). returns 0 on error (i.e. no codec found).
699 // this can be used to optimize calls to tex_write: when allocating the
700 // buffer that will hold the image, allocate this much extra and
701 // pass the pointer as base+hdr_size. this allows writing the header
702 // directly into the output buffer and makes for zero-copy IO.
703 size_t tex_hdr_size(const VfsPath& filename)
704 {
705  const TexCodecVTbl* c;
706 
707  const OsPath extension = filename.Extension();
709  return c->hdr_size(0);
710 }
711 
712 
713 //-----------------------------------------------------------------------------
714 // read/write from memory and disk
715 //-----------------------------------------------------------------------------
716 
717 Status tex_decode(const shared_ptr<u8>& data, size_t dataSize, Tex* t)
718 {
719  const TexCodecVTbl* c;
720  RETURN_STATUS_IF_ERR(tex_codec_for_header(data.get(), dataSize, &c));
721 
722  // make sure the entire header is available
723  const size_t min_hdr_size = c->hdr_size(0);
724  if(dataSize < min_hdr_size)
726  const size_t hdr_size = c->hdr_size(data.get());
727  if(dataSize < hdr_size)
729 
730  t->data = data;
731  t->dataSize = dataSize;
732  t->ofs = hdr_size;
733 
734  RETURN_STATUS_IF_ERR(c->decode((rpU8)data.get(), dataSize, t));
735 
736  // sanity checks
737  if(!t->w || !t->h || t->bpp > 32)
739  if(t->dataSize < t->ofs + tex_img_size(t))
741 
743 
744  CHECK_TEX(t);
745 
746  return INFO::OK;
747 }
748 
749 
751 {
752  CHECK_TEX(t);
754 
755  // we could be clever here and avoid the extra alloc if our current
756  // memory block ensued from the same kind of texture file. this is
757  // most likely the case if in_img == tex_get_data() + c->hdr_size(0).
758  // this would make for zero-copy IO.
759 
760  const size_t max_out_size = tex_img_size(t)*4 + 256*KiB;
761  RETURN_STATUS_IF_ERR(da_alloc(da, max_out_size));
762 
763  const TexCodecVTbl* c;
765 
766  // encode into <da>
767  Status err = c->encode(t, da);
768  if(err < 0)
769  {
770  (void)da_free(da);
771  WARN_RETURN(err);
772  }
773 
774  return INFO::OK;
775 }
const Status TEX_INVALID_COLOR_TYPE
Definition: tex.h:118
#define u8
Definition: types.h:39
size_t ofs
offset to image data in file.
Definition: tex.h:227
Status tex_codec_for_filename(const OsPath &extension, const TexCodecVTbl **c)
Find codec that recognizes the desired output file extension.
Definition: tex_codec.cpp:65
const Status _4
Definition: status.h:444
const Status _1
Definition: status.h:441
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
#define WARN_IF_ERR(expression)
Definition: status.h:265
const Status TEX_CODEC_CANNOT_HANDLE
Definition: tex.h:132
size_t prev_level_dataSize
Definition: tex.cpp:182
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:399
static int global_orientation
Definition: tex.cpp:508
const Status OK
Definition: status.h:386
const u8 * prev_level_data
Definition: tex.cpp:181
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:570
Status tex_transform_to(Tex *t, size_t new_flags)
Change &lt;t&gt;&#39;s pixel format (2nd version) (note: this is equivalent to tex_transform(t, t-&gt;flags^new_flags).
Definition: tex.cpp:494
const Status TEX_COMPRESSED
Definition: tex.h:121
static const StatusDefinition texStatusDefinitions[]
Definition: tex.cpp:42
size_t prev_level_h
Definition: tex.cpp:180
flags &amp; TEX_DXT is a field indicating compression.
Definition: tex.h:149
mask
Definition: tex.h:199
Status(* encode)(Tex *RESTRICT t, DynArray *RESTRICT da)
encode the texture data into the codec&#39;s file format (in memory).
Definition: tex_codec.h:65
indicates the image contains an alpha channel.
Definition: tex.h:171
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
shared_ptr< u8 > data
file buffer or image data.
Definition: tex.h:217
T round_up(T n, T multiple)
round number up/down to the next given multiple.
Definition: bits.h:265
indicates the image is 8bpp greyscale.
Definition: tex.h:178
const Status _3
Definition: status.h:443
size_t tex_hdr_size(const VfsPath &filename)
return the minimum header size (i.e.
Definition: tex.cpp:703
static void create_level(size_t level, size_t level_w, size_t level_h, const u8 *RESTRICT level_data, size_t level_dataSize, void *RESTRICT cbData)
Definition: tex.cpp:186
size_t h
Definition: tex.h:230
indicates B and R pixel components are exchanged.
Definition: tex.h:163
Status tex_validate_plain_format(size_t bpp, size_t flags)
check if the given texture format is acceptable: 8bpp grey, 24bpp color or 32bpp color+alpha (BGR / u...
Definition: tex.cpp:101
u32 tex_get_average_colour(const Tex *t)
return the ARGB value of the 1x1 mipmap level of the texture.
Definition: tex.cpp:641
flags &amp; TEX_ORIENTATION is a field indicating orientation, i.e.
Definition: tex.h:190
Status(* decode)(u8 *data, size_t size, Tex *RESTRICT t)
decode the file into a Tex structure.
Definition: tex_codec.h:52
#define TIMER_ACCRUE(client)
Measure the time taken to execute code up until end of the current scope; bill it to the given TimerC...
Definition: timer.h:389
Status tex_validate(const Tex *t)
Is the texture object valid and self-consistent?
Definition: tex.cpp:60
static Status plain_transform(Tex *t, size_t transforms)
Definition: tex.cpp:287
uint8_t *__restrict rpU8
const Status TEX_FMT_INVALID
Definition: tex.h:117
#define WARN_RETURN_STATUS_IF_ERR(expression)
Definition: status.h:287
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
size_t bpp
Definition: tex.h:231
size_t prev_level_w
Definition: tex.cpp:179
Definition: path.h:75
static Status add_mipmaps(Tex *t, size_t w, size_t h, size_t bpp, void *newData, size_t dataSize)
Definition: tex.cpp:253
const Status _5
Definition: status.h:445
const Status TEX_NOT_8BIT_PRECISION
Definition: tex.h:119
static const size_t KiB
Definition: alignment.h:71
u8 * tex_get_data(const Tex *t)
rationale: since Tex is a struct, its fields are accessible to callers.
Definition: tex.cpp:629
bool tex_orientations_match(size_t src_flags, size_t dst_orientation)
indicate if the two vertical orientations match.
Definition: tex.cpp:548
i64 Status
Error handling system.
Definition: status.h:171
size_t dataSize
Definition: tex.h:219
stores all data describing an image.
Definition: tex.h:210
#define STATUS_ADD_DEFINITIONS(definitions)
add a module&#39;s array of StatusDefinition to the list.
Definition: status.h:216
intptr_t ssize_t
Definition: wposix_types.h:82
void tex_set_global_orientation(int o)
Set the orientation to which all loaded images will automatically be converted (excepting file format...
Definition: tex.cpp:513
Status tex_codec_transform(Tex *t, size_t transforms)
transform the texture&#39;s pixel format.
Definition: tex_codec.cpp:107
we need a special value for DXT1a to avoid having to consider flags &amp; TEX_ALPHA to determine S3TC typ...
Definition: tex.h:156
const Status TEX_INVALID_DATA
Definition: tex.h:127
const char * extension
Definition: mongoose.cpp:1736
void tex_util_foreach_mipmap(size_t w, size_t h, size_t bpp, const u8 *pixels, 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:132
#define TIMER_ADD_CLIENT(id)
&quot;allocate&quot; a new TimerClient that will keep track of the total time billed to it, along with a descri...
Definition: timer.h:308
static Status AllocateAligned(shared_ptr< T > &p, size_t size, size_t alignment=cacheLineSize)
Definition: shared_ptr.h:66
#define u32
Definition: types.h:41
const Status TEX_INVALID_LAYOUT
Definition: tex.h:120
size_t w
Definition: tex.h:229
Path Extension() const
Definition: path.h:176
static void add_level_size(size_t level, size_t level_w, size_t level_h, const u8 *RESTRICT level_data, size_t level_dataSize, void *RESTRICT cbData)
Definition: tex.cpp:674
#define RESTRICT
bool is_pow2(T n)
Definition: bits.h:164
static void flip_to_global_orientation(Tex *t)
Definition: tex.cpp:520
Status tex_wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr< u8 > &data, size_t ofs, Tex *t)
store the given image data into a Tex object; this will be as if it had been loaded via tex_load...
Definition: tex.cpp:593
#define WARN_RETURN(status)
Definition: status.h:255
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:411
Status da_free(DynArray *da)
free all memory (address space + physical) that constitutes the given array.
Definition: dynarray.cpp:80
const Status TEX_INVALID_SIZE
Definition: tex.h:122
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
const Status _2
Definition: status.h:442
size_t tex_img_size(const Tex *t)
return total byte size of the image pixels.
Definition: tex.cpp:683
size_t num_components
Definition: tex.cpp:177
Status tex_transform(Tex *t, size_t transforms)
Change &lt;t&gt;&#39;s pixel format.
Definition: tex.cpp:467
#define CHECK_TEX(t)
Definition: tex.cpp:94
Status da_alloc(DynArray *da, size_t max_size)
ready the DynArray object for use.
Definition: dynarray.cpp:61
virtual method table for TexCodecs.
Definition: tex_codec.h:39
size_t(* hdr_size)(const u8 *file)
return size of the file header supported by this codec.
Definition: tex_codec.h:109
size_t flags
see TexFlags and &quot;Format Conversion&quot; in docs.
Definition: tex.h:234
Status tex_codec_for_header(const u8 *file, size_t file_size, const TexCodecVTbl **c)
find codec that recognizes the header&#39;s magic field.
Definition: tex_codec.cpp:79
Status tex_decode(const shared_ptr< u8 > &data, size_t dataSize, Tex *t)
decode an in-memory texture file into texture object.
Definition: tex.cpp:717
void tex_free(Tex *t)
free all resources associated with the image and make further use of it impossible.
Definition: tex.cpp:610
Status tex_encode(Tex *t, const OsPath &extension, DynArray *da)
encode a texture into a memory buffer in the desired file format.
Definition: tex.cpp:750
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276