ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/RpictSimulManager.cpp
Revision: 2.13
Committed: Fri Jan 10 19:09:12 2025 UTC (4 months, 2 weeks ago) by greg
Branch: MAIN
Changes since 2.12: +5 -4 lines
Log Message:
perf(rxpict): Improved error-handling

File Contents

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