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