Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
tex_png.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  * PNG codec using libpng.
25  */
26 
27 #include "precompiled.h"
28 
30 
31 #include "lib/byte_order.h"
32 #include "tex_codec.h"
34 #include "lib/timer.h"
35 
36 #if MSC_VERSION
37 
38 // squelch "dtor / setjmp interaction" warnings.
39 // all attempts to resolve the underlying problem failed; apparently
40 // the warning is generated if setjmp is used at all in C++ mode.
41 // (png_*_impl have no code that would trigger ctors/dtors, nor are any
42 // called in their prolog/epilog code).
43 # pragma warning(disable: 4611)
44 
45 #endif // MSC_VERSION
46 
47 
48 //-----------------------------------------------------------------------------
49 //
50 //-----------------------------------------------------------------------------
51 
53 {
54 public:
56  : data(data), size(size), pos(0)
57  {
58  }
59 
60  size_t RemainingSize() const
61  {
62  ASSERT(pos <= size);
63  return size-pos;
64  }
65 
66  void CopyTo(rpU8 dst, size_t dstSize)
67  {
68  memcpy(dst, data+pos, dstSize);
69  pos += dstSize;
70  }
71 
72 private:
74  size_t size;
75  size_t pos;
76 };
77 
78 
79 // pass data from PNG file in memory to libpng
80 static void io_read(png_struct* png_ptr, rpU8 data, png_size_t size)
81 {
82  MemoryStream* stream = (MemoryStream*)png_get_io_ptr(png_ptr);
83  if(stream->RemainingSize() < size)
84  {
85  png_error(png_ptr, "PNG: not enough input");
86  return;
87  }
88 
89  stream->CopyTo(data, size);
90 }
91 
92 
93 // write libpng output to PNG file
94 static void io_write(png_struct* png_ptr, u8* data, png_size_t length)
95 {
96  DynArray* da = (DynArray*)png_get_io_ptr(png_ptr);
97  if(da_append(da, data, length) != 0)
98  png_error(png_ptr, "io_write failed");
99 }
100 
101 
102 static void io_flush(png_structp UNUSED(png_ptr))
103 {
104 }
105 
106 
107 
108 //-----------------------------------------------------------------------------
109 
110 static Status png_transform(Tex* UNUSED(t), size_t UNUSED(transforms))
111 {
113 }
114 
115 
116 // note: it's not worth combining png_encode and png_decode, due to
117 // libpng read/write interface differences (grr).
118 
119 // split out of png_decode to simplify resource cleanup and avoid
120 // "dtor / setjmp interaction" warning.
121 static Status png_decode_impl(MemoryStream* stream, png_structp png_ptr, png_infop info_ptr, Tex* t)
122 {
123  png_set_read_fn(png_ptr, stream, io_read);
124 
125  // read header and determine format
126  png_read_info(png_ptr, info_ptr);
127  png_uint_32 w, h;
128  int bit_depth, colour_type;
129  png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &colour_type, 0, 0, 0);
130  const size_t pitch = png_get_rowbytes(png_ptr, info_ptr);
131  const u32 bpp = (u32)(pitch/w * 8);
132 
133  size_t flags = 0;
134  if(bpp == 32)
135  flags |= TEX_ALPHA;
136  if(colour_type == PNG_COLOR_TYPE_GRAY)
137  flags |= TEX_GREY;
138 
139  // make sure format is acceptable
140  if(bit_depth != 8)
142  if(colour_type & PNG_COLOR_MASK_PALETTE)
144 
145  const size_t img_size = pitch * h;
146  shared_ptr<u8> data;
147  AllocateAligned(data, img_size, pageSize);
148 
149  std::vector<RowPtr> rows = tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0);
150  png_read_image(png_ptr, (png_bytepp)&rows[0]);
151  png_read_end(png_ptr, info_ptr);
152 
153  // success; make sure all data was consumed.
154  ENSURE(stream->RemainingSize() == 0);
155 
156  // store image info
157  t->data = data;
158  t->dataSize = img_size;
159  t->ofs = 0;
160  t->w = w;
161  t->h = h;
162  t->bpp = bpp;
163  t->flags = flags;
164 
165  return INFO::OK;
166 }
167 
168 
169 // split out of png_encode to simplify resource cleanup and avoid
170 // "dtor / setjmp interaction" warning.
171 static Status png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, DynArray* da)
172 {
173  const png_uint_32 w = (png_uint_32)t->w, h = (png_uint_32)t->h;
174  const size_t pitch = w * t->bpp / 8;
175 
176  int colour_type;
177  switch(t->flags & (TEX_GREY|TEX_ALPHA))
178  {
179  case TEX_GREY|TEX_ALPHA:
180  colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
181  break;
182  case TEX_GREY:
183  colour_type = PNG_COLOR_TYPE_GRAY;
184  break;
185  case TEX_ALPHA:
186  colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
187  break;
188  default:
189  colour_type = PNG_COLOR_TYPE_RGB;
190  break;
191  }
192 
193  png_set_write_fn(png_ptr, da, io_write, io_flush);
194  png_set_IHDR(png_ptr, info_ptr, w, h, 8, colour_type,
195  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
196 
197  u8* data = tex_get_data(t);
198  std::vector<RowPtr> rows = tex_codec_alloc_rows(data, h, pitch, t->flags, TEX_TOP_DOWN);
199 
200  // PNG is native RGB.
201  const int png_transforms = (t->flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY;
202 
203  png_set_rows(png_ptr, info_ptr, (png_bytepp)&rows[0]);
204  png_write_png(png_ptr, info_ptr, png_transforms, 0);
205 
206  return INFO::OK;
207 }
208 
209 
210 
211 static bool png_is_hdr(const u8* file)
212 {
213  // don't use png_sig_cmp, so we don't pull in libpng for
214  // this check alone (it might not actually be used).
215  return *(u32*)file == FOURCC('\x89','P','N','G');
216 }
217 
218 
219 static bool png_is_ext(const OsPath& extension)
220 {
221  return extension == L".png";
222 }
223 
224 
225 static size_t png_hdr_size(const u8* UNUSED(file))
226 {
227  return 0; // libpng returns decoded image data; no header
228 }
229 
230 
231 TIMER_ADD_CLIENT(tc_png_decode);
232 
233 // limitation: palette images aren't supported
234 static Status png_decode(rpU8 data, size_t size, Tex* RESTRICT t)
235 {
236 TIMER_ACCRUE(tc_png_decode);
237 
238  png_infop info_ptr = 0;
239 
240  // allocate PNG structures; use default stderr and longjmp error handlers
241  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
242  if(!png_ptr)
244  info_ptr = png_create_info_struct(png_ptr);
245  if(!info_ptr)
246  {
247  png_destroy_read_struct(&png_ptr, &info_ptr, 0);
249  }
250  // setup error handling
251  if(setjmp(png_jmpbuf(png_ptr)))
252  {
253  // libpng longjmps here after an error
254  png_destroy_read_struct(&png_ptr, &info_ptr, 0);
256  }
257 
258  MemoryStream stream(data, size);
259  Status ret = png_decode_impl(&stream, png_ptr, info_ptr, t);
260 
261  png_destroy_read_struct(&png_ptr, &info_ptr, 0);
262 
263  return ret;
264 }
265 
266 
267 // limitation: palette images aren't supported
269 {
270  Status ret = ERR::FAIL;
271  png_infop info_ptr = 0;
272 
273  // allocate PNG structures; use default stderr and longjmp error handlers
274  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
275  if(!png_ptr)
277  info_ptr = png_create_info_struct(png_ptr);
278  if(!info_ptr)
279  goto fail;
280 
281  // setup error handling
282  if(setjmp(png_jmpbuf(png_ptr)))
283  {
284  // libpng longjmps here after an error
285  goto fail;
286  }
287 
288  ret = png_encode_impl(t, png_ptr, info_ptr, da);
289 
290  // shared cleanup
291 fail:
292  png_destroy_write_struct(&png_ptr, &info_ptr);
293  return ret;
294 }
295 
296 TEX_CODEC_REGISTER(png);
const Status TEX_INVALID_COLOR_TYPE
Definition: tex.h:118
static bool png_is_hdr(const u8 *file)
Definition: tex_png.cpp:211
#define u8
Definition: types.h:39
size_t ofs
offset to image data in file.
Definition: tex.h:227
static const size_t pageSize
Definition: alignment.h:61
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
const Status TEX_CODEC_CANNOT_HANDLE
Definition: tex.h:132
size_t RemainingSize() const
Definition: tex_png.cpp:60
const Status OK
Definition: status.h:386
static Status png_decode_impl(MemoryStream *stream, png_structp png_ptr, png_infop info_ptr, Tex *t)
Definition: tex_png.cpp:121
void CopyTo(rpU8 dst, size_t dstSize)
Definition: tex_png.cpp:66
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
shared_ptr< u8 > data
file buffer or image data.
Definition: tex.h:217
indicates the image is 8bpp greyscale.
Definition: tex.h:178
#define ASSERT(expr)
same as ENSURE in debug mode, does nothing in release mode.
Definition: debug.h:310
static Status png_encode(Tex *RESTRICT t, DynArray *RESTRICT da)
Definition: tex_png.cpp:268
static void io_write(png_struct *png_ptr, u8 *data, png_size_t length)
Definition: tex_png.cpp:94
size_t h
Definition: tex.h:230
indicates B and R pixel components are exchanged.
Definition: tex.h:163
#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
uint8_t *__restrict rpU8
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
size_t bpp
Definition: tex.h:231
#define FOURCC(a, b, c, d)
convert 4 characters to u32 (at compile time) for easy comparison.
Definition: byte_order.h:69
std::vector< RowPtr > tex_codec_alloc_rows(const u8 *data, size_t h, size_t pitch, size_t src_flags, size_t dst_orientation)
Definition: tex_codec.cpp:154
Definition: path.h:75
const Status TEX_NOT_8BIT_PRECISION
Definition: tex.h:119
static Status png_encode_impl(Tex *t, png_structp png_ptr, png_infop info_ptr, DynArray *da)
Definition: tex_png.cpp:171
u8 * tex_get_data(const Tex *t)
rationale: since Tex is a struct, its fields are accessible to callers.
Definition: tex.cpp:629
static bool png_is_ext(const OsPath &extension)
Definition: tex_png.cpp:219
Status da_append(DynArray *da, const void *data, size_t size)
&quot;write&quot; to array, i.e.
Definition: dynarray.cpp:139
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
size_t pos
Definition: tex_png.cpp:75
static Status png_decode(rpU8 data, size_t size, Tex *RESTRICT t)
Definition: tex_png.cpp:234
const char * extension
Definition: mongoose.cpp:1736
#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
size_t w
Definition: tex.h:229
MemoryStream(rpU8 data, size_t size)
Definition: tex_png.cpp:55
#define RESTRICT
static size_t png_hdr_size(const u8 *file)
Definition: tex_png.cpp:225
#define TEX_CODEC_REGISTER(name)
build codec vtbl and register it.
Definition: tex_codec.h:137
#define WARN_RETURN(status)
Definition: status.h:255
static void io_read(png_struct *png_ptr, rpU8 data, png_size_t size)
Definition: tex_png.cpp:80
const Status FAIL
Definition: status.h:406
static Status png_transform(Tex *t, size_t transforms)
Definition: tex_png.cpp:110
const Status NO_MEM
Definition: status.h:430
size_t flags
see TexFlags and &quot;Format Conversion&quot; in docs.
Definition: tex.h:234
static void io_flush(png_structp png_ptr)
Definition: tex_png.cpp:102
size_t size
Definition: tex_png.cpp:74