0001: //-----------------------------------------------------------------------------
0002: // File: main.cpp
0003: //
0004: // Desc: RADIANCE Picture File Format の読み込み
0005: // Copyright (c) 2003 IMAGIRE Takashi. All rights reserved.
0006: //-----------------------------------------------------------------------------
0007: #define STRICT
0008: #include <stdio.h>
0009: #include "hdr.h"
0010: 
0011: 
0012: 
0013: namespace HDR{
0014: 
0015: #define  COLXS      128     // 指数の基準値(指数がこの値の時にはRGBの色そのもの)
0016: #define MAXGSHIFT   31      // ガンマテーブルのための最大シフト
0017: static BYTE *g_mant = NULL, *g_nexp = NULL;
0018: static BYTE (*g_bval)[256] = NULL;
0019: 
0020: 
0021: //-----------------------------------------------------------------------------
0022: // 色情報
0023: //-----------------------------------------------------------------------------
0024: typedef BYTE  COLR[4];      /* RGBE */
0025: #define  RED        0
0026: #define  GRN        1
0027: #define  BLU        2
0028: #define  EXP        3
0029: 
0030: 
0031: //-----------------------------------------------------------------------------
0032: // ヘッダから読み取る情報
0033: //-----------------------------------------------------------------------------
0034: typedef struct{
0035:     char    mn[64]; // magic number (#?の後、"RADIANCE"にのみ対応)
0036:     char    fs[64]; // フォーマット
0037:     double  xp;     // 露光時間
0038:     int     x;      // 幅
0039:     int     y;      // 高さ
0040:     unsigned char or;// フラグ
0041:     int     sl;     // スキャンライン長さ
0042:     int     ns;     // スキャンライン幅
0043: }Info;
0044: 
0045: // スキャンラインの順番のためのフラグ
0046: #define  XDECR          1   // 負の方向に伸びるかどうか
0047: #define  YDECR          2   // 負の方向に伸びるかどうか 
0048: #define  YMAJOR         4   // 列と行のどちらが優先か
0049: 
0050: #define  MINELEN    8       // 最小画像解像度
0051: #define  MAXELEN    0x7fff  // 最大画像解像度
0052: 
0053: // 標準的な並び順
0054: #define  PIXSTANDARD        (YMAJOR|YDECR)
0055: #define  PIXSTDFMT      "-Y %d +X %d\n"
0056: 
0057: 
0058: 
0059: 
0060: //-----------------------------------------------------------------------------
0061: // Name: s_RemoveCRCF()
0062: // Desc: 余分な改行を取り除く
0063: //-----------------------------------------------------------------------------
0064: void s_RemoveCRCF(char *p)
0065: {
0066:     int len;
0067: 
0068:     for(len = strlen(p)-1; 0<=len; len--)
0069:     {
0070:         if( p[len]!='\r' && p[len]!='\n' ) break;
0071:         p[len] = '\0';
0072:     }
0073: }
0074: 
0075: 
0076: 
0077: //-----------------------------------------------------------------------------
0078: // Name: s_ValData()
0079: // Desc: データの評価
0080: //-----------------------------------------------------------------------------
0081: void s_ValData(Info *r, const char  *s)
0082: {
0083:     if(0 == strncmp(s, "#?", 2))
0084:     {// マジックナンバー
0085:         strcat(r->mn, s+2);
0086:         s_RemoveCRCF(r->mn);
0087:     }
0088:     else if(0 == strncmp(s, "FORMAT=", 7))
0089:     {// フォーマット
0090:         strcat(r->fs, s+7);
0091:         s_RemoveCRCF(r->fs);
0092:     }
0093: }
0094: 
0095: 
0096: //-----------------------------------------------------------------------------
0097: // Name: s_GetHeader()
0098: // Desc: ヘッダの読み込み
0099: //-----------------------------------------------------------------------------
0100: int s_GetHeader(FILE  *fp, Info *cp)
0101: {
0102:     const int MAXLINE = 512;
0103:     char  buf[MAXLINE];
0104: 
0105:     for ( ; ; ) {
0106:         buf[MAXLINE-2] = '\n';
0107:         if (fgets(buf, MAXLINE, fp) == NULL) return(-1);
0108: 
0109:         if (buf[0] == '\n') return(0);
0110:         if (buf[0] == '\r' && buf[1] == '\n') return(0);
0111: 
0112:         if (buf[MAXLINE-2] != '\n') {
0113:             // バイナリデータになったときには、元に戻す
0114:             buf[MAXLINE-2] = '\0';
0115:         }
0116:         s_ValData(cp, buf);
0117:     }
0118: }
0119: 
0120: 
0121: 
0122: 
0123: //-----------------------------------------------------------------------------
0124: // Name: s_Str2Resolu()
0125: // Desc: 文字列 s から解像度を抽出する
0126: //-----------------------------------------------------------------------------
0127: int s_Str2Resolu( Info *r, const char *s )
0128: {
0129:     const char  *xndx = NULL; // 'X' の値が始まる場所
0130:     const char  *yndx = NULL; // 'Y' の値が始まる場所
0131:     const char  *cp;
0132: 
0133:     if (s == NULL) return(0);
0134: 
0135:     // X や Y の始まる位置を調べる
0136:     for (cp = s; *cp; cp++){
0137:         if (*cp == 'X'){
0138:             xndx = cp;
0139:         }else if (*cp == 'Y'){
0140:             yndx = cp;
0141:         }
0142:     }
0143:     
0144:     // 両方の文字列が入っていないと正しいものではない。
0145:     if (xndx == NULL || yndx == NULL) return(0);
0146:     
0147:     // フラグの確認
0148:     r->or = 0;
0149:     if (xndx > yndx) r->or |= YMAJOR;
0150:     if (xndx[-1] == '-') r->or |= XDECR;
0151:     if (yndx[-1] == '-') r->or |= YDECR;
0152: 
0153:     // 値の所得
0154:     if ((r->x = atoi(xndx+1)) <= 0) return(0);
0155:     if ((r->y = atoi(yndx+1)) <= 0) return(0);
0156: 
0157:     return(1);
0158: }
0159: 
0160: 
0161: 
0162: 
0163: //-----------------------------------------------------------------------------
0164: // Name: s_GetResolution()
0165: // Desc: サイズの読み込み
0166: //-----------------------------------------------------------------------------
0167: int s_GetResolution(FILE  *fp, Info *cp)
0168: {
0169:     const int MAXLINE = 512;
0170:     char  buf[MAXLINE];
0171: 
0172:     buf[MAXLINE-2] = '\n';
0173:     if (fgets(buf, MAXLINE, fp) == NULL) return(-1);
0174: 
0175:     s_Str2Resolu(cp, buf);
0176: 
0177:     return (0);
0178: }
0179: 
0180: 
0181: 
0182: 
0183: //-----------------------------------------------------------------------------
0184: // Name: s_GetResolu()
0185: // Desc: scanline の length と numberを得る
0186: //-----------------------------------------------------------------------------
0187: int s_GetResolu(Info *p)
0188: {
0189:     if (p->or & YMAJOR) {
0190:         p->sl = p->x;
0191:         p->ns = p->y;
0192:     } else {
0193:         p->sl = p->y;
0194:         p->ns = p->x;
0195:     }
0196: 
0197:     return(0);
0198: }
0199: 
0200: 
0201: 
0202: //-----------------------------------------------------------------------------
0203: // Name: s_CheckHeader()
0204: // Desc: HDR ファイルの読み込み(返り値が1の時に、フォーマットが正しかった)
0205: //-----------------------------------------------------------------------------
0206: int s_CheckHeader(FILE  *fin, Info *pInfo)
0207: {
0208:     // データの初期化
0209:     pInfo->mn[0] = '\0';
0210:     pInfo->fs[0] = '\0';
0211:     pInfo->xp = 1.0000000000000;
0212:     pInfo->x = 0;
0213:     pInfo->y = 0;
0214:     pInfo->or = 0;
0215:     pInfo->sl = 0;
0216:     pInfo->ns = 0;
0217: 
0218:     // ヘッダの所得
0219:     if (s_GetHeader(fin, pInfo) < 0) return(-1);
0220:     
0221:     // ヘッダ情報チェック
0222:     if (pInfo->mn[0]=='\0' || strcmp("RADIANCE", pInfo->mn) ) return -1;
0223: 
0224:     // フォーマットチェック
0225:     if (pInfo->fs[0]=='\0' || strcmp("32-bit_rle_rgbe", pInfo->fs) ) return -1;
0226: 
0227:     // サイズチェック
0228:     if (s_GetResolution(fin, pInfo) < 0) return(-1);
0229:     if (pInfo->x <= 0 || pInfo->y <= 0) return -1;
0230:     s_GetResolu(pInfo);// 画像のスキャンラインの計算をする
0231: 
0232:     // 正しいデータ
0233:     return 1;
0234: }
0235: 
0236: 
0237: 
0238: 
0239: //-----------------------------------------------------------------------------
0240: // Name: s_OldReadColors()
0241: // Desc: 符号化された色情報を読み込む(古い版)
0242: //-----------------------------------------------------------------------------
0243: int s_OldReadColors(COLR *scanline, int len, FILE *fp)
0244: {
0245:     int  rshift;
0246:     register int  i;
0247:     
0248:     rshift = 0;
0249:     
0250:     while (0 < len) {
0251:         scanline[0][RED] = getc(fp);
0252:         scanline[0][GRN] = getc(fp);
0253:         scanline[0][BLU] = getc(fp);
0254:         scanline[0][EXP] = getc(fp);
0255:         // データがなかったりエラーで終了
0256:         if (feof(fp) || ferror(fp)) return(-1);
0257: 
0258:         if (scanline[0][RED] == 1 &&
0259:             scanline[0][GRN] == 1 &&
0260:             scanline[0][BLU] == 1)
0261:         {
0262:             // R=G=B=1 のときは、指数分だけ同じデータを続ける
0263:             // そのデータがさらに続くなら、長さを2倍にして続ける
0264:             for (i = scanline[0][EXP] << rshift; i > 0; i--) {
0265:                 scanline[0][0]=scanline[-1][0];
0266:                 scanline[0][1]=scanline[-1][1];
0267:                 scanline[0][2]=scanline[-1][2];
0268:                 scanline[0][3]=scanline[-1][3];
0269:                 scanline++;
0270:                 len--;
0271:             }
0272:             rshift += 8;
0273:         } else {
0274:             scanline++;
0275:             len--;
0276:             rshift = 0;
0277:         }
0278:     }
0279:     return(0);
0280: }
0281: 
0282: 
0283: 
0284: 
0285: //-----------------------------------------------------------------------------
0286: // Name: CreateTextureFromHdrFile()
0287: // Desc: 符号化された色情報を読み込む
0288: //-----------------------------------------------------------------------------
0289: int s_ReadColor (COLR *scanline, int len, FILE *fp)
0290: {
0291:     int  i, j;
0292:     int  code, val;
0293: 
0294:     // 読み込みのタイプを決める
0295:     // 最新のものならば、最初の4つの成分が
0296:     // [ 2 | 2 | 画像サイズの上位7ビット|画像サイズの下位8ビット ]
0297:     // になっているはず
0298:     if (len < MINELEN || MAXELEN < len ){
0299:         // 古いタイプ
0300:         return(s_OldReadColors(scanline, len, fp));
0301:     }
0302: 
0303:     i = getc(fp);
0304:     if (i == EOF) return(-1);// データがない
0305: 
0306:     if (i != 2) {
0307:         // 最初は2でないものは古いタイプ
0308:         ungetc(i, fp);
0309:         return(s_OldReadColors(scanline, len, fp));
0310:     }
0311:     // 最低4つのデータが必要
0312:     scanline[0][GRN] = getc(fp);
0313:     scanline[0][BLU] = getc(fp);
0314:     if ((i = getc(fp)) == EOF) return(-1);
0315:     
0316:     // 最初のデータが"22"とかになっていなかったり、
0317:     // 3バイト目のデータについて最大数を超えているかのチェック
0318:     if (scanline[0][GRN] != 2 || scanline[0][BLU] & 128) {
0319:         scanline[0][RED] = 2;
0320:         scanline[0][EXP] = i;
0321:         return(s_OldReadColors(scanline+1, len-1, fp));
0322:     }
0323: 
0324:     // 長さ情報の確認
0325:     if ((scanline[0][BLU]<<8 | i) != len) return(-1);
0326: 
0327:     // 各成分を読み込む
0328:     for (i = 0; i < 4; i++)
0329:     {
0330:         for (j = 0; j < len; )
0331:         {
0332:             // データが途中でなくなった?
0333:             if ((code = getc(fp)) == EOF) return(-1);
0334: 
0335:             if (128 < code) {
0336:                 // 長さ+値のランレングス
0337:                 code &= 127;
0338:                 val = getc(fp);
0339:                 while (code--) scanline[j++][i] = val;
0340:             } else {
0341:                 // ランレングスでないものがcode個続く
0342:                 while (code--) scanline[j++][i] = getc(fp);
0343:             }
0344:         }
0345:     }
0346:     
0347:     // 終端に達していたか確認
0348:     return(feof(fp) ? -1 : 0);
0349: }
0350: 
0351: 
0352: 
0353: 
0354: //-----------------------------------------------------------------------------
0355: // Name: CreateTextureFromHdrFile()
0356: // Desc: Radiance スキャンラインフォーマットをRGBEに変換する
0357: //-----------------------------------------------------------------------------
0358: int Ra2Skel(FILE *fp, int sl, int nl, LPCTSTR *pErr, LPDIRECT3DTEXTURE9 pTexture)
0359: {
0360:     int ret=0;
0361:     COLR    *scanin;
0362:     register int    x;
0363:     int y;
0364: 
0365:     // メモリの確保
0366:     if ((scanin = (COLR *) malloc(sl*sizeof(COLR))) == NULL)
0367:     {
0368:         static LPCTSTR ErrMsg = "Out of memory.";
0369:         if(pErr) *pErr = ErrMsg;
0370:         return -1;
0371:     }
0372: 
0373:     // テクスチャのロック
0374:     D3DLOCKED_RECT d3dlr;
0375:     if(FAILED(pTexture->LockRect(0, &d3dlr, NULL, 0)))
0376:     {
0377:         static LPCTSTR ErrMsg = "error to lock texture.";
0378:         if(pErr) *pErr = ErrMsg;
0379:         ret = -1;
0380:         goto err1_end;
0381:     }
0382: 
0383:     // 画像の変換
0384:     BYTE* pDstRow = (BYTE*)d3dlr.pBits;
0385:     for (y = nl-1; y >= 0; y--) {
0386:         if (s_ReadColor(scanin, sl, fp) < 0)
0387:         {
0388:             static LPCTSTR ErrMsg = "error reading Radiance picture.";
0389:             if(pErr) *pErr = ErrMsg;
0390:             ret = -1;
0391:             goto err2_end;
0392:         }
0393: 
0394:         BYTE* pDst = pDstRow;
0395:         for (x = 0; x < sl; x++) {
0396:             pDst[0] = scanin[x][BLU];
0397:             pDst[1] = scanin[x][GRN];
0398:             pDst[2] = scanin[x][RED];
0399:             pDst[3] = scanin[x][EXP];
0400:             pDst += 4;
0401:         }
0402:         pDstRow += d3dlr.Pitch;
0403:     }
0404: err2_end:
0405: 
0406:     pTexture->UnlockRect(0);
0407: 
0408: err1_end:
0409: 
0410:     // メモリの開放
0411:     free((char *)scanin);
0412: 
0413:     return ret;
0414: }
0415: 
0416: 
0417: 
0418: 
0419: //-----------------------------------------------------------------------------
0420: // Name: CreateTextureFromHdrFile()
0421: // Desc: HDR ファイルの読み込み
0422: //-----------------------------------------------------------------------------
0423: HRESULT CreateTextureFromFile(
0424:         LPDIRECT3DDEVICE9 pDevice,
0425:         LPCTSTR pSrcFile,
0426:         LPCTSTR *pErr,
0427:         LPDIRECT3DTEXTURE9 *pTexture)
0428: {
0429:     HRESULT hr = S_OK;
0430:     Info info;
0431: 
0432:     FILE *fd;
0433:     
0434:     if( (fd = fopen( pSrcFile,"rb" )) == NULL )
0435:     {// ファイルのオープンの失敗
0436:         static LPCTSTR ErrMsg = "Can't open input file.";
0437:         if(pErr) *pErr = ErrMsg;
0438:         hr = -1;
0439:         return -1;
0440:     }
0441: 
0442:     // ヘッダーを調べる
0443:     if (s_CheckHeader(fd, &info) < 0)
0444:     {
0445:         static LPCTSTR ErrMsg = "Bad picture format.";
0446:         if(pErr) *pErr = ErrMsg;
0447:         hr = -1;
0448:         goto err_data;// フォーマットがおかしい
0449:     }
0450: 
0451:     // テクスチャを確保する
0452:     if(FAILED(hr=D3DXCreateTexture(pDevice, info.x, info.y
0453:                     , 1, 0, D3DFMT_A8R8G8B8
0454:                     , D3DPOOL_MANAGED, pTexture)))
0455:     {
0456:         static LPCTSTR ErrMsg = "Failed to create texture.";
0457:         if(pErr) *pErr = ErrMsg;
0458:         goto err_data;// フォーマットがおかしい
0459:     }
0460: 
0461:     // コンバートする
0462:     if( Ra2Skel(fd, info.sl, info.ns, pErr, *pTexture) < 0)
0463:     {
0464:         hr = -1;
0465:     }
0466: 
0467: err_data:
0468:     // ファイルを閉じる
0469:     fclose( fd );
0470: 
0471:     return hr;
0472: }
0473: 
0474: }// namespace HDR
0475: 
0476: 
0477: