ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/RpictSimulManager.cpp
Revision: 2.7
Committed: Thu Aug 22 00:44:02 2024 UTC (8 months, 1 week ago) by greg
Branch: MAIN
Changes since 2.6: +3 -3 lines
Log Message:
perf(rxpict): Minor optimization calling getbinary() rather than read()

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: RpictSimulManager.cpp,v 2.6 2024/08/21 23:52:24 greg Exp $";
3 #endif
4 /*
5 * RpictSimulManager.cpp
6 *
7 * Rpict simulation manager implementation
8 *
9 * Created by Greg Ward on 07/11/2024.
10 */
11
12 #include <ctype.h>
13 #include "platform.h"
14 #include "RpictSimulManager.h"
15 #include "depthcodec.h"
16 #include "random.h"
17
18 /************* Imported globals from rxpmain.c *************/
19
20 extern VIEW ourview; /* viewing parameters */
21 extern int hres, vres; /* current image resolution */
22
23 extern int psample; /* pixel sample size */
24 extern double maxdiff; /* max. sample difference */
25 extern double dstrpix; /* square pixel distribution */
26
27 extern double mblur; /* motion blur parameter */
28
29 extern double dblur; /* depth-of-field blur parameter */
30
31 // Assign a pixel value (& depth) from rendered ray value
32 bool
33 PixelAccess::SetPixel(int x, int y, const RAY *rp)
34 {
35 if (!rp) return false;
36
37 COLOR col;
38 float zv = 0; // get depth if needed
39 if (DepthType())
40 zv = raydistance(rp);
41
42 switch (ColorSpace()) {
43 case RDTscolor: // keeping rendered spectrum?
44 case RDTscolr:
45 return SetPixel(x, y, rp->rcol, zv);
46 case RDTrgb:
47 case RDTrgbe:
48 case RDTxyz:
49 case RDTxyze:
50 scolor_out(col, primp, rp->rcol);
51 return SetPixel(x, y, col, zv);
52 default:
53 error(INTERNAL, "botched color space type in SetPixel()");
54 }
55 return false;
56 }
57
58 // Set color space after non-empty initialization
59 bool
60 PixelAccess::SetColorSpace(RenderDataType cs, RGBPRIMP pr)
61 {
62 if (!dtyp) return false;
63
64 if (!(cs = RDTcolorT(cs)))
65 cs = RDTcolorT(dtyp);
66 else if (RDTcommonE(cs) ^ RDTcommonE(dtyp))
67 return false;
68
69 if (NCSAMP == 3) {
70 if (cs == RDTscolr) cs = RDTrgbe;
71 else if (cs == RDTscolor) cs = RDTrgb;
72 }
73 switch (cs) {
74 case RDTxyze:
75 case RDTxyz:
76 primp = xyzprims;
77 break;
78 case RDTrgbe:
79 case RDTrgb:
80 primp = pr ? pr : stdprims;
81 break;
82 case RDTscolr:
83 case RDTscolor:
84 primp = NULL;
85 break;
86 default:
87 error(INTERNAL, "botched color space type in SetColorSpace()");
88 }
89 dtyp = RDTnewCT(dtyp, cs);
90 return true;
91 }
92
93 /*
94 * Set up rendering frame (call after octree loaded)
95 * Overall dimensions may be adjusted for view,
96 * optional pixel aspect ratio and tile grid
97 * Increments frameNo if >0
98 */
99 bool
100 RpictSimulManager::NewFrame(const VIEW &v, int xydim[2], double *ap, const int *tgrid)
101 {
102 double pasp = 1.;
103
104 if (!xydim) return false;
105 if (!ap) ap = &pasp;
106 pvw = vw; // save previous view for motion blur
107 vw = v;
108 const char * verr = setview(&vw);
109 if (verr) {
110 error(WARNING, verr);
111 vw = pvw;
112 return false;
113 }
114 const double va = viewaspect(&vw);
115 normaspect(va, ap, &xydim[0], &xydim[1]);
116 // set up tiling?
117 if (tgrid && (tgrid[0] > 0) & (tgrid[1] > 0) & (tgrid[0]*tgrid[1] > 1)) {
118 if ((8*tgrid[0] >= xydim[0]) | (8*tgrid[1] >= xydim[1])) {
119 error(WARNING, "Excessive tiling for image size");
120 return false;
121 }
122 xydim[0] -= xydim[0] % (tgsize[0] = tgrid[0]);
123 xydim[1] -= xydim[1] % (tgsize[1] = tgrid[1]);
124 *ap = va * xydim[0] / xydim[1];
125 } else
126 tgsize[0] = tgsize[1] = 1;
127
128 if (vw.vaft > FTINY) rtFlags |= RTlimDist;
129 else rtFlags &= ~RTlimDist;
130 hvres[0] = xydim[0]; hvres[1] = xydim[1];
131 thvres[0] = hvres[0]/tgsize[0]; // presumed tile width
132 thvres[1] = hvres[1]/tgsize[1]; // ...and height
133 frameNo += (frameNo > 0); // caller may override after
134 return true;
135 }
136
137 // Call-back for rendered pixel
138 int
139 RpictSimulManager::RtCall(RAY *r, void *cd)
140 {
141 RpictSimulManager * rsp = (RpictSimulManager *)cd;
142 const int ty = (r->rno-1) / rsp->TWidth();
143 const int tx = r->rno-1 - (RNUMBER)ty*rsp->TWidth();
144
145 if (ty >= rsp->THeight()) {
146 error(INTERNAL, "bad pixel calculation position in RtCall()");
147 return -1;
148 }
149 if (!rsp->doneMap.TestAndSet(tx, ty)) {
150 error(WARNING, "duplicate pixel calculation");
151 return 0;
152 }
153 return rsp->pacc.SetPixel(tx, ty, r);
154 }
155
156 // Set up the specified tile (or entire image if NULL)
157 bool
158 RpictSimulManager::SetTile(const int ti[2])
159 {
160 tvw = vw; ptvw = pvw;
161
162 if (ti) {
163 if ((ti[0] < 0) | (ti[0] >= tgsize[0]) |
164 (ti[1] < 0) | (ti[1] >= tgsize[1])) {
165 error(INTERNAL, "illegal tile specification in SetTile()");
166 return false;
167 }
168 const char * verr = cropview(&tvw,
169 (double)ti[0]/tgsize[0],
170 (double)ti[1]/tgsize[1],
171 (ti[0]+1.)/tgsize[0],
172 (ti[1]+1.)/tgsize[1]);
173 if (verr) {
174 sprintf(errmsg, "crop failure @ tile (%d,%d)/(%d,%d): %s",
175 ti[0], ti[1], tgsize[0], tgsize[1], verr);
176 error(USER, errmsg);
177 return false;
178 } // previous tile view for blur
179 if (!ptvw.type | (mblur <= FTINY) ||
180 cropview(&ptvw, (double)ti[0]/tgsize[0],
181 (double)ti[1]/tgsize[1],
182 (ti[0]+1.)/tgsize[0],
183 (ti[1]+1.)/tgsize[1]))
184 ptvw.type = 0;
185
186 } else if ((tgsize[0] > 1) | (tgsize[1] > 1)) {
187 error(INTERNAL, "missing tile specification in SetTile()");
188 return false;
189 }
190 return doneMap.NewBitMap(TWidth(), THeight());
191 }
192
193 #define pixjitter() (.5+dstrpix*(.5-frandom()))
194
195 // Send the indicated pixel to ray tracer
196 bool
197 RpictSimulManager::ComputePixel(int x, int y)
198 {
199 static const SCOLOR scBlack = {0};
200 int i;
201 FVECT rodir[2];
202 double hpos = (x+pixjitter())/TWidth();
203 double vpos = (y+pixjitter())/THeight();
204 double dlim = viewray(rodir[0], rodir[1], &tvw, hpos, vpos);
205 if (dlim < -FTINY) { // off view?
206 pacc.SetPixel(x, y, scBlack);
207 doneMap.Set(x, y);
208 return true;
209 }
210 if (ptvw.type) { // add motion blur if requested
211 FVECT rorg2, rdir2;
212 double dlim2 = viewray(rorg2, rdir2, &ptvw, hpos, vpos);
213 if (dlim2 >= -FTINY) {
214 const double d = mblur*(.5-frandom());
215 dlim = (1.-d)*dlim + d*dlim2;
216 for (i = 3; i--; ) {
217 rodir[0][i] = (1.-d)*rodir[0][i] + d*rorg2[i];
218 rodir[1][i] = (1.-d)*rodir[1][i] + d*rdir2[i];
219 }
220 if (normalize(rodir[1]) == 0)
221 return false;
222 }
223 }
224 // depth-of-field blur if any
225 if (!jitteraperture(rodir[0], rodir[1], &tvw, dblur))
226 return false;
227 // include aft clipping distance?
228 for (i = (dlim > FTINY)*3; i--; )
229 rodir[1][i] *= dlim;
230
231 return EnqueueRay(rodir[0], rodir[1], (RNUMBER)y*TWidth()+x+1);
232 }
233
234 // Check if neighbor differences are below pixel sampling threshold
235 bool
236 RpictSimulManager::BelowSampThresh(int x, int y, const int noff[4][2]) const
237 {
238 SCOLOR pval[4];
239 float dist[4];
240 int i, j;
241
242 for (i = 4; i--; ) { // get pixels from tile store
243 int px = x + noff[i][0];
244 int py = y + noff[i][1];
245 if (!doneMap.Check(px, py) ||
246 !pacc.GetPixel(px, py, pval[i], &dist[i]))
247 return false;
248 }
249 const bool spectr = (pacc.NC() > 3);
250 for (i = 4; --i; ) // do pairwise comparisons
251 for (j = i; j--; ) {
252 if (pacc.DepthType() &&
253 (dist[i] - dist[j] > maxdiff*dist[j]) |
254 (dist[j] - dist[i] > maxdiff*dist[i]))
255 return false;
256 if (spectr ? sbigsdiff(pval[i], pval[j], maxdiff) :
257 bigdiff(pval[i], pval[j], maxdiff))
258 return false;
259 }
260 return true; // linear interpolation OK
261 }
262
263 // Fill an interior square patch with interpolated values
264 void
265 RpictSimulManager::FillSquare(const int x, const int y, const int noff[4][2])
266 {
267 SCOLOR pval[4];
268 float dist[4];
269 int i, j;
270 // assumes 4 corners are valid!
271 for (i = 4; i--; )
272 pacc.GetPixel(x+noff[i][0], y+noff[i][1], pval[i], &dist[i]);
273
274 i = abs(noff[1][0]-noff[0][0]);
275 j = abs(noff[1][1]-noff[0][1]); // i==j for diamond fill
276 const int slen = (i > j) ? i : j;
277 const bool spectr = (pacc.NC() > 3);
278 for (i = slen+1 + (i==j)*slen; i--; ) {
279 const double c1 = (i>slen ? i-slen-.5 : (double)i)/slen;
280 for (j = slen + (i<=slen); j--; ) {
281 const double c2 = (j + (i>slen)*.5)/slen;
282 const int px = int(x + (1.-c1)*(1.-c2)*noff[0][0] +
283 c1*(1.-c2)*noff[1][0] +
284 (1.-c1)*c2*noff[2][0] +
285 c1*c2*noff[3][0] + .5);
286 const int py = int(y + (1.-c1)*(1.-c2)*noff[0][1] +
287 c1*(1.-c2)*noff[1][1] +
288 (1.-c1)*c2*noff[2][1] +
289 c1*c2*noff[3][1] + .5);
290 if (!doneMap.TestAndSet(px, py))
291 continue;
292 float zval = 0;
293 if (pacc.DepthType())
294 zval = (1.-c1)*(1.-c2)*dist[0] + c1*(1.-c2)*dist[1] +
295 (1.-c1)*c2*dist[2] + c1*c2*dist[3];
296 if (spectr) { // XXX assumes pacc.NC() == NCSAMP
297 SCOLOR ipval, tpval;
298 copyscolor(ipval, pval[0]);
299 scalescolor(ipval, (1.-c1)*(1.-c2));
300 copyscolor(tpval, pval[1]);
301 scalescolor(tpval, c1*(1.-c2));
302 saddscolor(ipval, tpval);
303 copyscolor(tpval, pval[2]);
304 scalescolor(tpval, (1.-c1)*c2);
305 saddscolor(ipval, tpval);
306 copyscolor(tpval, pval[3]);
307 scalescolor(tpval, c1*c2);
308 saddscolor(ipval, tpval);
309 pacc.SetPixel(px, py, ipval, zval);
310 } else { // tristimulus interpolation
311 COLOR ipval, tpval;
312 copycolor(ipval, pval[0]);
313 scalecolor(ipval, (1.-c1)*(1.-c2));
314 copycolor(tpval, pval[1]);
315 scalecolor(tpval, c1*(1.-c2));
316 addcolor(ipval, tpval);
317 copycolor(tpval, pval[2]);
318 scalecolor(tpval, (1.-c1)*c2);
319 addcolor(ipval, tpval);
320 copycolor(tpval, pval[3]);
321 scalecolor(tpval, c1*c2);
322 addcolor(ipval, tpval);
323 pacc.SetPixel(px, py, ipval, zval);
324 }
325 }
326 }
327 }
328
329 // helper function to set up quincunx sampling
330 static void
331 SetQuincunx(ABitMap2 *bmp2, int noff[4][2], const int spc, const bool odd)
332 {
333 for (int y = 0; y < bmp2->Height(); y += spc>>1)
334 for (int x = (odd^(y&1))*(spc>>1); x < bmp2->Width(); x += spc)
335 bmp2->Set(x, y);
336 // order neighbors CCW
337 if (odd) {
338 noff[0][0] = spc>>1; noff[0][1] = 0;
339 noff[1][0] = 0; noff[1][1] = spc>>1;
340 noff[2][0] = -(spc>>1); noff[2][1] = 0;
341 noff[3][0] = 0; noff[3][1] = -(spc>>1);
342 } else {
343 noff[0][0] = spc>>1; noff[0][1] = spc>>1;
344 noff[1][0] = -(spc>>1); noff[1][1] = spc>>1;
345 noff[2][0] = -(spc>>1); noff[2][1] = -(spc>>1);
346 noff[3][0] = spc>>1; noff[3][1] = -(spc>>1);
347 }
348 }
349
350 // Render (or finish rendering) current tile
351 bool
352 RpictSimulManager::RenderRect()
353 {
354 if (!tvw.type || !Ready()) {
355 error(INTERNAL, "need octree and view for RenderRect()");
356 return false;
357 }
358 ABitMap2 doneSamples = doneMap;
359 int sp2 = ceil(log2((TWidth()>THeight() ? TWidth() : THeight()) - 1.));
360 int layer = 0;
361 int x, y;
362 // fprintf(stderr, "Rendering %dx%d tile with psample=%d, maxdiff=%.3f ...\n",
363 // TWidth(), THeight(), psample, maxdiff);
364 while (sp2 > 0) {
365 ABitMap2 sampMap(TWidth(), THeight());
366 int noff[4][2];
367 if ((prCB != NULL) & (barPix == NULL))
368 (*prCB)(100.*doneMap.SumTotal()/doneMap.Width()/doneMap.Height());
369 SetQuincunx(&sampMap, noff, 1<<sp2, layer&1);
370 sampMap -= doneSamples; // avoid resampling pixels
371 // Are we into adaptive sampling realm?
372 if (noff[0][0]*noff[0][0] + noff[0][1]*noff[0][1] < psample*psample) {
373 if (FlushQueue() < 0) // need results to check thresholds
374 return false;
375 ABitMap2 fillMap = sampMap;
376 for (x = y = 0; sampMap.Find(&x, &y); x++)
377 if (BelowSampThresh(x, y, noff))
378 sampMap.Reset(x, y);
379 // spread sampling to neighbors...
380 const ABitMap2 origSampMap = sampMap;
381 for (x = 4; x--; ) {
382 ABitMap2 stamp = origSampMap;
383 stamp.Shift(noff[x][0], noff[x][1]);
384 sampMap |= stamp;
385 } // ...but don't resample what's done
386 sampMap -= doneSamples;
387 // interpolate smooth regions
388 fillMap -= sampMap;
389 for (x = y = 0; fillMap.Find(&x, &y); x++)
390 FillSquare(x, y, noff);
391 doneSamples |= doneMap;
392 } // compute required ray samples
393 for (x = y = 0; sampMap.Find(&x, &y); x++)
394 if (!ComputePixel(x, y))
395 return false;
396 doneSamples |= sampMap; // samples now done or at least queued
397 // fprintf(stderr, "Sampled %ld pixels at (sp2,layer)=(%d,%d)\n",
398 // (long)sampMap.SumTotal(), sp2, layer);
399 // fprintf(stderr, "\t%ld pixels (%.3f%%) completed (+%ld in process)\n",
400 // (long)doneMap.SumTotal(), 100.*doneMap.SumTotal()/doneMap.Width()/doneMap.Height(),
401 // (long)(doneSamples.SumTotal()-doneMap.SumTotal()));
402 sp2 -= layer++ & 1; // next denser sampling
403 }
404 if (FlushQueue() < 0) // make sure we got everyone
405 return false;
406 x = y = 0;
407 if (doneMap.Find(&x, &y, false)) {
408 sprintf(errmsg, "missed %ld tile pixels, e.g. (%d,%d)",
409 (long)doneMap.Width()*doneMap.Height() -
410 doneMap.SumTotal(), x, y);
411 error(WARNING, errmsg);
412 }
413 if ((prCB != NULL) & (barPix == NULL))
414 (*prCB)(100.);
415 return true;
416 }
417
418 /*
419 * Render the specified tile in frame
420 * Tile pixels are contiguous unless ystride != 0
421 * Tiles numbered from upper-left at (0,0)
422 * Pixel type influenced by this->prims assignment
423 */
424 bool
425 RpictSimulManager::RenderTile(COLORV *rp, int ystride, float *zp, const int *tile)
426 {
427 if (!rp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
428 return false;
429 if (!ystride) // contiguous rows?
430 ystride = TWidth();
431 pacc.Init(rp, ystride, zp);
432 if (prims == xyzprims)
433 pacc.SetColorSpace(RDTxyz);
434 else if (prims)
435 pacc.SetColorSpace(RDTrgb, prims);
436
437 return SetTile(tile) && RenderRect();
438 }
439
440 // Same but store as common-exponent COLR or SCOLR
441 bool
442 RpictSimulManager::RenderTile(COLRV *bp, int ystride, float *zp, const int *tile)
443 {
444 if (!bp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
445 return false;
446 if (!ystride) // contiguous rows?
447 ystride = TWidth();
448 pacc.Init(bp, ystride, zp);
449 if (prims == xyzprims)
450 pacc.SetColorSpace(RDTxyze);
451 else if (prims)
452 pacc.SetColorSpace(RDTrgbe, prims);
453
454 return SetTile(tile) && RenderRect();
455 }
456
457 // Same but also use 16-bit encoded depth buffer
458 bool
459 RpictSimulManager::RenderTile(COLRV *bp, int ystride, short *dp, const int *tile)
460 {
461 if (!bp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
462 return false;
463 if (!ystride) // contiguous rows?
464 ystride = TWidth();
465 pacc.Init(bp, ystride, dp);
466 if (prims == xyzprims)
467 pacc.SetColorSpace(RDTxyze);
468 else if (prims)
469 pacc.SetColorSpace(RDTrgbe, prims);
470
471 return SetTile(tile) && RenderRect();
472 }
473
474 // Back to float color with 16-bit depth
475 bool
476 RpictSimulManager::RenderTile(COLORV *rp, int ystride, short *dp, const int *tile)
477 {
478 if (!rp | (GetWidth() <= 0) | (GetHeight() <= 0) | !vw.type)
479 return false;
480 if (!ystride) // contiguous rows?
481 ystride = TWidth();
482 pacc.Init(rp, ystride, dp);
483 if (prims == xyzprims)
484 pacc.SetColorSpace(RDTxyz);
485 else if (prims)
486 pacc.SetColorSpace(RDTrgb, prims);
487
488 return SetTile(tile) && RenderRect();
489 }
490
491 // Allocate a new render bar
492 void
493 RpictSimulManager::NewBar(int ht)
494 {
495 delete [] barPix;
496 delete [] barDepth;
497 if (ht > GetHeight()) ht = GetHeight();
498 if ((ht <= 0) | (GetWidth() <= 0)) {
499 doneMap.NewBitMap(0,0);
500 pacc.Init();
501 barPix = NULL; barDepth = NULL;
502 return;
503 }
504 thvres[0] = GetWidth();
505 thvres[1] = ht;
506 const int NC = prims ? 3 : NCSAMP;
507 barPix = new COLORV [ht*thvres[0]*NC];
508 barDepth = new float [ht*thvres[0]];
509 pacc.Init(barPix + (ht-1)*thvres[0]*NC,
510 -thvres[0], barDepth + (ht-1)*thvres[0]);
511 if (prims == xyzprims)
512 pacc.SetColorSpace(RDTxyz);
513 else if (prims)
514 pacc.SetColorSpace(RDTrgb, prims);
515
516 doneMap.NewBitMap(TWidth(), THeight());
517 }
518
519 // Shift render bar area the specified amount down the frame
520 bool
521 RpictSimulManager::LowerBar(int v, int ytop)
522 {
523 if (!barPix | !barDepth | (v > THeight()) | !tvw.type)
524 return false;
525 if (v <= 0) return !v;
526 if ((ytop -= v) <= 0)
527 return true;
528 tvw.voff -= double(v)/THeight();
529 ptvw.voff -= double(v)/THeight();
530 if (v == THeight()) {
531 doneMap.ClearBitMap();
532 return true;
533 }
534 const int NC = pacc.NC();
535 doneMap.Shift(0, v, false); // lift finished pixel samples
536 memmove(barPix, barPix + NC*TWidth()*v,
537 sizeof(COLORV)*NC*TWidth()*(THeight()-v));
538 memmove(barDepth, barDepth + TWidth()*v,
539 sizeof(float)*TWidth()*(THeight()-v));
540 if (ytop < THeight()) { // mark what we won't do as finished
541 doneMap.ClearRect(0, 0, TWidth(), THeight()-ytop, true);
542 memset(barPix, 0, sizeof(COLORV)*NC*TWidth()*(THeight()-ytop));
543 memset(barDepth, 0, sizeof(float)*TWidth()*(THeight()-ytop));
544 }
545 return true;
546 }
547
548 // Continue rendering from the specified position
549 bool
550 RpictSimulManager::RenderBelow(int ytop, const int vstep, FILE *pfp, const int dt, FILE *dfp)
551 {
552 if (ytop <= 0)
553 return true;
554 ptvw = pvw; // set starting bar's view
555 tvw = vw;
556 const char * verr = cropview(&tvw, 0., double(ytop-THeight())/GetHeight(),
557 1., double(ytop)/GetHeight());
558 if (verr) {
559 sprintf(errmsg, "illegal render bar below y=%d: %s", ytop, verr);
560 error(INTERNAL, errmsg);
561 return false;
562 }
563 if (!ptvw.type | (mblur <= FTINY) ||
564 cropview(&ptvw, 0., double(ytop-THeight())/GetHeight(),
565 1., double(ytop)/GetHeight()))
566 ptvw.type = 0;
567 // set up spectral sampling
568 if (setspectrsamp(CNDX, WLPART) <= 0) {
569 error(USER, "unsupported spectral sampling");
570 return false;
571 }
572 COLORV ** parr = NULL; // set up tiny source drawing
573 float ** zarr = NULL;
574 if (!ptvw.type && directvis && dblur <= FTINY) {
575 parr = new COLORV * [THeight()];
576 zarr = new float * [THeight()];
577 for (int n = THeight(); n-- > 0; ) {
578 parr[THeight()-1-n] = barPix + pacc.NC()*TWidth()*n;
579 zarr[THeight()-1-n] = barDepth + TWidth()*n;
580 }
581 ourview = vw; hres = GetWidth(); vres = GetHeight();
582 init_drawsources(psample);
583 }
584 int lastOut = ytop; // render down frame
585 while (ytop > 0) {
586 // fprintf(stderr, "At y=%d, source drawing %s...\n", ytop, parr ? "ON" : "OFF");
587 if (prCB)
588 (*prCB)(100.*(GetHeight()-ytop)/GetHeight());
589 if (!RenderRect()) // render this bar
590 return false;
591 int nlines = lastOut - ytop + THeight();
592 if (nlines > ytop)
593 nlines = ytop;
594 else if (parr) // drawing sources?
595 drawsources(parr, prims, zarr,
596 0, hres, lastOut-nlines, nlines);
597
598 if (dfp) { // write out depth scanlines?
599 const float * dp = barDepth + TWidth()*(ytop-lastOut);
600 if (RDTdepthT(dt) == RDTdshort) {
601 for (int n = TWidth()*nlines; n-- > 0; dp++)
602 if (putint(depth2code(*dp, pacc.refDepth), 2, dfp) == EOF)
603 error(SYSTEM, "cannot write 16-bit depth buffer");
604 } else if (putbinary(dp, sizeof(float), TWidth()*nlines, dfp)
605 != TWidth()*nlines)
606 error(SYSTEM, "cannot write raw depth buffer");
607 }
608 COLORV * bpos = barPix + pacc.NC()*TWidth()*(ytop-lastOut);
609 while (nlines-- > 0) { // write pixel scanlines
610 switch (RDTcolorT(dt)) {
611 case RDTrgbe:
612 case RDTxyze:
613 if (fwritescan((COLOR *)bpos, TWidth(), pfp) < 0)
614 error(SYSTEM, "cannot write RGBE/XYZE output");
615 break;
616 case RDTscolr:
617 if (fwritesscan(bpos, pacc.NC(), TWidth(), pfp) < 0)
618 error(SYSTEM, "cannot write SCOLOR output");
619 break;
620 case RDTrgb:
621 case RDTxyz:
622 case RDTscolor:
623 if (putbinary(bpos, sizeof(COLORV)*pacc.NC(), TWidth(), pfp)
624 != TWidth())
625 error(SYSTEM, "cannot write SCOLOR output");
626 break;
627 default:
628 error(INTERNAL, "botched output color type in RenderBelow()");
629 break;
630 }
631 bpos += pacc.NC()*TWidth();
632 --lastOut;
633 } // flush each scan bar
634 if (fflush(pfp) == EOF || (dfp && fflush(dfp) == EOF))
635 error(SYSTEM, "output write error");
636 // advance down the frame
637 if (lastOut > 0 && !LowerBar(vstep, ytop))
638 return false;
639 ytop -= vstep;
640 }
641 delete [] parr;
642 delete [] zarr;
643 if (prCB)
644 (*prCB)(100.);
645 return true;
646 }
647
648 // Open new output picture file (and optional depth file)
649 RenderDataType
650 RpictSimulManager::NewOutput(FILE *pdfp[2], const char *pfname,
651 RenderDataType dt, const char *dfname)
652 {
653 pdfp[0] = pdfp[1] = NULL;
654 if (!RDTcolorT(dt))
655 error(INTERNAL, "botched color output type in NewOutput()");
656 if (NCSAMP == 3) {
657 if (RDTcolorT(dt) == RDTscolr)
658 dt = RDTnewCT(dt, prims==xyzprims ? RDTxyze : RDTrgbe);
659 else if (RDTcolorT(dt) == RDTscolor)
660 dt = RDTnewCT(dt, prims==xyzprims ? RDTxyz : RDTrgb);
661 }
662 if (!RDTdepthT(dt) ^ !dfname)
663 error(INTERNAL, "depth output requires file name and type in NewOutput()");
664 int fd = 1;
665 if (pfname) { // open picture output file
666 if (pfname[0] == '!') {
667 error(INTERNAL, "writing picture to a command not supported");
668 return RDTnone;
669 }
670 fd = open(pfname, O_WRONLY|O_CREAT|O_EXCL, 0666);
671 }
672 if (fd < 0) {
673 if ((frameNo <= 0) | (errno != EEXIST)) {
674 sprintf(errmsg, "cannot open picture file '%s'", pfname);
675 error(SYSTEM, errmsg);
676 }
677 return RDTnone; // expected in parallel sequence
678 }
679 if (fd == 1)
680 pdfp[0] = stdout;
681 else if (!(pdfp[0] = fdopen(fd, "w")))
682 error(SYSTEM, "failure calling fdopen()");
683 SET_FILE_BINARY(pdfp[0]); // write picture header
684 if ((pdfp[0] != stdout) | (frameNo <= 1)) {
685 newheader("RADIANCE", pdfp[0]);
686 fputs(GetHeader(), pdfp[0]);
687 }
688 fputs(VIEWSTR, pdfp[0]); fprintview(&vw, pdfp[0]); fputc('\n', pdfp[0]);
689 if (frameNo > 0)
690 fprintf(pdfp[0], "FRAME=%d\n", frameNo);
691 double pasp = viewaspect(&vw) * GetWidth() / GetHeight();
692 if ((0.99 > pasp) | (pasp > 1.01))
693 fputaspect(pasp, pdfp[0]);
694 fputnow(pdfp[0]);
695 switch (RDTcolorT(dt)) { // set primaries and picture format
696 case RDTrgbe:
697 if (!prims | (prims == xyzprims)) prims = stdprims;
698 fputprims(prims, pdfp[0]);
699 fputformat(COLRFMT, pdfp[0]);
700 break;
701 case RDTxyze:
702 prims = xyzprims;
703 fputformat(CIEFMT, pdfp[0]);
704 break;
705 case RDTscolr:
706 prims = NULL;
707 fputwlsplit(WLPART, pdfp[0]);
708 fputncomp(NCSAMP, pdfp[0]);
709 fputformat(SPECFMT, pdfp[0]);
710 break;
711 case RDTrgb:
712 if (!prims | (prims == xyzprims)) prims = stdprims;
713 fputprims(prims, pdfp[0]);
714 fputncomp(3, pdfp[0]);
715 fputendian(pdfp[0]);
716 fputformat("float", pdfp[0]);
717 break;
718 case RDTxyz:
719 prims = xyzprims;
720 fputprims(prims, pdfp[0]);
721 fputncomp(3, pdfp[0]);
722 fputendian(pdfp[0]);
723 fputformat("float", pdfp[0]);
724 break;
725 case RDTscolor:
726 prims = NULL;
727 fputwlsplit(WLPART, pdfp[0]);
728 fputncomp(NCSAMP, pdfp[0]);
729 fputendian(pdfp[0]);
730 fputformat("float", pdfp[0]);
731 break;
732 default:;
733 }
734 fputc('\n', pdfp[0]); // flush picture header + resolution
735 if (fflush(pdfp[0]) == EOF) {
736 sprintf(errmsg, "cannot write header to picture '%s'", pfname);
737 error(SYSTEM, errmsg);
738 fclose(pdfp[0]);
739 pdfp[0] = NULL;
740 return RDTnone;
741 }
742 if (dfname) { // open depth output
743 if (dfname[0] == '!')
744 pdfp[1] = popen(dfname+1, "w");
745 else
746 pdfp[1] = fopen(dfname, "w");
747 if (!pdfp[1]) {
748 sprintf(errmsg, "cannot open depth output '%s'", dfname);
749 error(SYSTEM, errmsg);
750 fclose(pdfp[0]);
751 pdfp[0] = NULL;
752 return RDTnone;
753 }
754 SET_FILE_BINARY(pdfp[1]);
755 }
756 if (RDTdepthT(dt) == RDTdshort) { // write header for 16-bit depth?
757 newheader("RADIANCE", pdfp[1]);
758 fputs(GetHeader(), pdfp[1]);
759 fputs(VIEWSTR, pdfp[1]); fprintview(&vw, pdfp[1]); fputc('\n', pdfp[1]);
760 fputs(DEPTHSTR, pdfp[1]); fputs(dunit, pdfp[1]); fputc('\n', pdfp[1]);
761 fputformat(DEPTH16FMT, pdfp[1]);
762 fputc('\n', pdfp[1]); // end-of-info
763 if (fflush(pdfp[1]) == EOF) {
764 sprintf(errmsg, "cannot write header to '%s'", dfname);
765 error(SYSTEM, errmsg);
766 fclose(pdfp[0]); fclose(pdfp[1]);
767 pdfp[0] = pdfp[1] = NULL;
768 return RDTnone;
769 }
770 }
771 return dt; // ready to roll
772 }
773
774 /*
775 * Render and write a frame to the named file
776 * Include any header lines set prior to call
777 * Picture file must not exist
778 * Write pixels to stdout if !pfname
779 * Write depth to a command if dfname[0]=='!'
780 */
781 RenderDataType
782 RpictSimulManager::RenderFrame(const char *pfname, RenderDataType dt, const char *dfname)
783 {
784 FILE *pdfp[2];
785 // prepare output file(s)
786 dt = NewOutput(pdfp, pfname, dt, dfname);
787 if (dt == RDTnone)
788 return RDTnone;
789 // add resolution string(s)
790 fprtresolu(GetWidth(), GetHeight(), pdfp[0]);
791 if (RDTdepthT(dt) == RDTdshort)
792 fprtresolu(GetWidth(), GetHeight(), pdfp[1]);
793
794 const int bheight = (psample > 1) ? int(2*psample+.99) : 4;
795 const int vstep = bheight >> (psample > 1);
796
797 NewBar(bheight); // render frame if we can
798 if (!RenderBelow(GetHeight(), vstep, pdfp[0], dt, pdfp[1])) {
799 fclose(pdfp[0]);
800 if (pdfp[1]) (dfname[0] == '!') ? pclose(pdfp[1]) : fclose(pdfp[1]);
801 Cleanup();
802 return RDTnone;
803 }
804 NewBar(); // clean up and return
805 if (pdfp[0] != stdout)
806 fclose(pdfp[0]);
807 if (pdfp[1]) {
808 if (dfname[0] == '!') {
809 int status = pclose(pdfp[1]);
810 if (status) {
811 sprintf(errmsg, "depth output (%s) error status: %d",
812 dfname, status);
813 error(USER, errmsg);
814 return RDTnone;
815 }
816 } else
817 fclose(pdfp[1]);
818 }
819 return dt;
820 }
821
822 // passed struct for header line callback
823 static struct HeaderInfo {
824 char fmt[MAXFMTLEN];
825 char depth_unit[32];
826 int ncomp;
827 RGBPRIMS prims;
828 VIEW vw;
829 bool gotWL;
830 bool gotprims;
831 bool gotview;
832 bool endianMatch;
833
834 HeaderInfo() {
835 strcpy(fmt, "MISSING");
836 depth_unit[0] = '\0';
837 ncomp = 3;
838 vw = stdview;
839 gotWL = false;
840 gotprims = false;
841 gotview = false;
842 endianMatch = true;
843 }
844 } hinfo; // XXX single copy to hold custom primitives
845
846 // helper function checks header line and records req. info.
847 static int
848 head_check(char *s, void *p)
849 {
850 HeaderInfo * hp = (HeaderInfo *)p;
851 int rval;
852
853 if (isncomp(s)) {
854 hp->ncomp = ncompval(s);
855 return 1;
856 }
857 if (iswlsplit(s)) {
858 hp->gotWL = wlsplitval(WLPART, s);
859 return 1;
860 }
861 if (isprims(s)) {
862 hp->gotprims = primsval(hp->prims, s);
863 return 1;
864 }
865 if (isview(s)) {
866 hp->gotview |= (sscanview(&hp->vw, s) > 0);
867 return 1;
868 }
869 if (!strncmp(s, DEPTHSTR, LDEPTHSTR)) {
870 strlcpy(hp->depth_unit, s+LDEPTHSTR, sizeof(hp->depth_unit));
871 char * cp = hp->depth_unit;
872 while (*cp) cp++;
873 while (cp > hp->depth_unit && isspace(cp[-1])) cp--;
874 *cp = '\0';
875 return 1;
876 }
877 if ((rval = isbigendian(s)) >= 0) {
878 hp->endianMatch = (rval == nativebigendian());
879 return 1;
880 }
881 if (formatval(hp->fmt, s))
882 return 1;
883 return 0;
884 }
885
886 // Reopen output file(s), leaving pointers at end of (each) header
887 RenderDataType
888 RpictSimulManager::ReopenOutput(FILE *pdfp[2], const char *pfname, const char *dfname)
889 {
890 extern const char HDRSTR[];
891
892 if (!pfname || pfname[0] == '!') {
893 pdfp[0] = pdfp[1] = NULL;
894 return RDTnone;
895 }
896 RenderDataType dt = RDTnone;
897 pdfp[1] = NULL;
898 pdfp[0] = fopen(pfname, "r+");
899 if (!pdfp[0]) {
900 sprintf(errmsg, "cannot reopen output picture '%s'", pfname);
901 error(SYSTEM, errmsg);
902 return RDTnone;
903 }
904 SET_FILE_BINARY(pdfp[0]); // read header information
905 if (getheader(pdfp[0], head_check, &hinfo) < 0) {
906 fclose(pdfp[0]);
907 pdfp[0] = NULL;
908 return RDTnone;
909 }
910 if (!hinfo.gotview) {
911 sprintf(errmsg, "missing view for '%s'", pfname);
912 error(USER, errmsg);
913 fclose(pdfp[0]);
914 pdfp[0] = NULL;
915 return RDTnone;
916 }
917 if (hinfo.ncomp < 3) {
918 sprintf(errmsg, "bad # components (%d) in '%s'", hinfo.ncomp, pfname);
919 error(USER, errmsg);
920 fclose(pdfp[0]);
921 pdfp[0] = NULL;
922 return RDTnone;
923 }
924 // set rendering/output space
925 if (!strcmp(hinfo.fmt, COLRFMT)) {
926 prims = hinfo.prims; // XXX static array
927 int n = 8*hinfo.gotprims;
928 while (n--)
929 if (!FABSEQ(hinfo.prims[0][n], stdprims[0][n]))
930 break;
931 if (n < 0)
932 prims = stdprims;
933 dt = RDTnewCT(dt, RDTrgbe);
934 } else if (!strcmp(hinfo.fmt, CIEFMT)) {
935 prims = xyzprims;
936 dt = RDTnewCT(dt, RDTxyze);
937 } else if (!strcmp(hinfo.fmt, SPECFMT)) {
938 if ((hinfo.ncomp <= 3) | (hinfo.ncomp > MAXCSAMP)) {
939 sprintf(errmsg, "incompatible sample count (%d) in '%s'",
940 hinfo.ncomp, pfname);
941 error(USER, errmsg);
942 fclose(pdfp[0]);
943 pdfp[0] = NULL;
944 return RDTnone;
945 }
946 NCSAMP = hinfo.ncomp; // overrides global setting
947 prims = NULL;
948 dt = RDTnewCT(dt, RDTscolr);
949 } else if (!strcmp(hinfo.fmt, "float")) {
950 if (!hinfo.endianMatch) {
951 sprintf(errmsg, "incompatible byte ordering in '%s'", pfname);
952 error(USER, errmsg);
953 fclose(pdfp[0]);
954 pdfp[0] = NULL;
955 return RDTnone;
956 }
957 if (hinfo.ncomp == 3) {
958 prims = hinfo.prims; // custom primaries?
959 int n = 8*hinfo.gotprims;
960 while (n--)
961 if (!FABSEQ(hinfo.prims[0][n], stdprims[0][n]))
962 break;
963 if (n < 0) // standard primaries?
964 prims = stdprims;
965 else if (hinfo.gotprims) { // or check if XYZ
966 for (n = 8; n--; )
967 if (!FABSEQ(prims[0][n], xyzprims[0][n]))
968 break;
969 if (n < 0)
970 prims = xyzprims;
971 }
972 if (prims == xyzprims)
973 dt = RDTnewCT(dt, RDTxyz);
974 else
975 dt = RDTnewCT(dt, RDTrgb);
976 } else {
977 NCSAMP = hinfo.ncomp; // overrides global setting
978 prims = NULL;
979 dt = RDTnewCT(dt, RDTscolor);
980 }
981 } else {
982 sprintf(errmsg, "unknown format (%s) for '%s'", hinfo.fmt, pfname);
983 error(USER, errmsg);
984 fclose(pdfp[0]);
985 pdfp[0] = NULL;
986 return RDTnone;
987 }
988 if (!dfname) // no depth file?
989 return dt;
990
991 if (dfname[0] == '!') {
992 error(USER, "depth data cannot be reloaded from command");
993 fclose(pdfp[0]);
994 pdfp[0] = NULL;
995 return RDTnone;
996 }
997 pdfp[1] = fopen(dfname, "r+");
998 if (!pdfp[1]) {
999 sprintf(errmsg, "cannot reopen depth file '%s'", dfname);
1000 error(SYSTEM, errmsg);
1001 fclose(pdfp[0]);
1002 pdfp[0] = NULL;
1003 return RDTnone;
1004 }
1005 SET_FILE_BINARY(pdfp[1]);
1006 int n, len = strlen(HDRSTR);
1007 char buf[32]; // sniff for 16-bit header
1008 if (getbinary(buf, 1, len+1, pdfp[1]) < len+1) {
1009 sprintf(errmsg, "empty depth file '%s'", dfname);
1010 error(SYSTEM, errmsg);
1011 fclose(pdfp[0]); fclose(pdfp[1]);
1012 pdfp[0] = pdfp[1] = NULL;
1013 return RDTnone;
1014 }
1015 for (n = 0; n < len; n++)
1016 if (buf[n] != HDRSTR[n])
1017 break; // not a Radiance header
1018 rewind(pdfp[1]);
1019 if ((n < len) | !isprint(buf[len]))
1020 return RDTnewDT(dt, RDTdfloat);
1021
1022 HeaderInfo dinfo; // thinking it's 16-bit encoded
1023 if (getheader(pdfp[1], head_check, &dinfo) < 0)
1024 sprintf(errmsg, "bad header in encoded depth file '%s'",
1025 dfname);
1026 else if (strcmp(dinfo.fmt, DEPTH16FMT))
1027 sprintf(errmsg, "wrong format (%s) for depth file '%s'",
1028 dinfo.fmt, dfname);
1029 else if (!SetReferenceDepth(dinfo.depth_unit))
1030 sprintf(errmsg, "bad/missing reference depth (%s) in '%s'",
1031 dinfo.depth_unit, dfname);
1032 else
1033 errmsg[0] = '\0';
1034
1035 if (errmsg[0]) {
1036 error(USER, errmsg);
1037 fclose(pdfp[1]); fclose(pdfp[0]);
1038 pdfp[0] = pdfp[1] = NULL;
1039 return RDTnone;
1040 }
1041 return RDTnewDT(dt, RDTdshort);
1042 }
1043
1044 // Resume partially finished rendering
1045 // Picture file must exist
1046 RenderDataType
1047 RpictSimulManager::ResumeFrame(const char *pfname, const char *dfname)
1048 {
1049 FILE *pdfp[2];
1050
1051 RenderDataType dt = ReopenOutput(pdfp, pfname, dfname);
1052 if (dt == RDTnone)
1053 return RDTnone;
1054
1055 int bytesPer = 0; // figure out how far we got...
1056 switch (RDTcolorT(dt)) {
1057 case RDTrgbe:
1058 case RDTxyze:
1059 break;
1060 case RDTscolr:
1061 bytesPer = hinfo.ncomp + 1; // XXX assumes no compression
1062 break;
1063 case RDTrgb:
1064 case RDTxyz:
1065 bytesPer = sizeof(float)*3;
1066 break;
1067 case RDTscolor:
1068 bytesPer = sizeof(float)*hinfo.ncomp;
1069 break;
1070 default:
1071 sprintf(errmsg, "unknown format (%s) for '%s'", hinfo.fmt, pfname);
1072 error(USER, errmsg);
1073 fclose(pdfp[0]);
1074 if (pdfp[1]) fclose(pdfp[1]);
1075 return RDTnone;
1076 }
1077 RESOLU res;
1078 if (!fgetsresolu(&res, pdfp[0]) || res.rt != PIXSTANDARD) {
1079 sprintf(errmsg, "missing/bad resolution for '%s'", pfname);
1080 error(USER, errmsg);
1081 fclose(pdfp[0]);
1082 if (pdfp[1]) fclose(pdfp[1]);
1083 return RDTnone;
1084 }
1085 vw.type = 0; // set up new (unreferenced) frame
1086 frameNo = 0;
1087 int hvdim[2] = {res.xr, res.yr};
1088 double noAdj = 0;
1089 if (!NewFrame(hinfo.vw, hvdim, &noAdj) ||
1090 (hvdim[0] != res.xr) | (hvdim[1] != res.yr)) {
1091 error(CONSISTENCY, "unexpected resolution change in ResumeFrame()");
1092 fclose(pdfp[0]);
1093 if (pdfp[1]) fclose(pdfp[1]);
1094 return RDTnone;
1095 }
1096 long dataStart = ftell(pdfp[0]); // picture starting point
1097 if (dataStart < 0) {
1098 sprintf(errmsg, "cannot seek on '%s'", pfname);
1099 error(SYSTEM, errmsg);
1100 fclose(pdfp[0]);
1101 if (pdfp[1]) fclose(pdfp[1]);
1102 return RDTnone;
1103 }
1104 long doneScans = 0;
1105 if (bytesPer) { // fixed-width records?
1106 fseek(pdfp[0], 0, SEEK_END);
1107 long dataEnd = ftell(pdfp[0]);
1108 doneScans = (dataEnd - dataStart)/(bytesPer*GetWidth());
1109 if (dataEnd-dataStart > bytesPer*GetWidth()*doneScans)
1110 fseek(pdfp[0], dataStart + bytesPer*GetWidth()*doneScans, SEEK_SET);
1111 } else { // else get compressed scanlines
1112 COLR * scan = (COLR *)tempbuffer(sizeof(COLR)*GetWidth());
1113 while (freadcolrs(scan, GetWidth(), pdfp[0]) >= 0)
1114 ++doneScans;
1115 if (!feof(pdfp[0])) {
1116 sprintf(errmsg, "error reading compressed scanline from '%s'", pfname);
1117 error(USER, errmsg);
1118 fclose(pdfp[0]);
1119 if (pdfp[1]) fclose(pdfp[1]);
1120 return RDTnone;
1121 }
1122 }
1123 if (doneScans >= GetHeight()) { // nothing left to do?
1124 sprintf(errmsg, "output file '%s' is already complete", pfname);
1125 error(WARNING, errmsg);
1126 fclose(pdfp[0]);
1127 if (pdfp[1]) fclose(pdfp[1]);
1128 return dt;
1129 }
1130 if (!doneScans) {
1131 sprintf(errmsg, "restarting empty frame '%s'", pfname);
1132 error(WARNING, errmsg);
1133 }
1134 long toSkip = 0;
1135 switch (RDTdepthT(dt)) { // append depth file, too?
1136 case RDTdfloat:
1137 toSkip = sizeof(float)*GetWidth()*doneScans;
1138 break;
1139 case RDTdshort:
1140 if (!fgetsresolu(&res, pdfp[1]) || (res.rt != PIXSTANDARD) |
1141 (res.xr != GetWidth()) | (res.yr != GetHeight())) {
1142 sprintf(errmsg, "missing/bad resolution for '%s'", dfname);
1143 error(USER, errmsg);
1144 fclose(pdfp[0]); fclose(pdfp[0]);
1145 return RDTnone;
1146 }
1147 toSkip = 2L*GetWidth()*doneScans;
1148 break;
1149 default:;
1150 } // fseek() needed for output
1151 if (pdfp[1] && fseek(pdfp[1], toSkip, SEEK_CUR) < 0) {
1152 sprintf(errmsg, "cannot seek on depth file '%s'", dfname);
1153 error(SYSTEM, errmsg);
1154 fclose(pdfp[0]); fclose(pdfp[1]);
1155 return RDTnone;
1156 }
1157 int bheight = (psample > 1) ? int(2*psample+.99) : 4;
1158 if (bheight > GetHeight()-doneScans)
1159 bheight = GetHeight()-doneScans;
1160 int vstep = bheight >> (psample > 1);
1161 vstep += !vstep;
1162
1163 NewBar(bheight); // render remainder if we can
1164 if (!RenderBelow(GetHeight()-doneScans, vstep, pdfp[0], dt, pdfp[1])) {
1165 fclose(pdfp[0]);
1166 if (pdfp[1]) fclose(pdfp[1]);
1167 Cleanup();
1168 return RDTnone;
1169 }
1170 NewBar(); // close up and return success
1171 fclose(pdfp[0]);
1172 if (pdfp[1]) fclose(pdfp[1]);
1173 return dt;
1174 }