1
+ var stream = require ( 'stream' ) ,
2
+ util = require ( 'util' ) ,
3
+ colorUtil = require ( './color' ) ;
4
+
5
+ module . exports = HDRReader ;
6
+
7
+ var MINLEN = 8 ,
8
+ MAXLEN = 0x7fff ,
9
+ HEADER_REGEX = / ( [ \# \? ] + ) ? ( [ ^ = \n \r ] + ) ? = ? ( [ ^ = \n \r ] * ) ? ( [ \n \r ] + ) / gi,
10
+ DIMENSION_REGEX = / ( [ + \- ] ) ( [ X Y ] ) \s ( \d + ) \s ( [ + \- ] ) ( [ X Y ] ) \s ( \d + ) / i,
11
+ HEADER_PREFIXES = {
12
+ '#?' : 'FILETYPE' ,
13
+ '#' : 'COMMENT' ,
14
+ 'undefined' : 'HEADER'
15
+ } ;
16
+
17
+ function HDRReader ( options ) {
18
+ if ( ! options ) options = { } ;
19
+ if ( ! options . highWaterMark ) options . highWaterMark = 65536 ;
20
+
21
+ stream . Writable . call ( this , options ) ;
22
+
23
+ this . headers = { } ;
24
+ this . comments = [ ] ;
25
+ this . data = [ ] ;
26
+ this . width = 0 ;
27
+ this . height = 0 ;
28
+
29
+ this . _lastChunk = null ;
30
+ this . _headerFinished = false ;
31
+ this . _startOfScanline = true ;
32
+ this . _newRLEScanline = false ;
33
+
34
+ this . _row_major = true ;
35
+ this . _scanlineSize = 0 ;
36
+ }
37
+
38
+ util . inherits ( HDRReader , stream . Writable ) ;
39
+
40
+ HDRReader . prototype . _write = function ( chunk , encoding , next ) {
41
+ if ( this . _lastChunk ) {
42
+ this . _lastChunk = Buffer . concat ( [ this . _lastChunk , chunk ] ) ;
43
+ } else {
44
+ this . _lastChunk = chunk ;
45
+ }
46
+
47
+ if ( ! this . _headerFinished ) {
48
+ this . _readHeader ( this . _lastChunk , encoding ) ;
49
+ return next ( ) ;
50
+ }
51
+
52
+ while ( this . _lastChunk . length >= this . _scanlineSize ) {
53
+ // this._processChunk(next);
54
+ this . _readScanline ( this . _lastChunk ) ;
55
+ }
56
+
57
+ // this._copyRemainingBuffer(this._lastChunk, 0);
58
+
59
+ return next ( ) ;
60
+ }
61
+
62
+ HDRReader . prototype . end = function ( chunk , encoding ) {
63
+ if ( this . _lastChunk ) {
64
+ if ( chunk ) this . _lastChunk = Buffer . concat ( [ this . _lastChunk , chunk ] ) ;
65
+
66
+ while ( this . _lastChunk . length > 0 ) {
67
+ this . _readScanline ( this . _lastChunk ) ;
68
+ }
69
+ }
70
+
71
+ this . emit ( 'load' ) ;
72
+ }
73
+
74
+ HDRReader . prototype . _processChunk = function ( next ) {
75
+ if ( this . _lastChunk . length >= this . _scanlineSize ) {
76
+ this . _readScanline ( this . _lastChunk ) ;
77
+ return setImmediate ( this . _processChunk . bind ( this , next ) ) ;
78
+ } else {
79
+ this . _copyRemainingBuffer ( this . _lastChunk , 0 ) ;
80
+ return next ( ) ;
81
+ }
82
+ }
83
+
84
+ HDRReader . prototype . _readScanline = function ( chunk ) {
85
+ var firstPixel = [ ] ,
86
+ scanline ;
87
+
88
+ firstPixel [ 0 ] = chunk . readUInt8 ( 0 ) ;
89
+ firstPixel [ 1 ] = chunk . readUInt8 ( 1 ) ;
90
+ firstPixel [ 2 ] = chunk . readUInt8 ( 2 ) ;
91
+ firstPixel [ 3 ] = chunk . readUInt8 ( 3 ) ;
92
+
93
+ if ( this . _isOldRLE ( firstPixel ) ) {
94
+ scanline = this . _readOldRLE ( chunk ) ;
95
+ } else {
96
+ scanline = this . _readNewRLE ( chunk . slice ( 4 ) )
97
+ }
98
+
99
+ firstPixel = null ;
100
+
101
+ if ( this . _row_major ) {
102
+ for ( var i = this . _start_x ; i !== this . _end_x ; i += this . _inc_x ) {
103
+ this . _writePixel ( i , this . _current_y , colorUtil . toFloatArray . apply ( null , scanline . shift ( ) ) ) ;
104
+ }
105
+ this . _current_y += this . _inc_y ;
106
+ } else {
107
+ for ( var i = this . _start_y ; i !== this . _end_y ; i += this . _inc_y ) {
108
+ this . _writePixel ( this . _current_x , i , colorUtil . toFloatArray . apply ( null , scanline . shift ( ) ) ) ;
109
+ }
110
+ this . _current_x += this . _inc_x ;
111
+ }
112
+ }
113
+
114
+ HDRReader . prototype . _readOldRLE = function ( chunk ) {
115
+ var scanline = [ ] ,
116
+ len = this . _getScanlinePixels ( ) ,
117
+ offset = 0 ,
118
+ writePos = 0 ,
119
+ i , rshift = 0 ;
120
+
121
+ while ( len > 0 ) {
122
+ scanline [ writePos ] = [ ] ;
123
+ scanline [ writePos ] [ 0 ] = chunk . readUInt8 ( offset ++ ) ;
124
+ scanline [ writePos ] [ 1 ] = chunk . readUInt8 ( offset ++ ) ;
125
+ scanline [ writePos ] [ 2 ] = chunk . readUInt8 ( offset ++ ) ;
126
+ scanline [ writePos ] [ 3 ] = chunk . readUInt8 ( offset ++ ) ;
127
+
128
+ if ( scanline [ writePos ] [ 0 ] === 1 &&
129
+ scanline [ writePos ] [ 1 ] === 1 &&
130
+ scanline [ writePos ] [ 2 ] === 1 ) {
131
+ for ( i = scanline [ writePos ] [ 3 ] << rshift ; i > 0 ; i -- ) {
132
+ scanline [ writePos ] = [ ] ;
133
+ scanline [ writePos ] [ 0 ] = scanline [ writePos - 1 ] [ 0 ] ;
134
+ scanline [ writePos ] [ 1 ] = scanline [ writePos - 1 ] [ 1 ] ;
135
+ scanline [ writePos ] [ 2 ] = scanline [ writePos - 1 ] [ 2 ] ;
136
+ scanline [ writePos ] [ 3 ] = scanline [ writePos - 1 ] [ 3 ] ;
137
+ writePos ++ ;
138
+ len -- ;
139
+ }
140
+ rshift += 8 ;
141
+ }
142
+ else {
143
+ writePos ++ ;
144
+ len -- ;
145
+ rshift = 0 ;
146
+ }
147
+ }
148
+
149
+ this . _trimRemainingBuffer ( chunk , offset ) ;
150
+ return scanline ;
151
+ }
152
+
153
+ HDRReader . prototype . _readNewRLE = function ( chunk ) {
154
+ var scanline = [ ] ,
155
+ offset = 0 ;
156
+
157
+ for ( var i = 0 ; i < 4 ; i ++ ) {
158
+ for ( var j = 0 ; j < this . _getScanlinePixels ( ) ; ) {
159
+ var code = chunk . readUInt8 ( offset ++ ) ;
160
+ if ( code > 128 ) { // run
161
+ code &= 127 ;
162
+ var val = chunk . readUInt8 ( offset ++ ) ;
163
+ while ( code -- ) {
164
+ if ( scanline [ j ] == null ) scanline [ j ] = [ ] ;
165
+ scanline [ j ++ ] [ i ] = val ;
166
+ }
167
+ } else { // non-run
168
+ while ( code -- ) {
169
+ if ( scanline [ j ] == null ) scanline [ j ] = [ ] ;
170
+ scanline [ j ++ ] [ i ] = chunk . readUInt8 ( offset ++ ) ;
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ this . _trimRemainingBuffer ( chunk , offset ) ;
177
+ return scanline ;
178
+ }
179
+
180
+ HDRReader . prototype . _copyRemainingBuffer = function ( chunk , consumed ) {
181
+ var remainingLen = chunk . length - consumed ;
182
+
183
+ this . _lastChunk = new Buffer ( remainingLen ) ;
184
+
185
+ chunk . copy ( this . _lastChunk , 0 , consumed ) ;
186
+ // this._lastChunk = chunk.slice(consumed);
187
+ }
188
+
189
+ HDRReader . prototype . _trimRemainingBuffer = function ( chunk , consumed ) {
190
+ this . _lastChunk = chunk . slice ( consumed ) ;
191
+ }
192
+
193
+ HDRReader . prototype . _writePixel = function ( x , y , pixelData ) {
194
+ var offset = ( x + y * this . width ) * 3 ;
195
+
196
+ this . data [ offset ++ ] = pixelData [ 0 ] ;
197
+ this . data [ offset ++ ] = pixelData [ 1 ] ;
198
+ this . data [ offset ++ ] = pixelData [ 2 ] ;
199
+ }
200
+
201
+ HDRReader . prototype . _readHeader = function ( chunk ) {
202
+ var str = chunk . toString ( 'ascii' ) ,
203
+ sliceOffset = 0 ,
204
+ headerData ;
205
+
206
+ while ( headerData = HEADER_REGEX . exec ( str ) ) {
207
+ sliceOffset += headerData [ 0 ] . length ;
208
+ if ( DIMENSION_REGEX . test ( headerData [ 2 ] ) ) {
209
+ // Parse size header
210
+ this . _readSizeHeader ( headerData [ 2 ] ) ;
211
+ this . _headerFinished = true ;
212
+ break ;
213
+ } else {
214
+ switch ( HEADER_PREFIXES [ String ( headerData [ 1 ] ) ] ) {
215
+ case 'FILETYPE' :
216
+ this . headers [ 'RADIANCE' ] = true ;
217
+ break ;
218
+ case 'HEADER' :
219
+ this . headers [ headerData [ 2 ] ] = this . _processHeader ( headerData [ 2 ] , headerData [ 3 ] ) ;
220
+ break ;
221
+ case 'COMMENT' :
222
+ this . comments . push ( headerData [ 2 ] ) ;
223
+ break ;
224
+ default :
225
+ }
226
+ }
227
+ }
228
+
229
+ this . _trimRemainingBuffer ( chunk , sliceOffset ) ;
230
+ }
231
+
232
+ HDRReader . prototype . _processHeader = function ( headerName , headerValue ) {
233
+ switch ( headerName . toUpperCase ( ) ) {
234
+ case 'EXPOSURE' :
235
+ var val = parseFloat ( headerValue ) ;
236
+ return ( 'EXPOSURE' in this . headers ) ? this . headers [ 'EXPOSURE' ] * val : val ;
237
+ case 'COLORCORR' :
238
+ var vals = headerValue . split ( / \s + / ) ;
239
+ vals = vals . map ( parseFloat ) ;
240
+ return ( 'COLORCORR' in this . headers ) ? this . headers [ 'COLORCORR' ] . map ( mults ( vals ) ) : vals ;
241
+ default :
242
+ return headerValue ;
243
+ }
244
+ }
245
+
246
+ HDRReader . prototype . _isOldRLE = function ( pixel ) {
247
+ var len = this . _getScanlinePixels ( ) ;
248
+
249
+ if ( len < MINLEN || len > MAXLEN ) return true ;
250
+
251
+ if ( pixel [ 0 ] !== 2 ) return true ;
252
+
253
+ if ( pixel [ 1 ] !== 2 || pixel [ 2 ] & 128 ) return true ;
254
+
255
+ return false ;
256
+ }
257
+
258
+ HDRReader . prototype . _getScanlinePixels = function ( ) {
259
+ if ( this . _row_major ) {
260
+ return this . width ;
261
+ } else {
262
+ return this . height ;
263
+ }
264
+ }
265
+
266
+ HDRReader . prototype . _readSizeHeader = function ( header ) {
267
+ var sizeData = header . match ( DIMENSION_REGEX ) ;
268
+
269
+ if ( sizeData [ 2 ] . toLowerCase ( ) === "y" ) {
270
+ this . _row_major = true ;
271
+
272
+ this . height = + sizeData [ 3 ] ;
273
+ if ( sizeData [ 1 ] === '-' ) {
274
+ this . _start_y = 0 ;
275
+ this . _end_y = this . height ;
276
+ this . _inc_y = 1 ;
277
+ this . _current_y = this . _start_y ;
278
+ } else {
279
+ this . _start_y = this . height - 1 ;
280
+ this . _end_y = - 1 ;
281
+ this . _inc_y = - 1 ;
282
+ this . _current_y = this . _start_y ;
283
+ }
284
+
285
+ this . width = + sizeData [ 6 ] ;
286
+ if ( sizeData [ 4 ] === '-' ) {
287
+ this . _start_x = this . width - 1 ;
288
+ this . _end_x = - 1 ;
289
+ this . _inc_x = - 1 ;
290
+ this . _current_x = this . _start_x ;
291
+ } else {
292
+ this . _start_x = 0 ;
293
+ this . _end_x = this . width ;
294
+ this . _inc_x = 1 ;
295
+ this . _current_x = this . _start_x ;
296
+ }
297
+ } else {
298
+ this . _row_major = false ;
299
+
300
+ this . width = + sizeData [ 3 ] ;
301
+ if ( sizeData [ 1 ] === '-' ) {
302
+ this . _start_x = 0 ;
303
+ this . _end_x = this . width ;
304
+ this . _inc_x = 1 ;
305
+ this . _current_y = this . _start_y ;
306
+ } else {
307
+ this . _start_x = this . width - 1 ;
308
+ this . _end_x = - 1 ;
309
+ this . _inc_x = - 1 ;
310
+ this . _current_x = this . _start_x ;
311
+ }
312
+
313
+ this . height = + sizeData [ 6 ] ;
314
+ if ( sizeData [ 4 ] === '-' ) {
315
+ this . _start_y = this . height - 1 ;
316
+ this . _end_y = - 1 ;
317
+ this . _inc_y = - 1 ;
318
+ this . _current_y = this . _start_y ;
319
+ } else {
320
+ this . _start_y = 0 ;
321
+ this . _end_y = this . height ;
322
+ this . _inc_y = 1 ;
323
+ this . _current_x = this . _start_x ;
324
+ }
325
+ }
326
+
327
+ this . _scanlineSize = this . _getScanlinePixels ( ) * 4 + 4 ; // Number of pixels + possible special pixel at beginning
328
+ }
329
+
330
+ function mults ( m ) {
331
+ return function ( v , i ) {
332
+ return m [ i ] * v ;
333
+ } ;
334
+ }
0 commit comments