// -*- mode: c++; c-set-style: "stroustrup"; tab-width: 4; -*- // // CReaderHDR.c // // Copyright (C) 2004 Koji Nakamaru // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // // NOTE: The following code is based on the code provided by Bruce // Jonathan Walter at http://www.graphics.cornell.edu/~bjw/rgbe.html // #include "common.h" #include "CReaderHDR.h" static const char *_magic[] = { "#?", NULL, }; static bool readData(CFile *fp, CImage *image, float *data, int num); static void rgbe2float(uint8_t rgbe[4], float *red, float *green, float *blue); #if 0 static void float2rgbe(float red, float green, float blue, uint8_t rgbe[4]); #endif // public functions CReaderHDR::CReaderHDR() : CReader() { magic = _magic; } CReaderHDR::~CReaderHDR() { } bool CReaderHDR::initialize( CFile *fp, char *magic) { float gamma = 1.0; float exposure = 1.0; char buf[256]; bool is_format_found; if (fp->gets(buf, sizeof(buf)) == NULL || buf[0] == '\0' || buf[strlen(buf) - 1] != '\n') { return false; } is_format_found = false; for (;;) { if (fp->gets(buf, sizeof(buf)) == NULL) { return false; } if (buf[0] == '#') { continue; } else if (buf[0] == '\n') { break; } else if (strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0) { is_format_found = true; } else if (sscanf(buf, "GAMMA=%g", &gamma) == 1) { continue; } else if (sscanf(buf, "EXPOSURE=%g", &exposure) == 1) { continue; } else if (buf[strlen(buf) - 1] != '\n') { // too long line, something wrong return false; } } if (! is_format_found || fp->gets(buf, sizeof(buf)) == NULL || sscanf(buf, "-Y %d +X %d", &_h, &_w) != 2 || _h <= 0 || _w <= 0) { return false; } return true; } void CReaderHDR::read( CFile *fp, CImage *image) { float *data = image->pixel(0, 0); if (_w < 8 || _w > 0x7fff) { // non-rle readData(fp, image, data, _w * _h); } uint8_t rgbe[4], scanline[4 * _w]; for (int y = 0; y < _h; y++) { if (fp->read(rgbe, sizeof(rgbe)) != sizeof(rgbe)) { return; } if (rgbe[0] != 2 || rgbe[1] != 2 || rgbe[2] & 0x80) { // non-rle rgbe2float(rgbe, &data[0], &data[1], &data[2]); data[3] = 1.0; data += 4; readData(fp, image, data, _w * _h - 1); break; } if (((int)rgbe[2] << 8 | rgbe[3]) != _w) { return; } uint8_t *ptr = &scanline[0]; // read each of the four channels for the scanline into the buffer for (int i = 0; i < 4; i++) { uint8_t *ptr_end = &scanline[(i + 1) * _w]; while (ptr < ptr_end) { uint8_t buf[2]; if (fp->read(buf, 2) != 2) { return; } if (buf[0] > 128) { // a run of the same value int count = buf[0] - 128; if (count == 0 || count > ptr_end - ptr) { return; } while (count-- > 0) { *ptr++ = buf[1]; } } else { // a non-run int count = buf[0]; if (count == 0 || count > ptr_end - ptr) { return; } *ptr++ = buf[1]; if (--count > 0) { if (fp->read(ptr, count) != (size_t)count) { return; } ptr += count; } } } } // now convert data from buffer into floats image->lock(); for (int x = 0; x < _w; x++) { rgbe[0] = scanline[x + 0 * _w]; rgbe[1] = scanline[x + 1 * _w]; rgbe[2] = scanline[x + 2 * _w]; rgbe[3] = scanline[x + 3 * _w]; rgbe2float(rgbe, &data[0], &data[1], &data[2]); data[3] = 1.0; data += 4; } image->setChanged(true); image->unlock(); } } // protected functions // private functions // local functions static bool readData( CFile *fp, CImage *image, float *data, int num) { uint8_t rgbe[4]; while (num-- > 0) { if (fp->read(rgbe, sizeof(rgbe)) == sizeof(rgbe)) { image->lock(); rgbe2float(rgbe, &data[0], &data[1], &data[2]); data[3] = 1.0; data += 4; image->setChanged(true); image->unlock(); } else { return false; } } return true; } static void rgbe2float( uint8_t rgbe[4], float *red, float *green, float *blue) { if (rgbe[3]) { float f = ldexp(1.0, rgbe[3] - (int)(128 + 8)); *red = rgbe[0] * f; *green = rgbe[1] * f; *blue = rgbe[2] * f; } else { *red = *green = *blue = 0.0; } } #if 0 static void float2rgbe( float red, float green, float blue, uint8_t rgbe[4]) { float v = max(max(red, green), blue); if (v < 1e-32) { rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; } else { int e; v = frexp(v, &e) * 256.0 / v; rgbe[0] = (uint8_t)(red * v); rgbe[1] = (uint8_t)(green * v); rgbe[2] = (uint8_t)(blue * v); rgbe[3] = (uint8_t)(e + 128); } } #endif