1
1
'use strict' ;
2
2
3
- var stream = require ( 'stream' ) ,
4
- util = require ( 'util' ) ,
5
- colorUtil = require ( './color' ) ;
3
+ var stream = require ( 'stream' ) ;
4
+ var util = require ( 'util' ) ;
5
+ var floatUtils = require ( 'mmme-encoding' ) ;
6
+ var xyzUtils = require ( 'xyz-utils' ) ;
6
7
7
8
module . exports = HDRLoader ;
8
9
9
- var MINLEN = 8 ,
10
- MAXLEN = 0x7fff ,
11
- HEADER_REGEX = / ( [ \# \? ] + ) ? ( [ ^ = \n \r ] + ) ? = ? ( [ ^ = \n \r ] * ) ? ( [ \n \r ] + ) / gi,
12
- DIMENSION_REGEX = / ( [ + \- ] ) ( [ X Y ] ) \s ( \d + ) \s ( [ + \- ] ) ( [ X Y ] ) \s ( \d + ) / i,
13
- HEADER_PREFIXES = {
14
- '#?' : 'FILETYPE' ,
15
- '#' : 'COMMENT' ,
16
- 'undefined' : 'HEADER'
17
- } ,
18
- R = 0 ,
19
- G = 1 ,
20
- B = 2 ,
21
- E = 3 ;
10
+ var MINLEN = 8 ;
11
+ var MAXLEN = 0x7fff ;
12
+ var HEADER_REGEX = / ( [ \# \? ] + ) ? ( [ ^ = \n \r ] + ) ? = ? ( [ ^ = \n \r ] * ) ? ( [ \n \r ] + ) / gi;
13
+ var DIMENSION_REGEX = / ^ ( [ + \- ] ) ( [ X Y ] ) \s ( \d + ) \s ( [ + \- ] ) ( [ X Y ] ) \s ( \d + ) / i;
14
+ var HEADER_PREFIXES = {
15
+ '#?' : 'FILETYPE' ,
16
+ '#' : 'COMMENT' ,
17
+ 'undefined' : 'HEADER'
18
+ } ;
19
+ var DEFAULT_HEADERS = {
20
+ 'EXPOSURE' : 1 ,
21
+ 'COLORCORR' : [ 1 , 1 , 1 ] ,
22
+ 'PIXASPECT' : 1 ,
23
+ 'PRIMARIES' : [ 0.640 , 0.330 , 0.290 , 0.600 , 0.150 , 0.060 , 0.333 , 0.333 ]
24
+ } ;
25
+ var X = 0 ;
26
+ var Y = 1 ;
27
+ var Z = 2 ;
28
+ var E = 3 ;
22
29
23
30
function HDRLoader ( options ) {
24
31
if ( ! options ) options = { } ;
@@ -37,6 +44,9 @@ function HDRLoader (options) {
37
44
this . _error = false ;
38
45
this . _row_major = true ;
39
46
this . _scanlineSize = - 1 ;
47
+ this . _isRGBE = true ;
48
+ this . _hasExposure = false ;
49
+ this . _exposureVals = [ 1 , 1 , 1 ] ;
40
50
}
41
51
42
52
util . inherits ( HDRLoader , stream . Writable ) ;
@@ -100,17 +110,18 @@ HDRLoader.prototype._readScanline = function (chunk) {
100
110
var firstPixel = [ ] ,
101
111
scanline ;
102
112
103
- firstPixel [ R ] = chunk . readUInt8 ( R ) ;
104
- firstPixel [ G ] = chunk . readUInt8 ( G ) ;
105
- firstPixel [ B ] = chunk . readUInt8 ( B ) ;
113
+ firstPixel [ X ] = chunk . readUInt8 ( X ) ;
114
+ firstPixel [ Y ] = chunk . readUInt8 ( Y ) ;
115
+ firstPixel [ Z ] = chunk . readUInt8 ( Z ) ;
106
116
firstPixel [ E ] = chunk . readUInt8 ( E ) ;
107
117
108
118
if ( this . _isOldRLE ( firstPixel ) ) {
109
119
scanline = this . _readOldRLE ( chunk ) ;
110
120
} else {
111
- if ( ( firstPixel [ B ] << 8 | firstPixel [ E ] ) !== this . _getScanlinePixels ( ) ) {
121
+ if ( ( firstPixel [ Z ] << 8 | firstPixel [ E ] ) !== this . _getScanlinePixels ( ) ) {
112
122
this . _lastChunk = null ;
113
123
this . data = null ;
124
+ console . log ( this , chunk )
114
125
this . _error = true ;
115
126
this . emit ( 'error' ) ;
116
127
return ;
@@ -123,14 +134,24 @@ HDRLoader.prototype._readScanline = function (chunk) {
123
134
}
124
135
125
136
HDRLoader . prototype . _processScanline = function ( scanline ) {
137
+ var tempValues ;
138
+ var tempFloats ;
139
+ var tempXYZ ;
140
+
126
141
if ( this . _row_major ) {
127
142
for ( var i = this . _start_x ; i !== this . _end_x ; i += this . _inc_x ) {
128
- this . _writePixel ( i , this . _current_y , colorUtil . toFloats . apply ( null , scanline . shift ( ) ) ) ;
143
+ tempValues = scanline . shift ( ) ;
144
+ tempFloats = floatUtils . toFloats ( tempValues [ 0 ] , tempValues [ 1 ] , tempValues [ 2 ] , tempValues [ 3 ] ) ;
145
+ tempXYZ = this . _processPixel ( tempFloats ) ;
146
+ this . _writePixel ( i , this . _current_y , tempXYZ ) ;
129
147
}
130
148
this . _current_y += this . _inc_y ;
131
149
} else {
132
150
for ( var i = this . _start_y ; i !== this . _end_y ; i += this . _inc_y ) {
133
- this . _writePixel ( this . _current_x , i , colorUtil . toFloats . apply ( null , scanline . shift ( ) ) ) ;
151
+ tempValues = scanline . shift ( ) ;
152
+ tempFloats = floatUtils . toFloats ( tempValues [ 0 ] , tempValues [ 1 ] , tempValues [ 2 ] , tempValues [ 3 ] ) ;
153
+ tempXYZ = this . _processPixel ( tempFloats ) ;
154
+ this . _writePixel ( this . _current_x , i , tempXYZ ) ;
134
155
}
135
156
this . _current_x += this . _inc_x ;
136
157
}
@@ -145,19 +166,19 @@ HDRLoader.prototype._readOldRLE = function (chunk) {
145
166
146
167
while ( len > 0 ) {
147
168
scanline [ writePos ] = [ ] ;
148
- scanline [ writePos ] [ R ] = chunk . readUInt8 ( offset ++ ) ;
149
- scanline [ writePos ] [ G ] = chunk . readUInt8 ( offset ++ ) ;
150
- scanline [ writePos ] [ B ] = chunk . readUInt8 ( offset ++ ) ;
169
+ scanline [ writePos ] [ X ] = chunk . readUInt8 ( offset ++ ) ;
170
+ scanline [ writePos ] [ Y ] = chunk . readUInt8 ( offset ++ ) ;
171
+ scanline [ writePos ] [ Z ] = chunk . readUInt8 ( offset ++ ) ;
151
172
scanline [ writePos ] [ E ] = chunk . readUInt8 ( offset ++ ) ;
152
173
153
- if ( scanline [ writePos ] [ R ] === 1
154
- && scanline [ writePos ] [ G ] === 1
155
- && scanline [ writePos ] [ B ] === 1 ) {
174
+ if ( scanline [ writePos ] [ X ] === 1
175
+ && scanline [ writePos ] [ Y ] === 1
176
+ && scanline [ writePos ] [ Z ] === 1 ) {
156
177
for ( i = scanline [ writePos ] [ E ] << rshift ; i > 0 ; i -- ) {
157
178
scanline [ writePos ] = [ ] ;
158
- scanline [ writePos ] [ R ] = scanline [ writePos - 1 ] [ R ] ;
159
- scanline [ writePos ] [ G ] = scanline [ writePos - 1 ] [ G ] ;
160
- scanline [ writePos ] [ B ] = scanline [ writePos - 1 ] [ B ] ;
179
+ scanline [ writePos ] [ X ] = scanline [ writePos - 1 ] [ X ] ;
180
+ scanline [ writePos ] [ Y ] = scanline [ writePos - 1 ] [ Y ] ;
181
+ scanline [ writePos ] [ X ] = scanline [ writePos - 1 ] [ Z ] ;
161
182
scanline [ writePos ] [ E ] = scanline [ writePos - 1 ] [ E ] ;
162
183
writePos ++ ;
163
184
len -- ;
@@ -214,12 +235,32 @@ HDRLoader.prototype._trimRemainingBuffer = function (chunk, consumed) {
214
235
this . _lastChunk = chunk . slice ( consumed ) ;
215
236
}
216
237
238
+ HDRLoader . prototype . _processPixel = function ( pixelData ) {
239
+ var XYZ = pixelData ;
240
+
241
+ // First adjust the pixel data according to exposure settings
242
+ pixelData [ 0 ] /= this . _exposureVals [ 0 ] ;
243
+ pixelData [ 1 ] /= this . _exposureVals [ 1 ] ;
244
+ pixelData [ 2 ] /= this . _exposureVals [ 2 ] ;
245
+
246
+ // If the pixel format is RGB we convert to XYZ because of the high
247
+ // likelyhood that any further HDR processing will be done in XYZ
248
+ // or a similar color space and XYZ offers the best intermediate for
249
+ // conversion between colorspaces
250
+ if ( this . _isRGBE ) {
251
+ // TODO: Use white point for conversion if supplied in headers
252
+ XYZ = xyzUtils . fromRGB ( pixelData ) ;
253
+ }
254
+
255
+ return XYZ ;
256
+ }
257
+
217
258
HDRLoader . prototype . _writePixel = function ( x , y , pixelData ) {
218
259
var offset = ( x + y * this . width ) * 3 ;
219
260
220
- this . data [ offset ++ ] = pixelData [ R ] ;
221
- this . data [ offset ++ ] = pixelData [ G ] ;
222
- this . data [ offset ++ ] = pixelData [ B ] ;
261
+ this . data [ offset ++ ] = pixelData [ X ] ;
262
+ this . data [ offset ++ ] = pixelData [ Y ] ;
263
+ this . data [ offset ++ ] = pixelData [ Z ] ;
223
264
}
224
265
225
266
HDRLoader . prototype . _readHeader = function ( chunk ) {
@@ -242,6 +283,20 @@ HDRLoader.prototype._readHeader = function (chunk) {
242
283
}
243
284
244
285
this . _headerFinished = true ;
286
+
287
+ if ( this . _hasExposure ) {
288
+ if ( this . headers [ 'EXPOSURE' ] ) {
289
+ var t = this . headers [ 'EXPOSURE' ] ;
290
+ this . _exposureVals = [ t , t , t ] ;
291
+ }
292
+ if ( this . headers [ 'COLORCORR' ] ) {
293
+ var t = this . headers [ 'COLORCORR' ] ;
294
+ this . _exposureVals [ 0 ] *= t [ 0 ] ;
295
+ this . _exposureVals [ 1 ] *= t [ 1 ] ;
296
+ this . _exposureVals [ 2 ] *= t [ 2 ] ;
297
+ }
298
+ }
299
+
245
300
break ;
246
301
} else {
247
302
switch ( HEADER_PREFIXES [ String ( headerData [ 1 ] ) ] ) {
@@ -267,19 +322,34 @@ HDRLoader.prototype._readHeader = function (chunk) {
267
322
this . _trimRemainingBuffer ( chunk , sliceOffset ) ;
268
323
}
269
324
325
+ // We only attempt to parse the most common headers
270
326
HDRLoader . prototype . _processHeader = function ( headerName , headerValue ) {
271
327
switch ( headerName . toUpperCase ( ) ) {
272
328
case 'EXPOSURE' :
329
+ this . _hasExposure = true ;
273
330
case 'PIXASPECT' :
274
331
var val = parseFloat ( headerValue ) ;
275
332
return this . headers [ headerName ] ? this . headers [ headerName ] * val : val ;
276
333
case 'PRIMARIES' :
277
334
var vals = headerValue . split ( / \s + / ) ;
278
335
return vals . map ( parseFloat ) ;
279
336
case 'COLORCORR' :
337
+ this . _hasExposure = true ;
280
338
var vals = headerValue . split ( / \s + / ) ;
281
339
vals = vals . map ( parseFloat ) ;
282
340
return this . headers [ headerName ] ? this . headers [ headerName ] . map ( mults ( vals ) ) : vals ;
341
+ case 'FORMAT' :
342
+ if ( headerValue === '32-bit_rle_rgbe' ) {
343
+ this . _isRGBE = true ;
344
+ return 'RGBE' ;
345
+ } else if ( headerValue === '32-bit_rle_xyze' ) {
346
+ this . _isRGBE = false ;
347
+ return 'XYZE' ;
348
+ } else {
349
+ // Must be a parse error
350
+ this . _error = true ;
351
+ this . emit ( 'error' ) ;
352
+ }
283
353
default :
284
354
return headerValue ;
285
355
}
@@ -290,9 +360,9 @@ HDRLoader.prototype._isOldRLE = function (pixel) {
290
360
291
361
if ( len < MINLEN || len > MAXLEN ) return true ;
292
362
293
- if ( pixel [ R ] !== 2 ) return true ;
363
+ if ( pixel [ X ] !== 2 ) return true ;
294
364
295
- if ( pixel [ G ] !== 2 || pixel [ B ] & 128 ) return true ;
365
+ if ( pixel [ Y ] !== 2 || pixel [ Z ] & 128 ) return true ;
296
366
297
367
return false ;
298
368
}
@@ -378,4 +448,4 @@ function mults (m) {
378
448
return function ( v , i ) {
379
449
return m [ i ] * v ;
380
450
} ;
381
- }
451
+ }
0 commit comments