1 /* Copyright (c) 2007 Christopher J. W. Lloyd
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
9 /* BMP decode is based on the public domain implementation by Sean Barrett http://www.nothings.org/stb_image.c V 1.00 */
11 #import <Onyx2D/O2ImageSource_BMP.h>
12 #import <Foundation/NSString.h>
13 #import <Foundation/NSData.h>
14 #import <Onyx2D/O2DataProvider.h>
15 #import <Onyx2D/O2ColorSpace.h>
16 #import <Onyx2D/O2Image.h>
19 typedef unsigned char uint8;
20 typedef unsigned short uint16;
21 typedef signed short int16;
22 typedef unsigned int uint32;
23 typedef signed int int32;
24 typedef unsigned int uint;
28 STBI_default = 0, // only used for req_comp
36 typedef unsigned char stbi_uc;
37 static int32 img_x, img_y;
38 static int img_n, img_out_n;
41 static const uint8 *img_buffer, *img_buffer_end;
44 static char *failure_reason;
45 static int e(char *str)
48 NSLog(@"BMP failure: %s",str);
53 #define ep(x,y) (e(x,y),NULL)
55 static void start_mem(const uint8 *buffer, int len)
58 img_buffer_end = buffer+len;
63 if (img_buffer < img_buffer_end)
69 static int get16le(void)
72 return z + (get8() << 8);
75 static uint32 get32le(void)
78 return z + (get16le() << 16);
81 static void skip(int n)
86 static uint8 compute_y(int r, int g, int b)
88 return (uint8) (((r*77) + (g*150) + (29*b)) >> 8);
91 static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp)
96 if (req_comp == img_n) return data;
97 assert(req_comp >= 1 && req_comp <= 4);
99 good = (unsigned char *) NSZoneMalloc(NULL,req_comp * img_x * img_y);
101 NSZoneFree(NULL,data);
102 return ep("outofmem", "Out of memory");
105 for (j=0; j < img_y; ++j) {
106 unsigned char *src = data + j * img_x * img_n ;
107 unsigned char *dest = good + j * img_x * req_comp;
109 #define COMBO(a,b) ((a)*8+(b))
110 #define CASE(a,b) case COMBO(a,b): for(i=0; i < img_x; ++i, src += a, dest += b)
112 // convert source image with img_n components to one with req_comp components
113 switch(COMBO(img_n, req_comp)) {
114 CASE(1,2) dest[0]=src[0], dest[1]=255; break;
115 CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break;
116 CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break;
117 CASE(2,1) dest[0]=src[0]; break;
118 CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break;
119 CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break;
120 CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break;
121 CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
122 CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break;
123 CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break;
124 CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break;
125 CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break;
131 NSZoneFree(NULL,data);
132 img_out_n = req_comp;
136 static int bmp_test(void)
139 if (get8() != 'B') return 0;
140 if (get8() != 'M') return 0;
141 get32le(); // discard filesize
142 get16le(); // discard reserved
143 get16le(); // discard reserved
144 get32le(); // discard data offset
146 if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1;
150 int stbi_bmp_test_memory (stbi_uc *buffer, int len)
152 start_mem(buffer, len);
156 // returns 0..31 for the highest set bit
157 static int high_bit(unsigned int z)
160 if (z == 0) return -1;
161 if (z >= 0x10000) n += 16, z >>= 16;
162 if (z >= 0x00100) n += 8, z >>= 8;
163 if (z >= 0x00010) n += 4, z >>= 4;
164 if (z >= 0x00004) n += 2, z >>= 2;
165 if (z >= 0x00002) n += 1, z >>= 1;
169 static int bitcount(unsigned int a)
171 a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
172 a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
173 a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
174 a = (a + (a >> 8)); // max 16 per 8 bits
175 a = (a + (a >> 16)); // max 32 per 8 bits
179 static int shiftsigned(int v, int shift, int bits)
184 if (shift < 0) v <<= -shift;
196 static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp)
198 unsigned int mr=0,mg=0,mb=0,ma=0;
200 int psize=0,i,j,compress=0,width;
201 int bpp, flip_vertically, pad, target, offset, hsz;
202 if (get8() != 'B' || get8() != 'M') return ep("not BMP", "Corrupt BMP");
203 get32le(); // discard filesize
204 get16le(); // discard reserved
205 get16le(); // discard reserved
208 if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return ep("unknown BMP", "BMP type not supported: unknown");
209 failure_reason = "bad BMP";
217 if (get16le() != 1) return 0;
219 if (bpp == 1) return ep("monochrome", "BMP type not supported: 1-bit");
220 flip_vertically = img_y > 0;
224 psize = (offset - 14 - 24) / 3;
226 compress = get32le();
227 if (compress == 1 || compress == 2) return ep("BMP RLE", "BMP type not supported: RLE");
228 get32le(); // discard sizeof
229 get32le(); // discard hres
230 get32le(); // discard vres
231 get32le(); // discard colorsused
232 get32le(); // discard max important
233 if (hsz == 40 || hsz == 56) {
240 if (bpp == 16 || bpp == 32) {
241 mr = mg = mb = ma = 0;
253 } else if (compress == 3) {
257 // not documented, but generated by photoshop and handled by mspaint
258 if (mr == mg && mg == mb) {
271 get32le(); // discard color space
272 for (i=0; i < 12; ++i)
273 get32le(); // discard color space parameters
276 psize = (offset - 14 - hsz) >> 2;
279 if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
282 target = img_n; // if they want monochrome, we'll post-convert
283 out = (stbi_uc *) NSZoneMalloc(NULL,target * img_x * img_y);
284 if (!out) return ep("outofmem", "Out of memory");
287 if (psize == 0 || psize > 256) return ep("invalid", "Corrupt BMP");
288 for (i=0; i < psize; ++i) {
292 if (hsz != 12) get8();
295 skip(offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
296 if (bpp == 4) width = (img_x + 1) >> 1;
297 else if (bpp == 8) width = img_x;
298 else return ep("bad bpp", "Corrupt BMP");
300 for (j=0; j < (int) img_y; ++j) {
301 for (i=0; i < (int) img_x; i += 2) {
307 out[z++] = pal[v][0];
308 out[z++] = pal[v][1];
309 out[z++] = pal[v][2];
310 if (target == 4) out[z++] = 255;
311 if (i+1 == (int) img_x) break;
312 v = (bpp == 8) ? get8() : v2;
313 out[z++] = pal[v][0];
314 out[z++] = pal[v][1];
315 out[z++] = pal[v][2];
316 if (target == 4) out[z++] = 255;
321 int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
324 skip(offset - 14 - hsz);
325 if (bpp == 24) width = 3 * img_x;
326 else if (bpp == 16) width = 2*img_x;
327 else /* bpp = 32 and pad = 0 */ width=0;
331 } else if (bpp == 32) {
332 if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000)
336 if (!mr || !mg || !mb) return ep("bad masks", "Corrupt BMP");
337 // right shift amt to put high bit in position #7
338 rshift = high_bit(mr)-7; rcount = bitcount(mr);
339 gshift = high_bit(mg)-7; gcount = bitcount(mr);
340 bshift = high_bit(mb)-7; bcount = bitcount(mr);
341 ashift = high_bit(ma)-7; acount = bitcount(mr);
343 for (j=0; j < (int) img_y; ++j) {
345 for (i=0; i < (int) img_x; ++i) {
351 a = (easy == 2 ? get8() : 255);
352 if (target == 4) out[z++] = a;
355 for (i=0; i < (int) img_x; ++i) {
356 unsigned long v = (bpp == 16 ? get16le() : get32le());
358 out[z++] = shiftsigned(v & mr, rshift, rcount);
359 out[z++] = shiftsigned(v & mg, gshift, gcount);
360 out[z++] = shiftsigned(v & mb, bshift, bcount);
361 a = (ma ? shiftsigned(v & ma, ashift, acount) : 255);
362 if (target == 4) out[z++] = a;
368 if (flip_vertically) {
370 for (j=0; j < (int) img_y>>1; ++j) {
371 stbi_uc *p1 = out + j *img_x*target;
372 stbi_uc *p2 = out + (img_y-1-j)*img_x*target;
373 for (i=0; i < (int) img_x*target; ++i) {
374 t = p1[i], p1[i] = p2[i], p2[i] = t;
379 if (req_comp && req_comp != target) {
380 out = convert_format(out, target, req_comp);
381 if (out == NULL) return out; // convert_format frees input on failure
386 if (comp) *comp = target;
390 stbi_uc *stbi_bmp_load_from_memory (const stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp)
392 start_mem(buffer, len);
393 return bmp_load(x,y,comp,req_comp);
396 @implementation O2ImageSource_BMP
398 +(BOOL)isPresentInDataProvider:(O2DataProvider *)provider {
399 enum { signatureLength=2 };
400 unsigned char signature[signatureLength] = { 'B','M' };
401 unsigned char check[signatureLength];
402 NSInteger i,size=[provider getBytes:check range:NSMakeRange(0,signatureLength)];
404 if(size!=signatureLength)
407 for(i=0;i<signatureLength;i++)
408 if(signature[i]!=check[i])
414 -initWithDataProvider:(O2DataProvider *)provider options:(NSDictionary *)options {
415 [super initWithDataProvider:provider options:options];
416 _bmp=(NSData *)O2DataProviderCopyData(provider);
427 return (CFStringRef)@"com.microsoft.bmp";
434 -(O2Image *)createImageAtIndex:(unsigned)index options:(NSDictionary *)options {
437 unsigned char *pixels=stbi_bmp_load_from_memory([_bmp bytes],[_bmp length],&width,&height,&comp,STBI_rgb_alpha);
439 int bytesPerRow=(bitsPerPixel/(sizeof(char)*8))*width;
445 bitmap=[[NSData alloc] initWithBytesNoCopy:pixels length:bytesPerRow*height];
447 O2DataProvider *provider=O2DataProviderCreateWithCFData((CFDataRef)bitmap);
448 O2ColorSpace *colorSpace=O2ColorSpaceCreateDeviceRGB();
449 O2Image *image=[[O2Image alloc] initWithWidth:width height:height bitsPerComponent:8 bitsPerPixel:bitsPerPixel bytesPerRow:bytesPerRow
450 colorSpace:colorSpace bitmapInfo:kO2BitmapByteOrder32Big|kO2ImageAlphaPremultipliedLast decoder:NULL
451 provider:provider decode:NULL interpolate:NO renderingIntent:kO2RenderingIntentDefault];
453 [colorSpace release];