Line data Source code
1 0 : /*
2 :
3 : OOPNGTextureLoader.m
4 :
5 :
6 : Copyright (C) 2007-2013 Jens Ayton
7 :
8 : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : of this software and associated documentation files (the "Software"), to deal
10 : in the Software without restriction, including without limitation the rights
11 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : copies of the Software, and to permit persons to whom the Software is
13 : furnished to do so, subject to the following conditions:
14 :
15 : The above copyright notice and this permission notice shall be included in all
16 : copies or substantial portions of the Software.
17 :
18 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : SOFTWARE.
25 :
26 : */
27 :
28 : #import "OOPNGTextureLoader.h"
29 : #import "OOFunctionAttributes.h"
30 : #import "OOLogging.h"
31 : #import "OOCPUInfo.h"
32 : #import "NSDataOOExtensions.h"
33 :
34 : //void png_error(png_structp, png_const_charp) NO_RETURN_FUNC;
35 :
36 :
37 : static void PNGError(png_structp png, png_const_charp message);
38 : static void PNGWarning(png_structp png, png_const_charp message);
39 : static void PNGRead(png_structp png, png_bytep bytes, png_size_t size);
40 :
41 :
42 : @interface OOPNGTextureLoader (OOPrivate)
43 :
44 0 : - (void)doLoadTexture;
45 0 : - (void)readBytes:(png_bytep)bytes count:(png_size_t)count;
46 :
47 : @end
48 :
49 :
50 : @implementation OOPNGTextureLoader
51 :
52 0 : - (void)loadTexture
53 : {
54 : // Get data from file
55 : fileData = [[NSData oo_dataWithOXZFile:_path] retain];
56 : if (fileData == nil) return;
57 : length = [fileData length];
58 :
59 : [self doLoadTexture];
60 :
61 : [fileData release];
62 : fileData = nil;
63 : }
64 :
65 :
66 0 : - (void)dealloc
67 : {
68 : [fileData release];
69 : if (png != NULL)
70 : {
71 : png_destroy_read_struct(&png, &pngInfo, &pngEndInfo);
72 : }
73 :
74 : [super dealloc];
75 : }
76 :
77 : @end
78 :
79 :
80 : @implementation OOPNGTextureLoader (OOPrivate)
81 :
82 : - (void)doLoadTexture
83 : {
84 : png_bytepp rows = NULL;
85 : png_uint_32 pngWidth,
86 : pngHeight;
87 : int depth,
88 : colorType;
89 : uint32_t i;
90 :
91 : // Set up PNG decoding
92 : png = png_create_read_struct(PNG_LIBPNG_VER_STRING, self, PNGError, PNGWarning);
93 : if (png != NULL) pngInfo = png_create_info_struct(png);
94 : if (pngInfo != NULL) pngEndInfo = png_create_info_struct(png);
95 : if (pngEndInfo == NULL)
96 : {
97 : OOLog(@"texture.load.png.setup.failed", @"***** Error preparing to read %@.", _path);
98 : goto FAIL;
99 : }
100 :
101 : if (EXPECT_NOT(setjmp(png_jmpbuf(png))))
102 : {
103 : // libpng will jump here on error.
104 : if (_data)
105 : {
106 : free(_data);
107 : _data = NULL;
108 : }
109 : goto FAIL;
110 : }
111 :
112 : png_set_read_fn(png, self, PNGRead);
113 :
114 : png_read_info(png, pngInfo);
115 : // Read header, get format info and check that it meets our expectations.
116 : if (EXPECT_NOT(!png_get_IHDR(png, pngInfo, &pngWidth, &pngHeight, &depth, &colorType, NULL, NULL, NULL)))
117 : {
118 : OOLog(@"texture.load.png.failed", @"Failed to get metadata from PNG %@", _path);
119 : goto FAIL;
120 : }
121 : png_set_strip_16(png); // 16 bits per channel -> 8 bpc
122 : if (depth < 8 || colorType == PNG_COLOR_TYPE_PALETTE)
123 : {
124 : png_set_expand(png); // Paletted -> RGB, greyscale -> 8 bpc
125 : }
126 :
127 : if (colorType == PNG_COLOR_TYPE_GRAY)
128 : {
129 : _format = kOOTextureDataGrayscale;
130 : }
131 : else if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
132 : {
133 : _format = kOOTextureDataGrayscaleAlpha;
134 : }
135 : else
136 : {
137 : _format = kOOTextureDataRGBA;
138 :
139 : #if OOLITE_BIG_ENDIAN
140 : png_set_bgr(png);
141 : png_set_swap_alpha(png); // RGBA->ARGB
142 : png_set_filler(png, 0xFF, PNG_FILLER_BEFORE);
143 : #elif OOLITE_LITTLE_ENDIAN
144 : png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
145 : #else
146 : #error Unknown handle byte order.
147 : #endif
148 : }
149 :
150 : png_read_update_info(png, pngInfo);
151 : png_set_interlace_handling(png);
152 :
153 : // Metadata is acceptable; load data.
154 : _width = pngWidth;
155 : _height = pngHeight;
156 : _rowBytes = png_get_rowbytes(png, pngInfo);
157 :
158 : // png_read_png
159 : rows = malloc(sizeof *rows * _height);
160 : _data = malloc(_rowBytes * _height);
161 : if (EXPECT_NOT(rows == NULL || _data == NULL))
162 : {
163 : if (rows != NULL)
164 : {
165 : free(rows);
166 : rows = NULL;
167 : }
168 : if (_data != NULL)
169 : {
170 : free(_data);
171 : _data = NULL;
172 : }
173 : OOLog(kOOLogAllocationFailure, @"Failed to allocate space (%zu bytes) for texture %@", _rowBytes * _height, _path);
174 : goto FAIL;
175 : }
176 :
177 : for (i = 0; i != _height; ++i)
178 : {
179 : rows[i] = ((png_bytep)_data) + i * _rowBytes;
180 : }
181 : png_read_image(png, rows);
182 : png_read_end(png, pngEndInfo);
183 :
184 : FAIL:
185 : free(rows);
186 : png_destroy_read_struct(&png, &pngInfo, &pngEndInfo);
187 : }
188 :
189 :
190 : - (void)readBytes:(png_bytep)bytes count:(png_size_t)count
191 : {
192 : // Check that we're within the file's bounds
193 : if (EXPECT_NOT(length - offset < count))
194 : {
195 : NSString *message = [NSString stringWithFormat:@"attempt to read beyond end of file (%@), file may be truncated.", _path];
196 : png_error(png, [message UTF8String]); // Will not return
197 : }
198 :
199 : assert(bytes != NULL);
200 :
201 : // Copy bytes
202 : memcpy(bytes, [fileData bytes] + offset, count);
203 : offset += count;
204 : }
205 :
206 : @end
207 :
208 :
209 : /* Minor detail: libpng 1.4.0 removed trailing .s from error and warning
210 : messages. It's also binary-incompatible with 1.2 and earlier, so it's
211 : reasonable to make this test at build time.
212 : */
213 : #if PNG_LIBPNG_VER >= 10400
214 : #define MSG_TERMINATOR "."
215 : #else
216 0 : #define MSG_TERMINATOR ""
217 : #endif
218 :
219 :
220 0 : static void PNGError(png_structp png, png_const_charp message)
221 : {
222 : OOPNGTextureLoader *loader = png_get_io_ptr(png);
223 : OOLog(@"texture.load.png.error", @"***** A PNG loading error occurred for %@: %s" MSG_TERMINATOR, [loader path], message);
224 :
225 : #if PNG_LIBPNG_VER >= 10500
226 : png_longjmp(png, 1);
227 : #else
228 : longjmp(png_jmpbuf(png), 1);
229 : #endif
230 : }
231 :
232 :
233 0 : static void PNGWarning(png_structp png, png_const_charp message)
234 : {
235 : OOPNGTextureLoader *loader = png_get_io_ptr(png);
236 : OOLog(@"texture.load.png.warning", @"----- A PNG loading warning occurred for %@: %s" MSG_TERMINATOR, [loader path], message);
237 : }
238 :
239 :
240 0 : static void PNGRead(png_structp png, png_bytep bytes, png_size_t size)
241 : {
242 : OOPNGTextureLoader *loader = png_get_io_ptr(png);
243 : [loader readBytes:bytes count:size];
244 : }
|