Merge pull request #10 from gunyarakun/fix-invalid-return
[cocotron.git] / Onyx2D / O2ImageSource_BMP.m
blob1d5121e7b5e7186938050a41f3d2766e51579a2f
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>
17 #import <assert.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;
26 enum
28    STBI_default = 0, // only used for req_comp
30    STBI_grey       = 1,
31    STBI_grey_alpha = 2,
32    STBI_rgb        = 3,
33    STBI_rgb_alpha  = 4,
36 typedef unsigned char stbi_uc;
37 static int32 img_x, img_y;
38 static int img_n, img_out_n;
40 static uint8  *out;
41 static const uint8 *img_buffer, *img_buffer_end;
44 static char *failure_reason;
45 static int e(char *str)
47    failure_reason = str;
48    NSLog(@"BMP failure: %s",str);
49    return 0;
52    #define e(x,y)  e(x)
53 #define ep(x,y)   (e(x,y),NULL)   
55 static void start_mem(const uint8 *buffer, int len)
57    img_buffer = buffer;
58    img_buffer_end = buffer+len;
61 static int get8(void)
63    if (img_buffer < img_buffer_end)
64       return *img_buffer++;
65    return 0;
69 static int get16le(void)
71    int z = get8();
72    return z + (get8() << 8);
75 static uint32 get32le(void)
77    uint32 z = get16le();
78    return z + (get16le() << 16);
81 static void skip(int n)
83       img_buffer += 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)
93    uint i,j;
94    unsigned char *good;
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);
100    if (good == NULL) {
101       NSZoneFree(NULL,data);
102       return ep("outofmem", "Out of memory");
103    }
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;
126          default: assert(0);
127       }
128       #undef CASE
129    }
131    NSZoneFree(NULL,data);
132    img_out_n = req_comp;
133    return good;
136 static int bmp_test(void)
138    int sz;
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
145    sz = get32le();
146    if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1;
147    return 0;
150 int      stbi_bmp_test_memory      (stbi_uc *buffer, int len)
152    start_mem(buffer, len);
153    return bmp_test();
156 // returns 0..31 for the highest set bit
157 static int high_bit(unsigned int z)
159    int n=0;
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;
166    return n;
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
176    return a & 0xff;
179 static int shiftsigned(int v, int shift, int bits)
181    int result;
182    int z=0;
184    if (shift < 0) v <<= -shift;
185    else v >>= shift;
186    result = v;
188    z = bits;
189    while (z < 8) {
190       result += v >> z;
191       z += bits;
192    }
193    return result;
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;
199    stbi_uc pal[256][4];
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
206    offset = get32le();
207    hsz = get32le();
208    if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return ep("unknown BMP", "BMP type not supported: unknown");
209    failure_reason = "bad BMP";
210    if (hsz == 12) {
211       img_x = get16le();
212       img_y = get16le();
213    } else {
214       img_x = get32le();
215       img_y = get32le();
216    }
217    if (get16le() != 1) return 0;
218    bpp = get16le();
219    if (bpp == 1) return ep("monochrome", "BMP type not supported: 1-bit");
220    flip_vertically = img_y > 0;
221    img_y = abs(img_y);
222    if (hsz == 12) {
223       if (bpp < 24)
224          psize = (offset - 14 - 24) / 3;
225    } else {
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) {
234          if (hsz == 56) {
235             get32le();
236             get32le();
237             get32le();
238             get32le();
239          }
240          if (bpp == 16 || bpp == 32) {
241             mr = mg = mb = ma = 0;
242             if (compress == 0) {
243                if (bpp == 32) {
244                   ma = 0xff << 24;
245                   mr = 0xff << 16;
246                   mg = 0xff <<  8;
247                   mb = 0xff <<  0;
248                } else {
249                   mr = 31 << 10;
250                   mg = 31 <<  5;
251                   mb = 31 <<  0;
252                }
253             } else if (compress == 3) {
254                mr = get32le();
255                mg = get32le();
256                mb = get32le();
257                // not documented, but generated by photoshop and handled by mspaint
258                if (mr == mg && mg == mb) {
259                   // ?!?!?
260                   return NULL;
261                }
262             } else
263                return NULL;
264          }
265       } else {
266          assert(hsz == 108);
267          mr = get32le();
268          mg = get32le();
269          mb = get32le();
270          ma = get32le();
271          get32le(); // discard color space
272          for (i=0; i < 12; ++i)
273             get32le(); // discard color space parameters
274       }
275       if (bpp < 16)
276          psize = (offset - 14 - hsz) >> 2;
277    }
278    img_n = ma ? 4 : 3;
279    if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
280       target = req_comp;
281    else
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");
285    if (bpp < 16) {
286       int z=0;
287       if (psize == 0 || psize > 256) return ep("invalid", "Corrupt BMP");
288       for (i=0; i < psize; ++i) {
289          pal[i][2] = get8();
290          pal[i][1] = get8();
291          pal[i][0] = get8();
292          if (hsz != 12) get8();
293          pal[i][3] = 255;
294       }
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");
299       pad = (-width)&3;
300       for (j=0; j < (int) img_y; ++j) {
301          for (i=0; i < (int) img_x; i += 2) {
302             int v=get8(),v2=0;
303             if (bpp == 4) {
304                v2 = v & 15;
305                v >>= 4;
306             }
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;
317          }
318          skip(pad);
319       }
320    } else {
321       int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
322       int z = 0;
323       int easy=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;
328       pad = (-width) & 3;
329       if (bpp == 24) {
330          easy = 1;
331       } else if (bpp == 32) {
332          if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000)
333             easy = 2;
334       }
335       if (!easy) {
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);
342       }
343       for (j=0; j < (int) img_y; ++j) {
344          if (easy) {
345             for (i=0; i < (int) img_x; ++i) {
346                int a;
347                out[z+2] = get8();
348                out[z+1] = get8();
349                out[z+0] = get8();
350                z += 3;
351                a = (easy == 2 ? get8() : 255);
352                if (target == 4) out[z++] = a;
353             }
354          } else {
355             for (i=0; i < (int) img_x; ++i) {
356                unsigned long v = (bpp == 16 ? get16le() : get32le());
357                int a;
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; 
363             }
364          }
365          skip(pad);
366       }
367    }
368    if (flip_vertically) {
369       stbi_uc t;
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;
375          }
376       }
377    }
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
382    }
384    *x = img_x;
385    *y = img_y;
386    if (comp) *comp = target;
387    return out;
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)];
403    
404    if(size!=signatureLength)
405     return NO;
406     
407    for(i=0;i<signatureLength;i++)
408     if(signature[i]!=check[i])
409      return NO;
410      
411    return YES;
414 -initWithDataProvider:(O2DataProvider *)provider options:(NSDictionary *)options {
415    [super initWithDataProvider:provider options:options];
416    _bmp=(NSData *)O2DataProviderCopyData(provider);
417    return self;
420 -(void)dealloc {
421    [_bmp release];
422    [super dealloc];
425 - (CFStringRef)type
427     return (CFStringRef)@"com.microsoft.bmp";
430 -(unsigned)count {
431    return 1;
434 -(O2Image *)createImageAtIndex:(unsigned)index options:(NSDictionary *)options {
435    int            width,height;
436    int            comp;
437    unsigned char *pixels=stbi_bmp_load_from_memory([_bmp bytes],[_bmp length],&width,&height,&comp,STBI_rgb_alpha);
438    int            bitsPerPixel=32;
439    int            bytesPerRow=(bitsPerPixel/(sizeof(char)*8))*width;
440    NSData        *bitmap;
441    
442    if(pixels==NULL)
443     return nil;
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];
452       
453    [colorSpace release];
454    [provider release];
455    [bitmap release];
456    
457    return image;
461 @end