1 // -*- mode: c++; c-set-style: "stroustrup"; tab-width: 4; -*-
2 //
3 // CReaderHDR.c
4 //
5 // Copyright (C) 2004 Koji Nakamaru
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2, or (at your option)
10 // any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software Foundation,
19 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 //
21 
22 //
23 // NOTE: The following code is based on the code provided by Bruce
24 // Jonathan Walter at http://www.graphics.cornell.edu/~bjw/rgbe.html
25 //
26 
27 #include "common.h"
28 #include "CReaderHDR.h"
29 
30 static const char *_magic[] = {
31 	"#?",
32 	NULL,
33 };
34 
35 static bool readData(CFile *fp, CImage<float, 4> *image, float *data, int num);
36 static void rgbe2float(uint8_t rgbe[4], float *red, float *green, float *blue);
37 #if 0
38 static void float2rgbe(float red, float green, float blue, uint8_t rgbe[4]);
39 #endif
40 
41 // public functions
42 
CReaderHDR()43 CReaderHDR::CReaderHDR()
44 : CReader()
45 {
46 	magic = _magic;
47 }
48 
~CReaderHDR()49 CReaderHDR::~CReaderHDR()
50 {
51 }
52 
initialize(CFile * fp,char * magic)53 bool CReaderHDR::initialize(
54 	CFile *fp,
55 	char *magic)
56 {
57 	float gamma = 1.0;
58 	float exposure = 1.0;
59 	char buf[256];
60 	bool is_format_found;
61 	if (fp->gets(buf, sizeof(buf)) == NULL
62 		|| buf[0] == '\0'
63 		|| buf[strlen(buf) - 1] != '\n') {
64 		return false;
65 	}
66 	is_format_found = false;
67 	for (;;) {
68 		if (fp->gets(buf, sizeof(buf)) == NULL) {
69 			return false;
70 		}
71 		if (buf[0] == '#') {
72 			continue;
73 		} else if (buf[0] == '\n') {
74 			break;
75 		} else if (strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0) {
76 			is_format_found = true;
77 		} else if (sscanf(buf, "GAMMA=%g", &gamma) == 1) {
78 			continue;
79 		} else if (sscanf(buf, "EXPOSURE=%g", &exposure) == 1) {
80 			continue;
81 		} else if (buf[strlen(buf) - 1] != '\n') {  // too long line, something wrong
82 			return false;
83 		}
84 	}
85 	if (! is_format_found
86 		|| fp->gets(buf, sizeof(buf)) == NULL
87 		|| sscanf(buf, "-Y %d +X %d", &_h, &_w) != 2
88 		|| _h <= 0
89 		|| _w <= 0) {
90 		return false;
91 	}
92 	return true;
93 }
94 
read(CFile * fp,CImage<float,4> * image)95 void CReaderHDR::read(
96 	CFile *fp,
97 	CImage<float, 4> *image)
98 {
99 	float *data = image->pixel(0, 0);
100 	if (_w < 8 || _w > 0x7fff) {
101 		// non-rle
102 		readData(fp, image, data, _w * _h);
103 	}
104 	uint8_t rgbe[4], scanline[4 * _w];
105 	for (int y = 0; y < _h; y++) {
106 		if (fp->read(rgbe, sizeof(rgbe)) != sizeof(rgbe)) {
107 			return;
108 		}
109 		if (rgbe[0] != 2 || rgbe[1] != 2 || rgbe[2] & 0x80) {
110 			// non-rle
111 			rgbe2float(rgbe, &data[0], &data[1], &data[2]);
112 			data[3] = 1.0;
113 			data += 4;
114 			readData(fp, image, data, _w * _h - 1);
115 			break;
116 		}
117 		if (((int)rgbe[2] << 8 | rgbe[3]) != _w) {
118 			return;
119 		}
120 		uint8_t *ptr = &scanline[0];
121 		// read each of the four channels for the scanline into the buffer
122 		for (int i = 0; i < 4; i++) {
123 			uint8_t *ptr_end = &scanline[(i + 1) * _w];
124 			while (ptr < ptr_end) {
125 				uint8_t buf[2];
126 				if (fp->read(buf, 2) != 2) {
127 					return;
128 				}
129 				if (buf[0] > 128) {
130 					// a run of the same value
131 					int count = buf[0] - 128;
132 					if (count == 0 || count > ptr_end - ptr) {
133 						return;
134 					}
135 					while (count-- > 0) {
136 						*ptr++ = buf[1];
137 					}
138 				} else {
139 					// a non-run
140 					int count = buf[0];
141 					if (count == 0 || count > ptr_end - ptr) {
142 						return;
143 					}
144 					*ptr++ = buf[1];
145 					if (--count > 0) {
146 						if (fp->read(ptr, count) != (size_t)count) {
147 							return;
148 						}
149 						ptr += count;
150 					}
151 				}
152 			}
153 		}
154 		// now convert data from buffer into floats
155 		image->lock();
156 		for (int x = 0; x < _w; x++) {
157 			rgbe[0] = scanline[x + 0 * _w];
158 			rgbe[1] = scanline[x + 1 * _w];
159 			rgbe[2] = scanline[x + 2 * _w];
160 			rgbe[3] = scanline[x + 3 * _w];
161 			rgbe2float(rgbe, &data[0], &data[1], &data[2]);
162 			data[3] = 1.0;
163 			data += 4;
164 		}
165 		image->setChanged(true);
166 		image->unlock();
167 	}
168 }
169 
170 // protected functions
171 // private functions
172 // local functions
173 
readData(CFile * fp,CImage<float,4> * image,float * data,int num)174 static bool readData(
175 	CFile *fp,
176 	CImage<float, 4> *image,
177 	float *data,
178 	int num)
179 {
180 	uint8_t rgbe[4];
181 
182 	while (num-- > 0) {
183 		if (fp->read(rgbe, sizeof(rgbe)) == sizeof(rgbe)) {
184 			image->lock();
185 			rgbe2float(rgbe, &data[0], &data[1], &data[2]);
186 			data[3] = 1.0;
187 			data += 4;
188 			image->setChanged(true);
189 			image->unlock();
190 		} else {
191 			return false;
192 		}
193 	}
194 	return true;
195 }
196 
rgbe2float(uint8_t rgbe[4],float * red,float * green,float * blue)197 static void rgbe2float(
198 	uint8_t rgbe[4],
199 	float *red,
200 	float *green,
201 	float *blue)
202 {
203 	if (rgbe[3]) {
204 		float f = ldexp(1.0, rgbe[3] - (int)(128 + 8));
205 		*red = rgbe[0] * f;
206 		*green = rgbe[1] * f;
207 		*blue = rgbe[2] * f;
208 	} else {
209 		*red = *green = *blue = 0.0;
210 	}
211 }
212 
213 #if 0
214 static void float2rgbe(
215 	float red,
216 	float green,
217 	float blue,
218 	uint8_t rgbe[4])
219 {
220 	float v = max(max(red, green), blue);
221 	if (v < 1e-32) {
222 		rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
223 	} else {
224 		int e;
225 		v = frexp(v, &e) * 256.0 / v;
226 		rgbe[0] = (uint8_t)(red * v);
227 		rgbe[1] = (uint8_t)(green * v);
228 		rgbe[2] = (uint8_t)(blue * v);
229 		rgbe[3] = (uint8_t)(e + 128);
230 	}
231 }
232 #endif
233