Blender V2.61 - r43446

radiance_hdr.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): none yet.
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00032 /* ----------------------------------------------------------------------
00033   Radiance High Dynamic Range image file IO
00034   For description and code for reading/writing of radiance hdr files 
00035     by Greg Ward, refer to:
00036   http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html
00037 ----------------------------------------------------------------------
00038 */
00039 
00040 #ifdef WIN32
00041 #  include <io.h>
00042 #endif
00043 
00044 #include "MEM_guardedalloc.h"
00045 
00046 #include "BLI_blenlib.h"
00047 
00048 #include "imbuf.h"
00049 
00050 #include "IMB_imbuf_types.h"
00051 #include "IMB_imbuf.h"
00052 
00053 #include "IMB_allocimbuf.h"
00054 #include "IMB_filetype.h"
00055 
00056 /* needed constants */
00057 #define MINELEN 8
00058 #define MAXELEN 0x7fff
00059 #define MINRUN  4   /* minimum run length */
00060 #define RED 0
00061 #define GRN 1
00062 #define BLU 2
00063 #define EXP 3
00064 #define COLXS 128
00065 #define STR_MAX 540
00066 typedef unsigned char RGBE[4];
00067 typedef float fCOLOR[3];
00068 /* copy source -> dest */
00069 #define copy_rgbe(c1, c2) (c2[RED]=c1[RED], c2[GRN]=c1[GRN], c2[BLU]=c1[BLU], c2[EXP]=c1[EXP])
00070 #define copy_fcol(f1, f2) (f2[RED]=f1[RED], f2[GRN]=f1[GRN], f2[BLU]=f1[BLU])
00071 
00072 /* read routines */
00073 static unsigned char* oldreadcolrs(RGBE *scan, unsigned char *mem, int xmax)
00074 {
00075     int i, rshift = 0, len = xmax;
00076     while (len > 0) {
00077         scan[0][RED] = *mem++;
00078         scan[0][GRN] = *mem++;
00079         scan[0][BLU] = *mem++;
00080         scan[0][EXP] = *mem++;
00081         if (scan[0][RED] == 1 && scan[0][GRN] == 1 && scan[0][BLU] == 1) {
00082             for (i=scan[0][EXP]<<rshift;i>0;i--) {
00083                 copy_rgbe(scan[-1], scan[0]);
00084                 scan++;
00085                 len--;
00086             }
00087             rshift += 8;
00088         }
00089         else {
00090             scan++;
00091             len--;
00092             rshift = 0;
00093         }
00094     }
00095     return mem;
00096 }
00097 
00098 static unsigned char* freadcolrs(RGBE *scan, unsigned char* mem, int xmax)
00099 {
00100     int i, j, code, val;
00101 
00102     if ((xmax < MINELEN) | (xmax > MAXELEN)) return oldreadcolrs(scan, mem, xmax);
00103 
00104     i = *mem++;
00105     if (i != 2) return oldreadcolrs(scan, mem-1, xmax);
00106 
00107     scan[0][GRN] = *mem++;
00108     scan[0][BLU] = *mem++;
00109 
00110     i = *mem++;
00111     if (((scan[0][BLU] << 8) | i) != xmax) return NULL;
00112 
00113     for (i=0;i<4;i++)
00114         for (j=0;j<xmax;) {
00115             code = *mem++;
00116             if (code > 128) {
00117                 code &= 127;
00118                 val = *mem++;
00119                 while (code--)
00120                     scan[j++][i] = (unsigned char)val;
00121             }
00122             else
00123                 while (code--)
00124                     scan[j++][i] = *mem++;
00125         }
00126     return mem;
00127 }
00128 
00129 /* helper functions */
00130 
00131 /* rgbe -> float color */
00132 static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
00133 {
00134     if (rgbe[EXP]==0) {
00135         fcol[RED] = fcol[GRN] = fcol[BLU] = 0;
00136     }
00137     else {
00138         float f = ldexp(1.0, rgbe[EXP]-(COLXS+8));
00139         fcol[RED] = f*(rgbe[RED] + 0.5f);
00140         fcol[GRN] = f*(rgbe[GRN] + 0.5f);
00141         fcol[BLU] = f*(rgbe[BLU] + 0.5f);
00142     }
00143 }
00144 
00145 /* float color -> rgbe */
00146 static void FLOAT2RGBE(fCOLOR fcol, RGBE rgbe)
00147 {
00148     int e;
00149     float d = (fcol[RED]>fcol[GRN]) ? fcol[RED] : fcol[GRN];
00150     if (fcol[BLU]>d) d = fcol[BLU];
00151     if (d <= 1e-32f)
00152         rgbe[RED] = rgbe[GRN] = rgbe[BLU] = rgbe[EXP] = 0;
00153     else {
00154         d = frexp(d, &e) * 256.f / d;
00155         rgbe[RED] = (unsigned char)(fcol[RED] * d);
00156         rgbe[GRN] = (unsigned char)(fcol[GRN] * d);
00157         rgbe[BLU] = (unsigned char)(fcol[BLU] * d);
00158         rgbe[EXP] = (unsigned char)(e + COLXS);
00159     }
00160 }
00161 
00162 /* ImBuf read */
00163 
00164 int imb_is_a_hdr(unsigned char *buf)
00165 {
00166     // For recognition, Blender only loads first 32 bytes, so use #?RADIANCE id instead
00167     // update: actually, the 'RADIANCE' part is just an optional program name, the magic word is really only the '#?' part
00168     //if (strstr((char*)buf, "#?RADIANCE")) return 1;
00169     if (strstr((char*)buf, "#?")) return 1;
00170     // if (strstr((char*)buf, "32-bit_rle_rgbe")) return 1;
00171     return 0;
00172 }
00173 
00174 struct ImBuf *imb_loadhdr(unsigned char *mem, size_t size, int flags)
00175 {
00176     struct ImBuf* ibuf;
00177     RGBE* sline;
00178     fCOLOR fcol;
00179     float* rect_float;
00180     int found=0;
00181     int width=0, height=0;
00182     int x, y;
00183     unsigned char* ptr;
00184     char oriY[80], oriX[80];
00185 
00186     if (imb_is_a_hdr((void*)mem))
00187     {
00188         /* find empty line, next line is resolution info */
00189         for (x=1;x<size;x++) {
00190             if ((mem[x-1]=='\n') && (mem[x]=='\n')) {
00191                 found = 1;
00192                 break;
00193             }
00194         }
00195         if (found && (x<(size + 2))) {
00196             if (sscanf((char *)&mem[x+1], "%79s %d %79s %d", (char*)&oriY, &height, 
00197                 (char*)&oriX, &width) != 4) return NULL;
00198 
00199             /* find end of this line, data right behind it */
00200             ptr = (unsigned char *)strchr((char*)&mem[x+1], '\n');
00201             ptr++;
00202 
00203             if (flags & IB_test) ibuf = IMB_allocImBuf(width, height, 32, 0);
00204             else ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect)|IB_rectfloat);
00205 
00206             if (ibuf==NULL) return NULL;
00207             ibuf->ftype = RADHDR;
00208             ibuf->profile = IB_PROFILE_LINEAR_RGB;
00209 
00210             if (flags & IB_test) return ibuf;
00211 
00212             /* read in and decode the actual data */
00213             sline = (RGBE*)MEM_mallocN(sizeof(RGBE)*width, "radhdr_read_tmpscan");
00214             rect_float = (float *)ibuf->rect_float;
00215             
00216             for (y=0;y<height;y++) {
00217                 ptr = freadcolrs(sline, ptr, width);
00218                 if (ptr==NULL) {
00219                     printf("HDR decode error\n");
00220                     MEM_freeN(sline);
00221                     return ibuf;
00222                 }
00223                 for (x=0;x<width;x++) {
00224                     /* convert to ldr */
00225                     RGBE2FLOAT(sline[x], fcol);
00226                     *rect_float++ = fcol[RED];
00227                     *rect_float++ = fcol[GRN];
00228                     *rect_float++ = fcol[BLU];
00229                     *rect_float++ = 1.0f;
00230                 }
00231             }
00232             MEM_freeN(sline);
00233             if (oriY[0]=='-') IMB_flipy(ibuf);
00234             
00235             if (flags & IB_rect) {
00236                 IMB_rect_from_float(ibuf);
00237             }
00238             
00239             return ibuf;
00240         }
00241         //else printf("Data not found!\n");
00242     }
00243     //else printf("Not a valid radiance HDR file!\n");
00244 
00245     return NULL;
00246 }
00247 
00248 /* ImBuf write */
00249 static int fwritecolrs(FILE* file, int width, int channels, unsigned char* ibufscan, float* fpscan)
00250 {
00251     int x, i, j, beg, c2, cnt=0;
00252     fCOLOR fcol;
00253     RGBE rgbe, *rgbe_scan;
00254 
00255     if ((ibufscan==NULL) && (fpscan==NULL)) return 0;
00256 
00257     rgbe_scan = (RGBE*)MEM_mallocN(sizeof(RGBE)*width, "radhdr_write_tmpscan");
00258 
00259     /* convert scanline */
00260         j= 0;
00261     for (i=0;i<width;i++) {
00262         if (fpscan) {
00263             fcol[RED] = fpscan[j];
00264             fcol[GRN] = (channels >= 2)? fpscan[j+1]: fpscan[j];
00265             fcol[BLU] = (channels >= 3)? fpscan[j+2]: fpscan[j];
00266         } else {
00267             fcol[RED] = (float)ibufscan[j] / 255.f;
00268             fcol[GRN] = (float)((channels >= 2)? ibufscan[j+1]: ibufscan[j]) / 255.f;
00269             fcol[BLU] = (float)((channels >= 3)? ibufscan[j+2]: ibufscan[j]) / 255.f;
00270         }
00271         FLOAT2RGBE(fcol, rgbe);
00272         copy_rgbe(rgbe, rgbe_scan[i]);
00273         j+=channels;
00274     }
00275 
00276     if ((width < MINELEN) | (width > MAXELEN)) {    /* OOBs, write out flat */
00277         x=fwrite((char *)rgbe_scan, sizeof(RGBE), width, file) - width;
00278         MEM_freeN(rgbe_scan);
00279         return x;
00280     }
00281     /* put magic header */
00282     putc(2, file);
00283     putc(2, file);
00284     putc((unsigned char)(width >> 8), file);
00285     putc((unsigned char)(width & 255), file);
00286     /* put components separately */
00287     for (i=0;i<4;i++) {
00288         for (j=0;j<width;j+=cnt) {  /* find next run */
00289             for (beg=j;beg<width;beg+=cnt) {
00290                 for (cnt=1;(cnt<127) && ((beg+cnt)<width) && (rgbe_scan[beg+cnt][i] == rgbe_scan[beg][i]); cnt++);
00291                 if (cnt>=MINRUN) break;   /* long enough */
00292             }
00293             if (((beg-j)>1) && ((beg-j) < MINRUN)) {
00294                 c2 = j+1;
00295                 while (rgbe_scan[c2++][i] == rgbe_scan[j][i])
00296                     if (c2 == beg) {        /* short run */
00297                         putc((unsigned char)(128+beg-j), file);
00298                         putc((unsigned char)(rgbe_scan[j][i]), file);
00299                         j = beg;
00300                         break;
00301                     }
00302             }
00303             while (j < beg) {     /* write out non-run */
00304                 if ((c2 = beg-j) > 128) c2 = 128;
00305                 putc((unsigned char)(c2), file);
00306                 while (c2--) putc(rgbe_scan[j++][i], file);
00307             }
00308             if (cnt >= MINRUN) {      /* write out run */
00309                 putc((unsigned char)(128+cnt), file);
00310                 putc(rgbe_scan[beg][i], file);
00311             }
00312             else cnt = 0;
00313         }
00314     }
00315     MEM_freeN(rgbe_scan);
00316     return(ferror(file) ? -1 : 0);
00317 }
00318 
00319 static void writeHeader(FILE *file, int width, int height)
00320 {
00321     fprintf(file, "#?RADIANCE");
00322     fputc(10, file);
00323     fprintf(file, "# %s", "Created with Blender");
00324     fputc(10, file);
00325     fprintf(file, "EXPOSURE=%25.13f", 1.0);
00326     fputc(10, file);
00327     fprintf(file, "FORMAT=32-bit_rle_rgbe");
00328     fputc(10, file);
00329     fputc(10, file);
00330     fprintf(file, "-Y %d +X %d", height, width);
00331     fputc(10, file);
00332 }
00333 
00334 int imb_savehdr(struct ImBuf *ibuf, const char *name, int flags)
00335 {
00336     FILE* file = fopen(name, "wb");
00337     float *fp= NULL;
00338     int y, width=ibuf->x, height=ibuf->y;
00339     unsigned char *cp= NULL;
00340     
00341     (void)flags; /* unused */
00342     
00343     if (file==NULL) return 0;
00344 
00345     writeHeader(file, width, height);
00346 
00347     if(ibuf->rect)
00348         cp= (unsigned char *)ibuf->rect + ibuf->channels*(height-1)*width;
00349     if(ibuf->rect_float)
00350         fp= ibuf->rect_float + ibuf->channels*(height-1)*width;
00351     
00352     for (y=height-1;y>=0;y--) {
00353         if (fwritecolrs(file, width, ibuf->channels, cp, fp) < 0) {
00354             fclose(file);
00355             printf("HDR write error\n");
00356             return 0;
00357         }
00358         if(cp) cp-= ibuf->channels*width;
00359         if(fp) fp-= ibuf->channels*width;
00360     }
00361 
00362     fclose(file);
00363     return 1;
00364 }